opentrons 8.3.0a2__py2.py3-none-any.whl → 8.3.0a4__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 (56) hide show
  1. opentrons/hardware_control/api.py +5 -1
  2. opentrons/hardware_control/ot3api.py +18 -8
  3. opentrons/hardware_control/protocols/liquid_handler.py +4 -1
  4. opentrons/hardware_control/protocols/motion_controller.py +1 -0
  5. opentrons/legacy_commands/commands.py +37 -0
  6. opentrons/legacy_commands/types.py +39 -0
  7. opentrons/protocol_api/core/engine/instrument.py +109 -0
  8. opentrons/protocol_api/core/instrument.py +27 -0
  9. opentrons/protocol_api/core/legacy/legacy_instrument_core.py +50 -0
  10. opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +24 -0
  11. opentrons/protocol_api/instrument_context.py +141 -0
  12. opentrons/protocol_engine/commands/__init__.py +40 -0
  13. opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +1 -1
  14. opentrons/protocol_engine/commands/absorbance_reader/initialize.py +1 -1
  15. opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +1 -1
  16. opentrons/protocol_engine/commands/absorbance_reader/read.py +4 -1
  17. opentrons/protocol_engine/commands/air_gap_in_place.py +1 -1
  18. opentrons/protocol_engine/commands/command_unions.py +39 -0
  19. opentrons/protocol_engine/commands/evotip_dispense.py +156 -0
  20. opentrons/protocol_engine/commands/evotip_seal_pipette.py +331 -0
  21. opentrons/protocol_engine/commands/evotip_unseal_pipette.py +160 -0
  22. opentrons/protocol_engine/commands/get_next_tip.py +1 -1
  23. opentrons/protocol_engine/commands/liquid_probe.py +63 -12
  24. opentrons/protocol_engine/commands/load_labware.py +5 -0
  25. opentrons/protocol_engine/commands/load_lid.py +1 -1
  26. opentrons/protocol_engine/commands/load_lid_stack.py +1 -1
  27. opentrons/protocol_engine/commands/load_liquid_class.py +1 -1
  28. opentrons/protocol_engine/commands/move_labware.py +9 -0
  29. opentrons/protocol_engine/commands/robot/close_gripper_jaw.py +1 -1
  30. opentrons/protocol_engine/commands/robot/move_axes_relative.py +1 -1
  31. opentrons/protocol_engine/commands/robot/move_axes_to.py +1 -1
  32. opentrons/protocol_engine/commands/robot/move_to.py +1 -1
  33. opentrons/protocol_engine/commands/robot/open_gripper_jaw.py +1 -1
  34. opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py +1 -1
  35. opentrons/protocol_engine/commands/unsafe/unsafe_blow_out_in_place.py +1 -1
  36. opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +1 -1
  37. opentrons/protocol_engine/commands/unsafe/unsafe_engage_axes.py +1 -1
  38. opentrons/protocol_engine/commands/unsafe/unsafe_place_labware.py +1 -1
  39. opentrons/protocol_engine/commands/unsafe/unsafe_ungrip_labware.py +1 -1
  40. opentrons/protocol_engine/commands/unsafe/update_position_estimators.py +1 -1
  41. opentrons/protocol_engine/errors/__init__.py +4 -0
  42. opentrons/protocol_engine/errors/exceptions.py +26 -0
  43. opentrons/protocol_engine/execution/gantry_mover.py +5 -0
  44. opentrons/protocol_engine/execution/hardware_stopper.py +7 -7
  45. opentrons/protocol_engine/execution/tip_handler.py +30 -9
  46. opentrons/protocol_engine/resources/labware_validation.py +13 -0
  47. opentrons/protocol_engine/state/commands.py +6 -2
  48. opentrons/protocol_engine/state/frustum_helpers.py +13 -44
  49. opentrons/protocol_engine/state/labware.py +13 -1
  50. opentrons/protocol_engine/state/pipettes.py +5 -0
  51. {opentrons-8.3.0a2.dist-info → opentrons-8.3.0a4.dist-info}/METADATA +4 -4
  52. {opentrons-8.3.0a2.dist-info → opentrons-8.3.0a4.dist-info}/RECORD +56 -53
  53. {opentrons-8.3.0a2.dist-info → opentrons-8.3.0a4.dist-info}/LICENSE +0 -0
  54. {opentrons-8.3.0a2.dist-info → opentrons-8.3.0a4.dist-info}/WHEEL +0 -0
  55. {opentrons-8.3.0a2.dist-info → opentrons-8.3.0a4.dist-info}/entry_points.txt +0 -0
  56. {opentrons-8.3.0a2.dist-info → opentrons-8.3.0a4.dist-info}/top_level.txt +0 -0
@@ -137,7 +137,7 @@ class Initialize(BaseCommand[InitializeParams, InitializeResult, ErrorOccurrence
137
137
 
138
138
  commandType: InitializeCommandType = "absorbanceReader/initialize"
139
139
  params: InitializeParams
140
- result: Optional[InitializeResult]
140
+ result: Optional[InitializeResult] = None
141
141
 
142
142
  _ImplementationCls: Type[InitializeImpl] = InitializeImpl
143
143
 
@@ -133,7 +133,7 @@ class OpenLid(BaseCommand[OpenLidParams, OpenLidResult, ErrorOccurrence]):
133
133
 
134
134
  commandType: OpenLidCommandType = "absorbanceReader/openLid"
135
135
  params: OpenLidParams
136
- result: Optional[OpenLidResult]
136
+ result: Optional[OpenLidResult] = None
137
137
 
138
138
  _ImplementationCls: Type[OpenLidImpl] = OpenLidImpl
139
139
 
@@ -182,6 +182,9 @@ class ReadAbsorbanceImpl(
182
182
  )
183
183
  file_ids.append(file_id)
184
184
 
185
+ state_update.files_added = update_types.FilesAddedUpdate(
186
+ file_ids=file_ids
187
+ )
185
188
  # Return success data to api
186
189
  return SuccessData(
187
190
  public=ReadAbsorbanceResult(
@@ -209,7 +212,7 @@ class ReadAbsorbance(
209
212
 
210
213
  commandType: ReadAbsorbanceCommandType = "absorbanceReader/read"
211
214
  params: ReadAbsorbanceParams
212
- result: Optional[ReadAbsorbanceResult]
215
+ result: Optional[ReadAbsorbanceResult] = None
213
216
 
214
217
  _ImplementationCls: Type[ReadAbsorbanceImpl] = ReadAbsorbanceImpl
215
218
 
@@ -146,7 +146,7 @@ class AirGapInPlace(
146
146
 
147
147
  commandType: AirGapInPlaceCommandType = "airGapInPlace"
148
148
  params: AirGapInPlaceParams
149
- result: Optional[AirGapInPlaceResult]
149
+ result: Optional[AirGapInPlaceResult] = None
150
150
 
151
151
  _ImplementationCls: Type[AirGapInPlaceImplementation] = AirGapInPlaceImplementation
152
152
 
@@ -360,6 +360,30 @@ from .liquid_probe import (
360
360
  TryLiquidProbeCommandType,
361
361
  )
362
362
 
363
+ from .evotip_seal_pipette import (
364
+ EvotipSealPipette,
365
+ EvotipSealPipetteParams,
366
+ EvotipSealPipetteCreate,
367
+ EvotipSealPipetteResult,
368
+ EvotipSealPipetteCommandType,
369
+ )
370
+
371
+ from .evotip_dispense import (
372
+ EvotipDispense,
373
+ EvotipDispenseParams,
374
+ EvotipDispenseCreate,
375
+ EvotipDispenseResult,
376
+ EvotipDispenseCommandType,
377
+ )
378
+
379
+ from .evotip_unseal_pipette import (
380
+ EvotipUnsealPipette,
381
+ EvotipUnsealPipetteParams,
382
+ EvotipUnsealPipetteCreate,
383
+ EvotipUnsealPipetteResult,
384
+ EvotipUnsealPipetteCommandType,
385
+ )
386
+
363
387
  Command = Annotated[
364
388
  Union[
365
389
  AirGapInPlace,
@@ -404,6 +428,9 @@ Command = Annotated[
404
428
  GetNextTip,
405
429
  LiquidProbe,
406
430
  TryLiquidProbe,
431
+ EvotipSealPipette,
432
+ EvotipDispense,
433
+ EvotipUnsealPipette,
407
434
  heater_shaker.WaitForTemperature,
408
435
  heater_shaker.SetTargetTemperature,
409
436
  heater_shaker.DeactivateHeater,
@@ -492,6 +519,9 @@ CommandParams = Union[
492
519
  GetNextTipParams,
493
520
  LiquidProbeParams,
494
521
  TryLiquidProbeParams,
522
+ EvotipSealPipetteParams,
523
+ EvotipDispenseParams,
524
+ EvotipUnsealPipetteParams,
495
525
  heater_shaker.WaitForTemperatureParams,
496
526
  heater_shaker.SetTargetTemperatureParams,
497
527
  heater_shaker.DeactivateHeaterParams,
@@ -578,6 +608,9 @@ CommandType = Union[
578
608
  GetNextTipCommandType,
579
609
  LiquidProbeCommandType,
580
610
  TryLiquidProbeCommandType,
611
+ EvotipSealPipetteCommandType,
612
+ EvotipDispenseCommandType,
613
+ EvotipUnsealPipetteCommandType,
581
614
  heater_shaker.WaitForTemperatureCommandType,
582
615
  heater_shaker.SetTargetTemperatureCommandType,
583
616
  heater_shaker.DeactivateHeaterCommandType,
@@ -665,6 +698,9 @@ CommandCreate = Annotated[
665
698
  GetNextTipCreate,
666
699
  LiquidProbeCreate,
667
700
  TryLiquidProbeCreate,
701
+ EvotipSealPipetteCreate,
702
+ EvotipDispenseCreate,
703
+ EvotipUnsealPipetteCreate,
668
704
  heater_shaker.WaitForTemperatureCreate,
669
705
  heater_shaker.SetTargetTemperatureCreate,
670
706
  heater_shaker.DeactivateHeaterCreate,
@@ -760,6 +796,9 @@ CommandResult = Union[
760
796
  GetNextTipResult,
761
797
  LiquidProbeResult,
762
798
  TryLiquidProbeResult,
799
+ EvotipSealPipetteResult,
800
+ EvotipDispenseResult,
801
+ EvotipUnsealPipetteResult,
763
802
  heater_shaker.WaitForTemperatureResult,
764
803
  heater_shaker.SetTargetTemperatureResult,
765
804
  heater_shaker.DeactivateHeaterResult,
@@ -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
@@ -0,0 +1,331 @@
1
+ """Seal evotip resin tip command request, result, and implementation models."""
2
+
3
+ from __future__ import annotations
4
+ from pydantic import Field, BaseModel
5
+ from typing import TYPE_CHECKING, Optional, Type, Union
6
+ from opentrons.types import MountType
7
+ from opentrons.protocol_engine.types import MotorAxis
8
+ from typing_extensions import Literal
9
+
10
+ from opentrons.protocol_engine.errors import UnsupportedLabwareForActionError
11
+ from ..resources import ModelUtils, labware_validation
12
+ from ..types import PickUpTipWellLocation, FluidKind, AspiratedFluid
13
+ from .pipetting_common import (
14
+ PipetteIdMixin,
15
+ )
16
+ from .movement_common import (
17
+ DestinationPositionResult,
18
+ StallOrCollisionError,
19
+ move_to_well,
20
+ )
21
+ from .command import (
22
+ AbstractCommandImpl,
23
+ BaseCommand,
24
+ BaseCommandCreate,
25
+ DefinedErrorData,
26
+ SuccessData,
27
+ )
28
+
29
+ from opentrons.hardware_control import HardwareControlAPI
30
+ from opentrons.hardware_control.types import Axis
31
+ from ..state.update_types import StateUpdate
32
+
33
+ if TYPE_CHECKING:
34
+ from ..state.state import StateView
35
+ from ..execution import (
36
+ MovementHandler,
37
+ TipHandler,
38
+ GantryMover,
39
+ PipettingHandler,
40
+ )
41
+
42
+
43
+ EvotipSealPipetteCommandType = Literal["evotipSealPipette"]
44
+ _PREP_DISTANCE_DEFAULT = 8.25
45
+ _PRESS_DISTANCE_DEFAULT = 3.5
46
+ _EJECTOR_PUSH_MM_DEFAULT = 7.0
47
+
48
+
49
+ class TipPickUpParams(BaseModel):
50
+ """Payload used to specify press-tip parameters for a seal command."""
51
+
52
+ prepDistance: float = Field(
53
+ default=0, description="The distance to move down to fit the tips on."
54
+ )
55
+ pressDistance: float = Field(
56
+ default=0, description="The distance to press on tips."
57
+ )
58
+ ejectorPushMm: float = Field(
59
+ default=0,
60
+ description="The distance to back off to ensure that the tip presence sensors are not triggered.",
61
+ )
62
+
63
+
64
+ class EvotipSealPipetteParams(PipetteIdMixin):
65
+ """Payload needed to seal resin tips to a pipette."""
66
+
67
+ labwareId: str = Field(..., description="Identifier of labware to use.")
68
+ wellName: str = Field(..., description="Name of well to use in labware.")
69
+ wellLocation: PickUpTipWellLocation = Field(
70
+ default_factory=PickUpTipWellLocation,
71
+ description="Relative well location at which to pick up the tip.",
72
+ )
73
+ tipPickUpParams: Optional[TipPickUpParams] = Field(
74
+ default=None, description="Specific parameters for "
75
+ )
76
+
77
+
78
+ class EvotipSealPipetteResult(DestinationPositionResult):
79
+ """Result data from the execution of a EvotipSealPipette."""
80
+
81
+ tipVolume: float = Field(
82
+ 0,
83
+ description="Maximum volume of liquid that the picked up tip can hold, in µL.",
84
+ ge=0,
85
+ )
86
+
87
+ tipLength: float = Field(
88
+ 0,
89
+ description="The length of the tip in mm.",
90
+ ge=0,
91
+ )
92
+
93
+ tipDiameter: float = Field(
94
+ 0,
95
+ description="The diameter of the tip in mm.",
96
+ ge=0,
97
+ )
98
+
99
+
100
+ _ExecuteReturn = Union[
101
+ SuccessData[EvotipSealPipetteResult],
102
+ DefinedErrorData[StallOrCollisionError],
103
+ ]
104
+
105
+
106
+ class EvotipSealPipetteImplementation(
107
+ AbstractCommandImpl[EvotipSealPipetteParams, _ExecuteReturn]
108
+ ):
109
+ """Evotip seal pipette command implementation."""
110
+
111
+ def __init__(
112
+ self,
113
+ state_view: StateView,
114
+ tip_handler: TipHandler,
115
+ model_utils: ModelUtils,
116
+ movement: MovementHandler,
117
+ hardware_api: HardwareControlAPI,
118
+ gantry_mover: GantryMover,
119
+ pipetting: PipettingHandler,
120
+ **kwargs: object,
121
+ ) -> None:
122
+ self._state_view = state_view
123
+ self._tip_handler = tip_handler
124
+ self._model_utils = model_utils
125
+ self._movement = movement
126
+ self._gantry_mover = gantry_mover
127
+ self._pipetting = pipetting
128
+ self._hardware_api = hardware_api
129
+
130
+ async def relative_pickup_tip(
131
+ self,
132
+ tip_pick_up_params: TipPickUpParams,
133
+ mount: MountType,
134
+ ) -> None:
135
+ """A relative press-fit pick up command using gantry moves."""
136
+ prep_distance = tip_pick_up_params.prepDistance
137
+ press_distance = tip_pick_up_params.pressDistance
138
+ retract_distance = -1 * (prep_distance + press_distance)
139
+
140
+ mount_axis = MotorAxis.LEFT_Z if mount == MountType.LEFT else MotorAxis.RIGHT_Z
141
+
142
+ # TODO chb, 2025-01-29): Factor out the movement constants and relocate this logic into the hardware controller
143
+ await self._gantry_mover.move_axes(
144
+ axis_map={mount_axis: prep_distance}, speed=10, relative_move=True
145
+ )
146
+
147
+ # Drive mount down for press-fit
148
+ await self._gantry_mover.move_axes(
149
+ axis_map={mount_axis: press_distance},
150
+ speed=10.0,
151
+ relative_move=True,
152
+ expect_stalls=True,
153
+ )
154
+ # retract cam : 11.05
155
+ await self._gantry_mover.move_axes(
156
+ axis_map={mount_axis: retract_distance}, speed=5.5, relative_move=True
157
+ )
158
+
159
+ async def cam_action_relative_pickup_tip(
160
+ self,
161
+ tip_pick_up_params: TipPickUpParams,
162
+ mount: MountType,
163
+ ) -> None:
164
+ """A cam action pick up command using gantry moves."""
165
+ prep_distance = tip_pick_up_params.prepDistance
166
+ press_distance = tip_pick_up_params.pressDistance
167
+ ejector_push_mm = tip_pick_up_params.ejectorPushMm
168
+ retract_distance = -1 * (prep_distance + press_distance)
169
+
170
+ mount_axis = MotorAxis.LEFT_Z if mount == MountType.LEFT else MotorAxis.RIGHT_Z
171
+
172
+ # TODO chb, 2025-01-29): Factor out the movement constants and relocate this logic into the hardware controller
173
+ await self._gantry_mover.move_axes(
174
+ axis_map={mount_axis: -6}, speed=10, relative_move=True
175
+ )
176
+
177
+ # Drive Q down 3mm at fast speed - look into the pick up tip fuinction to find slow and fast: 10.0
178
+ await self._gantry_mover.move_axes(
179
+ axis_map={MotorAxis.AXIS_96_CHANNEL_CAM: prep_distance},
180
+ speed=10.0,
181
+ relative_move=True,
182
+ )
183
+ # 2.8mm at slow speed - cam action pickup speed: 5.5
184
+ await self._gantry_mover.move_axes(
185
+ axis_map={MotorAxis.AXIS_96_CHANNEL_CAM: press_distance},
186
+ speed=5.5,
187
+ relative_move=True,
188
+ )
189
+ # retract cam : 11.05
190
+ await self._gantry_mover.move_axes(
191
+ axis_map={MotorAxis.AXIS_96_CHANNEL_CAM: retract_distance},
192
+ speed=5.5,
193
+ relative_move=True,
194
+ )
195
+
196
+ # Lower tip presence
197
+ await self._gantry_mover.move_axes(
198
+ axis_map={mount_axis: 2}, speed=10, relative_move=True
199
+ )
200
+ await self._gantry_mover.move_axes(
201
+ axis_map={MotorAxis.AXIS_96_CHANNEL_CAM: ejector_push_mm},
202
+ speed=5.5,
203
+ relative_move=True,
204
+ )
205
+ await self._gantry_mover.move_axes(
206
+ axis_map={MotorAxis.AXIS_96_CHANNEL_CAM: -1 * ejector_push_mm},
207
+ speed=5.5,
208
+ relative_move=True,
209
+ )
210
+
211
+ async def execute(
212
+ self, params: EvotipSealPipetteParams
213
+ ) -> Union[SuccessData[EvotipSealPipetteResult], _ExecuteReturn]:
214
+ """Move to and pick up a tip using the requested pipette."""
215
+ pipette_id = params.pipetteId
216
+ labware_id = params.labwareId
217
+ well_name = params.wellName
218
+
219
+ labware_definition = self._state_view.labware.get_definition(params.labwareId)
220
+ if not labware_validation.is_evotips(labware_definition.parameters.loadName):
221
+ raise UnsupportedLabwareForActionError(
222
+ f"Cannot use command: `EvotipSealPipette` with labware: {labware_definition.parameters.loadName}"
223
+ )
224
+
225
+ well_location = self._state_view.geometry.convert_pick_up_tip_well_location(
226
+ well_location=params.wellLocation
227
+ )
228
+ move_result = await move_to_well(
229
+ movement=self._movement,
230
+ model_utils=self._model_utils,
231
+ pipette_id=pipette_id,
232
+ labware_id=labware_id,
233
+ well_name=well_name,
234
+ well_location=well_location,
235
+ )
236
+ if isinstance(move_result, DefinedErrorData):
237
+ return move_result
238
+
239
+ # Aspirate to move plunger to a maximum volume position per pipette type
240
+ tip_geometry = self._state_view.geometry.get_nominal_tip_geometry(
241
+ pipette_id, labware_id, well_name
242
+ )
243
+ maximum_volume = self._state_view.pipettes.get_maximum_volume(pipette_id)
244
+ if self._state_view.pipettes.get_mount(pipette_id) == MountType.LEFT:
245
+ await self._hardware_api.home(axes=[Axis.P_L])
246
+ else:
247
+ await self._hardware_api.home(axes=[Axis.P_R])
248
+
249
+ # Begin relative pickup steps for the resin tips
250
+
251
+ channels = self._state_view.tips.get_pipette_active_channels(pipette_id)
252
+ mount = self._state_view.pipettes.get_mount(pipette_id)
253
+ tip_pick_up_params = params.tipPickUpParams
254
+ if tip_pick_up_params is None:
255
+ tip_pick_up_params = TipPickUpParams(
256
+ prepDistance=_PREP_DISTANCE_DEFAULT,
257
+ pressDistance=_PRESS_DISTANCE_DEFAULT,
258
+ ejectorPushMm=_EJECTOR_PUSH_MM_DEFAULT,
259
+ )
260
+
261
+ if channels != 96:
262
+ await self.relative_pickup_tip(
263
+ tip_pick_up_params=tip_pick_up_params,
264
+ mount=mount,
265
+ )
266
+ elif channels == 96:
267
+ await self.cam_action_relative_pickup_tip(
268
+ tip_pick_up_params=tip_pick_up_params,
269
+ mount=mount,
270
+ )
271
+ else:
272
+ tip_geometry = await self._tip_handler.pick_up_tip(
273
+ pipette_id=pipette_id,
274
+ labware_id=labware_id,
275
+ well_name=well_name,
276
+ do_not_ignore_tip_presence=True,
277
+ )
278
+
279
+ # cache_tip
280
+ if self._state_view.config.use_virtual_pipettes is False:
281
+ self._tip_handler.cache_tip(pipette_id, tip_geometry)
282
+ hw_instr = self._hardware_api.hardware_instruments[mount.to_hw_mount()]
283
+ if hw_instr is not None:
284
+ hw_instr.set_current_volume(maximum_volume)
285
+
286
+ state_update = StateUpdate()
287
+ state_update.update_pipette_tip_state(
288
+ pipette_id=pipette_id,
289
+ tip_geometry=tip_geometry,
290
+ )
291
+
292
+ state_update.set_fluid_aspirated(
293
+ pipette_id=pipette_id,
294
+ fluid=AspiratedFluid(kind=FluidKind.LIQUID, volume=maximum_volume),
295
+ )
296
+ return SuccessData(
297
+ public=EvotipSealPipetteResult(
298
+ tipVolume=tip_geometry.volume,
299
+ tipLength=tip_geometry.length,
300
+ tipDiameter=tip_geometry.diameter,
301
+ position=move_result.public.position,
302
+ ),
303
+ state_update=state_update,
304
+ )
305
+
306
+
307
+ class EvotipSealPipette(
308
+ BaseCommand[
309
+ EvotipSealPipetteParams,
310
+ EvotipSealPipetteResult,
311
+ StallOrCollisionError,
312
+ ]
313
+ ):
314
+ """Seal evotip resin tip command model."""
315
+
316
+ commandType: EvotipSealPipetteCommandType = "evotipSealPipette"
317
+ params: EvotipSealPipetteParams
318
+ result: Optional[EvotipSealPipetteResult] = None
319
+
320
+ _ImplementationCls: Type[
321
+ EvotipSealPipetteImplementation
322
+ ] = EvotipSealPipetteImplementation
323
+
324
+
325
+ class EvotipSealPipetteCreate(BaseCommandCreate[EvotipSealPipetteParams]):
326
+ """Seal evotip resin tip command creation request model."""
327
+
328
+ commandType: EvotipSealPipetteCommandType = "evotipSealPipette"
329
+ params: EvotipSealPipetteParams
330
+
331
+ _CommandCls: Type[EvotipSealPipette] = EvotipSealPipette