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
|
@@ -8,6 +8,7 @@ from .exceptions import (
|
|
|
8
8
|
InvalidSpecificationForRobotTypeError,
|
|
9
9
|
InvalidLoadPipetteSpecsError,
|
|
10
10
|
TipNotAttachedError,
|
|
11
|
+
PickUpTipTipNotAttachedError,
|
|
11
12
|
TipAttachedError,
|
|
12
13
|
CommandDoesNotExistError,
|
|
13
14
|
LabwareNotLoadedError,
|
|
@@ -54,6 +55,7 @@ from .exceptions import (
|
|
|
54
55
|
InvalidTargetTemperatureError,
|
|
55
56
|
InvalidBlockVolumeError,
|
|
56
57
|
InvalidHoldTimeError,
|
|
58
|
+
InvalidWavelengthError,
|
|
57
59
|
CannotPerformModuleAction,
|
|
58
60
|
PauseNotAllowedError,
|
|
59
61
|
ResumeFromRecoveryNotAllowedError,
|
|
@@ -65,9 +67,17 @@ from .exceptions import (
|
|
|
65
67
|
LocationIsOccupiedError,
|
|
66
68
|
LocationNotAccessibleByPipetteError,
|
|
67
69
|
LocationIsStagingSlotError,
|
|
70
|
+
LocationIsLidDockSlotError,
|
|
68
71
|
InvalidAxisForRobotType,
|
|
69
72
|
NotSupportedOnRobotType,
|
|
70
73
|
CommandNotAllowedError,
|
|
74
|
+
InvalidLiquidHeightFound,
|
|
75
|
+
LiquidHeightUnknownError,
|
|
76
|
+
IncompleteLabwareDefinitionError,
|
|
77
|
+
IncompleteWellDefinitionError,
|
|
78
|
+
OperationLocationNotInWellError,
|
|
79
|
+
InvalidDispenseVolumeError,
|
|
80
|
+
StorageLimitReachedError,
|
|
71
81
|
)
|
|
72
82
|
|
|
73
83
|
from .error_occurrence import ErrorOccurrence, ProtocolCommandFailedError
|
|
@@ -81,6 +91,7 @@ __all__ = [
|
|
|
81
91
|
"InvalidSpecificationForRobotTypeError",
|
|
82
92
|
"InvalidLoadPipetteSpecsError",
|
|
83
93
|
"TipNotAttachedError",
|
|
94
|
+
"PickUpTipTipNotAttachedError",
|
|
84
95
|
"TipAttachedError",
|
|
85
96
|
"CommandDoesNotExistError",
|
|
86
97
|
"LabwareNotLoadedError",
|
|
@@ -127,6 +138,7 @@ __all__ = [
|
|
|
127
138
|
"InvalidTargetSpeedError",
|
|
128
139
|
"InvalidBlockVolumeError",
|
|
129
140
|
"InvalidHoldTimeError",
|
|
141
|
+
"InvalidWavelengthError",
|
|
130
142
|
"CannotPerformModuleAction",
|
|
131
143
|
"ResumeFromRecoveryNotAllowedError",
|
|
132
144
|
"PauseNotAllowedError",
|
|
@@ -139,9 +151,17 @@ __all__ = [
|
|
|
139
151
|
"LocationIsOccupiedError",
|
|
140
152
|
"LocationNotAccessibleByPipetteError",
|
|
141
153
|
"LocationIsStagingSlotError",
|
|
154
|
+
"LocationIsLidDockSlotError",
|
|
142
155
|
"InvalidAxisForRobotType",
|
|
143
156
|
"NotSupportedOnRobotType",
|
|
144
157
|
# error occurrence models
|
|
145
158
|
"ErrorOccurrence",
|
|
146
159
|
"CommandNotAllowedError",
|
|
160
|
+
"InvalidLiquidHeightFound",
|
|
161
|
+
"LiquidHeightUnknownError",
|
|
162
|
+
"IncompleteLabwareDefinitionError",
|
|
163
|
+
"IncompleteWellDefinitionError",
|
|
164
|
+
"OperationLocationNotInWellError",
|
|
165
|
+
"InvalidDispenseVolumeError",
|
|
166
|
+
"StorageLimitReachedError",
|
|
147
167
|
]
|
|
@@ -12,8 +12,6 @@ from opentrons_shared_data.errors.exceptions import EnumeratedError
|
|
|
12
12
|
log = getLogger(__name__)
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
# TODO(mc, 2021-11-12): flesh this model out with structured error data
|
|
16
|
-
# for each error type so client may produce better error messages
|
|
17
15
|
class ErrorOccurrence(BaseModel):
|
|
18
16
|
"""An occurrence of a specific error during protocol execution."""
|
|
19
17
|
|
|
@@ -44,8 +42,15 @@ class ErrorOccurrence(BaseModel):
|
|
|
44
42
|
id: str = Field(..., description="Unique identifier of this error occurrence.")
|
|
45
43
|
createdAt: datetime = Field(..., description="When the error occurred.")
|
|
46
44
|
|
|
45
|
+
# Our Python should probably always set this to False--if we want it to be True,
|
|
46
|
+
# we should probably be using a more specific subclass of ErrorOccurrence anyway.
|
|
47
|
+
# However, we can't make this Literal[False], because we want this class to be able
|
|
48
|
+
# to act as a catch-all for parsing defined errors that might be missing some
|
|
49
|
+
# `errorInfo` fields because they were serialized by older software.
|
|
47
50
|
isDefined: bool = Field(
|
|
48
|
-
|
|
51
|
+
# default=False for database backwards compatibility, so we can parse objects
|
|
52
|
+
# serialized before isDefined existed.
|
|
53
|
+
default=False,
|
|
49
54
|
description=dedent(
|
|
50
55
|
"""\
|
|
51
56
|
Whether this error is *defined.*
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
"""Protocol engine exceptions."""
|
|
2
2
|
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
3
5
|
from logging import getLogger
|
|
4
|
-
from typing import Any, Dict, Optional, Union, Iterator, Sequence
|
|
6
|
+
from typing import Any, Dict, Final, Optional, Union, Iterator, Sequence, TYPE_CHECKING
|
|
5
7
|
|
|
6
8
|
from opentrons_shared_data.errors import ErrorCodes
|
|
7
9
|
from opentrons_shared_data.errors.exceptions import EnumeratedError, PythonException
|
|
8
10
|
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from opentrons.protocol_engine.types import TipGeometry
|
|
13
|
+
|
|
14
|
+
|
|
9
15
|
log = getLogger(__name__)
|
|
10
16
|
|
|
11
17
|
|
|
@@ -132,6 +138,21 @@ class TipNotAttachedError(ProtocolEngineError):
|
|
|
132
138
|
super().__init__(ErrorCodes.UNEXPECTED_TIP_REMOVAL, message, details, wrapping)
|
|
133
139
|
|
|
134
140
|
|
|
141
|
+
class PickUpTipTipNotAttachedError(TipNotAttachedError):
|
|
142
|
+
"""Raised from TipHandler.pick_up_tip().
|
|
143
|
+
|
|
144
|
+
This is like TipNotAttachedError except that it carries some extra information
|
|
145
|
+
about the attempted operation.
|
|
146
|
+
"""
|
|
147
|
+
|
|
148
|
+
tip_geometry: Final[TipGeometry]
|
|
149
|
+
"""The tip geometry that would have been on the pipette, had the operation succeeded."""
|
|
150
|
+
|
|
151
|
+
def __init__(self, tip_geometry: TipGeometry) -> None:
|
|
152
|
+
super().__init__()
|
|
153
|
+
self.tip_geometry = tip_geometry
|
|
154
|
+
|
|
155
|
+
|
|
135
156
|
class TipAttachedError(ProtocolEngineError):
|
|
136
157
|
"""Raised when a tip shouldn't be attached, but is."""
|
|
137
158
|
|
|
@@ -752,6 +773,19 @@ class InvalidBlockVolumeError(ProtocolEngineError):
|
|
|
752
773
|
super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
|
|
753
774
|
|
|
754
775
|
|
|
776
|
+
class InvalidWavelengthError(ProtocolEngineError):
|
|
777
|
+
"""Raised when attempting to set an invalid absorbance wavelength."""
|
|
778
|
+
|
|
779
|
+
def __init__(
|
|
780
|
+
self,
|
|
781
|
+
message: Optional[str] = None,
|
|
782
|
+
details: Optional[Dict[str, Any]] = None,
|
|
783
|
+
wrapping: Optional[Sequence[EnumeratedError]] = None,
|
|
784
|
+
) -> None:
|
|
785
|
+
"""Build a InvalidWavelengthError."""
|
|
786
|
+
super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
|
|
787
|
+
|
|
788
|
+
|
|
755
789
|
class InvalidHoldTimeError(ProtocolEngineError):
|
|
756
790
|
"""An error raised when attempting to set an invalid temperature hold time."""
|
|
757
791
|
|
|
@@ -897,6 +931,19 @@ class LocationIsStagingSlotError(ProtocolEngineError):
|
|
|
897
931
|
super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
|
|
898
932
|
|
|
899
933
|
|
|
934
|
+
class LocationIsLidDockSlotError(ProtocolEngineError):
|
|
935
|
+
"""Raised when referencing a labware on a lid dock slot when trying to get standard deck slot."""
|
|
936
|
+
|
|
937
|
+
def __init__(
|
|
938
|
+
self,
|
|
939
|
+
message: Optional[str] = None,
|
|
940
|
+
details: Optional[Dict[str, Any]] = None,
|
|
941
|
+
wrapping: Optional[Sequence[EnumeratedError]] = None,
|
|
942
|
+
) -> None:
|
|
943
|
+
"""Build a LocationIsLidDockSlotError."""
|
|
944
|
+
super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
|
|
945
|
+
|
|
946
|
+
|
|
900
947
|
class FirmwareUpdateRequired(ProtocolEngineError):
|
|
901
948
|
"""Raised when the firmware needs to be updated."""
|
|
902
949
|
|
|
@@ -939,7 +986,7 @@ class InvalidAspirateVolumeError(ProtocolEngineError):
|
|
|
939
986
|
"""Build a InvalidPipettingVolumeError."""
|
|
940
987
|
message = (
|
|
941
988
|
f"Cannot aspirate {attempted_aspirate_volume} µL when only"
|
|
942
|
-
f" {available_volume} is available."
|
|
989
|
+
f" {available_volume} is available in the tip."
|
|
943
990
|
)
|
|
944
991
|
details = {
|
|
945
992
|
"attempted_aspirate_volume": attempted_aspirate_volume,
|
|
@@ -989,6 +1036,32 @@ class InvalidAxisForRobotType(ProtocolEngineError):
|
|
|
989
1036
|
super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
|
|
990
1037
|
|
|
991
1038
|
|
|
1039
|
+
class InvalidLiquidHeightFound(ProtocolEngineError):
|
|
1040
|
+
"""Raised when attempting to estimate liquid height based on volume fails."""
|
|
1041
|
+
|
|
1042
|
+
def __init__(
|
|
1043
|
+
self,
|
|
1044
|
+
message: Optional[str] = None,
|
|
1045
|
+
details: Optional[Dict[str, Any]] = None,
|
|
1046
|
+
wrapping: Optional[Sequence[EnumeratedError]] = None,
|
|
1047
|
+
) -> None:
|
|
1048
|
+
"""Build an InvalidLiquidHeightFound error."""
|
|
1049
|
+
super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
|
|
1050
|
+
|
|
1051
|
+
|
|
1052
|
+
class LiquidHeightUnknownError(ProtocolEngineError):
|
|
1053
|
+
"""Raised when attempting to specify WellOrigin.MENISCUS before liquid probing has been done."""
|
|
1054
|
+
|
|
1055
|
+
def __init__(
|
|
1056
|
+
self,
|
|
1057
|
+
message: Optional[str] = None,
|
|
1058
|
+
details: Optional[Dict[str, Any]] = None,
|
|
1059
|
+
wrapping: Optional[Sequence[EnumeratedError]] = None,
|
|
1060
|
+
) -> None:
|
|
1061
|
+
"""Build a LiquidHeightUnknownError."""
|
|
1062
|
+
super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
|
|
1063
|
+
|
|
1064
|
+
|
|
992
1065
|
class EStopActivatedError(ProtocolEngineError):
|
|
993
1066
|
"""Represents an E-stop event."""
|
|
994
1067
|
|
|
@@ -1030,3 +1103,55 @@ class TipNotEmptyError(ProtocolEngineError):
|
|
|
1030
1103
|
) -> None:
|
|
1031
1104
|
"""Build a TipNotEmptyError."""
|
|
1032
1105
|
super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
|
|
1106
|
+
|
|
1107
|
+
|
|
1108
|
+
class IncompleteLabwareDefinitionError(ProtocolEngineError):
|
|
1109
|
+
"""Raised when a labware definition lacks innerLabwareGeometry in general or for a specific well_id."""
|
|
1110
|
+
|
|
1111
|
+
def __init__(
|
|
1112
|
+
self,
|
|
1113
|
+
message: Optional[str] = None,
|
|
1114
|
+
details: Optional[Dict[str, Any]] = None,
|
|
1115
|
+
wrapping: Optional[Sequence[EnumeratedError]] = None,
|
|
1116
|
+
) -> None:
|
|
1117
|
+
"""Build an IncompleteLabwareDefinitionError."""
|
|
1118
|
+
super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
|
|
1119
|
+
|
|
1120
|
+
|
|
1121
|
+
class IncompleteWellDefinitionError(ProtocolEngineError):
|
|
1122
|
+
"""Raised when a well definition lacks a geometryDefinitionId."""
|
|
1123
|
+
|
|
1124
|
+
def __init__(
|
|
1125
|
+
self,
|
|
1126
|
+
message: Optional[str] = None,
|
|
1127
|
+
details: Optional[Dict[str, Any]] = None,
|
|
1128
|
+
wrapping: Optional[Sequence[EnumeratedError]] = None,
|
|
1129
|
+
) -> None:
|
|
1130
|
+
"""Build an IncompleteWellDefinitionError."""
|
|
1131
|
+
super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
|
|
1132
|
+
|
|
1133
|
+
|
|
1134
|
+
class OperationLocationNotInWellError(ProtocolEngineError):
|
|
1135
|
+
"""Raised when a calculated operation location is not within a well."""
|
|
1136
|
+
|
|
1137
|
+
def __init__(
|
|
1138
|
+
self,
|
|
1139
|
+
message: Optional[str] = None,
|
|
1140
|
+
details: Optional[Dict[str, Any]] = None,
|
|
1141
|
+
wrapping: Optional[Sequence[EnumeratedError]] = None,
|
|
1142
|
+
) -> None:
|
|
1143
|
+
"""Build an OperationLocationNotInWellError."""
|
|
1144
|
+
super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
|
|
1145
|
+
|
|
1146
|
+
|
|
1147
|
+
class StorageLimitReachedError(ProtocolEngineError):
|
|
1148
|
+
"""Raised to indicate that a file cannot be created due to storage limitations."""
|
|
1149
|
+
|
|
1150
|
+
def __init__(
|
|
1151
|
+
self,
|
|
1152
|
+
message: Optional[str] = None,
|
|
1153
|
+
detail: Optional[Dict[str, str]] = None,
|
|
1154
|
+
wrapping: Optional[Sequence[EnumeratedError]] = None,
|
|
1155
|
+
) -> None:
|
|
1156
|
+
"""Build an StorageLimitReached."""
|
|
1157
|
+
super().__init__(ErrorCodes.GENERAL_ERROR, message, detail, wrapping)
|
|
@@ -21,6 +21,7 @@ from .run_control import RunControlHandler
|
|
|
21
21
|
from .hardware_stopper import HardwareStopper
|
|
22
22
|
from .door_watcher import DoorWatcher
|
|
23
23
|
from .status_bar import StatusBarHandler
|
|
24
|
+
from ..resources.file_provider import FileProvider
|
|
24
25
|
|
|
25
26
|
# .thermocycler_movement_flagger omitted from package's public interface.
|
|
26
27
|
|
|
@@ -45,4 +46,5 @@ __all__ = [
|
|
|
45
46
|
"DoorWatcher",
|
|
46
47
|
"RailLightsHandler",
|
|
47
48
|
"StatusBarHandler",
|
|
49
|
+
"FileProvider",
|
|
48
50
|
]
|
|
@@ -12,9 +12,10 @@ from opentrons_shared_data.errors.exceptions import (
|
|
|
12
12
|
)
|
|
13
13
|
|
|
14
14
|
from opentrons.protocol_engine.commands.command import SuccessData
|
|
15
|
+
from opentrons.protocol_engine.notes import make_error_recovery_debug_note
|
|
15
16
|
|
|
16
|
-
from ..state import StateStore
|
|
17
|
-
from ..resources import ModelUtils
|
|
17
|
+
from ..state.state import StateStore
|
|
18
|
+
from ..resources import ModelUtils, FileProvider
|
|
18
19
|
from ..commands import CommandStatus
|
|
19
20
|
from ..actions import (
|
|
20
21
|
ActionDispatcher,
|
|
@@ -72,6 +73,7 @@ class CommandExecutor:
|
|
|
72
73
|
def __init__(
|
|
73
74
|
self,
|
|
74
75
|
hardware_api: HardwareControlAPI,
|
|
76
|
+
file_provider: FileProvider,
|
|
75
77
|
state_store: StateStore,
|
|
76
78
|
action_dispatcher: ActionDispatcher,
|
|
77
79
|
equipment: EquipmentHandler,
|
|
@@ -88,6 +90,7 @@ class CommandExecutor:
|
|
|
88
90
|
) -> None:
|
|
89
91
|
"""Initialize the CommandExecutor with access to its dependencies."""
|
|
90
92
|
self._hardware_api = hardware_api
|
|
93
|
+
self._file_provider = file_provider
|
|
91
94
|
self._state_store = state_store
|
|
92
95
|
self._action_dispatcher = action_dispatcher
|
|
93
96
|
self._equipment = equipment
|
|
@@ -116,6 +119,7 @@ class CommandExecutor:
|
|
|
116
119
|
command_impl = queued_command._ImplementationCls(
|
|
117
120
|
state_view=self._state_store,
|
|
118
121
|
hardware_api=self._hardware_api,
|
|
122
|
+
file_provider=self._file_provider,
|
|
119
123
|
equipment=self._equipment,
|
|
120
124
|
movement=self._movement,
|
|
121
125
|
gantry_mover=self._gantry_mover,
|
|
@@ -158,6 +162,12 @@ class CommandExecutor:
|
|
|
158
162
|
elif not isinstance(error, EnumeratedError):
|
|
159
163
|
error = PythonException(error)
|
|
160
164
|
|
|
165
|
+
error_recovery_type = error_recovery_policy(
|
|
166
|
+
self._state_store.config,
|
|
167
|
+
running_command,
|
|
168
|
+
None,
|
|
169
|
+
)
|
|
170
|
+
note_tracker(make_error_recovery_debug_note(error_recovery_type))
|
|
161
171
|
self._action_dispatcher.dispatch(
|
|
162
172
|
FailCommandAction(
|
|
163
173
|
error=error,
|
|
@@ -166,11 +176,7 @@ class CommandExecutor:
|
|
|
166
176
|
error_id=self._model_utils.generate_id(),
|
|
167
177
|
failed_at=self._model_utils.get_timestamp(),
|
|
168
178
|
notes=note_tracker.get_notes(),
|
|
169
|
-
type=
|
|
170
|
-
self._state_store.config,
|
|
171
|
-
running_command,
|
|
172
|
-
None,
|
|
173
|
-
),
|
|
179
|
+
type=error_recovery_type,
|
|
174
180
|
)
|
|
175
181
|
)
|
|
176
182
|
|
|
@@ -185,11 +191,18 @@ class CommandExecutor:
|
|
|
185
191
|
succeeded_command = running_command.copy(update=update)
|
|
186
192
|
self._action_dispatcher.dispatch(
|
|
187
193
|
SucceedCommandAction(
|
|
188
|
-
command=succeeded_command,
|
|
194
|
+
command=succeeded_command,
|
|
195
|
+
state_update=result.state_update,
|
|
189
196
|
),
|
|
190
197
|
)
|
|
191
198
|
else:
|
|
192
199
|
# The command encountered a defined error.
|
|
200
|
+
error_recovery_type = error_recovery_policy(
|
|
201
|
+
self._state_store.config,
|
|
202
|
+
running_command,
|
|
203
|
+
result,
|
|
204
|
+
)
|
|
205
|
+
note_tracker(make_error_recovery_debug_note(error_recovery_type))
|
|
193
206
|
self._action_dispatcher.dispatch(
|
|
194
207
|
FailCommandAction(
|
|
195
208
|
error=result,
|
|
@@ -198,10 +211,6 @@ class CommandExecutor:
|
|
|
198
211
|
error_id=result.public.id,
|
|
199
212
|
failed_at=result.public.createdAt,
|
|
200
213
|
notes=note_tracker.get_notes(),
|
|
201
|
-
type=
|
|
202
|
-
self._state_store.config,
|
|
203
|
-
running_command,
|
|
204
|
-
result,
|
|
205
|
-
),
|
|
214
|
+
type=error_recovery_type,
|
|
206
215
|
)
|
|
207
216
|
)
|
|
@@ -4,8 +4,9 @@ from typing import AsyncGenerator, Callable
|
|
|
4
4
|
from opentrons.hardware_control import HardwareControlAPI
|
|
5
5
|
from opentrons.protocol_engine.execution.rail_lights import RailLightsHandler
|
|
6
6
|
|
|
7
|
-
from ..state import StateStore
|
|
7
|
+
from ..state.state import StateStore
|
|
8
8
|
from ..actions import ActionDispatcher
|
|
9
|
+
from ..resources import FileProvider
|
|
9
10
|
from .equipment import EquipmentHandler
|
|
10
11
|
from .movement import MovementHandler
|
|
11
12
|
from .gantry_mover import create_gantry_mover
|
|
@@ -20,6 +21,7 @@ from .status_bar import StatusBarHandler
|
|
|
20
21
|
|
|
21
22
|
def create_queue_worker(
|
|
22
23
|
hardware_api: HardwareControlAPI,
|
|
24
|
+
file_provider: FileProvider,
|
|
23
25
|
state_store: StateStore,
|
|
24
26
|
action_dispatcher: ActionDispatcher,
|
|
25
27
|
command_generator: Callable[[], AsyncGenerator[str, None]],
|
|
@@ -28,6 +30,7 @@ def create_queue_worker(
|
|
|
28
30
|
|
|
29
31
|
Arguments:
|
|
30
32
|
hardware_api: Hardware control API to pass down to dependencies.
|
|
33
|
+
file_provider: Provides access to robot server file writing procedures for protocol output.
|
|
31
34
|
state_store: StateStore to pass down to dependencies.
|
|
32
35
|
action_dispatcher: ActionDispatcher to pass down to dependencies.
|
|
33
36
|
error_recovery_policy: ErrorRecoveryPolicy to pass down to dependencies.
|
|
@@ -78,6 +81,7 @@ def create_queue_worker(
|
|
|
78
81
|
|
|
79
82
|
command_executor = CommandExecutor(
|
|
80
83
|
hardware_api=hardware_api,
|
|
84
|
+
file_provider=file_provider,
|
|
81
85
|
state_store=state_store,
|
|
82
86
|
action_dispatcher=action_dispatcher,
|
|
83
87
|
equipment=equipment_handler,
|
|
@@ -15,7 +15,7 @@ from opentrons.hardware_control.types import (
|
|
|
15
15
|
|
|
16
16
|
from opentrons.protocol_engine.actions import ActionDispatcher, DoorChangeAction
|
|
17
17
|
|
|
18
|
-
from ..state import StateStore
|
|
18
|
+
from ..state.state import StateStore
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
_UnsubscribeCallback = Callable[[], None]
|
|
@@ -35,7 +35,8 @@ from ..resources import (
|
|
|
35
35
|
ModelUtils,
|
|
36
36
|
pipette_data_provider,
|
|
37
37
|
)
|
|
38
|
-
from ..state import StateStore
|
|
38
|
+
from ..state.state import StateStore
|
|
39
|
+
from ..state.modules import HardwareModule
|
|
39
40
|
from ..types import (
|
|
40
41
|
LabwareLocation,
|
|
41
42
|
DeckSlotLocation,
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# noqa: D100
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
from opentrons.hardware_control import HardwareControlAPI
|
|
5
|
+
from opentrons.protocol_engine.actions.action_handler import ActionHandler
|
|
6
|
+
from opentrons.protocol_engine.actions.actions import (
|
|
7
|
+
Action,
|
|
8
|
+
FailCommandAction,
|
|
9
|
+
ResumeFromRecoveryAction,
|
|
10
|
+
)
|
|
11
|
+
from opentrons.protocol_engine.commands.command import DefinedErrorData
|
|
12
|
+
from opentrons.protocol_engine.error_recovery_policy import ErrorRecoveryType
|
|
13
|
+
from opentrons.protocol_engine.execution.tip_handler import HardwareTipHandler
|
|
14
|
+
from opentrons.protocol_engine.state import update_types
|
|
15
|
+
from opentrons.protocol_engine.state.state import StateView
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ErrorRecoveryHardwareStateSynchronizer(ActionHandler):
|
|
19
|
+
"""A hack to keep the hardware API's state correct through certain error recovery flows.
|
|
20
|
+
|
|
21
|
+
BACKGROUND:
|
|
22
|
+
|
|
23
|
+
Certain parts of robot state are duplicated between `opentrons.protocol_engine` and
|
|
24
|
+
`opentrons.hardware_control`. Stuff like "is there a tip attached."
|
|
25
|
+
|
|
26
|
+
Normally, Protocol Engine command implementations (`opentrons.protocol_engine.commands`)
|
|
27
|
+
mutate hardware API state when they execute; and then when they finish executing,
|
|
28
|
+
the Protocol Engine state stores (`opentrons.protocol_engine.state`) update Protocol
|
|
29
|
+
Engine state accordingly. So both halves are accounted for. This generally works fine.
|
|
30
|
+
|
|
31
|
+
However, we need to go out of our way to support
|
|
32
|
+
`ProtocolEngine.resume_from_recovery(reconcile_false_positive=True)`.
|
|
33
|
+
It wants to apply a second set of state updates to "fix things up" with the
|
|
34
|
+
new knowledge that some error was a false positive. The Protocol Engine half of that
|
|
35
|
+
is easy for us to apply the normal way, through the state stores; but the
|
|
36
|
+
hardware API half of that cannot be applied the normal way, from the command
|
|
37
|
+
implementation, because the command in question is no longer running.
|
|
38
|
+
|
|
39
|
+
THE HACK:
|
|
40
|
+
|
|
41
|
+
This listens for the same error recovery state updates that the state stores do,
|
|
42
|
+
figures out what hardware API state mutations ought to go along with them,
|
|
43
|
+
and then does those mutations.
|
|
44
|
+
|
|
45
|
+
The problem is that hardware API state is now mutated from two different places
|
|
46
|
+
(sometimes the command implementations, and sometimes here), which are bound
|
|
47
|
+
to grow accidental differences.
|
|
48
|
+
|
|
49
|
+
TO FIX:
|
|
50
|
+
|
|
51
|
+
Make Protocol Engine's use of the hardware API less stateful. e.g. supply
|
|
52
|
+
tip geometry every time we call a hardware API movement method, instead of
|
|
53
|
+
just once when we pick up a tip. Use Protocol Engine state as the single source
|
|
54
|
+
of truth.
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
def __init__(self, hardware_api: HardwareControlAPI, state_view: StateView) -> None:
|
|
58
|
+
self._hardware_api = hardware_api
|
|
59
|
+
self._state_view = state_view
|
|
60
|
+
|
|
61
|
+
def handle_action(self, action: Action) -> None:
|
|
62
|
+
"""Modify hardware API state in reaction to a Protocol Engine action."""
|
|
63
|
+
state_update = _get_state_update(action)
|
|
64
|
+
if state_update:
|
|
65
|
+
self._synchronize(state_update)
|
|
66
|
+
|
|
67
|
+
def _synchronize(self, state_update: update_types.StateUpdate) -> None:
|
|
68
|
+
tip_handler = HardwareTipHandler(self._state_view, self._hardware_api)
|
|
69
|
+
|
|
70
|
+
if state_update.pipette_tip_state != update_types.NO_CHANGE:
|
|
71
|
+
pipette_id = state_update.pipette_tip_state.pipette_id
|
|
72
|
+
tip_geometry = state_update.pipette_tip_state.tip_geometry
|
|
73
|
+
if tip_geometry is None:
|
|
74
|
+
tip_handler.remove_tip(pipette_id)
|
|
75
|
+
else:
|
|
76
|
+
tip_handler.cache_tip(pipette_id=pipette_id, tip=tip_geometry)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _get_state_update(action: Action) -> update_types.StateUpdate | None:
|
|
80
|
+
"""Get the mutations that we need to do on the hardware API to stay in sync with an engine action.
|
|
81
|
+
|
|
82
|
+
The mutations are returned in Protocol Engine terms, as a StateUpdate.
|
|
83
|
+
They then need to be converted to hardware API terms.
|
|
84
|
+
"""
|
|
85
|
+
match action:
|
|
86
|
+
case ResumeFromRecoveryAction(state_update=state_update):
|
|
87
|
+
return state_update
|
|
88
|
+
|
|
89
|
+
case FailCommandAction(
|
|
90
|
+
error=DefinedErrorData(
|
|
91
|
+
state_update_if_false_positive=state_update_if_false_positive
|
|
92
|
+
)
|
|
93
|
+
):
|
|
94
|
+
return (
|
|
95
|
+
state_update_if_false_positive
|
|
96
|
+
if action.type == ErrorRecoveryType.ASSUME_FALSE_POSITIVE_AND_CONTINUE
|
|
97
|
+
else None
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
case _:
|
|
101
|
+
return None
|
|
@@ -10,7 +10,7 @@ from opentrons_shared_data.errors.exceptions import PositionUnknownError
|
|
|
10
10
|
|
|
11
11
|
from opentrons.motion_planning import Waypoint
|
|
12
12
|
|
|
13
|
-
from ..state import StateView
|
|
13
|
+
from ..state.state import StateView
|
|
14
14
|
from ..types import MotorAxis, CurrentWell
|
|
15
15
|
from ..errors import MustHomeError, InvalidAxisForRobotType
|
|
16
16
|
|
|
@@ -273,7 +273,9 @@ class VirtualGantryMover(GantryMover):
|
|
|
273
273
|
)
|
|
274
274
|
else:
|
|
275
275
|
instrument_height = VIRTUAL_MAX_OT3_HEIGHT
|
|
276
|
-
|
|
276
|
+
|
|
277
|
+
tip = self._state_view.pipettes.get_attached_tip(pipette_id=pipette_id)
|
|
278
|
+
tip_length = tip.length if tip is not None else 0
|
|
277
279
|
return instrument_height - tip_length
|
|
278
280
|
|
|
279
281
|
async def move_to(
|
|
@@ -6,7 +6,7 @@ from opentrons.hardware_control import HardwareControlAPI
|
|
|
6
6
|
from opentrons.types import PipetteNotAttachedError as HwPipetteNotAttachedError
|
|
7
7
|
|
|
8
8
|
from ..resources.ot3_validation import ensure_ot3_hardware
|
|
9
|
-
from ..state import StateStore
|
|
9
|
+
from ..state.state import StateStore
|
|
10
10
|
from ..types import MotorAxis, PostRunHardwareState
|
|
11
11
|
from ..errors import HardwareNotSupportedError
|
|
12
12
|
|
|
@@ -78,7 +78,7 @@ class HardwareStopper:
|
|
|
78
78
|
try:
|
|
79
79
|
if self._state_store.labware.get_fixed_trash_id() == FIXED_TRASH_ID:
|
|
80
80
|
# OT-2 and Flex 2.15 protocols will default to the Fixed Trash Labware
|
|
81
|
-
|
|
81
|
+
self._tip_handler.cache_tip(pipette_id=pipette_id, tip=tip)
|
|
82
82
|
await self._movement_handler.move_to_well(
|
|
83
83
|
pipette_id=pipette_id,
|
|
84
84
|
labware_id=FIXED_TRASH_ID,
|
|
@@ -90,7 +90,7 @@ class HardwareStopper:
|
|
|
90
90
|
)
|
|
91
91
|
elif self._state_store.config.robot_type == "OT-2 Standard":
|
|
92
92
|
# API 2.16 and above OT2 protocols use addressable areas
|
|
93
|
-
|
|
93
|
+
self._tip_handler.cache_tip(pipette_id=pipette_id, tip=tip)
|
|
94
94
|
await self._movement_handler.move_to_addressable_area(
|
|
95
95
|
pipette_id=pipette_id,
|
|
96
96
|
addressable_area_name="fixedTrash",
|
|
@@ -13,7 +13,7 @@ from ..errors import (
|
|
|
13
13
|
HeaterShakerLabwareLatchStatusUnknown,
|
|
14
14
|
WrongModuleTypeError,
|
|
15
15
|
)
|
|
16
|
-
from ..state import StateStore
|
|
16
|
+
from ..state.state import StateStore
|
|
17
17
|
from ..state.module_substates import HeaterShakerModuleSubState
|
|
18
18
|
from ..types import (
|
|
19
19
|
HeaterShakerMovementRestrictors,
|
|
@@ -61,9 +61,6 @@ class HeaterShakerMovementFlagger:
|
|
|
61
61
|
return # Labware on a module, but not a Heater-Shaker.
|
|
62
62
|
|
|
63
63
|
if hs_substate.labware_latch_status == HeaterShakerLatchStatus.CLOSED:
|
|
64
|
-
# TODO (spp, 2022-10-27): This only raises if latch status is 'idle_closed'.
|
|
65
|
-
# We need to update the flagger to raise if latch status is anything other
|
|
66
|
-
# than 'idle_open'
|
|
67
64
|
raise HeaterShakerLabwareLatchNotOpenError(
|
|
68
65
|
"Heater-Shaker labware latch must be open when moving labware to/from it."
|
|
69
66
|
)
|