opentrons 8.7.0a1__py3-none-any.whl → 8.7.0a2__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/thermocycler/abstract.py +1 -0
- opentrons/drivers/thermocycler/driver.py +33 -4
- opentrons/drivers/thermocycler/simulator.py +2 -0
- opentrons/hardware_control/api.py +24 -5
- opentrons/hardware_control/backends/controller.py +8 -2
- opentrons/hardware_control/backends/ot3controller.py +3 -0
- opentrons/hardware_control/backends/ot3simulator.py +2 -1
- opentrons/hardware_control/backends/simulator.py +2 -1
- opentrons/hardware_control/backends/subsystem_manager.py +5 -2
- opentrons/hardware_control/module_control.py +82 -8
- opentrons/hardware_control/modules/__init__.py +3 -0
- opentrons/hardware_control/modules/absorbance_reader.py +11 -4
- opentrons/hardware_control/modules/flex_stacker.py +38 -9
- opentrons/hardware_control/modules/heater_shaker.py +30 -5
- opentrons/hardware_control/modules/magdeck.py +8 -4
- opentrons/hardware_control/modules/mod_abc.py +13 -5
- opentrons/hardware_control/modules/tempdeck.py +25 -5
- opentrons/hardware_control/modules/thermocycler.py +56 -10
- opentrons/hardware_control/modules/types.py +20 -1
- opentrons/hardware_control/modules/utils.py +11 -4
- opentrons/hardware_control/nozzle_manager.py +3 -0
- opentrons/hardware_control/ot3api.py +26 -5
- opentrons/hardware_control/scripts/update_module_fw.py +5 -0
- opentrons/hardware_control/types.py +31 -2
- opentrons/legacy_commands/protocol_commands.py +20 -0
- opentrons/legacy_commands/types.py +42 -0
- opentrons/motion_planning/waypoints.py +15 -29
- opentrons/protocol_api/__init__.py +5 -0
- opentrons/protocol_api/_types.py +6 -1
- opentrons/protocol_api/core/common.py +3 -1
- opentrons/protocol_api/core/engine/_default_labware_versions.py +32 -11
- opentrons/protocol_api/core/engine/labware.py +8 -1
- opentrons/protocol_api/core/engine/module_core.py +4 -0
- opentrons/protocol_api/core/engine/protocol.py +18 -1
- opentrons/protocol_api/core/engine/tasks.py +35 -0
- opentrons/protocol_api/core/legacy/legacy_module_core.py +2 -0
- opentrons/protocol_api/core/legacy/legacy_protocol_core.py +11 -1
- opentrons/protocol_api/core/legacy/tasks.py +19 -0
- opentrons/protocol_api/core/legacy_simulator/legacy_protocol_core.py +14 -2
- opentrons/protocol_api/core/legacy_simulator/tasks.py +19 -0
- opentrons/protocol_api/core/module.py +1 -0
- opentrons/protocol_api/core/protocol.py +11 -2
- opentrons/protocol_api/core/tasks.py +31 -0
- opentrons/protocol_api/module_contexts.py +1 -0
- opentrons/protocol_api/protocol_context.py +26 -4
- opentrons/protocol_api/robot_context.py +38 -21
- opentrons/protocol_api/tasks.py +48 -0
- opentrons/protocol_api/validation.py +6 -1
- opentrons/protocol_engine/actions/__init__.py +4 -2
- opentrons/protocol_engine/actions/actions.py +22 -9
- opentrons/protocol_engine/clients/sync_client.py +6 -7
- opentrons/protocol_engine/commands/__init__.py +42 -0
- opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +2 -15
- opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +2 -15
- opentrons/protocol_engine/commands/aspirate.py +1 -0
- opentrons/protocol_engine/commands/command.py +1 -0
- opentrons/protocol_engine/commands/command_unions.py +39 -0
- opentrons/protocol_engine/commands/create_timer.py +83 -0
- opentrons/protocol_engine/commands/dispense.py +1 -0
- opentrons/protocol_engine/commands/drop_tip.py +32 -8
- opentrons/protocol_engine/commands/movement_common.py +2 -0
- opentrons/protocol_engine/commands/pick_up_tip.py +21 -11
- opentrons/protocol_engine/commands/set_tip_state.py +97 -0
- opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py +6 -0
- opentrons/protocol_engine/commands/thermocycler/run_profile.py +8 -0
- opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py +17 -1
- opentrons/protocol_engine/commands/touch_tip.py +1 -1
- opentrons/protocol_engine/commands/unsafe/unsafe_place_labware.py +6 -22
- opentrons/protocol_engine/commands/wait_for_tasks.py +98 -0
- opentrons/protocol_engine/errors/__init__.py +4 -0
- opentrons/protocol_engine/errors/exceptions.py +55 -0
- opentrons/protocol_engine/execution/__init__.py +2 -0
- opentrons/protocol_engine/execution/command_executor.py +8 -0
- opentrons/protocol_engine/execution/create_queue_worker.py +5 -1
- opentrons/protocol_engine/execution/labware_movement.py +9 -12
- opentrons/protocol_engine/execution/movement.py +2 -0
- opentrons/protocol_engine/execution/queue_worker.py +4 -0
- opentrons/protocol_engine/execution/run_control.py +8 -0
- opentrons/protocol_engine/execution/task_handler.py +157 -0
- opentrons/protocol_engine/protocol_engine.py +67 -33
- opentrons/protocol_engine/resources/__init__.py +2 -0
- opentrons/protocol_engine/resources/concurrency_provider.py +27 -0
- opentrons/protocol_engine/resources/deck_configuration_provider.py +7 -0
- opentrons/protocol_engine/resources/labware_validation.py +10 -6
- opentrons/protocol_engine/state/_well_math.py +60 -18
- opentrons/protocol_engine/state/addressable_areas.py +2 -0
- opentrons/protocol_engine/state/commands.py +7 -7
- opentrons/protocol_engine/state/geometry.py +204 -374
- opentrons/protocol_engine/state/labware.py +52 -102
- opentrons/protocol_engine/state/labware_origin_math/errors.py +94 -0
- opentrons/protocol_engine/state/labware_origin_math/stackup_origin_to_labware_origin.py +1331 -0
- opentrons/protocol_engine/state/module_substates/thermocycler_module_substate.py +37 -0
- opentrons/protocol_engine/state/modules.py +21 -8
- opentrons/protocol_engine/state/motion.py +44 -0
- opentrons/protocol_engine/state/state.py +14 -0
- opentrons/protocol_engine/state/state_summary.py +2 -0
- opentrons/protocol_engine/state/tasks.py +139 -0
- opentrons/protocol_engine/state/tips.py +177 -258
- opentrons/protocol_engine/state/update_types.py +16 -9
- opentrons/protocol_engine/types/__init__.py +9 -3
- opentrons/protocol_engine/types/deck_configuration.py +5 -1
- opentrons/protocol_engine/types/instrument.py +8 -1
- opentrons/protocol_engine/types/labware.py +1 -13
- opentrons/protocol_engine/types/module.py +10 -0
- opentrons/protocol_engine/types/tasks.py +38 -0
- opentrons/protocol_engine/types/tip.py +9 -0
- opentrons/protocol_runner/create_simulating_orchestrator.py +29 -2
- opentrons/protocol_runner/run_orchestrator.py +18 -2
- opentrons/protocols/api_support/definitions.py +1 -1
- opentrons/protocols/api_support/types.py +2 -1
- opentrons/simulate.py +48 -15
- opentrons/system/camera.py +1 -1
- {opentrons-8.7.0a1.dist-info → opentrons-8.7.0a2.dist-info}/METADATA +4 -4
- {opentrons-8.7.0a1.dist-info → opentrons-8.7.0a2.dist-info}/RECORD +118 -105
- opentrons/protocol_engine/state/_labware_origin_math.py +0 -636
- {opentrons-8.7.0a1.dist-info → opentrons-8.7.0a2.dist-info}/WHEEL +0 -0
- {opentrons-8.7.0a1.dist-info → opentrons-8.7.0a2.dist-info}/entry_points.txt +0 -0
- {opentrons-8.7.0a1.dist-info → opentrons-8.7.0a2.dist-info}/licenses/LICENSE +0 -0
|
@@ -2,12 +2,26 @@ from asyncio import Queue
|
|
|
2
2
|
import enum
|
|
3
3
|
import logging
|
|
4
4
|
from dataclasses import dataclass
|
|
5
|
-
from typing import
|
|
5
|
+
from typing import (
|
|
6
|
+
cast,
|
|
7
|
+
Tuple,
|
|
8
|
+
Union,
|
|
9
|
+
List,
|
|
10
|
+
Callable,
|
|
11
|
+
Dict,
|
|
12
|
+
TypeVar,
|
|
13
|
+
Type,
|
|
14
|
+
TYPE_CHECKING,
|
|
15
|
+
)
|
|
6
16
|
from typing_extensions import Literal
|
|
7
17
|
from opentrons import types as top_types
|
|
8
18
|
from opentrons_shared_data.pipette.types import PipetteChannelType
|
|
19
|
+
from opentrons_shared_data.errors.exceptions import EnumeratedError
|
|
9
20
|
from opentrons.config import feature_flags
|
|
10
21
|
|
|
22
|
+
if TYPE_CHECKING:
|
|
23
|
+
from .modules.types import ModuleModel
|
|
24
|
+
|
|
11
25
|
MODULE_LOG = logging.getLogger(__name__)
|
|
12
26
|
|
|
13
27
|
|
|
@@ -384,6 +398,7 @@ class HardwareEventType(enum.Enum):
|
|
|
384
398
|
DOOR_SWITCH_CHANGE = enum.auto()
|
|
385
399
|
ERROR_MESSAGE = enum.auto()
|
|
386
400
|
ESTOP_CHANGE = enum.auto()
|
|
401
|
+
ASYNCHRONOUS_MODULE_ERROR = enum.auto()
|
|
387
402
|
|
|
388
403
|
|
|
389
404
|
@dataclass
|
|
@@ -428,10 +443,24 @@ class ErrorMessageNotification:
|
|
|
428
443
|
event: Literal[HardwareEventType.ERROR_MESSAGE] = HardwareEventType.ERROR_MESSAGE
|
|
429
444
|
|
|
430
445
|
|
|
446
|
+
@dataclass(frozen=True)
|
|
447
|
+
class AsynchronousModuleErrorNotification:
|
|
448
|
+
exception: EnumeratedError
|
|
449
|
+
module_serial: str | None
|
|
450
|
+
module_model: "ModuleModel"
|
|
451
|
+
port: str
|
|
452
|
+
event: Literal[
|
|
453
|
+
HardwareEventType.ASYNCHRONOUS_MODULE_ERROR
|
|
454
|
+
] = HardwareEventType.ASYNCHRONOUS_MODULE_ERROR
|
|
455
|
+
|
|
456
|
+
|
|
431
457
|
# new event types get new dataclasses
|
|
432
458
|
# when we add more event types we add them here
|
|
433
459
|
HardwareEvent = Union[
|
|
434
|
-
DoorStateNotification,
|
|
460
|
+
DoorStateNotification,
|
|
461
|
+
ErrorMessageNotification,
|
|
462
|
+
EstopStateNotification,
|
|
463
|
+
AsynchronousModuleErrorNotification,
|
|
435
464
|
]
|
|
436
465
|
|
|
437
466
|
HardwareEventHandler = Callable[[HardwareEvent], None]
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from datetime import timedelta
|
|
2
2
|
from typing import Optional
|
|
3
3
|
from . import types as command_types
|
|
4
|
+
from opentrons.protocol_api.tasks import Task
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
def comment(msg: str) -> command_types.CommentCommand:
|
|
@@ -52,3 +53,22 @@ def move_labware(text: str) -> command_types.MoveLabwareCommand:
|
|
|
52
53
|
"name": command_types.MOVE_LABWARE,
|
|
53
54
|
"payload": {"text": text},
|
|
54
55
|
}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def wait_for_tasks(tasks: list[Task]) -> command_types.WaitForTasksCommand:
|
|
59
|
+
task_ids = [task.created_at.strftime("%Y-%m-%d %H:%M:%S") for task in tasks]
|
|
60
|
+
msg = f"Waiting for tasks that started at: {task_ids}."
|
|
61
|
+
return {
|
|
62
|
+
"name": command_types.WAIT_FOR_TASKS,
|
|
63
|
+
"payload": {"text": msg},
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def create_timer(seconds: float) -> command_types.CreateTimerCommand:
|
|
68
|
+
return {
|
|
69
|
+
"name": command_types.CREATE_TIMER,
|
|
70
|
+
"payload": {
|
|
71
|
+
"text": f"Creating background timer for {seconds} seconds.",
|
|
72
|
+
"time": seconds,
|
|
73
|
+
},
|
|
74
|
+
}
|
|
@@ -102,6 +102,10 @@ ROBOT_MOVE_RELATIVE_TO: Final = "command.ROBOT_MOVE_RELATIVE_TO"
|
|
|
102
102
|
ROBOT_OPEN_GRIPPER_JAW: Final = "command.ROBOT_OPEN_GRIPPER_JAW"
|
|
103
103
|
ROBOT_CLOSE_GRIPPER_JAW: Final = "command.ROBOT_CLOSE_GRIPPER_JAW"
|
|
104
104
|
|
|
105
|
+
# Tasks #
|
|
106
|
+
WAIT_FOR_TASKS: Final = "command.WAIT_FOR_TASKS"
|
|
107
|
+
CREATE_TIMER: Final = "command.CREATE_TIMER"
|
|
108
|
+
|
|
105
109
|
|
|
106
110
|
class TextOnlyPayload(TypedDict):
|
|
107
111
|
text: str
|
|
@@ -714,6 +718,27 @@ class RobotCloseGripperJawCommand(TypedDict):
|
|
|
714
718
|
payload: GripperCommandPayload
|
|
715
719
|
|
|
716
720
|
|
|
721
|
+
# Task Commands and Payloads
|
|
722
|
+
|
|
723
|
+
|
|
724
|
+
class WaitForTasksPayload(TextOnlyPayload):
|
|
725
|
+
pass
|
|
726
|
+
|
|
727
|
+
|
|
728
|
+
class CreateTimerPayload(TextOnlyPayload):
|
|
729
|
+
time: float
|
|
730
|
+
|
|
731
|
+
|
|
732
|
+
class WaitForTasksCommand(TypedDict):
|
|
733
|
+
name: Literal["command.WAIT_FOR_TASKS"]
|
|
734
|
+
payload: WaitForTasksPayload
|
|
735
|
+
|
|
736
|
+
|
|
737
|
+
class CreateTimerCommand(TypedDict):
|
|
738
|
+
name: Literal["command.CREATE_TIMER"]
|
|
739
|
+
payload: CreateTimerPayload
|
|
740
|
+
|
|
741
|
+
|
|
717
742
|
Command = Union[
|
|
718
743
|
DropTipCommand,
|
|
719
744
|
DropTipInDisposalLocationCommand,
|
|
@@ -782,6 +807,9 @@ Command = Union[
|
|
|
782
807
|
FlexStackerStoreCommand,
|
|
783
808
|
FlexStackerEmptyCommand,
|
|
784
809
|
FlexStackerFillCommand,
|
|
810
|
+
# Task commands
|
|
811
|
+
WaitForTasksCommand,
|
|
812
|
+
CreateTimerCommand,
|
|
785
813
|
]
|
|
786
814
|
|
|
787
815
|
|
|
@@ -842,6 +870,9 @@ CommandPayload = Union[
|
|
|
842
870
|
RobotMoveAxisRelativeCommandPayload,
|
|
843
871
|
RobotMoveAxisToCommandPayload,
|
|
844
872
|
GripperCommandPayload,
|
|
873
|
+
# Task payloads
|
|
874
|
+
WaitForTasksPayload,
|
|
875
|
+
CreateTimerPayload,
|
|
845
876
|
]
|
|
846
877
|
|
|
847
878
|
|
|
@@ -1124,6 +1155,14 @@ class RobotCloseGripperJawMessage(CommandMessageFields, RobotCloseGripperJawComm
|
|
|
1124
1155
|
pass
|
|
1125
1156
|
|
|
1126
1157
|
|
|
1158
|
+
class WaitForTasksMessage(CommandMessageFields, WaitForTasksCommand):
|
|
1159
|
+
pass
|
|
1160
|
+
|
|
1161
|
+
|
|
1162
|
+
class CreateTimerMessage(CommandMessageFields, CreateTimerCommand):
|
|
1163
|
+
pass
|
|
1164
|
+
|
|
1165
|
+
|
|
1127
1166
|
CommandMessage = Union[
|
|
1128
1167
|
DropTipMessage,
|
|
1129
1168
|
DropTipInDisposalLocationMessage,
|
|
@@ -1183,4 +1222,7 @@ CommandMessage = Union[
|
|
|
1183
1222
|
FlexStackerStoreMessage,
|
|
1184
1223
|
FlexStackerEmptyMessage,
|
|
1185
1224
|
FlexStackerFillMessage,
|
|
1225
|
+
# Task Messages
|
|
1226
|
+
WaitForTasksMessage,
|
|
1227
|
+
CreateTimerMessage,
|
|
1186
1228
|
]
|
|
@@ -7,7 +7,6 @@ from opentrons.hardware_control.types import CriticalPoint
|
|
|
7
7
|
|
|
8
8
|
from .types import Waypoint, MoveType, GripperMovementWaypointsWithJawStatus
|
|
9
9
|
from .errors import DestinationOutOfBoundsError, ArcOutOfBoundsError
|
|
10
|
-
from ..protocol_engine.types import LabwareMovementOffsetData
|
|
11
10
|
|
|
12
11
|
DEFAULT_GENERAL_ARC_Z_MARGIN: Final[float] = 10.0
|
|
13
12
|
DEFAULT_IN_LABWARE_ARC_Z_MARGIN: Final[float] = 5.0
|
|
@@ -125,47 +124,41 @@ def get_gripper_labware_movement_waypoints(
|
|
|
125
124
|
from_labware_center: Point,
|
|
126
125
|
to_labware_center: Point,
|
|
127
126
|
gripper_home_z: float,
|
|
128
|
-
offset_data: LabwareMovementOffsetData,
|
|
129
127
|
post_drop_slide_offset: Optional[Point],
|
|
130
128
|
gripper_home_z_offset: Optional[float] = None,
|
|
131
129
|
) -> List[GripperMovementWaypointsWithJawStatus]:
|
|
132
130
|
"""Get waypoints for moving labware using a gripper."""
|
|
133
|
-
pick_up_offset = offset_data.pickUpOffset
|
|
134
|
-
drop_offset = offset_data.dropOffset
|
|
135
|
-
|
|
136
|
-
pick_up_location = from_labware_center + Point(
|
|
137
|
-
pick_up_offset.x, pick_up_offset.y, pick_up_offset.z
|
|
138
|
-
)
|
|
139
|
-
drop_location = to_labware_center + Point(
|
|
140
|
-
drop_offset.x, drop_offset.y, drop_offset.z
|
|
141
|
-
)
|
|
142
|
-
|
|
143
131
|
gripper_max_z_home = gripper_home_z - (gripper_home_z_offset or 0)
|
|
144
|
-
|
|
145
|
-
post_drop_home_pos = Point(drop_location.x, drop_location.y, gripper_home_z)
|
|
132
|
+
post_drop_home_pos = Point(to_labware_center.x, to_labware_center.y, gripper_home_z)
|
|
146
133
|
|
|
147
134
|
waypoints_with_jaw_status = [
|
|
148
135
|
GripperMovementWaypointsWithJawStatus(
|
|
149
|
-
position=Point(
|
|
136
|
+
position=Point(
|
|
137
|
+
from_labware_center.x, from_labware_center.y, gripper_home_z
|
|
138
|
+
),
|
|
150
139
|
jaw_open=False,
|
|
151
140
|
dropping=False,
|
|
152
141
|
),
|
|
153
142
|
GripperMovementWaypointsWithJawStatus(
|
|
154
|
-
position=
|
|
143
|
+
position=from_labware_center, jaw_open=True, dropping=False
|
|
155
144
|
),
|
|
156
145
|
# Gripper grips the labware here
|
|
157
146
|
GripperMovementWaypointsWithJawStatus(
|
|
158
|
-
position=Point(
|
|
147
|
+
position=Point(
|
|
148
|
+
from_labware_center.x, from_labware_center.y, gripper_max_z_home
|
|
149
|
+
),
|
|
159
150
|
jaw_open=False,
|
|
160
151
|
dropping=False,
|
|
161
152
|
),
|
|
162
153
|
GripperMovementWaypointsWithJawStatus(
|
|
163
|
-
position=Point(
|
|
154
|
+
position=Point(
|
|
155
|
+
to_labware_center.x, to_labware_center.y, gripper_max_z_home
|
|
156
|
+
),
|
|
164
157
|
jaw_open=False,
|
|
165
158
|
dropping=False,
|
|
166
159
|
),
|
|
167
160
|
GripperMovementWaypointsWithJawStatus(
|
|
168
|
-
position=
|
|
161
|
+
position=to_labware_center, jaw_open=False, dropping=False
|
|
169
162
|
),
|
|
170
163
|
# Gripper ungrips here
|
|
171
164
|
GripperMovementWaypointsWithJawStatus(
|
|
@@ -189,25 +182,18 @@ def get_gripper_labware_movement_waypoints(
|
|
|
189
182
|
def get_gripper_labware_placement_waypoints(
|
|
190
183
|
to_labware_center: Point,
|
|
191
184
|
gripper_home_z: float,
|
|
192
|
-
drop_offset: Optional[Point],
|
|
193
185
|
) -> List[GripperMovementWaypointsWithJawStatus]:
|
|
194
186
|
"""Get waypoints for placing labware using a gripper."""
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
drop_location = to_labware_center + Point(
|
|
198
|
-
drop_offset.x, drop_offset.y, drop_offset.z
|
|
199
|
-
)
|
|
200
|
-
|
|
201
|
-
post_drop_home_pos = Point(drop_location.x, drop_location.y, gripper_home_z)
|
|
187
|
+
post_drop_home_pos = Point(to_labware_center.x, to_labware_center.y, gripper_home_z)
|
|
202
188
|
|
|
203
189
|
return [
|
|
204
190
|
GripperMovementWaypointsWithJawStatus(
|
|
205
|
-
position=Point(
|
|
191
|
+
position=Point(to_labware_center.x, to_labware_center.y, gripper_home_z),
|
|
206
192
|
jaw_open=False,
|
|
207
193
|
dropping=False,
|
|
208
194
|
),
|
|
209
195
|
GripperMovementWaypointsWithJawStatus(
|
|
210
|
-
position=
|
|
196
|
+
position=to_labware_center, jaw_open=False, dropping=False
|
|
211
197
|
),
|
|
212
198
|
# Gripper ungrips here
|
|
213
199
|
GripperMovementWaypointsWithJawStatus(
|
|
@@ -30,9 +30,11 @@ from .module_contexts import (
|
|
|
30
30
|
AbsorbanceReaderContext,
|
|
31
31
|
FlexStackerContext,
|
|
32
32
|
)
|
|
33
|
+
from .tasks import Task
|
|
33
34
|
from .disposal_locations import TrashBin, WasteChute
|
|
34
35
|
from ._liquid import Liquid, LiquidClass
|
|
35
36
|
from ._types import (
|
|
37
|
+
OffDeckType,
|
|
36
38
|
OFF_DECK,
|
|
37
39
|
PLUNGER_BLOWOUT,
|
|
38
40
|
PLUNGER_TOP,
|
|
@@ -88,6 +90,7 @@ __all__ = [
|
|
|
88
90
|
"ROW",
|
|
89
91
|
"ALL",
|
|
90
92
|
# Deck location types
|
|
93
|
+
"OffDeckType",
|
|
91
94
|
"OFF_DECK",
|
|
92
95
|
# Pipette plunger types
|
|
93
96
|
"PLUNGER_BLOWOUT",
|
|
@@ -99,6 +102,8 @@ __all__ = [
|
|
|
99
102
|
"BLOWOUT_ACTION",
|
|
100
103
|
"RuntimeParameterRequiredError",
|
|
101
104
|
"CSVParameter",
|
|
105
|
+
# Concurrent task types
|
|
106
|
+
"Task",
|
|
102
107
|
# For internal Opentrons use only:
|
|
103
108
|
"create_protocol_context",
|
|
104
109
|
"ProtocolEngineCoreRequiredError",
|
opentrons/protocol_api/_types.py
CHANGED
|
@@ -3,8 +3,13 @@ from typing_extensions import Final
|
|
|
3
3
|
import enum
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# Implemented with an enum to support type narrowing via `== OFF_DECK`.
|
|
7
7
|
class OffDeckType(enum.Enum):
|
|
8
|
+
"""The type of the :py:obj:`OFF_DECK` constant.
|
|
9
|
+
|
|
10
|
+
Do not use directly, except in type annotations and ``isinstance`` calls.
|
|
11
|
+
"""
|
|
12
|
+
|
|
8
13
|
OFF_DECK = "off-deck"
|
|
9
14
|
|
|
10
15
|
|
|
@@ -16,6 +16,7 @@ from .module import (
|
|
|
16
16
|
from .protocol import AbstractProtocol
|
|
17
17
|
from .well import AbstractWellCore
|
|
18
18
|
from .robot import AbstractRobot
|
|
19
|
+
from .tasks import AbstractTaskCore
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
WellCore = AbstractWellCore
|
|
@@ -30,4 +31,5 @@ MagneticBlockCore = AbstractMagneticBlockCore[LabwareCore]
|
|
|
30
31
|
AbsorbanceReaderCore = AbstractAbsorbanceReaderCore[LabwareCore]
|
|
31
32
|
FlexStackerCore = AbstractFlexStackerCore[LabwareCore]
|
|
32
33
|
RobotCore = AbstractRobot
|
|
33
|
-
|
|
34
|
+
TaskCore = AbstractTaskCore
|
|
35
|
+
ProtocolCore = AbstractProtocol[InstrumentCore, LabwareCore, ModuleCore, TaskCore]
|
|
@@ -112,6 +112,37 @@ DEFAULT_LABWARE_VERSIONS: DefaultLabwareVersions = {
|
|
|
112
112
|
"thermoscientificnunc_96_wellplate_2000ul": 3,
|
|
113
113
|
"usascientific_96_wellplate_2.4ml_deep": 3,
|
|
114
114
|
},
|
|
115
|
+
APIVersion(2, 27): {
|
|
116
|
+
"agilent_1_reservoir_290ml": 4,
|
|
117
|
+
"axygen_1_reservoir_90ml": 3,
|
|
118
|
+
"biorad_96_wellplate_200ul_pcr": 5,
|
|
119
|
+
"corning_12_wellplate_6.9ml_flat": 5,
|
|
120
|
+
"corning_24_wellplate_3.4ml_flat": 5,
|
|
121
|
+
"corning_384_wellplate_112ul_flat": 5,
|
|
122
|
+
"corning_48_wellplate_1.6ml_flat": 6,
|
|
123
|
+
"corning_6_wellplate_16.8ml_flat": 5,
|
|
124
|
+
"corning_96_wellplate_360ul_flat": 5,
|
|
125
|
+
"nest_12_reservoir_15ml": 3,
|
|
126
|
+
"nest_1_reservoir_195ml": 4,
|
|
127
|
+
"nest_1_reservoir_290ml": 4,
|
|
128
|
+
"nest_96_wellplate_100ul_pcr_full_skirt": 5,
|
|
129
|
+
"nest_96_wellplate_200ul_flat": 5,
|
|
130
|
+
"nest_96_wellplate_2ml_deep": 5,
|
|
131
|
+
"opentrons_10_tuberack_falcon_4x50ml_6x15ml_conical": 3,
|
|
132
|
+
"opentrons_15_tuberack_falcon_15ml_conical": 3,
|
|
133
|
+
"opentrons_24_aluminumblock_nest_0.5ml_screwcap": 4,
|
|
134
|
+
"opentrons_24_aluminumblock_nest_1.5ml_screwcap": 3,
|
|
135
|
+
"opentrons_24_aluminumblock_nest_1.5ml_snapcap": 3,
|
|
136
|
+
"opentrons_24_aluminumblock_nest_2ml_screwcap": 3,
|
|
137
|
+
"opentrons_24_aluminumblock_nest_2ml_snapcap": 3,
|
|
138
|
+
"opentrons_24_tuberack_eppendorf_1.5ml_safelock_snapcap": 3,
|
|
139
|
+
"opentrons_24_tuberack_eppendorf_2ml_safelock_snapcap": 3,
|
|
140
|
+
"opentrons_24_tuberack_nest_0.5ml_screwcap": 4,
|
|
141
|
+
"opentrons_6_tuberack_nest_50ml_conical": 3,
|
|
142
|
+
"opentrons_96_aluminumblock_generic_pcr_strip_200ul": 4,
|
|
143
|
+
"usascientific_12_reservoir_22ml": 4,
|
|
144
|
+
"usascientific_96_wellplate_2.4ml_deep": 4,
|
|
145
|
+
},
|
|
115
146
|
}
|
|
116
147
|
|
|
117
148
|
|
|
@@ -139,17 +170,7 @@ KNOWN_EXCEPTIONS_FOR_TESTS: set[str] = {
|
|
|
139
170
|
"schema3test_flex_tiprack_lid",
|
|
140
171
|
"schema3test_tough_pcr_auto_sealing_lid",
|
|
141
172
|
"schema3test_universal_flat_adapter",
|
|
142
|
-
|
|
143
|
-
# https://github.com/Opentrons/opentrons/pull/18266 + https://github.com/Opentrons/opentrons/pull/18284,
|
|
144
|
-
# but the second punch took a while. We should merge the second punch after v8.6.0
|
|
145
|
-
# and remove these exceptions as part of that.
|
|
146
|
-
"agilent_1_reservoir_290ml",
|
|
147
|
-
"corning_384_wellplate_112ul_flat",
|
|
148
|
-
"nest_1_reservoir_290ml",
|
|
149
|
-
"opentrons_24_aluminumblock_nest_0.5ml_screwcap",
|
|
150
|
-
"opentrons_24_tuberack_nest_0.5ml_screwcap",
|
|
151
|
-
"opentrons_96_aluminumblock_generic_pcr_strip_200ul",
|
|
152
|
-
"usascientific_12_reservoir_22ml",
|
|
173
|
+
"schema3test_96_wellplate_360ul_flat",
|
|
153
174
|
}
|
|
154
175
|
|
|
155
176
|
|
|
@@ -23,6 +23,7 @@ from opentrons.protocol_engine.clients import SyncClient as ProtocolEngineClient
|
|
|
23
23
|
from opentrons.protocol_engine.types import (
|
|
24
24
|
LabwareOffsetCreate,
|
|
25
25
|
LabwareOffsetVector,
|
|
26
|
+
TipRackWellState,
|
|
26
27
|
)
|
|
27
28
|
from opentrons.types import DeckSlotName, NozzleMapInterface, Point, StagingSlotName
|
|
28
29
|
|
|
@@ -165,7 +166,13 @@ class LabwareCore(AbstractLabware[WellCore]):
|
|
|
165
166
|
|
|
166
167
|
def reset_tips(self) -> None:
|
|
167
168
|
if self.is_tip_rack():
|
|
168
|
-
self._engine_client.
|
|
169
|
+
self._engine_client.execute_command(
|
|
170
|
+
cmd.SetTipStateParams(
|
|
171
|
+
labwareId=self._labware_id,
|
|
172
|
+
wellNames=list(self._definition.wells),
|
|
173
|
+
tipWellState=TipRackWellState.CLEAN,
|
|
174
|
+
)
|
|
175
|
+
)
|
|
169
176
|
else:
|
|
170
177
|
raise TypeError(f"{self.get_display_name()} is not a tip rack.")
|
|
171
178
|
|
|
@@ -317,6 +317,7 @@ class ThermocyclerModuleCore(ModuleCore, AbstractThermocyclerCore[LabwareCore]):
|
|
|
317
317
|
def set_target_block_temperature(
|
|
318
318
|
self,
|
|
319
319
|
celsius: float,
|
|
320
|
+
ramp_rate: Optional[float],
|
|
320
321
|
hold_time_seconds: Optional[float] = None,
|
|
321
322
|
block_max_volume: Optional[float] = None,
|
|
322
323
|
) -> None:
|
|
@@ -327,6 +328,7 @@ class ThermocyclerModuleCore(ModuleCore, AbstractThermocyclerCore[LabwareCore]):
|
|
|
327
328
|
celsius=celsius,
|
|
328
329
|
blockMaxVolumeUl=block_max_volume,
|
|
329
330
|
holdTimeSeconds=hold_time_seconds,
|
|
331
|
+
ramp_rate=ramp_rate,
|
|
330
332
|
)
|
|
331
333
|
)
|
|
332
334
|
|
|
@@ -361,6 +363,7 @@ class ThermocyclerModuleCore(ModuleCore, AbstractThermocyclerCore[LabwareCore]):
|
|
|
361
363
|
cmd.thermocycler.RunProfileStepParams(
|
|
362
364
|
celsius=step["temperature"],
|
|
363
365
|
holdSeconds=step["hold_time_seconds"],
|
|
366
|
+
rampRate=step["ramp_rate"],
|
|
364
367
|
)
|
|
365
368
|
for step in steps
|
|
366
369
|
]
|
|
@@ -389,6 +392,7 @@ class ThermocyclerModuleCore(ModuleCore, AbstractThermocyclerCore[LabwareCore]):
|
|
|
389
392
|
cmd.thermocycler.ProfileStep(
|
|
390
393
|
celsius=step["temperature"],
|
|
391
394
|
holdSeconds=step["hold_time_seconds"],
|
|
395
|
+
rampRate=step["ramp_rate"],
|
|
392
396
|
)
|
|
393
397
|
for step in steps
|
|
394
398
|
],
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""ProtocolEngine-based Protocol API core implementation."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
|
-
from typing import Dict, Optional, Type, Union, List, Tuple, TYPE_CHECKING
|
|
4
|
+
from typing import Dict, Optional, Type, Union, List, Tuple, TYPE_CHECKING, Sequence
|
|
5
5
|
|
|
6
6
|
from opentrons_shared_data.liquid_classes import LiquidClassDefinitionDoesNotExist
|
|
7
7
|
from opentrons_shared_data.deck.types import DeckDefinitionV5, SlotDefV3
|
|
@@ -64,6 +64,7 @@ from ...disposal_locations import TrashBin, WasteChute
|
|
|
64
64
|
from ..protocol import AbstractProtocol
|
|
65
65
|
from ..labware import LabwareLoadParams
|
|
66
66
|
from .labware import LabwareCore
|
|
67
|
+
from .tasks import EngineTaskCore
|
|
67
68
|
from .instrument import InstrumentCore
|
|
68
69
|
from .robot import RobotCore
|
|
69
70
|
from .module_core import (
|
|
@@ -95,6 +96,7 @@ class ProtocolCore(
|
|
|
95
96
|
InstrumentCore,
|
|
96
97
|
LabwareCore,
|
|
97
98
|
Union[ModuleCore, NonConnectedModuleCore],
|
|
99
|
+
EngineTaskCore,
|
|
98
100
|
]
|
|
99
101
|
):
|
|
100
102
|
"""Protocol API core using a ProtocolEngine.
|
|
@@ -875,6 +877,21 @@ class ProtocolCore(
|
|
|
875
877
|
cmd.WaitForDurationParams(seconds=seconds, message=msg)
|
|
876
878
|
)
|
|
877
879
|
|
|
880
|
+
def wait_for_tasks(self, task_cores: Sequence[EngineTaskCore]) -> None:
|
|
881
|
+
"""Wait for specified tasks to complete."""
|
|
882
|
+
task_ids = [task._id for task in task_cores]
|
|
883
|
+
self._engine_client.execute_command(cmd.WaitForTasksParams(task_ids=task_ids))
|
|
884
|
+
|
|
885
|
+
def create_timer(self, seconds: float) -> EngineTaskCore:
|
|
886
|
+
"""Create a timer task that runs in the background."""
|
|
887
|
+
result = self._engine_client.execute_command_without_recovery(
|
|
888
|
+
cmd.CreateTimerParams(time=seconds)
|
|
889
|
+
)
|
|
890
|
+
timer_task = EngineTaskCore(
|
|
891
|
+
engine_client=self._engine_client, task_id=result.task_id
|
|
892
|
+
)
|
|
893
|
+
return timer_task
|
|
894
|
+
|
|
878
895
|
def home(self) -> None:
|
|
879
896
|
"""Move all axes to their home positions."""
|
|
880
897
|
self._engine_client.execute_command(cmd.HomeParams(axes=None))
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from ..tasks import AbstractTaskCore
|
|
3
|
+
from opentrons.protocol_engine.clients import SyncClient as ProtocolEngineClient
|
|
4
|
+
from opentrons.protocol_engine.errors.exceptions import NoTaskFoundError
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class EngineTaskCore(AbstractTaskCore):
|
|
8
|
+
def __init__(self, task_id: str, engine_client: ProtocolEngineClient) -> None:
|
|
9
|
+
self._id = task_id
|
|
10
|
+
self._engine_client = engine_client
|
|
11
|
+
|
|
12
|
+
def get_created_at_timestamp(self) -> datetime:
|
|
13
|
+
task = self._engine_client.state.tasks.get(self._id)
|
|
14
|
+
return task.createdAt
|
|
15
|
+
|
|
16
|
+
def is_done(self) -> bool:
|
|
17
|
+
try:
|
|
18
|
+
self._engine_client.state.tasks.get_finished(self._id)
|
|
19
|
+
return True
|
|
20
|
+
except NoTaskFoundError:
|
|
21
|
+
return False
|
|
22
|
+
|
|
23
|
+
def is_started(self) -> bool:
|
|
24
|
+
try:
|
|
25
|
+
self._engine_client.state.tasks.get_current(self._id)
|
|
26
|
+
return True
|
|
27
|
+
except NoTaskFoundError:
|
|
28
|
+
return self.is_done()
|
|
29
|
+
|
|
30
|
+
def get_finished_at_timestamp(self) -> datetime | None:
|
|
31
|
+
try:
|
|
32
|
+
finished_task = self._engine_client.state.tasks.get_finished(self._id)
|
|
33
|
+
return finished_task.finishedAt
|
|
34
|
+
except NoTaskFoundError:
|
|
35
|
+
return None
|
|
@@ -263,6 +263,7 @@ class LegacyThermocyclerCore(
|
|
|
263
263
|
def set_target_block_temperature(
|
|
264
264
|
self,
|
|
265
265
|
celsius: float,
|
|
266
|
+
ramp_rate: Optional[float],
|
|
266
267
|
hold_time_seconds: Optional[float] = None,
|
|
267
268
|
block_max_volume: Optional[float] = None,
|
|
268
269
|
) -> None:
|
|
@@ -271,6 +272,7 @@ class LegacyThermocyclerCore(
|
|
|
271
272
|
celsius=celsius,
|
|
272
273
|
hold_time_seconds=hold_time_seconds,
|
|
273
274
|
volume=block_max_volume,
|
|
275
|
+
ramp_rate=ramp_rate,
|
|
274
276
|
)
|
|
275
277
|
|
|
276
278
|
def wait_for_block_temperature(self) -> None:
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import logging
|
|
2
|
-
from typing import Dict, List, Optional, Set, Union, cast, Tuple
|
|
2
|
+
from typing import Dict, List, Optional, Set, Union, cast, Tuple, Sequence
|
|
3
3
|
|
|
4
4
|
from opentrons_shared_data.deck.types import DeckDefinitionV5, SlotDefV3
|
|
5
5
|
from opentrons_shared_data.labware.types import LabwareDefinition
|
|
@@ -34,6 +34,7 @@ from .legacy_instrument_core import LegacyInstrumentCore
|
|
|
34
34
|
from .labware_offset_provider import AbstractLabwareOffsetProvider
|
|
35
35
|
from .legacy_labware_core import LegacyLabwareCore
|
|
36
36
|
from .load_info import LoadInfo, InstrumentLoadInfo, LabwareLoadInfo, ModuleLoadInfo
|
|
37
|
+
from .tasks import LegacyTaskCore
|
|
37
38
|
|
|
38
39
|
logger = logging.getLogger(__name__)
|
|
39
40
|
|
|
@@ -43,6 +44,7 @@ class LegacyProtocolCore(
|
|
|
43
44
|
LegacyInstrumentCore,
|
|
44
45
|
LegacyLabwareCore,
|
|
45
46
|
legacy_module_core.LegacyModuleCore,
|
|
47
|
+
LegacyTaskCore,
|
|
46
48
|
]
|
|
47
49
|
):
|
|
48
50
|
def __init__(
|
|
@@ -610,3 +612,11 @@ class LegacyProtocolCore(
|
|
|
610
612
|
]:
|
|
611
613
|
"""Get labware parent location."""
|
|
612
614
|
assert False, "get_labware_location only supported on engine core"
|
|
615
|
+
|
|
616
|
+
def wait_for_tasks(self, task: Sequence[LegacyTaskCore]) -> None:
|
|
617
|
+
"""Wait for list of tasks to complete before executing subsequent commands."""
|
|
618
|
+
assert False, "wait_for_tasks only supported on engine core"
|
|
619
|
+
|
|
620
|
+
def create_timer(self, seconds: float) -> LegacyTaskCore:
|
|
621
|
+
"""Create a timer task that runs in the background."""
|
|
622
|
+
assert False, "create_timer only supported on engine core"
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from ..tasks import AbstractTaskCore
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class LegacyTaskCore(AbstractTaskCore):
|
|
6
|
+
def __init__(self, created_at: datetime) -> None:
|
|
7
|
+
raise NotImplementedError("Legacy protocols do not implement tasks.")
|
|
8
|
+
|
|
9
|
+
def get_created_at_timestamp(self) -> datetime:
|
|
10
|
+
raise NotImplementedError("Legacy protocols do not implement tasks.")
|
|
11
|
+
|
|
12
|
+
def is_done(self) -> bool:
|
|
13
|
+
raise NotImplementedError("Legacy protocols do not implement tasks.")
|
|
14
|
+
|
|
15
|
+
def is_started(self) -> bool:
|
|
16
|
+
raise NotImplementedError("Legacy protocols do not implement tasks.")
|
|
17
|
+
|
|
18
|
+
def get_finished_at_timestamp(self) -> datetime | None:
|
|
19
|
+
raise NotImplementedError("Legacy protocols do not implement tasks.")
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import logging
|
|
2
|
-
from typing import Dict, Optional
|
|
2
|
+
from typing import Dict, Optional, Sequence
|
|
3
3
|
|
|
4
4
|
from opentrons_shared_data.pipette.types import PipetteNameType
|
|
5
5
|
from opentrons_shared_data.pipette.pipette_load_name_conversions import (
|
|
@@ -16,6 +16,7 @@ from ..legacy.legacy_module_core import LegacyModuleCore
|
|
|
16
16
|
from ..legacy.load_info import InstrumentLoadInfo
|
|
17
17
|
|
|
18
18
|
from .legacy_instrument_core import LegacyInstrumentCoreSimulator
|
|
19
|
+
from .tasks import LegacyTaskCore
|
|
19
20
|
|
|
20
21
|
logger = logging.getLogger(__name__)
|
|
21
22
|
|
|
@@ -23,7 +24,10 @@ logger = logging.getLogger(__name__)
|
|
|
23
24
|
class LegacyProtocolCoreSimulator(
|
|
24
25
|
LegacyProtocolCore,
|
|
25
26
|
AbstractProtocol[
|
|
26
|
-
LegacyInstrumentCoreSimulator,
|
|
27
|
+
LegacyInstrumentCoreSimulator,
|
|
28
|
+
LegacyLabwareCore,
|
|
29
|
+
LegacyModuleCore,
|
|
30
|
+
LegacyTaskCore,
|
|
27
31
|
],
|
|
28
32
|
):
|
|
29
33
|
_instruments: Dict[Mount, Optional[LegacyInstrumentCoreSimulator]] # type: ignore[assignment]
|
|
@@ -83,3 +87,11 @@ class LegacyProtocolCoreSimulator(
|
|
|
83
87
|
)
|
|
84
88
|
|
|
85
89
|
return new_instr
|
|
90
|
+
|
|
91
|
+
def wait_for_tasks(self, task: Sequence[LegacyTaskCore]) -> None:
|
|
92
|
+
"""Wait for list of tasks to complete before executing subsequent commands."""
|
|
93
|
+
assert False, "wait_for_tasks only supported on engine core"
|
|
94
|
+
|
|
95
|
+
def create_timer(self, seconds: float) -> LegacyTaskCore:
|
|
96
|
+
"""Create a timer task that runs in the background."""
|
|
97
|
+
assert False, "create_timer only supported on engine core"
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from ..tasks import AbstractTaskCore
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class LegacyTaskCore(AbstractTaskCore):
|
|
6
|
+
def __init__(self, created_at: datetime) -> None:
|
|
7
|
+
raise NotImplementedError("Legacy protocols do not implement tasks.")
|
|
8
|
+
|
|
9
|
+
def get_created_at_timestamp(self) -> datetime:
|
|
10
|
+
raise NotImplementedError("Legacy protocols do not implement tasks.")
|
|
11
|
+
|
|
12
|
+
def is_done(self) -> bool:
|
|
13
|
+
raise NotImplementedError("Legacy protocols do not implement tasks.")
|
|
14
|
+
|
|
15
|
+
def is_started(self) -> bool:
|
|
16
|
+
raise NotImplementedError("Legacy protocols do not implement tasks.")
|
|
17
|
+
|
|
18
|
+
def get_finished_at_timestamp(self) -> datetime | None:
|
|
19
|
+
raise NotImplementedError("Legacy protocols do not implement tasks.")
|