opentrons 8.2.0a3__py2.py3-none-any.whl → 8.3.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/calibration_storage/deck_configuration.py +3 -3
- opentrons/calibration_storage/file_operators.py +3 -3
- opentrons/calibration_storage/helpers.py +3 -1
- opentrons/calibration_storage/ot2/models/v1.py +16 -29
- opentrons/calibration_storage/ot2/tip_length.py +7 -4
- opentrons/calibration_storage/ot3/models/v1.py +14 -23
- opentrons/cli/analyze.py +18 -6
- opentrons/config/defaults_ot3.py +1 -0
- opentrons/drivers/asyncio/communication/__init__.py +2 -0
- opentrons/drivers/asyncio/communication/errors.py +16 -3
- opentrons/drivers/asyncio/communication/serial_connection.py +24 -9
- opentrons/drivers/command_builder.py +2 -2
- opentrons/drivers/flex_stacker/__init__.py +9 -0
- opentrons/drivers/flex_stacker/abstract.py +89 -0
- opentrons/drivers/flex_stacker/driver.py +260 -0
- opentrons/drivers/flex_stacker/simulator.py +109 -0
- opentrons/drivers/flex_stacker/types.py +138 -0
- opentrons/drivers/heater_shaker/driver.py +18 -3
- opentrons/drivers/temp_deck/driver.py +13 -3
- opentrons/drivers/thermocycler/driver.py +17 -3
- opentrons/execute.py +3 -1
- opentrons/hardware_control/__init__.py +1 -2
- opentrons/hardware_control/api.py +33 -21
- opentrons/hardware_control/backends/flex_protocol.py +17 -7
- opentrons/hardware_control/backends/ot3controller.py +213 -63
- opentrons/hardware_control/backends/ot3simulator.py +18 -9
- opentrons/hardware_control/backends/ot3utils.py +43 -15
- opentrons/hardware_control/dev_types.py +4 -0
- opentrons/hardware_control/emulation/heater_shaker.py +4 -0
- opentrons/hardware_control/emulation/module_server/client.py +1 -1
- opentrons/hardware_control/emulation/module_server/server.py +5 -3
- opentrons/hardware_control/emulation/settings.py +3 -4
- opentrons/hardware_control/instruments/ot2/instrument_calibration.py +2 -1
- opentrons/hardware_control/instruments/ot2/pipette.py +15 -22
- opentrons/hardware_control/instruments/ot2/pipette_handler.py +8 -1
- opentrons/hardware_control/instruments/ot3/gripper.py +2 -2
- opentrons/hardware_control/instruments/ot3/pipette.py +23 -22
- opentrons/hardware_control/instruments/ot3/pipette_handler.py +10 -1
- opentrons/hardware_control/modules/mod_abc.py +2 -2
- opentrons/hardware_control/motion_utilities.py +68 -0
- opentrons/hardware_control/nozzle_manager.py +39 -41
- opentrons/hardware_control/ot3_calibration.py +1 -1
- opentrons/hardware_control/ot3api.py +78 -31
- opentrons/hardware_control/protocols/gripper_controller.py +3 -0
- opentrons/hardware_control/protocols/hardware_manager.py +5 -1
- opentrons/hardware_control/protocols/liquid_handler.py +22 -1
- opentrons/hardware_control/protocols/motion_controller.py +7 -0
- opentrons/hardware_control/robot_calibration.py +1 -1
- opentrons/hardware_control/types.py +61 -0
- opentrons/legacy_commands/commands.py +37 -0
- opentrons/legacy_commands/types.py +39 -0
- opentrons/protocol_api/__init__.py +20 -1
- opentrons/protocol_api/_liquid.py +24 -49
- opentrons/protocol_api/_liquid_properties.py +754 -0
- opentrons/protocol_api/_types.py +24 -0
- opentrons/protocol_api/core/common.py +2 -0
- opentrons/protocol_api/core/engine/instrument.py +191 -10
- opentrons/protocol_api/core/engine/labware.py +29 -7
- opentrons/protocol_api/core/engine/protocol.py +130 -5
- opentrons/protocol_api/core/engine/robot.py +139 -0
- opentrons/protocol_api/core/engine/well.py +4 -1
- opentrons/protocol_api/core/instrument.py +73 -4
- opentrons/protocol_api/core/labware.py +13 -4
- opentrons/protocol_api/core/legacy/legacy_instrument_core.py +87 -3
- opentrons/protocol_api/core/legacy/legacy_labware_core.py +13 -4
- opentrons/protocol_api/core/legacy/legacy_protocol_core.py +32 -1
- opentrons/protocol_api/core/legacy/legacy_robot_core.py +0 -0
- opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +61 -3
- opentrons/protocol_api/core/protocol.py +34 -1
- opentrons/protocol_api/core/robot.py +51 -0
- opentrons/protocol_api/instrument_context.py +299 -44
- opentrons/protocol_api/labware.py +248 -9
- opentrons/protocol_api/module_contexts.py +21 -17
- opentrons/protocol_api/protocol_context.py +125 -4
- opentrons/protocol_api/robot_context.py +204 -32
- opentrons/protocol_api/validation.py +262 -3
- opentrons/protocol_engine/__init__.py +4 -0
- opentrons/protocol_engine/actions/actions.py +2 -3
- opentrons/protocol_engine/clients/sync_client.py +18 -0
- opentrons/protocol_engine/commands/__init__.py +121 -0
- opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +1 -3
- opentrons/protocol_engine/commands/absorbance_reader/initialize.py +20 -6
- opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +1 -2
- opentrons/protocol_engine/commands/absorbance_reader/read.py +40 -10
- opentrons/protocol_engine/commands/air_gap_in_place.py +160 -0
- opentrons/protocol_engine/commands/aspirate.py +103 -53
- opentrons/protocol_engine/commands/aspirate_in_place.py +55 -51
- opentrons/protocol_engine/commands/blow_out.py +44 -39
- opentrons/protocol_engine/commands/blow_out_in_place.py +21 -32
- opentrons/protocol_engine/commands/calibration/calibrate_gripper.py +13 -6
- opentrons/protocol_engine/commands/calibration/calibrate_module.py +1 -1
- opentrons/protocol_engine/commands/calibration/calibrate_pipette.py +3 -3
- opentrons/protocol_engine/commands/calibration/move_to_maintenance_position.py +1 -1
- opentrons/protocol_engine/commands/command.py +73 -66
- opentrons/protocol_engine/commands/command_unions.py +140 -1
- opentrons/protocol_engine/commands/comment.py +1 -1
- opentrons/protocol_engine/commands/configure_for_volume.py +10 -3
- opentrons/protocol_engine/commands/configure_nozzle_layout.py +6 -4
- opentrons/protocol_engine/commands/custom.py +6 -12
- opentrons/protocol_engine/commands/dispense.py +82 -48
- opentrons/protocol_engine/commands/dispense_in_place.py +71 -51
- opentrons/protocol_engine/commands/drop_tip.py +52 -31
- opentrons/protocol_engine/commands/drop_tip_in_place.py +79 -8
- opentrons/protocol_engine/commands/evotip_dispense.py +156 -0
- opentrons/protocol_engine/commands/evotip_seal_pipette.py +331 -0
- opentrons/protocol_engine/commands/evotip_unseal_pipette.py +160 -0
- opentrons/protocol_engine/commands/generate_command_schema.py +4 -11
- opentrons/protocol_engine/commands/get_next_tip.py +134 -0
- opentrons/protocol_engine/commands/get_tip_presence.py +1 -1
- opentrons/protocol_engine/commands/heater_shaker/close_labware_latch.py +1 -1
- opentrons/protocol_engine/commands/heater_shaker/deactivate_heater.py +1 -1
- opentrons/protocol_engine/commands/heater_shaker/deactivate_shaker.py +1 -1
- opentrons/protocol_engine/commands/heater_shaker/open_labware_latch.py +1 -1
- opentrons/protocol_engine/commands/heater_shaker/set_and_wait_for_shake_speed.py +1 -1
- opentrons/protocol_engine/commands/heater_shaker/set_target_temperature.py +1 -1
- opentrons/protocol_engine/commands/heater_shaker/wait_for_temperature.py +10 -4
- opentrons/protocol_engine/commands/home.py +13 -4
- opentrons/protocol_engine/commands/liquid_probe.py +125 -31
- opentrons/protocol_engine/commands/load_labware.py +33 -6
- opentrons/protocol_engine/commands/load_lid.py +146 -0
- opentrons/protocol_engine/commands/load_lid_stack.py +189 -0
- opentrons/protocol_engine/commands/load_liquid.py +12 -4
- opentrons/protocol_engine/commands/load_liquid_class.py +144 -0
- opentrons/protocol_engine/commands/load_module.py +31 -10
- opentrons/protocol_engine/commands/load_pipette.py +19 -8
- opentrons/protocol_engine/commands/magnetic_module/disengage.py +1 -1
- opentrons/protocol_engine/commands/magnetic_module/engage.py +1 -1
- opentrons/protocol_engine/commands/move_labware.py +28 -6
- opentrons/protocol_engine/commands/move_relative.py +35 -25
- opentrons/protocol_engine/commands/move_to_addressable_area.py +40 -27
- opentrons/protocol_engine/commands/move_to_addressable_area_for_drop_tip.py +53 -32
- opentrons/protocol_engine/commands/move_to_coordinates.py +36 -22
- opentrons/protocol_engine/commands/move_to_well.py +40 -24
- opentrons/protocol_engine/commands/movement_common.py +338 -0
- opentrons/protocol_engine/commands/pick_up_tip.py +49 -27
- opentrons/protocol_engine/commands/pipetting_common.py +169 -87
- opentrons/protocol_engine/commands/prepare_to_aspirate.py +24 -33
- opentrons/protocol_engine/commands/reload_labware.py +1 -1
- opentrons/protocol_engine/commands/retract_axis.py +1 -1
- opentrons/protocol_engine/commands/robot/__init__.py +69 -0
- opentrons/protocol_engine/commands/robot/close_gripper_jaw.py +86 -0
- opentrons/protocol_engine/commands/robot/common.py +18 -0
- opentrons/protocol_engine/commands/robot/move_axes_relative.py +101 -0
- opentrons/protocol_engine/commands/robot/move_axes_to.py +100 -0
- opentrons/protocol_engine/commands/robot/move_to.py +94 -0
- opentrons/protocol_engine/commands/robot/open_gripper_jaw.py +77 -0
- opentrons/protocol_engine/commands/save_position.py +14 -5
- opentrons/protocol_engine/commands/set_rail_lights.py +1 -1
- opentrons/protocol_engine/commands/set_status_bar.py +1 -1
- opentrons/protocol_engine/commands/temperature_module/deactivate.py +1 -1
- opentrons/protocol_engine/commands/temperature_module/set_target_temperature.py +1 -1
- opentrons/protocol_engine/commands/temperature_module/wait_for_temperature.py +10 -4
- opentrons/protocol_engine/commands/thermocycler/close_lid.py +1 -1
- opentrons/protocol_engine/commands/thermocycler/deactivate_block.py +1 -1
- opentrons/protocol_engine/commands/thermocycler/deactivate_lid.py +1 -1
- opentrons/protocol_engine/commands/thermocycler/open_lid.py +1 -1
- opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py +9 -3
- opentrons/protocol_engine/commands/thermocycler/run_profile.py +9 -3
- opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py +11 -4
- opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py +1 -1
- opentrons/protocol_engine/commands/thermocycler/wait_for_block_temperature.py +1 -1
- opentrons/protocol_engine/commands/thermocycler/wait_for_lid_temperature.py +1 -1
- opentrons/protocol_engine/commands/touch_tip.py +65 -16
- opentrons/protocol_engine/commands/unsafe/unsafe_blow_out_in_place.py +5 -2
- opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +13 -4
- opentrons/protocol_engine/commands/unsafe/unsafe_engage_axes.py +2 -5
- opentrons/protocol_engine/commands/unsafe/unsafe_place_labware.py +1 -1
- opentrons/protocol_engine/commands/unsafe/unsafe_ungrip_labware.py +4 -2
- opentrons/protocol_engine/commands/unsafe/update_position_estimators.py +2 -5
- opentrons/protocol_engine/commands/verify_tip_presence.py +11 -4
- opentrons/protocol_engine/commands/wait_for_duration.py +10 -3
- opentrons/protocol_engine/commands/wait_for_resume.py +10 -3
- opentrons/protocol_engine/errors/__init__.py +12 -0
- opentrons/protocol_engine/errors/error_occurrence.py +19 -20
- opentrons/protocol_engine/errors/exceptions.py +76 -0
- opentrons/protocol_engine/execution/command_executor.py +1 -1
- opentrons/protocol_engine/execution/equipment.py +73 -5
- opentrons/protocol_engine/execution/gantry_mover.py +369 -8
- opentrons/protocol_engine/execution/hardware_stopper.py +7 -7
- opentrons/protocol_engine/execution/movement.py +27 -0
- opentrons/protocol_engine/execution/pipetting.py +5 -1
- opentrons/protocol_engine/execution/tip_handler.py +34 -15
- opentrons/protocol_engine/notes/notes.py +1 -1
- opentrons/protocol_engine/protocol_engine.py +7 -6
- opentrons/protocol_engine/resources/labware_data_provider.py +1 -1
- opentrons/protocol_engine/resources/labware_validation.py +18 -0
- opentrons/protocol_engine/resources/module_data_provider.py +1 -1
- opentrons/protocol_engine/resources/pipette_data_provider.py +26 -0
- opentrons/protocol_engine/slot_standardization.py +9 -9
- opentrons/protocol_engine/state/_move_types.py +9 -5
- opentrons/protocol_engine/state/_well_math.py +193 -0
- opentrons/protocol_engine/state/addressable_areas.py +25 -61
- opentrons/protocol_engine/state/command_history.py +12 -0
- opentrons/protocol_engine/state/commands.py +22 -14
- opentrons/protocol_engine/state/files.py +10 -12
- opentrons/protocol_engine/state/fluid_stack.py +138 -0
- opentrons/protocol_engine/state/frustum_helpers.py +63 -69
- opentrons/protocol_engine/state/geometry.py +47 -1
- opentrons/protocol_engine/state/labware.py +92 -26
- opentrons/protocol_engine/state/liquid_classes.py +82 -0
- opentrons/protocol_engine/state/liquids.py +16 -4
- opentrons/protocol_engine/state/modules.py +56 -71
- opentrons/protocol_engine/state/motion.py +6 -1
- opentrons/protocol_engine/state/pipettes.py +149 -58
- opentrons/protocol_engine/state/state.py +21 -2
- opentrons/protocol_engine/state/state_summary.py +4 -2
- opentrons/protocol_engine/state/tips.py +11 -44
- opentrons/protocol_engine/state/update_types.py +343 -48
- opentrons/protocol_engine/state/wells.py +19 -11
- opentrons/protocol_engine/types.py +176 -28
- opentrons/protocol_reader/extract_labware_definitions.py +5 -2
- opentrons/protocol_reader/file_format_validator.py +5 -5
- opentrons/protocol_runner/json_file_reader.py +9 -3
- opentrons/protocol_runner/json_translator.py +51 -25
- opentrons/protocol_runner/legacy_command_mapper.py +66 -64
- opentrons/protocol_runner/protocol_runner.py +35 -4
- opentrons/protocol_runner/python_protocol_wrappers.py +1 -1
- opentrons/protocol_runner/run_orchestrator.py +13 -3
- opentrons/protocols/advanced_control/common.py +38 -0
- opentrons/protocols/advanced_control/mix.py +1 -1
- opentrons/protocols/advanced_control/transfers/__init__.py +0 -0
- opentrons/protocols/advanced_control/transfers/common.py +56 -0
- opentrons/protocols/advanced_control/{transfers.py → transfers/transfer.py} +10 -85
- opentrons/protocols/api_support/definitions.py +1 -1
- opentrons/protocols/api_support/instrument.py +1 -1
- opentrons/protocols/api_support/util.py +10 -0
- opentrons/protocols/labware.py +70 -8
- opentrons/protocols/models/json_protocol.py +5 -9
- opentrons/simulate.py +3 -1
- opentrons/types.py +162 -2
- opentrons/util/entrypoint_util.py +2 -5
- opentrons/util/logging_config.py +1 -1
- {opentrons-8.2.0a3.dist-info → opentrons-8.3.0.dist-info}/METADATA +16 -15
- {opentrons-8.2.0a3.dist-info → opentrons-8.3.0.dist-info}/RECORD +238 -208
- {opentrons-8.2.0a3.dist-info → opentrons-8.3.0.dist-info}/WHEEL +1 -1
- {opentrons-8.2.0a3.dist-info → opentrons-8.3.0.dist-info}/LICENSE +0 -0
- {opentrons-8.2.0a3.dist-info → opentrons-8.3.0.dist-info}/entry_points.txt +0 -0
- {opentrons-8.2.0a3.dist-info → opentrons-8.3.0.dist-info}/top_level.txt +0 -0
|
@@ -6,14 +6,25 @@ from typing import Optional, Type, TYPE_CHECKING
|
|
|
6
6
|
from typing_extensions import Literal
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
from ..state import update_types
|
|
10
9
|
from ..types import DeckPoint
|
|
11
|
-
from .pipetting_common import PipetteIdMixin
|
|
12
|
-
from .
|
|
13
|
-
|
|
10
|
+
from .pipetting_common import PipetteIdMixin
|
|
11
|
+
from .movement_common import (
|
|
12
|
+
MovementMixin,
|
|
13
|
+
DestinationPositionResult,
|
|
14
|
+
move_to_coordinates,
|
|
15
|
+
StallOrCollisionError,
|
|
16
|
+
)
|
|
17
|
+
from .command import (
|
|
18
|
+
AbstractCommandImpl,
|
|
19
|
+
BaseCommand,
|
|
20
|
+
BaseCommandCreate,
|
|
21
|
+
SuccessData,
|
|
22
|
+
DefinedErrorData,
|
|
23
|
+
)
|
|
14
24
|
|
|
15
25
|
if TYPE_CHECKING:
|
|
16
26
|
from ..execution import MovementHandler
|
|
27
|
+
from ..resources.model_utils import ModelUtils
|
|
17
28
|
|
|
18
29
|
|
|
19
30
|
MoveToCoordinatesCommandType = Literal["moveToCoordinates"]
|
|
@@ -34,50 +45,53 @@ class MoveToCoordinatesResult(DestinationPositionResult):
|
|
|
34
45
|
pass
|
|
35
46
|
|
|
36
47
|
|
|
48
|
+
_ExecuteReturn = (
|
|
49
|
+
SuccessData[MoveToCoordinatesResult] | DefinedErrorData[StallOrCollisionError]
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
37
53
|
class MoveToCoordinatesImplementation(
|
|
38
|
-
AbstractCommandImpl[MoveToCoordinatesParams,
|
|
54
|
+
AbstractCommandImpl[MoveToCoordinatesParams, _ExecuteReturn]
|
|
39
55
|
):
|
|
40
56
|
"""Move to coordinates command implementation."""
|
|
41
57
|
|
|
42
58
|
def __init__(
|
|
43
59
|
self,
|
|
44
60
|
movement: MovementHandler,
|
|
61
|
+
model_utils: ModelUtils,
|
|
45
62
|
**kwargs: object,
|
|
46
63
|
) -> None:
|
|
47
64
|
self._movement = movement
|
|
65
|
+
self._model_utils = model_utils
|
|
48
66
|
|
|
49
|
-
async def execute(
|
|
50
|
-
self, params: MoveToCoordinatesParams
|
|
51
|
-
) -> SuccessData[MoveToCoordinatesResult]:
|
|
67
|
+
async def execute(self, params: MoveToCoordinatesParams) -> _ExecuteReturn:
|
|
52
68
|
"""Move the requested pipette to the requested coordinates."""
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
69
|
+
result = await move_to_coordinates(
|
|
70
|
+
movement=self._movement,
|
|
71
|
+
model_utils=self._model_utils,
|
|
56
72
|
pipette_id=params.pipetteId,
|
|
57
73
|
deck_coordinates=params.coordinates,
|
|
58
74
|
direct=params.forceDirect,
|
|
59
75
|
additional_min_travel_z=params.minimumZHeight,
|
|
60
76
|
speed=params.speed,
|
|
61
77
|
)
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
state_update=state_update,
|
|
70
|
-
)
|
|
78
|
+
if isinstance(result, DefinedErrorData):
|
|
79
|
+
return result
|
|
80
|
+
else:
|
|
81
|
+
return SuccessData(
|
|
82
|
+
public=MoveToCoordinatesResult(position=result.public.position),
|
|
83
|
+
state_update=result.state_update,
|
|
84
|
+
)
|
|
71
85
|
|
|
72
86
|
|
|
73
87
|
class MoveToCoordinates(
|
|
74
|
-
BaseCommand[MoveToCoordinatesParams, MoveToCoordinatesResult,
|
|
88
|
+
BaseCommand[MoveToCoordinatesParams, MoveToCoordinatesResult, StallOrCollisionError]
|
|
75
89
|
):
|
|
76
90
|
"""Move to well command model."""
|
|
77
91
|
|
|
78
92
|
commandType: MoveToCoordinatesCommandType = "moveToCoordinates"
|
|
79
93
|
params: MoveToCoordinatesParams
|
|
80
|
-
result: Optional[MoveToCoordinatesResult]
|
|
94
|
+
result: Optional[MoveToCoordinatesResult] = None
|
|
81
95
|
|
|
82
96
|
_ImplementationCls: Type[
|
|
83
97
|
MoveToCoordinatesImplementation
|
|
@@ -1,23 +1,32 @@
|
|
|
1
1
|
"""Move to well command request, result, and implementation models."""
|
|
2
|
+
|
|
2
3
|
from __future__ import annotations
|
|
3
4
|
from typing import TYPE_CHECKING, Optional, Type
|
|
4
5
|
from typing_extensions import Literal
|
|
5
6
|
|
|
6
|
-
from ..types import DeckPoint
|
|
7
7
|
from .pipetting_common import (
|
|
8
8
|
PipetteIdMixin,
|
|
9
|
+
)
|
|
10
|
+
from .movement_common import (
|
|
9
11
|
WellLocationMixin,
|
|
10
12
|
MovementMixin,
|
|
11
13
|
DestinationPositionResult,
|
|
14
|
+
StallOrCollisionError,
|
|
15
|
+
move_to_well,
|
|
16
|
+
)
|
|
17
|
+
from .command import (
|
|
18
|
+
AbstractCommandImpl,
|
|
19
|
+
BaseCommand,
|
|
20
|
+
BaseCommandCreate,
|
|
21
|
+
SuccessData,
|
|
22
|
+
DefinedErrorData,
|
|
12
23
|
)
|
|
13
|
-
from .command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
|
|
14
|
-
from ..errors.error_occurrence import ErrorOccurrence
|
|
15
|
-
from ..state import update_types
|
|
16
24
|
from ..errors import LabwareIsTipRackError
|
|
17
25
|
|
|
18
26
|
if TYPE_CHECKING:
|
|
19
27
|
from ..execution import MovementHandler
|
|
20
28
|
from ..state.state import StateView
|
|
29
|
+
from ..resources.model_utils import ModelUtils
|
|
21
30
|
|
|
22
31
|
MoveToWellCommandType = Literal["moveToWell"]
|
|
23
32
|
|
|
@@ -35,25 +44,33 @@ class MoveToWellResult(DestinationPositionResult):
|
|
|
35
44
|
|
|
36
45
|
|
|
37
46
|
class MoveToWellImplementation(
|
|
38
|
-
AbstractCommandImpl[
|
|
47
|
+
AbstractCommandImpl[
|
|
48
|
+
MoveToWellParams,
|
|
49
|
+
SuccessData[MoveToWellResult] | DefinedErrorData[StallOrCollisionError],
|
|
50
|
+
]
|
|
39
51
|
):
|
|
40
52
|
"""Move to well command implementation."""
|
|
41
53
|
|
|
42
54
|
def __init__(
|
|
43
|
-
self,
|
|
55
|
+
self,
|
|
56
|
+
state_view: StateView,
|
|
57
|
+
movement: MovementHandler,
|
|
58
|
+
model_utils: ModelUtils,
|
|
59
|
+
**kwargs: object,
|
|
44
60
|
) -> None:
|
|
45
61
|
self._state_view = state_view
|
|
46
62
|
self._movement = movement
|
|
63
|
+
self._model_utils = model_utils
|
|
47
64
|
|
|
48
|
-
async def execute(
|
|
65
|
+
async def execute(
|
|
66
|
+
self, params: MoveToWellParams
|
|
67
|
+
) -> SuccessData[MoveToWellResult] | DefinedErrorData[StallOrCollisionError]:
|
|
49
68
|
"""Move the requested pipette to the requested well."""
|
|
50
69
|
pipette_id = params.pipetteId
|
|
51
70
|
labware_id = params.labwareId
|
|
52
71
|
well_name = params.wellName
|
|
53
72
|
well_location = params.wellLocation
|
|
54
73
|
|
|
55
|
-
state_update = update_types.StateUpdate()
|
|
56
|
-
|
|
57
74
|
if (
|
|
58
75
|
self._state_view.labware.is_tiprack(labware_id)
|
|
59
76
|
and well_location.volumeOffset
|
|
@@ -62,7 +79,9 @@ class MoveToWellImplementation(
|
|
|
62
79
|
"Cannot specify a WellLocation with a volumeOffset with movement to a tip rack"
|
|
63
80
|
)
|
|
64
81
|
|
|
65
|
-
|
|
82
|
+
move_result = await move_to_well(
|
|
83
|
+
model_utils=self._model_utils,
|
|
84
|
+
movement=self._movement,
|
|
66
85
|
pipette_id=pipette_id,
|
|
67
86
|
labware_id=labware_id,
|
|
68
87
|
well_name=well_name,
|
|
@@ -71,26 +90,23 @@ class MoveToWellImplementation(
|
|
|
71
90
|
minimum_z_height=params.minimumZHeight,
|
|
72
91
|
speed=params.speed,
|
|
73
92
|
)
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
return SuccessData(
|
|
83
|
-
public=MoveToWellResult(position=deck_point),
|
|
84
|
-
state_update=state_update,
|
|
85
|
-
)
|
|
93
|
+
if isinstance(move_result, DefinedErrorData):
|
|
94
|
+
return move_result
|
|
95
|
+
else:
|
|
96
|
+
return SuccessData(
|
|
97
|
+
public=MoveToWellResult(position=move_result.public.position),
|
|
98
|
+
state_update=move_result.state_update,
|
|
99
|
+
)
|
|
86
100
|
|
|
87
101
|
|
|
88
|
-
class MoveToWell(
|
|
102
|
+
class MoveToWell(
|
|
103
|
+
BaseCommand[MoveToWellParams, MoveToWellResult, StallOrCollisionError]
|
|
104
|
+
):
|
|
89
105
|
"""Move to well command model."""
|
|
90
106
|
|
|
91
107
|
commandType: MoveToWellCommandType = "moveToWell"
|
|
92
108
|
params: MoveToWellParams
|
|
93
|
-
result: Optional[MoveToWellResult]
|
|
109
|
+
result: Optional[MoveToWellResult] = None
|
|
94
110
|
|
|
95
111
|
_ImplementationCls: Type[MoveToWellImplementation] = MoveToWellImplementation
|
|
96
112
|
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
"""Common movement base models."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Optional, Union, TYPE_CHECKING, Literal, Any
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel, Field
|
|
8
|
+
from pydantic.json_schema import SkipJsonSchema
|
|
9
|
+
|
|
10
|
+
from opentrons_shared_data.errors import ErrorCodes
|
|
11
|
+
from opentrons_shared_data.errors.exceptions import StallOrCollisionDetectedError
|
|
12
|
+
from ..errors import ErrorOccurrence
|
|
13
|
+
from ..types import (
|
|
14
|
+
WellLocation,
|
|
15
|
+
LiquidHandlingWellLocation,
|
|
16
|
+
DeckPoint,
|
|
17
|
+
CurrentWell,
|
|
18
|
+
MovementAxis,
|
|
19
|
+
AddressableOffsetVector,
|
|
20
|
+
)
|
|
21
|
+
from ..state.update_types import StateUpdate, PipetteLocationUpdate
|
|
22
|
+
from .command import SuccessData, DefinedErrorData
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
if TYPE_CHECKING:
|
|
26
|
+
from ..execution.movement import MovementHandler
|
|
27
|
+
from ..resources.model_utils import ModelUtils
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _remove_default(s: dict[str, Any]) -> None:
|
|
31
|
+
s.pop("default", None)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class WellLocationMixin(BaseModel):
|
|
35
|
+
"""Mixin for command requests that take a location that's somewhere in a well."""
|
|
36
|
+
|
|
37
|
+
labwareId: str = Field(
|
|
38
|
+
...,
|
|
39
|
+
description="Identifier of labware to use.",
|
|
40
|
+
)
|
|
41
|
+
wellName: str = Field(
|
|
42
|
+
...,
|
|
43
|
+
description="Name of well to use in labware.",
|
|
44
|
+
)
|
|
45
|
+
wellLocation: WellLocation = Field(
|
|
46
|
+
default_factory=WellLocation,
|
|
47
|
+
description="Relative well location at which to perform the operation",
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class LiquidHandlingWellLocationMixin(BaseModel):
|
|
52
|
+
"""Mixin for command requests that take a location that's somewhere in a well."""
|
|
53
|
+
|
|
54
|
+
labwareId: str = Field(
|
|
55
|
+
...,
|
|
56
|
+
description="Identifier of labware to use.",
|
|
57
|
+
)
|
|
58
|
+
wellName: str = Field(
|
|
59
|
+
...,
|
|
60
|
+
description="Name of well to use in labware.",
|
|
61
|
+
)
|
|
62
|
+
wellLocation: LiquidHandlingWellLocation = Field(
|
|
63
|
+
default_factory=LiquidHandlingWellLocation,
|
|
64
|
+
description="Relative well location at which to perform the operation",
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class MovementMixin(BaseModel):
|
|
69
|
+
"""Mixin for command requests that move a pipette."""
|
|
70
|
+
|
|
71
|
+
minimumZHeight: float | SkipJsonSchema[None] = Field(
|
|
72
|
+
None,
|
|
73
|
+
description=(
|
|
74
|
+
"Optional minimal Z margin in mm."
|
|
75
|
+
" If this is larger than the API's default safe Z margin,"
|
|
76
|
+
" it will make the arc higher. If it's smaller, it will have no effect."
|
|
77
|
+
),
|
|
78
|
+
json_schema_extra=_remove_default,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
forceDirect: bool = Field(
|
|
82
|
+
False,
|
|
83
|
+
description=(
|
|
84
|
+
"If true, moving from one labware/well to another"
|
|
85
|
+
" will not arc to the default safe z,"
|
|
86
|
+
" but instead will move directly to the specified location."
|
|
87
|
+
" This will also force the `minimumZHeight` param to be ignored."
|
|
88
|
+
" A 'direct' movement is in X/Y/Z simultaneously."
|
|
89
|
+
),
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
speed: float | SkipJsonSchema[None] = Field(
|
|
93
|
+
None,
|
|
94
|
+
description=(
|
|
95
|
+
"Override the travel speed in mm/s."
|
|
96
|
+
" This controls the straight linear speed of motion."
|
|
97
|
+
),
|
|
98
|
+
json_schema_extra=_remove_default,
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class StallOrCollisionError(ErrorOccurrence):
|
|
103
|
+
"""Returned when the machine detects that axis encoders are reading a different position than expected.
|
|
104
|
+
|
|
105
|
+
All axes are stopped at the point where the error was encountered.
|
|
106
|
+
|
|
107
|
+
The next thing to move the machine must account for the robot not having a valid estimate
|
|
108
|
+
of its position. It should be a `home` or `unsafe/updatePositionEstimators`.
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
isDefined: bool = True
|
|
112
|
+
errorType: Literal["stallOrCollision"] = "stallOrCollision"
|
|
113
|
+
|
|
114
|
+
errorCode: str = ErrorCodes.STALL_OR_COLLISION_DETECTED.value.code
|
|
115
|
+
detail: str = ErrorCodes.STALL_OR_COLLISION_DETECTED.value.detail
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class DestinationPositionResult(BaseModel):
|
|
119
|
+
"""Mixin for command results that move a pipette."""
|
|
120
|
+
|
|
121
|
+
# todo(mm, 2024-08-02): Consider deprecating or redefining this.
|
|
122
|
+
#
|
|
123
|
+
# This is here because opentrons.protocol_engine needed it for internal bookkeeping
|
|
124
|
+
# and, at the time, we didn't have a way to do that without adding this to the
|
|
125
|
+
# public command results. Its usefulness to callers outside
|
|
126
|
+
# opentrons.protocol_engine is questionable because they would need to know which
|
|
127
|
+
# critical point is in play, and I think that can change depending on obscure
|
|
128
|
+
# things like labware quirks.
|
|
129
|
+
position: DeckPoint = Field(
|
|
130
|
+
DeckPoint(x=0, y=0, z=0),
|
|
131
|
+
description=(
|
|
132
|
+
"The (x,y,z) coordinates of the pipette's critical point in deck space"
|
|
133
|
+
" after the move was completed."
|
|
134
|
+
),
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
MoveToWellOperationReturn = (
|
|
139
|
+
SuccessData[DestinationPositionResult] | DefinedErrorData[StallOrCollisionError]
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
async def move_to_well(
|
|
144
|
+
movement: MovementHandler,
|
|
145
|
+
model_utils: ModelUtils,
|
|
146
|
+
pipette_id: str,
|
|
147
|
+
labware_id: str,
|
|
148
|
+
well_name: str,
|
|
149
|
+
well_location: Optional[Union[WellLocation, LiquidHandlingWellLocation]] = None,
|
|
150
|
+
current_well: Optional[CurrentWell] = None,
|
|
151
|
+
force_direct: bool = False,
|
|
152
|
+
minimum_z_height: Optional[float] = None,
|
|
153
|
+
speed: Optional[float] = None,
|
|
154
|
+
operation_volume: Optional[float] = None,
|
|
155
|
+
) -> MoveToWellOperationReturn:
|
|
156
|
+
"""Execute a move to well microoperation."""
|
|
157
|
+
try:
|
|
158
|
+
position = await movement.move_to_well(
|
|
159
|
+
pipette_id=pipette_id,
|
|
160
|
+
labware_id=labware_id,
|
|
161
|
+
well_name=well_name,
|
|
162
|
+
well_location=well_location,
|
|
163
|
+
current_well=current_well,
|
|
164
|
+
force_direct=force_direct,
|
|
165
|
+
minimum_z_height=minimum_z_height,
|
|
166
|
+
speed=speed,
|
|
167
|
+
operation_volume=operation_volume,
|
|
168
|
+
)
|
|
169
|
+
except StallOrCollisionDetectedError as e:
|
|
170
|
+
return DefinedErrorData(
|
|
171
|
+
public=StallOrCollisionError(
|
|
172
|
+
id=model_utils.generate_id(),
|
|
173
|
+
createdAt=model_utils.get_timestamp(),
|
|
174
|
+
wrappedErrors=[
|
|
175
|
+
ErrorOccurrence.from_failed(
|
|
176
|
+
id=model_utils.generate_id(),
|
|
177
|
+
createdAt=model_utils.get_timestamp(),
|
|
178
|
+
error=e,
|
|
179
|
+
)
|
|
180
|
+
],
|
|
181
|
+
),
|
|
182
|
+
state_update=StateUpdate().clear_all_pipette_locations(),
|
|
183
|
+
)
|
|
184
|
+
else:
|
|
185
|
+
deck_point = DeckPoint.model_construct(x=position.x, y=position.y, z=position.z)
|
|
186
|
+
return SuccessData(
|
|
187
|
+
public=DestinationPositionResult(
|
|
188
|
+
position=deck_point,
|
|
189
|
+
),
|
|
190
|
+
state_update=StateUpdate().set_pipette_location(
|
|
191
|
+
pipette_id=pipette_id,
|
|
192
|
+
new_labware_id=labware_id,
|
|
193
|
+
new_well_name=well_name,
|
|
194
|
+
new_deck_point=deck_point,
|
|
195
|
+
),
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
async def move_relative(
|
|
200
|
+
movement: MovementHandler,
|
|
201
|
+
model_utils: ModelUtils,
|
|
202
|
+
pipette_id: str,
|
|
203
|
+
axis: MovementAxis,
|
|
204
|
+
distance: float,
|
|
205
|
+
) -> SuccessData[DestinationPositionResult] | DefinedErrorData[StallOrCollisionError]:
|
|
206
|
+
"""Move by a fixed displacement from the current position."""
|
|
207
|
+
try:
|
|
208
|
+
position = await movement.move_relative(pipette_id, axis, distance)
|
|
209
|
+
except StallOrCollisionDetectedError as e:
|
|
210
|
+
return DefinedErrorData(
|
|
211
|
+
public=StallOrCollisionError(
|
|
212
|
+
id=model_utils.generate_id(),
|
|
213
|
+
createdAt=model_utils.get_timestamp(),
|
|
214
|
+
wrappedErrors=[
|
|
215
|
+
ErrorOccurrence.from_failed(
|
|
216
|
+
id=model_utils.generate_id(),
|
|
217
|
+
createdAt=model_utils.get_timestamp(),
|
|
218
|
+
error=e,
|
|
219
|
+
)
|
|
220
|
+
],
|
|
221
|
+
),
|
|
222
|
+
state_update=StateUpdate().clear_all_pipette_locations(),
|
|
223
|
+
)
|
|
224
|
+
else:
|
|
225
|
+
deck_point = DeckPoint.model_construct(x=position.x, y=position.y, z=position.z)
|
|
226
|
+
return SuccessData(
|
|
227
|
+
public=DestinationPositionResult(
|
|
228
|
+
position=deck_point,
|
|
229
|
+
),
|
|
230
|
+
state_update=StateUpdate().set_pipette_location(
|
|
231
|
+
pipette_id=pipette_id, new_deck_point=deck_point
|
|
232
|
+
),
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
async def move_to_addressable_area(
|
|
237
|
+
movement: MovementHandler,
|
|
238
|
+
model_utils: ModelUtils,
|
|
239
|
+
pipette_id: str,
|
|
240
|
+
addressable_area_name: str,
|
|
241
|
+
offset: AddressableOffsetVector,
|
|
242
|
+
force_direct: bool = False,
|
|
243
|
+
minimum_z_height: float | None = None,
|
|
244
|
+
speed: float | None = None,
|
|
245
|
+
stay_at_highest_possible_z: bool = False,
|
|
246
|
+
ignore_tip_configuration: bool | None = True,
|
|
247
|
+
highest_possible_z_extra_offset: float | None = None,
|
|
248
|
+
) -> SuccessData[DestinationPositionResult] | DefinedErrorData[StallOrCollisionError]:
|
|
249
|
+
"""Move to an addressable area identified by name."""
|
|
250
|
+
try:
|
|
251
|
+
x, y, z = await movement.move_to_addressable_area(
|
|
252
|
+
pipette_id=pipette_id,
|
|
253
|
+
addressable_area_name=addressable_area_name,
|
|
254
|
+
offset=offset,
|
|
255
|
+
force_direct=force_direct,
|
|
256
|
+
minimum_z_height=minimum_z_height,
|
|
257
|
+
speed=speed,
|
|
258
|
+
stay_at_highest_possible_z=stay_at_highest_possible_z,
|
|
259
|
+
ignore_tip_configuration=ignore_tip_configuration,
|
|
260
|
+
highest_possible_z_extra_offset=highest_possible_z_extra_offset,
|
|
261
|
+
)
|
|
262
|
+
except StallOrCollisionDetectedError as e:
|
|
263
|
+
return DefinedErrorData(
|
|
264
|
+
public=StallOrCollisionError(
|
|
265
|
+
id=model_utils.generate_id(),
|
|
266
|
+
createdAt=model_utils.get_timestamp(),
|
|
267
|
+
wrappedErrors=[
|
|
268
|
+
ErrorOccurrence.from_failed(
|
|
269
|
+
id=model_utils.generate_id(),
|
|
270
|
+
createdAt=model_utils.get_timestamp(),
|
|
271
|
+
error=e,
|
|
272
|
+
)
|
|
273
|
+
],
|
|
274
|
+
),
|
|
275
|
+
state_update=StateUpdate()
|
|
276
|
+
.clear_all_pipette_locations()
|
|
277
|
+
.set_addressable_area_used(addressable_area_name=addressable_area_name),
|
|
278
|
+
)
|
|
279
|
+
else:
|
|
280
|
+
deck_point = DeckPoint.model_construct(x=x, y=y, z=z)
|
|
281
|
+
return SuccessData(
|
|
282
|
+
public=DestinationPositionResult(position=deck_point),
|
|
283
|
+
state_update=StateUpdate()
|
|
284
|
+
.set_pipette_location(
|
|
285
|
+
pipette_id=pipette_id,
|
|
286
|
+
new_addressable_area_name=addressable_area_name,
|
|
287
|
+
new_deck_point=deck_point,
|
|
288
|
+
)
|
|
289
|
+
.set_addressable_area_used(addressable_area_name=addressable_area_name),
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
async def move_to_coordinates(
|
|
294
|
+
movement: MovementHandler,
|
|
295
|
+
model_utils: ModelUtils,
|
|
296
|
+
pipette_id: str,
|
|
297
|
+
deck_coordinates: DeckPoint,
|
|
298
|
+
direct: bool,
|
|
299
|
+
additional_min_travel_z: float | None,
|
|
300
|
+
speed: float | None = None,
|
|
301
|
+
) -> SuccessData[DestinationPositionResult] | DefinedErrorData[StallOrCollisionError]:
|
|
302
|
+
"""Move to a set of coordinates."""
|
|
303
|
+
try:
|
|
304
|
+
x, y, z = await movement.move_to_coordinates(
|
|
305
|
+
pipette_id=pipette_id,
|
|
306
|
+
deck_coordinates=deck_coordinates,
|
|
307
|
+
direct=direct,
|
|
308
|
+
additional_min_travel_z=additional_min_travel_z,
|
|
309
|
+
speed=speed,
|
|
310
|
+
)
|
|
311
|
+
except StallOrCollisionDetectedError as e:
|
|
312
|
+
return DefinedErrorData(
|
|
313
|
+
public=StallOrCollisionError(
|
|
314
|
+
id=model_utils.generate_id(),
|
|
315
|
+
createdAt=model_utils.get_timestamp(),
|
|
316
|
+
wrappedErrors=[
|
|
317
|
+
ErrorOccurrence.from_failed(
|
|
318
|
+
id=model_utils.generate_id(),
|
|
319
|
+
createdAt=model_utils.get_timestamp(),
|
|
320
|
+
error=e,
|
|
321
|
+
)
|
|
322
|
+
],
|
|
323
|
+
),
|
|
324
|
+
state_update=StateUpdate().clear_all_pipette_locations(),
|
|
325
|
+
)
|
|
326
|
+
else:
|
|
327
|
+
deck_point = DeckPoint.model_construct(x=x, y=y, z=z)
|
|
328
|
+
|
|
329
|
+
return SuccessData(
|
|
330
|
+
public=DestinationPositionResult(position=DeckPoint(x=x, y=y, z=z)),
|
|
331
|
+
state_update=StateUpdate(
|
|
332
|
+
pipette_location=PipetteLocationUpdate(
|
|
333
|
+
pipette_id=pipette_id,
|
|
334
|
+
new_location=None,
|
|
335
|
+
new_deck_point=deck_point,
|
|
336
|
+
)
|
|
337
|
+
),
|
|
338
|
+
)
|