opentrons 8.2.0a4__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 +36 -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 +1 -1
- 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 +52 -70
- 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.0a4.dist-info → opentrons-8.3.0.dist-info}/METADATA +16 -15
- {opentrons-8.2.0a4.dist-info → opentrons-8.3.0.dist-info}/RECORD +238 -208
- {opentrons-8.2.0a4.dist-info → opentrons-8.3.0.dist-info}/WHEEL +1 -1
- {opentrons-8.2.0a4.dist-info → opentrons-8.3.0.dist-info}/LICENSE +0 -0
- {opentrons-8.2.0a4.dist-info → opentrons-8.3.0.dist-info}/entry_points.txt +0 -0
- {opentrons-8.2.0a4.dist-info → opentrons-8.3.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
"""Seal evotip resin tip command request, result, and implementation models."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
from pydantic import Field, BaseModel
|
|
5
|
+
from typing import TYPE_CHECKING, Optional, Type, Union
|
|
6
|
+
from opentrons.types import MountType
|
|
7
|
+
from opentrons.protocol_engine.types import MotorAxis
|
|
8
|
+
from typing_extensions import Literal
|
|
9
|
+
|
|
10
|
+
from opentrons.protocol_engine.errors import UnsupportedLabwareForActionError
|
|
11
|
+
from ..resources import ModelUtils, labware_validation
|
|
12
|
+
from ..types import PickUpTipWellLocation, FluidKind, AspiratedFluid
|
|
13
|
+
from .pipetting_common import (
|
|
14
|
+
PipetteIdMixin,
|
|
15
|
+
)
|
|
16
|
+
from .movement_common import (
|
|
17
|
+
DestinationPositionResult,
|
|
18
|
+
StallOrCollisionError,
|
|
19
|
+
move_to_well,
|
|
20
|
+
)
|
|
21
|
+
from .command import (
|
|
22
|
+
AbstractCommandImpl,
|
|
23
|
+
BaseCommand,
|
|
24
|
+
BaseCommandCreate,
|
|
25
|
+
DefinedErrorData,
|
|
26
|
+
SuccessData,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
from opentrons.hardware_control import HardwareControlAPI
|
|
30
|
+
from opentrons.hardware_control.types import Axis
|
|
31
|
+
from ..state.update_types import StateUpdate
|
|
32
|
+
|
|
33
|
+
if TYPE_CHECKING:
|
|
34
|
+
from ..state.state import StateView
|
|
35
|
+
from ..execution import (
|
|
36
|
+
MovementHandler,
|
|
37
|
+
TipHandler,
|
|
38
|
+
GantryMover,
|
|
39
|
+
PipettingHandler,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
EvotipSealPipetteCommandType = Literal["evotipSealPipette"]
|
|
44
|
+
_PREP_DISTANCE_DEFAULT = 8.25
|
|
45
|
+
_PRESS_DISTANCE_DEFAULT = 3.5
|
|
46
|
+
_EJECTOR_PUSH_MM_DEFAULT = 7.0
|
|
47
|
+
_SAFE_TOP_VOLUME = 400
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class TipPickUpParams(BaseModel):
|
|
51
|
+
"""Payload used to specify press-tip parameters for a seal command."""
|
|
52
|
+
|
|
53
|
+
prepDistance: float = Field(
|
|
54
|
+
default=0, description="The distance to move down to fit the tips on."
|
|
55
|
+
)
|
|
56
|
+
pressDistance: float = Field(
|
|
57
|
+
default=0, description="The distance to press on tips."
|
|
58
|
+
)
|
|
59
|
+
ejectorPushMm: float = Field(
|
|
60
|
+
default=0,
|
|
61
|
+
description="The distance to back off to ensure that the tip presence sensors are not triggered.",
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class EvotipSealPipetteParams(PipetteIdMixin):
|
|
66
|
+
"""Payload needed to seal resin tips to a pipette."""
|
|
67
|
+
|
|
68
|
+
labwareId: str = Field(..., description="Identifier of labware to use.")
|
|
69
|
+
wellName: str = Field(..., description="Name of well to use in labware.")
|
|
70
|
+
wellLocation: PickUpTipWellLocation = Field(
|
|
71
|
+
default_factory=PickUpTipWellLocation,
|
|
72
|
+
description="Relative well location at which to pick up the tip.",
|
|
73
|
+
)
|
|
74
|
+
tipPickUpParams: Optional[TipPickUpParams] = Field(
|
|
75
|
+
default=None, description="Specific parameters for "
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class EvotipSealPipetteResult(DestinationPositionResult):
|
|
80
|
+
"""Result data from the execution of a EvotipSealPipette."""
|
|
81
|
+
|
|
82
|
+
tipVolume: float = Field(
|
|
83
|
+
0,
|
|
84
|
+
description="Maximum volume of liquid that the picked up tip can hold, in µL.",
|
|
85
|
+
ge=0,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
tipLength: float = Field(
|
|
89
|
+
0,
|
|
90
|
+
description="The length of the tip in mm.",
|
|
91
|
+
ge=0,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
tipDiameter: float = Field(
|
|
95
|
+
0,
|
|
96
|
+
description="The diameter of the tip in mm.",
|
|
97
|
+
ge=0,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
_ExecuteReturn = Union[
|
|
102
|
+
SuccessData[EvotipSealPipetteResult],
|
|
103
|
+
DefinedErrorData[StallOrCollisionError],
|
|
104
|
+
]
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class EvotipSealPipetteImplementation(
|
|
108
|
+
AbstractCommandImpl[EvotipSealPipetteParams, _ExecuteReturn]
|
|
109
|
+
):
|
|
110
|
+
"""Evotip seal pipette command implementation."""
|
|
111
|
+
|
|
112
|
+
def __init__(
|
|
113
|
+
self,
|
|
114
|
+
state_view: StateView,
|
|
115
|
+
tip_handler: TipHandler,
|
|
116
|
+
model_utils: ModelUtils,
|
|
117
|
+
movement: MovementHandler,
|
|
118
|
+
hardware_api: HardwareControlAPI,
|
|
119
|
+
gantry_mover: GantryMover,
|
|
120
|
+
pipetting: PipettingHandler,
|
|
121
|
+
**kwargs: object,
|
|
122
|
+
) -> None:
|
|
123
|
+
self._state_view = state_view
|
|
124
|
+
self._tip_handler = tip_handler
|
|
125
|
+
self._model_utils = model_utils
|
|
126
|
+
self._movement = movement
|
|
127
|
+
self._gantry_mover = gantry_mover
|
|
128
|
+
self._pipetting = pipetting
|
|
129
|
+
self._hardware_api = hardware_api
|
|
130
|
+
|
|
131
|
+
async def relative_pickup_tip(
|
|
132
|
+
self,
|
|
133
|
+
tip_pick_up_params: TipPickUpParams,
|
|
134
|
+
mount: MountType,
|
|
135
|
+
) -> None:
|
|
136
|
+
"""A relative press-fit pick up command using gantry moves."""
|
|
137
|
+
prep_distance = tip_pick_up_params.prepDistance
|
|
138
|
+
press_distance = tip_pick_up_params.pressDistance
|
|
139
|
+
retract_distance = -1 * (prep_distance + press_distance)
|
|
140
|
+
|
|
141
|
+
mount_axis = MotorAxis.LEFT_Z if mount == MountType.LEFT else MotorAxis.RIGHT_Z
|
|
142
|
+
|
|
143
|
+
# TODO chb, 2025-01-29): Factor out the movement constants and relocate this logic into the hardware controller
|
|
144
|
+
await self._gantry_mover.move_axes(
|
|
145
|
+
axis_map={mount_axis: prep_distance}, speed=10, relative_move=True
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
# Drive mount down for press-fit
|
|
149
|
+
await self._gantry_mover.move_axes(
|
|
150
|
+
axis_map={mount_axis: press_distance},
|
|
151
|
+
speed=10.0,
|
|
152
|
+
relative_move=True,
|
|
153
|
+
expect_stalls=True,
|
|
154
|
+
)
|
|
155
|
+
# retract cam : 11.05
|
|
156
|
+
await self._gantry_mover.move_axes(
|
|
157
|
+
axis_map={mount_axis: retract_distance}, speed=5.5, relative_move=True
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
async def cam_action_relative_pickup_tip(
|
|
161
|
+
self,
|
|
162
|
+
tip_pick_up_params: TipPickUpParams,
|
|
163
|
+
mount: MountType,
|
|
164
|
+
) -> None:
|
|
165
|
+
"""A cam action pick up command using gantry moves."""
|
|
166
|
+
prep_distance = tip_pick_up_params.prepDistance
|
|
167
|
+
press_distance = tip_pick_up_params.pressDistance
|
|
168
|
+
ejector_push_mm = tip_pick_up_params.ejectorPushMm
|
|
169
|
+
retract_distance = -1 * (prep_distance + press_distance)
|
|
170
|
+
|
|
171
|
+
mount_axis = MotorAxis.LEFT_Z if mount == MountType.LEFT else MotorAxis.RIGHT_Z
|
|
172
|
+
|
|
173
|
+
# TODO chb, 2025-01-29): Factor out the movement constants and relocate this logic into the hardware controller
|
|
174
|
+
await self._gantry_mover.move_axes(
|
|
175
|
+
axis_map={mount_axis: -6}, speed=10, relative_move=True
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
# Drive Q down 3mm at fast speed - look into the pick up tip fuinction to find slow and fast: 10.0
|
|
179
|
+
await self._gantry_mover.move_axes(
|
|
180
|
+
axis_map={MotorAxis.AXIS_96_CHANNEL_CAM: prep_distance},
|
|
181
|
+
speed=10.0,
|
|
182
|
+
relative_move=True,
|
|
183
|
+
)
|
|
184
|
+
# 2.8mm at slow speed - cam action pickup speed: 5.5
|
|
185
|
+
await self._gantry_mover.move_axes(
|
|
186
|
+
axis_map={MotorAxis.AXIS_96_CHANNEL_CAM: press_distance},
|
|
187
|
+
speed=5.5,
|
|
188
|
+
relative_move=True,
|
|
189
|
+
)
|
|
190
|
+
# retract cam : 11.05
|
|
191
|
+
await self._gantry_mover.move_axes(
|
|
192
|
+
axis_map={MotorAxis.AXIS_96_CHANNEL_CAM: retract_distance},
|
|
193
|
+
speed=5.5,
|
|
194
|
+
relative_move=True,
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
# Lower tip presence
|
|
198
|
+
await self._gantry_mover.move_axes(
|
|
199
|
+
axis_map={mount_axis: 2}, speed=10, relative_move=True
|
|
200
|
+
)
|
|
201
|
+
await self._gantry_mover.move_axes(
|
|
202
|
+
axis_map={MotorAxis.AXIS_96_CHANNEL_CAM: ejector_push_mm},
|
|
203
|
+
speed=5.5,
|
|
204
|
+
relative_move=True,
|
|
205
|
+
)
|
|
206
|
+
await self._gantry_mover.move_axes(
|
|
207
|
+
axis_map={MotorAxis.AXIS_96_CHANNEL_CAM: -1 * ejector_push_mm},
|
|
208
|
+
speed=5.5,
|
|
209
|
+
relative_move=True,
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
async def execute(
|
|
213
|
+
self, params: EvotipSealPipetteParams
|
|
214
|
+
) -> Union[SuccessData[EvotipSealPipetteResult], _ExecuteReturn]:
|
|
215
|
+
"""Move to and pick up a tip using the requested pipette."""
|
|
216
|
+
pipette_id = params.pipetteId
|
|
217
|
+
labware_id = params.labwareId
|
|
218
|
+
well_name = params.wellName
|
|
219
|
+
|
|
220
|
+
labware_definition = self._state_view.labware.get_definition(params.labwareId)
|
|
221
|
+
if not labware_validation.is_evotips(labware_definition.parameters.loadName):
|
|
222
|
+
raise UnsupportedLabwareForActionError(
|
|
223
|
+
f"Cannot use command: `EvotipSealPipette` with labware: {labware_definition.parameters.loadName}"
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
well_location = self._state_view.geometry.convert_pick_up_tip_well_location(
|
|
227
|
+
well_location=params.wellLocation
|
|
228
|
+
)
|
|
229
|
+
move_result = await move_to_well(
|
|
230
|
+
movement=self._movement,
|
|
231
|
+
model_utils=self._model_utils,
|
|
232
|
+
pipette_id=pipette_id,
|
|
233
|
+
labware_id=labware_id,
|
|
234
|
+
well_name=well_name,
|
|
235
|
+
well_location=well_location,
|
|
236
|
+
)
|
|
237
|
+
if isinstance(move_result, DefinedErrorData):
|
|
238
|
+
return move_result
|
|
239
|
+
|
|
240
|
+
# Aspirate to move plunger to a maximum volume position per pipette type
|
|
241
|
+
tip_geometry = self._state_view.geometry.get_nominal_tip_geometry(
|
|
242
|
+
pipette_id, labware_id, well_name
|
|
243
|
+
)
|
|
244
|
+
if self._state_view.pipettes.get_mount(pipette_id) == MountType.LEFT:
|
|
245
|
+
await self._hardware_api.home(axes=[Axis.P_L])
|
|
246
|
+
else:
|
|
247
|
+
await self._hardware_api.home(axes=[Axis.P_R])
|
|
248
|
+
|
|
249
|
+
# Begin relative pickup steps for the resin tips
|
|
250
|
+
|
|
251
|
+
channels = self._state_view.tips.get_pipette_active_channels(pipette_id)
|
|
252
|
+
mount = self._state_view.pipettes.get_mount(pipette_id)
|
|
253
|
+
tip_pick_up_params = params.tipPickUpParams
|
|
254
|
+
if tip_pick_up_params is None:
|
|
255
|
+
tip_pick_up_params = TipPickUpParams(
|
|
256
|
+
prepDistance=_PREP_DISTANCE_DEFAULT,
|
|
257
|
+
pressDistance=_PRESS_DISTANCE_DEFAULT,
|
|
258
|
+
ejectorPushMm=_EJECTOR_PUSH_MM_DEFAULT,
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
if channels != 96:
|
|
262
|
+
await self.relative_pickup_tip(
|
|
263
|
+
tip_pick_up_params=tip_pick_up_params,
|
|
264
|
+
mount=mount,
|
|
265
|
+
)
|
|
266
|
+
elif channels == 96:
|
|
267
|
+
await self.cam_action_relative_pickup_tip(
|
|
268
|
+
tip_pick_up_params=tip_pick_up_params,
|
|
269
|
+
mount=mount,
|
|
270
|
+
)
|
|
271
|
+
else:
|
|
272
|
+
tip_geometry = await self._tip_handler.pick_up_tip(
|
|
273
|
+
pipette_id=pipette_id,
|
|
274
|
+
labware_id=labware_id,
|
|
275
|
+
well_name=well_name,
|
|
276
|
+
do_not_ignore_tip_presence=True,
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
# cache_tip
|
|
280
|
+
if self._state_view.config.use_virtual_pipettes is False:
|
|
281
|
+
self._tip_handler.cache_tip(pipette_id, tip_geometry)
|
|
282
|
+
hw_instr = self._hardware_api.hardware_instruments[mount.to_hw_mount()]
|
|
283
|
+
if hw_instr is not None:
|
|
284
|
+
hw_instr.set_current_volume(_SAFE_TOP_VOLUME)
|
|
285
|
+
|
|
286
|
+
state_update = StateUpdate()
|
|
287
|
+
state_update.update_pipette_tip_state(
|
|
288
|
+
pipette_id=pipette_id,
|
|
289
|
+
tip_geometry=tip_geometry,
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
state_update.set_fluid_aspirated(
|
|
293
|
+
pipette_id=pipette_id,
|
|
294
|
+
fluid=AspiratedFluid(kind=FluidKind.LIQUID, volume=_SAFE_TOP_VOLUME),
|
|
295
|
+
)
|
|
296
|
+
return SuccessData(
|
|
297
|
+
public=EvotipSealPipetteResult(
|
|
298
|
+
tipVolume=tip_geometry.volume,
|
|
299
|
+
tipLength=tip_geometry.length,
|
|
300
|
+
tipDiameter=tip_geometry.diameter,
|
|
301
|
+
position=move_result.public.position,
|
|
302
|
+
),
|
|
303
|
+
state_update=state_update,
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
class EvotipSealPipette(
|
|
308
|
+
BaseCommand[
|
|
309
|
+
EvotipSealPipetteParams,
|
|
310
|
+
EvotipSealPipetteResult,
|
|
311
|
+
StallOrCollisionError,
|
|
312
|
+
]
|
|
313
|
+
):
|
|
314
|
+
"""Seal evotip resin tip command model."""
|
|
315
|
+
|
|
316
|
+
commandType: EvotipSealPipetteCommandType = "evotipSealPipette"
|
|
317
|
+
params: EvotipSealPipetteParams
|
|
318
|
+
result: Optional[EvotipSealPipetteResult] = None
|
|
319
|
+
|
|
320
|
+
_ImplementationCls: Type[
|
|
321
|
+
EvotipSealPipetteImplementation
|
|
322
|
+
] = EvotipSealPipetteImplementation
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
class EvotipSealPipetteCreate(BaseCommandCreate[EvotipSealPipetteParams]):
|
|
326
|
+
"""Seal evotip resin tip command creation request model."""
|
|
327
|
+
|
|
328
|
+
commandType: EvotipSealPipetteCommandType = "evotipSealPipette"
|
|
329
|
+
params: EvotipSealPipetteParams
|
|
330
|
+
|
|
331
|
+
_CommandCls: Type[EvotipSealPipette] = EvotipSealPipette
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"""Unseal evotip resin tip command request, result, and implementation models."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pydantic import Field
|
|
6
|
+
from typing import TYPE_CHECKING, Optional, Type
|
|
7
|
+
from typing_extensions import Literal
|
|
8
|
+
|
|
9
|
+
from opentrons.protocol_engine.resources.model_utils import ModelUtils
|
|
10
|
+
from opentrons.protocol_engine.errors import UnsupportedLabwareForActionError
|
|
11
|
+
from opentrons.protocol_engine.types import MotorAxis
|
|
12
|
+
from opentrons.types import MountType
|
|
13
|
+
|
|
14
|
+
from ..types import DropTipWellLocation
|
|
15
|
+
from .pipetting_common import (
|
|
16
|
+
PipetteIdMixin,
|
|
17
|
+
)
|
|
18
|
+
from .movement_common import (
|
|
19
|
+
DestinationPositionResult,
|
|
20
|
+
move_to_well,
|
|
21
|
+
StallOrCollisionError,
|
|
22
|
+
)
|
|
23
|
+
from .command import (
|
|
24
|
+
AbstractCommandImpl,
|
|
25
|
+
BaseCommand,
|
|
26
|
+
BaseCommandCreate,
|
|
27
|
+
DefinedErrorData,
|
|
28
|
+
SuccessData,
|
|
29
|
+
)
|
|
30
|
+
from ..resources import labware_validation
|
|
31
|
+
|
|
32
|
+
if TYPE_CHECKING:
|
|
33
|
+
from ..state.state import StateView
|
|
34
|
+
from ..execution import MovementHandler, TipHandler, GantryMover
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
EvotipUnsealPipetteCommandType = Literal["evotipUnsealPipette"]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class EvotipUnsealPipetteParams(PipetteIdMixin):
|
|
41
|
+
"""Payload required to drop a tip in a specific well."""
|
|
42
|
+
|
|
43
|
+
labwareId: str = Field(..., description="Identifier of labware to use.")
|
|
44
|
+
wellName: str = Field(..., description="Name of well to use in labware.")
|
|
45
|
+
wellLocation: DropTipWellLocation = Field(
|
|
46
|
+
default_factory=DropTipWellLocation,
|
|
47
|
+
description="Relative well location at which to drop the tip.",
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class EvotipUnsealPipetteResult(DestinationPositionResult):
|
|
52
|
+
"""Result data from the execution of a DropTip command."""
|
|
53
|
+
|
|
54
|
+
pass
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
_ExecuteReturn = (
|
|
58
|
+
SuccessData[EvotipUnsealPipetteResult] | DefinedErrorData[StallOrCollisionError]
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class EvotipUnsealPipetteImplementation(
|
|
63
|
+
AbstractCommandImpl[EvotipUnsealPipetteParams, _ExecuteReturn]
|
|
64
|
+
):
|
|
65
|
+
"""Drop tip command implementation."""
|
|
66
|
+
|
|
67
|
+
def __init__(
|
|
68
|
+
self,
|
|
69
|
+
state_view: StateView,
|
|
70
|
+
tip_handler: TipHandler,
|
|
71
|
+
movement: MovementHandler,
|
|
72
|
+
model_utils: ModelUtils,
|
|
73
|
+
gantry_mover: GantryMover,
|
|
74
|
+
**kwargs: object,
|
|
75
|
+
) -> None:
|
|
76
|
+
self._state_view = state_view
|
|
77
|
+
self._tip_handler = tip_handler
|
|
78
|
+
self._movement_handler = movement
|
|
79
|
+
self._model_utils = model_utils
|
|
80
|
+
self._gantry_mover = gantry_mover
|
|
81
|
+
|
|
82
|
+
async def execute(self, params: EvotipUnsealPipetteParams) -> _ExecuteReturn:
|
|
83
|
+
"""Move to and drop a tip using the requested pipette."""
|
|
84
|
+
pipette_id = params.pipetteId
|
|
85
|
+
labware_id = params.labwareId
|
|
86
|
+
well_name = params.wellName
|
|
87
|
+
|
|
88
|
+
well_location = params.wellLocation
|
|
89
|
+
labware_definition = self._state_view.labware.get_definition(params.labwareId)
|
|
90
|
+
if not labware_validation.is_evotips(labware_definition.parameters.loadName):
|
|
91
|
+
raise UnsupportedLabwareForActionError(
|
|
92
|
+
f"Cannot use command: `EvotipUnsealPipette` with labware: {labware_definition.parameters.loadName}"
|
|
93
|
+
)
|
|
94
|
+
is_partially_configured = self._state_view.pipettes.get_is_partially_configured(
|
|
95
|
+
pipette_id=pipette_id
|
|
96
|
+
)
|
|
97
|
+
tip_drop_location = self._state_view.geometry.get_checked_tip_drop_location(
|
|
98
|
+
pipette_id=pipette_id,
|
|
99
|
+
labware_id=labware_id,
|
|
100
|
+
well_location=well_location,
|
|
101
|
+
partially_configured=is_partially_configured,
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
move_result = await move_to_well(
|
|
105
|
+
movement=self._movement_handler,
|
|
106
|
+
model_utils=self._model_utils,
|
|
107
|
+
pipette_id=pipette_id,
|
|
108
|
+
labware_id=labware_id,
|
|
109
|
+
well_name=well_name,
|
|
110
|
+
well_location=tip_drop_location,
|
|
111
|
+
)
|
|
112
|
+
if isinstance(move_result, DefinedErrorData):
|
|
113
|
+
return move_result
|
|
114
|
+
|
|
115
|
+
# Move to an appropriate position
|
|
116
|
+
mount = self._state_view.pipettes.get_mount(pipette_id)
|
|
117
|
+
|
|
118
|
+
mount_axis = MotorAxis.LEFT_Z if mount == MountType.LEFT else MotorAxis.RIGHT_Z
|
|
119
|
+
await self._gantry_mover.move_axes(
|
|
120
|
+
axis_map={mount_axis: -14}, speed=10, relative_move=True
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
await self._tip_handler.drop_tip(
|
|
124
|
+
pipette_id=pipette_id,
|
|
125
|
+
home_after=None,
|
|
126
|
+
do_not_ignore_tip_presence=False,
|
|
127
|
+
ignore_plunger=True,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
return SuccessData(
|
|
131
|
+
public=EvotipUnsealPipetteResult(position=move_result.public.position),
|
|
132
|
+
state_update=move_result.state_update.set_fluid_unknown(
|
|
133
|
+
pipette_id=pipette_id
|
|
134
|
+
).update_pipette_tip_state(pipette_id=params.pipetteId, tip_geometry=None),
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class EvotipUnsealPipette(
|
|
139
|
+
BaseCommand[
|
|
140
|
+
EvotipUnsealPipetteParams, EvotipUnsealPipetteResult, StallOrCollisionError
|
|
141
|
+
]
|
|
142
|
+
):
|
|
143
|
+
"""Evotip unseal command model."""
|
|
144
|
+
|
|
145
|
+
commandType: EvotipUnsealPipetteCommandType = "evotipUnsealPipette"
|
|
146
|
+
params: EvotipUnsealPipetteParams
|
|
147
|
+
result: Optional[EvotipUnsealPipetteResult] = None
|
|
148
|
+
|
|
149
|
+
_ImplementationCls: Type[
|
|
150
|
+
EvotipUnsealPipetteImplementation
|
|
151
|
+
] = EvotipUnsealPipetteImplementation
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
class EvotipUnsealPipetteCreate(BaseCommandCreate[EvotipUnsealPipetteParams]):
|
|
155
|
+
"""Evotip unseal command creation request model."""
|
|
156
|
+
|
|
157
|
+
commandType: EvotipUnsealPipetteCommandType = "evotipUnsealPipette"
|
|
158
|
+
params: EvotipUnsealPipetteParams
|
|
159
|
+
|
|
160
|
+
_CommandCls: Type[EvotipUnsealPipette] = EvotipUnsealPipette
|
|
@@ -1,24 +1,17 @@
|
|
|
1
1
|
"""Generate a JSON schema against which all create commands statically validate."""
|
|
2
|
+
|
|
2
3
|
import json
|
|
3
|
-
import pydantic
|
|
4
4
|
import argparse
|
|
5
5
|
import sys
|
|
6
|
-
from opentrons.protocol_engine.commands.command_unions import
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class CreateCommandUnion(pydantic.BaseModel):
|
|
10
|
-
"""Model that validates a union of all CommandCreate models."""
|
|
11
|
-
|
|
12
|
-
__root__: CommandCreate
|
|
6
|
+
from opentrons.protocol_engine.commands.command_unions import CommandCreateAdapter
|
|
13
7
|
|
|
14
8
|
|
|
15
9
|
def generate_command_schema(version: str) -> str:
|
|
16
10
|
"""Generate a JSON Schema that all valid create commands can validate against."""
|
|
17
|
-
|
|
18
|
-
schema_as_dict = json.loads(raw_json_schema)
|
|
11
|
+
schema_as_dict = CommandCreateAdapter.json_schema(mode="validation")
|
|
19
12
|
schema_as_dict["$id"] = f"opentronsCommandSchemaV{version}"
|
|
20
13
|
schema_as_dict["$schema"] = "http://json-schema.org/draft-07/schema#"
|
|
21
|
-
return json.dumps(schema_as_dict, indent=2)
|
|
14
|
+
return json.dumps(schema_as_dict, indent=2, sort_keys=True)
|
|
22
15
|
|
|
23
16
|
|
|
24
17
|
if __name__ == "__main__":
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"""Get next tip command request, result, and implementation models."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
from pydantic import BaseModel, Field
|
|
5
|
+
from typing import TYPE_CHECKING, Any, Optional, Type, List, Literal, Union
|
|
6
|
+
|
|
7
|
+
from pydantic.json_schema import SkipJsonSchema
|
|
8
|
+
|
|
9
|
+
from opentrons.types import NozzleConfigurationType
|
|
10
|
+
|
|
11
|
+
from ..errors import ErrorOccurrence
|
|
12
|
+
from ..types import NextTipInfo, NoTipAvailable, NoTipReason
|
|
13
|
+
from .pipetting_common import PipetteIdMixin
|
|
14
|
+
|
|
15
|
+
from .command import (
|
|
16
|
+
AbstractCommandImpl,
|
|
17
|
+
BaseCommand,
|
|
18
|
+
BaseCommandCreate,
|
|
19
|
+
SuccessData,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
if TYPE_CHECKING:
|
|
23
|
+
from ..state.state import StateView
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _remove_default(s: dict[str, Any]) -> None:
|
|
27
|
+
s.pop("default", None)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
GetNextTipCommandType = Literal["getNextTip"]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class GetNextTipParams(PipetteIdMixin):
|
|
34
|
+
"""Payload needed to resolve the next available tip."""
|
|
35
|
+
|
|
36
|
+
labwareIds: List[str] = Field(
|
|
37
|
+
...,
|
|
38
|
+
description="Labware ID(s) of tip racks to resolve next available tip(s) from"
|
|
39
|
+
" Labware IDs will be resolved sequentially",
|
|
40
|
+
)
|
|
41
|
+
startingTipWell: str | SkipJsonSchema[None] = Field(
|
|
42
|
+
None,
|
|
43
|
+
description="Name of starting tip rack 'well'."
|
|
44
|
+
" This only applies to the first tip rack in the list provided in labwareIDs",
|
|
45
|
+
json_schema_extra=_remove_default,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class GetNextTipResult(BaseModel):
|
|
50
|
+
"""Result data from the execution of a GetNextTip."""
|
|
51
|
+
|
|
52
|
+
nextTipInfo: Union[NextTipInfo, NoTipAvailable] = Field(
|
|
53
|
+
...,
|
|
54
|
+
description="Labware ID and well name of next available tip for a pipette,"
|
|
55
|
+
" or information why no tip could be resolved.",
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class GetNextTipImplementation(
|
|
60
|
+
AbstractCommandImpl[GetNextTipParams, SuccessData[GetNextTipResult]]
|
|
61
|
+
):
|
|
62
|
+
"""Get next tip command implementation."""
|
|
63
|
+
|
|
64
|
+
def __init__(
|
|
65
|
+
self,
|
|
66
|
+
state_view: StateView,
|
|
67
|
+
**kwargs: object,
|
|
68
|
+
) -> None:
|
|
69
|
+
self._state_view = state_view
|
|
70
|
+
|
|
71
|
+
async def execute(self, params: GetNextTipParams) -> SuccessData[GetNextTipResult]:
|
|
72
|
+
"""Get the next available tip for the requested pipette."""
|
|
73
|
+
pipette_id = params.pipetteId
|
|
74
|
+
starting_tip_name = params.startingTipWell
|
|
75
|
+
|
|
76
|
+
num_tips = self._state_view.tips.get_pipette_active_channels(pipette_id)
|
|
77
|
+
nozzle_map = self._state_view.tips.get_pipette_nozzle_map(pipette_id)
|
|
78
|
+
|
|
79
|
+
if (
|
|
80
|
+
starting_tip_name is not None
|
|
81
|
+
and nozzle_map.configuration != NozzleConfigurationType.FULL
|
|
82
|
+
):
|
|
83
|
+
# This is to match the behavior found in PAPI, but also because we don't have logic to automatically find
|
|
84
|
+
# the next tip with partial configuration and a starting tip. This will never work for a 96-channel due to
|
|
85
|
+
# x-axis overlap, but could eventually work with 8-channel if we better define starting tip USED or CLEAN
|
|
86
|
+
# state when starting a protocol to prevent accidental tip pick-up with starting non-full tip racks.
|
|
87
|
+
return SuccessData(
|
|
88
|
+
public=GetNextTipResult(
|
|
89
|
+
nextTipInfo=NoTipAvailable(
|
|
90
|
+
noTipReason=NoTipReason.STARTING_TIP_WITH_PARTIAL,
|
|
91
|
+
message="Cannot automatically resolve next tip with starting tip and partial tip configuration.",
|
|
92
|
+
)
|
|
93
|
+
)
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
next_tip: Union[NextTipInfo, NoTipAvailable]
|
|
97
|
+
for labware_id in params.labwareIds:
|
|
98
|
+
well_name = self._state_view.tips.get_next_tip(
|
|
99
|
+
labware_id=labware_id,
|
|
100
|
+
num_tips=num_tips,
|
|
101
|
+
starting_tip_name=starting_tip_name,
|
|
102
|
+
nozzle_map=nozzle_map,
|
|
103
|
+
)
|
|
104
|
+
if well_name is not None:
|
|
105
|
+
next_tip = NextTipInfo(labwareId=labware_id, tipStartingWell=well_name)
|
|
106
|
+
break
|
|
107
|
+
# After the first tip rack is exhausted, starting tip no longer applies
|
|
108
|
+
starting_tip_name = None
|
|
109
|
+
else:
|
|
110
|
+
next_tip = NoTipAvailable(
|
|
111
|
+
noTipReason=NoTipReason.NO_AVAILABLE_TIPS,
|
|
112
|
+
message="No available tips for given pipette, nozzle configuration and provided tip racks.",
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
return SuccessData(public=GetNextTipResult(nextTipInfo=next_tip))
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class GetNextTip(BaseCommand[GetNextTipParams, GetNextTipResult, ErrorOccurrence]):
|
|
119
|
+
"""Get next tip command model."""
|
|
120
|
+
|
|
121
|
+
commandType: GetNextTipCommandType = "getNextTip"
|
|
122
|
+
params: GetNextTipParams
|
|
123
|
+
result: Optional[GetNextTipResult] = None
|
|
124
|
+
|
|
125
|
+
_ImplementationCls: Type[GetNextTipImplementation] = GetNextTipImplementation
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
class GetNextTipCreate(BaseCommandCreate[GetNextTipParams]):
|
|
129
|
+
"""Get next tip command creation request model."""
|
|
130
|
+
|
|
131
|
+
commandType: GetNextTipCommandType = "getNextTip"
|
|
132
|
+
params: GetNextTipParams
|
|
133
|
+
|
|
134
|
+
_CommandCls: Type[GetNextTip] = GetNextTip
|
|
@@ -71,7 +71,7 @@ class GetTipPresence(
|
|
|
71
71
|
|
|
72
72
|
commandType: GetTipPresenceCommandType = "getTipPresence"
|
|
73
73
|
params: GetTipPresenceParams
|
|
74
|
-
result: Optional[GetTipPresenceResult]
|
|
74
|
+
result: Optional[GetTipPresenceResult] = None
|
|
75
75
|
|
|
76
76
|
_ImplementationCls: Type[
|
|
77
77
|
GetTipPresenceImplementation
|
|
@@ -69,7 +69,7 @@ class CloseLabwareLatch(
|
|
|
69
69
|
|
|
70
70
|
commandType: CloseLabwareLatchCommandType = "heaterShaker/closeLabwareLatch"
|
|
71
71
|
params: CloseLabwareLatchParams
|
|
72
|
-
result: Optional[CloseLabwareLatchResult]
|
|
72
|
+
result: Optional[CloseLabwareLatchResult] = None
|
|
73
73
|
|
|
74
74
|
_ImplementationCls: Type[CloseLabwareLatchImpl] = CloseLabwareLatchImpl
|
|
75
75
|
|
|
@@ -68,7 +68,7 @@ class DeactivateHeater(
|
|
|
68
68
|
|
|
69
69
|
commandType: DeactivateHeaterCommandType = "heaterShaker/deactivateHeater"
|
|
70
70
|
params: DeactivateHeaterParams
|
|
71
|
-
result: Optional[DeactivateHeaterResult]
|
|
71
|
+
result: Optional[DeactivateHeaterResult] = None
|
|
72
72
|
|
|
73
73
|
_ImplementationCls: Type[DeactivateHeaterImpl] = DeactivateHeaterImpl
|
|
74
74
|
|
|
@@ -70,7 +70,7 @@ class DeactivateShaker(
|
|
|
70
70
|
|
|
71
71
|
commandType: DeactivateShakerCommandType = "heaterShaker/deactivateShaker"
|
|
72
72
|
params: DeactivateShakerParams
|
|
73
|
-
result: Optional[DeactivateShakerResult]
|
|
73
|
+
result: Optional[DeactivateShakerResult] = None
|
|
74
74
|
|
|
75
75
|
_ImplementationCls: Type[DeactivateShakerImpl] = DeactivateShakerImpl
|
|
76
76
|
|