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,8 +1,15 @@
|
|
|
1
1
|
"""Basic pipette data state and store."""
|
|
2
2
|
from __future__ import annotations
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
from
|
|
3
|
+
|
|
4
|
+
import dataclasses
|
|
5
|
+
from typing import (
|
|
6
|
+
Dict,
|
|
7
|
+
List,
|
|
8
|
+
Mapping,
|
|
9
|
+
Optional,
|
|
10
|
+
Tuple,
|
|
11
|
+
Union,
|
|
12
|
+
)
|
|
6
13
|
|
|
7
14
|
from opentrons_shared_data.pipette import pipette_definition
|
|
8
15
|
from opentrons.config.defaults_ot2 import Z_RETRACT_DISTANCE
|
|
@@ -12,15 +19,9 @@ from opentrons.hardware_control.nozzle_manager import (
|
|
|
12
19
|
NozzleConfigurationType,
|
|
13
20
|
NozzleMap,
|
|
14
21
|
)
|
|
15
|
-
from opentrons.protocol_engine.actions.actions import FailCommandAction
|
|
16
|
-
from opentrons.protocol_engine.commands.command import DefinedErrorData
|
|
17
|
-
from opentrons.protocol_engine.commands.pipetting_common import (
|
|
18
|
-
LiquidNotFoundError,
|
|
19
|
-
OverpressureError,
|
|
20
|
-
OverpressureErrorInternalData,
|
|
21
|
-
)
|
|
22
22
|
from opentrons.types import MountType, Mount as HwMount, Point
|
|
23
23
|
|
|
24
|
+
from . import update_types
|
|
24
25
|
from .. import commands
|
|
25
26
|
from .. import errors
|
|
26
27
|
from ..types import (
|
|
@@ -33,19 +34,17 @@ from ..types import (
|
|
|
33
34
|
CurrentPipetteLocation,
|
|
34
35
|
TipGeometry,
|
|
35
36
|
)
|
|
36
|
-
from ..commands.configuring_common import (
|
|
37
|
-
PipetteConfigUpdateResultMixin,
|
|
38
|
-
PipetteNozzleLayoutResultMixin,
|
|
39
|
-
)
|
|
40
37
|
from ..actions import (
|
|
41
38
|
Action,
|
|
39
|
+
FailCommandAction,
|
|
42
40
|
SetPipetteMovementSpeedAction,
|
|
43
41
|
SucceedCommandAction,
|
|
42
|
+
get_state_updates,
|
|
44
43
|
)
|
|
45
|
-
from .
|
|
44
|
+
from ._abstract_store import HasState, HandlesActions
|
|
46
45
|
|
|
47
46
|
|
|
48
|
-
@dataclass(frozen=True)
|
|
47
|
+
@dataclasses.dataclass(frozen=True)
|
|
49
48
|
class HardwarePipette:
|
|
50
49
|
"""Hardware pipette data."""
|
|
51
50
|
|
|
@@ -53,7 +52,7 @@ class HardwarePipette:
|
|
|
53
52
|
config: PipetteDict
|
|
54
53
|
|
|
55
54
|
|
|
56
|
-
@dataclass(frozen=True)
|
|
55
|
+
@dataclasses.dataclass(frozen=True)
|
|
57
56
|
class CurrentDeckPoint:
|
|
58
57
|
"""The latest deck point and mount the robot has accessed."""
|
|
59
58
|
|
|
@@ -61,7 +60,7 @@ class CurrentDeckPoint:
|
|
|
61
60
|
deck_point: Optional[DeckPoint]
|
|
62
61
|
|
|
63
62
|
|
|
64
|
-
@dataclass(frozen=True)
|
|
63
|
+
@dataclasses.dataclass(frozen=True)
|
|
65
64
|
class BoundingNozzlesOffsets:
|
|
66
65
|
"""Offsets of the bounding nozzles of the pipette."""
|
|
67
66
|
|
|
@@ -69,7 +68,7 @@ class BoundingNozzlesOffsets:
|
|
|
69
68
|
front_right_offset: Point
|
|
70
69
|
|
|
71
70
|
|
|
72
|
-
@dataclass(frozen=True)
|
|
71
|
+
@dataclasses.dataclass(frozen=True)
|
|
73
72
|
class PipetteBoundingBoxOffsets:
|
|
74
73
|
"""Offsets of the corners of the pipette's bounding box."""
|
|
75
74
|
|
|
@@ -79,7 +78,7 @@ class PipetteBoundingBoxOffsets:
|
|
|
79
78
|
front_left_corner: Point
|
|
80
79
|
|
|
81
80
|
|
|
82
|
-
@dataclass(frozen=True)
|
|
81
|
+
@dataclasses.dataclass(frozen=True)
|
|
83
82
|
class StaticPipetteConfig:
|
|
84
83
|
"""Static config for a pipette."""
|
|
85
84
|
|
|
@@ -97,14 +96,17 @@ class StaticPipetteConfig:
|
|
|
97
96
|
nozzle_offset_z: float
|
|
98
97
|
pipette_bounding_box_offsets: PipetteBoundingBoxOffsets
|
|
99
98
|
bounding_nozzle_offsets: BoundingNozzlesOffsets
|
|
100
|
-
default_nozzle_map: NozzleMap
|
|
99
|
+
default_nozzle_map: NozzleMap # todo(mm, 2024-10-14): unused, remove?
|
|
101
100
|
lld_settings: Optional[Dict[str, Dict[str, float]]]
|
|
102
101
|
|
|
103
102
|
|
|
104
|
-
@dataclass
|
|
103
|
+
@dataclasses.dataclass
|
|
105
104
|
class PipetteState:
|
|
106
105
|
"""Basic pipette data state and getter methods."""
|
|
107
106
|
|
|
107
|
+
# todo(mm, 2024-10-14): It's getting difficult to ensure that all of these
|
|
108
|
+
# attributes are populated at the appropriate times. Refactor to a
|
|
109
|
+
# single dict-of-many-things instead of many dicts-of-single-things.
|
|
108
110
|
pipettes_by_id: Dict[str, LoadedPipette]
|
|
109
111
|
aspirated_volume_by_id: Dict[str, Optional[float]]
|
|
110
112
|
current_location: Optional[CurrentPipetteLocation]
|
|
@@ -113,7 +115,7 @@ class PipetteState:
|
|
|
113
115
|
movement_speed_by_id: Dict[str, Optional[float]]
|
|
114
116
|
static_config_by_id: Dict[str, StaticPipetteConfig]
|
|
115
117
|
flow_rates_by_id: Dict[str, FlowRates]
|
|
116
|
-
nozzle_configuration_by_id: Dict[str,
|
|
118
|
+
nozzle_configuration_by_id: Dict[str, NozzleMap]
|
|
117
119
|
liquid_presence_detection_by_id: Dict[str, bool]
|
|
118
120
|
|
|
119
121
|
|
|
@@ -139,29 +141,128 @@ class PipetteStore(HasState[PipetteState], HandlesActions):
|
|
|
139
141
|
|
|
140
142
|
def handle_action(self, action: Action) -> None:
|
|
141
143
|
"""Modify state in reaction to an action."""
|
|
144
|
+
for state_update in get_state_updates(action):
|
|
145
|
+
self._set_load_pipette(state_update)
|
|
146
|
+
self._update_current_location(state_update)
|
|
147
|
+
self._update_pipette_config(state_update)
|
|
148
|
+
self._update_pipette_nozzle_map(state_update)
|
|
149
|
+
self._update_tip_state(state_update)
|
|
150
|
+
|
|
142
151
|
if isinstance(action, (SucceedCommandAction, FailCommandAction)):
|
|
143
|
-
self.
|
|
152
|
+
self._update_volumes(action)
|
|
153
|
+
|
|
144
154
|
elif isinstance(action, SetPipetteMovementSpeedAction):
|
|
145
155
|
self._state.movement_speed_by_id[action.pipette_id] = action.speed
|
|
146
156
|
|
|
147
|
-
def
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
self._update_current_location(action)
|
|
151
|
-
self._update_deck_point(action)
|
|
152
|
-
self._update_volumes(action)
|
|
157
|
+
def _set_load_pipette(self, state_update: update_types.StateUpdate) -> None:
|
|
158
|
+
if state_update.loaded_pipette != update_types.NO_CHANGE:
|
|
159
|
+
pipette_id = state_update.loaded_pipette.pipette_id
|
|
153
160
|
|
|
154
|
-
|
|
155
|
-
|
|
161
|
+
self._state.pipettes_by_id[pipette_id] = LoadedPipette(
|
|
162
|
+
id=pipette_id,
|
|
163
|
+
pipetteName=state_update.loaded_pipette.pipette_name,
|
|
164
|
+
mount=state_update.loaded_pipette.mount,
|
|
165
|
+
)
|
|
166
|
+
self._state.liquid_presence_detection_by_id[pipette_id] = (
|
|
167
|
+
state_update.loaded_pipette.liquid_presence_detection or False
|
|
168
|
+
)
|
|
169
|
+
self._state.aspirated_volume_by_id[pipette_id] = None
|
|
170
|
+
self._state.movement_speed_by_id[pipette_id] = None
|
|
171
|
+
self._state.attached_tip_by_id[pipette_id] = None
|
|
156
172
|
|
|
157
|
-
|
|
173
|
+
def _update_tip_state(self, state_update: update_types.StateUpdate) -> None:
|
|
174
|
+
if state_update.pipette_tip_state != update_types.NO_CHANGE:
|
|
175
|
+
pipette_id = state_update.pipette_tip_state.pipette_id
|
|
176
|
+
if state_update.pipette_tip_state.tip_geometry:
|
|
177
|
+
attached_tip = state_update.pipette_tip_state.tip_geometry
|
|
178
|
+
|
|
179
|
+
self._state.attached_tip_by_id[pipette_id] = attached_tip
|
|
180
|
+
self._state.aspirated_volume_by_id[pipette_id] = 0
|
|
181
|
+
|
|
182
|
+
static_config = self._state.static_config_by_id.get(pipette_id)
|
|
183
|
+
if static_config:
|
|
184
|
+
try:
|
|
185
|
+
tip_configuration = (
|
|
186
|
+
static_config.tip_configuration_lookup_table[
|
|
187
|
+
attached_tip.volume
|
|
188
|
+
]
|
|
189
|
+
)
|
|
190
|
+
except KeyError:
|
|
191
|
+
# TODO(seth,9/11/2023): this is a bad way of doing defaults but better than max volume.
|
|
192
|
+
# we used to look up a default tip config via the pipette max volume, but if that isn't
|
|
193
|
+
# tip volume (as it isn't when we're in low-volume mode) then that lookup fails. Using
|
|
194
|
+
# the first entry in the table is ok I guess but we really need to generally rethink how
|
|
195
|
+
# we identify tip classes - looking things up by volume is not enough.
|
|
196
|
+
tip_configuration = list(
|
|
197
|
+
static_config.tip_configuration_lookup_table.values()
|
|
198
|
+
)[0]
|
|
199
|
+
self._state.flow_rates_by_id[pipette_id] = FlowRates(
|
|
200
|
+
default_blow_out=tip_configuration.default_blowout_flowrate.values_by_api_level,
|
|
201
|
+
default_aspirate=tip_configuration.default_aspirate_flowrate.values_by_api_level,
|
|
202
|
+
default_dispense=tip_configuration.default_dispense_flowrate.values_by_api_level,
|
|
203
|
+
)
|
|
158
204
|
|
|
159
|
-
|
|
160
|
-
|
|
205
|
+
else:
|
|
206
|
+
pipette_id = state_update.pipette_tip_state.pipette_id
|
|
207
|
+
self._state.aspirated_volume_by_id[pipette_id] = None
|
|
208
|
+
self._state.attached_tip_by_id[pipette_id] = None
|
|
209
|
+
|
|
210
|
+
static_config = self._state.static_config_by_id.get(pipette_id)
|
|
211
|
+
if static_config:
|
|
212
|
+
# TODO(seth,9/11/2023): bad way to do defaulting, see above.
|
|
213
|
+
tip_configuration = list(
|
|
214
|
+
static_config.tip_configuration_lookup_table.values()
|
|
215
|
+
)[0]
|
|
216
|
+
self._state.flow_rates_by_id[pipette_id] = FlowRates(
|
|
217
|
+
default_blow_out=tip_configuration.default_blowout_flowrate.values_by_api_level,
|
|
218
|
+
default_aspirate=tip_configuration.default_aspirate_flowrate.values_by_api_level,
|
|
219
|
+
default_dispense=tip_configuration.default_dispense_flowrate.values_by_api_level,
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
def _update_current_location(self, state_update: update_types.StateUpdate) -> None:
|
|
223
|
+
location_update = state_update.pipette_location
|
|
224
|
+
|
|
225
|
+
if location_update is update_types.NO_CHANGE:
|
|
226
|
+
pass
|
|
227
|
+
elif location_update is update_types.CLEAR:
|
|
228
|
+
self._state.current_location = None
|
|
229
|
+
self._state.current_deck_point = CurrentDeckPoint(
|
|
230
|
+
mount=None, deck_point=None
|
|
231
|
+
)
|
|
232
|
+
else:
|
|
233
|
+
new_logical_location = location_update.new_location
|
|
234
|
+
new_deck_point = location_update.new_deck_point
|
|
235
|
+
match new_logical_location:
|
|
236
|
+
case update_types.Well(labware_id=labware_id, well_name=well_name):
|
|
237
|
+
self._state.current_location = CurrentWell(
|
|
238
|
+
pipette_id=location_update.pipette_id,
|
|
239
|
+
labware_id=labware_id,
|
|
240
|
+
well_name=well_name,
|
|
241
|
+
)
|
|
242
|
+
case update_types.AddressableArea(
|
|
243
|
+
addressable_area_name=addressable_area_name
|
|
244
|
+
):
|
|
245
|
+
self._state.current_location = CurrentAddressableArea(
|
|
246
|
+
pipette_id=location_update.pipette_id,
|
|
247
|
+
addressable_area_name=addressable_area_name,
|
|
248
|
+
)
|
|
249
|
+
case None:
|
|
250
|
+
self._state.current_location = None
|
|
251
|
+
case update_types.NO_CHANGE:
|
|
252
|
+
pass
|
|
253
|
+
if new_deck_point is not update_types.NO_CHANGE:
|
|
254
|
+
loaded_pipette = self._state.pipettes_by_id[location_update.pipette_id]
|
|
255
|
+
self._state.current_deck_point = CurrentDeckPoint(
|
|
256
|
+
mount=loaded_pipette.mount, deck_point=new_deck_point
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
def _update_pipette_config(self, state_update: update_types.StateUpdate) -> None:
|
|
260
|
+
if state_update.pipette_config != update_types.NO_CHANGE:
|
|
261
|
+
config = state_update.pipette_config.config
|
|
161
262
|
self._state.static_config_by_id[
|
|
162
|
-
|
|
263
|
+
state_update.pipette_config.pipette_id
|
|
163
264
|
] = StaticPipetteConfig(
|
|
164
|
-
serial_number=
|
|
265
|
+
serial_number=state_update.pipette_config.serial_number,
|
|
165
266
|
model=config.model,
|
|
166
267
|
display_name=config.display_name,
|
|
167
268
|
min_volume=config.min_volume,
|
|
@@ -192,276 +293,27 @@ class PipetteStore(HasState[PipetteState], HandlesActions):
|
|
|
192
293
|
default_nozzle_map=config.nozzle_map,
|
|
193
294
|
lld_settings=config.pipette_lld_settings,
|
|
194
295
|
)
|
|
195
|
-
self._state.flow_rates_by_id[
|
|
296
|
+
self._state.flow_rates_by_id[
|
|
297
|
+
state_update.pipette_config.pipette_id
|
|
298
|
+
] = config.flow_rates
|
|
196
299
|
self._state.nozzle_configuration_by_id[
|
|
197
|
-
|
|
300
|
+
state_update.pipette_config.pipette_id
|
|
198
301
|
] = config.nozzle_map
|
|
199
|
-
elif isinstance(private_result, PipetteNozzleLayoutResultMixin):
|
|
200
|
-
self._state.nozzle_configuration_by_id[
|
|
201
|
-
private_result.pipette_id
|
|
202
|
-
] = private_result.nozzle_map
|
|
203
302
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
self._state.pipettes_by_id[pipette_id] = LoadedPipette(
|
|
208
|
-
id=pipette_id,
|
|
209
|
-
pipetteName=command.params.pipetteName,
|
|
210
|
-
mount=command.params.mount,
|
|
211
|
-
)
|
|
212
|
-
self._state.liquid_presence_detection_by_id[pipette_id] = (
|
|
213
|
-
command.params.liquidPresenceDetection or False
|
|
214
|
-
)
|
|
215
|
-
self._state.aspirated_volume_by_id[pipette_id] = None
|
|
216
|
-
self._state.movement_speed_by_id[pipette_id] = None
|
|
217
|
-
self._state.attached_tip_by_id[pipette_id] = None
|
|
218
|
-
static_config = self._state.static_config_by_id.get(pipette_id)
|
|
219
|
-
if static_config:
|
|
220
|
-
self._state.nozzle_configuration_by_id[
|
|
221
|
-
pipette_id
|
|
222
|
-
] = static_config.default_nozzle_map
|
|
223
|
-
|
|
224
|
-
elif isinstance(command.result, commands.PickUpTipResult):
|
|
225
|
-
pipette_id = command.params.pipetteId
|
|
226
|
-
attached_tip = TipGeometry(
|
|
227
|
-
length=command.result.tipLength,
|
|
228
|
-
volume=command.result.tipVolume,
|
|
229
|
-
diameter=command.result.tipDiameter,
|
|
230
|
-
)
|
|
231
|
-
|
|
232
|
-
self._state.attached_tip_by_id[pipette_id] = attached_tip
|
|
233
|
-
self._state.aspirated_volume_by_id[pipette_id] = 0
|
|
234
|
-
|
|
235
|
-
static_config = self._state.static_config_by_id.get(pipette_id)
|
|
236
|
-
if static_config:
|
|
237
|
-
try:
|
|
238
|
-
tip_configuration = static_config.tip_configuration_lookup_table[
|
|
239
|
-
attached_tip.volume
|
|
240
|
-
]
|
|
241
|
-
except KeyError:
|
|
242
|
-
# TODO(seth,9/11/2023): this is a bad way of doing defaults but better than max volume.
|
|
243
|
-
# we used to look up a default tip config via the pipette max volume, but if that isn't
|
|
244
|
-
# tip volume (as it isn't when we're in low-volume mode) then that lookup fails. Using
|
|
245
|
-
# the first entry in the table is ok I guess but we really need to generally rethink how
|
|
246
|
-
# we identify tip classes - looking things up by volume is not enough.
|
|
247
|
-
tip_configuration = list(
|
|
248
|
-
static_config.tip_configuration_lookup_table.values()
|
|
249
|
-
)[0]
|
|
250
|
-
self._state.flow_rates_by_id[pipette_id] = FlowRates(
|
|
251
|
-
default_blow_out=tip_configuration.default_blowout_flowrate.values_by_api_level,
|
|
252
|
-
default_aspirate=tip_configuration.default_aspirate_flowrate.values_by_api_level,
|
|
253
|
-
default_dispense=tip_configuration.default_dispense_flowrate.values_by_api_level,
|
|
254
|
-
)
|
|
255
|
-
|
|
256
|
-
elif isinstance(
|
|
257
|
-
command.result,
|
|
258
|
-
(
|
|
259
|
-
commands.DropTipResult,
|
|
260
|
-
commands.DropTipInPlaceResult,
|
|
261
|
-
commands.unsafe.UnsafeDropTipInPlaceResult,
|
|
262
|
-
),
|
|
263
|
-
):
|
|
264
|
-
pipette_id = command.params.pipetteId
|
|
265
|
-
self._state.aspirated_volume_by_id[pipette_id] = None
|
|
266
|
-
self._state.attached_tip_by_id[pipette_id] = None
|
|
267
|
-
|
|
268
|
-
static_config = self._state.static_config_by_id.get(pipette_id)
|
|
269
|
-
if static_config:
|
|
270
|
-
# TODO(seth,9/11/2023): bad way to do defaulting, see above.
|
|
271
|
-
tip_configuration = list(
|
|
272
|
-
static_config.tip_configuration_lookup_table.values()
|
|
273
|
-
)[0]
|
|
274
|
-
self._state.flow_rates_by_id[pipette_id] = FlowRates(
|
|
275
|
-
default_blow_out=tip_configuration.default_blowout_flowrate.values_by_api_level,
|
|
276
|
-
default_aspirate=tip_configuration.default_aspirate_flowrate.values_by_api_level,
|
|
277
|
-
default_dispense=tip_configuration.default_dispense_flowrate.values_by_api_level,
|
|
278
|
-
)
|
|
279
|
-
|
|
280
|
-
def _update_current_location( # noqa: C901
|
|
281
|
-
self, action: Union[SucceedCommandAction, FailCommandAction]
|
|
303
|
+
def _update_pipette_nozzle_map(
|
|
304
|
+
self, state_update: update_types.StateUpdate
|
|
282
305
|
) -> None:
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
(
|
|
288
|
-
commands.MoveToWellResult,
|
|
289
|
-
commands.PickUpTipResult,
|
|
290
|
-
commands.DropTipResult,
|
|
291
|
-
commands.AspirateResult,
|
|
292
|
-
commands.DispenseResult,
|
|
293
|
-
commands.BlowOutResult,
|
|
294
|
-
commands.TouchTipResult,
|
|
295
|
-
commands.LiquidProbeResult,
|
|
296
|
-
commands.TryLiquidProbeResult,
|
|
297
|
-
),
|
|
298
|
-
):
|
|
299
|
-
self._state.current_location = CurrentWell(
|
|
300
|
-
pipette_id=action.command.params.pipetteId,
|
|
301
|
-
labware_id=action.command.params.labwareId,
|
|
302
|
-
well_name=action.command.params.wellName,
|
|
303
|
-
)
|
|
304
|
-
elif isinstance(action, FailCommandAction) and (
|
|
305
|
-
isinstance(action.error, DefinedErrorData)
|
|
306
|
-
and (
|
|
307
|
-
(
|
|
308
|
-
isinstance(
|
|
309
|
-
action.running_command, (commands.Aspirate, commands.Dispense)
|
|
310
|
-
)
|
|
311
|
-
and isinstance(action.error.public, OverpressureError)
|
|
312
|
-
)
|
|
313
|
-
or (
|
|
314
|
-
isinstance(action.running_command, commands.LiquidProbe)
|
|
315
|
-
and isinstance(action.error.public, LiquidNotFoundError)
|
|
316
|
-
)
|
|
317
|
-
)
|
|
318
|
-
):
|
|
319
|
-
self._state.current_location = CurrentWell(
|
|
320
|
-
pipette_id=action.running_command.params.pipetteId,
|
|
321
|
-
labware_id=action.running_command.params.labwareId,
|
|
322
|
-
well_name=action.running_command.params.wellName,
|
|
323
|
-
)
|
|
324
|
-
elif isinstance(action, SucceedCommandAction) and isinstance(
|
|
325
|
-
action.command.result,
|
|
326
|
-
(
|
|
327
|
-
commands.MoveToAddressableAreaResult,
|
|
328
|
-
commands.MoveToAddressableAreaForDropTipResult,
|
|
329
|
-
),
|
|
330
|
-
):
|
|
331
|
-
self._state.current_location = CurrentAddressableArea(
|
|
332
|
-
pipette_id=action.command.params.pipetteId,
|
|
333
|
-
addressable_area_name=action.command.params.addressableAreaName,
|
|
334
|
-
)
|
|
335
|
-
|
|
336
|
-
# These commands leave the pipette in a place that we can't logically associate
|
|
337
|
-
# with a well. Clear current_location to reflect the fact that it's now unknown.
|
|
338
|
-
#
|
|
339
|
-
# TODO(mc, 2021-11-12): Wipe out current_location on movement failures, too.
|
|
340
|
-
# TODO(jbl 2023-02-14): Need to investigate whether move relative should clear current location
|
|
341
|
-
elif isinstance(action, SucceedCommandAction) and isinstance(
|
|
342
|
-
action.command.result,
|
|
343
|
-
(
|
|
344
|
-
commands.HomeResult,
|
|
345
|
-
commands.RetractAxisResult,
|
|
346
|
-
commands.MoveToCoordinatesResult,
|
|
347
|
-
commands.thermocycler.OpenLidResult,
|
|
348
|
-
commands.thermocycler.CloseLidResult,
|
|
349
|
-
),
|
|
350
|
-
):
|
|
351
|
-
self._state.current_location = None
|
|
352
|
-
|
|
353
|
-
# Heater-Shaker commands may have left the pipette in a place that we can't
|
|
354
|
-
# associate with a logical location, depending on their result.
|
|
355
|
-
elif isinstance(action, SucceedCommandAction) and isinstance(
|
|
356
|
-
action.command.result,
|
|
357
|
-
(
|
|
358
|
-
commands.heater_shaker.SetAndWaitForShakeSpeedResult,
|
|
359
|
-
commands.heater_shaker.OpenLabwareLatchResult,
|
|
360
|
-
),
|
|
361
|
-
):
|
|
362
|
-
if action.command.result.pipetteRetracted:
|
|
363
|
-
self._state.current_location = None
|
|
364
|
-
|
|
365
|
-
# A moveLabware command may have moved the labware that contains the current
|
|
366
|
-
# well out from under the pipette. Clear the current location to reflect the
|
|
367
|
-
# fact that the pipette is no longer over any labware.
|
|
368
|
-
#
|
|
369
|
-
# This is necessary for safe motion planning in case the next movement
|
|
370
|
-
# goes to the same labware (now in a new place).
|
|
371
|
-
elif isinstance(action, SucceedCommandAction) and isinstance(
|
|
372
|
-
action.command.result, commands.MoveLabwareResult
|
|
373
|
-
):
|
|
374
|
-
moved_labware_id = action.command.params.labwareId
|
|
375
|
-
if action.command.params.strategy == "usingGripper":
|
|
376
|
-
# All mounts will have been retracted.
|
|
377
|
-
self._state.current_location = None
|
|
378
|
-
elif (
|
|
379
|
-
isinstance(self._state.current_location, CurrentWell)
|
|
380
|
-
and self._state.current_location.labware_id == moved_labware_id
|
|
381
|
-
):
|
|
382
|
-
self._state.current_location = None
|
|
383
|
-
|
|
384
|
-
def _update_deck_point(
|
|
385
|
-
self, action: Union[SucceedCommandAction, FailCommandAction]
|
|
386
|
-
) -> None:
|
|
387
|
-
# This function mostly mirrors self._update_current_location().
|
|
388
|
-
# See there for explanations.
|
|
389
|
-
|
|
390
|
-
if isinstance(action, SucceedCommandAction) and isinstance(
|
|
391
|
-
action.command.result,
|
|
392
|
-
(
|
|
393
|
-
commands.MoveToWellResult,
|
|
394
|
-
commands.MoveToCoordinatesResult,
|
|
395
|
-
commands.MoveRelativeResult,
|
|
396
|
-
commands.MoveToAddressableAreaResult,
|
|
397
|
-
commands.MoveToAddressableAreaForDropTipResult,
|
|
398
|
-
commands.PickUpTipResult,
|
|
399
|
-
commands.DropTipResult,
|
|
400
|
-
commands.AspirateResult,
|
|
401
|
-
commands.DispenseResult,
|
|
402
|
-
commands.BlowOutResult,
|
|
403
|
-
commands.TouchTipResult,
|
|
404
|
-
),
|
|
405
|
-
):
|
|
406
|
-
pipette_id = action.command.params.pipetteId
|
|
407
|
-
deck_point = action.command.result.position
|
|
408
|
-
loaded_pipette = self._state.pipettes_by_id[pipette_id]
|
|
409
|
-
self._state.current_deck_point = CurrentDeckPoint(
|
|
410
|
-
mount=loaded_pipette.mount, deck_point=deck_point
|
|
411
|
-
)
|
|
412
|
-
elif (
|
|
413
|
-
isinstance(action, FailCommandAction)
|
|
414
|
-
and isinstance(
|
|
415
|
-
action.running_command,
|
|
416
|
-
(
|
|
417
|
-
commands.Aspirate,
|
|
418
|
-
commands.Dispense,
|
|
419
|
-
commands.AspirateInPlace,
|
|
420
|
-
commands.DispenseInPlace,
|
|
421
|
-
),
|
|
422
|
-
)
|
|
423
|
-
and isinstance(action.error, DefinedErrorData)
|
|
424
|
-
and isinstance(action.error.public, OverpressureError)
|
|
425
|
-
):
|
|
426
|
-
assert_type(action.error.private, OverpressureErrorInternalData)
|
|
427
|
-
pipette_id = action.running_command.params.pipetteId
|
|
428
|
-
deck_point = action.error.private.position
|
|
429
|
-
loaded_pipette = self._state.pipettes_by_id[pipette_id]
|
|
430
|
-
self._state.current_deck_point = CurrentDeckPoint(
|
|
431
|
-
mount=loaded_pipette.mount, deck_point=deck_point
|
|
432
|
-
)
|
|
433
|
-
|
|
434
|
-
elif isinstance(action, SucceedCommandAction) and isinstance(
|
|
435
|
-
action.command.result,
|
|
436
|
-
(
|
|
437
|
-
commands.HomeResult,
|
|
438
|
-
commands.RetractAxisResult,
|
|
439
|
-
commands.thermocycler.OpenLidResult,
|
|
440
|
-
commands.thermocycler.CloseLidResult,
|
|
441
|
-
),
|
|
442
|
-
):
|
|
443
|
-
self._clear_deck_point()
|
|
444
|
-
|
|
445
|
-
elif isinstance(action, SucceedCommandAction) and isinstance(
|
|
446
|
-
action.command.result,
|
|
447
|
-
(
|
|
448
|
-
commands.heater_shaker.SetAndWaitForShakeSpeedResult,
|
|
449
|
-
commands.heater_shaker.OpenLabwareLatchResult,
|
|
450
|
-
),
|
|
451
|
-
):
|
|
452
|
-
if action.command.result.pipetteRetracted:
|
|
453
|
-
self._clear_deck_point()
|
|
454
|
-
|
|
455
|
-
elif isinstance(action, SucceedCommandAction) and isinstance(
|
|
456
|
-
action.command.result, commands.MoveLabwareResult
|
|
457
|
-
):
|
|
458
|
-
if action.command.params.strategy == "usingGripper":
|
|
459
|
-
# All mounts will have been retracted.
|
|
460
|
-
self._clear_deck_point()
|
|
306
|
+
if state_update.pipette_nozzle_map != update_types.NO_CHANGE:
|
|
307
|
+
self._state.nozzle_configuration_by_id[
|
|
308
|
+
state_update.pipette_nozzle_map.pipette_id
|
|
309
|
+
] = state_update.pipette_nozzle_map.nozzle_map
|
|
461
310
|
|
|
462
311
|
def _update_volumes(
|
|
463
312
|
self, action: Union[SucceedCommandAction, FailCommandAction]
|
|
464
313
|
) -> None:
|
|
314
|
+
# todo(mm, 2024-10-10): Port these isinstance checks to StateUpdate.
|
|
315
|
+
# https://opentrons.atlassian.net/browse/EXEC-754
|
|
316
|
+
|
|
465
317
|
if isinstance(action, SucceedCommandAction) and isinstance(
|
|
466
318
|
action.command.result,
|
|
467
319
|
(commands.AspirateResult, commands.AspirateInPlaceResult),
|
|
@@ -502,10 +354,6 @@ class PipetteStore(HasState[PipetteState], HandlesActions):
|
|
|
502
354
|
pipette_id = action.command.params.pipetteId
|
|
503
355
|
self._state.aspirated_volume_by_id[pipette_id] = 0
|
|
504
356
|
|
|
505
|
-
def _clear_deck_point(self) -> None:
|
|
506
|
-
"""Reset last deck point to default None value for mount and point."""
|
|
507
|
-
self._state.current_deck_point = CurrentDeckPoint(mount=None, deck_point=None)
|
|
508
|
-
|
|
509
357
|
|
|
510
358
|
class PipetteView(HasState[PipetteState]):
|
|
511
359
|
"""Read-only view of computed pipettes state."""
|
|
@@ -781,31 +629,23 @@ class PipetteView(HasState[PipetteState]):
|
|
|
781
629
|
|
|
782
630
|
def get_nozzle_layout_type(self, pipette_id: str) -> NozzleConfigurationType:
|
|
783
631
|
"""Get the current set nozzle layout configuration."""
|
|
784
|
-
nozzle_map_for_pipette = self._state.nozzle_configuration_by_id
|
|
785
|
-
|
|
786
|
-
return nozzle_map_for_pipette.configuration
|
|
787
|
-
else:
|
|
788
|
-
return NozzleConfigurationType.FULL
|
|
632
|
+
nozzle_map_for_pipette = self._state.nozzle_configuration_by_id[pipette_id]
|
|
633
|
+
return nozzle_map_for_pipette.configuration
|
|
789
634
|
|
|
790
635
|
def get_is_partially_configured(self, pipette_id: str) -> bool:
|
|
791
636
|
"""Determine if the provided pipette is partially configured."""
|
|
792
637
|
return self.get_nozzle_layout_type(pipette_id) != NozzleConfigurationType.FULL
|
|
793
638
|
|
|
794
|
-
def get_primary_nozzle(self, pipette_id: str) ->
|
|
639
|
+
def get_primary_nozzle(self, pipette_id: str) -> str:
|
|
795
640
|
"""Get the primary nozzle, if any, related to the given pipette's nozzle configuration."""
|
|
796
|
-
nozzle_map = self._state.nozzle_configuration_by_id
|
|
797
|
-
return nozzle_map.starting_nozzle
|
|
641
|
+
nozzle_map = self._state.nozzle_configuration_by_id[pipette_id]
|
|
642
|
+
return nozzle_map.starting_nozzle
|
|
798
643
|
|
|
799
644
|
def _get_critical_point_offset_without_tip(
|
|
800
645
|
self, pipette_id: str, critical_point: Optional[CriticalPoint]
|
|
801
646
|
) -> Point:
|
|
802
647
|
"""Get the offset of the specified critical point from pipette's mount position."""
|
|
803
|
-
nozzle_map = self._state.nozzle_configuration_by_id
|
|
804
|
-
# Nozzle map is unavailable only when there's no pipette loaded
|
|
805
|
-
# so this is merely for satisfying the type checker
|
|
806
|
-
assert (
|
|
807
|
-
nozzle_map is not None
|
|
808
|
-
), "Error getting critical point offset. Nozzle map not found."
|
|
648
|
+
nozzle_map = self._state.nozzle_configuration_by_id[pipette_id]
|
|
809
649
|
match critical_point:
|
|
810
650
|
case CriticalPoint.INSTRUMENT_XY_CENTER:
|
|
811
651
|
return nozzle_map.instrument_xy_center_offset
|