opentrons 8.6.0a12__py3-none-any.whl → 8.7.0a1__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/_version.py +2 -2
- opentrons/hardware_control/backends/ot3controller.py +22 -13
- opentrons/hardware_control/backends/ot3simulator.py +1 -1
- opentrons/hardware_control/ot3api.py +1 -1
- opentrons/protocol_api/core/engine/_default_liquid_class_versions.py +56 -0
- opentrons/protocol_api/core/engine/pipette_movement_conflict.py +77 -17
- opentrons/protocol_api/core/engine/protocol.py +11 -2
- opentrons/protocol_api/core/legacy/legacy_protocol_core.py +1 -1
- opentrons/protocol_api/core/protocol.py +1 -1
- opentrons/protocol_api/labware.py +36 -2
- opentrons/protocol_api/module_contexts.py +100 -13
- opentrons/protocol_api/protocol_context.py +162 -12
- opentrons/protocol_api/validation.py +4 -0
- opentrons/protocol_engine/commands/command_unions.py +2 -0
- opentrons/protocol_engine/commands/flex_stacker/common.py +13 -0
- opentrons/protocol_engine/commands/flex_stacker/store.py +20 -2
- opentrons/protocol_engine/execution/labware_movement.py +5 -11
- opentrons/protocol_engine/state/geometry.py +33 -5
- opentrons/protocol_engine/state/labware.py +66 -0
- opentrons/protocol_engine/state/modules.py +6 -0
- opentrons/protocol_engine/types/__init__.py +2 -0
- opentrons/protocol_engine/types/labware.py +9 -0
- opentrons/protocols/api_support/definitions.py +1 -1
- {opentrons-8.6.0a12.dist-info → opentrons-8.7.0a1.dist-info}/METADATA +4 -4
- {opentrons-8.6.0a12.dist-info → opentrons-8.7.0a1.dist-info}/RECORD +28 -27
- {opentrons-8.6.0a12.dist-info → opentrons-8.7.0a1.dist-info}/WHEEL +0 -0
- {opentrons-8.6.0a12.dist-info → opentrons-8.7.0a1.dist-info}/entry_points.txt +0 -0
- {opentrons-8.6.0a12.dist-info → opentrons-8.7.0a1.dist-info}/licenses/LICENSE +0 -0
opentrons/_version.py
CHANGED
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '8.
|
|
32
|
-
__version_tuple__ = version_tuple = (8,
|
|
31
|
+
__version__ = version = '8.7.0a1'
|
|
32
|
+
__version_tuple__ = version_tuple = (8, 7, 0, 'a1')
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
|
@@ -686,9 +686,9 @@ class OT3Controller(FlexBackend):
|
|
|
686
686
|
return (
|
|
687
687
|
MoveGroupRunner(
|
|
688
688
|
move_groups=[move_group],
|
|
689
|
-
ignore_stalls=
|
|
690
|
-
|
|
691
|
-
|
|
689
|
+
ignore_stalls=(
|
|
690
|
+
True if not self._feature_flags.stall_detection_enabled else False
|
|
691
|
+
),
|
|
692
692
|
),
|
|
693
693
|
False,
|
|
694
694
|
)
|
|
@@ -712,9 +712,9 @@ class OT3Controller(FlexBackend):
|
|
|
712
712
|
return (
|
|
713
713
|
MoveGroupRunner(
|
|
714
714
|
move_groups=[tip_motor_move_group],
|
|
715
|
-
ignore_stalls=
|
|
716
|
-
|
|
717
|
-
|
|
715
|
+
ignore_stalls=(
|
|
716
|
+
True if not self._feature_flags.stall_detection_enabled else False
|
|
717
|
+
),
|
|
718
718
|
),
|
|
719
719
|
True,
|
|
720
720
|
)
|
|
@@ -939,9 +939,9 @@ class OT3Controller(FlexBackend):
|
|
|
939
939
|
|
|
940
940
|
runner = MoveGroupRunner(
|
|
941
941
|
move_groups=[move_group],
|
|
942
|
-
ignore_stalls=
|
|
943
|
-
|
|
944
|
-
|
|
942
|
+
ignore_stalls=(
|
|
943
|
+
True if not self._feature_flags.stall_detection_enabled else False
|
|
944
|
+
),
|
|
945
945
|
)
|
|
946
946
|
try:
|
|
947
947
|
positions = await runner.run(can_messenger=self._messenger)
|
|
@@ -976,9 +976,9 @@ class OT3Controller(FlexBackend):
|
|
|
976
976
|
move_group = self._build_tip_action_group(origin, targets)
|
|
977
977
|
runner = MoveGroupRunner(
|
|
978
978
|
move_groups=[move_group],
|
|
979
|
-
ignore_stalls=
|
|
980
|
-
|
|
981
|
-
|
|
979
|
+
ignore_stalls=(
|
|
980
|
+
True if not self._feature_flags.stall_detection_enabled else False
|
|
981
|
+
),
|
|
982
982
|
)
|
|
983
983
|
try:
|
|
984
984
|
positions = await runner.run(can_messenger=self._messenger)
|
|
@@ -1777,7 +1777,16 @@ class OT3Controller(FlexBackend):
|
|
|
1777
1777
|
expected_grip_width + grip_width_uncertainty_wider
|
|
1778
1778
|
)
|
|
1779
1779
|
current_gripper_position = jaw_width
|
|
1780
|
-
|
|
1780
|
+
log.info(
|
|
1781
|
+
f"Checking gripper position: current {jaw_width}; max error {max_allowed_grip_error}; hard limits {hard_limit_lower}, {hard_limit_upper}; expected {expected_gripper_position_min}, {expected_grip_width}, {expected_gripper_position_max}; uncertainty {grip_width_uncertainty_narrower}, {grip_width_uncertainty_wider}"
|
|
1782
|
+
)
|
|
1783
|
+
if (
|
|
1784
|
+
isclose(current_gripper_position, hard_limit_lower)
|
|
1785
|
+
# this odd check handles internal backlash that can lead the position to read as if
|
|
1786
|
+
# the gripper has overshot its lower bound; this is physically impossible and an
|
|
1787
|
+
# artifact of the gearing, so it always indicates a hard stop
|
|
1788
|
+
or current_gripper_position < hard_limit_lower
|
|
1789
|
+
):
|
|
1781
1790
|
raise FailedGripperPickupError(
|
|
1782
1791
|
message="Failed to grip: jaws all the way closed",
|
|
1783
1792
|
details={
|
|
@@ -1480,8 +1480,8 @@ class OT3API(
|
|
|
1480
1480
|
grip_width_uncertainty_narrower,
|
|
1481
1481
|
gripper.jaw_width,
|
|
1482
1482
|
gripper.max_allowed_grip_error,
|
|
1483
|
-
gripper.max_jaw_width,
|
|
1484
1483
|
gripper.min_jaw_width,
|
|
1484
|
+
gripper.max_jaw_width,
|
|
1485
1485
|
)
|
|
1486
1486
|
|
|
1487
1487
|
def gripper_jaw_can_home(self) -> bool:
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""The versions of standard liquid classes that the Protocol API should load by default."""
|
|
2
|
+
|
|
3
|
+
from typing import TypeAlias
|
|
4
|
+
from opentrons.protocols.api_support.types import APIVersion
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
DefaultLiquidClassVersions: TypeAlias = dict[APIVersion, dict[str, int]]
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# This:
|
|
11
|
+
#
|
|
12
|
+
# {
|
|
13
|
+
# APIVersion(2, 100): {
|
|
14
|
+
# "foo_liquid": 3,
|
|
15
|
+
# },
|
|
16
|
+
# APIVersion(2, 105): {
|
|
17
|
+
# "foo_liquid": 7
|
|
18
|
+
# }
|
|
19
|
+
# }
|
|
20
|
+
#
|
|
21
|
+
# Means this:
|
|
22
|
+
#
|
|
23
|
+
# apiLevels name Default liquid class version
|
|
24
|
+
# ---------------------------------------------------------------
|
|
25
|
+
# <2.100 foo_liquid 1
|
|
26
|
+
# >=2.100,<2.105 foo_liquid 3
|
|
27
|
+
# >=2.105 foo_liquid 7
|
|
28
|
+
# [any] [anything else] 1
|
|
29
|
+
DEFAULT_LIQUID_CLASS_VERSIONS: DefaultLiquidClassVersions = {
|
|
30
|
+
APIVersion(2, 26): {
|
|
31
|
+
"ethanol_80": 2,
|
|
32
|
+
"glycerol_50": 2,
|
|
33
|
+
"water": 2,
|
|
34
|
+
},
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def get_liquid_class_version(
|
|
39
|
+
api_version: APIVersion,
|
|
40
|
+
liquid_class_name: str,
|
|
41
|
+
) -> int:
|
|
42
|
+
"""Return what version of a liquid class the Protocol API should load by default."""
|
|
43
|
+
default_lc_versions_newest_to_oldest = sorted(
|
|
44
|
+
DEFAULT_LIQUID_CLASS_VERSIONS.items(), key=lambda kv: kv[0], reverse=True
|
|
45
|
+
)
|
|
46
|
+
for (
|
|
47
|
+
breakpoint_api_version,
|
|
48
|
+
breakpoint_liquid_class_versions,
|
|
49
|
+
) in default_lc_versions_newest_to_oldest:
|
|
50
|
+
if (
|
|
51
|
+
api_version >= breakpoint_api_version
|
|
52
|
+
and liquid_class_name in breakpoint_liquid_class_versions
|
|
53
|
+
):
|
|
54
|
+
return breakpoint_liquid_class_versions[liquid_class_name]
|
|
55
|
+
|
|
56
|
+
return 1
|
|
@@ -21,7 +21,11 @@ from opentrons.protocol_engine import (
|
|
|
21
21
|
OnLabwareLocation,
|
|
22
22
|
DropTipWellLocation,
|
|
23
23
|
)
|
|
24
|
-
from opentrons.protocol_engine.types import
|
|
24
|
+
from opentrons.protocol_engine.types import (
|
|
25
|
+
StagingSlotLocation,
|
|
26
|
+
WellLocationType,
|
|
27
|
+
LoadedModule,
|
|
28
|
+
)
|
|
25
29
|
from opentrons.types import DeckSlotName, StagingSlotName, Point
|
|
26
30
|
from . import point_calculations
|
|
27
31
|
|
|
@@ -136,22 +140,47 @@ def check_safe_for_pipette_movement( # noqa: C901
|
|
|
136
140
|
f" will result in collision with thermocycler lid in deck slot A1."
|
|
137
141
|
)
|
|
138
142
|
|
|
143
|
+
def _check_for_column_4_module_collision(slot: DeckSlotName) -> None:
|
|
144
|
+
slot_module = engine_state.modules.get_by_slot(slot)
|
|
145
|
+
if (
|
|
146
|
+
slot_module
|
|
147
|
+
and engine_state.modules.is_column_4_module(slot_module.model)
|
|
148
|
+
and _slot_has_potential_colliding_object(
|
|
149
|
+
engine_state=engine_state,
|
|
150
|
+
pipette_bounds=pipette_bounds_at_well_location,
|
|
151
|
+
surrounding_location=slot_module,
|
|
152
|
+
)
|
|
153
|
+
):
|
|
154
|
+
raise PartialTipMovementNotAllowedError(
|
|
155
|
+
f"Moving to {engine_state.labware.get_display_name(labware_id)} in slot"
|
|
156
|
+
f" {slot} with {primary_nozzle} nozzle partial configuration will"
|
|
157
|
+
f" result in collision with items on {slot_module.model} mounted in {slot}."
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
# We check the labware slot for a module that is mounted in the same cutout
|
|
161
|
+
# as the labwares slot but does not occupy the same heirarchy (like the stacker).
|
|
162
|
+
_check_for_column_4_module_collision(labware_slot)
|
|
163
|
+
|
|
139
164
|
for regular_slot in surrounding_slots.regular_slots:
|
|
140
165
|
if _slot_has_potential_colliding_object(
|
|
141
166
|
engine_state=engine_state,
|
|
142
167
|
pipette_bounds=pipette_bounds_at_well_location,
|
|
143
|
-
|
|
168
|
+
surrounding_location=regular_slot,
|
|
144
169
|
):
|
|
145
170
|
raise PartialTipMovementNotAllowedError(
|
|
146
171
|
f"Moving to {engine_state.labware.get_display_name(labware_id)} in slot"
|
|
147
172
|
f" {labware_slot} with {primary_nozzle} nozzle partial configuration"
|
|
148
173
|
f" will result in collision with items in deck slot {regular_slot}."
|
|
149
174
|
)
|
|
175
|
+
|
|
176
|
+
# Check for Column 4 Modules that may be descendants of a given surrounding slot
|
|
177
|
+
_check_for_column_4_module_collision(regular_slot)
|
|
178
|
+
|
|
150
179
|
for staging_slot in surrounding_slots.staging_slots:
|
|
151
180
|
if _slot_has_potential_colliding_object(
|
|
152
181
|
engine_state=engine_state,
|
|
153
182
|
pipette_bounds=pipette_bounds_at_well_location,
|
|
154
|
-
|
|
183
|
+
surrounding_location=staging_slot,
|
|
155
184
|
):
|
|
156
185
|
raise PartialTipMovementNotAllowedError(
|
|
157
186
|
f"Moving to {engine_state.labware.get_display_name(labware_id)} in slot"
|
|
@@ -178,18 +207,45 @@ def _get_critical_point_to_use(
|
|
|
178
207
|
def _slot_has_potential_colliding_object(
|
|
179
208
|
engine_state: StateView,
|
|
180
209
|
pipette_bounds: Tuple[Point, Point, Point, Point],
|
|
181
|
-
|
|
210
|
+
surrounding_location: Union[DeckSlotName, StagingSlotName, LoadedModule],
|
|
182
211
|
) -> bool:
|
|
183
|
-
"""Return the slot, if any, that has an item that the pipette might collide into.
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
212
|
+
"""Return the slot, if any, that has an item that the pipette might collide into.
|
|
213
|
+
Can be provided a Deck Slot, Staging Slot, or Column 4 Module.
|
|
214
|
+
"""
|
|
215
|
+
if isinstance(surrounding_location, LoadedModule):
|
|
216
|
+
if (
|
|
217
|
+
engine_state.modules.is_column_4_module(surrounding_location.model)
|
|
218
|
+
and surrounding_location.location is not None
|
|
219
|
+
):
|
|
220
|
+
module_area = (
|
|
221
|
+
engine_state.modules.ensure_and_convert_module_fixture_location(
|
|
222
|
+
surrounding_location.location.slotName, surrounding_location.model
|
|
223
|
+
)
|
|
224
|
+
)
|
|
225
|
+
slot_pos = engine_state.addressable_areas.get_addressable_area_position(
|
|
226
|
+
addressable_area_name=module_area,
|
|
227
|
+
do_compatibility_check=False,
|
|
228
|
+
)
|
|
229
|
+
slot_bounds = (
|
|
230
|
+
engine_state.addressable_areas.get_addressable_area_bounding_box(
|
|
231
|
+
addressable_area_name=module_area,
|
|
232
|
+
do_compatibility_check=False,
|
|
233
|
+
)
|
|
234
|
+
)
|
|
235
|
+
else:
|
|
236
|
+
raise ValueError(
|
|
237
|
+
f"Error during collision validation, Module {surrounding_location.model} must be in Column 4."
|
|
238
|
+
)
|
|
239
|
+
else:
|
|
240
|
+
# Check if slot overlaps with pipette position
|
|
241
|
+
slot_pos = engine_state.addressable_areas.get_addressable_area_position(
|
|
242
|
+
addressable_area_name=surrounding_location.id,
|
|
243
|
+
do_compatibility_check=False,
|
|
244
|
+
)
|
|
245
|
+
slot_bounds = engine_state.addressable_areas.get_addressable_area_bounding_box(
|
|
246
|
+
addressable_area_name=surrounding_location.id,
|
|
247
|
+
do_compatibility_check=False,
|
|
248
|
+
)
|
|
193
249
|
slot_back_left_coords = Point(slot_pos.x, slot_pos.y + slot_bounds.y, slot_pos.z)
|
|
194
250
|
slot_front_right_coords = Point(slot_pos.x + slot_bounds.x, slot_pos.y, slot_pos.z)
|
|
195
251
|
|
|
@@ -199,13 +255,17 @@ def _slot_has_potential_colliding_object(
|
|
|
199
255
|
rectangle2=(slot_back_left_coords, slot_front_right_coords),
|
|
200
256
|
):
|
|
201
257
|
# Check z-height of items in overlapping slot
|
|
202
|
-
if isinstance(
|
|
258
|
+
if isinstance(surrounding_location, DeckSlotName):
|
|
203
259
|
slot_highest_z = engine_state.geometry.get_highest_z_in_slot(
|
|
204
|
-
DeckSlotLocation(slotName=
|
|
260
|
+
DeckSlotLocation(slotName=surrounding_location)
|
|
261
|
+
)
|
|
262
|
+
elif isinstance(surrounding_location, LoadedModule):
|
|
263
|
+
slot_highest_z = engine_state.geometry.get_highest_z_of_column_4_module(
|
|
264
|
+
surrounding_location
|
|
205
265
|
)
|
|
206
266
|
else:
|
|
207
267
|
slot_highest_z = engine_state.geometry.get_highest_z_in_slot(
|
|
208
|
-
StagingSlotLocation(slotName=
|
|
268
|
+
StagingSlotLocation(slotName=surrounding_location)
|
|
209
269
|
)
|
|
210
270
|
return slot_highest_z >= pipette_bounds[0].z
|
|
211
271
|
return False
|
|
@@ -78,7 +78,12 @@ from .module_core import (
|
|
|
78
78
|
FlexStackerCore,
|
|
79
79
|
)
|
|
80
80
|
from .exceptions import InvalidModuleLocationError
|
|
81
|
-
from . import
|
|
81
|
+
from . import (
|
|
82
|
+
load_labware_params,
|
|
83
|
+
deck_conflict,
|
|
84
|
+
overlap_versions,
|
|
85
|
+
_default_liquid_class_versions,
|
|
86
|
+
)
|
|
82
87
|
from opentrons.protocol_engine.resources import labware_validation
|
|
83
88
|
|
|
84
89
|
if TYPE_CHECKING:
|
|
@@ -1068,8 +1073,12 @@ class ProtocolCore(
|
|
|
1068
1073
|
display_color=(liquid.displayColor.root if liquid.displayColor else None),
|
|
1069
1074
|
)
|
|
1070
1075
|
|
|
1071
|
-
def get_liquid_class(self, name: str, version: int) -> LiquidClass:
|
|
1076
|
+
def get_liquid_class(self, name: str, version: Optional[int]) -> LiquidClass:
|
|
1072
1077
|
"""Get an instance of a built-in liquid class."""
|
|
1078
|
+
if version is None:
|
|
1079
|
+
version = _default_liquid_class_versions.get_liquid_class_version(
|
|
1080
|
+
self._api_version, name
|
|
1081
|
+
)
|
|
1073
1082
|
try:
|
|
1074
1083
|
# Check if we have already loaded this liquid class' definition
|
|
1075
1084
|
liquid_class_def = self._liquid_class_def_cache[(name, version)]
|
|
@@ -599,7 +599,7 @@ class LegacyProtocolCore(
|
|
|
599
599
|
"""Define a liquid to load into a well."""
|
|
600
600
|
assert False, "define_liquid only supported on engine core"
|
|
601
601
|
|
|
602
|
-
def get_liquid_class(self, name: str, version: int) -> LiquidClass:
|
|
602
|
+
def get_liquid_class(self, name: str, version: Optional[int]) -> LiquidClass:
|
|
603
603
|
"""Get an instance of a built-in liquid class."""
|
|
604
604
|
assert False, "define_liquid_class is only supported on engine core"
|
|
605
605
|
|
|
@@ -297,7 +297,7 @@ class AbstractProtocol(
|
|
|
297
297
|
"""Define a liquid to load into a well."""
|
|
298
298
|
|
|
299
299
|
@abstractmethod
|
|
300
|
-
def get_liquid_class(self, name: str, version: int) -> LiquidClass:
|
|
300
|
+
def get_liquid_class(self, name: str, version: Optional[int]) -> LiquidClass:
|
|
301
301
|
"""Get an instance of a built-in liquid class."""
|
|
302
302
|
|
|
303
303
|
@abstractmethod
|
|
@@ -640,6 +640,9 @@ class Labware:
|
|
|
640
640
|
lid: Optional[str] = None,
|
|
641
641
|
namespace: Optional[str] = None,
|
|
642
642
|
version: Optional[int] = None,
|
|
643
|
+
*,
|
|
644
|
+
lid_namespace: Optional[str] = None,
|
|
645
|
+
lid_version: Optional[int] = None,
|
|
643
646
|
) -> Labware:
|
|
644
647
|
"""Load a compatible labware onto the labware using its load parameters.
|
|
645
648
|
|
|
@@ -650,6 +653,24 @@ class Labware:
|
|
|
650
653
|
|
|
651
654
|
:returns: The initialized and loaded labware object.
|
|
652
655
|
"""
|
|
656
|
+
if self._api_version < validation.NAMESPACE_VERSION_ADAPTER_LID_VERSION_GATE:
|
|
657
|
+
if lid_namespace is not None:
|
|
658
|
+
raise APIVersionError(
|
|
659
|
+
api_element="The `lid_namespace` parameter",
|
|
660
|
+
until_version=str(
|
|
661
|
+
validation.NAMESPACE_VERSION_ADAPTER_LID_VERSION_GATE
|
|
662
|
+
),
|
|
663
|
+
current_version=str(self._api_version),
|
|
664
|
+
)
|
|
665
|
+
if lid_version is not None:
|
|
666
|
+
raise APIVersionError(
|
|
667
|
+
api_element="The `lid_version` parameter",
|
|
668
|
+
until_version=str(
|
|
669
|
+
validation.NAMESPACE_VERSION_ADAPTER_LID_VERSION_GATE
|
|
670
|
+
),
|
|
671
|
+
current_version=str(self._api_version),
|
|
672
|
+
)
|
|
673
|
+
|
|
653
674
|
labware_core = self._protocol_core.load_labware(
|
|
654
675
|
load_name=name,
|
|
655
676
|
label=label,
|
|
@@ -674,11 +695,24 @@ class Labware:
|
|
|
674
695
|
until_version="2.23",
|
|
675
696
|
current_version=f"{self._api_version}",
|
|
676
697
|
)
|
|
698
|
+
if (
|
|
699
|
+
self._api_version
|
|
700
|
+
< validation.NAMESPACE_VERSION_ADAPTER_LID_VERSION_GATE
|
|
701
|
+
):
|
|
702
|
+
checked_lid_namespace = namespace
|
|
703
|
+
checked_lid_version = version
|
|
704
|
+
else:
|
|
705
|
+
# This is currently impossible to reach because of the
|
|
706
|
+
# `if self._api_version < validation.validation.LID_STACK_VERSION_GATE`
|
|
707
|
+
# check above. This is here for now in case that check is removed in
|
|
708
|
+
# the future, and for symmetry with the other labware load methods.
|
|
709
|
+
checked_lid_namespace = lid_namespace
|
|
710
|
+
checked_lid_version = lid_version
|
|
677
711
|
self._protocol_core.load_lid(
|
|
678
712
|
load_name=lid,
|
|
679
713
|
location=labware_core,
|
|
680
|
-
namespace=
|
|
681
|
-
version=
|
|
714
|
+
namespace=checked_lid_namespace,
|
|
715
|
+
version=checked_lid_version,
|
|
682
716
|
)
|
|
683
717
|
|
|
684
718
|
return labware
|
|
@@ -123,7 +123,7 @@ class ModuleContext(CommandPublisher):
|
|
|
123
123
|
|
|
124
124
|
return core.geometry.add_labware(labware)
|
|
125
125
|
|
|
126
|
-
def load_labware(
|
|
126
|
+
def load_labware( # noqa: C901
|
|
127
127
|
self,
|
|
128
128
|
name: str,
|
|
129
129
|
label: Optional[str] = None,
|
|
@@ -131,6 +131,11 @@ class ModuleContext(CommandPublisher):
|
|
|
131
131
|
version: Optional[int] = None,
|
|
132
132
|
adapter: Optional[str] = None,
|
|
133
133
|
lid: Optional[str] = None,
|
|
134
|
+
*,
|
|
135
|
+
adapter_namespace: Optional[str] = None,
|
|
136
|
+
adapter_version: Optional[int] = None,
|
|
137
|
+
lid_namespace: Optional[str] = None,
|
|
138
|
+
lid_version: Optional[int] = None,
|
|
134
139
|
) -> Labware:
|
|
135
140
|
"""Load a labware onto the module using its load parameters.
|
|
136
141
|
|
|
@@ -142,7 +147,11 @@ class ModuleContext(CommandPublisher):
|
|
|
142
147
|
:returns: The initialized and loaded labware object.
|
|
143
148
|
|
|
144
149
|
.. versionadded:: 2.1
|
|
145
|
-
The
|
|
150
|
+
The ``label``, ``namespace``, and ``version`` parameters.
|
|
151
|
+
|
|
152
|
+
.. versionadded:: 2.26
|
|
153
|
+
The ``adapter_namespace``, ``adapter_version``,
|
|
154
|
+
``lid_namespace``, and ``lid_version`` parameters.
|
|
146
155
|
"""
|
|
147
156
|
if self._api_version < APIVersion(2, 1) and (
|
|
148
157
|
label is not None or namespace is not None or version != 1
|
|
@@ -152,6 +161,40 @@ class ModuleContext(CommandPublisher):
|
|
|
152
161
|
"are trying to utilize new load_labware parameters in 2.1"
|
|
153
162
|
)
|
|
154
163
|
|
|
164
|
+
if self._api_version < validation.NAMESPACE_VERSION_ADAPTER_LID_VERSION_GATE:
|
|
165
|
+
if adapter_namespace is not None:
|
|
166
|
+
raise APIVersionError(
|
|
167
|
+
api_element="The `adapter_namespace` parameter",
|
|
168
|
+
until_version=str(
|
|
169
|
+
validation.NAMESPACE_VERSION_ADAPTER_LID_VERSION_GATE
|
|
170
|
+
),
|
|
171
|
+
current_version=str(self._api_version),
|
|
172
|
+
)
|
|
173
|
+
if adapter_version is not None:
|
|
174
|
+
raise APIVersionError(
|
|
175
|
+
api_element="The `adapter_version` parameter",
|
|
176
|
+
until_version=str(
|
|
177
|
+
validation.NAMESPACE_VERSION_ADAPTER_LID_VERSION_GATE
|
|
178
|
+
),
|
|
179
|
+
current_version=str(self._api_version),
|
|
180
|
+
)
|
|
181
|
+
if lid_namespace is not None:
|
|
182
|
+
raise APIVersionError(
|
|
183
|
+
api_element="The `lid_namespace` parameter",
|
|
184
|
+
until_version=str(
|
|
185
|
+
validation.NAMESPACE_VERSION_ADAPTER_LID_VERSION_GATE
|
|
186
|
+
),
|
|
187
|
+
current_version=str(self._api_version),
|
|
188
|
+
)
|
|
189
|
+
if lid_version is not None:
|
|
190
|
+
raise APIVersionError(
|
|
191
|
+
api_element="The `lid_version` parameter",
|
|
192
|
+
until_version=str(
|
|
193
|
+
validation.NAMESPACE_VERSION_ADAPTER_LID_VERSION_GATE
|
|
194
|
+
),
|
|
195
|
+
current_version=str(self._api_version),
|
|
196
|
+
)
|
|
197
|
+
|
|
155
198
|
load_location: Union[ModuleCore, LabwareCore]
|
|
156
199
|
if adapter is not None:
|
|
157
200
|
if self._api_version < APIVersion(2, 15):
|
|
@@ -160,9 +203,21 @@ class ModuleContext(CommandPublisher):
|
|
|
160
203
|
until_version="2.15",
|
|
161
204
|
current_version=f"{self._api_version}",
|
|
162
205
|
)
|
|
206
|
+
|
|
207
|
+
if (
|
|
208
|
+
self._api_version
|
|
209
|
+
< validation.NAMESPACE_VERSION_ADAPTER_LID_VERSION_GATE
|
|
210
|
+
):
|
|
211
|
+
checked_adapter_namespace = namespace
|
|
212
|
+
checked_adapter_version = None
|
|
213
|
+
else:
|
|
214
|
+
checked_adapter_namespace = adapter_namespace
|
|
215
|
+
checked_adapter_version = adapter_version
|
|
216
|
+
|
|
163
217
|
loaded_adapter = self.load_adapter(
|
|
164
218
|
name=adapter,
|
|
165
|
-
namespace=
|
|
219
|
+
namespace=checked_adapter_namespace,
|
|
220
|
+
version=checked_adapter_version,
|
|
166
221
|
)
|
|
167
222
|
load_location = loaded_adapter._core
|
|
168
223
|
else:
|
|
@@ -193,11 +248,22 @@ class ModuleContext(CommandPublisher):
|
|
|
193
248
|
until_version="2.23",
|
|
194
249
|
current_version=f"{self._api_version}",
|
|
195
250
|
)
|
|
251
|
+
|
|
252
|
+
if (
|
|
253
|
+
self._api_version
|
|
254
|
+
< validation.NAMESPACE_VERSION_ADAPTER_LID_VERSION_GATE
|
|
255
|
+
):
|
|
256
|
+
checked_lid_namespace = namespace
|
|
257
|
+
checked_lid_version = None
|
|
258
|
+
else:
|
|
259
|
+
checked_lid_namespace = lid_namespace
|
|
260
|
+
checked_lid_version = lid_version
|
|
261
|
+
|
|
196
262
|
self._protocol_core.load_lid(
|
|
197
263
|
load_name=lid,
|
|
198
264
|
location=labware_core,
|
|
199
|
-
namespace=
|
|
200
|
-
version=
|
|
265
|
+
namespace=checked_lid_namespace,
|
|
266
|
+
version=checked_lid_version,
|
|
201
267
|
)
|
|
202
268
|
|
|
203
269
|
if isinstance(self._core, LegacyModuleCore):
|
|
@@ -1330,6 +1396,11 @@ class FlexStackerContext(ModuleContext):
|
|
|
1330
1396
|
lid: str | None = None,
|
|
1331
1397
|
count: int | None = None,
|
|
1332
1398
|
stacking_offset_z: float | None = None,
|
|
1399
|
+
*,
|
|
1400
|
+
adapter_namespace: str | None = None,
|
|
1401
|
+
adapter_version: int | None = None,
|
|
1402
|
+
lid_namespace: str | None = None,
|
|
1403
|
+
lid_version: int | None = None,
|
|
1333
1404
|
) -> None:
|
|
1334
1405
|
"""Configure the type and starting quantity of labware the Flex Stacker will store during a protocol. This is the only type of labware you'll be able to store in the Stacker until it's reconfigured.
|
|
1335
1406
|
|
|
@@ -1338,6 +1409,7 @@ class FlexStackerContext(ModuleContext):
|
|
|
1338
1409
|
:param str load_name: A string to use for looking up a labware definition.
|
|
1339
1410
|
You can find the ``load_name`` for any Opentrons-verified labware on the
|
|
1340
1411
|
`Labware Library <https://labware.opentrons.com>`__.
|
|
1412
|
+
|
|
1341
1413
|
:param str namespace: The namespace that the labware definition belongs to.
|
|
1342
1414
|
If unspecified, the API will automatically search two namespaces:
|
|
1343
1415
|
|
|
@@ -1348,19 +1420,34 @@ class FlexStackerContext(ModuleContext):
|
|
|
1348
1420
|
You might need to specify an explicit ``namespace`` if you have a custom
|
|
1349
1421
|
definition whose ``load_name`` is the same as an Opentrons-verified
|
|
1350
1422
|
definition, and you want to explicitly choose one or the other.
|
|
1423
|
+
|
|
1351
1424
|
:param version: The version of the labware definition. You should normally
|
|
1352
1425
|
leave this unspecified to let the method choose a version
|
|
1353
1426
|
automatically.
|
|
1427
|
+
|
|
1354
1428
|
:param adapter: An adapter to load the labware on top of. Accepts the same
|
|
1355
|
-
values as the ``load_name`` parameter of :py:meth:`.load_adapter`.
|
|
1356
|
-
|
|
1357
|
-
|
|
1429
|
+
values as the ``load_name`` parameter of :py:meth:`.load_adapter`.
|
|
1430
|
+
|
|
1431
|
+
:param adapter_namespace: Applies to ``adapter`` the same way that ``namespace``
|
|
1432
|
+
applies to ``load_name``.
|
|
1433
|
+
|
|
1434
|
+
:param adapter_version: Applies to ``adapter`` the same way that ``version``
|
|
1435
|
+
applies to ``load_name``.
|
|
1436
|
+
|
|
1358
1437
|
:param lid: A lid to load the on top of the main labware. Accepts the same
|
|
1359
1438
|
values as the ``load_name`` parameter of :py:meth:`~.ProtocolContext.load_lid_stack`. The
|
|
1360
1439
|
lid will use the same namespace as the labware, and the API will
|
|
1361
1440
|
choose the lid's version automatically.
|
|
1441
|
+
|
|
1442
|
+
:param lid_namespace: Applies to ``lid`` the same way that ``namespace``
|
|
1443
|
+
applies to ``load_name``.
|
|
1444
|
+
|
|
1445
|
+
:param lid_version: Applies to ``lid`` the same way that ``version``
|
|
1446
|
+
applies to ``load_name``.
|
|
1447
|
+
|
|
1362
1448
|
:param count: The number of labware that the Flex Stacker should store. If not specified, this will be the maximum amount of this kind of
|
|
1363
1449
|
labware that the Flex Stacker is capable of storing.
|
|
1450
|
+
|
|
1364
1451
|
:param stacking_offset_z: Stacking ``z`` offset in mm of stored labware. If specified, this overrides the
|
|
1365
1452
|
calculated value in the labware definition.
|
|
1366
1453
|
|
|
@@ -1378,18 +1465,18 @@ class FlexStackerContext(ModuleContext):
|
|
|
1378
1465
|
- Labware on adapter: the adapter (bottom side) of the upper labware unit overlaps with the top side of the labware below.
|
|
1379
1466
|
- Labware with lid: the labware (bottom side) of the upper labware unit overlaps with the lid (top side) of the unit below.
|
|
1380
1467
|
- Labware with lid and adapter: the adapter (bottom side) of the upper labware unit overlaps with the lid (top side) of the unit below.
|
|
1381
|
-
|
|
1382
1468
|
"""
|
|
1469
|
+
|
|
1383
1470
|
self._core.set_stored_labware(
|
|
1384
1471
|
main_load_name=load_name,
|
|
1385
1472
|
main_namespace=namespace,
|
|
1386
1473
|
main_version=version,
|
|
1387
1474
|
lid_load_name=lid,
|
|
1388
|
-
lid_namespace=
|
|
1389
|
-
lid_version=
|
|
1475
|
+
lid_namespace=lid_namespace,
|
|
1476
|
+
lid_version=lid_version,
|
|
1390
1477
|
adapter_load_name=adapter,
|
|
1391
|
-
adapter_namespace=
|
|
1392
|
-
adapter_version=
|
|
1478
|
+
adapter_namespace=adapter_namespace,
|
|
1479
|
+
adapter_version=adapter_version,
|
|
1393
1480
|
count=count,
|
|
1394
1481
|
stacking_offset_z=stacking_offset_z,
|
|
1395
1482
|
)
|