opentrons 8.1.0a0__py2.py3-none-any.whl → 8.2.0__py2.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/cli/analyze.py +71 -7
- opentrons/config/__init__.py +9 -0
- opentrons/config/advanced_settings.py +22 -0
- opentrons/config/defaults_ot3.py +14 -36
- opentrons/config/feature_flags.py +4 -0
- opentrons/config/types.py +6 -17
- opentrons/drivers/absorbance_reader/abstract.py +27 -3
- opentrons/drivers/absorbance_reader/async_byonoy.py +208 -154
- opentrons/drivers/absorbance_reader/driver.py +24 -15
- opentrons/drivers/absorbance_reader/hid_protocol.py +79 -50
- opentrons/drivers/absorbance_reader/simulator.py +32 -6
- opentrons/drivers/types.py +23 -1
- opentrons/execute.py +2 -2
- opentrons/hardware_control/api.py +18 -10
- opentrons/hardware_control/backends/controller.py +3 -2
- opentrons/hardware_control/backends/flex_protocol.py +11 -5
- opentrons/hardware_control/backends/ot3controller.py +18 -50
- opentrons/hardware_control/backends/ot3simulator.py +7 -6
- opentrons/hardware_control/backends/ot3utils.py +1 -0
- opentrons/hardware_control/instruments/ot2/pipette_handler.py +22 -82
- opentrons/hardware_control/instruments/ot3/pipette_handler.py +10 -2
- opentrons/hardware_control/module_control.py +43 -2
- opentrons/hardware_control/modules/__init__.py +7 -1
- opentrons/hardware_control/modules/absorbance_reader.py +232 -83
- opentrons/hardware_control/modules/errors.py +7 -0
- opentrons/hardware_control/modules/heater_shaker.py +8 -3
- opentrons/hardware_control/modules/magdeck.py +12 -3
- opentrons/hardware_control/modules/mod_abc.py +27 -2
- opentrons/hardware_control/modules/tempdeck.py +15 -7
- opentrons/hardware_control/modules/thermocycler.py +69 -3
- opentrons/hardware_control/modules/types.py +11 -5
- opentrons/hardware_control/modules/update.py +11 -5
- opentrons/hardware_control/modules/utils.py +3 -1
- opentrons/hardware_control/ot3_calibration.py +6 -6
- opentrons/hardware_control/ot3api.py +131 -94
- opentrons/hardware_control/poller.py +15 -11
- opentrons/hardware_control/protocols/__init__.py +1 -7
- opentrons/hardware_control/protocols/instrument_configurer.py +14 -2
- opentrons/hardware_control/protocols/liquid_handler.py +5 -0
- opentrons/hardware_control/protocols/position_estimator.py +3 -1
- opentrons/hardware_control/types.py +2 -0
- opentrons/legacy_commands/helpers.py +8 -2
- opentrons/motion_planning/__init__.py +2 -0
- opentrons/motion_planning/waypoints.py +32 -0
- opentrons/protocol_api/__init__.py +2 -1
- opentrons/protocol_api/_liquid.py +87 -1
- opentrons/protocol_api/_parameter_context.py +10 -1
- opentrons/protocol_api/core/engine/deck_conflict.py +0 -297
- opentrons/protocol_api/core/engine/instrument.py +29 -25
- opentrons/protocol_api/core/engine/labware.py +20 -4
- opentrons/protocol_api/core/engine/module_core.py +166 -17
- opentrons/protocol_api/core/engine/pipette_movement_conflict.py +362 -0
- opentrons/protocol_api/core/engine/protocol.py +30 -2
- opentrons/protocol_api/core/instrument.py +2 -0
- opentrons/protocol_api/core/labware.py +4 -0
- opentrons/protocol_api/core/legacy/legacy_instrument_core.py +2 -0
- opentrons/protocol_api/core/legacy/legacy_labware_core.py +5 -0
- opentrons/protocol_api/core/legacy/legacy_protocol_core.py +6 -2
- opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +2 -0
- opentrons/protocol_api/core/module.py +22 -4
- opentrons/protocol_api/core/protocol.py +6 -2
- opentrons/protocol_api/instrument_context.py +52 -20
- opentrons/protocol_api/labware.py +13 -1
- opentrons/protocol_api/module_contexts.py +115 -17
- opentrons/protocol_api/protocol_context.py +49 -5
- opentrons/protocol_api/validation.py +5 -3
- opentrons/protocol_engine/__init__.py +10 -9
- opentrons/protocol_engine/actions/__init__.py +3 -0
- opentrons/protocol_engine/actions/actions.py +30 -25
- opentrons/protocol_engine/actions/get_state_update.py +38 -0
- opentrons/protocol_engine/clients/sync_client.py +1 -1
- opentrons/protocol_engine/clients/transports.py +1 -1
- opentrons/protocol_engine/commands/__init__.py +0 -4
- opentrons/protocol_engine/commands/absorbance_reader/__init__.py +41 -11
- opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +148 -0
- opentrons/protocol_engine/commands/absorbance_reader/initialize.py +65 -9
- opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +148 -0
- opentrons/protocol_engine/commands/absorbance_reader/read.py +200 -0
- opentrons/protocol_engine/commands/aspirate.py +29 -16
- opentrons/protocol_engine/commands/aspirate_in_place.py +33 -16
- opentrons/protocol_engine/commands/blow_out.py +63 -14
- opentrons/protocol_engine/commands/blow_out_in_place.py +55 -13
- opentrons/protocol_engine/commands/calibration/calibrate_gripper.py +2 -5
- opentrons/protocol_engine/commands/calibration/calibrate_module.py +3 -4
- opentrons/protocol_engine/commands/calibration/calibrate_pipette.py +2 -5
- opentrons/protocol_engine/commands/calibration/move_to_maintenance_position.py +6 -4
- opentrons/protocol_engine/commands/command.py +31 -18
- opentrons/protocol_engine/commands/command_unions.py +37 -24
- opentrons/protocol_engine/commands/comment.py +5 -3
- opentrons/protocol_engine/commands/configure_for_volume.py +11 -14
- opentrons/protocol_engine/commands/configure_nozzle_layout.py +9 -15
- opentrons/protocol_engine/commands/custom.py +5 -3
- opentrons/protocol_engine/commands/dispense.py +42 -20
- opentrons/protocol_engine/commands/dispense_in_place.py +32 -14
- opentrons/protocol_engine/commands/drop_tip.py +70 -16
- opentrons/protocol_engine/commands/drop_tip_in_place.py +59 -13
- opentrons/protocol_engine/commands/get_tip_presence.py +5 -3
- opentrons/protocol_engine/commands/heater_shaker/close_labware_latch.py +6 -6
- opentrons/protocol_engine/commands/heater_shaker/deactivate_heater.py +6 -6
- opentrons/protocol_engine/commands/heater_shaker/deactivate_shaker.py +6 -6
- opentrons/protocol_engine/commands/heater_shaker/open_labware_latch.py +8 -6
- opentrons/protocol_engine/commands/heater_shaker/set_and_wait_for_shake_speed.py +8 -4
- opentrons/protocol_engine/commands/heater_shaker/set_target_temperature.py +6 -4
- opentrons/protocol_engine/commands/heater_shaker/wait_for_temperature.py +6 -6
- opentrons/protocol_engine/commands/home.py +11 -5
- opentrons/protocol_engine/commands/liquid_probe.py +146 -88
- opentrons/protocol_engine/commands/load_labware.py +28 -5
- opentrons/protocol_engine/commands/load_liquid.py +18 -7
- opentrons/protocol_engine/commands/load_module.py +4 -6
- opentrons/protocol_engine/commands/load_pipette.py +18 -17
- opentrons/protocol_engine/commands/magnetic_module/disengage.py +6 -6
- opentrons/protocol_engine/commands/magnetic_module/engage.py +6 -4
- opentrons/protocol_engine/commands/move_labware.py +155 -23
- opentrons/protocol_engine/commands/move_relative.py +15 -3
- opentrons/protocol_engine/commands/move_to_addressable_area.py +29 -4
- opentrons/protocol_engine/commands/move_to_addressable_area_for_drop_tip.py +13 -4
- opentrons/protocol_engine/commands/move_to_coordinates.py +11 -5
- opentrons/protocol_engine/commands/move_to_well.py +37 -10
- opentrons/protocol_engine/commands/pick_up_tip.py +51 -30
- opentrons/protocol_engine/commands/pipetting_common.py +47 -16
- opentrons/protocol_engine/commands/prepare_to_aspirate.py +62 -15
- opentrons/protocol_engine/commands/reload_labware.py +13 -4
- opentrons/protocol_engine/commands/retract_axis.py +6 -3
- opentrons/protocol_engine/commands/save_position.py +2 -3
- opentrons/protocol_engine/commands/set_rail_lights.py +5 -3
- opentrons/protocol_engine/commands/set_status_bar.py +5 -3
- opentrons/protocol_engine/commands/temperature_module/deactivate.py +6 -4
- opentrons/protocol_engine/commands/temperature_module/set_target_temperature.py +3 -4
- opentrons/protocol_engine/commands/temperature_module/wait_for_temperature.py +6 -6
- opentrons/protocol_engine/commands/thermocycler/__init__.py +19 -0
- opentrons/protocol_engine/commands/thermocycler/close_lid.py +8 -8
- opentrons/protocol_engine/commands/thermocycler/deactivate_block.py +6 -4
- opentrons/protocol_engine/commands/thermocycler/deactivate_lid.py +6 -4
- opentrons/protocol_engine/commands/thermocycler/open_lid.py +8 -4
- opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py +165 -0
- opentrons/protocol_engine/commands/thermocycler/run_profile.py +6 -6
- opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py +3 -4
- opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py +3 -4
- opentrons/protocol_engine/commands/thermocycler/wait_for_block_temperature.py +6 -4
- opentrons/protocol_engine/commands/thermocycler/wait_for_lid_temperature.py +6 -4
- opentrons/protocol_engine/commands/touch_tip.py +19 -7
- opentrons/protocol_engine/commands/unsafe/__init__.py +30 -0
- opentrons/protocol_engine/commands/unsafe/unsafe_blow_out_in_place.py +6 -4
- opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +12 -4
- opentrons/protocol_engine/commands/unsafe/unsafe_engage_axes.py +5 -3
- opentrons/protocol_engine/commands/unsafe/unsafe_place_labware.py +208 -0
- opentrons/protocol_engine/commands/unsafe/unsafe_ungrip_labware.py +77 -0
- opentrons/protocol_engine/commands/unsafe/update_position_estimators.py +10 -4
- opentrons/protocol_engine/commands/verify_tip_presence.py +5 -5
- opentrons/protocol_engine/commands/wait_for_duration.py +5 -3
- opentrons/protocol_engine/commands/wait_for_resume.py +5 -3
- opentrons/protocol_engine/create_protocol_engine.py +60 -10
- opentrons/protocol_engine/engine_support.py +2 -1
- opentrons/protocol_engine/error_recovery_policy.py +14 -3
- opentrons/protocol_engine/errors/__init__.py +20 -0
- opentrons/protocol_engine/errors/error_occurrence.py +8 -3
- opentrons/protocol_engine/errors/exceptions.py +127 -2
- opentrons/protocol_engine/execution/__init__.py +2 -0
- opentrons/protocol_engine/execution/command_executor.py +22 -13
- opentrons/protocol_engine/execution/create_queue_worker.py +5 -1
- opentrons/protocol_engine/execution/door_watcher.py +1 -1
- opentrons/protocol_engine/execution/equipment.py +2 -1
- opentrons/protocol_engine/execution/error_recovery_hardware_state_synchronizer.py +101 -0
- opentrons/protocol_engine/execution/gantry_mover.py +4 -2
- opentrons/protocol_engine/execution/hardware_stopper.py +3 -3
- opentrons/protocol_engine/execution/heater_shaker_movement_flagger.py +1 -4
- opentrons/protocol_engine/execution/labware_movement.py +73 -22
- opentrons/protocol_engine/execution/movement.py +17 -7
- opentrons/protocol_engine/execution/pipetting.py +7 -4
- opentrons/protocol_engine/execution/queue_worker.py +6 -2
- opentrons/protocol_engine/execution/run_control.py +1 -1
- opentrons/protocol_engine/execution/thermocycler_movement_flagger.py +1 -1
- opentrons/protocol_engine/execution/thermocycler_plate_lifter.py +2 -1
- opentrons/protocol_engine/execution/tip_handler.py +77 -43
- opentrons/protocol_engine/notes/__init__.py +14 -2
- opentrons/protocol_engine/notes/notes.py +18 -1
- opentrons/protocol_engine/plugins.py +1 -1
- opentrons/protocol_engine/protocol_engine.py +47 -31
- opentrons/protocol_engine/resources/__init__.py +2 -0
- opentrons/protocol_engine/resources/deck_data_provider.py +19 -5
- opentrons/protocol_engine/resources/file_provider.py +161 -0
- opentrons/protocol_engine/resources/fixture_validation.py +11 -1
- opentrons/protocol_engine/resources/labware_validation.py +10 -0
- opentrons/protocol_engine/state/__init__.py +0 -70
- opentrons/protocol_engine/state/addressable_areas.py +1 -1
- opentrons/protocol_engine/state/command_history.py +21 -2
- opentrons/protocol_engine/state/commands.py +110 -31
- opentrons/protocol_engine/state/files.py +59 -0
- opentrons/protocol_engine/state/frustum_helpers.py +440 -0
- opentrons/protocol_engine/state/geometry.py +445 -59
- opentrons/protocol_engine/state/labware.py +264 -84
- opentrons/protocol_engine/state/liquids.py +1 -1
- opentrons/protocol_engine/state/module_substates/absorbance_reader_substate.py +21 -3
- opentrons/protocol_engine/state/modules.py +145 -90
- opentrons/protocol_engine/state/motion.py +33 -14
- opentrons/protocol_engine/state/pipettes.py +157 -317
- opentrons/protocol_engine/state/state.py +30 -1
- opentrons/protocol_engine/state/state_summary.py +3 -0
- opentrons/protocol_engine/state/tips.py +69 -114
- opentrons/protocol_engine/state/update_types.py +424 -0
- opentrons/protocol_engine/state/wells.py +236 -0
- opentrons/protocol_engine/types.py +90 -0
- opentrons/protocol_reader/file_format_validator.py +83 -15
- opentrons/protocol_runner/json_translator.py +21 -5
- opentrons/protocol_runner/legacy_command_mapper.py +27 -6
- opentrons/protocol_runner/legacy_context_plugin.py +27 -71
- opentrons/protocol_runner/protocol_runner.py +6 -3
- opentrons/protocol_runner/run_orchestrator.py +41 -6
- opentrons/protocols/advanced_control/mix.py +3 -5
- opentrons/protocols/advanced_control/transfers.py +125 -56
- opentrons/protocols/api_support/constants.py +1 -1
- opentrons/protocols/api_support/definitions.py +1 -1
- opentrons/protocols/api_support/labware_like.py +4 -4
- opentrons/protocols/api_support/tip_tracker.py +2 -2
- opentrons/protocols/api_support/types.py +15 -2
- opentrons/protocols/api_support/util.py +30 -42
- opentrons/protocols/duration/errors.py +1 -1
- opentrons/protocols/duration/estimator.py +50 -29
- opentrons/protocols/execution/dev_types.py +2 -2
- opentrons/protocols/execution/execute_json_v4.py +15 -10
- opentrons/protocols/execution/execute_python.py +8 -3
- opentrons/protocols/geometry/planning.py +12 -12
- opentrons/protocols/labware.py +17 -33
- opentrons/protocols/parameters/csv_parameter_interface.py +3 -1
- opentrons/simulate.py +3 -3
- opentrons/types.py +30 -3
- opentrons/util/logging_config.py +34 -0
- {opentrons-8.1.0a0.dist-info → opentrons-8.2.0.dist-info}/METADATA +5 -4
- {opentrons-8.1.0a0.dist-info → opentrons-8.2.0.dist-info}/RECORD +235 -223
- opentrons/protocol_engine/commands/absorbance_reader/measure.py +0 -94
- opentrons/protocol_engine/commands/configuring_common.py +0 -26
- opentrons/protocol_runner/thread_async_queue.py +0 -174
- /opentrons/protocol_engine/state/{abstract_store.py → _abstract_store.py} +0 -0
- /opentrons/protocol_engine/state/{move_types.py → _move_types.py} +0 -0
- {opentrons-8.1.0a0.dist-info → opentrons-8.2.0.dist-info}/LICENSE +0 -0
- {opentrons-8.1.0a0.dist-info → opentrons-8.2.0.dist-info}/WHEEL +0 -0
- {opentrons-8.1.0a0.dist-info → opentrons-8.2.0.dist-info}/entry_points.txt +0 -0
- {opentrons-8.1.0a0.dist-info → opentrons-8.2.0.dist-info}/top_level.txt +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""Command models to drop tip in place while plunger positions are unknown."""
|
|
2
2
|
from __future__ import annotations
|
|
3
|
+
from opentrons.protocol_engine.state.update_types import StateUpdate
|
|
3
4
|
from pydantic import Field, BaseModel
|
|
4
5
|
from typing import TYPE_CHECKING, Optional, Type
|
|
5
6
|
from typing_extensions import Literal
|
|
@@ -14,7 +15,7 @@ from ...resources import ensure_ot3_hardware
|
|
|
14
15
|
|
|
15
16
|
if TYPE_CHECKING:
|
|
16
17
|
from ...execution import TipHandler
|
|
17
|
-
from ...state import StateView
|
|
18
|
+
from ...state.state import StateView
|
|
18
19
|
|
|
19
20
|
|
|
20
21
|
UnsafeDropTipInPlaceCommandType = Literal["unsafe/dropTipInPlace"]
|
|
@@ -41,7 +42,7 @@ class UnsafeDropTipInPlaceResult(BaseModel):
|
|
|
41
42
|
|
|
42
43
|
class UnsafeDropTipInPlaceImplementation(
|
|
43
44
|
AbstractCommandImpl[
|
|
44
|
-
UnsafeDropTipInPlaceParams, SuccessData[UnsafeDropTipInPlaceResult
|
|
45
|
+
UnsafeDropTipInPlaceParams, SuccessData[UnsafeDropTipInPlaceResult]
|
|
45
46
|
]
|
|
46
47
|
):
|
|
47
48
|
"""Unsafe drop tip in place command implementation."""
|
|
@@ -59,7 +60,7 @@ class UnsafeDropTipInPlaceImplementation(
|
|
|
59
60
|
|
|
60
61
|
async def execute(
|
|
61
62
|
self, params: UnsafeDropTipInPlaceParams
|
|
62
|
-
) -> SuccessData[UnsafeDropTipInPlaceResult
|
|
63
|
+
) -> SuccessData[UnsafeDropTipInPlaceResult]:
|
|
63
64
|
"""Drop a tip using the requested pipette, even if the plunger position is not known."""
|
|
64
65
|
ot3_hardware_api = ensure_ot3_hardware(self._hardware_api)
|
|
65
66
|
pipette_location = self._state_view.motion.get_pipette_location(
|
|
@@ -72,7 +73,14 @@ class UnsafeDropTipInPlaceImplementation(
|
|
|
72
73
|
pipette_id=params.pipetteId, home_after=params.homeAfter
|
|
73
74
|
)
|
|
74
75
|
|
|
75
|
-
|
|
76
|
+
state_update = StateUpdate()
|
|
77
|
+
state_update.update_pipette_tip_state(
|
|
78
|
+
pipette_id=params.pipetteId, tip_geometry=None
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
return SuccessData(
|
|
82
|
+
public=UnsafeDropTipInPlaceResult(), state_update=state_update
|
|
83
|
+
)
|
|
76
84
|
|
|
77
85
|
|
|
78
86
|
class UnsafeDropTipInPlace(
|
|
@@ -32,7 +32,7 @@ class UnsafeEngageAxesResult(BaseModel):
|
|
|
32
32
|
class UnsafeEngageAxesImplementation(
|
|
33
33
|
AbstractCommandImpl[
|
|
34
34
|
UnsafeEngageAxesParams,
|
|
35
|
-
SuccessData[UnsafeEngageAxesResult
|
|
35
|
+
SuccessData[UnsafeEngageAxesResult],
|
|
36
36
|
]
|
|
37
37
|
):
|
|
38
38
|
"""Enable axes command implementation."""
|
|
@@ -48,7 +48,7 @@ class UnsafeEngageAxesImplementation(
|
|
|
48
48
|
|
|
49
49
|
async def execute(
|
|
50
50
|
self, params: UnsafeEngageAxesParams
|
|
51
|
-
) -> SuccessData[UnsafeEngageAxesResult
|
|
51
|
+
) -> SuccessData[UnsafeEngageAxesResult]:
|
|
52
52
|
"""Enable exes."""
|
|
53
53
|
ot3_hardware_api = ensure_ot3_hardware(self._hardware_api)
|
|
54
54
|
await ot3_hardware_api.engage_axes(
|
|
@@ -57,7 +57,9 @@ class UnsafeEngageAxesImplementation(
|
|
|
57
57
|
for axis in params.axes
|
|
58
58
|
]
|
|
59
59
|
)
|
|
60
|
-
return SuccessData(
|
|
60
|
+
return SuccessData(
|
|
61
|
+
public=UnsafeEngageAxesResult(),
|
|
62
|
+
)
|
|
61
63
|
|
|
62
64
|
|
|
63
65
|
class UnsafeEngageAxes(
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
"""Place labware payload, result, and implementaiton."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
from typing import TYPE_CHECKING, Optional, Type
|
|
5
|
+
from typing_extensions import Literal
|
|
6
|
+
|
|
7
|
+
from opentrons_shared_data.labware.types import LabwareUri
|
|
8
|
+
from opentrons_shared_data.labware.labware_definition import LabwareDefinition
|
|
9
|
+
from pydantic import BaseModel, Field
|
|
10
|
+
|
|
11
|
+
from opentrons.hardware_control.types import Axis, OT3Mount
|
|
12
|
+
from opentrons.motion_planning.waypoints import get_gripper_labware_placement_waypoints
|
|
13
|
+
from opentrons.protocol_engine.errors.exceptions import (
|
|
14
|
+
CannotPerformGripperAction,
|
|
15
|
+
GripperNotAttachedError,
|
|
16
|
+
)
|
|
17
|
+
from opentrons.types import Point
|
|
18
|
+
|
|
19
|
+
from ...types import (
|
|
20
|
+
DeckSlotLocation,
|
|
21
|
+
ModuleModel,
|
|
22
|
+
OnDeckLabwareLocation,
|
|
23
|
+
)
|
|
24
|
+
from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
|
|
25
|
+
from ...errors.error_occurrence import ErrorOccurrence
|
|
26
|
+
from ...resources import ensure_ot3_hardware
|
|
27
|
+
|
|
28
|
+
from opentrons.hardware_control import HardwareControlAPI, OT3HardwareControlAPI
|
|
29
|
+
|
|
30
|
+
if TYPE_CHECKING:
|
|
31
|
+
from ...state.state import StateView
|
|
32
|
+
from ...execution.equipment import EquipmentHandler
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
UnsafePlaceLabwareCommandType = Literal["unsafe/placeLabware"]
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class UnsafePlaceLabwareParams(BaseModel):
|
|
39
|
+
"""Payload required for an UnsafePlaceLabware command."""
|
|
40
|
+
|
|
41
|
+
labwareURI: str = Field(..., description="Labware URI for labware.")
|
|
42
|
+
location: OnDeckLabwareLocation = Field(
|
|
43
|
+
..., description="Where to place the labware."
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class UnsafePlaceLabwareResult(BaseModel):
|
|
48
|
+
"""Result data from the execution of an UnsafePlaceLabware command."""
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class UnsafePlaceLabwareImplementation(
|
|
52
|
+
AbstractCommandImpl[
|
|
53
|
+
UnsafePlaceLabwareParams,
|
|
54
|
+
SuccessData[UnsafePlaceLabwareResult],
|
|
55
|
+
]
|
|
56
|
+
):
|
|
57
|
+
"""The UnsafePlaceLabware command implementation."""
|
|
58
|
+
|
|
59
|
+
def __init__(
|
|
60
|
+
self,
|
|
61
|
+
hardware_api: HardwareControlAPI,
|
|
62
|
+
state_view: StateView,
|
|
63
|
+
equipment: EquipmentHandler,
|
|
64
|
+
**kwargs: object,
|
|
65
|
+
) -> None:
|
|
66
|
+
self._hardware_api = hardware_api
|
|
67
|
+
self._state_view = state_view
|
|
68
|
+
self._equipment = equipment
|
|
69
|
+
|
|
70
|
+
async def execute(
|
|
71
|
+
self, params: UnsafePlaceLabwareParams
|
|
72
|
+
) -> SuccessData[UnsafePlaceLabwareResult]:
|
|
73
|
+
"""Place Labware.
|
|
74
|
+
|
|
75
|
+
This command is used only when the gripper is in the middle of moving
|
|
76
|
+
labware but is interrupted before completing the move. (i.e., the e-stop
|
|
77
|
+
is pressed, get into error recovery, etc).
|
|
78
|
+
|
|
79
|
+
Unlike the `moveLabware` command, where you pick a source and destination
|
|
80
|
+
location, this command takes the labwareURI of the labware to be moved
|
|
81
|
+
and location to move it to.
|
|
82
|
+
|
|
83
|
+
"""
|
|
84
|
+
ot3api = ensure_ot3_hardware(self._hardware_api)
|
|
85
|
+
if not ot3api.has_gripper():
|
|
86
|
+
raise GripperNotAttachedError("No gripper found to perform labware place.")
|
|
87
|
+
|
|
88
|
+
if ot3api.gripper_jaw_can_home():
|
|
89
|
+
raise CannotPerformGripperAction(
|
|
90
|
+
"Cannot place labware when gripper is not gripping."
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
location = self._state_view.geometry.ensure_valid_gripper_location(
|
|
94
|
+
params.location,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
definition = self._state_view.labware.get_definition_by_uri(
|
|
98
|
+
# todo(mm, 2024-11-07): This is an unsafe cast from untrusted input.
|
|
99
|
+
# We need a str -> LabwareUri parse/validate function.
|
|
100
|
+
LabwareUri(params.labwareURI)
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
# todo(mm, 2024-11-06): This is only correct in the special case of an
|
|
104
|
+
# absorbance reader lid. Its definition currently puts the offsets for *itself*
|
|
105
|
+
# in the property that's normally meant for offsets for its *children.*
|
|
106
|
+
final_offsets = self._state_view.labware.get_child_gripper_offsets(
|
|
107
|
+
labware_definition=definition, slot_name=None
|
|
108
|
+
)
|
|
109
|
+
drop_offset = (
|
|
110
|
+
Point(
|
|
111
|
+
final_offsets.dropOffset.x,
|
|
112
|
+
final_offsets.dropOffset.y,
|
|
113
|
+
final_offsets.dropOffset.z,
|
|
114
|
+
)
|
|
115
|
+
if final_offsets
|
|
116
|
+
else None
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
if isinstance(params.location, DeckSlotLocation):
|
|
120
|
+
self._state_view.addressable_areas.raise_if_area_not_in_deck_configuration(
|
|
121
|
+
params.location.slotName.id
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
# This is an absorbance reader, move the lid to its dock (staging area).
|
|
125
|
+
if isinstance(location, DeckSlotLocation):
|
|
126
|
+
module = self._state_view.modules.get_by_slot(location.slotName)
|
|
127
|
+
if module and module.model == ModuleModel.ABSORBANCE_READER_V1:
|
|
128
|
+
location = self._state_view.modules.absorbance_reader_dock_location(
|
|
129
|
+
module.id
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
# NOTE: When the estop is pressed, the gantry loses position, lets use
|
|
133
|
+
# the encoders to sync position.
|
|
134
|
+
# Ideally, we'd do a full home, but this command is used when
|
|
135
|
+
# the gripper is holding the plate reader, and a full home would
|
|
136
|
+
# bang it into the right window.
|
|
137
|
+
await ot3api.home(axes=[Axis.Z_L, Axis.Z_R, Axis.Z_G])
|
|
138
|
+
await ot3api.engage_axes([Axis.X, Axis.Y])
|
|
139
|
+
await ot3api.update_axis_position_estimations([Axis.X, Axis.Y])
|
|
140
|
+
|
|
141
|
+
# Place the labware down
|
|
142
|
+
await self._start_movement(ot3api, definition, location, drop_offset)
|
|
143
|
+
|
|
144
|
+
return SuccessData(public=UnsafePlaceLabwareResult())
|
|
145
|
+
|
|
146
|
+
async def _start_movement(
|
|
147
|
+
self,
|
|
148
|
+
ot3api: OT3HardwareControlAPI,
|
|
149
|
+
labware_definition: LabwareDefinition,
|
|
150
|
+
location: OnDeckLabwareLocation,
|
|
151
|
+
drop_offset: Optional[Point],
|
|
152
|
+
) -> None:
|
|
153
|
+
gripper_homed_position = await ot3api.gantry_position(
|
|
154
|
+
mount=OT3Mount.GRIPPER,
|
|
155
|
+
refresh=True,
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
to_labware_center = self._state_view.geometry.get_labware_grip_point(
|
|
159
|
+
labware_definition=labware_definition, location=location
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
movement_waypoints = get_gripper_labware_placement_waypoints(
|
|
163
|
+
to_labware_center=to_labware_center,
|
|
164
|
+
gripper_home_z=gripper_homed_position.z,
|
|
165
|
+
drop_offset=drop_offset,
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
# start movement
|
|
169
|
+
for waypoint_data in movement_waypoints:
|
|
170
|
+
if waypoint_data.jaw_open:
|
|
171
|
+
if waypoint_data.dropping:
|
|
172
|
+
# This `disengage_axes` step is important in order to engage
|
|
173
|
+
# the electronic brake on the Z axis of the gripper. The brake
|
|
174
|
+
# has a stronger holding force on the axis than the hold current,
|
|
175
|
+
# and prevents the axis from spuriously dropping when e.g. the notch
|
|
176
|
+
# on the side of a falling tiprack catches the jaw.
|
|
177
|
+
await ot3api.disengage_axes([Axis.Z_G])
|
|
178
|
+
await ot3api.ungrip()
|
|
179
|
+
if waypoint_data.dropping:
|
|
180
|
+
# We lost the position estimation after disengaging the axis, so
|
|
181
|
+
# it is necessary to home it next
|
|
182
|
+
await ot3api.home_z(OT3Mount.GRIPPER)
|
|
183
|
+
await ot3api.move_to(
|
|
184
|
+
mount=OT3Mount.GRIPPER, abs_position=waypoint_data.position
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
class UnsafePlaceLabware(
|
|
189
|
+
BaseCommand[UnsafePlaceLabwareParams, UnsafePlaceLabwareResult, ErrorOccurrence]
|
|
190
|
+
):
|
|
191
|
+
"""UnsafePlaceLabware command model."""
|
|
192
|
+
|
|
193
|
+
commandType: UnsafePlaceLabwareCommandType = "unsafe/placeLabware"
|
|
194
|
+
params: UnsafePlaceLabwareParams
|
|
195
|
+
result: Optional[UnsafePlaceLabwareResult]
|
|
196
|
+
|
|
197
|
+
_ImplementationCls: Type[
|
|
198
|
+
UnsafePlaceLabwareImplementation
|
|
199
|
+
] = UnsafePlaceLabwareImplementation
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
class UnsafePlaceLabwareCreate(BaseCommandCreate[UnsafePlaceLabwareParams]):
|
|
203
|
+
"""UnsafePlaceLabware command request model."""
|
|
204
|
+
|
|
205
|
+
commandType: UnsafePlaceLabwareCommandType = "unsafe/placeLabware"
|
|
206
|
+
params: UnsafePlaceLabwareParams
|
|
207
|
+
|
|
208
|
+
_CommandCls: Type[UnsafePlaceLabware] = UnsafePlaceLabware
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""Ungrip labware payload, result, and implementaiton."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from opentrons.hardware_control.types import Axis
|
|
6
|
+
from opentrons.protocol_engine.errors.exceptions import GripperNotAttachedError
|
|
7
|
+
from pydantic import BaseModel
|
|
8
|
+
from typing import Optional, Type
|
|
9
|
+
from typing_extensions import Literal
|
|
10
|
+
|
|
11
|
+
from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
|
|
12
|
+
from ...errors.error_occurrence import ErrorOccurrence
|
|
13
|
+
from ...resources import ensure_ot3_hardware
|
|
14
|
+
|
|
15
|
+
from opentrons.hardware_control import HardwareControlAPI
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
UnsafeUngripLabwareCommandType = Literal["unsafe/ungripLabware"]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class UnsafeUngripLabwareParams(BaseModel):
|
|
22
|
+
"""Payload required for an UngripLabware command."""
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class UnsafeUngripLabwareResult(BaseModel):
|
|
26
|
+
"""Result data from the execution of an UngripLabware command."""
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class UnsafeUngripLabwareImplementation(
|
|
30
|
+
AbstractCommandImpl[
|
|
31
|
+
UnsafeUngripLabwareParams,
|
|
32
|
+
SuccessData[UnsafeUngripLabwareResult],
|
|
33
|
+
]
|
|
34
|
+
):
|
|
35
|
+
"""Ungrip labware command implementation."""
|
|
36
|
+
|
|
37
|
+
def __init__(
|
|
38
|
+
self,
|
|
39
|
+
hardware_api: HardwareControlAPI,
|
|
40
|
+
**kwargs: object,
|
|
41
|
+
) -> None:
|
|
42
|
+
self._hardware_api = hardware_api
|
|
43
|
+
|
|
44
|
+
async def execute(
|
|
45
|
+
self, params: UnsafeUngripLabwareParams
|
|
46
|
+
) -> SuccessData[UnsafeUngripLabwareResult]:
|
|
47
|
+
"""Ungrip Labware."""
|
|
48
|
+
ot3_hardware_api = ensure_ot3_hardware(self._hardware_api)
|
|
49
|
+
if not ot3_hardware_api.has_gripper():
|
|
50
|
+
raise GripperNotAttachedError("No gripper found to perform ungrip.")
|
|
51
|
+
await ot3_hardware_api.home([Axis.G])
|
|
52
|
+
return SuccessData(
|
|
53
|
+
public=UnsafeUngripLabwareResult(),
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class UnsafeUngripLabware(
|
|
58
|
+
BaseCommand[UnsafeUngripLabwareParams, UnsafeUngripLabwareResult, ErrorOccurrence]
|
|
59
|
+
):
|
|
60
|
+
"""UnsafeUngripLabware command model."""
|
|
61
|
+
|
|
62
|
+
commandType: UnsafeUngripLabwareCommandType = "unsafe/ungripLabware"
|
|
63
|
+
params: UnsafeUngripLabwareParams
|
|
64
|
+
result: Optional[UnsafeUngripLabwareResult]
|
|
65
|
+
|
|
66
|
+
_ImplementationCls: Type[
|
|
67
|
+
UnsafeUngripLabwareImplementation
|
|
68
|
+
] = UnsafeUngripLabwareImplementation
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class UnsafeUngripLabwareCreate(BaseCommandCreate[UnsafeUngripLabwareParams]):
|
|
72
|
+
"""UnsafeEngageAxes command request model."""
|
|
73
|
+
|
|
74
|
+
commandType: UnsafeUngripLabwareCommandType = "unsafe/ungripLabware"
|
|
75
|
+
params: UnsafeUngripLabwareParams
|
|
76
|
+
|
|
77
|
+
_CommandCls: Type[UnsafeUngripLabware] = UnsafeUngripLabware
|
|
@@ -23,7 +23,11 @@ class UpdatePositionEstimatorsParams(BaseModel):
|
|
|
23
23
|
"""Payload required for an UpdatePositionEstimators command."""
|
|
24
24
|
|
|
25
25
|
axes: List[MotorAxis] = Field(
|
|
26
|
-
...,
|
|
26
|
+
...,
|
|
27
|
+
description=(
|
|
28
|
+
"The axes for which to update the position estimators."
|
|
29
|
+
" Any axes that are not physically present will be ignored."
|
|
30
|
+
),
|
|
27
31
|
)
|
|
28
32
|
|
|
29
33
|
|
|
@@ -34,7 +38,7 @@ class UpdatePositionEstimatorsResult(BaseModel):
|
|
|
34
38
|
class UpdatePositionEstimatorsImplementation(
|
|
35
39
|
AbstractCommandImpl[
|
|
36
40
|
UpdatePositionEstimatorsParams,
|
|
37
|
-
SuccessData[UpdatePositionEstimatorsResult
|
|
41
|
+
SuccessData[UpdatePositionEstimatorsResult],
|
|
38
42
|
]
|
|
39
43
|
):
|
|
40
44
|
"""Update position estimators command implementation."""
|
|
@@ -50,7 +54,7 @@ class UpdatePositionEstimatorsImplementation(
|
|
|
50
54
|
|
|
51
55
|
async def execute(
|
|
52
56
|
self, params: UpdatePositionEstimatorsParams
|
|
53
|
-
) -> SuccessData[UpdatePositionEstimatorsResult
|
|
57
|
+
) -> SuccessData[UpdatePositionEstimatorsResult]:
|
|
54
58
|
"""Update axis position estimators from their encoders."""
|
|
55
59
|
ot3_hardware_api = ensure_ot3_hardware(self._hardware_api)
|
|
56
60
|
await ot3_hardware_api.update_axis_position_estimations(
|
|
@@ -59,7 +63,9 @@ class UpdatePositionEstimatorsImplementation(
|
|
|
59
63
|
for axis in params.axes
|
|
60
64
|
]
|
|
61
65
|
)
|
|
62
|
-
return SuccessData(
|
|
66
|
+
return SuccessData(
|
|
67
|
+
public=UpdatePositionEstimatorsResult(),
|
|
68
|
+
)
|
|
63
69
|
|
|
64
70
|
|
|
65
71
|
class UpdatePositionEstimators(
|
|
@@ -36,9 +36,7 @@ class VerifyTipPresenceResult(BaseModel):
|
|
|
36
36
|
|
|
37
37
|
|
|
38
38
|
class VerifyTipPresenceImplementation(
|
|
39
|
-
AbstractCommandImpl[
|
|
40
|
-
VerifyTipPresenceParams, SuccessData[VerifyTipPresenceResult, None]
|
|
41
|
-
]
|
|
39
|
+
AbstractCommandImpl[VerifyTipPresenceParams, SuccessData[VerifyTipPresenceResult]]
|
|
42
40
|
):
|
|
43
41
|
"""VerifyTipPresence command implementation."""
|
|
44
42
|
|
|
@@ -51,7 +49,7 @@ class VerifyTipPresenceImplementation(
|
|
|
51
49
|
|
|
52
50
|
async def execute(
|
|
53
51
|
self, params: VerifyTipPresenceParams
|
|
54
|
-
) -> SuccessData[VerifyTipPresenceResult
|
|
52
|
+
) -> SuccessData[VerifyTipPresenceResult]:
|
|
55
53
|
"""Verify if tip presence is as expected for the requested pipette."""
|
|
56
54
|
pipette_id = params.pipetteId
|
|
57
55
|
expected_state = params.expectedState
|
|
@@ -67,7 +65,9 @@ class VerifyTipPresenceImplementation(
|
|
|
67
65
|
follow_singular_sensor=follow_singular_sensor,
|
|
68
66
|
)
|
|
69
67
|
|
|
70
|
-
return SuccessData(
|
|
68
|
+
return SuccessData(
|
|
69
|
+
public=VerifyTipPresenceResult(),
|
|
70
|
+
)
|
|
71
71
|
|
|
72
72
|
|
|
73
73
|
class VerifyTipPresence(
|
|
@@ -29,7 +29,7 @@ class WaitForDurationResult(BaseModel):
|
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
class WaitForDurationImplementation(
|
|
32
|
-
AbstractCommandImpl[WaitForDurationParams, SuccessData[WaitForDurationResult
|
|
32
|
+
AbstractCommandImpl[WaitForDurationParams, SuccessData[WaitForDurationResult]]
|
|
33
33
|
):
|
|
34
34
|
"""Wait for duration command implementation."""
|
|
35
35
|
|
|
@@ -38,10 +38,12 @@ class WaitForDurationImplementation(
|
|
|
38
38
|
|
|
39
39
|
async def execute(
|
|
40
40
|
self, params: WaitForDurationParams
|
|
41
|
-
) -> SuccessData[WaitForDurationResult
|
|
41
|
+
) -> SuccessData[WaitForDurationResult]:
|
|
42
42
|
"""Wait for a duration of time."""
|
|
43
43
|
await self._run_control.wait_for_duration(params.seconds)
|
|
44
|
-
return SuccessData(
|
|
44
|
+
return SuccessData(
|
|
45
|
+
public=WaitForDurationResult(),
|
|
46
|
+
)
|
|
45
47
|
|
|
46
48
|
|
|
47
49
|
class WaitForDuration(
|
|
@@ -30,7 +30,7 @@ class WaitForResumeResult(BaseModel):
|
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
class WaitForResumeImplementation(
|
|
33
|
-
AbstractCommandImpl[WaitForResumeParams, SuccessData[WaitForResumeResult
|
|
33
|
+
AbstractCommandImpl[WaitForResumeParams, SuccessData[WaitForResumeResult]]
|
|
34
34
|
):
|
|
35
35
|
"""Wait for resume command implementation."""
|
|
36
36
|
|
|
@@ -39,10 +39,12 @@ class WaitForResumeImplementation(
|
|
|
39
39
|
|
|
40
40
|
async def execute(
|
|
41
41
|
self, params: WaitForResumeParams
|
|
42
|
-
) -> SuccessData[WaitForResumeResult
|
|
42
|
+
) -> SuccessData[WaitForResumeResult]:
|
|
43
43
|
"""Dispatch a PauseAction to the store to pause the protocol."""
|
|
44
44
|
await self._run_control.wait_for_resume()
|
|
45
|
-
return SuccessData(
|
|
45
|
+
return SuccessData(
|
|
46
|
+
public=WaitForResumeResult(),
|
|
47
|
+
)
|
|
46
48
|
|
|
47
49
|
|
|
48
50
|
class WaitForResume(
|
|
@@ -5,13 +5,25 @@ import typing
|
|
|
5
5
|
|
|
6
6
|
from opentrons.hardware_control import HardwareControlAPI
|
|
7
7
|
from opentrons.hardware_control.types import DoorState
|
|
8
|
-
from opentrons.protocol_engine.
|
|
8
|
+
from opentrons.protocol_engine.execution.error_recovery_hardware_state_synchronizer import (
|
|
9
|
+
ErrorRecoveryHardwareStateSynchronizer,
|
|
10
|
+
)
|
|
11
|
+
from opentrons.protocol_engine.resources.labware_data_provider import (
|
|
12
|
+
LabwareDataProvider,
|
|
13
|
+
)
|
|
9
14
|
from opentrons.util.async_helpers import async_context_manager_in_thread
|
|
15
|
+
|
|
10
16
|
from opentrons_shared_data.robot import load as load_robot
|
|
11
17
|
|
|
18
|
+
from .actions.action_dispatcher import ActionDispatcher
|
|
19
|
+
from .error_recovery_policy import ErrorRecoveryPolicy
|
|
20
|
+
from .execution.door_watcher import DoorWatcher
|
|
21
|
+
from .execution.hardware_stopper import HardwareStopper
|
|
22
|
+
from .plugins import PluginStarter
|
|
12
23
|
from .protocol_engine import ProtocolEngine
|
|
13
|
-
from .resources import DeckDataProvider, ModuleDataProvider
|
|
14
|
-
from .state import Config
|
|
24
|
+
from .resources import DeckDataProvider, ModuleDataProvider, FileProvider, ModelUtils
|
|
25
|
+
from .state.config import Config
|
|
26
|
+
from .state.state import StateStore
|
|
15
27
|
from .types import PostRunHardwareState, DeckConfigurationType
|
|
16
28
|
|
|
17
29
|
from .engine_support import create_run_orchestrator
|
|
@@ -25,6 +37,7 @@ async def create_protocol_engine(
|
|
|
25
37
|
error_recovery_policy: ErrorRecoveryPolicy,
|
|
26
38
|
load_fixed_trash: bool = False,
|
|
27
39
|
deck_configuration: typing.Optional[DeckConfigurationType] = None,
|
|
40
|
+
file_provider: typing.Optional[FileProvider] = None,
|
|
28
41
|
notify_publishers: typing.Optional[typing.Callable[[], None]] = None,
|
|
29
42
|
) -> ProtocolEngine:
|
|
30
43
|
"""Create a ProtocolEngine instance.
|
|
@@ -36,17 +49,18 @@ async def create_protocol_engine(
|
|
|
36
49
|
See documentation on `ErrorRecoveryPolicy`.
|
|
37
50
|
load_fixed_trash: Automatically load fixed trash labware in engine.
|
|
38
51
|
deck_configuration: The initial deck configuration the engine will be instantiated with.
|
|
52
|
+
file_provider: Provides access to robot server file writing procedures for protocol output.
|
|
39
53
|
notify_publishers: Notifies robot server publishers of internal state change.
|
|
40
54
|
"""
|
|
41
55
|
deck_data = DeckDataProvider(config.deck_type)
|
|
42
56
|
deck_definition = await deck_data.get_deck_definition()
|
|
43
|
-
deck_fixed_labware = (
|
|
44
|
-
|
|
45
|
-
if load_fixed_trash
|
|
46
|
-
else []
|
|
57
|
+
deck_fixed_labware = await deck_data.get_deck_fixed_labware(
|
|
58
|
+
load_fixed_trash, deck_definition, deck_configuration
|
|
47
59
|
)
|
|
60
|
+
|
|
48
61
|
module_calibration_offsets = ModuleDataProvider.load_module_calibrations()
|
|
49
62
|
robot_definition = load_robot(config.robot_type)
|
|
63
|
+
|
|
50
64
|
state_store = StateStore(
|
|
51
65
|
config=config,
|
|
52
66
|
deck_definition=deck_definition,
|
|
@@ -58,18 +72,51 @@ async def create_protocol_engine(
|
|
|
58
72
|
deck_configuration=deck_configuration,
|
|
59
73
|
notify_publishers=notify_publishers,
|
|
60
74
|
)
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
75
|
+
hardware_state_synchronizer = ErrorRecoveryHardwareStateSynchronizer(
|
|
76
|
+
hardware_api, state_store
|
|
77
|
+
)
|
|
78
|
+
action_dispatcher = ActionDispatcher(state_store)
|
|
79
|
+
action_dispatcher.add_handler(hardware_state_synchronizer)
|
|
80
|
+
plugin_starter = PluginStarter(state_store, action_dispatcher)
|
|
81
|
+
model_utils = ModelUtils()
|
|
82
|
+
hardware_stopper = HardwareStopper(hardware_api, state_store)
|
|
83
|
+
door_watcher = DoorWatcher(state_store, hardware_api, action_dispatcher)
|
|
84
|
+
module_data_provider = ModuleDataProvider()
|
|
85
|
+
file_provider = file_provider or FileProvider()
|
|
86
|
+
|
|
87
|
+
pe = ProtocolEngine(
|
|
64
88
|
hardware_api=hardware_api,
|
|
89
|
+
state_store=state_store,
|
|
90
|
+
action_dispatcher=action_dispatcher,
|
|
91
|
+
plugin_starter=plugin_starter,
|
|
92
|
+
model_utils=model_utils,
|
|
93
|
+
hardware_stopper=hardware_stopper,
|
|
94
|
+
door_watcher=door_watcher,
|
|
95
|
+
module_data_provider=module_data_provider,
|
|
96
|
+
file_provider=file_provider,
|
|
65
97
|
)
|
|
66
98
|
|
|
99
|
+
# todo(mm, 2024-11-08): This is a quick hack to support the absorbance reader, which
|
|
100
|
+
# expects the engine to have this special labware definition available. It would be
|
|
101
|
+
# cleaner for the `loadModule` command to do this I/O and insert the definition
|
|
102
|
+
# into state. That gets easier after https://opentrons.atlassian.net/browse/EXEC-756.
|
|
103
|
+
#
|
|
104
|
+
# NOTE: This needs to stay in sync with LabwareView.get_absorbance_reader_lid_definition().
|
|
105
|
+
pe.add_labware_definition(
|
|
106
|
+
await LabwareDataProvider().get_labware_definition(
|
|
107
|
+
"opentrons_flex_lid_absorbance_plate_reader_module", "opentrons", 1
|
|
108
|
+
)
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
return pe
|
|
112
|
+
|
|
67
113
|
|
|
68
114
|
@contextlib.contextmanager
|
|
69
115
|
def create_protocol_engine_in_thread(
|
|
70
116
|
hardware_api: HardwareControlAPI,
|
|
71
117
|
config: Config,
|
|
72
118
|
deck_configuration: typing.Optional[DeckConfigurationType],
|
|
119
|
+
file_provider: typing.Optional[FileProvider],
|
|
73
120
|
error_recovery_policy: ErrorRecoveryPolicy,
|
|
74
121
|
drop_tips_after_run: bool,
|
|
75
122
|
post_run_hardware_state: PostRunHardwareState,
|
|
@@ -97,6 +144,7 @@ def create_protocol_engine_in_thread(
|
|
|
97
144
|
with async_context_manager_in_thread(
|
|
98
145
|
_protocol_engine(
|
|
99
146
|
hardware_api,
|
|
147
|
+
file_provider,
|
|
100
148
|
config,
|
|
101
149
|
deck_configuration,
|
|
102
150
|
error_recovery_policy,
|
|
@@ -114,6 +162,7 @@ def create_protocol_engine_in_thread(
|
|
|
114
162
|
@contextlib.asynccontextmanager
|
|
115
163
|
async def _protocol_engine(
|
|
116
164
|
hardware_api: HardwareControlAPI,
|
|
165
|
+
file_provider: typing.Optional[FileProvider],
|
|
117
166
|
config: Config,
|
|
118
167
|
deck_configuration: typing.Optional[DeckConfigurationType],
|
|
119
168
|
error_recovery_policy: ErrorRecoveryPolicy,
|
|
@@ -123,6 +172,7 @@ async def _protocol_engine(
|
|
|
123
172
|
) -> typing.AsyncGenerator[ProtocolEngine, None]:
|
|
124
173
|
protocol_engine = await create_protocol_engine(
|
|
125
174
|
hardware_api=hardware_api,
|
|
175
|
+
file_provider=file_provider,
|
|
126
176
|
config=config,
|
|
127
177
|
error_recovery_policy=error_recovery_policy,
|
|
128
178
|
load_fixed_trash=load_fixed_trash,
|
|
@@ -6,7 +6,8 @@ from opentrons.protocol_runner import protocol_runner, RunOrchestrator
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
def create_run_orchestrator(
|
|
9
|
-
hardware_api: HardwareControlAPI,
|
|
9
|
+
hardware_api: HardwareControlAPI,
|
|
10
|
+
protocol_engine: ProtocolEngine,
|
|
10
11
|
) -> RunOrchestrator:
|
|
11
12
|
"""Create a RunOrchestrator instance."""
|
|
12
13
|
return RunOrchestrator(
|
|
@@ -26,10 +26,20 @@ class ErrorRecoveryType(enum.Enum):
|
|
|
26
26
|
"""
|
|
27
27
|
|
|
28
28
|
WAIT_FOR_RECOVERY = enum.auto()
|
|
29
|
-
"""
|
|
29
|
+
"""Enter interactive error recovery mode."""
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
"""Continue
|
|
31
|
+
CONTINUE_WITH_ERROR = enum.auto()
|
|
32
|
+
"""Continue without interruption, carrying on from whatever error state the failed
|
|
33
|
+
command left the engine in.
|
|
34
|
+
|
|
35
|
+
This is like `ProtocolEngine.resume_from_recovery(reconcile_false_positive=False)`.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
ASSUME_FALSE_POSITIVE_AND_CONTINUE = enum.auto()
|
|
39
|
+
"""Continue without interruption, acting as if the underlying error was a false positive.
|
|
40
|
+
|
|
41
|
+
This is like `ProtocolEngine.resume_from_recovery(reconcile_false_positive=True)`.
|
|
42
|
+
"""
|
|
33
43
|
|
|
34
44
|
|
|
35
45
|
class ErrorRecoveryPolicy(Protocol):
|
|
@@ -40,6 +50,7 @@ class ErrorRecoveryPolicy(Protocol):
|
|
|
40
50
|
and return an appropriate `ErrorRecoveryType`.
|
|
41
51
|
|
|
42
52
|
Args:
|
|
53
|
+
config: The config of the calling `ProtocolEngine`.
|
|
43
54
|
failed_command: The command that failed, in its final `status=="failed"` state.
|
|
44
55
|
defined_error_data: If the command failed with a defined error, details about
|
|
45
56
|
that error. If the command failed with an undefined error, `None`.
|