opentrons 8.3.1a1__py2.py3-none-any.whl → 8.4.0a0__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 (191) 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 +7 -2
  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 +245 -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 +2 -9
  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 +3 -1
  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/errors/__init__.py +8 -0
  119. opentrons/protocol_engine/errors/exceptions.py +50 -0
  120. opentrons/protocol_engine/execution/equipment.py +123 -106
  121. opentrons/protocol_engine/execution/labware_movement.py +8 -6
  122. opentrons/protocol_engine/execution/pipetting.py +233 -26
  123. opentrons/protocol_engine/execution/tip_handler.py +14 -5
  124. opentrons/protocol_engine/labware_offset_standardization.py +173 -0
  125. opentrons/protocol_engine/protocol_engine.py +22 -13
  126. opentrons/protocol_engine/resources/deck_configuration_provider.py +94 -2
  127. opentrons/protocol_engine/resources/deck_data_provider.py +1 -1
  128. opentrons/protocol_engine/resources/labware_data_provider.py +32 -12
  129. opentrons/protocol_engine/resources/labware_validation.py +7 -5
  130. opentrons/protocol_engine/slot_standardization.py +11 -23
  131. opentrons/protocol_engine/state/addressable_areas.py +84 -46
  132. opentrons/protocol_engine/state/frustum_helpers.py +26 -10
  133. opentrons/protocol_engine/state/geometry.py +683 -100
  134. opentrons/protocol_engine/state/labware.py +252 -55
  135. opentrons/protocol_engine/state/module_substates/__init__.py +4 -0
  136. opentrons/protocol_engine/state/module_substates/flex_stacker_substate.py +68 -0
  137. opentrons/protocol_engine/state/module_substates/heater_shaker_module_substate.py +22 -0
  138. opentrons/protocol_engine/state/module_substates/temperature_module_substate.py +13 -0
  139. opentrons/protocol_engine/state/module_substates/thermocycler_module_substate.py +20 -0
  140. opentrons/protocol_engine/state/modules.py +178 -52
  141. opentrons/protocol_engine/state/pipettes.py +54 -0
  142. opentrons/protocol_engine/state/state.py +1 -1
  143. opentrons/protocol_engine/state/tips.py +14 -0
  144. opentrons/protocol_engine/state/update_types.py +180 -25
  145. opentrons/protocol_engine/state/wells.py +54 -8
  146. opentrons/protocol_engine/types/__init__.py +292 -0
  147. opentrons/protocol_engine/types/automatic_tip_selection.py +39 -0
  148. opentrons/protocol_engine/types/command_annotations.py +53 -0
  149. opentrons/protocol_engine/types/deck_configuration.py +72 -0
  150. opentrons/protocol_engine/types/execution.py +96 -0
  151. opentrons/protocol_engine/types/hardware_passthrough.py +25 -0
  152. opentrons/protocol_engine/types/instrument.py +47 -0
  153. opentrons/protocol_engine/types/instrument_sensors.py +47 -0
  154. opentrons/protocol_engine/types/labware.py +110 -0
  155. opentrons/protocol_engine/types/labware_movement.py +22 -0
  156. opentrons/protocol_engine/types/labware_offset_location.py +108 -0
  157. opentrons/protocol_engine/types/labware_offset_vector.py +33 -0
  158. opentrons/protocol_engine/types/liquid.py +40 -0
  159. opentrons/protocol_engine/types/liquid_class.py +59 -0
  160. opentrons/protocol_engine/types/liquid_handling.py +13 -0
  161. opentrons/protocol_engine/types/liquid_level_detection.py +137 -0
  162. opentrons/protocol_engine/types/location.py +193 -0
  163. opentrons/protocol_engine/types/module.py +269 -0
  164. opentrons/protocol_engine/types/partial_tip_configuration.py +76 -0
  165. opentrons/protocol_engine/types/run_time_parameters.py +133 -0
  166. opentrons/protocol_engine/types/tip.py +18 -0
  167. opentrons/protocol_engine/types/util.py +21 -0
  168. opentrons/protocol_engine/types/well_position.py +107 -0
  169. opentrons/protocol_reader/extract_labware_definitions.py +7 -3
  170. opentrons/protocol_reader/file_format_validator.py +5 -3
  171. opentrons/protocol_runner/json_translator.py +4 -2
  172. opentrons/protocol_runner/legacy_command_mapper.py +6 -2
  173. opentrons/protocol_runner/run_orchestrator.py +4 -1
  174. opentrons/protocols/advanced_control/transfers/common.py +48 -1
  175. opentrons/protocols/advanced_control/transfers/transfer_liquid_utils.py +204 -0
  176. opentrons/protocols/api_support/definitions.py +1 -1
  177. opentrons/protocols/api_support/instrument.py +16 -3
  178. opentrons/protocols/labware.py +5 -6
  179. opentrons/protocols/models/__init__.py +0 -21
  180. opentrons/simulate.py +4 -2
  181. opentrons/types.py +15 -6
  182. {opentrons-8.3.1a1.dist-info → opentrons-8.4.0a0.dist-info}/METADATA +4 -4
  183. {opentrons-8.3.1a1.dist-info → opentrons-8.4.0a0.dist-info}/RECORD +187 -147
  184. opentrons/calibration_storage/ot2/models/defaults.py +0 -0
  185. opentrons/calibration_storage/ot3/models/defaults.py +0 -0
  186. opentrons/protocol_api/core/legacy/legacy_robot_core.py +0 -0
  187. opentrons/protocol_engine/types.py +0 -1311
  188. {opentrons-8.3.1a1.dist-info → opentrons-8.4.0a0.dist-info}/LICENSE +0 -0
  189. {opentrons-8.3.1a1.dist-info → opentrons-8.4.0a0.dist-info}/WHEEL +0 -0
  190. {opentrons-8.3.1a1.dist-info → opentrons-8.4.0a0.dist-info}/entry_points.txt +0 -0
  191. {opentrons-8.3.1a1.dist-info → opentrons-8.4.0a0.dist-info}/top_level.txt +0 -0
@@ -19,23 +19,34 @@ from typing import (
19
19
  List,
20
20
  Dict,
21
21
  Optional,
22
- Union,
23
22
  Tuple,
24
23
  cast,
25
24
  Sequence,
26
25
  Mapping,
26
+ Union,
27
+ Literal,
27
28
  )
28
29
 
29
- from opentrons_shared_data.labware.types import LabwareDefinition, LabwareParameters
30
+ from opentrons_shared_data.labware.types import (
31
+ LabwareDefinition,
32
+ LabwareDefinition2,
33
+ LabwareParameters2,
34
+ LabwareParameters3,
35
+ )
30
36
 
31
- from opentrons.types import Location, Point, NozzleMapInterface
37
+ from opentrons.types import (
38
+ Location,
39
+ Point,
40
+ NozzleMapInterface,
41
+ MeniscusTrackingTarget,
42
+ )
32
43
  from opentrons.protocols.api_support.types import APIVersion
33
44
  from opentrons.protocols.api_support.util import (
34
45
  requires_version,
35
46
  APIVersionError,
36
47
  UnsupportedAPIError,
37
48
  )
38
-
49
+ from opentrons.protocol_engine.types.liquid_level_detection import LiquidTrackingType
39
50
 
40
51
  # TODO(mc, 2022-09-02): re-exports provided for backwards compatibility
41
52
  # remove when their usage is no longer needed
@@ -49,7 +60,10 @@ from . import validation
49
60
  from ._liquid import Liquid
50
61
  from ._types import OffDeckType
51
62
  from .core import well_grid
52
- from .core.engine import ENGINE_CORE_API_VERSION, SET_OFFSET_RESTORED_API_VERSION
63
+ from .core.engine import (
64
+ ENGINE_CORE_API_VERSION,
65
+ SET_OFFSET_RESTORED_API_VERSION,
66
+ )
53
67
  from .core.labware import AbstractLabware
54
68
  from .core.module import AbstractModuleCore
55
69
  from .core.core_map import LoadedCoreMap
@@ -248,16 +262,21 @@ class Well:
248
262
  return Location(self._core.get_center(), self)
249
263
 
250
264
  @requires_version(2, 21)
251
- def meniscus(self, z: float = 0.0) -> Location:
265
+ def meniscus(
266
+ self, target: Literal["start", "end", "dynamic"], z: float = 0.0
267
+ ) -> Location:
252
268
  """
253
269
  :param z: An offset on the z-axis, in mm. Positive offsets are higher and
254
270
  negative offsets are lower.
271
+ :param target: The relative position inside the well to target when performing a liquid handling operation.
255
272
  :return: A :py:class:`~opentrons.types.Location` that indicates location is meniscus and that holds the ``z`` offset in its point.z field.
256
273
 
257
274
  :meta private:
258
275
  """
259
276
  return Location(
260
- point=Point(x=0, y=0, z=z), labware=self, _ot_internal_is_meniscus=True
277
+ point=Point(x=0, y=0, z=z),
278
+ labware=self,
279
+ _meniscus_tracking=MeniscusTrackingTarget(target),
261
280
  )
262
281
 
263
282
  @requires_version(2, 8)
@@ -316,6 +335,35 @@ class Well:
316
335
  volume=volume,
317
336
  )
318
337
 
338
+ @requires_version(2, 21)
339
+ def current_liquid_height(self) -> LiquidTrackingType:
340
+ """Get the current liquid height in a well."""
341
+ return self._core.current_liquid_height()
342
+
343
+ @requires_version(2, 21)
344
+ def current_liquid_volume(self) -> LiquidTrackingType:
345
+ """Get the current liquid volume in a well."""
346
+ return self._core.get_liquid_volume()
347
+
348
+ @requires_version(2, 21)
349
+ def estimate_liquid_height_after_pipetting(
350
+ self,
351
+ operation_volume: float,
352
+ ) -> LiquidTrackingType:
353
+ """Check the height of the liquid within a well.
354
+
355
+ :returns: The height, in mm, of the liquid from the deck.
356
+
357
+ :meta private:
358
+
359
+ This is intended for Opentrons internal use only and is not a guaranteed API.
360
+ """
361
+
362
+ projected_final_height = self._core.estimate_liquid_height_after_pipetting(
363
+ operation_volume=operation_volume,
364
+ )
365
+ return projected_final_height
366
+
319
367
  def _from_center_cartesian(self, x: float, y: float, z: float) -> Point:
320
368
  """
321
369
  Private version of from_center_cartesian. Present only for backward
@@ -509,7 +557,7 @@ class Labware:
509
557
 
510
558
  @property
511
559
  @requires_version(2, 0)
512
- def parameters(self) -> "LabwareParameters":
560
+ def parameters(self) -> "LabwareParameters2 | LabwareParameters3":
513
561
  """Internal properties of a labware including type and quirks."""
514
562
  return self._core.get_parameters()
515
563
 
@@ -658,6 +706,8 @@ class Labware:
658
706
  automatically.
659
707
 
660
708
  :return: The initialized and loaded labware object representing the Lid Stack.
709
+
710
+ :meta private:
661
711
  """
662
712
  if self._api_version < validation.LID_STACK_VERSION_GATE:
663
713
  raise APIVersionError(
@@ -712,11 +762,15 @@ class Labware:
712
762
 
713
763
  .. list-table::
714
764
  :header-rows: 1
765
+ :widths: 1 5
715
766
 
716
767
  * - API level
717
768
  - Offset behavior
718
769
  * - 2.12–2.13
719
770
  - Offsets only apply to the exact :py:class:`.Labware` instance.
771
+
772
+ If your protocol has multiple instances of the same type of labware,
773
+ you must either use ``set_offset()`` on all of them or none of them.
720
774
  * - 2.14–2.17
721
775
  - ``set_offset()`` is not available, and the API raises an error.
722
776
  * - 2.18 and newer
@@ -1417,7 +1471,7 @@ def next_available_tip(
1417
1471
  # TODO(mc, 2022-11-09): implementation detail, move somewhere else
1418
1472
  # only used in old calibration flows by robot-server
1419
1473
  def load_from_definition(
1420
- definition: "LabwareDefinition",
1474
+ definition: "LabwareDefinition2",
1421
1475
  parent: Location,
1422
1476
  label: Optional[str] = None,
1423
1477
  api_level: Optional[APIVersion] = None,
@@ -1461,8 +1515,8 @@ def load(
1461
1515
  label: Optional[str] = None,
1462
1516
  namespace: Optional[str] = None,
1463
1517
  version: int = 1,
1464
- bundled_defs: Optional[Dict[str, LabwareDefinition]] = None,
1465
- extra_defs: Optional[Dict[str, LabwareDefinition]] = None,
1518
+ bundled_defs: Optional[Mapping[str, LabwareDefinition2]] = None,
1519
+ extra_defs: Optional[Mapping[str, LabwareDefinition2]] = None,
1466
1520
  api_level: Optional[APIVersion] = None,
1467
1521
  ) -> Labware:
1468
1522
  """
@@ -1500,4 +1554,14 @@ def load(
1500
1554
  extra_defs=extra_defs,
1501
1555
  )
1502
1556
 
1557
+ # The legacy `load_from_definition()` function that we're calling only supports
1558
+ # schemaVersion==2 labware. Fortunately, when robot-server calls this function,
1559
+ # we only expect it to try to load schemaVersion==2 labware, so we never expect
1560
+ # this ValueError to be raised in practice.
1561
+ if definition["schemaVersion"] != 2:
1562
+ raise ValueError(
1563
+ f"{namespace}/{load_name}/{version} has schema {definition['schemaVersion']}."
1564
+ " Only schema 2 is supported."
1565
+ )
1566
+
1503
1567
  return load_from_definition(definition, parent, label, api_level)
@@ -29,6 +29,7 @@ from .core.common import (
29
29
  HeaterShakerCore,
30
30
  MagneticBlockCore,
31
31
  AbsorbanceReaderCore,
32
+ FlexStackerCore,
32
33
  )
33
34
  from .core.core_map import LoadedCoreMap
34
35
  from .core.engine import ENGINE_CORE_API_VERSION
@@ -1098,3 +1099,142 @@ class AbsorbanceReaderContext(ModuleContext):
1098
1099
  :returns: A dictionary of wavelengths to dictionary of values ordered by well name.
1099
1100
  """
1100
1101
  return self._core.read(filename=export_filename)
1102
+
1103
+
1104
+ class FlexStackerContext(ModuleContext):
1105
+ """An object representing a connected Flex Stacker module.
1106
+
1107
+ It should not be instantiated directly; instead, it should be
1108
+ created through :py:meth:`.ProtocolContext.load_module`.
1109
+
1110
+ .. versionadded:: 2.23
1111
+ """
1112
+
1113
+ _core: FlexStackerCore
1114
+
1115
+ @property
1116
+ @requires_version(2, 23)
1117
+ def serial_number(self) -> str:
1118
+ """Get the module's unique hardware serial number."""
1119
+ return self._core.get_serial_number()
1120
+
1121
+ @requires_version(2, 23)
1122
+ def retrieve(self) -> Labware:
1123
+ """Retrieve a labware from the Flex Stacker and place it on the shuttle.
1124
+
1125
+ :returns: The retrieved :py:class:`Labware` object. This will always be the main labware,
1126
+ even if the Flex Stacker contains labware on an adapter. To get the adapter object,
1127
+ call :py:class:`Labware.parent` on the returned labware.
1128
+
1129
+ """
1130
+ self._core.retrieve()
1131
+
1132
+ labware_core = self._protocol_core.get_labware_on_module(self._core)
1133
+ if labware_core is not None and labware_core.is_adapter():
1134
+ adapter_core = labware_core
1135
+ adapter = Labware(
1136
+ core=adapter_core,
1137
+ api_version=self._api_version,
1138
+ protocol_core=self._protocol_core,
1139
+ core_map=self._core_map,
1140
+ )
1141
+ self._core_map.add(adapter_core, adapter)
1142
+ labware_core = self._protocol_core.get_labware_on_labware(adapter_core)
1143
+
1144
+ # the core retrieve command should have already raised the error
1145
+ # if labware_core is None, this is just to satisfy the type checker
1146
+ assert labware_core is not None, "Retrieve failed to return labware"
1147
+ # check core map first
1148
+ try:
1149
+ labware = self._core_map.get(labware_core)
1150
+ except KeyError:
1151
+ # If the labware is not already in the core map,
1152
+ # create a new Labware object
1153
+ labware = Labware(
1154
+ core=labware_core,
1155
+ api_version=self._api_version,
1156
+ protocol_core=self._protocol_core,
1157
+ core_map=self._core_map,
1158
+ )
1159
+ self._core_map.add(labware_core, labware)
1160
+ return labware
1161
+
1162
+ @requires_version(2, 23)
1163
+ def store(self) -> None:
1164
+ """Move the labware currently on the Flex Stacker shuttle into the Flex Stacker."""
1165
+ self._core.store()
1166
+
1167
+ @requires_version(2, 23)
1168
+ def set_stored_labware(
1169
+ self,
1170
+ load_name: str,
1171
+ namespace: str | None = None,
1172
+ version: int | None = None,
1173
+ adapter: str | None = None,
1174
+ lid: str | None = None,
1175
+ count: int | None = None,
1176
+ ) -> None:
1177
+ """Configure what kind of labware the Flex Stacker will store.
1178
+
1179
+ :param str load_name: A string to use for looking up a labware definition.
1180
+ You can find the ``load_name`` for any Opentrons-verified labware on the
1181
+ `Labware Library <https://labware.opentrons.com>`__.
1182
+ :param str namespace: The namespace that the labware definition belongs to.
1183
+ If unspecified, the API will automatically search two namespaces:
1184
+
1185
+ - ``"opentrons"``, to load standard Opentrons labware definitions.
1186
+ - ``"custom_beta"``, to load custom labware definitions created with the
1187
+ `Custom Labware Creator <https://labware.opentrons.com/create>`__.
1188
+
1189
+ You might need to specify an explicit ``namespace`` if you have a custom
1190
+ definition whose ``load_name`` is the same as an Opentrons-verified
1191
+ definition, and you want to explicitly choose one or the other.
1192
+ :param version: The version of the labware definition. You should normally
1193
+ leave this unspecified to let ``load_labware()`` choose a version
1194
+ automatically.
1195
+ :param adapter: An adapter to load the labware on top of. Accepts the same
1196
+ values as the ``load_name`` parameter of :py:meth:`.load_adapter`. The
1197
+ adapter will use the same namespace as the labware, and the API will
1198
+ choose the adapter's version automatically.
1199
+ :param lid: A lid to load the on top of the main labware. Accepts the same
1200
+ values as the ``load_name`` parameter of :py:meth:`.load_lid_stack`. The
1201
+ lid will use the same namespace as the labware, and the API will
1202
+ choose the lid's version automatically.
1203
+ :param count: The number of labware that the Flex Stacker should start the protocol
1204
+ storing. If not specified, this will be the maximum amount of this kind of
1205
+ labware that the Flex Stacker is capable of storing.
1206
+
1207
+ """
1208
+ self._core.set_stored_labware(
1209
+ main_load_name=load_name,
1210
+ main_namespace=namespace,
1211
+ main_version=version,
1212
+ lid_load_name=lid,
1213
+ lid_namespace=namespace,
1214
+ lid_version=version,
1215
+ adapter_load_name=adapter,
1216
+ adapter_namespace=namespace,
1217
+ adapter_version=version,
1218
+ count=count,
1219
+ )
1220
+
1221
+ @requires_version(2, 23)
1222
+ def fill(self, message: str, count: int | None = None) -> None:
1223
+ """Pause the protocol to add more labware to the Flex Stacker.
1224
+
1225
+ :param message: A message to display in the Opentrons App to note what kind of labware to add.
1226
+ :param count: The amount of labware the Flex Stacker should hold after this command is executed.
1227
+ If not specified, the Flex Stacker should be full after this command is executed.
1228
+ """
1229
+ self._core.fill(message, count)
1230
+
1231
+ @requires_version(2, 23)
1232
+ def empty(self, message: str) -> None:
1233
+ """Pause the protocol to remove labware from the Flex Stacker.
1234
+
1235
+ :param message: A message to display in the Opentrons App to note what should be removed from
1236
+ the Flex Stacker.
1237
+ """
1238
+ self._core.empty(
1239
+ message,
1240
+ )
@@ -14,17 +14,19 @@ from typing import (
14
14
 
15
15
  from opentrons_shared_data.labware.types import LabwareDefinition
16
16
  from opentrons_shared_data.pipette.types import PipetteNameType
17
- from opentrons_shared_data.robot.types import RobotTypeEnum
18
17
 
19
18
  from opentrons.types import Mount, Location, DeckLocation, DeckSlotName, StagingSlotName
20
- from opentrons.config import feature_flags
21
19
  from opentrons.legacy_broker import LegacyBroker
22
20
  from opentrons.hardware_control.modules.types import (
23
21
  MagneticBlockModel,
24
22
  AbsorbanceReaderModel,
23
+ FlexStackerModuleModel,
25
24
  )
26
25
  from opentrons.legacy_commands import protocol_commands as cmds, types as cmd_types
27
- from opentrons.legacy_commands.helpers import stringify_labware_movement_command
26
+ from opentrons.legacy_commands.helpers import (
27
+ stringify_labware_movement_command,
28
+ stringify_lid_movement_command,
29
+ )
28
30
  from opentrons.legacy_commands.publisher import (
29
31
  CommandPublisher,
30
32
  publish,
@@ -58,6 +60,7 @@ from .core.module import (
58
60
  AbstractHeaterShakerCore,
59
61
  AbstractMagneticBlockCore,
60
62
  AbstractAbsorbanceReaderCore,
63
+ AbstractFlexStackerCore,
61
64
  )
62
65
  from .robot_context import RobotContext, HardwareManager
63
66
  from .core.engine import ENGINE_CORE_API_VERSION
@@ -76,6 +79,7 @@ from .module_contexts import (
76
79
  HeaterShakerContext,
77
80
  MagneticBlockContext,
78
81
  AbsorbanceReaderContext,
82
+ FlexStackerContext,
79
83
  ModuleContext,
80
84
  )
81
85
  from ._parameters import Parameters
@@ -91,6 +95,7 @@ ModuleTypes = Union[
91
95
  HeaterShakerContext,
92
96
  MagneticBlockContext,
93
97
  AbsorbanceReaderContext,
98
+ FlexStackerContext,
94
99
  ]
95
100
 
96
101
 
@@ -444,13 +449,16 @@ class ProtocolContext(CommandPublisher):
444
449
  values as the ``load_name`` parameter of :py:meth:`.load_adapter`. The
445
450
  adapter will use the same namespace as the labware, and the API will
446
451
  choose the adapter's version automatically.
447
- :param lid: A lid to load the on top of the main labware. Accepts the same
448
- values as the ``load_name`` parameter of :py:meth:`.load_lid_stack`. The
449
- lid will use the same namespace as the labware, and the API will
450
- choose the lid's version automatically.
451
452
 
452
453
  .. versionadded:: 2.15
453
454
  """
455
+ # TODO: re-include in docstring when 2.23 is ready
456
+ # :param lid: A lid to load the on top of the main labware. Accepts the same
457
+ # values as the ``load_name`` parameter of :py:meth:`.load_lid_stack`. The
458
+ # lid will use the same namespace as the labware, and the API will
459
+ # choose the lid's version automatically.
460
+ #
461
+ # .. versionadded:: 2.23
454
462
  if isinstance(location, OffDeckType) and self._api_version < APIVersion(2, 15):
455
463
  raise APIVersionError(
456
464
  api_element="Loading a labware off-deck",
@@ -859,6 +867,10 @@ class ProtocolContext(CommandPublisher):
859
867
 
860
868
  .. versionchanged:: 2.15
861
869
  Added ``MagneticBlockContext`` return value.
870
+
871
+ .. TODO uncomment when 2.23 is ready
872
+ versionchanged:: 2.23
873
+ Added ``FlexStackerModuleContext`` return value.
862
874
  """
863
875
  if configuration:
864
876
  if self._api_version < APIVersion(2, 4):
@@ -887,7 +899,18 @@ class ProtocolContext(CommandPublisher):
887
899
  requested_model, AbsorbanceReaderModel
888
900
  ) and self._api_version < APIVersion(2, 21):
889
901
  raise APIVersionError(
890
- f"Module of type {module_name} is only available in versions 2.21 and above."
902
+ api_element=f"Module of type {module_name}",
903
+ until_version="2.21",
904
+ current_version=f"{self._api_version}",
905
+ )
906
+ if (
907
+ isinstance(requested_model, FlexStackerModuleModel)
908
+ and self._api_version < validation.FLEX_STACKER_VERSION_GATE
909
+ ):
910
+ raise APIVersionError(
911
+ api_element=f"Module of type {module_name}",
912
+ until_version=str(validation.FLEX_STACKER_VERSION_GATE),
913
+ current_version=f"{self._api_version}",
891
914
  )
892
915
 
893
916
  deck_slot = (
@@ -898,7 +921,11 @@ class ProtocolContext(CommandPublisher):
898
921
  )
899
922
  )
900
923
  if isinstance(deck_slot, StagingSlotName):
901
- raise ValueError("Cannot load a module onto a staging slot.")
924
+ # flex stacker modules can only be loaded into staging slot inside a protocol
925
+ if isinstance(requested_model, FlexStackerModuleModel):
926
+ deck_slot = validation.convert_flex_stacker_load_slot(deck_slot)
927
+ else:
928
+ raise ValueError(f"Cannot load {module_name} onto a staging slot.")
902
929
 
903
930
  module_core = self._core.load_module(
904
931
  model=requested_model,
@@ -1329,17 +1356,17 @@ class ProtocolContext(CommandPublisher):
1329
1356
  display_color=display_color,
1330
1357
  )
1331
1358
 
1359
+ @requires_version(2, 23)
1332
1360
  def define_liquid_class(
1333
1361
  self,
1334
1362
  name: str,
1335
1363
  ) -> LiquidClass:
1336
- """Define a liquid class for use in the protocol."""
1337
- if feature_flags.allow_liquid_classes(
1338
- robot_type=RobotTypeEnum.robot_literal_to_enum(self._core.robot_type)
1339
- ):
1340
- return self._core.define_liquid_class(name=name)
1341
- else:
1342
- raise NotImplementedError("This method is not implemented.")
1364
+ """
1365
+ Define a liquid class for use in the protocol.
1366
+
1367
+ :meta private:
1368
+ """
1369
+ return self._core.define_liquid_class(name=name)
1343
1370
 
1344
1371
  @property
1345
1372
  @requires_version(2, 5)
@@ -1392,6 +1419,8 @@ class ProtocolContext(CommandPublisher):
1392
1419
  automatically.
1393
1420
 
1394
1421
  :return: The initialized and loaded labware object representing the Lid Stack.
1422
+
1423
+ :meta private:
1395
1424
  """
1396
1425
  if self._api_version < validation.LID_STACK_VERSION_GATE:
1397
1426
  raise APIVersionError(
@@ -1441,6 +1470,115 @@ class ProtocolContext(CommandPublisher):
1441
1470
  )
1442
1471
  return labware
1443
1472
 
1473
+ @requires_version(2, 23)
1474
+ def move_lid(
1475
+ self,
1476
+ source_location: Union[DeckLocation, Labware],
1477
+ new_location: Union[DeckLocation, Labware, OffDeckType, WasteChute, TrashBin],
1478
+ use_gripper: bool = False,
1479
+ pick_up_offset: Optional[Mapping[str, float]] = None,
1480
+ drop_offset: Optional[Mapping[str, float]] = None,
1481
+ ) -> Labware | None:
1482
+ """Move a lid from a valid source to a new location. Can return a Lid Stack if one is created.
1483
+
1484
+ :param source_location: Where to take the lid from. This is either:
1485
+
1486
+ * A deck slot like ``1``, ``"1"``, or ``"D1"``. See :ref:`deck-slots`.
1487
+ * A labware or adapter that's already been loaded on the deck
1488
+ with :py:meth:`load_labware` or :py:meth:`load_adapter`.
1489
+ * A lid stack that's already been loaded on the deck with
1490
+ with :py:meth:`load_lid_stack`.
1491
+
1492
+ :param new_location: Where to move the lid to. This is either:
1493
+
1494
+ * A deck slot like ``1``, ``"1"``, or ``"D1"``. See :ref:`deck-slots`.
1495
+ * A hardware module that's already been loaded on the deck
1496
+ with :py:meth:`load_module`.
1497
+ * A labware or adapter that's already been loaded on the deck
1498
+ with :py:meth:`load_labware` or :py:meth:`load_adapter`.
1499
+ * The special constant :py:obj:`OFF_DECK`.
1500
+
1501
+ :param use_gripper: Whether to use the Flex Gripper for this movement.
1502
+
1503
+ * If ``True``, use the gripper to perform an automatic
1504
+ movement. This will raise an error in an OT-2 protocol.
1505
+ * If ``False``, pause protocol execution until the user
1506
+ performs the movement. Protocol execution remains paused until
1507
+ the user presses **Confirm and resume**.
1508
+
1509
+ Gripper-only parameters:
1510
+
1511
+ :param pick_up_offset: Optional x, y, z vector offset to use when picking up a lid.
1512
+ :param drop_offset: Optional x, y, z vector offset to use when dropping off a lid.
1513
+
1514
+ Before moving a lid to or from a labware in a hardware module, make sure that the
1515
+ labware's current and new locations are accessible, i.e., open the Thermocycler lid
1516
+ or open the Heater-Shaker's labware latch.
1517
+
1518
+ :meta private:
1519
+ """
1520
+ source: Union[LabwareCore, DeckSlotName, StagingSlotName]
1521
+ if isinstance(source_location, Labware):
1522
+ source = source_location._core
1523
+ else:
1524
+ source = validation.ensure_and_convert_deck_slot(
1525
+ source_location, self._api_version, self._core.robot_type
1526
+ )
1527
+
1528
+ destination: Union[
1529
+ ModuleCore,
1530
+ LabwareCore,
1531
+ WasteChute,
1532
+ OffDeckType,
1533
+ DeckSlotName,
1534
+ StagingSlotName,
1535
+ TrashBin,
1536
+ ]
1537
+ if isinstance(new_location, Labware):
1538
+ destination = new_location._core
1539
+ elif isinstance(new_location, (OffDeckType, WasteChute, TrashBin)):
1540
+ destination = new_location
1541
+ else:
1542
+ destination = validation.ensure_and_convert_deck_slot(
1543
+ new_location, self._api_version, self._core.robot_type
1544
+ )
1545
+
1546
+ _pick_up_offset = (
1547
+ validation.ensure_valid_labware_offset_vector(pick_up_offset)
1548
+ if pick_up_offset
1549
+ else None
1550
+ )
1551
+ _drop_offset = (
1552
+ validation.ensure_valid_labware_offset_vector(drop_offset)
1553
+ if drop_offset
1554
+ else None
1555
+ )
1556
+ with publish_context(
1557
+ broker=self.broker,
1558
+ command=cmds.move_labware(
1559
+ # This needs to be called from protocol context and not the command for import loop reasons
1560
+ text=stringify_lid_movement_command(
1561
+ source_location, new_location, use_gripper
1562
+ )
1563
+ ),
1564
+ ):
1565
+ result = self._core.move_lid(
1566
+ source_location=source,
1567
+ new_location=destination,
1568
+ use_gripper=use_gripper,
1569
+ pause_for_manual_move=True,
1570
+ pick_up_offset=_pick_up_offset,
1571
+ drop_offset=_drop_offset,
1572
+ )
1573
+ if result is not None:
1574
+ return Labware(
1575
+ core=result,
1576
+ api_version=self._api_version,
1577
+ protocol_core=self._core,
1578
+ core_map=self._core_map,
1579
+ )
1580
+ return None
1581
+
1444
1582
 
1445
1583
  def _create_module_context(
1446
1584
  module_core: Union[ModuleCore, NonConnectedModuleCore],
@@ -1462,6 +1600,8 @@ def _create_module_context(
1462
1600
  module_cls = MagneticBlockContext
1463
1601
  elif isinstance(module_core, AbstractAbsorbanceReaderCore):
1464
1602
  module_cls = AbsorbanceReaderContext
1603
+ elif isinstance(module_core, AbstractFlexStackerCore):
1604
+ module_cls = FlexStackerContext
1465
1605
  else:
1466
1606
  assert False, "Unsupported module type"
1467
1607