opentrons 8.3.0a1__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.
- opentrons/hardware_control/api.py +5 -1
- opentrons/hardware_control/ot3api.py +18 -8
- opentrons/hardware_control/protocols/liquid_handler.py +4 -1
- opentrons/hardware_control/protocols/motion_controller.py +1 -0
- opentrons/legacy_commands/commands.py +37 -0
- opentrons/legacy_commands/types.py +39 -0
- opentrons/protocol_api/core/engine/instrument.py +109 -0
- opentrons/protocol_api/core/instrument.py +27 -0
- opentrons/protocol_api/core/legacy/legacy_instrument_core.py +50 -0
- opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +24 -0
- opentrons/protocol_api/instrument_context.py +141 -0
- opentrons/protocol_engine/commands/__init__.py +40 -0
- opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +1 -1
- opentrons/protocol_engine/commands/absorbance_reader/initialize.py +1 -1
- opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +1 -1
- opentrons/protocol_engine/commands/absorbance_reader/read.py +4 -1
- opentrons/protocol_engine/commands/air_gap_in_place.py +1 -1
- opentrons/protocol_engine/commands/command_unions.py +39 -0
- opentrons/protocol_engine/commands/evotip_dispense.py +156 -0
- opentrons/protocol_engine/commands/evotip_seal_pipette.py +331 -0
- opentrons/protocol_engine/commands/evotip_unseal_pipette.py +160 -0
- opentrons/protocol_engine/commands/get_next_tip.py +1 -1
- opentrons/protocol_engine/commands/liquid_probe.py +63 -12
- opentrons/protocol_engine/commands/load_labware.py +5 -0
- opentrons/protocol_engine/commands/load_lid.py +1 -1
- opentrons/protocol_engine/commands/load_lid_stack.py +1 -1
- opentrons/protocol_engine/commands/load_liquid_class.py +1 -1
- opentrons/protocol_engine/commands/move_labware.py +9 -0
- opentrons/protocol_engine/commands/robot/close_gripper_jaw.py +1 -1
- opentrons/protocol_engine/commands/robot/move_axes_relative.py +1 -1
- opentrons/protocol_engine/commands/robot/move_axes_to.py +1 -1
- opentrons/protocol_engine/commands/robot/move_to.py +1 -1
- opentrons/protocol_engine/commands/robot/open_gripper_jaw.py +1 -1
- opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py +1 -1
- opentrons/protocol_engine/commands/unsafe/unsafe_blow_out_in_place.py +1 -1
- opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +1 -1
- opentrons/protocol_engine/commands/unsafe/unsafe_engage_axes.py +1 -1
- opentrons/protocol_engine/commands/unsafe/unsafe_place_labware.py +1 -1
- opentrons/protocol_engine/commands/unsafe/unsafe_ungrip_labware.py +1 -1
- opentrons/protocol_engine/commands/unsafe/update_position_estimators.py +1 -1
- opentrons/protocol_engine/errors/__init__.py +4 -0
- opentrons/protocol_engine/errors/exceptions.py +26 -0
- opentrons/protocol_engine/execution/gantry_mover.py +5 -0
- opentrons/protocol_engine/execution/hardware_stopper.py +7 -7
- opentrons/protocol_engine/execution/tip_handler.py +30 -9
- opentrons/protocol_engine/resources/labware_validation.py +13 -0
- opentrons/protocol_engine/state/commands.py +6 -2
- opentrons/protocol_engine/state/frustum_helpers.py +13 -44
- opentrons/protocol_engine/state/labware.py +13 -1
- opentrons/protocol_engine/state/pipettes.py +5 -0
- opentrons/protocols/labware.py +37 -8
- opentrons/util/entrypoint_util.py +2 -5
- {opentrons-8.3.0a1.dist-info → opentrons-8.3.0a4.dist-info}/METADATA +4 -4
- {opentrons-8.3.0a1.dist-info → opentrons-8.3.0a4.dist-info}/RECORD +58 -55
- {opentrons-8.3.0a1.dist-info → opentrons-8.3.0a4.dist-info}/LICENSE +0 -0
- {opentrons-8.3.0a1.dist-info → opentrons-8.3.0a4.dist-info}/WHEEL +0 -0
- {opentrons-8.3.0a1.dist-info → opentrons-8.3.0a4.dist-info}/entry_points.txt +0 -0
- {opentrons-8.3.0a1.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
|