opentrons 8.2.0a4__py2.py3-none-any.whl → 8.3.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 (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/config/defaults_ot3.py +1 -0
  9. opentrons/drivers/asyncio/communication/__init__.py +2 -0
  10. opentrons/drivers/asyncio/communication/errors.py +16 -3
  11. opentrons/drivers/asyncio/communication/serial_connection.py +24 -9
  12. opentrons/drivers/command_builder.py +2 -2
  13. opentrons/drivers/flex_stacker/__init__.py +9 -0
  14. opentrons/drivers/flex_stacker/abstract.py +89 -0
  15. opentrons/drivers/flex_stacker/driver.py +260 -0
  16. opentrons/drivers/flex_stacker/simulator.py +109 -0
  17. opentrons/drivers/flex_stacker/types.py +138 -0
  18. opentrons/drivers/heater_shaker/driver.py +18 -3
  19. opentrons/drivers/temp_deck/driver.py +13 -3
  20. opentrons/drivers/thermocycler/driver.py +17 -3
  21. opentrons/execute.py +3 -1
  22. opentrons/hardware_control/__init__.py +1 -2
  23. opentrons/hardware_control/api.py +28 -20
  24. opentrons/hardware_control/backends/flex_protocol.py +17 -7
  25. opentrons/hardware_control/backends/ot3controller.py +213 -63
  26. opentrons/hardware_control/backends/ot3simulator.py +18 -9
  27. opentrons/hardware_control/backends/ot3utils.py +43 -15
  28. opentrons/hardware_control/dev_types.py +4 -0
  29. opentrons/hardware_control/emulation/heater_shaker.py +4 -0
  30. opentrons/hardware_control/emulation/module_server/client.py +1 -1
  31. opentrons/hardware_control/emulation/module_server/server.py +5 -3
  32. opentrons/hardware_control/emulation/settings.py +3 -4
  33. opentrons/hardware_control/instruments/ot2/instrument_calibration.py +2 -1
  34. opentrons/hardware_control/instruments/ot2/pipette.py +15 -22
  35. opentrons/hardware_control/instruments/ot2/pipette_handler.py +8 -1
  36. opentrons/hardware_control/instruments/ot3/gripper.py +2 -2
  37. opentrons/hardware_control/instruments/ot3/pipette.py +23 -22
  38. opentrons/hardware_control/instruments/ot3/pipette_handler.py +10 -1
  39. opentrons/hardware_control/modules/mod_abc.py +2 -2
  40. opentrons/hardware_control/motion_utilities.py +68 -0
  41. opentrons/hardware_control/nozzle_manager.py +39 -41
  42. opentrons/hardware_control/ot3_calibration.py +1 -1
  43. opentrons/hardware_control/ot3api.py +60 -23
  44. opentrons/hardware_control/protocols/gripper_controller.py +3 -0
  45. opentrons/hardware_control/protocols/hardware_manager.py +5 -1
  46. opentrons/hardware_control/protocols/liquid_handler.py +18 -0
  47. opentrons/hardware_control/protocols/motion_controller.py +6 -0
  48. opentrons/hardware_control/robot_calibration.py +1 -1
  49. opentrons/hardware_control/types.py +61 -0
  50. opentrons/protocol_api/__init__.py +20 -1
  51. opentrons/protocol_api/_liquid.py +24 -49
  52. opentrons/protocol_api/_liquid_properties.py +754 -0
  53. opentrons/protocol_api/_types.py +24 -0
  54. opentrons/protocol_api/core/common.py +2 -0
  55. opentrons/protocol_api/core/engine/instrument.py +82 -10
  56. opentrons/protocol_api/core/engine/labware.py +29 -7
  57. opentrons/protocol_api/core/engine/protocol.py +130 -5
  58. opentrons/protocol_api/core/engine/robot.py +139 -0
  59. opentrons/protocol_api/core/engine/well.py +4 -1
  60. opentrons/protocol_api/core/instrument.py +46 -4
  61. opentrons/protocol_api/core/labware.py +13 -4
  62. opentrons/protocol_api/core/legacy/legacy_instrument_core.py +37 -3
  63. opentrons/protocol_api/core/legacy/legacy_labware_core.py +13 -4
  64. opentrons/protocol_api/core/legacy/legacy_protocol_core.py +32 -1
  65. opentrons/protocol_api/core/legacy/legacy_robot_core.py +0 -0
  66. opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +37 -3
  67. opentrons/protocol_api/core/protocol.py +34 -1
  68. opentrons/protocol_api/core/robot.py +51 -0
  69. opentrons/protocol_api/instrument_context.py +158 -44
  70. opentrons/protocol_api/labware.py +231 -7
  71. opentrons/protocol_api/module_contexts.py +21 -17
  72. opentrons/protocol_api/protocol_context.py +125 -4
  73. opentrons/protocol_api/robot_context.py +204 -32
  74. opentrons/protocol_api/validation.py +262 -3
  75. opentrons/protocol_engine/__init__.py +4 -0
  76. opentrons/protocol_engine/actions/actions.py +2 -3
  77. opentrons/protocol_engine/clients/sync_client.py +18 -0
  78. opentrons/protocol_engine/commands/__init__.py +81 -0
  79. opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +0 -2
  80. opentrons/protocol_engine/commands/absorbance_reader/initialize.py +19 -5
  81. opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +0 -1
  82. opentrons/protocol_engine/commands/absorbance_reader/read.py +32 -9
  83. opentrons/protocol_engine/commands/air_gap_in_place.py +160 -0
  84. opentrons/protocol_engine/commands/aspirate.py +103 -53
  85. opentrons/protocol_engine/commands/aspirate_in_place.py +55 -51
  86. opentrons/protocol_engine/commands/blow_out.py +44 -39
  87. opentrons/protocol_engine/commands/blow_out_in_place.py +21 -32
  88. opentrons/protocol_engine/commands/calibration/calibrate_gripper.py +13 -6
  89. opentrons/protocol_engine/commands/calibration/calibrate_module.py +1 -1
  90. opentrons/protocol_engine/commands/calibration/calibrate_pipette.py +3 -3
  91. opentrons/protocol_engine/commands/calibration/move_to_maintenance_position.py +1 -1
  92. opentrons/protocol_engine/commands/command.py +73 -66
  93. opentrons/protocol_engine/commands/command_unions.py +101 -1
  94. opentrons/protocol_engine/commands/comment.py +1 -1
  95. opentrons/protocol_engine/commands/configure_for_volume.py +10 -3
  96. opentrons/protocol_engine/commands/configure_nozzle_layout.py +6 -4
  97. opentrons/protocol_engine/commands/custom.py +6 -12
  98. opentrons/protocol_engine/commands/dispense.py +82 -48
  99. opentrons/protocol_engine/commands/dispense_in_place.py +71 -51
  100. opentrons/protocol_engine/commands/drop_tip.py +52 -31
  101. opentrons/protocol_engine/commands/drop_tip_in_place.py +13 -3
  102. opentrons/protocol_engine/commands/generate_command_schema.py +4 -11
  103. opentrons/protocol_engine/commands/get_next_tip.py +134 -0
  104. opentrons/protocol_engine/commands/get_tip_presence.py +1 -1
  105. opentrons/protocol_engine/commands/heater_shaker/close_labware_latch.py +1 -1
  106. opentrons/protocol_engine/commands/heater_shaker/deactivate_heater.py +1 -1
  107. opentrons/protocol_engine/commands/heater_shaker/deactivate_shaker.py +1 -1
  108. opentrons/protocol_engine/commands/heater_shaker/open_labware_latch.py +1 -1
  109. opentrons/protocol_engine/commands/heater_shaker/set_and_wait_for_shake_speed.py +1 -1
  110. opentrons/protocol_engine/commands/heater_shaker/set_target_temperature.py +1 -1
  111. opentrons/protocol_engine/commands/heater_shaker/wait_for_temperature.py +10 -4
  112. opentrons/protocol_engine/commands/home.py +13 -4
  113. opentrons/protocol_engine/commands/liquid_probe.py +67 -24
  114. opentrons/protocol_engine/commands/load_labware.py +29 -7
  115. opentrons/protocol_engine/commands/load_lid.py +146 -0
  116. opentrons/protocol_engine/commands/load_lid_stack.py +189 -0
  117. opentrons/protocol_engine/commands/load_liquid.py +12 -4
  118. opentrons/protocol_engine/commands/load_liquid_class.py +144 -0
  119. opentrons/protocol_engine/commands/load_module.py +31 -10
  120. opentrons/protocol_engine/commands/load_pipette.py +19 -8
  121. opentrons/protocol_engine/commands/magnetic_module/disengage.py +1 -1
  122. opentrons/protocol_engine/commands/magnetic_module/engage.py +1 -1
  123. opentrons/protocol_engine/commands/move_labware.py +19 -6
  124. opentrons/protocol_engine/commands/move_relative.py +35 -25
  125. opentrons/protocol_engine/commands/move_to_addressable_area.py +40 -27
  126. opentrons/protocol_engine/commands/move_to_addressable_area_for_drop_tip.py +53 -32
  127. opentrons/protocol_engine/commands/move_to_coordinates.py +36 -22
  128. opentrons/protocol_engine/commands/move_to_well.py +40 -24
  129. opentrons/protocol_engine/commands/movement_common.py +338 -0
  130. opentrons/protocol_engine/commands/pick_up_tip.py +49 -27
  131. opentrons/protocol_engine/commands/pipetting_common.py +169 -87
  132. opentrons/protocol_engine/commands/prepare_to_aspirate.py +24 -33
  133. opentrons/protocol_engine/commands/reload_labware.py +1 -1
  134. opentrons/protocol_engine/commands/retract_axis.py +1 -1
  135. opentrons/protocol_engine/commands/robot/__init__.py +69 -0
  136. opentrons/protocol_engine/commands/robot/close_gripper_jaw.py +86 -0
  137. opentrons/protocol_engine/commands/robot/common.py +18 -0
  138. opentrons/protocol_engine/commands/robot/move_axes_relative.py +101 -0
  139. opentrons/protocol_engine/commands/robot/move_axes_to.py +100 -0
  140. opentrons/protocol_engine/commands/robot/move_to.py +94 -0
  141. opentrons/protocol_engine/commands/robot/open_gripper_jaw.py +77 -0
  142. opentrons/protocol_engine/commands/save_position.py +14 -5
  143. opentrons/protocol_engine/commands/set_rail_lights.py +1 -1
  144. opentrons/protocol_engine/commands/set_status_bar.py +1 -1
  145. opentrons/protocol_engine/commands/temperature_module/deactivate.py +1 -1
  146. opentrons/protocol_engine/commands/temperature_module/set_target_temperature.py +1 -1
  147. opentrons/protocol_engine/commands/temperature_module/wait_for_temperature.py +10 -4
  148. opentrons/protocol_engine/commands/thermocycler/close_lid.py +1 -1
  149. opentrons/protocol_engine/commands/thermocycler/deactivate_block.py +1 -1
  150. opentrons/protocol_engine/commands/thermocycler/deactivate_lid.py +1 -1
  151. opentrons/protocol_engine/commands/thermocycler/open_lid.py +1 -1
  152. opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py +8 -2
  153. opentrons/protocol_engine/commands/thermocycler/run_profile.py +9 -3
  154. opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py +11 -4
  155. opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py +1 -1
  156. opentrons/protocol_engine/commands/thermocycler/wait_for_block_temperature.py +1 -1
  157. opentrons/protocol_engine/commands/thermocycler/wait_for_lid_temperature.py +1 -1
  158. opentrons/protocol_engine/commands/touch_tip.py +65 -16
  159. opentrons/protocol_engine/commands/unsafe/unsafe_blow_out_in_place.py +4 -1
  160. opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +12 -3
  161. opentrons/protocol_engine/commands/unsafe/unsafe_engage_axes.py +1 -4
  162. opentrons/protocol_engine/commands/unsafe/update_position_estimators.py +1 -4
  163. opentrons/protocol_engine/commands/verify_tip_presence.py +11 -4
  164. opentrons/protocol_engine/commands/wait_for_duration.py +10 -3
  165. opentrons/protocol_engine/commands/wait_for_resume.py +10 -3
  166. opentrons/protocol_engine/errors/__init__.py +8 -0
  167. opentrons/protocol_engine/errors/error_occurrence.py +19 -20
  168. opentrons/protocol_engine/errors/exceptions.py +50 -0
  169. opentrons/protocol_engine/execution/command_executor.py +1 -1
  170. opentrons/protocol_engine/execution/equipment.py +73 -5
  171. opentrons/protocol_engine/execution/gantry_mover.py +364 -8
  172. opentrons/protocol_engine/execution/movement.py +27 -0
  173. opentrons/protocol_engine/execution/pipetting.py +5 -1
  174. opentrons/protocol_engine/execution/tip_handler.py +4 -6
  175. opentrons/protocol_engine/notes/notes.py +1 -1
  176. opentrons/protocol_engine/protocol_engine.py +7 -6
  177. opentrons/protocol_engine/resources/labware_data_provider.py +1 -1
  178. opentrons/protocol_engine/resources/labware_validation.py +5 -0
  179. opentrons/protocol_engine/resources/module_data_provider.py +1 -1
  180. opentrons/protocol_engine/resources/pipette_data_provider.py +26 -0
  181. opentrons/protocol_engine/slot_standardization.py +9 -9
  182. opentrons/protocol_engine/state/_move_types.py +9 -5
  183. opentrons/protocol_engine/state/_well_math.py +193 -0
  184. opentrons/protocol_engine/state/addressable_areas.py +25 -61
  185. opentrons/protocol_engine/state/command_history.py +12 -0
  186. opentrons/protocol_engine/state/commands.py +17 -13
  187. opentrons/protocol_engine/state/files.py +10 -12
  188. opentrons/protocol_engine/state/fluid_stack.py +138 -0
  189. opentrons/protocol_engine/state/frustum_helpers.py +57 -32
  190. opentrons/protocol_engine/state/geometry.py +47 -1
  191. opentrons/protocol_engine/state/labware.py +79 -25
  192. opentrons/protocol_engine/state/liquid_classes.py +82 -0
  193. opentrons/protocol_engine/state/liquids.py +16 -4
  194. opentrons/protocol_engine/state/modules.py +52 -70
  195. opentrons/protocol_engine/state/motion.py +6 -1
  196. opentrons/protocol_engine/state/pipettes.py +144 -58
  197. opentrons/protocol_engine/state/state.py +21 -2
  198. opentrons/protocol_engine/state/state_summary.py +4 -2
  199. opentrons/protocol_engine/state/tips.py +11 -44
  200. opentrons/protocol_engine/state/update_types.py +343 -48
  201. opentrons/protocol_engine/state/wells.py +19 -11
  202. opentrons/protocol_engine/types.py +176 -28
  203. opentrons/protocol_reader/extract_labware_definitions.py +5 -2
  204. opentrons/protocol_reader/file_format_validator.py +5 -5
  205. opentrons/protocol_runner/json_file_reader.py +9 -3
  206. opentrons/protocol_runner/json_translator.py +51 -25
  207. opentrons/protocol_runner/legacy_command_mapper.py +66 -64
  208. opentrons/protocol_runner/protocol_runner.py +35 -4
  209. opentrons/protocol_runner/python_protocol_wrappers.py +1 -1
  210. opentrons/protocol_runner/run_orchestrator.py +13 -3
  211. opentrons/protocols/advanced_control/common.py +38 -0
  212. opentrons/protocols/advanced_control/mix.py +1 -1
  213. opentrons/protocols/advanced_control/transfers/__init__.py +0 -0
  214. opentrons/protocols/advanced_control/transfers/common.py +56 -0
  215. opentrons/protocols/advanced_control/{transfers.py → transfers/transfer.py} +10 -85
  216. opentrons/protocols/api_support/definitions.py +1 -1
  217. opentrons/protocols/api_support/instrument.py +1 -1
  218. opentrons/protocols/api_support/util.py +10 -0
  219. opentrons/protocols/labware.py +39 -6
  220. opentrons/protocols/models/json_protocol.py +5 -9
  221. opentrons/simulate.py +3 -1
  222. opentrons/types.py +162 -2
  223. opentrons/util/logging_config.py +1 -1
  224. {opentrons-8.2.0a4.dist-info → opentrons-8.3.0a1.dist-info}/METADATA +16 -15
  225. {opentrons-8.2.0a4.dist-info → opentrons-8.3.0a1.dist-info}/RECORD +229 -202
  226. {opentrons-8.2.0a4.dist-info → opentrons-8.3.0a1.dist-info}/WHEEL +1 -1
  227. {opentrons-8.2.0a4.dist-info → opentrons-8.3.0a1.dist-info}/LICENSE +0 -0
  228. {opentrons-8.2.0a4.dist-info → opentrons-8.3.0a1.dist-info}/entry_points.txt +0 -0
  229. {opentrons-8.2.0a4.dist-info → opentrons-8.3.0a1.dist-info}/top_level.txt +0 -0
@@ -131,7 +131,7 @@ class LabwareStore(HasState[LabwareState], HandlesActions):
131
131
  for fixed_labware in deck_fixed_labware
132
132
  }
133
133
  labware_by_id = {
134
- fixed_labware.labware_id: LoadedLabware.construct(
134
+ fixed_labware.labware_id: LoadedLabware.model_construct(
135
135
  id=fixed_labware.labware_id,
136
136
  location=fixed_labware.location,
137
137
  loadName=fixed_labware.definition.parameters.loadName,
@@ -156,10 +156,12 @@ class LabwareStore(HasState[LabwareState], HandlesActions):
156
156
  """Modify state in reaction to an action."""
157
157
  for state_update in get_state_updates(action):
158
158
  self._add_loaded_labware(state_update)
159
+ self._add_loaded_lid_stack(state_update)
159
160
  self._set_labware_location(state_update)
161
+ self._set_labware_lid(state_update)
160
162
 
161
163
  if isinstance(action, AddLabwareOffsetAction):
162
- labware_offset = LabwareOffset.construct(
164
+ labware_offset = LabwareOffset.model_construct(
163
165
  id=action.labware_offset_id,
164
166
  createdAt=action.created_at,
165
167
  definitionUri=action.request.definitionUri,
@@ -212,7 +214,7 @@ class LabwareStore(HasState[LabwareState], HandlesActions):
212
214
 
213
215
  self._state.labware_by_id[
214
216
  loaded_labware_update.labware_id
215
- ] = LoadedLabware.construct(
217
+ ] = LoadedLabware.model_construct(
216
218
  id=loaded_labware_update.labware_id,
217
219
  location=location,
218
220
  loadName=loaded_labware_update.definition.parameters.loadName,
@@ -221,6 +223,63 @@ class LabwareStore(HasState[LabwareState], HandlesActions):
221
223
  displayName=display_name,
222
224
  )
223
225
 
226
+ def _add_loaded_lid_stack(self, state_update: update_types.StateUpdate) -> None:
227
+ loaded_lid_stack_update = state_update.loaded_lid_stack
228
+ if loaded_lid_stack_update != update_types.NO_CHANGE:
229
+ # Add the stack object
230
+ stack_definition_uri = uri_from_details(
231
+ namespace=loaded_lid_stack_update.stack_object_definition.namespace,
232
+ load_name=loaded_lid_stack_update.stack_object_definition.parameters.loadName,
233
+ version=loaded_lid_stack_update.stack_object_definition.version,
234
+ )
235
+ self.state.definitions_by_uri[
236
+ stack_definition_uri
237
+ ] = loaded_lid_stack_update.stack_object_definition
238
+ self._state.labware_by_id[
239
+ loaded_lid_stack_update.stack_id
240
+ ] = LoadedLabware.construct(
241
+ id=loaded_lid_stack_update.stack_id,
242
+ location=loaded_lid_stack_update.stack_location,
243
+ loadName=loaded_lid_stack_update.stack_object_definition.parameters.loadName,
244
+ definitionUri=stack_definition_uri,
245
+ offsetId=None,
246
+ displayName=None,
247
+ )
248
+
249
+ # Add the Lids on top of the stack object
250
+ for i in range(len(loaded_lid_stack_update.labware_ids)):
251
+ definition_uri = uri_from_details(
252
+ namespace=loaded_lid_stack_update.definition.namespace,
253
+ load_name=loaded_lid_stack_update.definition.parameters.loadName,
254
+ version=loaded_lid_stack_update.definition.version,
255
+ )
256
+
257
+ self._state.definitions_by_uri[
258
+ definition_uri
259
+ ] = loaded_lid_stack_update.definition
260
+
261
+ location = loaded_lid_stack_update.new_locations_by_id[
262
+ loaded_lid_stack_update.labware_ids[i]
263
+ ]
264
+
265
+ self._state.labware_by_id[
266
+ loaded_lid_stack_update.labware_ids[i]
267
+ ] = LoadedLabware.construct(
268
+ id=loaded_lid_stack_update.labware_ids[i],
269
+ location=location,
270
+ loadName=loaded_lid_stack_update.definition.parameters.loadName,
271
+ definitionUri=definition_uri,
272
+ offsetId=None,
273
+ displayName=None,
274
+ )
275
+
276
+ def _set_labware_lid(self, state_update: update_types.StateUpdate) -> None:
277
+ labware_lid_update = state_update.labware_lid
278
+ if labware_lid_update != update_types.NO_CHANGE:
279
+ parent_labware_id = labware_lid_update.parent_labware_id
280
+ lid_id = labware_lid_update.lid_id
281
+ self._state.labware_by_id[parent_labware_id].lid_id = lid_id
282
+
224
283
  def _set_labware_location(self, state_update: update_types.StateUpdate) -> None:
225
284
  labware_location_update = state_update.labware_location
226
285
  if labware_location_update != update_types.NO_CHANGE:
@@ -244,7 +303,7 @@ class LabwareStore(HasState[LabwareState], HandlesActions):
244
303
  self._state.labware_by_id[labware_id].location = new_location
245
304
 
246
305
 
247
- class LabwareView(HasState[LabwareState]):
306
+ class LabwareView:
248
307
  """Read-only labware state view."""
249
308
 
250
309
  _state: LabwareState
@@ -268,7 +327,7 @@ class LabwareView(HasState[LabwareState]):
268
327
 
269
328
  def get_id_by_module(self, module_id: str) -> str:
270
329
  """Return the ID of the labware loaded on the given module."""
271
- for labware_id, labware in self.state.labware_by_id.items():
330
+ for labware_id, labware in self._state.labware_by_id.items():
272
331
  if (
273
332
  isinstance(labware.location, ModuleLocation)
274
333
  and labware.location.moduleId == module_id
@@ -281,7 +340,7 @@ class LabwareView(HasState[LabwareState]):
281
340
 
282
341
  def get_id_by_labware(self, labware_id: str) -> str:
283
342
  """Return the ID of the labware loaded on the given labware."""
284
- for labware in self.state.labware_by_id.values():
343
+ for labware in self._state.labware_by_id.values():
285
344
  if (
286
345
  isinstance(labware.location, OnLabwareLocation)
287
346
  and labware.location.labwareId == labware_id
@@ -441,21 +500,7 @@ class LabwareView(HasState[LabwareState]):
441
500
 
442
501
  If not defined within a labware, defaults to one.
443
502
  """
444
- stacking_quirks = {
445
- "stackingMaxFive": 5,
446
- "stackingMaxFour": 4,
447
- "stackingMaxThree": 3,
448
- "stackingMaxTwo": 2,
449
- "stackingMaxOne": 1,
450
- "stackingMaxZero": 0,
451
- }
452
- for quirk in stacking_quirks.keys():
453
- if (
454
- labware.parameters.quirks is not None
455
- and quirk in labware.parameters.quirks
456
- ):
457
- return stacking_quirks[quirk]
458
- return 1
503
+ return labware.stackLimit if labware.stackLimit is not None else 1
459
504
 
460
505
  def get_should_center_pipette_on_target_well(self, labware_id: str) -> bool:
461
506
  """True if a pipette moving to a well of this labware should center its body on the target.
@@ -815,6 +860,11 @@ class LabwareView(HasState[LabwareState]):
815
860
  return self.raise_if_labware_inaccessible_by_pipette(
816
861
  labware_location.labwareId
817
862
  )
863
+ elif labware.lid_id is not None:
864
+ raise errors.LocationNotAccessibleByPipetteError(
865
+ f"Cannot move pipette to {labware.loadName} "
866
+ "because labware is currently covered by a lid."
867
+ )
818
868
  elif isinstance(labware_location, AddressableAreaLocation):
819
869
  if fixture_validation.is_staging_slot(labware_location.addressableAreaName):
820
870
  raise errors.LocationNotAccessibleByPipetteError(
@@ -998,11 +1048,15 @@ class LabwareView(HasState[LabwareState]):
998
1048
  return None
999
1049
  else:
1000
1050
  return LabwareMovementOffsetData(
1001
- pickUpOffset=cast(
1002
- LabwareOffsetVector, parsed_offsets[offset_key].pickUpOffset
1051
+ pickUpOffset=LabwareOffsetVector.model_construct(
1052
+ x=parsed_offsets[offset_key].pickUpOffset.x,
1053
+ y=parsed_offsets[offset_key].pickUpOffset.y,
1054
+ z=parsed_offsets[offset_key].pickUpOffset.z,
1003
1055
  ),
1004
- dropOffset=cast(
1005
- LabwareOffsetVector, parsed_offsets[offset_key].dropOffset
1056
+ dropOffset=LabwareOffsetVector.model_construct(
1057
+ x=parsed_offsets[offset_key].dropOffset.x,
1058
+ y=parsed_offsets[offset_key].dropOffset.y,
1059
+ z=parsed_offsets[offset_key].dropOffset.z,
1006
1060
  ),
1007
1061
  )
1008
1062
 
@@ -0,0 +1,82 @@
1
+ """A data store of liquid classes."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import dataclasses
6
+ from typing import Dict
7
+ from typing_extensions import Optional
8
+
9
+ from .. import errors
10
+ from ..actions import Action, get_state_updates
11
+ from ..types import LiquidClassRecord
12
+ from . import update_types
13
+ from ._abstract_store import HasState, HandlesActions
14
+
15
+
16
+ @dataclasses.dataclass
17
+ class LiquidClassState:
18
+ """Our state is a bidirectional mapping between IDs <-> LiquidClassRecords."""
19
+
20
+ # We use the bidirectional map to see if we've already assigned an ID to a liquid class when the
21
+ # engine is asked to store a new liquid class.
22
+ liquid_class_record_by_id: Dict[str, LiquidClassRecord]
23
+ liquid_class_record_to_id: Dict[LiquidClassRecord, str]
24
+
25
+
26
+ class LiquidClassStore(HasState[LiquidClassState], HandlesActions):
27
+ """Container for LiquidClassState."""
28
+
29
+ _state: LiquidClassState
30
+
31
+ def __init__(self) -> None:
32
+ self._state = LiquidClassState(
33
+ liquid_class_record_by_id={},
34
+ liquid_class_record_to_id={},
35
+ )
36
+
37
+ def handle_action(self, action: Action) -> None:
38
+ """Update the state in response to the action."""
39
+ for state_update in get_state_updates(action):
40
+ if state_update.liquid_class_loaded != update_types.NO_CHANGE:
41
+ self._handle_liquid_class_loaded_update(
42
+ state_update.liquid_class_loaded
43
+ )
44
+
45
+ def _handle_liquid_class_loaded_update(
46
+ self, state_update: update_types.LiquidClassLoadedUpdate
47
+ ) -> None:
48
+ # We're just a data store. All the validation and ID generation happens in the command implementation.
49
+ self._state.liquid_class_record_by_id[
50
+ state_update.liquid_class_id
51
+ ] = state_update.liquid_class_record
52
+ self._state.liquid_class_record_to_id[
53
+ state_update.liquid_class_record
54
+ ] = state_update.liquid_class_id
55
+
56
+
57
+ class LiquidClassView:
58
+ """Read-only view of the LiquidClassState."""
59
+
60
+ _state: LiquidClassState
61
+
62
+ def __init__(self, state: LiquidClassState) -> None:
63
+ self._state = state
64
+
65
+ def get(self, liquid_class_id: str) -> LiquidClassRecord:
66
+ """Get the LiquidClassRecord with the given identifier."""
67
+ try:
68
+ return self._state.liquid_class_record_by_id[liquid_class_id]
69
+ except KeyError as e:
70
+ raise errors.LiquidClassDoesNotExistError(
71
+ f"Liquid class ID {liquid_class_id} not found."
72
+ ) from e
73
+
74
+ def get_id_for_liquid_class_record(
75
+ self, liquid_class_record: LiquidClassRecord
76
+ ) -> Optional[str]:
77
+ """See if the given LiquidClassRecord if already in the store, and if so, return its identifier."""
78
+ return self._state.liquid_class_record_to_id.get(liquid_class_record)
79
+
80
+ def get_all(self) -> Dict[str, LiquidClassRecord]:
81
+ """Get all the LiquidClassRecords in the store."""
82
+ return self._state.liquid_class_record_by_id.copy()
@@ -1,11 +1,11 @@
1
1
  """Basic liquid data state and store."""
2
2
  from dataclasses import dataclass
3
3
  from typing import Dict, List
4
- from opentrons.protocol_engine.types import Liquid
4
+ from opentrons.protocol_engine.types import Liquid, LiquidId
5
5
 
6
6
  from ._abstract_store import HasState, HandlesActions
7
7
  from ..actions import Action, AddLiquidAction
8
- from ..errors import LiquidDoesNotExistError
8
+ from ..errors import LiquidDoesNotExistError, InvalidLiquidError
9
9
 
10
10
 
11
11
  @dataclass
@@ -34,7 +34,7 @@ class LiquidStore(HasState[LiquidState], HandlesActions):
34
34
  self._state.liquids_by_id[action.liquid.id] = action.liquid
35
35
 
36
36
 
37
- class LiquidView(HasState[LiquidState]):
37
+ class LiquidView:
38
38
  """Read-only liquid state view."""
39
39
 
40
40
  _state: LiquidState
@@ -51,11 +51,23 @@ class LiquidView(HasState[LiquidState]):
51
51
  """Get all protocol liquids."""
52
52
  return list(self._state.liquids_by_id.values())
53
53
 
54
- def validate_liquid_id(self, liquid_id: str) -> str:
54
+ def validate_liquid_id(self, liquid_id: LiquidId) -> LiquidId:
55
55
  """Check if liquid_id exists in liquids."""
56
+ is_empty = liquid_id == "EMPTY"
57
+ if is_empty:
58
+ return liquid_id
56
59
  has_liquid = liquid_id in self._state.liquids_by_id
57
60
  if not has_liquid:
58
61
  raise LiquidDoesNotExistError(
59
62
  f"Supplied liquidId: {liquid_id} does not exist in the loaded liquids."
60
63
  )
61
64
  return liquid_id
65
+
66
+ def validate_liquid_allowed(self, liquid: Liquid) -> Liquid:
67
+ """Validate that a liquid is legal to load."""
68
+ is_empty = liquid.id == "EMPTY"
69
+ if is_empty:
70
+ raise InvalidLiquidError(
71
+ message='Protocols may not define a liquid with the special id "EMPTY".'
72
+ )
73
+ return liquid
@@ -35,6 +35,7 @@ from opentrons.protocol_engine.state.module_substates.absorbance_reader_substate
35
35
  AbsorbanceReaderMeasureMode,
36
36
  )
37
37
  from opentrons.types import DeckSlotName, MountType, StagingSlotName
38
+ from .update_types import AbsorbanceReaderStateUpdate
38
39
  from ..errors import ModuleNotConnectedError
39
40
 
40
41
  from ..types import (
@@ -63,7 +64,6 @@ from ..commands import (
63
64
  heater_shaker,
64
65
  temperature_module,
65
66
  thermocycler,
66
- absorbance_reader,
67
67
  )
68
68
  from ..actions import (
69
69
  Action,
@@ -296,40 +296,10 @@ class ModuleStore(HasState[ModuleState], HandlesActions):
296
296
  ):
297
297
  self._handle_thermocycler_module_commands(command)
298
298
 
299
- if isinstance(
300
- command.result,
301
- (
302
- absorbance_reader.InitializeResult,
303
- absorbance_reader.ReadAbsorbanceResult,
304
- ),
305
- ):
306
- self._handle_absorbance_reader_commands(command)
307
-
308
299
  def _handle_state_update(self, state_update: update_types.StateUpdate) -> None:
309
- if state_update.absorbance_reader_lid != update_types.NO_CHANGE:
310
- module_id = state_update.absorbance_reader_lid.module_id
311
- is_lid_on = state_update.absorbance_reader_lid.is_lid_on
312
-
313
- # Get current values:
314
- absorbance_reader_substate = self._state.substate_by_module_id[module_id]
315
- assert isinstance(
316
- absorbance_reader_substate, AbsorbanceReaderSubState
317
- ), f"{module_id} is not an absorbance plate reader."
318
- configured = absorbance_reader_substate.configured
319
- measure_mode = absorbance_reader_substate.measure_mode
320
- configured_wavelengths = absorbance_reader_substate.configured_wavelengths
321
- reference_wavelength = absorbance_reader_substate.reference_wavelength
322
- data = absorbance_reader_substate.data
323
-
324
- self._state.substate_by_module_id[module_id] = AbsorbanceReaderSubState(
325
- module_id=AbsorbanceReaderId(module_id),
326
- configured=configured,
327
- measured=True,
328
- is_lid_on=is_lid_on,
329
- measure_mode=measure_mode,
330
- configured_wavelengths=configured_wavelengths,
331
- reference_wavelength=reference_wavelength,
332
- data=data,
300
+ if state_update.absorbance_reader_state_update != update_types.NO_CHANGE:
301
+ self._handle_absorbance_reader_commands(
302
+ state_update.absorbance_reader_state_update
333
303
  )
334
304
 
335
305
  def _add_module_substate(
@@ -589,50 +559,61 @@ class ModuleStore(HasState[ModuleState], HandlesActions):
589
559
  )
590
560
 
591
561
  def _handle_absorbance_reader_commands(
592
- self,
593
- command: Union[
594
- absorbance_reader.Initialize,
595
- absorbance_reader.ReadAbsorbance,
596
- ],
562
+ self, absorbance_reader_state_update: AbsorbanceReaderStateUpdate
597
563
  ) -> None:
598
- module_id = command.params.moduleId
564
+ # Get current values:
565
+ module_id = absorbance_reader_state_update.module_id
599
566
  absorbance_reader_substate = self._state.substate_by_module_id[module_id]
600
567
  assert isinstance(
601
568
  absorbance_reader_substate, AbsorbanceReaderSubState
602
569
  ), f"{module_id} is not an absorbance plate reader."
603
-
604
- # Get current values
570
+ is_lid_on = absorbance_reader_substate.is_lid_on
571
+ measured = True
605
572
  configured = absorbance_reader_substate.configured
606
573
  measure_mode = absorbance_reader_substate.measure_mode
607
574
  configured_wavelengths = absorbance_reader_substate.configured_wavelengths
608
575
  reference_wavelength = absorbance_reader_substate.reference_wavelength
609
- is_lid_on = absorbance_reader_substate.is_lid_on
610
-
611
- if isinstance(command.result, absorbance_reader.InitializeResult):
612
- self._state.substate_by_module_id[module_id] = AbsorbanceReaderSubState(
613
- module_id=AbsorbanceReaderId(module_id),
614
- configured=True,
615
- measured=False,
616
- is_lid_on=is_lid_on,
617
- measure_mode=AbsorbanceReaderMeasureMode(command.params.measureMode),
618
- configured_wavelengths=command.params.sampleWavelengths,
619
- reference_wavelength=command.params.referenceWavelength,
620
- data=None,
576
+ data = absorbance_reader_substate.data
577
+ if (
578
+ absorbance_reader_state_update.absorbance_reader_lid
579
+ != update_types.NO_CHANGE
580
+ ):
581
+ is_lid_on = absorbance_reader_state_update.absorbance_reader_lid.is_lid_on
582
+ elif (
583
+ absorbance_reader_state_update.initialize_absorbance_reader_update
584
+ != update_types.NO_CHANGE
585
+ ):
586
+ configured = True
587
+ measured = False
588
+ is_lid_on = is_lid_on
589
+ measure_mode = AbsorbanceReaderMeasureMode(
590
+ absorbance_reader_state_update.initialize_absorbance_reader_update.measure_mode
621
591
  )
622
- elif isinstance(command.result, absorbance_reader.ReadAbsorbanceResult):
623
- self._state.substate_by_module_id[module_id] = AbsorbanceReaderSubState(
624
- module_id=AbsorbanceReaderId(module_id),
625
- configured=configured,
626
- measured=True,
627
- is_lid_on=is_lid_on,
628
- measure_mode=measure_mode,
629
- configured_wavelengths=configured_wavelengths,
630
- reference_wavelength=reference_wavelength,
631
- data=command.result.data,
592
+ configured_wavelengths = (
593
+ absorbance_reader_state_update.initialize_absorbance_reader_update.sample_wave_lengths
594
+ )
595
+ reference_wavelength = (
596
+ absorbance_reader_state_update.initialize_absorbance_reader_update.reference_wave_length
632
597
  )
598
+ data = None
599
+ elif (
600
+ absorbance_reader_state_update.absorbance_reader_data
601
+ != update_types.NO_CHANGE
602
+ ):
603
+ data = absorbance_reader_state_update.absorbance_reader_data.read_result
604
+ self._state.substate_by_module_id[module_id] = AbsorbanceReaderSubState(
605
+ module_id=AbsorbanceReaderId(module_id),
606
+ configured=configured,
607
+ measured=measured,
608
+ is_lid_on=is_lid_on,
609
+ measure_mode=measure_mode,
610
+ configured_wavelengths=configured_wavelengths,
611
+ reference_wavelength=reference_wavelength,
612
+ data=data,
613
+ )
633
614
 
634
615
 
635
- class ModuleView(HasState[ModuleState]):
616
+ class ModuleView:
636
617
  """Read-only view of computed module state."""
637
618
 
638
619
  _state: ModuleState
@@ -654,7 +635,7 @@ class ModuleView(HasState[ModuleState]):
654
635
  DeckSlotLocation(slotName=slot_name) if slot_name is not None else None
655
636
  )
656
637
 
657
- return LoadedModule.construct(
638
+ return LoadedModule.model_construct(
658
639
  id=module_id,
659
640
  location=location,
660
641
  model=attached_module.definition.model,
@@ -860,8 +841,8 @@ class ModuleView(HasState[ModuleState]):
860
841
  Labware Position Check offset.
861
842
  """
862
843
  if (
863
- self.state.deck_type == DeckType.OT2_STANDARD
864
- or self.state.deck_type == DeckType.OT2_SHORT_TRASH
844
+ self._state.deck_type == DeckType.OT2_STANDARD
845
+ or self._state.deck_type == DeckType.OT2_SHORT_TRASH
865
846
  ):
866
847
  definition = self.get_definition(module_id)
867
848
  slot = self.get_location(module_id).slotName.id
@@ -908,7 +889,7 @@ class ModuleView(HasState[ModuleState]):
908
889
  "Module location invalid for nominal module offset calculation."
909
890
  )
910
891
  module_addressable_area = self.ensure_and_convert_module_fixture_location(
911
- location, self.state.deck_type, module.model
892
+ location, module.model
912
893
  )
913
894
  module_addressable_area_position = (
914
895
  addressable_areas.get_addressable_area_offsets_from_cutout(
@@ -1281,13 +1262,14 @@ class ModuleView(HasState[ModuleState]):
1281
1262
  def ensure_and_convert_module_fixture_location(
1282
1263
  self,
1283
1264
  deck_slot: DeckSlotName,
1284
- deck_type: DeckType,
1285
1265
  model: ModuleModel,
1286
1266
  ) -> str:
1287
1267
  """Ensure module fixture load location is valid.
1288
1268
 
1289
1269
  Also, convert the deck slot to a valid module fixture addressable area.
1290
1270
  """
1271
+ deck_type = self._state.deck_type
1272
+
1291
1273
  if deck_type == DeckType.OT2_STANDARD or deck_type == DeckType.OT2_SHORT_TRASH:
1292
1274
  raise ValueError(
1293
1275
  f"Invalid Deck Type: {deck_type.name} - Does not support modules as fixtures."
@@ -327,6 +327,7 @@ class MotionView:
327
327
  labware_id: str,
328
328
  well_name: str,
329
329
  center_point: Point,
330
+ mm_from_edge: float = 0,
330
331
  radius: float = 1.0,
331
332
  ) -> List[motion_planning.Waypoint]:
332
333
  """Get a list of touch points for a touch tip operation."""
@@ -346,7 +347,11 @@ class MotionView:
346
347
  )
347
348
 
348
349
  positions = _move_types.get_edge_point_list(
349
- center_point, x_offset, y_offset, edge_path_type
350
+ center=center_point,
351
+ x_radius=x_offset,
352
+ y_radius=y_offset,
353
+ mm_from_edge=mm_from_edge,
354
+ edge_path_type=edge_path_type,
350
355
  )
351
356
  critical_point: Optional[CriticalPoint] = None
352
357