opentrons 8.2.0a4__py2.py3-none-any.whl → 8.3.0a1__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.
Potentially problematic release.
This version of opentrons might be problematic. Click here for more details.
- 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 +28 -20
- 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 +60 -23
- 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 +18 -0
- opentrons/hardware_control/protocols/motion_controller.py +6 -0
- opentrons/hardware_control/robot_calibration.py +1 -1
- opentrons/hardware_control/types.py +61 -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 +82 -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 +46 -4
- opentrons/protocol_api/core/labware.py +13 -4
- opentrons/protocol_api/core/legacy/legacy_instrument_core.py +37 -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 +37 -3
- opentrons/protocol_api/core/protocol.py +34 -1
- opentrons/protocol_api/core/robot.py +51 -0
- opentrons/protocol_api/instrument_context.py +158 -44
- opentrons/protocol_api/labware.py +231 -7
- 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 +81 -0
- opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +0 -2
- opentrons/protocol_engine/commands/absorbance_reader/initialize.py +19 -5
- opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +0 -1
- opentrons/protocol_engine/commands/absorbance_reader/read.py +32 -9
- 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 +101 -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 +13 -3
- 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 +67 -24
- opentrons/protocol_engine/commands/load_labware.py +29 -7
- 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 +19 -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 +8 -2
- 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 +4 -1
- opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +12 -3
- opentrons/protocol_engine/commands/unsafe/unsafe_engage_axes.py +1 -4
- opentrons/protocol_engine/commands/unsafe/update_position_estimators.py +1 -4
- 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 +8 -0
- opentrons/protocol_engine/errors/error_occurrence.py +19 -20
- opentrons/protocol_engine/errors/exceptions.py +50 -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 +364 -8
- opentrons/protocol_engine/execution/movement.py +27 -0
- opentrons/protocol_engine/execution/pipetting.py +5 -1
- opentrons/protocol_engine/execution/tip_handler.py +4 -6
- 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 +5 -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 +17 -13
- opentrons/protocol_engine/state/files.py +10 -12
- opentrons/protocol_engine/state/fluid_stack.py +138 -0
- opentrons/protocol_engine/state/frustum_helpers.py +57 -32
- opentrons/protocol_engine/state/geometry.py +47 -1
- opentrons/protocol_engine/state/labware.py +79 -25
- opentrons/protocol_engine/state/liquid_classes.py +82 -0
- opentrons/protocol_engine/state/liquids.py +16 -4
- opentrons/protocol_engine/state/modules.py +52 -70
- opentrons/protocol_engine/state/motion.py +6 -1
- opentrons/protocol_engine/state/pipettes.py +144 -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 +39 -6
- opentrons/protocols/models/json_protocol.py +5 -9
- opentrons/simulate.py +3 -1
- opentrons/types.py +162 -2
- opentrons/util/logging_config.py +1 -1
- {opentrons-8.2.0a4.dist-info → opentrons-8.3.0a1.dist-info}/METADATA +16 -15
- {opentrons-8.2.0a4.dist-info → opentrons-8.3.0a1.dist-info}/RECORD +229 -202
- {opentrons-8.2.0a4.dist-info → opentrons-8.3.0a1.dist-info}/WHEEL +1 -1
- {opentrons-8.2.0a4.dist-info → opentrons-8.3.0a1.dist-info}/LICENSE +0 -0
- {opentrons-8.2.0a4.dist-info → opentrons-8.3.0a1.dist-info}/entry_points.txt +0 -0
- {opentrons-8.2.0a4.dist-info → opentrons-8.3.0a1.dist-info}/top_level.txt +0 -0
|
@@ -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
|
+
)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""Pick up tip command request, result, and implementation models."""
|
|
2
|
+
|
|
2
3
|
from __future__ import annotations
|
|
3
4
|
from opentrons_shared_data.errors import ErrorCodes
|
|
4
5
|
from pydantic import Field
|
|
@@ -9,10 +10,14 @@ from typing_extensions import Literal
|
|
|
9
10
|
from ..errors import ErrorOccurrence, PickUpTipTipNotAttachedError
|
|
10
11
|
from ..resources import ModelUtils
|
|
11
12
|
from ..state import update_types
|
|
12
|
-
from ..types import PickUpTipWellLocation
|
|
13
|
+
from ..types import PickUpTipWellLocation
|
|
13
14
|
from .pipetting_common import (
|
|
14
15
|
PipetteIdMixin,
|
|
16
|
+
)
|
|
17
|
+
from .movement_common import (
|
|
15
18
|
DestinationPositionResult,
|
|
19
|
+
StallOrCollisionError,
|
|
20
|
+
move_to_well,
|
|
16
21
|
)
|
|
17
22
|
from .command import (
|
|
18
23
|
AbstractCommandImpl,
|
|
@@ -87,7 +92,8 @@ class TipPhysicallyMissingError(ErrorOccurrence):
|
|
|
87
92
|
|
|
88
93
|
_ExecuteReturn = Union[
|
|
89
94
|
SuccessData[PickUpTipResult],
|
|
90
|
-
DefinedErrorData[TipPhysicallyMissingError]
|
|
95
|
+
DefinedErrorData[TipPhysicallyMissingError]
|
|
96
|
+
| DefinedErrorData[StallOrCollisionError],
|
|
91
97
|
]
|
|
92
98
|
|
|
93
99
|
|
|
@@ -115,24 +121,19 @@ class PickUpTipImplementation(AbstractCommandImpl[PickUpTipParams, _ExecuteRetur
|
|
|
115
121
|
labware_id = params.labwareId
|
|
116
122
|
well_name = params.wellName
|
|
117
123
|
|
|
118
|
-
state_update = update_types.StateUpdate()
|
|
119
|
-
|
|
120
124
|
well_location = self._state_view.geometry.convert_pick_up_tip_well_location(
|
|
121
125
|
well_location=params.wellLocation
|
|
122
126
|
)
|
|
123
|
-
|
|
127
|
+
move_result = await move_to_well(
|
|
128
|
+
movement=self._movement,
|
|
129
|
+
model_utils=self._model_utils,
|
|
124
130
|
pipette_id=pipette_id,
|
|
125
131
|
labware_id=labware_id,
|
|
126
132
|
well_name=well_name,
|
|
127
133
|
well_location=well_location,
|
|
128
134
|
)
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
pipette_id=pipette_id,
|
|
132
|
-
new_labware_id=labware_id,
|
|
133
|
-
new_well_name=well_name,
|
|
134
|
-
new_deck_point=deck_point,
|
|
135
|
-
)
|
|
135
|
+
if isinstance(move_result, DefinedErrorData):
|
|
136
|
+
return move_result
|
|
136
137
|
|
|
137
138
|
try:
|
|
138
139
|
tip_geometry = await self._tip_handler.pick_up_tip(
|
|
@@ -141,13 +142,27 @@ class PickUpTipImplementation(AbstractCommandImpl[PickUpTipParams, _ExecuteRetur
|
|
|
141
142
|
well_name=well_name,
|
|
142
143
|
)
|
|
143
144
|
except PickUpTipTipNotAttachedError as e:
|
|
144
|
-
state_update_if_false_positive =
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
145
|
+
state_update_if_false_positive = (
|
|
146
|
+
update_types.StateUpdate.reduce(
|
|
147
|
+
update_types.StateUpdate(), move_result.state_update
|
|
148
|
+
)
|
|
149
|
+
.update_pipette_tip_state(
|
|
150
|
+
pipette_id=pipette_id,
|
|
151
|
+
tip_geometry=e.tip_geometry,
|
|
152
|
+
)
|
|
153
|
+
.set_fluid_empty(pipette_id=pipette_id)
|
|
154
|
+
.mark_tips_as_used(
|
|
155
|
+
pipette_id=pipette_id, labware_id=labware_id, well_name=well_name
|
|
156
|
+
)
|
|
148
157
|
)
|
|
149
|
-
state_update
|
|
150
|
-
|
|
158
|
+
state_update = (
|
|
159
|
+
update_types.StateUpdate.reduce(
|
|
160
|
+
update_types.StateUpdate(), move_result.state_update
|
|
161
|
+
)
|
|
162
|
+
.mark_tips_as_used(
|
|
163
|
+
pipette_id=pipette_id, labware_id=labware_id, well_name=well_name
|
|
164
|
+
)
|
|
165
|
+
.set_fluid_unknown(pipette_id=pipette_id)
|
|
151
166
|
)
|
|
152
167
|
return DefinedErrorData(
|
|
153
168
|
public=TipPhysicallyMissingError(
|
|
@@ -165,32 +180,39 @@ class PickUpTipImplementation(AbstractCommandImpl[PickUpTipParams, _ExecuteRetur
|
|
|
165
180
|
state_update_if_false_positive=state_update_if_false_positive,
|
|
166
181
|
)
|
|
167
182
|
else:
|
|
168
|
-
state_update
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
183
|
+
state_update = (
|
|
184
|
+
move_result.state_update.update_pipette_tip_state(
|
|
185
|
+
pipette_id=pipette_id,
|
|
186
|
+
tip_geometry=tip_geometry,
|
|
187
|
+
)
|
|
188
|
+
.mark_tips_as_used(
|
|
189
|
+
pipette_id=pipette_id, labware_id=labware_id, well_name=well_name
|
|
190
|
+
)
|
|
191
|
+
.set_fluid_empty(pipette_id=pipette_id)
|
|
174
192
|
)
|
|
175
193
|
return SuccessData(
|
|
176
194
|
public=PickUpTipResult(
|
|
177
195
|
tipVolume=tip_geometry.volume,
|
|
178
196
|
tipLength=tip_geometry.length,
|
|
179
197
|
tipDiameter=tip_geometry.diameter,
|
|
180
|
-
position=
|
|
198
|
+
position=move_result.public.position,
|
|
181
199
|
),
|
|
182
200
|
state_update=state_update,
|
|
183
201
|
)
|
|
184
202
|
|
|
185
203
|
|
|
186
204
|
class PickUpTip(
|
|
187
|
-
BaseCommand[
|
|
205
|
+
BaseCommand[
|
|
206
|
+
PickUpTipParams,
|
|
207
|
+
PickUpTipResult,
|
|
208
|
+
TipPhysicallyMissingError | StallOrCollisionError,
|
|
209
|
+
]
|
|
188
210
|
):
|
|
189
211
|
"""Pick up tip command model."""
|
|
190
212
|
|
|
191
213
|
commandType: PickUpTipCommandType = "pickUpTip"
|
|
192
214
|
params: PickUpTipParams
|
|
193
|
-
result: Optional[PickUpTipResult]
|
|
215
|
+
result: Optional[PickUpTipResult] = None
|
|
194
216
|
|
|
195
217
|
_ImplementationCls: Type[PickUpTipImplementation] = PickUpTipImplementation
|
|
196
218
|
|