opentrons 8.1.0__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.0.dist-info → opentrons-8.2.0.dist-info}/METADATA +5 -4
- {opentrons-8.1.0.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.0.dist-info → opentrons-8.2.0.dist-info}/LICENSE +0 -0
- {opentrons-8.1.0.dist-info → opentrons-8.2.0.dist-info}/WHEEL +0 -0
- {opentrons-8.1.0.dist-info → opentrons-8.2.0.dist-info}/entry_points.txt +0 -0
- {opentrons-8.1.0.dist-info → opentrons-8.2.0.dist-info}/top_level.txt +0 -0
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"""Labware movement command handling."""
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
|
-
from typing import Optional, TYPE_CHECKING
|
|
4
|
+
from typing import Optional, TYPE_CHECKING, overload
|
|
5
|
+
|
|
6
|
+
from opentrons_shared_data.labware.labware_definition import LabwareDefinition
|
|
5
7
|
|
|
6
8
|
from opentrons.types import Point
|
|
7
9
|
|
|
@@ -9,7 +11,7 @@ from opentrons.hardware_control import HardwareControlAPI
|
|
|
9
11
|
from opentrons.hardware_control.types import OT3Mount, Axis
|
|
10
12
|
from opentrons.motion_planning import get_gripper_labware_movement_waypoints
|
|
11
13
|
|
|
12
|
-
from opentrons.protocol_engine.state import StateStore
|
|
14
|
+
from opentrons.protocol_engine.state.state import StateStore
|
|
13
15
|
from opentrons.protocol_engine.resources.ot3_validation import ensure_ot3_hardware
|
|
14
16
|
|
|
15
17
|
from .thermocycler_movement_flagger import ThermocyclerMovementFlagger
|
|
@@ -37,8 +39,6 @@ if TYPE_CHECKING:
|
|
|
37
39
|
_GRIPPER_HOMED_POSITION_Z = 166.125 # Height of the center of the gripper critical point from the deck when homed
|
|
38
40
|
|
|
39
41
|
|
|
40
|
-
# TODO (spp, 2022-10-20): name this GripperMovementHandler if it doesn't handle
|
|
41
|
-
# any non-gripper implementations
|
|
42
42
|
class LabwareMovementHandler:
|
|
43
43
|
"""Implementation logic for labware movement."""
|
|
44
44
|
|
|
@@ -81,24 +81,64 @@ class LabwareMovementHandler:
|
|
|
81
81
|
)
|
|
82
82
|
)
|
|
83
83
|
|
|
84
|
+
@overload
|
|
84
85
|
async def move_labware_with_gripper(
|
|
85
86
|
self,
|
|
87
|
+
*,
|
|
86
88
|
labware_id: str,
|
|
87
89
|
current_location: OnDeckLabwareLocation,
|
|
88
90
|
new_location: OnDeckLabwareLocation,
|
|
89
91
|
user_offset_data: LabwareMovementOffsetData,
|
|
90
92
|
post_drop_slide_offset: Optional[Point],
|
|
91
93
|
) -> None:
|
|
92
|
-
|
|
94
|
+
...
|
|
95
|
+
|
|
96
|
+
@overload
|
|
97
|
+
async def move_labware_with_gripper(
|
|
98
|
+
self,
|
|
99
|
+
*,
|
|
100
|
+
labware_definition: LabwareDefinition,
|
|
101
|
+
current_location: OnDeckLabwareLocation,
|
|
102
|
+
new_location: OnDeckLabwareLocation,
|
|
103
|
+
user_offset_data: LabwareMovementOffsetData,
|
|
104
|
+
post_drop_slide_offset: Optional[Point],
|
|
105
|
+
) -> None:
|
|
106
|
+
...
|
|
107
|
+
|
|
108
|
+
async def move_labware_with_gripper( # noqa: C901
|
|
109
|
+
self,
|
|
110
|
+
*,
|
|
111
|
+
labware_id: str | None = None,
|
|
112
|
+
labware_definition: LabwareDefinition | None = None,
|
|
113
|
+
current_location: OnDeckLabwareLocation,
|
|
114
|
+
new_location: OnDeckLabwareLocation,
|
|
115
|
+
user_offset_data: LabwareMovementOffsetData,
|
|
116
|
+
post_drop_slide_offset: Optional[Point],
|
|
117
|
+
) -> None:
|
|
118
|
+
"""Physically move a labware from one location to another using the gripper.
|
|
119
|
+
|
|
120
|
+
Generally, provide the `labware_id` of a loaded labware, and this method will
|
|
121
|
+
automatically look up its labware definition. If you're physically moving
|
|
122
|
+
something that has not been loaded as a labware (this is not common),
|
|
123
|
+
provide the `labware_definition` yourself instead.
|
|
124
|
+
"""
|
|
93
125
|
use_virtual_gripper = self._state_store.config.use_virtual_gripper
|
|
94
126
|
|
|
127
|
+
if labware_definition is None:
|
|
128
|
+
assert labware_id is not None # From this method's @typing.overloads.
|
|
129
|
+
labware_definition = self._state_store.labware.get_definition(labware_id)
|
|
130
|
+
|
|
95
131
|
if use_virtual_gripper:
|
|
96
|
-
#
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
132
|
+
# todo(mm, 2024-11-07): We should do this collision checking even when we
|
|
133
|
+
# only have a `labware_definition`, not a `labware_id`. Resolve when
|
|
134
|
+
# `check_gripper_labware_tip_collision()` can be made independent of `labware_id`.
|
|
135
|
+
if labware_id is not None:
|
|
136
|
+
self._state_store.geometry.check_gripper_labware_tip_collision(
|
|
137
|
+
# During Analysis we will pass in hard coded estimates for certain positions only accessible during execution
|
|
138
|
+
gripper_homed_position_z=_GRIPPER_HOMED_POSITION_Z,
|
|
139
|
+
labware_id=labware_id,
|
|
140
|
+
current_location=current_location,
|
|
141
|
+
)
|
|
102
142
|
return
|
|
103
143
|
|
|
104
144
|
ot3api = ensure_ot3_hardware(
|
|
@@ -121,12 +161,15 @@ class LabwareMovementHandler:
|
|
|
121
161
|
await ot3api.home(axes=[Axis.Z_L, Axis.Z_R, Axis.Z_G])
|
|
122
162
|
gripper_homed_position = await ot3api.gantry_position(mount=gripper_mount)
|
|
123
163
|
|
|
124
|
-
#
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
164
|
+
# todo(mm, 2024-11-07): We should do this collision checking even when we
|
|
165
|
+
# only have a `labware_definition`, not a `labware_id`. Resolve when
|
|
166
|
+
# `check_gripper_labware_tip_collision()` can be made independent of `labware_id`.
|
|
167
|
+
if labware_id is not None:
|
|
168
|
+
self._state_store.geometry.check_gripper_labware_tip_collision(
|
|
169
|
+
gripper_homed_position_z=gripper_homed_position.z,
|
|
170
|
+
labware_id=labware_id,
|
|
171
|
+
current_location=current_location,
|
|
172
|
+
)
|
|
130
173
|
|
|
131
174
|
async with self._thermocycler_plate_lifter.lift_plate_for_labware_movement(
|
|
132
175
|
labware_location=current_location
|
|
@@ -136,13 +179,14 @@ class LabwareMovementHandler:
|
|
|
136
179
|
from_location=current_location,
|
|
137
180
|
to_location=new_location,
|
|
138
181
|
additional_offset_vector=user_offset_data,
|
|
182
|
+
current_labware=labware_definition,
|
|
139
183
|
)
|
|
140
184
|
)
|
|
141
185
|
from_labware_center = self._state_store.geometry.get_labware_grip_point(
|
|
142
|
-
|
|
186
|
+
labware_definition=labware_definition, location=current_location
|
|
143
187
|
)
|
|
144
188
|
to_labware_center = self._state_store.geometry.get_labware_grip_point(
|
|
145
|
-
|
|
189
|
+
labware_definition=labware_definition, location=new_location
|
|
146
190
|
)
|
|
147
191
|
movement_waypoints = get_gripper_labware_movement_waypoints(
|
|
148
192
|
from_labware_center=from_labware_center,
|
|
@@ -151,7 +195,9 @@ class LabwareMovementHandler:
|
|
|
151
195
|
offset_data=final_offsets,
|
|
152
196
|
post_drop_slide_offset=post_drop_slide_offset,
|
|
153
197
|
)
|
|
154
|
-
labware_grip_force = self._state_store.labware.get_grip_force(
|
|
198
|
+
labware_grip_force = self._state_store.labware.get_grip_force(
|
|
199
|
+
labware_definition
|
|
200
|
+
)
|
|
155
201
|
holding_labware = False
|
|
156
202
|
for waypoint_data in movement_waypoints:
|
|
157
203
|
if waypoint_data.jaw_open:
|
|
@@ -174,9 +220,14 @@ class LabwareMovementHandler:
|
|
|
174
220
|
# should be holding labware
|
|
175
221
|
if holding_labware:
|
|
176
222
|
labware_bbox = self._state_store.labware.get_dimensions(
|
|
177
|
-
|
|
223
|
+
labware_definition=labware_definition
|
|
224
|
+
)
|
|
225
|
+
well_bbox = self._state_store.labware.get_well_bbox(
|
|
226
|
+
labware_definition=labware_definition
|
|
178
227
|
)
|
|
179
|
-
|
|
228
|
+
# todo(mm, 2024-09-26): This currently raises a lower-level 2015 FailedGripperPickupError.
|
|
229
|
+
# Convert this to a higher-level 3001 LabwareDroppedError or 3002 LabwareNotPickedUpError,
|
|
230
|
+
# depending on what waypoint we're at, to propagate a more specific error code to users.
|
|
180
231
|
ot3api.raise_error_if_gripper_pickup_failed(
|
|
181
232
|
expected_grip_width=labware_bbox.y,
|
|
182
233
|
grip_width_uncertainty_wider=abs(
|
|
@@ -2,21 +2,23 @@
|
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
4
|
import logging
|
|
5
|
-
from typing import Optional, List
|
|
5
|
+
from typing import Optional, List, Union
|
|
6
6
|
|
|
7
|
-
from opentrons.types import Point, MountType
|
|
7
|
+
from opentrons.types import Point, MountType, StagingSlotName
|
|
8
8
|
from opentrons.hardware_control import HardwareControlAPI
|
|
9
9
|
from opentrons_shared_data.errors.exceptions import PositionUnknownError
|
|
10
|
+
from opentrons.protocol_engine.errors import LocationIsStagingSlotError
|
|
10
11
|
|
|
11
12
|
from ..types import (
|
|
12
13
|
WellLocation,
|
|
14
|
+
LiquidHandlingWellLocation,
|
|
13
15
|
DeckPoint,
|
|
14
16
|
MovementAxis,
|
|
15
17
|
MotorAxis,
|
|
16
18
|
CurrentWell,
|
|
17
19
|
AddressableOffsetVector,
|
|
18
20
|
)
|
|
19
|
-
from ..state import StateStore
|
|
21
|
+
from ..state.state import StateStore
|
|
20
22
|
from ..resources import ModelUtils
|
|
21
23
|
from .thermocycler_movement_flagger import ThermocyclerMovementFlagger
|
|
22
24
|
from .heater_shaker_movement_flagger import HeaterShakerMovementFlagger
|
|
@@ -66,11 +68,12 @@ class MovementHandler:
|
|
|
66
68
|
pipette_id: str,
|
|
67
69
|
labware_id: str,
|
|
68
70
|
well_name: str,
|
|
69
|
-
well_location: Optional[WellLocation] = None,
|
|
71
|
+
well_location: Optional[Union[WellLocation, LiquidHandlingWellLocation]] = None,
|
|
70
72
|
current_well: Optional[CurrentWell] = None,
|
|
71
73
|
force_direct: bool = False,
|
|
72
74
|
minimum_z_height: Optional[float] = None,
|
|
73
75
|
speed: Optional[float] = None,
|
|
76
|
+
operation_volume: Optional[float] = None,
|
|
74
77
|
) -> Point:
|
|
75
78
|
"""Move to a specific well."""
|
|
76
79
|
self._state_store.labware.raise_if_labware_inaccessible_by_pipette(
|
|
@@ -91,9 +94,13 @@ class MovementHandler:
|
|
|
91
94
|
self._state_store.modules.get_heater_shaker_movement_restrictors()
|
|
92
95
|
)
|
|
93
96
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
+
ancestor = self._state_store.geometry.get_ancestor_slot_name(labware_id)
|
|
98
|
+
if isinstance(ancestor, StagingSlotName):
|
|
99
|
+
raise LocationIsStagingSlotError(
|
|
100
|
+
"Cannot move to well on labware in Staging Area Slot."
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
dest_slot_int = ancestor.as_int()
|
|
97
104
|
|
|
98
105
|
self._hs_movement_flagger.raise_if_movement_restricted(
|
|
99
106
|
hs_movement_restrictors=hs_movement_restrictors,
|
|
@@ -129,6 +136,7 @@ class MovementHandler:
|
|
|
129
136
|
current_well=current_well,
|
|
130
137
|
force_direct=force_direct,
|
|
131
138
|
minimum_z_height=minimum_z_height,
|
|
139
|
+
operation_volume=operation_volume,
|
|
132
140
|
)
|
|
133
141
|
|
|
134
142
|
speed = self._state_store.pipettes.get_movement_speed(
|
|
@@ -151,6 +159,7 @@ class MovementHandler:
|
|
|
151
159
|
speed: Optional[float] = None,
|
|
152
160
|
stay_at_highest_possible_z: bool = False,
|
|
153
161
|
ignore_tip_configuration: Optional[bool] = True,
|
|
162
|
+
highest_possible_z_extra_offset: Optional[float] = None,
|
|
154
163
|
) -> Point:
|
|
155
164
|
"""Move to a specific addressable area."""
|
|
156
165
|
# Check for presence of heater shakers on deck, and if planned
|
|
@@ -201,6 +210,7 @@ class MovementHandler:
|
|
|
201
210
|
minimum_z_height=minimum_z_height,
|
|
202
211
|
stay_at_max_travel_z=stay_at_highest_possible_z,
|
|
203
212
|
ignore_tip_configuration=ignore_tip_configuration,
|
|
213
|
+
max_travel_z_extra_margin=highest_possible_z_extra_offset,
|
|
204
214
|
)
|
|
205
215
|
|
|
206
216
|
speed = self._state_store.pipettes.get_movement_speed(
|
|
@@ -5,7 +5,8 @@ from contextlib import contextmanager
|
|
|
5
5
|
|
|
6
6
|
from opentrons.hardware_control import HardwareControlAPI
|
|
7
7
|
|
|
8
|
-
from ..state import StateView
|
|
8
|
+
from ..state.state import StateView
|
|
9
|
+
from ..state.pipettes import HardwarePipette
|
|
9
10
|
from ..notes import CommandNoteAdder, CommandNote
|
|
10
11
|
from ..errors.exceptions import (
|
|
11
12
|
TipNotAttachedError,
|
|
@@ -186,7 +187,9 @@ class HardwarePipettingHandler(PipettingHandler):
|
|
|
186
187
|
mount=hw_pipette.mount,
|
|
187
188
|
max_z_dist=well_depth - lld_min_height + well_location.offset.z,
|
|
188
189
|
)
|
|
189
|
-
|
|
190
|
+
labware_pos = self._state_view.geometry.get_labware_position(labware_id)
|
|
191
|
+
relative_height = z_pos - labware_pos.z - well_def.z
|
|
192
|
+
return float(relative_height)
|
|
190
193
|
|
|
191
194
|
@contextmanager
|
|
192
195
|
def _set_flow_rate(
|
|
@@ -285,8 +288,8 @@ class VirtualPipettingHandler(PipettingHandler):
|
|
|
285
288
|
well_location: WellLocation,
|
|
286
289
|
) -> float:
|
|
287
290
|
"""Detect liquid level."""
|
|
288
|
-
|
|
289
|
-
return
|
|
291
|
+
well_def = self._state_view.labware.get_well_definition(labware_id, well_name)
|
|
292
|
+
return well_def.depth
|
|
290
293
|
|
|
291
294
|
def _validate_tip_attached(self, pipette_id: str, command_name: str) -> None:
|
|
292
295
|
"""Validate if there is a tip attached."""
|
|
@@ -3,7 +3,7 @@ import asyncio
|
|
|
3
3
|
from logging import getLogger
|
|
4
4
|
from typing import Optional, AsyncGenerator, Callable
|
|
5
5
|
|
|
6
|
-
from ..state import StateStore
|
|
6
|
+
from ..state.state import StateStore
|
|
7
7
|
from .command_executor import CommandExecutor
|
|
8
8
|
|
|
9
9
|
log = getLogger(__name__)
|
|
@@ -69,7 +69,11 @@ class QueueWorker:
|
|
|
69
69
|
|
|
70
70
|
async def _run_commands(self) -> None:
|
|
71
71
|
async for command_id in self._command_generator():
|
|
72
|
-
|
|
72
|
+
try:
|
|
73
|
+
await self._command_executor.execute(command_id=command_id)
|
|
74
|
+
except BaseException:
|
|
75
|
+
log.exception("Unhandled failure in command executor")
|
|
76
|
+
raise
|
|
73
77
|
# Yield to the event loop in case we're executing a long sequence of commands
|
|
74
78
|
# that never yields internally. For example, a long sequence of comment commands.
|
|
75
79
|
await asyncio.sleep(0)
|
|
@@ -7,7 +7,7 @@ from opentrons.hardware_control import HardwareControlAPI
|
|
|
7
7
|
from opentrons.hardware_control.modules import Thermocycler as HardwareThermocycler
|
|
8
8
|
|
|
9
9
|
from ..types import ModuleLocation, LabwareLocation
|
|
10
|
-
from ..state import StateStore
|
|
10
|
+
from ..state.state import StateStore
|
|
11
11
|
from ..errors import ThermocyclerNotOpenError, WrongModuleTypeError
|
|
12
12
|
|
|
13
13
|
|
|
@@ -5,7 +5,8 @@ import asyncio
|
|
|
5
5
|
from typing import TYPE_CHECKING, AsyncGenerator, Optional
|
|
6
6
|
from opentrons.hardware_control.modules.thermocycler import Thermocycler
|
|
7
7
|
from opentrons.protocol_engine.types import LabwareLocation, ModuleLocation, ModuleModel
|
|
8
|
-
from opentrons.protocol_engine.state import StateStore
|
|
8
|
+
from opentrons.protocol_engine.state.state import StateStore
|
|
9
|
+
from opentrons.protocol_engine.state.module_substates import ThermocyclerModuleId
|
|
9
10
|
from contextlib import asynccontextmanager
|
|
10
11
|
|
|
11
12
|
if TYPE_CHECKING:
|
|
@@ -4,6 +4,9 @@ from typing_extensions import Protocol as TypingProtocol
|
|
|
4
4
|
|
|
5
5
|
from opentrons.hardware_control import HardwareControlAPI
|
|
6
6
|
from opentrons.hardware_control.types import FailedTipStateCheck, InstrumentProbeType
|
|
7
|
+
from opentrons.protocol_engine.errors.exceptions import PickUpTipTipNotAttachedError
|
|
8
|
+
from opentrons.types import Mount
|
|
9
|
+
|
|
7
10
|
from opentrons_shared_data.errors.exceptions import (
|
|
8
11
|
CommandPreconditionViolated,
|
|
9
12
|
CommandParameterLimitViolated,
|
|
@@ -11,7 +14,7 @@ from opentrons_shared_data.errors.exceptions import (
|
|
|
11
14
|
)
|
|
12
15
|
|
|
13
16
|
from ..resources import LabwareDataProvider, ensure_ot3_hardware
|
|
14
|
-
from ..state import StateView
|
|
17
|
+
from ..state.state import StateView
|
|
15
18
|
from ..types import TipGeometry, TipPresenceStatus
|
|
16
19
|
from ..errors import (
|
|
17
20
|
HardwareNotSupportedError,
|
|
@@ -68,18 +71,27 @@ class TipHandler(TypingProtocol):
|
|
|
68
71
|
|
|
69
72
|
Returns:
|
|
70
73
|
Tip geometry of the picked up tip.
|
|
74
|
+
|
|
75
|
+
Raises:
|
|
76
|
+
PickUpTipTipNotAttachedError
|
|
71
77
|
"""
|
|
72
78
|
...
|
|
73
79
|
|
|
74
80
|
async def drop_tip(self, pipette_id: str, home_after: Optional[bool]) -> None:
|
|
75
|
-
"""Drop the attached tip into the
|
|
81
|
+
"""Drop the attached tip into the current location.
|
|
76
82
|
|
|
77
83
|
Pipette should be in place over the destination prior to calling this method.
|
|
84
|
+
|
|
85
|
+
Raises:
|
|
86
|
+
TipAttachedError
|
|
78
87
|
"""
|
|
79
88
|
|
|
80
|
-
|
|
89
|
+
def cache_tip(self, pipette_id: str, tip: TipGeometry) -> None:
|
|
81
90
|
"""Tell the Hardware API that a tip is attached."""
|
|
82
91
|
|
|
92
|
+
def remove_tip(self, pipette_id: str) -> None:
|
|
93
|
+
"""Tell the hardware API that no tip is attached."""
|
|
94
|
+
|
|
83
95
|
async def get_tip_presence(self, pipette_id: str) -> TipPresenceStatus:
|
|
84
96
|
"""Get tip presence status on the pipette."""
|
|
85
97
|
|
|
@@ -89,7 +101,12 @@ class TipHandler(TypingProtocol):
|
|
|
89
101
|
expected: TipPresenceStatus,
|
|
90
102
|
follow_singular_sensor: Optional[InstrumentProbeType] = None,
|
|
91
103
|
) -> None:
|
|
92
|
-
"""
|
|
104
|
+
"""Use sensors to verify that a tip is or is not physically attached.
|
|
105
|
+
|
|
106
|
+
Raises:
|
|
107
|
+
TipNotAttachedError or TipAttachedError, as appropriate, if the physical
|
|
108
|
+
status doesn't match what was expected.
|
|
109
|
+
"""
|
|
93
110
|
|
|
94
111
|
|
|
95
112
|
async def _available_for_nozzle_layout( # noqa: C901
|
|
@@ -187,6 +204,11 @@ class HardwareTipHandler(TipHandler):
|
|
|
187
204
|
self._labware_data_provider = labware_data_provider or LabwareDataProvider()
|
|
188
205
|
self._state_view = state_view
|
|
189
206
|
|
|
207
|
+
# WARNING: ErrorRecoveryHardwareStateSynchronizer can currently construct several
|
|
208
|
+
# instances of this class per run, in addition to the main instance used
|
|
209
|
+
# for command execution. We're therefore depending on this class being
|
|
210
|
+
# stateless, so consider that before adding additional attributes here.
|
|
211
|
+
|
|
190
212
|
async def available_for_nozzle_layout(
|
|
191
213
|
self,
|
|
192
214
|
pipette_id: str,
|
|
@@ -195,7 +217,7 @@ class HardwareTipHandler(TipHandler):
|
|
|
195
217
|
front_right_nozzle: Optional[str] = None,
|
|
196
218
|
back_left_nozzle: Optional[str] = None,
|
|
197
219
|
) -> Dict[str, str]:
|
|
198
|
-
"""
|
|
220
|
+
"""See documentation on abstract base class."""
|
|
199
221
|
if self._state_view.pipettes.get_attached_tip(pipette_id):
|
|
200
222
|
raise CommandPreconditionViolated(
|
|
201
223
|
message=f"Cannot configure nozzle layout of {str(self)} while it has tips attached."
|
|
@@ -211,8 +233,8 @@ class HardwareTipHandler(TipHandler):
|
|
|
211
233
|
labware_id: str,
|
|
212
234
|
well_name: str,
|
|
213
235
|
) -> TipGeometry:
|
|
214
|
-
"""
|
|
215
|
-
hw_mount = self.
|
|
236
|
+
"""See documentation on abstract base class."""
|
|
237
|
+
hw_mount = self._get_hw_mount(pipette_id)
|
|
216
238
|
|
|
217
239
|
nominal_tip_geometry = self._state_view.geometry.get_nominal_tip_geometry(
|
|
218
240
|
pipette_id=pipette_id, labware_id=labware_id, well_name=well_name
|
|
@@ -224,33 +246,29 @@ class HardwareTipHandler(TipHandler):
|
|
|
224
246
|
nominal_fallback=nominal_tip_geometry.length,
|
|
225
247
|
)
|
|
226
248
|
|
|
249
|
+
tip_geometry = TipGeometry(
|
|
250
|
+
length=actual_tip_length,
|
|
251
|
+
diameter=nominal_tip_geometry.diameter,
|
|
252
|
+
volume=nominal_tip_geometry.volume,
|
|
253
|
+
)
|
|
254
|
+
|
|
227
255
|
await self._hardware_api.tip_pickup_moves(
|
|
228
256
|
mount=hw_mount, presses=None, increment=None
|
|
229
257
|
)
|
|
230
|
-
|
|
258
|
+
try:
|
|
259
|
+
await self.verify_tip_presence(pipette_id, TipPresenceStatus.PRESENT)
|
|
260
|
+
except TipNotAttachedError as e:
|
|
261
|
+
raise PickUpTipTipNotAttachedError(tip_geometry=tip_geometry) from e
|
|
231
262
|
|
|
232
|
-
self.
|
|
233
|
-
await self._hardware_api.prepare_for_aspirate(hw_mount)
|
|
263
|
+
self.cache_tip(pipette_id, tip_geometry)
|
|
234
264
|
|
|
235
|
-
self._hardware_api.
|
|
236
|
-
mount=hw_mount,
|
|
237
|
-
tiprack_diameter=nominal_tip_geometry.diameter,
|
|
238
|
-
)
|
|
239
|
-
|
|
240
|
-
self._hardware_api.set_working_volume(
|
|
241
|
-
mount=hw_mount,
|
|
242
|
-
tip_volume=nominal_tip_geometry.volume,
|
|
243
|
-
)
|
|
265
|
+
await self._hardware_api.prepare_for_aspirate(hw_mount)
|
|
244
266
|
|
|
245
|
-
return
|
|
246
|
-
length=actual_tip_length,
|
|
247
|
-
diameter=nominal_tip_geometry.diameter,
|
|
248
|
-
volume=nominal_tip_geometry.volume,
|
|
249
|
-
)
|
|
267
|
+
return tip_geometry
|
|
250
268
|
|
|
251
269
|
async def drop_tip(self, pipette_id: str, home_after: Optional[bool]) -> None:
|
|
252
|
-
"""
|
|
253
|
-
hw_mount = self.
|
|
270
|
+
"""See documentation on abstract base class."""
|
|
271
|
+
hw_mount = self._get_hw_mount(pipette_id)
|
|
254
272
|
|
|
255
273
|
# Let the hardware controller handle defaulting home_after since its behavior
|
|
256
274
|
# differs between machines
|
|
@@ -259,14 +277,18 @@ class HardwareTipHandler(TipHandler):
|
|
|
259
277
|
else:
|
|
260
278
|
kwargs = {}
|
|
261
279
|
|
|
262
|
-
await self._hardware_api.
|
|
280
|
+
await self._hardware_api.tip_drop_moves(mount=hw_mount, **kwargs)
|
|
281
|
+
|
|
282
|
+
# Allow TipNotAttachedError to propagate.
|
|
263
283
|
await self.verify_tip_presence(pipette_id, TipPresenceStatus.ABSENT)
|
|
264
284
|
|
|
265
|
-
|
|
266
|
-
"""Tell the Hardware API that a tip is attached."""
|
|
267
|
-
hw_mount = self._state_view.pipettes.get_mount(pipette_id).to_hw_mount()
|
|
285
|
+
self.remove_tip(pipette_id)
|
|
268
286
|
|
|
269
|
-
|
|
287
|
+
def cache_tip(self, pipette_id: str, tip: TipGeometry) -> None:
|
|
288
|
+
"""See documentation on abstract base class."""
|
|
289
|
+
hw_mount = self._get_hw_mount(pipette_id)
|
|
290
|
+
|
|
291
|
+
self._hardware_api.cache_tip(mount=hw_mount, tip_length=tip.length)
|
|
270
292
|
|
|
271
293
|
self._hardware_api.set_current_tiprack_diameter(
|
|
272
294
|
mount=hw_mount,
|
|
@@ -278,12 +300,18 @@ class HardwareTipHandler(TipHandler):
|
|
|
278
300
|
tip_volume=tip.volume,
|
|
279
301
|
)
|
|
280
302
|
|
|
303
|
+
def remove_tip(self, pipette_id: str) -> None:
|
|
304
|
+
"""See documentation on abstract base class."""
|
|
305
|
+
hw_mount = self._get_hw_mount(pipette_id)
|
|
306
|
+
self._hardware_api.remove_tip(hw_mount)
|
|
307
|
+
self._hardware_api.set_current_tiprack_diameter(hw_mount, 0)
|
|
308
|
+
|
|
281
309
|
async def get_tip_presence(self, pipette_id: str) -> TipPresenceStatus:
|
|
282
|
-
"""
|
|
310
|
+
"""See documentation on abstract base class."""
|
|
283
311
|
try:
|
|
284
312
|
ot3api = ensure_ot3_hardware(hardware_api=self._hardware_api)
|
|
285
313
|
|
|
286
|
-
hw_mount = self.
|
|
314
|
+
hw_mount = self._get_hw_mount(pipette_id)
|
|
287
315
|
|
|
288
316
|
status = await ot3api.get_tip_presence_status(hw_mount)
|
|
289
317
|
return TipPresenceStatus.from_hw_state(status)
|
|
@@ -297,11 +325,7 @@ class HardwareTipHandler(TipHandler):
|
|
|
297
325
|
expected: TipPresenceStatus,
|
|
298
326
|
follow_singular_sensor: Optional[InstrumentProbeType] = None,
|
|
299
327
|
) -> None:
|
|
300
|
-
"""
|
|
301
|
-
|
|
302
|
-
This function will raise an exception if the specified tip presence status
|
|
303
|
-
isn't matched.
|
|
304
|
-
"""
|
|
328
|
+
"""See documentation on abstract base class."""
|
|
305
329
|
nozzle_configuration = (
|
|
306
330
|
self._state_view.pipettes.state.nozzle_configuration_by_id[pipette_id]
|
|
307
331
|
)
|
|
@@ -328,7 +352,7 @@ class HardwareTipHandler(TipHandler):
|
|
|
328
352
|
return
|
|
329
353
|
try:
|
|
330
354
|
ot3api = ensure_ot3_hardware(hardware_api=self._hardware_api)
|
|
331
|
-
hw_mount = self.
|
|
355
|
+
hw_mount = self._get_hw_mount(pipette_id)
|
|
332
356
|
await ot3api.verify_tip_presence(
|
|
333
357
|
hw_mount, expected.to_hw_state(), follow_singular_sensor
|
|
334
358
|
)
|
|
@@ -346,6 +370,9 @@ class HardwareTipHandler(TipHandler):
|
|
|
346
370
|
wrapping=[PythonException(e)],
|
|
347
371
|
)
|
|
348
372
|
|
|
373
|
+
def _get_hw_mount(self, pipette_id: str) -> Mount:
|
|
374
|
+
return self._state_view.pipettes.get_mount(pipette_id).to_hw_mount()
|
|
375
|
+
|
|
349
376
|
|
|
350
377
|
class VirtualTipHandler(TipHandler):
|
|
351
378
|
"""Pick up and drop tips, using a virtual pipette."""
|
|
@@ -385,7 +412,7 @@ class VirtualTipHandler(TipHandler):
|
|
|
385
412
|
front_right_nozzle: Optional[str] = None,
|
|
386
413
|
back_left_nozzle: Optional[str] = None,
|
|
387
414
|
) -> Dict[str, str]:
|
|
388
|
-
"""
|
|
415
|
+
"""See documentation on abstract base class."""
|
|
389
416
|
if self._state_view.pipettes.get_attached_tip(pipette_id):
|
|
390
417
|
raise CommandPreconditionViolated(
|
|
391
418
|
message=f"Cannot configure nozzle layout of {str(self)} while it has tips attached."
|
|
@@ -409,12 +436,19 @@ class VirtualTipHandler(TipHandler):
|
|
|
409
436
|
expected_has_tip=True,
|
|
410
437
|
)
|
|
411
438
|
|
|
412
|
-
|
|
413
|
-
"""
|
|
439
|
+
def cache_tip(self, pipette_id: str, tip: TipGeometry) -> None:
|
|
440
|
+
"""See documentation on abstract base class.
|
|
441
|
+
|
|
442
|
+
This should not be called when using virtual pipettes.
|
|
443
|
+
"""
|
|
444
|
+
assert False, "TipHandler.cache_tip should not be used with virtual pipettes"
|
|
445
|
+
|
|
446
|
+
def remove_tip(self, pipette_id: str) -> None:
|
|
447
|
+
"""See documentation on abstract base class.
|
|
414
448
|
|
|
415
449
|
This should not be called when using virtual pipettes.
|
|
416
450
|
"""
|
|
417
|
-
assert False, "TipHandler.
|
|
451
|
+
assert False, "TipHandler.remove_tip should not be used with virtual pipettes"
|
|
418
452
|
|
|
419
453
|
async def verify_tip_presence(
|
|
420
454
|
self,
|
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
"""Protocol engine notes module."""
|
|
2
2
|
|
|
3
|
-
from .notes import
|
|
3
|
+
from .notes import (
|
|
4
|
+
NoteKind,
|
|
5
|
+
CommandNote,
|
|
6
|
+
CommandNoteAdder,
|
|
7
|
+
CommandNoteTracker,
|
|
8
|
+
make_error_recovery_debug_note,
|
|
9
|
+
)
|
|
4
10
|
|
|
5
|
-
__all__ = [
|
|
11
|
+
__all__ = [
|
|
12
|
+
"NoteKind",
|
|
13
|
+
"CommandNote",
|
|
14
|
+
"CommandNoteAdder",
|
|
15
|
+
"CommandNoteTracker",
|
|
16
|
+
"make_error_recovery_debug_note",
|
|
17
|
+
]
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
"""Definitions of data and interface shapes for notes."""
|
|
2
|
-
from typing import Union, Literal, Protocol, List
|
|
2
|
+
from typing import Union, Literal, Protocol, List, TYPE_CHECKING
|
|
3
3
|
from pydantic import BaseModel, Field
|
|
4
4
|
|
|
5
|
+
if TYPE_CHECKING:
|
|
6
|
+
from opentrons.protocol_engine.error_recovery_policy import ErrorRecoveryType
|
|
7
|
+
|
|
5
8
|
NoteKind = Union[Literal["warning", "information"], str]
|
|
6
9
|
|
|
7
10
|
|
|
@@ -26,6 +29,20 @@ class CommandNote(BaseModel):
|
|
|
26
29
|
)
|
|
27
30
|
|
|
28
31
|
|
|
32
|
+
def make_error_recovery_debug_note(type: "ErrorRecoveryType") -> CommandNote:
|
|
33
|
+
"""Return a note for debugging error recovery.
|
|
34
|
+
|
|
35
|
+
This is intended to be read by developers and support people, not computers.
|
|
36
|
+
"""
|
|
37
|
+
message = f"Handling this command failure with {type.name}."
|
|
38
|
+
return CommandNote.construct(
|
|
39
|
+
noteKind="debugErrorRecovery",
|
|
40
|
+
shortMessage=message,
|
|
41
|
+
longMessage=message,
|
|
42
|
+
source="execution",
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
29
46
|
class CommandNoteAdder(Protocol):
|
|
30
47
|
"""The shape of a function that something can use to add a command note."""
|
|
31
48
|
|