opentrons 8.2.0__py2.py3-none-any.whl → 8.2.0a0__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.
Potentially problematic release.
This version of opentrons might be problematic. Click here for more details.
- opentrons/drivers/absorbance_reader/async_byonoy.py +5 -6
- opentrons/hardware_control/backends/ot3utils.py +0 -1
- opentrons/hardware_control/modules/absorbance_reader.py +0 -2
- opentrons/hardware_control/ot3api.py +5 -5
- opentrons/hardware_control/protocols/position_estimator.py +1 -3
- opentrons/hardware_control/types.py +0 -2
- opentrons/legacy_commands/helpers.py +2 -8
- opentrons/protocol_api/core/engine/labware.py +2 -10
- opentrons/protocol_api/core/engine/module_core.py +1 -38
- opentrons/protocol_api/core/engine/pipette_movement_conflict.py +5 -12
- opentrons/protocol_api/core/engine/protocol.py +30 -5
- opentrons/protocol_api/core/labware.py +0 -4
- opentrons/protocol_api/core/legacy/legacy_labware_core.py +0 -5
- opentrons/protocol_api/core/legacy/legacy_protocol_core.py +0 -1
- opentrons/protocol_api/core/protocol.py +0 -1
- opentrons/protocol_api/module_contexts.py +26 -69
- opentrons/protocol_api/protocol_context.py +2 -12
- opentrons/protocol_engine/actions/__init__.py +2 -0
- opentrons/protocol_engine/actions/actions.py +12 -0
- opentrons/protocol_engine/clients/sync_client.py +6 -0
- opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +31 -18
- opentrons/protocol_engine/commands/absorbance_reader/initialize.py +7 -19
- opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +29 -17
- opentrons/protocol_engine/commands/absorbance_reader/read.py +0 -4
- opentrons/protocol_engine/commands/aspirate_in_place.py +3 -3
- opentrons/protocol_engine/commands/command.py +1 -3
- opentrons/protocol_engine/commands/dispense_in_place.py +1 -1
- opentrons/protocol_engine/commands/drop_tip.py +1 -2
- opentrons/protocol_engine/commands/drop_tip_in_place.py +2 -7
- opentrons/protocol_engine/commands/load_labware.py +0 -9
- opentrons/protocol_engine/commands/load_module.py +39 -0
- opentrons/protocol_engine/commands/move_labware.py +4 -49
- opentrons/protocol_engine/commands/pick_up_tip.py +1 -1
- opentrons/protocol_engine/commands/pipetting_common.py +1 -8
- opentrons/protocol_engine/commands/unsafe/unsafe_place_labware.py +35 -49
- opentrons/protocol_engine/commands/unsafe/unsafe_ungrip_labware.py +1 -3
- opentrons/protocol_engine/commands/unsafe/update_position_estimators.py +1 -5
- opentrons/protocol_engine/create_protocol_engine.py +1 -18
- opentrons/protocol_engine/errors/__init__.py +0 -2
- opentrons/protocol_engine/errors/error_occurrence.py +3 -8
- opentrons/protocol_engine/errors/exceptions.py +0 -13
- opentrons/protocol_engine/execution/labware_movement.py +21 -69
- opentrons/protocol_engine/execution/movement.py +4 -9
- opentrons/protocol_engine/protocol_engine.py +7 -0
- opentrons/protocol_engine/resources/deck_data_provider.py +39 -0
- opentrons/protocol_engine/resources/file_provider.py +7 -11
- opentrons/protocol_engine/resources/fixture_validation.py +1 -6
- opentrons/protocol_engine/state/geometry.py +49 -91
- opentrons/protocol_engine/state/labware.py +25 -102
- opentrons/protocol_engine/state/module_substates/absorbance_reader_substate.py +1 -3
- opentrons/protocol_engine/state/modules.py +80 -53
- opentrons/protocol_engine/state/motion.py +5 -17
- opentrons/protocol_engine/state/update_types.py +0 -16
- opentrons/protocol_runner/run_orchestrator.py +0 -15
- opentrons/protocols/parameters/csv_parameter_interface.py +1 -3
- opentrons/util/logging_config.py +1 -1
- {opentrons-8.2.0.dist-info → opentrons-8.2.0a0.dist-info}/METADATA +4 -4
- {opentrons-8.2.0.dist-info → opentrons-8.2.0a0.dist-info}/RECORD +62 -62
- {opentrons-8.2.0.dist-info → opentrons-8.2.0a0.dist-info}/LICENSE +0 -0
- {opentrons-8.2.0.dist-info → opentrons-8.2.0a0.dist-info}/WHEEL +0 -0
- {opentrons-8.2.0.dist-info → opentrons-8.2.0a0.dist-info}/entry_points.txt +0 -0
- {opentrons-8.2.0.dist-info → opentrons-8.2.0a0.dist-info}/top_level.txt +0 -0
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
"""Place labware payload, result, and implementaiton."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
|
-
from typing import TYPE_CHECKING, Optional, Type
|
|
5
|
-
from typing_extensions import Literal
|
|
6
|
-
|
|
7
|
-
from opentrons_shared_data.labware.types import LabwareUri
|
|
8
|
-
from opentrons_shared_data.labware.labware_definition import LabwareDefinition
|
|
9
4
|
from pydantic import BaseModel, Field
|
|
5
|
+
from typing import TYPE_CHECKING, Optional, Type, cast
|
|
6
|
+
from typing_extensions import Literal
|
|
10
7
|
|
|
11
8
|
from opentrons.hardware_control.types import Axis, OT3Mount
|
|
12
9
|
from opentrons.motion_planning.waypoints import get_gripper_labware_placement_waypoints
|
|
@@ -16,14 +13,11 @@ from opentrons.protocol_engine.errors.exceptions import (
|
|
|
16
13
|
)
|
|
17
14
|
from opentrons.types import Point
|
|
18
15
|
|
|
19
|
-
from ...types import
|
|
20
|
-
DeckSlotLocation,
|
|
21
|
-
ModuleModel,
|
|
22
|
-
OnDeckLabwareLocation,
|
|
23
|
-
)
|
|
16
|
+
from ...types import DeckSlotLocation, ModuleModel, OnDeckLabwareLocation
|
|
24
17
|
from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
|
|
25
18
|
from ...errors.error_occurrence import ErrorOccurrence
|
|
26
19
|
from ...resources import ensure_ot3_hardware
|
|
20
|
+
from ...state.update_types import StateUpdate
|
|
27
21
|
|
|
28
22
|
from opentrons.hardware_control import HardwareControlAPI, OT3HardwareControlAPI
|
|
29
23
|
|
|
@@ -38,7 +32,7 @@ UnsafePlaceLabwareCommandType = Literal["unsafe/placeLabware"]
|
|
|
38
32
|
class UnsafePlaceLabwareParams(BaseModel):
|
|
39
33
|
"""Payload required for an UnsafePlaceLabware command."""
|
|
40
34
|
|
|
41
|
-
|
|
35
|
+
labwareId: str = Field(..., description="The id of the labware to place.")
|
|
42
36
|
location: OnDeckLabwareLocation = Field(
|
|
43
37
|
..., description="Where to place the labware."
|
|
44
38
|
)
|
|
@@ -77,8 +71,8 @@ class UnsafePlaceLabwareImplementation(
|
|
|
77
71
|
is pressed, get into error recovery, etc).
|
|
78
72
|
|
|
79
73
|
Unlike the `moveLabware` command, where you pick a source and destination
|
|
80
|
-
location, this command takes the
|
|
81
|
-
|
|
74
|
+
location, this command takes the labwareId to be moved and location to
|
|
75
|
+
move it to.
|
|
82
76
|
|
|
83
77
|
"""
|
|
84
78
|
ot3api = ensure_ot3_hardware(self._hardware_api)
|
|
@@ -90,37 +84,23 @@ class UnsafePlaceLabwareImplementation(
|
|
|
90
84
|
"Cannot place labware when gripper is not gripping."
|
|
91
85
|
)
|
|
92
86
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
)
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
# todo(mm, 2024-11-07): This is an unsafe cast from untrusted input.
|
|
99
|
-
# We need a str -> LabwareUri parse/validate function.
|
|
100
|
-
LabwareUri(params.labwareURI)
|
|
101
|
-
)
|
|
102
|
-
|
|
103
|
-
# todo(mm, 2024-11-06): This is only correct in the special case of an
|
|
104
|
-
# absorbance reader lid. Its definition currently puts the offsets for *itself*
|
|
105
|
-
# in the property that's normally meant for offsets for its *children.*
|
|
106
|
-
final_offsets = self._state_view.labware.get_child_gripper_offsets(
|
|
107
|
-
labware_definition=definition, slot_name=None
|
|
108
|
-
)
|
|
109
|
-
drop_offset = (
|
|
110
|
-
Point(
|
|
111
|
-
final_offsets.dropOffset.x,
|
|
112
|
-
final_offsets.dropOffset.y,
|
|
113
|
-
final_offsets.dropOffset.z,
|
|
114
|
-
)
|
|
115
|
-
if final_offsets
|
|
116
|
-
else None
|
|
87
|
+
# Allow propagation of LabwareNotLoadedError.
|
|
88
|
+
labware_id = params.labwareId
|
|
89
|
+
definition_uri = self._state_view.labware.get(labware_id).definitionUri
|
|
90
|
+
final_offsets = self._state_view.labware.get_labware_gripper_offsets(
|
|
91
|
+
labware_id, None
|
|
117
92
|
)
|
|
93
|
+
drop_offset = cast(Point, final_offsets.dropOffset) if final_offsets else None
|
|
118
94
|
|
|
119
95
|
if isinstance(params.location, DeckSlotLocation):
|
|
120
96
|
self._state_view.addressable_areas.raise_if_area_not_in_deck_configuration(
|
|
121
97
|
params.location.slotName.id
|
|
122
98
|
)
|
|
123
99
|
|
|
100
|
+
location = self._state_view.geometry.ensure_valid_gripper_location(
|
|
101
|
+
params.location,
|
|
102
|
+
)
|
|
103
|
+
|
|
124
104
|
# This is an absorbance reader, move the lid to its dock (staging area).
|
|
125
105
|
if isinstance(location, DeckSlotLocation):
|
|
126
106
|
module = self._state_view.modules.get_by_slot(location.slotName)
|
|
@@ -129,24 +109,30 @@ class UnsafePlaceLabwareImplementation(
|
|
|
129
109
|
module.id
|
|
130
110
|
)
|
|
131
111
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
await ot3api.
|
|
112
|
+
new_offset_id = self._equipment.find_applicable_labware_offset_id(
|
|
113
|
+
labware_definition_uri=definition_uri,
|
|
114
|
+
labware_location=location,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
# NOTE: When the estop is pressed, the gantry loses position,
|
|
118
|
+
# so the robot needs to home x, y to sync.
|
|
119
|
+
await ot3api.home(axes=[Axis.Z_L, Axis.Z_R, Axis.Z_G, Axis.X, Axis.Y])
|
|
120
|
+
state_update = StateUpdate()
|
|
140
121
|
|
|
141
122
|
# Place the labware down
|
|
142
|
-
await self._start_movement(ot3api,
|
|
123
|
+
await self._start_movement(ot3api, labware_id, location, drop_offset)
|
|
143
124
|
|
|
144
|
-
|
|
125
|
+
state_update.set_labware_location(
|
|
126
|
+
labware_id=labware_id,
|
|
127
|
+
new_location=location,
|
|
128
|
+
new_offset_id=new_offset_id,
|
|
129
|
+
)
|
|
130
|
+
return SuccessData(public=UnsafePlaceLabwareResult(), state_update=state_update)
|
|
145
131
|
|
|
146
132
|
async def _start_movement(
|
|
147
133
|
self,
|
|
148
134
|
ot3api: OT3HardwareControlAPI,
|
|
149
|
-
|
|
135
|
+
labware_id: str,
|
|
150
136
|
location: OnDeckLabwareLocation,
|
|
151
137
|
drop_offset: Optional[Point],
|
|
152
138
|
) -> None:
|
|
@@ -156,7 +142,7 @@ class UnsafePlaceLabwareImplementation(
|
|
|
156
142
|
)
|
|
157
143
|
|
|
158
144
|
to_labware_center = self._state_view.geometry.get_labware_grip_point(
|
|
159
|
-
|
|
145
|
+
labware_id=labware_id, location=location
|
|
160
146
|
)
|
|
161
147
|
|
|
162
148
|
movement_waypoints = get_gripper_labware_placement_waypoints(
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
"""Ungrip labware payload, result, and implementaiton."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
from opentrons.hardware_control.types import Axis
|
|
6
4
|
from opentrons.protocol_engine.errors.exceptions import GripperNotAttachedError
|
|
7
5
|
from pydantic import BaseModel
|
|
8
6
|
from typing import Optional, Type
|
|
@@ -48,7 +46,7 @@ class UnsafeUngripLabwareImplementation(
|
|
|
48
46
|
ot3_hardware_api = ensure_ot3_hardware(self._hardware_api)
|
|
49
47
|
if not ot3_hardware_api.has_gripper():
|
|
50
48
|
raise GripperNotAttachedError("No gripper found to perform ungrip.")
|
|
51
|
-
await ot3_hardware_api.
|
|
49
|
+
await ot3_hardware_api.ungrip()
|
|
52
50
|
return SuccessData(
|
|
53
51
|
public=UnsafeUngripLabwareResult(),
|
|
54
52
|
)
|
|
@@ -23,11 +23,7 @@ class UpdatePositionEstimatorsParams(BaseModel):
|
|
|
23
23
|
"""Payload required for an UpdatePositionEstimators command."""
|
|
24
24
|
|
|
25
25
|
axes: List[MotorAxis] = Field(
|
|
26
|
-
...,
|
|
27
|
-
description=(
|
|
28
|
-
"The axes for which to update the position estimators."
|
|
29
|
-
" Any axes that are not physically present will be ignored."
|
|
30
|
-
),
|
|
26
|
+
..., description="The axes for which to update the position estimators."
|
|
31
27
|
)
|
|
32
28
|
|
|
33
29
|
|
|
@@ -8,9 +8,6 @@ from opentrons.hardware_control.types import DoorState
|
|
|
8
8
|
from opentrons.protocol_engine.execution.error_recovery_hardware_state_synchronizer import (
|
|
9
9
|
ErrorRecoveryHardwareStateSynchronizer,
|
|
10
10
|
)
|
|
11
|
-
from opentrons.protocol_engine.resources.labware_data_provider import (
|
|
12
|
-
LabwareDataProvider,
|
|
13
|
-
)
|
|
14
11
|
from opentrons.util.async_helpers import async_context_manager_in_thread
|
|
15
12
|
|
|
16
13
|
from opentrons_shared_data.robot import load as load_robot
|
|
@@ -84,7 +81,7 @@ async def create_protocol_engine(
|
|
|
84
81
|
module_data_provider = ModuleDataProvider()
|
|
85
82
|
file_provider = file_provider or FileProvider()
|
|
86
83
|
|
|
87
|
-
|
|
84
|
+
return ProtocolEngine(
|
|
88
85
|
hardware_api=hardware_api,
|
|
89
86
|
state_store=state_store,
|
|
90
87
|
action_dispatcher=action_dispatcher,
|
|
@@ -96,20 +93,6 @@ async def create_protocol_engine(
|
|
|
96
93
|
file_provider=file_provider,
|
|
97
94
|
)
|
|
98
95
|
|
|
99
|
-
# todo(mm, 2024-11-08): This is a quick hack to support the absorbance reader, which
|
|
100
|
-
# expects the engine to have this special labware definition available. It would be
|
|
101
|
-
# cleaner for the `loadModule` command to do this I/O and insert the definition
|
|
102
|
-
# into state. That gets easier after https://opentrons.atlassian.net/browse/EXEC-756.
|
|
103
|
-
#
|
|
104
|
-
# NOTE: This needs to stay in sync with LabwareView.get_absorbance_reader_lid_definition().
|
|
105
|
-
pe.add_labware_definition(
|
|
106
|
-
await LabwareDataProvider().get_labware_definition(
|
|
107
|
-
"opentrons_flex_lid_absorbance_plate_reader_module", "opentrons", 1
|
|
108
|
-
)
|
|
109
|
-
)
|
|
110
|
-
|
|
111
|
-
return pe
|
|
112
|
-
|
|
113
96
|
|
|
114
97
|
@contextlib.contextmanager
|
|
115
98
|
def create_protocol_engine_in_thread(
|
|
@@ -55,7 +55,6 @@ from .exceptions import (
|
|
|
55
55
|
InvalidTargetTemperatureError,
|
|
56
56
|
InvalidBlockVolumeError,
|
|
57
57
|
InvalidHoldTimeError,
|
|
58
|
-
InvalidWavelengthError,
|
|
59
58
|
CannotPerformModuleAction,
|
|
60
59
|
PauseNotAllowedError,
|
|
61
60
|
ResumeFromRecoveryNotAllowedError,
|
|
@@ -138,7 +137,6 @@ __all__ = [
|
|
|
138
137
|
"InvalidTargetSpeedError",
|
|
139
138
|
"InvalidBlockVolumeError",
|
|
140
139
|
"InvalidHoldTimeError",
|
|
141
|
-
"InvalidWavelengthError",
|
|
142
140
|
"CannotPerformModuleAction",
|
|
143
141
|
"ResumeFromRecoveryNotAllowedError",
|
|
144
142
|
"PauseNotAllowedError",
|
|
@@ -12,6 +12,8 @@ from opentrons_shared_data.errors.exceptions import EnumeratedError
|
|
|
12
12
|
log = getLogger(__name__)
|
|
13
13
|
|
|
14
14
|
|
|
15
|
+
# TODO(mc, 2021-11-12): flesh this model out with structured error data
|
|
16
|
+
# for each error type so client may produce better error messages
|
|
15
17
|
class ErrorOccurrence(BaseModel):
|
|
16
18
|
"""An occurrence of a specific error during protocol execution."""
|
|
17
19
|
|
|
@@ -42,15 +44,8 @@ class ErrorOccurrence(BaseModel):
|
|
|
42
44
|
id: str = Field(..., description="Unique identifier of this error occurrence.")
|
|
43
45
|
createdAt: datetime = Field(..., description="When the error occurred.")
|
|
44
46
|
|
|
45
|
-
# Our Python should probably always set this to False--if we want it to be True,
|
|
46
|
-
# we should probably be using a more specific subclass of ErrorOccurrence anyway.
|
|
47
|
-
# However, we can't make this Literal[False], because we want this class to be able
|
|
48
|
-
# to act as a catch-all for parsing defined errors that might be missing some
|
|
49
|
-
# `errorInfo` fields because they were serialized by older software.
|
|
50
47
|
isDefined: bool = Field(
|
|
51
|
-
# default=False for database backwards compatibility
|
|
52
|
-
# serialized before isDefined existed.
|
|
53
|
-
default=False,
|
|
48
|
+
default=False, # default=False for database backwards compatibility.
|
|
54
49
|
description=dedent(
|
|
55
50
|
"""\
|
|
56
51
|
Whether this error is *defined.*
|
|
@@ -773,19 +773,6 @@ class InvalidBlockVolumeError(ProtocolEngineError):
|
|
|
773
773
|
super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
|
|
774
774
|
|
|
775
775
|
|
|
776
|
-
class InvalidWavelengthError(ProtocolEngineError):
|
|
777
|
-
"""Raised when attempting to set an invalid absorbance wavelength."""
|
|
778
|
-
|
|
779
|
-
def __init__(
|
|
780
|
-
self,
|
|
781
|
-
message: Optional[str] = None,
|
|
782
|
-
details: Optional[Dict[str, Any]] = None,
|
|
783
|
-
wrapping: Optional[Sequence[EnumeratedError]] = None,
|
|
784
|
-
) -> None:
|
|
785
|
-
"""Build a InvalidWavelengthError."""
|
|
786
|
-
super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
|
|
787
|
-
|
|
788
|
-
|
|
789
776
|
class InvalidHoldTimeError(ProtocolEngineError):
|
|
790
777
|
"""An error raised when attempting to set an invalid temperature hold time."""
|
|
791
778
|
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
"""Labware movement command handling."""
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
|
-
from typing import Optional, TYPE_CHECKING
|
|
5
|
-
|
|
6
|
-
from opentrons_shared_data.labware.labware_definition import LabwareDefinition
|
|
4
|
+
from typing import Optional, TYPE_CHECKING
|
|
7
5
|
|
|
8
6
|
from opentrons.types import Point
|
|
9
7
|
|
|
@@ -81,64 +79,24 @@ class LabwareMovementHandler:
|
|
|
81
79
|
)
|
|
82
80
|
)
|
|
83
81
|
|
|
84
|
-
@overload
|
|
85
82
|
async def move_labware_with_gripper(
|
|
86
83
|
self,
|
|
87
|
-
*,
|
|
88
84
|
labware_id: str,
|
|
89
85
|
current_location: OnDeckLabwareLocation,
|
|
90
86
|
new_location: OnDeckLabwareLocation,
|
|
91
87
|
user_offset_data: LabwareMovementOffsetData,
|
|
92
88
|
post_drop_slide_offset: Optional[Point],
|
|
93
89
|
) -> None:
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
@overload
|
|
97
|
-
async def move_labware_with_gripper(
|
|
98
|
-
self,
|
|
99
|
-
*,
|
|
100
|
-
labware_definition: LabwareDefinition,
|
|
101
|
-
current_location: OnDeckLabwareLocation,
|
|
102
|
-
new_location: OnDeckLabwareLocation,
|
|
103
|
-
user_offset_data: LabwareMovementOffsetData,
|
|
104
|
-
post_drop_slide_offset: Optional[Point],
|
|
105
|
-
) -> None:
|
|
106
|
-
...
|
|
107
|
-
|
|
108
|
-
async def move_labware_with_gripper( # noqa: C901
|
|
109
|
-
self,
|
|
110
|
-
*,
|
|
111
|
-
labware_id: str | None = None,
|
|
112
|
-
labware_definition: LabwareDefinition | None = None,
|
|
113
|
-
current_location: OnDeckLabwareLocation,
|
|
114
|
-
new_location: OnDeckLabwareLocation,
|
|
115
|
-
user_offset_data: LabwareMovementOffsetData,
|
|
116
|
-
post_drop_slide_offset: Optional[Point],
|
|
117
|
-
) -> None:
|
|
118
|
-
"""Physically move a labware from one location to another using the gripper.
|
|
119
|
-
|
|
120
|
-
Generally, provide the `labware_id` of a loaded labware, and this method will
|
|
121
|
-
automatically look up its labware definition. If you're physically moving
|
|
122
|
-
something that has not been loaded as a labware (this is not common),
|
|
123
|
-
provide the `labware_definition` yourself instead.
|
|
124
|
-
"""
|
|
90
|
+
"""Move a loaded labware from one location to another using gripper."""
|
|
125
91
|
use_virtual_gripper = self._state_store.config.use_virtual_gripper
|
|
126
92
|
|
|
127
|
-
if labware_definition is None:
|
|
128
|
-
assert labware_id is not None # From this method's @typing.overloads.
|
|
129
|
-
labware_definition = self._state_store.labware.get_definition(labware_id)
|
|
130
|
-
|
|
131
93
|
if use_virtual_gripper:
|
|
132
|
-
#
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
gripper_homed_position_z=_GRIPPER_HOMED_POSITION_Z,
|
|
139
|
-
labware_id=labware_id,
|
|
140
|
-
current_location=current_location,
|
|
141
|
-
)
|
|
94
|
+
# During Analysis we will pass in hard coded estimates for certain positions only accessible during execution
|
|
95
|
+
self._state_store.geometry.check_gripper_labware_tip_collision(
|
|
96
|
+
gripper_homed_position_z=_GRIPPER_HOMED_POSITION_Z,
|
|
97
|
+
labware_id=labware_id,
|
|
98
|
+
current_location=current_location,
|
|
99
|
+
)
|
|
142
100
|
return
|
|
143
101
|
|
|
144
102
|
ot3api = ensure_ot3_hardware(
|
|
@@ -161,16 +119,14 @@ class LabwareMovementHandler:
|
|
|
161
119
|
await ot3api.home(axes=[Axis.Z_L, Axis.Z_R, Axis.Z_G])
|
|
162
120
|
gripper_homed_position = await ot3api.gantry_position(mount=gripper_mount)
|
|
163
121
|
|
|
164
|
-
#
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
labware_id=labware_id,
|
|
171
|
-
current_location=current_location,
|
|
172
|
-
)
|
|
122
|
+
# Verify that no tip collisions will occur during the move
|
|
123
|
+
self._state_store.geometry.check_gripper_labware_tip_collision(
|
|
124
|
+
gripper_homed_position_z=gripper_homed_position.z,
|
|
125
|
+
labware_id=labware_id,
|
|
126
|
+
current_location=current_location,
|
|
127
|
+
)
|
|
173
128
|
|
|
129
|
+
current_labware = self._state_store.labware.get_definition(labware_id)
|
|
174
130
|
async with self._thermocycler_plate_lifter.lift_plate_for_labware_movement(
|
|
175
131
|
labware_location=current_location
|
|
176
132
|
):
|
|
@@ -179,14 +135,14 @@ class LabwareMovementHandler:
|
|
|
179
135
|
from_location=current_location,
|
|
180
136
|
to_location=new_location,
|
|
181
137
|
additional_offset_vector=user_offset_data,
|
|
182
|
-
current_labware=
|
|
138
|
+
current_labware=current_labware,
|
|
183
139
|
)
|
|
184
140
|
)
|
|
185
141
|
from_labware_center = self._state_store.geometry.get_labware_grip_point(
|
|
186
|
-
|
|
142
|
+
labware_id=labware_id, location=current_location
|
|
187
143
|
)
|
|
188
144
|
to_labware_center = self._state_store.geometry.get_labware_grip_point(
|
|
189
|
-
|
|
145
|
+
labware_id=labware_id, location=new_location
|
|
190
146
|
)
|
|
191
147
|
movement_waypoints = get_gripper_labware_movement_waypoints(
|
|
192
148
|
from_labware_center=from_labware_center,
|
|
@@ -195,9 +151,7 @@ class LabwareMovementHandler:
|
|
|
195
151
|
offset_data=final_offsets,
|
|
196
152
|
post_drop_slide_offset=post_drop_slide_offset,
|
|
197
153
|
)
|
|
198
|
-
labware_grip_force = self._state_store.labware.get_grip_force(
|
|
199
|
-
labware_definition
|
|
200
|
-
)
|
|
154
|
+
labware_grip_force = self._state_store.labware.get_grip_force(labware_id)
|
|
201
155
|
holding_labware = False
|
|
202
156
|
for waypoint_data in movement_waypoints:
|
|
203
157
|
if waypoint_data.jaw_open:
|
|
@@ -220,11 +174,9 @@ class LabwareMovementHandler:
|
|
|
220
174
|
# should be holding labware
|
|
221
175
|
if holding_labware:
|
|
222
176
|
labware_bbox = self._state_store.labware.get_dimensions(
|
|
223
|
-
|
|
224
|
-
)
|
|
225
|
-
well_bbox = self._state_store.labware.get_well_bbox(
|
|
226
|
-
labware_definition=labware_definition
|
|
177
|
+
labware_id
|
|
227
178
|
)
|
|
179
|
+
well_bbox = self._state_store.labware.get_well_bbox(labware_id)
|
|
228
180
|
# todo(mm, 2024-09-26): This currently raises a lower-level 2015 FailedGripperPickupError.
|
|
229
181
|
# Convert this to a higher-level 3001 LabwareDroppedError or 3002 LabwareNotPickedUpError,
|
|
230
182
|
# depending on what waypoint we're at, to propagate a more specific error code to users.
|
|
@@ -4,10 +4,9 @@ from __future__ import annotations
|
|
|
4
4
|
import logging
|
|
5
5
|
from typing import Optional, List, Union
|
|
6
6
|
|
|
7
|
-
from opentrons.types import Point, MountType
|
|
7
|
+
from opentrons.types import Point, MountType
|
|
8
8
|
from opentrons.hardware_control import HardwareControlAPI
|
|
9
9
|
from opentrons_shared_data.errors.exceptions import PositionUnknownError
|
|
10
|
-
from opentrons.protocol_engine.errors import LocationIsStagingSlotError
|
|
11
10
|
|
|
12
11
|
from ..types import (
|
|
13
12
|
WellLocation,
|
|
@@ -94,13 +93,9 @@ class MovementHandler:
|
|
|
94
93
|
self._state_store.modules.get_heater_shaker_movement_restrictors()
|
|
95
94
|
)
|
|
96
95
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
"Cannot move to well on labware in Staging Area Slot."
|
|
101
|
-
)
|
|
102
|
-
|
|
103
|
-
dest_slot_int = ancestor.as_int()
|
|
96
|
+
dest_slot_int = self._state_store.geometry.get_ancestor_slot_name(
|
|
97
|
+
labware_id
|
|
98
|
+
).as_int()
|
|
104
99
|
|
|
105
100
|
self._hs_movement_flagger.raise_if_movement_restricted(
|
|
106
101
|
hs_movement_restrictors=hs_movement_restrictors,
|
|
@@ -59,6 +59,7 @@ from .actions import (
|
|
|
59
59
|
HardwareStoppedAction,
|
|
60
60
|
ResetTipsAction,
|
|
61
61
|
SetPipetteMovementSpeedAction,
|
|
62
|
+
AddAbsorbanceReaderLidAction,
|
|
62
63
|
)
|
|
63
64
|
|
|
64
65
|
|
|
@@ -576,6 +577,12 @@ class ProtocolEngine:
|
|
|
576
577
|
AddAddressableAreaAction(addressable_area=area)
|
|
577
578
|
)
|
|
578
579
|
|
|
580
|
+
def add_absorbance_reader_lid(self, module_id: str, lid_id: str) -> None:
|
|
581
|
+
"""Add an absorbance reader lid to the module state."""
|
|
582
|
+
self._action_dispatcher.dispatch(
|
|
583
|
+
AddAbsorbanceReaderLidAction(module_id=module_id, lid_id=lid_id)
|
|
584
|
+
)
|
|
585
|
+
|
|
579
586
|
def reset_tips(self, labware_id: str) -> None:
|
|
580
587
|
"""Reset the tip state of a given labware."""
|
|
581
588
|
# TODO(mm, 2023-03-10): Safely raise an error if the given labware isn't a
|
|
@@ -17,9 +17,11 @@ from ..types import (
|
|
|
17
17
|
DeckSlotLocation,
|
|
18
18
|
DeckType,
|
|
19
19
|
LabwareLocation,
|
|
20
|
+
AddressableAreaLocation,
|
|
20
21
|
DeckConfigurationType,
|
|
21
22
|
)
|
|
22
23
|
from .labware_data_provider import LabwareDataProvider
|
|
24
|
+
from ..resources import deck_configuration_provider
|
|
23
25
|
|
|
24
26
|
|
|
25
27
|
@final
|
|
@@ -69,6 +71,43 @@ class DeckDataProvider:
|
|
|
69
71
|
slot = cast(Optional[str], fixture.get("slot"))
|
|
70
72
|
|
|
71
73
|
if (
|
|
74
|
+
deck_configuration is not None
|
|
75
|
+
and load_name is not None
|
|
76
|
+
and slot is not None
|
|
77
|
+
and slot not in DeckSlotName._value2member_map_
|
|
78
|
+
):
|
|
79
|
+
# The provided slot is likely to be an addressable area for Module-required labware Eg: Plate Reader Lid
|
|
80
|
+
for (
|
|
81
|
+
cutout_id,
|
|
82
|
+
cutout_fixture_id,
|
|
83
|
+
opentrons_module_serial_number,
|
|
84
|
+
) in deck_configuration:
|
|
85
|
+
provided_addressable_areas = (
|
|
86
|
+
deck_configuration_provider.get_provided_addressable_area_names(
|
|
87
|
+
cutout_fixture_id=cutout_fixture_id,
|
|
88
|
+
cutout_id=cutout_id,
|
|
89
|
+
deck_definition=deck_definition,
|
|
90
|
+
)
|
|
91
|
+
)
|
|
92
|
+
if slot in provided_addressable_areas:
|
|
93
|
+
addressable_area_location = AddressableAreaLocation(
|
|
94
|
+
addressableAreaName=slot
|
|
95
|
+
)
|
|
96
|
+
definition = await self._labware_data.get_labware_definition(
|
|
97
|
+
load_name=load_name,
|
|
98
|
+
namespace="opentrons",
|
|
99
|
+
version=1,
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
labware.append(
|
|
103
|
+
DeckFixedLabware(
|
|
104
|
+
labware_id=labware_id,
|
|
105
|
+
definition=definition,
|
|
106
|
+
location=addressable_area_location,
|
|
107
|
+
)
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
elif (
|
|
72
111
|
load_fixed_trash
|
|
73
112
|
and load_name is not None
|
|
74
113
|
and slot is not None
|
|
@@ -66,7 +66,7 @@ class PlateReaderData(BaseModel):
|
|
|
66
66
|
row.append(str(measurement.data[f"{plate_alpharows[i]}{j+1}"]))
|
|
67
67
|
rows.append(row)
|
|
68
68
|
for i in range(3):
|
|
69
|
-
rows.append([])
|
|
69
|
+
rows.append([""])
|
|
70
70
|
rows.append(["", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"])
|
|
71
71
|
for i in range(8):
|
|
72
72
|
row = [plate_alpharows[i]]
|
|
@@ -74,7 +74,7 @@ class PlateReaderData(BaseModel):
|
|
|
74
74
|
row.append("")
|
|
75
75
|
rows.append(row)
|
|
76
76
|
for i in range(3):
|
|
77
|
-
rows.append([])
|
|
77
|
+
rows.append([""])
|
|
78
78
|
rows.append(
|
|
79
79
|
[
|
|
80
80
|
"",
|
|
@@ -86,7 +86,7 @@ class PlateReaderData(BaseModel):
|
|
|
86
86
|
]
|
|
87
87
|
)
|
|
88
88
|
for i in range(3):
|
|
89
|
-
rows.append([])
|
|
89
|
+
rows.append([""])
|
|
90
90
|
rows.append(
|
|
91
91
|
[
|
|
92
92
|
"",
|
|
@@ -100,7 +100,7 @@ class PlateReaderData(BaseModel):
|
|
|
100
100
|
)
|
|
101
101
|
rows.append(["1", "Sample 1", "", "", "", "1", "", "", "", "", "", ""])
|
|
102
102
|
for i in range(3):
|
|
103
|
-
rows.append([])
|
|
103
|
+
rows.append([""])
|
|
104
104
|
|
|
105
105
|
# end of file metadata
|
|
106
106
|
rows.append(["Protocol"])
|
|
@@ -109,17 +109,13 @@ class PlateReaderData(BaseModel):
|
|
|
109
109
|
if self.reference_wavelength is not None:
|
|
110
110
|
rows.append(["Reference Wavelength (nm)", str(self.reference_wavelength)])
|
|
111
111
|
rows.append(["Serial No.", self.serial_number])
|
|
112
|
-
rows.append(
|
|
113
|
-
|
|
114
|
-
)
|
|
115
|
-
rows.append(
|
|
116
|
-
["Measurement finished at", self.finish_time.strftime("%m %d %H:%M:%S %Y")]
|
|
117
|
-
)
|
|
112
|
+
rows.append(["Measurement started at", str(self.start_time)])
|
|
113
|
+
rows.append(["Measurement finished at", str(self.finish_time)])
|
|
118
114
|
|
|
119
115
|
# Ensure the filename adheres to ruleset contains the wavelength for a given measurement
|
|
120
116
|
if filename.endswith(".csv"):
|
|
121
117
|
filename = filename[:-4]
|
|
122
|
-
filename = filename + str(measurement.wavelength) + "
|
|
118
|
+
filename = filename + "_" + str(measurement.wavelength) + ".csv"
|
|
123
119
|
|
|
124
120
|
return GenericCsvTransform.build(
|
|
125
121
|
filename=filename,
|
|
@@ -29,12 +29,7 @@ def is_drop_tip_waste_chute(addressable_area_name: str) -> bool:
|
|
|
29
29
|
|
|
30
30
|
def is_trash(addressable_area_name: str) -> bool:
|
|
31
31
|
"""Check if an addressable area is a trash bin."""
|
|
32
|
-
return
|
|
33
|
-
[
|
|
34
|
-
s in addressable_area_name
|
|
35
|
-
for s in {"movableTrash", "fixedTrash", "shortFixedTrash"}
|
|
36
|
-
]
|
|
37
|
-
)
|
|
32
|
+
return addressable_area_name in {"movableTrash", "fixedTrash", "shortFixedTrash"}
|
|
38
33
|
|
|
39
34
|
|
|
40
35
|
def is_staging_slot(addressable_area_name: str) -> bool:
|