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,29 +1,49 @@
|
|
|
1
1
|
"""Models and implementation for the ``moveLabware`` command."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
|
+
from opentrons_shared_data.errors.exceptions import (
|
|
5
|
+
FailedGripperPickupError,
|
|
6
|
+
LabwareDroppedError,
|
|
7
|
+
StallOrCollisionDetectedError,
|
|
8
|
+
)
|
|
4
9
|
from pydantic import BaseModel, Field
|
|
5
10
|
from typing import TYPE_CHECKING, Optional, Type
|
|
6
11
|
from typing_extensions import Literal
|
|
7
12
|
|
|
13
|
+
from opentrons.protocol_engine.resources.model_utils import ModelUtils
|
|
8
14
|
from opentrons.types import Point
|
|
9
15
|
from ..types import (
|
|
16
|
+
ModuleModel,
|
|
17
|
+
CurrentWell,
|
|
10
18
|
LabwareLocation,
|
|
11
19
|
DeckSlotLocation,
|
|
20
|
+
ModuleLocation,
|
|
12
21
|
OnLabwareLocation,
|
|
13
22
|
AddressableAreaLocation,
|
|
14
23
|
LabwareMovementStrategy,
|
|
15
24
|
LabwareOffsetVector,
|
|
16
25
|
LabwareMovementOffsetData,
|
|
17
26
|
)
|
|
18
|
-
from ..errors import
|
|
27
|
+
from ..errors import (
|
|
28
|
+
LabwareMovementNotAllowedError,
|
|
29
|
+
NotSupportedOnRobotType,
|
|
30
|
+
LabwareOffsetDoesNotExistError,
|
|
31
|
+
)
|
|
19
32
|
from ..resources import labware_validation, fixture_validation
|
|
20
|
-
from .command import
|
|
33
|
+
from .command import (
|
|
34
|
+
AbstractCommandImpl,
|
|
35
|
+
BaseCommand,
|
|
36
|
+
BaseCommandCreate,
|
|
37
|
+
DefinedErrorData,
|
|
38
|
+
SuccessData,
|
|
39
|
+
)
|
|
21
40
|
from ..errors.error_occurrence import ErrorOccurrence
|
|
41
|
+
from ..state.update_types import StateUpdate
|
|
22
42
|
from opentrons_shared_data.gripper.constants import GRIPPER_PADDLE_WIDTH
|
|
23
43
|
|
|
24
44
|
if TYPE_CHECKING:
|
|
25
45
|
from ..execution import EquipmentHandler, RunControlHandler, LabwareMovementHandler
|
|
26
|
-
from ..state import StateView
|
|
46
|
+
from ..state.state import StateView
|
|
27
47
|
|
|
28
48
|
|
|
29
49
|
MoveLabwareCommandType = Literal["moveLabware"]
|
|
@@ -33,7 +53,6 @@ MoveLabwareCommandType = Literal["moveLabware"]
|
|
|
33
53
|
_TRASH_CHUTE_DROP_BUFFER_MM = 8
|
|
34
54
|
|
|
35
55
|
|
|
36
|
-
# TODO (spp, 2022-12-14): https://opentrons.atlassian.net/browse/RLAB-237
|
|
37
56
|
class MoveLabwareParams(BaseModel):
|
|
38
57
|
"""Input parameters for a ``moveLabware`` command."""
|
|
39
58
|
|
|
@@ -74,28 +93,42 @@ class MoveLabwareResult(BaseModel):
|
|
|
74
93
|
)
|
|
75
94
|
|
|
76
95
|
|
|
77
|
-
class
|
|
78
|
-
|
|
79
|
-
|
|
96
|
+
class GripperMovementError(ErrorOccurrence):
|
|
97
|
+
"""Returned when something physically goes wrong when the gripper moves labware.
|
|
98
|
+
|
|
99
|
+
When this error happens, the engine will leave the labware in its original place.
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
isDefined: bool = True
|
|
103
|
+
|
|
104
|
+
errorType: Literal["gripperMovement"] = "gripperMovement"
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
_ExecuteReturn = SuccessData[MoveLabwareResult] | DefinedErrorData[GripperMovementError]
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class MoveLabwareImplementation(AbstractCommandImpl[MoveLabwareParams, _ExecuteReturn]):
|
|
80
111
|
"""The execution implementation for ``moveLabware`` commands."""
|
|
81
112
|
|
|
82
113
|
def __init__(
|
|
83
114
|
self,
|
|
115
|
+
model_utils: ModelUtils,
|
|
84
116
|
state_view: StateView,
|
|
85
117
|
equipment: EquipmentHandler,
|
|
86
118
|
labware_movement: LabwareMovementHandler,
|
|
87
119
|
run_control: RunControlHandler,
|
|
88
120
|
**kwargs: object,
|
|
89
121
|
) -> None:
|
|
122
|
+
self._model_utils = model_utils
|
|
90
123
|
self._state_view = state_view
|
|
91
124
|
self._equipment = equipment
|
|
92
125
|
self._labware_movement = labware_movement
|
|
93
126
|
self._run_control = run_control
|
|
94
127
|
|
|
95
|
-
async def execute( # noqa: C901
|
|
96
|
-
self, params: MoveLabwareParams
|
|
97
|
-
) -> SuccessData[MoveLabwareResult, None]:
|
|
128
|
+
async def execute(self, params: MoveLabwareParams) -> _ExecuteReturn: # noqa: C901
|
|
98
129
|
"""Move a loaded labware to a new location."""
|
|
130
|
+
state_update = StateUpdate()
|
|
131
|
+
|
|
99
132
|
# Allow propagation of LabwareNotLoadedError.
|
|
100
133
|
current_labware = self._state_view.labware.get(labware_id=params.labwareId)
|
|
101
134
|
current_labware_definition = self._state_view.labware.get_definition(
|
|
@@ -103,6 +136,7 @@ class MoveLabwareImplementation(
|
|
|
103
136
|
)
|
|
104
137
|
definition_uri = current_labware.definitionUri
|
|
105
138
|
post_drop_slide_offset: Optional[Point] = None
|
|
139
|
+
trash_lid_drop_offset: Optional[LabwareOffsetVector] = None
|
|
106
140
|
|
|
107
141
|
if self._state_view.labware.is_fixed_trash(params.labwareId):
|
|
108
142
|
raise LabwareMovementNotAllowedError(
|
|
@@ -111,9 +145,11 @@ class MoveLabwareImplementation(
|
|
|
111
145
|
|
|
112
146
|
if isinstance(params.newLocation, AddressableAreaLocation):
|
|
113
147
|
area_name = params.newLocation.addressableAreaName
|
|
114
|
-
if
|
|
115
|
-
area_name
|
|
116
|
-
|
|
148
|
+
if (
|
|
149
|
+
not fixture_validation.is_gripper_waste_chute(area_name)
|
|
150
|
+
and not fixture_validation.is_deck_slot(area_name)
|
|
151
|
+
and not fixture_validation.is_trash(area_name)
|
|
152
|
+
):
|
|
117
153
|
raise LabwareMovementNotAllowedError(
|
|
118
154
|
f"Cannot move {current_labware.loadName} to addressable area {area_name}"
|
|
119
155
|
)
|
|
@@ -135,6 +171,32 @@ class MoveLabwareImplementation(
|
|
|
135
171
|
y=0,
|
|
136
172
|
z=0,
|
|
137
173
|
)
|
|
174
|
+
elif fixture_validation.is_trash(area_name):
|
|
175
|
+
# When dropping labware in the trash bins we want to ensure they are lids
|
|
176
|
+
# and enforce a y-axis drop offset to ensure they fall within the trash bin
|
|
177
|
+
if labware_validation.validate_definition_is_lid(
|
|
178
|
+
self._state_view.labware.get_definition(params.labwareId)
|
|
179
|
+
):
|
|
180
|
+
lid_disposable_offfets = (
|
|
181
|
+
current_labware_definition.gripperOffsets.get(
|
|
182
|
+
"lidDisposalOffsets"
|
|
183
|
+
)
|
|
184
|
+
)
|
|
185
|
+
if lid_disposable_offfets is not None:
|
|
186
|
+
trash_lid_drop_offset = LabwareOffsetVector(
|
|
187
|
+
x=lid_disposable_offfets.dropOffset.x,
|
|
188
|
+
y=lid_disposable_offfets.dropOffset.y,
|
|
189
|
+
z=lid_disposable_offfets.dropOffset.z,
|
|
190
|
+
)
|
|
191
|
+
else:
|
|
192
|
+
raise LabwareOffsetDoesNotExistError(
|
|
193
|
+
f"Labware Definition {current_labware.loadName} does not contain required field 'lidDisposalOffsets' of 'gripperOffsets'."
|
|
194
|
+
)
|
|
195
|
+
else:
|
|
196
|
+
raise LabwareMovementNotAllowedError(
|
|
197
|
+
"Can only move labware with allowed role 'Lid' to a Trash Bin."
|
|
198
|
+
)
|
|
199
|
+
|
|
138
200
|
elif isinstance(params.newLocation, DeckSlotLocation):
|
|
139
201
|
self._state_view.addressable_areas.raise_if_area_not_in_deck_configuration(
|
|
140
202
|
params.newLocation.slotName.id
|
|
@@ -157,6 +219,17 @@ class MoveLabwareImplementation(
|
|
|
157
219
|
top_labware_definition=current_labware_definition,
|
|
158
220
|
bottom_labware_id=available_new_location.labwareId,
|
|
159
221
|
)
|
|
222
|
+
if params.labwareId == available_new_location.labwareId:
|
|
223
|
+
raise LabwareMovementNotAllowedError(
|
|
224
|
+
"Cannot move a labware onto itself."
|
|
225
|
+
)
|
|
226
|
+
# Validate labware for the absorbance reader
|
|
227
|
+
elif isinstance(available_new_location, ModuleLocation):
|
|
228
|
+
module = self._state_view.modules.get(available_new_location.moduleId)
|
|
229
|
+
if module is not None and module.model == ModuleModel.ABSORBANCE_READER_V1:
|
|
230
|
+
self._state_view.labware.raise_if_labware_incompatible_with_plate_reader(
|
|
231
|
+
current_labware_definition
|
|
232
|
+
)
|
|
160
233
|
|
|
161
234
|
# Allow propagation of ModuleNotLoadedError.
|
|
162
235
|
new_offset_id = self._equipment.find_applicable_labware_offset_id(
|
|
@@ -201,24 +274,83 @@ class MoveLabwareImplementation(
|
|
|
201
274
|
dropOffset=params.dropOffset or LabwareOffsetVector(x=0, y=0, z=0),
|
|
202
275
|
)
|
|
203
276
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
277
|
+
if trash_lid_drop_offset:
|
|
278
|
+
user_offset_data.dropOffset += trash_lid_drop_offset
|
|
279
|
+
|
|
280
|
+
try:
|
|
281
|
+
# Skips gripper moves when using virtual gripper
|
|
282
|
+
await self._labware_movement.move_labware_with_gripper(
|
|
283
|
+
labware_id=params.labwareId,
|
|
284
|
+
current_location=validated_current_loc,
|
|
285
|
+
new_location=validated_new_loc,
|
|
286
|
+
user_offset_data=user_offset_data,
|
|
287
|
+
post_drop_slide_offset=post_drop_slide_offset,
|
|
288
|
+
)
|
|
289
|
+
except (
|
|
290
|
+
FailedGripperPickupError,
|
|
291
|
+
LabwareDroppedError,
|
|
292
|
+
StallOrCollisionDetectedError,
|
|
293
|
+
# todo(mm, 2024-09-26): Catch LabwareNotPickedUpError when that exists and
|
|
294
|
+
# move_labware_with_gripper() raises it.
|
|
295
|
+
) as exception:
|
|
296
|
+
gripper_movement_error: GripperMovementError | None = (
|
|
297
|
+
GripperMovementError(
|
|
298
|
+
id=self._model_utils.generate_id(),
|
|
299
|
+
createdAt=self._model_utils.get_timestamp(),
|
|
300
|
+
errorCode=exception.code.value.code,
|
|
301
|
+
detail=exception.code.value.detail,
|
|
302
|
+
wrappedErrors=[
|
|
303
|
+
ErrorOccurrence.from_failed(
|
|
304
|
+
id=self._model_utils.generate_id(),
|
|
305
|
+
createdAt=self._model_utils.get_timestamp(),
|
|
306
|
+
error=exception,
|
|
307
|
+
)
|
|
308
|
+
],
|
|
309
|
+
)
|
|
310
|
+
)
|
|
311
|
+
else:
|
|
312
|
+
gripper_movement_error = None
|
|
313
|
+
|
|
314
|
+
# All mounts will have been retracted as part of the gripper move.
|
|
315
|
+
state_update.clear_all_pipette_locations()
|
|
316
|
+
|
|
317
|
+
if gripper_movement_error:
|
|
318
|
+
return DefinedErrorData(
|
|
319
|
+
public=gripper_movement_error,
|
|
320
|
+
state_update=state_update,
|
|
321
|
+
)
|
|
322
|
+
|
|
212
323
|
elif params.strategy == LabwareMovementStrategy.MANUAL_MOVE_WITH_PAUSE:
|
|
213
324
|
# Pause to allow for manual labware movement
|
|
214
325
|
await self._run_control.wait_for_resume()
|
|
215
326
|
|
|
327
|
+
# We may have just moved the labware that contains the current well out from
|
|
328
|
+
# under the pipette. Clear the current location to reflect the fact that the
|
|
329
|
+
# pipette is no longer over any labware. This is necessary for safe path
|
|
330
|
+
# planning in case the next movement goes to the same labware (now in a new
|
|
331
|
+
# place).
|
|
332
|
+
pipette_location = self._state_view.pipettes.get_current_location()
|
|
333
|
+
if (
|
|
334
|
+
isinstance(pipette_location, CurrentWell)
|
|
335
|
+
and pipette_location.labware_id == params.labwareId
|
|
336
|
+
):
|
|
337
|
+
state_update.clear_all_pipette_locations()
|
|
338
|
+
|
|
339
|
+
state_update.set_labware_location(
|
|
340
|
+
labware_id=params.labwareId,
|
|
341
|
+
new_location=available_new_location,
|
|
342
|
+
new_offset_id=new_offset_id,
|
|
343
|
+
)
|
|
344
|
+
|
|
216
345
|
return SuccessData(
|
|
217
|
-
public=MoveLabwareResult(offsetId=new_offset_id),
|
|
346
|
+
public=MoveLabwareResult(offsetId=new_offset_id),
|
|
347
|
+
state_update=state_update,
|
|
218
348
|
)
|
|
219
349
|
|
|
220
350
|
|
|
221
|
-
class MoveLabware(
|
|
351
|
+
class MoveLabware(
|
|
352
|
+
BaseCommand[MoveLabwareParams, MoveLabwareResult, GripperMovementError]
|
|
353
|
+
):
|
|
222
354
|
"""A ``moveLabware`` command."""
|
|
223
355
|
|
|
224
356
|
commandType: MoveLabwareCommandType = "moveLabware"
|
|
@@ -4,6 +4,8 @@ from pydantic import BaseModel, Field
|
|
|
4
4
|
from typing import TYPE_CHECKING, Optional, Type
|
|
5
5
|
from typing_extensions import Literal
|
|
6
6
|
|
|
7
|
+
|
|
8
|
+
from ..state import update_types
|
|
7
9
|
from ..types import MovementAxis, DeckPoint
|
|
8
10
|
from .command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
|
|
9
11
|
from ..errors.error_occurrence import ErrorOccurrence
|
|
@@ -37,7 +39,7 @@ class MoveRelativeResult(DestinationPositionResult):
|
|
|
37
39
|
|
|
38
40
|
|
|
39
41
|
class MoveRelativeImplementation(
|
|
40
|
-
AbstractCommandImpl[MoveRelativeParams, SuccessData[MoveRelativeResult
|
|
42
|
+
AbstractCommandImpl[MoveRelativeParams, SuccessData[MoveRelativeResult]]
|
|
41
43
|
):
|
|
42
44
|
"""Move relative command implementation."""
|
|
43
45
|
|
|
@@ -46,16 +48,26 @@ class MoveRelativeImplementation(
|
|
|
46
48
|
|
|
47
49
|
async def execute(
|
|
48
50
|
self, params: MoveRelativeParams
|
|
49
|
-
) -> SuccessData[MoveRelativeResult
|
|
51
|
+
) -> SuccessData[MoveRelativeResult]:
|
|
50
52
|
"""Move (jog) a given pipette a relative distance."""
|
|
53
|
+
state_update = update_types.StateUpdate()
|
|
54
|
+
|
|
51
55
|
x, y, z = await self._movement.move_relative(
|
|
52
56
|
pipette_id=params.pipetteId,
|
|
53
57
|
axis=params.axis,
|
|
54
58
|
distance=params.distance,
|
|
55
59
|
)
|
|
60
|
+
deck_point = DeckPoint.construct(x=x, y=y, z=z)
|
|
61
|
+
state_update.pipette_location = update_types.PipetteLocationUpdate(
|
|
62
|
+
pipette_id=params.pipetteId,
|
|
63
|
+
# TODO(jbl 2023-02-14): Need to investigate whether move relative should clear current location
|
|
64
|
+
new_location=update_types.NO_CHANGE,
|
|
65
|
+
new_deck_point=deck_point,
|
|
66
|
+
)
|
|
56
67
|
|
|
57
68
|
return SuccessData(
|
|
58
|
-
public=MoveRelativeResult(position=
|
|
69
|
+
public=MoveRelativeResult(position=deck_point),
|
|
70
|
+
state_update=state_update,
|
|
59
71
|
)
|
|
60
72
|
|
|
61
73
|
|
|
@@ -4,7 +4,10 @@ from pydantic import Field
|
|
|
4
4
|
from typing import TYPE_CHECKING, Optional, Type
|
|
5
5
|
from typing_extensions import Literal
|
|
6
6
|
|
|
7
|
+
from opentrons_shared_data.pipette.types import PipetteNameType
|
|
8
|
+
|
|
7
9
|
from ..errors import LocationNotAccessibleByPipetteError
|
|
10
|
+
from ..state import update_types
|
|
8
11
|
from ..types import DeckPoint, AddressableOffsetVector
|
|
9
12
|
from ..resources import fixture_validation
|
|
10
13
|
from .pipetting_common import (
|
|
@@ -17,7 +20,7 @@ from ..errors.error_occurrence import ErrorOccurrence
|
|
|
17
20
|
|
|
18
21
|
if TYPE_CHECKING:
|
|
19
22
|
from ..execution import MovementHandler
|
|
20
|
-
from ..state import StateView
|
|
23
|
+
from ..state.state import StateView
|
|
21
24
|
|
|
22
25
|
MoveToAddressableAreaCommandType = Literal["moveToAddressableArea"]
|
|
23
26
|
|
|
@@ -73,7 +76,7 @@ class MoveToAddressableAreaResult(DestinationPositionResult):
|
|
|
73
76
|
|
|
74
77
|
class MoveToAddressableAreaImplementation(
|
|
75
78
|
AbstractCommandImpl[
|
|
76
|
-
MoveToAddressableAreaParams, SuccessData[MoveToAddressableAreaResult
|
|
79
|
+
MoveToAddressableAreaParams, SuccessData[MoveToAddressableAreaResult]
|
|
77
80
|
]
|
|
78
81
|
):
|
|
79
82
|
"""Move to addressable area command implementation."""
|
|
@@ -86,11 +89,26 @@ class MoveToAddressableAreaImplementation(
|
|
|
86
89
|
|
|
87
90
|
async def execute(
|
|
88
91
|
self, params: MoveToAddressableAreaParams
|
|
89
|
-
) -> SuccessData[MoveToAddressableAreaResult
|
|
92
|
+
) -> SuccessData[MoveToAddressableAreaResult]:
|
|
90
93
|
"""Move the requested pipette to the requested addressable area."""
|
|
94
|
+
state_update = update_types.StateUpdate()
|
|
95
|
+
|
|
91
96
|
self._state_view.addressable_areas.raise_if_area_not_in_deck_configuration(
|
|
92
97
|
params.addressableAreaName
|
|
93
98
|
)
|
|
99
|
+
loaded_pipette = self._state_view.pipettes.get(params.pipetteId)
|
|
100
|
+
if loaded_pipette.pipetteName in (
|
|
101
|
+
PipetteNameType.P10_SINGLE,
|
|
102
|
+
PipetteNameType.P10_MULTI,
|
|
103
|
+
PipetteNameType.P50_MULTI,
|
|
104
|
+
PipetteNameType.P50_SINGLE,
|
|
105
|
+
PipetteNameType.P300_SINGLE,
|
|
106
|
+
PipetteNameType.P300_MULTI,
|
|
107
|
+
PipetteNameType.P1000_SINGLE,
|
|
108
|
+
):
|
|
109
|
+
extra_z_offset: Optional[float] = 5.0
|
|
110
|
+
else:
|
|
111
|
+
extra_z_offset = None
|
|
94
112
|
|
|
95
113
|
if fixture_validation.is_staging_slot(params.addressableAreaName):
|
|
96
114
|
raise LocationNotAccessibleByPipetteError(
|
|
@@ -105,11 +123,18 @@ class MoveToAddressableAreaImplementation(
|
|
|
105
123
|
minimum_z_height=params.minimumZHeight,
|
|
106
124
|
speed=params.speed,
|
|
107
125
|
stay_at_highest_possible_z=params.stayAtHighestPossibleZ,
|
|
126
|
+
highest_possible_z_extra_offset=extra_z_offset,
|
|
127
|
+
)
|
|
128
|
+
deck_point = DeckPoint.construct(x=x, y=y, z=z)
|
|
129
|
+
state_update.set_pipette_location(
|
|
130
|
+
pipette_id=params.pipetteId,
|
|
131
|
+
new_addressable_area_name=params.addressableAreaName,
|
|
132
|
+
new_deck_point=deck_point,
|
|
108
133
|
)
|
|
109
134
|
|
|
110
135
|
return SuccessData(
|
|
111
136
|
public=MoveToAddressableAreaResult(position=DeckPoint(x=x, y=y, z=z)),
|
|
112
|
-
|
|
137
|
+
state_update=state_update,
|
|
113
138
|
)
|
|
114
139
|
|
|
115
140
|
|
|
@@ -5,6 +5,7 @@ from typing import TYPE_CHECKING, Optional, Type
|
|
|
5
5
|
from typing_extensions import Literal
|
|
6
6
|
|
|
7
7
|
from ..errors import LocationNotAccessibleByPipetteError
|
|
8
|
+
from ..state import update_types
|
|
8
9
|
from ..types import DeckPoint, AddressableOffsetVector
|
|
9
10
|
from ..resources import fixture_validation
|
|
10
11
|
from .pipetting_common import (
|
|
@@ -17,7 +18,7 @@ from ..errors.error_occurrence import ErrorOccurrence
|
|
|
17
18
|
|
|
18
19
|
if TYPE_CHECKING:
|
|
19
20
|
from ..execution import MovementHandler
|
|
20
|
-
from ..state import StateView
|
|
21
|
+
from ..state.state import StateView
|
|
21
22
|
|
|
22
23
|
MoveToAddressableAreaForDropTipCommandType = Literal["moveToAddressableAreaForDropTip"]
|
|
23
24
|
|
|
@@ -85,7 +86,7 @@ class MoveToAddressableAreaForDropTipResult(DestinationPositionResult):
|
|
|
85
86
|
class MoveToAddressableAreaForDropTipImplementation(
|
|
86
87
|
AbstractCommandImpl[
|
|
87
88
|
MoveToAddressableAreaForDropTipParams,
|
|
88
|
-
SuccessData[MoveToAddressableAreaForDropTipResult
|
|
89
|
+
SuccessData[MoveToAddressableAreaForDropTipResult],
|
|
89
90
|
]
|
|
90
91
|
):
|
|
91
92
|
"""Move to addressable area for drop tip command implementation."""
|
|
@@ -98,8 +99,10 @@ class MoveToAddressableAreaForDropTipImplementation(
|
|
|
98
99
|
|
|
99
100
|
async def execute(
|
|
100
101
|
self, params: MoveToAddressableAreaForDropTipParams
|
|
101
|
-
) -> SuccessData[MoveToAddressableAreaForDropTipResult
|
|
102
|
+
) -> SuccessData[MoveToAddressableAreaForDropTipResult]:
|
|
102
103
|
"""Move the requested pipette to the requested addressable area in preperation of a drop tip."""
|
|
104
|
+
state_update = update_types.StateUpdate()
|
|
105
|
+
|
|
103
106
|
self._state_view.addressable_areas.raise_if_area_not_in_deck_configuration(
|
|
104
107
|
params.addressableAreaName
|
|
105
108
|
)
|
|
@@ -126,12 +129,18 @@ class MoveToAddressableAreaForDropTipImplementation(
|
|
|
126
129
|
speed=params.speed,
|
|
127
130
|
ignore_tip_configuration=params.ignoreTipConfiguration,
|
|
128
131
|
)
|
|
132
|
+
deck_point = DeckPoint.construct(x=x, y=y, z=z)
|
|
133
|
+
state_update.set_pipette_location(
|
|
134
|
+
pipette_id=params.pipetteId,
|
|
135
|
+
new_addressable_area_name=params.addressableAreaName,
|
|
136
|
+
new_deck_point=deck_point,
|
|
137
|
+
)
|
|
129
138
|
|
|
130
139
|
return SuccessData(
|
|
131
140
|
public=MoveToAddressableAreaForDropTipResult(
|
|
132
141
|
position=DeckPoint(x=x, y=y, z=z)
|
|
133
142
|
),
|
|
134
|
-
|
|
143
|
+
state_update=state_update,
|
|
135
144
|
)
|
|
136
145
|
|
|
137
146
|
|
|
@@ -5,6 +5,8 @@ from pydantic import Field
|
|
|
5
5
|
from typing import Optional, Type, TYPE_CHECKING
|
|
6
6
|
from typing_extensions import Literal
|
|
7
7
|
|
|
8
|
+
|
|
9
|
+
from ..state import update_types
|
|
8
10
|
from ..types import DeckPoint
|
|
9
11
|
from .pipetting_common import PipetteIdMixin, MovementMixin, DestinationPositionResult
|
|
10
12
|
from .command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
|
|
@@ -33,9 +35,7 @@ class MoveToCoordinatesResult(DestinationPositionResult):
|
|
|
33
35
|
|
|
34
36
|
|
|
35
37
|
class MoveToCoordinatesImplementation(
|
|
36
|
-
AbstractCommandImpl[
|
|
37
|
-
MoveToCoordinatesParams, SuccessData[MoveToCoordinatesResult, None]
|
|
38
|
-
]
|
|
38
|
+
AbstractCommandImpl[MoveToCoordinatesParams, SuccessData[MoveToCoordinatesResult]]
|
|
39
39
|
):
|
|
40
40
|
"""Move to coordinates command implementation."""
|
|
41
41
|
|
|
@@ -48,8 +48,10 @@ class MoveToCoordinatesImplementation(
|
|
|
48
48
|
|
|
49
49
|
async def execute(
|
|
50
50
|
self, params: MoveToCoordinatesParams
|
|
51
|
-
) -> SuccessData[MoveToCoordinatesResult
|
|
51
|
+
) -> SuccessData[MoveToCoordinatesResult]:
|
|
52
52
|
"""Move the requested pipette to the requested coordinates."""
|
|
53
|
+
state_update = update_types.StateUpdate()
|
|
54
|
+
|
|
53
55
|
x, y, z = await self._movement.move_to_coordinates(
|
|
54
56
|
pipette_id=params.pipetteId,
|
|
55
57
|
deck_coordinates=params.coordinates,
|
|
@@ -57,10 +59,14 @@ class MoveToCoordinatesImplementation(
|
|
|
57
59
|
additional_min_travel_z=params.minimumZHeight,
|
|
58
60
|
speed=params.speed,
|
|
59
61
|
)
|
|
62
|
+
deck_point = DeckPoint.construct(x=x, y=y, z=z)
|
|
63
|
+
state_update.pipette_location = update_types.PipetteLocationUpdate(
|
|
64
|
+
pipette_id=params.pipetteId, new_location=None, new_deck_point=deck_point
|
|
65
|
+
)
|
|
60
66
|
|
|
61
67
|
return SuccessData(
|
|
62
68
|
public=MoveToCoordinatesResult(position=DeckPoint(x=x, y=y, z=z)),
|
|
63
|
-
|
|
69
|
+
state_update=state_update,
|
|
64
70
|
)
|
|
65
71
|
|
|
66
72
|
|
|
@@ -12,9 +12,12 @@ from .pipetting_common import (
|
|
|
12
12
|
)
|
|
13
13
|
from .command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
|
|
14
14
|
from ..errors.error_occurrence import ErrorOccurrence
|
|
15
|
+
from ..state import update_types
|
|
16
|
+
from ..errors import LabwareIsTipRackError
|
|
15
17
|
|
|
16
18
|
if TYPE_CHECKING:
|
|
17
19
|
from ..execution import MovementHandler
|
|
20
|
+
from ..state.state import StateView
|
|
18
21
|
|
|
19
22
|
MoveToWellCommandType = Literal["moveToWell"]
|
|
20
23
|
|
|
@@ -32,29 +35,53 @@ class MoveToWellResult(DestinationPositionResult):
|
|
|
32
35
|
|
|
33
36
|
|
|
34
37
|
class MoveToWellImplementation(
|
|
35
|
-
AbstractCommandImpl[MoveToWellParams, SuccessData[MoveToWellResult
|
|
38
|
+
AbstractCommandImpl[MoveToWellParams, SuccessData[MoveToWellResult]]
|
|
36
39
|
):
|
|
37
40
|
"""Move to well command implementation."""
|
|
38
41
|
|
|
39
|
-
def __init__(
|
|
42
|
+
def __init__(
|
|
43
|
+
self, state_view: StateView, movement: MovementHandler, **kwargs: object
|
|
44
|
+
) -> None:
|
|
45
|
+
self._state_view = state_view
|
|
40
46
|
self._movement = movement
|
|
41
47
|
|
|
42
|
-
async def execute(
|
|
43
|
-
self, params: MoveToWellParams
|
|
44
|
-
) -> SuccessData[MoveToWellResult, None]:
|
|
48
|
+
async def execute(self, params: MoveToWellParams) -> SuccessData[MoveToWellResult]:
|
|
45
49
|
"""Move the requested pipette to the requested well."""
|
|
50
|
+
pipette_id = params.pipetteId
|
|
51
|
+
labware_id = params.labwareId
|
|
52
|
+
well_name = params.wellName
|
|
53
|
+
well_location = params.wellLocation
|
|
54
|
+
|
|
55
|
+
state_update = update_types.StateUpdate()
|
|
56
|
+
|
|
57
|
+
if (
|
|
58
|
+
self._state_view.labware.is_tiprack(labware_id)
|
|
59
|
+
and well_location.volumeOffset
|
|
60
|
+
):
|
|
61
|
+
raise LabwareIsTipRackError(
|
|
62
|
+
"Cannot specify a WellLocation with a volumeOffset with movement to a tip rack"
|
|
63
|
+
)
|
|
64
|
+
|
|
46
65
|
x, y, z = await self._movement.move_to_well(
|
|
47
|
-
pipette_id=
|
|
48
|
-
labware_id=
|
|
49
|
-
well_name=
|
|
50
|
-
well_location=
|
|
66
|
+
pipette_id=pipette_id,
|
|
67
|
+
labware_id=labware_id,
|
|
68
|
+
well_name=well_name,
|
|
69
|
+
well_location=well_location,
|
|
51
70
|
force_direct=params.forceDirect,
|
|
52
71
|
minimum_z_height=params.minimumZHeight,
|
|
53
72
|
speed=params.speed,
|
|
54
73
|
)
|
|
74
|
+
deck_point = DeckPoint.construct(x=x, y=y, z=z)
|
|
75
|
+
state_update.set_pipette_location(
|
|
76
|
+
pipette_id=pipette_id,
|
|
77
|
+
new_labware_id=labware_id,
|
|
78
|
+
new_well_name=well_name,
|
|
79
|
+
new_deck_point=deck_point,
|
|
80
|
+
)
|
|
55
81
|
|
|
56
82
|
return SuccessData(
|
|
57
|
-
public=MoveToWellResult(position=
|
|
83
|
+
public=MoveToWellResult(position=deck_point),
|
|
84
|
+
state_update=state_update,
|
|
58
85
|
)
|
|
59
86
|
|
|
60
87
|
|