opentrons 8.3.2a0__py2.py3-none-any.whl → 8.4.0__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of opentrons might be problematic. Click here for more details.

Files changed (196) hide show
  1. opentrons/calibration_storage/ot2/mark_bad_calibration.py +2 -0
  2. opentrons/calibration_storage/ot2/tip_length.py +6 -6
  3. opentrons/config/advanced_settings.py +9 -11
  4. opentrons/config/feature_flags.py +0 -4
  5. opentrons/config/reset.py +7 -2
  6. opentrons/drivers/asyncio/communication/__init__.py +2 -0
  7. opentrons/drivers/asyncio/communication/async_serial.py +4 -0
  8. opentrons/drivers/asyncio/communication/errors.py +41 -8
  9. opentrons/drivers/asyncio/communication/serial_connection.py +36 -10
  10. opentrons/drivers/flex_stacker/__init__.py +9 -3
  11. opentrons/drivers/flex_stacker/abstract.py +140 -15
  12. opentrons/drivers/flex_stacker/driver.py +593 -47
  13. opentrons/drivers/flex_stacker/errors.py +64 -0
  14. opentrons/drivers/flex_stacker/simulator.py +222 -24
  15. opentrons/drivers/flex_stacker/types.py +211 -15
  16. opentrons/drivers/flex_stacker/utils.py +19 -0
  17. opentrons/execute.py +4 -2
  18. opentrons/hardware_control/api.py +5 -0
  19. opentrons/hardware_control/backends/flex_protocol.py +4 -0
  20. opentrons/hardware_control/backends/ot3controller.py +12 -1
  21. opentrons/hardware_control/backends/ot3simulator.py +3 -0
  22. opentrons/hardware_control/backends/subsystem_manager.py +8 -4
  23. opentrons/hardware_control/instruments/ot2/instrument_calibration.py +10 -6
  24. opentrons/hardware_control/instruments/ot3/pipette_handler.py +59 -6
  25. opentrons/hardware_control/modules/__init__.py +12 -1
  26. opentrons/hardware_control/modules/absorbance_reader.py +11 -9
  27. opentrons/hardware_control/modules/flex_stacker.py +498 -0
  28. opentrons/hardware_control/modules/heater_shaker.py +12 -10
  29. opentrons/hardware_control/modules/magdeck.py +5 -1
  30. opentrons/hardware_control/modules/tempdeck.py +5 -1
  31. opentrons/hardware_control/modules/thermocycler.py +15 -14
  32. opentrons/hardware_control/modules/types.py +191 -1
  33. opentrons/hardware_control/modules/utils.py +3 -0
  34. opentrons/hardware_control/motion_utilities.py +20 -0
  35. opentrons/hardware_control/ot3api.py +145 -15
  36. opentrons/hardware_control/protocols/liquid_handler.py +47 -1
  37. opentrons/hardware_control/types.py +6 -0
  38. opentrons/legacy_commands/commands.py +102 -5
  39. opentrons/legacy_commands/helpers.py +74 -1
  40. opentrons/legacy_commands/types.py +33 -2
  41. opentrons/protocol_api/__init__.py +2 -0
  42. opentrons/protocol_api/_liquid.py +39 -8
  43. opentrons/protocol_api/_liquid_properties.py +20 -19
  44. opentrons/protocol_api/_transfer_liquid_validation.py +91 -0
  45. opentrons/protocol_api/core/common.py +3 -1
  46. opentrons/protocol_api/core/engine/deck_conflict.py +11 -1
  47. opentrons/protocol_api/core/engine/instrument.py +1356 -107
  48. opentrons/protocol_api/core/engine/labware.py +8 -4
  49. opentrons/protocol_api/core/engine/load_labware_params.py +68 -10
  50. opentrons/protocol_api/core/engine/module_core.py +118 -2
  51. opentrons/protocol_api/core/engine/pipette_movement_conflict.py +6 -14
  52. opentrons/protocol_api/core/engine/protocol.py +253 -11
  53. opentrons/protocol_api/core/engine/stringify.py +19 -8
  54. opentrons/protocol_api/core/engine/transfer_components_executor.py +858 -0
  55. opentrons/protocol_api/core/engine/well.py +73 -5
  56. opentrons/protocol_api/core/instrument.py +71 -21
  57. opentrons/protocol_api/core/labware.py +6 -2
  58. opentrons/protocol_api/core/legacy/labware_offset_provider.py +7 -3
  59. opentrons/protocol_api/core/legacy/legacy_instrument_core.py +76 -49
  60. opentrons/protocol_api/core/legacy/legacy_labware_core.py +8 -4
  61. opentrons/protocol_api/core/legacy/legacy_protocol_core.py +36 -0
  62. opentrons/protocol_api/core/legacy/legacy_well_core.py +27 -2
  63. opentrons/protocol_api/core/legacy/load_info.py +4 -12
  64. opentrons/protocol_api/core/legacy/module_geometry.py +6 -1
  65. opentrons/protocol_api/core/legacy/well_geometry.py +3 -3
  66. opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +73 -23
  67. opentrons/protocol_api/core/module.py +43 -0
  68. opentrons/protocol_api/core/protocol.py +33 -0
  69. opentrons/protocol_api/core/well.py +23 -2
  70. opentrons/protocol_api/instrument_context.py +454 -150
  71. opentrons/protocol_api/labware.py +98 -50
  72. opentrons/protocol_api/module_contexts.py +140 -0
  73. opentrons/protocol_api/protocol_context.py +163 -19
  74. opentrons/protocol_api/validation.py +51 -41
  75. opentrons/protocol_engine/__init__.py +21 -2
  76. opentrons/protocol_engine/actions/actions.py +5 -5
  77. opentrons/protocol_engine/clients/sync_client.py +6 -0
  78. opentrons/protocol_engine/commands/__init__.py +66 -36
  79. opentrons/protocol_engine/commands/absorbance_reader/__init__.py +0 -1
  80. opentrons/protocol_engine/commands/air_gap_in_place.py +3 -2
  81. opentrons/protocol_engine/commands/aspirate.py +6 -2
  82. opentrons/protocol_engine/commands/aspirate_in_place.py +3 -1
  83. opentrons/protocol_engine/commands/aspirate_while_tracking.py +210 -0
  84. opentrons/protocol_engine/commands/blow_out.py +2 -0
  85. opentrons/protocol_engine/commands/blow_out_in_place.py +4 -1
  86. opentrons/protocol_engine/commands/command_unions.py +102 -33
  87. opentrons/protocol_engine/commands/configure_for_volume.py +3 -0
  88. opentrons/protocol_engine/commands/dispense.py +3 -1
  89. opentrons/protocol_engine/commands/dispense_in_place.py +3 -0
  90. opentrons/protocol_engine/commands/dispense_while_tracking.py +204 -0
  91. opentrons/protocol_engine/commands/drop_tip.py +23 -1
  92. opentrons/protocol_engine/commands/flex_stacker/__init__.py +106 -0
  93. opentrons/protocol_engine/commands/flex_stacker/close_latch.py +72 -0
  94. opentrons/protocol_engine/commands/flex_stacker/common.py +15 -0
  95. opentrons/protocol_engine/commands/flex_stacker/empty.py +161 -0
  96. opentrons/protocol_engine/commands/flex_stacker/fill.py +164 -0
  97. opentrons/protocol_engine/commands/flex_stacker/open_latch.py +70 -0
  98. opentrons/protocol_engine/commands/flex_stacker/prepare_shuttle.py +112 -0
  99. opentrons/protocol_engine/commands/flex_stacker/retrieve.py +394 -0
  100. opentrons/protocol_engine/commands/flex_stacker/set_stored_labware.py +190 -0
  101. opentrons/protocol_engine/commands/flex_stacker/store.py +291 -0
  102. opentrons/protocol_engine/commands/generate_command_schema.py +31 -2
  103. opentrons/protocol_engine/commands/labware_handling_common.py +29 -0
  104. opentrons/protocol_engine/commands/liquid_probe.py +27 -13
  105. opentrons/protocol_engine/commands/load_labware.py +42 -39
  106. opentrons/protocol_engine/commands/load_lid.py +21 -13
  107. opentrons/protocol_engine/commands/load_lid_stack.py +130 -47
  108. opentrons/protocol_engine/commands/load_module.py +18 -17
  109. opentrons/protocol_engine/commands/load_pipette.py +3 -0
  110. opentrons/protocol_engine/commands/move_labware.py +139 -20
  111. opentrons/protocol_engine/commands/move_to_well.py +5 -11
  112. opentrons/protocol_engine/commands/pick_up_tip.py +5 -2
  113. opentrons/protocol_engine/commands/pipetting_common.py +159 -8
  114. opentrons/protocol_engine/commands/prepare_to_aspirate.py +15 -5
  115. opentrons/protocol_engine/commands/{evotip_dispense.py → pressure_dispense.py} +33 -34
  116. opentrons/protocol_engine/commands/reload_labware.py +6 -19
  117. opentrons/protocol_engine/commands/{evotip_seal_pipette.py → seal_pipette_to_tip.py} +97 -76
  118. opentrons/protocol_engine/commands/unsafe/unsafe_blow_out_in_place.py +3 -1
  119. opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +6 -1
  120. opentrons/protocol_engine/commands/{evotip_unseal_pipette.py → unseal_pipette_from_tip.py} +31 -40
  121. opentrons/protocol_engine/errors/__init__.py +10 -0
  122. opentrons/protocol_engine/errors/exceptions.py +62 -0
  123. opentrons/protocol_engine/execution/equipment.py +123 -106
  124. opentrons/protocol_engine/execution/labware_movement.py +8 -6
  125. opentrons/protocol_engine/execution/pipetting.py +235 -25
  126. opentrons/protocol_engine/execution/tip_handler.py +82 -32
  127. opentrons/protocol_engine/labware_offset_standardization.py +194 -0
  128. opentrons/protocol_engine/protocol_engine.py +22 -13
  129. opentrons/protocol_engine/resources/deck_configuration_provider.py +98 -2
  130. opentrons/protocol_engine/resources/deck_data_provider.py +1 -1
  131. opentrons/protocol_engine/resources/labware_data_provider.py +32 -12
  132. opentrons/protocol_engine/resources/labware_validation.py +7 -5
  133. opentrons/protocol_engine/slot_standardization.py +11 -23
  134. opentrons/protocol_engine/state/addressable_areas.py +84 -46
  135. opentrons/protocol_engine/state/frustum_helpers.py +36 -14
  136. opentrons/protocol_engine/state/geometry.py +892 -227
  137. opentrons/protocol_engine/state/labware.py +252 -55
  138. opentrons/protocol_engine/state/module_substates/__init__.py +4 -0
  139. opentrons/protocol_engine/state/module_substates/flex_stacker_substate.py +68 -0
  140. opentrons/protocol_engine/state/module_substates/heater_shaker_module_substate.py +22 -0
  141. opentrons/protocol_engine/state/module_substates/temperature_module_substate.py +13 -0
  142. opentrons/protocol_engine/state/module_substates/thermocycler_module_substate.py +20 -0
  143. opentrons/protocol_engine/state/modules.py +210 -67
  144. opentrons/protocol_engine/state/pipettes.py +54 -0
  145. opentrons/protocol_engine/state/state.py +1 -1
  146. opentrons/protocol_engine/state/tips.py +14 -0
  147. opentrons/protocol_engine/state/update_types.py +180 -25
  148. opentrons/protocol_engine/state/wells.py +55 -9
  149. opentrons/protocol_engine/types/__init__.py +300 -0
  150. opentrons/protocol_engine/types/automatic_tip_selection.py +39 -0
  151. opentrons/protocol_engine/types/command_annotations.py +53 -0
  152. opentrons/protocol_engine/types/deck_configuration.py +72 -0
  153. opentrons/protocol_engine/types/execution.py +96 -0
  154. opentrons/protocol_engine/types/hardware_passthrough.py +25 -0
  155. opentrons/protocol_engine/types/instrument.py +47 -0
  156. opentrons/protocol_engine/types/instrument_sensors.py +47 -0
  157. opentrons/protocol_engine/types/labware.py +111 -0
  158. opentrons/protocol_engine/types/labware_movement.py +22 -0
  159. opentrons/protocol_engine/types/labware_offset_location.py +111 -0
  160. opentrons/protocol_engine/types/labware_offset_vector.py +33 -0
  161. opentrons/protocol_engine/types/liquid.py +40 -0
  162. opentrons/protocol_engine/types/liquid_class.py +59 -0
  163. opentrons/protocol_engine/types/liquid_handling.py +13 -0
  164. opentrons/protocol_engine/types/liquid_level_detection.py +131 -0
  165. opentrons/protocol_engine/types/location.py +194 -0
  166. opentrons/protocol_engine/types/module.py +301 -0
  167. opentrons/protocol_engine/types/partial_tip_configuration.py +76 -0
  168. opentrons/protocol_engine/types/run_time_parameters.py +133 -0
  169. opentrons/protocol_engine/types/tip.py +18 -0
  170. opentrons/protocol_engine/types/util.py +21 -0
  171. opentrons/protocol_engine/types/well_position.py +124 -0
  172. opentrons/protocol_reader/extract_labware_definitions.py +7 -3
  173. opentrons/protocol_reader/file_format_validator.py +5 -3
  174. opentrons/protocol_runner/json_translator.py +4 -2
  175. opentrons/protocol_runner/legacy_command_mapper.py +6 -2
  176. opentrons/protocol_runner/run_orchestrator.py +4 -1
  177. opentrons/protocols/advanced_control/transfers/common.py +48 -1
  178. opentrons/protocols/advanced_control/transfers/transfer_liquid_utils.py +204 -0
  179. opentrons/protocols/api_support/definitions.py +1 -1
  180. opentrons/protocols/api_support/instrument.py +16 -3
  181. opentrons/protocols/labware.py +27 -23
  182. opentrons/protocols/models/__init__.py +0 -21
  183. opentrons/simulate.py +4 -2
  184. opentrons/types.py +20 -7
  185. opentrons/util/logging_config.py +94 -25
  186. opentrons/util/logging_queue_handler.py +61 -0
  187. {opentrons-8.3.2a0.dist-info → opentrons-8.4.0.dist-info}/METADATA +4 -4
  188. {opentrons-8.3.2a0.dist-info → opentrons-8.4.0.dist-info}/RECORD +192 -151
  189. opentrons/calibration_storage/ot2/models/defaults.py +0 -0
  190. opentrons/calibration_storage/ot3/models/defaults.py +0 -0
  191. opentrons/protocol_api/core/legacy/legacy_robot_core.py +0 -0
  192. opentrons/protocol_engine/types.py +0 -1311
  193. {opentrons-8.3.2a0.dist-info → opentrons-8.4.0.dist-info}/LICENSE +0 -0
  194. {opentrons-8.3.2a0.dist-info → opentrons-8.4.0.dist-info}/WHEEL +0 -0
  195. {opentrons-8.3.2a0.dist-info → opentrons-8.4.0.dist-info}/entry_points.txt +0 -0
  196. {opentrons-8.3.2a0.dist-info → opentrons-8.4.0.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,5 @@
1
1
  """Load lid command request, result, and implementation models."""
2
+
2
3
  from __future__ import annotations
3
4
  from pydantic import BaseModel, Field
4
5
  from typing import TYPE_CHECKING, Optional, Type
@@ -9,10 +10,12 @@ from opentrons_shared_data.labware.labware_definition import LabwareDefinition
9
10
  from ..errors import LabwareCannotBeStackedError, LabwareIsNotAllowedInLocationError
10
11
  from ..resources import labware_validation
11
12
  from ..types import (
12
- LabwareLocation,
13
+ LoadableLabwareLocation,
13
14
  OnLabwareLocation,
15
+ OnLabwareLocationSequenceComponent,
14
16
  )
15
17
 
18
+ from .labware_handling_common import LabwareHandlingResultMixin
16
19
  from .command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
17
20
  from ..errors.error_occurrence import ErrorOccurrence
18
21
  from ..state.update_types import StateUpdate
@@ -28,7 +31,7 @@ LoadLidCommandType = Literal["loadLid"]
28
31
  class LoadLidParams(BaseModel):
29
32
  """Payload required to load a lid onto a labware."""
30
33
 
31
- location: LabwareLocation = Field(
34
+ location: LoadableLabwareLocation = Field(
32
35
  ...,
33
36
  description="Labware the lid should be loaded onto.",
34
37
  )
@@ -46,13 +49,9 @@ class LoadLidParams(BaseModel):
46
49
  )
47
50
 
48
51
 
49
- class LoadLidResult(BaseModel):
52
+ class LoadLidResult(LabwareHandlingResultMixin):
50
53
  """Result data from the execution of a LoadLabware command."""
51
54
 
52
- labwareId: str = Field(
53
- ...,
54
- description="An ID to reference this lid labware in subsequent commands.",
55
- )
56
55
  definition: LabwareDefinition = Field(
57
56
  ...,
58
57
  description="The full definition data for this lid labware.",
@@ -88,9 +87,6 @@ class LoadLidImplementation(
88
87
  labware_id=None,
89
88
  )
90
89
 
91
- # TODO(chb 2024-12-12) these validation checks happen after the labware is loaded, because they rely on
92
- # on the definition. In practice this will not cause any issues since they will raise protocol ending
93
- # exception, but for correctness should be refactored to do this check beforehand.
94
90
  if not labware_validation.validate_definition_is_lid(loaded_labware.definition):
95
91
  raise LabwareCannotBeStackedError(
96
92
  f"Labware {params.loadName} is not a Lid and cannot be loaded onto {self._state_view.labware.get_display_name(params.location.labwareId)}."
@@ -99,9 +95,9 @@ class LoadLidImplementation(
99
95
  state_update = StateUpdate()
100
96
 
101
97
  # In the case of lids being loaded on top of other labware, set the parent labware's lid
102
- state_update.set_lid(
103
- parent_labware_id=params.location.labwareId,
104
- lid_id=loaded_labware.labware_id,
98
+ state_update.set_lids(
99
+ parent_labware_ids=[params.location.labwareId],
100
+ lid_ids=[loaded_labware.labware_id],
105
101
  )
106
102
 
107
103
  state_update.set_loaded_labware(
@@ -122,6 +118,18 @@ class LoadLidImplementation(
122
118
  public=LoadLidResult(
123
119
  labwareId=loaded_labware.labware_id,
124
120
  definition=loaded_labware.definition,
121
+ # Note: the lid is not yet loaded and therefore won't be found as the lid id for the
122
+ # labware onto which we're loading it, so build that part of the location sequence
123
+ # here and then build the rest of the sequence from the parent labware
124
+ locationSequence=[
125
+ OnLabwareLocationSequenceComponent(
126
+ labwareId=params.location.labwareId,
127
+ lidId=loaded_labware.labware_id,
128
+ )
129
+ ]
130
+ + self._state_view.geometry.get_location_sequence(
131
+ params.location.labwareId
132
+ ),
125
133
  ),
126
134
  state_update=state_update,
127
135
  )
@@ -1,7 +1,9 @@
1
1
  """Load lid stack command request, result, and implementation models."""
2
+
2
3
  from __future__ import annotations
3
4
  from pydantic import BaseModel, Field
4
- from typing import TYPE_CHECKING, Optional, Type, List
5
+ from typing import TYPE_CHECKING, Optional, Type, List, Any
6
+ from pydantic.json_schema import SkipJsonSchema
5
7
  from typing_extensions import Literal
6
8
 
7
9
  from opentrons_shared_data.labware.labware_definition import LabwareDefinition
@@ -9,10 +11,13 @@ from opentrons_shared_data.labware.labware_definition import LabwareDefinition
9
11
  from ..errors import LabwareIsNotAllowedInLocationError, ProtocolEngineError
10
12
  from ..resources import fixture_validation, labware_validation
11
13
  from ..types import (
12
- LabwareLocation,
14
+ LoadableLabwareLocation,
15
+ SYSTEM_LOCATION,
13
16
  OnLabwareLocation,
14
17
  DeckSlotLocation,
15
18
  AddressableAreaLocation,
19
+ LabwareLocationSequence,
20
+ OnLabwareLocationSequenceComponent,
16
21
  )
17
22
 
18
23
  from .command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
@@ -21,11 +26,16 @@ from ..state.update_types import StateUpdate
21
26
 
22
27
  if TYPE_CHECKING:
23
28
  from ..state.state import StateView
24
- from ..execution import EquipmentHandler
29
+ from ..execution import LoadedLabwareData, EquipmentHandler
25
30
 
26
31
 
27
32
  LoadLidStackCommandType = Literal["loadLidStack"]
28
33
 
34
+
35
+ def _remove_default(s: dict[str, Any]) -> None:
36
+ s.pop("default", None)
37
+
38
+
29
39
  _LID_STACK_PE_LABWARE = "protocol_engine_lid_stack_object"
30
40
  _LID_STACK_PE_NAMESPACE = "opentrons"
31
41
  _LID_STACK_PE_VERSION = 1
@@ -34,7 +44,7 @@ _LID_STACK_PE_VERSION = 1
34
44
  class LoadLidStackParams(BaseModel):
35
45
  """Payload required to load a lid stack onto a location."""
36
46
 
37
- location: LabwareLocation = Field(
47
+ location: LoadableLabwareLocation = Field(
38
48
  ...,
39
49
  description="Location the lid stack should be loaded into.",
40
50
  )
@@ -50,6 +60,18 @@ class LoadLidStackParams(BaseModel):
50
60
  ...,
51
61
  description="The lid labware definition version.",
52
62
  )
63
+ stackLabwareId: str | SkipJsonSchema[None] = Field(
64
+ None,
65
+ description="An optional ID to assign to the lid stack labware object created."
66
+ "If None, an ID will be generated.",
67
+ json_schema_extra=_remove_default,
68
+ )
69
+ labwareIds: List[str] | SkipJsonSchema[None] = Field(
70
+ None,
71
+ description="An optional list of IDs to assign to the lids in the stack."
72
+ "If None, an ID will be generated.",
73
+ json_schema_extra=_remove_default,
74
+ )
53
75
  quantity: int = Field(
54
76
  ...,
55
77
  description="The quantity of lids to load.",
@@ -61,19 +83,31 @@ class LoadLidStackResult(BaseModel):
61
83
 
62
84
  stackLabwareId: str = Field(
63
85
  ...,
64
- description="An ID to reference the Protocol Engine Labware Lid Stack in subsequent commands.",
86
+ description="An ID to reference the lid stack labware object created.",
65
87
  )
66
88
  labwareIds: List[str] = Field(
67
89
  ...,
68
90
  description="A list of lid labware IDs to reference the lids in this stack by. The first ID is the bottom of the stack.",
69
91
  )
70
- definition: LabwareDefinition = Field(
92
+ definition: LabwareDefinition | None = Field(
71
93
  ...,
72
94
  description="The full definition data for this lid labware.",
73
95
  )
74
- location: LabwareLocation = Field(
96
+ location: LoadableLabwareLocation = Field(
75
97
  ..., description="The Location that the stack of lid labware has been loaded."
76
98
  )
99
+ lidStackDefinition: LabwareDefinition | None = Field(
100
+ None,
101
+ description="The definition of the lid stack object. Optional for backwards-compatibility.",
102
+ )
103
+ stackLocationSequence: LabwareLocationSequence | None = Field(
104
+ None,
105
+ description="The location sequence for the lid stack labware object created.",
106
+ )
107
+ locationSequences: List[LabwareLocationSequence] | None = Field(
108
+ None,
109
+ description="The location sequences for the lids just loaded into the stack. These are in the same order as labwareIds.",
110
+ )
77
111
 
78
112
 
79
113
  class LoadLidStackImplementation(
@@ -87,10 +121,7 @@ class LoadLidStackImplementation(
87
121
  self._equipment = equipment
88
122
  self._state_view = state_view
89
123
 
90
- async def execute(
91
- self, params: LoadLidStackParams
92
- ) -> SuccessData[LoadLidStackResult]:
93
- """Load definition and calibration data necessary for a lid stack."""
124
+ def _validate_location(self, params: LoadLidStackParams) -> LoadableLabwareLocation:
94
125
  if isinstance(params.location, AddressableAreaLocation):
95
126
  area_name = params.location.addressableAreaName
96
127
  if not (
@@ -107,36 +138,47 @@ class LoadLidStackImplementation(
107
138
  self._state_view.addressable_areas.raise_if_area_not_in_deck_configuration(
108
139
  params.location.slotName.id
109
140
  )
110
-
111
- verified_location = self._state_view.geometry.ensure_location_not_occupied(
112
- params.location
113
- )
114
-
115
- lid_stack_object = await self._equipment.load_labware(
116
- load_name=_LID_STACK_PE_LABWARE,
117
- namespace=_LID_STACK_PE_NAMESPACE,
118
- version=_LID_STACK_PE_VERSION,
119
- location=verified_location,
120
- labware_id=None,
121
- )
122
- if not labware_validation.validate_definition_is_system(
123
- lid_stack_object.definition
124
- ):
141
+ if params.quantity <= 0 and params.location != SYSTEM_LOCATION:
125
142
  raise ProtocolEngineError(
126
- message="Lid Stack Labware Object Labware Definition does not contain required allowed role 'system'."
143
+ message="Lid Stack Labware Object with quantity 0 must be loaded onto System Location."
127
144
  )
145
+ return self._state_view.geometry.ensure_location_not_occupied(params.location)
128
146
 
129
- loaded_lid_labwares = await self._equipment.load_lids(
130
- load_name=params.loadName,
131
- namespace=params.namespace,
132
- version=params.version,
133
- location=OnLabwareLocation(labwareId=lid_stack_object.labware_id),
134
- quantity=params.quantity,
147
+ def _format_results(
148
+ self,
149
+ verified_location: LoadableLabwareLocation,
150
+ lid_stack_object: LoadedLabwareData,
151
+ loaded_lid_labwares: list[LoadedLabwareData],
152
+ lid_labware_definition: LabwareDefinition | None,
153
+ ) -> SuccessData[LoadLidStackResult]:
154
+ stack_location_sequence = (
155
+ self._state_view.geometry.get_predicted_location_sequence(verified_location)
135
156
  )
136
- loaded_lid_locations_by_id = {}
157
+ loaded_lid_locations_by_id: dict[str, OnLabwareLocation] = {}
158
+ loaded_lid_ids_ordered: list[str] = []
159
+ loaded_lid_location_sequences_ordered: list[LabwareLocationSequence] = []
160
+ lid_location_sequence_accumulated: LabwareLocationSequence = [
161
+ OnLabwareLocationSequenceComponent(
162
+ labwareId=lid_stack_object.labware_id, lidId=None
163
+ )
164
+ ] + stack_location_sequence
137
165
  load_location = OnLabwareLocation(labwareId=lid_stack_object.labware_id)
166
+ last_lid_id: str | None = None
138
167
  for loaded_lid in loaded_lid_labwares:
139
168
  loaded_lid_locations_by_id[loaded_lid.labware_id] = load_location
169
+ loaded_lid_ids_ordered.append(loaded_lid.labware_id)
170
+ if last_lid_id is None:
171
+ last_lid_id = loaded_lid.labware_id
172
+ else:
173
+ lid_location_sequence_accumulated = [
174
+ OnLabwareLocationSequenceComponent(
175
+ labwareId=last_lid_id, lidId=None
176
+ )
177
+ ] + lid_location_sequence_accumulated
178
+ last_lid_id = loaded_lid.labware_id
179
+ loaded_lid_location_sequences_ordered.append(
180
+ [loc for loc in lid_location_sequence_accumulated]
181
+ )
140
182
  load_location = OnLabwareLocation(labwareId=loaded_lid.labware_id)
141
183
 
142
184
  state_update = StateUpdate()
@@ -144,29 +186,70 @@ class LoadLidStackImplementation(
144
186
  stack_id=lid_stack_object.labware_id,
145
187
  stack_object_definition=lid_stack_object.definition,
146
188
  stack_location=verified_location,
147
- labware_ids=list(loaded_lid_locations_by_id.keys()),
148
- labware_definition=loaded_lid_labwares[0].definition,
149
189
  locations=loaded_lid_locations_by_id,
190
+ labware_definition=lid_labware_definition,
150
191
  )
151
192
 
152
- if isinstance(verified_location, OnLabwareLocation):
153
- self._state_view.labware.raise_if_labware_cannot_be_stacked(
154
- top_labware_definition=loaded_lid_labwares[
155
- params.quantity - 1
156
- ].definition,
157
- bottom_labware_id=verified_location.labwareId,
158
- )
159
-
160
193
  return SuccessData(
161
194
  public=LoadLidStackResult(
162
195
  stackLabwareId=lid_stack_object.labware_id,
163
- labwareIds=list(loaded_lid_locations_by_id.keys()),
164
- definition=loaded_lid_labwares[0].definition,
165
- location=params.location,
196
+ lidStackDefinition=lid_stack_object.definition,
197
+ labwareIds=loaded_lid_ids_ordered,
198
+ definition=lid_labware_definition,
199
+ location=verified_location,
200
+ stackLocationSequence=stack_location_sequence,
201
+ locationSequences=loaded_lid_location_sequences_ordered,
166
202
  ),
167
203
  state_update=state_update,
168
204
  )
169
205
 
206
+ async def execute(
207
+ self, params: LoadLidStackParams
208
+ ) -> SuccessData[LoadLidStackResult]:
209
+ """Load definition and calibration data necessary for a lid stack."""
210
+ verified_location = self._validate_location(params)
211
+
212
+ lid_stack_object = await self._equipment.load_labware(
213
+ load_name=_LID_STACK_PE_LABWARE,
214
+ namespace=_LID_STACK_PE_NAMESPACE,
215
+ version=_LID_STACK_PE_VERSION,
216
+ location=verified_location,
217
+ labware_id=params.stackLabwareId,
218
+ )
219
+
220
+ if not labware_validation.validate_definition_is_system(
221
+ lid_stack_object.definition
222
+ ):
223
+ raise ProtocolEngineError(
224
+ message="Lid Stack Labware Object Labware Definition does not contain required allowed role 'system'."
225
+ )
226
+
227
+ loaded_lid_labwares: list[LoadedLabwareData] = []
228
+ lid_labware_definition: LabwareDefinition | None = None
229
+
230
+ if params.quantity > 0:
231
+ loaded_lid_labwares = await self._equipment.load_lids(
232
+ load_name=params.loadName,
233
+ namespace=params.namespace,
234
+ version=params.version,
235
+ location=OnLabwareLocation(labwareId=lid_stack_object.labware_id),
236
+ quantity=params.quantity,
237
+ labware_ids=params.labwareIds,
238
+ )
239
+ lid_labware_definition = loaded_lid_labwares[-1].definition
240
+ if isinstance(verified_location, OnLabwareLocation):
241
+ self._state_view.labware.raise_if_labware_cannot_be_stacked(
242
+ top_labware_definition=lid_labware_definition,
243
+ bottom_labware_id=verified_location.labwareId,
244
+ )
245
+
246
+ return self._format_results(
247
+ verified_location,
248
+ lid_stack_object,
249
+ loaded_lid_labwares,
250
+ lid_labware_definition,
251
+ )
252
+
170
253
 
171
254
  class LoadLidStack(
172
255
  BaseCommand[LoadLidStackParams, LoadLidStackResult, ErrorOccurrence]
@@ -11,6 +11,7 @@ from .command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, Succes
11
11
  from ..errors.error_occurrence import ErrorOccurrence
12
12
  from ..types import (
13
13
  DeckSlotLocation,
14
+ AddressableAreaLocation,
14
15
  ModuleType,
15
16
  ModuleModel,
16
17
  ModuleDefinition,
@@ -129,41 +130,41 @@ class LoadModuleImplementation(
129
130
  module_type = params.model.as_type()
130
131
  self._ensure_module_location(params.location.slotName, module_type)
131
132
 
132
- # todo(mm, 2024-12-03): Theoretically, we should be able to deal with
133
- # addressable areas and deck configurations the same way between OT-2 and Flex.
134
- # Can this be simplified?
135
- if self._state_view.config.robot_type == "OT-2 Standard":
136
- self._state_view.addressable_areas.raise_if_area_not_in_deck_configuration(
137
- params.location.slotName.id
138
- )
139
- else:
140
- addressable_area_provided_by_module = (
133
+ if self._state_view.modules.get_deck_supports_module_fixtures():
134
+ addressable_area_module_reference = (
141
135
  self._state_view.modules.ensure_and_convert_module_fixture_location(
142
136
  deck_slot=params.location.slotName,
143
137
  model=params.model,
144
138
  )
145
139
  )
146
- self._state_view.addressable_areas.raise_if_area_not_in_deck_configuration(
147
- addressable_area_provided_by_module
140
+ else:
141
+ addressable_area_module_reference = params.location.slotName.id
142
+ state_update.set_addressable_area_used(
143
+ addressable_area_name=addressable_area_module_reference
148
144
  )
149
145
 
150
- verified_location = self._state_view.geometry.ensure_location_not_occupied(
151
- params.location
146
+ self._state_view.addressable_areas.raise_if_area_not_in_deck_configuration(
147
+ addressable_area_module_reference
152
148
  )
153
- state_update.set_addressable_area_used(
154
- addressable_area_name=params.location.slotName.id
149
+
150
+ self._state_view.geometry.ensure_location_not_occupied(
151
+ params.location, addressable_area_module_reference
155
152
  )
156
153
 
157
154
  if params.model == ModuleModel.MAGNETIC_BLOCK_V1:
158
155
  loaded_module = await self._equipment.load_magnetic_block(
159
156
  model=params.model,
160
- location=verified_location,
157
+ location=AddressableAreaLocation(
158
+ addressableAreaName=addressable_area_module_reference
159
+ ),
161
160
  module_id=params.moduleId,
162
161
  )
163
162
  else:
164
163
  loaded_module = await self._equipment.load_module(
165
164
  model=params.model,
166
- location=verified_location,
165
+ location=AddressableAreaLocation(
166
+ addressableAreaName=addressable_area_module_reference
167
+ ),
167
168
  module_id=params.moduleId,
168
169
  )
169
170
 
@@ -138,6 +138,9 @@ class LoadPipetteImplementation(
138
138
  config=loaded_pipette.static_config,
139
139
  )
140
140
  state_update.set_fluid_unknown(pipette_id=loaded_pipette.pipette_id)
141
+ state_update.set_pipette_ready_to_aspirate(
142
+ pipette_id=loaded_pipette.pipette_id, ready_to_aspirate=False
143
+ ),
141
144
 
142
145
  return SuccessData(
143
146
  public=LoadPipetteResult(pipetteId=loaded_pipette.pipette_id),