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
@@ -9,7 +9,7 @@ from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, Succe
9
9
  from ...errors.error_occurrence import ErrorOccurrence
10
10
 
11
11
  if TYPE_CHECKING:
12
- from opentrons.protocol_engine.state import StateView
12
+ from opentrons.protocol_engine.state.state import StateView
13
13
  from opentrons.protocol_engine.execution import EquipmentHandler
14
14
 
15
15
 
@@ -27,9 +27,7 @@ class DeactivateHeaterResult(BaseModel):
27
27
 
28
28
 
29
29
  class DeactivateHeaterImpl(
30
- AbstractCommandImpl[
31
- DeactivateHeaterParams, SuccessData[DeactivateHeaterResult, None]
32
- ]
30
+ AbstractCommandImpl[DeactivateHeaterParams, SuccessData[DeactivateHeaterResult]]
33
31
  ):
34
32
  """Execution implementation of a Heater-Shaker's deactivate heater command."""
35
33
 
@@ -44,7 +42,7 @@ class DeactivateHeaterImpl(
44
42
 
45
43
  async def execute(
46
44
  self, params: DeactivateHeaterParams
47
- ) -> SuccessData[DeactivateHeaterResult, None]:
45
+ ) -> SuccessData[DeactivateHeaterResult]:
48
46
  """Unset a Heater-Shaker's target temperature."""
49
47
  hs_module_substate = self._state_view.modules.get_heater_shaker_module_substate(
50
48
  module_id=params.moduleId
@@ -58,7 +56,9 @@ class DeactivateHeaterImpl(
58
56
  if hs_hardware_module is not None:
59
57
  await hs_hardware_module.deactivate_heater()
60
58
 
61
- return SuccessData(public=DeactivateHeaterResult(), private=None)
59
+ return SuccessData(
60
+ public=DeactivateHeaterResult(),
61
+ )
62
62
 
63
63
 
64
64
  class DeactivateHeater(
@@ -9,7 +9,7 @@ from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, Succe
9
9
  from ...errors.error_occurrence import ErrorOccurrence
10
10
 
11
11
  if TYPE_CHECKING:
12
- from opentrons.protocol_engine.state import StateView
12
+ from opentrons.protocol_engine.state.state import StateView
13
13
  from opentrons.protocol_engine.execution import EquipmentHandler
14
14
 
15
15
  DeactivateShakerCommandType = Literal["heaterShaker/deactivateShaker"]
@@ -26,9 +26,7 @@ class DeactivateShakerResult(BaseModel):
26
26
 
27
27
 
28
28
  class DeactivateShakerImpl(
29
- AbstractCommandImpl[
30
- DeactivateShakerParams, SuccessData[DeactivateShakerResult, None]
31
- ]
29
+ AbstractCommandImpl[DeactivateShakerParams, SuccessData[DeactivateShakerResult]]
32
30
  ):
33
31
  """Execution implementation of a Heater-Shaker's deactivate shaker command."""
34
32
 
@@ -43,7 +41,7 @@ class DeactivateShakerImpl(
43
41
 
44
42
  async def execute(
45
43
  self, params: DeactivateShakerParams
46
- ) -> SuccessData[DeactivateShakerResult, None]:
44
+ ) -> SuccessData[DeactivateShakerResult]:
47
45
  """Deactivate shaker for a Heater-Shaker."""
48
46
  # Allow propagation of ModuleNotLoadedError and WrongModuleTypeError.
49
47
  hs_module_substate = self._state_view.modules.get_heater_shaker_module_substate(
@@ -60,7 +58,9 @@ class DeactivateShakerImpl(
60
58
  if hs_hardware_module is not None:
61
59
  await hs_hardware_module.deactivate_shaker()
62
60
 
63
- return SuccessData(public=DeactivateShakerResult(), private=None)
61
+ return SuccessData(
62
+ public=DeactivateShakerResult(),
63
+ )
64
64
 
65
65
 
66
66
  class DeactivateShaker(
@@ -6,9 +6,10 @@ from pydantic import BaseModel, Field
6
6
 
7
7
  from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
8
8
  from ...errors.error_occurrence import ErrorOccurrence
9
+ from ...state import update_types
9
10
 
10
11
  if TYPE_CHECKING:
11
- from opentrons.protocol_engine.state import StateView
12
+ from opentrons.protocol_engine.state.state import StateView
12
13
  from opentrons.protocol_engine.execution import EquipmentHandler, MovementHandler
13
14
 
14
15
  OpenLabwareLatchCommandType = Literal["heaterShaker/openLabwareLatch"]
@@ -33,9 +34,7 @@ class OpenLabwareLatchResult(BaseModel):
33
34
 
34
35
 
35
36
  class OpenLabwareLatchImpl(
36
- AbstractCommandImpl[
37
- OpenLabwareLatchParams, SuccessData[OpenLabwareLatchResult, None]
38
- ]
37
+ AbstractCommandImpl[OpenLabwareLatchParams, SuccessData[OpenLabwareLatchResult]]
39
38
  ):
40
39
  """Execution implementation of a Heater-Shaker's open latch labware command."""
41
40
 
@@ -52,8 +51,10 @@ class OpenLabwareLatchImpl(
52
51
 
53
52
  async def execute(
54
53
  self, params: OpenLabwareLatchParams
55
- ) -> SuccessData[OpenLabwareLatchResult, None]:
54
+ ) -> SuccessData[OpenLabwareLatchResult]:
56
55
  """Open a Heater-Shaker's labware latch."""
56
+ state_update = update_types.StateUpdate()
57
+
57
58
  # Allow propagation of ModuleNotLoadedError and WrongModuleTypeError.
58
59
  hs_module_substate = self._state_view.modules.get_heater_shaker_module_substate(
59
60
  module_id=params.moduleId
@@ -72,6 +73,7 @@ class OpenLabwareLatchImpl(
72
73
  await self._movement.home(
73
74
  axes=self._state_view.motion.get_robot_mount_axes()
74
75
  )
76
+ state_update.clear_all_pipette_locations()
75
77
 
76
78
  # Allow propagation of ModuleNotAttachedError.
77
79
  hs_hardware_module = self._equipment.get_module_hardware_api(
@@ -83,7 +85,7 @@ class OpenLabwareLatchImpl(
83
85
 
84
86
  return SuccessData(
85
87
  public=OpenLabwareLatchResult(pipetteRetracted=pipette_should_retract),
86
- private=None,
88
+ state_update=state_update,
87
89
  )
88
90
 
89
91
 
@@ -6,9 +6,10 @@ from pydantic import BaseModel, Field
6
6
 
7
7
  from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
8
8
  from ...errors.error_occurrence import ErrorOccurrence
9
+ from ...state import update_types
9
10
 
10
11
  if TYPE_CHECKING:
11
- from opentrons.protocol_engine.state import StateView
12
+ from opentrons.protocol_engine.state.state import StateView
12
13
  from opentrons.protocol_engine.execution import EquipmentHandler, MovementHandler
13
14
 
14
15
  SetAndWaitForShakeSpeedCommandType = Literal["heaterShaker/setAndWaitForShakeSpeed"]
@@ -35,7 +36,7 @@ class SetAndWaitForShakeSpeedResult(BaseModel):
35
36
 
36
37
  class SetAndWaitForShakeSpeedImpl(
37
38
  AbstractCommandImpl[
38
- SetAndWaitForShakeSpeedParams, SuccessData[SetAndWaitForShakeSpeedResult, None]
39
+ SetAndWaitForShakeSpeedParams, SuccessData[SetAndWaitForShakeSpeedResult]
39
40
  ]
40
41
  ):
41
42
  """Execution implementation of Heater-Shaker's set and wait shake speed command."""
@@ -54,8 +55,10 @@ class SetAndWaitForShakeSpeedImpl(
54
55
  async def execute(
55
56
  self,
56
57
  params: SetAndWaitForShakeSpeedParams,
57
- ) -> SuccessData[SetAndWaitForShakeSpeedResult, None]:
58
+ ) -> SuccessData[SetAndWaitForShakeSpeedResult]:
58
59
  """Set and wait for a Heater-Shaker's target shake speed."""
60
+ state_update = update_types.StateUpdate()
61
+
59
62
  # Allow propagation of ModuleNotLoadedError and WrongModuleTypeError.
60
63
  hs_module_substate = self._state_view.modules.get_heater_shaker_module_substate(
61
64
  module_id=params.moduleId
@@ -77,6 +80,7 @@ class SetAndWaitForShakeSpeedImpl(
77
80
  await self._movement.home(
78
81
  axes=self._state_view.motion.get_robot_mount_axes()
79
82
  )
83
+ state_update.clear_all_pipette_locations()
80
84
 
81
85
  # Allow propagation of ModuleNotAttachedError.
82
86
  hs_hardware_module = self._equipment.get_module_hardware_api(
@@ -90,7 +94,7 @@ class SetAndWaitForShakeSpeedImpl(
90
94
  public=SetAndWaitForShakeSpeedResult(
91
95
  pipetteRetracted=pipette_should_retract
92
96
  ),
93
- private=None,
97
+ state_update=state_update,
94
98
  )
95
99
 
96
100
 
@@ -9,7 +9,7 @@ from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, Succe
9
9
  from ...errors.error_occurrence import ErrorOccurrence
10
10
 
11
11
  if TYPE_CHECKING:
12
- from opentrons.protocol_engine.state import StateView
12
+ from opentrons.protocol_engine.state.state import StateView
13
13
  from opentrons.protocol_engine.execution import EquipmentHandler
14
14
 
15
15
 
@@ -29,7 +29,7 @@ class SetTargetTemperatureResult(BaseModel):
29
29
 
30
30
  class SetTargetTemperatureImpl(
31
31
  AbstractCommandImpl[
32
- SetTargetTemperatureParams, SuccessData[SetTargetTemperatureResult, None]
32
+ SetTargetTemperatureParams, SuccessData[SetTargetTemperatureResult]
33
33
  ]
34
34
  ):
35
35
  """Execution implementation of a Heater-Shaker's set temperature command."""
@@ -46,7 +46,7 @@ class SetTargetTemperatureImpl(
46
46
  async def execute(
47
47
  self,
48
48
  params: SetTargetTemperatureParams,
49
- ) -> SuccessData[SetTargetTemperatureResult, None]:
49
+ ) -> SuccessData[SetTargetTemperatureResult]:
50
50
  """Set a Heater-Shaker's target temperature."""
51
51
  # Allow propagation of ModuleNotLoadedError and WrongModuleTypeError.
52
52
  hs_module_substate = self._state_view.modules.get_heater_shaker_module_substate(
@@ -64,7 +64,9 @@ class SetTargetTemperatureImpl(
64
64
  if hs_hardware_module is not None:
65
65
  await hs_hardware_module.start_set_temperature(validated_temp)
66
66
 
67
- return SuccessData(public=SetTargetTemperatureResult(), private=None)
67
+ return SuccessData(
68
+ public=SetTargetTemperatureResult(),
69
+ )
68
70
 
69
71
 
70
72
  class SetTargetTemperature(
@@ -9,7 +9,7 @@ from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, Succe
9
9
  from ...errors.error_occurrence import ErrorOccurrence
10
10
 
11
11
  if TYPE_CHECKING:
12
- from opentrons.protocol_engine.state import StateView
12
+ from opentrons.protocol_engine.state.state import StateView
13
13
  from opentrons.protocol_engine.execution import EquipmentHandler
14
14
 
15
15
 
@@ -36,9 +36,7 @@ class WaitForTemperatureResult(BaseModel):
36
36
 
37
37
 
38
38
  class WaitForTemperatureImpl(
39
- AbstractCommandImpl[
40
- WaitForTemperatureParams, SuccessData[WaitForTemperatureResult, None]
41
- ]
39
+ AbstractCommandImpl[WaitForTemperatureParams, SuccessData[WaitForTemperatureResult]]
42
40
  ):
43
41
  """Execution implementation of a Heater-Shaker's wait for temperature command."""
44
42
 
@@ -53,7 +51,7 @@ class WaitForTemperatureImpl(
53
51
 
54
52
  async def execute(
55
53
  self, params: WaitForTemperatureParams
56
- ) -> SuccessData[WaitForTemperatureResult, None]:
54
+ ) -> SuccessData[WaitForTemperatureResult]:
57
55
  """Wait for a Heater-Shaker's target temperature to be reached."""
58
56
  hs_module_substate = self._state_view.modules.get_heater_shaker_module_substate(
59
57
  module_id=params.moduleId
@@ -72,7 +70,9 @@ class WaitForTemperatureImpl(
72
70
  if hs_hardware_module is not None:
73
71
  await hs_hardware_module.await_temperature(awaiting_temperature=target_temp)
74
72
 
75
- return SuccessData(public=WaitForTemperatureResult(), private=None)
73
+ return SuccessData(
74
+ public=WaitForTemperatureResult(),
75
+ )
76
76
 
77
77
 
78
78
  class WaitForTemperature(
@@ -5,6 +5,7 @@ from typing import TYPE_CHECKING, Optional, List, Type
5
5
  from typing_extensions import Literal
6
6
 
7
7
  from opentrons.types import MountType
8
+ from ..state import update_types
8
9
  from ..types import MotorAxis
9
10
  from .command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
10
11
  from ..errors.error_occurrence import ErrorOccurrence
@@ -41,16 +42,16 @@ class HomeResult(BaseModel):
41
42
  """Result data from the execution of a Home command."""
42
43
 
43
44
 
44
- class HomeImplementation(
45
- AbstractCommandImpl[HomeParams, SuccessData[HomeResult, None]]
46
- ):
45
+ class HomeImplementation(AbstractCommandImpl[HomeParams, SuccessData[HomeResult]]):
47
46
  """Home command implementation."""
48
47
 
49
48
  def __init__(self, movement: MovementHandler, **kwargs: object) -> None:
50
49
  self._movement = movement
51
50
 
52
- async def execute(self, params: HomeParams) -> SuccessData[HomeResult, None]:
51
+ async def execute(self, params: HomeParams) -> SuccessData[HomeResult]:
53
52
  """Home some or all motors to establish positional accuracy."""
53
+ state_update = update_types.StateUpdate()
54
+
54
55
  if (
55
56
  params.skipIfMountPositionOk is None
56
57
  or not await self._movement.check_for_valid_position(
@@ -58,7 +59,12 @@ class HomeImplementation(
58
59
  )
59
60
  ):
60
61
  await self._movement.home(axes=params.axes)
61
- return SuccessData(public=HomeResult(), private=None)
62
+
63
+ # todo(mm, 2024-09-17): Clearing all pipette locations *unconditionally* is to
64
+ # preserve prior behavior, but we might only want to do this if we actually home.
65
+ state_update.clear_all_pipette_locations()
66
+
67
+ return SuccessData(public=HomeResult(), state_update=state_update)
62
68
 
63
69
 
64
70
  class Home(BaseCommand[HomeParams, HomeResult, ErrorOccurrence]):
@@ -1,24 +1,26 @@
1
1
  """The liquidProbe and tryLiquidProbe commands."""
2
2
 
3
3
  from __future__ import annotations
4
- from typing import TYPE_CHECKING, Optional, Type, Union
4
+ from typing import TYPE_CHECKING, NamedTuple, Optional, Type, Union
5
+ from typing_extensions import Literal
6
+
7
+ from pydantic import Field
8
+
9
+ from opentrons.protocol_engine.state import update_types
5
10
  from opentrons.protocol_engine.errors.exceptions import (
6
11
  MustHomeError,
7
12
  PipetteNotReadyToAspirateError,
8
13
  TipNotEmptyError,
14
+ IncompleteLabwareDefinitionError,
9
15
  )
10
16
  from opentrons.types import MountType
11
17
  from opentrons_shared_data.errors.exceptions import (
12
18
  PipetteLiquidNotFoundError,
13
19
  )
14
- from typing_extensions import Literal
15
-
16
- from pydantic import Field
17
20
 
18
21
  from ..types import DeckPoint
19
22
  from .pipetting_common import (
20
23
  LiquidNotFoundError,
21
- LiquidNotFoundErrorInternalData,
22
24
  PipetteIdMixin,
23
25
  WellLocationMixin,
24
26
  DestinationPositionResult,
@@ -36,7 +38,7 @@ from ..errors.error_occurrence import ErrorOccurrence
36
38
  if TYPE_CHECKING:
37
39
  from ..execution import MovementHandler, PipettingHandler
38
40
  from ..resources import ModelUtils
39
- from ..state import StateView
41
+ from ..state.state import StateView
40
42
 
41
43
 
42
44
  LiquidProbeCommandType = Literal["liquidProbe"]
@@ -84,10 +86,86 @@ class TryLiquidProbeResult(DestinationPositionResult):
84
86
 
85
87
 
86
88
  _LiquidProbeExecuteReturn = Union[
87
- SuccessData[LiquidProbeResult, None],
88
- DefinedErrorData[LiquidNotFoundError, LiquidNotFoundErrorInternalData],
89
+ SuccessData[LiquidProbeResult],
90
+ DefinedErrorData[LiquidNotFoundError],
89
91
  ]
90
- _TryLiquidProbeExecuteReturn = SuccessData[TryLiquidProbeResult, None]
92
+ _TryLiquidProbeExecuteReturn = SuccessData[TryLiquidProbeResult]
93
+
94
+
95
+ class _ExecuteCommonResult(NamedTuple):
96
+ # If the probe succeeded, the z_pos that it returned.
97
+ # Or, if the probe found no liquid, the error representing that,
98
+ # so calling code can propagate those details up.
99
+ z_pos_or_error: float | PipetteLiquidNotFoundError
100
+
101
+ state_update: update_types.StateUpdate
102
+ deck_point: DeckPoint
103
+
104
+
105
+ async def _execute_common(
106
+ state_view: StateView,
107
+ movement: MovementHandler,
108
+ pipetting: PipettingHandler,
109
+ params: _CommonParams,
110
+ ) -> _ExecuteCommonResult:
111
+ pipette_id = params.pipetteId
112
+ labware_id = params.labwareId
113
+ well_name = params.wellName
114
+
115
+ state_update = update_types.StateUpdate()
116
+
117
+ # May raise TipNotAttachedError.
118
+ aspirated_volume = state_view.pipettes.get_aspirated_volume(pipette_id)
119
+
120
+ if aspirated_volume is None:
121
+ # Theoretically, we could avoid raising an error by automatically preparing
122
+ # to aspirate above the well like AspirateImplementation does. However, the
123
+ # only way for this to happen is if someone tries to do a liquid probe with
124
+ # a tip that's previously held liquid, which they should avoid anyway.
125
+ raise PipetteNotReadyToAspirateError(
126
+ "The pipette cannot probe liquid because of a previous blow out."
127
+ " The plunger must be reset while the tip is somewhere away from liquid."
128
+ )
129
+ elif aspirated_volume != 0:
130
+ raise TipNotEmptyError(
131
+ message="The pipette cannot probe for liquid when the tip has liquid in it."
132
+ )
133
+
134
+ if await movement.check_for_valid_position(mount=MountType.LEFT) is False:
135
+ raise MustHomeError(
136
+ message="Current position of pipette is invalid. Please home."
137
+ )
138
+
139
+ # liquid_probe process start position
140
+ position = await movement.move_to_well(
141
+ pipette_id=pipette_id,
142
+ labware_id=labware_id,
143
+ well_name=well_name,
144
+ well_location=params.wellLocation,
145
+ )
146
+ deck_point = DeckPoint.construct(x=position.x, y=position.y, z=position.z)
147
+ state_update.set_pipette_location(
148
+ pipette_id=pipette_id,
149
+ new_labware_id=labware_id,
150
+ new_well_name=well_name,
151
+ new_deck_point=deck_point,
152
+ )
153
+
154
+ try:
155
+ z_pos = await pipetting.liquid_probe_in_place(
156
+ pipette_id=pipette_id,
157
+ labware_id=labware_id,
158
+ well_name=well_name,
159
+ well_location=params.wellLocation,
160
+ )
161
+ except PipetteLiquidNotFoundError as exception:
162
+ return _ExecuteCommonResult(
163
+ z_pos_or_error=exception, state_update=state_update, deck_point=deck_point
164
+ )
165
+ else:
166
+ return _ExecuteCommonResult(
167
+ z_pos_or_error=z_pos, state_update=state_update, deck_point=deck_point
168
+ )
91
169
 
92
170
 
93
171
  class LiquidProbeImplementation(
@@ -124,48 +202,17 @@ class LiquidProbeImplementation(
124
202
  MustHomeError: as an undefined error, if the plunger is not in a valid
125
203
  position.
126
204
  """
127
- pipette_id = params.pipetteId
128
- labware_id = params.labwareId
129
- well_name = params.wellName
130
-
131
- # May raise TipNotAttachedError.
132
- aspirated_volume = self._state_view.pipettes.get_aspirated_volume(pipette_id)
133
-
134
- if aspirated_volume is None:
135
- # Theoretically, we could avoid raising an error by automatically preparing
136
- # to aspirate above the well like AspirateImplementation does. However, the
137
- # only way for this to happen is if someone tries to do a liquid probe with
138
- # a tip that's previously held liquid, which they should avoid anyway.
139
- raise PipetteNotReadyToAspirateError(
140
- "The pipette cannot probe liquid because of a previous blow out."
141
- " The plunger must be reset while the tip is somewhere away from liquid."
142
- )
143
- elif aspirated_volume != 0:
144
- raise TipNotEmptyError(
145
- message="The pipette cannot probe for liquid when the tip has liquid in it."
146
- )
147
-
148
- if await self._movement.check_for_valid_position(mount=MountType.LEFT) is False:
149
- raise MustHomeError(
150
- message="Current position of pipette is invalid. Please home."
151
- )
152
-
153
- # liquid_probe process start position
154
- position = await self._movement.move_to_well(
155
- pipette_id=pipette_id,
156
- labware_id=labware_id,
157
- well_name=well_name,
158
- well_location=params.wellLocation,
205
+ z_pos_or_error, state_update, deck_point = await _execute_common(
206
+ self._state_view, self._movement, self._pipetting, params
159
207
  )
160
-
161
- try:
162
- z_pos = await self._pipetting.liquid_probe_in_place(
163
- pipette_id=pipette_id,
164
- labware_id=labware_id,
165
- well_name=well_name,
166
- well_location=params.wellLocation,
208
+ if isinstance(z_pos_or_error, PipetteLiquidNotFoundError):
209
+ state_update.set_liquid_probed(
210
+ labware_id=params.labwareId,
211
+ well_name=params.wellName,
212
+ height=update_types.CLEAR,
213
+ volume=update_types.CLEAR,
214
+ last_probed=self._model_utils.get_timestamp(),
167
215
  )
168
- except PipetteLiquidNotFoundError as e:
169
216
  return DefinedErrorData(
170
217
  public=LiquidNotFoundError(
171
218
  id=self._model_utils.generate_id(),
@@ -174,21 +221,35 @@ class LiquidProbeImplementation(
174
221
  ErrorOccurrence.from_failed(
175
222
  id=self._model_utils.generate_id(),
176
223
  createdAt=self._model_utils.get_timestamp(),
177
- error=e,
224
+ error=z_pos_or_error,
178
225
  )
179
226
  ],
180
227
  ),
181
- private=LiquidNotFoundErrorInternalData(
182
- position=DeckPoint(x=position.x, y=position.y, z=position.z)
183
- ),
228
+ state_update=state_update,
184
229
  )
185
230
  else:
231
+ try:
232
+ well_volume: float | update_types.ClearType = (
233
+ self._state_view.geometry.get_well_volume_at_height(
234
+ labware_id=params.labwareId,
235
+ well_name=params.wellName,
236
+ height=z_pos_or_error,
237
+ )
238
+ )
239
+ except IncompleteLabwareDefinitionError:
240
+ well_volume = update_types.CLEAR
241
+ state_update.set_liquid_probed(
242
+ labware_id=params.labwareId,
243
+ well_name=params.wellName,
244
+ height=z_pos_or_error,
245
+ volume=well_volume,
246
+ last_probed=self._model_utils.get_timestamp(),
247
+ )
186
248
  return SuccessData(
187
249
  public=LiquidProbeResult(
188
- z_position=z_pos,
189
- position=DeckPoint(x=position.x, y=position.y, z=position.z),
250
+ z_position=z_pos_or_error, position=deck_point
190
251
  ),
191
- private=None,
252
+ state_update=state_update,
192
253
  )
193
254
 
194
255
 
@@ -217,40 +278,37 @@ class TryLiquidProbeImplementation(
217
278
  found, `tryLiquidProbe` returns a success result with `z_position=null` instead
218
279
  of a defined error.
219
280
  """
220
- # We defer to the `liquidProbe` implementation. If it returns a defined
221
- # `liquidNotFound` error, we remap that to a success result.
222
- # Otherwise, we return the result or propagate the exception unchanged.
223
-
224
- original_impl = LiquidProbeImplementation(
225
- state_view=self._state_view,
226
- movement=self._movement,
227
- pipetting=self._pipetting,
228
- model_utils=self._model_utils,
281
+ z_pos_or_error, state_update, deck_point = await _execute_common(
282
+ self._state_view, self._movement, self._pipetting, params
229
283
  )
230
- original_result = await original_impl.execute(params)
231
-
232
- match original_result:
233
- case DefinedErrorData(
234
- public=LiquidNotFoundError(),
235
- private=LiquidNotFoundErrorInternalData() as original_private,
236
- ):
237
- return SuccessData(
238
- public=TryLiquidProbeResult(
239
- z_position=None,
240
- position=original_private.position,
241
- ),
242
- private=None,
243
- )
244
- case SuccessData(
245
- public=LiquidProbeResult() as original_public, private=None
246
- ):
247
- return SuccessData(
248
- public=TryLiquidProbeResult(
249
- position=original_public.position,
250
- z_position=original_public.z_position,
251
- ),
252
- private=None,
284
+
285
+ if isinstance(z_pos_or_error, PipetteLiquidNotFoundError):
286
+ z_pos = None
287
+ well_volume: float | update_types.ClearType = update_types.CLEAR
288
+ else:
289
+ z_pos = z_pos_or_error
290
+ try:
291
+ well_volume = self._state_view.geometry.get_well_volume_at_height(
292
+ labware_id=params.labwareId, well_name=params.wellName, height=z_pos
253
293
  )
294
+ except IncompleteLabwareDefinitionError:
295
+ well_volume = update_types.CLEAR
296
+
297
+ state_update.set_liquid_probed(
298
+ labware_id=params.labwareId,
299
+ well_name=params.wellName,
300
+ height=z_pos if z_pos is not None else update_types.CLEAR,
301
+ volume=well_volume,
302
+ last_probed=self._model_utils.get_timestamp(),
303
+ )
304
+
305
+ return SuccessData(
306
+ public=TryLiquidProbeResult(
307
+ z_position=z_pos,
308
+ position=deck_point,
309
+ ),
310
+ state_update=state_update,
311
+ )
254
312
 
255
313
 
256
314
  class LiquidProbe(
@@ -17,9 +17,10 @@ from ..types import (
17
17
 
18
18
  from .command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
19
19
  from ..errors.error_occurrence import ErrorOccurrence
20
+ from ..state.update_types import StateUpdate
20
21
 
21
22
  if TYPE_CHECKING:
22
- from ..state import StateView
23
+ from ..state.state import StateView
23
24
  from ..execution import EquipmentHandler
24
25
 
25
26
 
@@ -87,7 +88,7 @@ class LoadLabwareResult(BaseModel):
87
88
 
88
89
 
89
90
  class LoadLabwareImplementation(
90
- AbstractCommandImpl[LoadLabwareParams, SuccessData[LoadLabwareResult, None]]
91
+ AbstractCommandImpl[LoadLabwareParams, SuccessData[LoadLabwareResult]]
91
92
  ):
92
93
  """Load labware command implementation."""
93
94
 
@@ -99,7 +100,7 @@ class LoadLabwareImplementation(
99
100
 
100
101
  async def execute(
101
102
  self, params: LoadLabwareParams
102
- ) -> SuccessData[LoadLabwareResult, None]:
103
+ ) -> SuccessData[LoadLabwareResult]:
103
104
  """Load definition and calibration data necessary for a labware."""
104
105
  # TODO (tz, 8-15-2023): extend column validation to column 1 when working
105
106
  # on https://opentrons.atlassian.net/browse/RSS-258 and completing
@@ -115,7 +116,10 @@ class LoadLabwareImplementation(
115
116
 
116
117
  if isinstance(params.location, AddressableAreaLocation):
117
118
  area_name = params.location.addressableAreaName
118
- if not fixture_validation.is_deck_slot(params.location.addressableAreaName):
119
+ if not (
120
+ fixture_validation.is_deck_slot(params.location.addressableAreaName)
121
+ or fixture_validation.is_abs_reader(params.location.addressableAreaName)
122
+ ):
119
123
  raise LabwareIsNotAllowedInLocationError(
120
124
  f"Cannot load {params.loadName} onto addressable area {area_name}"
121
125
  )
@@ -138,6 +142,16 @@ class LoadLabwareImplementation(
138
142
  labware_id=params.labwareId,
139
143
  )
140
144
 
145
+ state_update = StateUpdate()
146
+
147
+ state_update.set_loaded_labware(
148
+ labware_id=loaded_labware.labware_id,
149
+ offset_id=loaded_labware.offsetId,
150
+ definition=loaded_labware.definition,
151
+ location=verified_location,
152
+ display_name=params.displayName,
153
+ )
154
+
141
155
  # TODO(jbl 2023-06-23) these validation checks happen after the labware is loaded, because they rely on
142
156
  # on the definition. In practice this will not cause any issues since they will raise protocol ending
143
157
  # exception, but for correctness should be refactored to do this check beforehand.
@@ -153,7 +167,7 @@ class LoadLabwareImplementation(
153
167
  definition=loaded_labware.definition,
154
168
  offsetId=loaded_labware.offsetId,
155
169
  ),
156
- private=None,
170
+ state_update=state_update,
157
171
  )
158
172
 
159
173