opentrons 8.1.0__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.0.dist-info → opentrons-8.2.0.dist-info}/METADATA +5 -4
  229. {opentrons-8.1.0.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.0.dist-info → opentrons-8.2.0.dist-info}/LICENSE +0 -0
  236. {opentrons-8.1.0.dist-info → opentrons-8.2.0.dist-info}/WHEEL +0 -0
  237. {opentrons-8.1.0.dist-info → opentrons-8.2.0.dist-info}/entry_points.txt +0 -0
  238. {opentrons-8.1.0.dist-info → opentrons-8.2.0.dist-info}/top_level.txt +0 -0
@@ -12,7 +12,6 @@ from .pipetting_common import (
12
12
  FlowRateMixin,
13
13
  BaseLiquidHandlingResult,
14
14
  OverpressureError,
15
- OverpressureErrorInternalData,
16
15
  )
17
16
  from .command import (
18
17
  AbstractCommandImpl,
@@ -22,11 +21,13 @@ from .command import (
22
21
  DefinedErrorData,
23
22
  )
24
23
  from ..errors.error_occurrence import ErrorOccurrence
25
- from ..types import DeckPoint
24
+ from ..state.update_types import StateUpdate, CLEAR
25
+ from ..types import CurrentWell
26
26
 
27
27
  if TYPE_CHECKING:
28
28
  from ..execution import PipettingHandler, GantryMover
29
29
  from ..resources import ModelUtils
30
+ from ..state.state import StateView
30
31
 
31
32
 
32
33
  DispenseInPlaceCommandType = Literal["dispenseInPlace"]
@@ -48,8 +49,8 @@ class DispenseInPlaceResult(BaseLiquidHandlingResult):
48
49
 
49
50
 
50
51
  _ExecuteReturn = Union[
51
- SuccessData[DispenseInPlaceResult, None],
52
- DefinedErrorData[OverpressureError, OverpressureErrorInternalData],
52
+ SuccessData[DispenseInPlaceResult],
53
+ DefinedErrorData[OverpressureError],
53
54
  ]
54
55
 
55
56
 
@@ -61,16 +62,21 @@ class DispenseInPlaceImplementation(
61
62
  def __init__(
62
63
  self,
63
64
  pipetting: PipettingHandler,
65
+ state_view: StateView,
64
66
  gantry_mover: GantryMover,
65
67
  model_utils: ModelUtils,
66
68
  **kwargs: object,
67
69
  ) -> None:
68
70
  self._pipetting = pipetting
71
+ self._state_view = state_view
69
72
  self._gantry_mover = gantry_mover
70
73
  self._model_utils = model_utils
71
74
 
72
75
  async def execute(self, params: DispenseInPlaceParams) -> _ExecuteReturn:
73
76
  """Dispense without moving the pipette."""
77
+ state_update = StateUpdate()
78
+ current_location = self._state_view.pipettes.get_current_location()
79
+ current_position = await self._gantry_mover.get_position(params.pipetteId)
74
80
  try:
75
81
  volume = await self._pipetting.dispense_in_place(
76
82
  pipette_id=params.pipetteId,
@@ -79,7 +85,15 @@ class DispenseInPlaceImplementation(
79
85
  push_out=params.pushOut,
80
86
  )
81
87
  except PipetteOverpressureError as e:
82
- current_position = await self._gantry_mover.get_position(params.pipetteId)
88
+ if (
89
+ isinstance(current_location, CurrentWell)
90
+ and current_location.pipette_id == params.pipetteId
91
+ ):
92
+ state_update.set_liquid_operated(
93
+ labware_id=current_location.labware_id,
94
+ well_name=current_location.well_name,
95
+ volume_added=CLEAR,
96
+ )
83
97
  return DefinedErrorData(
84
98
  public=OverpressureError(
85
99
  id=self._model_utils.generate_id(),
@@ -101,22 +115,26 @@ class DispenseInPlaceImplementation(
101
115
  }
102
116
  ),
103
117
  ),
104
- private=OverpressureErrorInternalData(
105
- position=DeckPoint(
106
- x=current_position.x,
107
- y=current_position.y,
108
- z=current_position.z,
109
- ),
110
- ),
118
+ state_update=state_update,
111
119
  )
112
120
  else:
121
+ if (
122
+ isinstance(current_location, CurrentWell)
123
+ and current_location.pipette_id == params.pipetteId
124
+ ):
125
+ state_update.set_liquid_operated(
126
+ labware_id=current_location.labware_id,
127
+ well_name=current_location.well_name,
128
+ volume_added=volume,
129
+ )
113
130
  return SuccessData(
114
- public=DispenseInPlaceResult(volume=volume), private=None
131
+ public=DispenseInPlaceResult(volume=volume),
132
+ state_update=state_update,
115
133
  )
116
134
 
117
135
 
118
136
  class DispenseInPlace(
119
- BaseCommand[DispenseInPlaceParams, DispenseInPlaceResult, ErrorOccurrence]
137
+ BaseCommand[DispenseInPlaceParams, DispenseInPlaceResult, OverpressureError]
120
138
  ):
121
139
  """DispenseInPlace command model."""
122
140
 
@@ -5,13 +5,27 @@ from pydantic import Field
5
5
  from typing import TYPE_CHECKING, Optional, Type
6
6
  from typing_extensions import Literal
7
7
 
8
+ from opentrons.protocol_engine.errors.exceptions import TipAttachedError
9
+ from opentrons.protocol_engine.resources.model_utils import ModelUtils
10
+
11
+ from ..state import update_types
8
12
  from ..types import DropTipWellLocation, DeckPoint
9
- from .pipetting_common import PipetteIdMixin, DestinationPositionResult
10
- from .command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
13
+ from .pipetting_common import (
14
+ PipetteIdMixin,
15
+ DestinationPositionResult,
16
+ TipPhysicallyAttachedError,
17
+ )
18
+ from .command import (
19
+ AbstractCommandImpl,
20
+ BaseCommand,
21
+ BaseCommandCreate,
22
+ DefinedErrorData,
23
+ SuccessData,
24
+ )
11
25
  from ..errors.error_occurrence import ErrorOccurrence
12
26
 
13
27
  if TYPE_CHECKING:
14
- from ..state import StateView
28
+ from ..state.state import StateView
15
29
  from ..execution import MovementHandler, TipHandler
16
30
 
17
31
 
@@ -53,9 +67,12 @@ class DropTipResult(DestinationPositionResult):
53
67
  pass
54
68
 
55
69
 
56
- class DropTipImplementation(
57
- AbstractCommandImpl[DropTipParams, SuccessData[DropTipResult, None]]
58
- ):
70
+ _ExecuteReturn = (
71
+ SuccessData[DropTipResult] | DefinedErrorData[TipPhysicallyAttachedError]
72
+ )
73
+
74
+
75
+ class DropTipImplementation(AbstractCommandImpl[DropTipParams, _ExecuteReturn]):
59
76
  """Drop tip command implementation."""
60
77
 
61
78
  def __init__(
@@ -63,19 +80,23 @@ class DropTipImplementation(
63
80
  state_view: StateView,
64
81
  tip_handler: TipHandler,
65
82
  movement: MovementHandler,
83
+ model_utils: ModelUtils,
66
84
  **kwargs: object,
67
85
  ) -> None:
68
86
  self._state_view = state_view
69
87
  self._tip_handler = tip_handler
70
88
  self._movement_handler = movement
89
+ self._model_utils = model_utils
71
90
 
72
- async def execute(self, params: DropTipParams) -> SuccessData[DropTipResult, None]:
91
+ async def execute(self, params: DropTipParams) -> _ExecuteReturn:
73
92
  """Move to and drop a tip using the requested pipette."""
74
93
  pipette_id = params.pipetteId
75
94
  labware_id = params.labwareId
76
95
  well_name = params.wellName
77
96
  home_after = params.homeAfter
78
97
 
98
+ state_update = update_types.StateUpdate()
99
+
79
100
  if params.alternateDropLocation:
80
101
  well_location = self._state_view.geometry.get_next_tip_drop_location(
81
102
  labware_id=labware_id,
@@ -101,18 +122,51 @@ class DropTipImplementation(
101
122
  well_name=well_name,
102
123
  well_location=tip_drop_location,
103
124
  )
104
-
105
- await self._tip_handler.drop_tip(pipette_id=pipette_id, home_after=home_after)
106
-
107
- return SuccessData(
108
- public=DropTipResult(
109
- position=DeckPoint(x=position.x, y=position.y, z=position.z)
110
- ),
111
- private=None,
125
+ deck_point = DeckPoint.construct(x=position.x, y=position.y, z=position.z)
126
+ state_update.set_pipette_location(
127
+ pipette_id=pipette_id,
128
+ new_labware_id=labware_id,
129
+ new_well_name=well_name,
130
+ new_deck_point=deck_point,
112
131
  )
113
132
 
133
+ try:
134
+ await self._tip_handler.drop_tip(
135
+ pipette_id=pipette_id, home_after=home_after
136
+ )
137
+ except TipAttachedError as exception:
138
+ error = TipPhysicallyAttachedError(
139
+ id=self._model_utils.generate_id(),
140
+ createdAt=self._model_utils.get_timestamp(),
141
+ wrappedErrors=[
142
+ ErrorOccurrence.from_failed(
143
+ id=self._model_utils.generate_id(),
144
+ createdAt=self._model_utils.get_timestamp(),
145
+ error=exception,
146
+ )
147
+ ],
148
+ errorInfo={"retryLocation": position},
149
+ )
150
+ state_update_if_false_positive = update_types.StateUpdate()
151
+ state_update_if_false_positive.update_pipette_tip_state(
152
+ pipette_id=params.pipetteId, tip_geometry=None
153
+ )
154
+ return DefinedErrorData(
155
+ public=error,
156
+ state_update=state_update,
157
+ state_update_if_false_positive=state_update_if_false_positive,
158
+ )
159
+ else:
160
+ state_update.update_pipette_tip_state(
161
+ pipette_id=params.pipetteId, tip_geometry=None
162
+ )
163
+ return SuccessData(
164
+ public=DropTipResult(position=deck_point),
165
+ state_update=state_update,
166
+ )
167
+
114
168
 
115
- class DropTip(BaseCommand[DropTipParams, DropTipResult, ErrorOccurrence]):
169
+ class DropTip(BaseCommand[DropTipParams, DropTipResult, TipPhysicallyAttachedError]):
116
170
  """Drop tip command model."""
117
171
 
118
172
  commandType: DropTipCommandType = "dropTip"
@@ -4,12 +4,21 @@ from pydantic import Field, BaseModel
4
4
  from typing import TYPE_CHECKING, Optional, Type
5
5
  from typing_extensions import Literal
6
6
 
7
- from .pipetting_common import PipetteIdMixin
8
- from .command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
7
+ from .command import (
8
+ AbstractCommandImpl,
9
+ BaseCommand,
10
+ BaseCommandCreate,
11
+ DefinedErrorData,
12
+ SuccessData,
13
+ )
14
+ from .pipetting_common import PipetteIdMixin, TipPhysicallyAttachedError
15
+ from ..errors.exceptions import TipAttachedError
9
16
  from ..errors.error_occurrence import ErrorOccurrence
17
+ from ..resources.model_utils import ModelUtils
18
+ from ..state import update_types
10
19
 
11
20
  if TYPE_CHECKING:
12
- from ..execution import TipHandler
21
+ from ..execution import TipHandler, GantryMover
13
22
 
14
23
 
15
24
  DropTipInPlaceCommandType = Literal["dropTipInPlace"]
@@ -34,31 +43,68 @@ class DropTipInPlaceResult(BaseModel):
34
43
  pass
35
44
 
36
45
 
46
+ _ExecuteReturn = (
47
+ SuccessData[DropTipInPlaceResult] | DefinedErrorData[TipPhysicallyAttachedError]
48
+ )
49
+
50
+
37
51
  class DropTipInPlaceImplementation(
38
- AbstractCommandImpl[DropTipInPlaceParams, SuccessData[DropTipInPlaceResult, None]]
52
+ AbstractCommandImpl[DropTipInPlaceParams, _ExecuteReturn]
39
53
  ):
40
54
  """Drop tip in place command implementation."""
41
55
 
42
56
  def __init__(
43
57
  self,
44
58
  tip_handler: TipHandler,
59
+ model_utils: ModelUtils,
60
+ gantry_mover: GantryMover,
45
61
  **kwargs: object,
46
62
  ) -> None:
47
63
  self._tip_handler = tip_handler
64
+ self._model_utils = model_utils
65
+ self._gantry_mover = gantry_mover
48
66
 
49
- async def execute(
50
- self, params: DropTipInPlaceParams
51
- ) -> SuccessData[DropTipInPlaceResult, None]:
67
+ async def execute(self, params: DropTipInPlaceParams) -> _ExecuteReturn:
52
68
  """Drop a tip using the requested pipette."""
53
- await self._tip_handler.drop_tip(
54
- pipette_id=params.pipetteId, home_after=params.homeAfter
55
- )
56
-
57
- return SuccessData(public=DropTipInPlaceResult(), private=None)
69
+ state_update = update_types.StateUpdate()
70
+
71
+ retry_location = await self._gantry_mover.get_position(params.pipetteId)
72
+
73
+ try:
74
+ await self._tip_handler.drop_tip(
75
+ pipette_id=params.pipetteId, home_after=params.homeAfter
76
+ )
77
+ except TipAttachedError as exception:
78
+ state_update_if_false_positive = update_types.StateUpdate()
79
+ state_update_if_false_positive.update_pipette_tip_state(
80
+ pipette_id=params.pipetteId, tip_geometry=None
81
+ )
82
+ error = TipPhysicallyAttachedError(
83
+ id=self._model_utils.generate_id(),
84
+ createdAt=self._model_utils.get_timestamp(),
85
+ wrappedErrors=[
86
+ ErrorOccurrence.from_failed(
87
+ id=self._model_utils.generate_id(),
88
+ createdAt=self._model_utils.get_timestamp(),
89
+ error=exception,
90
+ )
91
+ ],
92
+ errorInfo={"retryLocation": retry_location},
93
+ )
94
+ return DefinedErrorData(
95
+ public=error,
96
+ state_update=state_update,
97
+ state_update_if_false_positive=state_update_if_false_positive,
98
+ )
99
+ else:
100
+ state_update.update_pipette_tip_state(
101
+ pipette_id=params.pipetteId, tip_geometry=None
102
+ )
103
+ return SuccessData(public=DropTipInPlaceResult(), state_update=state_update)
58
104
 
59
105
 
60
106
  class DropTipInPlace(
61
- BaseCommand[DropTipInPlaceParams, DropTipInPlaceResult, ErrorOccurrence]
107
+ BaseCommand[DropTipInPlaceParams, DropTipInPlaceResult, TipPhysicallyAttachedError]
62
108
  ):
63
109
  """Drop tip in place command model."""
64
110
 
@@ -38,7 +38,7 @@ class GetTipPresenceResult(BaseModel):
38
38
 
39
39
 
40
40
  class GetTipPresenceImplementation(
41
- AbstractCommandImpl[GetTipPresenceParams, SuccessData[GetTipPresenceResult, None]]
41
+ AbstractCommandImpl[GetTipPresenceParams, SuccessData[GetTipPresenceResult]]
42
42
  ):
43
43
  """GetTipPresence command implementation."""
44
44
 
@@ -51,7 +51,7 @@ class GetTipPresenceImplementation(
51
51
 
52
52
  async def execute(
53
53
  self, params: GetTipPresenceParams
54
- ) -> SuccessData[GetTipPresenceResult, None]:
54
+ ) -> SuccessData[GetTipPresenceResult]:
55
55
  """Verify if tip presence is as expected for the requested pipette."""
56
56
  pipette_id = params.pipetteId
57
57
 
@@ -59,7 +59,9 @@ class GetTipPresenceImplementation(
59
59
  pipette_id=pipette_id,
60
60
  )
61
61
 
62
- return SuccessData(public=GetTipPresenceResult(status=result), private=None)
62
+ return SuccessData(
63
+ public=GetTipPresenceResult(status=result),
64
+ )
63
65
 
64
66
 
65
67
  class GetTipPresence(
@@ -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 CloseLabwareLatchResult(BaseModel):
27
27
 
28
28
 
29
29
  class CloseLabwareLatchImpl(
30
- AbstractCommandImpl[
31
- CloseLabwareLatchParams, SuccessData[CloseLabwareLatchResult, None]
32
- ]
30
+ AbstractCommandImpl[CloseLabwareLatchParams, SuccessData[CloseLabwareLatchResult]]
33
31
  ):
34
32
  """Execution implementation of a Heater-Shaker's close labware latch command."""
35
33
 
@@ -44,7 +42,7 @@ class CloseLabwareLatchImpl(
44
42
 
45
43
  async def execute(
46
44
  self, params: CloseLabwareLatchParams
47
- ) -> SuccessData[CloseLabwareLatchResult, None]:
45
+ ) -> SuccessData[CloseLabwareLatchResult]:
48
46
  """Close a Heater-Shaker's labware latch."""
49
47
  # Allow propagation of ModuleNotLoadedError and WrongModuleTypeError.
50
48
  hs_module_substate = self._state_view.modules.get_heater_shaker_module_substate(
@@ -59,7 +57,9 @@ class CloseLabwareLatchImpl(
59
57
  if hs_hardware_module is not None:
60
58
  await hs_hardware_module.close_labware_latch()
61
59
 
62
- return SuccessData(public=CloseLabwareLatchResult(), private=None)
60
+ return SuccessData(
61
+ public=CloseLabwareLatchResult(),
62
+ )
63
63
 
64
64
 
65
65
  class CloseLabwareLatch(
@@ -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(