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
|
@@ -1,26 +1,35 @@
|
|
|
1
|
-
"""Command models
|
|
1
|
+
"""Command models for heating a Thermocycler's lid."""
|
|
2
2
|
from __future__ import annotations
|
|
3
|
-
from typing import Optional, TYPE_CHECKING
|
|
3
|
+
from typing import Optional, TYPE_CHECKING, Any
|
|
4
4
|
from typing_extensions import Literal, Type
|
|
5
5
|
|
|
6
6
|
from pydantic import BaseModel, Field
|
|
7
|
+
from pydantic.json_schema import SkipJsonSchema
|
|
7
8
|
|
|
8
9
|
from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
|
|
9
10
|
from ...errors.error_occurrence import ErrorOccurrence
|
|
10
11
|
|
|
11
12
|
if TYPE_CHECKING:
|
|
12
13
|
from opentrons.protocol_engine.state.state import StateView
|
|
13
|
-
from opentrons.protocol_engine.execution import EquipmentHandler
|
|
14
|
-
|
|
14
|
+
from opentrons.protocol_engine.execution import EquipmentHandler, TaskHandler
|
|
15
15
|
|
|
16
16
|
SetTargetLidTemperatureCommandType = Literal["thermocycler/setTargetLidTemperature"]
|
|
17
17
|
|
|
18
18
|
|
|
19
|
+
def _remove_default(s: dict[str, Any]) -> None:
|
|
20
|
+
s.pop("default", None)
|
|
21
|
+
|
|
22
|
+
|
|
19
23
|
class SetTargetLidTemperatureParams(BaseModel):
|
|
20
|
-
"""Input parameters to set a Thermocycler's target lid temperature."""
|
|
24
|
+
"""Input parameters to to set a Thermocycler's target lid temperature."""
|
|
21
25
|
|
|
22
26
|
moduleId: str = Field(..., description="Unique ID of the Thermocycler Module.")
|
|
23
27
|
celsius: float = Field(..., description="Target temperature in °C.")
|
|
28
|
+
taskId: str | SkipJsonSchema[None] = Field(
|
|
29
|
+
None,
|
|
30
|
+
description="Id for the background task that manages the temperature.",
|
|
31
|
+
json_schema_extra=_remove_default,
|
|
32
|
+
)
|
|
24
33
|
|
|
25
34
|
|
|
26
35
|
class SetTargetLidTemperatureResult(BaseModel):
|
|
@@ -30,29 +39,37 @@ class SetTargetLidTemperatureResult(BaseModel):
|
|
|
30
39
|
...,
|
|
31
40
|
description="The target lid temperature that was set after validation.",
|
|
32
41
|
)
|
|
42
|
+
taskId: str | SkipJsonSchema[None] = Field(
|
|
43
|
+
None,
|
|
44
|
+
description="The task id for the setTargetBlockTemperature",
|
|
45
|
+
json_schema_extra=_remove_default,
|
|
46
|
+
)
|
|
33
47
|
|
|
34
48
|
|
|
35
49
|
class SetTargetLidTemperatureImpl(
|
|
36
50
|
AbstractCommandImpl[
|
|
37
|
-
SetTargetLidTemperatureParams,
|
|
51
|
+
SetTargetLidTemperatureParams,
|
|
52
|
+
SuccessData[SetTargetLidTemperatureResult],
|
|
38
53
|
]
|
|
39
54
|
):
|
|
40
|
-
"""Execution implementation of a Thermocycler's set lid temperature command."""
|
|
55
|
+
"""Execution implementation of a Thermocycler's to set lid temperature command."""
|
|
41
56
|
|
|
42
57
|
def __init__(
|
|
43
58
|
self,
|
|
44
59
|
state_view: StateView,
|
|
45
60
|
equipment: EquipmentHandler,
|
|
61
|
+
task_handler: TaskHandler,
|
|
46
62
|
**unused_dependencies: object,
|
|
47
63
|
) -> None:
|
|
48
64
|
self._state_view = state_view
|
|
49
65
|
self._equipment = equipment
|
|
66
|
+
self._task_handler = task_handler
|
|
50
67
|
|
|
51
68
|
async def execute(
|
|
52
69
|
self,
|
|
53
70
|
params: SetTargetLidTemperatureParams,
|
|
54
71
|
) -> SuccessData[SetTargetLidTemperatureResult]:
|
|
55
|
-
"""
|
|
72
|
+
"""To set a Thermocycler's target lid temperature."""
|
|
56
73
|
thermocycler_state = self._state_view.modules.get_thermocycler_module_substate(
|
|
57
74
|
params.moduleId
|
|
58
75
|
)
|
|
@@ -63,22 +80,34 @@ class SetTargetLidTemperatureImpl(
|
|
|
63
80
|
thermocycler_state.module_id
|
|
64
81
|
)
|
|
65
82
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
83
|
+
async def set_target_lid_temperature(task_handler: TaskHandler) -> None:
|
|
84
|
+
if thermocycler_hardware is not None:
|
|
85
|
+
async with task_handler.synchronize_cancel_latest(
|
|
86
|
+
thermocycler_state.module_id + "-lid"
|
|
87
|
+
):
|
|
88
|
+
await thermocycler_hardware.set_target_lid_temperature(
|
|
89
|
+
target_temperature
|
|
90
|
+
)
|
|
91
|
+
await thermocycler_hardware.wait_for_lid_target()
|
|
92
|
+
|
|
93
|
+
task = await self._task_handler.create_task(
|
|
94
|
+
task_function=set_target_lid_temperature, id=params.taskId
|
|
95
|
+
)
|
|
69
96
|
return SuccessData(
|
|
70
97
|
public=SetTargetLidTemperatureResult(
|
|
71
|
-
targetLidTemperature=target_temperature
|
|
98
|
+
targetLidTemperature=target_temperature, taskId=task.id
|
|
72
99
|
),
|
|
73
100
|
)
|
|
74
101
|
|
|
75
102
|
|
|
76
103
|
class SetTargetLidTemperature(
|
|
77
104
|
BaseCommand[
|
|
78
|
-
SetTargetLidTemperatureParams,
|
|
105
|
+
SetTargetLidTemperatureParams,
|
|
106
|
+
SetTargetLidTemperatureResult,
|
|
107
|
+
ErrorOccurrence,
|
|
79
108
|
]
|
|
80
109
|
):
|
|
81
|
-
"""A command to set a Thermocycler's target lid temperature."""
|
|
110
|
+
"""A command to to set a Thermocycler's target lid temperature."""
|
|
82
111
|
|
|
83
112
|
commandType: SetTargetLidTemperatureCommandType = (
|
|
84
113
|
"thermocycler/setTargetLidTemperature"
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"""StartRunProfile command request, result, and implementation models."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
from pydantic import BaseModel, Field
|
|
5
|
+
from typing import List, Optional, TYPE_CHECKING, overload, Union, Any
|
|
6
|
+
from typing_extensions import Literal, Type
|
|
7
|
+
from pydantic.json_schema import SkipJsonSchema
|
|
8
|
+
|
|
9
|
+
from opentrons.hardware_control.modules.types import ThermocyclerStep, ThermocyclerCycle
|
|
10
|
+
|
|
11
|
+
from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
|
|
12
|
+
from ...errors.error_occurrence import ErrorOccurrence
|
|
13
|
+
from .run_extended_profile import ProfileStep, ProfileCycle
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from opentrons.protocol_engine.state.state import StateView
|
|
17
|
+
from opentrons.protocol_engine.execution import (
|
|
18
|
+
TaskHandler,
|
|
19
|
+
EquipmentHandler,
|
|
20
|
+
)
|
|
21
|
+
from opentrons.protocol_engine.state.module_substates.thermocycler_module_substate import (
|
|
22
|
+
ThermocyclerModuleSubState,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
StartRunExtendedProfileCommandType = Literal["thermocycler/startRunExtendedProfile"]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _remove_default(s: dict[str, Any]) -> None:
|
|
29
|
+
s.pop("default", None)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class StartRunExtendedProfileStepParams(BaseModel):
|
|
33
|
+
"""Input parameters for an individual Thermocycler profile step."""
|
|
34
|
+
|
|
35
|
+
celsius: float = Field(..., description="Target temperature in °C.")
|
|
36
|
+
holdSeconds: float = Field(
|
|
37
|
+
..., description="Time to hold target temperature at in seconds."
|
|
38
|
+
)
|
|
39
|
+
rampRate: float | SkipJsonSchema[None] = Field(
|
|
40
|
+
None,
|
|
41
|
+
description="How quickly to change temperature in °C/second.",
|
|
42
|
+
json_schema_extra=_remove_default,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class StartRunExtendedProfileParams(BaseModel):
|
|
47
|
+
"""Input parameters to run a Thermocycler profile."""
|
|
48
|
+
|
|
49
|
+
moduleId: str = Field(..., description="Unique ID of the Thermocycler.")
|
|
50
|
+
profileElements: List[Union[ProfileStep, ProfileCycle]] = Field(
|
|
51
|
+
...,
|
|
52
|
+
description="Elements of the profile. Each can be either a step or a cycle.",
|
|
53
|
+
)
|
|
54
|
+
blockMaxVolumeUl: float | SkipJsonSchema[None] = Field(
|
|
55
|
+
None,
|
|
56
|
+
description="Amount of liquid in uL of the most-full well"
|
|
57
|
+
" in labware loaded onto the thermocycler.",
|
|
58
|
+
json_schema_extra=_remove_default,
|
|
59
|
+
)
|
|
60
|
+
taskId: str | None = Field(None, description="The id of the profile task")
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class StartRunExtendedProfileResult(BaseModel):
|
|
64
|
+
"""Result data from running a Thermocycler profile."""
|
|
65
|
+
|
|
66
|
+
taskId: str = Field(..., description="The id of the profile task")
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _transform_profile_step(
|
|
70
|
+
step: ProfileStep, thermocycler_state: ThermocyclerModuleSubState
|
|
71
|
+
) -> ThermocyclerStep:
|
|
72
|
+
return ThermocyclerStep(
|
|
73
|
+
temperature=thermocycler_state.validate_target_block_temperature(step.celsius),
|
|
74
|
+
hold_time_seconds=step.holdSeconds,
|
|
75
|
+
ramp_rate=thermocycler_state.validate_ramp_rate(step.rampRate, step.celsius),
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@overload
|
|
80
|
+
def _transform_profile_element(
|
|
81
|
+
element: ProfileStep, thermocycler_state: ThermocyclerModuleSubState
|
|
82
|
+
) -> ThermocyclerStep:
|
|
83
|
+
...
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@overload
|
|
87
|
+
def _transform_profile_element(
|
|
88
|
+
element: ProfileCycle, thermocycler_state: ThermocyclerModuleSubState
|
|
89
|
+
) -> ThermocyclerCycle:
|
|
90
|
+
...
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _transform_profile_element(
|
|
94
|
+
element: Union[ProfileStep, ProfileCycle],
|
|
95
|
+
thermocycler_state: ThermocyclerModuleSubState,
|
|
96
|
+
) -> Union[ThermocyclerStep, ThermocyclerCycle]:
|
|
97
|
+
if isinstance(element, ProfileStep):
|
|
98
|
+
return _transform_profile_step(element, thermocycler_state)
|
|
99
|
+
else:
|
|
100
|
+
return ThermocyclerCycle(
|
|
101
|
+
steps=[
|
|
102
|
+
_transform_profile_step(step, thermocycler_state)
|
|
103
|
+
for step in element.steps
|
|
104
|
+
],
|
|
105
|
+
repetitions=element.repetitions,
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class StartRunExtendedProfileImpl(
|
|
110
|
+
AbstractCommandImpl[
|
|
111
|
+
StartRunExtendedProfileParams, SuccessData[StartRunExtendedProfileResult]
|
|
112
|
+
]
|
|
113
|
+
):
|
|
114
|
+
"""Execution implementation of a Thermocycler's run profile command."""
|
|
115
|
+
|
|
116
|
+
def __init__(
|
|
117
|
+
self,
|
|
118
|
+
state_view: StateView,
|
|
119
|
+
equipment: EquipmentHandler,
|
|
120
|
+
task_handler: TaskHandler,
|
|
121
|
+
**unused_dependencies: object,
|
|
122
|
+
) -> None:
|
|
123
|
+
self._state_view = state_view
|
|
124
|
+
self._equipment = equipment
|
|
125
|
+
self._task_handler = task_handler
|
|
126
|
+
|
|
127
|
+
async def execute(
|
|
128
|
+
self, params: StartRunExtendedProfileParams
|
|
129
|
+
) -> SuccessData[StartRunExtendedProfileResult]:
|
|
130
|
+
"""Run a Thermocycler profile."""
|
|
131
|
+
thermocycler_state = self._state_view.modules.get_thermocycler_module_substate(
|
|
132
|
+
params.moduleId
|
|
133
|
+
)
|
|
134
|
+
thermocycler_hardware = self._equipment.get_module_hardware_api(
|
|
135
|
+
thermocycler_state.module_id
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
profile = [
|
|
139
|
+
_transform_profile_element(element, thermocycler_state)
|
|
140
|
+
for element in params.profileElements
|
|
141
|
+
]
|
|
142
|
+
target_volume: Optional[float]
|
|
143
|
+
if params.blockMaxVolumeUl is not None:
|
|
144
|
+
target_volume = thermocycler_state.validate_max_block_volume(
|
|
145
|
+
params.blockMaxVolumeUl
|
|
146
|
+
)
|
|
147
|
+
else:
|
|
148
|
+
target_volume = None
|
|
149
|
+
|
|
150
|
+
async def start_run_profile(task_handler: TaskHandler) -> None:
|
|
151
|
+
if thermocycler_hardware is not None:
|
|
152
|
+
async with task_handler.synchronize_cancel_latest(
|
|
153
|
+
thermocycler_state.module_id + "-block"
|
|
154
|
+
):
|
|
155
|
+
await thermocycler_hardware.execute_profile(
|
|
156
|
+
profile=profile, volume=target_volume
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
task = await self._task_handler.create_task(
|
|
160
|
+
task_function=start_run_profile, id=params.taskId
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
return SuccessData(
|
|
164
|
+
public=StartRunExtendedProfileResult(taskId=task.id),
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
class StartRunExtendedProfile(
|
|
169
|
+
BaseCommand[
|
|
170
|
+
StartRunExtendedProfileParams, StartRunExtendedProfileResult, ErrorOccurrence
|
|
171
|
+
]
|
|
172
|
+
):
|
|
173
|
+
"""A command to start the execution of a Thermocycler profile run without waiting for its completion."""
|
|
174
|
+
|
|
175
|
+
commandType: StartRunExtendedProfileCommandType = (
|
|
176
|
+
"thermocycler/startRunExtendedProfile"
|
|
177
|
+
)
|
|
178
|
+
params: StartRunExtendedProfileParams
|
|
179
|
+
|
|
180
|
+
_ImplementationCls: Type[StartRunExtendedProfileImpl] = StartRunExtendedProfileImpl
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
class StartRunExtendedProfileCreate(BaseCommandCreate[StartRunExtendedProfileParams]):
|
|
184
|
+
"""A request to execute a Thermocycler profile run."""
|
|
185
|
+
|
|
186
|
+
commandType: StartRunExtendedProfileCommandType = (
|
|
187
|
+
"thermocycler/startRunExtendedProfile"
|
|
188
|
+
)
|
|
189
|
+
params: StartRunExtendedProfileParams
|
|
190
|
+
|
|
191
|
+
_CommandCls: Type[StartRunExtendedProfile] = StartRunExtendedProfile
|
|
@@ -114,7 +114,7 @@ class TouchTipImplementation(
|
|
|
114
114
|
|
|
115
115
|
if self._state_view.labware.get_has_quirk(labware_id, "touchTipDisabled"):
|
|
116
116
|
raise TouchTipDisabledError(
|
|
117
|
-
f"Touch tip not allowed on labware {labware_id}"
|
|
117
|
+
f"Touch tip not allowed on labware {self._state_view.labware.get_display_name(labware_id)}"
|
|
118
118
|
)
|
|
119
119
|
|
|
120
120
|
if self._state_view.labware.is_tiprack(labware_id):
|
|
@@ -14,12 +14,11 @@ from opentrons.protocol_engine.errors.exceptions import (
|
|
|
14
14
|
CannotPerformGripperAction,
|
|
15
15
|
GripperNotAttachedError,
|
|
16
16
|
)
|
|
17
|
-
from opentrons.types import Point
|
|
18
|
-
|
|
19
17
|
from ...types import (
|
|
20
18
|
DeckSlotLocation,
|
|
21
19
|
ModuleModel,
|
|
22
20
|
OnDeckLabwareLocation,
|
|
21
|
+
GripperMoveType,
|
|
23
22
|
)
|
|
24
23
|
from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
|
|
25
24
|
from ...errors.error_occurrence import ErrorOccurrence
|
|
@@ -100,22 +99,6 @@ class UnsafePlaceLabwareImplementation(
|
|
|
100
99
|
LabwareUri(params.labwareURI)
|
|
101
100
|
)
|
|
102
101
|
|
|
103
|
-
# todo(mm, 2024-11-06): This is only correct in the special case of an
|
|
104
|
-
# absorbance reader lid. Its definition currently puts the offsets for *itself*
|
|
105
|
-
# in the property that's normally meant for offsets for its *children.*
|
|
106
|
-
final_offsets = self._state_view.labware.get_child_gripper_offsets(
|
|
107
|
-
labware_definition=definition, slot_name=None
|
|
108
|
-
)
|
|
109
|
-
drop_offset = (
|
|
110
|
-
Point(
|
|
111
|
-
final_offsets.dropOffset.x,
|
|
112
|
-
final_offsets.dropOffset.y,
|
|
113
|
-
final_offsets.dropOffset.z,
|
|
114
|
-
)
|
|
115
|
-
if final_offsets
|
|
116
|
-
else None
|
|
117
|
-
)
|
|
118
|
-
|
|
119
102
|
if isinstance(params.location, DeckSlotLocation):
|
|
120
103
|
self._state_view.addressable_areas.raise_if_area_not_in_deck_configuration(
|
|
121
104
|
params.location.slotName.id
|
|
@@ -139,7 +122,7 @@ class UnsafePlaceLabwareImplementation(
|
|
|
139
122
|
await ot3api.update_axis_position_estimations([Axis.X, Axis.Y])
|
|
140
123
|
|
|
141
124
|
# Place the labware down
|
|
142
|
-
await self._start_movement(ot3api, definition, location
|
|
125
|
+
await self._start_movement(ot3api, definition, location)
|
|
143
126
|
|
|
144
127
|
return SuccessData(public=UnsafePlaceLabwareResult())
|
|
145
128
|
|
|
@@ -148,7 +131,6 @@ class UnsafePlaceLabwareImplementation(
|
|
|
148
131
|
ot3api: OT3HardwareControlAPI,
|
|
149
132
|
labware_definition: LabwareDefinition,
|
|
150
133
|
location: OnDeckLabwareLocation,
|
|
151
|
-
drop_offset: Optional[Point],
|
|
152
134
|
) -> None:
|
|
153
135
|
gripper_homed_position = await ot3api.gantry_position(
|
|
154
136
|
mount=OT3Mount.GRIPPER,
|
|
@@ -156,13 +138,15 @@ class UnsafePlaceLabwareImplementation(
|
|
|
156
138
|
)
|
|
157
139
|
|
|
158
140
|
to_labware_center = self._state_view.geometry.get_labware_grip_point(
|
|
159
|
-
labware_definition=labware_definition,
|
|
141
|
+
labware_definition=labware_definition,
|
|
142
|
+
location=location,
|
|
143
|
+
move_type=GripperMoveType.DROP_LABWARE,
|
|
144
|
+
user_additional_offset=None,
|
|
160
145
|
)
|
|
161
146
|
|
|
162
147
|
movement_waypoints = get_gripper_labware_placement_waypoints(
|
|
163
148
|
to_labware_center=to_labware_center,
|
|
164
149
|
gripper_home_z=gripper_homed_position.z,
|
|
165
|
-
drop_offset=drop_offset,
|
|
166
150
|
)
|
|
167
151
|
|
|
168
152
|
# start movement
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"""WaitForTasks command request, result, and implementation models."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
from pydantic import BaseModel, Field
|
|
4
|
+
from typing import Optional, Type, TYPE_CHECKING
|
|
5
|
+
from typing_extensions import Literal
|
|
6
|
+
|
|
7
|
+
from .command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
|
|
8
|
+
from ..errors.error_occurrence import ErrorOccurrence, ProtocolCommandFailedError
|
|
9
|
+
from ..errors.exceptions import TaskFailedError
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from ..execution import TaskHandler, RunControlHandler
|
|
13
|
+
from ..state.state import StateView
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
WaitForTasksCommandType = Literal["waitForTasks"]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class WaitForTasksParams(BaseModel):
|
|
20
|
+
"""Payload required to annotate execution with a WaitForTasks."""
|
|
21
|
+
|
|
22
|
+
task_ids: list[str] = Field(
|
|
23
|
+
...,
|
|
24
|
+
description="The list of task ids to wait for.",
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class WaitForTasksResult(BaseModel):
|
|
29
|
+
"""Result data from the execution of a WaitForTasks command."""
|
|
30
|
+
|
|
31
|
+
task_ids: list[str] = Field(
|
|
32
|
+
...,
|
|
33
|
+
description="The list of completed task ids.",
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class WaitForTasksImplementation(
|
|
38
|
+
AbstractCommandImpl[WaitForTasksParams, SuccessData[WaitForTasksResult]]
|
|
39
|
+
):
|
|
40
|
+
"""WaitForTasks command implementation."""
|
|
41
|
+
|
|
42
|
+
def __init__(
|
|
43
|
+
self,
|
|
44
|
+
task_handler: TaskHandler,
|
|
45
|
+
run_control: RunControlHandler,
|
|
46
|
+
state_view: StateView,
|
|
47
|
+
**kwargs: object,
|
|
48
|
+
) -> None:
|
|
49
|
+
self._task_handler = task_handler
|
|
50
|
+
self._run_control = run_control
|
|
51
|
+
self._state_view = state_view
|
|
52
|
+
|
|
53
|
+
async def execute(
|
|
54
|
+
self, params: WaitForTasksParams
|
|
55
|
+
) -> SuccessData[WaitForTasksResult]:
|
|
56
|
+
"""Checks for existance of task id and then asynchronously waits for the valid, specified tasks to finish."""
|
|
57
|
+
# Raises the exception if we don't have a valid task id.
|
|
58
|
+
for task_id in params.task_ids:
|
|
59
|
+
_ = self._state_view.tasks.get(task_id)
|
|
60
|
+
|
|
61
|
+
await self._run_control.wait_for_tasks(params.task_ids)
|
|
62
|
+
|
|
63
|
+
failed_tasks = self._state_view.tasks.get_failed_tasks(params.task_ids)
|
|
64
|
+
if failed_tasks:
|
|
65
|
+
raise TaskFailedError(
|
|
66
|
+
message=f"{len(failed_tasks)} tasks failed.",
|
|
67
|
+
details={"failed_task_ids": failed_tasks},
|
|
68
|
+
wrapping=[
|
|
69
|
+
ProtocolCommandFailedError(
|
|
70
|
+
original_error=self._state_view.tasks.get_finished(
|
|
71
|
+
task_id
|
|
72
|
+
).error
|
|
73
|
+
)
|
|
74
|
+
for task_id in failed_tasks
|
|
75
|
+
],
|
|
76
|
+
)
|
|
77
|
+
return SuccessData(public=WaitForTasksResult(task_ids=params.task_ids))
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class WaitForTasks(
|
|
81
|
+
BaseCommand[WaitForTasksParams, WaitForTasksResult, ErrorOccurrence]
|
|
82
|
+
):
|
|
83
|
+
"""WaitForTasks command model."""
|
|
84
|
+
|
|
85
|
+
commandType: WaitForTasksCommandType = "waitForTasks"
|
|
86
|
+
params: WaitForTasksParams
|
|
87
|
+
result: Optional[WaitForTasksResult] = None
|
|
88
|
+
|
|
89
|
+
_ImplementationCls: Type[WaitForTasksImplementation] = WaitForTasksImplementation
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class WaitForTasksCreate(BaseCommandCreate[WaitForTasksParams]):
|
|
93
|
+
"""WaitForTasks command request model."""
|
|
94
|
+
|
|
95
|
+
commandType: WaitForTasksCommandType = "waitForTasks"
|
|
96
|
+
params: WaitForTasksParams
|
|
97
|
+
|
|
98
|
+
_CommandCls: Type[WaitForTasks] = WaitForTasks
|
|
@@ -11,6 +11,7 @@ from opentrons.protocol_engine.execution.error_recovery_hardware_state_synchroni
|
|
|
11
11
|
from opentrons.protocol_engine.resources.labware_data_provider import (
|
|
12
12
|
LabwareDataProvider,
|
|
13
13
|
)
|
|
14
|
+
from opentrons.protocol_engine.resources.camera_provider import CameraProvider
|
|
14
15
|
from opentrons.util.async_helpers import async_context_manager_in_thread
|
|
15
16
|
|
|
16
17
|
from opentrons_shared_data.robot import load as load_robot
|
|
@@ -38,6 +39,7 @@ async def create_protocol_engine(
|
|
|
38
39
|
load_fixed_trash: bool = False,
|
|
39
40
|
deck_configuration: typing.Optional[DeckConfigurationType] = None,
|
|
40
41
|
file_provider: typing.Optional[FileProvider] = None,
|
|
42
|
+
camera_provider: typing.Optional[CameraProvider] = None,
|
|
41
43
|
notify_publishers: typing.Optional[typing.Callable[[], None]] = None,
|
|
42
44
|
) -> ProtocolEngine:
|
|
43
45
|
"""Create a ProtocolEngine instance.
|
|
@@ -50,6 +52,7 @@ async def create_protocol_engine(
|
|
|
50
52
|
load_fixed_trash: Automatically load fixed trash labware in engine.
|
|
51
53
|
deck_configuration: The initial deck configuration the engine will be instantiated with.
|
|
52
54
|
file_provider: Provides access to robot server file writing procedures for protocol output.
|
|
55
|
+
camera_provider: Provides access to camera interface with image capture and callbacks.
|
|
53
56
|
notify_publishers: Notifies robot server publishers of internal state change.
|
|
54
57
|
"""
|
|
55
58
|
deck_data = DeckDataProvider(config.deck_type)
|
|
@@ -83,6 +86,7 @@ async def create_protocol_engine(
|
|
|
83
86
|
door_watcher = DoorWatcher(state_store, hardware_api, action_dispatcher)
|
|
84
87
|
module_data_provider = ModuleDataProvider()
|
|
85
88
|
file_provider = file_provider or FileProvider()
|
|
89
|
+
camera_provider = camera_provider or CameraProvider()
|
|
86
90
|
|
|
87
91
|
pe = ProtocolEngine(
|
|
88
92
|
hardware_api=hardware_api,
|
|
@@ -94,6 +98,7 @@ async def create_protocol_engine(
|
|
|
94
98
|
door_watcher=door_watcher,
|
|
95
99
|
module_data_provider=module_data_provider,
|
|
96
100
|
file_provider=file_provider,
|
|
101
|
+
camera_provider=camera_provider,
|
|
97
102
|
)
|
|
98
103
|
|
|
99
104
|
# todo(mm, 2024-11-08): This is a quick hack to support the absorbance reader, which
|
|
@@ -121,6 +126,7 @@ def create_protocol_engine_in_thread(
|
|
|
121
126
|
drop_tips_after_run: bool,
|
|
122
127
|
post_run_hardware_state: PostRunHardwareState,
|
|
123
128
|
load_fixed_trash: bool = False,
|
|
129
|
+
camera_provider: typing.Optional[CameraProvider] = None,
|
|
124
130
|
) -> typing.Generator[
|
|
125
131
|
typing.Tuple[ProtocolEngine, asyncio.AbstractEventLoop], None, None
|
|
126
132
|
]:
|
|
@@ -151,6 +157,7 @@ def create_protocol_engine_in_thread(
|
|
|
151
157
|
drop_tips_after_run,
|
|
152
158
|
post_run_hardware_state,
|
|
153
159
|
load_fixed_trash,
|
|
160
|
+
camera_provider,
|
|
154
161
|
)
|
|
155
162
|
) as (
|
|
156
163
|
protocol_engine,
|
|
@@ -169,6 +176,7 @@ async def _protocol_engine(
|
|
|
169
176
|
drop_tips_after_run: bool,
|
|
170
177
|
post_run_hardware_state: PostRunHardwareState,
|
|
171
178
|
load_fixed_trash: bool = False,
|
|
179
|
+
camera_provider: typing.Optional[CameraProvider] = None,
|
|
172
180
|
) -> typing.AsyncGenerator[ProtocolEngine, None]:
|
|
173
181
|
protocol_engine = await create_protocol_engine(
|
|
174
182
|
hardware_api=hardware_api,
|
|
@@ -179,9 +187,13 @@ async def _protocol_engine(
|
|
|
179
187
|
)
|
|
180
188
|
|
|
181
189
|
# TODO(tz, 6-20-2024): This feels like a hack, we should probably return the orchestrator instead of pe.
|
|
190
|
+
|
|
182
191
|
orchestrator = create_run_orchestrator(
|
|
183
192
|
hardware_api=hardware_api,
|
|
184
193
|
protocol_engine=protocol_engine,
|
|
194
|
+
camera_provider=camera_provider
|
|
195
|
+
if camera_provider is not None
|
|
196
|
+
else CameraProvider(),
|
|
185
197
|
)
|
|
186
198
|
try:
|
|
187
199
|
orchestrator.play(deck_configuration)
|
|
@@ -3,16 +3,19 @@ from . import ProtocolEngine
|
|
|
3
3
|
from ..hardware_control import HardwareControlAPI
|
|
4
4
|
|
|
5
5
|
from opentrons.protocol_runner import protocol_runner, RunOrchestrator
|
|
6
|
+
from opentrons.protocol_engine.resources.camera_provider import CameraProvider
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
def create_run_orchestrator(
|
|
9
10
|
hardware_api: HardwareControlAPI,
|
|
10
11
|
protocol_engine: ProtocolEngine,
|
|
12
|
+
camera_provider: CameraProvider,
|
|
11
13
|
) -> RunOrchestrator:
|
|
12
14
|
"""Create a RunOrchestrator instance."""
|
|
13
15
|
return RunOrchestrator(
|
|
14
16
|
protocol_engine=protocol_engine,
|
|
15
17
|
hardware_api=hardware_api,
|
|
18
|
+
camera_provider=camera_provider,
|
|
16
19
|
setup_runner=protocol_runner.LiveRunner(
|
|
17
20
|
protocol_engine=protocol_engine,
|
|
18
21
|
hardware_api=hardware_api,
|
|
@@ -57,6 +57,7 @@ from .exceptions import (
|
|
|
57
57
|
InvalidTargetSpeedError,
|
|
58
58
|
InvalidTargetTemperatureError,
|
|
59
59
|
InvalidBlockVolumeError,
|
|
60
|
+
InvalidRampRateError,
|
|
60
61
|
InvalidHoldTimeError,
|
|
61
62
|
InvalidWavelengthError,
|
|
62
63
|
CannotPerformModuleAction,
|
|
@@ -83,6 +84,7 @@ from .exceptions import (
|
|
|
83
84
|
OperationLocationNotInWellError,
|
|
84
85
|
InvalidDispenseVolumeError,
|
|
85
86
|
StorageLimitReachedError,
|
|
87
|
+
FileNameInvalidError,
|
|
86
88
|
InvalidLiquidError,
|
|
87
89
|
LiquidClassDoesNotExistError,
|
|
88
90
|
LiquidClassRedefinitionError,
|
|
@@ -90,6 +92,10 @@ from .exceptions import (
|
|
|
90
92
|
FlexStackerLabwarePoolNotYetDefinedError,
|
|
91
93
|
FlexStackerNotLogicallyEmptyError,
|
|
92
94
|
InvalidLabwarePositionError,
|
|
95
|
+
InvalidModuleOrientation,
|
|
96
|
+
CameraCaptureError,
|
|
97
|
+
CameraDisabledError,
|
|
98
|
+
CameraSettingsInvalidError,
|
|
93
99
|
)
|
|
94
100
|
|
|
95
101
|
from .error_occurrence import ErrorOccurrence, ProtocolCommandFailedError
|
|
@@ -151,6 +157,7 @@ __all__ = [
|
|
|
151
157
|
"NoTargetTemperatureSetError",
|
|
152
158
|
"InvalidTargetTemperatureError",
|
|
153
159
|
"InvalidTargetSpeedError",
|
|
160
|
+
"InvalidRampRateError",
|
|
154
161
|
"InvalidBlockVolumeError",
|
|
155
162
|
"InvalidHoldTimeError",
|
|
156
163
|
"InvalidLiquidError",
|
|
@@ -174,6 +181,7 @@ __all__ = [
|
|
|
174
181
|
"FlexStackerLabwarePoolNotYetDefinedError",
|
|
175
182
|
"FlexStackerNotLogicallyEmptyError",
|
|
176
183
|
"InvalidLabwarePositionError",
|
|
184
|
+
"InvalidModuleOrientation",
|
|
177
185
|
# error occurrence models
|
|
178
186
|
"ErrorOccurrence",
|
|
179
187
|
"CommandNotAllowedError",
|
|
@@ -186,6 +194,10 @@ __all__ = [
|
|
|
186
194
|
"OperationLocationNotInWellError",
|
|
187
195
|
"InvalidDispenseVolumeError",
|
|
188
196
|
"StorageLimitReachedError",
|
|
197
|
+
"FileNameInvalidError",
|
|
189
198
|
"LiquidClassDoesNotExistError",
|
|
190
199
|
"LiquidClassRedefinitionError",
|
|
200
|
+
"CameraCaptureError",
|
|
201
|
+
"CameraDisabledError",
|
|
202
|
+
"CameraSettingsInvalidError",
|
|
191
203
|
]
|