opentrons 8.7.0a9__py3-none-any.whl → 8.8.0a8__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- opentrons/_version.py +2 -2
- opentrons/cli/analyze.py +4 -1
- opentrons/config/__init__.py +7 -0
- opentrons/drivers/asyncio/communication/serial_connection.py +126 -49
- opentrons/drivers/heater_shaker/abstract.py +5 -0
- opentrons/drivers/heater_shaker/driver.py +10 -0
- opentrons/drivers/heater_shaker/simulator.py +4 -0
- opentrons/drivers/thermocycler/abstract.py +6 -0
- opentrons/drivers/thermocycler/driver.py +61 -10
- opentrons/drivers/thermocycler/simulator.py +6 -0
- opentrons/drivers/vacuum_module/__init__.py +5 -0
- opentrons/drivers/vacuum_module/abstract.py +93 -0
- opentrons/drivers/vacuum_module/driver.py +208 -0
- opentrons/drivers/vacuum_module/errors.py +39 -0
- opentrons/drivers/vacuum_module/simulator.py +85 -0
- opentrons/drivers/vacuum_module/types.py +79 -0
- opentrons/execute.py +3 -0
- opentrons/hardware_control/api.py +24 -5
- opentrons/hardware_control/backends/controller.py +8 -2
- opentrons/hardware_control/backends/flex_protocol.py +1 -0
- opentrons/hardware_control/backends/ot3controller.py +35 -2
- opentrons/hardware_control/backends/ot3simulator.py +3 -1
- opentrons/hardware_control/backends/ot3utils.py +37 -0
- opentrons/hardware_control/backends/simulator.py +2 -1
- opentrons/hardware_control/backends/subsystem_manager.py +5 -2
- opentrons/hardware_control/emulation/abstract_emulator.py +6 -4
- opentrons/hardware_control/emulation/connection_handler.py +8 -5
- opentrons/hardware_control/emulation/heater_shaker.py +12 -3
- opentrons/hardware_control/emulation/settings.py +1 -1
- opentrons/hardware_control/emulation/thermocycler.py +67 -15
- opentrons/hardware_control/module_control.py +105 -10
- 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 +42 -5
- opentrons/hardware_control/modules/magdeck.py +8 -4
- opentrons/hardware_control/modules/mod_abc.py +14 -6
- opentrons/hardware_control/modules/tempdeck.py +25 -5
- opentrons/hardware_control/modules/thermocycler.py +68 -11
- opentrons/hardware_control/modules/types.py +20 -1
- opentrons/hardware_control/modules/utils.py +11 -4
- opentrons/hardware_control/motion_utilities.py +6 -6
- opentrons/hardware_control/nozzle_manager.py +3 -0
- opentrons/hardware_control/ot3api.py +92 -17
- opentrons/hardware_control/poller.py +22 -8
- opentrons/hardware_control/protocols/liquid_handler.py +12 -4
- opentrons/hardware_control/scripts/update_module_fw.py +5 -0
- opentrons/hardware_control/types.py +43 -2
- opentrons/legacy_commands/commands.py +58 -5
- opentrons/legacy_commands/module_commands.py +52 -0
- opentrons/legacy_commands/protocol_commands.py +53 -1
- opentrons/legacy_commands/types.py +155 -1
- opentrons/motion_planning/deck_conflict.py +17 -12
- opentrons/motion_planning/waypoints.py +15 -29
- opentrons/protocol_api/__init__.py +5 -1
- opentrons/protocol_api/_transfer_liquid_validation.py +17 -2
- opentrons/protocol_api/_types.py +8 -1
- opentrons/protocol_api/core/common.py +3 -1
- opentrons/protocol_api/core/engine/_default_labware_versions.py +33 -11
- opentrons/protocol_api/core/engine/deck_conflict.py +3 -1
- opentrons/protocol_api/core/engine/instrument.py +109 -26
- opentrons/protocol_api/core/engine/labware.py +8 -1
- opentrons/protocol_api/core/engine/module_core.py +95 -4
- opentrons/protocol_api/core/engine/pipette_movement_conflict.py +4 -18
- opentrons/protocol_api/core/engine/protocol.py +51 -2
- opentrons/protocol_api/core/engine/stringify.py +2 -0
- opentrons/protocol_api/core/engine/tasks.py +48 -0
- opentrons/protocol_api/core/engine/well.py +8 -0
- opentrons/protocol_api/core/instrument.py +19 -2
- opentrons/protocol_api/core/legacy/legacy_instrument_core.py +19 -2
- opentrons/protocol_api/core/legacy/legacy_module_core.py +33 -2
- opentrons/protocol_api/core/legacy/legacy_protocol_core.py +23 -1
- opentrons/protocol_api/core/legacy/legacy_well_core.py +4 -0
- opentrons/protocol_api/core/legacy/tasks.py +19 -0
- opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +19 -2
- 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 +58 -2
- opentrons/protocol_api/core/protocol.py +23 -2
- opentrons/protocol_api/core/tasks.py +31 -0
- opentrons/protocol_api/core/well.py +4 -0
- opentrons/protocol_api/instrument_context.py +388 -2
- opentrons/protocol_api/labware.py +10 -2
- opentrons/protocol_api/module_contexts.py +170 -6
- opentrons/protocol_api/protocol_context.py +87 -21
- opentrons/protocol_api/robot_context.py +41 -25
- opentrons/protocol_api/tasks.py +48 -0
- opentrons/protocol_api/validation.py +49 -3
- opentrons/protocol_engine/__init__.py +4 -0
- opentrons/protocol_engine/actions/__init__.py +6 -2
- opentrons/protocol_engine/actions/actions.py +31 -9
- opentrons/protocol_engine/clients/sync_client.py +42 -7
- opentrons/protocol_engine/commands/__init__.py +56 -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/absorbance_reader/read.py +22 -23
- opentrons/protocol_engine/commands/aspirate.py +1 -0
- opentrons/protocol_engine/commands/aspirate_while_tracking.py +52 -19
- opentrons/protocol_engine/commands/capture_image.py +302 -0
- opentrons/protocol_engine/commands/command.py +2 -0
- opentrons/protocol_engine/commands/command_unions.py +62 -0
- opentrons/protocol_engine/commands/create_timer.py +83 -0
- opentrons/protocol_engine/commands/dispense.py +1 -0
- opentrons/protocol_engine/commands/dispense_while_tracking.py +56 -19
- opentrons/protocol_engine/commands/drop_tip.py +32 -8
- opentrons/protocol_engine/commands/flex_stacker/common.py +35 -0
- opentrons/protocol_engine/commands/flex_stacker/set_stored_labware.py +7 -0
- opentrons/protocol_engine/commands/heater_shaker/__init__.py +14 -0
- opentrons/protocol_engine/commands/heater_shaker/common.py +20 -0
- opentrons/protocol_engine/commands/heater_shaker/set_and_wait_for_shake_speed.py +5 -4
- opentrons/protocol_engine/commands/heater_shaker/set_shake_speed.py +136 -0
- opentrons/protocol_engine/commands/heater_shaker/set_target_temperature.py +31 -5
- opentrons/protocol_engine/commands/move_labware.py +3 -4
- opentrons/protocol_engine/commands/move_to_addressable_area_for_drop_tip.py +1 -1
- opentrons/protocol_engine/commands/movement_common.py +31 -2
- opentrons/protocol_engine/commands/pick_up_tip.py +21 -11
- opentrons/protocol_engine/commands/pipetting_common.py +48 -3
- opentrons/protocol_engine/commands/set_tip_state.py +97 -0
- opentrons/protocol_engine/commands/temperature_module/set_target_temperature.py +38 -7
- opentrons/protocol_engine/commands/thermocycler/__init__.py +16 -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 +44 -7
- opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py +43 -14
- opentrons/protocol_engine/commands/thermocycler/start_run_extended_profile.py +191 -0
- 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/create_protocol_engine.py +12 -0
- opentrons/protocol_engine/engine_support.py +3 -0
- opentrons/protocol_engine/errors/__init__.py +12 -0
- opentrons/protocol_engine/errors/exceptions.py +119 -0
- opentrons/protocol_engine/execution/__init__.py +4 -0
- opentrons/protocol_engine/execution/command_executor.py +62 -1
- opentrons/protocol_engine/execution/create_queue_worker.py +9 -2
- opentrons/protocol_engine/execution/labware_movement.py +13 -15
- opentrons/protocol_engine/execution/movement.py +2 -0
- opentrons/protocol_engine/execution/pipetting.py +26 -25
- 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 +137 -36
- opentrons/protocol_engine/resources/__init__.py +4 -0
- opentrons/protocol_engine/resources/camera_provider.py +110 -0
- opentrons/protocol_engine/resources/concurrency_provider.py +27 -0
- opentrons/protocol_engine/resources/deck_configuration_provider.py +7 -0
- opentrons/protocol_engine/resources/file_provider.py +133 -58
- opentrons/protocol_engine/resources/labware_validation.py +10 -6
- opentrons/protocol_engine/slot_standardization.py +2 -0
- opentrons/protocol_engine/state/_well_math.py +60 -18
- opentrons/protocol_engine/state/addressable_areas.py +2 -0
- opentrons/protocol_engine/state/camera.py +54 -0
- opentrons/protocol_engine/state/commands.py +37 -14
- opentrons/protocol_engine/state/geometry.py +276 -379
- opentrons/protocol_engine/state/labware.py +62 -108
- opentrons/protocol_engine/state/labware_origin_math/errors.py +94 -0
- opentrons/protocol_engine/state/labware_origin_math/stackup_origin_to_labware_origin.py +1336 -0
- opentrons/protocol_engine/state/module_substates/thermocycler_module_substate.py +37 -0
- opentrons/protocol_engine/state/modules.py +30 -8
- opentrons/protocol_engine/state/motion.py +60 -18
- opentrons/protocol_engine/state/preconditions.py +59 -0
- opentrons/protocol_engine/state/state.py +44 -0
- opentrons/protocol_engine/state/state_summary.py +4 -0
- opentrons/protocol_engine/state/tasks.py +139 -0
- opentrons/protocol_engine/state/tips.py +177 -258
- opentrons/protocol_engine/state/update_types.py +26 -9
- opentrons/protocol_engine/types/__init__.py +23 -4
- opentrons/protocol_engine/types/command_preconditions.py +18 -0
- 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/location.py +26 -2
- opentrons/protocol_engine/types/module.py +11 -1
- 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/protocol_runner.py +14 -1
- opentrons/protocol_runner/run_orchestrator.py +49 -2
- opentrons/protocols/advanced_control/transfers/transfer_liquid_utils.py +2 -2
- opentrons/protocols/api_support/definitions.py +1 -1
- opentrons/protocols/api_support/types.py +2 -1
- opentrons/simulate.py +51 -15
- opentrons/system/camera.py +334 -4
- opentrons/system/ffmpeg.py +110 -0
- {opentrons-8.7.0a9.dist-info → opentrons-8.8.0a8.dist-info}/METADATA +4 -4
- {opentrons-8.7.0a9.dist-info → opentrons-8.8.0a8.dist-info}/RECORD +189 -161
- opentrons/protocol_engine/state/_labware_origin_math.py +0 -636
- {opentrons-8.7.0a9.dist-info → opentrons-8.8.0a8.dist-info}/WHEEL +0 -0
- {opentrons-8.7.0a9.dist-info → opentrons-8.8.0a8.dist-info}/entry_points.txt +0 -0
- {opentrons-8.7.0a9.dist-info → opentrons-8.8.0a8.dist-info}/licenses/LICENSE +0 -0
|
@@ -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()
|
|
@@ -99,11 +99,18 @@ class InvalidFixtureLocationError(ValueError):
|
|
|
99
99
|
"""An error raised when attempting to load a fixture in an invalid cutout."""
|
|
100
100
|
|
|
101
101
|
|
|
102
|
+
def is_pipette_96_channel(pipette: Optional[PipetteNameType]) -> bool:
|
|
103
|
+
"""Return if this pipette type is a 96 channel."""
|
|
104
|
+
if pipette is not None:
|
|
105
|
+
return pipette in [PipetteNameType.P1000_96, PipetteNameType.P200_96]
|
|
106
|
+
return False
|
|
107
|
+
|
|
108
|
+
|
|
102
109
|
def ensure_mount_for_pipette(
|
|
103
110
|
mount: Union[str, Mount, None], pipette: PipetteNameType
|
|
104
111
|
) -> Mount:
|
|
105
112
|
"""Ensure that an input value represents a valid mount, and is valid for the given pipette."""
|
|
106
|
-
if pipette
|
|
113
|
+
if is_pipette_96_channel(pipette):
|
|
107
114
|
# Always validate the raw mount input, even if the pipette is a 96-channel and we're not going
|
|
108
115
|
# to use the mount value.
|
|
109
116
|
if mount is not None:
|
|
@@ -370,7 +377,7 @@ def ensure_definition_is_not_lid_after_api_version(
|
|
|
370
377
|
and api_version >= LID_STACK_VERSION_GATE
|
|
371
378
|
):
|
|
372
379
|
raise APIVersionError(
|
|
373
|
-
f"Labware Lids cannot be loaded like standard labware in Protocols written with an API version
|
|
380
|
+
f"Labware Lids cannot be loaded like standard labware in Protocols written with an API version of {LID_STACK_VERSION_GATE} or higher."
|
|
374
381
|
)
|
|
375
382
|
|
|
376
383
|
|
|
@@ -489,6 +496,7 @@ def ensure_thermocycler_profile_steps(
|
|
|
489
496
|
temperature = step.get("temperature")
|
|
490
497
|
hold_mins = step.get("hold_time_minutes")
|
|
491
498
|
hold_secs = step.get("hold_time_seconds")
|
|
499
|
+
ramp_rate = step.get("ramp_rate")
|
|
492
500
|
if temperature is None:
|
|
493
501
|
raise ValueError("temperature must be defined for each step in cycle")
|
|
494
502
|
if hold_mins is None and hold_secs is None:
|
|
@@ -496,10 +504,14 @@ def ensure_thermocycler_profile_steps(
|
|
|
496
504
|
"either hold_time_minutes or hold_time_seconds must be"
|
|
497
505
|
"defined for each step in cycle"
|
|
498
506
|
)
|
|
507
|
+
if ramp_rate is not None and ramp_rate <= 0:
|
|
508
|
+
raise ValueError("Ramp rate must be greater than 0.")
|
|
499
509
|
validated_seconds = ensure_hold_time_seconds(hold_secs, hold_mins)
|
|
500
510
|
validated_steps.append(
|
|
501
511
|
ThermocyclerStep(
|
|
502
|
-
temperature=temperature,
|
|
512
|
+
temperature=temperature,
|
|
513
|
+
hold_time_seconds=validated_seconds,
|
|
514
|
+
ramp_rate=ramp_rate,
|
|
503
515
|
)
|
|
504
516
|
)
|
|
505
517
|
return validated_steps
|
|
@@ -565,6 +577,40 @@ class LocationTypeError(TypeError):
|
|
|
565
577
|
ValidTarget = Union[WellTarget, PointTarget, DisposalTarget]
|
|
566
578
|
|
|
567
579
|
|
|
580
|
+
def validate_dynamic_locations(
|
|
581
|
+
location: Optional[Union[Location, Well, TrashBin, WasteChute]],
|
|
582
|
+
end_location: Location,
|
|
583
|
+
) -> None:
|
|
584
|
+
"""Given that we have an end_location we check that they're a vaild dynamic pair."""
|
|
585
|
+
if location is None:
|
|
586
|
+
raise ValueError("Location must be supplied if using an End Location.")
|
|
587
|
+
if not isinstance(location, Location):
|
|
588
|
+
raise ValueError(
|
|
589
|
+
"Location must be a point within a well when dynamic pipetting."
|
|
590
|
+
)
|
|
591
|
+
# Shouldn't be true ever if using typing but a customer protocol may not check
|
|
592
|
+
if not isinstance(end_location, Location):
|
|
593
|
+
raise ValueError(
|
|
594
|
+
"End location must be a point within a well when dynamic pipetting."
|
|
595
|
+
)
|
|
596
|
+
if not location.labware.is_well:
|
|
597
|
+
raise ValueError("Start location must be within a well when dynamic pipetting")
|
|
598
|
+
if not end_location.labware.is_well:
|
|
599
|
+
raise ValueError("End location must be within a well when dynamic pipetting")
|
|
600
|
+
(
|
|
601
|
+
_,
|
|
602
|
+
start_well,
|
|
603
|
+
) = location.labware.get_parent_labware_and_well()
|
|
604
|
+
(
|
|
605
|
+
_,
|
|
606
|
+
end_well,
|
|
607
|
+
) = end_location.labware.get_parent_labware_and_well()
|
|
608
|
+
if start_well != end_well:
|
|
609
|
+
raise ValueError(
|
|
610
|
+
"Start and end locations must be within the same well when dynamic pipetting"
|
|
611
|
+
)
|
|
612
|
+
|
|
613
|
+
|
|
568
614
|
def validate_location(
|
|
569
615
|
location: Optional[Union[Location, Well, TrashBin, WasteChute]],
|
|
570
616
|
last_location: Optional[Union[Location, TrashBin, WasteChute]],
|
|
@@ -41,6 +41,8 @@ from .types import (
|
|
|
41
41
|
DeckType,
|
|
42
42
|
DeckSlotLocation,
|
|
43
43
|
InStackerHopperLocation,
|
|
44
|
+
WASTE_CHUTE_LOCATION,
|
|
45
|
+
AccessibleByGripperLocation,
|
|
44
46
|
ModuleLocation,
|
|
45
47
|
OnLabwareLocation,
|
|
46
48
|
AddressableAreaLocation,
|
|
@@ -120,6 +122,8 @@ __all__ = [
|
|
|
120
122
|
"ModuleLocation",
|
|
121
123
|
"OnLabwareLocation",
|
|
122
124
|
"AddressableAreaLocation",
|
|
125
|
+
"WASTE_CHUTE_LOCATION",
|
|
126
|
+
"AccessibleByGripperLocation",
|
|
123
127
|
"InStackerHopperLocation",
|
|
124
128
|
"OFF_DECK_LOCATION",
|
|
125
129
|
"SYSTEM_LOCATION",
|
|
@@ -21,13 +21,15 @@ from .actions import (
|
|
|
21
21
|
AddLabwareOffsetAction,
|
|
22
22
|
AddLabwareDefinitionAction,
|
|
23
23
|
AddLiquidAction,
|
|
24
|
+
AddCameraSettingsAction,
|
|
24
25
|
SetDeckConfigurationAction,
|
|
25
26
|
AddAddressableAreaAction,
|
|
26
27
|
AddModuleAction,
|
|
27
28
|
FinishErrorDetails,
|
|
28
29
|
DoorChangeAction,
|
|
29
|
-
ResetTipsAction,
|
|
30
30
|
SetPipetteMovementSpeedAction,
|
|
31
|
+
StartTaskAction,
|
|
32
|
+
FinishTaskAction,
|
|
31
33
|
)
|
|
32
34
|
from .get_state_update import get_state_updates
|
|
33
35
|
|
|
@@ -50,13 +52,15 @@ __all__ = [
|
|
|
50
52
|
"FailCommandAction",
|
|
51
53
|
"AddLabwareOffsetAction",
|
|
52
54
|
"AddLabwareDefinitionAction",
|
|
55
|
+
"AddCameraSettingsAction",
|
|
53
56
|
"AddLiquidAction",
|
|
54
57
|
"SetDeckConfigurationAction",
|
|
55
58
|
"AddAddressableAreaAction",
|
|
56
59
|
"AddModuleAction",
|
|
57
60
|
"DoorChangeAction",
|
|
58
|
-
"ResetTipsAction",
|
|
59
61
|
"SetPipetteMovementSpeedAction",
|
|
62
|
+
"StartTaskAction",
|
|
63
|
+
"FinishTaskAction",
|
|
60
64
|
# action payload values
|
|
61
65
|
"PauseSource",
|
|
62
66
|
"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,13 +21,16 @@ 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
|
|
27
|
+
from ..resources.camera_provider import CameraSettings
|
|
25
28
|
from ..types import (
|
|
26
29
|
LabwareOffsetCreateInternal,
|
|
27
30
|
ModuleDefinition,
|
|
28
31
|
Liquid,
|
|
29
32
|
DeckConfigurationType,
|
|
33
|
+
Task,
|
|
30
34
|
)
|
|
31
35
|
|
|
32
36
|
|
|
@@ -60,7 +64,7 @@ class PauseAction:
|
|
|
60
64
|
class StopAction:
|
|
61
65
|
"""Request engine execution to stop soon."""
|
|
62
66
|
|
|
63
|
-
|
|
67
|
+
from_asynchronous_error: bool = False
|
|
64
68
|
|
|
65
69
|
|
|
66
70
|
@dataclasses.dataclass(frozen=True)
|
|
@@ -201,6 +205,22 @@ class FailCommandAction:
|
|
|
201
205
|
"""The command to fail, in its prior `running` state."""
|
|
202
206
|
|
|
203
207
|
|
|
208
|
+
@dataclasses.dataclass(frozen=True)
|
|
209
|
+
class StartTaskAction:
|
|
210
|
+
"""Store new task in state."""
|
|
211
|
+
|
|
212
|
+
task: Task
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
@dataclasses.dataclass(frozen=True)
|
|
216
|
+
class FinishTaskAction:
|
|
217
|
+
"""Mark task as finished in state."""
|
|
218
|
+
|
|
219
|
+
task_id: str
|
|
220
|
+
finished_at: datetime
|
|
221
|
+
error: ErrorOccurrence | None
|
|
222
|
+
|
|
223
|
+
|
|
204
224
|
@dataclasses.dataclass(frozen=True)
|
|
205
225
|
class AddLabwareOffsetAction:
|
|
206
226
|
"""Add a labware offset, to apply to subsequent `LoadLabwareCommand`s."""
|
|
@@ -217,6 +237,13 @@ class AddLabwareDefinitionAction:
|
|
|
217
237
|
definition: LabwareDefinition
|
|
218
238
|
|
|
219
239
|
|
|
240
|
+
@dataclasses.dataclass(frozen=True)
|
|
241
|
+
class AddCameraSettingsAction:
|
|
242
|
+
"""Add Camera settings to be used in place of the Camera Provider accessible settings."""
|
|
243
|
+
|
|
244
|
+
enablement_settings: CameraSettings
|
|
245
|
+
|
|
246
|
+
|
|
220
247
|
@dataclasses.dataclass(frozen=True)
|
|
221
248
|
class AddLiquidAction:
|
|
222
249
|
"""Add a liquid, to apply to subsequent `LoadLiquid`s."""
|
|
@@ -253,13 +280,6 @@ class AddModuleAction:
|
|
|
253
280
|
module_live_data: LiveData
|
|
254
281
|
|
|
255
282
|
|
|
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
283
|
@dataclasses.dataclass(frozen=True)
|
|
264
284
|
class SetPipetteMovementSpeedAction:
|
|
265
285
|
"""Set the speed of a pipette's X/Y/Z movements. Does not affect plunger speed.
|
|
@@ -293,10 +313,12 @@ Action = Union[
|
|
|
293
313
|
AddLabwareOffsetAction,
|
|
294
314
|
AddLabwareDefinitionAction,
|
|
295
315
|
AddModuleAction,
|
|
316
|
+
AddCameraSettingsAction,
|
|
296
317
|
SetDeckConfigurationAction,
|
|
297
318
|
AddAddressableAreaAction,
|
|
298
319
|
AddLiquidAction,
|
|
299
|
-
ResetTipsAction,
|
|
300
320
|
SetPipetteMovementSpeedAction,
|
|
301
321
|
SetErrorRecoveryPolicyAction,
|
|
322
|
+
StartTaskAction,
|
|
323
|
+
FinishTaskAction,
|
|
302
324
|
]
|
|
@@ -65,6 +65,48 @@ 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
|
+
|
|
74
|
+
@overload
|
|
75
|
+
def execute_command_without_recovery(
|
|
76
|
+
self, params: commands.temperature_module.SetTargetTemperatureParams
|
|
77
|
+
) -> commands.temperature_module.SetTargetTemperatureResult:
|
|
78
|
+
pass
|
|
79
|
+
|
|
80
|
+
@overload
|
|
81
|
+
def execute_command_without_recovery(
|
|
82
|
+
self, params: commands.thermocycler.StartRunExtendedProfileParams
|
|
83
|
+
) -> commands.thermocycler.StartRunExtendedProfileResult:
|
|
84
|
+
pass
|
|
85
|
+
|
|
86
|
+
@overload
|
|
87
|
+
def execute_command_without_recovery(
|
|
88
|
+
self, params: commands.heater_shaker.SetTargetTemperatureParams
|
|
89
|
+
) -> commands.heater_shaker.SetTargetTemperatureResult:
|
|
90
|
+
pass
|
|
91
|
+
|
|
92
|
+
@overload
|
|
93
|
+
def execute_command_without_recovery(
|
|
94
|
+
self, params: commands.heater_shaker.SetShakeSpeedParams
|
|
95
|
+
) -> commands.heater_shaker.SetShakeSpeedResult:
|
|
96
|
+
pass
|
|
97
|
+
|
|
98
|
+
@overload
|
|
99
|
+
def execute_command_without_recovery(
|
|
100
|
+
self, params: commands.thermocycler.SetTargetBlockTemperatureParams
|
|
101
|
+
) -> commands.thermocycler.SetTargetBlockTemperatureResult:
|
|
102
|
+
pass
|
|
103
|
+
|
|
104
|
+
@overload
|
|
105
|
+
def execute_command_without_recovery(
|
|
106
|
+
self, params: commands.thermocycler.SetTargetLidTemperatureParams
|
|
107
|
+
) -> commands.thermocycler.SetTargetLidTemperatureResult:
|
|
108
|
+
pass
|
|
109
|
+
|
|
68
110
|
@overload
|
|
69
111
|
def execute_command_without_recovery(
|
|
70
112
|
self, params: commands.LoadModuleParams
|
|
@@ -149,13 +191,6 @@ class SyncClient:
|
|
|
149
191
|
"""Add a liquid to the engine."""
|
|
150
192
|
return self._transport.call_method("add_liquid", name=name, color=color, description=description) # type: ignore[no-any-return]
|
|
151
193
|
|
|
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
194
|
def add_labware_offset(self, request: LabwareOffsetCreate) -> None:
|
|
160
195
|
"""Add a labware offset."""
|
|
161
196
|
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,
|
|
@@ -427,6 +451,14 @@ from .identify_module import (
|
|
|
427
451
|
IdentifyModuleCommandType,
|
|
428
452
|
)
|
|
429
453
|
|
|
454
|
+
from .capture_image import (
|
|
455
|
+
CaptureImage,
|
|
456
|
+
CaptureImageParams,
|
|
457
|
+
CaptureImageCreate,
|
|
458
|
+
CaptureImageResult,
|
|
459
|
+
CaptureImageCommandType,
|
|
460
|
+
)
|
|
461
|
+
|
|
430
462
|
__all__ = [
|
|
431
463
|
# command type unions
|
|
432
464
|
"Command",
|
|
@@ -619,6 +651,12 @@ __all__ = [
|
|
|
619
651
|
"WaitForDurationCreate",
|
|
620
652
|
"WaitForDurationResult",
|
|
621
653
|
"WaitForDurationCommandType",
|
|
654
|
+
# Timer command models
|
|
655
|
+
"CreateTimer",
|
|
656
|
+
"CreateTimerCreate",
|
|
657
|
+
"CreateTimerParams",
|
|
658
|
+
"CreateTimerResult",
|
|
659
|
+
"CreateTimerCommandType",
|
|
622
660
|
# pick up tip command models
|
|
623
661
|
"PickUpTip",
|
|
624
662
|
"PickUpTipCreate",
|
|
@@ -725,6 +763,12 @@ __all__ = [
|
|
|
725
763
|
"GetNextTipParams",
|
|
726
764
|
"GetNextTipResult",
|
|
727
765
|
"GetNextTipCommandType",
|
|
766
|
+
# set tip state command bundle
|
|
767
|
+
"SetTipState",
|
|
768
|
+
"SetTipStateCreate",
|
|
769
|
+
"SetTipStateParams",
|
|
770
|
+
"SetTipStateResult",
|
|
771
|
+
"SetTipStateCommandType",
|
|
728
772
|
# liquid probe command bundle
|
|
729
773
|
"LiquidProbe",
|
|
730
774
|
"LiquidProbeParams",
|
|
@@ -754,4 +798,16 @@ __all__ = [
|
|
|
754
798
|
"PressureDispenseCreate",
|
|
755
799
|
"PressureDispenseResult",
|
|
756
800
|
"PressureDispenseCommandType",
|
|
801
|
+
# wait for tasks command bundle
|
|
802
|
+
"WaitForTasks",
|
|
803
|
+
"WaitForTasksCreate",
|
|
804
|
+
"WaitForTasksParams",
|
|
805
|
+
"WaitForTasksResult",
|
|
806
|
+
"WaitForTasksCommandType",
|
|
807
|
+
# capture image command bundle
|
|
808
|
+
"CaptureImage",
|
|
809
|
+
"CaptureImageCreate",
|
|
810
|
+
"CaptureImageParams",
|
|
811
|
+
"CaptureImageResult",
|
|
812
|
+
"CaptureImageCommandType",
|
|
757
813
|
]
|
|
@@ -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
|
)
|
|
@@ -7,14 +7,15 @@ from typing_extensions import Literal, Type
|
|
|
7
7
|
from pydantic import BaseModel, Field
|
|
8
8
|
from pydantic.json_schema import SkipJsonSchema
|
|
9
9
|
|
|
10
|
+
from opentrons_shared_data.data_files import MimeType
|
|
10
11
|
from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
|
|
11
|
-
from ...errors import CannotPerformModuleAction
|
|
12
|
+
from ...errors import CannotPerformModuleAction
|
|
12
13
|
from ...errors.error_occurrence import ErrorOccurrence
|
|
13
14
|
|
|
14
15
|
from ...resources.file_provider import (
|
|
15
16
|
PlateReaderData,
|
|
16
17
|
ReadData,
|
|
17
|
-
|
|
18
|
+
ReadCmdFileNameMetadata,
|
|
18
19
|
)
|
|
19
20
|
from ...resources import FileProvider
|
|
20
21
|
from ...state import update_types
|
|
@@ -93,21 +94,6 @@ class ReadAbsorbanceImpl(
|
|
|
93
94
|
"Absorbance Plate Reader can't read a plate with the lid open. Call `close_lid()` first."
|
|
94
95
|
)
|
|
95
96
|
|
|
96
|
-
# TODO: we need to return a file ID and increase the file count even when a moduel is not attached
|
|
97
|
-
if (
|
|
98
|
-
params.fileName is not None
|
|
99
|
-
and abs_reader_substate.configured_wavelengths is not None
|
|
100
|
-
):
|
|
101
|
-
# Validate that the amount of files we are about to generate does not put us higher than the limit
|
|
102
|
-
if (
|
|
103
|
-
self._state_view.files.get_filecount()
|
|
104
|
-
+ len(abs_reader_substate.configured_wavelengths)
|
|
105
|
-
> MAXIMUM_CSV_FILE_LIMIT
|
|
106
|
-
):
|
|
107
|
-
raise StorageLimitReachedError(
|
|
108
|
-
message=f"Attempt to write file {params.fileName} exceeds file creation limit of {MAXIMUM_CSV_FILE_LIMIT} files."
|
|
109
|
-
)
|
|
110
|
-
|
|
111
97
|
asbsorbance_result: Dict[int, Dict[str, float]] = {}
|
|
112
98
|
transform_results = []
|
|
113
99
|
# Handle the measurement and begin building data for return
|
|
@@ -172,15 +158,28 @@ class ReadAbsorbanceImpl(
|
|
|
172
158
|
)
|
|
173
159
|
|
|
174
160
|
if isinstance(plate_read_result, PlateReaderData):
|
|
161
|
+
this_cmd_id = self._state_view.commands.get_running_command_id()
|
|
162
|
+
prev_cmd = (
|
|
163
|
+
self._state_view.commands.get_most_recently_finalized_command()
|
|
164
|
+
)
|
|
165
|
+
prev_cmd_id = prev_cmd.command.id if prev_cmd is not None else None
|
|
166
|
+
|
|
175
167
|
# Write a CSV file for each of the measurements taken
|
|
176
168
|
for measurement in plate_read_result.read_results:
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
169
|
+
csv_bytes = plate_read_result.build_csv_bytes(
|
|
170
|
+
measurement=measurement,
|
|
171
|
+
)
|
|
172
|
+
file_info = await self._file_provider.write_file(
|
|
173
|
+
data=csv_bytes,
|
|
174
|
+
mime_type=MimeType.TEXT_CSV,
|
|
175
|
+
command_metadata=ReadCmdFileNameMetadata(
|
|
176
|
+
base_filename=params.fileName,
|
|
177
|
+
wavelength=measurement.wavelength,
|
|
178
|
+
command_id=this_cmd_id or "",
|
|
179
|
+
prev_command_id=prev_cmd_id or "",
|
|
180
|
+
),
|
|
182
181
|
)
|
|
183
|
-
file_ids.append(
|
|
182
|
+
file_ids.append(file_info.id)
|
|
184
183
|
|
|
185
184
|
state_update.files_added = update_types.FilesAddedUpdate(
|
|
186
185
|
file_ids=file_ids
|
|
@@ -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):
|