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,29 +1,49 @@
1
1
  """Models and implementation for the ``moveLabware`` command."""
2
2
 
3
3
  from __future__ import annotations
4
+ from opentrons_shared_data.errors.exceptions import (
5
+ FailedGripperPickupError,
6
+ LabwareDroppedError,
7
+ StallOrCollisionDetectedError,
8
+ )
4
9
  from pydantic import BaseModel, Field
5
10
  from typing import TYPE_CHECKING, Optional, Type
6
11
  from typing_extensions import Literal
7
12
 
13
+ from opentrons.protocol_engine.resources.model_utils import ModelUtils
8
14
  from opentrons.types import Point
9
15
  from ..types import (
16
+ ModuleModel,
17
+ CurrentWell,
10
18
  LabwareLocation,
11
19
  DeckSlotLocation,
20
+ ModuleLocation,
12
21
  OnLabwareLocation,
13
22
  AddressableAreaLocation,
14
23
  LabwareMovementStrategy,
15
24
  LabwareOffsetVector,
16
25
  LabwareMovementOffsetData,
17
26
  )
18
- from ..errors import LabwareMovementNotAllowedError, NotSupportedOnRobotType
27
+ from ..errors import (
28
+ LabwareMovementNotAllowedError,
29
+ NotSupportedOnRobotType,
30
+ LabwareOffsetDoesNotExistError,
31
+ )
19
32
  from ..resources import labware_validation, fixture_validation
20
- from .command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
33
+ from .command import (
34
+ AbstractCommandImpl,
35
+ BaseCommand,
36
+ BaseCommandCreate,
37
+ DefinedErrorData,
38
+ SuccessData,
39
+ )
21
40
  from ..errors.error_occurrence import ErrorOccurrence
41
+ from ..state.update_types import StateUpdate
22
42
  from opentrons_shared_data.gripper.constants import GRIPPER_PADDLE_WIDTH
23
43
 
24
44
  if TYPE_CHECKING:
25
45
  from ..execution import EquipmentHandler, RunControlHandler, LabwareMovementHandler
26
- from ..state import StateView
46
+ from ..state.state import StateView
27
47
 
28
48
 
29
49
  MoveLabwareCommandType = Literal["moveLabware"]
@@ -33,7 +53,6 @@ MoveLabwareCommandType = Literal["moveLabware"]
33
53
  _TRASH_CHUTE_DROP_BUFFER_MM = 8
34
54
 
35
55
 
36
- # TODO (spp, 2022-12-14): https://opentrons.atlassian.net/browse/RLAB-237
37
56
  class MoveLabwareParams(BaseModel):
38
57
  """Input parameters for a ``moveLabware`` command."""
39
58
 
@@ -74,28 +93,42 @@ class MoveLabwareResult(BaseModel):
74
93
  )
75
94
 
76
95
 
77
- class MoveLabwareImplementation(
78
- AbstractCommandImpl[MoveLabwareParams, SuccessData[MoveLabwareResult, None]]
79
- ):
96
+ class GripperMovementError(ErrorOccurrence):
97
+ """Returned when something physically goes wrong when the gripper moves labware.
98
+
99
+ When this error happens, the engine will leave the labware in its original place.
100
+ """
101
+
102
+ isDefined: bool = True
103
+
104
+ errorType: Literal["gripperMovement"] = "gripperMovement"
105
+
106
+
107
+ _ExecuteReturn = SuccessData[MoveLabwareResult] | DefinedErrorData[GripperMovementError]
108
+
109
+
110
+ class MoveLabwareImplementation(AbstractCommandImpl[MoveLabwareParams, _ExecuteReturn]):
80
111
  """The execution implementation for ``moveLabware`` commands."""
81
112
 
82
113
  def __init__(
83
114
  self,
115
+ model_utils: ModelUtils,
84
116
  state_view: StateView,
85
117
  equipment: EquipmentHandler,
86
118
  labware_movement: LabwareMovementHandler,
87
119
  run_control: RunControlHandler,
88
120
  **kwargs: object,
89
121
  ) -> None:
122
+ self._model_utils = model_utils
90
123
  self._state_view = state_view
91
124
  self._equipment = equipment
92
125
  self._labware_movement = labware_movement
93
126
  self._run_control = run_control
94
127
 
95
- async def execute( # noqa: C901
96
- self, params: MoveLabwareParams
97
- ) -> SuccessData[MoveLabwareResult, None]:
128
+ async def execute(self, params: MoveLabwareParams) -> _ExecuteReturn: # noqa: C901
98
129
  """Move a loaded labware to a new location."""
130
+ state_update = StateUpdate()
131
+
99
132
  # Allow propagation of LabwareNotLoadedError.
100
133
  current_labware = self._state_view.labware.get(labware_id=params.labwareId)
101
134
  current_labware_definition = self._state_view.labware.get_definition(
@@ -103,6 +136,7 @@ class MoveLabwareImplementation(
103
136
  )
104
137
  definition_uri = current_labware.definitionUri
105
138
  post_drop_slide_offset: Optional[Point] = None
139
+ trash_lid_drop_offset: Optional[LabwareOffsetVector] = None
106
140
 
107
141
  if self._state_view.labware.is_fixed_trash(params.labwareId):
108
142
  raise LabwareMovementNotAllowedError(
@@ -111,9 +145,11 @@ class MoveLabwareImplementation(
111
145
 
112
146
  if isinstance(params.newLocation, AddressableAreaLocation):
113
147
  area_name = params.newLocation.addressableAreaName
114
- if not fixture_validation.is_gripper_waste_chute(
115
- area_name
116
- ) and not fixture_validation.is_deck_slot(area_name):
148
+ if (
149
+ not fixture_validation.is_gripper_waste_chute(area_name)
150
+ and not fixture_validation.is_deck_slot(area_name)
151
+ and not fixture_validation.is_trash(area_name)
152
+ ):
117
153
  raise LabwareMovementNotAllowedError(
118
154
  f"Cannot move {current_labware.loadName} to addressable area {area_name}"
119
155
  )
@@ -135,6 +171,32 @@ class MoveLabwareImplementation(
135
171
  y=0,
136
172
  z=0,
137
173
  )
174
+ elif fixture_validation.is_trash(area_name):
175
+ # When dropping labware in the trash bins we want to ensure they are lids
176
+ # and enforce a y-axis drop offset to ensure they fall within the trash bin
177
+ if labware_validation.validate_definition_is_lid(
178
+ self._state_view.labware.get_definition(params.labwareId)
179
+ ):
180
+ lid_disposable_offfets = (
181
+ current_labware_definition.gripperOffsets.get(
182
+ "lidDisposalOffsets"
183
+ )
184
+ )
185
+ if lid_disposable_offfets is not None:
186
+ trash_lid_drop_offset = LabwareOffsetVector(
187
+ x=lid_disposable_offfets.dropOffset.x,
188
+ y=lid_disposable_offfets.dropOffset.y,
189
+ z=lid_disposable_offfets.dropOffset.z,
190
+ )
191
+ else:
192
+ raise LabwareOffsetDoesNotExistError(
193
+ f"Labware Definition {current_labware.loadName} does not contain required field 'lidDisposalOffsets' of 'gripperOffsets'."
194
+ )
195
+ else:
196
+ raise LabwareMovementNotAllowedError(
197
+ "Can only move labware with allowed role 'Lid' to a Trash Bin."
198
+ )
199
+
138
200
  elif isinstance(params.newLocation, DeckSlotLocation):
139
201
  self._state_view.addressable_areas.raise_if_area_not_in_deck_configuration(
140
202
  params.newLocation.slotName.id
@@ -157,6 +219,17 @@ class MoveLabwareImplementation(
157
219
  top_labware_definition=current_labware_definition,
158
220
  bottom_labware_id=available_new_location.labwareId,
159
221
  )
222
+ if params.labwareId == available_new_location.labwareId:
223
+ raise LabwareMovementNotAllowedError(
224
+ "Cannot move a labware onto itself."
225
+ )
226
+ # Validate labware for the absorbance reader
227
+ elif isinstance(available_new_location, ModuleLocation):
228
+ module = self._state_view.modules.get(available_new_location.moduleId)
229
+ if module is not None and module.model == ModuleModel.ABSORBANCE_READER_V1:
230
+ self._state_view.labware.raise_if_labware_incompatible_with_plate_reader(
231
+ current_labware_definition
232
+ )
160
233
 
161
234
  # Allow propagation of ModuleNotLoadedError.
162
235
  new_offset_id = self._equipment.find_applicable_labware_offset_id(
@@ -201,24 +274,83 @@ class MoveLabwareImplementation(
201
274
  dropOffset=params.dropOffset or LabwareOffsetVector(x=0, y=0, z=0),
202
275
  )
203
276
 
204
- # Skips gripper moves when using virtual gripper
205
- await self._labware_movement.move_labware_with_gripper(
206
- labware_id=params.labwareId,
207
- current_location=validated_current_loc,
208
- new_location=validated_new_loc,
209
- user_offset_data=user_offset_data,
210
- post_drop_slide_offset=post_drop_slide_offset,
211
- )
277
+ if trash_lid_drop_offset:
278
+ user_offset_data.dropOffset += trash_lid_drop_offset
279
+
280
+ try:
281
+ # Skips gripper moves when using virtual gripper
282
+ await self._labware_movement.move_labware_with_gripper(
283
+ labware_id=params.labwareId,
284
+ current_location=validated_current_loc,
285
+ new_location=validated_new_loc,
286
+ user_offset_data=user_offset_data,
287
+ post_drop_slide_offset=post_drop_slide_offset,
288
+ )
289
+ except (
290
+ FailedGripperPickupError,
291
+ LabwareDroppedError,
292
+ StallOrCollisionDetectedError,
293
+ # todo(mm, 2024-09-26): Catch LabwareNotPickedUpError when that exists and
294
+ # move_labware_with_gripper() raises it.
295
+ ) as exception:
296
+ gripper_movement_error: GripperMovementError | None = (
297
+ GripperMovementError(
298
+ id=self._model_utils.generate_id(),
299
+ createdAt=self._model_utils.get_timestamp(),
300
+ errorCode=exception.code.value.code,
301
+ detail=exception.code.value.detail,
302
+ wrappedErrors=[
303
+ ErrorOccurrence.from_failed(
304
+ id=self._model_utils.generate_id(),
305
+ createdAt=self._model_utils.get_timestamp(),
306
+ error=exception,
307
+ )
308
+ ],
309
+ )
310
+ )
311
+ else:
312
+ gripper_movement_error = None
313
+
314
+ # All mounts will have been retracted as part of the gripper move.
315
+ state_update.clear_all_pipette_locations()
316
+
317
+ if gripper_movement_error:
318
+ return DefinedErrorData(
319
+ public=gripper_movement_error,
320
+ state_update=state_update,
321
+ )
322
+
212
323
  elif params.strategy == LabwareMovementStrategy.MANUAL_MOVE_WITH_PAUSE:
213
324
  # Pause to allow for manual labware movement
214
325
  await self._run_control.wait_for_resume()
215
326
 
327
+ # We may have just moved the labware that contains the current well out from
328
+ # under the pipette. Clear the current location to reflect the fact that the
329
+ # pipette is no longer over any labware. This is necessary for safe path
330
+ # planning in case the next movement goes to the same labware (now in a new
331
+ # place).
332
+ pipette_location = self._state_view.pipettes.get_current_location()
333
+ if (
334
+ isinstance(pipette_location, CurrentWell)
335
+ and pipette_location.labware_id == params.labwareId
336
+ ):
337
+ state_update.clear_all_pipette_locations()
338
+
339
+ state_update.set_labware_location(
340
+ labware_id=params.labwareId,
341
+ new_location=available_new_location,
342
+ new_offset_id=new_offset_id,
343
+ )
344
+
216
345
  return SuccessData(
217
- public=MoveLabwareResult(offsetId=new_offset_id), private=None
346
+ public=MoveLabwareResult(offsetId=new_offset_id),
347
+ state_update=state_update,
218
348
  )
219
349
 
220
350
 
221
- class MoveLabware(BaseCommand[MoveLabwareParams, MoveLabwareResult, ErrorOccurrence]):
351
+ class MoveLabware(
352
+ BaseCommand[MoveLabwareParams, MoveLabwareResult, GripperMovementError]
353
+ ):
222
354
  """A ``moveLabware`` command."""
223
355
 
224
356
  commandType: MoveLabwareCommandType = "moveLabware"
@@ -4,6 +4,8 @@ from pydantic import BaseModel, Field
4
4
  from typing import TYPE_CHECKING, Optional, Type
5
5
  from typing_extensions import Literal
6
6
 
7
+
8
+ from ..state import update_types
7
9
  from ..types import MovementAxis, DeckPoint
8
10
  from .command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
9
11
  from ..errors.error_occurrence import ErrorOccurrence
@@ -37,7 +39,7 @@ class MoveRelativeResult(DestinationPositionResult):
37
39
 
38
40
 
39
41
  class MoveRelativeImplementation(
40
- AbstractCommandImpl[MoveRelativeParams, SuccessData[MoveRelativeResult, None]]
42
+ AbstractCommandImpl[MoveRelativeParams, SuccessData[MoveRelativeResult]]
41
43
  ):
42
44
  """Move relative command implementation."""
43
45
 
@@ -46,16 +48,26 @@ class MoveRelativeImplementation(
46
48
 
47
49
  async def execute(
48
50
  self, params: MoveRelativeParams
49
- ) -> SuccessData[MoveRelativeResult, None]:
51
+ ) -> SuccessData[MoveRelativeResult]:
50
52
  """Move (jog) a given pipette a relative distance."""
53
+ state_update = update_types.StateUpdate()
54
+
51
55
  x, y, z = await self._movement.move_relative(
52
56
  pipette_id=params.pipetteId,
53
57
  axis=params.axis,
54
58
  distance=params.distance,
55
59
  )
60
+ deck_point = DeckPoint.construct(x=x, y=y, z=z)
61
+ state_update.pipette_location = update_types.PipetteLocationUpdate(
62
+ pipette_id=params.pipetteId,
63
+ # TODO(jbl 2023-02-14): Need to investigate whether move relative should clear current location
64
+ new_location=update_types.NO_CHANGE,
65
+ new_deck_point=deck_point,
66
+ )
56
67
 
57
68
  return SuccessData(
58
- public=MoveRelativeResult(position=DeckPoint(x=x, y=y, z=z)), private=None
69
+ public=MoveRelativeResult(position=deck_point),
70
+ state_update=state_update,
59
71
  )
60
72
 
61
73
 
@@ -4,7 +4,10 @@ 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_shared_data.pipette.types import PipetteNameType
8
+
7
9
  from ..errors import LocationNotAccessibleByPipetteError
10
+ from ..state import update_types
8
11
  from ..types import DeckPoint, AddressableOffsetVector
9
12
  from ..resources import fixture_validation
10
13
  from .pipetting_common import (
@@ -17,7 +20,7 @@ from ..errors.error_occurrence import ErrorOccurrence
17
20
 
18
21
  if TYPE_CHECKING:
19
22
  from ..execution import MovementHandler
20
- from ..state import StateView
23
+ from ..state.state import StateView
21
24
 
22
25
  MoveToAddressableAreaCommandType = Literal["moveToAddressableArea"]
23
26
 
@@ -73,7 +76,7 @@ class MoveToAddressableAreaResult(DestinationPositionResult):
73
76
 
74
77
  class MoveToAddressableAreaImplementation(
75
78
  AbstractCommandImpl[
76
- MoveToAddressableAreaParams, SuccessData[MoveToAddressableAreaResult, None]
79
+ MoveToAddressableAreaParams, SuccessData[MoveToAddressableAreaResult]
77
80
  ]
78
81
  ):
79
82
  """Move to addressable area command implementation."""
@@ -86,11 +89,26 @@ class MoveToAddressableAreaImplementation(
86
89
 
87
90
  async def execute(
88
91
  self, params: MoveToAddressableAreaParams
89
- ) -> SuccessData[MoveToAddressableAreaResult, None]:
92
+ ) -> SuccessData[MoveToAddressableAreaResult]:
90
93
  """Move the requested pipette to the requested addressable area."""
94
+ state_update = update_types.StateUpdate()
95
+
91
96
  self._state_view.addressable_areas.raise_if_area_not_in_deck_configuration(
92
97
  params.addressableAreaName
93
98
  )
99
+ loaded_pipette = self._state_view.pipettes.get(params.pipetteId)
100
+ if loaded_pipette.pipetteName in (
101
+ PipetteNameType.P10_SINGLE,
102
+ PipetteNameType.P10_MULTI,
103
+ PipetteNameType.P50_MULTI,
104
+ PipetteNameType.P50_SINGLE,
105
+ PipetteNameType.P300_SINGLE,
106
+ PipetteNameType.P300_MULTI,
107
+ PipetteNameType.P1000_SINGLE,
108
+ ):
109
+ extra_z_offset: Optional[float] = 5.0
110
+ else:
111
+ extra_z_offset = None
94
112
 
95
113
  if fixture_validation.is_staging_slot(params.addressableAreaName):
96
114
  raise LocationNotAccessibleByPipetteError(
@@ -105,11 +123,18 @@ class MoveToAddressableAreaImplementation(
105
123
  minimum_z_height=params.minimumZHeight,
106
124
  speed=params.speed,
107
125
  stay_at_highest_possible_z=params.stayAtHighestPossibleZ,
126
+ highest_possible_z_extra_offset=extra_z_offset,
127
+ )
128
+ deck_point = DeckPoint.construct(x=x, y=y, z=z)
129
+ state_update.set_pipette_location(
130
+ pipette_id=params.pipetteId,
131
+ new_addressable_area_name=params.addressableAreaName,
132
+ new_deck_point=deck_point,
108
133
  )
109
134
 
110
135
  return SuccessData(
111
136
  public=MoveToAddressableAreaResult(position=DeckPoint(x=x, y=y, z=z)),
112
- private=None,
137
+ state_update=state_update,
113
138
  )
114
139
 
115
140
 
@@ -5,6 +5,7 @@ from typing import TYPE_CHECKING, Optional, Type
5
5
  from typing_extensions import Literal
6
6
 
7
7
  from ..errors import LocationNotAccessibleByPipetteError
8
+ from ..state import update_types
8
9
  from ..types import DeckPoint, AddressableOffsetVector
9
10
  from ..resources import fixture_validation
10
11
  from .pipetting_common import (
@@ -17,7 +18,7 @@ from ..errors.error_occurrence import ErrorOccurrence
17
18
 
18
19
  if TYPE_CHECKING:
19
20
  from ..execution import MovementHandler
20
- from ..state import StateView
21
+ from ..state.state import StateView
21
22
 
22
23
  MoveToAddressableAreaForDropTipCommandType = Literal["moveToAddressableAreaForDropTip"]
23
24
 
@@ -85,7 +86,7 @@ class MoveToAddressableAreaForDropTipResult(DestinationPositionResult):
85
86
  class MoveToAddressableAreaForDropTipImplementation(
86
87
  AbstractCommandImpl[
87
88
  MoveToAddressableAreaForDropTipParams,
88
- SuccessData[MoveToAddressableAreaForDropTipResult, None],
89
+ SuccessData[MoveToAddressableAreaForDropTipResult],
89
90
  ]
90
91
  ):
91
92
  """Move to addressable area for drop tip command implementation."""
@@ -98,8 +99,10 @@ class MoveToAddressableAreaForDropTipImplementation(
98
99
 
99
100
  async def execute(
100
101
  self, params: MoveToAddressableAreaForDropTipParams
101
- ) -> SuccessData[MoveToAddressableAreaForDropTipResult, None]:
102
+ ) -> SuccessData[MoveToAddressableAreaForDropTipResult]:
102
103
  """Move the requested pipette to the requested addressable area in preperation of a drop tip."""
104
+ state_update = update_types.StateUpdate()
105
+
103
106
  self._state_view.addressable_areas.raise_if_area_not_in_deck_configuration(
104
107
  params.addressableAreaName
105
108
  )
@@ -126,12 +129,18 @@ class MoveToAddressableAreaForDropTipImplementation(
126
129
  speed=params.speed,
127
130
  ignore_tip_configuration=params.ignoreTipConfiguration,
128
131
  )
132
+ deck_point = DeckPoint.construct(x=x, y=y, z=z)
133
+ state_update.set_pipette_location(
134
+ pipette_id=params.pipetteId,
135
+ new_addressable_area_name=params.addressableAreaName,
136
+ new_deck_point=deck_point,
137
+ )
129
138
 
130
139
  return SuccessData(
131
140
  public=MoveToAddressableAreaForDropTipResult(
132
141
  position=DeckPoint(x=x, y=y, z=z)
133
142
  ),
134
- private=None,
143
+ state_update=state_update,
135
144
  )
136
145
 
137
146
 
@@ -5,6 +5,8 @@ from pydantic import Field
5
5
  from typing import Optional, Type, TYPE_CHECKING
6
6
  from typing_extensions import Literal
7
7
 
8
+
9
+ from ..state import update_types
8
10
  from ..types import DeckPoint
9
11
  from .pipetting_common import PipetteIdMixin, MovementMixin, DestinationPositionResult
10
12
  from .command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
@@ -33,9 +35,7 @@ class MoveToCoordinatesResult(DestinationPositionResult):
33
35
 
34
36
 
35
37
  class MoveToCoordinatesImplementation(
36
- AbstractCommandImpl[
37
- MoveToCoordinatesParams, SuccessData[MoveToCoordinatesResult, None]
38
- ]
38
+ AbstractCommandImpl[MoveToCoordinatesParams, SuccessData[MoveToCoordinatesResult]]
39
39
  ):
40
40
  """Move to coordinates command implementation."""
41
41
 
@@ -48,8 +48,10 @@ class MoveToCoordinatesImplementation(
48
48
 
49
49
  async def execute(
50
50
  self, params: MoveToCoordinatesParams
51
- ) -> SuccessData[MoveToCoordinatesResult, None]:
51
+ ) -> SuccessData[MoveToCoordinatesResult]:
52
52
  """Move the requested pipette to the requested coordinates."""
53
+ state_update = update_types.StateUpdate()
54
+
53
55
  x, y, z = await self._movement.move_to_coordinates(
54
56
  pipette_id=params.pipetteId,
55
57
  deck_coordinates=params.coordinates,
@@ -57,10 +59,14 @@ class MoveToCoordinatesImplementation(
57
59
  additional_min_travel_z=params.minimumZHeight,
58
60
  speed=params.speed,
59
61
  )
62
+ deck_point = DeckPoint.construct(x=x, y=y, z=z)
63
+ state_update.pipette_location = update_types.PipetteLocationUpdate(
64
+ pipette_id=params.pipetteId, new_location=None, new_deck_point=deck_point
65
+ )
60
66
 
61
67
  return SuccessData(
62
68
  public=MoveToCoordinatesResult(position=DeckPoint(x=x, y=y, z=z)),
63
- private=None,
69
+ state_update=state_update,
64
70
  )
65
71
 
66
72
 
@@ -12,9 +12,12 @@ from .pipetting_common import (
12
12
  )
13
13
  from .command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
14
14
  from ..errors.error_occurrence import ErrorOccurrence
15
+ from ..state import update_types
16
+ from ..errors import LabwareIsTipRackError
15
17
 
16
18
  if TYPE_CHECKING:
17
19
  from ..execution import MovementHandler
20
+ from ..state.state import StateView
18
21
 
19
22
  MoveToWellCommandType = Literal["moveToWell"]
20
23
 
@@ -32,29 +35,53 @@ class MoveToWellResult(DestinationPositionResult):
32
35
 
33
36
 
34
37
  class MoveToWellImplementation(
35
- AbstractCommandImpl[MoveToWellParams, SuccessData[MoveToWellResult, None]]
38
+ AbstractCommandImpl[MoveToWellParams, SuccessData[MoveToWellResult]]
36
39
  ):
37
40
  """Move to well command implementation."""
38
41
 
39
- def __init__(self, movement: MovementHandler, **kwargs: object) -> None:
42
+ def __init__(
43
+ self, state_view: StateView, movement: MovementHandler, **kwargs: object
44
+ ) -> None:
45
+ self._state_view = state_view
40
46
  self._movement = movement
41
47
 
42
- async def execute(
43
- self, params: MoveToWellParams
44
- ) -> SuccessData[MoveToWellResult, None]:
48
+ async def execute(self, params: MoveToWellParams) -> SuccessData[MoveToWellResult]:
45
49
  """Move the requested pipette to the requested well."""
50
+ pipette_id = params.pipetteId
51
+ labware_id = params.labwareId
52
+ well_name = params.wellName
53
+ well_location = params.wellLocation
54
+
55
+ state_update = update_types.StateUpdate()
56
+
57
+ if (
58
+ self._state_view.labware.is_tiprack(labware_id)
59
+ and well_location.volumeOffset
60
+ ):
61
+ raise LabwareIsTipRackError(
62
+ "Cannot specify a WellLocation with a volumeOffset with movement to a tip rack"
63
+ )
64
+
46
65
  x, y, z = await self._movement.move_to_well(
47
- pipette_id=params.pipetteId,
48
- labware_id=params.labwareId,
49
- well_name=params.wellName,
50
- well_location=params.wellLocation,
66
+ pipette_id=pipette_id,
67
+ labware_id=labware_id,
68
+ well_name=well_name,
69
+ well_location=well_location,
51
70
  force_direct=params.forceDirect,
52
71
  minimum_z_height=params.minimumZHeight,
53
72
  speed=params.speed,
54
73
  )
74
+ deck_point = DeckPoint.construct(x=x, y=y, z=z)
75
+ state_update.set_pipette_location(
76
+ pipette_id=pipette_id,
77
+ new_labware_id=labware_id,
78
+ new_well_name=well_name,
79
+ new_deck_point=deck_point,
80
+ )
55
81
 
56
82
  return SuccessData(
57
- public=MoveToWellResult(position=DeckPoint(x=x, y=y, z=z)), private=None
83
+ public=MoveToWellResult(position=deck_point),
84
+ state_update=state_update,
58
85
  )
59
86
 
60
87