opentrons 8.4.0a3__py2.py3-none-any.whl → 8.4.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.
- opentrons/legacy_commands/commands.py +83 -2
- opentrons/legacy_commands/helpers.py +59 -1
- opentrons/legacy_commands/types.py +30 -0
- opentrons/protocol_api/core/engine/instrument.py +154 -83
- opentrons/protocol_api/core/engine/pipette_movement_conflict.py +6 -14
- opentrons/protocol_api/core/engine/transfer_components_executor.py +12 -23
- opentrons/protocol_api/core/instrument.py +4 -1
- opentrons/protocol_api/core/legacy/legacy_instrument_core.py +4 -27
- opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +4 -1
- opentrons/protocol_api/core/well.py +1 -1
- opentrons/protocol_api/instrument_context.py +116 -60
- opentrons/protocol_api/labware.py +7 -6
- opentrons/protocol_api/protocol_context.py +18 -16
- opentrons/protocol_engine/commands/__init__.py +38 -38
- opentrons/protocol_engine/commands/aspirate_while_tracking.py +0 -6
- opentrons/protocol_engine/commands/command_unions.py +33 -33
- opentrons/protocol_engine/commands/dispense_while_tracking.py +1 -6
- opentrons/protocol_engine/commands/labware_handling_common.py +6 -1
- opentrons/protocol_engine/commands/liquid_probe.py +1 -2
- opentrons/protocol_engine/commands/move_to_well.py +5 -11
- opentrons/protocol_engine/commands/{evotip_dispense.py → pressure_dispense.py} +27 -27
- opentrons/protocol_engine/commands/{evotip_seal_pipette.py → seal_pipette_to_tip.py} +32 -27
- opentrons/protocol_engine/commands/{evotip_unseal_pipette.py → unseal_pipette_from_tip.py} +22 -22
- opentrons/protocol_engine/labware_offset_standardization.py +22 -1
- opentrons/protocol_engine/resources/deck_configuration_provider.py +8 -4
- opentrons/protocol_engine/state/frustum_helpers.py +12 -4
- opentrons/protocol_engine/state/geometry.py +121 -72
- opentrons/protocol_engine/state/update_types.py +1 -1
- opentrons/protocol_engine/state/wells.py +1 -1
- opentrons/protocol_engine/types/__init__.py +6 -0
- opentrons/protocol_engine/types/well_position.py +18 -1
- opentrons/protocols/advanced_control/transfers/transfer_liquid_utils.py +1 -1
- opentrons/protocols/labware.py +23 -18
- {opentrons-8.4.0a3.dist-info → opentrons-8.4.0a4.dist-info}/METADATA +4 -4
- {opentrons-8.4.0a3.dist-info → opentrons-8.4.0a4.dist-info}/RECORD +39 -39
- {opentrons-8.4.0a3.dist-info → opentrons-8.4.0a4.dist-info}/LICENSE +0 -0
- {opentrons-8.4.0a3.dist-info → opentrons-8.4.0a4.dist-info}/WHEEL +0 -0
- {opentrons-8.4.0a3.dist-info → opentrons-8.4.0a4.dist-info}/entry_points.txt +0 -0
- {opentrons-8.4.0a3.dist-info → opentrons-8.4.0a4.dist-info}/top_level.txt +0 -0
|
@@ -378,28 +378,28 @@ from .liquid_probe import (
|
|
|
378
378
|
TryLiquidProbeCommandType,
|
|
379
379
|
)
|
|
380
380
|
|
|
381
|
-
from .
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
381
|
+
from .seal_pipette_to_tip import (
|
|
382
|
+
SealPipetteToTip,
|
|
383
|
+
SealPipetteToTipParams,
|
|
384
|
+
SealPipetteToTipCreate,
|
|
385
|
+
SealPipetteToTipResult,
|
|
386
|
+
SealPipetteToTipCommandType,
|
|
387
387
|
)
|
|
388
388
|
|
|
389
|
-
from .
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
389
|
+
from .pressure_dispense import (
|
|
390
|
+
PressureDispense,
|
|
391
|
+
PressureDispenseParams,
|
|
392
|
+
PressureDispenseCreate,
|
|
393
|
+
PressureDispenseResult,
|
|
394
|
+
PressureDispenseCommandType,
|
|
395
395
|
)
|
|
396
396
|
|
|
397
|
-
from .
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
397
|
+
from .unseal_pipette_from_tip import (
|
|
398
|
+
UnsealPipetteFromTip,
|
|
399
|
+
UnsealPipetteFromTipParams,
|
|
400
|
+
UnsealPipetteFromTipCreate,
|
|
401
|
+
UnsealPipetteFromTipResult,
|
|
402
|
+
UnsealPipetteFromTipCommandType,
|
|
403
403
|
)
|
|
404
404
|
|
|
405
405
|
Command = Annotated[
|
|
@@ -448,9 +448,9 @@ Command = Annotated[
|
|
|
448
448
|
GetNextTip,
|
|
449
449
|
LiquidProbe,
|
|
450
450
|
TryLiquidProbe,
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
451
|
+
SealPipetteToTip,
|
|
452
|
+
PressureDispense,
|
|
453
|
+
UnsealPipetteFromTip,
|
|
454
454
|
heater_shaker.WaitForTemperature,
|
|
455
455
|
heater_shaker.SetTargetTemperature,
|
|
456
456
|
heater_shaker.DeactivateHeater,
|
|
@@ -549,9 +549,9 @@ CommandParams = Union[
|
|
|
549
549
|
GetNextTipParams,
|
|
550
550
|
LiquidProbeParams,
|
|
551
551
|
TryLiquidProbeParams,
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
552
|
+
SealPipetteToTipParams,
|
|
553
|
+
PressureDispenseParams,
|
|
554
|
+
UnsealPipetteFromTipParams,
|
|
555
555
|
heater_shaker.WaitForTemperatureParams,
|
|
556
556
|
heater_shaker.SetTargetTemperatureParams,
|
|
557
557
|
heater_shaker.DeactivateHeaterParams,
|
|
@@ -648,9 +648,9 @@ CommandType = Union[
|
|
|
648
648
|
GetNextTipCommandType,
|
|
649
649
|
LiquidProbeCommandType,
|
|
650
650
|
TryLiquidProbeCommandType,
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
651
|
+
SealPipetteToTipCommandType,
|
|
652
|
+
PressureDispenseCommandType,
|
|
653
|
+
UnsealPipetteFromTipCommandType,
|
|
654
654
|
heater_shaker.WaitForTemperatureCommandType,
|
|
655
655
|
heater_shaker.SetTargetTemperatureCommandType,
|
|
656
656
|
heater_shaker.DeactivateHeaterCommandType,
|
|
@@ -748,9 +748,9 @@ CommandCreate = Annotated[
|
|
|
748
748
|
GetNextTipCreate,
|
|
749
749
|
LiquidProbeCreate,
|
|
750
750
|
TryLiquidProbeCreate,
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
751
|
+
SealPipetteToTipCreate,
|
|
752
|
+
PressureDispenseCreate,
|
|
753
|
+
UnsealPipetteFromTipCreate,
|
|
754
754
|
heater_shaker.WaitForTemperatureCreate,
|
|
755
755
|
heater_shaker.SetTargetTemperatureCreate,
|
|
756
756
|
heater_shaker.DeactivateHeaterCreate,
|
|
@@ -856,9 +856,9 @@ CommandResult = Union[
|
|
|
856
856
|
GetNextTipResult,
|
|
857
857
|
LiquidProbeResult,
|
|
858
858
|
TryLiquidProbeResult,
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
859
|
+
SealPipetteToTipResult,
|
|
860
|
+
PressureDispenseResult,
|
|
861
|
+
UnsealPipetteFromTipResult,
|
|
862
862
|
heater_shaker.WaitForTemperatureResult,
|
|
863
863
|
heater_shaker.SetTargetTemperatureResult,
|
|
864
864
|
heater_shaker.DeactivateHeaterResult,
|
|
@@ -101,11 +101,7 @@ class DispenseWhileTrackingImplementation(
|
|
|
101
101
|
|
|
102
102
|
current_location = self._state_view.pipettes.get_current_location()
|
|
103
103
|
current_position = await self._gantry_mover.get_position(params.pipetteId)
|
|
104
|
-
|
|
105
|
-
pipette_id=params.pipetteId,
|
|
106
|
-
labware_id=params.labwareId,
|
|
107
|
-
well_name=params.wellName,
|
|
108
|
-
)
|
|
104
|
+
|
|
109
105
|
state_update = StateUpdate()
|
|
110
106
|
move_result = await move_to_well(
|
|
111
107
|
movement=self._movement,
|
|
@@ -114,7 +110,6 @@ class DispenseWhileTrackingImplementation(
|
|
|
114
110
|
labware_id=params.labwareId,
|
|
115
111
|
well_name=params.wellName,
|
|
116
112
|
well_location=params.wellLocation,
|
|
117
|
-
current_well=current_well,
|
|
118
113
|
operation_volume=-params.volume,
|
|
119
114
|
)
|
|
120
115
|
state_update.append(move_result.state_update)
|
|
@@ -11,7 +11,12 @@ class LabwareHandlingResultMixin(BaseModel):
|
|
|
11
11
|
labwareId: str = Field(..., description="The id of the labware.")
|
|
12
12
|
locationSequence: LabwareLocationSequence | None = Field(
|
|
13
13
|
None,
|
|
14
|
-
description=
|
|
14
|
+
description=(
|
|
15
|
+
"The full location down to the deck on which this labware exists."
|
|
16
|
+
" The reason this can be `null` or omitted is just backwards compatibility,"
|
|
17
|
+
" for older runs and analyses. This should always be present"
|
|
18
|
+
" for new runs and analyses, even for labware whose location is off-deck."
|
|
19
|
+
),
|
|
15
20
|
)
|
|
16
21
|
|
|
17
22
|
|
|
@@ -22,8 +22,7 @@ from opentrons_shared_data.errors.exceptions import (
|
|
|
22
22
|
PipetteOverpressureError,
|
|
23
23
|
)
|
|
24
24
|
|
|
25
|
-
from ..types import DeckPoint
|
|
26
|
-
from ..types.liquid_level_detection import LiquidTrackingType
|
|
25
|
+
from ..types import DeckPoint, LiquidTrackingType
|
|
27
26
|
from .pipetting_common import (
|
|
28
27
|
LiquidNotFoundError,
|
|
29
28
|
PipetteIdMixin,
|
|
@@ -8,7 +8,7 @@ from .pipetting_common import (
|
|
|
8
8
|
PipetteIdMixin,
|
|
9
9
|
)
|
|
10
10
|
from .movement_common import (
|
|
11
|
-
|
|
11
|
+
LiquidHandlingWellLocationMixin,
|
|
12
12
|
MovementMixin,
|
|
13
13
|
DestinationPositionResult,
|
|
14
14
|
StallOrCollisionError,
|
|
@@ -21,7 +21,6 @@ from .command import (
|
|
|
21
21
|
SuccessData,
|
|
22
22
|
DefinedErrorData,
|
|
23
23
|
)
|
|
24
|
-
from ..errors import LabwareIsTipRackError
|
|
25
24
|
|
|
26
25
|
if TYPE_CHECKING:
|
|
27
26
|
from ..execution import MovementHandler
|
|
@@ -31,7 +30,7 @@ if TYPE_CHECKING:
|
|
|
31
30
|
MoveToWellCommandType = Literal["moveToWell"]
|
|
32
31
|
|
|
33
32
|
|
|
34
|
-
class MoveToWellParams(PipetteIdMixin,
|
|
33
|
+
class MoveToWellParams(PipetteIdMixin, LiquidHandlingWellLocationMixin, MovementMixin):
|
|
35
34
|
"""Payload required to move a pipette to a specific well."""
|
|
36
35
|
|
|
37
36
|
pass
|
|
@@ -70,14 +69,9 @@ class MoveToWellImplementation(
|
|
|
70
69
|
labware_id = params.labwareId
|
|
71
70
|
well_name = params.wellName
|
|
72
71
|
well_location = params.wellLocation
|
|
73
|
-
|
|
74
|
-
if
|
|
75
|
-
|
|
76
|
-
and well_location.volumeOffset
|
|
77
|
-
):
|
|
78
|
-
raise LabwareIsTipRackError(
|
|
79
|
-
"Cannot specify a WellLocation with a volumeOffset with movement to a tip rack"
|
|
80
|
-
)
|
|
72
|
+
# TODO(cm): implement move_to_well with meniscus + volume offset
|
|
73
|
+
if well_location.volumeOffset and well_location.volumeOffset != 0:
|
|
74
|
+
raise ValueError("volume offset not supported with MoveToWell")
|
|
81
75
|
|
|
82
76
|
move_result = await move_to_well(
|
|
83
77
|
model_utils=self._model_utils,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Pressure Dispense-in-place command request, result, and implementation models."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
from typing import TYPE_CHECKING, Optional, Type, Union
|
|
@@ -35,33 +35,33 @@ if TYPE_CHECKING:
|
|
|
35
35
|
from ..state.state import StateView
|
|
36
36
|
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
PressureDispenseCommandType = Literal["pressureDispense"]
|
|
39
39
|
|
|
40
40
|
|
|
41
|
-
class
|
|
41
|
+
class PressureDispenseParams(
|
|
42
42
|
PipetteIdMixin, DispenseVolumeMixin, FlowRateMixin, LiquidHandlingWellLocationMixin
|
|
43
43
|
):
|
|
44
|
-
"""Payload required to dispense in place."""
|
|
44
|
+
"""Payload required to pressure dispense in place."""
|
|
45
45
|
|
|
46
46
|
pass
|
|
47
47
|
|
|
48
48
|
|
|
49
|
-
class
|
|
50
|
-
"""Result data from the execution of a
|
|
49
|
+
class PressureDispenseResult(BaseLiquidHandlingResult):
|
|
50
|
+
"""Result data from the execution of a PressureDispense command."""
|
|
51
51
|
|
|
52
52
|
pass
|
|
53
53
|
|
|
54
54
|
|
|
55
55
|
_ExecuteReturn = Union[
|
|
56
|
-
SuccessData[
|
|
56
|
+
SuccessData[PressureDispenseResult],
|
|
57
57
|
DefinedErrorData[StallOrCollisionError],
|
|
58
58
|
]
|
|
59
59
|
|
|
60
60
|
|
|
61
|
-
class
|
|
62
|
-
AbstractCommandImpl[
|
|
61
|
+
class PressureDispenseImplementation(
|
|
62
|
+
AbstractCommandImpl[PressureDispenseParams, _ExecuteReturn]
|
|
63
63
|
):
|
|
64
|
-
"""
|
|
64
|
+
"""Pressure dispense command implementation."""
|
|
65
65
|
|
|
66
66
|
def __init__(
|
|
67
67
|
self,
|
|
@@ -78,8 +78,8 @@ class EvotipDispenseImplementation(
|
|
|
78
78
|
self._model_utils = model_utils
|
|
79
79
|
self._movement = movement
|
|
80
80
|
|
|
81
|
-
async def execute(self, params:
|
|
82
|
-
"""Move to and dispense to the requested well."""
|
|
81
|
+
async def execute(self, params: PressureDispenseParams) -> _ExecuteReturn:
|
|
82
|
+
"""Move to and pressure dispense to the requested well."""
|
|
83
83
|
well_location = params.wellLocation
|
|
84
84
|
labware_id = params.labwareId
|
|
85
85
|
well_name = params.wellName
|
|
@@ -121,35 +121,35 @@ class EvotipDispenseImplementation(
|
|
|
121
121
|
message="Overpressure Error during Resin Tip Dispense Command."
|
|
122
122
|
)
|
|
123
123
|
return SuccessData(
|
|
124
|
-
public=
|
|
124
|
+
public=PressureDispenseResult(volume=result.public.volume),
|
|
125
125
|
state_update=StateUpdate.reduce(
|
|
126
126
|
move_result.state_update, result.state_update
|
|
127
127
|
),
|
|
128
128
|
)
|
|
129
129
|
|
|
130
130
|
|
|
131
|
-
class
|
|
131
|
+
class PressureDispense(
|
|
132
132
|
BaseCommand[
|
|
133
|
-
|
|
134
|
-
|
|
133
|
+
PressureDispenseParams,
|
|
134
|
+
PressureDispenseResult,
|
|
135
135
|
StallOrCollisionError,
|
|
136
136
|
]
|
|
137
137
|
):
|
|
138
|
-
"""
|
|
138
|
+
"""PressureDispense command model."""
|
|
139
139
|
|
|
140
|
-
commandType:
|
|
141
|
-
params:
|
|
142
|
-
result: Optional[
|
|
140
|
+
commandType: PressureDispenseCommandType = "pressureDispense"
|
|
141
|
+
params: PressureDispenseParams
|
|
142
|
+
result: Optional[PressureDispenseResult] = None
|
|
143
143
|
|
|
144
144
|
_ImplementationCls: Type[
|
|
145
|
-
|
|
146
|
-
] =
|
|
145
|
+
PressureDispenseImplementation
|
|
146
|
+
] = PressureDispenseImplementation
|
|
147
147
|
|
|
148
148
|
|
|
149
|
-
class
|
|
150
|
-
"""
|
|
149
|
+
class PressureDispenseCreate(BaseCommandCreate[PressureDispenseParams]):
|
|
150
|
+
"""PressureDispense command request model."""
|
|
151
151
|
|
|
152
|
-
commandType:
|
|
153
|
-
params:
|
|
152
|
+
commandType: PressureDispenseCommandType = "pressureDispense"
|
|
153
|
+
params: PressureDispenseParams
|
|
154
154
|
|
|
155
|
-
_CommandCls: Type[
|
|
155
|
+
_CommandCls: Type[PressureDispense] = PressureDispense
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Seal
|
|
1
|
+
"""Seal tips to pipette command request, result, and implementation models."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
from pydantic import Field, BaseModel
|
|
@@ -7,7 +7,7 @@ from opentrons.types import MountType
|
|
|
7
7
|
from opentrons.protocol_engine.types import MotorAxis
|
|
8
8
|
from typing_extensions import Literal
|
|
9
9
|
|
|
10
|
-
from ..resources import ModelUtils
|
|
10
|
+
from ..resources import ModelUtils, ensure_ot3_hardware
|
|
11
11
|
from ..types import PickUpTipWellLocation, FluidKind, AspiratedFluid
|
|
12
12
|
from .pipetting_common import (
|
|
13
13
|
PipetteIdMixin,
|
|
@@ -39,7 +39,7 @@ if TYPE_CHECKING:
|
|
|
39
39
|
)
|
|
40
40
|
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
SealPipetteToTipCommandType = Literal["sealPipetteToTip"]
|
|
43
43
|
_CAM_PREP_DISTANCE_DEFAULT = 8.25
|
|
44
44
|
_CAM_PRESS_DISTANCE_DEFAULT = 3.5
|
|
45
45
|
_CAM_EJECTOR_PUSH_MM_DEFAULT = 7.0
|
|
@@ -64,7 +64,7 @@ class TipPickUpParams(BaseModel):
|
|
|
64
64
|
)
|
|
65
65
|
|
|
66
66
|
|
|
67
|
-
class
|
|
67
|
+
class SealPipetteToTipParams(PipetteIdMixin):
|
|
68
68
|
"""Payload needed to seal resin tips to a pipette."""
|
|
69
69
|
|
|
70
70
|
labwareId: str = Field(..., description="Identifier of labware to use.")
|
|
@@ -78,8 +78,8 @@ class EvotipSealPipetteParams(PipetteIdMixin):
|
|
|
78
78
|
)
|
|
79
79
|
|
|
80
80
|
|
|
81
|
-
class
|
|
82
|
-
"""Result data from the execution of a
|
|
81
|
+
class SealPipetteToTipResult(DestinationPositionResult):
|
|
82
|
+
"""Result data from the execution of a SealPipetteToTip."""
|
|
83
83
|
|
|
84
84
|
tipVolume: float = Field(
|
|
85
85
|
0,
|
|
@@ -101,15 +101,15 @@ class EvotipSealPipetteResult(DestinationPositionResult):
|
|
|
101
101
|
|
|
102
102
|
|
|
103
103
|
_ExecuteReturn = Union[
|
|
104
|
-
SuccessData[
|
|
104
|
+
SuccessData[SealPipetteToTipResult],
|
|
105
105
|
DefinedErrorData[StallOrCollisionError],
|
|
106
106
|
]
|
|
107
107
|
|
|
108
108
|
|
|
109
|
-
class
|
|
110
|
-
AbstractCommandImpl[
|
|
109
|
+
class SealPipetteToTipImplementation(
|
|
110
|
+
AbstractCommandImpl[SealPipetteToTipParams, _ExecuteReturn]
|
|
111
111
|
):
|
|
112
|
-
"""
|
|
112
|
+
"""Seal pipette command implementation."""
|
|
113
113
|
|
|
114
114
|
def __init__(
|
|
115
115
|
self,
|
|
@@ -159,6 +159,11 @@ class EvotipSealPipetteImplementation(
|
|
|
159
159
|
axis_map={mount_axis: retract_distance}, speed=5.5, relative_move=True
|
|
160
160
|
)
|
|
161
161
|
|
|
162
|
+
ot3_hardware_api = ensure_ot3_hardware(self._hardware_api)
|
|
163
|
+
await ot3_hardware_api.update_axis_position_estimations(
|
|
164
|
+
self._gantry_mover.motor_axes_to_present_hardware_axes([mount_axis])
|
|
165
|
+
)
|
|
166
|
+
|
|
162
167
|
async def cam_action_relative_pickup_tip(
|
|
163
168
|
self,
|
|
164
169
|
tip_pick_up_params: TipPickUpParams,
|
|
@@ -212,8 +217,8 @@ class EvotipSealPipetteImplementation(
|
|
|
212
217
|
)
|
|
213
218
|
|
|
214
219
|
async def execute(
|
|
215
|
-
self, params:
|
|
216
|
-
) -> Union[SuccessData[
|
|
220
|
+
self, params: SealPipetteToTipParams
|
|
221
|
+
) -> Union[SuccessData[SealPipetteToTipResult], _ExecuteReturn]:
|
|
217
222
|
"""Move to and pick up a tip using the requested pipette."""
|
|
218
223
|
pipette_id = params.pipetteId
|
|
219
224
|
labware_id = params.labwareId
|
|
@@ -289,7 +294,7 @@ class EvotipSealPipetteImplementation(
|
|
|
289
294
|
fluid=AspiratedFluid(kind=FluidKind.LIQUID, volume=_SAFE_TOP_VOLUME),
|
|
290
295
|
)
|
|
291
296
|
return SuccessData(
|
|
292
|
-
public=
|
|
297
|
+
public=SealPipetteToTipResult(
|
|
293
298
|
tipVolume=tip_geometry.volume,
|
|
294
299
|
tipLength=tip_geometry.length,
|
|
295
300
|
tipDiameter=tip_geometry.diameter,
|
|
@@ -299,28 +304,28 @@ class EvotipSealPipetteImplementation(
|
|
|
299
304
|
)
|
|
300
305
|
|
|
301
306
|
|
|
302
|
-
class
|
|
307
|
+
class SealPipetteToTip(
|
|
303
308
|
BaseCommand[
|
|
304
|
-
|
|
305
|
-
|
|
309
|
+
SealPipetteToTipParams,
|
|
310
|
+
SealPipetteToTipResult,
|
|
306
311
|
StallOrCollisionError,
|
|
307
312
|
]
|
|
308
313
|
):
|
|
309
|
-
"""Seal
|
|
314
|
+
"""Seal tip command model."""
|
|
310
315
|
|
|
311
|
-
commandType:
|
|
312
|
-
params:
|
|
313
|
-
result: Optional[
|
|
316
|
+
commandType: SealPipetteToTipCommandType = "sealPipetteToTip"
|
|
317
|
+
params: SealPipetteToTipParams
|
|
318
|
+
result: Optional[SealPipetteToTipResult] = None
|
|
314
319
|
|
|
315
320
|
_ImplementationCls: Type[
|
|
316
|
-
|
|
317
|
-
] =
|
|
321
|
+
SealPipetteToTipImplementation
|
|
322
|
+
] = SealPipetteToTipImplementation
|
|
318
323
|
|
|
319
324
|
|
|
320
|
-
class
|
|
321
|
-
"""Seal
|
|
325
|
+
class SealPipetteToTipCreate(BaseCommandCreate[SealPipetteToTipParams]):
|
|
326
|
+
"""Seal tip command creation request model."""
|
|
322
327
|
|
|
323
|
-
commandType:
|
|
324
|
-
params:
|
|
328
|
+
commandType: SealPipetteToTipCommandType = "sealPipetteToTip"
|
|
329
|
+
params: SealPipetteToTipParams
|
|
325
330
|
|
|
326
|
-
_CommandCls: Type[
|
|
331
|
+
_CommandCls: Type[SealPipetteToTip] = SealPipetteToTip
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Unseal
|
|
1
|
+
"""Unseal tip from pipette command request, result, and implementation models."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
@@ -32,10 +32,10 @@ if TYPE_CHECKING:
|
|
|
32
32
|
from ..execution import MovementHandler, TipHandler, GantryMover
|
|
33
33
|
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
UnsealPipetteFromTipCommandType = Literal["unsealPipetteFromTip"]
|
|
36
36
|
|
|
37
37
|
|
|
38
|
-
class
|
|
38
|
+
class UnsealPipetteFromTipParams(PipetteIdMixin):
|
|
39
39
|
"""Payload required to drop a tip in a specific well."""
|
|
40
40
|
|
|
41
41
|
labwareId: str = Field(..., description="Identifier of labware to use.")
|
|
@@ -46,19 +46,19 @@ class EvotipUnsealPipetteParams(PipetteIdMixin):
|
|
|
46
46
|
)
|
|
47
47
|
|
|
48
48
|
|
|
49
|
-
class
|
|
49
|
+
class UnsealPipetteFromTipResult(DestinationPositionResult):
|
|
50
50
|
"""Result data from the execution of a DropTip command."""
|
|
51
51
|
|
|
52
52
|
pass
|
|
53
53
|
|
|
54
54
|
|
|
55
55
|
_ExecuteReturn = (
|
|
56
|
-
SuccessData[
|
|
56
|
+
SuccessData[UnsealPipetteFromTipResult] | DefinedErrorData[StallOrCollisionError]
|
|
57
57
|
)
|
|
58
58
|
|
|
59
59
|
|
|
60
|
-
class
|
|
61
|
-
AbstractCommandImpl[
|
|
60
|
+
class UnsealPipetteFromTipImplementation(
|
|
61
|
+
AbstractCommandImpl[UnsealPipetteFromTipParams, _ExecuteReturn]
|
|
62
62
|
):
|
|
63
63
|
"""Drop tip command implementation."""
|
|
64
64
|
|
|
@@ -77,7 +77,7 @@ class EvotipUnsealPipetteImplementation(
|
|
|
77
77
|
self._model_utils = model_utils
|
|
78
78
|
self._gantry_mover = gantry_mover
|
|
79
79
|
|
|
80
|
-
async def execute(self, params:
|
|
80
|
+
async def execute(self, params: UnsealPipetteFromTipParams) -> _ExecuteReturn:
|
|
81
81
|
"""Move to and drop a tip using the requested pipette."""
|
|
82
82
|
pipette_id = params.pipetteId
|
|
83
83
|
labware_id = params.labwareId
|
|
@@ -122,33 +122,33 @@ class EvotipUnsealPipetteImplementation(
|
|
|
122
122
|
)
|
|
123
123
|
|
|
124
124
|
return SuccessData(
|
|
125
|
-
public=
|
|
125
|
+
public=UnsealPipetteFromTipResult(position=move_result.public.position),
|
|
126
126
|
state_update=move_result.state_update.set_fluid_unknown(
|
|
127
127
|
pipette_id=pipette_id
|
|
128
128
|
).update_pipette_tip_state(pipette_id=params.pipetteId, tip_geometry=None),
|
|
129
129
|
)
|
|
130
130
|
|
|
131
131
|
|
|
132
|
-
class
|
|
132
|
+
class UnsealPipetteFromTip(
|
|
133
133
|
BaseCommand[
|
|
134
|
-
|
|
134
|
+
UnsealPipetteFromTipParams, UnsealPipetteFromTipResult, StallOrCollisionError
|
|
135
135
|
]
|
|
136
136
|
):
|
|
137
|
-
"""
|
|
137
|
+
"""Unseal pipette command model."""
|
|
138
138
|
|
|
139
|
-
commandType:
|
|
140
|
-
params:
|
|
141
|
-
result: Optional[
|
|
139
|
+
commandType: UnsealPipetteFromTipCommandType = "unsealPipetteFromTip"
|
|
140
|
+
params: UnsealPipetteFromTipParams
|
|
141
|
+
result: Optional[UnsealPipetteFromTipResult] = None
|
|
142
142
|
|
|
143
143
|
_ImplementationCls: Type[
|
|
144
|
-
|
|
145
|
-
] =
|
|
144
|
+
UnsealPipetteFromTipImplementation
|
|
145
|
+
] = UnsealPipetteFromTipImplementation
|
|
146
146
|
|
|
147
147
|
|
|
148
|
-
class
|
|
149
|
-
"""
|
|
148
|
+
class UnsealPipetteFromTipCreate(BaseCommandCreate[UnsealPipetteFromTipParams]):
|
|
149
|
+
"""Unseal pipette command creation request model."""
|
|
150
150
|
|
|
151
|
-
commandType:
|
|
152
|
-
params:
|
|
151
|
+
commandType: UnsealPipetteFromTipCommandType = "unsealPipetteFromTip"
|
|
152
|
+
params: UnsealPipetteFromTipParams
|
|
153
153
|
|
|
154
|
-
_CommandCls: Type[
|
|
154
|
+
_CommandCls: Type[UnsealPipetteFromTip] = UnsealPipetteFromTip
|
|
@@ -53,7 +53,28 @@ def legacy_offset_location_to_offset_location_sequence(
|
|
|
53
53
|
cutout_id = deck_configuration_provider.get_cutout_id_by_deck_slot_name(
|
|
54
54
|
location.slotName
|
|
55
55
|
)
|
|
56
|
-
|
|
56
|
+
|
|
57
|
+
# Given a module model, try to figure out the equivalent cutout fixture.
|
|
58
|
+
#
|
|
59
|
+
# The Thermocycler is special. A single Thermocycler is represented in a deck
|
|
60
|
+
# configuration as two separate cutout fixtures, because it spans two separate
|
|
61
|
+
# cutouts. This makes it the only module whose module model string does not map
|
|
62
|
+
# 1:1 with a cutout fixture ID string.
|
|
63
|
+
#
|
|
64
|
+
# TODO(mm, 2025-04-11): This is fragile, and the failure mode when it does the
|
|
65
|
+
# wrong thing can mean labware offsets don't apply, which is pretty bad. We
|
|
66
|
+
# either need a more explicit module<->cutout-fixture mapping, or we need to
|
|
67
|
+
# avoid this mapping entirely.
|
|
68
|
+
if (
|
|
69
|
+
# Check for v2 specifically because v1 is OT-2-only and OT-2s don't have
|
|
70
|
+
# modules in their deck definitions; and v3 does not exist at the time of writing.
|
|
71
|
+
location.moduleModel
|
|
72
|
+
== ModuleModel.THERMOCYCLER_MODULE_V2
|
|
73
|
+
):
|
|
74
|
+
possible_cutout_fixture_id = "thermocyclerModuleV2Front"
|
|
75
|
+
else:
|
|
76
|
+
possible_cutout_fixture_id = location.moduleModel.value
|
|
77
|
+
|
|
57
78
|
try:
|
|
58
79
|
addressable_area = deck_configuration_provider.get_labware_hosting_addressable_area_name_for_cutout_and_cutout_fixture(
|
|
59
80
|
cutout_id, possible_cutout_fixture_id, deck_definition
|
|
@@ -144,16 +144,20 @@ def get_deck_slot_for_cutout_id(cutout_id: str) -> DeckSlotName:
|
|
|
144
144
|
"""Get the corresponding deck slot for an addressable area."""
|
|
145
145
|
try:
|
|
146
146
|
return CUTOUT_TO_DECK_SLOT_MAP[cutout_id]
|
|
147
|
-
except KeyError:
|
|
148
|
-
raise CutoutDoesNotExistError(
|
|
147
|
+
except KeyError as e:
|
|
148
|
+
raise CutoutDoesNotExistError(
|
|
149
|
+
f"Could not find data for cutout {cutout_id}"
|
|
150
|
+
) from e
|
|
149
151
|
|
|
150
152
|
|
|
151
153
|
def get_cutout_id_by_deck_slot_name(slot_name: DeckSlotName) -> str:
|
|
152
154
|
"""Get the Cutout ID of a given Deck Slot by Deck Slot Name."""
|
|
153
155
|
try:
|
|
154
156
|
return DECK_SLOT_TO_CUTOUT_MAP[slot_name]
|
|
155
|
-
except KeyError:
|
|
156
|
-
raise SlotDoesNotExistError(
|
|
157
|
+
except KeyError as e:
|
|
158
|
+
raise SlotDoesNotExistError(
|
|
159
|
+
f"Could not find data for slot {slot_name.value}"
|
|
160
|
+
) from e
|
|
157
161
|
|
|
158
162
|
|
|
159
163
|
def get_labware_hosting_addressable_area_name_for_cutout_and_cutout_fixture(
|
|
@@ -353,8 +353,9 @@ def _find_volume_in_partial_frustum(
|
|
|
353
353
|
section_height=section_height,
|
|
354
354
|
)
|
|
355
355
|
# if we've looked through all sections and can't find the target volume, raise an error
|
|
356
|
+
# this code should never be reached- an error should be raised by find_volume_at_well_height
|
|
356
357
|
raise InvalidLiquidHeightFound(
|
|
357
|
-
f"
|
|
358
|
+
f"Target height {target_height} mm exceeds the well height."
|
|
358
359
|
)
|
|
359
360
|
|
|
360
361
|
|
|
@@ -369,7 +370,9 @@ def find_volume_at_well_height(
|
|
|
369
370
|
volumetric_capacity = get_well_volumetric_capacity(well_geometry)
|
|
370
371
|
max_height = volumetric_capacity[-1][0]
|
|
371
372
|
if target_height < 0 or target_height > max_height:
|
|
372
|
-
raise InvalidLiquidHeightFound(
|
|
373
|
+
raise InvalidLiquidHeightFound(
|
|
374
|
+
"Invalid target height {target_height} mm; max well height is {max_height} mm."
|
|
375
|
+
)
|
|
373
376
|
# volumes in volumetric_capacity are relative to each frustum,
|
|
374
377
|
# so we have to find the volume of all the full sections enclosed
|
|
375
378
|
# beneath the target height
|
|
@@ -417,9 +420,12 @@ def _find_height_in_partial_frustum(
|
|
|
417
420
|
# viewed section
|
|
418
421
|
bottom_section_volume += section_volume
|
|
419
422
|
|
|
423
|
+
# if we finish looping through the whole well, bottom_section will be the well's volume
|
|
424
|
+
total_well_volume = bottom_section_volume
|
|
420
425
|
# if we've looked through all sections and can't find the target volume, raise an error
|
|
426
|
+
# also this code should never be reached bc an error should be raised by find_height_at_well_volume
|
|
421
427
|
raise InvalidLiquidHeightFound(
|
|
422
|
-
f"
|
|
428
|
+
f"Target volume {target_volume} uL exceeds the well volume {total_well_volume} uL."
|
|
423
429
|
)
|
|
424
430
|
|
|
425
431
|
|
|
@@ -439,7 +445,9 @@ def find_height_at_well_volume(
|
|
|
439
445
|
|
|
440
446
|
if raise_error_if_result_invalid:
|
|
441
447
|
if target_volume < 0 or target_volume > max_volume:
|
|
442
|
-
raise InvalidLiquidHeightFound(
|
|
448
|
+
raise InvalidLiquidHeightFound(
|
|
449
|
+
f"Invalid target volume {target_volume} uL; max volume is {max_volume} uL"
|
|
450
|
+
)
|
|
443
451
|
|
|
444
452
|
sorted_well = sorted(well_geometry.sections, key=lambda section: section.topHeight)
|
|
445
453
|
# find the section the target volume is in and compute the height
|