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
@@ -1,19 +1,17 @@
1
1
  """Pick up tip command request, result, and implementation models."""
2
2
  from __future__ import annotations
3
- from dataclasses import dataclass
4
3
  from opentrons_shared_data.errors import ErrorCodes
5
4
  from pydantic import Field
6
5
  from typing import TYPE_CHECKING, Optional, Type, Union
7
6
  from typing_extensions import Literal
8
7
 
9
- from opentrons.protocol_engine.errors.exceptions import TipNotAttachedError
10
8
 
11
- from ..errors import ErrorOccurrence
9
+ from ..errors import ErrorOccurrence, PickUpTipTipNotAttachedError
12
10
  from ..resources import ModelUtils
13
- from ..types import DeckPoint
11
+ from ..state import update_types
12
+ from ..types import PickUpTipWellLocation, DeckPoint
14
13
  from .pipetting_common import (
15
14
  PipetteIdMixin,
16
- WellLocationMixin,
17
15
  DestinationPositionResult,
18
16
  )
19
17
  from .command import (
@@ -25,17 +23,22 @@ from .command import (
25
23
  )
26
24
 
27
25
  if TYPE_CHECKING:
28
- from ..state import StateView
26
+ from ..state.state import StateView
29
27
  from ..execution import MovementHandler, TipHandler
30
28
 
31
29
 
32
30
  PickUpTipCommandType = Literal["pickUpTip"]
33
31
 
34
32
 
35
- class PickUpTipParams(PipetteIdMixin, WellLocationMixin):
33
+ class PickUpTipParams(PipetteIdMixin):
36
34
  """Payload needed to move a pipette to a specific well."""
37
35
 
38
- pass
36
+ labwareId: str = Field(..., description="Identifier of labware to use.")
37
+ wellName: str = Field(..., description="Name of well to use in labware.")
38
+ wellLocation: PickUpTipWellLocation = Field(
39
+ default_factory=PickUpTipWellLocation,
40
+ description="Relative well location at which to pick up the tip.",
41
+ )
39
42
 
40
43
 
41
44
  class PickUpTipResult(DestinationPositionResult):
@@ -72,24 +75,19 @@ class TipPhysicallyMissingError(ErrorOccurrence):
72
75
  of the pipette.
73
76
  """
74
77
 
78
+ # The thing above about marking the tips as used makes it so that
79
+ # when the protocol is resumed and the Python Protocol API calls
80
+ # `get_next_tip()`, we'll move on to other tips as expected.
81
+
75
82
  isDefined: bool = True
76
83
  errorType: Literal["tipPhysicallyMissing"] = "tipPhysicallyMissing"
77
84
  errorCode: str = ErrorCodes.TIP_PICKUP_FAILED.value.code
78
- detail: str = "No tip detected."
79
-
80
-
81
- @dataclass(frozen=True)
82
- class TipPhysicallyMissingErrorInternalData:
83
- """Internal-to-ProtocolEngine data about a TipPhysicallyMissingError."""
84
-
85
- pipette_id: str
86
- labware_id: str
87
- well_name: str
85
+ detail: str = "No Tip Detected"
88
86
 
89
87
 
90
88
  _ExecuteReturn = Union[
91
- SuccessData[PickUpTipResult, None],
92
- DefinedErrorData[TipPhysicallyMissingError, TipPhysicallyMissingErrorInternalData],
89
+ SuccessData[PickUpTipResult],
90
+ DefinedErrorData[TipPhysicallyMissingError],
93
91
  ]
94
92
 
95
93
 
@@ -111,19 +109,30 @@ class PickUpTipImplementation(AbstractCommandImpl[PickUpTipParams, _ExecuteRetur
111
109
 
112
110
  async def execute(
113
111
  self, params: PickUpTipParams
114
- ) -> Union[SuccessData[PickUpTipResult, None], _ExecuteReturn]:
112
+ ) -> Union[SuccessData[PickUpTipResult], _ExecuteReturn]:
115
113
  """Move to and pick up a tip using the requested pipette."""
116
114
  pipette_id = params.pipetteId
117
115
  labware_id = params.labwareId
118
116
  well_name = params.wellName
119
- well_location = params.wellLocation
120
117
 
118
+ state_update = update_types.StateUpdate()
119
+
120
+ well_location = self._state_view.geometry.convert_pick_up_tip_well_location(
121
+ well_location=params.wellLocation
122
+ )
121
123
  position = await self._movement.move_to_well(
122
124
  pipette_id=pipette_id,
123
125
  labware_id=labware_id,
124
126
  well_name=well_name,
125
127
  well_location=well_location,
126
128
  )
129
+ deck_point = DeckPoint.construct(x=position.x, y=position.y, z=position.z)
130
+ state_update.set_pipette_location(
131
+ pipette_id=pipette_id,
132
+ new_labware_id=labware_id,
133
+ new_well_name=well_name,
134
+ new_deck_point=deck_point,
135
+ )
127
136
 
128
137
  try:
129
138
  tip_geometry = await self._tip_handler.pick_up_tip(
@@ -131,7 +140,15 @@ class PickUpTipImplementation(AbstractCommandImpl[PickUpTipParams, _ExecuteRetur
131
140
  labware_id=labware_id,
132
141
  well_name=well_name,
133
142
  )
134
- except TipNotAttachedError as e:
143
+ except PickUpTipTipNotAttachedError as e:
144
+ state_update_if_false_positive = update_types.StateUpdate()
145
+ state_update_if_false_positive.update_pipette_tip_state(
146
+ pipette_id=pipette_id,
147
+ tip_geometry=e.tip_geometry,
148
+ )
149
+ state_update.mark_tips_as_used(
150
+ pipette_id=pipette_id, labware_id=labware_id, well_name=well_name
151
+ )
135
152
  return DefinedErrorData(
136
153
  public=TipPhysicallyMissingError(
137
154
  id=self._model_utils.generate_id(),
@@ -144,21 +161,25 @@ class PickUpTipImplementation(AbstractCommandImpl[PickUpTipParams, _ExecuteRetur
144
161
  )
145
162
  ],
146
163
  ),
147
- private=TipPhysicallyMissingErrorInternalData(
148
- pipette_id=pipette_id,
149
- labware_id=labware_id,
150
- well_name=well_name,
151
- ),
164
+ state_update=state_update,
165
+ state_update_if_false_positive=state_update_if_false_positive,
152
166
  )
153
167
  else:
168
+ state_update.update_pipette_tip_state(
169
+ pipette_id=pipette_id,
170
+ tip_geometry=tip_geometry,
171
+ )
172
+ state_update.mark_tips_as_used(
173
+ pipette_id=pipette_id, labware_id=labware_id, well_name=well_name
174
+ )
154
175
  return SuccessData(
155
176
  public=PickUpTipResult(
156
177
  tipVolume=tip_geometry.volume,
157
178
  tipLength=tip_geometry.length,
158
179
  tipDiameter=tip_geometry.diameter,
159
- position=DeckPoint(x=position.x, y=position.y, z=position.z),
180
+ position=deck_point,
160
181
  ),
161
- private=None,
182
+ state_update=state_update,
162
183
  )
163
184
 
164
185
 
@@ -1,12 +1,11 @@
1
1
  """Common pipetting command base models."""
2
- from dataclasses import dataclass
3
2
  from opentrons_shared_data.errors import ErrorCodes
4
3
  from pydantic import BaseModel, Field
5
4
  from typing import Literal, Optional, Tuple, TypedDict
6
5
 
7
6
  from opentrons.protocol_engine.errors.error_occurrence import ErrorOccurrence
8
7
 
9
- from ..types import WellLocation, DeckPoint
8
+ from ..types import WellLocation, LiquidHandlingWellLocation, DeckPoint
10
9
 
11
10
 
12
11
  class PipetteIdMixin(BaseModel):
@@ -69,6 +68,23 @@ class WellLocationMixin(BaseModel):
69
68
  )
70
69
 
71
70
 
71
+ class LiquidHandlingWellLocationMixin(BaseModel):
72
+ """Mixin for command requests that take a location that's somewhere in a well."""
73
+
74
+ labwareId: str = Field(
75
+ ...,
76
+ description="Identifier of labware to use.",
77
+ )
78
+ wellName: str = Field(
79
+ ...,
80
+ description="Name of well to use in labware.",
81
+ )
82
+ wellLocation: LiquidHandlingWellLocation = Field(
83
+ default_factory=LiquidHandlingWellLocation,
84
+ description="Relative well location at which to perform the operation",
85
+ )
86
+
87
+
72
88
  class MovementMixin(BaseModel):
73
89
  """Mixin for command requests that move a pipette."""
74
90
 
@@ -114,6 +130,14 @@ class BaseLiquidHandlingResult(BaseModel):
114
130
  class DestinationPositionResult(BaseModel):
115
131
  """Mixin for command results that move a pipette."""
116
132
 
133
+ # todo(mm, 2024-08-02): Consider deprecating or redefining this.
134
+ #
135
+ # This is here because opentrons.protocol_engine needed it for internal bookkeeping
136
+ # and, at the time, we didn't have a way to do that without adding this to the
137
+ # public command results. Its usefulness to callers outside
138
+ # opentrons.protocol_engine is questionable because they would need to know which
139
+ # critical point is in play, and I think that can change depending on obscure
140
+ # things like labware quirks.
117
141
  position: DeckPoint = Field(
118
142
  DeckPoint(x=0, y=0, z=0),
119
143
  description=(
@@ -124,7 +148,12 @@ class DestinationPositionResult(BaseModel):
124
148
 
125
149
 
126
150
  class ErrorLocationInfo(TypedDict):
127
- """Holds a retry location for in-place error recovery."""
151
+ """Holds a retry location for in-place error recovery.
152
+
153
+ This is appropriate to pass to a `moveToCoordinates` command,
154
+ assuming the pipette has not been configured with a different nozzle layout
155
+ in the meantime.
156
+ """
128
157
 
129
158
  retryLocation: Tuple[float, float, float]
130
159
 
@@ -149,14 +178,6 @@ class OverpressureError(ErrorOccurrence):
149
178
  errorInfo: ErrorLocationInfo
150
179
 
151
180
 
152
- @dataclass(frozen=True)
153
- class OverpressureErrorInternalData:
154
- """Internal-to-ProtocolEngine data about an OverpressureError."""
155
-
156
- position: DeckPoint
157
- """Same meaning as DestinationPositionResult.position."""
158
-
159
-
160
181
  class LiquidNotFoundError(ErrorOccurrence):
161
182
  """Returned when no liquid is detected during the liquid probe process/move.
162
183
 
@@ -171,9 +192,19 @@ class LiquidNotFoundError(ErrorOccurrence):
171
192
  detail: str = ErrorCodes.PIPETTE_LIQUID_NOT_FOUND.value.detail
172
193
 
173
194
 
174
- @dataclass(frozen=True)
175
- class LiquidNotFoundErrorInternalData:
176
- """Internal-to-ProtocolEngine data about a LiquidNotFoundError."""
195
+ class TipPhysicallyAttachedError(ErrorOccurrence):
196
+ """Returned when sensors determine that a tip remains on the pipette after a drop attempt.
177
197
 
178
- position: DeckPoint
179
- """Same meaning as DestinationPositionResult.position."""
198
+ The pipette will act as if the tip was not dropped. So, you won't be able to pick
199
+ up a new tip without dropping the current one, and movement commands will assume
200
+ there is a tip hanging off the bottom of the pipette.
201
+ """
202
+
203
+ isDefined: bool = True
204
+
205
+ errorType: Literal["tipPhysicallyAttached"] = "tipPhysicallyAttached"
206
+
207
+ errorCode: str = ErrorCodes.TIP_DROP_FAILED.value.code
208
+ detail: str = ErrorCodes.TIP_DROP_FAILED.value.detail
209
+
210
+ errorInfo: ErrorLocationInfo
@@ -1,18 +1,28 @@
1
1
  """Prepare to aspirate command request, result, and implementation models."""
2
2
 
3
3
  from __future__ import annotations
4
+ from opentrons_shared_data.errors.exceptions import PipetteOverpressureError
4
5
  from pydantic import BaseModel
5
- from typing import TYPE_CHECKING, Optional, Type
6
+ from typing import TYPE_CHECKING, Optional, Type, Union
6
7
  from typing_extensions import Literal
7
8
 
8
9
  from .pipetting_common import (
10
+ OverpressureError,
9
11
  PipetteIdMixin,
10
12
  )
11
- from .command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
13
+ from .command import (
14
+ AbstractCommandImpl,
15
+ BaseCommand,
16
+ BaseCommandCreate,
17
+ DefinedErrorData,
18
+ SuccessData,
19
+ )
12
20
  from ..errors.error_occurrence import ErrorOccurrence
13
21
 
14
22
  if TYPE_CHECKING:
15
- from ..execution.pipetting import PipettingHandler
23
+ from ..execution import PipettingHandler, GantryMover
24
+ from ..resources import ModelUtils
25
+
16
26
 
17
27
  PrepareToAspirateCommandType = Literal["prepareToAspirate"]
18
28
 
@@ -29,25 +39,62 @@ class PrepareToAspirateResult(BaseModel):
29
39
  pass
30
40
 
31
41
 
42
+ _ExecuteReturn = Union[
43
+ SuccessData[PrepareToAspirateResult],
44
+ DefinedErrorData[OverpressureError],
45
+ ]
46
+
47
+
32
48
  class PrepareToAspirateImplementation(
33
- AbstractCommandImpl[
34
- PrepareToAspirateParams, SuccessData[PrepareToAspirateResult, None]
35
- ]
49
+ AbstractCommandImpl[PrepareToAspirateParams, _ExecuteReturn]
36
50
  ):
37
51
  """Prepare for aspirate command implementation."""
38
52
 
39
- def __init__(self, pipetting: PipettingHandler, **kwargs: object) -> None:
53
+ def __init__(
54
+ self,
55
+ pipetting: PipettingHandler,
56
+ model_utils: ModelUtils,
57
+ gantry_mover: GantryMover,
58
+ **kwargs: object,
59
+ ) -> None:
40
60
  self._pipetting_handler = pipetting
61
+ self._model_utils = model_utils
62
+ self._gantry_mover = gantry_mover
41
63
 
42
- async def execute(
43
- self, params: PrepareToAspirateParams
44
- ) -> SuccessData[PrepareToAspirateResult, None]:
64
+ async def execute(self, params: PrepareToAspirateParams) -> _ExecuteReturn:
45
65
  """Prepare the pipette to aspirate."""
46
- await self._pipetting_handler.prepare_for_aspirate(
47
- pipette_id=params.pipetteId,
48
- )
49
-
50
- return SuccessData(public=PrepareToAspirateResult(), private=None)
66
+ current_position = await self._gantry_mover.get_position(params.pipetteId)
67
+ try:
68
+ await self._pipetting_handler.prepare_for_aspirate(
69
+ pipette_id=params.pipetteId,
70
+ )
71
+ except PipetteOverpressureError as e:
72
+ return DefinedErrorData(
73
+ public=OverpressureError(
74
+ id=self._model_utils.generate_id(),
75
+ createdAt=self._model_utils.get_timestamp(),
76
+ wrappedErrors=[
77
+ ErrorOccurrence.from_failed(
78
+ id=self._model_utils.generate_id(),
79
+ createdAt=self._model_utils.get_timestamp(),
80
+ error=e,
81
+ )
82
+ ],
83
+ errorInfo=(
84
+ {
85
+ "retryLocation": (
86
+ current_position.x,
87
+ current_position.y,
88
+ current_position.z,
89
+ )
90
+ }
91
+ ),
92
+ ),
93
+ )
94
+ else:
95
+ return SuccessData(
96
+ public=PrepareToAspirateResult(),
97
+ )
51
98
 
52
99
 
53
100
  class PrepareToAspirate(
@@ -6,9 +6,10 @@ from typing_extensions import Literal
6
6
 
7
7
  from .command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
8
8
  from ..errors.error_occurrence import ErrorOccurrence
9
+ from ..state.update_types import StateUpdate
9
10
 
10
11
  if TYPE_CHECKING:
11
- from ..state import StateView
12
+ from ..state.state import StateView
12
13
  from ..execution import EquipmentHandler
13
14
 
14
15
 
@@ -46,7 +47,7 @@ class ReloadLabwareResult(BaseModel):
46
47
 
47
48
 
48
49
  class ReloadLabwareImplementation(
49
- AbstractCommandImpl[ReloadLabwareParams, SuccessData[ReloadLabwareResult, None]]
50
+ AbstractCommandImpl[ReloadLabwareParams, SuccessData[ReloadLabwareResult]]
50
51
  ):
51
52
  """Reload labware command implementation."""
52
53
 
@@ -58,18 +59,26 @@ class ReloadLabwareImplementation(
58
59
 
59
60
  async def execute(
60
61
  self, params: ReloadLabwareParams
61
- ) -> SuccessData[ReloadLabwareResult, None]:
62
+ ) -> SuccessData[ReloadLabwareResult]:
62
63
  """Reload the definition and calibration data for a specific labware."""
63
64
  reloaded_labware = await self._equipment.reload_labware(
64
65
  labware_id=params.labwareId,
65
66
  )
66
67
 
68
+ state_update = StateUpdate()
69
+
70
+ state_update.set_labware_location(
71
+ labware_id=params.labwareId,
72
+ new_location=reloaded_labware.location,
73
+ new_offset_id=reloaded_labware.offsetId,
74
+ )
75
+
67
76
  return SuccessData(
68
77
  public=ReloadLabwareResult(
69
78
  labwareId=params.labwareId,
70
79
  offsetId=reloaded_labware.offsetId,
71
80
  ),
72
- private=None,
81
+ state_update=state_update,
73
82
  )
74
83
 
75
84
 
@@ -4,6 +4,7 @@ from pydantic import BaseModel, Field
4
4
  from typing import TYPE_CHECKING, Optional, Type
5
5
  from typing_extensions import Literal
6
6
 
7
+ from ..state import update_types
7
8
  from ..types import MotorAxis
8
9
  from .command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
9
10
  from ..errors.error_occurrence import ErrorOccurrence
@@ -38,7 +39,7 @@ class RetractAxisResult(BaseModel):
38
39
 
39
40
 
40
41
  class RetractAxisImplementation(
41
- AbstractCommandImpl[RetractAxisParams, SuccessData[RetractAxisResult, None]]
42
+ AbstractCommandImpl[RetractAxisParams, SuccessData[RetractAxisResult]]
42
43
  ):
43
44
  """Retract Axis command implementation."""
44
45
 
@@ -47,10 +48,12 @@ class RetractAxisImplementation(
47
48
 
48
49
  async def execute(
49
50
  self, params: RetractAxisParams
50
- ) -> SuccessData[RetractAxisResult, None]:
51
+ ) -> SuccessData[RetractAxisResult]:
51
52
  """Retract the specified axis."""
53
+ state_update = update_types.StateUpdate()
52
54
  await self._movement.retract_axis(axis=params.axis)
53
- return SuccessData(public=RetractAxisResult(), private=None)
55
+ state_update.clear_all_pipette_locations()
56
+ return SuccessData(public=RetractAxisResult(), state_update=state_update)
54
57
 
55
58
 
56
59
  class RetractAxis(BaseCommand[RetractAxisParams, RetractAxisResult, ErrorOccurrence]):
@@ -46,7 +46,7 @@ class SavePositionResult(BaseModel):
46
46
 
47
47
 
48
48
  class SavePositionImplementation(
49
- AbstractCommandImpl[SavePositionParams, SuccessData[SavePositionResult, None]]
49
+ AbstractCommandImpl[SavePositionParams, SuccessData[SavePositionResult]]
50
50
  ):
51
51
  """Save position command implementation."""
52
52
 
@@ -61,7 +61,7 @@ class SavePositionImplementation(
61
61
 
62
62
  async def execute(
63
63
  self, params: SavePositionParams
64
- ) -> SuccessData[SavePositionResult, None]:
64
+ ) -> SuccessData[SavePositionResult]:
65
65
  """Check the requested pipette's current position."""
66
66
  position_id = self._model_utils.ensure_id(params.positionId)
67
67
  fail_on_not_homed = (
@@ -76,7 +76,6 @@ class SavePositionImplementation(
76
76
  positionId=position_id,
77
77
  position=DeckPoint(x=x, y=y, z=z),
78
78
  ),
79
- private=None,
80
79
  )
81
80
 
82
81
 
@@ -29,7 +29,7 @@ class SetRailLightsResult(BaseModel):
29
29
 
30
30
 
31
31
  class SetRailLightsImplementation(
32
- AbstractCommandImpl[SetRailLightsParams, SuccessData[SetRailLightsResult, None]]
32
+ AbstractCommandImpl[SetRailLightsParams, SuccessData[SetRailLightsResult]]
33
33
  ):
34
34
  """setRailLights command implementation."""
35
35
 
@@ -38,10 +38,12 @@ class SetRailLightsImplementation(
38
38
 
39
39
  async def execute(
40
40
  self, params: SetRailLightsParams
41
- ) -> SuccessData[SetRailLightsResult, None]:
41
+ ) -> SuccessData[SetRailLightsResult]:
42
42
  """Dispatch a set lights command setting the state of the rail lights."""
43
43
  await self._rail_lights.set_rail_lights(params.on)
44
- return SuccessData(public=SetRailLightsResult(), private=None)
44
+ return SuccessData(
45
+ public=SetRailLightsResult(),
46
+ )
45
47
 
46
48
 
47
49
  class SetRailLights(
@@ -49,7 +49,7 @@ class SetStatusBarResult(BaseModel):
49
49
 
50
50
 
51
51
  class SetStatusBarImplementation(
52
- AbstractCommandImpl[SetStatusBarParams, SuccessData[SetStatusBarResult, None]]
52
+ AbstractCommandImpl[SetStatusBarParams, SuccessData[SetStatusBarResult]]
53
53
  ):
54
54
  """setStatusBar command implementation."""
55
55
 
@@ -58,12 +58,14 @@ class SetStatusBarImplementation(
58
58
 
59
59
  async def execute(
60
60
  self, params: SetStatusBarParams
61
- ) -> SuccessData[SetStatusBarResult, None]:
61
+ ) -> SuccessData[SetStatusBarResult]:
62
62
  """Execute the setStatusBar command."""
63
63
  if not self._status_bar.status_bar_should_not_be_changed():
64
64
  state = _animation_to_status_bar_state(params.animation)
65
65
  await self._status_bar.set_status_bar(state)
66
- return SuccessData(public=SetStatusBarResult(), private=None)
66
+ return SuccessData(
67
+ public=SetStatusBarResult(),
68
+ )
67
69
 
68
70
 
69
71
  class SetStatusBar(
@@ -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
  DeactivateTemperatureCommandType = Literal["temperatureModule/deactivate"]
@@ -27,7 +27,7 @@ class DeactivateTemperatureResult(BaseModel):
27
27
 
28
28
  class DeactivateTemperatureImpl(
29
29
  AbstractCommandImpl[
30
- DeactivateTemperatureParams, SuccessData[DeactivateTemperatureResult, None]
30
+ DeactivateTemperatureParams, SuccessData[DeactivateTemperatureResult]
31
31
  ]
32
32
  ):
33
33
  """Execution implementation of a Temperature Module's deactivate command."""
@@ -43,7 +43,7 @@ class DeactivateTemperatureImpl(
43
43
 
44
44
  async def execute(
45
45
  self, params: DeactivateTemperatureParams
46
- ) -> SuccessData[DeactivateTemperatureResult, None]:
46
+ ) -> SuccessData[DeactivateTemperatureResult]:
47
47
  """Deactivate a Temperature Module."""
48
48
  # Allow propagation of ModuleNotLoadedError and WrongModuleTypeError.
49
49
  module_substate = self._state_view.modules.get_temperature_module_substate(
@@ -57,7 +57,9 @@ class DeactivateTemperatureImpl(
57
57
 
58
58
  if temp_hardware_module is not None:
59
59
  await temp_hardware_module.deactivate()
60
- return SuccessData(public=DeactivateTemperatureResult(), private=None)
60
+ return SuccessData(
61
+ public=DeactivateTemperatureResult(),
62
+ )
61
63
 
62
64
 
63
65
  class DeactivateTemperature(
@@ -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
  SetTargetTemperatureCommandType = Literal["temperatureModule/setTargetTemperature"]
@@ -34,7 +34,7 @@ class SetTargetTemperatureResult(BaseModel):
34
34
 
35
35
  class SetTargetTemperatureImpl(
36
36
  AbstractCommandImpl[
37
- SetTargetTemperatureParams, SuccessData[SetTargetTemperatureResult, None]
37
+ SetTargetTemperatureParams, SuccessData[SetTargetTemperatureResult]
38
38
  ]
39
39
  ):
40
40
  """Execution implementation of a Temperature Module's set temperature command."""
@@ -50,7 +50,7 @@ class SetTargetTemperatureImpl(
50
50
 
51
51
  async def execute(
52
52
  self, params: SetTargetTemperatureParams
53
- ) -> SuccessData[SetTargetTemperatureResult, None]:
53
+ ) -> SuccessData[SetTargetTemperatureResult]:
54
54
  """Set a Temperature Module's target temperature."""
55
55
  # Allow propagation of ModuleNotLoadedError and WrongModuleTypeError.
56
56
  module_substate = self._state_view.modules.get_temperature_module_substate(
@@ -69,7 +69,6 @@ class SetTargetTemperatureImpl(
69
69
  await temp_hardware_module.start_set_temperature(celsius=validated_temp)
70
70
  return SuccessData(
71
71
  public=SetTargetTemperatureResult(targetTemperature=validated_temp),
72
- private=None,
73
72
  )
74
73
 
75
74
 
@@ -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
  WaitForTemperatureCommandType = Literal["temperatureModule/waitForTemperature"]
@@ -35,9 +35,7 @@ class WaitForTemperatureResult(BaseModel):
35
35
 
36
36
 
37
37
  class WaitForTemperatureImpl(
38
- AbstractCommandImpl[
39
- WaitForTemperatureParams, SuccessData[WaitForTemperatureResult, None]
40
- ]
38
+ AbstractCommandImpl[WaitForTemperatureParams, SuccessData[WaitForTemperatureResult]]
41
39
  ):
42
40
  """Execution implementation of Temperature Module's wait for temperature command."""
43
41
 
@@ -52,7 +50,7 @@ class WaitForTemperatureImpl(
52
50
 
53
51
  async def execute(
54
52
  self, params: WaitForTemperatureParams
55
- ) -> SuccessData[WaitForTemperatureResult, None]:
53
+ ) -> SuccessData[WaitForTemperatureResult]:
56
54
  """Wait for a Temperature Module's target temperature."""
57
55
  # Allow propagation of ModuleNotLoadedError and WrongModuleTypeError.
58
56
  module_substate = self._state_view.modules.get_temperature_module_substate(
@@ -74,7 +72,9 @@ class WaitForTemperatureImpl(
74
72
  await temp_hardware_module.await_temperature(
75
73
  awaiting_temperature=target_temp
76
74
  )
77
- return SuccessData(public=WaitForTemperatureResult(), private=None)
75
+ return SuccessData(
76
+ public=WaitForTemperatureResult(),
77
+ )
78
78
 
79
79
 
80
80
  class WaitForTemperature(
@@ -73,6 +73,16 @@ from .run_profile import (
73
73
  RunProfileCreate,
74
74
  )
75
75
 
76
+ from .run_extended_profile import (
77
+ RunExtendedProfileCommandType,
78
+ RunExtendedProfileParams,
79
+ RunExtendedProfileResult,
80
+ RunExtendedProfile,
81
+ RunExtendedProfileCreate,
82
+ ProfileCycle,
83
+ ProfileStep,
84
+ )
85
+
76
86
 
77
87
  __all__ = [
78
88
  # Set target block temperature command models
@@ -130,4 +140,13 @@ __all__ = [
130
140
  "RunProfileResult",
131
141
  "RunProfile",
132
142
  "RunProfileCreate",
143
+ # Run extended profile command models.
144
+ "RunExtendedProfileCommandType",
145
+ "RunExtendedProfileParams",
146
+ "RunExtendedProfileStepParams",
147
+ "RunExtendedProfileResult",
148
+ "RunExtendedProfile",
149
+ "RunExtendedProfileCreate",
150
+ "ProfileCycle",
151
+ "ProfileStep",
133
152
  ]