opentrons 8.2.0a4__py2.py3-none-any.whl → 8.3.0a1__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 (229) 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 +28 -20
  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 +60 -23
  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 +18 -0
  47. opentrons/hardware_control/protocols/motion_controller.py +6 -0
  48. opentrons/hardware_control/robot_calibration.py +1 -1
  49. opentrons/hardware_control/types.py +61 -0
  50. opentrons/protocol_api/__init__.py +20 -1
  51. opentrons/protocol_api/_liquid.py +24 -49
  52. opentrons/protocol_api/_liquid_properties.py +754 -0
  53. opentrons/protocol_api/_types.py +24 -0
  54. opentrons/protocol_api/core/common.py +2 -0
  55. opentrons/protocol_api/core/engine/instrument.py +82 -10
  56. opentrons/protocol_api/core/engine/labware.py +29 -7
  57. opentrons/protocol_api/core/engine/protocol.py +130 -5
  58. opentrons/protocol_api/core/engine/robot.py +139 -0
  59. opentrons/protocol_api/core/engine/well.py +4 -1
  60. opentrons/protocol_api/core/instrument.py +46 -4
  61. opentrons/protocol_api/core/labware.py +13 -4
  62. opentrons/protocol_api/core/legacy/legacy_instrument_core.py +37 -3
  63. opentrons/protocol_api/core/legacy/legacy_labware_core.py +13 -4
  64. opentrons/protocol_api/core/legacy/legacy_protocol_core.py +32 -1
  65. opentrons/protocol_api/core/legacy/legacy_robot_core.py +0 -0
  66. opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +37 -3
  67. opentrons/protocol_api/core/protocol.py +34 -1
  68. opentrons/protocol_api/core/robot.py +51 -0
  69. opentrons/protocol_api/instrument_context.py +158 -44
  70. opentrons/protocol_api/labware.py +231 -7
  71. opentrons/protocol_api/module_contexts.py +21 -17
  72. opentrons/protocol_api/protocol_context.py +125 -4
  73. opentrons/protocol_api/robot_context.py +204 -32
  74. opentrons/protocol_api/validation.py +262 -3
  75. opentrons/protocol_engine/__init__.py +4 -0
  76. opentrons/protocol_engine/actions/actions.py +2 -3
  77. opentrons/protocol_engine/clients/sync_client.py +18 -0
  78. opentrons/protocol_engine/commands/__init__.py +81 -0
  79. opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +0 -2
  80. opentrons/protocol_engine/commands/absorbance_reader/initialize.py +19 -5
  81. opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +0 -1
  82. opentrons/protocol_engine/commands/absorbance_reader/read.py +32 -9
  83. opentrons/protocol_engine/commands/air_gap_in_place.py +160 -0
  84. opentrons/protocol_engine/commands/aspirate.py +103 -53
  85. opentrons/protocol_engine/commands/aspirate_in_place.py +55 -51
  86. opentrons/protocol_engine/commands/blow_out.py +44 -39
  87. opentrons/protocol_engine/commands/blow_out_in_place.py +21 -32
  88. opentrons/protocol_engine/commands/calibration/calibrate_gripper.py +13 -6
  89. opentrons/protocol_engine/commands/calibration/calibrate_module.py +1 -1
  90. opentrons/protocol_engine/commands/calibration/calibrate_pipette.py +3 -3
  91. opentrons/protocol_engine/commands/calibration/move_to_maintenance_position.py +1 -1
  92. opentrons/protocol_engine/commands/command.py +73 -66
  93. opentrons/protocol_engine/commands/command_unions.py +101 -1
  94. opentrons/protocol_engine/commands/comment.py +1 -1
  95. opentrons/protocol_engine/commands/configure_for_volume.py +10 -3
  96. opentrons/protocol_engine/commands/configure_nozzle_layout.py +6 -4
  97. opentrons/protocol_engine/commands/custom.py +6 -12
  98. opentrons/protocol_engine/commands/dispense.py +82 -48
  99. opentrons/protocol_engine/commands/dispense_in_place.py +71 -51
  100. opentrons/protocol_engine/commands/drop_tip.py +52 -31
  101. opentrons/protocol_engine/commands/drop_tip_in_place.py +13 -3
  102. opentrons/protocol_engine/commands/generate_command_schema.py +4 -11
  103. opentrons/protocol_engine/commands/get_next_tip.py +134 -0
  104. opentrons/protocol_engine/commands/get_tip_presence.py +1 -1
  105. opentrons/protocol_engine/commands/heater_shaker/close_labware_latch.py +1 -1
  106. opentrons/protocol_engine/commands/heater_shaker/deactivate_heater.py +1 -1
  107. opentrons/protocol_engine/commands/heater_shaker/deactivate_shaker.py +1 -1
  108. opentrons/protocol_engine/commands/heater_shaker/open_labware_latch.py +1 -1
  109. opentrons/protocol_engine/commands/heater_shaker/set_and_wait_for_shake_speed.py +1 -1
  110. opentrons/protocol_engine/commands/heater_shaker/set_target_temperature.py +1 -1
  111. opentrons/protocol_engine/commands/heater_shaker/wait_for_temperature.py +10 -4
  112. opentrons/protocol_engine/commands/home.py +13 -4
  113. opentrons/protocol_engine/commands/liquid_probe.py +67 -24
  114. opentrons/protocol_engine/commands/load_labware.py +29 -7
  115. opentrons/protocol_engine/commands/load_lid.py +146 -0
  116. opentrons/protocol_engine/commands/load_lid_stack.py +189 -0
  117. opentrons/protocol_engine/commands/load_liquid.py +12 -4
  118. opentrons/protocol_engine/commands/load_liquid_class.py +144 -0
  119. opentrons/protocol_engine/commands/load_module.py +31 -10
  120. opentrons/protocol_engine/commands/load_pipette.py +19 -8
  121. opentrons/protocol_engine/commands/magnetic_module/disengage.py +1 -1
  122. opentrons/protocol_engine/commands/magnetic_module/engage.py +1 -1
  123. opentrons/protocol_engine/commands/move_labware.py +19 -6
  124. opentrons/protocol_engine/commands/move_relative.py +35 -25
  125. opentrons/protocol_engine/commands/move_to_addressable_area.py +40 -27
  126. opentrons/protocol_engine/commands/move_to_addressable_area_for_drop_tip.py +53 -32
  127. opentrons/protocol_engine/commands/move_to_coordinates.py +36 -22
  128. opentrons/protocol_engine/commands/move_to_well.py +40 -24
  129. opentrons/protocol_engine/commands/movement_common.py +338 -0
  130. opentrons/protocol_engine/commands/pick_up_tip.py +49 -27
  131. opentrons/protocol_engine/commands/pipetting_common.py +169 -87
  132. opentrons/protocol_engine/commands/prepare_to_aspirate.py +24 -33
  133. opentrons/protocol_engine/commands/reload_labware.py +1 -1
  134. opentrons/protocol_engine/commands/retract_axis.py +1 -1
  135. opentrons/protocol_engine/commands/robot/__init__.py +69 -0
  136. opentrons/protocol_engine/commands/robot/close_gripper_jaw.py +86 -0
  137. opentrons/protocol_engine/commands/robot/common.py +18 -0
  138. opentrons/protocol_engine/commands/robot/move_axes_relative.py +101 -0
  139. opentrons/protocol_engine/commands/robot/move_axes_to.py +100 -0
  140. opentrons/protocol_engine/commands/robot/move_to.py +94 -0
  141. opentrons/protocol_engine/commands/robot/open_gripper_jaw.py +77 -0
  142. opentrons/protocol_engine/commands/save_position.py +14 -5
  143. opentrons/protocol_engine/commands/set_rail_lights.py +1 -1
  144. opentrons/protocol_engine/commands/set_status_bar.py +1 -1
  145. opentrons/protocol_engine/commands/temperature_module/deactivate.py +1 -1
  146. opentrons/protocol_engine/commands/temperature_module/set_target_temperature.py +1 -1
  147. opentrons/protocol_engine/commands/temperature_module/wait_for_temperature.py +10 -4
  148. opentrons/protocol_engine/commands/thermocycler/close_lid.py +1 -1
  149. opentrons/protocol_engine/commands/thermocycler/deactivate_block.py +1 -1
  150. opentrons/protocol_engine/commands/thermocycler/deactivate_lid.py +1 -1
  151. opentrons/protocol_engine/commands/thermocycler/open_lid.py +1 -1
  152. opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py +8 -2
  153. opentrons/protocol_engine/commands/thermocycler/run_profile.py +9 -3
  154. opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py +11 -4
  155. opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py +1 -1
  156. opentrons/protocol_engine/commands/thermocycler/wait_for_block_temperature.py +1 -1
  157. opentrons/protocol_engine/commands/thermocycler/wait_for_lid_temperature.py +1 -1
  158. opentrons/protocol_engine/commands/touch_tip.py +65 -16
  159. opentrons/protocol_engine/commands/unsafe/unsafe_blow_out_in_place.py +4 -1
  160. opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +12 -3
  161. opentrons/protocol_engine/commands/unsafe/unsafe_engage_axes.py +1 -4
  162. opentrons/protocol_engine/commands/unsafe/update_position_estimators.py +1 -4
  163. opentrons/protocol_engine/commands/verify_tip_presence.py +11 -4
  164. opentrons/protocol_engine/commands/wait_for_duration.py +10 -3
  165. opentrons/protocol_engine/commands/wait_for_resume.py +10 -3
  166. opentrons/protocol_engine/errors/__init__.py +8 -0
  167. opentrons/protocol_engine/errors/error_occurrence.py +19 -20
  168. opentrons/protocol_engine/errors/exceptions.py +50 -0
  169. opentrons/protocol_engine/execution/command_executor.py +1 -1
  170. opentrons/protocol_engine/execution/equipment.py +73 -5
  171. opentrons/protocol_engine/execution/gantry_mover.py +364 -8
  172. opentrons/protocol_engine/execution/movement.py +27 -0
  173. opentrons/protocol_engine/execution/pipetting.py +5 -1
  174. opentrons/protocol_engine/execution/tip_handler.py +4 -6
  175. opentrons/protocol_engine/notes/notes.py +1 -1
  176. opentrons/protocol_engine/protocol_engine.py +7 -6
  177. opentrons/protocol_engine/resources/labware_data_provider.py +1 -1
  178. opentrons/protocol_engine/resources/labware_validation.py +5 -0
  179. opentrons/protocol_engine/resources/module_data_provider.py +1 -1
  180. opentrons/protocol_engine/resources/pipette_data_provider.py +26 -0
  181. opentrons/protocol_engine/slot_standardization.py +9 -9
  182. opentrons/protocol_engine/state/_move_types.py +9 -5
  183. opentrons/protocol_engine/state/_well_math.py +193 -0
  184. opentrons/protocol_engine/state/addressable_areas.py +25 -61
  185. opentrons/protocol_engine/state/command_history.py +12 -0
  186. opentrons/protocol_engine/state/commands.py +17 -13
  187. opentrons/protocol_engine/state/files.py +10 -12
  188. opentrons/protocol_engine/state/fluid_stack.py +138 -0
  189. opentrons/protocol_engine/state/frustum_helpers.py +57 -32
  190. opentrons/protocol_engine/state/geometry.py +47 -1
  191. opentrons/protocol_engine/state/labware.py +79 -25
  192. opentrons/protocol_engine/state/liquid_classes.py +82 -0
  193. opentrons/protocol_engine/state/liquids.py +16 -4
  194. opentrons/protocol_engine/state/modules.py +52 -70
  195. opentrons/protocol_engine/state/motion.py +6 -1
  196. opentrons/protocol_engine/state/pipettes.py +144 -58
  197. opentrons/protocol_engine/state/state.py +21 -2
  198. opentrons/protocol_engine/state/state_summary.py +4 -2
  199. opentrons/protocol_engine/state/tips.py +11 -44
  200. opentrons/protocol_engine/state/update_types.py +343 -48
  201. opentrons/protocol_engine/state/wells.py +19 -11
  202. opentrons/protocol_engine/types.py +176 -28
  203. opentrons/protocol_reader/extract_labware_definitions.py +5 -2
  204. opentrons/protocol_reader/file_format_validator.py +5 -5
  205. opentrons/protocol_runner/json_file_reader.py +9 -3
  206. opentrons/protocol_runner/json_translator.py +51 -25
  207. opentrons/protocol_runner/legacy_command_mapper.py +66 -64
  208. opentrons/protocol_runner/protocol_runner.py +35 -4
  209. opentrons/protocol_runner/python_protocol_wrappers.py +1 -1
  210. opentrons/protocol_runner/run_orchestrator.py +13 -3
  211. opentrons/protocols/advanced_control/common.py +38 -0
  212. opentrons/protocols/advanced_control/mix.py +1 -1
  213. opentrons/protocols/advanced_control/transfers/__init__.py +0 -0
  214. opentrons/protocols/advanced_control/transfers/common.py +56 -0
  215. opentrons/protocols/advanced_control/{transfers.py → transfers/transfer.py} +10 -85
  216. opentrons/protocols/api_support/definitions.py +1 -1
  217. opentrons/protocols/api_support/instrument.py +1 -1
  218. opentrons/protocols/api_support/util.py +10 -0
  219. opentrons/protocols/labware.py +39 -6
  220. opentrons/protocols/models/json_protocol.py +5 -9
  221. opentrons/simulate.py +3 -1
  222. opentrons/types.py +162 -2
  223. opentrons/util/logging_config.py +1 -1
  224. {opentrons-8.2.0a4.dist-info → opentrons-8.3.0a1.dist-info}/METADATA +16 -15
  225. {opentrons-8.2.0a4.dist-info → opentrons-8.3.0a1.dist-info}/RECORD +229 -202
  226. {opentrons-8.2.0a4.dist-info → opentrons-8.3.0a1.dist-info}/WHEEL +1 -1
  227. {opentrons-8.2.0a4.dist-info → opentrons-8.3.0a1.dist-info}/LICENSE +0 -0
  228. {opentrons-8.2.0a4.dist-info → opentrons-8.3.0a1.dist-info}/entry_points.txt +0 -0
  229. {opentrons-8.2.0a4.dist-info → opentrons-8.3.0a1.dist-info}/top_level.txt +0 -0
@@ -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]
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
@@ -0,0 +1,18 @@
1
+ """Shared result types for robot API commands."""
2
+ from pydantic import BaseModel, Field
3
+
4
+ from typing import Dict
5
+ from opentrons.protocol_engine.types import MotorAxis
6
+
7
+
8
+ MotorAxisMapType = Dict[MotorAxis, float]
9
+ default_position = {ax: 0.0 for ax in MotorAxis}
10
+
11
+
12
+ class DestinationRobotPositionResult(BaseModel):
13
+ """The result dictionary of `MotorAxis` type."""
14
+
15
+ position: MotorAxisMapType = Field(
16
+ default=default_position,
17
+ description="The position of all axes on the robot. If no mount was provided, the last moved mount is used to determine the location.",
18
+ )
@@ -0,0 +1,101 @@
1
+ """Command models for moving any robot axis relative."""
2
+
3
+ from __future__ import annotations
4
+ from typing import Literal, Type, Optional, TYPE_CHECKING, Any
5
+
6
+ from pydantic import BaseModel, Field
7
+ from pydantic.json_schema import SkipJsonSchema
8
+ from opentrons.hardware_control import HardwareControlAPI
9
+ from opentrons.protocol_engine.resources import ensure_ot3_hardware
10
+
11
+ from .common import MotorAxisMapType, DestinationRobotPositionResult
12
+
13
+ from ..command import (
14
+ AbstractCommandImpl,
15
+ BaseCommand,
16
+ BaseCommandCreate,
17
+ SuccessData,
18
+ )
19
+ from ...errors.error_occurrence import ErrorOccurrence
20
+
21
+ if TYPE_CHECKING:
22
+ from opentrons.protocol_engine.execution import GantryMover
23
+
24
+
25
+ MoveAxesRelativeCommandType = Literal["robot/moveAxesRelative"]
26
+
27
+
28
+ def _remove_default(s: dict[str, Any]) -> None:
29
+ s.pop("default", None)
30
+
31
+
32
+ class MoveAxesRelativeParams(BaseModel):
33
+ """Payload required to move axes relative to position."""
34
+
35
+ axis_map: MotorAxisMapType = Field(
36
+ ..., description="A dictionary mapping axes to relative movements in mm."
37
+ )
38
+ speed: float | SkipJsonSchema[None] = Field(
39
+ default=None,
40
+ description="The max velocity to move the axes at. Will fall to hardware defaults if none provided.",
41
+ json_schema_extra=_remove_default,
42
+ )
43
+
44
+
45
+ class MoveAxesRelativeResult(DestinationRobotPositionResult):
46
+ """Result data from the execution of a MoveAxesRelative command."""
47
+
48
+ pass
49
+
50
+
51
+ class MoveAxesRelativeImplementation(
52
+ AbstractCommandImpl[MoveAxesRelativeParams, SuccessData[MoveAxesRelativeResult]]
53
+ ):
54
+ """MoveAxesRelative command implementation."""
55
+
56
+ def __init__(
57
+ self,
58
+ gantry_mover: GantryMover,
59
+ hardware_api: HardwareControlAPI,
60
+ **kwargs: object,
61
+ ) -> None:
62
+ self._gantry_mover = gantry_mover
63
+ self._hardware_api = hardware_api
64
+
65
+ async def execute(
66
+ self, params: MoveAxesRelativeParams
67
+ ) -> SuccessData[MoveAxesRelativeResult]:
68
+ """Move the axes on a flex a relative distance."""
69
+ # TODO (lc 08-16-2024) implement `move_axes` for OT 2 hardware controller
70
+ # and then we can remove this validation.
71
+ ensure_ot3_hardware(self._hardware_api)
72
+
73
+ current_position = await self._gantry_mover.move_axes(
74
+ axis_map=params.axis_map, speed=params.speed, relative_move=True
75
+ )
76
+ return SuccessData(
77
+ public=MoveAxesRelativeResult(position=current_position),
78
+ )
79
+
80
+
81
+ class MoveAxesRelative(
82
+ BaseCommand[MoveAxesRelativeParams, MoveAxesRelativeResult, ErrorOccurrence]
83
+ ):
84
+ """MoveAxesRelative command model."""
85
+
86
+ commandType: MoveAxesRelativeCommandType = "robot/moveAxesRelative"
87
+ params: MoveAxesRelativeParams
88
+ result: Optional[MoveAxesRelativeResult]
89
+
90
+ _ImplementationCls: Type[
91
+ MoveAxesRelativeImplementation
92
+ ] = MoveAxesRelativeImplementation
93
+
94
+
95
+ class MoveAxesRelativeCreate(BaseCommandCreate[MoveAxesRelativeParams]):
96
+ """MoveAxesRelative command request model."""
97
+
98
+ commandType: MoveAxesRelativeCommandType = "robot/moveAxesRelative"
99
+ params: MoveAxesRelativeParams
100
+
101
+ _CommandCls: Type[MoveAxesRelative] = MoveAxesRelative