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
@@ -472,8 +472,9 @@ class TouchTipCommand(TypedDict):
472
472
  payload: TouchTipCommandPayload
473
473
 
474
474
 
475
- class AirGapCommandPayload(TextOnlyPayload):
476
- pass
475
+ class AirGapCommandPayload(TextOnlyPayload, SingleInstrumentPayload):
476
+ volume: Union[float, None]
477
+ height: Union[float, None]
477
478
 
478
479
 
479
480
  class AirGapCommand(TypedDict):
@@ -27,6 +27,7 @@ from .module_contexts import (
27
27
  HeaterShakerContext,
28
28
  MagneticBlockContext,
29
29
  AbsorbanceReaderContext,
30
+ FlexStackerContext,
30
31
  )
31
32
  from .disposal_locations import TrashBin, WasteChute
32
33
  from ._liquid import Liquid, LiquidClass
@@ -70,6 +71,7 @@ __all__ = [
70
71
  "HeaterShakerContext",
71
72
  "MagneticBlockContext",
72
73
  "AbsorbanceReaderContext",
74
+ "FlexStackerContext",
73
75
  "ParameterContext",
74
76
  "Labware",
75
77
  "TrashBin",
@@ -1,17 +1,24 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from dataclasses import dataclass
4
- from typing import Optional, Dict
4
+ from typing import Optional, Dict, Union, TYPE_CHECKING
5
5
 
6
6
  from opentrons_shared_data.liquid_classes.liquid_class_definition import (
7
7
  LiquidClassSchemaV1,
8
8
  )
9
9
 
10
+ from opentrons.protocols.advanced_control.transfers.common import (
11
+ NoLiquidClassPropertyError,
12
+ )
13
+
10
14
  from ._liquid_properties import (
11
15
  TransferProperties,
12
16
  build_transfer_properties,
13
17
  )
14
18
 
19
+ if TYPE_CHECKING:
20
+ from . import InstrumentContext, Labware
21
+
15
22
 
16
23
  @dataclass(frozen=True)
17
24
  class Liquid:
@@ -64,18 +71,42 @@ class LiquidClass:
64
71
  def display_name(self) -> str:
65
72
  return self._display_name
66
73
 
67
- def get_for(self, pipette: str, tiprack: str) -> TransferProperties:
74
+ def get_for(
75
+ self, pipette: Union[str, InstrumentContext], tip_rack: Union[str, Labware]
76
+ ) -> TransferProperties:
68
77
  """Get liquid class transfer properties for the specified pipette and tip."""
78
+ from . import InstrumentContext, Labware
79
+
80
+ if isinstance(pipette, InstrumentContext):
81
+ pipette_name = pipette.name
82
+ elif isinstance(pipette, str):
83
+ pipette_name = pipette
84
+ else:
85
+ raise ValueError(
86
+ f"{pipette} should either be an InstrumentContext object"
87
+ f" or a pipette name string."
88
+ )
89
+
90
+ if isinstance(tip_rack, Labware):
91
+ tiprack_uri = tip_rack.uri
92
+ elif isinstance(tip_rack, str):
93
+ tiprack_uri = tip_rack
94
+ else:
95
+ raise ValueError(
96
+ f"{tip_rack} should either be a tiprack Labware object"
97
+ f" or a tiprack URI string."
98
+ )
99
+
69
100
  try:
70
- settings_for_pipette = self._by_pipette_setting[pipette]
101
+ settings_for_pipette = self._by_pipette_setting[pipette_name]
71
102
  except KeyError:
72
- raise ValueError(
73
- f"No properties found for {pipette} in {self._name} liquid class"
103
+ raise NoLiquidClassPropertyError(
104
+ f"No properties found for {pipette_name} in {self._name} liquid class"
74
105
  )
75
106
  try:
76
- transfer_properties = settings_for_pipette[tiprack]
107
+ transfer_properties = settings_for_pipette[tiprack_uri]
77
108
  except KeyError:
78
- raise ValueError(
79
- f"No properties found for {tiprack} in {self._name} liquid class"
109
+ raise NoLiquidClassPropertyError(
110
+ f"No properties found for {tiprack_uri} for {pipette_name} in {self._name} liquid class"
80
111
  )
81
112
  return transfer_properties
@@ -83,7 +83,10 @@ class LiquidHandlingPropertyByVolume:
83
83
  )
84
84
 
85
85
 
86
- @dataclass
86
+ # We use slots for this dataclass (and the rest of liquid properties) to prevent dynamic creation of attributes
87
+ # not defined in the class, not for any performance reasons. This is so that mistyping properties when overriding
88
+ # values will cause the protocol to fail analysis, rather than silently passing.
89
+ @dataclass(slots=True)
87
90
  class DelayProperties:
88
91
 
89
92
  _enabled: bool
@@ -118,7 +121,7 @@ class DelayProperties:
118
121
  )
119
122
 
120
123
 
121
- @dataclass
124
+ @dataclass(slots=True)
122
125
  class TouchTipProperties:
123
126
 
124
127
  _enabled: bool
@@ -157,7 +160,7 @@ class TouchTipProperties:
157
160
  @mm_to_edge.setter
158
161
  def mm_to_edge(self, new_mm: float) -> None:
159
162
  validated_mm = validation.ensure_float(new_mm)
160
- self._z_offset = validated_mm
163
+ self._mm_to_edge = validated_mm
161
164
 
162
165
  @property
163
166
  def speed(self) -> Optional[float]:
@@ -165,7 +168,7 @@ class TouchTipProperties:
165
168
 
166
169
  @speed.setter
167
170
  def speed(self, new_speed: float) -> None:
168
- validated_speed = validation.ensure_positive_float(new_speed)
171
+ validated_speed = validation.ensure_greater_than_zero_float(new_speed)
169
172
  self._speed = validated_speed
170
173
 
171
174
  def _get_shared_data_params(self) -> Optional[SharedDataTouchTipParams]:
@@ -190,7 +193,7 @@ class TouchTipProperties:
190
193
  )
191
194
 
192
195
 
193
- @dataclass
196
+ @dataclass(slots=True)
194
197
  class MixProperties:
195
198
 
196
199
  _enabled: bool
@@ -223,7 +226,7 @@ class MixProperties:
223
226
 
224
227
  @volume.setter
225
228
  def volume(self, new_volume: float) -> None:
226
- validated_volume = validation.ensure_positive_float(new_volume)
229
+ validated_volume = validation.ensure_greater_than_zero_float(new_volume)
227
230
  self._volume = validated_volume
228
231
 
229
232
  def _get_shared_data_params(self) -> Optional[SharedDataMixParams]:
@@ -243,7 +246,7 @@ class MixProperties:
243
246
  )
244
247
 
245
248
 
246
- @dataclass
249
+ @dataclass(slots=True)
247
250
  class BlowoutProperties:
248
251
 
249
252
  _enabled: bool
@@ -277,7 +280,7 @@ class BlowoutProperties:
277
280
 
278
281
  @flow_rate.setter
279
282
  def flow_rate(self, new_flow_rate: float) -> None:
280
- validated_flow_rate = validation.ensure_positive_float(new_flow_rate)
283
+ validated_flow_rate = validation.ensure_greater_than_zero_float(new_flow_rate)
281
284
  self._flow_rate = validated_flow_rate
282
285
 
283
286
  def _get_shared_data_params(self) -> Optional[SharedDataBlowoutParams]:
@@ -297,7 +300,7 @@ class BlowoutProperties:
297
300
  )
298
301
 
299
302
 
300
- @dataclass
303
+ @dataclass(slots=True)
301
304
  class SubmergeRetractCommon:
302
305
 
303
306
  _position_reference: PositionReference
@@ -336,10 +339,8 @@ class SubmergeRetractCommon:
336
339
  return self._delay
337
340
 
338
341
 
339
- @dataclass
342
+ @dataclass(slots=True)
340
343
  class Submerge(SubmergeRetractCommon):
341
- ...
342
-
343
344
  def as_shared_data_model(self) -> SharedDataSubmerge:
344
345
  return SharedDataSubmerge(
345
346
  positionReference=self._position_reference,
@@ -349,7 +350,7 @@ class Submerge(SubmergeRetractCommon):
349
350
  )
350
351
 
351
352
 
352
- @dataclass
353
+ @dataclass(slots=True)
353
354
  class RetractAspirate(SubmergeRetractCommon):
354
355
 
355
356
  _air_gap_by_volume: LiquidHandlingPropertyByVolume
@@ -374,7 +375,7 @@ class RetractAspirate(SubmergeRetractCommon):
374
375
  )
375
376
 
376
377
 
377
- @dataclass
378
+ @dataclass(slots=True)
378
379
  class RetractDispense(SubmergeRetractCommon):
379
380
 
380
381
  _air_gap_by_volume: LiquidHandlingPropertyByVolume
@@ -405,7 +406,7 @@ class RetractDispense(SubmergeRetractCommon):
405
406
  )
406
407
 
407
408
 
408
- @dataclass
409
+ @dataclass(slots=True)
409
410
  class BaseLiquidHandlingProperties:
410
411
 
411
412
  _submerge: Submerge
@@ -449,7 +450,7 @@ class BaseLiquidHandlingProperties:
449
450
  return self._delay
450
451
 
451
452
 
452
- @dataclass
453
+ @dataclass(slots=True)
453
454
  class AspirateProperties(BaseLiquidHandlingProperties):
454
455
 
455
456
  _retract: RetractAspirate
@@ -487,7 +488,7 @@ class AspirateProperties(BaseLiquidHandlingProperties):
487
488
  )
488
489
 
489
490
 
490
- @dataclass
491
+ @dataclass(slots=True)
491
492
  class SingleDispenseProperties(BaseLiquidHandlingProperties):
492
493
 
493
494
  _retract: RetractDispense
@@ -520,7 +521,7 @@ class SingleDispenseProperties(BaseLiquidHandlingProperties):
520
521
  )
521
522
 
522
523
 
523
- @dataclass
524
+ @dataclass(slots=True)
524
525
  class MultiDispenseProperties(BaseLiquidHandlingProperties):
525
526
 
526
527
  _retract: RetractDispense
@@ -553,7 +554,7 @@ class MultiDispenseProperties(BaseLiquidHandlingProperties):
553
554
  )
554
555
 
555
556
 
556
- @dataclass
557
+ @dataclass(slots=True)
557
558
  class TransferProperties:
558
559
  _aspirate: AspirateProperties
559
560
  _dispense: SingleDispenseProperties
@@ -0,0 +1,91 @@
1
+ from dataclasses import dataclass
2
+ from typing import List, Union, Sequence, Optional
3
+
4
+ from opentrons.types import Location, NozzleMapInterface
5
+ from opentrons.protocols.api_support import instrument
6
+ from opentrons.protocols.advanced_control.transfers import (
7
+ transfer_liquid_utils as tx_liquid_utils,
8
+ )
9
+ from opentrons.protocols.advanced_control.transfers.common import (
10
+ TransferTipPolicyV2,
11
+ TransferTipPolicyV2Type,
12
+ )
13
+
14
+ from .disposal_locations import TrashBin, WasteChute
15
+ from .labware import Labware, Well
16
+ from . import validation
17
+
18
+
19
+ @dataclass
20
+ class TransferInfo:
21
+
22
+ sources_list: List[Well]
23
+ destinations_list: List[Well]
24
+ tip_policy: TransferTipPolicyV2
25
+ tip_racks: List[Labware]
26
+ trash_location: Union[Location, TrashBin, WasteChute]
27
+
28
+
29
+ def verify_and_normalize_transfer_args(
30
+ source: Union[Well, Sequence[Well], Sequence[Sequence[Well]]],
31
+ dest: Union[Well, Sequence[Well], Sequence[Sequence[Well]]],
32
+ tip_policy: TransferTipPolicyV2Type,
33
+ last_tip_picked_up_from: Optional[Well],
34
+ tip_racks: List[Labware],
35
+ nozzle_map: NozzleMapInterface,
36
+ target_all_wells: bool,
37
+ current_volume: float,
38
+ trash_location: Union[Location, Well, Labware, TrashBin, WasteChute],
39
+ ) -> TransferInfo:
40
+ flat_sources_list = validation.ensure_valid_flat_wells_list_for_transfer_v2(source)
41
+ flat_dests_list = validation.ensure_valid_flat_wells_list_for_transfer_v2(dest)
42
+ if not target_all_wells and nozzle_map.tip_count > 1:
43
+ flat_sources_list = tx_liquid_utils.group_wells_for_multi_channel_transfer(
44
+ flat_sources_list, nozzle_map
45
+ )
46
+ flat_dests_list = tx_liquid_utils.group_wells_for_multi_channel_transfer(
47
+ flat_dests_list, nozzle_map
48
+ )
49
+ for well in flat_sources_list + flat_dests_list:
50
+ instrument.validate_takes_liquid(
51
+ location=well.top(),
52
+ reject_module=True,
53
+ reject_adapter=True,
54
+ )
55
+
56
+ valid_new_tip = validation.ensure_new_tip_policy(tip_policy)
57
+ if valid_new_tip == TransferTipPolicyV2.NEVER:
58
+ if last_tip_picked_up_from is None:
59
+ raise RuntimeError(
60
+ "Pipette has no tip attached to perform transfer."
61
+ " Either do a pick_up_tip beforehand or specify a new_tip parameter"
62
+ " of 'once' or 'always'."
63
+ )
64
+ else:
65
+ valid_tip_racks = [last_tip_picked_up_from.parent]
66
+ else:
67
+ valid_tip_racks = tip_racks
68
+ if current_volume != 0:
69
+ raise RuntimeError(
70
+ "A transfer on a liquid class cannot start with liquid already in the tip."
71
+ " Ensure that all previously aspirated liquid is dispensed before starting"
72
+ " a new transfer."
73
+ )
74
+
75
+ _trash_location: Union[Location, Well, TrashBin, WasteChute]
76
+ if isinstance(trash_location, Labware):
77
+ _trash_location = trash_location.wells()[0]
78
+ else:
79
+ _trash_location = trash_location
80
+
81
+ valid_trash_location = validation.ensure_valid_trash_location_for_transfer_v2(
82
+ trash_location=_trash_location
83
+ )
84
+
85
+ return TransferInfo(
86
+ sources_list=flat_sources_list,
87
+ destinations_list=flat_dests_list,
88
+ tip_policy=valid_new_tip,
89
+ tip_racks=valid_tip_racks,
90
+ trash_location=valid_trash_location,
91
+ )
@@ -11,6 +11,7 @@ from .module import (
11
11
  AbstractHeaterShakerCore,
12
12
  AbstractMagneticBlockCore,
13
13
  AbstractAbsorbanceReaderCore,
14
+ AbstractFlexStackerCore,
14
15
  )
15
16
  from .protocol import AbstractProtocol
16
17
  from .well import AbstractWellCore
@@ -19,7 +20,7 @@ from .robot import AbstractRobot
19
20
 
20
21
  WellCore = AbstractWellCore
21
22
  LabwareCore = AbstractLabware[WellCore]
22
- InstrumentCore = AbstractInstrument[WellCore]
23
+ InstrumentCore = AbstractInstrument[WellCore, LabwareCore]
23
24
  ModuleCore = AbstractModuleCore
24
25
  TemperatureModuleCore = AbstractTemperatureModuleCore
25
26
  MagneticModuleCore = AbstractMagneticModuleCore
@@ -27,5 +28,6 @@ ThermocyclerCore = AbstractThermocyclerCore
27
28
  HeaterShakerCore = AbstractHeaterShakerCore
28
29
  MagneticBlockCore = AbstractMagneticBlockCore
29
30
  AbsorbanceReaderCore = AbstractAbsorbanceReaderCore
31
+ FlexStackerCore = AbstractFlexStackerCore
30
32
  RobotCore = AbstractRobot
31
33
  ProtocolCore = AbstractProtocol[InstrumentCore, LabwareCore, ModuleCore]
@@ -1,4 +1,5 @@
1
1
  """A Protocol-Engine-friendly wrapper for opentrons.motion_planning.deck_conflict."""
2
+
2
3
  from __future__ import annotations
3
4
  import itertools
4
5
  import logging
@@ -24,7 +25,9 @@ from opentrons.protocol_engine import (
24
25
  ModuleLocation,
25
26
  OnLabwareLocation,
26
27
  AddressableAreaLocation,
28
+ InStackerHopperLocation,
27
29
  OFF_DECK_LOCATION,
30
+ SYSTEM_LOCATION,
28
31
  )
29
32
  from opentrons.protocol_engine.errors.exceptions import LabwareNotLoadedOnModuleError
30
33
  from opentrons.types import DeckSlotName, StagingSlotName, Point
@@ -245,7 +248,11 @@ def _map_labware(
245
248
  # TODO(jbl 2023-06-08) check if we need to do any logic here or if this is correct
246
249
  return None
247
250
 
248
- elif location_from_engine == OFF_DECK_LOCATION:
251
+ elif (
252
+ location_from_engine == OFF_DECK_LOCATION
253
+ or location_from_engine == SYSTEM_LOCATION
254
+ or isinstance(location_from_engine, InStackerHopperLocation)
255
+ ):
249
256
  # This labware is off-deck. Exclude it from conflict checking.
250
257
  # todo(mm, 2023-02-23): Move this logic into wrapped_deck_conflict.
251
258
  return None
@@ -296,6 +303,9 @@ def _map_module(
296
303
  is_semi_configuration=False,
297
304
  ),
298
305
  )
306
+ elif module_type == ModuleType.FLEX_STACKER:
307
+ # TODO: This is a placeholder. We need to implement this.
308
+ return None
299
309
  else:
300
310
  return (
301
311
  mapped_location,