opentrons 8.1.0a0__py2.py3-none-any.whl → 8.2.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.

Potentially problematic release.


This version of opentrons might be problematic. Click here for more details.

Files changed (230) hide show
  1. opentrons/cli/analyze.py +71 -7
  2. opentrons/config/__init__.py +9 -0
  3. opentrons/config/advanced_settings.py +22 -0
  4. opentrons/config/defaults_ot3.py +14 -36
  5. opentrons/config/feature_flags.py +4 -0
  6. opentrons/config/types.py +6 -17
  7. opentrons/drivers/absorbance_reader/abstract.py +27 -3
  8. opentrons/drivers/absorbance_reader/async_byonoy.py +207 -154
  9. opentrons/drivers/absorbance_reader/driver.py +24 -15
  10. opentrons/drivers/absorbance_reader/hid_protocol.py +79 -50
  11. opentrons/drivers/absorbance_reader/simulator.py +32 -6
  12. opentrons/drivers/types.py +23 -1
  13. opentrons/execute.py +2 -2
  14. opentrons/hardware_control/api.py +18 -10
  15. opentrons/hardware_control/backends/controller.py +3 -2
  16. opentrons/hardware_control/backends/flex_protocol.py +11 -5
  17. opentrons/hardware_control/backends/ot3controller.py +18 -50
  18. opentrons/hardware_control/backends/ot3simulator.py +7 -6
  19. opentrons/hardware_control/instruments/ot2/pipette_handler.py +22 -82
  20. opentrons/hardware_control/instruments/ot3/pipette_handler.py +10 -2
  21. opentrons/hardware_control/module_control.py +43 -2
  22. opentrons/hardware_control/modules/__init__.py +7 -1
  23. opentrons/hardware_control/modules/absorbance_reader.py +230 -83
  24. opentrons/hardware_control/modules/errors.py +7 -0
  25. opentrons/hardware_control/modules/heater_shaker.py +8 -3
  26. opentrons/hardware_control/modules/magdeck.py +12 -3
  27. opentrons/hardware_control/modules/mod_abc.py +27 -2
  28. opentrons/hardware_control/modules/tempdeck.py +15 -7
  29. opentrons/hardware_control/modules/thermocycler.py +69 -3
  30. opentrons/hardware_control/modules/types.py +11 -5
  31. opentrons/hardware_control/modules/update.py +11 -5
  32. opentrons/hardware_control/modules/utils.py +3 -1
  33. opentrons/hardware_control/ot3_calibration.py +6 -6
  34. opentrons/hardware_control/ot3api.py +126 -89
  35. opentrons/hardware_control/poller.py +15 -11
  36. opentrons/hardware_control/protocols/__init__.py +1 -7
  37. opentrons/hardware_control/protocols/instrument_configurer.py +14 -2
  38. opentrons/hardware_control/protocols/liquid_handler.py +5 -0
  39. opentrons/motion_planning/__init__.py +2 -0
  40. opentrons/motion_planning/waypoints.py +32 -0
  41. opentrons/protocol_api/__init__.py +2 -1
  42. opentrons/protocol_api/_liquid.py +87 -1
  43. opentrons/protocol_api/_parameter_context.py +10 -1
  44. opentrons/protocol_api/core/engine/deck_conflict.py +0 -297
  45. opentrons/protocol_api/core/engine/instrument.py +29 -25
  46. opentrons/protocol_api/core/engine/labware.py +10 -2
  47. opentrons/protocol_api/core/engine/module_core.py +129 -17
  48. opentrons/protocol_api/core/engine/pipette_movement_conflict.py +355 -0
  49. opentrons/protocol_api/core/engine/protocol.py +55 -2
  50. opentrons/protocol_api/core/instrument.py +2 -0
  51. opentrons/protocol_api/core/legacy/legacy_instrument_core.py +2 -0
  52. opentrons/protocol_api/core/legacy/legacy_protocol_core.py +5 -2
  53. opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +2 -0
  54. opentrons/protocol_api/core/module.py +22 -4
  55. opentrons/protocol_api/core/protocol.py +5 -2
  56. opentrons/protocol_api/instrument_context.py +52 -20
  57. opentrons/protocol_api/labware.py +13 -1
  58. opentrons/protocol_api/module_contexts.py +68 -13
  59. opentrons/protocol_api/protocol_context.py +38 -4
  60. opentrons/protocol_api/validation.py +5 -3
  61. opentrons/protocol_engine/__init__.py +10 -9
  62. opentrons/protocol_engine/actions/__init__.py +5 -0
  63. opentrons/protocol_engine/actions/actions.py +42 -25
  64. opentrons/protocol_engine/actions/get_state_update.py +38 -0
  65. opentrons/protocol_engine/clients/sync_client.py +7 -1
  66. opentrons/protocol_engine/clients/transports.py +1 -1
  67. opentrons/protocol_engine/commands/__init__.py +0 -4
  68. opentrons/protocol_engine/commands/absorbance_reader/__init__.py +41 -11
  69. opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +161 -0
  70. opentrons/protocol_engine/commands/absorbance_reader/initialize.py +53 -9
  71. opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +160 -0
  72. opentrons/protocol_engine/commands/absorbance_reader/read.py +196 -0
  73. opentrons/protocol_engine/commands/aspirate.py +29 -16
  74. opentrons/protocol_engine/commands/aspirate_in_place.py +32 -15
  75. opentrons/protocol_engine/commands/blow_out.py +63 -14
  76. opentrons/protocol_engine/commands/blow_out_in_place.py +55 -13
  77. opentrons/protocol_engine/commands/calibration/calibrate_gripper.py +2 -5
  78. opentrons/protocol_engine/commands/calibration/calibrate_module.py +3 -4
  79. opentrons/protocol_engine/commands/calibration/calibrate_pipette.py +2 -5
  80. opentrons/protocol_engine/commands/calibration/move_to_maintenance_position.py +6 -4
  81. opentrons/protocol_engine/commands/command.py +28 -17
  82. opentrons/protocol_engine/commands/command_unions.py +37 -24
  83. opentrons/protocol_engine/commands/comment.py +5 -3
  84. opentrons/protocol_engine/commands/configure_for_volume.py +11 -14
  85. opentrons/protocol_engine/commands/configure_nozzle_layout.py +9 -15
  86. opentrons/protocol_engine/commands/custom.py +5 -3
  87. opentrons/protocol_engine/commands/dispense.py +42 -20
  88. opentrons/protocol_engine/commands/dispense_in_place.py +32 -14
  89. opentrons/protocol_engine/commands/drop_tip.py +68 -15
  90. opentrons/protocol_engine/commands/drop_tip_in_place.py +52 -11
  91. opentrons/protocol_engine/commands/get_tip_presence.py +5 -3
  92. opentrons/protocol_engine/commands/heater_shaker/close_labware_latch.py +6 -6
  93. opentrons/protocol_engine/commands/heater_shaker/deactivate_heater.py +6 -6
  94. opentrons/protocol_engine/commands/heater_shaker/deactivate_shaker.py +6 -6
  95. opentrons/protocol_engine/commands/heater_shaker/open_labware_latch.py +8 -6
  96. opentrons/protocol_engine/commands/heater_shaker/set_and_wait_for_shake_speed.py +8 -4
  97. opentrons/protocol_engine/commands/heater_shaker/set_target_temperature.py +6 -4
  98. opentrons/protocol_engine/commands/heater_shaker/wait_for_temperature.py +6 -6
  99. opentrons/protocol_engine/commands/home.py +11 -5
  100. opentrons/protocol_engine/commands/liquid_probe.py +146 -88
  101. opentrons/protocol_engine/commands/load_labware.py +19 -5
  102. opentrons/protocol_engine/commands/load_liquid.py +18 -7
  103. opentrons/protocol_engine/commands/load_module.py +43 -6
  104. opentrons/protocol_engine/commands/load_pipette.py +18 -17
  105. opentrons/protocol_engine/commands/magnetic_module/disengage.py +6 -6
  106. opentrons/protocol_engine/commands/magnetic_module/engage.py +6 -4
  107. opentrons/protocol_engine/commands/move_labware.py +106 -19
  108. opentrons/protocol_engine/commands/move_relative.py +15 -3
  109. opentrons/protocol_engine/commands/move_to_addressable_area.py +29 -4
  110. opentrons/protocol_engine/commands/move_to_addressable_area_for_drop_tip.py +13 -4
  111. opentrons/protocol_engine/commands/move_to_coordinates.py +11 -5
  112. opentrons/protocol_engine/commands/move_to_well.py +37 -10
  113. opentrons/protocol_engine/commands/pick_up_tip.py +50 -29
  114. opentrons/protocol_engine/commands/pipetting_common.py +39 -15
  115. opentrons/protocol_engine/commands/prepare_to_aspirate.py +62 -15
  116. opentrons/protocol_engine/commands/reload_labware.py +13 -4
  117. opentrons/protocol_engine/commands/retract_axis.py +6 -3
  118. opentrons/protocol_engine/commands/save_position.py +2 -3
  119. opentrons/protocol_engine/commands/set_rail_lights.py +5 -3
  120. opentrons/protocol_engine/commands/set_status_bar.py +5 -3
  121. opentrons/protocol_engine/commands/temperature_module/deactivate.py +6 -4
  122. opentrons/protocol_engine/commands/temperature_module/set_target_temperature.py +3 -4
  123. opentrons/protocol_engine/commands/temperature_module/wait_for_temperature.py +6 -6
  124. opentrons/protocol_engine/commands/thermocycler/__init__.py +19 -0
  125. opentrons/protocol_engine/commands/thermocycler/close_lid.py +8 -8
  126. opentrons/protocol_engine/commands/thermocycler/deactivate_block.py +6 -4
  127. opentrons/protocol_engine/commands/thermocycler/deactivate_lid.py +6 -4
  128. opentrons/protocol_engine/commands/thermocycler/open_lid.py +8 -4
  129. opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py +165 -0
  130. opentrons/protocol_engine/commands/thermocycler/run_profile.py +6 -6
  131. opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py +3 -4
  132. opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py +3 -4
  133. opentrons/protocol_engine/commands/thermocycler/wait_for_block_temperature.py +6 -4
  134. opentrons/protocol_engine/commands/thermocycler/wait_for_lid_temperature.py +6 -4
  135. opentrons/protocol_engine/commands/touch_tip.py +19 -7
  136. opentrons/protocol_engine/commands/unsafe/__init__.py +30 -0
  137. opentrons/protocol_engine/commands/unsafe/unsafe_blow_out_in_place.py +6 -4
  138. opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +12 -4
  139. opentrons/protocol_engine/commands/unsafe/unsafe_engage_axes.py +5 -3
  140. opentrons/protocol_engine/commands/unsafe/unsafe_place_labware.py +194 -0
  141. opentrons/protocol_engine/commands/unsafe/unsafe_ungrip_labware.py +75 -0
  142. opentrons/protocol_engine/commands/unsafe/update_position_estimators.py +5 -3
  143. opentrons/protocol_engine/commands/verify_tip_presence.py +5 -5
  144. opentrons/protocol_engine/commands/wait_for_duration.py +5 -3
  145. opentrons/protocol_engine/commands/wait_for_resume.py +5 -3
  146. opentrons/protocol_engine/create_protocol_engine.py +41 -8
  147. opentrons/protocol_engine/engine_support.py +2 -1
  148. opentrons/protocol_engine/error_recovery_policy.py +14 -3
  149. opentrons/protocol_engine/errors/__init__.py +18 -0
  150. opentrons/protocol_engine/errors/exceptions.py +114 -2
  151. opentrons/protocol_engine/execution/__init__.py +2 -0
  152. opentrons/protocol_engine/execution/command_executor.py +22 -13
  153. opentrons/protocol_engine/execution/create_queue_worker.py +5 -1
  154. opentrons/protocol_engine/execution/door_watcher.py +1 -1
  155. opentrons/protocol_engine/execution/equipment.py +2 -1
  156. opentrons/protocol_engine/execution/error_recovery_hardware_state_synchronizer.py +101 -0
  157. opentrons/protocol_engine/execution/gantry_mover.py +4 -2
  158. opentrons/protocol_engine/execution/hardware_stopper.py +3 -3
  159. opentrons/protocol_engine/execution/heater_shaker_movement_flagger.py +1 -4
  160. opentrons/protocol_engine/execution/labware_movement.py +6 -3
  161. opentrons/protocol_engine/execution/movement.py +8 -3
  162. opentrons/protocol_engine/execution/pipetting.py +7 -4
  163. opentrons/protocol_engine/execution/queue_worker.py +6 -2
  164. opentrons/protocol_engine/execution/run_control.py +1 -1
  165. opentrons/protocol_engine/execution/thermocycler_movement_flagger.py +1 -1
  166. opentrons/protocol_engine/execution/thermocycler_plate_lifter.py +2 -1
  167. opentrons/protocol_engine/execution/tip_handler.py +77 -43
  168. opentrons/protocol_engine/notes/__init__.py +14 -2
  169. opentrons/protocol_engine/notes/notes.py +18 -1
  170. opentrons/protocol_engine/plugins.py +1 -1
  171. opentrons/protocol_engine/protocol_engine.py +54 -31
  172. opentrons/protocol_engine/resources/__init__.py +2 -0
  173. opentrons/protocol_engine/resources/deck_data_provider.py +58 -5
  174. opentrons/protocol_engine/resources/file_provider.py +157 -0
  175. opentrons/protocol_engine/resources/fixture_validation.py +5 -0
  176. opentrons/protocol_engine/resources/labware_validation.py +10 -0
  177. opentrons/protocol_engine/state/__init__.py +0 -70
  178. opentrons/protocol_engine/state/addressable_areas.py +1 -1
  179. opentrons/protocol_engine/state/command_history.py +21 -2
  180. opentrons/protocol_engine/state/commands.py +110 -31
  181. opentrons/protocol_engine/state/files.py +59 -0
  182. opentrons/protocol_engine/state/frustum_helpers.py +440 -0
  183. opentrons/protocol_engine/state/geometry.py +359 -15
  184. opentrons/protocol_engine/state/labware.py +166 -63
  185. opentrons/protocol_engine/state/liquids.py +1 -1
  186. opentrons/protocol_engine/state/module_substates/absorbance_reader_substate.py +19 -3
  187. opentrons/protocol_engine/state/modules.py +167 -85
  188. opentrons/protocol_engine/state/motion.py +16 -9
  189. opentrons/protocol_engine/state/pipettes.py +157 -317
  190. opentrons/protocol_engine/state/state.py +30 -1
  191. opentrons/protocol_engine/state/state_summary.py +3 -0
  192. opentrons/protocol_engine/state/tips.py +69 -114
  193. opentrons/protocol_engine/state/update_types.py +408 -0
  194. opentrons/protocol_engine/state/wells.py +236 -0
  195. opentrons/protocol_engine/types.py +90 -0
  196. opentrons/protocol_reader/file_format_validator.py +83 -15
  197. opentrons/protocol_runner/json_translator.py +21 -5
  198. opentrons/protocol_runner/legacy_command_mapper.py +27 -6
  199. opentrons/protocol_runner/legacy_context_plugin.py +27 -71
  200. opentrons/protocol_runner/protocol_runner.py +6 -3
  201. opentrons/protocol_runner/run_orchestrator.py +26 -6
  202. opentrons/protocols/advanced_control/mix.py +3 -5
  203. opentrons/protocols/advanced_control/transfers.py +125 -56
  204. opentrons/protocols/api_support/constants.py +1 -1
  205. opentrons/protocols/api_support/definitions.py +1 -1
  206. opentrons/protocols/api_support/labware_like.py +4 -4
  207. opentrons/protocols/api_support/tip_tracker.py +2 -2
  208. opentrons/protocols/api_support/types.py +15 -2
  209. opentrons/protocols/api_support/util.py +30 -42
  210. opentrons/protocols/duration/errors.py +1 -1
  211. opentrons/protocols/duration/estimator.py +50 -29
  212. opentrons/protocols/execution/dev_types.py +2 -2
  213. opentrons/protocols/execution/execute_json_v4.py +15 -10
  214. opentrons/protocols/execution/execute_python.py +8 -3
  215. opentrons/protocols/geometry/planning.py +12 -12
  216. opentrons/protocols/labware.py +17 -33
  217. opentrons/simulate.py +3 -3
  218. opentrons/types.py +30 -3
  219. opentrons/util/logging_config.py +34 -0
  220. {opentrons-8.1.0a0.dist-info → opentrons-8.2.0a0.dist-info}/METADATA +5 -4
  221. {opentrons-8.1.0a0.dist-info → opentrons-8.2.0a0.dist-info}/RECORD +227 -215
  222. opentrons/protocol_engine/commands/absorbance_reader/measure.py +0 -94
  223. opentrons/protocol_engine/commands/configuring_common.py +0 -26
  224. opentrons/protocol_runner/thread_async_queue.py +0 -174
  225. /opentrons/protocol_engine/state/{abstract_store.py → _abstract_store.py} +0 -0
  226. /opentrons/protocol_engine/state/{move_types.py → _move_types.py} +0 -0
  227. {opentrons-8.1.0a0.dist-info → opentrons-8.2.0a0.dist-info}/LICENSE +0 -0
  228. {opentrons-8.1.0a0.dist-info → opentrons-8.2.0a0.dist-info}/WHEEL +0 -0
  229. {opentrons-8.1.0a0.dist-info → opentrons-8.2.0a0.dist-info}/entry_points.txt +0 -0
  230. {opentrons-8.1.0a0.dist-info → opentrons-8.2.0a0.dist-info}/top_level.txt +0 -0
@@ -24,10 +24,14 @@ from opentrons.hardware_control.modules.types import LiveData
24
24
  from opentrons.motion_planning.adjacent_slots_getters import (
25
25
  get_east_slot,
26
26
  get_west_slot,
27
+ get_adjacent_staging_slot,
27
28
  )
28
29
  from opentrons.protocol_engine.commands.calibration.calibrate_module import (
29
30
  CalibrateModuleResult,
30
31
  )
32
+ from opentrons.protocol_engine.state.module_substates.absorbance_reader_substate import (
33
+ AbsorbanceReaderMeasureMode,
34
+ )
31
35
  from opentrons.types import DeckSlotName, MountType
32
36
  from ..errors import ModuleNotConnectedError
33
37
 
@@ -45,7 +49,10 @@ from ..types import (
45
49
  HeaterShakerMovementRestrictors,
46
50
  DeckType,
47
51
  LabwareMovementOffsetData,
52
+ AddressableAreaLocation,
48
53
  )
54
+
55
+ from ..resources import DeckFixedLabware
49
56
  from .addressable_areas import AddressableAreaView
50
57
  from .. import errors
51
58
  from ..commands import (
@@ -56,8 +63,13 @@ from ..commands import (
56
63
  thermocycler,
57
64
  absorbance_reader,
58
65
  )
59
- from ..actions import Action, SucceedCommandAction, AddModuleAction
60
- from .abstract_store import HasState, HandlesActions
66
+ from ..actions import (
67
+ Action,
68
+ SucceedCommandAction,
69
+ AddModuleAction,
70
+ AddAbsorbanceReaderLidAction,
71
+ )
72
+ from ._abstract_store import HasState, HandlesActions
61
73
  from .module_substates import (
62
74
  MagneticModuleSubState,
63
75
  HeaterShakerModuleSubState,
@@ -174,6 +186,15 @@ class ModuleState:
174
186
  deck_type: DeckType
175
187
  """Type of deck that the modules are on."""
176
188
 
189
+ deck_fixed_labware: Sequence[DeckFixedLabware]
190
+ """Fixed labware from the deck which may be assigned to a module.
191
+
192
+ The Opentrons Plate Reader module makes use of an electronic Lid labware which moves
193
+ between the Reader and Dock positions, and is pre-loaded into the engine as to persist
194
+ even when not in use. For this reason, we inject it here when an appropriate match
195
+ is identified.
196
+ """
197
+
177
198
 
178
199
  class ModuleStore(HasState[ModuleState], HandlesActions):
179
200
  """Module state container."""
@@ -183,6 +204,7 @@ class ModuleStore(HasState[ModuleState], HandlesActions):
183
204
  def __init__(
184
205
  self,
185
206
  config: Config,
207
+ deck_fixed_labware: Sequence[DeckFixedLabware],
186
208
  module_calibration_offsets: Optional[Dict[str, ModuleOffsetData]] = None,
187
209
  ) -> None:
188
210
  """Initialize a ModuleStore and its state."""
@@ -194,6 +216,7 @@ class ModuleStore(HasState[ModuleState], HandlesActions):
194
216
  substate_by_module_id={},
195
217
  module_offset_by_serial=module_calibration_offsets or {},
196
218
  deck_type=config.deck_type,
219
+ deck_fixed_labware=deck_fixed_labware,
197
220
  )
198
221
  self._robot_type = config.robot_type
199
222
 
@@ -211,6 +234,11 @@ class ModuleStore(HasState[ModuleState], HandlesActions):
211
234
  requested_model=None,
212
235
  module_live_data=action.module_live_data,
213
236
  )
237
+ elif isinstance(action, AddAbsorbanceReaderLidAction):
238
+ self._update_absorbance_reader_lid_id(
239
+ module_id=action.module_id,
240
+ lid_id=action.lid_id,
241
+ )
214
242
 
215
243
  def _handle_command(self, command: Command) -> None:
216
244
  if isinstance(command.result, LoadModuleResult):
@@ -269,13 +297,38 @@ class ModuleStore(HasState[ModuleState], HandlesActions):
269
297
  if isinstance(
270
298
  command.result,
271
299
  (
300
+ absorbance_reader.CloseLidResult,
301
+ absorbance_reader.OpenLidResult,
272
302
  absorbance_reader.InitializeResult,
273
- absorbance_reader.MeasureAbsorbanceResult,
303
+ absorbance_reader.ReadAbsorbanceResult,
274
304
  ),
275
305
  ):
276
306
  self._handle_absorbance_reader_commands(command)
277
307
 
278
- def _add_module_substate(
308
+ def _update_absorbance_reader_lid_id(
309
+ self,
310
+ module_id: str,
311
+ lid_id: str,
312
+ ) -> None:
313
+ abs_substate = self._state.substate_by_module_id.get(module_id)
314
+ assert isinstance(
315
+ abs_substate, AbsorbanceReaderSubState
316
+ ), f"{module_id} is not an absorbance plate reader."
317
+
318
+ prev_state: AbsorbanceReaderSubState = abs_substate
319
+ self._state.substate_by_module_id[module_id] = AbsorbanceReaderSubState(
320
+ module_id=AbsorbanceReaderId(module_id),
321
+ configured=prev_state.configured,
322
+ measured=prev_state.measured,
323
+ is_lid_on=prev_state.is_lid_on,
324
+ data=prev_state.data,
325
+ measure_mode=prev_state.measure_mode,
326
+ configured_wavelengths=prev_state.configured_wavelengths,
327
+ reference_wavelength=prev_state.reference_wavelength,
328
+ lid_id=lid_id,
329
+ )
330
+
331
+ def _add_module_substate( # noqa: C901
279
332
  self,
280
333
  module_id: str,
281
334
  serial_number: Optional[str],
@@ -334,12 +387,26 @@ class ModuleStore(HasState[ModuleState], HandlesActions):
334
387
  module_id=MagneticBlockId(module_id)
335
388
  )
336
389
  elif ModuleModel.is_absorbance_reader(actual_model):
390
+ lid_labware_id = None
391
+ slot = self._state.slot_by_module_id[module_id]
392
+ if slot is not None:
393
+ reader_addressable_area = f"absorbanceReaderV1{slot.value}"
394
+ for labware in self._state.deck_fixed_labware:
395
+ if labware.location == AddressableAreaLocation(
396
+ addressableAreaName=reader_addressable_area
397
+ ):
398
+ lid_labware_id = labware.labware_id
399
+ break
337
400
  self._state.substate_by_module_id[module_id] = AbsorbanceReaderSubState(
338
401
  module_id=AbsorbanceReaderId(module_id),
339
402
  configured=False,
340
403
  measured=False,
404
+ is_lid_on=True,
341
405
  data=None,
342
- configured_wavelength=None,
406
+ measure_mode=None,
407
+ configured_wavelengths=None,
408
+ reference_wavelength=None,
409
+ lid_id=lid_labware_id,
343
410
  )
344
411
 
345
412
  def _update_additional_slots_occupied_by_thermocycler(
@@ -513,7 +580,6 @@ class ModuleStore(HasState[ModuleState], HandlesActions):
513
580
  target_block_temperature=block_temperature,
514
581
  target_lid_temperature=None,
515
582
  )
516
- # TODO (spp, 2022-08-01): set is_lid_open to False upon lid commands' failure
517
583
  elif isinstance(command.result, thermocycler.OpenLidResult):
518
584
  self._state.substate_by_module_id[module_id] = ThermocyclerModuleSubState(
519
585
  module_id=ThermocyclerModuleId(module_id),
@@ -533,7 +599,9 @@ class ModuleStore(HasState[ModuleState], HandlesActions):
533
599
  self,
534
600
  command: Union[
535
601
  absorbance_reader.Initialize,
536
- absorbance_reader.MeasureAbsorbance,
602
+ absorbance_reader.ReadAbsorbance,
603
+ absorbance_reader.CloseLid,
604
+ absorbance_reader.OpenLid,
537
605
  ],
538
606
  ) -> None:
539
607
  module_id = command.params.moduleId
@@ -544,25 +612,64 @@ class ModuleStore(HasState[ModuleState], HandlesActions):
544
612
 
545
613
  # Get current values
546
614
  configured = absorbance_reader_substate.configured
547
- configured_wavelength = absorbance_reader_substate.configured_wavelength
615
+ measure_mode = absorbance_reader_substate.measure_mode
616
+ configured_wavelengths = absorbance_reader_substate.configured_wavelengths
617
+ reference_wavelength = absorbance_reader_substate.reference_wavelength
618
+ is_lid_on = absorbance_reader_substate.is_lid_on
619
+ lid_id = absorbance_reader_substate.lid_id
620
+ data = absorbance_reader_substate.data
548
621
 
549
622
  if isinstance(command.result, absorbance_reader.InitializeResult):
550
623
  self._state.substate_by_module_id[module_id] = AbsorbanceReaderSubState(
551
624
  module_id=AbsorbanceReaderId(module_id),
552
625
  configured=True,
553
626
  measured=False,
627
+ is_lid_on=is_lid_on,
628
+ lid_id=lid_id,
629
+ measure_mode=AbsorbanceReaderMeasureMode(command.params.measureMode),
630
+ configured_wavelengths=command.params.sampleWavelengths,
631
+ reference_wavelength=command.params.referenceWavelength,
554
632
  data=None,
555
- configured_wavelength=command.params.sampleWavelength,
556
633
  )
557
- elif isinstance(command.result, absorbance_reader.MeasureAbsorbanceResult):
634
+ elif isinstance(command.result, absorbance_reader.ReadAbsorbanceResult):
558
635
  self._state.substate_by_module_id[module_id] = AbsorbanceReaderSubState(
559
636
  module_id=AbsorbanceReaderId(module_id),
560
637
  configured=configured,
561
- configured_wavelength=configured_wavelength,
562
638
  measured=True,
639
+ is_lid_on=is_lid_on,
640
+ lid_id=lid_id,
641
+ measure_mode=measure_mode,
642
+ configured_wavelengths=configured_wavelengths,
643
+ reference_wavelength=reference_wavelength,
563
644
  data=command.result.data,
564
645
  )
565
646
 
647
+ elif isinstance(command.result, absorbance_reader.OpenLidResult):
648
+ self._state.substate_by_module_id[module_id] = AbsorbanceReaderSubState(
649
+ module_id=AbsorbanceReaderId(module_id),
650
+ configured=configured,
651
+ measured=True,
652
+ is_lid_on=False,
653
+ lid_id=lid_id,
654
+ measure_mode=measure_mode,
655
+ configured_wavelengths=configured_wavelengths,
656
+ reference_wavelength=reference_wavelength,
657
+ data=data,
658
+ )
659
+
660
+ elif isinstance(command.result, absorbance_reader.CloseLidResult):
661
+ self._state.substate_by_module_id[module_id] = AbsorbanceReaderSubState(
662
+ module_id=AbsorbanceReaderId(module_id),
663
+ configured=configured,
664
+ measured=True,
665
+ is_lid_on=True,
666
+ lid_id=lid_id,
667
+ measure_mode=measure_mode,
668
+ configured_wavelengths=configured_wavelengths,
669
+ reference_wavelength=reference_wavelength,
670
+ data=data,
671
+ )
672
+
566
673
 
567
674
  class ModuleView(HasState[ModuleState]):
568
675
  """Read-only view of computed module state."""
@@ -711,7 +818,7 @@ class ModuleView(HasState[ModuleState]):
711
818
  return self._get_module_substate(
712
819
  module_id=module_id,
713
820
  expected_type=AbsorbanceReaderSubState,
714
- expected_name="Thermocycler Module",
821
+ expected_name="Absorbance Reader",
715
822
  )
716
823
 
717
824
  def get_location(self, module_id: str) -> DeckSlotLocation:
@@ -1179,6 +1286,25 @@ class ModuleView(HasState[ModuleState]):
1179
1286
  else:
1180
1287
  return False
1181
1288
 
1289
+ def convert_absorbance_reader_data_points(
1290
+ self, data: List[float]
1291
+ ) -> Dict[str, float]:
1292
+ """Return the data from the Absorbance Reader module in a map of wells for each read value."""
1293
+ if len(data) == 96:
1294
+ # We have to reverse the reader values because the Opentrons Absorbance Reader is rotated 180 degrees on the deck
1295
+ data.reverse()
1296
+ well_map: Dict[str, float] = {}
1297
+ for i, value in enumerate(data):
1298
+ row = chr(ord("A") + i // 12) # Convert index to row (A-H)
1299
+ col = (i % 12) + 1 # Convert index to column (1-12)
1300
+ well_key = f"{row}{col}"
1301
+ well_map[well_key] = value
1302
+ return well_map
1303
+ else:
1304
+ raise ValueError(
1305
+ "Only readings of 96 Well labware are supported for conversion to map of values by well."
1306
+ )
1307
+
1182
1308
  def ensure_and_convert_module_fixture_location(
1183
1309
  self,
1184
1310
  deck_slot: DeckSlotName,
@@ -1194,84 +1320,40 @@ class ModuleView(HasState[ModuleState]):
1194
1320
  f"Invalid Deck Type: {deck_type.name} - Does not support modules as fixtures."
1195
1321
  )
1196
1322
 
1323
+ assert deck_slot in DeckSlotName.ot3_slots()
1197
1324
  if model == ModuleModel.MAGNETIC_BLOCK_V1:
1198
- valid_slots = [
1199
- slot
1200
- for slot in [
1201
- "A1",
1202
- "B1",
1203
- "C1",
1204
- "D1",
1205
- "A2",
1206
- "B2",
1207
- "C2",
1208
- "D2",
1209
- "A3",
1210
- "B3",
1211
- "C3",
1212
- "D3",
1213
- ]
1214
- ]
1215
- addressable_areas = [
1216
- "magneticBlockV1A1",
1217
- "magneticBlockV1B1",
1218
- "magneticBlockV1C1",
1219
- "magneticBlockV1D1",
1220
- "magneticBlockV1A2",
1221
- "magneticBlockV1B2",
1222
- "magneticBlockV1C2",
1223
- "magneticBlockV1D2",
1224
- "magneticBlockV1A3",
1225
- "magneticBlockV1B3",
1226
- "magneticBlockV1C3",
1227
- "magneticBlockV1D3",
1228
- ]
1325
+ return f"magneticBlockV1{deck_slot.value}"
1229
1326
 
1230
1327
  elif model == ModuleModel.HEATER_SHAKER_MODULE_V1:
1231
- valid_slots = [
1232
- slot for slot in ["A1", "B1", "C1", "D1", "A3", "B3", "C3", "D3"]
1233
- ]
1234
- addressable_areas = [
1235
- "heaterShakerV1A1",
1236
- "heaterShakerV1B1",
1237
- "heaterShakerV1C1",
1238
- "heaterShakerV1D1",
1239
- "heaterShakerV1A3",
1240
- "heaterShakerV1B3",
1241
- "heaterShakerV1C3",
1242
- "heaterShakerV1D3",
1243
- ]
1328
+ # only allowed in column 1 & 3
1329
+ assert deck_slot.value[-1] in ("1", "3")
1330
+ return f"heaterShakerV1{deck_slot.value}"
1331
+
1244
1332
  elif model == ModuleModel.TEMPERATURE_MODULE_V2:
1245
- valid_slots = [
1246
- slot for slot in ["A1", "B1", "C1", "D1", "A3", "B3", "C3", "D3"]
1247
- ]
1248
- addressable_areas = [
1249
- "temperatureModuleV2A1",
1250
- "temperatureModuleV2B1",
1251
- "temperatureModuleV2C1",
1252
- "temperatureModuleV2D1",
1253
- "temperatureModuleV2A3",
1254
- "temperatureModuleV2B3",
1255
- "temperatureModuleV2C3",
1256
- "temperatureModuleV2D3",
1257
- ]
1333
+ # only allowed in column 1 & 3
1334
+ assert deck_slot.value[-1] in ("1", "3")
1335
+ return f"temperatureModuleV2{deck_slot.value}"
1336
+
1258
1337
  elif model == ModuleModel.THERMOCYCLER_MODULE_V2:
1259
1338
  return "thermocyclerModuleV2"
1339
+
1260
1340
  elif model == ModuleModel.ABSORBANCE_READER_V1:
1261
- valid_slots = ["A3", "B3", "C3", "D3"]
1262
- addressable_areas = [
1263
- "absorbanceReaderV1A3",
1264
- "absorbanceReaderV1B3",
1265
- "absorbanceReaderV1C3",
1266
- "absorbanceReaderV1D3",
1267
- ]
1268
- else:
1269
- raise ValueError(
1270
- f"Unknown module {model.name} has no addressable areas to provide."
1271
- )
1341
+ # only allowed in column 3
1342
+ assert deck_slot.value[-1] == "3"
1343
+ return f"absorbanceReaderV1{deck_slot.value}"
1344
+
1345
+ raise ValueError(
1346
+ f"Unknown module {model.name} has no addressable areas to provide."
1347
+ )
1272
1348
 
1273
- map_addressable_area = {
1274
- slot: addressable_area
1275
- for slot, addressable_area in zip(valid_slots, addressable_areas)
1276
- }
1277
- return map_addressable_area[deck_slot.value]
1349
+ def absorbance_reader_dock_location(
1350
+ self, module_id: str
1351
+ ) -> AddressableAreaLocation:
1352
+ """Get the addressable area for the absorbance reader dock."""
1353
+ reader_slot = self.get_location(module_id)
1354
+ lid_doc_slot = get_adjacent_staging_slot(reader_slot.slotName)
1355
+ assert lid_doc_slot is not None
1356
+ lid_dock_area = AddressableAreaLocation(
1357
+ addressableAreaName="absorbanceReaderV1LidDock" + lid_doc_slot.value
1358
+ )
1359
+ return lid_dock_area
@@ -1,6 +1,6 @@
1
1
  """Motion state store and getters."""
2
2
  from dataclasses import dataclass
3
- from typing import List, Optional
3
+ from typing import List, Optional, Union
4
4
 
5
5
  from opentrons.types import MountType, Point
6
6
  from opentrons.hardware_control.types import CriticalPoint
@@ -10,11 +10,12 @@ from opentrons.motion_planning.adjacent_slots_getters import (
10
10
  )
11
11
  from opentrons import motion_planning
12
12
 
13
- from . import move_types
13
+ from . import _move_types
14
14
  from .. import errors
15
15
  from ..types import (
16
16
  MotorAxis,
17
17
  WellLocation,
18
+ LiquidHandlingWellLocation,
18
19
  CurrentWell,
19
20
  CurrentPipetteLocation,
20
21
  AddressableOffsetVector,
@@ -89,13 +90,14 @@ class MotionView:
89
90
  pipette_id: str,
90
91
  labware_id: str,
91
92
  well_name: str,
92
- well_location: Optional[WellLocation],
93
+ well_location: Optional[Union[WellLocation, LiquidHandlingWellLocation]],
93
94
  origin: Point,
94
95
  origin_cp: Optional[CriticalPoint],
95
96
  max_travel_z: float,
96
97
  current_well: Optional[CurrentWell] = None,
97
98
  force_direct: bool = False,
98
99
  minimum_z_height: Optional[float] = None,
100
+ operation_volume: Optional[float] = None,
99
101
  ) -> List[motion_planning.Waypoint]:
100
102
  """Calculate waypoints to a destination that's specified as a well."""
101
103
  location = current_well or self._pipettes.get_current_location()
@@ -107,12 +109,14 @@ class MotionView:
107
109
  destination_cp = CriticalPoint.XY_CENTER
108
110
 
109
111
  destination = self._geometry.get_well_position(
110
- labware_id,
111
- well_name,
112
- well_location,
112
+ labware_id=labware_id,
113
+ well_name=well_name,
114
+ well_location=well_location,
115
+ operation_volume=operation_volume,
116
+ pipette_id=pipette_id,
113
117
  )
114
118
 
115
- move_type = move_types.get_move_type_to_well(
119
+ move_type = _move_types.get_move_type_to_well(
116
120
  pipette_id, labware_id, well_name, location, force_direct
117
121
  )
118
122
  min_travel_z = self._geometry.get_min_travel_z(
@@ -151,6 +155,7 @@ class MotionView:
151
155
  minimum_z_height: Optional[float] = None,
152
156
  stay_at_max_travel_z: bool = False,
153
157
  ignore_tip_configuration: Optional[bool] = True,
158
+ max_travel_z_extra_margin: Optional[float] = None,
154
159
  ) -> List[motion_planning.Waypoint]:
155
160
  """Calculate waypoints to a destination that's specified as an addressable area."""
156
161
  location = self._pipettes.get_current_location()
@@ -169,7 +174,9 @@ class MotionView:
169
174
  # beneath max_travel_z. Investigate why motion_planning.get_waypoints() does not
170
175
  # let us travel at max_travel_z, and whether it's safe to make it do that.
171
176
  # Possibly related: https://github.com/Opentrons/opentrons/pull/6882#discussion_r514248062
172
- max_travel_z - motion_planning.waypoints.MINIMUM_Z_MARGIN,
177
+ max_travel_z
178
+ - motion_planning.waypoints.MINIMUM_Z_MARGIN
179
+ - (max_travel_z_extra_margin or 0.0),
173
180
  )
174
181
  destination = base_destination_at_max_z + Point(
175
182
  offset.x, offset.y, offset.z
@@ -326,7 +333,7 @@ class MotionView:
326
333
  labware_id, well_name, radius
327
334
  )
328
335
 
329
- positions = move_types.get_edge_point_list(
336
+ positions = _move_types.get_edge_point_list(
330
337
  center_point, x_offset, y_offset, edge_path_type
331
338
  )
332
339
  critical_point: Optional[CriticalPoint] = None