opentrons 8.3.0a0__py2.py3-none-any.whl → 8.3.0a2__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 (229) hide show
  1. opentrons/calibration_storage/deck_configuration.py +3 -3
  2. opentrons/calibration_storage/file_operators.py +3 -3
  3. opentrons/calibration_storage/helpers.py +3 -1
  4. opentrons/calibration_storage/ot2/models/v1.py +16 -29
  5. opentrons/calibration_storage/ot2/tip_length.py +7 -4
  6. opentrons/calibration_storage/ot3/models/v1.py +14 -23
  7. opentrons/cli/analyze.py +18 -6
  8. opentrons/drivers/asyncio/communication/__init__.py +2 -0
  9. opentrons/drivers/asyncio/communication/errors.py +16 -3
  10. opentrons/drivers/asyncio/communication/serial_connection.py +24 -9
  11. opentrons/drivers/command_builder.py +2 -2
  12. opentrons/drivers/flex_stacker/__init__.py +9 -0
  13. opentrons/drivers/flex_stacker/abstract.py +89 -0
  14. opentrons/drivers/flex_stacker/driver.py +260 -0
  15. opentrons/drivers/flex_stacker/simulator.py +109 -0
  16. opentrons/drivers/flex_stacker/types.py +138 -0
  17. opentrons/drivers/heater_shaker/driver.py +18 -3
  18. opentrons/drivers/temp_deck/driver.py +13 -3
  19. opentrons/drivers/thermocycler/driver.py +17 -3
  20. opentrons/execute.py +3 -1
  21. opentrons/hardware_control/__init__.py +1 -2
  22. opentrons/hardware_control/api.py +28 -20
  23. opentrons/hardware_control/backends/flex_protocol.py +4 -6
  24. opentrons/hardware_control/backends/ot3controller.py +177 -59
  25. opentrons/hardware_control/backends/ot3simulator.py +10 -8
  26. opentrons/hardware_control/backends/ot3utils.py +3 -13
  27. opentrons/hardware_control/dev_types.py +2 -0
  28. opentrons/hardware_control/emulation/heater_shaker.py +4 -0
  29. opentrons/hardware_control/emulation/module_server/client.py +1 -1
  30. opentrons/hardware_control/emulation/module_server/server.py +5 -3
  31. opentrons/hardware_control/emulation/settings.py +3 -4
  32. opentrons/hardware_control/instruments/ot2/instrument_calibration.py +2 -1
  33. opentrons/hardware_control/instruments/ot2/pipette.py +9 -21
  34. opentrons/hardware_control/instruments/ot2/pipette_handler.py +8 -1
  35. opentrons/hardware_control/instruments/ot3/gripper.py +2 -2
  36. opentrons/hardware_control/instruments/ot3/pipette.py +13 -22
  37. opentrons/hardware_control/instruments/ot3/pipette_handler.py +10 -1
  38. opentrons/hardware_control/modules/mod_abc.py +2 -2
  39. opentrons/hardware_control/motion_utilities.py +68 -0
  40. opentrons/hardware_control/nozzle_manager.py +39 -41
  41. opentrons/hardware_control/ot3_calibration.py +1 -1
  42. opentrons/hardware_control/ot3api.py +34 -22
  43. opentrons/hardware_control/protocols/gripper_controller.py +3 -0
  44. opentrons/hardware_control/protocols/hardware_manager.py +5 -1
  45. opentrons/hardware_control/protocols/liquid_handler.py +18 -0
  46. opentrons/hardware_control/protocols/motion_controller.py +6 -0
  47. opentrons/hardware_control/robot_calibration.py +1 -1
  48. opentrons/hardware_control/types.py +61 -0
  49. opentrons/protocol_api/__init__.py +20 -1
  50. opentrons/protocol_api/_liquid.py +24 -49
  51. opentrons/protocol_api/_liquid_properties.py +754 -0
  52. opentrons/protocol_api/_types.py +24 -0
  53. opentrons/protocol_api/core/common.py +2 -0
  54. opentrons/protocol_api/core/engine/instrument.py +67 -10
  55. opentrons/protocol_api/core/engine/labware.py +29 -7
  56. opentrons/protocol_api/core/engine/protocol.py +130 -5
  57. opentrons/protocol_api/core/engine/robot.py +139 -0
  58. opentrons/protocol_api/core/engine/well.py +4 -1
  59. opentrons/protocol_api/core/instrument.py +42 -4
  60. opentrons/protocol_api/core/labware.py +13 -4
  61. opentrons/protocol_api/core/legacy/legacy_instrument_core.py +34 -3
  62. opentrons/protocol_api/core/legacy/legacy_labware_core.py +13 -4
  63. opentrons/protocol_api/core/legacy/legacy_protocol_core.py +32 -1
  64. opentrons/protocol_api/core/legacy/legacy_robot_core.py +0 -0
  65. opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +34 -3
  66. opentrons/protocol_api/core/protocol.py +34 -1
  67. opentrons/protocol_api/core/robot.py +51 -0
  68. opentrons/protocol_api/instrument_context.py +145 -43
  69. opentrons/protocol_api/labware.py +231 -7
  70. opentrons/protocol_api/module_contexts.py +21 -17
  71. opentrons/protocol_api/protocol_context.py +125 -4
  72. opentrons/protocol_api/robot_context.py +204 -32
  73. opentrons/protocol_api/validation.py +261 -3
  74. opentrons/protocol_engine/__init__.py +4 -0
  75. opentrons/protocol_engine/actions/actions.py +2 -3
  76. opentrons/protocol_engine/clients/sync_client.py +18 -0
  77. opentrons/protocol_engine/commands/__init__.py +81 -0
  78. opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +0 -2
  79. opentrons/protocol_engine/commands/absorbance_reader/initialize.py +19 -5
  80. opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +0 -1
  81. opentrons/protocol_engine/commands/absorbance_reader/read.py +32 -9
  82. opentrons/protocol_engine/commands/air_gap_in_place.py +160 -0
  83. opentrons/protocol_engine/commands/aspirate.py +103 -53
  84. opentrons/protocol_engine/commands/aspirate_in_place.py +55 -51
  85. opentrons/protocol_engine/commands/blow_out.py +44 -39
  86. opentrons/protocol_engine/commands/blow_out_in_place.py +21 -32
  87. opentrons/protocol_engine/commands/calibration/calibrate_gripper.py +13 -6
  88. opentrons/protocol_engine/commands/calibration/calibrate_module.py +1 -1
  89. opentrons/protocol_engine/commands/calibration/calibrate_pipette.py +3 -3
  90. opentrons/protocol_engine/commands/calibration/move_to_maintenance_position.py +1 -1
  91. opentrons/protocol_engine/commands/command.py +73 -66
  92. opentrons/protocol_engine/commands/command_unions.py +101 -1
  93. opentrons/protocol_engine/commands/comment.py +1 -1
  94. opentrons/protocol_engine/commands/configure_for_volume.py +10 -3
  95. opentrons/protocol_engine/commands/configure_nozzle_layout.py +6 -4
  96. opentrons/protocol_engine/commands/custom.py +6 -12
  97. opentrons/protocol_engine/commands/dispense.py +82 -48
  98. opentrons/protocol_engine/commands/dispense_in_place.py +71 -51
  99. opentrons/protocol_engine/commands/drop_tip.py +52 -31
  100. opentrons/protocol_engine/commands/drop_tip_in_place.py +13 -3
  101. opentrons/protocol_engine/commands/generate_command_schema.py +4 -11
  102. opentrons/protocol_engine/commands/get_next_tip.py +134 -0
  103. opentrons/protocol_engine/commands/get_tip_presence.py +1 -1
  104. opentrons/protocol_engine/commands/heater_shaker/close_labware_latch.py +1 -1
  105. opentrons/protocol_engine/commands/heater_shaker/deactivate_heater.py +1 -1
  106. opentrons/protocol_engine/commands/heater_shaker/deactivate_shaker.py +1 -1
  107. opentrons/protocol_engine/commands/heater_shaker/open_labware_latch.py +1 -1
  108. opentrons/protocol_engine/commands/heater_shaker/set_and_wait_for_shake_speed.py +1 -1
  109. opentrons/protocol_engine/commands/heater_shaker/set_target_temperature.py +1 -1
  110. opentrons/protocol_engine/commands/heater_shaker/wait_for_temperature.py +10 -4
  111. opentrons/protocol_engine/commands/home.py +13 -4
  112. opentrons/protocol_engine/commands/liquid_probe.py +60 -25
  113. opentrons/protocol_engine/commands/load_labware.py +29 -7
  114. opentrons/protocol_engine/commands/load_lid.py +146 -0
  115. opentrons/protocol_engine/commands/load_lid_stack.py +189 -0
  116. opentrons/protocol_engine/commands/load_liquid.py +12 -4
  117. opentrons/protocol_engine/commands/load_liquid_class.py +144 -0
  118. opentrons/protocol_engine/commands/load_module.py +31 -10
  119. opentrons/protocol_engine/commands/load_pipette.py +19 -8
  120. opentrons/protocol_engine/commands/magnetic_module/disengage.py +1 -1
  121. opentrons/protocol_engine/commands/magnetic_module/engage.py +1 -1
  122. opentrons/protocol_engine/commands/move_labware.py +19 -6
  123. opentrons/protocol_engine/commands/move_relative.py +35 -25
  124. opentrons/protocol_engine/commands/move_to_addressable_area.py +40 -27
  125. opentrons/protocol_engine/commands/move_to_addressable_area_for_drop_tip.py +53 -32
  126. opentrons/protocol_engine/commands/move_to_coordinates.py +36 -22
  127. opentrons/protocol_engine/commands/move_to_well.py +40 -24
  128. opentrons/protocol_engine/commands/movement_common.py +338 -0
  129. opentrons/protocol_engine/commands/pick_up_tip.py +49 -27
  130. opentrons/protocol_engine/commands/pipetting_common.py +169 -87
  131. opentrons/protocol_engine/commands/prepare_to_aspirate.py +24 -33
  132. opentrons/protocol_engine/commands/reload_labware.py +1 -1
  133. opentrons/protocol_engine/commands/retract_axis.py +1 -1
  134. opentrons/protocol_engine/commands/robot/__init__.py +69 -0
  135. opentrons/protocol_engine/commands/robot/close_gripper_jaw.py +86 -0
  136. opentrons/protocol_engine/commands/robot/common.py +18 -0
  137. opentrons/protocol_engine/commands/robot/move_axes_relative.py +101 -0
  138. opentrons/protocol_engine/commands/robot/move_axes_to.py +100 -0
  139. opentrons/protocol_engine/commands/robot/move_to.py +94 -0
  140. opentrons/protocol_engine/commands/robot/open_gripper_jaw.py +77 -0
  141. opentrons/protocol_engine/commands/save_position.py +14 -5
  142. opentrons/protocol_engine/commands/set_rail_lights.py +1 -1
  143. opentrons/protocol_engine/commands/set_status_bar.py +1 -1
  144. opentrons/protocol_engine/commands/temperature_module/deactivate.py +1 -1
  145. opentrons/protocol_engine/commands/temperature_module/set_target_temperature.py +1 -1
  146. opentrons/protocol_engine/commands/temperature_module/wait_for_temperature.py +10 -4
  147. opentrons/protocol_engine/commands/thermocycler/close_lid.py +1 -1
  148. opentrons/protocol_engine/commands/thermocycler/deactivate_block.py +1 -1
  149. opentrons/protocol_engine/commands/thermocycler/deactivate_lid.py +1 -1
  150. opentrons/protocol_engine/commands/thermocycler/open_lid.py +1 -1
  151. opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py +8 -2
  152. opentrons/protocol_engine/commands/thermocycler/run_profile.py +9 -3
  153. opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py +11 -4
  154. opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py +1 -1
  155. opentrons/protocol_engine/commands/thermocycler/wait_for_block_temperature.py +1 -1
  156. opentrons/protocol_engine/commands/thermocycler/wait_for_lid_temperature.py +1 -1
  157. opentrons/protocol_engine/commands/touch_tip.py +65 -16
  158. opentrons/protocol_engine/commands/unsafe/unsafe_blow_out_in_place.py +4 -1
  159. opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +12 -3
  160. opentrons/protocol_engine/commands/unsafe/unsafe_engage_axes.py +1 -4
  161. opentrons/protocol_engine/commands/unsafe/update_position_estimators.py +1 -4
  162. opentrons/protocol_engine/commands/verify_tip_presence.py +11 -4
  163. opentrons/protocol_engine/commands/wait_for_duration.py +10 -3
  164. opentrons/protocol_engine/commands/wait_for_resume.py +10 -3
  165. opentrons/protocol_engine/errors/__init__.py +8 -0
  166. opentrons/protocol_engine/errors/error_occurrence.py +19 -20
  167. opentrons/protocol_engine/errors/exceptions.py +50 -0
  168. opentrons/protocol_engine/execution/command_executor.py +1 -1
  169. opentrons/protocol_engine/execution/equipment.py +73 -5
  170. opentrons/protocol_engine/execution/gantry_mover.py +364 -8
  171. opentrons/protocol_engine/execution/movement.py +27 -0
  172. opentrons/protocol_engine/execution/pipetting.py +5 -1
  173. opentrons/protocol_engine/execution/tip_handler.py +4 -6
  174. opentrons/protocol_engine/notes/notes.py +1 -1
  175. opentrons/protocol_engine/protocol_engine.py +7 -6
  176. opentrons/protocol_engine/resources/labware_data_provider.py +1 -1
  177. opentrons/protocol_engine/resources/labware_validation.py +5 -0
  178. opentrons/protocol_engine/resources/module_data_provider.py +1 -1
  179. opentrons/protocol_engine/resources/pipette_data_provider.py +12 -0
  180. opentrons/protocol_engine/slot_standardization.py +9 -9
  181. opentrons/protocol_engine/state/_move_types.py +9 -5
  182. opentrons/protocol_engine/state/_well_math.py +193 -0
  183. opentrons/protocol_engine/state/addressable_areas.py +25 -61
  184. opentrons/protocol_engine/state/command_history.py +12 -0
  185. opentrons/protocol_engine/state/commands.py +17 -13
  186. opentrons/protocol_engine/state/files.py +10 -12
  187. opentrons/protocol_engine/state/fluid_stack.py +138 -0
  188. opentrons/protocol_engine/state/frustum_helpers.py +57 -32
  189. opentrons/protocol_engine/state/geometry.py +47 -1
  190. opentrons/protocol_engine/state/labware.py +79 -25
  191. opentrons/protocol_engine/state/liquid_classes.py +82 -0
  192. opentrons/protocol_engine/state/liquids.py +16 -4
  193. opentrons/protocol_engine/state/modules.py +52 -70
  194. opentrons/protocol_engine/state/motion.py +6 -1
  195. opentrons/protocol_engine/state/pipettes.py +135 -58
  196. opentrons/protocol_engine/state/state.py +21 -2
  197. opentrons/protocol_engine/state/state_summary.py +4 -2
  198. opentrons/protocol_engine/state/tips.py +11 -44
  199. opentrons/protocol_engine/state/update_types.py +343 -48
  200. opentrons/protocol_engine/state/wells.py +19 -11
  201. opentrons/protocol_engine/types.py +176 -28
  202. opentrons/protocol_reader/extract_labware_definitions.py +5 -2
  203. opentrons/protocol_reader/file_format_validator.py +5 -5
  204. opentrons/protocol_runner/json_file_reader.py +9 -3
  205. opentrons/protocol_runner/json_translator.py +51 -25
  206. opentrons/protocol_runner/legacy_command_mapper.py +66 -64
  207. opentrons/protocol_runner/protocol_runner.py +35 -4
  208. opentrons/protocol_runner/python_protocol_wrappers.py +1 -1
  209. opentrons/protocol_runner/run_orchestrator.py +13 -3
  210. opentrons/protocols/advanced_control/common.py +38 -0
  211. opentrons/protocols/advanced_control/mix.py +1 -1
  212. opentrons/protocols/advanced_control/transfers/__init__.py +0 -0
  213. opentrons/protocols/advanced_control/transfers/common.py +56 -0
  214. opentrons/protocols/advanced_control/{transfers.py → transfers/transfer.py} +10 -85
  215. opentrons/protocols/api_support/definitions.py +1 -1
  216. opentrons/protocols/api_support/instrument.py +1 -1
  217. opentrons/protocols/api_support/util.py +10 -0
  218. opentrons/protocols/labware.py +70 -8
  219. opentrons/protocols/models/json_protocol.py +5 -9
  220. opentrons/simulate.py +3 -1
  221. opentrons/types.py +162 -2
  222. opentrons/util/entrypoint_util.py +2 -5
  223. opentrons/util/logging_config.py +1 -1
  224. {opentrons-8.3.0a0.dist-info → opentrons-8.3.0a2.dist-info}/METADATA +16 -15
  225. {opentrons-8.3.0a0.dist-info → opentrons-8.3.0a2.dist-info}/RECORD +229 -202
  226. {opentrons-8.3.0a0.dist-info → opentrons-8.3.0a2.dist-info}/WHEEL +1 -1
  227. {opentrons-8.3.0a0.dist-info → opentrons-8.3.0a2.dist-info}/LICENSE +0 -0
  228. {opentrons-8.3.0a0.dist-info → opentrons-8.3.0a2.dist-info}/entry_points.txt +0 -0
  229. {opentrons-8.3.0a0.dist-info → opentrons-8.3.0a2.dist-info}/top_level.txt +0 -0
@@ -125,6 +125,7 @@ class ModuleContext(CommandPublisher):
125
125
  namespace: Optional[str] = None,
126
126
  version: Optional[int] = None,
127
127
  adapter: Optional[str] = None,
128
+ lid: Optional[str] = None,
128
129
  ) -> Labware:
129
130
  """Load a labware onto the module using its load parameters.
130
131
 
@@ -180,6 +181,19 @@ class ModuleContext(CommandPublisher):
180
181
  version=version,
181
182
  location=load_location,
182
183
  )
184
+ if lid is not None:
185
+ if self._api_version < validation.LID_STACK_VERSION_GATE:
186
+ raise APIVersionError(
187
+ api_element="Loading a lid on a Labware",
188
+ until_version="2.23",
189
+ current_version=f"{self._api_version}",
190
+ )
191
+ self._protocol_core.load_lid(
192
+ load_name=lid,
193
+ location=labware_core,
194
+ namespace=namespace,
195
+ version=version,
196
+ )
183
197
 
184
198
  if isinstance(self._core, LegacyModuleCore):
185
199
  labware = self._core.add_labware_core(cast(LegacyLabwareCore, labware_core))
@@ -608,7 +622,7 @@ class ThermocyclerContext(ModuleContext):
608
622
  .. note::
609
623
 
610
624
  The Thermocycler will proceed to the next command immediately after
611
- ``temperature`` has been reached.
625
+ ``temperature`` is reached.
612
626
 
613
627
  """
614
628
  self._core.set_target_lid_temperature(celsius=temperature)
@@ -625,28 +639,18 @@ class ThermocyclerContext(ModuleContext):
625
639
  """Execute a Thermocycler profile, defined as a cycle of
626
640
  ``steps``, for a given number of ``repetitions``.
627
641
 
628
- :param steps: List of unique steps that make up a single cycle.
629
- Each list item should be a dictionary that maps to
630
- the parameters of the :py:meth:`set_block_temperature`
631
- method with a ``temperature`` key, and either or both of
642
+ :param steps: List of steps that make up a single cycle.
643
+ Each list item should be a dictionary that maps to the parameters
644
+ of the :py:meth:`set_block_temperature` method. The dictionary's
645
+ keys must be ``temperature`` and one or both of
632
646
  ``hold_time_seconds`` and ``hold_time_minutes``.
633
647
  :param repetitions: The number of times to repeat the cycled steps.
634
648
  :param block_max_volume: The greatest volume of liquid contained in any
635
649
  individual well of the loaded labware, in µL.
636
650
  If not specified, the default is 25 µL.
637
651
 
638
- .. note::
639
-
640
- Unlike with :py:meth:`set_block_temperature`, either or both of
641
- ``hold_time_minutes`` and ``hold_time_seconds`` must be defined
642
- and for each step.
643
-
644
- .. note::
645
-
646
- Before API Version 2.21, Thermocycler profiles run with this command
647
- would be listed in the app as having a number of repetitions equal to
648
- their step count. At or above API Version 2.21, the structure of the
649
- Thermocycler cycles is preserved.
652
+ .. versionchanged:: 2.21
653
+ Fixed run log listing number of steps instead of number of repetitions.
650
654
 
651
655
  """
652
656
  repetitions = validation.ensure_thermocycler_repetition_count(repetitions)
@@ -186,7 +186,14 @@ class ProtocolContext(CommandPublisher):
186
186
  self._commands: List[str] = []
187
187
  self._params: Parameters = Parameters()
188
188
  self._unsubscribe_commands: Optional[Callable[[], None]] = None
189
- self._robot = RobotContext(self._core)
189
+ try:
190
+ self._robot: Optional[RobotContext] = RobotContext(
191
+ core=self._core.load_robot(),
192
+ protocol_core=self._core,
193
+ api_version=self._api_version,
194
+ )
195
+ except APIVersionError:
196
+ self._robot = None
190
197
  self.clear_commands()
191
198
 
192
199
  @property
@@ -212,12 +219,14 @@ class ProtocolContext(CommandPublisher):
212
219
  return self._api_version
213
220
 
214
221
  @property
215
- @requires_version(2, 21)
222
+ @requires_version(2, 22)
216
223
  def robot(self) -> RobotContext:
217
224
  """The :py:class:`.RobotContext` for the protocol.
218
225
 
219
226
  :meta private:
220
227
  """
228
+ if self._core.robot_type != "OT-3 Standard" or not self._robot:
229
+ raise RobotTypeError("The RobotContext is only available on Flex robots.")
221
230
  return self._robot
222
231
 
223
232
  @property
@@ -229,7 +238,9 @@ class ProtocolContext(CommandPublisher):
229
238
  "This function will be deprecated in later versions."
230
239
  "Please use with caution."
231
240
  )
232
- return self._robot.hardware
241
+ if self._robot:
242
+ return self._robot.hardware
243
+ return HardwareManager(hardware=self._core.get_hardware())
233
244
 
234
245
  @property
235
246
  @requires_version(2, 0)
@@ -388,6 +399,7 @@ class ProtocolContext(CommandPublisher):
388
399
  namespace: Optional[str] = None,
389
400
  version: Optional[int] = None,
390
401
  adapter: Optional[str] = None,
402
+ lid: Optional[str] = None,
391
403
  ) -> Labware:
392
404
  """Load a labware onto a location.
393
405
 
@@ -432,6 +444,10 @@ class ProtocolContext(CommandPublisher):
432
444
  values as the ``load_name`` parameter of :py:meth:`.load_adapter`. The
433
445
  adapter will use the same namespace as the labware, and the API will
434
446
  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.
435
451
 
436
452
  .. versionadded:: 2.15
437
453
  """
@@ -472,6 +488,20 @@ class ProtocolContext(CommandPublisher):
472
488
  version=version,
473
489
  )
474
490
 
491
+ if lid is not None:
492
+ if self._api_version < validation.LID_STACK_VERSION_GATE:
493
+ raise APIVersionError(
494
+ api_element="Loading a Lid on a Labware",
495
+ until_version="2.23",
496
+ current_version=f"{self._api_version}",
497
+ )
498
+ self._core.load_lid(
499
+ load_name=lid,
500
+ location=labware_core,
501
+ namespace=namespace,
502
+ version=version,
503
+ )
504
+
475
505
  labware = Labware(
476
506
  core=labware_core,
477
507
  api_version=self._api_version,
@@ -956,7 +986,10 @@ class ProtocolContext(CommandPublisher):
956
986
  mount, checked_instrument_name
957
987
  )
958
988
 
959
- is_96_channel = checked_instrument_name == PipetteNameType.P1000_96
989
+ is_96_channel = checked_instrument_name in [
990
+ PipetteNameType.P1000_96,
991
+ PipetteNameType.P200_96,
992
+ ]
960
993
 
961
994
  tip_racks = tip_racks or []
962
995
 
@@ -1320,6 +1353,94 @@ class ProtocolContext(CommandPublisher):
1320
1353
  """Returns ``True`` if the front door of the robot is closed."""
1321
1354
  return self._core.door_closed()
1322
1355
 
1356
+ @requires_version(2, 23)
1357
+ def load_lid_stack(
1358
+ self,
1359
+ load_name: str,
1360
+ location: Union[DeckLocation, Labware],
1361
+ quantity: int,
1362
+ adapter: Optional[str] = None,
1363
+ namespace: Optional[str] = None,
1364
+ version: Optional[int] = None,
1365
+ ) -> Labware:
1366
+ """
1367
+ Load a stack of Lids onto a valid Deck Location or Adapter.
1368
+
1369
+ :param str load_name: A string to use for looking up a lid definition.
1370
+ You can find the ``load_name`` for any standard lid on the Opentrons
1371
+ `Labware Library <https://labware.opentrons.com>`_.
1372
+ :param location: Either a :ref:`deck slot <deck-slots>`,
1373
+ like ``1``, ``"1"``, or ``"D1"``, or the a valid Opentrons Adapter.
1374
+ :param int quantity: The quantity of lids to be loaded in the stack.
1375
+ :param adapter: An adapter to load the lid stack on top of. Accepts the same
1376
+ values as the ``load_name`` parameter of :py:meth:`.load_adapter`. The
1377
+ adapter will use the same namespace as the lid labware, and the API will
1378
+ choose the adapter's version automatically.
1379
+ :param str namespace: The namespace that the lid labware definition belongs to.
1380
+ If unspecified, the API will automatically search two namespaces:
1381
+
1382
+ - ``"opentrons"``, to load standard Opentrons labware definitions.
1383
+ - ``"custom_beta"``, to load custom labware definitions created with the
1384
+ `Custom Labware Creator <https://labware.opentrons.com/create>`__.
1385
+
1386
+ You might need to specify an explicit ``namespace`` if you have a custom
1387
+ definition whose ``load_name`` is the same as an Opentrons-verified
1388
+ definition, and you want to explicitly choose one or the other.
1389
+
1390
+ :param version: The version of the labware definition. You should normally
1391
+ leave this unspecified to let ``load_lid_stack()`` choose a version
1392
+ automatically.
1393
+
1394
+ :return: The initialized and loaded labware object representing the Lid Stack.
1395
+ """
1396
+ if self._api_version < validation.LID_STACK_VERSION_GATE:
1397
+ raise APIVersionError(
1398
+ api_element="Loading a Lid Stack",
1399
+ until_version="2.23",
1400
+ current_version=f"{self._api_version}",
1401
+ )
1402
+
1403
+ load_location: Union[DeckSlotName, StagingSlotName, LabwareCore]
1404
+ if isinstance(location, Labware):
1405
+ load_location = location._core
1406
+ else:
1407
+ load_location = validation.ensure_and_convert_deck_slot(
1408
+ location, self._api_version, self._core.robot_type
1409
+ )
1410
+
1411
+ if adapter is not None:
1412
+ if isinstance(load_location, DeckSlotName) or isinstance(
1413
+ load_location, StagingSlotName
1414
+ ):
1415
+ loaded_adapter = self.load_adapter(
1416
+ load_name=adapter,
1417
+ location=load_location.value,
1418
+ namespace=namespace,
1419
+ )
1420
+ load_location = loaded_adapter._core
1421
+ else:
1422
+ raise ValueError(
1423
+ "Location cannot be a Labware or Adapter when the 'adapter' field is not None."
1424
+ )
1425
+
1426
+ load_name = validation.ensure_lowercase_name(load_name)
1427
+
1428
+ result = self._core.load_lid_stack(
1429
+ load_name=load_name,
1430
+ location=load_location,
1431
+ quantity=quantity,
1432
+ namespace=namespace,
1433
+ version=version,
1434
+ )
1435
+
1436
+ labware = Labware(
1437
+ core=result,
1438
+ api_version=self._api_version,
1439
+ protocol_core=self._core,
1440
+ core_map=self._core_map,
1441
+ )
1442
+ return labware
1443
+
1323
1444
 
1324
1445
  def _create_module_context(
1325
1446
  module_core: Union[ModuleCore, NonConnectedModuleCore],
@@ -1,11 +1,25 @@
1
- from typing import NamedTuple, Union, Dict, Optional
1
+ from typing import NamedTuple, Union, Optional
2
2
 
3
- from opentrons.types import Mount, DeckLocation, Point
3
+ from opentrons.types import (
4
+ Mount,
5
+ DeckLocation,
6
+ Location,
7
+ Point,
8
+ AxisMapType,
9
+ AxisType,
10
+ StringAxisMap,
11
+ )
4
12
  from opentrons.legacy_commands import publisher
5
- from opentrons.hardware_control import SyncHardwareAPI, types as hw_types
13
+ from opentrons.hardware_control import SyncHardwareAPI
14
+ from opentrons.protocols.api_support.util import requires_version
15
+ from opentrons.protocols.api_support.types import APIVersion
16
+ from opentrons_shared_data.pipette.types import PipetteNameType
6
17
 
7
- from ._types import OffDeckType
8
- from .core.common import ProtocolCore
18
+ from . import validation
19
+ from .core.common import ProtocolCore, RobotCore
20
+ from .module_contexts import ModuleContext
21
+ from .labware import Labware
22
+ from ._types import PipetteActionTypes, PlungerPositionTypes
9
23
 
10
24
 
11
25
  class HardwareManager(NamedTuple):
@@ -34,56 +48,214 @@ class RobotContext(publisher.CommandPublisher):
34
48
 
35
49
  """
36
50
 
37
- def __init__(self, core: ProtocolCore) -> None:
38
- self._hardware = HardwareManager(hardware=core.get_hardware())
51
+ def __init__(
52
+ self, core: RobotCore, protocol_core: ProtocolCore, api_version: APIVersion
53
+ ) -> None:
54
+ self._hardware = HardwareManager(hardware=protocol_core.get_hardware())
55
+ self._core = core
56
+ self._protocol_core = protocol_core
57
+ self._api_version = api_version
58
+
59
+ @property
60
+ @requires_version(2, 22)
61
+ def api_version(self) -> APIVersion:
62
+ return self._api_version
39
63
 
40
64
  @property
41
65
  def hardware(self) -> HardwareManager:
66
+ # TODO this hardware attribute should be deprecated
67
+ # in version 3.0+ as we will only support exposed robot
68
+ # context commands.
42
69
  return self._hardware
43
70
 
71
+ @requires_version(2, 22)
44
72
  def move_to(
45
73
  self,
46
74
  mount: Union[Mount, str],
47
- destination: Point,
48
- velocity: float,
75
+ destination: Location,
76
+ speed: Optional[float] = None,
49
77
  ) -> None:
50
- raise NotImplementedError()
78
+ """
79
+ Move a specified mount to a destination location on the deck.
80
+
81
+ :param mount: The mount of the instrument you wish to move.
82
+ This can either be an instance of :py:class:`.types.Mount` or one
83
+ of the strings ``"left"``, ``"right"``, ``"extension"``, ``"gripper"``. Note
84
+ that the gripper mount can be referred to either as ``"extension"`` or ``"gripper"``.
85
+ :type mount: types.Mount or str
86
+ :param Location destination:
87
+ :param speed:
88
+ """
89
+ mount = validation.ensure_instrument_mount(mount)
90
+ self._core.move_to(mount, destination.point, speed)
51
91
 
92
+ @requires_version(2, 22)
52
93
  def move_axes_to(
53
94
  self,
54
- abs_axis_map: Dict[hw_types.Axis, hw_types.AxisMapValue],
55
- velocity: float,
56
- critical_point: Optional[hw_types.CriticalPoint],
95
+ axis_map: Union[AxisMapType, StringAxisMap],
96
+ critical_point: Optional[Union[AxisMapType, StringAxisMap]] = None,
97
+ speed: Optional[float] = None,
57
98
  ) -> None:
58
- raise NotImplementedError()
99
+ """
100
+ Move a set of axes to an absolute position on the deck.
59
101
 
102
+ :param axis_map: A dictionary mapping axes to an absolute position on the deck in mm.
103
+ :param critical_point: The critical point to move the axes with. It should only
104
+ specify the gantry axes (i.e. `x`, `y`, `z`).
105
+ :param float speed: The maximum speed with which you want to move all the axes
106
+ in the axis map.
107
+ """
108
+ instrument_on_left = self._core.get_pipette_type_from_engine(Mount.LEFT)
109
+ is_96_channel = instrument_on_left == PipetteNameType.P1000_96
110
+ axis_map = validation.ensure_axis_map_type(
111
+ axis_map, self._protocol_core.robot_type, is_96_channel
112
+ )
113
+ if critical_point:
114
+ critical_point = validation.ensure_axis_map_type(
115
+ critical_point, self._protocol_core.robot_type, is_96_channel
116
+ )
117
+ validation.ensure_only_gantry_axis_map_type(
118
+ critical_point, self._protocol_core.robot_type
119
+ )
120
+ else:
121
+ critical_point = None
122
+ self._core.move_axes_to(axis_map, critical_point, speed)
123
+
124
+ @requires_version(2, 22)
60
125
  def move_axes_relative(
61
- self, rel_axis_map: Dict[hw_types.Axis, hw_types.AxisMapValue], velocity: float
126
+ self,
127
+ axis_map: Union[AxisMapType, StringAxisMap],
128
+ speed: Optional[float] = None,
62
129
  ) -> None:
63
- raise NotImplementedError()
130
+ """
131
+ Move a set of axes to a relative position on the deck.
132
+
133
+ :param axis_map: A dictionary mapping axes to relative movements in mm.
134
+ :type mount: types.Mount or str
64
135
 
65
- def close_gripper_jaw(self, force: float) -> None:
66
- raise NotImplementedError()
136
+ :param float speed: The maximum speed with which you want to move all the axes
137
+ in the axis map.
138
+ """
139
+ instrument_on_left = self._core.get_pipette_type_from_engine(Mount.LEFT)
140
+ is_96_channel = instrument_on_left == PipetteNameType.P1000_96
141
+
142
+ axis_map = validation.ensure_axis_map_type(
143
+ axis_map, self._protocol_core.robot_type, is_96_channel
144
+ )
145
+ self._core.move_axes_relative(axis_map, speed)
146
+
147
+ def close_gripper_jaw(self, force: Optional[float] = None) -> None:
148
+ """Command the gripper closed with some force."""
149
+ self._core.close_gripper(force)
67
150
 
68
151
  def open_gripper_jaw(self) -> None:
69
- raise NotImplementedError()
152
+ """Command the gripper open."""
153
+ self._core.release_grip()
70
154
 
71
155
  def axis_coordinates_for(
72
- self, mount: Union[Mount, str], location: Union[DeckLocation, OffDeckType]
73
- ) -> None:
74
- raise NotImplementedError()
156
+ self,
157
+ mount: Union[Mount, str],
158
+ location: Union[Location, ModuleContext, DeckLocation],
159
+ ) -> AxisMapType:
160
+ """
161
+ Build a :py:class:`.types.AxisMapType` from a location to be compatible with
162
+ either :py:meth:`.RobotContext.move_axes_to` or :py:meth:`.RobotContext.move_axes_relative`.
163
+ You must provide only one of `location`, `slot`, or `module` to build
164
+ the axis map.
165
+
166
+ :param mount: The mount of the instrument you wish create an axis map for.
167
+ This can either be an instance of :py:class:`.types.Mount` or one
168
+ of the strings ``"left"``, ``"right"``, ``"extension"``, ``"gripper"``. Note
169
+ that the gripper mount can be referred to either as ``"extension"`` or ``"gripper"``.
170
+ :type mount: types.Mount or str
171
+ :param location: The location to format an axis map for.
172
+ :type location: `Well`, `ModuleContext`, `DeckLocation` or `OffDeckType`
173
+ """
174
+ mount = validation.ensure_instrument_mount(mount)
175
+
176
+ mount_axis = AxisType.axis_for_mount(mount)
177
+ if location:
178
+ loc: Union[Point, Labware, None]
179
+ if isinstance(location, ModuleContext):
180
+ loc = location.labware
181
+ if not loc:
182
+ raise ValueError(f"There must be a labware on {location}")
183
+ top_of_labware = loc.wells()[0].top()
184
+ loc = top_of_labware.point
185
+ return {mount_axis: loc.z, AxisType.X: loc.x, AxisType.Y: loc.y}
186
+ elif location is DeckLocation and not isinstance(location, Location):
187
+ slot_name = validation.ensure_and_convert_deck_slot(
188
+ location,
189
+ api_version=self._api_version,
190
+ robot_type=self._protocol_core.robot_type,
191
+ )
192
+ loc = self._protocol_core.get_slot_center(slot_name)
193
+ return {mount_axis: loc.z, AxisType.X: loc.x, AxisType.Y: loc.y}
194
+ elif isinstance(location, Location):
195
+ assert isinstance(location, Location)
196
+ loc = location.point
197
+ return {mount_axis: loc.z, AxisType.X: loc.x, AxisType.Y: loc.y}
198
+ else:
199
+ raise ValueError(
200
+ "Location parameter must be a Module, Deck Location, or Location type."
201
+ )
202
+ else:
203
+ raise TypeError("You must specify a location to move to.")
75
204
 
76
205
  def plunger_coordinates_for_volume(
77
- self, mount: Union[Mount, str], volume: float
78
- ) -> None:
79
- raise NotImplementedError()
206
+ self, mount: Union[Mount, str], volume: float, action: PipetteActionTypes
207
+ ) -> AxisMapType:
208
+ """
209
+ Build a :py:class:`.types.AxisMapType` for a pipette plunger motor from volume.
210
+
211
+ """
212
+ pipette_name = self._core.get_pipette_type_from_engine(mount)
213
+ if not pipette_name:
214
+ raise ValueError(
215
+ f"Expected a pipette to be attached to provided mount {mount}"
216
+ )
217
+ mount = validation.ensure_mount_for_pipette(mount, pipette_name)
218
+ pipette_axis = AxisType.plunger_axis_for_mount(mount)
219
+
220
+ pipette_position = self._core.get_plunger_position_from_volume(
221
+ mount, volume, action, self._protocol_core.robot_type
222
+ )
223
+ return {pipette_axis: pipette_position}
80
224
 
81
225
  def plunger_coordinates_for_named_position(
82
- self, mount: Union[Mount, str], position_name: str
83
- ) -> None:
84
- raise NotImplementedError()
226
+ self, mount: Union[Mount, str], position_name: PlungerPositionTypes
227
+ ) -> AxisMapType:
228
+ """
229
+ Build a :py:class:`.types.AxisMapType` for a pipette plunger motor from position_name.
85
230
 
86
- def build_axis_map(
87
- self, axis_map: Dict[hw_types.Axis, hw_types.AxisMapValue]
88
- ) -> None:
89
- raise NotImplementedError()
231
+ """
232
+ pipette_name = self._core.get_pipette_type_from_engine(mount)
233
+ if not pipette_name:
234
+ raise ValueError(
235
+ f"Expected a pipette to be attached to provided mount {mount}"
236
+ )
237
+ mount = validation.ensure_mount_for_pipette(mount, pipette_name)
238
+ pipette_axis = AxisType.plunger_axis_for_mount(mount)
239
+ pipette_position = self._core.get_plunger_position_from_name(
240
+ mount, position_name
241
+ )
242
+ return {pipette_axis: pipette_position}
243
+
244
+ def build_axis_map(self, axis_map: StringAxisMap) -> AxisMapType:
245
+ """Take in a :py:class:`.types.StringAxisMap` and output a :py:class:`.types.AxisMapType`.
246
+ A :py:class:`.types.StringAxisMap` is allowed to contain any of the following strings:
247
+ ``"x"``, ``"y"``, "``z_l"``, "``z_r"``, "``z_g"``, ``"q"``.
248
+
249
+ An example of a valid axis map could be:
250
+
251
+ {"x": 1, "y": 2} or {"Z_L": 100}
252
+
253
+ Note that capitalization does not matter.
254
+
255
+ """
256
+ instrument_on_left = self._core.get_pipette_type_from_engine(Mount.LEFT)
257
+ is_96_channel = instrument_on_left == PipetteNameType.P1000_96
258
+
259
+ return validation.ensure_axis_map_type(
260
+ axis_map, self._protocol_core.robot_type, is_96_channel
261
+ )