opentrons 8.2.0a4__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.

Potentially problematic release.


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

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 +36 -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 +1 -1
  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 +52 -70
  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.0a4.dist-info → opentrons-8.3.0.dist-info}/METADATA +16 -15
  234. {opentrons-8.2.0a4.dist-info → opentrons-8.3.0.dist-info}/RECORD +238 -208
  235. {opentrons-8.2.0a4.dist-info → opentrons-8.3.0.dist-info}/WHEEL +1 -1
  236. {opentrons-8.2.0a4.dist-info → opentrons-8.3.0.dist-info}/LICENSE +0 -0
  237. {opentrons-8.2.0a4.dist-info → opentrons-8.3.0.dist-info}/entry_points.txt +0 -0
  238. {opentrons-8.2.0a4.dist-info → opentrons-8.3.0.dist-info}/top_level.txt +0 -0
@@ -1,20 +1,27 @@
1
1
  """Drop tip command request, result, and implementation models."""
2
+
2
3
  from __future__ import annotations
4
+ from typing import TYPE_CHECKING, Optional, Type, Any
3
5
 
4
6
  from pydantic import Field
5
- from typing import TYPE_CHECKING, Optional, Type
7
+ from pydantic.json_schema import SkipJsonSchema
8
+
6
9
  from typing_extensions import Literal
7
10
 
8
11
  from opentrons.protocol_engine.errors.exceptions import TipAttachedError
9
12
  from opentrons.protocol_engine.resources.model_utils import ModelUtils
10
13
 
11
- from ..state import update_types
12
- from ..types import DropTipWellLocation, DeckPoint
14
+ from ..state.update_types import StateUpdate
15
+ from ..types import DropTipWellLocation
13
16
  from .pipetting_common import (
14
17
  PipetteIdMixin,
15
- DestinationPositionResult,
16
18
  TipPhysicallyAttachedError,
17
19
  )
20
+ from .movement_common import (
21
+ DestinationPositionResult,
22
+ move_to_well,
23
+ StallOrCollisionError,
24
+ )
18
25
  from .command import (
19
26
  AbstractCommandImpl,
20
27
  BaseCommand,
@@ -32,6 +39,10 @@ if TYPE_CHECKING:
32
39
  DropTipCommandType = Literal["dropTip"]
33
40
 
34
41
 
42
+ def _remove_default(s: dict[str, Any]) -> None:
43
+ s.pop("default", None)
44
+
45
+
35
46
  class DropTipParams(PipetteIdMixin):
36
47
  """Payload required to drop a tip in a specific well."""
37
48
 
@@ -41,15 +52,16 @@ class DropTipParams(PipetteIdMixin):
41
52
  default_factory=DropTipWellLocation,
42
53
  description="Relative well location at which to drop the tip.",
43
54
  )
44
- homeAfter: Optional[bool] = Field(
55
+ homeAfter: bool | SkipJsonSchema[None] = Field(
45
56
  None,
46
57
  description=(
47
58
  "Whether to home this pipette's plunger after dropping the tip."
48
59
  " You should normally leave this unspecified to let the robot choose"
49
60
  " a safe default depending on its hardware."
50
61
  ),
62
+ json_schema_extra=_remove_default,
51
63
  )
52
- alternateDropLocation: Optional[bool] = Field(
64
+ alternateDropLocation: bool | SkipJsonSchema[None] = Field(
53
65
  False,
54
66
  description=(
55
67
  "Whether to alternate location where tip is dropped within the labware."
@@ -58,6 +70,7 @@ class DropTipParams(PipetteIdMixin):
58
70
  " labware well."
59
71
  " If False, the tip will be dropped at the top center of the well."
60
72
  ),
73
+ json_schema_extra=_remove_default,
61
74
  )
62
75
 
63
76
 
@@ -68,7 +81,9 @@ class DropTipResult(DestinationPositionResult):
68
81
 
69
82
 
70
83
  _ExecuteReturn = (
71
- SuccessData[DropTipResult] | DefinedErrorData[TipPhysicallyAttachedError]
84
+ SuccessData[DropTipResult]
85
+ | DefinedErrorData[TipPhysicallyAttachedError]
86
+ | DefinedErrorData[StallOrCollisionError]
72
87
  )
73
88
 
74
89
 
@@ -95,8 +110,6 @@ class DropTipImplementation(AbstractCommandImpl[DropTipParams, _ExecuteReturn]):
95
110
  well_name = params.wellName
96
111
  home_after = params.homeAfter
97
112
 
98
- state_update = update_types.StateUpdate()
99
-
100
113
  if params.alternateDropLocation:
101
114
  well_location = self._state_view.geometry.get_next_tip_drop_location(
102
115
  labware_id=labware_id,
@@ -116,19 +129,16 @@ class DropTipImplementation(AbstractCommandImpl[DropTipParams, _ExecuteReturn]):
116
129
  partially_configured=is_partially_configured,
117
130
  )
118
131
 
119
- position = await self._movement_handler.move_to_well(
132
+ move_result = await move_to_well(
133
+ movement=self._movement_handler,
134
+ model_utils=self._model_utils,
120
135
  pipette_id=pipette_id,
121
136
  labware_id=labware_id,
122
137
  well_name=well_name,
123
138
  well_location=tip_drop_location,
124
139
  )
125
- deck_point = DeckPoint.construct(x=position.x, y=position.y, z=position.z)
126
- state_update.set_pipette_location(
127
- pipette_id=pipette_id,
128
- new_labware_id=labware_id,
129
- new_well_name=well_name,
130
- new_deck_point=deck_point,
131
- )
140
+ if isinstance(move_result, DefinedErrorData):
141
+ return move_result
132
142
 
133
143
  try:
134
144
  await self._tip_handler.drop_tip(
@@ -145,33 +155,44 @@ class DropTipImplementation(AbstractCommandImpl[DropTipParams, _ExecuteReturn]):
145
155
  error=exception,
146
156
  )
147
157
  ],
148
- errorInfo={"retryLocation": position},
149
- )
150
- state_update_if_false_positive = update_types.StateUpdate()
151
- state_update_if_false_positive.update_pipette_tip_state(
152
- pipette_id=params.pipetteId, tip_geometry=None
158
+ errorInfo={
159
+ "retryLocation": (
160
+ move_result.public.position.x,
161
+ move_result.public.position.y,
162
+ move_result.public.position.z,
163
+ )
164
+ },
153
165
  )
154
166
  return DefinedErrorData(
155
167
  public=error,
156
- state_update=state_update,
157
- state_update_if_false_positive=state_update_if_false_positive,
168
+ state_update=StateUpdate.reduce(
169
+ StateUpdate(), move_result.state_update
170
+ ).set_fluid_unknown(pipette_id=pipette_id),
171
+ state_update_if_false_positive=move_result.state_update.update_pipette_tip_state(
172
+ pipette_id=params.pipetteId, tip_geometry=None
173
+ ),
158
174
  )
159
175
  else:
160
- state_update.update_pipette_tip_state(
161
- pipette_id=params.pipetteId, tip_geometry=None
162
- )
163
176
  return SuccessData(
164
- public=DropTipResult(position=deck_point),
165
- state_update=state_update,
177
+ public=DropTipResult(position=move_result.public.position),
178
+ state_update=move_result.state_update.set_fluid_unknown(
179
+ pipette_id=pipette_id
180
+ ).update_pipette_tip_state(
181
+ pipette_id=params.pipetteId, tip_geometry=None
182
+ ),
166
183
  )
167
184
 
168
185
 
169
- class DropTip(BaseCommand[DropTipParams, DropTipResult, TipPhysicallyAttachedError]):
186
+ class DropTip(
187
+ BaseCommand[
188
+ DropTipParams, DropTipResult, TipPhysicallyAttachedError | StallOrCollisionError
189
+ ]
190
+ ):
170
191
  """Drop tip command model."""
171
192
 
172
193
  commandType: DropTipCommandType = "dropTip"
173
194
  params: DropTipParams
174
- result: Optional[DropTipResult]
195
+ result: Optional[DropTipResult] = None
175
196
 
176
197
  _ImplementationCls: Type[DropTipImplementation] = DropTipImplementation
177
198
 
@@ -1,9 +1,17 @@
1
1
  """Drop tip in place command request, result, and implementation models."""
2
2
  from __future__ import annotations
3
+
4
+ from typing import TYPE_CHECKING, Optional, Type, Any, Union
5
+
3
6
  from pydantic import Field, BaseModel
4
- from typing import TYPE_CHECKING, Optional, Type
7
+ from pydantic.json_schema import SkipJsonSchema
5
8
  from typing_extensions import Literal
6
9
 
10
+ from opentrons_shared_data.errors.exceptions import (
11
+ PipetteOverpressureError,
12
+ StallOrCollisionDetectedError,
13
+ )
14
+
7
15
  from .command import (
8
16
  AbstractCommandImpl,
9
17
  BaseCommand,
@@ -11,7 +19,12 @@ from .command import (
11
19
  DefinedErrorData,
12
20
  SuccessData,
13
21
  )
14
- from .pipetting_common import PipetteIdMixin, TipPhysicallyAttachedError
22
+ from .movement_common import StallOrCollisionError
23
+ from .pipetting_common import (
24
+ PipetteIdMixin,
25
+ TipPhysicallyAttachedError,
26
+ OverpressureError,
27
+ )
15
28
  from ..errors.exceptions import TipAttachedError
16
29
  from ..errors.error_occurrence import ErrorOccurrence
17
30
  from ..resources.model_utils import ModelUtils
@@ -24,16 +37,21 @@ if TYPE_CHECKING:
24
37
  DropTipInPlaceCommandType = Literal["dropTipInPlace"]
25
38
 
26
39
 
40
+ def _remove_default(s: dict[str, Any]) -> None:
41
+ s.pop("default", None)
42
+
43
+
27
44
  class DropTipInPlaceParams(PipetteIdMixin):
28
45
  """Payload required to drop a tip in place."""
29
46
 
30
- homeAfter: Optional[bool] = Field(
47
+ homeAfter: bool | SkipJsonSchema[None] = Field(
31
48
  None,
32
49
  description=(
33
50
  "Whether to home this pipette's plunger after dropping the tip."
34
51
  " You should normally leave this unspecified to let the robot choose"
35
52
  " a safe default depending on its hardware."
36
53
  ),
54
+ json_schema_extra=_remove_default,
37
55
  )
38
56
 
39
57
 
@@ -43,9 +61,12 @@ class DropTipInPlaceResult(BaseModel):
43
61
  pass
44
62
 
45
63
 
46
- _ExecuteReturn = (
47
- SuccessData[DropTipInPlaceResult] | DefinedErrorData[TipPhysicallyAttachedError]
48
- )
64
+ _ExecuteReturn = Union[
65
+ SuccessData[DropTipInPlaceResult]
66
+ | DefinedErrorData[TipPhysicallyAttachedError]
67
+ | DefinedErrorData[OverpressureError]
68
+ | DefinedErrorData[StallOrCollisionError]
69
+ ]
49
70
 
50
71
 
51
72
  class DropTipInPlaceImplementation(
@@ -79,6 +100,7 @@ class DropTipInPlaceImplementation(
79
100
  state_update_if_false_positive.update_pipette_tip_state(
80
101
  pipette_id=params.pipetteId, tip_geometry=None
81
102
  )
103
+ state_update.set_fluid_unknown(pipette_id=params.pipetteId)
82
104
  error = TipPhysicallyAttachedError(
83
105
  id=self._model_utils.generate_id(),
84
106
  createdAt=self._model_utils.get_timestamp(),
@@ -96,7 +118,52 @@ class DropTipInPlaceImplementation(
96
118
  state_update=state_update,
97
119
  state_update_if_false_positive=state_update_if_false_positive,
98
120
  )
121
+ except PipetteOverpressureError as exception:
122
+ state_update_if_false_positive = update_types.StateUpdate()
123
+ state_update_if_false_positive.update_pipette_tip_state(
124
+ pipette_id=params.pipetteId, tip_geometry=None
125
+ )
126
+ state_update.set_fluid_unknown(pipette_id=params.pipetteId)
127
+ return DefinedErrorData(
128
+ public=OverpressureError(
129
+ id=self._model_utils.generate_id(),
130
+ createdAt=self._model_utils.get_timestamp(),
131
+ wrappedErrors=[
132
+ ErrorOccurrence.from_failed(
133
+ id=self._model_utils.generate_id(),
134
+ createdAt=self._model_utils.get_timestamp(),
135
+ error=exception,
136
+ )
137
+ ],
138
+ errorInfo={"retryLocation": retry_location},
139
+ ),
140
+ state_update=state_update,
141
+ state_update_if_false_positive=state_update_if_false_positive,
142
+ )
143
+ except StallOrCollisionDetectedError as exception:
144
+ state_update_if_false_positive = update_types.StateUpdate()
145
+ state_update_if_false_positive.update_pipette_tip_state(
146
+ pipette_id=params.pipetteId, tip_geometry=None
147
+ )
148
+ state_update.set_fluid_unknown(pipette_id=params.pipetteId)
149
+ return DefinedErrorData(
150
+ public=StallOrCollisionError(
151
+ id=self._model_utils.generate_id(),
152
+ createdAt=self._model_utils.get_timestamp(),
153
+ wrappedErrors=[
154
+ ErrorOccurrence.from_failed(
155
+ id=self._model_utils.generate_id(),
156
+ createdAt=self._model_utils.get_timestamp(),
157
+ error=exception,
158
+ )
159
+ ],
160
+ errorInfo={"retryLocation": retry_location},
161
+ ),
162
+ state_update=state_update,
163
+ state_update_if_false_positive=state_update_if_false_positive,
164
+ )
99
165
  else:
166
+ state_update.set_fluid_unknown(pipette_id=params.pipetteId)
100
167
  state_update.update_pipette_tip_state(
101
168
  pipette_id=params.pipetteId, tip_geometry=None
102
169
  )
@@ -104,13 +171,17 @@ class DropTipInPlaceImplementation(
104
171
 
105
172
 
106
173
  class DropTipInPlace(
107
- BaseCommand[DropTipInPlaceParams, DropTipInPlaceResult, TipPhysicallyAttachedError]
174
+ BaseCommand[
175
+ DropTipInPlaceParams,
176
+ DropTipInPlaceResult,
177
+ TipPhysicallyAttachedError | OverpressureError | StallOrCollisionError,
178
+ ]
108
179
  ):
109
180
  """Drop tip in place command model."""
110
181
 
111
182
  commandType: DropTipInPlaceCommandType = "dropTipInPlace"
112
183
  params: DropTipInPlaceParams
113
- result: Optional[DropTipInPlaceResult]
184
+ result: Optional[DropTipInPlaceResult] = None
114
185
 
115
186
  _ImplementationCls: Type[
116
187
  DropTipInPlaceImplementation
@@ -0,0 +1,156 @@
1
+ """Evotip Dispense-in-place command request, result, and implementation models."""
2
+
3
+ from __future__ import annotations
4
+ from typing import TYPE_CHECKING, Optional, Type, Union
5
+ from typing_extensions import Literal
6
+
7
+ from opentrons.protocol_engine.errors import UnsupportedLabwareForActionError
8
+ from .pipetting_common import (
9
+ PipetteIdMixin,
10
+ FlowRateMixin,
11
+ DispenseVolumeMixin,
12
+ BaseLiquidHandlingResult,
13
+ dispense_in_place,
14
+ )
15
+ from .movement_common import (
16
+ LiquidHandlingWellLocationMixin,
17
+ StallOrCollisionError,
18
+ move_to_well,
19
+ )
20
+
21
+ from .command import (
22
+ AbstractCommandImpl,
23
+ BaseCommand,
24
+ BaseCommandCreate,
25
+ SuccessData,
26
+ DefinedErrorData,
27
+ )
28
+ from ..state.update_types import StateUpdate
29
+ from ..resources import labware_validation
30
+ from ..errors import ProtocolEngineError
31
+
32
+ if TYPE_CHECKING:
33
+ from ..execution import PipettingHandler, GantryMover, MovementHandler
34
+ from ..resources import ModelUtils
35
+ from ..state.state import StateView
36
+
37
+
38
+ EvotipDispenseCommandType = Literal["evotipDispense"]
39
+
40
+
41
+ class EvotipDispenseParams(
42
+ PipetteIdMixin, DispenseVolumeMixin, FlowRateMixin, LiquidHandlingWellLocationMixin
43
+ ):
44
+ """Payload required to dispense in place."""
45
+
46
+ pass
47
+
48
+
49
+ class EvotipDispenseResult(BaseLiquidHandlingResult):
50
+ """Result data from the execution of a DispenseInPlace command."""
51
+
52
+ pass
53
+
54
+
55
+ _ExecuteReturn = Union[
56
+ SuccessData[EvotipDispenseResult],
57
+ DefinedErrorData[StallOrCollisionError],
58
+ ]
59
+
60
+
61
+ class EvotipDispenseImplementation(
62
+ AbstractCommandImpl[EvotipDispenseParams, _ExecuteReturn]
63
+ ):
64
+ """DispenseInPlace command implementation."""
65
+
66
+ def __init__(
67
+ self,
68
+ pipetting: PipettingHandler,
69
+ state_view: StateView,
70
+ gantry_mover: GantryMover,
71
+ model_utils: ModelUtils,
72
+ movement: MovementHandler,
73
+ **kwargs: object,
74
+ ) -> None:
75
+ self._pipetting = pipetting
76
+ self._state_view = state_view
77
+ self._gantry_mover = gantry_mover
78
+ self._model_utils = model_utils
79
+ self._movement = movement
80
+
81
+ async def execute(self, params: EvotipDispenseParams) -> _ExecuteReturn:
82
+ """Move to and dispense to the requested well."""
83
+ well_location = params.wellLocation
84
+ labware_id = params.labwareId
85
+ well_name = params.wellName
86
+
87
+ labware_definition = self._state_view.labware.get_definition(params.labwareId)
88
+ if not labware_validation.is_evotips(labware_definition.parameters.loadName):
89
+ raise UnsupportedLabwareForActionError(
90
+ f"Cannot use command: `EvotipDispense` with labware: {labware_definition.parameters.loadName}"
91
+ )
92
+ move_result = await move_to_well(
93
+ movement=self._movement,
94
+ model_utils=self._model_utils,
95
+ pipette_id=params.pipetteId,
96
+ labware_id=labware_id,
97
+ well_name=well_name,
98
+ well_location=well_location,
99
+ )
100
+ if isinstance(move_result, DefinedErrorData):
101
+ return move_result
102
+
103
+ current_position = await self._gantry_mover.get_position(params.pipetteId)
104
+ result = await dispense_in_place(
105
+ pipette_id=params.pipetteId,
106
+ volume=params.volume,
107
+ flow_rate=params.flowRate,
108
+ push_out=None,
109
+ location_if_error={
110
+ "retryLocation": (
111
+ current_position.x,
112
+ current_position.y,
113
+ current_position.z,
114
+ )
115
+ },
116
+ pipetting=self._pipetting,
117
+ model_utils=self._model_utils,
118
+ )
119
+ if isinstance(result, DefinedErrorData):
120
+ # TODO (chb, 2025-01-29): Remove this and the OverpressureError returns once disabled for this function
121
+ raise ProtocolEngineError(
122
+ message="Overpressure Error during Resin Tip Dispense Command."
123
+ )
124
+ return SuccessData(
125
+ public=EvotipDispenseResult(volume=result.public.volume),
126
+ state_update=StateUpdate.reduce(
127
+ move_result.state_update, result.state_update
128
+ ),
129
+ )
130
+
131
+
132
+ class EvotipDispense(
133
+ BaseCommand[
134
+ EvotipDispenseParams,
135
+ EvotipDispenseResult,
136
+ StallOrCollisionError,
137
+ ]
138
+ ):
139
+ """DispenseInPlace command model."""
140
+
141
+ commandType: EvotipDispenseCommandType = "evotipDispense"
142
+ params: EvotipDispenseParams
143
+ result: Optional[EvotipDispenseResult] = None
144
+
145
+ _ImplementationCls: Type[
146
+ EvotipDispenseImplementation
147
+ ] = EvotipDispenseImplementation
148
+
149
+
150
+ class EvotipDispenseCreate(BaseCommandCreate[EvotipDispenseParams]):
151
+ """DispenseInPlace command request model."""
152
+
153
+ commandType: EvotipDispenseCommandType = "evotipDispense"
154
+ params: EvotipDispenseParams
155
+
156
+ _CommandCls: Type[EvotipDispense] = EvotipDispense