opentrons 8.7.0a7__py3-none-any.whl → 8.7.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.
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 +55 -129
- opentrons/drivers/flex_stacker/driver.py +6 -1
- opentrons/drivers/heater_shaker/abstract.py +0 -5
- opentrons/drivers/heater_shaker/driver.py +0 -10
- opentrons/drivers/heater_shaker/simulator.py +0 -4
- opentrons/drivers/thermocycler/abstract.py +0 -6
- opentrons/drivers/thermocycler/driver.py +10 -61
- opentrons/drivers/thermocycler/simulator.py +0 -6
- opentrons/hardware_control/api.py +5 -24
- opentrons/hardware_control/backends/controller.py +2 -8
- opentrons/hardware_control/backends/flex_protocol.py +1 -0
- opentrons/hardware_control/backends/ot3controller.py +3 -3
- opentrons/hardware_control/backends/ot3simulator.py +2 -2
- opentrons/hardware_control/backends/simulator.py +1 -2
- opentrons/hardware_control/backends/subsystem_manager.py +2 -5
- opentrons/hardware_control/emulation/abstract_emulator.py +4 -6
- opentrons/hardware_control/emulation/connection_handler.py +5 -8
- opentrons/hardware_control/emulation/heater_shaker.py +3 -12
- opentrons/hardware_control/emulation/settings.py +1 -1
- opentrons/hardware_control/emulation/thermocycler.py +15 -67
- opentrons/hardware_control/module_control.py +8 -82
- opentrons/hardware_control/modules/__init__.py +0 -3
- opentrons/hardware_control/modules/absorbance_reader.py +4 -11
- opentrons/hardware_control/modules/flex_stacker.py +9 -38
- opentrons/hardware_control/modules/heater_shaker.py +5 -42
- opentrons/hardware_control/modules/magdeck.py +4 -8
- opentrons/hardware_control/modules/mod_abc.py +5 -13
- opentrons/hardware_control/modules/tempdeck.py +5 -25
- opentrons/hardware_control/modules/thermocycler.py +11 -68
- opentrons/hardware_control/modules/types.py +1 -20
- opentrons/hardware_control/modules/utils.py +4 -11
- opentrons/hardware_control/nozzle_manager.py +0 -3
- opentrons/hardware_control/ot3api.py +7 -26
- opentrons/hardware_control/poller.py +8 -22
- opentrons/hardware_control/protocols/gripper_controller.py +1 -0
- opentrons/hardware_control/scripts/update_module_fw.py +0 -5
- opentrons/hardware_control/types.py +2 -31
- opentrons/legacy_commands/module_commands.py +0 -23
- opentrons/legacy_commands/protocol_commands.py +0 -20
- opentrons/legacy_commands/types.py +0 -80
- opentrons/motion_planning/deck_conflict.py +12 -17
- opentrons/motion_planning/waypoints.py +29 -15
- opentrons/protocol_api/__init__.py +1 -5
- opentrons/protocol_api/_types.py +1 -6
- opentrons/protocol_api/core/common.py +1 -3
- opentrons/protocol_api/core/engine/_default_labware_versions.py +11 -32
- opentrons/protocol_api/core/engine/labware.py +1 -8
- opentrons/protocol_api/core/engine/module_core.py +8 -75
- opentrons/protocol_api/core/engine/protocol.py +1 -18
- opentrons/protocol_api/core/engine/well.py +0 -8
- opentrons/protocol_api/core/legacy/legacy_module_core.py +4 -24
- opentrons/protocol_api/core/legacy/legacy_protocol_core.py +1 -11
- opentrons/protocol_api/core/legacy/legacy_well_core.py +0 -4
- opentrons/protocol_api/core/legacy_simulator/legacy_protocol_core.py +2 -14
- opentrons/protocol_api/core/module.py +4 -37
- opentrons/protocol_api/core/protocol.py +2 -11
- opentrons/protocol_api/core/well.py +0 -4
- opentrons/protocol_api/labware.py +0 -5
- opentrons/protocol_api/module_contexts.py +11 -117
- opentrons/protocol_api/protocol_context.py +4 -26
- opentrons/protocol_api/robot_context.py +21 -38
- opentrons/protocol_api/validation.py +1 -6
- opentrons/protocol_engine/actions/__init__.py +2 -4
- opentrons/protocol_engine/actions/actions.py +9 -22
- opentrons/protocol_engine/clients/sync_client.py +7 -42
- opentrons/protocol_engine/commands/__init__.py +0 -42
- opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +15 -2
- opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +15 -2
- opentrons/protocol_engine/commands/aspirate.py +0 -1
- opentrons/protocol_engine/commands/command.py +0 -1
- opentrons/protocol_engine/commands/command_unions.py +0 -49
- opentrons/protocol_engine/commands/dispense.py +0 -1
- opentrons/protocol_engine/commands/drop_tip.py +8 -32
- opentrons/protocol_engine/commands/heater_shaker/__init__.py +0 -14
- opentrons/protocol_engine/commands/heater_shaker/set_and_wait_for_shake_speed.py +4 -5
- opentrons/protocol_engine/commands/heater_shaker/set_target_temperature.py +5 -31
- opentrons/protocol_engine/commands/movement_common.py +0 -2
- opentrons/protocol_engine/commands/pick_up_tip.py +11 -21
- opentrons/protocol_engine/commands/temperature_module/set_target_temperature.py +7 -38
- opentrons/protocol_engine/commands/thermocycler/__init__.py +0 -16
- opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py +0 -6
- opentrons/protocol_engine/commands/thermocycler/run_profile.py +0 -8
- opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py +6 -40
- opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py +5 -29
- opentrons/protocol_engine/commands/touch_tip.py +1 -1
- opentrons/protocol_engine/commands/unsafe/unsafe_place_labware.py +22 -6
- opentrons/protocol_engine/errors/__init__.py +0 -4
- opentrons/protocol_engine/errors/exceptions.py +0 -55
- opentrons/protocol_engine/execution/__init__.py +0 -2
- opentrons/protocol_engine/execution/command_executor.py +0 -8
- opentrons/protocol_engine/execution/create_queue_worker.py +1 -5
- opentrons/protocol_engine/execution/labware_movement.py +21 -10
- opentrons/protocol_engine/execution/movement.py +0 -2
- opentrons/protocol_engine/execution/queue_worker.py +0 -4
- opentrons/protocol_engine/execution/run_control.py +0 -8
- opentrons/protocol_engine/protocol_engine.py +34 -75
- opentrons/protocol_engine/resources/__init__.py +0 -2
- opentrons/protocol_engine/resources/deck_configuration_provider.py +0 -7
- opentrons/protocol_engine/resources/labware_validation.py +6 -10
- opentrons/protocol_engine/state/_labware_origin_math.py +636 -0
- opentrons/protocol_engine/state/_well_math.py +18 -60
- opentrons/protocol_engine/state/addressable_areas.py +0 -2
- opentrons/protocol_engine/state/commands.py +11 -14
- opentrons/protocol_engine/state/geometry.py +374 -213
- opentrons/protocol_engine/state/labware.py +102 -52
- opentrons/protocol_engine/state/module_substates/thermocycler_module_substate.py +0 -37
- opentrons/protocol_engine/state/modules.py +8 -21
- opentrons/protocol_engine/state/motion.py +0 -44
- opentrons/protocol_engine/state/state.py +0 -14
- opentrons/protocol_engine/state/state_summary.py +0 -2
- opentrons/protocol_engine/state/tips.py +258 -177
- opentrons/protocol_engine/state/update_types.py +9 -16
- opentrons/protocol_engine/types/__init__.py +3 -9
- opentrons/protocol_engine/types/deck_configuration.py +1 -5
- opentrons/protocol_engine/types/instrument.py +1 -8
- opentrons/protocol_engine/types/labware.py +13 -1
- opentrons/protocol_engine/types/module.py +0 -10
- opentrons/protocol_engine/types/tip.py +0 -9
- opentrons/protocol_runner/create_simulating_orchestrator.py +2 -29
- opentrons/protocol_runner/run_orchestrator.py +2 -18
- opentrons/protocols/api_support/definitions.py +1 -1
- opentrons/protocols/api_support/types.py +1 -2
- opentrons/simulate.py +15 -48
- opentrons/system/camera.py +1 -1
- {opentrons-8.7.0a7.dist-info → opentrons-8.7.0a8.dist-info}/METADATA +4 -4
- {opentrons-8.7.0a7.dist-info → opentrons-8.7.0a8.dist-info}/RECORD +130 -146
- opentrons/protocol_api/core/engine/tasks.py +0 -48
- opentrons/protocol_api/core/legacy/tasks.py +0 -19
- opentrons/protocol_api/core/legacy_simulator/tasks.py +0 -19
- opentrons/protocol_api/core/tasks.py +0 -31
- opentrons/protocol_api/tasks.py +0 -48
- opentrons/protocol_engine/commands/create_timer.py +0 -83
- opentrons/protocol_engine/commands/heater_shaker/common.py +0 -20
- opentrons/protocol_engine/commands/heater_shaker/set_shake_speed.py +0 -136
- opentrons/protocol_engine/commands/set_tip_state.py +0 -97
- opentrons/protocol_engine/commands/thermocycler/start_run_extended_profile.py +0 -191
- opentrons/protocol_engine/commands/wait_for_tasks.py +0 -98
- opentrons/protocol_engine/execution/task_handler.py +0 -157
- opentrons/protocol_engine/resources/concurrency_provider.py +0 -27
- opentrons/protocol_engine/state/labware_origin_math/errors.py +0 -94
- opentrons/protocol_engine/state/labware_origin_math/stackup_origin_to_labware_origin.py +0 -1331
- opentrons/protocol_engine/state/tasks.py +0 -139
- opentrons/protocol_engine/types/tasks.py +0 -38
- {opentrons-8.7.0a7.dist-info → opentrons-8.7.0a8.dist-info}/WHEEL +0 -0
- {opentrons-8.7.0a7.dist-info → opentrons-8.7.0a8.dist-info}/entry_points.txt +0 -0
- {opentrons-8.7.0a7.dist-info → opentrons-8.7.0a8.dist-info}/licenses/LICENSE +0 -0
|
@@ -56,6 +56,7 @@ from ..types import (
|
|
|
56
56
|
LoadedLabware,
|
|
57
57
|
ModuleLocation,
|
|
58
58
|
OverlapOffset,
|
|
59
|
+
LabwareMovementOffsetData,
|
|
59
60
|
OnDeckLabwareLocation,
|
|
60
61
|
OFF_DECK_LOCATION,
|
|
61
62
|
)
|
|
@@ -400,7 +401,7 @@ class LabwareView:
|
|
|
400
401
|
return self._state.labware_by_id[labware_id]
|
|
401
402
|
except KeyError as e:
|
|
402
403
|
raise errors.LabwareNotLoadedError(
|
|
403
|
-
f"Labware
|
|
404
|
+
f"Labware {labware_id} not found."
|
|
404
405
|
) from e
|
|
405
406
|
|
|
406
407
|
def known(self, labware_id: str) -> bool:
|
|
@@ -429,7 +430,7 @@ class LabwareView:
|
|
|
429
430
|
):
|
|
430
431
|
return labware.id
|
|
431
432
|
raise errors.exceptions.LabwareNotLoadedOnLabwareError(
|
|
432
|
-
f"There is not labware loaded onto labware {
|
|
433
|
+
f"There is not labware loaded onto labware {labware_id}"
|
|
433
434
|
)
|
|
434
435
|
|
|
435
436
|
def raise_if_labware_has_non_lid_labware_on_top(self, labware_id: str) -> None:
|
|
@@ -442,8 +443,7 @@ class LabwareView:
|
|
|
442
443
|
and candidate_id != lid_id
|
|
443
444
|
):
|
|
444
445
|
raise errors.LabwareIsInStackError(
|
|
445
|
-
f"Cannot access labware {
|
|
446
|
-
" a non-lid labware stacked on top."
|
|
446
|
+
f"Cannot access labware {labware_id} because it has 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,29 +454,9 @@ class LabwareView:
|
|
|
454
454
|
and labware.location.labwareId == labware_id
|
|
455
455
|
):
|
|
456
456
|
raise errors.LabwareIsInStackError(
|
|
457
|
-
f"Cannot access labware {
|
|
458
|
-
" another labware stacked on top."
|
|
457
|
+
f"Cannot access labware {labware_id} because it has another labware stacked on top."
|
|
459
458
|
)
|
|
460
459
|
|
|
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
|
-
|
|
480
460
|
def get_by_slot(
|
|
481
461
|
self,
|
|
482
462
|
slot_name: Union[DeckSlotName, StagingSlotName],
|
|
@@ -683,14 +663,6 @@ class LabwareView:
|
|
|
683
663
|
or len(self.get_definition(labware_id).wells) >= 96
|
|
684
664
|
)
|
|
685
665
|
|
|
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
|
-
|
|
694
666
|
def get_well_definition(
|
|
695
667
|
self,
|
|
696
668
|
labware_id: str,
|
|
@@ -709,7 +681,7 @@ class LabwareView:
|
|
|
709
681
|
return definition.wells[well_name]
|
|
710
682
|
except KeyError as e:
|
|
711
683
|
raise errors.WellDoesNotExistError(
|
|
712
|
-
f"{well_name} does not exist in {
|
|
684
|
+
f"{well_name} does not exist in {labware_id}."
|
|
713
685
|
) from e
|
|
714
686
|
|
|
715
687
|
def get_well_geometry(
|
|
@@ -719,21 +691,19 @@ class LabwareView:
|
|
|
719
691
|
labware_def = self.get_definition(labware_id)
|
|
720
692
|
if labware_def.innerLabwareGeometry is None:
|
|
721
693
|
raise errors.IncompleteLabwareDefinitionError(
|
|
722
|
-
message=f"No innerLabwareGeometry found in labware definition for {
|
|
694
|
+
message=f"No innerLabwareGeometry found in labware definition for labware_id: {labware_id}."
|
|
723
695
|
)
|
|
724
696
|
well_def = self.get_well_definition(labware_id, well_name)
|
|
725
697
|
geometry_id = well_def.geometryDefinitionId
|
|
726
698
|
if geometry_id is None:
|
|
727
699
|
raise errors.IncompleteWellDefinitionError(
|
|
728
|
-
message=f"No geometryDefinitionId found in well definition for well {well_name}"
|
|
729
|
-
f" for {self.get_display_name(labware_id)}"
|
|
700
|
+
message=f"No geometryDefinitionId found in well definition for well: {well_name} in labware_id: {labware_id}"
|
|
730
701
|
)
|
|
731
702
|
else:
|
|
732
703
|
well_geometry = labware_def.innerLabwareGeometry.get(geometry_id)
|
|
733
704
|
if well_geometry is None:
|
|
734
705
|
raise errors.IncompleteLabwareDefinitionError(
|
|
735
|
-
message=f"No innerLabwareGeometry found in labware definition for
|
|
736
|
-
f" for {self.get_display_name(labware_id)}"
|
|
706
|
+
message=f"No innerLabwareGeometry found in labware definition for well_id: {geometry_id} in labware_id: {labware_id}"
|
|
737
707
|
)
|
|
738
708
|
return well_geometry
|
|
739
709
|
|
|
@@ -802,15 +772,15 @@ class LabwareView:
|
|
|
802
772
|
contains_wells = all(well_name in labware_wells for well_name in iter(wells))
|
|
803
773
|
if labware_definition.parameters.isTiprack:
|
|
804
774
|
raise errors.LabwareIsTipRackError(
|
|
805
|
-
f"Given labware {
|
|
775
|
+
f"Given labware: {labware_id} is a tiprack. Can not load liquid."
|
|
806
776
|
)
|
|
807
777
|
if LabwareRole.adapter in labware_definition.allowedRoles:
|
|
808
778
|
raise errors.LabwareIsAdapterError(
|
|
809
|
-
f"Given labware {
|
|
779
|
+
f"Given labware: {labware_id} is an adapter. Can not load liquid."
|
|
810
780
|
)
|
|
811
781
|
if not contains_wells:
|
|
812
782
|
raise errors.WellDoesNotExistError(
|
|
813
|
-
f"Some of the supplied wells do not match the
|
|
783
|
+
f"Some of the supplied wells do not match the labwareId: {labware_id}."
|
|
814
784
|
)
|
|
815
785
|
return list(wells)
|
|
816
786
|
|
|
@@ -819,7 +789,7 @@ class LabwareView:
|
|
|
819
789
|
definition = self.get_definition(labware_id)
|
|
820
790
|
if definition.parameters.tipLength is None:
|
|
821
791
|
raise errors.LabwareIsNotTipRackError(
|
|
822
|
-
f"Labware {
|
|
792
|
+
f"Labware {labware_id} has no tip length defined."
|
|
823
793
|
)
|
|
824
794
|
|
|
825
795
|
return definition.parameters.tipLength - overlap
|
|
@@ -1125,9 +1095,7 @@ class LabwareView:
|
|
|
1125
1095
|
raise errors.LabwareCannotBeStackedError(
|
|
1126
1096
|
f"Labware {lid_labware_definition.parameters.loadName} cannot be used as a lid in the Flex Stacker."
|
|
1127
1097
|
)
|
|
1128
|
-
if
|
|
1129
|
-
lid_labware_definition, LabwareDefinition2
|
|
1130
|
-
) and not labware_validation.validate_legacy_labware_can_be_stacked(
|
|
1098
|
+
if not labware_validation.validate_labware_can_be_stacked(
|
|
1131
1099
|
lid_labware_definition, primary_labware_definition.parameters.loadName
|
|
1132
1100
|
):
|
|
1133
1101
|
raise errors.LabwareCannotBeStackedError(
|
|
@@ -1140,9 +1108,7 @@ class LabwareView:
|
|
|
1140
1108
|
raise errors.LabwareCannotBeStackedError(
|
|
1141
1109
|
f"Labware {adapter_labware_definition.parameters.loadName} cannot be used as an adapter in the Flex Stacker."
|
|
1142
1110
|
)
|
|
1143
|
-
if
|
|
1144
|
-
primary_labware_definition, LabwareDefinition2
|
|
1145
|
-
) and not labware_validation.validate_legacy_labware_can_be_stacked(
|
|
1111
|
+
if not labware_validation.validate_labware_can_be_stacked(
|
|
1146
1112
|
primary_labware_definition,
|
|
1147
1113
|
adapter_labware_definition.parameters.loadName,
|
|
1148
1114
|
):
|
|
@@ -1196,9 +1162,9 @@ class LabwareView:
|
|
|
1196
1162
|
below_labware = self.get(bottom_labware_id)
|
|
1197
1163
|
if isinstance(
|
|
1198
1164
|
top_labware_definition, LabwareDefinition2
|
|
1199
|
-
) and not labware_validation.
|
|
1200
|
-
|
|
1201
|
-
|
|
1165
|
+
) and not labware_validation.validate_labware_can_be_stacked(
|
|
1166
|
+
top_labware_definition=top_labware_definition,
|
|
1167
|
+
below_labware_load_name=below_labware.loadName,
|
|
1202
1168
|
):
|
|
1203
1169
|
raise errors.LabwareCannotBeStackedError(
|
|
1204
1170
|
f"Labware {top_labware_definition.parameters.loadName} cannot be loaded onto labware {below_labware.loadName}"
|
|
@@ -1259,6 +1225,28 @@ class LabwareView:
|
|
|
1259
1225
|
uri = self.get_uri_from_definition(self.get_definition(labware_id))
|
|
1260
1226
|
return uri in _MAGDECK_HALF_MM_LABWARE
|
|
1261
1227
|
|
|
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
|
+
|
|
1262
1250
|
def get_absorbance_reader_lid_definition(self) -> LabwareDefinition:
|
|
1263
1251
|
"""Return the special labware definition for the plate reader lid.
|
|
1264
1252
|
|
|
@@ -1269,6 +1257,68 @@ class LabwareView:
|
|
|
1269
1257
|
"opentrons/opentrons_flex_lid_absorbance_plate_reader_module/1"
|
|
1270
1258
|
]
|
|
1271
1259
|
|
|
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
|
+
|
|
1272
1322
|
def get_grip_force(self, labware_definition: LabwareDefinition) -> float:
|
|
1273
1323
|
"""Get the recommended grip force for gripping labware using gripper."""
|
|
1274
1324
|
recommended_force = labware_definition.gripForce
|
|
@@ -5,7 +5,6 @@ from typing import NewType, Optional
|
|
|
5
5
|
from opentrons.protocol_engine.errors import (
|
|
6
6
|
InvalidTargetTemperatureError,
|
|
7
7
|
InvalidBlockVolumeError,
|
|
8
|
-
InvalidRampRateError,
|
|
9
8
|
NoTargetTemperatureSetError,
|
|
10
9
|
InvalidHoldTimeError,
|
|
11
10
|
)
|
|
@@ -24,10 +23,6 @@ from opentrons.hardware_control.modules import ModuleData, ModuleDataValidator
|
|
|
24
23
|
|
|
25
24
|
ThermocyclerModuleId = NewType("ThermocyclerModuleId", str)
|
|
26
25
|
|
|
27
|
-
# These are our published numbers, and from testing they are good bounds
|
|
28
|
-
MAX_HEATING_RATE = 4.25
|
|
29
|
-
MAX_COOLING_RATE = 2.0
|
|
30
|
-
|
|
31
26
|
|
|
32
27
|
@dataclass(frozen=True)
|
|
33
28
|
class ThermocyclerModuleSubState:
|
|
@@ -148,38 +143,6 @@ class ThermocyclerModuleSubState:
|
|
|
148
143
|
)
|
|
149
144
|
return target
|
|
150
145
|
|
|
151
|
-
def validate_ramp_rate(
|
|
152
|
-
self, ramp_rate: Optional[float], target_temp: float
|
|
153
|
-
) -> Optional[float]:
|
|
154
|
-
"""Validate a given temperature ramp rate.
|
|
155
|
-
|
|
156
|
-
Args:
|
|
157
|
-
ramp_rate: The requested ramp rate in °C/second.
|
|
158
|
-
target_temp: The requested block temperature.
|
|
159
|
-
|
|
160
|
-
Raises:
|
|
161
|
-
InvalidRampRateError: The given ramp_rate is invalid
|
|
162
|
-
|
|
163
|
-
Returns:
|
|
164
|
-
The validated ramp rate in °C/second
|
|
165
|
-
"""
|
|
166
|
-
if ramp_rate is None:
|
|
167
|
-
return ramp_rate
|
|
168
|
-
|
|
169
|
-
heating = target_temp > self.get_target_block_temperature()
|
|
170
|
-
if (heating and ramp_rate > MAX_HEATING_RATE) or (
|
|
171
|
-
not heating and ramp_rate > MAX_COOLING_RATE
|
|
172
|
-
):
|
|
173
|
-
raise InvalidRampRateError(
|
|
174
|
-
f"Thermocycler ramp rate cannot exceed {MAX_HEATING_RATE}°C/s"
|
|
175
|
-
f" while heating or {MAX_COOLING_RATE}°C/s when cooling."
|
|
176
|
-
)
|
|
177
|
-
if ramp_rate <= 0:
|
|
178
|
-
raise InvalidRampRateError(
|
|
179
|
-
f"Thermocycler ramp rate cannot be less than or equal to 0, got {ramp_rate}"
|
|
180
|
-
)
|
|
181
|
-
return ramp_rate
|
|
182
|
-
|
|
183
146
|
@classmethod
|
|
184
147
|
def from_live_data(
|
|
185
148
|
cls, module_id: ThermocyclerModuleId, data: ModuleData | None
|
|
@@ -56,6 +56,7 @@ from ..types import (
|
|
|
56
56
|
HeaterShakerLatchStatus,
|
|
57
57
|
HeaterShakerMovementRestrictors,
|
|
58
58
|
DeckType,
|
|
59
|
+
LabwareMovementOffsetData,
|
|
59
60
|
AddressableAreaLocation,
|
|
60
61
|
StackerStoredLabwareGroup,
|
|
61
62
|
)
|
|
@@ -1335,6 +1336,13 @@ class ModuleView:
|
|
|
1335
1336
|
return True
|
|
1336
1337
|
return False
|
|
1337
1338
|
|
|
1339
|
+
def get_default_gripper_offsets(
|
|
1340
|
+
self, module_id: str
|
|
1341
|
+
) -> Optional[LabwareMovementOffsetData]:
|
|
1342
|
+
"""Get the deck's default gripper offsets."""
|
|
1343
|
+
offsets = self.get_definition(module_id).gripperOffsets
|
|
1344
|
+
return offsets.get("default") if offsets else None
|
|
1345
|
+
|
|
1338
1346
|
def get_overflowed_module_in_slot(
|
|
1339
1347
|
self, slot_name: DeckSlotName
|
|
1340
1348
|
) -> Optional[LoadedModule]:
|
|
@@ -1511,24 +1519,3 @@ class ModuleView:
|
|
|
1511
1519
|
f"Provided overlap offset {overlap_offset} does not match "
|
|
1512
1520
|
f"configured {configured}."
|
|
1513
1521
|
)
|
|
1514
|
-
|
|
1515
|
-
def get_has_module_probably_matching_hardware_details(
|
|
1516
|
-
self, module_model: ModuleModel, module_serial: str | None
|
|
1517
|
-
) -> bool:
|
|
1518
|
-
"""Get the ID of a model that possibly matches the provided details.
|
|
1519
|
-
|
|
1520
|
-
If the provided serial is not None, return True if there is a module with the same serial or
|
|
1521
|
-
False if there is not.
|
|
1522
|
-
If the provided serial is None, return True if there is a module with the same model or False if
|
|
1523
|
-
there is not.
|
|
1524
|
-
|
|
1525
|
-
This is intended to provide a good probability that a module matching the provided details
|
|
1526
|
-
is or is not present in the state store. It is used to drive whether the engine cancels a protocol
|
|
1527
|
-
in response to an asynchronous module error or not.
|
|
1528
|
-
"""
|
|
1529
|
-
for module_id, module in self._state.hardware_by_module_id.items():
|
|
1530
|
-
if module_serial is not None and module_serial == module.serial_number:
|
|
1531
|
-
return True
|
|
1532
|
-
if module_serial is None and module.definition.model == module_model:
|
|
1533
|
-
return True
|
|
1534
|
-
return False
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"""Motion state store and getters."""
|
|
2
2
|
from dataclasses import dataclass
|
|
3
3
|
from typing import List, Optional, Union
|
|
4
|
-
import logging
|
|
5
4
|
|
|
6
5
|
from opentrons.types import MountType, Point, StagingSlotName
|
|
7
6
|
from opentrons.hardware_control.types import CriticalPoint
|
|
@@ -29,8 +28,6 @@ from .geometry import GeometryView
|
|
|
29
28
|
from .modules import ModuleView
|
|
30
29
|
from .module_substates import HeaterShakerModuleId
|
|
31
30
|
|
|
32
|
-
log = logging.getLogger(__name__)
|
|
33
|
-
|
|
34
31
|
|
|
35
32
|
@dataclass(frozen=True)
|
|
36
33
|
class PipetteLocationData:
|
|
@@ -88,42 +85,6 @@ class MotionView:
|
|
|
88
85
|
critical_point = CriticalPoint.XY_CENTER
|
|
89
86
|
return PipetteLocationData(mount=mount, critical_point=critical_point)
|
|
90
87
|
|
|
91
|
-
def _get_pipette_offset_for_reservoirs(
|
|
92
|
-
self, labware_id: str, well_name: str, pipette_id: str
|
|
93
|
-
) -> Point:
|
|
94
|
-
# 8 rows, 12 columns
|
|
95
|
-
subwells_96 = self._labware.get_has_96_subwells(labware_id)
|
|
96
|
-
# 1 row, 12 columns
|
|
97
|
-
subwells_12 = self._labware.get_has_12_subwells(labware_id)
|
|
98
|
-
if subwells_12 and subwells_96:
|
|
99
|
-
log.warning(
|
|
100
|
-
f"{self._labware.get_display_name(labware_id)} has both offsetPipetteFor96GridSubwells and"
|
|
101
|
-
" offsetPipetteFor12GridSubwells quirks."
|
|
102
|
-
)
|
|
103
|
-
|
|
104
|
-
pipette_rows = self._pipettes.get_nozzle_configuration(pipette_id).rows
|
|
105
|
-
pipette_cols = self._pipettes.get_nozzle_configuration(pipette_id).columns
|
|
106
|
-
|
|
107
|
-
even_labware_rows = subwells_96
|
|
108
|
-
even_labware_columns = subwells_96 or subwells_12
|
|
109
|
-
odd_pipette_rows = len(pipette_rows) % 2 == 1
|
|
110
|
-
odd_pipette_cols = len(pipette_cols) % 2 == 1
|
|
111
|
-
|
|
112
|
-
well_x_dim, well_y_dim, well_z_dim = self._labware.get_well_size(
|
|
113
|
-
labware_id=labware_id, well_name=well_name
|
|
114
|
-
)
|
|
115
|
-
x_offset = 0.0
|
|
116
|
-
y_offset = 0.0
|
|
117
|
-
if even_labware_rows and odd_pipette_rows:
|
|
118
|
-
# need to move up half a row
|
|
119
|
-
# there's 8 rows, so move 1/16 of reservoir length
|
|
120
|
-
y_offset = well_y_dim / 16
|
|
121
|
-
if even_labware_columns and odd_pipette_cols:
|
|
122
|
-
# need to move left half a column
|
|
123
|
-
# there's 12 columns, so move 1/24 of reservoir width
|
|
124
|
-
x_offset = -1 * well_x_dim / 24
|
|
125
|
-
return Point(x=x_offset, y=y_offset)
|
|
126
|
-
|
|
127
88
|
def get_movement_waypoints_to_well(
|
|
128
89
|
self,
|
|
129
90
|
pipette_id: str,
|
|
@@ -137,7 +98,6 @@ class MotionView:
|
|
|
137
98
|
force_direct: bool = False,
|
|
138
99
|
minimum_z_height: Optional[float] = None,
|
|
139
100
|
operation_volume: Optional[float] = None,
|
|
140
|
-
offset_pipette_for_reservoir_subwells: bool = False,
|
|
141
101
|
) -> List[motion_planning.Waypoint]:
|
|
142
102
|
"""Calculate waypoints to a destination that's specified as a well."""
|
|
143
103
|
location = current_well or self._pipettes.get_current_location()
|
|
@@ -155,10 +115,6 @@ class MotionView:
|
|
|
155
115
|
operation_volume=operation_volume,
|
|
156
116
|
pipette_id=pipette_id,
|
|
157
117
|
)
|
|
158
|
-
if offset_pipette_for_reservoir_subwells:
|
|
159
|
-
destination += self._get_pipette_offset_for_reservoirs(
|
|
160
|
-
labware_id=labware_id, well_name=well_name, pipette_id=pipette_id
|
|
161
|
-
)
|
|
162
118
|
|
|
163
119
|
move_type = _move_types.get_move_type_to_well(
|
|
164
120
|
pipette_id, labware_id, well_name, location, force_direct
|
|
@@ -34,7 +34,6 @@ from .files import FileView, FileState, FileStore
|
|
|
34
34
|
from .config import Config
|
|
35
35
|
from .state_summary import StateSummary
|
|
36
36
|
from ..types import DeckConfigurationType
|
|
37
|
-
from .tasks import TaskState, TaskView, TaskStore
|
|
38
37
|
|
|
39
38
|
|
|
40
39
|
_ParamsT = ParamSpec("_ParamsT")
|
|
@@ -55,7 +54,6 @@ class State:
|
|
|
55
54
|
tips: TipState
|
|
56
55
|
wells: WellState
|
|
57
56
|
files: FileState
|
|
58
|
-
tasks: TaskState
|
|
59
57
|
|
|
60
58
|
|
|
61
59
|
class StateView(HasState[State]):
|
|
@@ -75,7 +73,6 @@ class StateView(HasState[State]):
|
|
|
75
73
|
_motion: MotionView
|
|
76
74
|
_files: FileView
|
|
77
75
|
_config: Config
|
|
78
|
-
_tasks: TaskView
|
|
79
76
|
|
|
80
77
|
@property
|
|
81
78
|
def commands(self) -> CommandView:
|
|
@@ -142,11 +139,6 @@ class StateView(HasState[State]):
|
|
|
142
139
|
"""Get ProtocolEngine configuration."""
|
|
143
140
|
return self._config
|
|
144
141
|
|
|
145
|
-
@property
|
|
146
|
-
def tasks(self) -> TaskView:
|
|
147
|
-
"""Get state view selectors for task state."""
|
|
148
|
-
return self._tasks
|
|
149
|
-
|
|
150
142
|
def get_summary(self) -> StateSummary:
|
|
151
143
|
"""Get protocol run data."""
|
|
152
144
|
error = self._commands.get_error()
|
|
@@ -170,7 +162,6 @@ class StateView(HasState[State]):
|
|
|
170
162
|
)
|
|
171
163
|
for liquid_class_id, liquid_class_record in self._liquid_classes.get_all().items()
|
|
172
164
|
],
|
|
173
|
-
tasks=self._tasks.get_summary(),
|
|
174
165
|
)
|
|
175
166
|
|
|
176
167
|
|
|
@@ -240,7 +231,6 @@ class StateStore(StateView, ActionHandler):
|
|
|
240
231
|
self._tip_store = TipStore()
|
|
241
232
|
self._well_store = WellStore()
|
|
242
233
|
self._file_store = FileStore()
|
|
243
|
-
self._task_store = TaskStore()
|
|
244
234
|
|
|
245
235
|
self._substores: List[HandlesActions] = [
|
|
246
236
|
self._command_store,
|
|
@@ -253,7 +243,6 @@ class StateStore(StateView, ActionHandler):
|
|
|
253
243
|
self._tip_store,
|
|
254
244
|
self._well_store,
|
|
255
245
|
self._file_store,
|
|
256
|
-
self._task_store,
|
|
257
246
|
]
|
|
258
247
|
self._config = config
|
|
259
248
|
self._change_notifier = change_notifier or ChangeNotifier()
|
|
@@ -377,7 +366,6 @@ class StateStore(StateView, ActionHandler):
|
|
|
377
366
|
tips=self._tip_store.state,
|
|
378
367
|
wells=self._well_store.state,
|
|
379
368
|
files=self._file_store.state,
|
|
380
|
-
tasks=self._task_store.state,
|
|
381
369
|
)
|
|
382
370
|
|
|
383
371
|
def _initialize_state(self) -> None:
|
|
@@ -396,7 +384,6 @@ class StateStore(StateView, ActionHandler):
|
|
|
396
384
|
self._tips = TipView(state.tips)
|
|
397
385
|
self._wells = WellView(state.wells)
|
|
398
386
|
self._files = FileView(state.files)
|
|
399
|
-
self._tasks = TaskView(state.tasks)
|
|
400
387
|
|
|
401
388
|
# Derived states
|
|
402
389
|
self._geometry = GeometryView(
|
|
@@ -429,7 +416,6 @@ class StateStore(StateView, ActionHandler):
|
|
|
429
416
|
self._liquid_classes._state = next_state.liquid_classes
|
|
430
417
|
self._tips._state = next_state.tips
|
|
431
418
|
self._wells._state = next_state.wells
|
|
432
|
-
self._tasks._state = next_state.tasks
|
|
433
419
|
self._change_notifier.notify()
|
|
434
420
|
if self._notify_robot_server is not None:
|
|
435
421
|
self._notify_robot_server()
|
|
@@ -13,7 +13,6 @@ from ..types import (
|
|
|
13
13
|
Liquid,
|
|
14
14
|
LiquidClassRecordWithId,
|
|
15
15
|
WellInfoSummary,
|
|
16
|
-
TaskSummary,
|
|
17
16
|
)
|
|
18
17
|
|
|
19
18
|
|
|
@@ -35,4 +34,3 @@ class StateSummary(BaseModel):
|
|
|
35
34
|
wells: List[WellInfoSummary] = Field(default_factory=list)
|
|
36
35
|
files: List[str] = Field(default_factory=list)
|
|
37
36
|
liquidClasses: List[LiquidClassRecordWithId] = Field(default_factory=list)
|
|
38
|
-
tasks: List[TaskSummary] = Field(default_factory=list)
|