opentrons 8.7.0a7__py3-none-any.whl → 8.8.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.
- opentrons/_version.py +2 -2
- opentrons/cli/analyze.py +4 -1
- opentrons/config/__init__.py +7 -0
- opentrons/drivers/asyncio/communication/serial_connection.py +8 -5
- opentrons/drivers/flex_stacker/driver.py +6 -1
- 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/backends/flex_protocol.py +2 -0
- opentrons/hardware_control/backends/ot3controller.py +35 -2
- opentrons/hardware_control/backends/ot3simulator.py +2 -0
- opentrons/hardware_control/backends/ot3utils.py +37 -0
- opentrons/hardware_control/module_control.py +23 -2
- opentrons/hardware_control/modules/mod_abc.py +1 -1
- opentrons/hardware_control/modules/types.py +1 -1
- opentrons/hardware_control/motion_utilities.py +6 -6
- opentrons/hardware_control/ot3api.py +62 -13
- opentrons/hardware_control/protocols/gripper_controller.py +1 -0
- opentrons/hardware_control/protocols/liquid_handler.py +6 -2
- opentrons/hardware_control/types.py +12 -0
- opentrons/legacy_commands/commands.py +58 -5
- opentrons/legacy_commands/module_commands.py +29 -0
- opentrons/legacy_commands/protocol_commands.py +33 -1
- opentrons/legacy_commands/types.py +75 -1
- opentrons/protocol_api/_transfer_liquid_validation.py +17 -2
- opentrons/protocol_api/_types.py +2 -0
- opentrons/protocol_api/core/engine/_default_labware_versions.py +1 -0
- opentrons/protocol_api/core/engine/deck_conflict.py +3 -1
- opentrons/protocol_api/core/engine/instrument.py +109 -26
- opentrons/protocol_api/core/engine/module_core.py +27 -3
- opentrons/protocol_api/core/engine/protocol.py +33 -1
- opentrons/protocol_api/core/engine/stringify.py +2 -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 +15 -4
- opentrons/protocol_api/core/legacy/legacy_protocol_core.py +12 -0
- opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +19 -2
- opentrons/protocol_api/core/module.py +25 -2
- opentrons/protocol_api/core/protocol.py +12 -0
- opentrons/protocol_api/instrument_context.py +388 -2
- opentrons/protocol_api/labware.py +5 -2
- opentrons/protocol_api/module_contexts.py +133 -30
- opentrons/protocol_api/protocol_context.py +61 -17
- opentrons/protocol_api/robot_context.py +3 -4
- opentrons/protocol_api/validation.py +43 -2
- opentrons/protocol_engine/__init__.py +4 -0
- opentrons/protocol_engine/actions/__init__.py +2 -0
- opentrons/protocol_engine/actions/actions.py +9 -0
- opentrons/protocol_engine/commands/__init__.py +14 -0
- opentrons/protocol_engine/commands/absorbance_reader/read.py +22 -23
- 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 +1 -0
- opentrons/protocol_engine/commands/command_unions.py +13 -0
- opentrons/protocol_engine/commands/dispense_while_tracking.py +56 -19
- 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/set_shake_speed.py +1 -1
- opentrons/protocol_engine/commands/heater_shaker/set_target_temperature.py +1 -1
- 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 +29 -2
- opentrons/protocol_engine/commands/pipetting_common.py +48 -3
- opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py +12 -9
- opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py +17 -12
- opentrons/protocol_engine/commands/thermocycler/start_run_extended_profile.py +1 -1
- opentrons/protocol_engine/create_protocol_engine.py +12 -0
- opentrons/protocol_engine/engine_support.py +3 -0
- opentrons/protocol_engine/errors/__init__.py +8 -0
- opentrons/protocol_engine/errors/exceptions.py +64 -0
- opentrons/protocol_engine/execution/__init__.py +2 -0
- opentrons/protocol_engine/execution/command_executor.py +54 -1
- opentrons/protocol_engine/execution/create_queue_worker.py +4 -1
- opentrons/protocol_engine/execution/labware_movement.py +13 -4
- opentrons/protocol_engine/execution/pipetting.py +19 -25
- opentrons/protocol_engine/protocol_engine.py +62 -2
- opentrons/protocol_engine/resources/__init__.py +2 -0
- opentrons/protocol_engine/resources/camera_provider.py +110 -0
- opentrons/protocol_engine/resources/file_provider.py +133 -58
- opentrons/protocol_engine/slot_standardization.py +2 -0
- opentrons/protocol_engine/state/camera.py +54 -0
- opentrons/protocol_engine/state/commands.py +24 -4
- opentrons/protocol_engine/state/geometry.py +68 -10
- opentrons/protocol_engine/state/labware.py +10 -6
- opentrons/protocol_engine/state/labware_origin_math/stackup_origin_to_labware_origin.py +6 -1
- opentrons/protocol_engine/state/modules.py +9 -0
- opentrons/protocol_engine/state/preconditions.py +59 -0
- opentrons/protocol_engine/state/state.py +30 -0
- opentrons/protocol_engine/state/state_summary.py +2 -0
- opentrons/protocol_engine/state/update_types.py +10 -0
- opentrons/protocol_engine/types/__init__.py +14 -1
- opentrons/protocol_engine/types/command_preconditions.py +18 -0
- opentrons/protocol_engine/types/location.py +26 -2
- opentrons/protocol_engine/types/module.py +1 -1
- opentrons/protocol_runner/protocol_runner.py +14 -1
- opentrons/protocol_runner/run_orchestrator.py +31 -0
- opentrons/protocols/advanced_control/transfers/transfer_liquid_utils.py +2 -2
- opentrons/simulate.py +3 -0
- opentrons/system/camera.py +333 -3
- opentrons/system/ffmpeg.py +110 -0
- {opentrons-8.7.0a7.dist-info → opentrons-8.8.0a7.dist-info}/METADATA +4 -4
- {opentrons-8.7.0a7.dist-info → opentrons-8.8.0a7.dist-info}/RECORD +109 -97
- {opentrons-8.7.0a7.dist-info → opentrons-8.8.0a7.dist-info}/WHEEL +0 -0
- {opentrons-8.7.0a7.dist-info → opentrons-8.8.0a7.dist-info}/entry_points.txt +0 -0
- {opentrons-8.7.0a7.dist-info → opentrons-8.8.0a7.dist-info}/licenses/LICENSE +0 -0
|
@@ -82,7 +82,7 @@ class SetTargetTemperatureImpl(
|
|
|
82
82
|
async def start_set_temperature(task_handler: TaskHandler) -> None:
|
|
83
83
|
if hs_hardware_module is not None:
|
|
84
84
|
async with task_handler.synchronize_cancel_previous(
|
|
85
|
-
hs_module_substate.module_id
|
|
85
|
+
hs_module_substate.module_id + "-temp"
|
|
86
86
|
):
|
|
87
87
|
await hs_hardware_module.start_set_temperature(validated_temp)
|
|
88
88
|
await hs_hardware_module.await_temperature(validated_temp)
|
|
@@ -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
|
|
|
@@ -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(
|
|
@@ -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
|
|
@@ -61,7 +61,7 @@ class SetTargetBlockTemperatureResult(BaseModel):
|
|
|
61
61
|
)
|
|
62
62
|
taskId: str | SkipJsonSchema[None] = Field(
|
|
63
63
|
None,
|
|
64
|
-
description="
|
|
64
|
+
description="Id for the background task that manages the temperature.",
|
|
65
65
|
json_schema_extra=_remove_default,
|
|
66
66
|
)
|
|
67
67
|
|
|
@@ -122,13 +122,16 @@ class SetTargetBlockTemperatureImpl(
|
|
|
122
122
|
|
|
123
123
|
async def set_target_block_temperature(task_handler: TaskHandler) -> None:
|
|
124
124
|
if thermocycler_hardware is not None:
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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()
|
|
132
135
|
|
|
133
136
|
task = await self._task_handler.create_task(
|
|
134
137
|
task_function=set_target_block_temperature, id=params.taskId
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Command models
|
|
1
|
+
"""Command models for heating a Thermocycler's lid."""
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
from typing import Optional, TYPE_CHECKING, Any
|
|
4
4
|
from typing_extensions import Literal, Type
|
|
@@ -13,7 +13,6 @@ if TYPE_CHECKING:
|
|
|
13
13
|
from opentrons.protocol_engine.state.state import StateView
|
|
14
14
|
from opentrons.protocol_engine.execution import EquipmentHandler, TaskHandler
|
|
15
15
|
|
|
16
|
-
|
|
17
16
|
SetTargetLidTemperatureCommandType = Literal["thermocycler/setTargetLidTemperature"]
|
|
18
17
|
|
|
19
18
|
|
|
@@ -22,7 +21,7 @@ def _remove_default(s: dict[str, Any]) -> None:
|
|
|
22
21
|
|
|
23
22
|
|
|
24
23
|
class SetTargetLidTemperatureParams(BaseModel):
|
|
25
|
-
"""Input parameters to set a Thermocycler's target lid temperature."""
|
|
24
|
+
"""Input parameters to to set a Thermocycler's target lid temperature."""
|
|
26
25
|
|
|
27
26
|
moduleId: str = Field(..., description="Unique ID of the Thermocycler Module.")
|
|
28
27
|
celsius: float = Field(..., description="Target temperature in °C.")
|
|
@@ -49,10 +48,11 @@ class SetTargetLidTemperatureResult(BaseModel):
|
|
|
49
48
|
|
|
50
49
|
class SetTargetLidTemperatureImpl(
|
|
51
50
|
AbstractCommandImpl[
|
|
52
|
-
SetTargetLidTemperatureParams,
|
|
51
|
+
SetTargetLidTemperatureParams,
|
|
52
|
+
SuccessData[SetTargetLidTemperatureResult],
|
|
53
53
|
]
|
|
54
54
|
):
|
|
55
|
-
"""Execution implementation of a Thermocycler's set lid temperature command."""
|
|
55
|
+
"""Execution implementation of a Thermocycler's to set lid temperature command."""
|
|
56
56
|
|
|
57
57
|
def __init__(
|
|
58
58
|
self,
|
|
@@ -69,7 +69,7 @@ class SetTargetLidTemperatureImpl(
|
|
|
69
69
|
self,
|
|
70
70
|
params: SetTargetLidTemperatureParams,
|
|
71
71
|
) -> SuccessData[SetTargetLidTemperatureResult]:
|
|
72
|
-
"""
|
|
72
|
+
"""To set a Thermocycler's target lid temperature."""
|
|
73
73
|
thermocycler_state = self._state_view.modules.get_thermocycler_module_substate(
|
|
74
74
|
params.moduleId
|
|
75
75
|
)
|
|
@@ -82,10 +82,13 @@ class SetTargetLidTemperatureImpl(
|
|
|
82
82
|
|
|
83
83
|
async def set_target_lid_temperature(task_handler: TaskHandler) -> None:
|
|
84
84
|
if thermocycler_hardware is not None:
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
)
|
|
88
|
-
|
|
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()
|
|
89
92
|
|
|
90
93
|
task = await self._task_handler.create_task(
|
|
91
94
|
task_function=set_target_lid_temperature, id=params.taskId
|
|
@@ -99,10 +102,12 @@ class SetTargetLidTemperatureImpl(
|
|
|
99
102
|
|
|
100
103
|
class SetTargetLidTemperature(
|
|
101
104
|
BaseCommand[
|
|
102
|
-
SetTargetLidTemperatureParams,
|
|
105
|
+
SetTargetLidTemperatureParams,
|
|
106
|
+
SetTargetLidTemperatureResult,
|
|
107
|
+
ErrorOccurrence,
|
|
103
108
|
]
|
|
104
109
|
):
|
|
105
|
-
"""A command to set a Thermocycler's target lid temperature."""
|
|
110
|
+
"""A command to to set a Thermocycler's target lid temperature."""
|
|
106
111
|
|
|
107
112
|
commandType: SetTargetLidTemperatureCommandType = (
|
|
108
113
|
"thermocycler/setTargetLidTemperature"
|
|
@@ -150,7 +150,7 @@ class StartRunExtendedProfileImpl(
|
|
|
150
150
|
async def start_run_profile(task_handler: TaskHandler) -> None:
|
|
151
151
|
if thermocycler_hardware is not None:
|
|
152
152
|
async with task_handler.synchronize_cancel_latest(
|
|
153
|
-
thermocycler_state.module_id
|
|
153
|
+
thermocycler_state.module_id + "-block"
|
|
154
154
|
):
|
|
155
155
|
await thermocycler_hardware.execute_profile(
|
|
156
156
|
profile=profile, volume=target_volume
|
|
@@ -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,
|
|
@@ -84,6 +84,7 @@ from .exceptions import (
|
|
|
84
84
|
OperationLocationNotInWellError,
|
|
85
85
|
InvalidDispenseVolumeError,
|
|
86
86
|
StorageLimitReachedError,
|
|
87
|
+
FileNameInvalidError,
|
|
87
88
|
InvalidLiquidError,
|
|
88
89
|
LiquidClassDoesNotExistError,
|
|
89
90
|
LiquidClassRedefinitionError,
|
|
@@ -92,6 +93,9 @@ from .exceptions import (
|
|
|
92
93
|
FlexStackerNotLogicallyEmptyError,
|
|
93
94
|
InvalidLabwarePositionError,
|
|
94
95
|
InvalidModuleOrientation,
|
|
96
|
+
CameraCaptureError,
|
|
97
|
+
CameraDisabledError,
|
|
98
|
+
CameraSettingsInvalidError,
|
|
95
99
|
)
|
|
96
100
|
|
|
97
101
|
from .error_occurrence import ErrorOccurrence, ProtocolCommandFailedError
|
|
@@ -190,6 +194,10 @@ __all__ = [
|
|
|
190
194
|
"OperationLocationNotInWellError",
|
|
191
195
|
"InvalidDispenseVolumeError",
|
|
192
196
|
"StorageLimitReachedError",
|
|
197
|
+
"FileNameInvalidError",
|
|
193
198
|
"LiquidClassDoesNotExistError",
|
|
194
199
|
"LiquidClassRedefinitionError",
|
|
200
|
+
"CameraCaptureError",
|
|
201
|
+
"CameraDisabledError",
|
|
202
|
+
"CameraSettingsInvalidError",
|
|
195
203
|
]
|
|
@@ -1291,6 +1291,19 @@ class StorageLimitReachedError(ProtocolEngineError):
|
|
|
1291
1291
|
super().__init__(ErrorCodes.GENERAL_ERROR, message, detail, wrapping)
|
|
1292
1292
|
|
|
1293
1293
|
|
|
1294
|
+
class FileNameInvalidError(ProtocolEngineError):
|
|
1295
|
+
"""Raised to indicate that a file cannot be saved with a given name."""
|
|
1296
|
+
|
|
1297
|
+
def __init__(
|
|
1298
|
+
self,
|
|
1299
|
+
message: Optional[str] = None,
|
|
1300
|
+
detail: Optional[Dict[str, str]] = None,
|
|
1301
|
+
wrapping: Optional[Sequence[EnumeratedError]] = None,
|
|
1302
|
+
) -> None:
|
|
1303
|
+
"""Build an FileNameInvalidError."""
|
|
1304
|
+
super().__init__(ErrorCodes.GENERAL_ERROR, message, detail, wrapping)
|
|
1305
|
+
|
|
1306
|
+
|
|
1294
1307
|
class LiquidClassDoesNotExistError(ProtocolEngineError):
|
|
1295
1308
|
"""Raised when referencing a liquid class that has not been loaded."""
|
|
1296
1309
|
|
|
@@ -1339,6 +1352,18 @@ class FlexStackerLabwarePoolNotYetDefinedError(ProtocolEngineError):
|
|
|
1339
1352
|
super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
|
|
1340
1353
|
|
|
1341
1354
|
|
|
1355
|
+
class LabwarePoolNotCompatibleWithModuleError(ProtocolEngineError):
|
|
1356
|
+
"""Raised when attempting to use a labware pool that is incompatible with a module."""
|
|
1357
|
+
|
|
1358
|
+
def __init__(
|
|
1359
|
+
self,
|
|
1360
|
+
message: Optional[str] = None,
|
|
1361
|
+
details: Optional[dict[str, Any]] = None,
|
|
1362
|
+
wrapping: Optional[Sequence[EnumeratedError]] = None,
|
|
1363
|
+
) -> None:
|
|
1364
|
+
super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
|
|
1365
|
+
|
|
1366
|
+
|
|
1342
1367
|
class InvalidLabwarePositionError(ProtocolEngineError):
|
|
1343
1368
|
"""Raised when a labware position is internally invalid."""
|
|
1344
1369
|
|
|
@@ -1361,3 +1386,42 @@ class InvalidModuleOrientation(ProtocolEngineError):
|
|
|
1361
1386
|
wrapping: Optional[Sequence[EnumeratedError]] = None,
|
|
1362
1387
|
) -> None:
|
|
1363
1388
|
super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
|
|
1389
|
+
|
|
1390
|
+
|
|
1391
|
+
class CameraCaptureError(ProtocolEngineError):
|
|
1392
|
+
"""Raised when an Camera Capture attempt fails."""
|
|
1393
|
+
|
|
1394
|
+
def __init__(
|
|
1395
|
+
self,
|
|
1396
|
+
message: Optional[str] = None,
|
|
1397
|
+
details: Optional[Dict[str, Any]] = None,
|
|
1398
|
+
wrapping: Optional[Sequence[EnumeratedError]] = None,
|
|
1399
|
+
) -> None:
|
|
1400
|
+
"""Build a CameraCaptureError."""
|
|
1401
|
+
super().__init__(ErrorCodes.CAMERA_ERROR, message, details, wrapping)
|
|
1402
|
+
|
|
1403
|
+
|
|
1404
|
+
class CameraDisabledError(ProtocolEngineError):
|
|
1405
|
+
"""Raised when a Camera was referenced while cameras are disabled."""
|
|
1406
|
+
|
|
1407
|
+
def __init__(
|
|
1408
|
+
self,
|
|
1409
|
+
message: Optional[str] = None,
|
|
1410
|
+
details: Optional[Dict[str, Any]] = None,
|
|
1411
|
+
wrapping: Optional[Sequence[EnumeratedError]] = None,
|
|
1412
|
+
) -> None:
|
|
1413
|
+
"""Build a CameraDisabledError."""
|
|
1414
|
+
super().__init__(ErrorCodes.CAMERA_ERROR, message, details, wrapping)
|
|
1415
|
+
|
|
1416
|
+
|
|
1417
|
+
class CameraSettingsInvalidError(ProtocolEngineError):
|
|
1418
|
+
"""Raised when a Camera was given invalid settings."""
|
|
1419
|
+
|
|
1420
|
+
def __init__(
|
|
1421
|
+
self,
|
|
1422
|
+
message: Optional[str] = None,
|
|
1423
|
+
details: Optional[Dict[str, Any]] = None,
|
|
1424
|
+
wrapping: Optional[Sequence[EnumeratedError]] = None,
|
|
1425
|
+
) -> None:
|
|
1426
|
+
"""Build a CameraSettingsInvalidError."""
|
|
1427
|
+
super().__init__(ErrorCodes.CAMERA_ERROR, message, details, wrapping)
|
|
@@ -23,6 +23,7 @@ from .door_watcher import DoorWatcher
|
|
|
23
23
|
from .status_bar import StatusBarHandler
|
|
24
24
|
from .task_handler import TaskHandler
|
|
25
25
|
from ..resources.file_provider import FileProvider
|
|
26
|
+
from ..resources.camera_provider import CameraProvider
|
|
26
27
|
|
|
27
28
|
# .thermocycler_movement_flagger omitted from package's public interface.
|
|
28
29
|
|
|
@@ -49,4 +50,5 @@ __all__ = [
|
|
|
49
50
|
"StatusBarHandler",
|
|
50
51
|
"TaskHandler",
|
|
51
52
|
"FileProvider",
|
|
53
|
+
"CameraProvider",
|
|
52
54
|
]
|