opentrons 8.7.0a6__py3-none-any.whl → 8.7.0a7__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 +129 -52
- 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/hardware_control/api.py +24 -5
- opentrons/hardware_control/backends/controller.py +8 -2
- opentrons/hardware_control/backends/ot3controller.py +3 -0
- opentrons/hardware_control/backends/ot3simulator.py +2 -1
- opentrons/hardware_control/backends/simulator.py +2 -1
- opentrons/hardware_control/backends/subsystem_manager.py +5 -2
- opentrons/hardware_control/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 +82 -8
- opentrons/hardware_control/modules/__init__.py +3 -0
- opentrons/hardware_control/modules/absorbance_reader.py +11 -4
- opentrons/hardware_control/modules/flex_stacker.py +38 -9
- opentrons/hardware_control/modules/heater_shaker.py +42 -5
- opentrons/hardware_control/modules/magdeck.py +8 -4
- opentrons/hardware_control/modules/mod_abc.py +13 -5
- opentrons/hardware_control/modules/tempdeck.py +25 -5
- opentrons/hardware_control/modules/thermocycler.py +68 -11
- opentrons/hardware_control/modules/types.py +20 -1
- opentrons/hardware_control/modules/utils.py +11 -4
- opentrons/hardware_control/nozzle_manager.py +3 -0
- opentrons/hardware_control/ot3api.py +26 -5
- opentrons/hardware_control/poller.py +22 -8
- opentrons/hardware_control/scripts/update_module_fw.py +5 -0
- opentrons/hardware_control/types.py +31 -2
- opentrons/legacy_commands/module_commands.py +23 -0
- opentrons/legacy_commands/protocol_commands.py +20 -0
- opentrons/legacy_commands/types.py +80 -0
- 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/_types.py +6 -1
- opentrons/protocol_api/core/common.py +3 -1
- opentrons/protocol_api/core/engine/_default_labware_versions.py +32 -11
- opentrons/protocol_api/core/engine/labware.py +8 -1
- opentrons/protocol_api/core/engine/module_core.py +75 -8
- opentrons/protocol_api/core/engine/protocol.py +18 -1
- opentrons/protocol_api/core/engine/tasks.py +48 -0
- opentrons/protocol_api/core/engine/well.py +8 -0
- opentrons/protocol_api/core/legacy/legacy_module_core.py +24 -4
- opentrons/protocol_api/core/legacy/legacy_protocol_core.py +11 -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_protocol_core.py +14 -2
- opentrons/protocol_api/core/legacy_simulator/tasks.py +19 -0
- opentrons/protocol_api/core/module.py +37 -4
- opentrons/protocol_api/core/protocol.py +11 -2
- opentrons/protocol_api/core/tasks.py +31 -0
- opentrons/protocol_api/core/well.py +4 -0
- opentrons/protocol_api/labware.py +5 -0
- opentrons/protocol_api/module_contexts.py +117 -11
- opentrons/protocol_api/protocol_context.py +26 -4
- opentrons/protocol_api/robot_context.py +38 -21
- opentrons/protocol_api/tasks.py +48 -0
- opentrons/protocol_api/validation.py +6 -1
- opentrons/protocol_engine/actions/__init__.py +4 -2
- opentrons/protocol_engine/actions/actions.py +22 -9
- opentrons/protocol_engine/clients/sync_client.py +42 -7
- opentrons/protocol_engine/commands/__init__.py +42 -0
- opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +2 -15
- opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +2 -15
- opentrons/protocol_engine/commands/aspirate.py +1 -0
- opentrons/protocol_engine/commands/command.py +1 -0
- opentrons/protocol_engine/commands/command_unions.py +49 -0
- opentrons/protocol_engine/commands/create_timer.py +83 -0
- opentrons/protocol_engine/commands/dispense.py +1 -0
- opentrons/protocol_engine/commands/drop_tip.py +32 -8
- opentrons/protocol_engine/commands/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/movement_common.py +2 -0
- opentrons/protocol_engine/commands/pick_up_tip.py +21 -11
- opentrons/protocol_engine/commands/set_tip_state.py +97 -0
- opentrons/protocol_engine/commands/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 +40 -6
- opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py +29 -5
- 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/errors/__init__.py +4 -0
- opentrons/protocol_engine/errors/exceptions.py +55 -0
- opentrons/protocol_engine/execution/__init__.py +2 -0
- opentrons/protocol_engine/execution/command_executor.py +8 -0
- opentrons/protocol_engine/execution/create_queue_worker.py +5 -1
- opentrons/protocol_engine/execution/labware_movement.py +9 -12
- opentrons/protocol_engine/execution/movement.py +2 -0
- opentrons/protocol_engine/execution/queue_worker.py +4 -0
- opentrons/protocol_engine/execution/run_control.py +8 -0
- opentrons/protocol_engine/execution/task_handler.py +157 -0
- opentrons/protocol_engine/protocol_engine.py +75 -34
- opentrons/protocol_engine/resources/__init__.py +2 -0
- opentrons/protocol_engine/resources/concurrency_provider.py +27 -0
- opentrons/protocol_engine/resources/deck_configuration_provider.py +7 -0
- opentrons/protocol_engine/resources/labware_validation.py +10 -6
- opentrons/protocol_engine/state/_well_math.py +60 -18
- opentrons/protocol_engine/state/addressable_areas.py +2 -0
- opentrons/protocol_engine/state/commands.py +14 -11
- opentrons/protocol_engine/state/geometry.py +213 -374
- opentrons/protocol_engine/state/labware.py +52 -102
- opentrons/protocol_engine/state/labware_origin_math/errors.py +94 -0
- opentrons/protocol_engine/state/labware_origin_math/stackup_origin_to_labware_origin.py +1331 -0
- opentrons/protocol_engine/state/module_substates/thermocycler_module_substate.py +37 -0
- opentrons/protocol_engine/state/modules.py +21 -8
- opentrons/protocol_engine/state/motion.py +44 -0
- opentrons/protocol_engine/state/state.py +14 -0
- opentrons/protocol_engine/state/state_summary.py +2 -0
- opentrons/protocol_engine/state/tasks.py +139 -0
- opentrons/protocol_engine/state/tips.py +177 -258
- opentrons/protocol_engine/state/update_types.py +16 -9
- opentrons/protocol_engine/types/__init__.py +9 -3
- opentrons/protocol_engine/types/deck_configuration.py +5 -1
- opentrons/protocol_engine/types/instrument.py +8 -1
- opentrons/protocol_engine/types/labware.py +1 -13
- opentrons/protocol_engine/types/module.py +10 -0
- opentrons/protocol_engine/types/tasks.py +38 -0
- opentrons/protocol_engine/types/tip.py +9 -0
- opentrons/protocol_runner/create_simulating_orchestrator.py +29 -2
- opentrons/protocol_runner/run_orchestrator.py +18 -2
- opentrons/protocols/api_support/definitions.py +1 -1
- opentrons/protocols/api_support/types.py +2 -1
- opentrons/simulate.py +48 -15
- opentrons/system/camera.py +1 -1
- {opentrons-8.7.0a6.dist-info → opentrons-8.7.0a7.dist-info}/METADATA +4 -4
- {opentrons-8.7.0a6.dist-info → opentrons-8.7.0a7.dist-info}/RECORD +143 -127
- opentrons/protocol_engine/state/_labware_origin_math.py +0 -636
- {opentrons-8.7.0a6.dist-info → opentrons-8.7.0a7.dist-info}/WHEEL +0 -0
- {opentrons-8.7.0a6.dist-info → opentrons-8.7.0a7.dist-info}/entry_points.txt +0 -0
- {opentrons-8.7.0a6.dist-info → opentrons-8.7.0a7.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"""Command models to set a shake speed for a Heater-Shaker Module."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
from typing import Optional, TYPE_CHECKING
|
|
4
|
+
from typing_extensions import Literal, Type
|
|
5
|
+
from pydantic import BaseModel, Field
|
|
6
|
+
|
|
7
|
+
from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
|
|
8
|
+
from ...errors.error_occurrence import ErrorOccurrence
|
|
9
|
+
from ...state import update_types
|
|
10
|
+
from .common import get_heatershaker_ready_to_shake
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from opentrons.protocol_engine.state.state import StateView
|
|
14
|
+
from opentrons.protocol_engine.execution import (
|
|
15
|
+
EquipmentHandler,
|
|
16
|
+
MovementHandler,
|
|
17
|
+
TaskHandler,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
SetShakeSpeedCommandType = Literal["heaterShaker/setShakeSpeed"]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class SetShakeSpeedParams(BaseModel):
|
|
24
|
+
"""Input parameters to set a shake speed for a Heater-Shaker Module."""
|
|
25
|
+
|
|
26
|
+
moduleId: str = Field(..., description="Unique ID of the Heater-Shaker Module.")
|
|
27
|
+
rpm: float = Field(..., description="Target speed in rotations per minute.")
|
|
28
|
+
taskId: str | None = Field(
|
|
29
|
+
None,
|
|
30
|
+
description="Id for the background task that manages the temperature",
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class SetShakeSpeedResult(BaseModel):
|
|
35
|
+
"""Result data from setting and waiting for a Heater-Shaker's shake speed."""
|
|
36
|
+
|
|
37
|
+
pipetteRetracted: bool = Field(
|
|
38
|
+
...,
|
|
39
|
+
description=(
|
|
40
|
+
"Whether this command automatically retracted the pipettes"
|
|
41
|
+
" before starting the shake, to avoid a potential collision."
|
|
42
|
+
),
|
|
43
|
+
)
|
|
44
|
+
taskId: str = Field(
|
|
45
|
+
description="The task id for the setTargetTemperature task",
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class SetShakeSpeedImpl(
|
|
50
|
+
AbstractCommandImpl[SetShakeSpeedParams, SuccessData[SetShakeSpeedResult]]
|
|
51
|
+
):
|
|
52
|
+
"""Execution implementation of Heater-Shaker's set shake speed command."""
|
|
53
|
+
|
|
54
|
+
def __init__(
|
|
55
|
+
self,
|
|
56
|
+
state_view: StateView,
|
|
57
|
+
equipment: EquipmentHandler,
|
|
58
|
+
movement: MovementHandler,
|
|
59
|
+
task_handler: TaskHandler,
|
|
60
|
+
**unused_dependencies: object,
|
|
61
|
+
) -> None:
|
|
62
|
+
self._state_view = state_view
|
|
63
|
+
self._equipment = equipment
|
|
64
|
+
self._movement = movement
|
|
65
|
+
self._task_handler = task_handler
|
|
66
|
+
|
|
67
|
+
async def execute(
|
|
68
|
+
self,
|
|
69
|
+
params: SetShakeSpeedParams,
|
|
70
|
+
) -> SuccessData[SetShakeSpeedResult]:
|
|
71
|
+
"""Set a Heater-Shaker's target shake speed."""
|
|
72
|
+
state_update = update_types.StateUpdate()
|
|
73
|
+
|
|
74
|
+
# Allow propagation of ModuleNotLoadedError and WrongModuleTypeError.
|
|
75
|
+
hs_module_substate = self._state_view.modules.get_heater_shaker_module_substate(
|
|
76
|
+
module_id=params.moduleId
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
validated_speed = await get_heatershaker_ready_to_shake(
|
|
80
|
+
hs_module_substate, params.rpm
|
|
81
|
+
)
|
|
82
|
+
pipette_should_retract = (
|
|
83
|
+
self._state_view.motion.check_pipette_blocking_hs_shaker(
|
|
84
|
+
hs_module_substate.module_id
|
|
85
|
+
)
|
|
86
|
+
)
|
|
87
|
+
if pipette_should_retract:
|
|
88
|
+
# Move pipette away if it is close to the heater-shaker
|
|
89
|
+
# TODO(jbl 2022-07-28) replace home movement with a retract movement
|
|
90
|
+
await self._movement.home(
|
|
91
|
+
axes=self._state_view.motion.get_robot_mount_axes()
|
|
92
|
+
)
|
|
93
|
+
state_update.clear_all_pipette_locations()
|
|
94
|
+
|
|
95
|
+
# Allow propagation of ModuleNotAttachedError.
|
|
96
|
+
hs_hardware_module = self._equipment.get_module_hardware_api(
|
|
97
|
+
hs_module_substate.module_id
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
async def start_shake(task_handler: TaskHandler) -> None:
|
|
101
|
+
if hs_hardware_module is not None:
|
|
102
|
+
async with task_handler.synchronize_cancel_previous(
|
|
103
|
+
hs_module_substate.module_id
|
|
104
|
+
):
|
|
105
|
+
await hs_hardware_module.set_speed(rpm=validated_speed)
|
|
106
|
+
|
|
107
|
+
task = await self._task_handler.create_task(
|
|
108
|
+
task_function=start_shake, id=params.taskId
|
|
109
|
+
)
|
|
110
|
+
return SuccessData(
|
|
111
|
+
public=SetShakeSpeedResult(
|
|
112
|
+
pipetteRetracted=pipette_should_retract, taskId=task.id
|
|
113
|
+
),
|
|
114
|
+
state_update=state_update,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class SetShakeSpeed(
|
|
119
|
+
BaseCommand[SetShakeSpeedParams, SetShakeSpeedResult, ErrorOccurrence]
|
|
120
|
+
):
|
|
121
|
+
"""A command to set a Heater-Shaker's shake speed."""
|
|
122
|
+
|
|
123
|
+
commandType: SetShakeSpeedCommandType = "heaterShaker/setShakeSpeed"
|
|
124
|
+
params: SetShakeSpeedParams
|
|
125
|
+
result: Optional[SetShakeSpeedResult] = None
|
|
126
|
+
|
|
127
|
+
_ImplementationCls: Type[SetShakeSpeedImpl] = SetShakeSpeedImpl
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class SetShakeSpeedCreate(BaseCommandCreate[SetShakeSpeedParams]):
|
|
131
|
+
"""A request to create a Heater-Shaker's set shake speed command."""
|
|
132
|
+
|
|
133
|
+
commandType: SetShakeSpeedCommandType = "heaterShaker/setShakeSpeed"
|
|
134
|
+
params: SetShakeSpeedParams
|
|
135
|
+
|
|
136
|
+
_CommandCls: Type[SetShakeSpeed] = SetShakeSpeed
|
|
@@ -1,31 +1,47 @@
|
|
|
1
1
|
"""Command models to start heating a Heater-Shaker Module."""
|
|
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
|
+
from opentrons.protocol_engine.execution import EquipmentHandler, TaskHandler
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
SetTargetTemperatureCommandType = Literal["heaterShaker/setTargetTemperature"]
|
|
17
18
|
|
|
18
19
|
|
|
20
|
+
def _remove_default(s: dict[str, Any]) -> None:
|
|
21
|
+
s.pop("default", None)
|
|
22
|
+
|
|
23
|
+
|
|
19
24
|
class SetTargetTemperatureParams(BaseModel):
|
|
20
25
|
"""Input parameters to set a Heater-Shaker's target temperature."""
|
|
21
26
|
|
|
22
27
|
moduleId: str = Field(..., description="Unique ID of the Heater-Shaker Module.")
|
|
23
28
|
celsius: float = Field(..., description="Target temperature in °C.")
|
|
29
|
+
taskId: str | SkipJsonSchema[None] = Field(
|
|
30
|
+
None,
|
|
31
|
+
description="Id for the background task that manages the temperature",
|
|
32
|
+
json_schema_extra=_remove_default,
|
|
33
|
+
)
|
|
24
34
|
|
|
25
35
|
|
|
26
36
|
class SetTargetTemperatureResult(BaseModel):
|
|
27
37
|
"""Result data from setting a Heater-Shaker's target temperature."""
|
|
28
38
|
|
|
39
|
+
taskId: str | SkipJsonSchema[None] = Field(
|
|
40
|
+
None,
|
|
41
|
+
description="The task id for the setTargetTemperature task",
|
|
42
|
+
json_schema_extra=_remove_default,
|
|
43
|
+
)
|
|
44
|
+
|
|
29
45
|
|
|
30
46
|
class SetTargetTemperatureImpl(
|
|
31
47
|
AbstractCommandImpl[
|
|
@@ -38,10 +54,12 @@ class SetTargetTemperatureImpl(
|
|
|
38
54
|
self,
|
|
39
55
|
state_view: StateView,
|
|
40
56
|
equipment: EquipmentHandler,
|
|
57
|
+
task_handler: TaskHandler,
|
|
41
58
|
**unused_dependencies: object,
|
|
42
59
|
) -> None:
|
|
43
60
|
self._state_view = state_view
|
|
44
61
|
self._equipment = equipment
|
|
62
|
+
self._task_handler = task_handler
|
|
45
63
|
|
|
46
64
|
async def execute(
|
|
47
65
|
self,
|
|
@@ -61,11 +79,19 @@ class SetTargetTemperatureImpl(
|
|
|
61
79
|
hs_module_substate.module_id
|
|
62
80
|
)
|
|
63
81
|
|
|
64
|
-
|
|
65
|
-
|
|
82
|
+
async def start_set_temperature(task_handler: TaskHandler) -> None:
|
|
83
|
+
if hs_hardware_module is not None:
|
|
84
|
+
async with task_handler.synchronize_cancel_previous(
|
|
85
|
+
hs_module_substate.module_id
|
|
86
|
+
):
|
|
87
|
+
await hs_hardware_module.start_set_temperature(validated_temp)
|
|
88
|
+
await hs_hardware_module.await_temperature(validated_temp)
|
|
66
89
|
|
|
90
|
+
task = await self._task_handler.create_task(
|
|
91
|
+
task_function=start_set_temperature, id=params.taskId
|
|
92
|
+
)
|
|
67
93
|
return SuccessData(
|
|
68
|
-
public=SetTargetTemperatureResult(),
|
|
94
|
+
public=SetTargetTemperatureResult(taskId=task.id),
|
|
69
95
|
)
|
|
70
96
|
|
|
71
97
|
|
|
@@ -152,6 +152,7 @@ async def move_to_well(
|
|
|
152
152
|
minimum_z_height: Optional[float] = None,
|
|
153
153
|
speed: Optional[float] = None,
|
|
154
154
|
operation_volume: Optional[float] = None,
|
|
155
|
+
offset_pipette_for_reservoir_subwells: bool = False,
|
|
155
156
|
) -> MoveToWellOperationReturn:
|
|
156
157
|
"""Execute a move to well microoperation."""
|
|
157
158
|
try:
|
|
@@ -165,6 +166,7 @@ async def move_to_well(
|
|
|
165
166
|
minimum_z_height=minimum_z_height,
|
|
166
167
|
speed=speed,
|
|
167
168
|
operation_volume=operation_volume,
|
|
169
|
+
offset_pipette_for_reservoir_subwells=offset_pipette_for_reservoir_subwells,
|
|
168
170
|
)
|
|
169
171
|
except StallOrCollisionDetectedError as e:
|
|
170
172
|
return DefinedErrorData(
|
|
@@ -10,7 +10,7 @@ from typing_extensions import Literal
|
|
|
10
10
|
from ..errors import ErrorOccurrence, PickUpTipTipNotAttachedError
|
|
11
11
|
from ..resources import ModelUtils
|
|
12
12
|
from ..state import update_types
|
|
13
|
-
from ..types import PickUpTipWellLocation, LabwareWellId
|
|
13
|
+
from ..types import PickUpTipWellLocation, LabwareWellId, TipRackWellState
|
|
14
14
|
from .pipetting_common import (
|
|
15
15
|
PipetteIdMixin,
|
|
16
16
|
)
|
|
@@ -121,10 +121,14 @@ class PickUpTipImplementation(AbstractCommandImpl[PickUpTipParams, _ExecuteRetur
|
|
|
121
121
|
labware_id = params.labwareId
|
|
122
122
|
well_name = params.wellName
|
|
123
123
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
124
|
+
tips_to_mark_as_empty = (
|
|
125
|
+
self._state_view.tips.compute_tips_to_mark_as_used_or_empty(
|
|
126
|
+
labware_id=labware_id,
|
|
127
|
+
well_name=well_name,
|
|
128
|
+
nozzle_map=self._state_view.pipettes.get_nozzle_configuration(
|
|
129
|
+
pipette_id
|
|
130
|
+
),
|
|
131
|
+
)
|
|
128
132
|
)
|
|
129
133
|
|
|
130
134
|
well_location = self._state_view.geometry.convert_pick_up_tip_well_location(
|
|
@@ -160,16 +164,20 @@ class PickUpTipImplementation(AbstractCommandImpl[PickUpTipParams, _ExecuteRetur
|
|
|
160
164
|
),
|
|
161
165
|
)
|
|
162
166
|
.set_fluid_empty(pipette_id=pipette_id, clean_tip=True)
|
|
163
|
-
.
|
|
164
|
-
|
|
167
|
+
.update_tip_rack_well_state(
|
|
168
|
+
tip_state=TipRackWellState.EMPTY,
|
|
169
|
+
labware_id=labware_id,
|
|
170
|
+
well_names=tips_to_mark_as_empty,
|
|
165
171
|
)
|
|
166
172
|
)
|
|
167
173
|
state_update = (
|
|
168
174
|
update_types.StateUpdate.reduce(
|
|
169
175
|
update_types.StateUpdate(), move_result.state_update
|
|
170
176
|
)
|
|
171
|
-
.
|
|
172
|
-
|
|
177
|
+
.update_tip_rack_well_state(
|
|
178
|
+
tip_state=TipRackWellState.EMPTY,
|
|
179
|
+
labware_id=labware_id,
|
|
180
|
+
well_names=tips_to_mark_as_empty,
|
|
173
181
|
)
|
|
174
182
|
.set_fluid_unknown(pipette_id=pipette_id)
|
|
175
183
|
)
|
|
@@ -197,8 +205,10 @@ class PickUpTipImplementation(AbstractCommandImpl[PickUpTipParams, _ExecuteRetur
|
|
|
197
205
|
labware_id=labware_id, well_name=well_name
|
|
198
206
|
),
|
|
199
207
|
)
|
|
200
|
-
.
|
|
201
|
-
|
|
208
|
+
.update_tip_rack_well_state(
|
|
209
|
+
tip_state=TipRackWellState.EMPTY,
|
|
210
|
+
labware_id=labware_id,
|
|
211
|
+
well_names=tips_to_mark_as_empty,
|
|
202
212
|
)
|
|
203
213
|
.set_fluid_empty(pipette_id=pipette_id, clean_tip=True)
|
|
204
214
|
.set_pipette_ready_to_aspirate(
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""Set tip state command request, result, and implementation models."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
from pydantic import BaseModel, Field
|
|
5
|
+
from typing import TYPE_CHECKING, Optional, Type, List
|
|
6
|
+
from typing_extensions import Literal
|
|
7
|
+
|
|
8
|
+
from ..errors.error_occurrence import ErrorOccurrence
|
|
9
|
+
from ..state.update_types import StateUpdate
|
|
10
|
+
from ..types import TipRackWellState
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
from .command import (
|
|
14
|
+
AbstractCommandImpl,
|
|
15
|
+
BaseCommand,
|
|
16
|
+
BaseCommandCreate,
|
|
17
|
+
SuccessData,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
if TYPE_CHECKING:
|
|
21
|
+
from ..state.state import StateView
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
SetTipStateCommandType = Literal["setTipState"]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class SetTipStateParams(BaseModel):
|
|
28
|
+
"""Payload needed to set tip wells of a tip rack to the requested state."""
|
|
29
|
+
|
|
30
|
+
labwareId: str = Field(
|
|
31
|
+
..., description="Identifier of tip rack labware to set tip wells in."
|
|
32
|
+
)
|
|
33
|
+
wellNames: List[str] = Field(
|
|
34
|
+
..., description="Names of the well to set tip well state for."
|
|
35
|
+
)
|
|
36
|
+
tipWellState: TipRackWellState = Field(
|
|
37
|
+
..., description="State to set tip wells to."
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class SetTipStateResult(BaseModel):
|
|
42
|
+
"""Result data from the execution of a setTipState command."""
|
|
43
|
+
|
|
44
|
+
pass
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class SetTipStateImplementation(
|
|
48
|
+
AbstractCommandImpl[SetTipStateParams, SuccessData[SetTipStateResult]]
|
|
49
|
+
):
|
|
50
|
+
"""Set tip state command implementation."""
|
|
51
|
+
|
|
52
|
+
def __init__(
|
|
53
|
+
self,
|
|
54
|
+
state_view: StateView,
|
|
55
|
+
**kwargs: object,
|
|
56
|
+
) -> None:
|
|
57
|
+
self._state_view = state_view
|
|
58
|
+
|
|
59
|
+
async def execute(
|
|
60
|
+
self, params: SetTipStateParams
|
|
61
|
+
) -> SuccessData[SetTipStateResult]:
|
|
62
|
+
"""Set the tip rack wells to the requested state."""
|
|
63
|
+
labware_id = params.labwareId
|
|
64
|
+
well_names = params.wellNames
|
|
65
|
+
|
|
66
|
+
self._state_view.labware.raise_if_not_tip_rack(labware_id=labware_id)
|
|
67
|
+
self._state_view.labware.raise_if_wells_are_invalid(
|
|
68
|
+
labware_id=labware_id, well_names=well_names
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
return SuccessData(
|
|
72
|
+
public=SetTipStateResult(),
|
|
73
|
+
state_update=StateUpdate().update_tip_rack_well_state(
|
|
74
|
+
tip_state=params.tipWellState,
|
|
75
|
+
labware_id=labware_id,
|
|
76
|
+
well_names=well_names,
|
|
77
|
+
),
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class SetTipState(BaseCommand[SetTipStateParams, SetTipStateResult, ErrorOccurrence]):
|
|
82
|
+
"""Set tip state command model."""
|
|
83
|
+
|
|
84
|
+
commandType: SetTipStateCommandType = "setTipState"
|
|
85
|
+
params: SetTipStateParams
|
|
86
|
+
result: Optional[SetTipStateResult] = None
|
|
87
|
+
|
|
88
|
+
_ImplementationCls: Type[SetTipStateImplementation] = SetTipStateImplementation
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class SetTipStateCreate(BaseCommandCreate[SetTipStateParams]):
|
|
92
|
+
"""Set tip state command creation request model."""
|
|
93
|
+
|
|
94
|
+
commandType: SetTipStateCommandType = "setTipState"
|
|
95
|
+
params: SetTipStateParams
|
|
96
|
+
|
|
97
|
+
_CommandCls: Type[SetTipState] = SetTipState
|
|
@@ -1,25 +1,35 @@
|
|
|
1
1
|
"""Command models to start heating a Temperature Module."""
|
|
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
|
+
from opentrons.protocol_engine.execution import EquipmentHandler, TaskHandler
|
|
14
15
|
|
|
15
16
|
SetTargetTemperatureCommandType = Literal["temperatureModule/setTargetTemperature"]
|
|
16
17
|
|
|
17
18
|
|
|
19
|
+
def _remove_default(s: dict[str, Any]) -> None:
|
|
20
|
+
s.pop("default", None)
|
|
21
|
+
|
|
22
|
+
|
|
18
23
|
class SetTargetTemperatureParams(BaseModel):
|
|
19
24
|
"""Input parameters to set a Temperature Module's target temperature."""
|
|
20
25
|
|
|
21
26
|
moduleId: str = Field(..., description="Unique ID of the Temperature Module.")
|
|
22
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
|
+
)
|
|
23
33
|
|
|
24
34
|
|
|
25
35
|
class SetTargetTemperatureResult(BaseModel):
|
|
@@ -30,6 +40,11 @@ class SetTargetTemperatureResult(BaseModel):
|
|
|
30
40
|
description="The target temperature that was set after validation "
|
|
31
41
|
"and type conversion (if any).",
|
|
32
42
|
)
|
|
43
|
+
taskId: str | SkipJsonSchema[None] = Field(
|
|
44
|
+
None,
|
|
45
|
+
description="The task id for the setTargetTemperature task",
|
|
46
|
+
json_schema_extra=_remove_default,
|
|
47
|
+
)
|
|
33
48
|
|
|
34
49
|
|
|
35
50
|
class SetTargetTemperatureImpl(
|
|
@@ -43,10 +58,12 @@ class SetTargetTemperatureImpl(
|
|
|
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, params: SetTargetTemperatureParams
|
|
@@ -56,19 +73,33 @@ class SetTargetTemperatureImpl(
|
|
|
56
73
|
module_substate = self._state_view.modules.get_temperature_module_substate(
|
|
57
74
|
module_id=params.moduleId
|
|
58
75
|
)
|
|
59
|
-
|
|
60
76
|
# Verify temperature from temperature module view
|
|
61
77
|
validated_temp = module_substate.validate_target_temperature(params.celsius)
|
|
62
|
-
|
|
63
78
|
# Allow propagation of ModuleNotAttachedError.
|
|
64
79
|
temp_hardware_module = self._equipment.get_module_hardware_api(
|
|
65
80
|
module_substate.module_id
|
|
66
81
|
)
|
|
67
82
|
|
|
68
|
-
|
|
69
|
-
|
|
83
|
+
async def start_set_temperature(task_handler: TaskHandler) -> None:
|
|
84
|
+
if temp_hardware_module is not None:
|
|
85
|
+
async with task_handler.synchronize_cancel_previous(
|
|
86
|
+
module_substate.module_id
|
|
87
|
+
):
|
|
88
|
+
await temp_hardware_module.start_set_temperature(
|
|
89
|
+
celsius=validated_temp
|
|
90
|
+
)
|
|
91
|
+
await temp_hardware_module.await_temperature(
|
|
92
|
+
awaiting_temperature=validated_temp
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
task = await self._task_handler.create_task(
|
|
96
|
+
task_function=start_set_temperature, id=params.taskId
|
|
97
|
+
)
|
|
98
|
+
|
|
70
99
|
return SuccessData(
|
|
71
|
-
public=SetTargetTemperatureResult(
|
|
100
|
+
public=SetTargetTemperatureResult(
|
|
101
|
+
targetTemperature=validated_temp, taskId=task.id
|
|
102
|
+
),
|
|
72
103
|
)
|
|
73
104
|
|
|
74
105
|
|
|
@@ -73,6 +73,15 @@ from .run_profile import (
|
|
|
73
73
|
RunProfileCreate,
|
|
74
74
|
)
|
|
75
75
|
|
|
76
|
+
from .start_run_extended_profile import (
|
|
77
|
+
StartRunExtendedProfileCommandType,
|
|
78
|
+
StartRunExtendedProfileParams,
|
|
79
|
+
StartRunExtendedProfileStepParams,
|
|
80
|
+
StartRunExtendedProfileResult,
|
|
81
|
+
StartRunExtendedProfile,
|
|
82
|
+
StartRunExtendedProfileCreate,
|
|
83
|
+
)
|
|
84
|
+
|
|
76
85
|
from .run_extended_profile import (
|
|
77
86
|
RunExtendedProfileCommandType,
|
|
78
87
|
RunExtendedProfileParams,
|
|
@@ -140,6 +149,13 @@ __all__ = [
|
|
|
140
149
|
"RunProfileResult",
|
|
141
150
|
"RunProfile",
|
|
142
151
|
"RunProfileCreate",
|
|
152
|
+
# Start run profile command models,
|
|
153
|
+
"StartRunExtendedProfileCommandType",
|
|
154
|
+
"StartRunExtendedProfileParams",
|
|
155
|
+
"StartRunExtendedProfileStepParams",
|
|
156
|
+
"StartRunExtendedProfileResult",
|
|
157
|
+
"StartRunExtendedProfile",
|
|
158
|
+
"StartRunExtendedProfileCreate",
|
|
143
159
|
# Run extended profile command models.
|
|
144
160
|
"RunExtendedProfileCommandType",
|
|
145
161
|
"RunExtendedProfileParams",
|
|
@@ -33,6 +33,11 @@ class ProfileStep(BaseModel):
|
|
|
33
33
|
holdSeconds: float = Field(
|
|
34
34
|
..., description="Time to hold target temperature in seconds."
|
|
35
35
|
)
|
|
36
|
+
rampRate: float | SkipJsonSchema[None] = Field(
|
|
37
|
+
None,
|
|
38
|
+
description="How quickly to change temperature in °C/second.",
|
|
39
|
+
json_schema_extra=_remove_default,
|
|
40
|
+
)
|
|
36
41
|
|
|
37
42
|
|
|
38
43
|
class ProfileCycle(BaseModel):
|
|
@@ -68,6 +73,7 @@ def _transform_profile_step(
|
|
|
68
73
|
return ThermocyclerStep(
|
|
69
74
|
temperature=thermocycler_state.validate_target_block_temperature(step.celsius),
|
|
70
75
|
hold_time_seconds=step.holdSeconds,
|
|
76
|
+
ramp_rate=thermocycler_state.validate_ramp_rate(step.rampRate, step.celsius),
|
|
71
77
|
)
|
|
72
78
|
|
|
73
79
|
|
|
@@ -30,6 +30,11 @@ class RunProfileStepParams(BaseModel):
|
|
|
30
30
|
holdSeconds: float = Field(
|
|
31
31
|
..., description="Time to hold target temperature at in seconds."
|
|
32
32
|
)
|
|
33
|
+
rampRate: float | SkipJsonSchema[None] = Field(
|
|
34
|
+
None,
|
|
35
|
+
description="How quickly to change temperature in °C/second.",
|
|
36
|
+
json_schema_extra=_remove_default,
|
|
37
|
+
)
|
|
33
38
|
|
|
34
39
|
|
|
35
40
|
class RunProfileParams(BaseModel):
|
|
@@ -81,6 +86,9 @@ class RunProfileImpl(
|
|
|
81
86
|
profile_step.celsius
|
|
82
87
|
),
|
|
83
88
|
hold_time_seconds=profile_step.holdSeconds,
|
|
89
|
+
ramp_rate=thermocycler_state.validate_ramp_rate(
|
|
90
|
+
profile_step.rampRate, profile_step.celsius
|
|
91
|
+
),
|
|
84
92
|
)
|
|
85
93
|
for profile_step in params.profile
|
|
86
94
|
]
|
|
@@ -11,7 +11,7 @@ from ...errors.error_occurrence import ErrorOccurrence
|
|
|
11
11
|
|
|
12
12
|
if TYPE_CHECKING:
|
|
13
13
|
from opentrons.protocol_engine.state.state import StateView
|
|
14
|
-
from opentrons.protocol_engine.execution import EquipmentHandler
|
|
14
|
+
from opentrons.protocol_engine.execution import EquipmentHandler, TaskHandler
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
SetTargetBlockTemperatureCommandType = Literal["thermocycler/setTargetBlockTemperature"]
|
|
@@ -39,6 +39,17 @@ class SetTargetBlockTemperatureParams(BaseModel):
|
|
|
39
39
|
" the given hold time has elapsed.",
|
|
40
40
|
json_schema_extra=_remove_default,
|
|
41
41
|
)
|
|
42
|
+
ramp_rate: float | SkipJsonSchema[None] = Field(
|
|
43
|
+
None,
|
|
44
|
+
description="The rate in C°/second to change temperature from the current target."
|
|
45
|
+
" If unspecified, the Thermocycler will change temperature at the fastest possible rate.",
|
|
46
|
+
json_schema_extra=_remove_default,
|
|
47
|
+
)
|
|
48
|
+
taskId: str | SkipJsonSchema[None] = Field(
|
|
49
|
+
None,
|
|
50
|
+
description="Id for the background task that manages the temperature.",
|
|
51
|
+
json_schema_extra=_remove_default,
|
|
52
|
+
)
|
|
42
53
|
|
|
43
54
|
|
|
44
55
|
class SetTargetBlockTemperatureResult(BaseModel):
|
|
@@ -48,6 +59,11 @@ class SetTargetBlockTemperatureResult(BaseModel):
|
|
|
48
59
|
...,
|
|
49
60
|
description="The target block temperature that was set after validation.",
|
|
50
61
|
)
|
|
62
|
+
taskId: str | SkipJsonSchema[None] = Field(
|
|
63
|
+
None,
|
|
64
|
+
description="The task id for the setTargetBlockTemperature",
|
|
65
|
+
json_schema_extra=_remove_default,
|
|
66
|
+
)
|
|
51
67
|
|
|
52
68
|
|
|
53
69
|
class SetTargetBlockTemperatureImpl(
|
|
@@ -62,10 +78,12 @@ class SetTargetBlockTemperatureImpl(
|
|
|
62
78
|
self,
|
|
63
79
|
state_view: StateView,
|
|
64
80
|
equipment: EquipmentHandler,
|
|
81
|
+
task_handler: TaskHandler,
|
|
65
82
|
**unused_dependencies: object,
|
|
66
83
|
) -> None:
|
|
67
84
|
self._state_view = state_view
|
|
68
85
|
self._equipment = equipment
|
|
86
|
+
self._task_handler = task_handler
|
|
69
87
|
|
|
70
88
|
async def execute(
|
|
71
89
|
self,
|
|
@@ -90,19 +108,35 @@ class SetTargetBlockTemperatureImpl(
|
|
|
90
108
|
hold_time = thermocycler_state.validate_hold_time(params.holdTimeSeconds)
|
|
91
109
|
else:
|
|
92
110
|
hold_time = None
|
|
111
|
+
target_ramp_rate: Optional[float]
|
|
112
|
+
if params.ramp_rate is not None:
|
|
113
|
+
target_ramp_rate = thermocycler_state.validate_ramp_rate(
|
|
114
|
+
params.ramp_rate, target_temperature
|
|
115
|
+
)
|
|
116
|
+
else:
|
|
117
|
+
target_ramp_rate = None
|
|
93
118
|
|
|
94
119
|
thermocycler_hardware = self._equipment.get_module_hardware_api(
|
|
95
120
|
thermocycler_state.module_id
|
|
96
121
|
)
|
|
97
122
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
123
|
+
async def set_target_block_temperature(task_handler: TaskHandler) -> None:
|
|
124
|
+
if thermocycler_hardware is not None:
|
|
125
|
+
await thermocycler_hardware.set_target_block_temperature(
|
|
126
|
+
target_temperature,
|
|
127
|
+
volume=target_volume,
|
|
128
|
+
hold_time_seconds=hold_time,
|
|
129
|
+
ramp_rate=target_ramp_rate,
|
|
130
|
+
)
|
|
131
|
+
await thermocycler_hardware.wait_for_block_target()
|
|
132
|
+
|
|
133
|
+
task = await self._task_handler.create_task(
|
|
134
|
+
task_function=set_target_block_temperature, id=params.taskId
|
|
135
|
+
)
|
|
102
136
|
|
|
103
137
|
return SuccessData(
|
|
104
138
|
public=SetTargetBlockTemperatureResult(
|
|
105
|
-
targetBlockTemperature=target_temperature
|
|
139
|
+
targetBlockTemperature=target_temperature, taskId=task.id
|
|
106
140
|
),
|
|
107
141
|
)
|
|
108
142
|
|