opentrons 8.4.0a13__py2.py3-none-any.whl → 8.5.0a1__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of opentrons might be problematic. Click here for more details.
- opentrons/config/defaults_ot3.py +1 -1
- opentrons/legacy_commands/commands.py +16 -4
- opentrons/legacy_commands/robot_commands.py +51 -0
- opentrons/legacy_commands/types.py +91 -2
- opentrons/protocol_api/_liquid.py +60 -15
- opentrons/protocol_api/_liquid_properties.py +137 -90
- opentrons/protocol_api/_transfer_liquid_validation.py +10 -6
- opentrons/protocol_api/core/engine/instrument.py +172 -75
- opentrons/protocol_api/core/engine/protocol.py +13 -14
- opentrons/protocol_api/core/engine/robot.py +2 -2
- opentrons/protocol_api/core/engine/transfer_components_executor.py +157 -126
- opentrons/protocol_api/core/engine/well.py +16 -0
- opentrons/protocol_api/core/instrument.py +2 -2
- opentrons/protocol_api/core/legacy/legacy_instrument_core.py +2 -2
- opentrons/protocol_api/core/legacy/legacy_protocol_core.py +1 -1
- opentrons/protocol_api/core/legacy/legacy_well_core.py +8 -0
- opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +2 -2
- opentrons/protocol_api/core/protocol.py +2 -2
- opentrons/protocol_api/core/well.py +8 -0
- opentrons/protocol_api/instrument_context.py +377 -86
- opentrons/protocol_api/labware.py +10 -0
- opentrons/protocol_api/protocol_context.py +79 -4
- opentrons/protocol_api/robot_context.py +48 -6
- opentrons/protocol_api/validation.py +15 -8
- opentrons/protocol_engine/commands/command_unions.py +10 -10
- opentrons/protocol_engine/commands/generate_command_schema.py +1 -1
- opentrons/protocol_engine/commands/get_next_tip.py +2 -2
- opentrons/protocol_engine/commands/pick_up_tip.py +9 -3
- opentrons/protocol_engine/commands/robot/__init__.py +20 -20
- opentrons/protocol_engine/commands/robot/close_gripper_jaw.py +34 -24
- opentrons/protocol_engine/commands/robot/open_gripper_jaw.py +29 -20
- opentrons/protocol_engine/commands/seal_pipette_to_tip.py +1 -1
- opentrons/protocol_engine/commands/unsafe/__init__.py +17 -1
- opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +1 -2
- opentrons/protocol_engine/execution/labware_movement.py +9 -2
- opentrons/protocol_engine/execution/movement.py +12 -9
- opentrons/protocol_engine/execution/queue_worker.py +8 -1
- opentrons/protocol_engine/execution/thermocycler_movement_flagger.py +52 -19
- opentrons/protocol_engine/state/_well_math.py +2 -2
- opentrons/protocol_engine/state/commands.py +14 -28
- opentrons/protocol_engine/state/frustum_helpers.py +11 -7
- opentrons/protocol_engine/state/modules.py +1 -1
- opentrons/protocol_engine/state/pipettes.py +8 -0
- opentrons/protocol_engine/state/tips.py +46 -83
- opentrons/protocol_engine/state/update_types.py +8 -23
- opentrons/protocol_runner/legacy_command_mapper.py +11 -4
- opentrons/protocol_runner/run_orchestrator.py +1 -1
- opentrons/protocols/advanced_control/transfers/common.py +54 -11
- opentrons/protocols/advanced_control/transfers/transfer_liquid_utils.py +1 -1
- opentrons/protocols/api_support/definitions.py +1 -1
- opentrons/types.py +6 -6
- {opentrons-8.4.0a13.dist-info → opentrons-8.5.0a1.dist-info}/METADATA +4 -4
- {opentrons-8.4.0a13.dist-info → opentrons-8.5.0a1.dist-info}/RECORD +57 -56
- {opentrons-8.4.0a13.dist-info → opentrons-8.5.0a1.dist-info}/LICENSE +0 -0
- {opentrons-8.4.0a13.dist-info → opentrons-8.5.0a1.dist-info}/WHEEL +0 -0
- {opentrons-8.4.0a13.dist-info → opentrons-8.5.0a1.dist-info}/entry_points.txt +0 -0
- {opentrons-8.4.0a13.dist-info → opentrons-8.5.0a1.dist-info}/top_level.txt +0 -0
opentrons/config/defaults_ot3.py
CHANGED
|
@@ -158,7 +158,7 @@ DEFAULT_HOLD_CURRENT: Final[ByGantryLoad[Dict[OT3AxisKind, float]]] = ByGantryLo
|
|
|
158
158
|
OT3AxisKind.X: 0.5,
|
|
159
159
|
OT3AxisKind.Y: 0.5,
|
|
160
160
|
OT3AxisKind.Z: 0.5,
|
|
161
|
-
OT3AxisKind.P: 0.
|
|
161
|
+
OT3AxisKind.P: 0.8,
|
|
162
162
|
OT3AxisKind.Z_G: 0.2,
|
|
163
163
|
OT3AxisKind.Q: 0.3,
|
|
164
164
|
},
|
|
@@ -328,12 +328,18 @@ def transfer_with_liquid_class(
|
|
|
328
328
|
liquid_class: LiquidClass,
|
|
329
329
|
volume: float,
|
|
330
330
|
source: Union[Well, Sequence[Well], Sequence[Sequence[Well]]],
|
|
331
|
-
destination: Union[
|
|
331
|
+
destination: Union[
|
|
332
|
+
Well, Sequence[Well], Sequence[Sequence[Well]], TrashBin, WasteChute
|
|
333
|
+
],
|
|
332
334
|
) -> command_types.TransferWithLiquidClassCommand:
|
|
335
|
+
if isinstance(destination, (TrashBin, WasteChute)):
|
|
336
|
+
destination_text = stringify_disposal_location(destination)
|
|
337
|
+
else:
|
|
338
|
+
destination_text = stringify_well_list(destination)
|
|
333
339
|
text = (
|
|
334
340
|
"Transferring "
|
|
335
341
|
+ f"{volume} uL of {liquid_class.display_name} liquid class from "
|
|
336
|
-
+ f"{stringify_well_list(source)} to {
|
|
342
|
+
+ f"{stringify_well_list(source)} to {destination_text}"
|
|
337
343
|
)
|
|
338
344
|
return {
|
|
339
345
|
"name": command_types.TRANSFER_WITH_LIQUID_CLASS,
|
|
@@ -378,12 +384,18 @@ def consolidate_with_liquid_class(
|
|
|
378
384
|
liquid_class: LiquidClass,
|
|
379
385
|
volume: float,
|
|
380
386
|
source: Union[Well, Sequence[Well], Sequence[Sequence[Well]]],
|
|
381
|
-
destination: Union[
|
|
387
|
+
destination: Union[
|
|
388
|
+
Well, Sequence[Well], Sequence[Sequence[Well]], TrashBin, WasteChute
|
|
389
|
+
],
|
|
382
390
|
) -> command_types.ConsolidateWithLiquidClassCommand:
|
|
391
|
+
if isinstance(destination, (TrashBin, WasteChute)):
|
|
392
|
+
destination_text = stringify_disposal_location(destination)
|
|
393
|
+
else:
|
|
394
|
+
destination_text = stringify_well_list(destination)
|
|
383
395
|
text = (
|
|
384
396
|
"Consolidating "
|
|
385
397
|
+ f"{volume} uL of {liquid_class.display_name} liquid class from "
|
|
386
|
-
+ f"{stringify_well_list(source)} to {
|
|
398
|
+
+ f"{stringify_well_list(source)} to {destination_text}"
|
|
387
399
|
)
|
|
388
400
|
return {
|
|
389
401
|
"name": command_types.CONSOLIDATE_WITH_LIQUID_CLASS,
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from opentrons.types import Location, Mount, AxisMapType
|
|
2
|
+
from .helpers import stringify_location
|
|
3
|
+
from . import types as command_types
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def move_to(
|
|
8
|
+
mount: Mount, location: Location, speed: Optional[float]
|
|
9
|
+
) -> command_types.RobotMoveToCommand:
|
|
10
|
+
location_text = stringify_location(location)
|
|
11
|
+
text = f"Moving to {location_text} at {speed}"
|
|
12
|
+
return {
|
|
13
|
+
"name": command_types.ROBOT_MOVE_TO,
|
|
14
|
+
"payload": {"mount": mount, "location": location, "text": text},
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def move_axis_to(
|
|
19
|
+
axis_map: AxisMapType, speed: Optional[float]
|
|
20
|
+
) -> command_types.RobotMoveAxisToCommand:
|
|
21
|
+
text = f"Moving to the provided absolute axis map {axis_map} at {speed}."
|
|
22
|
+
return {
|
|
23
|
+
"name": command_types.ROBOT_MOVE_AXES_TO,
|
|
24
|
+
"payload": {"absolute_axes": axis_map, "text": text},
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def move_axis_relative(
|
|
29
|
+
axis_map: AxisMapType, speed: Optional[float]
|
|
30
|
+
) -> command_types.RobotMoveAxisRelativeCommand:
|
|
31
|
+
text = f"Moving to the provided relative axis map {axis_map} as {speed}"
|
|
32
|
+
return {
|
|
33
|
+
"name": command_types.ROBOT_MOVE_RELATIVE_TO,
|
|
34
|
+
"payload": {"relative_axes": axis_map, "text": text},
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def open_gripper() -> command_types.RobotOpenGripperJawCommand:
|
|
39
|
+
text = "Opening the gripper jaw."
|
|
40
|
+
return {
|
|
41
|
+
"name": command_types.ROBOT_OPEN_GRIPPER_JAW,
|
|
42
|
+
"payload": {"text": text},
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def close_gripper(force: Optional[float]) -> command_types.RobotCloseGripperJawCommand:
|
|
47
|
+
text = f"Closing the gripper jaw with force {force}."
|
|
48
|
+
return {
|
|
49
|
+
"name": command_types.ROBOT_CLOSE_GRIPPER_JAW,
|
|
50
|
+
"payload": {"text": text},
|
|
51
|
+
}
|
|
@@ -10,7 +10,7 @@ if TYPE_CHECKING:
|
|
|
10
10
|
from opentrons.protocol_api.disposal_locations import TrashBin, WasteChute
|
|
11
11
|
from opentrons.protocol_api._liquid import LiquidClass
|
|
12
12
|
|
|
13
|
-
from opentrons.types import Location
|
|
13
|
+
from opentrons.types import Location, Mount, AxisMapType
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
# type for subscriptions
|
|
@@ -86,6 +86,13 @@ THERMOCYCLER_SET_LID_TEMP: Final = "command.THERMOCYCLER_SET_LID_TEMP"
|
|
|
86
86
|
THERMOCYCLER_DEACTIVATE_LID: Final = "command.THERMOCYCLER_DEACTIVATE_LID"
|
|
87
87
|
THERMOCYCLER_DEACTIVATE_BLOCK: Final = "command.THERMOCYCLER_DEACTIVATE_BLOCK"
|
|
88
88
|
|
|
89
|
+
# Robot #
|
|
90
|
+
ROBOT_MOVE_TO: Final = "command.ROBOT_MOVE_TO"
|
|
91
|
+
ROBOT_MOVE_AXES_TO: Final = "command.ROBOT_MOVE_AXES_TO"
|
|
92
|
+
ROBOT_MOVE_RELATIVE_TO: Final = "command.ROBOT_MOVE_RELATIVE_TO"
|
|
93
|
+
ROBOT_OPEN_GRIPPER_JAW: Final = "command.ROBOT_OPEN_GRIPPER_JAW"
|
|
94
|
+
ROBOT_CLOSE_GRIPPER_JAW: Final = "command.ROBOT_CLOSE_GRIPPER_JAW"
|
|
95
|
+
|
|
89
96
|
|
|
90
97
|
class TextOnlyPayload(TypedDict):
|
|
91
98
|
text: str
|
|
@@ -548,7 +555,9 @@ class LiquidClassCommandPayload(TextOnlyPayload, SingleInstrumentPayload):
|
|
|
548
555
|
liquid_class: LiquidClass
|
|
549
556
|
volume: float
|
|
550
557
|
source: Union[Well, Sequence[Well], Sequence[Sequence[Well]]]
|
|
551
|
-
destination: Union[
|
|
558
|
+
destination: Union[
|
|
559
|
+
Well, Sequence[Well], Sequence[Sequence[Well]], TrashBin, WasteChute
|
|
560
|
+
]
|
|
552
561
|
|
|
553
562
|
|
|
554
563
|
class TransferWithLiquidClassCommand(TypedDict):
|
|
@@ -600,6 +609,49 @@ class PressurizeCommand(TypedDict):
|
|
|
600
609
|
payload: PressurizeCommandPayload
|
|
601
610
|
|
|
602
611
|
|
|
612
|
+
# Robot Commands and Payloads
|
|
613
|
+
class GripperCommandPayload(TextOnlyPayload):
|
|
614
|
+
pass
|
|
615
|
+
|
|
616
|
+
|
|
617
|
+
class RobotMoveToCommandPayload(TextOnlyPayload):
|
|
618
|
+
location: Location
|
|
619
|
+
mount: Mount
|
|
620
|
+
|
|
621
|
+
|
|
622
|
+
class RobotMoveAxisToCommandPayload(TextOnlyPayload):
|
|
623
|
+
absolute_axes: AxisMapType
|
|
624
|
+
|
|
625
|
+
|
|
626
|
+
class RobotMoveAxisRelativeCommandPayload(TextOnlyPayload):
|
|
627
|
+
relative_axes: AxisMapType
|
|
628
|
+
|
|
629
|
+
|
|
630
|
+
class RobotMoveToCommand(TypedDict):
|
|
631
|
+
name: Literal["command.ROBOT_MOVE_TO"]
|
|
632
|
+
payload: RobotMoveToCommandPayload
|
|
633
|
+
|
|
634
|
+
|
|
635
|
+
class RobotMoveAxisToCommand(TypedDict):
|
|
636
|
+
name: Literal["command.ROBOT_MOVE_AXES_TO"]
|
|
637
|
+
payload: RobotMoveAxisToCommandPayload
|
|
638
|
+
|
|
639
|
+
|
|
640
|
+
class RobotMoveAxisRelativeCommand(TypedDict):
|
|
641
|
+
name: Literal["command.ROBOT_MOVE_RELATIVE_TO"]
|
|
642
|
+
payload: RobotMoveAxisRelativeCommandPayload
|
|
643
|
+
|
|
644
|
+
|
|
645
|
+
class RobotOpenGripperJawCommand(TypedDict):
|
|
646
|
+
name: Literal["command.ROBOT_OPEN_GRIPPER_JAW"]
|
|
647
|
+
payload: GripperCommandPayload
|
|
648
|
+
|
|
649
|
+
|
|
650
|
+
class RobotCloseGripperJawCommand(TypedDict):
|
|
651
|
+
name: Literal["command.ROBOT_CLOSE_GRIPPER_JAW"]
|
|
652
|
+
payload: GripperCommandPayload
|
|
653
|
+
|
|
654
|
+
|
|
603
655
|
Command = Union[
|
|
604
656
|
DropTipCommand,
|
|
605
657
|
DropTipInDisposalLocationCommand,
|
|
@@ -654,6 +706,12 @@ Command = Union[
|
|
|
654
706
|
SealCommand,
|
|
655
707
|
UnsealCommand,
|
|
656
708
|
PressurizeCommand,
|
|
709
|
+
# Robot commands
|
|
710
|
+
RobotMoveToCommand,
|
|
711
|
+
RobotMoveAxisToCommand,
|
|
712
|
+
RobotMoveAxisRelativeCommand,
|
|
713
|
+
RobotOpenGripperJawCommand,
|
|
714
|
+
RobotCloseGripperJawCommand,
|
|
657
715
|
]
|
|
658
716
|
|
|
659
717
|
|
|
@@ -707,6 +765,11 @@ CommandPayload = Union[
|
|
|
707
765
|
SealCommandPayload,
|
|
708
766
|
UnsealCommandPayload,
|
|
709
767
|
PressurizeCommandPayload,
|
|
768
|
+
# Robot payloads
|
|
769
|
+
RobotMoveToCommandPayload,
|
|
770
|
+
RobotMoveAxisRelativeCommandPayload,
|
|
771
|
+
RobotMoveAxisToCommandPayload,
|
|
772
|
+
GripperCommandPayload,
|
|
710
773
|
]
|
|
711
774
|
|
|
712
775
|
|
|
@@ -947,6 +1010,26 @@ class MoveLabwareMessage(CommandMessageFields, MoveLabwareCommand):
|
|
|
947
1010
|
pass
|
|
948
1011
|
|
|
949
1012
|
|
|
1013
|
+
class RobotMoveToMessage(CommandMessageFields, RobotMoveToCommand):
|
|
1014
|
+
pass
|
|
1015
|
+
|
|
1016
|
+
|
|
1017
|
+
class RobotMoveAxisToMessage(CommandMessageFields, RobotMoveAxisToCommand):
|
|
1018
|
+
pass
|
|
1019
|
+
|
|
1020
|
+
|
|
1021
|
+
class RobotMoveAxisRelativeMessage(CommandMessageFields, RobotMoveAxisRelativeCommand):
|
|
1022
|
+
pass
|
|
1023
|
+
|
|
1024
|
+
|
|
1025
|
+
class RobotOpenGripperJawMessage(CommandMessageFields, RobotOpenGripperJawCommand):
|
|
1026
|
+
pass
|
|
1027
|
+
|
|
1028
|
+
|
|
1029
|
+
class RobotCloseGripperJawMessage(CommandMessageFields, RobotCloseGripperJawCommand):
|
|
1030
|
+
pass
|
|
1031
|
+
|
|
1032
|
+
|
|
950
1033
|
CommandMessage = Union[
|
|
951
1034
|
DropTipMessage,
|
|
952
1035
|
DropTipInDisposalLocationMessage,
|
|
@@ -994,4 +1077,10 @@ CommandMessage = Union[
|
|
|
994
1077
|
MoveToMessage,
|
|
995
1078
|
MoveToDisposalLocationMessage,
|
|
996
1079
|
MoveLabwareMessage,
|
|
1080
|
+
# Robot Messages
|
|
1081
|
+
RobotMoveToMessage,
|
|
1082
|
+
RobotMoveAxisToMessage,
|
|
1083
|
+
RobotMoveAxisRelativeMessage,
|
|
1084
|
+
RobotOpenGripperJawMessage,
|
|
1085
|
+
RobotCloseGripperJawMessage,
|
|
997
1086
|
]
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from dataclasses import dataclass
|
|
4
|
-
from typing import Optional, Dict, Union, TYPE_CHECKING
|
|
4
|
+
from typing import Optional, Dict, Union, TYPE_CHECKING, Tuple
|
|
5
5
|
|
|
6
6
|
from opentrons_shared_data.liquid_classes.liquid_class_definition import (
|
|
7
7
|
LiquidClassSchemaV1,
|
|
@@ -63,6 +63,20 @@ class LiquidClass:
|
|
|
63
63
|
_by_pipette_setting=by_pipette_settings,
|
|
64
64
|
)
|
|
65
65
|
|
|
66
|
+
@classmethod
|
|
67
|
+
def create_from(
|
|
68
|
+
cls,
|
|
69
|
+
name: str,
|
|
70
|
+
display_name: str,
|
|
71
|
+
by_pipette_setting: Dict[str, Dict[str, TransferProperties]],
|
|
72
|
+
) -> "LiquidClass":
|
|
73
|
+
"""Create a liquid class from the passed in args."""
|
|
74
|
+
return cls(
|
|
75
|
+
_name=name,
|
|
76
|
+
_display_name=display_name,
|
|
77
|
+
_by_pipette_setting=by_pipette_setting,
|
|
78
|
+
)
|
|
79
|
+
|
|
66
80
|
@property
|
|
67
81
|
def name(self) -> str:
|
|
68
82
|
return self._name
|
|
@@ -71,10 +85,54 @@ class LiquidClass:
|
|
|
71
85
|
def display_name(self) -> str:
|
|
72
86
|
return self._display_name
|
|
73
87
|
|
|
88
|
+
def update_for(
|
|
89
|
+
self,
|
|
90
|
+
pipette: Union[str, InstrumentContext],
|
|
91
|
+
tip_rack: Union[str, Labware],
|
|
92
|
+
transfer_properties: TransferProperties,
|
|
93
|
+
) -> None:
|
|
94
|
+
"""Update the transfer properties for the given pipette and tip combo.
|
|
95
|
+
|
|
96
|
+
If an entry does not exist, it will be created.
|
|
97
|
+
"""
|
|
98
|
+
pipette_name, tiprack_uri = self._get_pipette_and_tiprack_names(
|
|
99
|
+
pipette, tip_rack
|
|
100
|
+
)
|
|
101
|
+
try:
|
|
102
|
+
self._by_pipette_setting[pipette_name].update(
|
|
103
|
+
{tiprack_uri: transfer_properties}
|
|
104
|
+
)
|
|
105
|
+
except KeyError:
|
|
106
|
+
self._by_pipette_setting[pipette_name] = {tiprack_uri: transfer_properties}
|
|
107
|
+
|
|
74
108
|
def get_for(
|
|
75
109
|
self, pipette: Union[str, InstrumentContext], tip_rack: Union[str, Labware]
|
|
76
110
|
) -> TransferProperties:
|
|
77
111
|
"""Get liquid class transfer properties for the specified pipette and tip."""
|
|
112
|
+
pipette_name, tiprack_uri = self._get_pipette_and_tiprack_names(
|
|
113
|
+
pipette, tip_rack
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
try:
|
|
117
|
+
settings_for_pipette = self._by_pipette_setting[pipette_name]
|
|
118
|
+
except KeyError:
|
|
119
|
+
raise NoLiquidClassPropertyError(
|
|
120
|
+
f"No properties found for {pipette_name} in {self._name} liquid class"
|
|
121
|
+
)
|
|
122
|
+
try:
|
|
123
|
+
transfer_properties = settings_for_pipette[tiprack_uri]
|
|
124
|
+
except KeyError:
|
|
125
|
+
raise NoLiquidClassPropertyError(
|
|
126
|
+
f"No properties found for {tiprack_uri} for {pipette_name} in {self._name} liquid class"
|
|
127
|
+
)
|
|
128
|
+
return transfer_properties
|
|
129
|
+
|
|
130
|
+
@staticmethod
|
|
131
|
+
def _get_pipette_and_tiprack_names(
|
|
132
|
+
pipette: Union[str, InstrumentContext],
|
|
133
|
+
tip_rack: Union[str, Labware],
|
|
134
|
+
) -> Tuple[str, str]:
|
|
135
|
+
"""Return the pipette and tip rack name strings from the given pipette and tip rack."""
|
|
78
136
|
from . import InstrumentContext, Labware
|
|
79
137
|
|
|
80
138
|
if isinstance(pipette, InstrumentContext):
|
|
@@ -96,17 +154,4 @@ class LiquidClass:
|
|
|
96
154
|
f"{tip_rack} should either be a tiprack Labware object"
|
|
97
155
|
f" or a tiprack URI string."
|
|
98
156
|
)
|
|
99
|
-
|
|
100
|
-
try:
|
|
101
|
-
settings_for_pipette = self._by_pipette_setting[pipette_name]
|
|
102
|
-
except KeyError:
|
|
103
|
-
raise NoLiquidClassPropertyError(
|
|
104
|
-
f"No properties found for {pipette_name} in {self._name} liquid class"
|
|
105
|
-
)
|
|
106
|
-
try:
|
|
107
|
-
transfer_properties = settings_for_pipette[tiprack_uri]
|
|
108
|
-
except KeyError:
|
|
109
|
-
raise NoLiquidClassPropertyError(
|
|
110
|
-
f"No properties found for {tiprack_uri} for {pipette_name} in {self._name} liquid class"
|
|
111
|
-
)
|
|
112
|
-
return transfer_properties
|
|
157
|
+
return pipette_name, tiprack_uri
|