opentrons 8.7.0a0__py3-none-any.whl → 8.7.0a2__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/drivers/thermocycler/abstract.py +1 -0
- opentrons/drivers/thermocycler/driver.py +33 -4
- opentrons/drivers/thermocycler/simulator.py +2 -0
- opentrons/hardware_control/api.py +24 -5
- opentrons/hardware_control/backends/controller.py +8 -2
- opentrons/hardware_control/backends/ot3controller.py +3 -0
- opentrons/hardware_control/backends/ot3simulator.py +2 -1
- opentrons/hardware_control/backends/simulator.py +2 -1
- opentrons/hardware_control/backends/subsystem_manager.py +5 -2
- opentrons/hardware_control/module_control.py +82 -8
- opentrons/hardware_control/modules/__init__.py +3 -0
- opentrons/hardware_control/modules/absorbance_reader.py +11 -4
- opentrons/hardware_control/modules/flex_stacker.py +38 -9
- opentrons/hardware_control/modules/heater_shaker.py +30 -5
- opentrons/hardware_control/modules/magdeck.py +8 -4
- opentrons/hardware_control/modules/mod_abc.py +13 -5
- opentrons/hardware_control/modules/tempdeck.py +25 -5
- opentrons/hardware_control/modules/thermocycler.py +56 -10
- opentrons/hardware_control/modules/types.py +20 -1
- opentrons/hardware_control/modules/utils.py +11 -4
- opentrons/hardware_control/nozzle_manager.py +3 -0
- opentrons/hardware_control/ot3api.py +26 -5
- opentrons/hardware_control/scripts/update_module_fw.py +5 -0
- opentrons/hardware_control/types.py +31 -2
- opentrons/legacy_commands/protocol_commands.py +20 -0
- opentrons/legacy_commands/types.py +42 -0
- opentrons/motion_planning/waypoints.py +15 -29
- opentrons/protocol_api/__init__.py +5 -0
- opentrons/protocol_api/_types.py +6 -1
- opentrons/protocol_api/core/common.py +3 -1
- opentrons/protocol_api/core/engine/_default_labware_versions.py +32 -11
- opentrons/protocol_api/core/engine/_default_liquid_class_versions.py +2 -0
- opentrons/protocol_api/core/engine/labware.py +8 -1
- opentrons/protocol_api/core/engine/module_core.py +4 -0
- opentrons/protocol_api/core/engine/pipette_movement_conflict.py +77 -17
- opentrons/protocol_api/core/engine/protocol.py +18 -1
- opentrons/protocol_api/core/engine/tasks.py +35 -0
- opentrons/protocol_api/core/legacy/legacy_module_core.py +2 -0
- opentrons/protocol_api/core/legacy/legacy_protocol_core.py +11 -1
- opentrons/protocol_api/core/legacy/tasks.py +19 -0
- opentrons/protocol_api/core/legacy_simulator/legacy_protocol_core.py +14 -2
- opentrons/protocol_api/core/legacy_simulator/tasks.py +19 -0
- opentrons/protocol_api/core/module.py +1 -0
- opentrons/protocol_api/core/protocol.py +11 -2
- opentrons/protocol_api/core/tasks.py +31 -0
- opentrons/protocol_api/module_contexts.py +1 -0
- opentrons/protocol_api/protocol_context.py +26 -4
- opentrons/protocol_api/robot_context.py +38 -21
- opentrons/protocol_api/tasks.py +48 -0
- opentrons/protocol_api/validation.py +6 -1
- opentrons/protocol_engine/actions/__init__.py +4 -2
- opentrons/protocol_engine/actions/actions.py +22 -9
- opentrons/protocol_engine/clients/sync_client.py +6 -7
- opentrons/protocol_engine/commands/__init__.py +42 -0
- opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +2 -15
- opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +2 -15
- opentrons/protocol_engine/commands/aspirate.py +1 -0
- opentrons/protocol_engine/commands/command.py +1 -0
- opentrons/protocol_engine/commands/command_unions.py +39 -0
- opentrons/protocol_engine/commands/create_timer.py +83 -0
- opentrons/protocol_engine/commands/dispense.py +1 -0
- opentrons/protocol_engine/commands/drop_tip.py +32 -8
- opentrons/protocol_engine/commands/movement_common.py +2 -0
- opentrons/protocol_engine/commands/pick_up_tip.py +21 -11
- opentrons/protocol_engine/commands/set_tip_state.py +97 -0
- opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py +6 -0
- opentrons/protocol_engine/commands/thermocycler/run_profile.py +8 -0
- opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py +17 -1
- opentrons/protocol_engine/commands/touch_tip.py +1 -1
- opentrons/protocol_engine/commands/unsafe/unsafe_place_labware.py +6 -22
- opentrons/protocol_engine/commands/wait_for_tasks.py +98 -0
- opentrons/protocol_engine/errors/__init__.py +4 -0
- opentrons/protocol_engine/errors/exceptions.py +55 -0
- opentrons/protocol_engine/execution/__init__.py +2 -0
- opentrons/protocol_engine/execution/command_executor.py +8 -0
- opentrons/protocol_engine/execution/create_queue_worker.py +5 -1
- opentrons/protocol_engine/execution/labware_movement.py +9 -12
- opentrons/protocol_engine/execution/movement.py +2 -0
- opentrons/protocol_engine/execution/queue_worker.py +4 -0
- opentrons/protocol_engine/execution/run_control.py +8 -0
- opentrons/protocol_engine/execution/task_handler.py +157 -0
- opentrons/protocol_engine/protocol_engine.py +67 -33
- opentrons/protocol_engine/resources/__init__.py +2 -0
- opentrons/protocol_engine/resources/concurrency_provider.py +27 -0
- opentrons/protocol_engine/resources/deck_configuration_provider.py +7 -0
- opentrons/protocol_engine/resources/labware_validation.py +10 -6
- opentrons/protocol_engine/state/_well_math.py +60 -18
- opentrons/protocol_engine/state/addressable_areas.py +2 -0
- opentrons/protocol_engine/state/commands.py +7 -7
- opentrons/protocol_engine/state/geometry.py +237 -379
- opentrons/protocol_engine/state/labware.py +52 -102
- opentrons/protocol_engine/state/labware_origin_math/errors.py +94 -0
- opentrons/protocol_engine/state/labware_origin_math/stackup_origin_to_labware_origin.py +1331 -0
- opentrons/protocol_engine/state/module_substates/thermocycler_module_substate.py +37 -0
- opentrons/protocol_engine/state/modules.py +26 -7
- opentrons/protocol_engine/state/motion.py +44 -0
- opentrons/protocol_engine/state/state.py +14 -0
- opentrons/protocol_engine/state/state_summary.py +2 -0
- opentrons/protocol_engine/state/tasks.py +139 -0
- opentrons/protocol_engine/state/tips.py +177 -258
- opentrons/protocol_engine/state/update_types.py +16 -9
- opentrons/protocol_engine/types/__init__.py +9 -3
- opentrons/protocol_engine/types/deck_configuration.py +5 -1
- opentrons/protocol_engine/types/instrument.py +8 -1
- opentrons/protocol_engine/types/labware.py +1 -13
- opentrons/protocol_engine/types/module.py +10 -0
- opentrons/protocol_engine/types/tasks.py +38 -0
- opentrons/protocol_engine/types/tip.py +9 -0
- opentrons/protocol_runner/create_simulating_orchestrator.py +29 -2
- opentrons/protocol_runner/run_orchestrator.py +18 -2
- opentrons/protocols/api_support/definitions.py +1 -1
- opentrons/protocols/api_support/types.py +2 -1
- opentrons/simulate.py +48 -15
- opentrons/system/camera.py +1 -1
- {opentrons-8.7.0a0.dist-info → opentrons-8.7.0a2.dist-info}/METADATA +4 -4
- {opentrons-8.7.0a0.dist-info → opentrons-8.7.0a2.dist-info}/RECORD +120 -107
- opentrons/protocol_engine/state/_labware_origin_math.py +0 -636
- {opentrons-8.7.0a0.dist-info → opentrons-8.7.0a2.dist-info}/WHEEL +0 -0
- {opentrons-8.7.0a0.dist-info → opentrons-8.7.0a2.dist-info}/entry_points.txt +0 -0
- {opentrons-8.7.0a0.dist-info → opentrons-8.7.0a2.dist-info}/licenses/LICENSE +0 -0
|
@@ -24,6 +24,7 @@ from opentrons_shared_data.labware.constants import WELL_NAME_PATTERN
|
|
|
24
24
|
from opentrons_shared_data.labware.labware_definition import (
|
|
25
25
|
LabwareDefinition,
|
|
26
26
|
LabwareDefinition2,
|
|
27
|
+
LabwareDefinition3,
|
|
27
28
|
InnerWellGeometry,
|
|
28
29
|
)
|
|
29
30
|
from opentrons_shared_data.deck.types import CutoutFixture
|
|
@@ -44,7 +45,6 @@ from ..errors.exceptions import (
|
|
|
44
45
|
)
|
|
45
46
|
from ..resources import (
|
|
46
47
|
fixture_validation,
|
|
47
|
-
labware_validation,
|
|
48
48
|
deck_configuration_provider,
|
|
49
49
|
)
|
|
50
50
|
from ..types import (
|
|
@@ -63,12 +63,10 @@ from ..types import (
|
|
|
63
63
|
ModuleLocation,
|
|
64
64
|
OnLabwareLocation,
|
|
65
65
|
LabwareLocation,
|
|
66
|
-
LabwareOffsetVector,
|
|
67
66
|
ModuleOffsetData,
|
|
68
67
|
CurrentWell,
|
|
69
68
|
CurrentPipetteLocation,
|
|
70
69
|
TipGeometry,
|
|
71
|
-
LabwareMovementOffsetData,
|
|
72
70
|
InStackerHopperLocation,
|
|
73
71
|
OnDeckLabwareLocation,
|
|
74
72
|
AddressableAreaLocation,
|
|
@@ -91,7 +89,7 @@ from ..types import (
|
|
|
91
89
|
labware_location_is_system,
|
|
92
90
|
WellLocationType,
|
|
93
91
|
WellLocationFunction,
|
|
94
|
-
|
|
92
|
+
GripperMoveType,
|
|
95
93
|
AddressableArea,
|
|
96
94
|
)
|
|
97
95
|
from ..types.liquid_level_detection import SimulatedProbeResult, LiquidTrackingType
|
|
@@ -108,8 +106,11 @@ from .inner_well_math_utils import (
|
|
|
108
106
|
find_volume_user_defined_volumes,
|
|
109
107
|
)
|
|
110
108
|
from ._well_math import wells_covered_by_pipette_configuration, nozzles_per_well
|
|
111
|
-
from .
|
|
112
|
-
|
|
109
|
+
from .labware_origin_math.stackup_origin_to_labware_origin import (
|
|
110
|
+
get_stackup_origin_to_labware_origin,
|
|
111
|
+
LabwareOriginContext,
|
|
112
|
+
LabwareStackupAncestorDefinition,
|
|
113
|
+
)
|
|
113
114
|
|
|
114
115
|
_LOG = getLogger(__name__)
|
|
115
116
|
SLOT_WIDTH = 128
|
|
@@ -125,13 +126,6 @@ class _TipDropSection(enum.Enum):
|
|
|
125
126
|
RIGHT = "right"
|
|
126
127
|
|
|
127
128
|
|
|
128
|
-
class _GripperMoveType(enum.Enum):
|
|
129
|
-
"""Types of gripper movement."""
|
|
130
|
-
|
|
131
|
-
PICK_UP_LABWARE = enum.auto()
|
|
132
|
-
DROP_LABWARE = enum.auto()
|
|
133
|
-
|
|
134
|
-
|
|
135
129
|
@dataclass
|
|
136
130
|
class _AbsoluteRobotExtents:
|
|
137
131
|
front_left: Dict[MountType, Point]
|
|
@@ -274,12 +268,20 @@ class GeometryView:
|
|
|
274
268
|
try:
|
|
275
269
|
labware_id = self._labware.get_id_by_module(module_id=module_id)
|
|
276
270
|
except LabwareNotLoadedOnModuleError:
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
271
|
+
# For the time being we will ignore column 4 modules in this check to avoid conflating results
|
|
272
|
+
if self._modules.is_column_4_module(slot_item.model) is False:
|
|
273
|
+
return self._modules.get_module_highest_z(
|
|
274
|
+
module_id=module_id,
|
|
275
|
+
addressable_areas=self._addressable_areas,
|
|
276
|
+
)
|
|
281
277
|
else:
|
|
282
|
-
|
|
278
|
+
# For the time being we will ignore column 4 modules in this check to avoid conflating results
|
|
279
|
+
if self._modules.is_column_4_module(slot_item.model) is False:
|
|
280
|
+
return self.get_highest_z_of_labware_stack(labware_id)
|
|
281
|
+
# todo (cb, 2025-09-15): For now we skip column 4 modules and handle them seperately in
|
|
282
|
+
# get_highest_z_of_column_4_module, so this will return 0. In the future we may want to consolidate
|
|
283
|
+
# this to make it more apparently at this point in the query process.
|
|
284
|
+
return 0
|
|
283
285
|
elif isinstance(slot_item, LoadedLabware):
|
|
284
286
|
# get stacked heights of all labware in the slot
|
|
285
287
|
return self.get_highest_z_of_labware_stack(slot_item.id)
|
|
@@ -301,6 +303,26 @@ class GeometryView:
|
|
|
301
303
|
return self.get_labware_highest_z(labware_id)
|
|
302
304
|
return self.get_highest_z_of_labware_stack(stacked_labware_id)
|
|
303
305
|
|
|
306
|
+
def get_highest_z_of_column_4_module(self, module: LoadedModule) -> float:
|
|
307
|
+
"""Get the highest Z-point of the topmost labware in the stack of labware on the given column 4 module.
|
|
308
|
+
|
|
309
|
+
If there is no labware on the given module, returns highest z of the module.
|
|
310
|
+
"""
|
|
311
|
+
if self._modules.is_column_4_module(module.model):
|
|
312
|
+
try:
|
|
313
|
+
labware_id = self._labware.get_id_by_module(module_id=module.id)
|
|
314
|
+
except LabwareNotLoadedOnModuleError:
|
|
315
|
+
return self._modules.get_module_highest_z(
|
|
316
|
+
module_id=module.id,
|
|
317
|
+
addressable_areas=self._addressable_areas,
|
|
318
|
+
)
|
|
319
|
+
else:
|
|
320
|
+
return self.get_highest_z_of_labware_stack(labware_id)
|
|
321
|
+
else:
|
|
322
|
+
raise ValueError(
|
|
323
|
+
"Module must be a Column 4 Module to determine maximum z height."
|
|
324
|
+
)
|
|
325
|
+
|
|
304
326
|
def get_min_travel_z(
|
|
305
327
|
self,
|
|
306
328
|
pipette_id: str,
|
|
@@ -382,145 +404,114 @@ class GeometryView:
|
|
|
382
404
|
"""
|
|
383
405
|
location = self._labware.get(labware_id).location
|
|
384
406
|
definition = self._labware.get_definition(labware_id)
|
|
407
|
+
aa_name = self._get_underlying_addressable_area_name(location)
|
|
408
|
+
# TODO(jh, 08-18-25): Labware locations return the underlying slot as the "on location" for the fixed trash,
|
|
409
|
+
# but the underlying slot's name does not exist in addressable area state. Getting the addressable area from data is
|
|
410
|
+
# a workaround. Investigate further.
|
|
411
|
+
addressable_area = self._addressable_areas._get_addressable_area_from_deck_data(
|
|
412
|
+
aa_name, do_compatibility_check=False
|
|
413
|
+
)
|
|
414
|
+
stackup_lw_defs_locs = self._get_stackup_lw_info_top_to_bottom(
|
|
415
|
+
labware_definition=definition, location=location
|
|
416
|
+
)
|
|
417
|
+
underlying_ancestor_def = self._get_stackup_underlying_ancestor_definition(
|
|
418
|
+
location
|
|
419
|
+
)
|
|
420
|
+
module_parent_to_child_offset = self._get_stackup_module_parent_to_child_offset(
|
|
421
|
+
location
|
|
422
|
+
)
|
|
385
423
|
|
|
386
|
-
slot_front_left = self.
|
|
387
|
-
stackup_origin_to_lw_origin =
|
|
388
|
-
|
|
424
|
+
slot_front_left = self._addressable_areas.get_addressable_area_position(aa_name)
|
|
425
|
+
stackup_origin_to_lw_origin = get_stackup_origin_to_labware_origin(
|
|
426
|
+
context=LabwareOriginContext.PIPETTING,
|
|
427
|
+
stackup_lw_info_top_to_bottom=stackup_lw_defs_locs,
|
|
428
|
+
underlying_ancestor_definition=underlying_ancestor_def,
|
|
429
|
+
module_parent_to_child_offset=module_parent_to_child_offset,
|
|
430
|
+
deck_definition=self._addressable_areas.deck_definition,
|
|
431
|
+
slot_name=addressable_area.base_slot,
|
|
389
432
|
)
|
|
390
433
|
module_cal_offset = self._get_calibrated_module_offset(location)
|
|
391
434
|
|
|
392
435
|
return slot_front_left + stackup_origin_to_lw_origin + module_cal_offset
|
|
393
436
|
|
|
394
|
-
def
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
)
|
|
399
|
-
parent_pos = self._addressable_areas.get_addressable_area_position(slot_name)
|
|
400
|
-
|
|
401
|
-
return parent_pos
|
|
402
|
-
|
|
403
|
-
def _get_stackup_placement_origin_to_lw_origin(
|
|
404
|
-
self,
|
|
405
|
-
location: LabwareLocation,
|
|
406
|
-
definition: LabwareDefinition,
|
|
407
|
-
is_topmost_labware: bool,
|
|
408
|
-
) -> Point:
|
|
409
|
-
"""Get the offset vector from the lowest entity in a stackup to the labware."""
|
|
410
|
-
if isinstance(
|
|
411
|
-
location, (AddressableAreaLocation, DeckSlotLocation, ModuleLocation)
|
|
412
|
-
):
|
|
413
|
-
return self._get_parent_placement_origin_to_lw_origin(
|
|
414
|
-
labware_location=location,
|
|
415
|
-
labware_definition=definition,
|
|
416
|
-
is_topmost_labware=is_topmost_labware,
|
|
417
|
-
)
|
|
418
|
-
elif isinstance(location, OnLabwareLocation):
|
|
419
|
-
parent_id = location.labwareId
|
|
420
|
-
parent_location = self._labware.get(parent_id).location
|
|
421
|
-
parent_definition = self._labware.get_definition(parent_id)
|
|
422
|
-
|
|
423
|
-
parent_placement_origin_to_lw_origin = (
|
|
424
|
-
self._get_parent_placement_origin_to_lw_origin(
|
|
425
|
-
labware_location=location,
|
|
426
|
-
labware_definition=definition,
|
|
427
|
-
is_topmost_labware=is_topmost_labware,
|
|
428
|
-
)
|
|
429
|
-
)
|
|
430
|
-
|
|
431
|
-
return (
|
|
432
|
-
parent_placement_origin_to_lw_origin
|
|
433
|
-
+ self._get_stackup_placement_origin_to_lw_origin(
|
|
434
|
-
location=parent_location,
|
|
435
|
-
definition=parent_definition,
|
|
436
|
-
is_topmost_labware=False,
|
|
437
|
-
)
|
|
438
|
-
)
|
|
439
|
-
else:
|
|
440
|
-
raise errors.LabwareNotOnDeckError(
|
|
441
|
-
"Cannot access labware since it is not on the deck. "
|
|
442
|
-
"Either it has been loaded off-deck or its been moved off-deck."
|
|
443
|
-
)
|
|
437
|
+
def _get_stackup_lw_info_top_to_bottom(
|
|
438
|
+
self, labware_definition: LabwareDefinition, location: LabwareLocation
|
|
439
|
+
) -> list[tuple[LabwareDefinition, LabwareLocation]]:
|
|
440
|
+
"""Returns info about each labware in the stackup.
|
|
444
441
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
442
|
+
The list is ordered from the top labware to the bottom-most labware.
|
|
443
|
+
The first entry will always be the definition and location of the given labware itself.
|
|
444
|
+
"""
|
|
445
|
+
definitions_locations_top_to_bottom: list[
|
|
446
|
+
tuple[LabwareDefinition, LabwareLocation]
|
|
447
|
+
] = []
|
|
448
|
+
current_location = location
|
|
449
|
+
current_definition = labware_definition
|
|
452
450
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
module_id=labware_location.moduleId,
|
|
457
|
-
)
|
|
458
|
-
)
|
|
459
|
-
return get_parent_placement_origin_to_lw_origin(
|
|
460
|
-
child_labware=labware_definition,
|
|
461
|
-
parent_deck_item=parent_deck_item, # type: ignore[arg-type]
|
|
462
|
-
module_parent_to_child_offset=module_parent_to_child_offset,
|
|
463
|
-
deck_definition=self._addressable_areas.deck_definition,
|
|
464
|
-
is_topmost_labware=is_topmost_labware,
|
|
465
|
-
labware_location=labware_location,
|
|
466
|
-
)
|
|
467
|
-
elif isinstance(labware_location, OnLabwareLocation):
|
|
468
|
-
return get_parent_placement_origin_to_lw_origin(
|
|
469
|
-
child_labware=labware_definition,
|
|
470
|
-
parent_deck_item=parent_deck_item, # type: ignore[arg-type]
|
|
471
|
-
module_parent_to_child_offset=None,
|
|
472
|
-
deck_definition=self._addressable_areas.deck_definition,
|
|
473
|
-
is_topmost_labware=is_topmost_labware,
|
|
474
|
-
labware_location=labware_location,
|
|
475
|
-
)
|
|
476
|
-
elif isinstance(labware_location, (DeckSlotLocation, AddressableAreaLocation)):
|
|
477
|
-
return get_parent_placement_origin_to_lw_origin(
|
|
478
|
-
child_labware=labware_definition,
|
|
479
|
-
parent_deck_item=parent_deck_item, # type: ignore[arg-type]
|
|
480
|
-
module_parent_to_child_offset=None,
|
|
481
|
-
deck_definition=self._addressable_areas.deck_definition,
|
|
482
|
-
is_topmost_labware=is_topmost_labware,
|
|
483
|
-
labware_location=labware_location,
|
|
451
|
+
while True:
|
|
452
|
+
definitions_locations_top_to_bottom.append(
|
|
453
|
+
(current_definition, current_location)
|
|
484
454
|
)
|
|
485
|
-
else:
|
|
486
|
-
raise ValueError(f"Invalid labware location: {labware_location}")
|
|
487
455
|
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
return self._addressable_areas.get_slot_definition(addressable_area_name)
|
|
456
|
+
if isinstance(current_location, OnLabwareLocation):
|
|
457
|
+
current_labware_id = current_location.labwareId
|
|
458
|
+
current_location = self._labware.get(current_labware_id).location
|
|
459
|
+
current_definition = self._labware.get_definition(current_labware_id)
|
|
460
|
+
else:
|
|
461
|
+
break
|
|
495
462
|
|
|
496
|
-
|
|
497
|
-
addressable_area_name = location.addressableAreaName
|
|
498
|
-
return self._addressable_areas.get_addressable_area(addressable_area_name)
|
|
463
|
+
return definitions_locations_top_to_bottom
|
|
499
464
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
465
|
+
def _get_stackup_module_parent_to_child_offset(
|
|
466
|
+
self, top_most_lw_location: LabwareLocation
|
|
467
|
+
) -> Union[Point, None]:
|
|
468
|
+
"""Traverse the stackup to find the first parent-to-child module offset, if any."""
|
|
469
|
+
current_location = top_most_lw_location
|
|
503
470
|
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
471
|
+
while True:
|
|
472
|
+
if isinstance(current_location, ModuleLocation):
|
|
473
|
+
module_parent_to_child_offset = (
|
|
474
|
+
self._modules.get_nominal_offset_to_child_from_addressable_area(
|
|
475
|
+
module_id=current_location.moduleId,
|
|
476
|
+
)
|
|
477
|
+
)
|
|
478
|
+
return module_parent_to_child_offset
|
|
507
479
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
480
|
+
if isinstance(current_location, OnLabwareLocation):
|
|
481
|
+
current_labware_id = current_location.labwareId
|
|
482
|
+
current_labware = self._labware.get(current_labware_id)
|
|
483
|
+
current_location = current_labware.location
|
|
484
|
+
else:
|
|
485
|
+
break
|
|
513
486
|
|
|
514
|
-
|
|
515
|
-
raise errors.LabwareNotOnDeckError(
|
|
516
|
-
"Labware does not have a slot or module associated with it"
|
|
517
|
-
" since it is no longer on the deck."
|
|
518
|
-
)
|
|
487
|
+
return None
|
|
519
488
|
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
489
|
+
def _get_stackup_underlying_ancestor_definition(
|
|
490
|
+
self, top_most_lw_location: LabwareLocation
|
|
491
|
+
) -> LabwareStackupAncestorDefinition:
|
|
492
|
+
"""Traverse the stackup to find the first non-labware definition."""
|
|
493
|
+
current_location = top_most_lw_location
|
|
494
|
+
|
|
495
|
+
while True:
|
|
496
|
+
if isinstance(current_location, OnLabwareLocation):
|
|
497
|
+
current_labware_id = current_location.labwareId
|
|
498
|
+
current_labware = self._labware.get(current_labware_id)
|
|
499
|
+
current_location = current_labware.location
|
|
500
|
+
else:
|
|
501
|
+
if isinstance(current_location, ModuleLocation):
|
|
502
|
+
return self._modules.get_definition(current_location.moduleId)
|
|
503
|
+
elif isinstance(current_location, AddressableAreaLocation):
|
|
504
|
+
return self._addressable_areas.get_addressable_area(
|
|
505
|
+
current_location.addressableAreaName
|
|
506
|
+
)
|
|
507
|
+
elif isinstance(current_location, DeckSlotLocation):
|
|
508
|
+
return self._addressable_areas.get_slot_definition(
|
|
509
|
+
current_location.slotName.id
|
|
510
|
+
)
|
|
511
|
+
else:
|
|
512
|
+
raise errors.InvalidLabwarePositionError(
|
|
513
|
+
f"Cannot get ancestor slot of location {current_location}"
|
|
514
|
+
)
|
|
524
515
|
|
|
525
516
|
def _get_underlying_addressable_area_name(self, location: LabwareLocation) -> str:
|
|
526
517
|
if isinstance(location, DeckSlotLocation):
|
|
@@ -748,7 +739,7 @@ class GeometryView:
|
|
|
748
739
|
|
|
749
740
|
if well_def.shape != "circular":
|
|
750
741
|
raise errors.LabwareIsNotTipRackError(
|
|
751
|
-
f"Well {well_name} in labware {labware_id} is not circular."
|
|
742
|
+
f"Well {well_name} in labware {self._labware.get_display_name(labware_id)} is not circular."
|
|
752
743
|
)
|
|
753
744
|
|
|
754
745
|
return TipGeometry(
|
|
@@ -839,7 +830,7 @@ class GeometryView:
|
|
|
839
830
|
slot_name = DeckSlotName.from_primitive(area_name)
|
|
840
831
|
elif labware.location == OFF_DECK_LOCATION:
|
|
841
832
|
raise errors.LabwareNotOnDeckError(
|
|
842
|
-
f"Labware {labware_id} does not have a slot associated with it"
|
|
833
|
+
f"Labware {self._labware.get_display_name(labware_id)} does not have a slot associated with it"
|
|
843
834
|
f" since it is no longer on the deck."
|
|
844
835
|
)
|
|
845
836
|
else:
|
|
@@ -1030,6 +1021,8 @@ class GeometryView:
|
|
|
1030
1021
|
location: Union[
|
|
1031
1022
|
DeckSlotLocation, ModuleLocation, OnLabwareLocation, AddressableAreaLocation
|
|
1032
1023
|
],
|
|
1024
|
+
move_type: GripperMoveType,
|
|
1025
|
+
user_additional_offset: Point | None,
|
|
1033
1026
|
) -> Point:
|
|
1034
1027
|
"""Get the grip point of the labware as placed on the given location.
|
|
1035
1028
|
|
|
@@ -1041,28 +1034,107 @@ class GeometryView:
|
|
|
1041
1034
|
It is calculated as the xy center of the slot with z as the point indicated by
|
|
1042
1035
|
z-position of labware bottom + grip height from labware bottom.
|
|
1043
1036
|
"""
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1037
|
+
mod_cal_offset = self._get_calibrated_module_offset(location)
|
|
1038
|
+
user_additional_offset = user_additional_offset or Point()
|
|
1039
|
+
aa_origin_to_nominal_grip_point = self._get_aa_origin_to_nominal_grip_point(
|
|
1040
|
+
labware_definition=labware_definition,
|
|
1047
1041
|
location=location,
|
|
1048
|
-
|
|
1049
|
-
is_topmost_labware=True, # We aren't concerned with entities above the gripped labware.
|
|
1042
|
+
move_type=move_type,
|
|
1050
1043
|
)
|
|
1044
|
+
|
|
1045
|
+
return aa_origin_to_nominal_grip_point + mod_cal_offset + user_additional_offset
|
|
1046
|
+
|
|
1047
|
+
def _get_aa_origin_to_nominal_grip_point(
|
|
1048
|
+
self,
|
|
1049
|
+
labware_definition: LabwareDefinition,
|
|
1050
|
+
location: Union[
|
|
1051
|
+
DeckSlotLocation, ModuleLocation, OnLabwareLocation, AddressableAreaLocation
|
|
1052
|
+
],
|
|
1053
|
+
move_type: GripperMoveType,
|
|
1054
|
+
) -> Point:
|
|
1055
|
+
"""Get the nominal grip point of a labware.
|
|
1056
|
+
|
|
1057
|
+
Does not include module calibration offsets or user additional offsets.
|
|
1058
|
+
"""
|
|
1059
|
+
grip_z_from_lw_origin = self._labware.get_grip_z(labware_definition)
|
|
1060
|
+
aa_name = self._get_underlying_addressable_area_name(location)
|
|
1051
1061
|
addressable_area = self._addressable_areas.get_addressable_area(aa_name)
|
|
1052
|
-
|
|
1053
|
-
labware_definition=labware_definition,
|
|
1062
|
+
stackup_defs_locs = self._get_stackup_lw_info_top_to_bottom(
|
|
1063
|
+
labware_definition=labware_definition, location=location
|
|
1064
|
+
)
|
|
1065
|
+
module_parent_to_child_offset = self._get_stackup_module_parent_to_child_offset(
|
|
1066
|
+
location
|
|
1067
|
+
)
|
|
1068
|
+
underlying_ancestor_def = self._get_stackup_underlying_ancestor_definition(
|
|
1069
|
+
location
|
|
1070
|
+
)
|
|
1071
|
+
context_type = (
|
|
1072
|
+
LabwareOriginContext.GRIPPER_PICKING_UP
|
|
1073
|
+
if move_type == GripperMoveType.PICK_UP_LABWARE
|
|
1074
|
+
else LabwareOriginContext.GRIPPER_DROPPING
|
|
1054
1075
|
)
|
|
1055
|
-
mod_cal_offset = self._get_calibrated_module_offset(location)
|
|
1056
|
-
location_center = self._addressable_areas.get_addressable_area_center(aa_name)
|
|
1057
1076
|
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1077
|
+
aa_origin_to_lw_origin = get_stackup_origin_to_labware_origin(
|
|
1078
|
+
context=context_type,
|
|
1079
|
+
module_parent_to_child_offset=module_parent_to_child_offset,
|
|
1080
|
+
underlying_ancestor_definition=underlying_ancestor_def,
|
|
1081
|
+
stackup_lw_info_top_to_bottom=stackup_defs_locs,
|
|
1082
|
+
slot_name=addressable_area.base_slot,
|
|
1083
|
+
deck_definition=self._addressable_areas.deck_definition,
|
|
1064
1084
|
)
|
|
1065
1085
|
|
|
1086
|
+
if isinstance(labware_definition, LabwareDefinition2):
|
|
1087
|
+
lw_origin_to_aa_origin = self._get_lw_origin_to_parent(
|
|
1088
|
+
labware_definition=labware_definition, addressable_area=addressable_area
|
|
1089
|
+
)
|
|
1090
|
+
aa_origin_to_aa_center = (
|
|
1091
|
+
self._addressable_areas.get_addressable_area_center(aa_name)
|
|
1092
|
+
)
|
|
1093
|
+
aa_center_to_nominal_grip_point = Point(0, 0, grip_z_from_lw_origin)
|
|
1094
|
+
|
|
1095
|
+
return (
|
|
1096
|
+
aa_origin_to_lw_origin
|
|
1097
|
+
+ lw_origin_to_aa_origin
|
|
1098
|
+
+ aa_origin_to_aa_center
|
|
1099
|
+
+ aa_center_to_nominal_grip_point
|
|
1100
|
+
)
|
|
1101
|
+
|
|
1102
|
+
else:
|
|
1103
|
+
assert isinstance(labware_definition, LabwareDefinition3)
|
|
1104
|
+
|
|
1105
|
+
aa_origin = self._addressable_areas.get_addressable_area_position(aa_name)
|
|
1106
|
+
lw_origin_to_lw_center = self._get_lw_origin_to_lw_center(
|
|
1107
|
+
labware_definition
|
|
1108
|
+
)
|
|
1109
|
+
lw_origin_to_lw_grip_center = Point(
|
|
1110
|
+
x=lw_origin_to_lw_center.x,
|
|
1111
|
+
y=lw_origin_to_lw_center.y,
|
|
1112
|
+
z=grip_z_from_lw_origin,
|
|
1113
|
+
)
|
|
1114
|
+
|
|
1115
|
+
return aa_origin + aa_origin_to_lw_origin + lw_origin_to_lw_grip_center
|
|
1116
|
+
|
|
1117
|
+
def _get_lw_origin_to_lw_center(
|
|
1118
|
+
self, labware_definition: LabwareDefinition
|
|
1119
|
+
) -> Point:
|
|
1120
|
+
"""Get the x,y,z center of the labware."""
|
|
1121
|
+
if isinstance(labware_definition, LabwareDefinition2):
|
|
1122
|
+
dimensions = labware_definition.dimensions
|
|
1123
|
+
x = dimensions.xDimension / 2
|
|
1124
|
+
y = dimensions.yDimension / 2
|
|
1125
|
+
z = dimensions.zDimension / 2
|
|
1126
|
+
|
|
1127
|
+
return Point(x, y, z)
|
|
1128
|
+
else:
|
|
1129
|
+
front_right_top = labware_definition.extents.total.frontRightTop
|
|
1130
|
+
back_left_bottom = labware_definition.extents.total.backLeftBottom
|
|
1131
|
+
|
|
1132
|
+
x = (front_right_top.x - back_left_bottom.x) / 2
|
|
1133
|
+
y = (front_right_top.y - back_left_bottom.y) / 2
|
|
1134
|
+
z = (front_right_top.z - back_left_bottom.z) / 2
|
|
1135
|
+
|
|
1136
|
+
return Point(x, y, z)
|
|
1137
|
+
|
|
1066
1138
|
def _get_lw_origin_to_parent(
|
|
1067
1139
|
self, labware_definition: LabwareDefinition, addressable_area: AddressableArea
|
|
1068
1140
|
) -> Point:
|
|
@@ -1096,7 +1168,6 @@ class GeometryView:
|
|
|
1096
1168
|
if self._modules.should_dodge_thermocycler(
|
|
1097
1169
|
from_slot=from_slot, to_slot=to_slot
|
|
1098
1170
|
):
|
|
1099
|
-
|
|
1100
1171
|
middle_slot_fixture = (
|
|
1101
1172
|
self._addressable_areas.get_fixture_by_deck_slot_name(
|
|
1102
1173
|
DeckSlotName.SLOT_C2.to_equivalent_for_robot_type(
|
|
@@ -1365,41 +1436,6 @@ class GeometryView:
|
|
|
1365
1436
|
x_well_offset = 0
|
|
1366
1437
|
return x_well_offset
|
|
1367
1438
|
|
|
1368
|
-
def get_final_labware_movement_offset_vectors(
|
|
1369
|
-
self,
|
|
1370
|
-
from_location: OnDeckLabwareLocation,
|
|
1371
|
-
to_location: OnDeckLabwareLocation,
|
|
1372
|
-
additional_pick_up_offset: Point,
|
|
1373
|
-
additional_drop_offset: Point,
|
|
1374
|
-
current_labware: LabwareDefinition,
|
|
1375
|
-
) -> LabwareMovementOffsetData:
|
|
1376
|
-
"""Calculate the final labware offset vector to use in labware movement."""
|
|
1377
|
-
pick_up_offset = (
|
|
1378
|
-
self.get_total_nominal_gripper_offset_for_move_type(
|
|
1379
|
-
location=from_location,
|
|
1380
|
-
move_type=_GripperMoveType.PICK_UP_LABWARE,
|
|
1381
|
-
current_labware=current_labware,
|
|
1382
|
-
)
|
|
1383
|
-
+ additional_pick_up_offset
|
|
1384
|
-
)
|
|
1385
|
-
drop_offset = (
|
|
1386
|
-
self.get_total_nominal_gripper_offset_for_move_type(
|
|
1387
|
-
location=to_location,
|
|
1388
|
-
move_type=_GripperMoveType.DROP_LABWARE,
|
|
1389
|
-
current_labware=current_labware,
|
|
1390
|
-
)
|
|
1391
|
-
+ additional_drop_offset
|
|
1392
|
-
)
|
|
1393
|
-
|
|
1394
|
-
return LabwareMovementOffsetData(
|
|
1395
|
-
pickUpOffset=LabwareOffsetVector(
|
|
1396
|
-
x=pick_up_offset.x, y=pick_up_offset.y, z=pick_up_offset.z
|
|
1397
|
-
),
|
|
1398
|
-
dropOffset=LabwareOffsetVector(
|
|
1399
|
-
x=drop_offset.x, y=drop_offset.y, z=drop_offset.z
|
|
1400
|
-
),
|
|
1401
|
-
)
|
|
1402
|
-
|
|
1403
1439
|
@staticmethod
|
|
1404
1440
|
def ensure_valid_gripper_location(
|
|
1405
1441
|
location: LabwareLocation,
|
|
@@ -1421,130 +1457,6 @@ class GeometryView:
|
|
|
1421
1457
|
)
|
|
1422
1458
|
return location
|
|
1423
1459
|
|
|
1424
|
-
def get_total_nominal_gripper_offset_for_move_type(
|
|
1425
|
-
self,
|
|
1426
|
-
location: OnDeckLabwareLocation,
|
|
1427
|
-
move_type: _GripperMoveType,
|
|
1428
|
-
current_labware: LabwareDefinition,
|
|
1429
|
-
) -> Point:
|
|
1430
|
-
"""Get the total of the offsets to be used to pick up labware in its current location."""
|
|
1431
|
-
if move_type == _GripperMoveType.PICK_UP_LABWARE:
|
|
1432
|
-
if isinstance(
|
|
1433
|
-
location, (ModuleLocation, DeckSlotLocation, AddressableAreaLocation)
|
|
1434
|
-
):
|
|
1435
|
-
return Point.from_xyz_attrs(
|
|
1436
|
-
self._nominal_gripper_offsets_for_location(location).pickUpOffset
|
|
1437
|
-
)
|
|
1438
|
-
else:
|
|
1439
|
-
# If it's a labware on a labware (most likely an adapter),
|
|
1440
|
-
# we calculate the offset as sum of offsets for the direct parent labware
|
|
1441
|
-
# and the underlying non-labware parent location.
|
|
1442
|
-
direct_parent_offset = self._nominal_gripper_offsets_for_location(
|
|
1443
|
-
location
|
|
1444
|
-
)
|
|
1445
|
-
ancestor = self._labware.get_parent_location(location.labwareId)
|
|
1446
|
-
extra_offset = Point(x=0, y=0, z=0)
|
|
1447
|
-
if (
|
|
1448
|
-
isinstance(ancestor, ModuleLocation)
|
|
1449
|
-
# todo(mm, 2025-06-20): Avoid this private attribute access.
|
|
1450
|
-
and self._modules._state.requested_model_by_id[ancestor.moduleId]
|
|
1451
|
-
== ModuleModel.THERMOCYCLER_MODULE_V2
|
|
1452
|
-
and labware_validation.validate_definition_is_lid(current_labware)
|
|
1453
|
-
):
|
|
1454
|
-
if "lidOffsets" in current_labware.gripperOffsets.keys():
|
|
1455
|
-
extra_offset = Point(
|
|
1456
|
-
x=current_labware.gripperOffsets[
|
|
1457
|
-
"lidOffsets"
|
|
1458
|
-
].pickUpOffset.x,
|
|
1459
|
-
y=current_labware.gripperOffsets[
|
|
1460
|
-
"lidOffsets"
|
|
1461
|
-
].pickUpOffset.y,
|
|
1462
|
-
z=current_labware.gripperOffsets[
|
|
1463
|
-
"lidOffsets"
|
|
1464
|
-
].pickUpOffset.z,
|
|
1465
|
-
)
|
|
1466
|
-
else:
|
|
1467
|
-
raise errors.LabwareOffsetDoesNotExistError(
|
|
1468
|
-
f"Labware Definition {current_labware.parameters.loadName} does not contain required field 'lidOffsets' of 'gripperOffsets'."
|
|
1469
|
-
)
|
|
1470
|
-
|
|
1471
|
-
assert isinstance(
|
|
1472
|
-
ancestor,
|
|
1473
|
-
(
|
|
1474
|
-
DeckSlotLocation,
|
|
1475
|
-
ModuleLocation,
|
|
1476
|
-
OnLabwareLocation,
|
|
1477
|
-
AddressableAreaLocation,
|
|
1478
|
-
),
|
|
1479
|
-
), "No gripper offsets for off-deck labware"
|
|
1480
|
-
return (
|
|
1481
|
-
Point.from_xyz_attrs(direct_parent_offset.pickUpOffset)
|
|
1482
|
-
+ Point.from_xyz_attrs(
|
|
1483
|
-
self._nominal_gripper_offsets_for_location(
|
|
1484
|
-
location=ancestor
|
|
1485
|
-
).pickUpOffset
|
|
1486
|
-
)
|
|
1487
|
-
+ extra_offset
|
|
1488
|
-
)
|
|
1489
|
-
else:
|
|
1490
|
-
if isinstance(
|
|
1491
|
-
location, (ModuleLocation, DeckSlotLocation, AddressableAreaLocation)
|
|
1492
|
-
):
|
|
1493
|
-
return Point.from_xyz_attrs(
|
|
1494
|
-
self._nominal_gripper_offsets_for_location(location).dropOffset
|
|
1495
|
-
)
|
|
1496
|
-
else:
|
|
1497
|
-
# If it's a labware on a labware (most likely an adapter),
|
|
1498
|
-
# we calculate the offset as sum of offsets for the direct parent labware
|
|
1499
|
-
# and the underlying non-labware parent location.
|
|
1500
|
-
direct_parent_offset = self._nominal_gripper_offsets_for_location(
|
|
1501
|
-
location
|
|
1502
|
-
)
|
|
1503
|
-
ancestor = self._labware.get_parent_location(location.labwareId)
|
|
1504
|
-
extra_offset = Point(x=0, y=0, z=0)
|
|
1505
|
-
if (
|
|
1506
|
-
isinstance(ancestor, ModuleLocation)
|
|
1507
|
-
# todo(mm, 2024-11-06): Do not access private module state; only use public ModuleView methods.
|
|
1508
|
-
and self._modules._state.requested_model_by_id[ancestor.moduleId]
|
|
1509
|
-
== ModuleModel.THERMOCYCLER_MODULE_V2
|
|
1510
|
-
and labware_validation.validate_definition_is_lid(current_labware)
|
|
1511
|
-
):
|
|
1512
|
-
if "lidOffsets" in current_labware.gripperOffsets.keys():
|
|
1513
|
-
extra_offset = Point(
|
|
1514
|
-
x=current_labware.gripperOffsets[
|
|
1515
|
-
"lidOffsets"
|
|
1516
|
-
].pickUpOffset.x,
|
|
1517
|
-
y=current_labware.gripperOffsets[
|
|
1518
|
-
"lidOffsets"
|
|
1519
|
-
].pickUpOffset.y,
|
|
1520
|
-
z=current_labware.gripperOffsets[
|
|
1521
|
-
"lidOffsets"
|
|
1522
|
-
].pickUpOffset.z,
|
|
1523
|
-
)
|
|
1524
|
-
else:
|
|
1525
|
-
raise errors.LabwareOffsetDoesNotExistError(
|
|
1526
|
-
f"Labware Definition {current_labware.parameters.loadName} does not contain required field 'lidOffsets' of 'gripperOffsets'."
|
|
1527
|
-
)
|
|
1528
|
-
|
|
1529
|
-
assert isinstance(
|
|
1530
|
-
ancestor,
|
|
1531
|
-
(
|
|
1532
|
-
DeckSlotLocation,
|
|
1533
|
-
ModuleLocation,
|
|
1534
|
-
OnLabwareLocation,
|
|
1535
|
-
AddressableAreaLocation,
|
|
1536
|
-
),
|
|
1537
|
-
), "No gripper offsets for off-deck labware"
|
|
1538
|
-
return (
|
|
1539
|
-
Point.from_xyz_attrs(direct_parent_offset.dropOffset)
|
|
1540
|
-
+ Point.from_xyz_attrs(
|
|
1541
|
-
self._nominal_gripper_offsets_for_location(
|
|
1542
|
-
location=ancestor
|
|
1543
|
-
).dropOffset
|
|
1544
|
-
)
|
|
1545
|
-
+ extra_offset
|
|
1546
|
-
)
|
|
1547
|
-
|
|
1548
1460
|
# todo(mm, 2024-11-05): This may be incorrect because it does not take the following
|
|
1549
1461
|
# offsets into account, which *are* taken into account for the actual gripper movement:
|
|
1550
1462
|
#
|
|
@@ -1596,64 +1508,6 @@ class GeometryView:
|
|
|
1596
1508
|
f"Cannot move labware '{labware_definition.parameters.loadName}' when {int(tip.volume)} µL tips are attached."
|
|
1597
1509
|
)
|
|
1598
1510
|
|
|
1599
|
-
def _nominal_gripper_offsets_for_location(
|
|
1600
|
-
self, location: OnDeckLabwareLocation
|
|
1601
|
-
) -> LabwareMovementOffsetData:
|
|
1602
|
-
"""Provide the default gripper offset data for the given location type."""
|
|
1603
|
-
if isinstance(location, (DeckSlotLocation, AddressableAreaLocation)):
|
|
1604
|
-
# TODO we might need a separate type of gripper offset for addressable areas but that also might just
|
|
1605
|
-
# be covered by the drop labware offset/location
|
|
1606
|
-
offsets = self._labware.get_deck_default_gripper_offsets()
|
|
1607
|
-
elif isinstance(location, ModuleLocation):
|
|
1608
|
-
offsets = self._modules.get_default_gripper_offsets(location.moduleId)
|
|
1609
|
-
else:
|
|
1610
|
-
# Labware is on a labware/adapter
|
|
1611
|
-
offsets = self._labware_gripper_offsets(location.labwareId)
|
|
1612
|
-
return offsets or LabwareMovementOffsetData(
|
|
1613
|
-
pickUpOffset=LabwareOffsetVector(x=0, y=0, z=0),
|
|
1614
|
-
dropOffset=LabwareOffsetVector(x=0, y=0, z=0),
|
|
1615
|
-
)
|
|
1616
|
-
|
|
1617
|
-
def _labware_gripper_offsets(
|
|
1618
|
-
self, labware_id: str
|
|
1619
|
-
) -> Optional[LabwareMovementOffsetData]:
|
|
1620
|
-
"""Provide the most appropriate gripper offset data for the specified labware.
|
|
1621
|
-
|
|
1622
|
-
We check the types of gripper offsets available for the labware ("default" or slot-based)
|
|
1623
|
-
and return the most appropriate one for the overall location of the labware.
|
|
1624
|
-
Currently, only module adapters (specifically, the H/S universal flat adapter)
|
|
1625
|
-
have non-default offsets that are specific to location of the module on deck,
|
|
1626
|
-
so, this code only checks for the presence of those known offsets.
|
|
1627
|
-
"""
|
|
1628
|
-
parent_location = self._labware.get_parent_location(labware_id)
|
|
1629
|
-
assert isinstance(
|
|
1630
|
-
parent_location,
|
|
1631
|
-
(
|
|
1632
|
-
DeckSlotLocation,
|
|
1633
|
-
ModuleLocation,
|
|
1634
|
-
AddressableAreaLocation,
|
|
1635
|
-
OnLabwareLocation,
|
|
1636
|
-
),
|
|
1637
|
-
), "No gripper offsets for off-deck labware"
|
|
1638
|
-
|
|
1639
|
-
if isinstance(parent_location, DeckSlotLocation):
|
|
1640
|
-
slot_name = parent_location.slotName
|
|
1641
|
-
elif isinstance(parent_location, AddressableAreaLocation):
|
|
1642
|
-
slot_name = self._addressable_areas.get_addressable_area_base_slot(
|
|
1643
|
-
parent_location.addressableAreaName
|
|
1644
|
-
)
|
|
1645
|
-
else:
|
|
1646
|
-
module_loc = self._modules.get_location(parent_location.moduleId)
|
|
1647
|
-
slot_name = module_loc.slotName
|
|
1648
|
-
|
|
1649
|
-
slot_based_offset = self._labware.get_child_gripper_offsets(
|
|
1650
|
-
labware_id=labware_id, slot_name=slot_name.to_ot3_equivalent()
|
|
1651
|
-
)
|
|
1652
|
-
|
|
1653
|
-
return slot_based_offset or self._labware.get_child_gripper_offsets(
|
|
1654
|
-
labware_id=labware_id, slot_name=None
|
|
1655
|
-
)
|
|
1656
|
-
|
|
1657
1511
|
def get_location_sequence(self, labware_id: str) -> LabwareLocationSequence:
|
|
1658
1512
|
"""Provide the LocationSequence specifying the current position of the labware.
|
|
1659
1513
|
|
|
@@ -2260,7 +2114,8 @@ class GeometryView:
|
|
|
2260
2114
|
except InvalidLiquidHeightFound as _exception:
|
|
2261
2115
|
raise InvalidLiquidHeightFound(
|
|
2262
2116
|
message=_exception.message
|
|
2263
|
-
+ f"for well {well_name} of {self._labware.get_display_name(labware_id)}
|
|
2117
|
+
+ f"for well {well_name} of {self._labware.get_display_name(labware_id)}"
|
|
2118
|
+
f" on slot {self.get_ancestor_slot_name(labware_id)}"
|
|
2264
2119
|
)
|
|
2265
2120
|
# if meniscus volume is a simulated value, comparisons aren't meaningful
|
|
2266
2121
|
if isinstance(meniscus_volume, SimulatedProbeResult):
|
|
@@ -2268,13 +2123,16 @@ class GeometryView:
|
|
|
2268
2123
|
remaining_volume = well_volumetric_capacity - meniscus_volume
|
|
2269
2124
|
if volume > remaining_volume:
|
|
2270
2125
|
raise errors.InvalidDispenseVolumeError(
|
|
2271
|
-
f"Attempting to dispense {volume}µL of liquid into a well that can currently only hold
|
|
2126
|
+
f"Attempting to dispense {volume}µL of liquid into a well that can currently only hold"
|
|
2127
|
+
f" {remaining_volume}µL (well {well_name} in labware {self._labware.get_display_name(labware_id)})"
|
|
2272
2128
|
)
|
|
2273
2129
|
else:
|
|
2274
2130
|
# TODO(pbm, 10-08-24): factor in well (LabwareStore) state volume
|
|
2275
2131
|
if volume > well_volumetric_capacity:
|
|
2276
2132
|
raise errors.InvalidDispenseVolumeError(
|
|
2277
|
-
f"Attempting to dispense {volume}µL of liquid into a well that can only hold
|
|
2133
|
+
f"Attempting to dispense {volume}µL of liquid into a well that can only hold"
|
|
2134
|
+
f" {well_volumetric_capacity}µL (well {well_name} in"
|
|
2135
|
+
f" labware {self._labware.get_display_name(labware_id)})"
|
|
2278
2136
|
)
|
|
2279
2137
|
|
|
2280
2138
|
def get_wells_covered_by_pipette_with_active_well(
|