opentrons 8.7.0a9__py3-none-any.whl → 8.8.0a8__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- opentrons/_version.py +2 -2
- opentrons/cli/analyze.py +4 -1
- opentrons/config/__init__.py +7 -0
- opentrons/drivers/asyncio/communication/serial_connection.py +126 -49
- opentrons/drivers/heater_shaker/abstract.py +5 -0
- opentrons/drivers/heater_shaker/driver.py +10 -0
- opentrons/drivers/heater_shaker/simulator.py +4 -0
- opentrons/drivers/thermocycler/abstract.py +6 -0
- opentrons/drivers/thermocycler/driver.py +61 -10
- opentrons/drivers/thermocycler/simulator.py +6 -0
- opentrons/drivers/vacuum_module/__init__.py +5 -0
- opentrons/drivers/vacuum_module/abstract.py +93 -0
- opentrons/drivers/vacuum_module/driver.py +208 -0
- opentrons/drivers/vacuum_module/errors.py +39 -0
- opentrons/drivers/vacuum_module/simulator.py +85 -0
- opentrons/drivers/vacuum_module/types.py +79 -0
- opentrons/execute.py +3 -0
- opentrons/hardware_control/api.py +24 -5
- opentrons/hardware_control/backends/controller.py +8 -2
- opentrons/hardware_control/backends/flex_protocol.py +1 -0
- opentrons/hardware_control/backends/ot3controller.py +35 -2
- opentrons/hardware_control/backends/ot3simulator.py +3 -1
- opentrons/hardware_control/backends/ot3utils.py +37 -0
- opentrons/hardware_control/backends/simulator.py +2 -1
- opentrons/hardware_control/backends/subsystem_manager.py +5 -2
- opentrons/hardware_control/emulation/abstract_emulator.py +6 -4
- opentrons/hardware_control/emulation/connection_handler.py +8 -5
- opentrons/hardware_control/emulation/heater_shaker.py +12 -3
- opentrons/hardware_control/emulation/settings.py +1 -1
- opentrons/hardware_control/emulation/thermocycler.py +67 -15
- opentrons/hardware_control/module_control.py +105 -10
- 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 +42 -5
- opentrons/hardware_control/modules/magdeck.py +8 -4
- opentrons/hardware_control/modules/mod_abc.py +14 -6
- opentrons/hardware_control/modules/tempdeck.py +25 -5
- opentrons/hardware_control/modules/thermocycler.py +68 -11
- opentrons/hardware_control/modules/types.py +20 -1
- opentrons/hardware_control/modules/utils.py +11 -4
- opentrons/hardware_control/motion_utilities.py +6 -6
- opentrons/hardware_control/nozzle_manager.py +3 -0
- opentrons/hardware_control/ot3api.py +92 -17
- opentrons/hardware_control/poller.py +22 -8
- opentrons/hardware_control/protocols/liquid_handler.py +12 -4
- opentrons/hardware_control/scripts/update_module_fw.py +5 -0
- opentrons/hardware_control/types.py +43 -2
- opentrons/legacy_commands/commands.py +58 -5
- opentrons/legacy_commands/module_commands.py +52 -0
- opentrons/legacy_commands/protocol_commands.py +53 -1
- opentrons/legacy_commands/types.py +155 -1
- opentrons/motion_planning/deck_conflict.py +17 -12
- opentrons/motion_planning/waypoints.py +15 -29
- opentrons/protocol_api/__init__.py +5 -1
- opentrons/protocol_api/_transfer_liquid_validation.py +17 -2
- opentrons/protocol_api/_types.py +8 -1
- opentrons/protocol_api/core/common.py +3 -1
- opentrons/protocol_api/core/engine/_default_labware_versions.py +33 -11
- opentrons/protocol_api/core/engine/deck_conflict.py +3 -1
- opentrons/protocol_api/core/engine/instrument.py +109 -26
- opentrons/protocol_api/core/engine/labware.py +8 -1
- opentrons/protocol_api/core/engine/module_core.py +95 -4
- opentrons/protocol_api/core/engine/pipette_movement_conflict.py +4 -18
- opentrons/protocol_api/core/engine/protocol.py +51 -2
- opentrons/protocol_api/core/engine/stringify.py +2 -0
- opentrons/protocol_api/core/engine/tasks.py +48 -0
- opentrons/protocol_api/core/engine/well.py +8 -0
- opentrons/protocol_api/core/instrument.py +19 -2
- opentrons/protocol_api/core/legacy/legacy_instrument_core.py +19 -2
- opentrons/protocol_api/core/legacy/legacy_module_core.py +33 -2
- opentrons/protocol_api/core/legacy/legacy_protocol_core.py +23 -1
- opentrons/protocol_api/core/legacy/legacy_well_core.py +4 -0
- opentrons/protocol_api/core/legacy/tasks.py +19 -0
- opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +19 -2
- 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 +58 -2
- opentrons/protocol_api/core/protocol.py +23 -2
- opentrons/protocol_api/core/tasks.py +31 -0
- opentrons/protocol_api/core/well.py +4 -0
- opentrons/protocol_api/instrument_context.py +388 -2
- opentrons/protocol_api/labware.py +10 -2
- opentrons/protocol_api/module_contexts.py +170 -6
- opentrons/protocol_api/protocol_context.py +87 -21
- opentrons/protocol_api/robot_context.py +41 -25
- opentrons/protocol_api/tasks.py +48 -0
- opentrons/protocol_api/validation.py +49 -3
- opentrons/protocol_engine/__init__.py +4 -0
- opentrons/protocol_engine/actions/__init__.py +6 -2
- opentrons/protocol_engine/actions/actions.py +31 -9
- opentrons/protocol_engine/clients/sync_client.py +42 -7
- opentrons/protocol_engine/commands/__init__.py +56 -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/absorbance_reader/read.py +22 -23
- opentrons/protocol_engine/commands/aspirate.py +1 -0
- opentrons/protocol_engine/commands/aspirate_while_tracking.py +52 -19
- opentrons/protocol_engine/commands/capture_image.py +302 -0
- opentrons/protocol_engine/commands/command.py +2 -0
- opentrons/protocol_engine/commands/command_unions.py +62 -0
- opentrons/protocol_engine/commands/create_timer.py +83 -0
- opentrons/protocol_engine/commands/dispense.py +1 -0
- opentrons/protocol_engine/commands/dispense_while_tracking.py +56 -19
- opentrons/protocol_engine/commands/drop_tip.py +32 -8
- opentrons/protocol_engine/commands/flex_stacker/common.py +35 -0
- opentrons/protocol_engine/commands/flex_stacker/set_stored_labware.py +7 -0
- opentrons/protocol_engine/commands/heater_shaker/__init__.py +14 -0
- opentrons/protocol_engine/commands/heater_shaker/common.py +20 -0
- opentrons/protocol_engine/commands/heater_shaker/set_and_wait_for_shake_speed.py +5 -4
- opentrons/protocol_engine/commands/heater_shaker/set_shake_speed.py +136 -0
- opentrons/protocol_engine/commands/heater_shaker/set_target_temperature.py +31 -5
- opentrons/protocol_engine/commands/move_labware.py +3 -4
- opentrons/protocol_engine/commands/move_to_addressable_area_for_drop_tip.py +1 -1
- opentrons/protocol_engine/commands/movement_common.py +31 -2
- opentrons/protocol_engine/commands/pick_up_tip.py +21 -11
- opentrons/protocol_engine/commands/pipetting_common.py +48 -3
- opentrons/protocol_engine/commands/set_tip_state.py +97 -0
- opentrons/protocol_engine/commands/temperature_module/set_target_temperature.py +38 -7
- opentrons/protocol_engine/commands/thermocycler/__init__.py +16 -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 +44 -7
- opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py +43 -14
- opentrons/protocol_engine/commands/thermocycler/start_run_extended_profile.py +191 -0
- 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/create_protocol_engine.py +12 -0
- opentrons/protocol_engine/engine_support.py +3 -0
- opentrons/protocol_engine/errors/__init__.py +12 -0
- opentrons/protocol_engine/errors/exceptions.py +119 -0
- opentrons/protocol_engine/execution/__init__.py +4 -0
- opentrons/protocol_engine/execution/command_executor.py +62 -1
- opentrons/protocol_engine/execution/create_queue_worker.py +9 -2
- opentrons/protocol_engine/execution/labware_movement.py +13 -15
- opentrons/protocol_engine/execution/movement.py +2 -0
- opentrons/protocol_engine/execution/pipetting.py +26 -25
- 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 +137 -36
- opentrons/protocol_engine/resources/__init__.py +4 -0
- opentrons/protocol_engine/resources/camera_provider.py +110 -0
- opentrons/protocol_engine/resources/concurrency_provider.py +27 -0
- opentrons/protocol_engine/resources/deck_configuration_provider.py +7 -0
- opentrons/protocol_engine/resources/file_provider.py +133 -58
- opentrons/protocol_engine/resources/labware_validation.py +10 -6
- opentrons/protocol_engine/slot_standardization.py +2 -0
- opentrons/protocol_engine/state/_well_math.py +60 -18
- opentrons/protocol_engine/state/addressable_areas.py +2 -0
- opentrons/protocol_engine/state/camera.py +54 -0
- opentrons/protocol_engine/state/commands.py +37 -14
- opentrons/protocol_engine/state/geometry.py +276 -379
- opentrons/protocol_engine/state/labware.py +62 -108
- opentrons/protocol_engine/state/labware_origin_math/errors.py +94 -0
- opentrons/protocol_engine/state/labware_origin_math/stackup_origin_to_labware_origin.py +1336 -0
- opentrons/protocol_engine/state/module_substates/thermocycler_module_substate.py +37 -0
- opentrons/protocol_engine/state/modules.py +30 -8
- opentrons/protocol_engine/state/motion.py +60 -18
- opentrons/protocol_engine/state/preconditions.py +59 -0
- opentrons/protocol_engine/state/state.py +44 -0
- opentrons/protocol_engine/state/state_summary.py +4 -0
- opentrons/protocol_engine/state/tasks.py +139 -0
- opentrons/protocol_engine/state/tips.py +177 -258
- opentrons/protocol_engine/state/update_types.py +26 -9
- opentrons/protocol_engine/types/__init__.py +23 -4
- opentrons/protocol_engine/types/command_preconditions.py +18 -0
- 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/location.py +26 -2
- opentrons/protocol_engine/types/module.py +11 -1
- 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/protocol_runner.py +14 -1
- opentrons/protocol_runner/run_orchestrator.py +49 -2
- opentrons/protocols/advanced_control/transfers/transfer_liquid_utils.py +2 -2
- opentrons/protocols/api_support/definitions.py +1 -1
- opentrons/protocols/api_support/types.py +2 -1
- opentrons/simulate.py +51 -15
- opentrons/system/camera.py +334 -4
- opentrons/system/ffmpeg.py +110 -0
- {opentrons-8.7.0a9.dist-info → opentrons-8.8.0a8.dist-info}/METADATA +4 -4
- {opentrons-8.7.0a9.dist-info → opentrons-8.8.0a8.dist-info}/RECORD +189 -161
- opentrons/protocol_engine/state/_labware_origin_math.py +0 -636
- {opentrons-8.7.0a9.dist-info → opentrons-8.8.0a8.dist-info}/WHEEL +0 -0
- {opentrons-8.7.0a9.dist-info → opentrons-8.8.0a8.dist-info}/entry_points.txt +0 -0
- {opentrons-8.7.0a9.dist-info → opentrons-8.8.0a8.dist-info}/licenses/LICENSE +0 -0
|
@@ -52,11 +52,11 @@ from ..types import (
|
|
|
52
52
|
LabwareOffsetLocationSequence,
|
|
53
53
|
LegacyLabwareOffsetLocation,
|
|
54
54
|
InStackerHopperLocation,
|
|
55
|
+
WASTE_CHUTE_LOCATION,
|
|
55
56
|
LabwareLocation,
|
|
56
57
|
LoadedLabware,
|
|
57
58
|
ModuleLocation,
|
|
58
59
|
OverlapOffset,
|
|
59
|
-
LabwareMovementOffsetData,
|
|
60
60
|
OnDeckLabwareLocation,
|
|
61
61
|
OFF_DECK_LOCATION,
|
|
62
62
|
)
|
|
@@ -344,14 +344,18 @@ class LabwareStore(HasState[LabwareState], HandlesActions):
|
|
|
344
344
|
self, labware_id: str, new_location: LabwareLocation, new_offset_id: str | None
|
|
345
345
|
) -> None:
|
|
346
346
|
self._state.labware_by_id[labware_id].offsetId = new_offset_id
|
|
347
|
-
|
|
348
347
|
if isinstance(new_location, AddressableAreaLocation) and (
|
|
349
|
-
fixture_validation.
|
|
350
|
-
or fixture_validation.is_trash(new_location.addressableAreaName)
|
|
348
|
+
fixture_validation.is_trash(new_location.addressableAreaName)
|
|
351
349
|
):
|
|
352
|
-
#
|
|
350
|
+
# TODO (RC, 2025-10-07: create a specific trash off deck location)
|
|
351
|
+
# If a labware has been moved into trash and is now technically off deck
|
|
353
352
|
new_location = OFF_DECK_LOCATION
|
|
354
|
-
|
|
353
|
+
elif isinstance(
|
|
354
|
+
new_location, AddressableAreaLocation
|
|
355
|
+
) and fixture_validation.is_gripper_waste_chute(
|
|
356
|
+
new_location.addressableAreaName
|
|
357
|
+
):
|
|
358
|
+
new_location = WASTE_CHUTE_LOCATION
|
|
355
359
|
self._state.labware_by_id[labware_id].location = new_location
|
|
356
360
|
|
|
357
361
|
def _set_labware_location(self, state_update: update_types.StateUpdate) -> None:
|
|
@@ -401,7 +405,7 @@ class LabwareView:
|
|
|
401
405
|
return self._state.labware_by_id[labware_id]
|
|
402
406
|
except KeyError as e:
|
|
403
407
|
raise errors.LabwareNotLoadedError(
|
|
404
|
-
f"Labware {labware_id} not found."
|
|
408
|
+
f"Labware with id {labware_id} not found."
|
|
405
409
|
) from e
|
|
406
410
|
|
|
407
411
|
def known(self, labware_id: str) -> bool:
|
|
@@ -430,7 +434,7 @@ class LabwareView:
|
|
|
430
434
|
):
|
|
431
435
|
return labware.id
|
|
432
436
|
raise errors.exceptions.LabwareNotLoadedOnLabwareError(
|
|
433
|
-
f"There is not labware loaded onto labware {labware_id}"
|
|
437
|
+
f"There is not labware loaded onto labware {self.get_display_name(labware_id)}"
|
|
434
438
|
)
|
|
435
439
|
|
|
436
440
|
def raise_if_labware_has_non_lid_labware_on_top(self, labware_id: str) -> None:
|
|
@@ -443,7 +447,8 @@ class LabwareView:
|
|
|
443
447
|
and candidate_id != lid_id
|
|
444
448
|
):
|
|
445
449
|
raise errors.LabwareIsInStackError(
|
|
446
|
-
f"Cannot access labware {labware_id} because it has
|
|
450
|
+
f"Cannot access labware {self.get_display_name(labware_id)} because it has"
|
|
451
|
+
" a non-lid labware stacked on top."
|
|
447
452
|
)
|
|
448
453
|
|
|
449
454
|
def raise_if_labware_has_labware_on_top(self, labware_id: str) -> None:
|
|
@@ -454,16 +459,35 @@ class LabwareView:
|
|
|
454
459
|
and labware.location.labwareId == labware_id
|
|
455
460
|
):
|
|
456
461
|
raise errors.LabwareIsInStackError(
|
|
457
|
-
f"Cannot access labware {labware_id} because it has
|
|
462
|
+
f"Cannot access labware {self.get_display_name(labware_id)} because it has"
|
|
463
|
+
" another labware stacked on top."
|
|
458
464
|
)
|
|
459
465
|
|
|
466
|
+
def raise_if_not_tip_rack(self, labware_id: str) -> None:
|
|
467
|
+
"""Raise if a labware is not a tip rack."""
|
|
468
|
+
if not self.is_tiprack(labware_id):
|
|
469
|
+
raise errors.LabwareIsNotTipRackError(
|
|
470
|
+
f"Labware {self.get_display_name(labware_id)} is not a tip rack and cannot have its well states set."
|
|
471
|
+
)
|
|
472
|
+
|
|
473
|
+
def raise_if_wells_are_invalid(
|
|
474
|
+
self, labware_id: str, well_names: List[str]
|
|
475
|
+
) -> None:
|
|
476
|
+
"""Raise if given wells do not exist with the given labware ID."""
|
|
477
|
+
non_existent_wells = set(well_names) - set(
|
|
478
|
+
self.get_definition(labware_id).wells
|
|
479
|
+
)
|
|
480
|
+
if non_existent_wells:
|
|
481
|
+
raise errors.WellDoesNotExistError(
|
|
482
|
+
f"Tip rack {self.get_display_name(labware_id)} does not have wells: {', '.join(non_existent_wells)}"
|
|
483
|
+
)
|
|
484
|
+
|
|
460
485
|
def get_by_slot(
|
|
461
486
|
self,
|
|
462
487
|
slot_name: Union[DeckSlotName, StagingSlotName],
|
|
463
488
|
) -> Optional[LoadedLabware]:
|
|
464
489
|
"""Get the labware located in a given slot, if any."""
|
|
465
490
|
loaded_labware = list(self._state.labware_by_id.values())
|
|
466
|
-
|
|
467
491
|
for labware in loaded_labware:
|
|
468
492
|
if (
|
|
469
493
|
isinstance(labware.location, DeckSlotLocation)
|
|
@@ -663,6 +687,14 @@ class LabwareView:
|
|
|
663
687
|
or len(self.get_definition(labware_id).wells) >= 96
|
|
664
688
|
)
|
|
665
689
|
|
|
690
|
+
def get_has_96_subwells(self, labware_id: str) -> bool:
|
|
691
|
+
"""True if a labware is a reservoir with a 96-grid of sub-wells."""
|
|
692
|
+
return self.get_has_quirk(labware_id, "offsetPipetteFor96GridSubwells")
|
|
693
|
+
|
|
694
|
+
def get_has_12_subwells(self, labware_id: str) -> bool:
|
|
695
|
+
"""True if a labware is a reservoir with a 12-grid of sub-wells."""
|
|
696
|
+
return self.get_has_quirk(labware_id, "offsetPipetteFor12GridSubwells")
|
|
697
|
+
|
|
666
698
|
def get_well_definition(
|
|
667
699
|
self,
|
|
668
700
|
labware_id: str,
|
|
@@ -681,7 +713,7 @@ class LabwareView:
|
|
|
681
713
|
return definition.wells[well_name]
|
|
682
714
|
except KeyError as e:
|
|
683
715
|
raise errors.WellDoesNotExistError(
|
|
684
|
-
f"{well_name} does not exist in {labware_id}."
|
|
716
|
+
f"{well_name} does not exist in {self.get_display_name(labware_id)}."
|
|
685
717
|
) from e
|
|
686
718
|
|
|
687
719
|
def get_well_geometry(
|
|
@@ -691,19 +723,21 @@ class LabwareView:
|
|
|
691
723
|
labware_def = self.get_definition(labware_id)
|
|
692
724
|
if labware_def.innerLabwareGeometry is None:
|
|
693
725
|
raise errors.IncompleteLabwareDefinitionError(
|
|
694
|
-
message=f"No innerLabwareGeometry found in labware definition for
|
|
726
|
+
message=f"No innerLabwareGeometry found in labware definition for {self.get_display_name(labware_id)}."
|
|
695
727
|
)
|
|
696
728
|
well_def = self.get_well_definition(labware_id, well_name)
|
|
697
729
|
geometry_id = well_def.geometryDefinitionId
|
|
698
730
|
if geometry_id is None:
|
|
699
731
|
raise errors.IncompleteWellDefinitionError(
|
|
700
|
-
message=f"No geometryDefinitionId found in well definition for well
|
|
732
|
+
message=f"No geometryDefinitionId found in well definition for well {well_name}"
|
|
733
|
+
f" for {self.get_display_name(labware_id)}"
|
|
701
734
|
)
|
|
702
735
|
else:
|
|
703
736
|
well_geometry = labware_def.innerLabwareGeometry.get(geometry_id)
|
|
704
737
|
if well_geometry is None:
|
|
705
738
|
raise errors.IncompleteLabwareDefinitionError(
|
|
706
|
-
message=f"No innerLabwareGeometry found in labware definition for
|
|
739
|
+
message=f"No innerLabwareGeometry found in labware definition for geometry id {geometry_id}"
|
|
740
|
+
f" for {self.get_display_name(labware_id)}"
|
|
707
741
|
)
|
|
708
742
|
return well_geometry
|
|
709
743
|
|
|
@@ -772,15 +806,15 @@ class LabwareView:
|
|
|
772
806
|
contains_wells = all(well_name in labware_wells for well_name in iter(wells))
|
|
773
807
|
if labware_definition.parameters.isTiprack:
|
|
774
808
|
raise errors.LabwareIsTipRackError(
|
|
775
|
-
f"Given labware
|
|
809
|
+
f"Given labware {self.get_display_name(labware_id)} is a tip rack. Can not load liquid."
|
|
776
810
|
)
|
|
777
811
|
if LabwareRole.adapter in labware_definition.allowedRoles:
|
|
778
812
|
raise errors.LabwareIsAdapterError(
|
|
779
|
-
f"Given labware
|
|
813
|
+
f"Given labware {self.get_display_name(labware_id)} is an adapter. Can not load liquid."
|
|
780
814
|
)
|
|
781
815
|
if not contains_wells:
|
|
782
816
|
raise errors.WellDoesNotExistError(
|
|
783
|
-
f"Some of the supplied wells do not match the
|
|
817
|
+
f"Some of the supplied wells do not match the labware {self.get_display_name(labware_id)}."
|
|
784
818
|
)
|
|
785
819
|
return list(wells)
|
|
786
820
|
|
|
@@ -789,7 +823,7 @@ class LabwareView:
|
|
|
789
823
|
definition = self.get_definition(labware_id)
|
|
790
824
|
if definition.parameters.tipLength is None:
|
|
791
825
|
raise errors.LabwareIsNotTipRackError(
|
|
792
|
-
f"Labware {labware_id} has no tip length defined."
|
|
826
|
+
f"Labware {self.get_display_name(labware_id)} has no tip length defined."
|
|
793
827
|
)
|
|
794
828
|
|
|
795
829
|
return definition.parameters.tipLength - overlap
|
|
@@ -1095,7 +1129,9 @@ class LabwareView:
|
|
|
1095
1129
|
raise errors.LabwareCannotBeStackedError(
|
|
1096
1130
|
f"Labware {lid_labware_definition.parameters.loadName} cannot be used as a lid in the Flex Stacker."
|
|
1097
1131
|
)
|
|
1098
|
-
if
|
|
1132
|
+
if isinstance(
|
|
1133
|
+
lid_labware_definition, LabwareDefinition2
|
|
1134
|
+
) and not labware_validation.validate_legacy_labware_can_be_stacked(
|
|
1099
1135
|
lid_labware_definition, primary_labware_definition.parameters.loadName
|
|
1100
1136
|
):
|
|
1101
1137
|
raise errors.LabwareCannotBeStackedError(
|
|
@@ -1108,7 +1144,9 @@ class LabwareView:
|
|
|
1108
1144
|
raise errors.LabwareCannotBeStackedError(
|
|
1109
1145
|
f"Labware {adapter_labware_definition.parameters.loadName} cannot be used as an adapter in the Flex Stacker."
|
|
1110
1146
|
)
|
|
1111
|
-
if
|
|
1147
|
+
if isinstance(
|
|
1148
|
+
primary_labware_definition, LabwareDefinition2
|
|
1149
|
+
) and not labware_validation.validate_legacy_labware_can_be_stacked(
|
|
1112
1150
|
primary_labware_definition,
|
|
1113
1151
|
adapter_labware_definition.parameters.loadName,
|
|
1114
1152
|
):
|
|
@@ -1162,9 +1200,9 @@ class LabwareView:
|
|
|
1162
1200
|
below_labware = self.get(bottom_labware_id)
|
|
1163
1201
|
if isinstance(
|
|
1164
1202
|
top_labware_definition, LabwareDefinition2
|
|
1165
|
-
) and not labware_validation.
|
|
1166
|
-
|
|
1167
|
-
|
|
1203
|
+
) and not labware_validation.validate_legacy_labware_can_be_stacked(
|
|
1204
|
+
child_labware_definition=top_labware_definition,
|
|
1205
|
+
parent_labware_load_name=below_labware.loadName,
|
|
1168
1206
|
):
|
|
1169
1207
|
raise errors.LabwareCannotBeStackedError(
|
|
1170
1208
|
f"Labware {top_labware_definition.parameters.loadName} cannot be loaded onto labware {below_labware.loadName}"
|
|
@@ -1225,28 +1263,6 @@ class LabwareView:
|
|
|
1225
1263
|
uri = self.get_uri_from_definition(self.get_definition(labware_id))
|
|
1226
1264
|
return uri in _MAGDECK_HALF_MM_LABWARE
|
|
1227
1265
|
|
|
1228
|
-
def get_deck_default_gripper_offsets(self) -> Optional[LabwareMovementOffsetData]:
|
|
1229
|
-
"""Get the deck's default gripper offsets."""
|
|
1230
|
-
parsed_offsets = (
|
|
1231
|
-
self.get_deck_definition().get("gripperOffsets", {}).get("default")
|
|
1232
|
-
)
|
|
1233
|
-
return (
|
|
1234
|
-
LabwareMovementOffsetData(
|
|
1235
|
-
pickUpOffset=LabwareOffsetVector(
|
|
1236
|
-
x=parsed_offsets["pickUpOffset"]["x"],
|
|
1237
|
-
y=parsed_offsets["pickUpOffset"]["y"],
|
|
1238
|
-
z=parsed_offsets["pickUpOffset"]["z"],
|
|
1239
|
-
),
|
|
1240
|
-
dropOffset=LabwareOffsetVector(
|
|
1241
|
-
x=parsed_offsets["dropOffset"]["x"],
|
|
1242
|
-
y=parsed_offsets["dropOffset"]["y"],
|
|
1243
|
-
z=parsed_offsets["dropOffset"]["z"],
|
|
1244
|
-
),
|
|
1245
|
-
)
|
|
1246
|
-
if parsed_offsets
|
|
1247
|
-
else None
|
|
1248
|
-
)
|
|
1249
|
-
|
|
1250
1266
|
def get_absorbance_reader_lid_definition(self) -> LabwareDefinition:
|
|
1251
1267
|
"""Return the special labware definition for the plate reader lid.
|
|
1252
1268
|
|
|
@@ -1257,68 +1273,6 @@ class LabwareView:
|
|
|
1257
1273
|
"opentrons/opentrons_flex_lid_absorbance_plate_reader_module/1"
|
|
1258
1274
|
]
|
|
1259
1275
|
|
|
1260
|
-
@overload
|
|
1261
|
-
def get_child_gripper_offsets(
|
|
1262
|
-
self,
|
|
1263
|
-
*,
|
|
1264
|
-
labware_definition: LabwareDefinition,
|
|
1265
|
-
slot_name: Optional[DeckSlotName],
|
|
1266
|
-
) -> Optional[LabwareMovementOffsetData]:
|
|
1267
|
-
pass
|
|
1268
|
-
|
|
1269
|
-
@overload
|
|
1270
|
-
def get_child_gripper_offsets(
|
|
1271
|
-
self, *, labware_id: str, slot_name: Optional[DeckSlotName]
|
|
1272
|
-
) -> Optional[LabwareMovementOffsetData]:
|
|
1273
|
-
pass
|
|
1274
|
-
|
|
1275
|
-
def get_child_gripper_offsets(
|
|
1276
|
-
self,
|
|
1277
|
-
*,
|
|
1278
|
-
labware_definition: Optional[LabwareDefinition] = None,
|
|
1279
|
-
labware_id: Optional[str] = None,
|
|
1280
|
-
slot_name: Optional[DeckSlotName],
|
|
1281
|
-
) -> Optional[LabwareMovementOffsetData]:
|
|
1282
|
-
"""Get the grip offsets that a labware says should be applied to children stacked atop it.
|
|
1283
|
-
|
|
1284
|
-
Params:
|
|
1285
|
-
labware_id: The ID of a parent labware (atop which another labware, the child, will be stacked).
|
|
1286
|
-
slot_name: The ancestor slot that the parent labware is ultimately loaded into,
|
|
1287
|
-
perhaps after going through a module in the middle.
|
|
1288
|
-
|
|
1289
|
-
Returns:
|
|
1290
|
-
If `slot_name` is provided, returns the gripper offsets that the parent labware definition
|
|
1291
|
-
specifies just for that slot, or `None` if the labware definition doesn't have an
|
|
1292
|
-
exact match.
|
|
1293
|
-
|
|
1294
|
-
If `slot_name` is `None`, returns the gripper offsets that the parent labware
|
|
1295
|
-
definition designates as "default," or `None` if it doesn't designate any as such.
|
|
1296
|
-
"""
|
|
1297
|
-
if labware_id is not None:
|
|
1298
|
-
labware_definition = self.get_definition(labware_id)
|
|
1299
|
-
else:
|
|
1300
|
-
# Should be ensured by our @overloads.
|
|
1301
|
-
assert labware_definition is not None
|
|
1302
|
-
|
|
1303
|
-
parsed_offsets = labware_definition.gripperOffsets
|
|
1304
|
-
offset_key = slot_name.id if slot_name else "default"
|
|
1305
|
-
|
|
1306
|
-
if parsed_offsets is None or offset_key not in parsed_offsets:
|
|
1307
|
-
return None
|
|
1308
|
-
else:
|
|
1309
|
-
return LabwareMovementOffsetData(
|
|
1310
|
-
pickUpOffset=LabwareOffsetVector.model_construct(
|
|
1311
|
-
x=parsed_offsets[offset_key].pickUpOffset.x,
|
|
1312
|
-
y=parsed_offsets[offset_key].pickUpOffset.y,
|
|
1313
|
-
z=parsed_offsets[offset_key].pickUpOffset.z,
|
|
1314
|
-
),
|
|
1315
|
-
dropOffset=LabwareOffsetVector.model_construct(
|
|
1316
|
-
x=parsed_offsets[offset_key].dropOffset.x,
|
|
1317
|
-
y=parsed_offsets[offset_key].dropOffset.y,
|
|
1318
|
-
z=parsed_offsets[offset_key].dropOffset.z,
|
|
1319
|
-
),
|
|
1320
|
-
)
|
|
1321
|
-
|
|
1322
1276
|
def get_grip_force(self, labware_definition: LabwareDefinition) -> float:
|
|
1323
1277
|
"""Get the recommended grip force for gripping labware using gripper."""
|
|
1324
1278
|
recommended_force = labware_definition.gripForce
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""Labware origin math errors."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict, Optional, Sequence
|
|
4
|
+
|
|
5
|
+
from opentrons.protocol_engine.errors import ProtocolEngineError
|
|
6
|
+
from opentrons_shared_data.errors import ErrorCodes
|
|
7
|
+
from opentrons_shared_data.errors.exceptions import EnumeratedError
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class LabwareLocatingFeatureError(ProtocolEngineError):
|
|
11
|
+
"""Base class for errors related to labware locating features."""
|
|
12
|
+
|
|
13
|
+
def __init__(
|
|
14
|
+
self,
|
|
15
|
+
message: Optional[str] = None,
|
|
16
|
+
details: Optional[Dict[str, Any]] = None,
|
|
17
|
+
wrapping: Optional[Sequence[EnumeratedError]] = None,
|
|
18
|
+
) -> None:
|
|
19
|
+
"""Build a LabwareLocatingFeatureError."""
|
|
20
|
+
super().__init__(
|
|
21
|
+
ErrorCodes.LABWARE_LOCATING_FEATURE_ERROR, message, details, wrapping
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class MissingLocatingFeatureError(LabwareLocatingFeatureError):
|
|
26
|
+
"""Raised when a labware definition is missing a required locating feature."""
|
|
27
|
+
|
|
28
|
+
def __init__(
|
|
29
|
+
self,
|
|
30
|
+
labware_name: str,
|
|
31
|
+
required_feature: str,
|
|
32
|
+
message: Optional[str] = None,
|
|
33
|
+
details: Optional[Dict[str, Any]] = None,
|
|
34
|
+
wrapping: Optional[Sequence[EnumeratedError]] = None,
|
|
35
|
+
) -> None:
|
|
36
|
+
"""Build a MissingLocatingFeatureError."""
|
|
37
|
+
if message is None:
|
|
38
|
+
message = f"Expected {labware_name} to have {required_feature} feature"
|
|
39
|
+
|
|
40
|
+
if details is None:
|
|
41
|
+
details = {
|
|
42
|
+
"labware_name": labware_name,
|
|
43
|
+
"required_feature": required_feature,
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
super().__init__(message, details, wrapping)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class InvalidLabwarePlacementError(LabwareLocatingFeatureError):
|
|
50
|
+
"""Raised when a labware cannot be placed in the specified location due to locating feature constraints."""
|
|
51
|
+
|
|
52
|
+
def __init__(
|
|
53
|
+
self,
|
|
54
|
+
feature_name: str,
|
|
55
|
+
invalid_placement: str,
|
|
56
|
+
message: Optional[str] = None,
|
|
57
|
+
details: Optional[Dict[str, Any]] = None,
|
|
58
|
+
wrapping: Optional[Sequence[EnumeratedError]] = None,
|
|
59
|
+
) -> None:
|
|
60
|
+
"""Build an InvalidLabwarePlacementError."""
|
|
61
|
+
if message is None:
|
|
62
|
+
message = f"{feature_name} feature does not support placement: {invalid_placement}"
|
|
63
|
+
|
|
64
|
+
if details is None:
|
|
65
|
+
details = {
|
|
66
|
+
"feature_name": feature_name,
|
|
67
|
+
"invalid_placement": invalid_placement,
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
super().__init__(message, details, wrapping)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class IncompatibleLocatingFeatureError(LabwareLocatingFeatureError):
|
|
74
|
+
"""Raised when parent and child labware have incompatible locating features."""
|
|
75
|
+
|
|
76
|
+
def __init__(
|
|
77
|
+
self,
|
|
78
|
+
parent_feature: str,
|
|
79
|
+
child_feature: str,
|
|
80
|
+
message: Optional[str] = None,
|
|
81
|
+
details: Optional[Dict[str, Any]] = None,
|
|
82
|
+
wrapping: Optional[Sequence[EnumeratedError]] = None,
|
|
83
|
+
) -> None:
|
|
84
|
+
"""Build an IncompatibleLocatingFeatureError."""
|
|
85
|
+
if message is None:
|
|
86
|
+
message = f"Incompatible labware features: parent {parent_feature}, child {child_feature}"
|
|
87
|
+
|
|
88
|
+
if details is None:
|
|
89
|
+
details = {
|
|
90
|
+
"parent_feature": parent_feature,
|
|
91
|
+
"child_feature": child_feature,
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
super().__init__(message, details, wrapping)
|