opentrons 8.2.0a0__py2.py3-none-any.whl → 8.2.0a2__py2.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/drivers/absorbance_reader/async_byonoy.py +3 -3
- opentrons/hardware_control/ot3api.py +5 -5
- opentrons/hardware_control/protocols/position_estimator.py +3 -1
- opentrons/legacy_commands/helpers.py +8 -2
- opentrons/protocol_api/core/engine/labware.py +10 -2
- opentrons/protocol_api/core/engine/module_core.py +38 -1
- opentrons/protocol_api/core/engine/pipette_movement_conflict.py +12 -5
- opentrons/protocol_api/core/engine/protocol.py +5 -30
- opentrons/protocol_api/core/labware.py +4 -0
- opentrons/protocol_api/core/legacy/legacy_labware_core.py +5 -0
- opentrons/protocol_api/core/legacy/legacy_protocol_core.py +1 -0
- opentrons/protocol_api/core/protocol.py +1 -0
- opentrons/protocol_api/module_contexts.py +13 -0
- opentrons/protocol_api/protocol_context.py +12 -2
- opentrons/protocol_engine/actions/__init__.py +0 -2
- opentrons/protocol_engine/actions/actions.py +0 -12
- opentrons/protocol_engine/clients/sync_client.py +0 -6
- opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +18 -31
- opentrons/protocol_engine/commands/absorbance_reader/initialize.py +19 -7
- opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +17 -29
- opentrons/protocol_engine/commands/load_labware.py +9 -0
- opentrons/protocol_engine/commands/load_module.py +0 -39
- opentrons/protocol_engine/commands/move_labware.py +49 -4
- opentrons/protocol_engine/commands/unsafe/unsafe_place_labware.py +49 -35
- opentrons/protocol_engine/commands/unsafe/update_position_estimators.py +5 -1
- opentrons/protocol_engine/create_protocol_engine.py +18 -1
- opentrons/protocol_engine/errors/__init__.py +2 -0
- opentrons/protocol_engine/errors/exceptions.py +13 -0
- opentrons/protocol_engine/execution/labware_movement.py +69 -21
- opentrons/protocol_engine/execution/movement.py +9 -4
- opentrons/protocol_engine/protocol_engine.py +0 -7
- opentrons/protocol_engine/resources/deck_data_provider.py +0 -39
- opentrons/protocol_engine/resources/file_provider.py +11 -7
- opentrons/protocol_engine/resources/fixture_validation.py +6 -1
- opentrons/protocol_engine/state/geometry.py +91 -49
- opentrons/protocol_engine/state/labware.py +102 -25
- opentrons/protocol_engine/state/module_substates/absorbance_reader_substate.py +3 -1
- opentrons/protocol_engine/state/modules.py +49 -79
- opentrons/protocol_engine/state/motion.py +17 -5
- opentrons/protocol_engine/state/update_types.py +16 -0
- opentrons/util/logging_config.py +1 -1
- {opentrons-8.2.0a0.dist-info → opentrons-8.2.0a2.dist-info}/METADATA +4 -4
- {opentrons-8.2.0a0.dist-info → opentrons-8.2.0a2.dist-info}/RECORD +47 -47
- {opentrons-8.2.0a0.dist-info → opentrons-8.2.0a2.dist-info}/LICENSE +0 -0
- {opentrons-8.2.0a0.dist-info → opentrons-8.2.0a2.dist-info}/WHEEL +0 -0
- {opentrons-8.2.0a0.dist-info → opentrons-8.2.0a2.dist-info}/entry_points.txt +0 -0
- {opentrons-8.2.0a0.dist-info → opentrons-8.2.0a2.dist-info}/top_level.txt +0 -0
|
@@ -13,6 +13,7 @@ from typing import (
|
|
|
13
13
|
NamedTuple,
|
|
14
14
|
cast,
|
|
15
15
|
Union,
|
|
16
|
+
overload,
|
|
16
17
|
)
|
|
17
18
|
|
|
18
19
|
from opentrons.protocol_engine.state import update_types
|
|
@@ -81,6 +82,10 @@ _RIGHT_SIDE_SLOTS = {
|
|
|
81
82
|
}
|
|
82
83
|
|
|
83
84
|
|
|
85
|
+
# The max height of the labware that can fit in a plate reader
|
|
86
|
+
_PLATE_READER_MAX_LABWARE_Z_MM = 16
|
|
87
|
+
|
|
88
|
+
|
|
84
89
|
class LabwareLoadParams(NamedTuple):
|
|
85
90
|
"""Parameters required to load a labware in Protocol Engine."""
|
|
86
91
|
|
|
@@ -227,10 +232,11 @@ class LabwareStore(HasState[LabwareState], HandlesActions):
|
|
|
227
232
|
if labware_location_update.new_location:
|
|
228
233
|
new_location = labware_location_update.new_location
|
|
229
234
|
|
|
230
|
-
if isinstance(
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
235
|
+
if isinstance(new_location, AddressableAreaLocation) and (
|
|
236
|
+
fixture_validation.is_gripper_waste_chute(
|
|
237
|
+
new_location.addressableAreaName
|
|
238
|
+
)
|
|
239
|
+
or fixture_validation.is_trash(new_location.addressableAreaName)
|
|
234
240
|
):
|
|
235
241
|
# If a labware has been moved into a waste chute it's been chuted away and is now technically off deck
|
|
236
242
|
new_location = OFF_DECK_LOCATION
|
|
@@ -625,10 +631,26 @@ class LabwareView(HasState[LabwareState]):
|
|
|
625
631
|
definition = self.get_definition(labware_id)
|
|
626
632
|
return definition.parameters.loadName
|
|
627
633
|
|
|
628
|
-
|
|
634
|
+
@overload
|
|
635
|
+
def get_dimensions(self, *, labware_definition: LabwareDefinition) -> Dimensions:
|
|
636
|
+
pass
|
|
637
|
+
|
|
638
|
+
@overload
|
|
639
|
+
def get_dimensions(self, *, labware_id: str) -> Dimensions:
|
|
640
|
+
pass
|
|
641
|
+
|
|
642
|
+
def get_dimensions(
|
|
643
|
+
self,
|
|
644
|
+
*,
|
|
645
|
+
labware_definition: LabwareDefinition | None = None,
|
|
646
|
+
labware_id: str | None = None,
|
|
647
|
+
) -> Dimensions:
|
|
629
648
|
"""Get the labware's dimensions."""
|
|
630
|
-
|
|
631
|
-
|
|
649
|
+
if labware_definition is None:
|
|
650
|
+
assert labware_id is not None # From our @overloads.
|
|
651
|
+
labware_definition = self.get_definition(labware_id)
|
|
652
|
+
|
|
653
|
+
dims = labware_definition.dimensions
|
|
632
654
|
|
|
633
655
|
return Dimensions(
|
|
634
656
|
x=dims.xDimension,
|
|
@@ -637,10 +659,9 @@ class LabwareView(HasState[LabwareState]):
|
|
|
637
659
|
)
|
|
638
660
|
|
|
639
661
|
def get_labware_overlap_offsets(
|
|
640
|
-
self,
|
|
662
|
+
self, definition: LabwareDefinition, below_labware_name: str
|
|
641
663
|
) -> OverlapOffset:
|
|
642
664
|
"""Get the labware's overlap with requested labware's load name."""
|
|
643
|
-
definition = self.get_definition(labware_id)
|
|
644
665
|
if below_labware_name in definition.stackingOffsetWithLabware.keys():
|
|
645
666
|
stacking_overlap = definition.stackingOffsetWithLabware.get(
|
|
646
667
|
below_labware_name, OverlapOffset(x=0, y=0, z=0)
|
|
@@ -654,10 +675,9 @@ class LabwareView(HasState[LabwareState]):
|
|
|
654
675
|
)
|
|
655
676
|
|
|
656
677
|
def get_module_overlap_offsets(
|
|
657
|
-
self,
|
|
678
|
+
self, definition: LabwareDefinition, module_model: ModuleModel
|
|
658
679
|
) -> OverlapOffset:
|
|
659
680
|
"""Get the labware's overlap with requested module model."""
|
|
660
|
-
definition = self.get_definition(labware_id)
|
|
661
681
|
stacking_overlap = definition.stackingOffsetWithModule.get(
|
|
662
682
|
str(module_model.value)
|
|
663
683
|
)
|
|
@@ -817,6 +837,24 @@ class LabwareView(HasState[LabwareState]):
|
|
|
817
837
|
f"Labware {labware.loadName} is already present at {location}."
|
|
818
838
|
)
|
|
819
839
|
|
|
840
|
+
def raise_if_labware_incompatible_with_plate_reader(
|
|
841
|
+
self,
|
|
842
|
+
labware_definition: LabwareDefinition,
|
|
843
|
+
) -> None:
|
|
844
|
+
"""Raise an error if the labware is not compatible with the plate reader."""
|
|
845
|
+
load_name = labware_definition.parameters.loadName
|
|
846
|
+
number_of_wells = len(labware_definition.wells)
|
|
847
|
+
if number_of_wells != 96:
|
|
848
|
+
raise errors.LabwareMovementNotAllowedError(
|
|
849
|
+
f"Cannot move '{load_name}' into plate reader because the"
|
|
850
|
+
f" labware contains {number_of_wells} wells where 96 wells is expected."
|
|
851
|
+
)
|
|
852
|
+
elif labware_definition.dimensions.zDimension > _PLATE_READER_MAX_LABWARE_Z_MM:
|
|
853
|
+
raise errors.LabwareMovementNotAllowedError(
|
|
854
|
+
f"Cannot move '{load_name}' into plate reader because the"
|
|
855
|
+
f" maximum allowed labware height is {_PLATE_READER_MAX_LABWARE_Z_MM}mm."
|
|
856
|
+
)
|
|
857
|
+
|
|
820
858
|
def raise_if_labware_cannot_be_stacked( # noqa: C901
|
|
821
859
|
self, top_labware_definition: LabwareDefinition, bottom_labware_id: str
|
|
822
860
|
) -> None:
|
|
@@ -900,22 +938,60 @@ class LabwareView(HasState[LabwareState]):
|
|
|
900
938
|
else None
|
|
901
939
|
)
|
|
902
940
|
|
|
903
|
-
def
|
|
941
|
+
def get_absorbance_reader_lid_definition(self) -> LabwareDefinition:
|
|
942
|
+
"""Return the special labware definition for the plate reader lid.
|
|
943
|
+
|
|
944
|
+
See todo comments in `create_protocol_engine().
|
|
945
|
+
"""
|
|
946
|
+
# NOTE: This needs to stay in sync with create_protocol_engine().
|
|
947
|
+
return self._state.definitions_by_uri[
|
|
948
|
+
"opentrons/opentrons_flex_lid_absorbance_plate_reader_module/1"
|
|
949
|
+
]
|
|
950
|
+
|
|
951
|
+
@overload
|
|
952
|
+
def get_child_gripper_offsets(
|
|
904
953
|
self,
|
|
905
|
-
|
|
954
|
+
*,
|
|
955
|
+
labware_definition: LabwareDefinition,
|
|
906
956
|
slot_name: Optional[DeckSlotName],
|
|
907
957
|
) -> Optional[LabwareMovementOffsetData]:
|
|
908
|
-
|
|
958
|
+
pass
|
|
959
|
+
|
|
960
|
+
@overload
|
|
961
|
+
def get_child_gripper_offsets(
|
|
962
|
+
self, *, labware_id: str, slot_name: Optional[DeckSlotName]
|
|
963
|
+
) -> Optional[LabwareMovementOffsetData]:
|
|
964
|
+
pass
|
|
965
|
+
|
|
966
|
+
def get_child_gripper_offsets(
|
|
967
|
+
self,
|
|
968
|
+
*,
|
|
969
|
+
labware_definition: Optional[LabwareDefinition] = None,
|
|
970
|
+
labware_id: Optional[str] = None,
|
|
971
|
+
slot_name: Optional[DeckSlotName],
|
|
972
|
+
) -> Optional[LabwareMovementOffsetData]:
|
|
973
|
+
"""Get the grip offsets that a labware says should be applied to children stacked atop it.
|
|
974
|
+
|
|
975
|
+
Params:
|
|
976
|
+
labware_id: The ID of a parent labware (atop which another labware, the child, will be stacked).
|
|
977
|
+
slot_name: The ancestor slot that the parent labware is ultimately loaded into,
|
|
978
|
+
perhaps after going through a module in the middle.
|
|
909
979
|
|
|
910
980
|
Returns:
|
|
911
|
-
If `slot_name` is provided, returns the gripper offsets that the labware definition
|
|
981
|
+
If `slot_name` is provided, returns the gripper offsets that the parent labware definition
|
|
912
982
|
specifies just for that slot, or `None` if the labware definition doesn't have an
|
|
913
983
|
exact match.
|
|
914
984
|
|
|
915
|
-
If `slot_name` is `None`, returns the gripper offsets that the labware
|
|
985
|
+
If `slot_name` is `None`, returns the gripper offsets that the parent labware
|
|
916
986
|
definition designates as "default," or `None` if it doesn't designate any as such.
|
|
917
987
|
"""
|
|
918
|
-
|
|
988
|
+
if labware_id is not None:
|
|
989
|
+
labware_definition = self.get_definition(labware_id)
|
|
990
|
+
else:
|
|
991
|
+
# Should be ensured by our @overloads.
|
|
992
|
+
assert labware_definition is not None
|
|
993
|
+
|
|
994
|
+
parsed_offsets = labware_definition.gripperOffsets
|
|
919
995
|
offset_key = slot_name.id if slot_name else "default"
|
|
920
996
|
|
|
921
997
|
if parsed_offsets is None or offset_key not in parsed_offsets:
|
|
@@ -930,20 +1006,22 @@ class LabwareView(HasState[LabwareState]):
|
|
|
930
1006
|
),
|
|
931
1007
|
)
|
|
932
1008
|
|
|
933
|
-
def get_grip_force(self,
|
|
1009
|
+
def get_grip_force(self, labware_definition: LabwareDefinition) -> float:
|
|
934
1010
|
"""Get the recommended grip force for gripping labware using gripper."""
|
|
935
|
-
recommended_force =
|
|
1011
|
+
recommended_force = labware_definition.gripForce
|
|
936
1012
|
return (
|
|
937
1013
|
recommended_force if recommended_force is not None else LABWARE_GRIP_FORCE
|
|
938
1014
|
)
|
|
939
1015
|
|
|
940
|
-
def get_grip_height_from_labware_bottom(
|
|
1016
|
+
def get_grip_height_from_labware_bottom(
|
|
1017
|
+
self, labware_definition: LabwareDefinition
|
|
1018
|
+
) -> float:
|
|
941
1019
|
"""Get the recommended grip height from labware bottom, if present."""
|
|
942
|
-
recommended_height =
|
|
1020
|
+
recommended_height = labware_definition.gripHeightFromLabwareBottom
|
|
943
1021
|
return (
|
|
944
1022
|
recommended_height
|
|
945
1023
|
if recommended_height is not None
|
|
946
|
-
else self.get_dimensions(
|
|
1024
|
+
else self.get_dimensions(labware_definition=labware_definition).z / 2
|
|
947
1025
|
)
|
|
948
1026
|
|
|
949
1027
|
@staticmethod
|
|
@@ -986,7 +1064,7 @@ class LabwareView(HasState[LabwareState]):
|
|
|
986
1064
|
def _max_z_of_well(well_defn: WellDefinition) -> float:
|
|
987
1065
|
return well_defn.z + well_defn.depth
|
|
988
1066
|
|
|
989
|
-
def get_well_bbox(self,
|
|
1067
|
+
def get_well_bbox(self, labware_definition: LabwareDefinition) -> Dimensions:
|
|
990
1068
|
"""Get the bounding box implied by the wells.
|
|
991
1069
|
|
|
992
1070
|
The bounding box of the labware that is implied by the wells is that required
|
|
@@ -997,14 +1075,13 @@ class LabwareView(HasState[LabwareState]):
|
|
|
997
1075
|
This is used for the specific purpose of finding the reasonable uncertainty bounds of
|
|
998
1076
|
where and how a gripper will interact with a labware.
|
|
999
1077
|
"""
|
|
1000
|
-
defn = self.get_definition(labware_id)
|
|
1001
1078
|
max_x: Optional[float] = None
|
|
1002
1079
|
min_x: Optional[float] = None
|
|
1003
1080
|
max_y: Optional[float] = None
|
|
1004
1081
|
min_y: Optional[float] = None
|
|
1005
1082
|
max_z: Optional[float] = None
|
|
1006
1083
|
|
|
1007
|
-
for well in
|
|
1084
|
+
for well in labware_definition.wells.values():
|
|
1008
1085
|
well_max_x = self._max_x_of_well(well)
|
|
1009
1086
|
well_min_x = self._min_x_of_well(well)
|
|
1010
1087
|
well_max_y = self._max_y_of_well(well)
|
|
@@ -9,6 +9,9 @@ AbsorbanceReaderLidId = NewType("AbsorbanceReaderLidId", str)
|
|
|
9
9
|
AbsorbanceReaderMeasureMode = NewType("AbsorbanceReaderMeasureMode", str)
|
|
10
10
|
|
|
11
11
|
|
|
12
|
+
# todo(mm, 2024-11-08): frozen=True is getting pretty painful because ModuleStore has
|
|
13
|
+
# no type-safe way to modify just a single attribute. Consider unfreezing this
|
|
14
|
+
# (taking care to ensure that consumers of ModuleView still only get a read-only view).
|
|
12
15
|
@dataclass(frozen=True)
|
|
13
16
|
class AbsorbanceReaderSubState:
|
|
14
17
|
"""Absorbance-Plate-Reader-specific state."""
|
|
@@ -21,7 +24,6 @@ class AbsorbanceReaderSubState:
|
|
|
21
24
|
configured_wavelengths: Optional[List[int]]
|
|
22
25
|
measure_mode: Optional[AbsorbanceReaderMeasureMode]
|
|
23
26
|
reference_wavelength: Optional[int]
|
|
24
|
-
lid_id: Optional[str]
|
|
25
27
|
|
|
26
28
|
def raise_if_lid_status_not_expected(self, lid_on_expected: bool) -> None:
|
|
27
29
|
"""Raise if the lid status is not correct."""
|
|
@@ -26,13 +26,15 @@ from opentrons.motion_planning.adjacent_slots_getters import (
|
|
|
26
26
|
get_west_slot,
|
|
27
27
|
get_adjacent_staging_slot,
|
|
28
28
|
)
|
|
29
|
+
from opentrons.protocol_engine.actions.get_state_update import get_state_updates
|
|
29
30
|
from opentrons.protocol_engine.commands.calibration.calibrate_module import (
|
|
30
31
|
CalibrateModuleResult,
|
|
31
32
|
)
|
|
33
|
+
from opentrons.protocol_engine.state import update_types
|
|
32
34
|
from opentrons.protocol_engine.state.module_substates.absorbance_reader_substate import (
|
|
33
35
|
AbsorbanceReaderMeasureMode,
|
|
34
36
|
)
|
|
35
|
-
from opentrons.types import DeckSlotName, MountType
|
|
37
|
+
from opentrons.types import DeckSlotName, MountType, StagingSlotName
|
|
36
38
|
from ..errors import ModuleNotConnectedError
|
|
37
39
|
|
|
38
40
|
from ..types import (
|
|
@@ -67,7 +69,6 @@ from ..actions import (
|
|
|
67
69
|
Action,
|
|
68
70
|
SucceedCommandAction,
|
|
69
71
|
AddModuleAction,
|
|
70
|
-
AddAbsorbanceReaderLidAction,
|
|
71
72
|
)
|
|
72
73
|
from ._abstract_store import HasState, HandlesActions
|
|
73
74
|
from .module_substates import (
|
|
@@ -234,13 +235,14 @@ class ModuleStore(HasState[ModuleState], HandlesActions):
|
|
|
234
235
|
requested_model=None,
|
|
235
236
|
module_live_data=action.module_live_data,
|
|
236
237
|
)
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
lid_id=action.lid_id,
|
|
241
|
-
)
|
|
238
|
+
|
|
239
|
+
for state_update in get_state_updates(action):
|
|
240
|
+
self._handle_state_update(state_update)
|
|
242
241
|
|
|
243
242
|
def _handle_command(self, command: Command) -> None:
|
|
243
|
+
# todo(mm, 2024-11-04): Delete this function. Port these isinstance()
|
|
244
|
+
# checks to the update_types.StateUpdate mechanism.
|
|
245
|
+
|
|
244
246
|
if isinstance(command.result, LoadModuleResult):
|
|
245
247
|
slot_name = command.params.location.slotName
|
|
246
248
|
self._add_module_substate(
|
|
@@ -297,38 +299,40 @@ class ModuleStore(HasState[ModuleState], HandlesActions):
|
|
|
297
299
|
if isinstance(
|
|
298
300
|
command.result,
|
|
299
301
|
(
|
|
300
|
-
absorbance_reader.CloseLidResult,
|
|
301
|
-
absorbance_reader.OpenLidResult,
|
|
302
302
|
absorbance_reader.InitializeResult,
|
|
303
303
|
absorbance_reader.ReadAbsorbanceResult,
|
|
304
304
|
),
|
|
305
305
|
):
|
|
306
306
|
self._handle_absorbance_reader_commands(command)
|
|
307
307
|
|
|
308
|
-
def
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
308
|
+
def _handle_state_update(self, state_update: update_types.StateUpdate) -> None:
|
|
309
|
+
if state_update.absorbance_reader_lid != update_types.NO_CHANGE:
|
|
310
|
+
module_id = state_update.absorbance_reader_lid.module_id
|
|
311
|
+
is_lid_on = state_update.absorbance_reader_lid.is_lid_on
|
|
312
|
+
|
|
313
|
+
# Get current values:
|
|
314
|
+
absorbance_reader_substate = self._state.substate_by_module_id[module_id]
|
|
315
|
+
assert isinstance(
|
|
316
|
+
absorbance_reader_substate, AbsorbanceReaderSubState
|
|
317
|
+
), f"{module_id} is not an absorbance plate reader."
|
|
318
|
+
configured = absorbance_reader_substate.configured
|
|
319
|
+
measure_mode = absorbance_reader_substate.measure_mode
|
|
320
|
+
configured_wavelengths = absorbance_reader_substate.configured_wavelengths
|
|
321
|
+
reference_wavelength = absorbance_reader_substate.reference_wavelength
|
|
322
|
+
data = absorbance_reader_substate.data
|
|
317
323
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
lid_id=lid_id,
|
|
329
|
-
)
|
|
324
|
+
self._state.substate_by_module_id[module_id] = AbsorbanceReaderSubState(
|
|
325
|
+
module_id=AbsorbanceReaderId(module_id),
|
|
326
|
+
configured=configured,
|
|
327
|
+
measured=True,
|
|
328
|
+
is_lid_on=is_lid_on,
|
|
329
|
+
measure_mode=measure_mode,
|
|
330
|
+
configured_wavelengths=configured_wavelengths,
|
|
331
|
+
reference_wavelength=reference_wavelength,
|
|
332
|
+
data=data,
|
|
333
|
+
)
|
|
330
334
|
|
|
331
|
-
def _add_module_substate(
|
|
335
|
+
def _add_module_substate(
|
|
332
336
|
self,
|
|
333
337
|
module_id: str,
|
|
334
338
|
serial_number: Optional[str],
|
|
@@ -387,16 +391,6 @@ class ModuleStore(HasState[ModuleState], HandlesActions):
|
|
|
387
391
|
module_id=MagneticBlockId(module_id)
|
|
388
392
|
)
|
|
389
393
|
elif ModuleModel.is_absorbance_reader(actual_model):
|
|
390
|
-
lid_labware_id = None
|
|
391
|
-
slot = self._state.slot_by_module_id[module_id]
|
|
392
|
-
if slot is not None:
|
|
393
|
-
reader_addressable_area = f"absorbanceReaderV1{slot.value}"
|
|
394
|
-
for labware in self._state.deck_fixed_labware:
|
|
395
|
-
if labware.location == AddressableAreaLocation(
|
|
396
|
-
addressableAreaName=reader_addressable_area
|
|
397
|
-
):
|
|
398
|
-
lid_labware_id = labware.labware_id
|
|
399
|
-
break
|
|
400
394
|
self._state.substate_by_module_id[module_id] = AbsorbanceReaderSubState(
|
|
401
395
|
module_id=AbsorbanceReaderId(module_id),
|
|
402
396
|
configured=False,
|
|
@@ -406,7 +400,6 @@ class ModuleStore(HasState[ModuleState], HandlesActions):
|
|
|
406
400
|
measure_mode=None,
|
|
407
401
|
configured_wavelengths=None,
|
|
408
402
|
reference_wavelength=None,
|
|
409
|
-
lid_id=lid_labware_id,
|
|
410
403
|
)
|
|
411
404
|
|
|
412
405
|
def _update_additional_slots_occupied_by_thermocycler(
|
|
@@ -600,8 +593,6 @@ class ModuleStore(HasState[ModuleState], HandlesActions):
|
|
|
600
593
|
command: Union[
|
|
601
594
|
absorbance_reader.Initialize,
|
|
602
595
|
absorbance_reader.ReadAbsorbance,
|
|
603
|
-
absorbance_reader.CloseLid,
|
|
604
|
-
absorbance_reader.OpenLid,
|
|
605
596
|
],
|
|
606
597
|
) -> None:
|
|
607
598
|
module_id = command.params.moduleId
|
|
@@ -616,8 +607,6 @@ class ModuleStore(HasState[ModuleState], HandlesActions):
|
|
|
616
607
|
configured_wavelengths = absorbance_reader_substate.configured_wavelengths
|
|
617
608
|
reference_wavelength = absorbance_reader_substate.reference_wavelength
|
|
618
609
|
is_lid_on = absorbance_reader_substate.is_lid_on
|
|
619
|
-
lid_id = absorbance_reader_substate.lid_id
|
|
620
|
-
data = absorbance_reader_substate.data
|
|
621
610
|
|
|
622
611
|
if isinstance(command.result, absorbance_reader.InitializeResult):
|
|
623
612
|
self._state.substate_by_module_id[module_id] = AbsorbanceReaderSubState(
|
|
@@ -625,7 +614,6 @@ class ModuleStore(HasState[ModuleState], HandlesActions):
|
|
|
625
614
|
configured=True,
|
|
626
615
|
measured=False,
|
|
627
616
|
is_lid_on=is_lid_on,
|
|
628
|
-
lid_id=lid_id,
|
|
629
617
|
measure_mode=AbsorbanceReaderMeasureMode(command.params.measureMode),
|
|
630
618
|
configured_wavelengths=command.params.sampleWavelengths,
|
|
631
619
|
reference_wavelength=command.params.referenceWavelength,
|
|
@@ -637,39 +625,12 @@ class ModuleStore(HasState[ModuleState], HandlesActions):
|
|
|
637
625
|
configured=configured,
|
|
638
626
|
measured=True,
|
|
639
627
|
is_lid_on=is_lid_on,
|
|
640
|
-
lid_id=lid_id,
|
|
641
628
|
measure_mode=measure_mode,
|
|
642
629
|
configured_wavelengths=configured_wavelengths,
|
|
643
630
|
reference_wavelength=reference_wavelength,
|
|
644
631
|
data=command.result.data,
|
|
645
632
|
)
|
|
646
633
|
|
|
647
|
-
elif isinstance(command.result, absorbance_reader.OpenLidResult):
|
|
648
|
-
self._state.substate_by_module_id[module_id] = AbsorbanceReaderSubState(
|
|
649
|
-
module_id=AbsorbanceReaderId(module_id),
|
|
650
|
-
configured=configured,
|
|
651
|
-
measured=True,
|
|
652
|
-
is_lid_on=False,
|
|
653
|
-
lid_id=lid_id,
|
|
654
|
-
measure_mode=measure_mode,
|
|
655
|
-
configured_wavelengths=configured_wavelengths,
|
|
656
|
-
reference_wavelength=reference_wavelength,
|
|
657
|
-
data=data,
|
|
658
|
-
)
|
|
659
|
-
|
|
660
|
-
elif isinstance(command.result, absorbance_reader.CloseLidResult):
|
|
661
|
-
self._state.substate_by_module_id[module_id] = AbsorbanceReaderSubState(
|
|
662
|
-
module_id=AbsorbanceReaderId(module_id),
|
|
663
|
-
configured=configured,
|
|
664
|
-
measured=True,
|
|
665
|
-
is_lid_on=True,
|
|
666
|
-
lid_id=lid_id,
|
|
667
|
-
measure_mode=measure_mode,
|
|
668
|
-
configured_wavelengths=configured_wavelengths,
|
|
669
|
-
reference_wavelength=reference_wavelength,
|
|
670
|
-
data=data,
|
|
671
|
-
)
|
|
672
|
-
|
|
673
634
|
|
|
674
635
|
class ModuleView(HasState[ModuleState]):
|
|
675
636
|
"""Read-only view of computed module state."""
|
|
@@ -883,12 +844,21 @@ class ModuleView(HasState[ModuleState]):
|
|
|
883
844
|
"""Get the specified module's dimensions."""
|
|
884
845
|
return self.get_definition(module_id).dimensions
|
|
885
846
|
|
|
886
|
-
def
|
|
847
|
+
def get_nominal_offset_to_child(
|
|
887
848
|
self,
|
|
888
849
|
module_id: str,
|
|
850
|
+
# todo(mm, 2024-11-07): A method of one view taking a sibling view as an argument
|
|
851
|
+
# is unusual, and may be bug-prone if the order in which the views are updated
|
|
852
|
+
# matters. If we need to compute something that depends on module info and
|
|
853
|
+
# addressable area info, can we do that computation in GeometryView instead of
|
|
854
|
+
# here?
|
|
889
855
|
addressable_areas: AddressableAreaView,
|
|
890
856
|
) -> LabwareOffsetVector:
|
|
891
|
-
"""Get the module's
|
|
857
|
+
"""Get the nominal offset from a module's location to its child labware's location.
|
|
858
|
+
|
|
859
|
+
Includes the slot-specific transform. Does not include the child's
|
|
860
|
+
Labware Position Check offset.
|
|
861
|
+
"""
|
|
892
862
|
if (
|
|
893
863
|
self.state.deck_type == DeckType.OT2_STANDARD
|
|
894
864
|
or self.state.deck_type == DeckType.OT2_SHORT_TRASH
|
|
@@ -996,7 +966,7 @@ class ModuleView(HasState[ModuleState]):
|
|
|
996
966
|
default_lw_offset_point = self.get_definition(module_id).labwareOffset.z
|
|
997
967
|
z_difference = module_height - default_lw_offset_point
|
|
998
968
|
|
|
999
|
-
nominal_transformed_lw_offset_z = self.
|
|
969
|
+
nominal_transformed_lw_offset_z = self.get_nominal_offset_to_child(
|
|
1000
970
|
module_id=module_id, addressable_areas=addressable_areas
|
|
1001
971
|
).z
|
|
1002
972
|
calibration_offset = self.get_module_calibration_offset(module_id)
|
|
@@ -1124,8 +1094,8 @@ class ModuleView(HasState[ModuleState]):
|
|
|
1124
1094
|
|
|
1125
1095
|
def should_dodge_thermocycler(
|
|
1126
1096
|
self,
|
|
1127
|
-
from_slot: DeckSlotName,
|
|
1128
|
-
to_slot: DeckSlotName,
|
|
1097
|
+
from_slot: Union[DeckSlotName, StagingSlotName],
|
|
1098
|
+
to_slot: Union[DeckSlotName, StagingSlotName],
|
|
1129
1099
|
) -> bool:
|
|
1130
1100
|
"""Decide if the requested path would cross the thermocycler, if installed.
|
|
1131
1101
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
from dataclasses import dataclass
|
|
3
3
|
from typing import List, Optional, Union
|
|
4
4
|
|
|
5
|
-
from opentrons.types import MountType, Point
|
|
5
|
+
from opentrons.types import MountType, Point, StagingSlotName
|
|
6
6
|
from opentrons.hardware_control.types import CriticalPoint
|
|
7
7
|
from opentrons.motion_planning.adjacent_slots_getters import (
|
|
8
8
|
get_east_west_slots,
|
|
@@ -277,9 +277,13 @@ class MotionView:
|
|
|
277
277
|
current_location = self._pipettes.get_current_location()
|
|
278
278
|
if current_location is not None:
|
|
279
279
|
if isinstance(current_location, CurrentWell):
|
|
280
|
-
|
|
280
|
+
ancestor = self._geometry.get_ancestor_slot_name(
|
|
281
281
|
current_location.labware_id
|
|
282
|
-
)
|
|
282
|
+
)
|
|
283
|
+
if isinstance(ancestor, StagingSlotName):
|
|
284
|
+
# Staging Area Slots cannot intersect with the h/s
|
|
285
|
+
return False
|
|
286
|
+
pipette_deck_slot = ancestor.as_int()
|
|
283
287
|
else:
|
|
284
288
|
pipette_deck_slot = (
|
|
285
289
|
self._addressable_areas.get_addressable_area_base_slot(
|
|
@@ -299,9 +303,13 @@ class MotionView:
|
|
|
299
303
|
current_location = self._pipettes.get_current_location()
|
|
300
304
|
if current_location is not None:
|
|
301
305
|
if isinstance(current_location, CurrentWell):
|
|
302
|
-
|
|
306
|
+
ancestor = self._geometry.get_ancestor_slot_name(
|
|
303
307
|
current_location.labware_id
|
|
304
|
-
)
|
|
308
|
+
)
|
|
309
|
+
if isinstance(ancestor, StagingSlotName):
|
|
310
|
+
# Staging Area Slots cannot intersect with the h/s
|
|
311
|
+
return False
|
|
312
|
+
pipette_deck_slot = ancestor.as_int()
|
|
305
313
|
else:
|
|
306
314
|
pipette_deck_slot = (
|
|
307
315
|
self._addressable_areas.get_addressable_area_base_slot(
|
|
@@ -324,6 +332,10 @@ class MotionView:
|
|
|
324
332
|
"""Get a list of touch points for a touch tip operation."""
|
|
325
333
|
mount = self._pipettes.get_mount(pipette_id)
|
|
326
334
|
labware_slot = self._geometry.get_ancestor_slot_name(labware_id)
|
|
335
|
+
if isinstance(labware_slot, StagingSlotName):
|
|
336
|
+
raise errors.LocationIsStagingSlotError(
|
|
337
|
+
"Cannot perform Touch Tip on labware in Staging Area Slot."
|
|
338
|
+
)
|
|
327
339
|
next_to_module = self._modules.is_edge_move_unsafe(mount, labware_slot)
|
|
328
340
|
edge_path_type = self._labware.get_edge_path_type(
|
|
329
341
|
labware_id, well_name, mount, labware_slot, next_to_module
|
|
@@ -205,6 +205,14 @@ class LiquidOperatedUpdate:
|
|
|
205
205
|
volume_added: float | ClearType
|
|
206
206
|
|
|
207
207
|
|
|
208
|
+
@dataclasses.dataclass
|
|
209
|
+
class AbsorbanceReaderLidUpdate:
|
|
210
|
+
"""An update to an absorbance reader's lid location."""
|
|
211
|
+
|
|
212
|
+
module_id: str
|
|
213
|
+
is_lid_on: bool
|
|
214
|
+
|
|
215
|
+
|
|
208
216
|
@dataclasses.dataclass
|
|
209
217
|
class StateUpdate:
|
|
210
218
|
"""Represents an update to perform on engine state."""
|
|
@@ -231,6 +239,8 @@ class StateUpdate:
|
|
|
231
239
|
|
|
232
240
|
liquid_operated: LiquidOperatedUpdate | NoChangeType = NO_CHANGE
|
|
233
241
|
|
|
242
|
+
absorbance_reader_lid: AbsorbanceReaderLidUpdate | NoChangeType = NO_CHANGE
|
|
243
|
+
|
|
234
244
|
# These convenience functions let the caller avoid the boilerplate of constructing a
|
|
235
245
|
# complicated dataclass tree.
|
|
236
246
|
|
|
@@ -406,3 +416,9 @@ class StateUpdate:
|
|
|
406
416
|
well_name=well_name,
|
|
407
417
|
volume_added=volume_added,
|
|
408
418
|
)
|
|
419
|
+
|
|
420
|
+
def set_absorbance_reader_lid(self, module_id: str, is_lid_on: bool) -> None:
|
|
421
|
+
"""Update an absorbance reader's lid location. See `AbsorbanceReaderLidUpdate`."""
|
|
422
|
+
self.absorbance_reader_lid = AbsorbanceReaderLidUpdate(
|
|
423
|
+
module_id=module_id, is_lid_on=is_lid_on
|
|
424
|
+
)
|
opentrons/util/logging_config.py
CHANGED
|
@@ -5,7 +5,7 @@ from typing import Any, Dict
|
|
|
5
5
|
|
|
6
6
|
from opentrons.config import CONFIG, ARCHITECTURE, SystemArchitecture
|
|
7
7
|
|
|
8
|
-
if ARCHITECTURE is SystemArchitecture.
|
|
8
|
+
if ARCHITECTURE is SystemArchitecture.YOCTO:
|
|
9
9
|
from opentrons_hardware.sensors import SENSOR_LOG_NAME
|
|
10
10
|
else:
|
|
11
11
|
# we don't use the sensor log on ot2 or host
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: opentrons
|
|
3
|
-
Version: 8.2.
|
|
3
|
+
Version: 8.2.0a2
|
|
4
4
|
Summary: The Opentrons API is a simple framework designed to make writing automated biology lab protocols easy.
|
|
5
5
|
Author: Opentrons
|
|
6
6
|
Author-email: engineering@opentrons.com
|
|
@@ -21,7 +21,7 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
21
21
|
Classifier: Topic :: Scientific/Engineering
|
|
22
22
|
Requires-Python: >=3.10
|
|
23
23
|
License-File: ../LICENSE
|
|
24
|
-
Requires-Dist: opentrons-shared-data ==8.2.
|
|
24
|
+
Requires-Dist: opentrons-shared-data ==8.2.0a2
|
|
25
25
|
Requires-Dist: aionotify ==0.3.1
|
|
26
26
|
Requires-Dist: anyio <4.0.0,>=3.6.1
|
|
27
27
|
Requires-Dist: jsonschema <4.18.0,>=3.0.1
|
|
@@ -34,9 +34,9 @@ Requires-Dist: pyusb ==1.2.1
|
|
|
34
34
|
Requires-Dist: packaging >=21.0
|
|
35
35
|
Requires-Dist: importlib-metadata >=1.0 ; python_version < "3.8"
|
|
36
36
|
Provides-Extra: flex-hardware
|
|
37
|
-
Requires-Dist: opentrons-hardware[flex] ==8.2.
|
|
37
|
+
Requires-Dist: opentrons-hardware[flex] ==8.2.0a2 ; extra == 'flex-hardware'
|
|
38
38
|
Provides-Extra: ot2-hardware
|
|
39
|
-
Requires-Dist: opentrons-hardware ==8.2.
|
|
39
|
+
Requires-Dist: opentrons-hardware ==8.2.0a2 ; extra == 'ot2-hardware'
|
|
40
40
|
|
|
41
41
|
.. _Full API Documentation: http://docs.opentrons.com
|
|
42
42
|
|