opentrons 8.3.1a1__py2.py3-none-any.whl → 8.4.0a1__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.
Files changed (192) 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 +19 -3
  39. opentrons/legacy_commands/helpers.py +15 -0
  40. opentrons/legacy_commands/types.py +3 -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 +1233 -65
  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/protocol.py +253 -11
  52. opentrons/protocol_api/core/engine/stringify.py +19 -8
  53. opentrons/protocol_api/core/engine/transfer_components_executor.py +853 -0
  54. opentrons/protocol_api/core/engine/well.py +60 -5
  55. opentrons/protocol_api/core/instrument.py +65 -19
  56. opentrons/protocol_api/core/labware.py +6 -2
  57. opentrons/protocol_api/core/legacy/labware_offset_provider.py +7 -3
  58. opentrons/protocol_api/core/legacy/legacy_instrument_core.py +69 -21
  59. opentrons/protocol_api/core/legacy/legacy_labware_core.py +8 -4
  60. opentrons/protocol_api/core/legacy/legacy_protocol_core.py +36 -0
  61. opentrons/protocol_api/core/legacy/legacy_well_core.py +25 -1
  62. opentrons/protocol_api/core/legacy/load_info.py +4 -12
  63. opentrons/protocol_api/core/legacy/module_geometry.py +6 -1
  64. opentrons/protocol_api/core/legacy/well_geometry.py +3 -3
  65. opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +67 -21
  66. opentrons/protocol_api/core/module.py +43 -0
  67. opentrons/protocol_api/core/protocol.py +33 -0
  68. opentrons/protocol_api/core/well.py +21 -1
  69. opentrons/protocol_api/instrument_context.py +246 -123
  70. opentrons/protocol_api/labware.py +75 -11
  71. opentrons/protocol_api/module_contexts.py +140 -0
  72. opentrons/protocol_api/protocol_context.py +156 -16
  73. opentrons/protocol_api/validation.py +51 -41
  74. opentrons/protocol_engine/__init__.py +21 -2
  75. opentrons/protocol_engine/actions/actions.py +5 -5
  76. opentrons/protocol_engine/clients/sync_client.py +6 -0
  77. opentrons/protocol_engine/commands/__init__.py +30 -0
  78. opentrons/protocol_engine/commands/absorbance_reader/__init__.py +0 -1
  79. opentrons/protocol_engine/commands/air_gap_in_place.py +3 -2
  80. opentrons/protocol_engine/commands/aspirate.py +6 -2
  81. opentrons/protocol_engine/commands/aspirate_in_place.py +3 -1
  82. opentrons/protocol_engine/commands/aspirate_while_tracking.py +237 -0
  83. opentrons/protocol_engine/commands/blow_out.py +2 -0
  84. opentrons/protocol_engine/commands/blow_out_in_place.py +4 -1
  85. opentrons/protocol_engine/commands/command_unions.py +69 -0
  86. opentrons/protocol_engine/commands/configure_for_volume.py +3 -0
  87. opentrons/protocol_engine/commands/dispense.py +3 -1
  88. opentrons/protocol_engine/commands/dispense_in_place.py +3 -0
  89. opentrons/protocol_engine/commands/dispense_while_tracking.py +240 -0
  90. opentrons/protocol_engine/commands/drop_tip.py +23 -1
  91. opentrons/protocol_engine/commands/evotip_dispense.py +6 -7
  92. opentrons/protocol_engine/commands/evotip_seal_pipette.py +24 -29
  93. opentrons/protocol_engine/commands/evotip_unseal_pipette.py +1 -7
  94. opentrons/protocol_engine/commands/flex_stacker/__init__.py +106 -0
  95. opentrons/protocol_engine/commands/flex_stacker/close_latch.py +72 -0
  96. opentrons/protocol_engine/commands/flex_stacker/common.py +15 -0
  97. opentrons/protocol_engine/commands/flex_stacker/empty.py +161 -0
  98. opentrons/protocol_engine/commands/flex_stacker/fill.py +164 -0
  99. opentrons/protocol_engine/commands/flex_stacker/open_latch.py +70 -0
  100. opentrons/protocol_engine/commands/flex_stacker/prepare_shuttle.py +112 -0
  101. opentrons/protocol_engine/commands/flex_stacker/retrieve.py +394 -0
  102. opentrons/protocol_engine/commands/flex_stacker/set_stored_labware.py +190 -0
  103. opentrons/protocol_engine/commands/flex_stacker/store.py +288 -0
  104. opentrons/protocol_engine/commands/generate_command_schema.py +31 -2
  105. opentrons/protocol_engine/commands/labware_handling_common.py +24 -0
  106. opentrons/protocol_engine/commands/liquid_probe.py +21 -12
  107. opentrons/protocol_engine/commands/load_labware.py +42 -39
  108. opentrons/protocol_engine/commands/load_lid.py +21 -13
  109. opentrons/protocol_engine/commands/load_lid_stack.py +130 -47
  110. opentrons/protocol_engine/commands/load_module.py +18 -17
  111. opentrons/protocol_engine/commands/load_pipette.py +3 -0
  112. opentrons/protocol_engine/commands/move_labware.py +139 -20
  113. opentrons/protocol_engine/commands/pick_up_tip.py +5 -2
  114. opentrons/protocol_engine/commands/pipetting_common.py +154 -7
  115. opentrons/protocol_engine/commands/prepare_to_aspirate.py +17 -2
  116. opentrons/protocol_engine/commands/reload_labware.py +6 -19
  117. opentrons/protocol_engine/commands/unsafe/unsafe_blow_out_in_place.py +3 -1
  118. opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +6 -1
  119. opentrons/protocol_engine/errors/__init__.py +8 -0
  120. opentrons/protocol_engine/errors/exceptions.py +50 -0
  121. opentrons/protocol_engine/execution/equipment.py +123 -106
  122. opentrons/protocol_engine/execution/labware_movement.py +8 -6
  123. opentrons/protocol_engine/execution/pipetting.py +233 -26
  124. opentrons/protocol_engine/execution/tip_handler.py +14 -5
  125. opentrons/protocol_engine/labware_offset_standardization.py +173 -0
  126. opentrons/protocol_engine/protocol_engine.py +22 -13
  127. opentrons/protocol_engine/resources/deck_configuration_provider.py +94 -2
  128. opentrons/protocol_engine/resources/deck_data_provider.py +1 -1
  129. opentrons/protocol_engine/resources/labware_data_provider.py +32 -12
  130. opentrons/protocol_engine/resources/labware_validation.py +7 -5
  131. opentrons/protocol_engine/slot_standardization.py +11 -23
  132. opentrons/protocol_engine/state/addressable_areas.py +84 -46
  133. opentrons/protocol_engine/state/frustum_helpers.py +26 -10
  134. opentrons/protocol_engine/state/geometry.py +683 -100
  135. opentrons/protocol_engine/state/labware.py +252 -55
  136. opentrons/protocol_engine/state/module_substates/__init__.py +4 -0
  137. opentrons/protocol_engine/state/module_substates/flex_stacker_substate.py +68 -0
  138. opentrons/protocol_engine/state/module_substates/heater_shaker_module_substate.py +22 -0
  139. opentrons/protocol_engine/state/module_substates/temperature_module_substate.py +13 -0
  140. opentrons/protocol_engine/state/module_substates/thermocycler_module_substate.py +20 -0
  141. opentrons/protocol_engine/state/modules.py +178 -52
  142. opentrons/protocol_engine/state/pipettes.py +54 -0
  143. opentrons/protocol_engine/state/state.py +1 -1
  144. opentrons/protocol_engine/state/tips.py +14 -0
  145. opentrons/protocol_engine/state/update_types.py +180 -25
  146. opentrons/protocol_engine/state/wells.py +54 -8
  147. opentrons/protocol_engine/types/__init__.py +292 -0
  148. opentrons/protocol_engine/types/automatic_tip_selection.py +39 -0
  149. opentrons/protocol_engine/types/command_annotations.py +53 -0
  150. opentrons/protocol_engine/types/deck_configuration.py +72 -0
  151. opentrons/protocol_engine/types/execution.py +96 -0
  152. opentrons/protocol_engine/types/hardware_passthrough.py +25 -0
  153. opentrons/protocol_engine/types/instrument.py +47 -0
  154. opentrons/protocol_engine/types/instrument_sensors.py +47 -0
  155. opentrons/protocol_engine/types/labware.py +110 -0
  156. opentrons/protocol_engine/types/labware_movement.py +22 -0
  157. opentrons/protocol_engine/types/labware_offset_location.py +108 -0
  158. opentrons/protocol_engine/types/labware_offset_vector.py +33 -0
  159. opentrons/protocol_engine/types/liquid.py +40 -0
  160. opentrons/protocol_engine/types/liquid_class.py +59 -0
  161. opentrons/protocol_engine/types/liquid_handling.py +13 -0
  162. opentrons/protocol_engine/types/liquid_level_detection.py +137 -0
  163. opentrons/protocol_engine/types/location.py +193 -0
  164. opentrons/protocol_engine/types/module.py +269 -0
  165. opentrons/protocol_engine/types/partial_tip_configuration.py +76 -0
  166. opentrons/protocol_engine/types/run_time_parameters.py +133 -0
  167. opentrons/protocol_engine/types/tip.py +18 -0
  168. opentrons/protocol_engine/types/util.py +21 -0
  169. opentrons/protocol_engine/types/well_position.py +107 -0
  170. opentrons/protocol_reader/extract_labware_definitions.py +7 -3
  171. opentrons/protocol_reader/file_format_validator.py +5 -3
  172. opentrons/protocol_runner/json_translator.py +4 -2
  173. opentrons/protocol_runner/legacy_command_mapper.py +6 -2
  174. opentrons/protocol_runner/run_orchestrator.py +4 -1
  175. opentrons/protocols/advanced_control/transfers/common.py +48 -1
  176. opentrons/protocols/advanced_control/transfers/transfer_liquid_utils.py +204 -0
  177. opentrons/protocols/api_support/definitions.py +1 -1
  178. opentrons/protocols/api_support/instrument.py +16 -3
  179. opentrons/protocols/labware.py +5 -6
  180. opentrons/protocols/models/__init__.py +0 -21
  181. opentrons/simulate.py +4 -2
  182. opentrons/types.py +15 -6
  183. {opentrons-8.3.1a1.dist-info → opentrons-8.4.0a1.dist-info}/METADATA +4 -4
  184. {opentrons-8.3.1a1.dist-info → opentrons-8.4.0a1.dist-info}/RECORD +188 -148
  185. opentrons/calibration_storage/ot2/models/defaults.py +0 -0
  186. opentrons/calibration_storage/ot3/models/defaults.py +0 -0
  187. opentrons/protocol_api/core/legacy/legacy_robot_core.py +0 -0
  188. opentrons/protocol_engine/types.py +0 -1311
  189. {opentrons-8.3.1a1.dist-info → opentrons-8.4.0a1.dist-info}/LICENSE +0 -0
  190. {opentrons-8.3.1a1.dist-info → opentrons-8.4.0a1.dist-info}/WHEEL +0 -0
  191. {opentrons-8.3.1a1.dist-info → opentrons-8.4.0a1.dist-info}/entry_points.txt +0 -0
  192. {opentrons-8.3.1a1.dist-info → opentrons-8.4.0a1.dist-info}/top_level.txt +0 -0
@@ -7,8 +7,7 @@ from opentrons.types import MountType
7
7
  from opentrons.protocol_engine.types import MotorAxis
8
8
  from typing_extensions import Literal
9
9
 
10
- from opentrons.protocol_engine.errors import UnsupportedLabwareForActionError
11
- from ..resources import ModelUtils, labware_validation
10
+ from ..resources import ModelUtils
12
11
  from ..types import PickUpTipWellLocation, FluidKind, AspiratedFluid
13
12
  from .pipetting_common import (
14
13
  PipetteIdMixin,
@@ -41,10 +40,13 @@ if TYPE_CHECKING:
41
40
 
42
41
 
43
42
  EvotipSealPipetteCommandType = Literal["evotipSealPipette"]
44
- _PREP_DISTANCE_DEFAULT = 8.25
45
- _PRESS_DISTANCE_DEFAULT = 3.5
46
- _EJECTOR_PUSH_MM_DEFAULT = 7.0
47
- _SAFE_TOP_VOLUME = 400
43
+ _CAM_PREP_DISTANCE_DEFAULT = 8.25
44
+ _CAM_PRESS_DISTANCE_DEFAULT = 3.5
45
+ _CAM_EJECTOR_PUSH_MM_DEFAULT = 7.0
46
+ _PRESS_FIT_PREP_DISTANCE_DEFAULT = 0
47
+ _PRESS_FIT_PRESS_DISTANCE_DEFAULT = -11.0
48
+ _PRESS_FIT_EJECTOR_PUSH_MM_DEFAULT = 0
49
+ _SAFE_TOP_VOLUME = 1000
48
50
 
49
51
 
50
52
  class TipPickUpParams(BaseModel):
@@ -217,12 +219,6 @@ class EvotipSealPipetteImplementation(
217
219
  labware_id = params.labwareId
218
220
  well_name = params.wellName
219
221
 
220
- labware_definition = self._state_view.labware.get_definition(params.labwareId)
221
- if not labware_validation.is_evotips(labware_definition.parameters.loadName):
222
- raise UnsupportedLabwareForActionError(
223
- f"Cannot use command: `EvotipSealPipette` with labware: {labware_definition.parameters.loadName}"
224
- )
225
-
226
222
  well_location = self._state_view.geometry.convert_pick_up_tip_well_location(
227
223
  well_location=params.wellLocation
228
224
  )
@@ -251,29 +247,28 @@ class EvotipSealPipetteImplementation(
251
247
  channels = self._state_view.tips.get_pipette_active_channels(pipette_id)
252
248
  mount = self._state_view.pipettes.get_mount(pipette_id)
253
249
  tip_pick_up_params = params.tipPickUpParams
254
- if tip_pick_up_params is None:
255
- tip_pick_up_params = TipPickUpParams(
256
- prepDistance=_PREP_DISTANCE_DEFAULT,
257
- pressDistance=_PRESS_DISTANCE_DEFAULT,
258
- ejectorPushMm=_EJECTOR_PUSH_MM_DEFAULT,
259
- )
260
250
 
261
- if channels != 96:
262
- await self.relative_pickup_tip(
263
- tip_pick_up_params=tip_pick_up_params,
264
- mount=mount,
265
- )
266
- elif channels == 96:
251
+ if channels == 96:
252
+ if tip_pick_up_params is None:
253
+ tip_pick_up_params = TipPickUpParams(
254
+ prepDistance=_CAM_PREP_DISTANCE_DEFAULT,
255
+ pressDistance=_CAM_PRESS_DISTANCE_DEFAULT,
256
+ ejectorPushMm=_CAM_EJECTOR_PUSH_MM_DEFAULT,
257
+ )
267
258
  await self.cam_action_relative_pickup_tip(
268
259
  tip_pick_up_params=tip_pick_up_params,
269
260
  mount=mount,
270
261
  )
271
262
  else:
272
- tip_geometry = await self._tip_handler.pick_up_tip(
273
- pipette_id=pipette_id,
274
- labware_id=labware_id,
275
- well_name=well_name,
276
- do_not_ignore_tip_presence=True,
263
+ if tip_pick_up_params is None:
264
+ tip_pick_up_params = TipPickUpParams(
265
+ prepDistance=_PRESS_FIT_PREP_DISTANCE_DEFAULT,
266
+ pressDistance=_PRESS_FIT_PRESS_DISTANCE_DEFAULT,
267
+ ejectorPushMm=_PRESS_FIT_EJECTOR_PUSH_MM_DEFAULT,
268
+ )
269
+ await self.relative_pickup_tip(
270
+ tip_pick_up_params=tip_pick_up_params,
271
+ mount=mount,
277
272
  )
278
273
 
279
274
  # cache_tip
@@ -7,7 +7,6 @@ from typing import TYPE_CHECKING, Optional, Type
7
7
  from typing_extensions import Literal
8
8
 
9
9
  from opentrons.protocol_engine.resources.model_utils import ModelUtils
10
- from opentrons.protocol_engine.errors import UnsupportedLabwareForActionError
11
10
  from opentrons.protocol_engine.types import MotorAxis
12
11
  from opentrons.types import MountType
13
12
 
@@ -27,7 +26,6 @@ from .command import (
27
26
  DefinedErrorData,
28
27
  SuccessData,
29
28
  )
30
- from ..resources import labware_validation
31
29
 
32
30
  if TYPE_CHECKING:
33
31
  from ..state.state import StateView
@@ -86,11 +84,7 @@ class EvotipUnsealPipetteImplementation(
86
84
  well_name = params.wellName
87
85
 
88
86
  well_location = params.wellLocation
89
- labware_definition = self._state_view.labware.get_definition(params.labwareId)
90
- if not labware_validation.is_evotips(labware_definition.parameters.loadName):
91
- raise UnsupportedLabwareForActionError(
92
- f"Cannot use command: `EvotipUnsealPipette` with labware: {labware_definition.parameters.loadName}"
93
- )
87
+
94
88
  is_partially_configured = self._state_view.pipettes.get_is_partially_configured(
95
89
  pipette_id=pipette_id
96
90
  )
@@ -0,0 +1,106 @@
1
+ """Command models for Flex Stacker commands."""
2
+
3
+ from .store import (
4
+ StoreCommandType,
5
+ StoreParams,
6
+ StoreResult,
7
+ Store,
8
+ StoreCreate,
9
+ )
10
+
11
+ from .retrieve import (
12
+ RetrieveCommandType,
13
+ RetrieveParams,
14
+ RetrieveResult,
15
+ Retrieve,
16
+ RetrieveCreate,
17
+ )
18
+
19
+ from .set_stored_labware import (
20
+ SetStoredLabwareCommandType,
21
+ SetStoredLabwareParams,
22
+ SetStoredLabwareResult,
23
+ SetStoredLabware,
24
+ SetStoredLabwareCreate,
25
+ StackerStoredLabwareDetails,
26
+ )
27
+
28
+ from .fill import FillCommandType, FillParams, FillResult, Fill, FillCreate
29
+
30
+ from .empty import EmptyCommandType, EmptyParams, EmptyResult, Empty, EmptyCreate
31
+
32
+ from .close_latch import (
33
+ CloseLatchCommandType,
34
+ CloseLatchParams,
35
+ CloseLatchResult,
36
+ CloseLatch,
37
+ CloseLatchCreate,
38
+ )
39
+ from .open_latch import (
40
+ OpenLatchCommandType,
41
+ OpenLatchParams,
42
+ OpenLatchResult,
43
+ OpenLatch,
44
+ OpenLatchCreate,
45
+ )
46
+
47
+ from .prepare_shuttle import (
48
+ PrepareShuttleCommandType,
49
+ PrepareShuttleParams,
50
+ PrepareShuttleResult,
51
+ PrepareShuttle,
52
+ PrepareShuttleCreate,
53
+ )
54
+
55
+
56
+ __all__ = [
57
+ # flexStacker/store
58
+ "StoreCommandType",
59
+ "StoreParams",
60
+ "StoreResult",
61
+ "Store",
62
+ "StoreCreate",
63
+ # flexStacker/retrieve
64
+ "RetrieveCommandType",
65
+ "RetrieveParams",
66
+ "RetrieveResult",
67
+ "Retrieve",
68
+ "RetrieveCreate",
69
+ # flexStacker/setStoredLabware
70
+ "SetStoredLabwareCommandType",
71
+ "SetStoredLabwareParams",
72
+ "SetStoredLabwareResult",
73
+ "SetStoredLabware",
74
+ "SetStoredLabwareCreate",
75
+ "StackerStoredLabwareDetails",
76
+ # flexStacker/fill
77
+ "FillCommandType",
78
+ "FillParams",
79
+ "FillResult",
80
+ "Fill",
81
+ "FillCreate",
82
+ # flexStacker/empty
83
+ "EmptyCommandType",
84
+ "EmptyParams",
85
+ "EmptyResult",
86
+ "Empty",
87
+ "EmptyCreate",
88
+ # flexStacker/closeLatch
89
+ "CloseLatchCommandType",
90
+ "CloseLatchParams",
91
+ "CloseLatchResult",
92
+ "CloseLatch",
93
+ "CloseLatchCreate",
94
+ # flexStacker/openLatch
95
+ "OpenLatchCommandType",
96
+ "OpenLatchParams",
97
+ "OpenLatchResult",
98
+ "OpenLatch",
99
+ "OpenLatchCreate",
100
+ # flexStacker/prepareShuttle
101
+ "PrepareShuttleCommandType",
102
+ "PrepareShuttleParams",
103
+ "PrepareShuttleResult",
104
+ "PrepareShuttle",
105
+ "PrepareShuttleCreate",
106
+ ]
@@ -0,0 +1,72 @@
1
+ """Command models to close the latch of a Flex Stacker."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from __future__ import annotations
6
+ from typing import Optional, Literal, TYPE_CHECKING
7
+ from typing_extensions import Type
8
+
9
+ from pydantic import BaseModel, Field
10
+
11
+ from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
12
+ from ...errors import (
13
+ ErrorOccurrence,
14
+ )
15
+
16
+ if TYPE_CHECKING:
17
+ from ...state.state import StateView
18
+ from opentrons.protocol_engine.execution import EquipmentHandler
19
+
20
+ CloseLatchCommandType = Literal["flexStacker/closeLatch"]
21
+
22
+
23
+ class CloseLatchParams(BaseModel):
24
+ """The parameters defining how a stacker should close its latch."""
25
+
26
+ moduleId: str = Field(..., description="Unique ID of the Flex Stacker")
27
+
28
+
29
+ class CloseLatchResult(BaseModel):
30
+ """Result data from a stacker CloseLatch command."""
31
+
32
+
33
+ class CloseLatchImpl(
34
+ AbstractCommandImpl[CloseLatchParams, SuccessData[CloseLatchResult]]
35
+ ):
36
+ """Implementation of a stacker CloseLatch command."""
37
+
38
+ def __init__(
39
+ self, state_view: StateView, equipment: EquipmentHandler, **kwargs: object
40
+ ) -> None:
41
+ self._state_view = state_view
42
+ self._equipment = equipment
43
+
44
+ async def execute(self, params: CloseLatchParams) -> SuccessData[CloseLatchResult]:
45
+ """Execute the stacker CloseLatch command."""
46
+ stacker_state = self._state_view.modules.get_flex_stacker_substate(
47
+ params.moduleId
48
+ )
49
+ stacker_hw = self._equipment.get_module_hardware_api(stacker_state.module_id)
50
+
51
+ if stacker_hw is not None:
52
+ await stacker_hw.close_latch()
53
+ return SuccessData(public=CloseLatchResult())
54
+
55
+
56
+ class CloseLatch(BaseCommand[CloseLatchParams, CloseLatchResult, ErrorOccurrence]):
57
+ """A command to CloseLatch the Flex Stacker of labware."""
58
+
59
+ commandType: CloseLatchCommandType = "flexStacker/closeLatch"
60
+ params: CloseLatchParams
61
+ result: Optional[CloseLatchResult] = None
62
+
63
+ _ImplementationCls: Type[CloseLatchImpl] = CloseLatchImpl
64
+
65
+
66
+ class CloseLatchCreate(BaseCommandCreate[CloseLatchParams]):
67
+ """A request to execute a Flex Stacker CloseLatch command."""
68
+
69
+ commandType: CloseLatchCommandType = "flexStacker/closeLatch"
70
+ params: CloseLatchParams
71
+
72
+ _CommandCls: Type[CloseLatch] = CloseLatch
@@ -0,0 +1,15 @@
1
+ """Common flex stacker base models."""
2
+ from typing import Literal
3
+
4
+ from ...errors import ErrorOccurrence
5
+ from opentrons_shared_data.errors import ErrorCodes
6
+
7
+
8
+ class FlexStackerStallOrCollisionError(ErrorOccurrence):
9
+ """Returned when the motor driver detects a stall."""
10
+
11
+ isDefined: bool = True
12
+ errorType: Literal["flexStackerStallOrCollision"] = "flexStackerStallOrCollision"
13
+
14
+ errorCode: str = ErrorCodes.FLEX_STACKER_STALL_OR_COLLISION_DETECTED.value.code
15
+ detail: str = ErrorCodes.FLEX_STACKER_STALL_OR_COLLISION_DETECTED.value.detail
@@ -0,0 +1,161 @@
1
+ """Command models to engage a user to empty a Flex Stacker."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from __future__ import annotations
6
+ from typing import Optional, Literal, TYPE_CHECKING
7
+ from typing_extensions import Type
8
+
9
+ from pydantic import BaseModel, Field
10
+
11
+ from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
12
+ from ...errors import (
13
+ ErrorOccurrence,
14
+ )
15
+ from ...errors.exceptions import FlexStackerLabwarePoolNotYetDefinedError
16
+ from ...state import update_types
17
+ from ...types import StackerFillEmptyStrategy
18
+ from opentrons.calibration_storage.helpers import uri_from_details
19
+
20
+ if TYPE_CHECKING:
21
+ from ...state.state import StateView
22
+ from ...execution import RunControlHandler
23
+
24
+ EmptyCommandType = Literal["flexStacker/empty"]
25
+
26
+
27
+ class EmptyParams(BaseModel):
28
+ """The parameters defining how a stacker should be emptied."""
29
+
30
+ moduleId: str = Field(..., description="Unique ID of the Flex Stacker")
31
+
32
+ strategy: StackerFillEmptyStrategy = Field(
33
+ ...,
34
+ description=(
35
+ "How to empty the stacker. "
36
+ "If manualWithPause, pause the protocol until the client sends an interaction, and mark "
37
+ "the labware pool as empty thereafter. If logical, do not pause but immediately apply the "
38
+ "specified count."
39
+ ),
40
+ )
41
+
42
+ message: str | None = Field(
43
+ None,
44
+ description="The message to display on connected clients during a manualWithPause strategy empty.",
45
+ )
46
+
47
+ count: int | None = Field(
48
+ None,
49
+ description=(
50
+ "The new count of labware in the pool. If None, default to an empty pool. If this number is "
51
+ "larger than the amount of labware currently in the pool, default to the smaller amount. "
52
+ "Do not use the value in the parameters as an outside observer; instead, use the count value "
53
+ "from the results."
54
+ ),
55
+ ge=0,
56
+ )
57
+
58
+
59
+ class EmptyResult(BaseModel):
60
+ """Result data from a stacker empty command."""
61
+
62
+ count: int = Field(
63
+ ..., description="The new amount of labware stored in the stacker labware pool."
64
+ )
65
+ primaryLabwareURI: str = Field(
66
+ ...,
67
+ description="The labware definition URI of the primary labware.",
68
+ )
69
+ adapterLabwareURI: str | None = Field(
70
+ None,
71
+ description="The labware definition URI of the adapter labware.",
72
+ )
73
+ lidLabwareURI: str | None = Field(
74
+ None,
75
+ description="The labware definition URI of the lid labware.",
76
+ )
77
+
78
+
79
+ class EmptyImpl(AbstractCommandImpl[EmptyParams, SuccessData[EmptyResult]]):
80
+ """Implementation of a stacker empty command."""
81
+
82
+ def __init__(
83
+ self, state_view: StateView, run_control: RunControlHandler, **kwargs: object
84
+ ) -> None:
85
+ self._state_view = state_view
86
+ self._run_control = run_control
87
+
88
+ async def execute(self, params: EmptyParams) -> SuccessData[EmptyResult]:
89
+ """Execute the stacker empty command."""
90
+ stacker_state = self._state_view.modules.get_flex_stacker_substate(
91
+ params.moduleId
92
+ )
93
+
94
+ if stacker_state.pool_primary_definition is None:
95
+ location = self._state_view.modules.get_location(params.moduleId)
96
+ raise FlexStackerLabwarePoolNotYetDefinedError(
97
+ message=f"The Flex Stacker in {location} has not been configured yet and cannot be emptied."
98
+ )
99
+
100
+ count = params.count if params.count is not None else 0
101
+
102
+ new_count = min(stacker_state.pool_count, count)
103
+
104
+ state_update = (
105
+ update_types.StateUpdate().update_flex_stacker_labware_pool_count(
106
+ params.moduleId, new_count
107
+ )
108
+ )
109
+
110
+ if params.strategy == StackerFillEmptyStrategy.MANUAL_WITH_PAUSE:
111
+ await self._run_control.wait_for_resume()
112
+
113
+ if stacker_state.pool_primary_definition is None:
114
+ raise FlexStackerLabwarePoolNotYetDefinedError(
115
+ "The Primary Labware must be defined in the stacker pool."
116
+ )
117
+
118
+ return SuccessData(
119
+ public=EmptyResult(
120
+ count=new_count,
121
+ primaryLabwareURI=uri_from_details(
122
+ stacker_state.pool_primary_definition.namespace,
123
+ stacker_state.pool_primary_definition.parameters.loadName,
124
+ stacker_state.pool_primary_definition.version,
125
+ ),
126
+ adapterLabwareURI=uri_from_details(
127
+ stacker_state.pool_adapter_definition.namespace,
128
+ stacker_state.pool_adapter_definition.parameters.loadName,
129
+ stacker_state.pool_adapter_definition.version,
130
+ )
131
+ if stacker_state.pool_adapter_definition is not None
132
+ else None,
133
+ lidLabwareURI=uri_from_details(
134
+ stacker_state.pool_lid_definition.namespace,
135
+ stacker_state.pool_lid_definition.parameters.loadName,
136
+ stacker_state.pool_lid_definition.version,
137
+ )
138
+ if stacker_state.pool_lid_definition is not None
139
+ else None,
140
+ ),
141
+ state_update=state_update,
142
+ )
143
+
144
+
145
+ class Empty(BaseCommand[EmptyParams, EmptyResult, ErrorOccurrence]):
146
+ """A command to empty the Flex Stacker of labware."""
147
+
148
+ commandType: EmptyCommandType = "flexStacker/empty"
149
+ params: EmptyParams
150
+ result: Optional[EmptyResult] = None
151
+
152
+ _ImplementationCls: Type[EmptyImpl] = EmptyImpl
153
+
154
+
155
+ class EmptyCreate(BaseCommandCreate[EmptyParams]):
156
+ """A request to execute a Flex Stacker empty command."""
157
+
158
+ commandType: EmptyCommandType = "flexStacker/empty"
159
+ params: EmptyParams
160
+
161
+ _CommandCls: Type[Empty] = Empty
@@ -0,0 +1,164 @@
1
+ """Command models to engage a user to empty a Flex Stacker."""
2
+
3
+ from __future__ import annotations
4
+ from typing import Optional, Literal, TYPE_CHECKING
5
+ from typing_extensions import Type
6
+
7
+ from pydantic import BaseModel, Field
8
+
9
+ from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
10
+ from ...errors import (
11
+ ErrorOccurrence,
12
+ )
13
+ from ...errors.exceptions import FlexStackerLabwarePoolNotYetDefinedError
14
+ from ...state import update_types
15
+ from ...types import StackerFillEmptyStrategy
16
+ from opentrons.calibration_storage.helpers import uri_from_details
17
+
18
+ if TYPE_CHECKING:
19
+ from ...state.state import StateView
20
+ from ...execution import RunControlHandler
21
+
22
+ FillCommandType = Literal["flexStacker/fill"]
23
+
24
+
25
+ class FillParams(BaseModel):
26
+ """The parameters defining how a stacker should be filled."""
27
+
28
+ moduleId: str = Field(..., description="Unique ID of the Flex Stacker")
29
+
30
+ strategy: StackerFillEmptyStrategy = Field(
31
+ ...,
32
+ description=(
33
+ "How to fill the stacker. "
34
+ "If manualWithPause, pause the protocol until the client sends an interaction, and apply "
35
+ "the new specified count thereafter. If logical, do not pause but immediately apply the "
36
+ "specified count."
37
+ ),
38
+ )
39
+
40
+ message: str | None = Field(
41
+ None,
42
+ description="The message to display on connected clients during a manualWithPause strategy fill.",
43
+ )
44
+
45
+ count: int | None = Field(
46
+ None,
47
+ description=(
48
+ "How full the labware pool should now be. If None, default to the maximum amount "
49
+ "of the currently-configured labware the pool can hold. "
50
+ "If this number is larger than the maximum the pool can hold, it will be clamped to "
51
+ "the maximum. If this number is smaller than the current amount of labware the pool "
52
+ "holds, it will be clamped to that minimum. Do not use the value in the parameters as "
53
+ "an outside observer; instead, use the count value from the results."
54
+ ),
55
+ ge=1,
56
+ )
57
+
58
+
59
+ class FillResult(BaseModel):
60
+ """Result data from a stacker fill command."""
61
+
62
+ count: int = Field(
63
+ ..., description="The new amount of labware stored in the stacker labware pool."
64
+ )
65
+ primaryLabwareURI: str = Field(
66
+ ...,
67
+ description="The labware definition URI of the primary labware.",
68
+ )
69
+ adapterLabwareURI: str | None = Field(
70
+ None,
71
+ description="The labware definition URI of the adapter labware.",
72
+ )
73
+ lidLabwareURI: str | None = Field(
74
+ None,
75
+ description="The labware definition URI of the lid labware.",
76
+ )
77
+
78
+
79
+ class FillImpl(AbstractCommandImpl[FillParams, SuccessData[FillResult]]):
80
+ """Implementation of a stacker fill command."""
81
+
82
+ def __init__(
83
+ self, state_view: StateView, run_control: RunControlHandler, **kwargs: object
84
+ ) -> None:
85
+ self._state_view = state_view
86
+ self._run_control = run_control
87
+
88
+ async def execute(self, params: FillParams) -> SuccessData[FillResult]:
89
+ """Execute the stacker fill command."""
90
+ stacker_state = self._state_view.modules.get_flex_stacker_substate(
91
+ params.moduleId
92
+ )
93
+
94
+ if stacker_state.pool_primary_definition is None:
95
+ location = self._state_view.modules.get_location(params.moduleId)
96
+ raise FlexStackerLabwarePoolNotYetDefinedError(
97
+ message=f"The Flex Stacker in {location} has not been configured yet and cannot be filled."
98
+ )
99
+
100
+ count = (
101
+ params.count if params.count is not None else stacker_state.max_pool_count
102
+ )
103
+ new_count = min(
104
+ stacker_state.max_pool_count, max(stacker_state.pool_count, count)
105
+ )
106
+
107
+ state_update = (
108
+ update_types.StateUpdate().update_flex_stacker_labware_pool_count(
109
+ params.moduleId, new_count
110
+ )
111
+ )
112
+
113
+ if params.strategy == StackerFillEmptyStrategy.MANUAL_WITH_PAUSE:
114
+ await self._run_control.wait_for_resume()
115
+
116
+ if stacker_state.pool_primary_definition is None:
117
+ raise FlexStackerLabwarePoolNotYetDefinedError(
118
+ "The Primary Labware must be defined in the stacker pool."
119
+ )
120
+
121
+ return SuccessData(
122
+ public=FillResult(
123
+ count=new_count,
124
+ primaryLabwareURI=uri_from_details(
125
+ stacker_state.pool_primary_definition.namespace,
126
+ stacker_state.pool_primary_definition.parameters.loadName,
127
+ stacker_state.pool_primary_definition.version,
128
+ ),
129
+ adapterLabwareURI=uri_from_details(
130
+ stacker_state.pool_adapter_definition.namespace,
131
+ stacker_state.pool_adapter_definition.parameters.loadName,
132
+ stacker_state.pool_adapter_definition.version,
133
+ )
134
+ if stacker_state.pool_adapter_definition is not None
135
+ else None,
136
+ lidLabwareURI=uri_from_details(
137
+ stacker_state.pool_lid_definition.namespace,
138
+ stacker_state.pool_lid_definition.parameters.loadName,
139
+ stacker_state.pool_lid_definition.version,
140
+ )
141
+ if stacker_state.pool_lid_definition is not None
142
+ else None,
143
+ ),
144
+ state_update=state_update,
145
+ )
146
+
147
+
148
+ class Fill(BaseCommand[FillParams, FillResult, ErrorOccurrence]):
149
+ """A command to fill the Flex Stacker with labware."""
150
+
151
+ commandType: FillCommandType = "flexStacker/fill"
152
+ params: FillParams
153
+ result: Optional[FillResult] = None
154
+
155
+ _ImplementationCls: Type[FillImpl] = FillImpl
156
+
157
+
158
+ class FillCreate(BaseCommandCreate[FillParams]):
159
+ """A request to execute a Flex Stacker fill command."""
160
+
161
+ commandType: FillCommandType = "flexStacker/fill"
162
+ params: FillParams
163
+
164
+ _CommandCls: Type[Fill] = Fill