opentrons 8.3.0a2__py2.py3-none-any.whl → 8.3.0a5__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) 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_api/labware.py +17 -2
  13. opentrons/protocol_engine/commands/__init__.py +40 -0
  14. opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +1 -1
  15. opentrons/protocol_engine/commands/absorbance_reader/initialize.py +1 -1
  16. opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +1 -1
  17. opentrons/protocol_engine/commands/absorbance_reader/read.py +4 -1
  18. opentrons/protocol_engine/commands/air_gap_in_place.py +1 -1
  19. opentrons/protocol_engine/commands/command_unions.py +39 -0
  20. opentrons/protocol_engine/commands/evotip_dispense.py +156 -0
  21. opentrons/protocol_engine/commands/evotip_seal_pipette.py +331 -0
  22. opentrons/protocol_engine/commands/evotip_unseal_pipette.py +160 -0
  23. opentrons/protocol_engine/commands/get_next_tip.py +1 -1
  24. opentrons/protocol_engine/commands/liquid_probe.py +63 -12
  25. opentrons/protocol_engine/commands/load_labware.py +5 -0
  26. opentrons/protocol_engine/commands/load_lid.py +1 -1
  27. opentrons/protocol_engine/commands/load_lid_stack.py +1 -1
  28. opentrons/protocol_engine/commands/load_liquid_class.py +1 -1
  29. opentrons/protocol_engine/commands/move_labware.py +9 -0
  30. opentrons/protocol_engine/commands/robot/close_gripper_jaw.py +1 -1
  31. opentrons/protocol_engine/commands/robot/move_axes_relative.py +1 -1
  32. opentrons/protocol_engine/commands/robot/move_axes_to.py +1 -1
  33. opentrons/protocol_engine/commands/robot/move_to.py +1 -1
  34. opentrons/protocol_engine/commands/robot/open_gripper_jaw.py +1 -1
  35. opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py +1 -1
  36. opentrons/protocol_engine/commands/unsafe/unsafe_blow_out_in_place.py +1 -1
  37. opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +1 -1
  38. opentrons/protocol_engine/commands/unsafe/unsafe_engage_axes.py +1 -1
  39. opentrons/protocol_engine/commands/unsafe/unsafe_place_labware.py +1 -1
  40. opentrons/protocol_engine/commands/unsafe/unsafe_ungrip_labware.py +1 -1
  41. opentrons/protocol_engine/commands/unsafe/update_position_estimators.py +1 -1
  42. opentrons/protocol_engine/errors/__init__.py +4 -0
  43. opentrons/protocol_engine/errors/exceptions.py +26 -0
  44. opentrons/protocol_engine/execution/gantry_mover.py +5 -0
  45. opentrons/protocol_engine/execution/hardware_stopper.py +7 -7
  46. opentrons/protocol_engine/execution/tip_handler.py +30 -9
  47. opentrons/protocol_engine/resources/labware_validation.py +13 -0
  48. opentrons/protocol_engine/state/commands.py +6 -2
  49. opentrons/protocol_engine/state/frustum_helpers.py +13 -44
  50. opentrons/protocol_engine/state/labware.py +13 -1
  51. opentrons/protocol_engine/state/pipettes.py +5 -0
  52. {opentrons-8.3.0a2.dist-info → opentrons-8.3.0a5.dist-info}/METADATA +4 -4
  53. {opentrons-8.3.0a2.dist-info → opentrons-8.3.0a5.dist-info}/RECORD +57 -54
  54. {opentrons-8.3.0a2.dist-info → opentrons-8.3.0a5.dist-info}/LICENSE +0 -0
  55. {opentrons-8.3.0a2.dist-info → opentrons-8.3.0a5.dist-info}/WHEEL +0 -0
  56. {opentrons-8.3.0a2.dist-info → opentrons-8.3.0a5.dist-info}/entry_points.txt +0 -0
  57. {opentrons-8.3.0a2.dist-info → opentrons-8.3.0a5.dist-info}/top_level.txt +0 -0
@@ -380,6 +380,28 @@ from .liquid_probe import (
380
380
  TryLiquidProbeCommandType,
381
381
  )
382
382
 
383
+ from .evotip_seal_pipette import (
384
+ EvotipSealPipette,
385
+ EvotipSealPipetteParams,
386
+ EvotipSealPipetteCreate,
387
+ EvotipSealPipetteResult,
388
+ EvotipSealPipetteCommandType,
389
+ )
390
+ from .evotip_unseal_pipette import (
391
+ EvotipUnsealPipette,
392
+ EvotipUnsealPipetteParams,
393
+ EvotipUnsealPipetteCreate,
394
+ EvotipUnsealPipetteResult,
395
+ EvotipUnsealPipetteCommandType,
396
+ )
397
+ from .evotip_dispense import (
398
+ EvotipDispense,
399
+ EvotipDispenseParams,
400
+ EvotipDispenseCreate,
401
+ EvotipDispenseResult,
402
+ EvotipDispenseCommandType,
403
+ )
404
+
383
405
  __all__ = [
384
406
  # command type unions
385
407
  "Command",
@@ -670,4 +692,22 @@ __all__ = [
670
692
  "TryLiquidProbeCreate",
671
693
  "TryLiquidProbeResult",
672
694
  "TryLiquidProbeCommandType",
695
+ # evotip seal command bundle
696
+ "EvotipSealPipette",
697
+ "EvotipSealPipetteParams",
698
+ "EvotipSealPipetteCreate",
699
+ "EvotipSealPipetteResult",
700
+ "EvotipSealPipetteCommandType",
701
+ # evotip unseal command bundle
702
+ "EvotipUnsealPipette",
703
+ "EvotipUnsealPipetteParams",
704
+ "EvotipUnsealPipetteCreate",
705
+ "EvotipUnsealPipetteResult",
706
+ "EvotipUnsealPipetteCommandType",
707
+ # evotip dispense command bundle
708
+ "EvotipDispense",
709
+ "EvotipDispenseParams",
710
+ "EvotipDispenseCreate",
711
+ "EvotipDispenseResult",
712
+ "EvotipDispenseCommandType",
673
713
  ]
@@ -132,7 +132,7 @@ class CloseLid(BaseCommand[CloseLidParams, CloseLidResult, ErrorOccurrence]):
132
132
 
133
133
  commandType: CloseLidCommandType = "absorbanceReader/closeLid"
134
134
  params: CloseLidParams
135
- result: Optional[CloseLidResult]
135
+ result: Optional[CloseLidResult] = None
136
136
 
137
137
  _ImplementationCls: Type[CloseLidImpl] = CloseLidImpl
138
138
 
@@ -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
+ _SAFE_TOP_VOLUME = 400
48
+
49
+
50
+ class TipPickUpParams(BaseModel):
51
+ """Payload used to specify press-tip parameters for a seal command."""
52
+
53
+ prepDistance: float = Field(
54
+ default=0, description="The distance to move down to fit the tips on."
55
+ )
56
+ pressDistance: float = Field(
57
+ default=0, description="The distance to press on tips."
58
+ )
59
+ ejectorPushMm: float = Field(
60
+ default=0,
61
+ description="The distance to back off to ensure that the tip presence sensors are not triggered.",
62
+ )
63
+
64
+
65
+ class EvotipSealPipetteParams(PipetteIdMixin):
66
+ """Payload needed to seal resin tips to a pipette."""
67
+
68
+ labwareId: str = Field(..., description="Identifier of labware to use.")
69
+ wellName: str = Field(..., description="Name of well to use in labware.")
70
+ wellLocation: PickUpTipWellLocation = Field(
71
+ default_factory=PickUpTipWellLocation,
72
+ description="Relative well location at which to pick up the tip.",
73
+ )
74
+ tipPickUpParams: Optional[TipPickUpParams] = Field(
75
+ default=None, description="Specific parameters for "
76
+ )
77
+
78
+
79
+ class EvotipSealPipetteResult(DestinationPositionResult):
80
+ """Result data from the execution of a EvotipSealPipette."""
81
+
82
+ tipVolume: float = Field(
83
+ 0,
84
+ description="Maximum volume of liquid that the picked up tip can hold, in µL.",
85
+ ge=0,
86
+ )
87
+
88
+ tipLength: float = Field(
89
+ 0,
90
+ description="The length of the tip in mm.",
91
+ ge=0,
92
+ )
93
+
94
+ tipDiameter: float = Field(
95
+ 0,
96
+ description="The diameter of the tip in mm.",
97
+ ge=0,
98
+ )
99
+
100
+
101
+ _ExecuteReturn = Union[
102
+ SuccessData[EvotipSealPipetteResult],
103
+ DefinedErrorData[StallOrCollisionError],
104
+ ]
105
+
106
+
107
+ class EvotipSealPipetteImplementation(
108
+ AbstractCommandImpl[EvotipSealPipetteParams, _ExecuteReturn]
109
+ ):
110
+ """Evotip seal pipette command implementation."""
111
+
112
+ def __init__(
113
+ self,
114
+ state_view: StateView,
115
+ tip_handler: TipHandler,
116
+ model_utils: ModelUtils,
117
+ movement: MovementHandler,
118
+ hardware_api: HardwareControlAPI,
119
+ gantry_mover: GantryMover,
120
+ pipetting: PipettingHandler,
121
+ **kwargs: object,
122
+ ) -> None:
123
+ self._state_view = state_view
124
+ self._tip_handler = tip_handler
125
+ self._model_utils = model_utils
126
+ self._movement = movement
127
+ self._gantry_mover = gantry_mover
128
+ self._pipetting = pipetting
129
+ self._hardware_api = hardware_api
130
+
131
+ async def relative_pickup_tip(
132
+ self,
133
+ tip_pick_up_params: TipPickUpParams,
134
+ mount: MountType,
135
+ ) -> None:
136
+ """A relative press-fit pick up command using gantry moves."""
137
+ prep_distance = tip_pick_up_params.prepDistance
138
+ press_distance = tip_pick_up_params.pressDistance
139
+ retract_distance = -1 * (prep_distance + press_distance)
140
+
141
+ mount_axis = MotorAxis.LEFT_Z if mount == MountType.LEFT else MotorAxis.RIGHT_Z
142
+
143
+ # TODO chb, 2025-01-29): Factor out the movement constants and relocate this logic into the hardware controller
144
+ await self._gantry_mover.move_axes(
145
+ axis_map={mount_axis: prep_distance}, speed=10, relative_move=True
146
+ )
147
+
148
+ # Drive mount down for press-fit
149
+ await self._gantry_mover.move_axes(
150
+ axis_map={mount_axis: press_distance},
151
+ speed=10.0,
152
+ relative_move=True,
153
+ expect_stalls=True,
154
+ )
155
+ # retract cam : 11.05
156
+ await self._gantry_mover.move_axes(
157
+ axis_map={mount_axis: retract_distance}, speed=5.5, relative_move=True
158
+ )
159
+
160
+ async def cam_action_relative_pickup_tip(
161
+ self,
162
+ tip_pick_up_params: TipPickUpParams,
163
+ mount: MountType,
164
+ ) -> None:
165
+ """A cam action pick up command using gantry moves."""
166
+ prep_distance = tip_pick_up_params.prepDistance
167
+ press_distance = tip_pick_up_params.pressDistance
168
+ ejector_push_mm = tip_pick_up_params.ejectorPushMm
169
+ retract_distance = -1 * (prep_distance + press_distance)
170
+
171
+ mount_axis = MotorAxis.LEFT_Z if mount == MountType.LEFT else MotorAxis.RIGHT_Z
172
+
173
+ # TODO chb, 2025-01-29): Factor out the movement constants and relocate this logic into the hardware controller
174
+ await self._gantry_mover.move_axes(
175
+ axis_map={mount_axis: -6}, speed=10, relative_move=True
176
+ )
177
+
178
+ # Drive Q down 3mm at fast speed - look into the pick up tip fuinction to find slow and fast: 10.0
179
+ await self._gantry_mover.move_axes(
180
+ axis_map={MotorAxis.AXIS_96_CHANNEL_CAM: prep_distance},
181
+ speed=10.0,
182
+ relative_move=True,
183
+ )
184
+ # 2.8mm at slow speed - cam action pickup speed: 5.5
185
+ await self._gantry_mover.move_axes(
186
+ axis_map={MotorAxis.AXIS_96_CHANNEL_CAM: press_distance},
187
+ speed=5.5,
188
+ relative_move=True,
189
+ )
190
+ # retract cam : 11.05
191
+ await self._gantry_mover.move_axes(
192
+ axis_map={MotorAxis.AXIS_96_CHANNEL_CAM: retract_distance},
193
+ speed=5.5,
194
+ relative_move=True,
195
+ )
196
+
197
+ # Lower tip presence
198
+ await self._gantry_mover.move_axes(
199
+ axis_map={mount_axis: 2}, speed=10, relative_move=True
200
+ )
201
+ await self._gantry_mover.move_axes(
202
+ axis_map={MotorAxis.AXIS_96_CHANNEL_CAM: ejector_push_mm},
203
+ speed=5.5,
204
+ relative_move=True,
205
+ )
206
+ await self._gantry_mover.move_axes(
207
+ axis_map={MotorAxis.AXIS_96_CHANNEL_CAM: -1 * ejector_push_mm},
208
+ speed=5.5,
209
+ relative_move=True,
210
+ )
211
+
212
+ async def execute(
213
+ self, params: EvotipSealPipetteParams
214
+ ) -> Union[SuccessData[EvotipSealPipetteResult], _ExecuteReturn]:
215
+ """Move to and pick up a tip using the requested pipette."""
216
+ pipette_id = params.pipetteId
217
+ labware_id = params.labwareId
218
+ well_name = params.wellName
219
+
220
+ labware_definition = self._state_view.labware.get_definition(params.labwareId)
221
+ if not labware_validation.is_evotips(labware_definition.parameters.loadName):
222
+ raise UnsupportedLabwareForActionError(
223
+ f"Cannot use command: `EvotipSealPipette` with labware: {labware_definition.parameters.loadName}"
224
+ )
225
+
226
+ well_location = self._state_view.geometry.convert_pick_up_tip_well_location(
227
+ well_location=params.wellLocation
228
+ )
229
+ move_result = await move_to_well(
230
+ movement=self._movement,
231
+ model_utils=self._model_utils,
232
+ pipette_id=pipette_id,
233
+ labware_id=labware_id,
234
+ well_name=well_name,
235
+ well_location=well_location,
236
+ )
237
+ if isinstance(move_result, DefinedErrorData):
238
+ return move_result
239
+
240
+ # Aspirate to move plunger to a maximum volume position per pipette type
241
+ tip_geometry = self._state_view.geometry.get_nominal_tip_geometry(
242
+ pipette_id, labware_id, well_name
243
+ )
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(_SAFE_TOP_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=_SAFE_TOP_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