opentrons 8.7.0a5__py3-none-any.whl → 8.7.0a7__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/asyncio/communication/serial_connection.py +129 -52
- 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/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/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 +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 +42 -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 +68 -11
- 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/poller.py +22 -8
- opentrons/hardware_control/scripts/update_module_fw.py +5 -0
- opentrons/hardware_control/types.py +31 -2
- opentrons/legacy_commands/module_commands.py +23 -0
- opentrons/legacy_commands/protocol_commands.py +20 -0
- opentrons/legacy_commands/types.py +80 -0
- 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/_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/labware.py +8 -1
- opentrons/protocol_api/core/engine/module_core.py +75 -8
- opentrons/protocol_api/core/engine/protocol.py +18 -1
- opentrons/protocol_api/core/engine/tasks.py +48 -0
- opentrons/protocol_api/core/engine/well.py +8 -0
- opentrons/protocol_api/core/legacy/legacy_module_core.py +24 -4
- opentrons/protocol_api/core/legacy/legacy_protocol_core.py +11 -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_protocol_core.py +14 -2
- opentrons/protocol_api/core/legacy_simulator/tasks.py +19 -0
- opentrons/protocol_api/core/module.py +37 -4
- opentrons/protocol_api/core/protocol.py +11 -2
- opentrons/protocol_api/core/tasks.py +31 -0
- opentrons/protocol_api/core/well.py +4 -0
- opentrons/protocol_api/labware.py +5 -0
- opentrons/protocol_api/module_contexts.py +117 -11
- 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 +42 -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 +49 -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/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/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/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 +40 -6
- opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py +29 -5
- 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/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 +75 -34
- 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 +14 -11
- opentrons/protocol_engine/state/geometry.py +213 -374
- 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 +21 -8
- 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.0a5.dist-info → opentrons-8.7.0a7.dist-info}/METADATA +4 -4
- {opentrons-8.7.0a5.dist-info → opentrons-8.7.0a7.dist-info}/RECORD +143 -127
- opentrons/protocol_engine/state/_labware_origin_math.py +0 -636
- {opentrons-8.7.0a5.dist-info → opentrons-8.7.0a7.dist-info}/WHEEL +0 -0
- {opentrons-8.7.0a5.dist-info → opentrons-8.7.0a7.dist-info}/entry_points.txt +0 -0
- {opentrons-8.7.0a5.dist-info → opentrons-8.7.0a7.dist-info}/licenses/LICENSE +0 -0
|
@@ -56,7 +56,6 @@ from ..types import (
|
|
|
56
56
|
LoadedLabware,
|
|
57
57
|
ModuleLocation,
|
|
58
58
|
OverlapOffset,
|
|
59
|
-
LabwareMovementOffsetData,
|
|
60
59
|
OnDeckLabwareLocation,
|
|
61
60
|
OFF_DECK_LOCATION,
|
|
62
61
|
)
|
|
@@ -401,7 +400,7 @@ class LabwareView:
|
|
|
401
400
|
return self._state.labware_by_id[labware_id]
|
|
402
401
|
except KeyError as e:
|
|
403
402
|
raise errors.LabwareNotLoadedError(
|
|
404
|
-
f"Labware {labware_id} not found."
|
|
403
|
+
f"Labware with id {labware_id} not found."
|
|
405
404
|
) from e
|
|
406
405
|
|
|
407
406
|
def known(self, labware_id: str) -> bool:
|
|
@@ -430,7 +429,7 @@ class LabwareView:
|
|
|
430
429
|
):
|
|
431
430
|
return labware.id
|
|
432
431
|
raise errors.exceptions.LabwareNotLoadedOnLabwareError(
|
|
433
|
-
f"There is not labware loaded onto labware {labware_id}"
|
|
432
|
+
f"There is not labware loaded onto labware {self.get_display_name(labware_id)}"
|
|
434
433
|
)
|
|
435
434
|
|
|
436
435
|
def raise_if_labware_has_non_lid_labware_on_top(self, labware_id: str) -> None:
|
|
@@ -443,7 +442,8 @@ class LabwareView:
|
|
|
443
442
|
and candidate_id != lid_id
|
|
444
443
|
):
|
|
445
444
|
raise errors.LabwareIsInStackError(
|
|
446
|
-
f"Cannot access labware {labware_id} because it has
|
|
445
|
+
f"Cannot access labware {self.get_display_name(labware_id)} because it has"
|
|
446
|
+
" a non-lid labware stacked on top."
|
|
447
447
|
)
|
|
448
448
|
|
|
449
449
|
def raise_if_labware_has_labware_on_top(self, labware_id: str) -> None:
|
|
@@ -454,9 +454,29 @@ class LabwareView:
|
|
|
454
454
|
and labware.location.labwareId == labware_id
|
|
455
455
|
):
|
|
456
456
|
raise errors.LabwareIsInStackError(
|
|
457
|
-
f"Cannot access labware {labware_id} because it has
|
|
457
|
+
f"Cannot access labware {self.get_display_name(labware_id)} because it has"
|
|
458
|
+
" another labware stacked on top."
|
|
458
459
|
)
|
|
459
460
|
|
|
461
|
+
def raise_if_not_tip_rack(self, labware_id: str) -> None:
|
|
462
|
+
"""Raise if a labware is not a tip rack."""
|
|
463
|
+
if not self.is_tiprack(labware_id):
|
|
464
|
+
raise errors.LabwareIsNotTipRackError(
|
|
465
|
+
f"Labware {self.get_display_name(labware_id)} is not a tip rack and cannot have its well states set."
|
|
466
|
+
)
|
|
467
|
+
|
|
468
|
+
def raise_if_wells_are_invalid(
|
|
469
|
+
self, labware_id: str, well_names: List[str]
|
|
470
|
+
) -> None:
|
|
471
|
+
"""Raise if given wells do not exist with the given labware ID."""
|
|
472
|
+
non_existent_wells = set(well_names) - set(
|
|
473
|
+
self.get_definition(labware_id).wells
|
|
474
|
+
)
|
|
475
|
+
if non_existent_wells:
|
|
476
|
+
raise errors.WellDoesNotExistError(
|
|
477
|
+
f"Tip rack {self.get_display_name(labware_id)} does not have wells: {', '.join(non_existent_wells)}"
|
|
478
|
+
)
|
|
479
|
+
|
|
460
480
|
def get_by_slot(
|
|
461
481
|
self,
|
|
462
482
|
slot_name: Union[DeckSlotName, StagingSlotName],
|
|
@@ -663,6 +683,14 @@ class LabwareView:
|
|
|
663
683
|
or len(self.get_definition(labware_id).wells) >= 96
|
|
664
684
|
)
|
|
665
685
|
|
|
686
|
+
def get_has_96_subwells(self, labware_id: str) -> bool:
|
|
687
|
+
"""True if a labware is a reservoir with a 96-grid of sub-wells."""
|
|
688
|
+
return self.get_has_quirk(labware_id, "offsetPipetteFor96GridSubwells")
|
|
689
|
+
|
|
690
|
+
def get_has_12_subwells(self, labware_id: str) -> bool:
|
|
691
|
+
"""True if a labware is a reservoir with a 12-grid of sub-wells."""
|
|
692
|
+
return self.get_has_quirk(labware_id, "offsetPipetteFor12GridSubwells")
|
|
693
|
+
|
|
666
694
|
def get_well_definition(
|
|
667
695
|
self,
|
|
668
696
|
labware_id: str,
|
|
@@ -681,7 +709,7 @@ class LabwareView:
|
|
|
681
709
|
return definition.wells[well_name]
|
|
682
710
|
except KeyError as e:
|
|
683
711
|
raise errors.WellDoesNotExistError(
|
|
684
|
-
f"{well_name} does not exist in {labware_id}."
|
|
712
|
+
f"{well_name} does not exist in {self.get_display_name(labware_id)}."
|
|
685
713
|
) from e
|
|
686
714
|
|
|
687
715
|
def get_well_geometry(
|
|
@@ -691,19 +719,21 @@ class LabwareView:
|
|
|
691
719
|
labware_def = self.get_definition(labware_id)
|
|
692
720
|
if labware_def.innerLabwareGeometry is None:
|
|
693
721
|
raise errors.IncompleteLabwareDefinitionError(
|
|
694
|
-
message=f"No innerLabwareGeometry found in labware definition for
|
|
722
|
+
message=f"No innerLabwareGeometry found in labware definition for {self.get_display_name(labware_id)}."
|
|
695
723
|
)
|
|
696
724
|
well_def = self.get_well_definition(labware_id, well_name)
|
|
697
725
|
geometry_id = well_def.geometryDefinitionId
|
|
698
726
|
if geometry_id is None:
|
|
699
727
|
raise errors.IncompleteWellDefinitionError(
|
|
700
|
-
message=f"No geometryDefinitionId found in well definition for well
|
|
728
|
+
message=f"No geometryDefinitionId found in well definition for well {well_name}"
|
|
729
|
+
f" for {self.get_display_name(labware_id)}"
|
|
701
730
|
)
|
|
702
731
|
else:
|
|
703
732
|
well_geometry = labware_def.innerLabwareGeometry.get(geometry_id)
|
|
704
733
|
if well_geometry is None:
|
|
705
734
|
raise errors.IncompleteLabwareDefinitionError(
|
|
706
|
-
message=f"No innerLabwareGeometry found in labware definition for
|
|
735
|
+
message=f"No innerLabwareGeometry found in labware definition for geometry id {geometry_id}"
|
|
736
|
+
f" for {self.get_display_name(labware_id)}"
|
|
707
737
|
)
|
|
708
738
|
return well_geometry
|
|
709
739
|
|
|
@@ -772,15 +802,15 @@ class LabwareView:
|
|
|
772
802
|
contains_wells = all(well_name in labware_wells for well_name in iter(wells))
|
|
773
803
|
if labware_definition.parameters.isTiprack:
|
|
774
804
|
raise errors.LabwareIsTipRackError(
|
|
775
|
-
f"Given labware
|
|
805
|
+
f"Given labware {self.get_display_name(labware_id)} is a tip rack. Can not load liquid."
|
|
776
806
|
)
|
|
777
807
|
if LabwareRole.adapter in labware_definition.allowedRoles:
|
|
778
808
|
raise errors.LabwareIsAdapterError(
|
|
779
|
-
f"Given labware
|
|
809
|
+
f"Given labware {self.get_display_name(labware_id)} is an adapter. Can not load liquid."
|
|
780
810
|
)
|
|
781
811
|
if not contains_wells:
|
|
782
812
|
raise errors.WellDoesNotExistError(
|
|
783
|
-
f"Some of the supplied wells do not match the
|
|
813
|
+
f"Some of the supplied wells do not match the labware {self.get_display_name(labware_id)}."
|
|
784
814
|
)
|
|
785
815
|
return list(wells)
|
|
786
816
|
|
|
@@ -789,7 +819,7 @@ class LabwareView:
|
|
|
789
819
|
definition = self.get_definition(labware_id)
|
|
790
820
|
if definition.parameters.tipLength is None:
|
|
791
821
|
raise errors.LabwareIsNotTipRackError(
|
|
792
|
-
f"Labware {labware_id} has no tip length defined."
|
|
822
|
+
f"Labware {self.get_display_name(labware_id)} has no tip length defined."
|
|
793
823
|
)
|
|
794
824
|
|
|
795
825
|
return definition.parameters.tipLength - overlap
|
|
@@ -1095,7 +1125,9 @@ class LabwareView:
|
|
|
1095
1125
|
raise errors.LabwareCannotBeStackedError(
|
|
1096
1126
|
f"Labware {lid_labware_definition.parameters.loadName} cannot be used as a lid in the Flex Stacker."
|
|
1097
1127
|
)
|
|
1098
|
-
if
|
|
1128
|
+
if isinstance(
|
|
1129
|
+
lid_labware_definition, LabwareDefinition2
|
|
1130
|
+
) and not labware_validation.validate_legacy_labware_can_be_stacked(
|
|
1099
1131
|
lid_labware_definition, primary_labware_definition.parameters.loadName
|
|
1100
1132
|
):
|
|
1101
1133
|
raise errors.LabwareCannotBeStackedError(
|
|
@@ -1108,7 +1140,9 @@ class LabwareView:
|
|
|
1108
1140
|
raise errors.LabwareCannotBeStackedError(
|
|
1109
1141
|
f"Labware {adapter_labware_definition.parameters.loadName} cannot be used as an adapter in the Flex Stacker."
|
|
1110
1142
|
)
|
|
1111
|
-
if
|
|
1143
|
+
if isinstance(
|
|
1144
|
+
primary_labware_definition, LabwareDefinition2
|
|
1145
|
+
) and not labware_validation.validate_legacy_labware_can_be_stacked(
|
|
1112
1146
|
primary_labware_definition,
|
|
1113
1147
|
adapter_labware_definition.parameters.loadName,
|
|
1114
1148
|
):
|
|
@@ -1162,9 +1196,9 @@ class LabwareView:
|
|
|
1162
1196
|
below_labware = self.get(bottom_labware_id)
|
|
1163
1197
|
if isinstance(
|
|
1164
1198
|
top_labware_definition, LabwareDefinition2
|
|
1165
|
-
) and not labware_validation.
|
|
1166
|
-
|
|
1167
|
-
|
|
1199
|
+
) and not labware_validation.validate_legacy_labware_can_be_stacked(
|
|
1200
|
+
child_labware_definition=top_labware_definition,
|
|
1201
|
+
parent_labware_load_name=below_labware.loadName,
|
|
1168
1202
|
):
|
|
1169
1203
|
raise errors.LabwareCannotBeStackedError(
|
|
1170
1204
|
f"Labware {top_labware_definition.parameters.loadName} cannot be loaded onto labware {below_labware.loadName}"
|
|
@@ -1225,28 +1259,6 @@ class LabwareView:
|
|
|
1225
1259
|
uri = self.get_uri_from_definition(self.get_definition(labware_id))
|
|
1226
1260
|
return uri in _MAGDECK_HALF_MM_LABWARE
|
|
1227
1261
|
|
|
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
1262
|
def get_absorbance_reader_lid_definition(self) -> LabwareDefinition:
|
|
1251
1263
|
"""Return the special labware definition for the plate reader lid.
|
|
1252
1264
|
|
|
@@ -1257,68 +1269,6 @@ class LabwareView:
|
|
|
1257
1269
|
"opentrons/opentrons_flex_lid_absorbance_plate_reader_module/1"
|
|
1258
1270
|
]
|
|
1259
1271
|
|
|
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
1272
|
def get_grip_force(self, labware_definition: LabwareDefinition) -> float:
|
|
1323
1273
|
"""Get the recommended grip force for gripping labware using gripper."""
|
|
1324
1274
|
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)
|