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
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
from abc import abstractmethod, ABC
|
|
6
|
-
from typing import Generic, List, Optional, Union, Tuple, Dict, TYPE_CHECKING
|
|
6
|
+
from typing import Generic, List, Optional, Union, Tuple, Dict, TYPE_CHECKING, Sequence
|
|
7
7
|
|
|
8
8
|
from opentrons_shared_data.deck.types import DeckDefinitionV5, SlotDefV3
|
|
9
9
|
from opentrons_shared_data.pipette.types import PipetteNameType
|
|
@@ -24,6 +24,7 @@ from opentrons.protocols.api_support.util import AxisMaxSpeeds
|
|
|
24
24
|
from .instrument import InstrumentCoreType
|
|
25
25
|
from .labware import LabwareCoreType, LabwareLoadParams
|
|
26
26
|
from .module import ModuleCoreType
|
|
27
|
+
from .tasks import TaskCoreType
|
|
27
28
|
from .._liquid import Liquid, LiquidClass
|
|
28
29
|
from .robot import AbstractRobot
|
|
29
30
|
from .._types import OffDeckType
|
|
@@ -34,7 +35,7 @@ if TYPE_CHECKING:
|
|
|
34
35
|
|
|
35
36
|
|
|
36
37
|
class AbstractProtocol(
|
|
37
|
-
ABC, Generic[InstrumentCoreType, LabwareCoreType, ModuleCoreType]
|
|
38
|
+
ABC, Generic[InstrumentCoreType, LabwareCoreType, ModuleCoreType, TaskCoreType]
|
|
38
39
|
):
|
|
39
40
|
@property
|
|
40
41
|
@abstractmethod
|
|
@@ -192,6 +193,14 @@ class AbstractProtocol(
|
|
|
192
193
|
def delay(self, seconds: float, msg: Optional[str]) -> None:
|
|
193
194
|
...
|
|
194
195
|
|
|
196
|
+
@abstractmethod
|
|
197
|
+
def wait_for_tasks(self, task_cores: Sequence[TaskCoreType]) -> None:
|
|
198
|
+
...
|
|
199
|
+
|
|
200
|
+
@abstractmethod
|
|
201
|
+
def create_timer(self, seconds: float) -> TaskCoreType:
|
|
202
|
+
...
|
|
203
|
+
|
|
195
204
|
@abstractmethod
|
|
196
205
|
def home(self) -> None:
|
|
197
206
|
...
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from abc import abstractmethod, ABC
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from typing import TypeVar
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class AbstractTaskCore(ABC):
|
|
7
|
+
@abstractmethod
|
|
8
|
+
def get_created_at_timestamp(self) -> datetime:
|
|
9
|
+
"""Get the createdAt timestamp of the task."""
|
|
10
|
+
...
|
|
11
|
+
|
|
12
|
+
@abstractmethod
|
|
13
|
+
def is_done(self) -> bool:
|
|
14
|
+
"""Returns ``True`` if the task is done."""
|
|
15
|
+
...
|
|
16
|
+
|
|
17
|
+
@abstractmethod
|
|
18
|
+
def is_started(self) -> bool:
|
|
19
|
+
"""Returns ``True`` if the task has started."""
|
|
20
|
+
...
|
|
21
|
+
|
|
22
|
+
@abstractmethod
|
|
23
|
+
def get_finished_at_timestamp(self) -> datetime | None:
|
|
24
|
+
"""The timestamp of the when the task finished.
|
|
25
|
+
|
|
26
|
+
Returns ``None`` if the task hasn't finished yet.
|
|
27
|
+
"""
|
|
28
|
+
...
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
TaskCoreType = TypeVar("TaskCoreType", bound=AbstractTaskCore)
|
|
@@ -89,6 +89,7 @@ from .module_contexts import (
|
|
|
89
89
|
FlexStackerContext,
|
|
90
90
|
ModuleContext,
|
|
91
91
|
)
|
|
92
|
+
from .tasks import Task
|
|
92
93
|
from ._parameters import Parameters
|
|
93
94
|
|
|
94
95
|
|
|
@@ -234,10 +235,7 @@ class ProtocolContext(CommandPublisher):
|
|
|
234
235
|
@property
|
|
235
236
|
@requires_version(2, 22)
|
|
236
237
|
def robot(self) -> RobotContext:
|
|
237
|
-
"""The :py:class:`.RobotContext` for the protocol.
|
|
238
|
-
|
|
239
|
-
:meta private:
|
|
240
|
-
"""
|
|
238
|
+
"""The :py:class:`.RobotContext` for the protocol."""
|
|
241
239
|
if self._core.robot_type != "OT-3 Standard" or not self._robot:
|
|
242
240
|
raise RobotTypeError("The RobotContext is only available on Flex robots.")
|
|
243
241
|
return self._robot
|
|
@@ -1289,6 +1287,30 @@ class ProtocolContext(CommandPublisher):
|
|
|
1289
1287
|
delay_time = seconds + minutes * 60
|
|
1290
1288
|
self._core.delay(seconds=delay_time, msg=msg)
|
|
1291
1289
|
|
|
1290
|
+
@publish(command=cmds.wait_for_tasks)
|
|
1291
|
+
@requires_version(2, 27)
|
|
1292
|
+
def wait_for_tasks(self, tasks: list[Task]) -> None:
|
|
1293
|
+
"""Wait for a list of tasks to complete before executing subsequent commands.
|
|
1294
|
+
|
|
1295
|
+
:param list Task: tasks: A list of Task objects to wait for.
|
|
1296
|
+
|
|
1297
|
+
Task objects can be commands that are allowed to run concurrently.
|
|
1298
|
+
"""
|
|
1299
|
+
task_cores = [task._core for task in tasks]
|
|
1300
|
+
self._core.wait_for_tasks(task_cores)
|
|
1301
|
+
|
|
1302
|
+
@publish(command=cmds.create_timer)
|
|
1303
|
+
@requires_version(2, 27)
|
|
1304
|
+
def create_timer(self, seconds: float) -> Task:
|
|
1305
|
+
"""Create a timer task that runs in the background.
|
|
1306
|
+
|
|
1307
|
+
:param float seconds: The time to delay in seconds.
|
|
1308
|
+
|
|
1309
|
+
This timer will continue to run until it is complete and will not block subsequent commands.
|
|
1310
|
+
"""
|
|
1311
|
+
task_core = self._core.create_timer(seconds=seconds)
|
|
1312
|
+
return Task(core=task_core, api_version=self._api_version)
|
|
1313
|
+
|
|
1292
1314
|
@requires_version(2, 0)
|
|
1293
1315
|
def home(self) -> None:
|
|
1294
1316
|
"""Home the movement system of the robot."""
|
|
@@ -46,7 +46,7 @@ class RobotContext(publisher.CommandPublisher):
|
|
|
46
46
|
Objects in this class should not be instantiated directly. Instead, instances are
|
|
47
47
|
returned by :py:meth:`ProtocolContext.robot`.
|
|
48
48
|
|
|
49
|
-
.. versionadded:: 2.
|
|
49
|
+
.. versionadded:: 2.22
|
|
50
50
|
|
|
51
51
|
"""
|
|
52
52
|
|
|
@@ -83,15 +83,19 @@ class RobotContext(publisher.CommandPublisher):
|
|
|
83
83
|
speed: Optional[float] = None,
|
|
84
84
|
) -> None:
|
|
85
85
|
"""
|
|
86
|
-
Move a specified mount to a
|
|
86
|
+
Move a specified mount to a location on the deck.
|
|
87
87
|
|
|
88
88
|
:param mount: The mount of the instrument you wish to move.
|
|
89
89
|
This can either be an instance of :py:class:`.types.Mount` or one
|
|
90
90
|
of the strings ``"left"``, ``"right"``, ``"extension"``, ``"gripper"``. Note
|
|
91
91
|
that the gripper mount can be referred to either as ``"extension"`` or ``"gripper"``.
|
|
92
92
|
:type mount: types.Mount or str
|
|
93
|
-
:param
|
|
94
|
-
|
|
93
|
+
:param destination: Any location on the deck, specified as:
|
|
94
|
+
|
|
95
|
+
- a slot, like ``"A1"``
|
|
96
|
+
- a defined location, like labware in a deck slot
|
|
97
|
+
- an absolute location, like a point {x=10 , y=10, z=10} or a deck location and point ("A1" + point {x=10 , y=10, z=10})
|
|
98
|
+
:param speed: The absolute speed in mm/s.
|
|
95
99
|
"""
|
|
96
100
|
mount = validation.ensure_instrument_mount(mount)
|
|
97
101
|
with publisher.publish_context(
|
|
@@ -116,10 +120,9 @@ class RobotContext(publisher.CommandPublisher):
|
|
|
116
120
|
Move a set of axes to an absolute position on the deck.
|
|
117
121
|
|
|
118
122
|
:param axis_map: A dictionary mapping axes to an absolute position on the deck in mm.
|
|
119
|
-
:param critical_point: The critical point to move the axes with. It should only
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
in the axis map.
|
|
123
|
+
:param critical_point: The critical point, or specific point on the object being moved, to move the axes with. It should only specify the gantry axes (i.e. `x`, `y`, `z`). When you specify a critical point, you're specifying the object on the gantry to be moved. If not specified, the critical point defaults to the center of the carriage attached to the gantry.
|
|
124
|
+
:param float speed: The maximum speed with which to move all axes in mm/s.
|
|
125
|
+
|
|
123
126
|
"""
|
|
124
127
|
instrument_on_left = self._core.get_pipette_type_from_engine(Mount.LEFT)
|
|
125
128
|
is_96_channel = instrument_on_left == PipetteNameType.P1000_96
|
|
@@ -154,11 +157,9 @@ class RobotContext(publisher.CommandPublisher):
|
|
|
154
157
|
"""
|
|
155
158
|
Move a set of axes to a relative position on the deck.
|
|
156
159
|
|
|
157
|
-
:param axis_map: A dictionary mapping axes to relative movements in mm.
|
|
158
|
-
:type mount: types.Mount or str
|
|
160
|
+
:param axis_map: A dictionary mapping axes to relative movements from the current position in mm.
|
|
159
161
|
|
|
160
|
-
:param float speed: The maximum speed with which
|
|
161
|
-
in the axis map.
|
|
162
|
+
:param float speed: The maximum speed with which to move all axes in mm/s.
|
|
162
163
|
"""
|
|
163
164
|
instrument_on_left = self._core.get_pipette_type_from_engine(Mount.LEFT)
|
|
164
165
|
is_96_channel = instrument_on_left == PipetteNameType.P1000_96
|
|
@@ -177,7 +178,10 @@ class RobotContext(publisher.CommandPublisher):
|
|
|
177
178
|
self._core.move_axes_relative(axis_map, speed)
|
|
178
179
|
|
|
179
180
|
def close_gripper_jaw(self, force: Optional[float] = None) -> None:
|
|
180
|
-
"""
|
|
181
|
+
"""Closes the Flex Gripper jaws with a specified force.
|
|
182
|
+
|
|
183
|
+
:param force: Force with which to close the gripper jaws in newtons.
|
|
184
|
+
"""
|
|
181
185
|
with publisher.publish_context(
|
|
182
186
|
broker=self.broker,
|
|
183
187
|
command=cmds.close_gripper(
|
|
@@ -187,7 +191,10 @@ class RobotContext(publisher.CommandPublisher):
|
|
|
187
191
|
self._core.close_gripper(force)
|
|
188
192
|
|
|
189
193
|
def open_gripper_jaw(self) -> None:
|
|
190
|
-
"""
|
|
194
|
+
"""Opens the Flex Gripper jaws with a specified force.
|
|
195
|
+
|
|
196
|
+
:param force: Force with which to open the gripper jaws in newtons.
|
|
197
|
+
"""
|
|
191
198
|
with publisher.publish_context(
|
|
192
199
|
broker=self.broker,
|
|
193
200
|
command=cmds.open_gripper(),
|
|
@@ -200,9 +207,9 @@ class RobotContext(publisher.CommandPublisher):
|
|
|
200
207
|
location: Union[Location, ModuleContext, DeckLocation],
|
|
201
208
|
) -> AxisMapType:
|
|
202
209
|
"""
|
|
203
|
-
Build
|
|
210
|
+
Build an axis map from a location to provide to
|
|
204
211
|
either :py:meth:`.RobotContext.move_axes_to` or :py:meth:`.RobotContext.move_axes_relative`.
|
|
205
|
-
You must provide only one of `location`, `slot`, or `module` to build
|
|
212
|
+
You must provide only one of either a `location`, `slot`, or `module` to build
|
|
206
213
|
the axis map.
|
|
207
214
|
|
|
208
215
|
:param mount: The mount of the instrument you wish create an axis map for.
|
|
@@ -210,7 +217,10 @@ class RobotContext(publisher.CommandPublisher):
|
|
|
210
217
|
of the strings ``"left"``, ``"right"``, ``"extension"``, ``"gripper"``. Note
|
|
211
218
|
that the gripper mount can be referred to either as ``"extension"`` or ``"gripper"``.
|
|
212
219
|
:type mount: types.Mount or str
|
|
213
|
-
:param location:
|
|
220
|
+
:param location: Any location on the deck, specified as:
|
|
221
|
+
|
|
222
|
+
- a deck location, like slot ``"A1"``.
|
|
223
|
+
- a defined location, like a module on the deck.
|
|
214
224
|
:type location: `Well`, `ModuleContext`, `DeckLocation` or `OffDeckType`
|
|
215
225
|
"""
|
|
216
226
|
mount = validation.ensure_instrument_mount(mount)
|
|
@@ -248,7 +258,11 @@ class RobotContext(publisher.CommandPublisher):
|
|
|
248
258
|
self, mount: Union[Mount, str], volume: float, action: PipetteActionTypes
|
|
249
259
|
) -> AxisMapType:
|
|
250
260
|
"""
|
|
251
|
-
Build
|
|
261
|
+
Build an axis map to move a pipette plunger motor to complete liquid handling actions.
|
|
262
|
+
|
|
263
|
+
:mount: The left or right instrument mount the pipette is attached to.
|
|
264
|
+
:param volume: A volume to convert to an axis map for linear plunger displacement.
|
|
265
|
+
:param action: Choose to ``aspirate`` or ``dispense``.
|
|
252
266
|
|
|
253
267
|
"""
|
|
254
268
|
pipette_name = self._core.get_pipette_type_from_engine(mount)
|
|
@@ -268,7 +282,9 @@ class RobotContext(publisher.CommandPublisher):
|
|
|
268
282
|
self, mount: Union[Mount, str], position_name: PlungerPositionTypes
|
|
269
283
|
) -> AxisMapType:
|
|
270
284
|
"""
|
|
271
|
-
Build
|
|
285
|
+
Build an axis map to move a pipette plunger motor to a named position.
|
|
286
|
+
|
|
287
|
+
:param position_name: A named position to move the pipette plunger to. Choose from ``top``, ``bottom``, ``blowout``, or ``drop`` plunger positions.
|
|
272
288
|
|
|
273
289
|
"""
|
|
274
290
|
pipette_name = self._core.get_pipette_type_from_engine(mount)
|
|
@@ -284,8 +300,9 @@ class RobotContext(publisher.CommandPublisher):
|
|
|
284
300
|
return {pipette_axis: pipette_position}
|
|
285
301
|
|
|
286
302
|
def build_axis_map(self, axis_map: StringAxisMap) -> AxisMapType:
|
|
287
|
-
"""Take in a :py:class:`.types.StringAxisMap` and output
|
|
288
|
-
|
|
303
|
+
"""Take in a :py:class:`.types.StringAxisMap` and output an axis map.
|
|
304
|
+
|
|
305
|
+
The :py:class:`.types.StringAxisMap` is allowed to contain any of the following strings:
|
|
289
306
|
``"x"``, ``"y"``, "``z_l"``, "``z_r"``, "``z_g"``, ``"q"``.
|
|
290
307
|
|
|
291
308
|
An example of a valid axis map could be:
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""Data for concurrent protocol tasks."""
|
|
2
|
+
from typing import TYPE_CHECKING
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from opentrons.protocols.api_support.util import requires_version
|
|
5
|
+
from opentrons.protocols.api_support.types import APIVersion
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from .core.common import TaskCore
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Task:
|
|
12
|
+
"""A concurrent protocol task created by a protocol API function.
|
|
13
|
+
|
|
14
|
+
.. versionadded:: 2.27
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def __init__(self, core: "TaskCore", api_version: APIVersion) -> None:
|
|
18
|
+
"""Initialize a Task."""
|
|
19
|
+
self._core = core
|
|
20
|
+
self._api_version = api_version
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
@requires_version(2, 27)
|
|
24
|
+
def created_at(self) -> datetime:
|
|
25
|
+
"""The timestamp of when the task was created."""
|
|
26
|
+
return self._core.get_created_at_timestamp()
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
@requires_version(2, 27)
|
|
30
|
+
def done(self) -> bool:
|
|
31
|
+
"""Returns ``True`` if the task is done."""
|
|
32
|
+
return self._core.is_done()
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
@requires_version(2, 27)
|
|
36
|
+
def started(self) -> bool:
|
|
37
|
+
"""Returns ``True`` if the task has started."""
|
|
38
|
+
return self._core.is_started()
|
|
39
|
+
...
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
@requires_version(2, 27)
|
|
43
|
+
def finished_at(self) -> datetime | None:
|
|
44
|
+
"""The timestamp of the when the task finished.
|
|
45
|
+
|
|
46
|
+
Returns ``None`` if the task hasn't finished yet.
|
|
47
|
+
"""
|
|
48
|
+
return self._core.get_finished_at_timestamp()
|
|
@@ -489,6 +489,7 @@ def ensure_thermocycler_profile_steps(
|
|
|
489
489
|
temperature = step.get("temperature")
|
|
490
490
|
hold_mins = step.get("hold_time_minutes")
|
|
491
491
|
hold_secs = step.get("hold_time_seconds")
|
|
492
|
+
ramp_rate = step.get("ramp_rate")
|
|
492
493
|
if temperature is None:
|
|
493
494
|
raise ValueError("temperature must be defined for each step in cycle")
|
|
494
495
|
if hold_mins is None and hold_secs is None:
|
|
@@ -496,10 +497,14 @@ def ensure_thermocycler_profile_steps(
|
|
|
496
497
|
"either hold_time_minutes or hold_time_seconds must be"
|
|
497
498
|
"defined for each step in cycle"
|
|
498
499
|
)
|
|
500
|
+
if ramp_rate is not None and ramp_rate <= 0:
|
|
501
|
+
raise ValueError("Ramp rate must be greater than 0.")
|
|
499
502
|
validated_seconds = ensure_hold_time_seconds(hold_secs, hold_mins)
|
|
500
503
|
validated_steps.append(
|
|
501
504
|
ThermocyclerStep(
|
|
502
|
-
temperature=temperature,
|
|
505
|
+
temperature=temperature,
|
|
506
|
+
hold_time_seconds=validated_seconds,
|
|
507
|
+
ramp_rate=ramp_rate,
|
|
503
508
|
)
|
|
504
509
|
)
|
|
505
510
|
return validated_steps
|
|
@@ -26,8 +26,9 @@ from .actions import (
|
|
|
26
26
|
AddModuleAction,
|
|
27
27
|
FinishErrorDetails,
|
|
28
28
|
DoorChangeAction,
|
|
29
|
-
ResetTipsAction,
|
|
30
29
|
SetPipetteMovementSpeedAction,
|
|
30
|
+
StartTaskAction,
|
|
31
|
+
FinishTaskAction,
|
|
31
32
|
)
|
|
32
33
|
from .get_state_update import get_state_updates
|
|
33
34
|
|
|
@@ -55,8 +56,9 @@ __all__ = [
|
|
|
55
56
|
"AddAddressableAreaAction",
|
|
56
57
|
"AddModuleAction",
|
|
57
58
|
"DoorChangeAction",
|
|
58
|
-
"ResetTipsAction",
|
|
59
59
|
"SetPipetteMovementSpeedAction",
|
|
60
|
+
"StartTaskAction",
|
|
61
|
+
"FinishTaskAction",
|
|
60
62
|
# action payload values
|
|
61
63
|
"PauseSource",
|
|
62
64
|
"FinishErrorDetails",
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
Actions can be passed to the ActionDispatcher, where they will trigger
|
|
4
4
|
reactions in objects that subscribe to the pipeline, like the StateStore.
|
|
5
5
|
"""
|
|
6
|
+
|
|
6
7
|
import dataclasses
|
|
7
8
|
from datetime import datetime
|
|
8
9
|
from enum import Enum
|
|
@@ -20,6 +21,7 @@ from ..commands import (
|
|
|
20
21
|
CommandDefinedErrorData,
|
|
21
22
|
)
|
|
22
23
|
from ..error_recovery_policy import ErrorRecoveryPolicy, ErrorRecoveryType
|
|
24
|
+
from ..errors import ErrorOccurrence
|
|
23
25
|
from ..notes.notes import CommandNote
|
|
24
26
|
from ..state.update_types import StateUpdate
|
|
25
27
|
from ..types import (
|
|
@@ -27,6 +29,7 @@ from ..types import (
|
|
|
27
29
|
ModuleDefinition,
|
|
28
30
|
Liquid,
|
|
29
31
|
DeckConfigurationType,
|
|
32
|
+
Task,
|
|
30
33
|
)
|
|
31
34
|
|
|
32
35
|
|
|
@@ -60,7 +63,7 @@ class PauseAction:
|
|
|
60
63
|
class StopAction:
|
|
61
64
|
"""Request engine execution to stop soon."""
|
|
62
65
|
|
|
63
|
-
|
|
66
|
+
from_asynchronous_error: bool = False
|
|
64
67
|
|
|
65
68
|
|
|
66
69
|
@dataclasses.dataclass(frozen=True)
|
|
@@ -201,6 +204,22 @@ class FailCommandAction:
|
|
|
201
204
|
"""The command to fail, in its prior `running` state."""
|
|
202
205
|
|
|
203
206
|
|
|
207
|
+
@dataclasses.dataclass(frozen=True)
|
|
208
|
+
class StartTaskAction:
|
|
209
|
+
"""Store new task in state."""
|
|
210
|
+
|
|
211
|
+
task: Task
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
@dataclasses.dataclass(frozen=True)
|
|
215
|
+
class FinishTaskAction:
|
|
216
|
+
"""Mark task as finished in state."""
|
|
217
|
+
|
|
218
|
+
task_id: str
|
|
219
|
+
finished_at: datetime
|
|
220
|
+
error: ErrorOccurrence | None
|
|
221
|
+
|
|
222
|
+
|
|
204
223
|
@dataclasses.dataclass(frozen=True)
|
|
205
224
|
class AddLabwareOffsetAction:
|
|
206
225
|
"""Add a labware offset, to apply to subsequent `LoadLabwareCommand`s."""
|
|
@@ -253,13 +272,6 @@ class AddModuleAction:
|
|
|
253
272
|
module_live_data: LiveData
|
|
254
273
|
|
|
255
274
|
|
|
256
|
-
@dataclasses.dataclass(frozen=True)
|
|
257
|
-
class ResetTipsAction:
|
|
258
|
-
"""Reset the tip tracking state of a given tip rack."""
|
|
259
|
-
|
|
260
|
-
labware_id: str
|
|
261
|
-
|
|
262
|
-
|
|
263
275
|
@dataclasses.dataclass(frozen=True)
|
|
264
276
|
class SetPipetteMovementSpeedAction:
|
|
265
277
|
"""Set the speed of a pipette's X/Y/Z movements. Does not affect plunger speed.
|
|
@@ -296,7 +308,8 @@ Action = Union[
|
|
|
296
308
|
SetDeckConfigurationAction,
|
|
297
309
|
AddAddressableAreaAction,
|
|
298
310
|
AddLiquidAction,
|
|
299
|
-
ResetTipsAction,
|
|
300
311
|
SetPipetteMovementSpeedAction,
|
|
301
312
|
SetErrorRecoveryPolicyAction,
|
|
313
|
+
StartTaskAction,
|
|
314
|
+
FinishTaskAction,
|
|
302
315
|
]
|
|
@@ -65,6 +65,12 @@ class SyncClient:
|
|
|
65
65
|
) -> commands.LoadLabwareResult:
|
|
66
66
|
pass
|
|
67
67
|
|
|
68
|
+
@overload
|
|
69
|
+
def execute_command_without_recovery(
|
|
70
|
+
self, params: commands.CreateTimerParams
|
|
71
|
+
) -> commands.CreateTimerResult:
|
|
72
|
+
pass
|
|
73
|
+
|
|
68
74
|
@overload
|
|
69
75
|
def execute_command_without_recovery(
|
|
70
76
|
self, params: commands.LoadModuleParams
|
|
@@ -149,13 +155,6 @@ class SyncClient:
|
|
|
149
155
|
"""Add a liquid to the engine."""
|
|
150
156
|
return self._transport.call_method("add_liquid", name=name, color=color, description=description) # type: ignore[no-any-return]
|
|
151
157
|
|
|
152
|
-
def reset_tips(self, labware_id: str) -> None:
|
|
153
|
-
"""Reset a labware's tip tracking state.."""
|
|
154
|
-
self._transport.call_method(
|
|
155
|
-
"reset_tips",
|
|
156
|
-
labware_id=labware_id,
|
|
157
|
-
)
|
|
158
|
-
|
|
159
158
|
def add_labware_offset(self, request: LabwareOffsetCreate) -> None:
|
|
160
159
|
"""Add a labware offset."""
|
|
161
160
|
self._transport.call_method("add_labware_offset", request=request)
|
|
@@ -271,6 +271,22 @@ from .wait_for_duration import (
|
|
|
271
271
|
WaitForDurationCommandType,
|
|
272
272
|
)
|
|
273
273
|
|
|
274
|
+
from .create_timer import (
|
|
275
|
+
CreateTimer,
|
|
276
|
+
CreateTimerCreate,
|
|
277
|
+
CreateTimerParams,
|
|
278
|
+
CreateTimerResult,
|
|
279
|
+
CreateTimerCommandType,
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
from .wait_for_tasks import (
|
|
283
|
+
WaitForTasks,
|
|
284
|
+
WaitForTasksCreate,
|
|
285
|
+
WaitForTasksParams,
|
|
286
|
+
WaitForTasksResult,
|
|
287
|
+
WaitForTasksCommandType,
|
|
288
|
+
)
|
|
289
|
+
|
|
274
290
|
from .pick_up_tip import (
|
|
275
291
|
PickUpTip,
|
|
276
292
|
PickUpTipParams,
|
|
@@ -384,6 +400,14 @@ from .get_next_tip import (
|
|
|
384
400
|
GetNextTipCommandType,
|
|
385
401
|
)
|
|
386
402
|
|
|
403
|
+
from .set_tip_state import (
|
|
404
|
+
SetTipState,
|
|
405
|
+
SetTipStateCreate,
|
|
406
|
+
SetTipStateParams,
|
|
407
|
+
SetTipStateResult,
|
|
408
|
+
SetTipStateCommandType,
|
|
409
|
+
)
|
|
410
|
+
|
|
387
411
|
from .liquid_probe import (
|
|
388
412
|
LiquidProbe,
|
|
389
413
|
LiquidProbeParams,
|
|
@@ -619,6 +643,12 @@ __all__ = [
|
|
|
619
643
|
"WaitForDurationCreate",
|
|
620
644
|
"WaitForDurationResult",
|
|
621
645
|
"WaitForDurationCommandType",
|
|
646
|
+
# Timer command models
|
|
647
|
+
"CreateTimer",
|
|
648
|
+
"CreateTimerCreate",
|
|
649
|
+
"CreateTimerParams",
|
|
650
|
+
"CreateTimerResult",
|
|
651
|
+
"CreateTimerCommandType",
|
|
622
652
|
# pick up tip command models
|
|
623
653
|
"PickUpTip",
|
|
624
654
|
"PickUpTipCreate",
|
|
@@ -725,6 +755,12 @@ __all__ = [
|
|
|
725
755
|
"GetNextTipParams",
|
|
726
756
|
"GetNextTipResult",
|
|
727
757
|
"GetNextTipCommandType",
|
|
758
|
+
# set tip state command bundle
|
|
759
|
+
"SetTipState",
|
|
760
|
+
"SetTipStateCreate",
|
|
761
|
+
"SetTipStateParams",
|
|
762
|
+
"SetTipStateResult",
|
|
763
|
+
"SetTipStateCommandType",
|
|
728
764
|
# liquid probe command bundle
|
|
729
765
|
"LiquidProbe",
|
|
730
766
|
"LiquidProbeParams",
|
|
@@ -754,4 +790,10 @@ __all__ = [
|
|
|
754
790
|
"PressureDispenseCreate",
|
|
755
791
|
"PressureDispenseResult",
|
|
756
792
|
"PressureDispenseCommandType",
|
|
793
|
+
# wait for tasks command bundle
|
|
794
|
+
"WaitForTasks",
|
|
795
|
+
"WaitForTasksCreate",
|
|
796
|
+
"WaitForTasksParams",
|
|
797
|
+
"WaitForTasksResult",
|
|
798
|
+
"WaitForTasksCommandType",
|
|
757
799
|
]
|
|
@@ -102,25 +102,12 @@ class CloseLidImpl(AbstractCommandImpl[CloseLidParams, SuccessData[CloseLidResul
|
|
|
102
102
|
)
|
|
103
103
|
)
|
|
104
104
|
|
|
105
|
-
# The lid's labware definition stores gripper offsets for itself in the
|
|
106
|
-
# space normally meant for offsets for labware stacked atop it.
|
|
107
|
-
lid_gripper_offsets = self._state_view.labware.get_child_gripper_offsets(
|
|
108
|
-
labware_definition=lid_definition,
|
|
109
|
-
slot_name=None,
|
|
110
|
-
)
|
|
111
|
-
if lid_gripper_offsets is None:
|
|
112
|
-
raise ValueError(
|
|
113
|
-
"Gripper Offset values for Absorbance Reader Lid labware must not be None."
|
|
114
|
-
)
|
|
115
|
-
|
|
116
105
|
await self._labware_movement.move_labware_with_gripper(
|
|
117
106
|
labware_definition=lid_definition,
|
|
118
107
|
current_location=current_location,
|
|
119
108
|
new_location=new_location,
|
|
120
|
-
user_pick_up_offset=Point
|
|
121
|
-
|
|
122
|
-
),
|
|
123
|
-
user_drop_offset=Point.from_xyz_attrs(lid_gripper_offsets.dropOffset),
|
|
109
|
+
user_pick_up_offset=Point(),
|
|
110
|
+
user_drop_offset=Point(),
|
|
124
111
|
post_drop_slide_offset=None,
|
|
125
112
|
gripper_z_offset=LID_Z_CLEARANCE,
|
|
126
113
|
)
|
|
@@ -103,25 +103,12 @@ class OpenLidImpl(AbstractCommandImpl[OpenLidParams, SuccessData[OpenLidResult]]
|
|
|
103
103
|
mod_substate.module_id
|
|
104
104
|
)
|
|
105
105
|
|
|
106
|
-
# The lid's labware definition stores gripper offsets for itself in the
|
|
107
|
-
# space normally meant for offsets for labware stacked atop it.
|
|
108
|
-
lid_gripper_offsets = self._state_view.labware.get_child_gripper_offsets(
|
|
109
|
-
labware_definition=lid_definition,
|
|
110
|
-
slot_name=None,
|
|
111
|
-
)
|
|
112
|
-
if lid_gripper_offsets is None:
|
|
113
|
-
raise ValueError(
|
|
114
|
-
"Gripper Offset values for Absorbance Reader Lid labware must not be None."
|
|
115
|
-
)
|
|
116
|
-
|
|
117
106
|
await self._labware_movement.move_labware_with_gripper(
|
|
118
107
|
labware_definition=lid_definition,
|
|
119
108
|
current_location=current_location,
|
|
120
109
|
new_location=new_location,
|
|
121
|
-
user_pick_up_offset=Point
|
|
122
|
-
|
|
123
|
-
),
|
|
124
|
-
user_drop_offset=Point.from_xyz_attrs(lid_gripper_offsets.dropOffset),
|
|
110
|
+
user_pick_up_offset=Point(),
|
|
111
|
+
user_drop_offset=Point(),
|
|
125
112
|
post_drop_slide_offset=None,
|
|
126
113
|
gripper_z_offset=LID_Z_CLEARANCE,
|
|
127
114
|
)
|
|
@@ -161,6 +161,7 @@ class AspirateImplementation(AbstractCommandImpl[AspirateParams, _ExecuteReturn]
|
|
|
161
161
|
well_location=well_location,
|
|
162
162
|
current_well=current_well,
|
|
163
163
|
operation_volume=-params.volume,
|
|
164
|
+
offset_pipette_for_reservoir_subwells=False,
|
|
164
165
|
)
|
|
165
166
|
state_update.append(move_result.state_update)
|
|
166
167
|
if isinstance(move_result, DefinedErrorData):
|
|
@@ -185,6 +185,7 @@ class AbstractCommandImpl(
|
|
|
185
185
|
tip_handler: execution.TipHandler,
|
|
186
186
|
run_control: execution.RunControlHandler,
|
|
187
187
|
rail_lights: execution.RailLightsHandler,
|
|
188
|
+
task_handler: execution.TaskHandler,
|
|
188
189
|
model_utils: ModelUtils,
|
|
189
190
|
status_bar: execution.StatusBarHandler,
|
|
190
191
|
command_note_adder: CommandNoteAdder,
|