opentrons 8.2.0a3__py2.py3-none-any.whl → 8.3.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/calibration_storage/deck_configuration.py +3 -3
  2. opentrons/calibration_storage/file_operators.py +3 -3
  3. opentrons/calibration_storage/helpers.py +3 -1
  4. opentrons/calibration_storage/ot2/models/v1.py +16 -29
  5. opentrons/calibration_storage/ot2/tip_length.py +7 -4
  6. opentrons/calibration_storage/ot3/models/v1.py +14 -23
  7. opentrons/cli/analyze.py +18 -6
  8. opentrons/config/defaults_ot3.py +1 -0
  9. opentrons/drivers/asyncio/communication/__init__.py +2 -0
  10. opentrons/drivers/asyncio/communication/errors.py +16 -3
  11. opentrons/drivers/asyncio/communication/serial_connection.py +24 -9
  12. opentrons/drivers/command_builder.py +2 -2
  13. opentrons/drivers/flex_stacker/__init__.py +9 -0
  14. opentrons/drivers/flex_stacker/abstract.py +89 -0
  15. opentrons/drivers/flex_stacker/driver.py +260 -0
  16. opentrons/drivers/flex_stacker/simulator.py +109 -0
  17. opentrons/drivers/flex_stacker/types.py +138 -0
  18. opentrons/drivers/heater_shaker/driver.py +18 -3
  19. opentrons/drivers/temp_deck/driver.py +13 -3
  20. opentrons/drivers/thermocycler/driver.py +17 -3
  21. opentrons/execute.py +3 -1
  22. opentrons/hardware_control/__init__.py +1 -2
  23. opentrons/hardware_control/api.py +33 -21
  24. opentrons/hardware_control/backends/flex_protocol.py +17 -7
  25. opentrons/hardware_control/backends/ot3controller.py +213 -63
  26. opentrons/hardware_control/backends/ot3simulator.py +18 -9
  27. opentrons/hardware_control/backends/ot3utils.py +43 -15
  28. opentrons/hardware_control/dev_types.py +4 -0
  29. opentrons/hardware_control/emulation/heater_shaker.py +4 -0
  30. opentrons/hardware_control/emulation/module_server/client.py +1 -1
  31. opentrons/hardware_control/emulation/module_server/server.py +5 -3
  32. opentrons/hardware_control/emulation/settings.py +3 -4
  33. opentrons/hardware_control/instruments/ot2/instrument_calibration.py +2 -1
  34. opentrons/hardware_control/instruments/ot2/pipette.py +15 -22
  35. opentrons/hardware_control/instruments/ot2/pipette_handler.py +8 -1
  36. opentrons/hardware_control/instruments/ot3/gripper.py +2 -2
  37. opentrons/hardware_control/instruments/ot3/pipette.py +23 -22
  38. opentrons/hardware_control/instruments/ot3/pipette_handler.py +10 -1
  39. opentrons/hardware_control/modules/mod_abc.py +2 -2
  40. opentrons/hardware_control/motion_utilities.py +68 -0
  41. opentrons/hardware_control/nozzle_manager.py +39 -41
  42. opentrons/hardware_control/ot3_calibration.py +1 -1
  43. opentrons/hardware_control/ot3api.py +78 -31
  44. opentrons/hardware_control/protocols/gripper_controller.py +3 -0
  45. opentrons/hardware_control/protocols/hardware_manager.py +5 -1
  46. opentrons/hardware_control/protocols/liquid_handler.py +22 -1
  47. opentrons/hardware_control/protocols/motion_controller.py +7 -0
  48. opentrons/hardware_control/robot_calibration.py +1 -1
  49. opentrons/hardware_control/types.py +61 -0
  50. opentrons/legacy_commands/commands.py +37 -0
  51. opentrons/legacy_commands/types.py +39 -0
  52. opentrons/protocol_api/__init__.py +20 -1
  53. opentrons/protocol_api/_liquid.py +24 -49
  54. opentrons/protocol_api/_liquid_properties.py +754 -0
  55. opentrons/protocol_api/_types.py +24 -0
  56. opentrons/protocol_api/core/common.py +2 -0
  57. opentrons/protocol_api/core/engine/instrument.py +191 -10
  58. opentrons/protocol_api/core/engine/labware.py +29 -7
  59. opentrons/protocol_api/core/engine/protocol.py +130 -5
  60. opentrons/protocol_api/core/engine/robot.py +139 -0
  61. opentrons/protocol_api/core/engine/well.py +4 -1
  62. opentrons/protocol_api/core/instrument.py +73 -4
  63. opentrons/protocol_api/core/labware.py +13 -4
  64. opentrons/protocol_api/core/legacy/legacy_instrument_core.py +87 -3
  65. opentrons/protocol_api/core/legacy/legacy_labware_core.py +13 -4
  66. opentrons/protocol_api/core/legacy/legacy_protocol_core.py +32 -1
  67. opentrons/protocol_api/core/legacy/legacy_robot_core.py +0 -0
  68. opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +61 -3
  69. opentrons/protocol_api/core/protocol.py +34 -1
  70. opentrons/protocol_api/core/robot.py +51 -0
  71. opentrons/protocol_api/instrument_context.py +299 -44
  72. opentrons/protocol_api/labware.py +248 -9
  73. opentrons/protocol_api/module_contexts.py +21 -17
  74. opentrons/protocol_api/protocol_context.py +125 -4
  75. opentrons/protocol_api/robot_context.py +204 -32
  76. opentrons/protocol_api/validation.py +262 -3
  77. opentrons/protocol_engine/__init__.py +4 -0
  78. opentrons/protocol_engine/actions/actions.py +2 -3
  79. opentrons/protocol_engine/clients/sync_client.py +18 -0
  80. opentrons/protocol_engine/commands/__init__.py +121 -0
  81. opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +1 -3
  82. opentrons/protocol_engine/commands/absorbance_reader/initialize.py +20 -6
  83. opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +1 -2
  84. opentrons/protocol_engine/commands/absorbance_reader/read.py +40 -10
  85. opentrons/protocol_engine/commands/air_gap_in_place.py +160 -0
  86. opentrons/protocol_engine/commands/aspirate.py +103 -53
  87. opentrons/protocol_engine/commands/aspirate_in_place.py +55 -51
  88. opentrons/protocol_engine/commands/blow_out.py +44 -39
  89. opentrons/protocol_engine/commands/blow_out_in_place.py +21 -32
  90. opentrons/protocol_engine/commands/calibration/calibrate_gripper.py +13 -6
  91. opentrons/protocol_engine/commands/calibration/calibrate_module.py +1 -1
  92. opentrons/protocol_engine/commands/calibration/calibrate_pipette.py +3 -3
  93. opentrons/protocol_engine/commands/calibration/move_to_maintenance_position.py +1 -1
  94. opentrons/protocol_engine/commands/command.py +73 -66
  95. opentrons/protocol_engine/commands/command_unions.py +140 -1
  96. opentrons/protocol_engine/commands/comment.py +1 -1
  97. opentrons/protocol_engine/commands/configure_for_volume.py +10 -3
  98. opentrons/protocol_engine/commands/configure_nozzle_layout.py +6 -4
  99. opentrons/protocol_engine/commands/custom.py +6 -12
  100. opentrons/protocol_engine/commands/dispense.py +82 -48
  101. opentrons/protocol_engine/commands/dispense_in_place.py +71 -51
  102. opentrons/protocol_engine/commands/drop_tip.py +52 -31
  103. opentrons/protocol_engine/commands/drop_tip_in_place.py +79 -8
  104. opentrons/protocol_engine/commands/evotip_dispense.py +156 -0
  105. opentrons/protocol_engine/commands/evotip_seal_pipette.py +331 -0
  106. opentrons/protocol_engine/commands/evotip_unseal_pipette.py +160 -0
  107. opentrons/protocol_engine/commands/generate_command_schema.py +4 -11
  108. opentrons/protocol_engine/commands/get_next_tip.py +134 -0
  109. opentrons/protocol_engine/commands/get_tip_presence.py +1 -1
  110. opentrons/protocol_engine/commands/heater_shaker/close_labware_latch.py +1 -1
  111. opentrons/protocol_engine/commands/heater_shaker/deactivate_heater.py +1 -1
  112. opentrons/protocol_engine/commands/heater_shaker/deactivate_shaker.py +1 -1
  113. opentrons/protocol_engine/commands/heater_shaker/open_labware_latch.py +1 -1
  114. opentrons/protocol_engine/commands/heater_shaker/set_and_wait_for_shake_speed.py +1 -1
  115. opentrons/protocol_engine/commands/heater_shaker/set_target_temperature.py +1 -1
  116. opentrons/protocol_engine/commands/heater_shaker/wait_for_temperature.py +10 -4
  117. opentrons/protocol_engine/commands/home.py +13 -4
  118. opentrons/protocol_engine/commands/liquid_probe.py +125 -31
  119. opentrons/protocol_engine/commands/load_labware.py +33 -6
  120. opentrons/protocol_engine/commands/load_lid.py +146 -0
  121. opentrons/protocol_engine/commands/load_lid_stack.py +189 -0
  122. opentrons/protocol_engine/commands/load_liquid.py +12 -4
  123. opentrons/protocol_engine/commands/load_liquid_class.py +144 -0
  124. opentrons/protocol_engine/commands/load_module.py +31 -10
  125. opentrons/protocol_engine/commands/load_pipette.py +19 -8
  126. opentrons/protocol_engine/commands/magnetic_module/disengage.py +1 -1
  127. opentrons/protocol_engine/commands/magnetic_module/engage.py +1 -1
  128. opentrons/protocol_engine/commands/move_labware.py +28 -6
  129. opentrons/protocol_engine/commands/move_relative.py +35 -25
  130. opentrons/protocol_engine/commands/move_to_addressable_area.py +40 -27
  131. opentrons/protocol_engine/commands/move_to_addressable_area_for_drop_tip.py +53 -32
  132. opentrons/protocol_engine/commands/move_to_coordinates.py +36 -22
  133. opentrons/protocol_engine/commands/move_to_well.py +40 -24
  134. opentrons/protocol_engine/commands/movement_common.py +338 -0
  135. opentrons/protocol_engine/commands/pick_up_tip.py +49 -27
  136. opentrons/protocol_engine/commands/pipetting_common.py +169 -87
  137. opentrons/protocol_engine/commands/prepare_to_aspirate.py +24 -33
  138. opentrons/protocol_engine/commands/reload_labware.py +1 -1
  139. opentrons/protocol_engine/commands/retract_axis.py +1 -1
  140. opentrons/protocol_engine/commands/robot/__init__.py +69 -0
  141. opentrons/protocol_engine/commands/robot/close_gripper_jaw.py +86 -0
  142. opentrons/protocol_engine/commands/robot/common.py +18 -0
  143. opentrons/protocol_engine/commands/robot/move_axes_relative.py +101 -0
  144. opentrons/protocol_engine/commands/robot/move_axes_to.py +100 -0
  145. opentrons/protocol_engine/commands/robot/move_to.py +94 -0
  146. opentrons/protocol_engine/commands/robot/open_gripper_jaw.py +77 -0
  147. opentrons/protocol_engine/commands/save_position.py +14 -5
  148. opentrons/protocol_engine/commands/set_rail_lights.py +1 -1
  149. opentrons/protocol_engine/commands/set_status_bar.py +1 -1
  150. opentrons/protocol_engine/commands/temperature_module/deactivate.py +1 -1
  151. opentrons/protocol_engine/commands/temperature_module/set_target_temperature.py +1 -1
  152. opentrons/protocol_engine/commands/temperature_module/wait_for_temperature.py +10 -4
  153. opentrons/protocol_engine/commands/thermocycler/close_lid.py +1 -1
  154. opentrons/protocol_engine/commands/thermocycler/deactivate_block.py +1 -1
  155. opentrons/protocol_engine/commands/thermocycler/deactivate_lid.py +1 -1
  156. opentrons/protocol_engine/commands/thermocycler/open_lid.py +1 -1
  157. opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py +9 -3
  158. opentrons/protocol_engine/commands/thermocycler/run_profile.py +9 -3
  159. opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py +11 -4
  160. opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py +1 -1
  161. opentrons/protocol_engine/commands/thermocycler/wait_for_block_temperature.py +1 -1
  162. opentrons/protocol_engine/commands/thermocycler/wait_for_lid_temperature.py +1 -1
  163. opentrons/protocol_engine/commands/touch_tip.py +65 -16
  164. opentrons/protocol_engine/commands/unsafe/unsafe_blow_out_in_place.py +5 -2
  165. opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +13 -4
  166. opentrons/protocol_engine/commands/unsafe/unsafe_engage_axes.py +2 -5
  167. opentrons/protocol_engine/commands/unsafe/unsafe_place_labware.py +1 -1
  168. opentrons/protocol_engine/commands/unsafe/unsafe_ungrip_labware.py +4 -2
  169. opentrons/protocol_engine/commands/unsafe/update_position_estimators.py +2 -5
  170. opentrons/protocol_engine/commands/verify_tip_presence.py +11 -4
  171. opentrons/protocol_engine/commands/wait_for_duration.py +10 -3
  172. opentrons/protocol_engine/commands/wait_for_resume.py +10 -3
  173. opentrons/protocol_engine/errors/__init__.py +12 -0
  174. opentrons/protocol_engine/errors/error_occurrence.py +19 -20
  175. opentrons/protocol_engine/errors/exceptions.py +76 -0
  176. opentrons/protocol_engine/execution/command_executor.py +1 -1
  177. opentrons/protocol_engine/execution/equipment.py +73 -5
  178. opentrons/protocol_engine/execution/gantry_mover.py +369 -8
  179. opentrons/protocol_engine/execution/hardware_stopper.py +7 -7
  180. opentrons/protocol_engine/execution/movement.py +27 -0
  181. opentrons/protocol_engine/execution/pipetting.py +5 -1
  182. opentrons/protocol_engine/execution/tip_handler.py +34 -15
  183. opentrons/protocol_engine/notes/notes.py +1 -1
  184. opentrons/protocol_engine/protocol_engine.py +7 -6
  185. opentrons/protocol_engine/resources/labware_data_provider.py +1 -1
  186. opentrons/protocol_engine/resources/labware_validation.py +18 -0
  187. opentrons/protocol_engine/resources/module_data_provider.py +1 -1
  188. opentrons/protocol_engine/resources/pipette_data_provider.py +26 -0
  189. opentrons/protocol_engine/slot_standardization.py +9 -9
  190. opentrons/protocol_engine/state/_move_types.py +9 -5
  191. opentrons/protocol_engine/state/_well_math.py +193 -0
  192. opentrons/protocol_engine/state/addressable_areas.py +25 -61
  193. opentrons/protocol_engine/state/command_history.py +12 -0
  194. opentrons/protocol_engine/state/commands.py +22 -14
  195. opentrons/protocol_engine/state/files.py +10 -12
  196. opentrons/protocol_engine/state/fluid_stack.py +138 -0
  197. opentrons/protocol_engine/state/frustum_helpers.py +63 -69
  198. opentrons/protocol_engine/state/geometry.py +47 -1
  199. opentrons/protocol_engine/state/labware.py +92 -26
  200. opentrons/protocol_engine/state/liquid_classes.py +82 -0
  201. opentrons/protocol_engine/state/liquids.py +16 -4
  202. opentrons/protocol_engine/state/modules.py +56 -71
  203. opentrons/protocol_engine/state/motion.py +6 -1
  204. opentrons/protocol_engine/state/pipettes.py +149 -58
  205. opentrons/protocol_engine/state/state.py +21 -2
  206. opentrons/protocol_engine/state/state_summary.py +4 -2
  207. opentrons/protocol_engine/state/tips.py +11 -44
  208. opentrons/protocol_engine/state/update_types.py +343 -48
  209. opentrons/protocol_engine/state/wells.py +19 -11
  210. opentrons/protocol_engine/types.py +176 -28
  211. opentrons/protocol_reader/extract_labware_definitions.py +5 -2
  212. opentrons/protocol_reader/file_format_validator.py +5 -5
  213. opentrons/protocol_runner/json_file_reader.py +9 -3
  214. opentrons/protocol_runner/json_translator.py +51 -25
  215. opentrons/protocol_runner/legacy_command_mapper.py +66 -64
  216. opentrons/protocol_runner/protocol_runner.py +35 -4
  217. opentrons/protocol_runner/python_protocol_wrappers.py +1 -1
  218. opentrons/protocol_runner/run_orchestrator.py +13 -3
  219. opentrons/protocols/advanced_control/common.py +38 -0
  220. opentrons/protocols/advanced_control/mix.py +1 -1
  221. opentrons/protocols/advanced_control/transfers/__init__.py +0 -0
  222. opentrons/protocols/advanced_control/transfers/common.py +56 -0
  223. opentrons/protocols/advanced_control/{transfers.py → transfers/transfer.py} +10 -85
  224. opentrons/protocols/api_support/definitions.py +1 -1
  225. opentrons/protocols/api_support/instrument.py +1 -1
  226. opentrons/protocols/api_support/util.py +10 -0
  227. opentrons/protocols/labware.py +70 -8
  228. opentrons/protocols/models/json_protocol.py +5 -9
  229. opentrons/simulate.py +3 -1
  230. opentrons/types.py +162 -2
  231. opentrons/util/entrypoint_util.py +2 -5
  232. opentrons/util/logging_config.py +1 -1
  233. {opentrons-8.2.0a3.dist-info → opentrons-8.3.0.dist-info}/METADATA +16 -15
  234. {opentrons-8.2.0a3.dist-info → opentrons-8.3.0.dist-info}/RECORD +238 -208
  235. {opentrons-8.2.0a3.dist-info → opentrons-8.3.0.dist-info}/WHEEL +1 -1
  236. {opentrons-8.2.0a3.dist-info → opentrons-8.3.0.dist-info}/LICENSE +0 -0
  237. {opentrons-8.2.0a3.dist-info → opentrons-8.3.0.dist-info}/entry_points.txt +0 -0
  238. {opentrons-8.2.0a3.dist-info → opentrons-8.3.0.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,5 @@
1
1
  """Pick up tip command request, result, and implementation models."""
2
+
2
3
  from __future__ import annotations
3
4
  from opentrons_shared_data.errors import ErrorCodes
4
5
  from pydantic import Field
@@ -9,10 +10,14 @@ from typing_extensions import Literal
9
10
  from ..errors import ErrorOccurrence, PickUpTipTipNotAttachedError
10
11
  from ..resources import ModelUtils
11
12
  from ..state import update_types
12
- from ..types import PickUpTipWellLocation, DeckPoint
13
+ from ..types import PickUpTipWellLocation
13
14
  from .pipetting_common import (
14
15
  PipetteIdMixin,
16
+ )
17
+ from .movement_common import (
15
18
  DestinationPositionResult,
19
+ StallOrCollisionError,
20
+ move_to_well,
16
21
  )
17
22
  from .command import (
18
23
  AbstractCommandImpl,
@@ -87,7 +92,8 @@ class TipPhysicallyMissingError(ErrorOccurrence):
87
92
 
88
93
  _ExecuteReturn = Union[
89
94
  SuccessData[PickUpTipResult],
90
- DefinedErrorData[TipPhysicallyMissingError],
95
+ DefinedErrorData[TipPhysicallyMissingError]
96
+ | DefinedErrorData[StallOrCollisionError],
91
97
  ]
92
98
 
93
99
 
@@ -115,24 +121,19 @@ class PickUpTipImplementation(AbstractCommandImpl[PickUpTipParams, _ExecuteRetur
115
121
  labware_id = params.labwareId
116
122
  well_name = params.wellName
117
123
 
118
- state_update = update_types.StateUpdate()
119
-
120
124
  well_location = self._state_view.geometry.convert_pick_up_tip_well_location(
121
125
  well_location=params.wellLocation
122
126
  )
123
- position = await self._movement.move_to_well(
127
+ move_result = await move_to_well(
128
+ movement=self._movement,
129
+ model_utils=self._model_utils,
124
130
  pipette_id=pipette_id,
125
131
  labware_id=labware_id,
126
132
  well_name=well_name,
127
133
  well_location=well_location,
128
134
  )
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
- )
135
+ if isinstance(move_result, DefinedErrorData):
136
+ return move_result
136
137
 
137
138
  try:
138
139
  tip_geometry = await self._tip_handler.pick_up_tip(
@@ -141,13 +142,27 @@ class PickUpTipImplementation(AbstractCommandImpl[PickUpTipParams, _ExecuteRetur
141
142
  well_name=well_name,
142
143
  )
143
144
  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,
145
+ state_update_if_false_positive = (
146
+ update_types.StateUpdate.reduce(
147
+ update_types.StateUpdate(), move_result.state_update
148
+ )
149
+ .update_pipette_tip_state(
150
+ pipette_id=pipette_id,
151
+ tip_geometry=e.tip_geometry,
152
+ )
153
+ .set_fluid_empty(pipette_id=pipette_id)
154
+ .mark_tips_as_used(
155
+ pipette_id=pipette_id, labware_id=labware_id, well_name=well_name
156
+ )
148
157
  )
149
- state_update.mark_tips_as_used(
150
- pipette_id=pipette_id, labware_id=labware_id, well_name=well_name
158
+ state_update = (
159
+ update_types.StateUpdate.reduce(
160
+ update_types.StateUpdate(), move_result.state_update
161
+ )
162
+ .mark_tips_as_used(
163
+ pipette_id=pipette_id, labware_id=labware_id, well_name=well_name
164
+ )
165
+ .set_fluid_unknown(pipette_id=pipette_id)
151
166
  )
152
167
  return DefinedErrorData(
153
168
  public=TipPhysicallyMissingError(
@@ -165,32 +180,39 @@ class PickUpTipImplementation(AbstractCommandImpl[PickUpTipParams, _ExecuteRetur
165
180
  state_update_if_false_positive=state_update_if_false_positive,
166
181
  )
167
182
  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
183
+ state_update = (
184
+ move_result.state_update.update_pipette_tip_state(
185
+ pipette_id=pipette_id,
186
+ tip_geometry=tip_geometry,
187
+ )
188
+ .mark_tips_as_used(
189
+ pipette_id=pipette_id, labware_id=labware_id, well_name=well_name
190
+ )
191
+ .set_fluid_empty(pipette_id=pipette_id)
174
192
  )
175
193
  return SuccessData(
176
194
  public=PickUpTipResult(
177
195
  tipVolume=tip_geometry.volume,
178
196
  tipLength=tip_geometry.length,
179
197
  tipDiameter=tip_geometry.diameter,
180
- position=deck_point,
198
+ position=move_result.public.position,
181
199
  ),
182
200
  state_update=state_update,
183
201
  )
184
202
 
185
203
 
186
204
  class PickUpTip(
187
- BaseCommand[PickUpTipParams, PickUpTipResult, TipPhysicallyMissingError]
205
+ BaseCommand[
206
+ PickUpTipParams,
207
+ PickUpTipResult,
208
+ TipPhysicallyMissingError | StallOrCollisionError,
209
+ ]
188
210
  ):
189
211
  """Pick up tip command model."""
190
212
 
191
213
  commandType: PickUpTipCommandType = "pickUpTip"
192
214
  params: PickUpTipParams
193
- result: Optional[PickUpTipResult]
215
+ result: Optional[PickUpTipResult] = None
194
216
 
195
217
  _ImplementationCls: Type[PickUpTipImplementation] = PickUpTipImplementation
196
218
 
@@ -1,11 +1,23 @@
1
1
  """Common pipetting command base models."""
2
- from opentrons_shared_data.errors import ErrorCodes
2
+
3
+ from __future__ import annotations
4
+ from typing import Literal, Tuple, TYPE_CHECKING
5
+
6
+ from typing_extensions import TypedDict
3
7
  from pydantic import BaseModel, Field
4
- from typing import Literal, Optional, Tuple, TypedDict
5
8
 
9
+ from opentrons_shared_data.errors import ErrorCodes
6
10
  from opentrons.protocol_engine.errors.error_occurrence import ErrorOccurrence
11
+ from opentrons.protocol_engine.types import AspiratedFluid, FluidKind
12
+ from opentrons_shared_data.errors.exceptions import PipetteOverpressureError
13
+ from .command import DefinedErrorData, SuccessData
14
+ from opentrons.protocol_engine.state.update_types import StateUpdate
7
15
 
8
- from ..types import WellLocation, LiquidHandlingWellLocation, DeckPoint
16
+
17
+ if TYPE_CHECKING:
18
+ from ..execution.pipetting import PipettingHandler
19
+ from ..resources import ModelUtils
20
+ from ..notes import CommandNoteAdder
9
21
 
10
22
 
11
23
  class PipetteIdMixin(BaseModel):
@@ -51,72 +63,6 @@ class FlowRateMixin(BaseModel):
51
63
  )
52
64
 
53
65
 
54
- class WellLocationMixin(BaseModel):
55
- """Mixin for command requests that take a location that's somewhere in a well."""
56
-
57
- labwareId: str = Field(
58
- ...,
59
- description="Identifier of labware to use.",
60
- )
61
- wellName: str = Field(
62
- ...,
63
- description="Name of well to use in labware.",
64
- )
65
- wellLocation: WellLocation = Field(
66
- default_factory=WellLocation,
67
- description="Relative well location at which to perform the operation",
68
- )
69
-
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
-
88
- class MovementMixin(BaseModel):
89
- """Mixin for command requests that move a pipette."""
90
-
91
- minimumZHeight: Optional[float] = Field(
92
- None,
93
- description=(
94
- "Optional minimal Z margin in mm."
95
- " If this is larger than the API's default safe Z margin,"
96
- " it will make the arc higher. If it's smaller, it will have no effect."
97
- ),
98
- )
99
-
100
- forceDirect: bool = Field(
101
- False,
102
- description=(
103
- "If true, moving from one labware/well to another"
104
- " will not arc to the default safe z,"
105
- " but instead will move directly to the specified location."
106
- " This will also force the `minimumZHeight` param to be ignored."
107
- " A 'direct' movement is in X/Y/Z simultaneously."
108
- ),
109
- )
110
-
111
- speed: Optional[float] = Field(
112
- None,
113
- description=(
114
- "Override the travel speed in mm/s."
115
- " This controls the straight linear speed of motion."
116
- ),
117
- )
118
-
119
-
120
66
  class BaseLiquidHandlingResult(BaseModel):
121
67
  """Base properties of a liquid handling result."""
122
68
 
@@ -127,24 +73,8 @@ class BaseLiquidHandlingResult(BaseModel):
127
73
  )
128
74
 
129
75
 
130
- class DestinationPositionResult(BaseModel):
131
- """Mixin for command results that move a pipette."""
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.
141
- position: DeckPoint = Field(
142
- DeckPoint(x=0, y=0, z=0),
143
- description=(
144
- "The (x,y,z) coordinates of the pipette's critical point in deck space"
145
- " after the move was completed."
146
- ),
147
- )
76
+ class EmptyResult(BaseModel):
77
+ """A result with no data."""
148
78
 
149
79
 
150
80
  class ErrorLocationInfo(TypedDict):
@@ -208,3 +138,155 @@ class TipPhysicallyAttachedError(ErrorOccurrence):
208
138
  detail: str = ErrorCodes.TIP_DROP_FAILED.value.detail
209
139
 
210
140
  errorInfo: ErrorLocationInfo
141
+
142
+
143
+ async def prepare_for_aspirate(
144
+ pipette_id: str,
145
+ pipetting: PipettingHandler,
146
+ model_utils: ModelUtils,
147
+ location_if_error: ErrorLocationInfo,
148
+ ) -> SuccessData[EmptyResult] | DefinedErrorData[OverpressureError]:
149
+ """Execute pipetting.prepare_for_aspirate, handle errors, and marshal success."""
150
+ try:
151
+ await pipetting.prepare_for_aspirate(pipette_id)
152
+ except PipetteOverpressureError as e:
153
+ return DefinedErrorData(
154
+ public=OverpressureError(
155
+ id=model_utils.generate_id(),
156
+ createdAt=model_utils.get_timestamp(),
157
+ wrappedErrors=[
158
+ ErrorOccurrence.from_failed(
159
+ id=model_utils.generate_id(),
160
+ createdAt=model_utils.get_timestamp(),
161
+ error=e,
162
+ )
163
+ ],
164
+ errorInfo=location_if_error,
165
+ ),
166
+ state_update=StateUpdate().set_fluid_unknown(pipette_id=pipette_id),
167
+ )
168
+ else:
169
+ return SuccessData(
170
+ public=EmptyResult(),
171
+ state_update=StateUpdate().set_fluid_empty(pipette_id=pipette_id),
172
+ )
173
+
174
+
175
+ async def aspirate_in_place(
176
+ pipette_id: str,
177
+ volume: float,
178
+ flow_rate: float,
179
+ location_if_error: ErrorLocationInfo,
180
+ command_note_adder: CommandNoteAdder,
181
+ pipetting: PipettingHandler,
182
+ model_utils: ModelUtils,
183
+ ) -> SuccessData[BaseLiquidHandlingResult] | DefinedErrorData[OverpressureError]:
184
+ """Execute an aspirate in place microoperation."""
185
+ try:
186
+ volume_aspirated = await pipetting.aspirate_in_place(
187
+ pipette_id=pipette_id,
188
+ volume=volume,
189
+ flow_rate=flow_rate,
190
+ command_note_adder=command_note_adder,
191
+ )
192
+ except PipetteOverpressureError as e:
193
+ return DefinedErrorData(
194
+ public=OverpressureError(
195
+ id=model_utils.generate_id(),
196
+ createdAt=model_utils.get_timestamp(),
197
+ wrappedErrors=[
198
+ ErrorOccurrence.from_failed(
199
+ id=model_utils.generate_id(),
200
+ createdAt=model_utils.get_timestamp(),
201
+ error=e,
202
+ )
203
+ ],
204
+ errorInfo=location_if_error,
205
+ ),
206
+ state_update=StateUpdate().set_fluid_unknown(pipette_id=pipette_id),
207
+ )
208
+ else:
209
+ return SuccessData(
210
+ public=BaseLiquidHandlingResult(
211
+ volume=volume_aspirated,
212
+ ),
213
+ state_update=StateUpdate().set_fluid_aspirated(
214
+ pipette_id=pipette_id,
215
+ fluid=AspiratedFluid(kind=FluidKind.LIQUID, volume=volume_aspirated),
216
+ ),
217
+ )
218
+
219
+
220
+ async def dispense_in_place(
221
+ pipette_id: str,
222
+ volume: float,
223
+ flow_rate: float,
224
+ push_out: float | None,
225
+ location_if_error: ErrorLocationInfo,
226
+ pipetting: PipettingHandler,
227
+ model_utils: ModelUtils,
228
+ ) -> SuccessData[BaseLiquidHandlingResult] | DefinedErrorData[OverpressureError]:
229
+ """Dispense-in-place as a microoperation."""
230
+ try:
231
+ volume = await pipetting.dispense_in_place(
232
+ pipette_id=pipette_id,
233
+ volume=volume,
234
+ flow_rate=flow_rate,
235
+ push_out=push_out,
236
+ )
237
+ except PipetteOverpressureError as e:
238
+ return DefinedErrorData(
239
+ public=OverpressureError(
240
+ id=model_utils.generate_id(),
241
+ createdAt=model_utils.get_timestamp(),
242
+ wrappedErrors=[
243
+ ErrorOccurrence.from_failed(
244
+ id=model_utils.generate_id(),
245
+ createdAt=model_utils.get_timestamp(),
246
+ error=e,
247
+ )
248
+ ],
249
+ errorInfo=location_if_error,
250
+ ),
251
+ state_update=StateUpdate().set_fluid_unknown(pipette_id=pipette_id),
252
+ )
253
+ else:
254
+ return SuccessData(
255
+ public=BaseLiquidHandlingResult(volume=volume),
256
+ state_update=StateUpdate().set_fluid_ejected(
257
+ pipette_id=pipette_id, volume=volume
258
+ ),
259
+ )
260
+
261
+
262
+ async def blow_out_in_place(
263
+ pipette_id: str,
264
+ flow_rate: float,
265
+ location_if_error: ErrorLocationInfo,
266
+ pipetting: PipettingHandler,
267
+ model_utils: ModelUtils,
268
+ ) -> SuccessData[EmptyResult] | DefinedErrorData[OverpressureError]:
269
+ """Execute a blow-out-in-place micro-operation."""
270
+ try:
271
+ await pipetting.blow_out_in_place(pipette_id=pipette_id, flow_rate=flow_rate)
272
+ except PipetteOverpressureError as e:
273
+ return DefinedErrorData(
274
+ public=OverpressureError(
275
+ id=model_utils.generate_id(),
276
+ createdAt=model_utils.get_timestamp(),
277
+ wrappedErrors=[
278
+ ErrorOccurrence.from_failed(
279
+ id=model_utils.generate_id(),
280
+ createdAt=model_utils.get_timestamp(),
281
+ error=e,
282
+ )
283
+ ],
284
+ errorInfo=location_if_error,
285
+ ),
286
+ state_update=StateUpdate().set_fluid_unknown(pipette_id=pipette_id),
287
+ )
288
+ else:
289
+ return SuccessData(
290
+ public=EmptyResult(),
291
+ state_update=StateUpdate().set_fluid_empty(pipette_id=pipette_id),
292
+ )
@@ -1,15 +1,11 @@
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
5
4
  from pydantic import BaseModel
6
5
  from typing import TYPE_CHECKING, Optional, Type, Union
7
6
  from typing_extensions import Literal
8
7
 
9
- from .pipetting_common import (
10
- OverpressureError,
11
- PipetteIdMixin,
12
- )
8
+ from .pipetting_common import OverpressureError, PipetteIdMixin, prepare_for_aspirate
13
9
  from .command import (
14
10
  AbstractCommandImpl,
15
11
  BaseCommand,
@@ -61,39 +57,34 @@ class PrepareToAspirateImplementation(
61
57
  self._model_utils = model_utils
62
58
  self._gantry_mover = gantry_mover
63
59
 
60
+ def _transform_result(
61
+ self, result: SuccessData[BaseModel]
62
+ ) -> SuccessData[PrepareToAspirateResult]:
63
+ return SuccessData(
64
+ public=PrepareToAspirateResult(), state_update=result.state_update
65
+ )
66
+
64
67
  async def execute(self, params: PrepareToAspirateParams) -> _ExecuteReturn:
65
68
  """Prepare the pipette to aspirate."""
66
69
  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
- )
70
+ prepare_result = await prepare_for_aspirate(
71
+ pipette_id=params.pipetteId,
72
+ pipetting=self._pipetting_handler,
73
+ model_utils=self._model_utils,
74
+ location_if_error={
75
+ "retryLocation": (
76
+ current_position.x,
77
+ current_position.y,
78
+ current_position.z,
79
+ )
80
+ },
81
+ )
82
+ if isinstance(prepare_result, DefinedErrorData):
83
+ return prepare_result
94
84
  else:
95
85
  return SuccessData(
96
86
  public=PrepareToAspirateResult(),
87
+ state_update=prepare_result.state_update,
97
88
  )
98
89
 
99
90
 
@@ -104,7 +95,7 @@ class PrepareToAspirate(
104
95
 
105
96
  commandType: PrepareToAspirateCommandType = "prepareToAspirate"
106
97
  params: PrepareToAspirateParams
107
- result: Optional[PrepareToAspirateResult]
98
+ result: Optional[PrepareToAspirateResult] = None
108
99
 
109
100
  _ImplementationCls: Type[
110
101
  PrepareToAspirateImplementation
@@ -89,7 +89,7 @@ class ReloadLabware(
89
89
 
90
90
  commandType: ReloadLabwareCommandType = "reloadLabware"
91
91
  params: ReloadLabwareParams
92
- result: Optional[ReloadLabwareResult]
92
+ result: Optional[ReloadLabwareResult] = None
93
93
 
94
94
  _ImplementationCls: Type[ReloadLabwareImplementation] = ReloadLabwareImplementation
95
95
 
@@ -61,7 +61,7 @@ class RetractAxis(BaseCommand[RetractAxisParams, RetractAxisResult, ErrorOccurre
61
61
 
62
62
  commandType: RetractAxisCommandType = "retractAxis"
63
63
  params: RetractAxisParams
64
- result: Optional[RetractAxisResult]
64
+ result: Optional[RetractAxisResult] = None
65
65
 
66
66
  _ImplementationCls: Type[RetractAxisImplementation] = RetractAxisImplementation
67
67
 
@@ -1 +1,70 @@
1
1
  """Robot movement commands."""
2
+
3
+ from .move_to import (
4
+ MoveTo,
5
+ MoveToCreate,
6
+ MoveToParams,
7
+ MoveToResult,
8
+ MoveToCommandType,
9
+ )
10
+ from .move_axes_to import (
11
+ MoveAxesTo,
12
+ MoveAxesToCreate,
13
+ MoveAxesToParams,
14
+ MoveAxesToResult,
15
+ MoveAxesToCommandType,
16
+ )
17
+ from .move_axes_relative import (
18
+ MoveAxesRelative,
19
+ MoveAxesRelativeCreate,
20
+ MoveAxesRelativeParams,
21
+ MoveAxesRelativeResult,
22
+ MoveAxesRelativeCommandType,
23
+ )
24
+ from .open_gripper_jaw import (
25
+ openGripperJaw,
26
+ openGripperJawCreate,
27
+ openGripperJawParams,
28
+ openGripperJawResult,
29
+ openGripperJawCommandType,
30
+ )
31
+ from .close_gripper_jaw import (
32
+ closeGripperJaw,
33
+ closeGripperJawCreate,
34
+ closeGripperJawParams,
35
+ closeGripperJawResult,
36
+ closeGripperJawCommandType,
37
+ )
38
+
39
+ __all__ = [
40
+ # robot/moveTo
41
+ "MoveTo",
42
+ "MoveToCreate",
43
+ "MoveToParams",
44
+ "MoveToResult",
45
+ "MoveToCommandType",
46
+ # robot/moveAxesTo
47
+ "MoveAxesTo",
48
+ "MoveAxesToCreate",
49
+ "MoveAxesToParams",
50
+ "MoveAxesToResult",
51
+ "MoveAxesToCommandType",
52
+ # robot/moveAxesRelative
53
+ "MoveAxesRelative",
54
+ "MoveAxesRelativeCreate",
55
+ "MoveAxesRelativeParams",
56
+ "MoveAxesRelativeResult",
57
+ "MoveAxesRelativeCommandType",
58
+ # robot/openGripperJaw
59
+ "openGripperJaw",
60
+ "openGripperJawCreate",
61
+ "openGripperJawParams",
62
+ "openGripperJawResult",
63
+ "openGripperJawCommandType",
64
+ # robot/closeGripperJaw
65
+ "closeGripperJaw",
66
+ "closeGripperJawCreate",
67
+ "closeGripperJawParams",
68
+ "closeGripperJawResult",
69
+ "closeGripperJawCommandType",
70
+ ]
@@ -0,0 +1,86 @@
1
+ """Command models for opening a gripper jaw."""
2
+ from __future__ import annotations
3
+ from typing import Literal, Type, Optional, Any
4
+
5
+ from pydantic import BaseModel, Field
6
+ from pydantic.json_schema import SkipJsonSchema
7
+
8
+ from opentrons.hardware_control import HardwareControlAPI
9
+ from opentrons.protocol_engine.resources import ensure_ot3_hardware
10
+
11
+ from ..command import (
12
+ AbstractCommandImpl,
13
+ BaseCommand,
14
+ BaseCommandCreate,
15
+ SuccessData,
16
+ )
17
+ from opentrons.protocol_engine.errors.error_occurrence import ErrorOccurrence
18
+
19
+
20
+ closeGripperJawCommandType = Literal["robot/closeGripperJaw"]
21
+
22
+
23
+ def _remove_default(s: dict[str, Any]) -> None:
24
+ s.pop("default", None)
25
+
26
+
27
+ class closeGripperJawParams(BaseModel):
28
+ """Payload required to close a gripper."""
29
+
30
+ force: float | SkipJsonSchema[None] = Field(
31
+ default=None,
32
+ description="The force the gripper should use to hold the jaws, falls to default if none is provided.",
33
+ json_schema_extra=_remove_default,
34
+ )
35
+
36
+
37
+ class closeGripperJawResult(BaseModel):
38
+ """Result data from the execution of a closeGripperJaw command."""
39
+
40
+ pass
41
+
42
+
43
+ class closeGripperJawImplementation(
44
+ AbstractCommandImpl[closeGripperJawParams, SuccessData[closeGripperJawResult]]
45
+ ):
46
+ """closeGripperJaw command implementation."""
47
+
48
+ def __init__(
49
+ self,
50
+ hardware_api: HardwareControlAPI,
51
+ **kwargs: object,
52
+ ) -> None:
53
+ self._hardware_api = hardware_api
54
+
55
+ async def execute(
56
+ self, params: closeGripperJawParams
57
+ ) -> SuccessData[closeGripperJawResult]:
58
+ """Release the gripper."""
59
+ ot3_hardware_api = ensure_ot3_hardware(self._hardware_api)
60
+ await ot3_hardware_api.grip(force_newtons=params.force)
61
+ return SuccessData(
62
+ public=closeGripperJawResult(),
63
+ )
64
+
65
+
66
+ class closeGripperJaw(
67
+ BaseCommand[closeGripperJawParams, closeGripperJawResult, ErrorOccurrence]
68
+ ):
69
+ """closeGripperJaw command model."""
70
+
71
+ commandType: closeGripperJawCommandType = "robot/closeGripperJaw"
72
+ params: closeGripperJawParams
73
+ result: Optional[closeGripperJawResult] = None
74
+
75
+ _ImplementationCls: Type[
76
+ closeGripperJawImplementation
77
+ ] = closeGripperJawImplementation
78
+
79
+
80
+ class closeGripperJawCreate(BaseCommandCreate[closeGripperJawParams]):
81
+ """closeGripperJaw command request model."""
82
+
83
+ commandType: closeGripperJawCommandType = "robot/closeGripperJaw"
84
+ params: closeGripperJawParams
85
+
86
+ _CommandCls: Type[closeGripperJaw] = closeGripperJaw