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.
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
@@ -3,7 +3,7 @@
3
3
  Actions can be passed to the ActionDispatcher, where they will trigger
4
4
  reactions in objects that subscribe to the pipeline, like the StateStore.
5
5
  """
6
- from dataclasses import dataclass
6
+ import dataclasses
7
7
  from datetime import datetime
8
8
  from enum import Enum
9
9
  from typing import List, Optional, Union
@@ -18,10 +18,10 @@ from ..commands import (
18
18
  Command,
19
19
  CommandCreate,
20
20
  CommandDefinedErrorData,
21
- CommandPrivateResult,
22
21
  )
23
22
  from ..error_recovery_policy import ErrorRecoveryPolicy, ErrorRecoveryType
24
23
  from ..notes.notes import CommandNote
24
+ from ..state.update_types import StateUpdate
25
25
  from ..types import (
26
26
  LabwareOffsetCreate,
27
27
  ModuleDefinition,
@@ -31,7 +31,7 @@ from ..types import (
31
31
  )
32
32
 
33
33
 
34
- @dataclass(frozen=True)
34
+ @dataclasses.dataclass(frozen=True)
35
35
  class PlayAction:
36
36
  """Start or resume processing commands in the engine."""
37
37
 
@@ -50,28 +50,28 @@ class PauseSource(str, Enum):
50
50
  PROTOCOL = "protocol"
51
51
 
52
52
 
53
- @dataclass(frozen=True)
53
+ @dataclasses.dataclass(frozen=True)
54
54
  class PauseAction:
55
55
  """Pause processing commands in the engine."""
56
56
 
57
57
  source: PauseSource
58
58
 
59
59
 
60
- @dataclass(frozen=True)
60
+ @dataclasses.dataclass(frozen=True)
61
61
  class StopAction:
62
62
  """Request engine execution to stop soon."""
63
63
 
64
64
  from_estop: bool = False
65
65
 
66
66
 
67
- @dataclass(frozen=True)
67
+ @dataclasses.dataclass(frozen=True)
68
68
  class ResumeFromRecoveryAction:
69
69
  """See `ProtocolEngine.resume_from_recovery()`."""
70
70
 
71
- pass
71
+ state_update: StateUpdate
72
72
 
73
73
 
74
- @dataclass(frozen=True)
74
+ @dataclasses.dataclass(frozen=True)
75
75
  class FinishErrorDetails:
76
76
  """Error details for the payload of a FinishAction or HardwareStoppedAction."""
77
77
 
@@ -80,7 +80,7 @@ class FinishErrorDetails:
80
80
  created_at: datetime
81
81
 
82
82
 
83
- @dataclass(frozen=True)
83
+ @dataclasses.dataclass(frozen=True)
84
84
  class FinishAction:
85
85
  """Gracefully stop processing commands in the engine."""
86
86
 
@@ -95,7 +95,7 @@ class FinishAction:
95
95
  """The fatal error that caused the run to fail."""
96
96
 
97
97
 
98
- @dataclass(frozen=True)
98
+ @dataclasses.dataclass(frozen=True)
99
99
  class HardwareStoppedAction:
100
100
  """An action dispatched after hardware has been stopped for good, for this engine instance."""
101
101
 
@@ -105,14 +105,14 @@ class HardwareStoppedAction:
105
105
  """The error that happened while doing post-run finish steps (homing and dropping tips)."""
106
106
 
107
107
 
108
- @dataclass(frozen=True)
108
+ @dataclasses.dataclass(frozen=True)
109
109
  class DoorChangeAction:
110
110
  """Handle events coming in from hardware control."""
111
111
 
112
112
  door_state: DoorState
113
113
 
114
114
 
115
- @dataclass(frozen=True)
115
+ @dataclasses.dataclass(frozen=True)
116
116
  class QueueCommandAction:
117
117
  """Add a command request to the queue."""
118
118
 
@@ -123,7 +123,7 @@ class QueueCommandAction:
123
123
  failed_command_id: Optional[str] = None
124
124
 
125
125
 
126
- @dataclass(frozen=True)
126
+ @dataclasses.dataclass(frozen=True)
127
127
  class RunCommandAction:
128
128
  """Mark a given command as running.
129
129
 
@@ -135,7 +135,7 @@ class RunCommandAction:
135
135
  started_at: datetime
136
136
 
137
137
 
138
- @dataclass(frozen=True)
138
+ @dataclasses.dataclass(frozen=True)
139
139
  class SucceedCommandAction:
140
140
  """Mark a given command as succeeded.
141
141
 
@@ -145,10 +145,15 @@ class SucceedCommandAction:
145
145
  command: Command
146
146
  """The command in its new succeeded state."""
147
147
 
148
- private_result: CommandPrivateResult
148
+ state_update: StateUpdate = dataclasses.field(
149
+ # todo(mm, 2024-08-26): This has a default only to make it easier to transition
150
+ # old tests while https://opentrons.atlassian.net/browse/EXEC-639 is in
151
+ # progress. Make this mandatory when that's completed.
152
+ default_factory=StateUpdate
153
+ )
149
154
 
150
155
 
151
- @dataclass(frozen=True)
156
+ @dataclasses.dataclass(frozen=True)
152
157
  class FailCommandAction:
153
158
  """Mark a given command as failed.
154
159
 
@@ -196,7 +201,7 @@ class FailCommandAction:
196
201
  """The command to fail, in its prior `running` state."""
197
202
 
198
203
 
199
- @dataclass(frozen=True)
204
+ @dataclasses.dataclass(frozen=True)
200
205
  class AddLabwareOffsetAction:
201
206
  """Add a labware offset, to apply to subsequent `LoadLabwareCommand`s."""
202
207
 
@@ -205,28 +210,28 @@ class AddLabwareOffsetAction:
205
210
  request: LabwareOffsetCreate
206
211
 
207
212
 
208
- @dataclass(frozen=True)
213
+ @dataclasses.dataclass(frozen=True)
209
214
  class AddLabwareDefinitionAction:
210
215
  """Add a labware definition, to apply to subsequent `LoadLabwareCommand`s."""
211
216
 
212
217
  definition: LabwareDefinition
213
218
 
214
219
 
215
- @dataclass(frozen=True)
220
+ @dataclasses.dataclass(frozen=True)
216
221
  class AddLiquidAction:
217
222
  """Add a liquid, to apply to subsequent `LoadLiquid`s."""
218
223
 
219
224
  liquid: Liquid
220
225
 
221
226
 
222
- @dataclass(frozen=True)
227
+ @dataclasses.dataclass(frozen=True)
223
228
  class SetDeckConfigurationAction:
224
229
  """See `ProtocolEngine.set_deck_configuration()`."""
225
230
 
226
231
  deck_configuration: Optional[DeckConfigurationType]
227
232
 
228
233
 
229
- @dataclass(frozen=True)
234
+ @dataclasses.dataclass(frozen=True)
230
235
  class AddAddressableAreaAction:
231
236
  """Add a single addressable area to state.
232
237
 
@@ -238,7 +243,7 @@ class AddAddressableAreaAction:
238
243
  addressable_area: AddressableAreaLocation
239
244
 
240
245
 
241
- @dataclass(frozen=True)
246
+ @dataclasses.dataclass(frozen=True)
242
247
  class AddModuleAction:
243
248
  """Add an attached module directly to state without a location."""
244
249
 
@@ -248,14 +253,14 @@ class AddModuleAction:
248
253
  module_live_data: LiveData
249
254
 
250
255
 
251
- @dataclass(frozen=True)
256
+ @dataclasses.dataclass(frozen=True)
252
257
  class ResetTipsAction:
253
258
  """Reset the tip tracking state of a given tip rack."""
254
259
 
255
260
  labware_id: str
256
261
 
257
262
 
258
- @dataclass(frozen=True)
263
+ @dataclasses.dataclass(frozen=True)
259
264
  class SetPipetteMovementSpeedAction:
260
265
  """Set the speed of a pipette's X/Y/Z movements. Does not affect plunger speed.
261
266
 
@@ -266,7 +271,18 @@ class SetPipetteMovementSpeedAction:
266
271
  speed: Optional[float]
267
272
 
268
273
 
269
- @dataclass(frozen=True)
274
+ @dataclasses.dataclass(frozen=True)
275
+ class AddAbsorbanceReaderLidAction:
276
+ """Add the absorbance reader lid id to the absorbance reader module substate.
277
+
278
+ This action is dispatched the absorbance reader module is first loaded.
279
+ """
280
+
281
+ module_id: str
282
+ lid_id: str
283
+
284
+
285
+ @dataclasses.dataclass(frozen=True)
270
286
  class SetErrorRecoveryPolicyAction:
271
287
  """See `ProtocolEngine.set_error_recovery_policy()`."""
272
288
 
@@ -293,5 +309,6 @@ Action = Union[
293
309
  AddLiquidAction,
294
310
  ResetTipsAction,
295
311
  SetPipetteMovementSpeedAction,
312
+ AddAbsorbanceReaderLidAction,
296
313
  SetErrorRecoveryPolicyAction,
297
314
  ]
@@ -0,0 +1,38 @@
1
+ # noqa: D100
2
+ from __future__ import annotations
3
+ from typing import TYPE_CHECKING
4
+
5
+ from .actions import (
6
+ Action,
7
+ ResumeFromRecoveryAction,
8
+ SucceedCommandAction,
9
+ FailCommandAction,
10
+ )
11
+ from ..commands.command import DefinedErrorData
12
+ from ..error_recovery_policy import ErrorRecoveryType
13
+
14
+ if TYPE_CHECKING:
15
+ from ..state.update_types import StateUpdate
16
+
17
+
18
+ def get_state_updates(action: Action) -> list[StateUpdate]:
19
+ """Extract all the StateUpdates that the StateStores should apply when they apply an action."""
20
+ if isinstance(action, SucceedCommandAction):
21
+ return [action.state_update]
22
+
23
+ elif isinstance(action, FailCommandAction) and isinstance(
24
+ action.error, DefinedErrorData
25
+ ):
26
+ if action.type == ErrorRecoveryType.ASSUME_FALSE_POSITIVE_AND_CONTINUE:
27
+ return [
28
+ action.error.state_update,
29
+ action.error.state_update_if_false_positive,
30
+ ]
31
+ else:
32
+ return [action.error.state_update]
33
+
34
+ elif isinstance(action, ResumeFromRecoveryAction):
35
+ return [action.state_update]
36
+
37
+ else:
38
+ return []
@@ -7,7 +7,7 @@ from opentrons_shared_data.labware.labware_definition import LabwareDefinition
7
7
 
8
8
  from .. import commands
9
9
  from ..commands.command_unions import CREATE_TYPES_BY_PARAMS_TYPE
10
- from ..state import StateView
10
+ from ..state.state import StateView
11
11
  from ..types import (
12
12
  Liquid,
13
13
  LabwareOffsetCreate,
@@ -119,6 +119,12 @@ class SyncClient:
119
119
  "add_addressable_area", addressable_area_name=addressable_area_name
120
120
  )
121
121
 
122
+ def add_absorbance_reader_lid(self, module_id: str, lid_id: str) -> None:
123
+ """Add an absorbance reader lid to the module state."""
124
+ self._transport.call_method(
125
+ "add_absorbance_reader_lid", module_id=module_id, lid_id=lid_id
126
+ )
127
+
122
128
  def add_liquid(
123
129
  self, name: str, color: Optional[str], description: Optional[str]
124
130
  ) -> Liquid:
@@ -10,7 +10,7 @@ from opentrons_shared_data.labware.labware_definition import LabwareDefinition
10
10
  from ..protocol_engine import ProtocolEngine
11
11
  from ..errors import ProtocolCommandFailedError
12
12
  from ..error_recovery_policy import ErrorRecoveryType
13
- from ..state import StateView
13
+ from ..state.state import StateView
14
14
  from ..commands import Command, CommandCreate, CommandResult, CommandStatus
15
15
 
16
16
 
@@ -38,7 +38,6 @@ from .command_unions import (
38
38
  CommandCreate,
39
39
  CommandResult,
40
40
  CommandType,
41
- CommandPrivateResult,
42
41
  CommandDefinedErrorData,
43
42
  )
44
43
 
@@ -153,7 +152,6 @@ from .load_pipette import (
153
152
  LoadPipetteCreate,
154
153
  LoadPipetteResult,
155
154
  LoadPipetteCommandType,
156
- LoadPipettePrivateResult,
157
155
  )
158
156
 
159
157
  from .move_labware import (
@@ -306,7 +304,6 @@ from .configure_nozzle_layout import (
306
304
  ConfigureNozzleLayoutCreate,
307
305
  ConfigureNozzleLayoutParams,
308
306
  ConfigureNozzleLayoutResult,
309
- ConfigureNozzleLayoutPrivateResult,
310
307
  ConfigureNozzleLayoutCommandType,
311
308
  )
312
309
 
@@ -569,7 +566,6 @@ __all__ = [
569
566
  "ConfigureNozzleLayoutParams",
570
567
  "ConfigureNozzleLayoutResult",
571
568
  "ConfigureNozzleLayoutCommandType",
572
- "ConfigureNozzleLayoutPrivateResult",
573
569
  # get pipette tip presence bundle
574
570
  "GetTipPresence",
575
571
  "GetTipPresenceCreate",
@@ -1,4 +1,19 @@
1
1
  """Command models for Absorbance Reader commands."""
2
+ from .close_lid import (
3
+ CloseLidCommandType,
4
+ CloseLidParams,
5
+ CloseLidResult,
6
+ CloseLid,
7
+ CloseLidCreate,
8
+ )
9
+
10
+ from .open_lid import (
11
+ OpenLidCommandType,
12
+ OpenLidParams,
13
+ OpenLidResult,
14
+ OpenLid,
15
+ OpenLidCreate,
16
+ )
2
17
 
3
18
  from .initialize import (
4
19
  InitializeCommandType,
@@ -8,15 +23,29 @@ from .initialize import (
8
23
  InitializeCreate,
9
24
  )
10
25
 
11
- from .measure import (
12
- MeasureAbsorbanceCommandType,
13
- MeasureAbsorbanceParams,
14
- MeasureAbsorbanceResult,
15
- MeasureAbsorbance,
16
- MeasureAbsorbanceCreate,
26
+ from .read import (
27
+ ReadAbsorbanceCommandType,
28
+ ReadAbsorbanceParams,
29
+ ReadAbsorbanceResult,
30
+ ReadAbsorbance,
31
+ ReadAbsorbanceCreate,
17
32
  )
18
33
 
34
+
19
35
  __all__ = [
36
+ "MoveLidResult",
37
+ # absorbanace_reader/closeLid
38
+ "CloseLidCommandType",
39
+ "CloseLidParams",
40
+ "CloseLidResult",
41
+ "CloseLid",
42
+ "CloseLidCreate",
43
+ # absorbanace_reader/openLid
44
+ "OpenLidCommandType",
45
+ "OpenLidParams",
46
+ "OpenLidResult",
47
+ "OpenLid",
48
+ "OpenLidCreate",
20
49
  # absorbanace_reader/initialize
21
50
  "InitializeCommandType",
22
51
  "InitializeParams",
@@ -24,9 +53,10 @@ __all__ = [
24
53
  "Initialize",
25
54
  "InitializeCreate",
26
55
  # absorbanace_reader/measure
27
- "MeasureAbsorbanceCommandType",
28
- "MeasureAbsorbanceParams",
29
- "MeasureAbsorbanceResult",
30
- "MeasureAbsorbance",
31
- "MeasureAbsorbanceCreate",
56
+ "ReadAbsorbanceCommandType",
57
+ "ReadAbsorbanceParams",
58
+ "ReadAbsorbanceResult",
59
+ "ReadAbsorbance",
60
+ "ReadAbsorbanceCreate",
61
+ # union type
32
62
  ]
@@ -0,0 +1,161 @@
1
+ """Command models to close the lid on an Absorbance Reader."""
2
+ from __future__ import annotations
3
+ from typing import Optional, Literal, TYPE_CHECKING
4
+ from typing_extensions import Type
5
+
6
+ from pydantic import BaseModel, Field
7
+
8
+ from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
9
+ from ...errors.error_occurrence import ErrorOccurrence
10
+ from ...errors import CannotPerformModuleAction
11
+ from opentrons.protocol_engine.types import AddressableAreaLocation
12
+
13
+ from opentrons.protocol_engine.resources import labware_validation
14
+ from ...state.update_types import StateUpdate
15
+
16
+
17
+ from opentrons.drivers.types import AbsorbanceReaderLidStatus
18
+
19
+ if TYPE_CHECKING:
20
+ from opentrons.protocol_engine.state.state import StateView
21
+ from opentrons.protocol_engine.execution import (
22
+ EquipmentHandler,
23
+ LabwareMovementHandler,
24
+ )
25
+
26
+
27
+ CloseLidCommandType = Literal["absorbanceReader/closeLid"]
28
+
29
+
30
+ class CloseLidParams(BaseModel):
31
+ """Input parameters to close the lid on an absorbance reading."""
32
+
33
+ moduleId: str = Field(..., description="Unique ID of the absorbance reader.")
34
+
35
+
36
+ class CloseLidResult(BaseModel):
37
+ """Result data from closing the lid on an aborbance reading."""
38
+
39
+
40
+ class CloseLidImpl(AbstractCommandImpl[CloseLidParams, SuccessData[CloseLidResult]]):
41
+ """Execution implementation of closing the lid on an Absorbance Reader."""
42
+
43
+ def __init__(
44
+ self,
45
+ state_view: StateView,
46
+ equipment: EquipmentHandler,
47
+ labware_movement: LabwareMovementHandler,
48
+ **unused_dependencies: object,
49
+ ) -> None:
50
+ self._state_view = state_view
51
+ self._equipment = equipment
52
+ self._labware_movement = labware_movement
53
+
54
+ async def execute(self, params: CloseLidParams) -> SuccessData[CloseLidResult]:
55
+ """Execute the close lid command."""
56
+ mod_substate = self._state_view.modules.get_absorbance_reader_substate(
57
+ module_id=params.moduleId
58
+ )
59
+
60
+ # lid should currently be on the module
61
+ assert mod_substate.lid_id is not None
62
+ loaded_lid = self._state_view.labware.get(mod_substate.lid_id)
63
+ assert labware_validation.is_absorbance_reader_lid(loaded_lid.loadName)
64
+
65
+ hardware_lid_status = AbsorbanceReaderLidStatus.OFF
66
+ # If the lid is closed, if the lid is open No-op out
67
+ if not self._state_view.config.use_virtual_modules:
68
+ abs_reader = self._equipment.get_module_hardware_api(mod_substate.module_id)
69
+
70
+ if abs_reader is not None:
71
+ result = await abs_reader.get_current_lid_status()
72
+ hardware_lid_status = result
73
+ else:
74
+ raise CannotPerformModuleAction(
75
+ "Could not reach the Hardware API for Opentrons Plate Reader Module."
76
+ )
77
+
78
+ # If the lid is already ON, no-op losing lid
79
+ if hardware_lid_status is AbsorbanceReaderLidStatus.ON:
80
+ # The lid is already On, so we can no-op and return the lids current location data
81
+ assert isinstance(loaded_lid.location, AddressableAreaLocation)
82
+ new_location = loaded_lid.location
83
+ new_offset_id = self._equipment.find_applicable_labware_offset_id(
84
+ labware_definition_uri=loaded_lid.definitionUri,
85
+ labware_location=loaded_lid.location,
86
+ )
87
+ else:
88
+ # Allow propagation of ModuleNotAttachedError.
89
+ _ = self._equipment.get_module_hardware_api(mod_substate.module_id)
90
+
91
+ current_location = self._state_view.modules.absorbance_reader_dock_location(
92
+ params.moduleId
93
+ )
94
+
95
+ # we need to move the lid onto the module reader
96
+ absorbance_model = self._state_view.modules.get_requested_model(
97
+ params.moduleId
98
+ )
99
+ assert absorbance_model is not None
100
+ new_location = AddressableAreaLocation(
101
+ addressableAreaName=self._state_view.modules.ensure_and_convert_module_fixture_location(
102
+ deck_slot=self._state_view.modules.get_location(
103
+ params.moduleId
104
+ ).slotName,
105
+ deck_type=self._state_view.config.deck_type,
106
+ model=absorbance_model,
107
+ )
108
+ )
109
+
110
+ lid_gripper_offsets = self._state_view.labware.get_labware_gripper_offsets(
111
+ loaded_lid.id, None
112
+ )
113
+ if lid_gripper_offsets is None:
114
+ raise ValueError(
115
+ "Gripper Offset values for Absorbance Reader Lid labware must not be None."
116
+ )
117
+
118
+ # Skips gripper moves when using virtual gripper
119
+ await self._labware_movement.move_labware_with_gripper(
120
+ labware_id=loaded_lid.id,
121
+ current_location=current_location,
122
+ new_location=new_location,
123
+ user_offset_data=lid_gripper_offsets,
124
+ post_drop_slide_offset=None,
125
+ )
126
+
127
+ new_offset_id = self._equipment.find_applicable_labware_offset_id(
128
+ labware_definition_uri=loaded_lid.definitionUri,
129
+ labware_location=new_location,
130
+ )
131
+
132
+ state_update = StateUpdate()
133
+ state_update.set_labware_location(
134
+ labware_id=loaded_lid.id,
135
+ new_location=new_location,
136
+ new_offset_id=new_offset_id,
137
+ )
138
+
139
+ return SuccessData(
140
+ public=CloseLidResult(),
141
+ state_update=state_update,
142
+ )
143
+
144
+
145
+ class CloseLid(BaseCommand[CloseLidParams, CloseLidResult, ErrorOccurrence]):
146
+ """A command to close the lid on an Absorbance Reader."""
147
+
148
+ commandType: CloseLidCommandType = "absorbanceReader/closeLid"
149
+ params: CloseLidParams
150
+ result: Optional[CloseLidResult]
151
+
152
+ _ImplementationCls: Type[CloseLidImpl] = CloseLidImpl
153
+
154
+
155
+ class CloseLidCreate(BaseCommandCreate[CloseLidParams]):
156
+ """A request to execute an Absorbance Reader close lid command."""
157
+
158
+ commandType: CloseLidCommandType = "absorbanceReader/closeLid"
159
+ params: CloseLidParams
160
+
161
+ _CommandCls: Type[CloseLid] = CloseLid
@@ -1,15 +1,18 @@
1
1
  """Command models to initialize an Absorbance Reader."""
2
2
  from __future__ import annotations
3
- from typing import Optional, Literal, TYPE_CHECKING
3
+ from typing import List, Optional, Literal, TYPE_CHECKING
4
4
  from typing_extensions import Type
5
5
 
6
6
  from pydantic import BaseModel, Field
7
7
 
8
+ from opentrons.drivers.types import ABSMeasurementMode
9
+ from opentrons.protocol_engine.types import ABSMeasureMode
10
+
8
11
  from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
9
12
  from ...errors.error_occurrence import ErrorOccurrence
10
13
 
11
14
  if TYPE_CHECKING:
12
- from opentrons.protocol_engine.state import StateView
15
+ from opentrons.protocol_engine.state.state import StateView
13
16
  from opentrons.protocol_engine.execution import EquipmentHandler
14
17
 
15
18
 
@@ -20,7 +23,13 @@ class InitializeParams(BaseModel):
20
23
  """Input parameters to initialize an absorbance reading."""
21
24
 
22
25
  moduleId: str = Field(..., description="Unique ID of the absorbance reader.")
23
- sampleWavelength: int = Field(..., description="Sample wavelength in nm.")
26
+ measureMode: ABSMeasureMode = Field(
27
+ ..., description="Initialize single or multi measurement mode."
28
+ )
29
+ sampleWavelengths: List[int] = Field(..., description="Sample wavelengths in nm.")
30
+ referenceWavelength: Optional[int] = Field(
31
+ None, description="Optional reference wavelength in nm."
32
+ )
24
33
 
25
34
 
26
35
  class InitializeResult(BaseModel):
@@ -28,7 +37,7 @@ class InitializeResult(BaseModel):
28
37
 
29
38
 
30
39
  class InitializeImpl(
31
- AbstractCommandImpl[InitializeParams, SuccessData[InitializeResult, None]]
40
+ AbstractCommandImpl[InitializeParams, SuccessData[InitializeResult]]
32
41
  ):
33
42
  """Execution implementation of initializing an Absorbance Reader."""
34
43
 
@@ -41,9 +50,7 @@ class InitializeImpl(
41
50
  self._state_view = state_view
42
51
  self._equipment = equipment
43
52
 
44
- async def execute(
45
- self, params: InitializeParams
46
- ) -> SuccessData[InitializeResult, None]:
53
+ async def execute(self, params: InitializeParams) -> SuccessData[InitializeResult]:
47
54
  """Initiate a single absorbance measurement."""
48
55
  abs_reader_substate = self._state_view.modules.get_absorbance_reader_substate(
49
56
  module_id=params.moduleId
@@ -54,11 +61,48 @@ class InitializeImpl(
54
61
  )
55
62
 
56
63
  if abs_reader is not None:
57
- await abs_reader.set_sample_wavelength(wavelength=params.sampleWavelength)
64
+ # Validate the parameters before initializing.
65
+ sample_wavelengths = set(params.sampleWavelengths)
66
+ sample_wavelengths_len = len(params.sampleWavelengths)
67
+ reference_wavelength = params.referenceWavelength
68
+ supported_wavelengths = set(abs_reader.supported_wavelengths)
69
+ unsupported_wavelengths = sample_wavelengths.difference(
70
+ supported_wavelengths
71
+ )
72
+ if unsupported_wavelengths:
73
+ raise ValueError(f"Unsupported wavelengths: {unsupported_wavelengths}")
74
+
75
+ if params.measureMode == "single":
76
+ if sample_wavelengths_len != 1:
77
+ raise ValueError(
78
+ f"single requires one sample wavelength, provided {sample_wavelengths}"
79
+ )
80
+ if (
81
+ reference_wavelength is not None
82
+ and reference_wavelength not in supported_wavelengths
83
+ ):
84
+ raise ValueError(
85
+ f"Reference wavelength {reference_wavelength} not supported {supported_wavelengths}"
86
+ )
87
+
88
+ if params.measureMode == "multi":
89
+ if sample_wavelengths_len < 1 or sample_wavelengths_len > 6:
90
+ raise ValueError(
91
+ f"multi requires 1-6 sample wavelengths, provided {sample_wavelengths}"
92
+ )
93
+ if reference_wavelength is not None:
94
+ raise RuntimeError(
95
+ "Reference wavelength cannot be used with multi mode."
96
+ )
97
+
98
+ await abs_reader.set_sample_wavelength(
99
+ ABSMeasurementMode(params.measureMode),
100
+ params.sampleWavelengths,
101
+ reference_wavelength=params.referenceWavelength,
102
+ )
58
103
 
59
104
  return SuccessData(
60
105
  public=InitializeResult(),
61
- private=None,
62
106
  )
63
107
 
64
108