opentrons 8.7.0a7__py3-none-any.whl → 8.7.0a9__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of opentrons might be problematic. Click here for more details.
- opentrons/_version.py +2 -2
- opentrons/drivers/asyncio/communication/serial_connection.py +55 -129
- opentrons/drivers/flex_stacker/driver.py +6 -1
- opentrons/drivers/heater_shaker/abstract.py +0 -5
- opentrons/drivers/heater_shaker/driver.py +0 -10
- opentrons/drivers/heater_shaker/simulator.py +0 -4
- opentrons/drivers/thermocycler/abstract.py +0 -6
- opentrons/drivers/thermocycler/driver.py +10 -61
- opentrons/drivers/thermocycler/simulator.py +0 -6
- opentrons/hardware_control/api.py +5 -24
- opentrons/hardware_control/backends/controller.py +2 -8
- opentrons/hardware_control/backends/flex_protocol.py +1 -0
- opentrons/hardware_control/backends/ot3controller.py +3 -3
- opentrons/hardware_control/backends/ot3simulator.py +2 -2
- opentrons/hardware_control/backends/simulator.py +1 -2
- opentrons/hardware_control/backends/subsystem_manager.py +2 -5
- opentrons/hardware_control/emulation/abstract_emulator.py +4 -6
- opentrons/hardware_control/emulation/connection_handler.py +5 -8
- opentrons/hardware_control/emulation/heater_shaker.py +3 -12
- opentrons/hardware_control/emulation/settings.py +1 -1
- opentrons/hardware_control/emulation/thermocycler.py +15 -67
- opentrons/hardware_control/module_control.py +8 -82
- opentrons/hardware_control/modules/__init__.py +0 -3
- opentrons/hardware_control/modules/absorbance_reader.py +4 -11
- opentrons/hardware_control/modules/flex_stacker.py +9 -38
- opentrons/hardware_control/modules/heater_shaker.py +5 -42
- opentrons/hardware_control/modules/magdeck.py +4 -8
- opentrons/hardware_control/modules/mod_abc.py +5 -13
- opentrons/hardware_control/modules/tempdeck.py +5 -25
- opentrons/hardware_control/modules/thermocycler.py +11 -68
- opentrons/hardware_control/modules/types.py +1 -20
- opentrons/hardware_control/modules/utils.py +4 -11
- opentrons/hardware_control/nozzle_manager.py +0 -3
- opentrons/hardware_control/ot3api.py +7 -26
- opentrons/hardware_control/poller.py +8 -22
- opentrons/hardware_control/protocols/gripper_controller.py +1 -0
- opentrons/hardware_control/scripts/update_module_fw.py +0 -5
- opentrons/hardware_control/types.py +2 -31
- opentrons/legacy_commands/module_commands.py +0 -23
- opentrons/legacy_commands/protocol_commands.py +0 -20
- opentrons/legacy_commands/types.py +0 -80
- opentrons/motion_planning/deck_conflict.py +12 -17
- opentrons/motion_planning/waypoints.py +29 -15
- opentrons/protocol_api/__init__.py +1 -5
- opentrons/protocol_api/_types.py +1 -6
- opentrons/protocol_api/core/common.py +1 -3
- opentrons/protocol_api/core/engine/_default_labware_versions.py +11 -32
- opentrons/protocol_api/core/engine/labware.py +1 -8
- opentrons/protocol_api/core/engine/module_core.py +8 -75
- opentrons/protocol_api/core/engine/protocol.py +1 -18
- opentrons/protocol_api/core/engine/well.py +0 -8
- opentrons/protocol_api/core/legacy/legacy_module_core.py +4 -24
- opentrons/protocol_api/core/legacy/legacy_protocol_core.py +1 -11
- opentrons/protocol_api/core/legacy/legacy_well_core.py +0 -4
- opentrons/protocol_api/core/legacy_simulator/legacy_protocol_core.py +2 -14
- opentrons/protocol_api/core/module.py +4 -37
- opentrons/protocol_api/core/protocol.py +2 -11
- opentrons/protocol_api/core/well.py +0 -4
- opentrons/protocol_api/labware.py +0 -5
- opentrons/protocol_api/module_contexts.py +61 -122
- opentrons/protocol_api/protocol_context.py +4 -26
- opentrons/protocol_api/robot_context.py +21 -38
- opentrons/protocol_api/validation.py +1 -6
- opentrons/protocol_engine/actions/__init__.py +2 -4
- opentrons/protocol_engine/actions/actions.py +9 -22
- opentrons/protocol_engine/clients/sync_client.py +7 -42
- opentrons/protocol_engine/commands/__init__.py +0 -42
- opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +15 -2
- opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +15 -2
- opentrons/protocol_engine/commands/aspirate.py +0 -1
- opentrons/protocol_engine/commands/command.py +0 -1
- opentrons/protocol_engine/commands/command_unions.py +0 -49
- opentrons/protocol_engine/commands/dispense.py +0 -1
- opentrons/protocol_engine/commands/drop_tip.py +8 -32
- opentrons/protocol_engine/commands/heater_shaker/__init__.py +0 -14
- opentrons/protocol_engine/commands/heater_shaker/set_and_wait_for_shake_speed.py +4 -5
- opentrons/protocol_engine/commands/heater_shaker/set_target_temperature.py +5 -31
- opentrons/protocol_engine/commands/movement_common.py +0 -2
- opentrons/protocol_engine/commands/pick_up_tip.py +11 -21
- opentrons/protocol_engine/commands/temperature_module/set_target_temperature.py +7 -38
- opentrons/protocol_engine/commands/thermocycler/__init__.py +0 -16
- opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py +0 -6
- opentrons/protocol_engine/commands/thermocycler/run_profile.py +0 -8
- opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py +6 -40
- opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py +5 -29
- opentrons/protocol_engine/commands/touch_tip.py +1 -1
- opentrons/protocol_engine/commands/unsafe/unsafe_place_labware.py +22 -6
- opentrons/protocol_engine/errors/__init__.py +0 -4
- opentrons/protocol_engine/errors/exceptions.py +0 -55
- opentrons/protocol_engine/execution/__init__.py +0 -2
- opentrons/protocol_engine/execution/command_executor.py +0 -8
- opentrons/protocol_engine/execution/create_queue_worker.py +1 -5
- opentrons/protocol_engine/execution/labware_movement.py +21 -10
- opentrons/protocol_engine/execution/movement.py +0 -2
- opentrons/protocol_engine/execution/queue_worker.py +0 -4
- opentrons/protocol_engine/execution/run_control.py +0 -8
- opentrons/protocol_engine/protocol_engine.py +34 -75
- opentrons/protocol_engine/resources/__init__.py +0 -2
- opentrons/protocol_engine/resources/deck_configuration_provider.py +0 -7
- opentrons/protocol_engine/resources/labware_validation.py +6 -10
- opentrons/protocol_engine/state/_labware_origin_math.py +636 -0
- opentrons/protocol_engine/state/_well_math.py +18 -60
- opentrons/protocol_engine/state/addressable_areas.py +0 -2
- opentrons/protocol_engine/state/commands.py +11 -14
- opentrons/protocol_engine/state/geometry.py +374 -213
- opentrons/protocol_engine/state/labware.py +102 -52
- opentrons/protocol_engine/state/module_substates/thermocycler_module_substate.py +0 -37
- opentrons/protocol_engine/state/modules.py +8 -21
- opentrons/protocol_engine/state/motion.py +0 -44
- opentrons/protocol_engine/state/state.py +0 -14
- opentrons/protocol_engine/state/state_summary.py +0 -2
- opentrons/protocol_engine/state/tips.py +258 -177
- opentrons/protocol_engine/state/update_types.py +9 -16
- opentrons/protocol_engine/types/__init__.py +3 -9
- opentrons/protocol_engine/types/deck_configuration.py +1 -5
- opentrons/protocol_engine/types/instrument.py +1 -8
- opentrons/protocol_engine/types/labware.py +13 -1
- opentrons/protocol_engine/types/module.py +0 -10
- opentrons/protocol_engine/types/tip.py +0 -9
- opentrons/protocol_runner/create_simulating_orchestrator.py +2 -29
- opentrons/protocol_runner/run_orchestrator.py +2 -18
- opentrons/protocols/api_support/definitions.py +1 -1
- opentrons/protocols/api_support/types.py +1 -2
- opentrons/simulate.py +15 -48
- opentrons/system/camera.py +1 -1
- {opentrons-8.7.0a7.dist-info → opentrons-8.7.0a9.dist-info}/METADATA +4 -4
- {opentrons-8.7.0a7.dist-info → opentrons-8.7.0a9.dist-info}/RECORD +130 -146
- opentrons/protocol_api/core/engine/tasks.py +0 -48
- opentrons/protocol_api/core/legacy/tasks.py +0 -19
- opentrons/protocol_api/core/legacy_simulator/tasks.py +0 -19
- opentrons/protocol_api/core/tasks.py +0 -31
- opentrons/protocol_api/tasks.py +0 -48
- opentrons/protocol_engine/commands/create_timer.py +0 -83
- opentrons/protocol_engine/commands/heater_shaker/common.py +0 -20
- opentrons/protocol_engine/commands/heater_shaker/set_shake_speed.py +0 -136
- opentrons/protocol_engine/commands/set_tip_state.py +0 -97
- opentrons/protocol_engine/commands/thermocycler/start_run_extended_profile.py +0 -191
- opentrons/protocol_engine/commands/wait_for_tasks.py +0 -98
- opentrons/protocol_engine/execution/task_handler.py +0 -157
- opentrons/protocol_engine/resources/concurrency_provider.py +0 -27
- opentrons/protocol_engine/state/labware_origin_math/errors.py +0 -94
- opentrons/protocol_engine/state/labware_origin_math/stackup_origin_to_labware_origin.py +0 -1331
- opentrons/protocol_engine/state/tasks.py +0 -139
- opentrons/protocol_engine/types/tasks.py +0 -38
- {opentrons-8.7.0a7.dist-info → opentrons-8.7.0a9.dist-info}/WHEEL +0 -0
- {opentrons-8.7.0a7.dist-info → opentrons-8.7.0a9.dist-info}/entry_points.txt +0 -0
- {opentrons-8.7.0a7.dist-info → opentrons-8.7.0a9.dist-info}/licenses/LICENSE +0 -0
|
@@ -413,36 +413,6 @@ class WellDoesNotExistError(ProtocolEngineError):
|
|
|
413
413
|
super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
|
|
414
414
|
|
|
415
415
|
|
|
416
|
-
class NoTaskFoundError(ProtocolEngineError):
|
|
417
|
-
"""Raised when referencing a task that does not exist.
|
|
418
|
-
|
|
419
|
-
This error could be raised if a protocol references a task before it
|
|
420
|
-
has been created.
|
|
421
|
-
"""
|
|
422
|
-
|
|
423
|
-
def __init__(
|
|
424
|
-
self,
|
|
425
|
-
message: Optional[str] = None,
|
|
426
|
-
details: Optional[Dict[str, Any]] = None,
|
|
427
|
-
wrapping: Optional[Sequence[EnumeratedError]] = None,
|
|
428
|
-
) -> None:
|
|
429
|
-
"""Build a NoTaskFoundError."""
|
|
430
|
-
super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
class TaskFailedError(ProtocolEngineError):
|
|
434
|
-
"""Raised when waiting on a task that failed."""
|
|
435
|
-
|
|
436
|
-
def __init__(
|
|
437
|
-
self,
|
|
438
|
-
message: Optional[str] = None,
|
|
439
|
-
details: Optional[Dict[str, Any]] = None,
|
|
440
|
-
wrapping: Optional[Sequence[EnumeratedError]] = None,
|
|
441
|
-
) -> None:
|
|
442
|
-
"""Build a TaskFailedError."""
|
|
443
|
-
super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
|
|
444
|
-
|
|
445
|
-
|
|
446
416
|
class PipetteNotLoadedError(ProtocolEngineError):
|
|
447
417
|
"""Raised when referencing a pipette that has not been loaded."""
|
|
448
418
|
|
|
@@ -855,19 +825,6 @@ class InvalidTargetTemperatureError(ProtocolEngineError):
|
|
|
855
825
|
super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
|
|
856
826
|
|
|
857
827
|
|
|
858
|
-
class InvalidRampRateError(ProtocolEngineError):
|
|
859
|
-
"""Raised when attempting to set an invalid ramp rate."""
|
|
860
|
-
|
|
861
|
-
def __init__(
|
|
862
|
-
self,
|
|
863
|
-
message: Optional[str] = None,
|
|
864
|
-
details: Optional[Dict[str, Any]] = None,
|
|
865
|
-
wrapping: Optional[Sequence[EnumeratedError]] = None,
|
|
866
|
-
) -> None:
|
|
867
|
-
"""Build a InvalidRampRateError."""
|
|
868
|
-
super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
|
|
869
|
-
|
|
870
|
-
|
|
871
828
|
class InvalidBlockVolumeError(ProtocolEngineError):
|
|
872
829
|
"""Raised when attempting to set an invalid block max volume."""
|
|
873
830
|
|
|
@@ -1349,15 +1306,3 @@ class InvalidLabwarePositionError(ProtocolEngineError):
|
|
|
1349
1306
|
wrapping: Optional[Sequence[EnumeratedError]] = None,
|
|
1350
1307
|
) -> None:
|
|
1351
1308
|
super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
class InvalidModuleOrientation(ProtocolEngineError):
|
|
1355
|
-
"""Raised when a module orientation is invalid for a slot id."""
|
|
1356
|
-
|
|
1357
|
-
def __init__(
|
|
1358
|
-
self,
|
|
1359
|
-
message: Optional[str] = None,
|
|
1360
|
-
details: Optional[dict[str, Any]] = None,
|
|
1361
|
-
wrapping: Optional[Sequence[EnumeratedError]] = None,
|
|
1362
|
-
) -> None:
|
|
1363
|
-
super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
|
|
@@ -21,7 +21,6 @@ from .run_control import RunControlHandler
|
|
|
21
21
|
from .hardware_stopper import HardwareStopper
|
|
22
22
|
from .door_watcher import DoorWatcher
|
|
23
23
|
from .status_bar import StatusBarHandler
|
|
24
|
-
from .task_handler import TaskHandler
|
|
25
24
|
from ..resources.file_provider import FileProvider
|
|
26
25
|
|
|
27
26
|
# .thermocycler_movement_flagger omitted from package's public interface.
|
|
@@ -47,6 +46,5 @@ __all__ = [
|
|
|
47
46
|
"DoorWatcher",
|
|
48
47
|
"RailLightsHandler",
|
|
49
48
|
"StatusBarHandler",
|
|
50
|
-
"TaskHandler",
|
|
51
49
|
"FileProvider",
|
|
52
50
|
]
|
|
@@ -35,7 +35,6 @@ from .tip_handler import TipHandler
|
|
|
35
35
|
from .run_control import RunControlHandler
|
|
36
36
|
from .rail_lights import RailLightsHandler
|
|
37
37
|
from .status_bar import StatusBarHandler
|
|
38
|
-
from .task_handler import TaskHandler
|
|
39
38
|
|
|
40
39
|
|
|
41
40
|
log = getLogger(__name__)
|
|
@@ -86,7 +85,6 @@ class CommandExecutor:
|
|
|
86
85
|
run_control: RunControlHandler,
|
|
87
86
|
rail_lights: RailLightsHandler,
|
|
88
87
|
status_bar: StatusBarHandler,
|
|
89
|
-
task_handler: TaskHandler,
|
|
90
88
|
model_utils: Optional[ModelUtils] = None,
|
|
91
89
|
command_note_tracker_provider: Optional[CommandNoteTrackerProvider] = None,
|
|
92
90
|
) -> None:
|
|
@@ -108,7 +106,6 @@ class CommandExecutor:
|
|
|
108
106
|
self._command_note_tracker_provider = (
|
|
109
107
|
command_note_tracker_provider or _NoteTracker
|
|
110
108
|
)
|
|
111
|
-
self._task_handler = task_handler
|
|
112
109
|
|
|
113
110
|
async def execute(self, command_id: str) -> None:
|
|
114
111
|
"""Run a given command's execution procedure.
|
|
@@ -134,7 +131,6 @@ class CommandExecutor:
|
|
|
134
131
|
model_utils=self._model_utils,
|
|
135
132
|
status_bar=self._status_bar,
|
|
136
133
|
command_note_adder=note_tracker,
|
|
137
|
-
task_handler=self._task_handler,
|
|
138
134
|
)
|
|
139
135
|
|
|
140
136
|
started_at = self._model_utils.get_timestamp()
|
|
@@ -218,7 +214,3 @@ class CommandExecutor:
|
|
|
218
214
|
type=error_recovery_type,
|
|
219
215
|
)
|
|
220
216
|
)
|
|
221
|
-
|
|
222
|
-
def cancel_tasks(self, message: str | None = None) -> None:
|
|
223
|
-
"""Cancel all concurrent tasks."""
|
|
224
|
-
self._task_handler.cancel_all(message=message)
|
|
@@ -17,7 +17,6 @@ from .run_control import RunControlHandler
|
|
|
17
17
|
from .command_executor import CommandExecutor
|
|
18
18
|
from .queue_worker import QueueWorker
|
|
19
19
|
from .status_bar import StatusBarHandler
|
|
20
|
-
from .task_handler import TaskHandler
|
|
21
20
|
|
|
22
21
|
|
|
23
22
|
def create_queue_worker(
|
|
@@ -77,9 +76,7 @@ def create_queue_worker(
|
|
|
77
76
|
rail_lights_handler = RailLightsHandler(
|
|
78
77
|
hardware_api=hardware_api,
|
|
79
78
|
)
|
|
80
|
-
|
|
81
|
-
state_store=state_store, action_dispatcher=action_dispatcher
|
|
82
|
-
)
|
|
79
|
+
|
|
83
80
|
status_bar_handler = StatusBarHandler(hardware_api=hardware_api)
|
|
84
81
|
|
|
85
82
|
command_executor = CommandExecutor(
|
|
@@ -96,7 +93,6 @@ def create_queue_worker(
|
|
|
96
93
|
run_control=run_control_handler,
|
|
97
94
|
rail_lights=rail_lights_handler,
|
|
98
95
|
status_bar=status_bar_handler,
|
|
99
|
-
task_handler=task_handler,
|
|
100
96
|
)
|
|
101
97
|
|
|
102
98
|
return QueueWorker(
|
|
@@ -4,7 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
from typing import Optional, TYPE_CHECKING, overload
|
|
6
6
|
|
|
7
|
-
from opentrons_shared_data.labware.labware_definition import LabwareDefinition
|
|
7
|
+
from opentrons_shared_data.labware.labware_definition import LabwareDefinition, Quirks
|
|
8
8
|
|
|
9
9
|
from opentrons.types import Point
|
|
10
10
|
|
|
@@ -31,7 +31,6 @@ from ..types import (
|
|
|
31
31
|
OnLabwareLocation,
|
|
32
32
|
LabwareLocation,
|
|
33
33
|
OnDeckLabwareLocation,
|
|
34
|
-
GripperMoveType,
|
|
35
34
|
)
|
|
36
35
|
|
|
37
36
|
if TYPE_CHECKING:
|
|
@@ -142,16 +141,10 @@ class LabwareMovementHandler:
|
|
|
142
141
|
labware_definition = self._state_store.labware.get_definition(labware_id)
|
|
143
142
|
|
|
144
143
|
from_labware_center = self._state_store.geometry.get_labware_grip_point(
|
|
145
|
-
labware_definition=labware_definition,
|
|
146
|
-
location=current_location,
|
|
147
|
-
move_type=GripperMoveType.PICK_UP_LABWARE,
|
|
148
|
-
user_additional_offset=user_pick_up_offset,
|
|
144
|
+
labware_definition=labware_definition, location=current_location
|
|
149
145
|
)
|
|
150
146
|
to_labware_center = self._state_store.geometry.get_labware_grip_point(
|
|
151
|
-
labware_definition=labware_definition,
|
|
152
|
-
location=new_location,
|
|
153
|
-
move_type=GripperMoveType.DROP_LABWARE,
|
|
154
|
-
user_additional_offset=user_drop_offset,
|
|
147
|
+
labware_definition=labware_definition, location=new_location
|
|
155
148
|
)
|
|
156
149
|
|
|
157
150
|
if use_virtual_gripper:
|
|
@@ -200,10 +193,20 @@ class LabwareMovementHandler:
|
|
|
200
193
|
async with self._thermocycler_plate_lifter.lift_plate_for_labware_movement(
|
|
201
194
|
labware_location=current_location
|
|
202
195
|
):
|
|
196
|
+
final_offsets = (
|
|
197
|
+
self._state_store.geometry.get_final_labware_movement_offset_vectors(
|
|
198
|
+
from_location=current_location,
|
|
199
|
+
to_location=new_location,
|
|
200
|
+
additional_pick_up_offset=user_pick_up_offset,
|
|
201
|
+
additional_drop_offset=user_drop_offset,
|
|
202
|
+
current_labware=labware_definition,
|
|
203
|
+
)
|
|
204
|
+
)
|
|
203
205
|
movement_waypoints = get_gripper_labware_movement_waypoints(
|
|
204
206
|
from_labware_center=from_labware_center,
|
|
205
207
|
to_labware_center=to_labware_center,
|
|
206
208
|
gripper_home_z=gripper_homed_position.z,
|
|
209
|
+
offset_data=final_offsets,
|
|
207
210
|
post_drop_slide_offset=post_drop_slide_offset,
|
|
208
211
|
gripper_home_z_offset=gripper_z_offset,
|
|
209
212
|
)
|
|
@@ -235,6 +238,13 @@ class LabwareMovementHandler:
|
|
|
235
238
|
labware_definition=labware_definition
|
|
236
239
|
)
|
|
237
240
|
|
|
241
|
+
disable_geometry_grip_check = False
|
|
242
|
+
if labware_definition.parameters.quirks is not None:
|
|
243
|
+
disable_geometry_grip_check = (
|
|
244
|
+
Quirks.disableGeometryBasedGripCheck.value
|
|
245
|
+
in labware_definition.parameters.quirks
|
|
246
|
+
)
|
|
247
|
+
|
|
238
248
|
# todo(mm, 2024-09-26): This currently raises a lower-level 2015 FailedGripperPickupError.
|
|
239
249
|
# Convert this to a higher-level 3001 LabwareDroppedError or 3002 LabwareNotPickedUpError,
|
|
240
250
|
# depending on what waypoint we're at, to propagate a more specific error code to users.
|
|
@@ -242,6 +252,7 @@ class LabwareMovementHandler:
|
|
|
242
252
|
expected_grip_width=grip_specs.targetY,
|
|
243
253
|
grip_width_uncertainty_wider=grip_specs.uncertaintyWider,
|
|
244
254
|
grip_width_uncertainty_narrower=grip_specs.uncertaintyNarrower,
|
|
255
|
+
disable_geometry_grip_check=disable_geometry_grip_check,
|
|
245
256
|
)
|
|
246
257
|
await ot3api.move_to(
|
|
247
258
|
mount=gripper_mount, abs_position=waypoint_data.position
|
|
@@ -83,7 +83,6 @@ class MovementHandler:
|
|
|
83
83
|
minimum_z_height: Optional[float] = None,
|
|
84
84
|
speed: Optional[float] = None,
|
|
85
85
|
operation_volume: Optional[float] = None,
|
|
86
|
-
offset_pipette_for_reservoir_subwells: bool = False,
|
|
87
86
|
) -> Point:
|
|
88
87
|
"""Move to a specific well."""
|
|
89
88
|
self._state_store.geometry.raise_if_labware_inaccessible_by_pipette(
|
|
@@ -144,7 +143,6 @@ class MovementHandler:
|
|
|
144
143
|
force_direct=force_direct,
|
|
145
144
|
minimum_z_height=minimum_z_height,
|
|
146
145
|
operation_volume=operation_volume,
|
|
147
|
-
offset_pipette_for_reservoir_subwells=offset_pipette_for_reservoir_subwells,
|
|
148
146
|
)
|
|
149
147
|
|
|
150
148
|
speed = self._state_store.pipettes.get_movement_speed(
|
|
@@ -51,7 +51,6 @@ class QueueWorker:
|
|
|
51
51
|
"""
|
|
52
52
|
if self._worker_task:
|
|
53
53
|
self._worker_task.cancel()
|
|
54
|
-
self._command_executor.cancel_tasks("Engine cancelled")
|
|
55
54
|
|
|
56
55
|
async def join(self) -> None:
|
|
57
56
|
"""Wait for the worker to finish, propagating any errors."""
|
|
@@ -66,10 +65,7 @@ class QueueWorker:
|
|
|
66
65
|
pass
|
|
67
66
|
except Exception as e:
|
|
68
67
|
log.error("Unhandled exception in QueueWorker job", exc_info=e)
|
|
69
|
-
self._command_executor.cancel_tasks("Engine failed")
|
|
70
68
|
raise e
|
|
71
|
-
else:
|
|
72
|
-
self._command_executor.cancel_tasks("Engine commands complete")
|
|
73
69
|
|
|
74
70
|
async def _run_commands(self) -> None:
|
|
75
71
|
async for command_id in self._command_generator():
|
|
@@ -31,11 +31,3 @@ class RunControlHandler:
|
|
|
31
31
|
"""Delay protocol execution for a duration."""
|
|
32
32
|
if not self._state_store.config.ignore_pause:
|
|
33
33
|
await asyncio.sleep(seconds)
|
|
34
|
-
|
|
35
|
-
async def wait_for_tasks(self, tasks: list[str]) -> None:
|
|
36
|
-
"""Wait for concurrent tasks to complete."""
|
|
37
|
-
await self._state_store.wait_for(
|
|
38
|
-
condition=lambda: self._state_store.tasks.all_tasks_finished_or_any_task_failed(
|
|
39
|
-
task_ids=tasks
|
|
40
|
-
)
|
|
41
|
-
)
|
|
@@ -59,6 +59,7 @@ from .actions import (
|
|
|
59
59
|
AddAddressableAreaAction,
|
|
60
60
|
AddModuleAction,
|
|
61
61
|
HardwareStoppedAction,
|
|
62
|
+
ResetTipsAction,
|
|
62
63
|
SetPipetteMovementSpeedAction,
|
|
63
64
|
)
|
|
64
65
|
|
|
@@ -321,10 +322,24 @@ class ProtocolEngine:
|
|
|
321
322
|
)
|
|
322
323
|
return completed_command
|
|
323
324
|
|
|
324
|
-
def
|
|
325
|
+
def estop(self) -> None:
|
|
326
|
+
"""Signal to the engine that an E-stop event occurred.
|
|
327
|
+
|
|
328
|
+
If an estop happens while the robot is moving, lower layers physically stop
|
|
329
|
+
motion and raise the event as an exception, which fails the Protocol Engine
|
|
330
|
+
command. No action from the `ProtocolEngine` caller is needed to handle that.
|
|
331
|
+
|
|
332
|
+
However, if an estop happens in between commands, or in the middle of
|
|
333
|
+
a command like `comment` or `waitForDuration` that doesn't access the hardware,
|
|
334
|
+
`ProtocolEngine` needs to be told about it so it can interrupt the command
|
|
335
|
+
and stop executing any more. This method is how to do that.
|
|
336
|
+
|
|
337
|
+
This acts roughly like `request_stop()`. After calling this, you should call
|
|
338
|
+
`finish()` with an EStopActivatedError.
|
|
339
|
+
"""
|
|
325
340
|
try:
|
|
326
341
|
action = self._state_store.commands.validate_action_allowed(
|
|
327
|
-
StopAction(
|
|
342
|
+
StopAction(from_estop=True)
|
|
328
343
|
)
|
|
329
344
|
except Exception: # todo(mm, 2024-04-16): Catch a more specific type.
|
|
330
345
|
# This is likely called from some hardware API callback that doesn't care
|
|
@@ -343,79 +358,9 @@ class ProtocolEngine:
|
|
|
343
358
|
# do this because we want to make sure non-hardware commands, like
|
|
344
359
|
# `waitForDuration`, are also interrupted.
|
|
345
360
|
self._get_queue_worker.cancel()
|
|
346
|
-
|
|
347
|
-
def estop(self) -> None:
|
|
348
|
-
"""Signal to the engine that an E-stop event occurred.
|
|
349
|
-
|
|
350
|
-
If an estop happens while the robot is moving, lower layers physically stop
|
|
351
|
-
motion and raise the event as an exception, which fails the Protocol Engine
|
|
352
|
-
command. No action from the `ProtocolEngine` caller is needed to handle that.
|
|
353
|
-
|
|
354
|
-
However, if an estop happens in between commands, or in the middle of
|
|
355
|
-
a command like `comment` or `waitForDuration` that doesn't access the hardware,
|
|
356
|
-
`ProtocolEngine` needs to be told about it so it can interrupt the command
|
|
357
|
-
and stop executing any more. This method is how to do that.
|
|
358
|
-
|
|
359
|
-
This acts roughly like `request_stop()`. After calling this, you should call
|
|
360
|
-
`finish()` with an EStopActivatedError.
|
|
361
|
-
"""
|
|
362
361
|
# Unlike self.request_stop(), we don't need to do
|
|
363
362
|
# self._hardware_api.cancel_execution_and_running_tasks(). Since this was an
|
|
364
363
|
# E-stop event, the hardware API already knows.
|
|
365
|
-
self._stop_from_asynchronous_error()
|
|
366
|
-
|
|
367
|
-
async def async_module_error(
|
|
368
|
-
self, module_model: ModuleModel, serial: str | None
|
|
369
|
-
) -> bool:
|
|
370
|
-
"""Signal to the engine that an asynchronous module error occured.
|
|
371
|
-
|
|
372
|
-
The return value of this function signals whether the error is relevant to the protocol
|
|
373
|
-
or not. If the function returns True, the error is relevant. The engine will stop, and
|
|
374
|
-
the caller should call `finish()` with the error object that signaled the error. If
|
|
375
|
-
the function returns False, the error is not relevant. The engine will not stop, and the
|
|
376
|
-
caller should not call `finish()`.
|
|
377
|
-
|
|
378
|
-
Asynchronous module errors are signaled when a module enters a hardware error state
|
|
379
|
-
- for instance, a thermocycler's thermistors fail because of condensation, or a
|
|
380
|
-
heater-shaker's wires fray and snap, or a module is accidentally disconnected. These
|
|
381
|
-
errors are not related to a particular command, even a currently-happening module
|
|
382
|
-
control command for the module in the error state.
|
|
383
|
-
|
|
384
|
-
Similar to an estop error, the error can occur at any time relative to the lifecycle
|
|
385
|
-
of the engine run or of any particular command.
|
|
386
|
-
|
|
387
|
-
Unlike an estop, the motion control hardware will not be raising an error and will not
|
|
388
|
-
stop on its own; the stop action derived from this call will do that.
|
|
389
|
-
"""
|
|
390
|
-
if not self._state_store.modules.get_has_module_probably_matching_hardware_details(
|
|
391
|
-
module_model, serial
|
|
392
|
-
):
|
|
393
|
-
return False
|
|
394
|
-
|
|
395
|
-
if self._state_store.commands.get_is_terminal():
|
|
396
|
-
# Do not stop multiple times; it will be common for this action to fire
|
|
397
|
-
# many times when a module enters an error state, and we don't want to do
|
|
398
|
-
# the stop behavior over and over
|
|
399
|
-
return False
|
|
400
|
-
|
|
401
|
-
self._stop_from_asynchronous_error()
|
|
402
|
-
# like self.request_stop, and unlike self.estop(), we must explicitly request that the
|
|
403
|
-
# hardware stops execution, since not all asynchronous errors will cause the hardware
|
|
404
|
-
# to know that it should stop.
|
|
405
|
-
await self._do_hardware_stop()
|
|
406
|
-
return True
|
|
407
|
-
|
|
408
|
-
async def _do_hardware_stop(self) -> None:
|
|
409
|
-
"""Make the hardware stop now."""
|
|
410
|
-
if self._hardware_api.is_movement_execution_taskified():
|
|
411
|
-
# We 'taskify' hardware controller movement functions when running protocols
|
|
412
|
-
# that are not backed by the engine. Such runs cannot be stopped by cancelling
|
|
413
|
-
# the queue worker and hence need to be stopped via the execution manager.
|
|
414
|
-
# `cancel_execution_and_running_tasks()` sets the execution manager in a CANCELLED state
|
|
415
|
-
# and cancels the running tasks, which raises an error and gets us out of the
|
|
416
|
-
# run function execution, just like `_queue_worker.cancel()` does for
|
|
417
|
-
# engine-backed runs.
|
|
418
|
-
await self._hardware_api.cancel_execution_and_running_tasks()
|
|
419
364
|
|
|
420
365
|
async def request_stop(self) -> None:
|
|
421
366
|
"""Make command execution stop soon.
|
|
@@ -433,7 +378,15 @@ class ProtocolEngine:
|
|
|
433
378
|
action = self._state_store.commands.validate_action_allowed(StopAction())
|
|
434
379
|
self._action_dispatcher.dispatch(action)
|
|
435
380
|
self._get_queue_worker.cancel()
|
|
436
|
-
|
|
381
|
+
if self._hardware_api.is_movement_execution_taskified():
|
|
382
|
+
# We 'taskify' hardware controller movement functions when running protocols
|
|
383
|
+
# that are not backed by the engine. Such runs cannot be stopped by cancelling
|
|
384
|
+
# the queue worker and hence need to be stopped via the execution manager.
|
|
385
|
+
# `cancel_execution_and_running_tasks()` sets the execution manager in a CANCELLED state
|
|
386
|
+
# and cancels the running tasks, which raises an error and gets us out of the
|
|
387
|
+
# run function execution, just like `_queue_worker.cancel()` does for
|
|
388
|
+
# engine-backed runs.
|
|
389
|
+
await self._hardware_api.cancel_execution_and_running_tasks()
|
|
437
390
|
|
|
438
391
|
async def wait_until_complete(self) -> None:
|
|
439
392
|
"""Wait until there are no more commands to execute.
|
|
@@ -476,13 +429,13 @@ class ProtocolEngine:
|
|
|
476
429
|
post_run_hardware_state: The state in which to leave the gantry and motors in
|
|
477
430
|
after the run is over.
|
|
478
431
|
"""
|
|
479
|
-
if self._state_store.commands.
|
|
432
|
+
if self._state_store.commands.get_is_stopped_by_estop():
|
|
480
433
|
# This handles the case where the E-stop was pressed while we were *not* in the middle
|
|
481
434
|
# of some hardware interaction that would raise it as an exception. For example, imagine
|
|
482
435
|
# we were paused between two commands, or imagine we were executing a waitForDuration.
|
|
483
436
|
drop_tips_after_run = False
|
|
484
437
|
post_run_hardware_state = PostRunHardwareState.DISENGAGE_IN_PLACE
|
|
485
|
-
if error is None
|
|
438
|
+
if error is None:
|
|
486
439
|
error = EStopActivatedError()
|
|
487
440
|
|
|
488
441
|
if error:
|
|
@@ -633,6 +586,12 @@ class ProtocolEngine:
|
|
|
633
586
|
AddAddressableAreaAction(addressable_area_name)
|
|
634
587
|
)
|
|
635
588
|
|
|
589
|
+
def reset_tips(self, labware_id: str) -> None:
|
|
590
|
+
"""Reset the tip state of a given labware."""
|
|
591
|
+
# TODO(mm, 2023-03-10): Safely raise an error if the given labware isn't a
|
|
592
|
+
# tip rack?
|
|
593
|
+
self._action_dispatcher.dispatch(ResetTipsAction(labware_id=labware_id))
|
|
594
|
+
|
|
636
595
|
# TODO(mm, 2022-11-10): This is a method on ProtocolEngine instead of a command
|
|
637
596
|
# as a quick hack to support Python protocols. We should consider making this a
|
|
638
597
|
# command, or adding speed parameters to existing commands.
|
|
@@ -11,7 +11,6 @@ from .labware_data_provider import LabwareDataProvider
|
|
|
11
11
|
from .module_data_provider import ModuleDataProvider
|
|
12
12
|
from .file_provider import FileProvider
|
|
13
13
|
from .ot3_validation import ensure_ot3_hardware
|
|
14
|
-
from .concurrency_provider import ConcurrencyProvider
|
|
15
14
|
|
|
16
15
|
|
|
17
16
|
__all__ = [
|
|
@@ -19,7 +18,6 @@ __all__ = [
|
|
|
19
18
|
"LabwareDataProvider",
|
|
20
19
|
"DeckDataProvider",
|
|
21
20
|
"DeckFixedLabware",
|
|
22
|
-
"ConcurrencyProvider",
|
|
23
21
|
"ModuleDataProvider",
|
|
24
22
|
"FileProvider",
|
|
25
23
|
"ensure_ot3_hardware",
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
from typing import List, Set, Tuple
|
|
4
4
|
|
|
5
|
-
from opentrons_shared_data.module.types import ModuleOrientation
|
|
6
5
|
from opentrons_shared_data.deck.types import (
|
|
7
6
|
DeckDefinitionV5,
|
|
8
7
|
CutoutFixture,
|
|
@@ -125,11 +124,6 @@ def get_addressable_area_from_name(
|
|
|
125
124
|
z=addressable_area["boundingBox"]["zDimension"],
|
|
126
125
|
)
|
|
127
126
|
features = addressable_area["features"]
|
|
128
|
-
orientation = (
|
|
129
|
-
addressable_area["orientation"]
|
|
130
|
-
if addressable_area["orientation"]
|
|
131
|
-
else ModuleOrientation.NOT_APPLICABLE
|
|
132
|
-
)
|
|
133
127
|
mating_surface_unit_vector = addressable_area.get("matingSurfaceUnitVector")
|
|
134
128
|
|
|
135
129
|
return AddressableArea(
|
|
@@ -144,7 +138,6 @@ def get_addressable_area_from_name(
|
|
|
144
138
|
"compatibleModuleTypes", []
|
|
145
139
|
),
|
|
146
140
|
features=features,
|
|
147
|
-
orientation=orientation,
|
|
148
141
|
)
|
|
149
142
|
raise AddressableAreaDoesNotExistError(
|
|
150
143
|
f"Could not find addressable area with name {addressable_area_name}"
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
from opentrons_shared_data.labware.labware_definition import (
|
|
4
4
|
LabwareDefinition,
|
|
5
|
-
LabwareDefinition2,
|
|
6
5
|
LabwareRole,
|
|
7
6
|
)
|
|
8
7
|
|
|
@@ -45,18 +44,15 @@ def validate_definition_is_system(definition: LabwareDefinition) -> bool:
|
|
|
45
44
|
return LabwareRole.system in definition.allowedRoles
|
|
46
45
|
|
|
47
46
|
|
|
48
|
-
def
|
|
49
|
-
|
|
47
|
+
def validate_labware_can_be_stacked(
|
|
48
|
+
top_labware_definition: LabwareDefinition, below_labware_load_name: str
|
|
50
49
|
) -> bool:
|
|
51
|
-
"""Validate that the
|
|
52
|
-
|
|
53
|
-
Schema 3 Labware stacking validation is handled in locating features.
|
|
54
|
-
"""
|
|
50
|
+
"""Validate that the labware being loaded onto is in the above labware's stackingOffsetWithLabware definition."""
|
|
55
51
|
return (
|
|
56
|
-
|
|
52
|
+
below_labware_load_name in top_labware_definition.stackingOffsetWithLabware
|
|
57
53
|
or (
|
|
58
|
-
"default" in
|
|
59
|
-
and
|
|
54
|
+
"default" in top_labware_definition.stackingOffsetWithLabware
|
|
55
|
+
and top_labware_definition.compatibleParentLabware is None
|
|
60
56
|
)
|
|
61
57
|
)
|
|
62
58
|
|