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
|
@@ -38,6 +38,7 @@ from ..types import (
|
|
|
38
38
|
LabwareLocationSequence,
|
|
39
39
|
NotOnDeckLocationSequenceComponent,
|
|
40
40
|
OFF_DECK_LOCATION,
|
|
41
|
+
WASTE_CHUTE_LOCATION,
|
|
41
42
|
)
|
|
42
43
|
from ..errors import (
|
|
43
44
|
LabwareMovementNotAllowedError,
|
|
@@ -228,9 +229,10 @@ class MoveLabwareImplementation(AbstractCommandImpl[MoveLabwareParams, _ExecuteR
|
|
|
228
229
|
)
|
|
229
230
|
eventual_destination_location_sequence = [
|
|
230
231
|
NotOnDeckLocationSequenceComponent(
|
|
231
|
-
logicalLocationName=
|
|
232
|
+
logicalLocationName=WASTE_CHUTE_LOCATION
|
|
232
233
|
)
|
|
233
234
|
]
|
|
235
|
+
|
|
234
236
|
elif fixture_validation.is_trash(area_name):
|
|
235
237
|
# When dropping labware in the trash bins we want to ensure they are lids
|
|
236
238
|
# and enforce a y-axis drop offset to ensure they fall within the trash bin
|
|
@@ -351,7 +353,6 @@ class MoveLabwareImplementation(AbstractCommandImpl[MoveLabwareParams, _ExecuteR
|
|
|
351
353
|
validated_new_loc = self._state_view.geometry.ensure_valid_gripper_location(
|
|
352
354
|
available_new_location,
|
|
353
355
|
)
|
|
354
|
-
|
|
355
356
|
user_pick_up_offset = (
|
|
356
357
|
Point.from_xyz_attrs(params.pickUpOffset)
|
|
357
358
|
if params.pickUpOffset is not None
|
|
@@ -464,7 +465,6 @@ class MoveLabwareImplementation(AbstractCommandImpl[MoveLabwareParams, _ExecuteR
|
|
|
464
465
|
new_location=available_new_location,
|
|
465
466
|
new_offset_id=new_offset_id,
|
|
466
467
|
)
|
|
467
|
-
|
|
468
468
|
if labware_validation.validate_definition_is_lid(
|
|
469
469
|
definition=self._state_view.labware.get_definition(params.labwareId)
|
|
470
470
|
):
|
|
@@ -498,7 +498,6 @@ class MoveLabwareImplementation(AbstractCommandImpl[MoveLabwareParams, _ExecuteR
|
|
|
498
498
|
parent_labware_ids=parent_updates,
|
|
499
499
|
lid_ids=lid_updates,
|
|
500
500
|
)
|
|
501
|
-
|
|
502
501
|
return SuccessData(
|
|
503
502
|
public=MoveLabwareResult(
|
|
504
503
|
offsetId=new_offset_id,
|
|
@@ -85,7 +85,7 @@ class MoveToAddressableAreaForDropTipParams(PipetteIdMixin, MovementMixin):
|
|
|
85
85
|
ignoreTipConfiguration: bool | SkipJsonSchema[None] = Field(
|
|
86
86
|
True,
|
|
87
87
|
description=(
|
|
88
|
-
"Whether to utilize the critical point of the tip
|
|
88
|
+
"Whether to utilize the critical point of the tip configuration when moving to an addressable area."
|
|
89
89
|
" If True, this command will ignore the tip configuration and use the center of the entire instrument"
|
|
90
90
|
" as the critical point for movement."
|
|
91
91
|
" If False, this command will use the critical point provided by the current tip configuration."
|
|
@@ -48,8 +48,8 @@ class WellLocationMixin(BaseModel):
|
|
|
48
48
|
)
|
|
49
49
|
|
|
50
50
|
|
|
51
|
-
class
|
|
52
|
-
"""Mixin for command requests that take a
|
|
51
|
+
class LiquidHandlingWellMixin(BaseModel):
|
|
52
|
+
"""Base Mixin for command requests that take a well."""
|
|
53
53
|
|
|
54
54
|
labwareId: str = Field(
|
|
55
55
|
...,
|
|
@@ -59,12 +59,39 @@ class LiquidHandlingWellLocationMixin(BaseModel):
|
|
|
59
59
|
...,
|
|
60
60
|
description="Name of well to use in labware.",
|
|
61
61
|
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class LiquidHandlingWellLocationMixin(LiquidHandlingWellMixin):
|
|
65
|
+
"""Mixin for command requests that take a location that's somewhere in a well."""
|
|
66
|
+
|
|
62
67
|
wellLocation: LiquidHandlingWellLocation = Field(
|
|
63
68
|
default_factory=LiquidHandlingWellLocation,
|
|
64
69
|
description="Relative well location at which to perform the operation",
|
|
65
70
|
)
|
|
66
71
|
|
|
67
72
|
|
|
73
|
+
class DynamicLiquidHandlingWellLocationMixin(LiquidHandlingWellMixin):
|
|
74
|
+
"""Mixin for command requests that move between two locations in a well."""
|
|
75
|
+
|
|
76
|
+
trackFromLocation: LiquidHandlingWellLocation = Field(
|
|
77
|
+
default_factory=LiquidHandlingWellLocation,
|
|
78
|
+
description="Relative well location at which to start the operation",
|
|
79
|
+
)
|
|
80
|
+
trackToLocation: LiquidHandlingWellLocation = Field(
|
|
81
|
+
default_factory=LiquidHandlingWellLocation,
|
|
82
|
+
description="Relative well location at which to end the operation",
|
|
83
|
+
)
|
|
84
|
+
movement_delay: float | SkipJsonSchema[None] = Field(
|
|
85
|
+
None,
|
|
86
|
+
description=(
|
|
87
|
+
"Optional movement delay in seconds."
|
|
88
|
+
" When this argument is used, the x/y/z movement will wait movement_delay seconds"
|
|
89
|
+
" after the pipetting action starts before beginning the x/y/z move."
|
|
90
|
+
),
|
|
91
|
+
json_schema_extra=_remove_default,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
|
|
68
95
|
class MovementMixin(BaseModel):
|
|
69
96
|
"""Mixin for command requests that move a pipette."""
|
|
70
97
|
|
|
@@ -152,6 +179,7 @@ async def move_to_well(
|
|
|
152
179
|
minimum_z_height: Optional[float] = None,
|
|
153
180
|
speed: Optional[float] = None,
|
|
154
181
|
operation_volume: Optional[float] = None,
|
|
182
|
+
offset_pipette_for_reservoir_subwells: bool = False,
|
|
155
183
|
) -> MoveToWellOperationReturn:
|
|
156
184
|
"""Execute a move to well microoperation."""
|
|
157
185
|
try:
|
|
@@ -165,6 +193,7 @@ async def move_to_well(
|
|
|
165
193
|
minimum_z_height=minimum_z_height,
|
|
166
194
|
speed=speed,
|
|
167
195
|
operation_volume=operation_volume,
|
|
196
|
+
offset_pipette_for_reservoir_subwells=offset_pipette_for_reservoir_subwells,
|
|
168
197
|
)
|
|
169
198
|
except StallOrCollisionDetectedError as e:
|
|
170
199
|
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(
|
|
@@ -9,10 +9,15 @@ from pydantic import BaseModel, Field
|
|
|
9
9
|
from opentrons_shared_data.errors import ErrorCodes
|
|
10
10
|
from opentrons.protocol_engine.errors.error_occurrence import ErrorOccurrence
|
|
11
11
|
from opentrons.protocol_engine.types import AspiratedFluid, FluidKind
|
|
12
|
-
from opentrons_shared_data.errors.exceptions import
|
|
12
|
+
from opentrons_shared_data.errors.exceptions import (
|
|
13
|
+
PipetteOverpressureError,
|
|
14
|
+
StallOrCollisionDetectedError,
|
|
15
|
+
)
|
|
13
16
|
from .command import DefinedErrorData, SuccessData
|
|
14
17
|
from opentrons.protocol_engine.state.update_types import StateUpdate
|
|
18
|
+
from opentrons.types import Point
|
|
15
19
|
|
|
20
|
+
from .movement_common import StallOrCollisionError
|
|
16
21
|
|
|
17
22
|
if TYPE_CHECKING:
|
|
18
23
|
from ..execution.pipetting import PipettingHandler
|
|
@@ -241,11 +246,15 @@ async def aspirate_while_tracking(
|
|
|
241
246
|
well_name: str,
|
|
242
247
|
volume: float,
|
|
243
248
|
flow_rate: float,
|
|
249
|
+
end_point: Point,
|
|
244
250
|
location_if_error: ErrorLocationInfo,
|
|
245
251
|
command_note_adder: CommandNoteAdder,
|
|
246
252
|
pipetting: PipettingHandler,
|
|
247
253
|
model_utils: ModelUtils,
|
|
248
|
-
|
|
254
|
+
movement_delay: Optional[float] = None,
|
|
255
|
+
) -> SuccessData[BaseLiquidHandlingResult] | DefinedErrorData[
|
|
256
|
+
OverpressureError
|
|
257
|
+
] | DefinedErrorData[StallOrCollisionError]:
|
|
249
258
|
"""Execute an aspirate while tracking microoperation."""
|
|
250
259
|
try:
|
|
251
260
|
volume_aspirated = await pipetting.aspirate_while_tracking(
|
|
@@ -254,6 +263,7 @@ async def aspirate_while_tracking(
|
|
|
254
263
|
well_name=well_name,
|
|
255
264
|
volume=volume,
|
|
256
265
|
flow_rate=flow_rate,
|
|
266
|
+
end_point=end_point,
|
|
257
267
|
command_note_adder=command_note_adder,
|
|
258
268
|
)
|
|
259
269
|
except PipetteOverpressureError as e:
|
|
@@ -272,6 +282,21 @@ async def aspirate_while_tracking(
|
|
|
272
282
|
),
|
|
273
283
|
state_update=StateUpdate().set_fluid_unknown(pipette_id=pipette_id),
|
|
274
284
|
)
|
|
285
|
+
except StallOrCollisionDetectedError as e:
|
|
286
|
+
return DefinedErrorData(
|
|
287
|
+
public=StallOrCollisionError(
|
|
288
|
+
id=model_utils.generate_id(),
|
|
289
|
+
createdAt=model_utils.get_timestamp(),
|
|
290
|
+
wrappedErrors=[
|
|
291
|
+
ErrorOccurrence.from_failed(
|
|
292
|
+
id=model_utils.generate_id(),
|
|
293
|
+
createdAt=model_utils.get_timestamp(),
|
|
294
|
+
error=e,
|
|
295
|
+
)
|
|
296
|
+
],
|
|
297
|
+
),
|
|
298
|
+
state_update=StateUpdate().clear_all_pipette_locations(),
|
|
299
|
+
)
|
|
275
300
|
else:
|
|
276
301
|
return SuccessData(
|
|
277
302
|
public=BaseLiquidHandlingResult(
|
|
@@ -290,11 +315,15 @@ async def dispense_while_tracking(
|
|
|
290
315
|
well_name: str,
|
|
291
316
|
volume: float,
|
|
292
317
|
flow_rate: float,
|
|
318
|
+
end_point: Point,
|
|
293
319
|
push_out: float | None,
|
|
294
320
|
location_if_error: ErrorLocationInfo,
|
|
295
321
|
pipetting: PipettingHandler,
|
|
296
322
|
model_utils: ModelUtils,
|
|
297
|
-
|
|
323
|
+
movement_delay: Optional[float] = None,
|
|
324
|
+
) -> SuccessData[BaseLiquidHandlingResult] | DefinedErrorData[
|
|
325
|
+
OverpressureError
|
|
326
|
+
] | DefinedErrorData[StallOrCollisionError]:
|
|
298
327
|
"""Execute an dispense while tracking microoperation."""
|
|
299
328
|
# The current volume won't be none since it passed validation
|
|
300
329
|
current_volume = (
|
|
@@ -309,6 +338,7 @@ async def dispense_while_tracking(
|
|
|
309
338
|
well_name=well_name,
|
|
310
339
|
volume=volume,
|
|
311
340
|
flow_rate=flow_rate,
|
|
341
|
+
end_point=end_point,
|
|
312
342
|
push_out=push_out,
|
|
313
343
|
is_full_dispense=is_full_dispense,
|
|
314
344
|
)
|
|
@@ -332,6 +362,21 @@ async def dispense_while_tracking(
|
|
|
332
362
|
pipette_id=pipette_id, ready_to_aspirate=False
|
|
333
363
|
),
|
|
334
364
|
)
|
|
365
|
+
except StallOrCollisionDetectedError as e:
|
|
366
|
+
return DefinedErrorData(
|
|
367
|
+
public=StallOrCollisionError(
|
|
368
|
+
id=model_utils.generate_id(),
|
|
369
|
+
createdAt=model_utils.get_timestamp(),
|
|
370
|
+
wrappedErrors=[
|
|
371
|
+
ErrorOccurrence.from_failed(
|
|
372
|
+
id=model_utils.generate_id(),
|
|
373
|
+
createdAt=model_utils.get_timestamp(),
|
|
374
|
+
error=e,
|
|
375
|
+
)
|
|
376
|
+
],
|
|
377
|
+
),
|
|
378
|
+
state_update=StateUpdate().clear_all_pipette_locations(),
|
|
379
|
+
)
|
|
335
380
|
else:
|
|
336
381
|
return SuccessData(
|
|
337
382
|
public=BaseLiquidHandlingResult(
|
|
@@ -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
|
]
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Command models
|
|
1
|
+
"""Command models for heating a Thermocycler's block."""
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
from typing import Optional, TYPE_CHECKING, Any
|
|
4
4
|
from typing_extensions import Literal, Type
|
|
@@ -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="Id for the background task that manages the temperature.",
|
|
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,38 @@ 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
|
+
async with task_handler.synchronize_cancel_latest(
|
|
126
|
+
thermocycler_state.module_id + "-block"
|
|
127
|
+
):
|
|
128
|
+
await thermocycler_hardware.set_target_block_temperature(
|
|
129
|
+
celsius=target_temperature,
|
|
130
|
+
volume=target_volume,
|
|
131
|
+
ramp_rate=target_ramp_rate,
|
|
132
|
+
hold_time_seconds=hold_time,
|
|
133
|
+
)
|
|
134
|
+
await thermocycler_hardware.wait_for_block_target()
|
|
135
|
+
|
|
136
|
+
task = await self._task_handler.create_task(
|
|
137
|
+
task_function=set_target_block_temperature, id=params.taskId
|
|
138
|
+
)
|
|
102
139
|
|
|
103
140
|
return SuccessData(
|
|
104
141
|
public=SetTargetBlockTemperatureResult(
|
|
105
|
-
targetBlockTemperature=target_temperature
|
|
142
|
+
targetBlockTemperature=target_temperature, taskId=task.id
|
|
106
143
|
),
|
|
107
144
|
)
|
|
108
145
|
|