opentrons 8.1.0a0__py2.py3-none-any.whl → 8.2.0__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 (238) 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 +208 -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/backends/ot3utils.py +1 -0
  20. opentrons/hardware_control/instruments/ot2/pipette_handler.py +22 -82
  21. opentrons/hardware_control/instruments/ot3/pipette_handler.py +10 -2
  22. opentrons/hardware_control/module_control.py +43 -2
  23. opentrons/hardware_control/modules/__init__.py +7 -1
  24. opentrons/hardware_control/modules/absorbance_reader.py +232 -83
  25. opentrons/hardware_control/modules/errors.py +7 -0
  26. opentrons/hardware_control/modules/heater_shaker.py +8 -3
  27. opentrons/hardware_control/modules/magdeck.py +12 -3
  28. opentrons/hardware_control/modules/mod_abc.py +27 -2
  29. opentrons/hardware_control/modules/tempdeck.py +15 -7
  30. opentrons/hardware_control/modules/thermocycler.py +69 -3
  31. opentrons/hardware_control/modules/types.py +11 -5
  32. opentrons/hardware_control/modules/update.py +11 -5
  33. opentrons/hardware_control/modules/utils.py +3 -1
  34. opentrons/hardware_control/ot3_calibration.py +6 -6
  35. opentrons/hardware_control/ot3api.py +131 -94
  36. opentrons/hardware_control/poller.py +15 -11
  37. opentrons/hardware_control/protocols/__init__.py +1 -7
  38. opentrons/hardware_control/protocols/instrument_configurer.py +14 -2
  39. opentrons/hardware_control/protocols/liquid_handler.py +5 -0
  40. opentrons/hardware_control/protocols/position_estimator.py +3 -1
  41. opentrons/hardware_control/types.py +2 -0
  42. opentrons/legacy_commands/helpers.py +8 -2
  43. opentrons/motion_planning/__init__.py +2 -0
  44. opentrons/motion_planning/waypoints.py +32 -0
  45. opentrons/protocol_api/__init__.py +2 -1
  46. opentrons/protocol_api/_liquid.py +87 -1
  47. opentrons/protocol_api/_parameter_context.py +10 -1
  48. opentrons/protocol_api/core/engine/deck_conflict.py +0 -297
  49. opentrons/protocol_api/core/engine/instrument.py +29 -25
  50. opentrons/protocol_api/core/engine/labware.py +20 -4
  51. opentrons/protocol_api/core/engine/module_core.py +166 -17
  52. opentrons/protocol_api/core/engine/pipette_movement_conflict.py +362 -0
  53. opentrons/protocol_api/core/engine/protocol.py +30 -2
  54. opentrons/protocol_api/core/instrument.py +2 -0
  55. opentrons/protocol_api/core/labware.py +4 -0
  56. opentrons/protocol_api/core/legacy/legacy_instrument_core.py +2 -0
  57. opentrons/protocol_api/core/legacy/legacy_labware_core.py +5 -0
  58. opentrons/protocol_api/core/legacy/legacy_protocol_core.py +6 -2
  59. opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +2 -0
  60. opentrons/protocol_api/core/module.py +22 -4
  61. opentrons/protocol_api/core/protocol.py +6 -2
  62. opentrons/protocol_api/instrument_context.py +52 -20
  63. opentrons/protocol_api/labware.py +13 -1
  64. opentrons/protocol_api/module_contexts.py +115 -17
  65. opentrons/protocol_api/protocol_context.py +49 -5
  66. opentrons/protocol_api/validation.py +5 -3
  67. opentrons/protocol_engine/__init__.py +10 -9
  68. opentrons/protocol_engine/actions/__init__.py +3 -0
  69. opentrons/protocol_engine/actions/actions.py +30 -25
  70. opentrons/protocol_engine/actions/get_state_update.py +38 -0
  71. opentrons/protocol_engine/clients/sync_client.py +1 -1
  72. opentrons/protocol_engine/clients/transports.py +1 -1
  73. opentrons/protocol_engine/commands/__init__.py +0 -4
  74. opentrons/protocol_engine/commands/absorbance_reader/__init__.py +41 -11
  75. opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +148 -0
  76. opentrons/protocol_engine/commands/absorbance_reader/initialize.py +65 -9
  77. opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +148 -0
  78. opentrons/protocol_engine/commands/absorbance_reader/read.py +200 -0
  79. opentrons/protocol_engine/commands/aspirate.py +29 -16
  80. opentrons/protocol_engine/commands/aspirate_in_place.py +33 -16
  81. opentrons/protocol_engine/commands/blow_out.py +63 -14
  82. opentrons/protocol_engine/commands/blow_out_in_place.py +55 -13
  83. opentrons/protocol_engine/commands/calibration/calibrate_gripper.py +2 -5
  84. opentrons/protocol_engine/commands/calibration/calibrate_module.py +3 -4
  85. opentrons/protocol_engine/commands/calibration/calibrate_pipette.py +2 -5
  86. opentrons/protocol_engine/commands/calibration/move_to_maintenance_position.py +6 -4
  87. opentrons/protocol_engine/commands/command.py +31 -18
  88. opentrons/protocol_engine/commands/command_unions.py +37 -24
  89. opentrons/protocol_engine/commands/comment.py +5 -3
  90. opentrons/protocol_engine/commands/configure_for_volume.py +11 -14
  91. opentrons/protocol_engine/commands/configure_nozzle_layout.py +9 -15
  92. opentrons/protocol_engine/commands/custom.py +5 -3
  93. opentrons/protocol_engine/commands/dispense.py +42 -20
  94. opentrons/protocol_engine/commands/dispense_in_place.py +32 -14
  95. opentrons/protocol_engine/commands/drop_tip.py +70 -16
  96. opentrons/protocol_engine/commands/drop_tip_in_place.py +59 -13
  97. opentrons/protocol_engine/commands/get_tip_presence.py +5 -3
  98. opentrons/protocol_engine/commands/heater_shaker/close_labware_latch.py +6 -6
  99. opentrons/protocol_engine/commands/heater_shaker/deactivate_heater.py +6 -6
  100. opentrons/protocol_engine/commands/heater_shaker/deactivate_shaker.py +6 -6
  101. opentrons/protocol_engine/commands/heater_shaker/open_labware_latch.py +8 -6
  102. opentrons/protocol_engine/commands/heater_shaker/set_and_wait_for_shake_speed.py +8 -4
  103. opentrons/protocol_engine/commands/heater_shaker/set_target_temperature.py +6 -4
  104. opentrons/protocol_engine/commands/heater_shaker/wait_for_temperature.py +6 -6
  105. opentrons/protocol_engine/commands/home.py +11 -5
  106. opentrons/protocol_engine/commands/liquid_probe.py +146 -88
  107. opentrons/protocol_engine/commands/load_labware.py +28 -5
  108. opentrons/protocol_engine/commands/load_liquid.py +18 -7
  109. opentrons/protocol_engine/commands/load_module.py +4 -6
  110. opentrons/protocol_engine/commands/load_pipette.py +18 -17
  111. opentrons/protocol_engine/commands/magnetic_module/disengage.py +6 -6
  112. opentrons/protocol_engine/commands/magnetic_module/engage.py +6 -4
  113. opentrons/protocol_engine/commands/move_labware.py +155 -23
  114. opentrons/protocol_engine/commands/move_relative.py +15 -3
  115. opentrons/protocol_engine/commands/move_to_addressable_area.py +29 -4
  116. opentrons/protocol_engine/commands/move_to_addressable_area_for_drop_tip.py +13 -4
  117. opentrons/protocol_engine/commands/move_to_coordinates.py +11 -5
  118. opentrons/protocol_engine/commands/move_to_well.py +37 -10
  119. opentrons/protocol_engine/commands/pick_up_tip.py +51 -30
  120. opentrons/protocol_engine/commands/pipetting_common.py +47 -16
  121. opentrons/protocol_engine/commands/prepare_to_aspirate.py +62 -15
  122. opentrons/protocol_engine/commands/reload_labware.py +13 -4
  123. opentrons/protocol_engine/commands/retract_axis.py +6 -3
  124. opentrons/protocol_engine/commands/save_position.py +2 -3
  125. opentrons/protocol_engine/commands/set_rail_lights.py +5 -3
  126. opentrons/protocol_engine/commands/set_status_bar.py +5 -3
  127. opentrons/protocol_engine/commands/temperature_module/deactivate.py +6 -4
  128. opentrons/protocol_engine/commands/temperature_module/set_target_temperature.py +3 -4
  129. opentrons/protocol_engine/commands/temperature_module/wait_for_temperature.py +6 -6
  130. opentrons/protocol_engine/commands/thermocycler/__init__.py +19 -0
  131. opentrons/protocol_engine/commands/thermocycler/close_lid.py +8 -8
  132. opentrons/protocol_engine/commands/thermocycler/deactivate_block.py +6 -4
  133. opentrons/protocol_engine/commands/thermocycler/deactivate_lid.py +6 -4
  134. opentrons/protocol_engine/commands/thermocycler/open_lid.py +8 -4
  135. opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py +165 -0
  136. opentrons/protocol_engine/commands/thermocycler/run_profile.py +6 -6
  137. opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py +3 -4
  138. opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py +3 -4
  139. opentrons/protocol_engine/commands/thermocycler/wait_for_block_temperature.py +6 -4
  140. opentrons/protocol_engine/commands/thermocycler/wait_for_lid_temperature.py +6 -4
  141. opentrons/protocol_engine/commands/touch_tip.py +19 -7
  142. opentrons/protocol_engine/commands/unsafe/__init__.py +30 -0
  143. opentrons/protocol_engine/commands/unsafe/unsafe_blow_out_in_place.py +6 -4
  144. opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +12 -4
  145. opentrons/protocol_engine/commands/unsafe/unsafe_engage_axes.py +5 -3
  146. opentrons/protocol_engine/commands/unsafe/unsafe_place_labware.py +208 -0
  147. opentrons/protocol_engine/commands/unsafe/unsafe_ungrip_labware.py +77 -0
  148. opentrons/protocol_engine/commands/unsafe/update_position_estimators.py +10 -4
  149. opentrons/protocol_engine/commands/verify_tip_presence.py +5 -5
  150. opentrons/protocol_engine/commands/wait_for_duration.py +5 -3
  151. opentrons/protocol_engine/commands/wait_for_resume.py +5 -3
  152. opentrons/protocol_engine/create_protocol_engine.py +60 -10
  153. opentrons/protocol_engine/engine_support.py +2 -1
  154. opentrons/protocol_engine/error_recovery_policy.py +14 -3
  155. opentrons/protocol_engine/errors/__init__.py +20 -0
  156. opentrons/protocol_engine/errors/error_occurrence.py +8 -3
  157. opentrons/protocol_engine/errors/exceptions.py +127 -2
  158. opentrons/protocol_engine/execution/__init__.py +2 -0
  159. opentrons/protocol_engine/execution/command_executor.py +22 -13
  160. opentrons/protocol_engine/execution/create_queue_worker.py +5 -1
  161. opentrons/protocol_engine/execution/door_watcher.py +1 -1
  162. opentrons/protocol_engine/execution/equipment.py +2 -1
  163. opentrons/protocol_engine/execution/error_recovery_hardware_state_synchronizer.py +101 -0
  164. opentrons/protocol_engine/execution/gantry_mover.py +4 -2
  165. opentrons/protocol_engine/execution/hardware_stopper.py +3 -3
  166. opentrons/protocol_engine/execution/heater_shaker_movement_flagger.py +1 -4
  167. opentrons/protocol_engine/execution/labware_movement.py +73 -22
  168. opentrons/protocol_engine/execution/movement.py +17 -7
  169. opentrons/protocol_engine/execution/pipetting.py +7 -4
  170. opentrons/protocol_engine/execution/queue_worker.py +6 -2
  171. opentrons/protocol_engine/execution/run_control.py +1 -1
  172. opentrons/protocol_engine/execution/thermocycler_movement_flagger.py +1 -1
  173. opentrons/protocol_engine/execution/thermocycler_plate_lifter.py +2 -1
  174. opentrons/protocol_engine/execution/tip_handler.py +77 -43
  175. opentrons/protocol_engine/notes/__init__.py +14 -2
  176. opentrons/protocol_engine/notes/notes.py +18 -1
  177. opentrons/protocol_engine/plugins.py +1 -1
  178. opentrons/protocol_engine/protocol_engine.py +47 -31
  179. opentrons/protocol_engine/resources/__init__.py +2 -0
  180. opentrons/protocol_engine/resources/deck_data_provider.py +19 -5
  181. opentrons/protocol_engine/resources/file_provider.py +161 -0
  182. opentrons/protocol_engine/resources/fixture_validation.py +11 -1
  183. opentrons/protocol_engine/resources/labware_validation.py +10 -0
  184. opentrons/protocol_engine/state/__init__.py +0 -70
  185. opentrons/protocol_engine/state/addressable_areas.py +1 -1
  186. opentrons/protocol_engine/state/command_history.py +21 -2
  187. opentrons/protocol_engine/state/commands.py +110 -31
  188. opentrons/protocol_engine/state/files.py +59 -0
  189. opentrons/protocol_engine/state/frustum_helpers.py +440 -0
  190. opentrons/protocol_engine/state/geometry.py +445 -59
  191. opentrons/protocol_engine/state/labware.py +264 -84
  192. opentrons/protocol_engine/state/liquids.py +1 -1
  193. opentrons/protocol_engine/state/module_substates/absorbance_reader_substate.py +21 -3
  194. opentrons/protocol_engine/state/modules.py +145 -90
  195. opentrons/protocol_engine/state/motion.py +33 -14
  196. opentrons/protocol_engine/state/pipettes.py +157 -317
  197. opentrons/protocol_engine/state/state.py +30 -1
  198. opentrons/protocol_engine/state/state_summary.py +3 -0
  199. opentrons/protocol_engine/state/tips.py +69 -114
  200. opentrons/protocol_engine/state/update_types.py +424 -0
  201. opentrons/protocol_engine/state/wells.py +236 -0
  202. opentrons/protocol_engine/types.py +90 -0
  203. opentrons/protocol_reader/file_format_validator.py +83 -15
  204. opentrons/protocol_runner/json_translator.py +21 -5
  205. opentrons/protocol_runner/legacy_command_mapper.py +27 -6
  206. opentrons/protocol_runner/legacy_context_plugin.py +27 -71
  207. opentrons/protocol_runner/protocol_runner.py +6 -3
  208. opentrons/protocol_runner/run_orchestrator.py +41 -6
  209. opentrons/protocols/advanced_control/mix.py +3 -5
  210. opentrons/protocols/advanced_control/transfers.py +125 -56
  211. opentrons/protocols/api_support/constants.py +1 -1
  212. opentrons/protocols/api_support/definitions.py +1 -1
  213. opentrons/protocols/api_support/labware_like.py +4 -4
  214. opentrons/protocols/api_support/tip_tracker.py +2 -2
  215. opentrons/protocols/api_support/types.py +15 -2
  216. opentrons/protocols/api_support/util.py +30 -42
  217. opentrons/protocols/duration/errors.py +1 -1
  218. opentrons/protocols/duration/estimator.py +50 -29
  219. opentrons/protocols/execution/dev_types.py +2 -2
  220. opentrons/protocols/execution/execute_json_v4.py +15 -10
  221. opentrons/protocols/execution/execute_python.py +8 -3
  222. opentrons/protocols/geometry/planning.py +12 -12
  223. opentrons/protocols/labware.py +17 -33
  224. opentrons/protocols/parameters/csv_parameter_interface.py +3 -1
  225. opentrons/simulate.py +3 -3
  226. opentrons/types.py +30 -3
  227. opentrons/util/logging_config.py +34 -0
  228. {opentrons-8.1.0a0.dist-info → opentrons-8.2.0.dist-info}/METADATA +5 -4
  229. {opentrons-8.1.0a0.dist-info → opentrons-8.2.0.dist-info}/RECORD +235 -223
  230. opentrons/protocol_engine/commands/absorbance_reader/measure.py +0 -94
  231. opentrons/protocol_engine/commands/configuring_common.py +0 -26
  232. opentrons/protocol_runner/thread_async_queue.py +0 -174
  233. /opentrons/protocol_engine/state/{abstract_store.py → _abstract_store.py} +0 -0
  234. /opentrons/protocol_engine/state/{move_types.py → _move_types.py} +0 -0
  235. {opentrons-8.1.0a0.dist-info → opentrons-8.2.0.dist-info}/LICENSE +0 -0
  236. {opentrons-8.1.0a0.dist-info → opentrons-8.2.0.dist-info}/WHEEL +0 -0
  237. {opentrons-8.1.0a0.dist-info → opentrons-8.2.0.dist-info}/entry_points.txt +0 -0
  238. {opentrons-8.1.0a0.dist-info → opentrons-8.2.0.dist-info}/top_level.txt +0 -0
@@ -14,7 +14,7 @@ from opentrons.util.change_notifier import ChangeNotifier
14
14
 
15
15
  from ..resources import DeckFixedLabware
16
16
  from ..actions import Action, ActionHandler
17
- from .abstract_store import HasState, HandlesActions
17
+ from ._abstract_store import HasState, HandlesActions
18
18
  from .commands import CommandState, CommandStore, CommandView
19
19
  from .addressable_areas import (
20
20
  AddressableAreaState,
@@ -26,8 +26,10 @@ from .pipettes import PipetteState, PipetteStore, PipetteView
26
26
  from .modules import ModuleState, ModuleStore, ModuleView
27
27
  from .liquids import LiquidState, LiquidView, LiquidStore
28
28
  from .tips import TipState, TipView, TipStore
29
+ from .wells import WellState, WellView, WellStore
29
30
  from .geometry import GeometryView
30
31
  from .motion import MotionView
32
+ from .files import FileView, FileState, FileStore
31
33
  from .config import Config
32
34
  from .state_summary import StateSummary
33
35
  from ..types import DeckConfigurationType
@@ -48,6 +50,8 @@ class State:
48
50
  modules: ModuleState
49
51
  liquids: LiquidState
50
52
  tips: TipState
53
+ wells: WellState
54
+ files: FileState
51
55
 
52
56
 
53
57
  class StateView(HasState[State]):
@@ -61,8 +65,10 @@ class StateView(HasState[State]):
61
65
  _modules: ModuleView
62
66
  _liquid: LiquidView
63
67
  _tips: TipView
68
+ _wells: WellView
64
69
  _geometry: GeometryView
65
70
  _motion: MotionView
71
+ _files: FileView
66
72
  _config: Config
67
73
 
68
74
  @property
@@ -100,6 +106,11 @@ class StateView(HasState[State]):
100
106
  """Get state view selectors for tip state."""
101
107
  return self._tips
102
108
 
109
+ @property
110
+ def wells(self) -> WellView:
111
+ """Get state view selectors for well state."""
112
+ return self._wells
113
+
103
114
  @property
104
115
  def geometry(self) -> GeometryView:
105
116
  """Get state view selectors for derived geometry state."""
@@ -110,6 +121,11 @@ class StateView(HasState[State]):
110
121
  """Get state view selectors for derived motion state."""
111
122
  return self._motion
112
123
 
124
+ @property
125
+ def files(self) -> FileView:
126
+ """Get state view selectors for engine create file state."""
127
+ return self._files
128
+
113
129
  @property
114
130
  def config(self) -> Config:
115
131
  """Get ProtocolEngine configuration."""
@@ -129,7 +145,9 @@ class StateView(HasState[State]):
129
145
  completedAt=self._state.commands.run_completed_at,
130
146
  startedAt=self._state.commands.run_started_at,
131
147
  liquids=self._liquid.get_all(),
148
+ wells=self._wells.get_all(),
132
149
  hasEverEnteredErrorRecovery=self._commands.get_has_entered_recovery_mode(),
150
+ files=self._state.files.file_ids,
133
151
  )
134
152
 
135
153
 
@@ -191,10 +209,13 @@ class StateStore(StateView, ActionHandler):
191
209
  )
192
210
  self._module_store = ModuleStore(
193
211
  config=config,
212
+ deck_fixed_labware=deck_fixed_labware,
194
213
  module_calibration_offsets=module_calibration_offsets,
195
214
  )
196
215
  self._liquid_store = LiquidStore()
197
216
  self._tip_store = TipStore()
217
+ self._well_store = WellStore()
218
+ self._file_store = FileStore()
198
219
 
199
220
  self._substores: List[HandlesActions] = [
200
221
  self._command_store,
@@ -204,6 +225,8 @@ class StateStore(StateView, ActionHandler):
204
225
  self._module_store,
205
226
  self._liquid_store,
206
227
  self._tip_store,
228
+ self._well_store,
229
+ self._file_store,
207
230
  ]
208
231
  self._config = config
209
232
  self._change_notifier = change_notifier or ChangeNotifier()
@@ -320,6 +343,8 @@ class StateStore(StateView, ActionHandler):
320
343
  modules=self._module_store.state,
321
344
  liquids=self._liquid_store.state,
322
345
  tips=self._tip_store.state,
346
+ wells=self._well_store.state,
347
+ files=self._file_store.state,
323
348
  )
324
349
 
325
350
  def _initialize_state(self) -> None:
@@ -335,11 +360,14 @@ class StateStore(StateView, ActionHandler):
335
360
  self._modules = ModuleView(state.modules)
336
361
  self._liquid = LiquidView(state.liquids)
337
362
  self._tips = TipView(state.tips)
363
+ self._wells = WellView(state.wells)
364
+ self._files = FileView(state.files)
338
365
 
339
366
  # Derived states
340
367
  self._geometry = GeometryView(
341
368
  config=self._config,
342
369
  labware_view=self._labware,
370
+ well_view=self._wells,
343
371
  module_view=self._modules,
344
372
  pipette_view=self._pipettes,
345
373
  addressable_area_view=self._addressable_areas,
@@ -364,6 +392,7 @@ class StateStore(StateView, ActionHandler):
364
392
  self._modules._state = next_state.modules
365
393
  self._liquid._state = next_state.liquids
366
394
  self._tips._state = next_state.tips
395
+ self._wells._state = next_state.wells
367
396
  self._change_notifier.notify()
368
397
  if self._notify_robot_server is not None:
369
398
  self._notify_robot_server()
@@ -11,6 +11,7 @@ from ..types import (
11
11
  LoadedModule,
12
12
  LoadedPipette,
13
13
  Liquid,
14
+ WellInfoSummary,
14
15
  )
15
16
 
16
17
 
@@ -29,3 +30,5 @@ class StateSummary(BaseModel):
29
30
  startedAt: Optional[datetime]
30
31
  completedAt: Optional[datetime]
31
32
  liquids: List[Liquid] = Field(default_factory=list)
33
+ wells: List[WellInfoSummary] = Field(default_factory=list)
34
+ files: List[str] = Field(default_factory=list)
@@ -3,27 +3,10 @@ from dataclasses import dataclass
3
3
  from enum import Enum
4
4
  from typing import Dict, Optional, List, Union
5
5
 
6
- from .abstract_store import HasState, HandlesActions
7
- from ..actions import (
8
- Action,
9
- SucceedCommandAction,
10
- FailCommandAction,
11
- ResetTipsAction,
12
- )
13
- from ..commands import (
14
- Command,
15
- LoadLabwareResult,
16
- PickUpTip,
17
- PickUpTipResult,
18
- DropTipResult,
19
- DropTipInPlaceResult,
20
- unsafe,
21
- )
22
- from ..commands.configuring_common import (
23
- PipetteConfigUpdateResultMixin,
24
- PipetteNozzleLayoutResultMixin,
25
- )
26
- from ..error_recovery_policy import ErrorRecoveryType
6
+ from opentrons.protocol_engine.state import update_types
7
+
8
+ from ._abstract_store import HasState, HandlesActions
9
+ from ..actions import Action, ResetTipsAction, get_state_updates
27
10
 
28
11
  from opentrons.hardware_control.nozzle_manager import NozzleMap
29
12
 
@@ -38,16 +21,26 @@ class TipRackWellState(Enum):
38
21
  TipRackStateByWellName = Dict[str, TipRackWellState]
39
22
 
40
23
 
24
+ # todo(mm, 2024-10-10): This info is duplicated between here and PipetteState because
25
+ # TipStore is using it to compute which tips a PickUpTip removes from the tip rack,
26
+ # given the pipette's current nozzle map. We could avoid this duplication by moving the
27
+ # computation to TipView, calling it from PickUpTipImplementation, and passing the
28
+ # precomputed list of wells to TipStore.
29
+ @dataclass
30
+ class _PipetteInfo:
31
+ channels: int
32
+ active_channels: int
33
+ nozzle_map: NozzleMap
34
+
35
+
41
36
  @dataclass
42
37
  class TipState:
43
38
  """State of all tips."""
44
39
 
45
40
  tips_by_labware_id: Dict[str, TipRackStateByWellName]
46
41
  column_by_labware_id: Dict[str, List[List[str]]]
47
- channels_by_pipette_id: Dict[str, int]
48
- length_by_pipette_id: Dict[str, float]
49
- active_channels_by_pipette_id: Dict[str, int]
50
- nozzle_map_by_pipette_id: Dict[str, NozzleMap]
42
+
43
+ pipette_info_by_pipette_id: Dict[str, _PipetteInfo]
51
44
 
52
45
 
53
46
  class TipStore(HasState[TipState], HandlesActions):
@@ -60,40 +53,15 @@ class TipStore(HasState[TipState], HandlesActions):
60
53
  self._state = TipState(
61
54
  tips_by_labware_id={},
62
55
  column_by_labware_id={},
63
- channels_by_pipette_id={},
64
- length_by_pipette_id={},
65
- active_channels_by_pipette_id={},
66
- nozzle_map_by_pipette_id={},
56
+ pipette_info_by_pipette_id={},
67
57
  )
68
58
 
69
59
  def handle_action(self, action: Action) -> None:
70
60
  """Modify state in reaction to an action."""
71
- if isinstance(action, SucceedCommandAction):
72
- if isinstance(action.private_result, PipetteConfigUpdateResultMixin):
73
- pipette_id = action.private_result.pipette_id
74
- config = action.private_result.config
75
- self._state.channels_by_pipette_id[pipette_id] = config.channels
76
- self._state.active_channels_by_pipette_id[pipette_id] = config.channels
77
- self._state.nozzle_map_by_pipette_id[pipette_id] = config.nozzle_map
78
- self._handle_succeeded_command(action.command)
79
-
80
- if isinstance(action.private_result, PipetteNozzleLayoutResultMixin):
81
- pipette_id = action.private_result.pipette_id
82
- nozzle_map = action.private_result.nozzle_map
83
- if nozzle_map:
84
- self._state.active_channels_by_pipette_id[
85
- pipette_id
86
- ] = nozzle_map.tip_count
87
- self._state.nozzle_map_by_pipette_id[pipette_id] = nozzle_map
88
- else:
89
- self._state.active_channels_by_pipette_id[
90
- pipette_id
91
- ] = self._state.channels_by_pipette_id[pipette_id]
61
+ for state_update in get_state_updates(action):
62
+ self._handle_state_update(state_update)
92
63
 
93
- elif isinstance(action, FailCommandAction):
94
- self._handle_failed_command(action)
95
-
96
- elif isinstance(action, ResetTipsAction):
64
+ if isinstance(action, ResetTipsAction):
97
65
  labware_id = action.labware_id
98
66
 
99
67
  for well_name in self._state.tips_by_labware_id[labware_id].keys():
@@ -101,67 +69,51 @@ class TipStore(HasState[TipState], HandlesActions):
101
69
  well_name
102
70
  ] = TipRackWellState.CLEAN
103
71
 
104
- def _handle_succeeded_command(self, command: Command) -> None:
105
- if (
106
- isinstance(command.result, LoadLabwareResult)
107
- and command.result.definition.parameters.isTiprack
108
- ):
109
- labware_id = command.result.labwareId
110
- definition = command.result.definition
111
- self._state.tips_by_labware_id[labware_id] = {
112
- well_name: TipRackWellState.CLEAN
113
- for column in definition.ordering
114
- for well_name in column
115
- }
116
- self._state.column_by_labware_id[labware_id] = [
117
- column for column in definition.ordering
118
- ]
72
+ def _handle_state_update(self, state_update: update_types.StateUpdate) -> None:
73
+ if state_update.pipette_config != update_types.NO_CHANGE:
74
+ self._state.pipette_info_by_pipette_id[
75
+ state_update.pipette_config.pipette_id
76
+ ] = _PipetteInfo(
77
+ channels=state_update.pipette_config.config.channels,
78
+ active_channels=state_update.pipette_config.config.channels,
79
+ nozzle_map=state_update.pipette_config.config.nozzle_map,
80
+ )
119
81
 
120
- elif isinstance(command.result, PickUpTipResult):
121
- labware_id = command.params.labwareId
122
- well_name = command.params.wellName
123
- pipette_id = command.params.pipetteId
124
- length = command.result.tipLength
82
+ if state_update.tips_used != update_types.NO_CHANGE:
125
83
  self._set_used_tips(
126
- pipette_id=pipette_id, well_name=well_name, labware_id=labware_id
84
+ pipette_id=state_update.tips_used.pipette_id,
85
+ labware_id=state_update.tips_used.labware_id,
86
+ well_name=state_update.tips_used.well_name,
127
87
  )
128
- self._state.length_by_pipette_id[pipette_id] = length
129
-
130
- elif isinstance(
131
- command.result,
132
- (DropTipResult, DropTipInPlaceResult, unsafe.UnsafeDropTipInPlaceResult),
133
- ):
134
- pipette_id = command.params.pipetteId
135
- self._state.length_by_pipette_id.pop(pipette_id, None)
136
88
 
137
- def _handle_failed_command(
138
- self,
139
- action: FailCommandAction,
140
- ) -> None:
141
- # If a pickUpTip command fails recoverably, mark the tips as used. This way,
142
- # when the protocol is resumed and the Python Protocol API calls
143
- # `get_next_tip()`, we'll move on to other tips as expected.
144
- #
145
- # We don't attempt this for nonrecoverable errors because maybe the failure
146
- # was due to a bad labware ID or well name.
147
- if (
148
- isinstance(action.running_command, PickUpTip)
149
- and action.type != ErrorRecoveryType.FAIL_RUN
150
- ):
151
- self._set_used_tips(
152
- pipette_id=action.running_command.params.pipetteId,
153
- labware_id=action.running_command.params.labwareId,
154
- well_name=action.running_command.params.wellName,
89
+ if state_update.pipette_nozzle_map != update_types.NO_CHANGE:
90
+ pipette_info = self._state.pipette_info_by_pipette_id[
91
+ state_update.pipette_nozzle_map.pipette_id
92
+ ]
93
+ pipette_info.active_channels = (
94
+ state_update.pipette_nozzle_map.nozzle_map.tip_count
155
95
  )
156
- # Note: We're logically removing the tip from the tip rack,
157
- # but we're not logically updating the pipette to have that tip on it.
96
+ pipette_info.nozzle_map = state_update.pipette_nozzle_map.nozzle_map
97
+
98
+ if state_update.loaded_labware != update_types.NO_CHANGE:
99
+ labware_id = state_update.loaded_labware.labware_id
100
+ definition = state_update.loaded_labware.definition
101
+ if definition.parameters.isTiprack:
102
+ self._state.tips_by_labware_id[labware_id] = {
103
+ well_name: TipRackWellState.CLEAN
104
+ for column in definition.ordering
105
+ for well_name in column
106
+ }
107
+ self._state.column_by_labware_id[labware_id] = [
108
+ column for column in definition.ordering
109
+ ]
158
110
 
159
111
  def _set_used_tips( # noqa: C901
160
112
  self, pipette_id: str, well_name: str, labware_id: str
161
113
  ) -> None:
162
114
  columns = self._state.column_by_labware_id.get(labware_id, [])
163
115
  wells = self._state.tips_by_labware_id.get(labware_id, {})
164
- nozzle_map = self._state.nozzle_map_by_pipette_id[pipette_id]
116
+ nozzle_map = self._state.pipette_info_by_pipette_id[pipette_id].nozzle_map
165
117
 
166
118
  # TODO (cb, 02-28-2024): Transition from using partial nozzle map to full instrument map for the set used logic
167
119
  num_nozzle_cols = len(nozzle_map.columns)
@@ -229,7 +181,7 @@ class TipView(HasState[TipState]):
229
181
  critical_row: int,
230
182
  entry_well: str,
231
183
  ) -> Optional[List[str]]:
232
- tip_cluster = []
184
+ tip_cluster: list[str] = []
233
185
  for i in range(active_columns):
234
186
  if entry_well == "A1" or entry_well == "H1":
235
187
  if critical_column - i >= 0:
@@ -280,12 +232,12 @@ class TipView(HasState[TipState]):
280
232
 
281
233
  # In the case of a 96ch we can attempt to index in by singular rows and columns assuming that indexed direction is safe
282
234
  # The tip cluster list is ordered: Each row from a column in order by columns
283
- tip_cluster_final_column = []
235
+ tip_cluster_final_column: list[str] = []
284
236
  for i in range(active_rows):
285
237
  tip_cluster_final_column.append(
286
238
  tip_cluster[((active_columns * active_rows) - 1) - i]
287
239
  )
288
- tip_cluster_final_row = []
240
+ tip_cluster_final_row: list[str] = []
289
241
  for i in range(active_columns):
290
242
  tip_cluster_final_row.append(
291
243
  tip_cluster[(active_rows - 1) + (i * active_rows)]
@@ -476,15 +428,22 @@ class TipView(HasState[TipState]):
476
428
 
477
429
  def get_pipette_channels(self, pipette_id: str) -> int:
478
430
  """Return the given pipette's number of channels."""
479
- return self._state.channels_by_pipette_id[pipette_id]
431
+ return self._state.pipette_info_by_pipette_id[pipette_id].channels
480
432
 
481
433
  def get_pipette_active_channels(self, pipette_id: str) -> int:
482
434
  """Get the number of channels being used in the given pipette's configuration."""
483
- return self._state.active_channels_by_pipette_id[pipette_id]
435
+ return self._state.pipette_info_by_pipette_id[pipette_id].active_channels
484
436
 
485
437
  def get_pipette_nozzle_map(self, pipette_id: str) -> NozzleMap:
486
438
  """Get the current nozzle map the given pipette's configuration."""
487
- return self._state.nozzle_map_by_pipette_id[pipette_id]
439
+ return self._state.pipette_info_by_pipette_id[pipette_id].nozzle_map
440
+
441
+ def get_pipette_nozzle_maps(self) -> Dict[str, NozzleMap]:
442
+ """Get current nozzle maps keyed by pipette id."""
443
+ return {
444
+ pipette_id: pipette_info.nozzle_map
445
+ for pipette_id, pipette_info in self._state.pipette_info_by_pipette_id.items()
446
+ }
488
447
 
489
448
  def has_clean_tip(self, labware_id: str, well_name: str) -> bool:
490
449
  """Get whether a well in a labware has a clean tip.
@@ -502,10 +461,6 @@ class TipView(HasState[TipState]):
502
461
 
503
462
  return well_state == TipRackWellState.CLEAN
504
463
 
505
- def get_tip_length(self, pipette_id: str) -> float:
506
- """Return the given pipette's tip length."""
507
- return self._state.length_by_pipette_id.get(pipette_id, 0)
508
-
509
464
 
510
465
  def _drop_wells_before_starting_tip(
511
466
  wells: TipRackStateByWellName, starting_tip_name: str