opentrons 8.1.0a0__py2.py3-none-any.whl → 8.2.0a0__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (230) hide show
  1. opentrons/cli/analyze.py +71 -7
  2. opentrons/config/__init__.py +9 -0
  3. opentrons/config/advanced_settings.py +22 -0
  4. opentrons/config/defaults_ot3.py +14 -36
  5. opentrons/config/feature_flags.py +4 -0
  6. opentrons/config/types.py +6 -17
  7. opentrons/drivers/absorbance_reader/abstract.py +27 -3
  8. opentrons/drivers/absorbance_reader/async_byonoy.py +207 -154
  9. opentrons/drivers/absorbance_reader/driver.py +24 -15
  10. opentrons/drivers/absorbance_reader/hid_protocol.py +79 -50
  11. opentrons/drivers/absorbance_reader/simulator.py +32 -6
  12. opentrons/drivers/types.py +23 -1
  13. opentrons/execute.py +2 -2
  14. opentrons/hardware_control/api.py +18 -10
  15. opentrons/hardware_control/backends/controller.py +3 -2
  16. opentrons/hardware_control/backends/flex_protocol.py +11 -5
  17. opentrons/hardware_control/backends/ot3controller.py +18 -50
  18. opentrons/hardware_control/backends/ot3simulator.py +7 -6
  19. opentrons/hardware_control/instruments/ot2/pipette_handler.py +22 -82
  20. opentrons/hardware_control/instruments/ot3/pipette_handler.py +10 -2
  21. opentrons/hardware_control/module_control.py +43 -2
  22. opentrons/hardware_control/modules/__init__.py +7 -1
  23. opentrons/hardware_control/modules/absorbance_reader.py +230 -83
  24. opentrons/hardware_control/modules/errors.py +7 -0
  25. opentrons/hardware_control/modules/heater_shaker.py +8 -3
  26. opentrons/hardware_control/modules/magdeck.py +12 -3
  27. opentrons/hardware_control/modules/mod_abc.py +27 -2
  28. opentrons/hardware_control/modules/tempdeck.py +15 -7
  29. opentrons/hardware_control/modules/thermocycler.py +69 -3
  30. opentrons/hardware_control/modules/types.py +11 -5
  31. opentrons/hardware_control/modules/update.py +11 -5
  32. opentrons/hardware_control/modules/utils.py +3 -1
  33. opentrons/hardware_control/ot3_calibration.py +6 -6
  34. opentrons/hardware_control/ot3api.py +126 -89
  35. opentrons/hardware_control/poller.py +15 -11
  36. opentrons/hardware_control/protocols/__init__.py +1 -7
  37. opentrons/hardware_control/protocols/instrument_configurer.py +14 -2
  38. opentrons/hardware_control/protocols/liquid_handler.py +5 -0
  39. opentrons/motion_planning/__init__.py +2 -0
  40. opentrons/motion_planning/waypoints.py +32 -0
  41. opentrons/protocol_api/__init__.py +2 -1
  42. opentrons/protocol_api/_liquid.py +87 -1
  43. opentrons/protocol_api/_parameter_context.py +10 -1
  44. opentrons/protocol_api/core/engine/deck_conflict.py +0 -297
  45. opentrons/protocol_api/core/engine/instrument.py +29 -25
  46. opentrons/protocol_api/core/engine/labware.py +10 -2
  47. opentrons/protocol_api/core/engine/module_core.py +129 -17
  48. opentrons/protocol_api/core/engine/pipette_movement_conflict.py +355 -0
  49. opentrons/protocol_api/core/engine/protocol.py +55 -2
  50. opentrons/protocol_api/core/instrument.py +2 -0
  51. opentrons/protocol_api/core/legacy/legacy_instrument_core.py +2 -0
  52. opentrons/protocol_api/core/legacy/legacy_protocol_core.py +5 -2
  53. opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +2 -0
  54. opentrons/protocol_api/core/module.py +22 -4
  55. opentrons/protocol_api/core/protocol.py +5 -2
  56. opentrons/protocol_api/instrument_context.py +52 -20
  57. opentrons/protocol_api/labware.py +13 -1
  58. opentrons/protocol_api/module_contexts.py +68 -13
  59. opentrons/protocol_api/protocol_context.py +38 -4
  60. opentrons/protocol_api/validation.py +5 -3
  61. opentrons/protocol_engine/__init__.py +10 -9
  62. opentrons/protocol_engine/actions/__init__.py +5 -0
  63. opentrons/protocol_engine/actions/actions.py +42 -25
  64. opentrons/protocol_engine/actions/get_state_update.py +38 -0
  65. opentrons/protocol_engine/clients/sync_client.py +7 -1
  66. opentrons/protocol_engine/clients/transports.py +1 -1
  67. opentrons/protocol_engine/commands/__init__.py +0 -4
  68. opentrons/protocol_engine/commands/absorbance_reader/__init__.py +41 -11
  69. opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +161 -0
  70. opentrons/protocol_engine/commands/absorbance_reader/initialize.py +53 -9
  71. opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +160 -0
  72. opentrons/protocol_engine/commands/absorbance_reader/read.py +196 -0
  73. opentrons/protocol_engine/commands/aspirate.py +29 -16
  74. opentrons/protocol_engine/commands/aspirate_in_place.py +32 -15
  75. opentrons/protocol_engine/commands/blow_out.py +63 -14
  76. opentrons/protocol_engine/commands/blow_out_in_place.py +55 -13
  77. opentrons/protocol_engine/commands/calibration/calibrate_gripper.py +2 -5
  78. opentrons/protocol_engine/commands/calibration/calibrate_module.py +3 -4
  79. opentrons/protocol_engine/commands/calibration/calibrate_pipette.py +2 -5
  80. opentrons/protocol_engine/commands/calibration/move_to_maintenance_position.py +6 -4
  81. opentrons/protocol_engine/commands/command.py +28 -17
  82. opentrons/protocol_engine/commands/command_unions.py +37 -24
  83. opentrons/protocol_engine/commands/comment.py +5 -3
  84. opentrons/protocol_engine/commands/configure_for_volume.py +11 -14
  85. opentrons/protocol_engine/commands/configure_nozzle_layout.py +9 -15
  86. opentrons/protocol_engine/commands/custom.py +5 -3
  87. opentrons/protocol_engine/commands/dispense.py +42 -20
  88. opentrons/protocol_engine/commands/dispense_in_place.py +32 -14
  89. opentrons/protocol_engine/commands/drop_tip.py +68 -15
  90. opentrons/protocol_engine/commands/drop_tip_in_place.py +52 -11
  91. opentrons/protocol_engine/commands/get_tip_presence.py +5 -3
  92. opentrons/protocol_engine/commands/heater_shaker/close_labware_latch.py +6 -6
  93. opentrons/protocol_engine/commands/heater_shaker/deactivate_heater.py +6 -6
  94. opentrons/protocol_engine/commands/heater_shaker/deactivate_shaker.py +6 -6
  95. opentrons/protocol_engine/commands/heater_shaker/open_labware_latch.py +8 -6
  96. opentrons/protocol_engine/commands/heater_shaker/set_and_wait_for_shake_speed.py +8 -4
  97. opentrons/protocol_engine/commands/heater_shaker/set_target_temperature.py +6 -4
  98. opentrons/protocol_engine/commands/heater_shaker/wait_for_temperature.py +6 -6
  99. opentrons/protocol_engine/commands/home.py +11 -5
  100. opentrons/protocol_engine/commands/liquid_probe.py +146 -88
  101. opentrons/protocol_engine/commands/load_labware.py +19 -5
  102. opentrons/protocol_engine/commands/load_liquid.py +18 -7
  103. opentrons/protocol_engine/commands/load_module.py +43 -6
  104. opentrons/protocol_engine/commands/load_pipette.py +18 -17
  105. opentrons/protocol_engine/commands/magnetic_module/disengage.py +6 -6
  106. opentrons/protocol_engine/commands/magnetic_module/engage.py +6 -4
  107. opentrons/protocol_engine/commands/move_labware.py +106 -19
  108. opentrons/protocol_engine/commands/move_relative.py +15 -3
  109. opentrons/protocol_engine/commands/move_to_addressable_area.py +29 -4
  110. opentrons/protocol_engine/commands/move_to_addressable_area_for_drop_tip.py +13 -4
  111. opentrons/protocol_engine/commands/move_to_coordinates.py +11 -5
  112. opentrons/protocol_engine/commands/move_to_well.py +37 -10
  113. opentrons/protocol_engine/commands/pick_up_tip.py +50 -29
  114. opentrons/protocol_engine/commands/pipetting_common.py +39 -15
  115. opentrons/protocol_engine/commands/prepare_to_aspirate.py +62 -15
  116. opentrons/protocol_engine/commands/reload_labware.py +13 -4
  117. opentrons/protocol_engine/commands/retract_axis.py +6 -3
  118. opentrons/protocol_engine/commands/save_position.py +2 -3
  119. opentrons/protocol_engine/commands/set_rail_lights.py +5 -3
  120. opentrons/protocol_engine/commands/set_status_bar.py +5 -3
  121. opentrons/protocol_engine/commands/temperature_module/deactivate.py +6 -4
  122. opentrons/protocol_engine/commands/temperature_module/set_target_temperature.py +3 -4
  123. opentrons/protocol_engine/commands/temperature_module/wait_for_temperature.py +6 -6
  124. opentrons/protocol_engine/commands/thermocycler/__init__.py +19 -0
  125. opentrons/protocol_engine/commands/thermocycler/close_lid.py +8 -8
  126. opentrons/protocol_engine/commands/thermocycler/deactivate_block.py +6 -4
  127. opentrons/protocol_engine/commands/thermocycler/deactivate_lid.py +6 -4
  128. opentrons/protocol_engine/commands/thermocycler/open_lid.py +8 -4
  129. opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py +165 -0
  130. opentrons/protocol_engine/commands/thermocycler/run_profile.py +6 -6
  131. opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py +3 -4
  132. opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py +3 -4
  133. opentrons/protocol_engine/commands/thermocycler/wait_for_block_temperature.py +6 -4
  134. opentrons/protocol_engine/commands/thermocycler/wait_for_lid_temperature.py +6 -4
  135. opentrons/protocol_engine/commands/touch_tip.py +19 -7
  136. opentrons/protocol_engine/commands/unsafe/__init__.py +30 -0
  137. opentrons/protocol_engine/commands/unsafe/unsafe_blow_out_in_place.py +6 -4
  138. opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +12 -4
  139. opentrons/protocol_engine/commands/unsafe/unsafe_engage_axes.py +5 -3
  140. opentrons/protocol_engine/commands/unsafe/unsafe_place_labware.py +194 -0
  141. opentrons/protocol_engine/commands/unsafe/unsafe_ungrip_labware.py +75 -0
  142. opentrons/protocol_engine/commands/unsafe/update_position_estimators.py +5 -3
  143. opentrons/protocol_engine/commands/verify_tip_presence.py +5 -5
  144. opentrons/protocol_engine/commands/wait_for_duration.py +5 -3
  145. opentrons/protocol_engine/commands/wait_for_resume.py +5 -3
  146. opentrons/protocol_engine/create_protocol_engine.py +41 -8
  147. opentrons/protocol_engine/engine_support.py +2 -1
  148. opentrons/protocol_engine/error_recovery_policy.py +14 -3
  149. opentrons/protocol_engine/errors/__init__.py +18 -0
  150. opentrons/protocol_engine/errors/exceptions.py +114 -2
  151. opentrons/protocol_engine/execution/__init__.py +2 -0
  152. opentrons/protocol_engine/execution/command_executor.py +22 -13
  153. opentrons/protocol_engine/execution/create_queue_worker.py +5 -1
  154. opentrons/protocol_engine/execution/door_watcher.py +1 -1
  155. opentrons/protocol_engine/execution/equipment.py +2 -1
  156. opentrons/protocol_engine/execution/error_recovery_hardware_state_synchronizer.py +101 -0
  157. opentrons/protocol_engine/execution/gantry_mover.py +4 -2
  158. opentrons/protocol_engine/execution/hardware_stopper.py +3 -3
  159. opentrons/protocol_engine/execution/heater_shaker_movement_flagger.py +1 -4
  160. opentrons/protocol_engine/execution/labware_movement.py +6 -3
  161. opentrons/protocol_engine/execution/movement.py +8 -3
  162. opentrons/protocol_engine/execution/pipetting.py +7 -4
  163. opentrons/protocol_engine/execution/queue_worker.py +6 -2
  164. opentrons/protocol_engine/execution/run_control.py +1 -1
  165. opentrons/protocol_engine/execution/thermocycler_movement_flagger.py +1 -1
  166. opentrons/protocol_engine/execution/thermocycler_plate_lifter.py +2 -1
  167. opentrons/protocol_engine/execution/tip_handler.py +77 -43
  168. opentrons/protocol_engine/notes/__init__.py +14 -2
  169. opentrons/protocol_engine/notes/notes.py +18 -1
  170. opentrons/protocol_engine/plugins.py +1 -1
  171. opentrons/protocol_engine/protocol_engine.py +54 -31
  172. opentrons/protocol_engine/resources/__init__.py +2 -0
  173. opentrons/protocol_engine/resources/deck_data_provider.py +58 -5
  174. opentrons/protocol_engine/resources/file_provider.py +157 -0
  175. opentrons/protocol_engine/resources/fixture_validation.py +5 -0
  176. opentrons/protocol_engine/resources/labware_validation.py +10 -0
  177. opentrons/protocol_engine/state/__init__.py +0 -70
  178. opentrons/protocol_engine/state/addressable_areas.py +1 -1
  179. opentrons/protocol_engine/state/command_history.py +21 -2
  180. opentrons/protocol_engine/state/commands.py +110 -31
  181. opentrons/protocol_engine/state/files.py +59 -0
  182. opentrons/protocol_engine/state/frustum_helpers.py +440 -0
  183. opentrons/protocol_engine/state/geometry.py +359 -15
  184. opentrons/protocol_engine/state/labware.py +166 -63
  185. opentrons/protocol_engine/state/liquids.py +1 -1
  186. opentrons/protocol_engine/state/module_substates/absorbance_reader_substate.py +19 -3
  187. opentrons/protocol_engine/state/modules.py +167 -85
  188. opentrons/protocol_engine/state/motion.py +16 -9
  189. opentrons/protocol_engine/state/pipettes.py +157 -317
  190. opentrons/protocol_engine/state/state.py +30 -1
  191. opentrons/protocol_engine/state/state_summary.py +3 -0
  192. opentrons/protocol_engine/state/tips.py +69 -114
  193. opentrons/protocol_engine/state/update_types.py +408 -0
  194. opentrons/protocol_engine/state/wells.py +236 -0
  195. opentrons/protocol_engine/types.py +90 -0
  196. opentrons/protocol_reader/file_format_validator.py +83 -15
  197. opentrons/protocol_runner/json_translator.py +21 -5
  198. opentrons/protocol_runner/legacy_command_mapper.py +27 -6
  199. opentrons/protocol_runner/legacy_context_plugin.py +27 -71
  200. opentrons/protocol_runner/protocol_runner.py +6 -3
  201. opentrons/protocol_runner/run_orchestrator.py +26 -6
  202. opentrons/protocols/advanced_control/mix.py +3 -5
  203. opentrons/protocols/advanced_control/transfers.py +125 -56
  204. opentrons/protocols/api_support/constants.py +1 -1
  205. opentrons/protocols/api_support/definitions.py +1 -1
  206. opentrons/protocols/api_support/labware_like.py +4 -4
  207. opentrons/protocols/api_support/tip_tracker.py +2 -2
  208. opentrons/protocols/api_support/types.py +15 -2
  209. opentrons/protocols/api_support/util.py +30 -42
  210. opentrons/protocols/duration/errors.py +1 -1
  211. opentrons/protocols/duration/estimator.py +50 -29
  212. opentrons/protocols/execution/dev_types.py +2 -2
  213. opentrons/protocols/execution/execute_json_v4.py +15 -10
  214. opentrons/protocols/execution/execute_python.py +8 -3
  215. opentrons/protocols/geometry/planning.py +12 -12
  216. opentrons/protocols/labware.py +17 -33
  217. opentrons/simulate.py +3 -3
  218. opentrons/types.py +30 -3
  219. opentrons/util/logging_config.py +34 -0
  220. {opentrons-8.1.0a0.dist-info → opentrons-8.2.0a0.dist-info}/METADATA +5 -4
  221. {opentrons-8.1.0a0.dist-info → opentrons-8.2.0a0.dist-info}/RECORD +227 -215
  222. opentrons/protocol_engine/commands/absorbance_reader/measure.py +0 -94
  223. opentrons/protocol_engine/commands/configuring_common.py +0 -26
  224. opentrons/protocol_runner/thread_async_queue.py +0 -174
  225. /opentrons/protocol_engine/state/{abstract_store.py → _abstract_store.py} +0 -0
  226. /opentrons/protocol_engine/state/{move_types.py → _move_types.py} +0 -0
  227. {opentrons-8.1.0a0.dist-info → opentrons-8.2.0a0.dist-info}/LICENSE +0 -0
  228. {opentrons-8.1.0a0.dist-info → opentrons-8.2.0a0.dist-info}/WHEEL +0 -0
  229. {opentrons-8.1.0a0.dist-info → opentrons-8.2.0a0.dist-info}/entry_points.txt +0 -0
  230. {opentrons-8.1.0a0.dist-info → opentrons-8.2.0a0.dist-info}/top_level.txt +0 -0
@@ -4,6 +4,8 @@ from pydantic import Field
4
4
  from typing import TYPE_CHECKING, Optional, Type
5
5
  from typing_extensions import Literal
6
6
 
7
+ from opentrons.protocol_engine.state import update_types
8
+
7
9
  from ..errors import TouchTipDisabledError, LabwareIsTipRackError
8
10
  from ..types import DeckPoint
9
11
  from .command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
@@ -16,7 +18,7 @@ from .pipetting_common import (
16
18
 
17
19
  if TYPE_CHECKING:
18
20
  from ..execution import MovementHandler, GantryMover
19
- from ..state import StateView
21
+ from ..state.state import StateView
20
22
 
21
23
 
22
24
  TouchTipCommandType = Literal["touchTip"]
@@ -48,7 +50,7 @@ class TouchTipResult(DestinationPositionResult):
48
50
 
49
51
 
50
52
  class TouchTipImplementation(
51
- AbstractCommandImpl[TouchTipParams, SuccessData[TouchTipResult, None]]
53
+ AbstractCommandImpl[TouchTipParams, SuccessData[TouchTipResult]]
52
54
  ):
53
55
  """Touch tip command implementation."""
54
56
 
@@ -63,14 +65,14 @@ class TouchTipImplementation(
63
65
  self._movement = movement
64
66
  self._gantry_mover = gantry_mover
65
67
 
66
- async def execute(
67
- self, params: TouchTipParams
68
- ) -> SuccessData[TouchTipResult, None]:
68
+ async def execute(self, params: TouchTipParams) -> SuccessData[TouchTipResult]:
69
69
  """Touch tip to sides of a well using the requested pipette."""
70
70
  pipette_id = params.pipetteId
71
71
  labware_id = params.labwareId
72
72
  well_name = params.wellName
73
73
 
74
+ state_update = update_types.StateUpdate()
75
+
74
76
  if self._state_view.labware.get_has_quirk(labware_id, "touchTipDisabled"):
75
77
  raise TouchTipDisabledError(
76
78
  f"Touch tip not allowed on labware {labware_id}"
@@ -98,14 +100,24 @@ class TouchTipImplementation(
98
100
  center_point=center_point,
99
101
  )
100
102
 
101
- x, y, z = await self._gantry_mover.move_to(
103
+ final_point = await self._gantry_mover.move_to(
102
104
  pipette_id=pipette_id,
103
105
  waypoints=touch_waypoints,
104
106
  speed=touch_speed,
105
107
  )
108
+ final_deck_point = DeckPoint.construct(
109
+ x=final_point.x, y=final_point.y, z=final_point.z
110
+ )
111
+ state_update.set_pipette_location(
112
+ pipette_id=pipette_id,
113
+ new_labware_id=labware_id,
114
+ new_well_name=well_name,
115
+ new_deck_point=final_deck_point,
116
+ )
106
117
 
107
118
  return SuccessData(
108
- public=TouchTipResult(position=DeckPoint(x=x, y=y, z=z)), private=None
119
+ public=TouchTipResult(position=final_deck_point),
120
+ state_update=state_update,
109
121
  )
110
122
 
111
123
 
@@ -31,6 +31,24 @@ from .unsafe_engage_axes import (
31
31
  UnsafeEngageAxesCreate,
32
32
  )
33
33
 
34
+ from .unsafe_ungrip_labware import (
35
+ UnsafeUngripLabwareCommandType,
36
+ UnsafeUngripLabwareParams,
37
+ UnsafeUngripLabwareResult,
38
+ UnsafeUngripLabware,
39
+ UnsafeUngripLabwareCreate,
40
+ )
41
+
42
+
43
+ from .unsafe_place_labware import (
44
+ UnsafePlaceLabwareCommandType,
45
+ UnsafePlaceLabwareParams,
46
+ UnsafePlaceLabwareResult,
47
+ UnsafePlaceLabware,
48
+ UnsafePlaceLabwareCreate,
49
+ )
50
+
51
+
34
52
  __all__ = [
35
53
  # Unsafe blow-out-in-place command models
36
54
  "UnsafeBlowOutInPlaceCommandType",
@@ -56,4 +74,16 @@ __all__ = [
56
74
  "UnsafeEngageAxesResult",
57
75
  "UnsafeEngageAxes",
58
76
  "UnsafeEngageAxesCreate",
77
+ # Unsafe ungrip labware
78
+ "UnsafeUngripLabwareCommandType",
79
+ "UnsafeUngripLabwareParams",
80
+ "UnsafeUngripLabwareResult",
81
+ "UnsafeUngripLabware",
82
+ "UnsafeUngripLabwareCreate",
83
+ # Unsafe place labware
84
+ "UnsafePlaceLabwareCommandType",
85
+ "UnsafePlaceLabwareParams",
86
+ "UnsafePlaceLabwareResult",
87
+ "UnsafePlaceLabware",
88
+ "UnsafePlaceLabwareCreate",
59
89
  ]
@@ -16,7 +16,7 @@ from opentrons.hardware_control.types import Axis
16
16
 
17
17
  if TYPE_CHECKING:
18
18
  from ...execution import PipettingHandler
19
- from ...state import StateView
19
+ from ...state.state import StateView
20
20
 
21
21
 
22
22
  UnsafeBlowOutInPlaceCommandType = Literal["unsafe/blowOutInPlace"]
@@ -36,7 +36,7 @@ class UnsafeBlowOutInPlaceResult(BaseModel):
36
36
 
37
37
  class UnsafeBlowOutInPlaceImplementation(
38
38
  AbstractCommandImpl[
39
- UnsafeBlowOutInPlaceParams, SuccessData[UnsafeBlowOutInPlaceResult, None]
39
+ UnsafeBlowOutInPlaceParams, SuccessData[UnsafeBlowOutInPlaceResult]
40
40
  ]
41
41
  ):
42
42
  """UnsafeBlowOutInPlace command implementation."""
@@ -54,7 +54,7 @@ class UnsafeBlowOutInPlaceImplementation(
54
54
 
55
55
  async def execute(
56
56
  self, params: UnsafeBlowOutInPlaceParams
57
- ) -> SuccessData[UnsafeBlowOutInPlaceResult, None]:
57
+ ) -> SuccessData[UnsafeBlowOutInPlaceResult]:
58
58
  """Blow-out without moving the pipette even when position is unknown."""
59
59
  ot3_hardware_api = ensure_ot3_hardware(self._hardware_api)
60
60
  pipette_location = self._state_view.motion.get_pipette_location(
@@ -67,7 +67,9 @@ class UnsafeBlowOutInPlaceImplementation(
67
67
  pipette_id=params.pipetteId, flow_rate=params.flowRate
68
68
  )
69
69
 
70
- return SuccessData(public=UnsafeBlowOutInPlaceResult(), private=None)
70
+ return SuccessData(
71
+ public=UnsafeBlowOutInPlaceResult(),
72
+ )
71
73
 
72
74
 
73
75
  class UnsafeBlowOutInPlace(
@@ -1,5 +1,6 @@
1
1
  """Command models to drop tip in place while plunger positions are unknown."""
2
2
  from __future__ import annotations
3
+ from opentrons.protocol_engine.state.update_types import StateUpdate
3
4
  from pydantic import Field, BaseModel
4
5
  from typing import TYPE_CHECKING, Optional, Type
5
6
  from typing_extensions import Literal
@@ -14,7 +15,7 @@ from ...resources import ensure_ot3_hardware
14
15
 
15
16
  if TYPE_CHECKING:
16
17
  from ...execution import TipHandler
17
- from ...state import StateView
18
+ from ...state.state import StateView
18
19
 
19
20
 
20
21
  UnsafeDropTipInPlaceCommandType = Literal["unsafe/dropTipInPlace"]
@@ -41,7 +42,7 @@ class UnsafeDropTipInPlaceResult(BaseModel):
41
42
 
42
43
  class UnsafeDropTipInPlaceImplementation(
43
44
  AbstractCommandImpl[
44
- UnsafeDropTipInPlaceParams, SuccessData[UnsafeDropTipInPlaceResult, None]
45
+ UnsafeDropTipInPlaceParams, SuccessData[UnsafeDropTipInPlaceResult]
45
46
  ]
46
47
  ):
47
48
  """Unsafe drop tip in place command implementation."""
@@ -59,7 +60,7 @@ class UnsafeDropTipInPlaceImplementation(
59
60
 
60
61
  async def execute(
61
62
  self, params: UnsafeDropTipInPlaceParams
62
- ) -> SuccessData[UnsafeDropTipInPlaceResult, None]:
63
+ ) -> SuccessData[UnsafeDropTipInPlaceResult]:
63
64
  """Drop a tip using the requested pipette, even if the plunger position is not known."""
64
65
  ot3_hardware_api = ensure_ot3_hardware(self._hardware_api)
65
66
  pipette_location = self._state_view.motion.get_pipette_location(
@@ -72,7 +73,14 @@ class UnsafeDropTipInPlaceImplementation(
72
73
  pipette_id=params.pipetteId, home_after=params.homeAfter
73
74
  )
74
75
 
75
- return SuccessData(public=UnsafeDropTipInPlaceResult(), private=None)
76
+ state_update = StateUpdate()
77
+ state_update.update_pipette_tip_state(
78
+ pipette_id=params.pipetteId, tip_geometry=None
79
+ )
80
+
81
+ return SuccessData(
82
+ public=UnsafeDropTipInPlaceResult(), state_update=state_update
83
+ )
76
84
 
77
85
 
78
86
  class UnsafeDropTipInPlace(
@@ -32,7 +32,7 @@ class UnsafeEngageAxesResult(BaseModel):
32
32
  class UnsafeEngageAxesImplementation(
33
33
  AbstractCommandImpl[
34
34
  UnsafeEngageAxesParams,
35
- SuccessData[UnsafeEngageAxesResult, None],
35
+ SuccessData[UnsafeEngageAxesResult],
36
36
  ]
37
37
  ):
38
38
  """Enable axes command implementation."""
@@ -48,7 +48,7 @@ class UnsafeEngageAxesImplementation(
48
48
 
49
49
  async def execute(
50
50
  self, params: UnsafeEngageAxesParams
51
- ) -> SuccessData[UnsafeEngageAxesResult, None]:
51
+ ) -> SuccessData[UnsafeEngageAxesResult]:
52
52
  """Enable exes."""
53
53
  ot3_hardware_api = ensure_ot3_hardware(self._hardware_api)
54
54
  await ot3_hardware_api.engage_axes(
@@ -57,7 +57,9 @@ class UnsafeEngageAxesImplementation(
57
57
  for axis in params.axes
58
58
  ]
59
59
  )
60
- return SuccessData(public=UnsafeEngageAxesResult(), private=None)
60
+ return SuccessData(
61
+ public=UnsafeEngageAxesResult(),
62
+ )
61
63
 
62
64
 
63
65
  class UnsafeEngageAxes(
@@ -0,0 +1,194 @@
1
+ """Place labware payload, result, and implementaiton."""
2
+
3
+ from __future__ import annotations
4
+ from pydantic import BaseModel, Field
5
+ from typing import TYPE_CHECKING, Optional, Type, cast
6
+ from typing_extensions import Literal
7
+
8
+ from opentrons.hardware_control.types import Axis, OT3Mount
9
+ from opentrons.motion_planning.waypoints import get_gripper_labware_placement_waypoints
10
+ from opentrons.protocol_engine.errors.exceptions import (
11
+ CannotPerformGripperAction,
12
+ GripperNotAttachedError,
13
+ )
14
+ from opentrons.types import Point
15
+
16
+ from ...types import DeckSlotLocation, ModuleModel, OnDeckLabwareLocation
17
+ from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
18
+ from ...errors.error_occurrence import ErrorOccurrence
19
+ from ...resources import ensure_ot3_hardware
20
+ from ...state.update_types import StateUpdate
21
+
22
+ from opentrons.hardware_control import HardwareControlAPI, OT3HardwareControlAPI
23
+
24
+ if TYPE_CHECKING:
25
+ from ...state.state import StateView
26
+ from ...execution.equipment import EquipmentHandler
27
+
28
+
29
+ UnsafePlaceLabwareCommandType = Literal["unsafe/placeLabware"]
30
+
31
+
32
+ class UnsafePlaceLabwareParams(BaseModel):
33
+ """Payload required for an UnsafePlaceLabware command."""
34
+
35
+ labwareId: str = Field(..., description="The id of the labware to place.")
36
+ location: OnDeckLabwareLocation = Field(
37
+ ..., description="Where to place the labware."
38
+ )
39
+
40
+
41
+ class UnsafePlaceLabwareResult(BaseModel):
42
+ """Result data from the execution of an UnsafePlaceLabware command."""
43
+
44
+
45
+ class UnsafePlaceLabwareImplementation(
46
+ AbstractCommandImpl[
47
+ UnsafePlaceLabwareParams,
48
+ SuccessData[UnsafePlaceLabwareResult],
49
+ ]
50
+ ):
51
+ """The UnsafePlaceLabware command implementation."""
52
+
53
+ def __init__(
54
+ self,
55
+ hardware_api: HardwareControlAPI,
56
+ state_view: StateView,
57
+ equipment: EquipmentHandler,
58
+ **kwargs: object,
59
+ ) -> None:
60
+ self._hardware_api = hardware_api
61
+ self._state_view = state_view
62
+ self._equipment = equipment
63
+
64
+ async def execute(
65
+ self, params: UnsafePlaceLabwareParams
66
+ ) -> SuccessData[UnsafePlaceLabwareResult]:
67
+ """Place Labware.
68
+
69
+ This command is used only when the gripper is in the middle of moving
70
+ labware but is interrupted before completing the move. (i.e., the e-stop
71
+ is pressed, get into error recovery, etc).
72
+
73
+ Unlike the `moveLabware` command, where you pick a source and destination
74
+ location, this command takes the labwareId to be moved and location to
75
+ move it to.
76
+
77
+ """
78
+ ot3api = ensure_ot3_hardware(self._hardware_api)
79
+ if not ot3api.has_gripper():
80
+ raise GripperNotAttachedError("No gripper found to perform labware place.")
81
+
82
+ if ot3api.gripper_jaw_can_home():
83
+ raise CannotPerformGripperAction(
84
+ "Cannot place labware when gripper is not gripping."
85
+ )
86
+
87
+ # Allow propagation of LabwareNotLoadedError.
88
+ labware_id = params.labwareId
89
+ definition_uri = self._state_view.labware.get(labware_id).definitionUri
90
+ final_offsets = self._state_view.labware.get_labware_gripper_offsets(
91
+ labware_id, None
92
+ )
93
+ drop_offset = cast(Point, final_offsets.dropOffset) if final_offsets else None
94
+
95
+ if isinstance(params.location, DeckSlotLocation):
96
+ self._state_view.addressable_areas.raise_if_area_not_in_deck_configuration(
97
+ params.location.slotName.id
98
+ )
99
+
100
+ location = self._state_view.geometry.ensure_valid_gripper_location(
101
+ params.location,
102
+ )
103
+
104
+ # This is an absorbance reader, move the lid to its dock (staging area).
105
+ if isinstance(location, DeckSlotLocation):
106
+ module = self._state_view.modules.get_by_slot(location.slotName)
107
+ if module and module.model == ModuleModel.ABSORBANCE_READER_V1:
108
+ location = self._state_view.modules.absorbance_reader_dock_location(
109
+ module.id
110
+ )
111
+
112
+ new_offset_id = self._equipment.find_applicable_labware_offset_id(
113
+ labware_definition_uri=definition_uri,
114
+ labware_location=location,
115
+ )
116
+
117
+ # NOTE: When the estop is pressed, the gantry loses position,
118
+ # so the robot needs to home x, y to sync.
119
+ await ot3api.home(axes=[Axis.Z_L, Axis.Z_R, Axis.Z_G, Axis.X, Axis.Y])
120
+ state_update = StateUpdate()
121
+
122
+ # Place the labware down
123
+ await self._start_movement(ot3api, labware_id, location, drop_offset)
124
+
125
+ state_update.set_labware_location(
126
+ labware_id=labware_id,
127
+ new_location=location,
128
+ new_offset_id=new_offset_id,
129
+ )
130
+ return SuccessData(public=UnsafePlaceLabwareResult(), state_update=state_update)
131
+
132
+ async def _start_movement(
133
+ self,
134
+ ot3api: OT3HardwareControlAPI,
135
+ labware_id: str,
136
+ location: OnDeckLabwareLocation,
137
+ drop_offset: Optional[Point],
138
+ ) -> None:
139
+ gripper_homed_position = await ot3api.gantry_position(
140
+ mount=OT3Mount.GRIPPER,
141
+ refresh=True,
142
+ )
143
+
144
+ to_labware_center = self._state_view.geometry.get_labware_grip_point(
145
+ labware_id=labware_id, location=location
146
+ )
147
+
148
+ movement_waypoints = get_gripper_labware_placement_waypoints(
149
+ to_labware_center=to_labware_center,
150
+ gripper_home_z=gripper_homed_position.z,
151
+ drop_offset=drop_offset,
152
+ )
153
+
154
+ # start movement
155
+ for waypoint_data in movement_waypoints:
156
+ if waypoint_data.jaw_open:
157
+ if waypoint_data.dropping:
158
+ # This `disengage_axes` step is important in order to engage
159
+ # the electronic brake on the Z axis of the gripper. The brake
160
+ # has a stronger holding force on the axis than the hold current,
161
+ # and prevents the axis from spuriously dropping when e.g. the notch
162
+ # on the side of a falling tiprack catches the jaw.
163
+ await ot3api.disengage_axes([Axis.Z_G])
164
+ await ot3api.ungrip()
165
+ if waypoint_data.dropping:
166
+ # We lost the position estimation after disengaging the axis, so
167
+ # it is necessary to home it next
168
+ await ot3api.home_z(OT3Mount.GRIPPER)
169
+ await ot3api.move_to(
170
+ mount=OT3Mount.GRIPPER, abs_position=waypoint_data.position
171
+ )
172
+
173
+
174
+ class UnsafePlaceLabware(
175
+ BaseCommand[UnsafePlaceLabwareParams, UnsafePlaceLabwareResult, ErrorOccurrence]
176
+ ):
177
+ """UnsafePlaceLabware command model."""
178
+
179
+ commandType: UnsafePlaceLabwareCommandType = "unsafe/placeLabware"
180
+ params: UnsafePlaceLabwareParams
181
+ result: Optional[UnsafePlaceLabwareResult]
182
+
183
+ _ImplementationCls: Type[
184
+ UnsafePlaceLabwareImplementation
185
+ ] = UnsafePlaceLabwareImplementation
186
+
187
+
188
+ class UnsafePlaceLabwareCreate(BaseCommandCreate[UnsafePlaceLabwareParams]):
189
+ """UnsafePlaceLabware command request model."""
190
+
191
+ commandType: UnsafePlaceLabwareCommandType = "unsafe/placeLabware"
192
+ params: UnsafePlaceLabwareParams
193
+
194
+ _CommandCls: Type[UnsafePlaceLabware] = UnsafePlaceLabware
@@ -0,0 +1,75 @@
1
+ """Ungrip labware payload, result, and implementaiton."""
2
+
3
+ from __future__ import annotations
4
+ from opentrons.protocol_engine.errors.exceptions import GripperNotAttachedError
5
+ from pydantic import BaseModel
6
+ from typing import Optional, Type
7
+ from typing_extensions import Literal
8
+
9
+ from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
10
+ from ...errors.error_occurrence import ErrorOccurrence
11
+ from ...resources import ensure_ot3_hardware
12
+
13
+ from opentrons.hardware_control import HardwareControlAPI
14
+
15
+
16
+ UnsafeUngripLabwareCommandType = Literal["unsafe/ungripLabware"]
17
+
18
+
19
+ class UnsafeUngripLabwareParams(BaseModel):
20
+ """Payload required for an UngripLabware command."""
21
+
22
+
23
+ class UnsafeUngripLabwareResult(BaseModel):
24
+ """Result data from the execution of an UngripLabware command."""
25
+
26
+
27
+ class UnsafeUngripLabwareImplementation(
28
+ AbstractCommandImpl[
29
+ UnsafeUngripLabwareParams,
30
+ SuccessData[UnsafeUngripLabwareResult],
31
+ ]
32
+ ):
33
+ """Ungrip labware command implementation."""
34
+
35
+ def __init__(
36
+ self,
37
+ hardware_api: HardwareControlAPI,
38
+ **kwargs: object,
39
+ ) -> None:
40
+ self._hardware_api = hardware_api
41
+
42
+ async def execute(
43
+ self, params: UnsafeUngripLabwareParams
44
+ ) -> SuccessData[UnsafeUngripLabwareResult]:
45
+ """Ungrip Labware."""
46
+ ot3_hardware_api = ensure_ot3_hardware(self._hardware_api)
47
+ if not ot3_hardware_api.has_gripper():
48
+ raise GripperNotAttachedError("No gripper found to perform ungrip.")
49
+ await ot3_hardware_api.ungrip()
50
+ return SuccessData(
51
+ public=UnsafeUngripLabwareResult(),
52
+ )
53
+
54
+
55
+ class UnsafeUngripLabware(
56
+ BaseCommand[UnsafeUngripLabwareParams, UnsafeUngripLabwareResult, ErrorOccurrence]
57
+ ):
58
+ """UnsafeUngripLabware command model."""
59
+
60
+ commandType: UnsafeUngripLabwareCommandType = "unsafe/ungripLabware"
61
+ params: UnsafeUngripLabwareParams
62
+ result: Optional[UnsafeUngripLabwareResult]
63
+
64
+ _ImplementationCls: Type[
65
+ UnsafeUngripLabwareImplementation
66
+ ] = UnsafeUngripLabwareImplementation
67
+
68
+
69
+ class UnsafeUngripLabwareCreate(BaseCommandCreate[UnsafeUngripLabwareParams]):
70
+ """UnsafeEngageAxes command request model."""
71
+
72
+ commandType: UnsafeUngripLabwareCommandType = "unsafe/ungripLabware"
73
+ params: UnsafeUngripLabwareParams
74
+
75
+ _CommandCls: Type[UnsafeUngripLabware] = UnsafeUngripLabware
@@ -34,7 +34,7 @@ class UpdatePositionEstimatorsResult(BaseModel):
34
34
  class UpdatePositionEstimatorsImplementation(
35
35
  AbstractCommandImpl[
36
36
  UpdatePositionEstimatorsParams,
37
- SuccessData[UpdatePositionEstimatorsResult, None],
37
+ SuccessData[UpdatePositionEstimatorsResult],
38
38
  ]
39
39
  ):
40
40
  """Update position estimators command implementation."""
@@ -50,7 +50,7 @@ class UpdatePositionEstimatorsImplementation(
50
50
 
51
51
  async def execute(
52
52
  self, params: UpdatePositionEstimatorsParams
53
- ) -> SuccessData[UpdatePositionEstimatorsResult, None]:
53
+ ) -> SuccessData[UpdatePositionEstimatorsResult]:
54
54
  """Update axis position estimators from their encoders."""
55
55
  ot3_hardware_api = ensure_ot3_hardware(self._hardware_api)
56
56
  await ot3_hardware_api.update_axis_position_estimations(
@@ -59,7 +59,9 @@ class UpdatePositionEstimatorsImplementation(
59
59
  for axis in params.axes
60
60
  ]
61
61
  )
62
- return SuccessData(public=UpdatePositionEstimatorsResult(), private=None)
62
+ return SuccessData(
63
+ public=UpdatePositionEstimatorsResult(),
64
+ )
63
65
 
64
66
 
65
67
  class UpdatePositionEstimators(
@@ -36,9 +36,7 @@ class VerifyTipPresenceResult(BaseModel):
36
36
 
37
37
 
38
38
  class VerifyTipPresenceImplementation(
39
- AbstractCommandImpl[
40
- VerifyTipPresenceParams, SuccessData[VerifyTipPresenceResult, None]
41
- ]
39
+ AbstractCommandImpl[VerifyTipPresenceParams, SuccessData[VerifyTipPresenceResult]]
42
40
  ):
43
41
  """VerifyTipPresence command implementation."""
44
42
 
@@ -51,7 +49,7 @@ class VerifyTipPresenceImplementation(
51
49
 
52
50
  async def execute(
53
51
  self, params: VerifyTipPresenceParams
54
- ) -> SuccessData[VerifyTipPresenceResult, None]:
52
+ ) -> SuccessData[VerifyTipPresenceResult]:
55
53
  """Verify if tip presence is as expected for the requested pipette."""
56
54
  pipette_id = params.pipetteId
57
55
  expected_state = params.expectedState
@@ -67,7 +65,9 @@ class VerifyTipPresenceImplementation(
67
65
  follow_singular_sensor=follow_singular_sensor,
68
66
  )
69
67
 
70
- return SuccessData(public=VerifyTipPresenceResult(), private=None)
68
+ return SuccessData(
69
+ public=VerifyTipPresenceResult(),
70
+ )
71
71
 
72
72
 
73
73
  class VerifyTipPresence(
@@ -29,7 +29,7 @@ class WaitForDurationResult(BaseModel):
29
29
 
30
30
 
31
31
  class WaitForDurationImplementation(
32
- AbstractCommandImpl[WaitForDurationParams, SuccessData[WaitForDurationResult, None]]
32
+ AbstractCommandImpl[WaitForDurationParams, SuccessData[WaitForDurationResult]]
33
33
  ):
34
34
  """Wait for duration command implementation."""
35
35
 
@@ -38,10 +38,12 @@ class WaitForDurationImplementation(
38
38
 
39
39
  async def execute(
40
40
  self, params: WaitForDurationParams
41
- ) -> SuccessData[WaitForDurationResult, None]:
41
+ ) -> SuccessData[WaitForDurationResult]:
42
42
  """Wait for a duration of time."""
43
43
  await self._run_control.wait_for_duration(params.seconds)
44
- return SuccessData(public=WaitForDurationResult(), private=None)
44
+ return SuccessData(
45
+ public=WaitForDurationResult(),
46
+ )
45
47
 
46
48
 
47
49
  class WaitForDuration(
@@ -30,7 +30,7 @@ class WaitForResumeResult(BaseModel):
30
30
 
31
31
 
32
32
  class WaitForResumeImplementation(
33
- AbstractCommandImpl[WaitForResumeParams, SuccessData[WaitForResumeResult, None]]
33
+ AbstractCommandImpl[WaitForResumeParams, SuccessData[WaitForResumeResult]]
34
34
  ):
35
35
  """Wait for resume command implementation."""
36
36
 
@@ -39,10 +39,12 @@ class WaitForResumeImplementation(
39
39
 
40
40
  async def execute(
41
41
  self, params: WaitForResumeParams
42
- ) -> SuccessData[WaitForResumeResult, None]:
42
+ ) -> SuccessData[WaitForResumeResult]:
43
43
  """Dispatch a PauseAction to the store to pause the protocol."""
44
44
  await self._run_control.wait_for_resume()
45
- return SuccessData(public=WaitForResumeResult(), private=None)
45
+ return SuccessData(
46
+ public=WaitForResumeResult(),
47
+ )
46
48
 
47
49
 
48
50
  class WaitForResume(