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
|
@@ -1,11 +1,25 @@
|
|
|
1
|
-
from typing import NamedTuple, Union,
|
|
1
|
+
from typing import NamedTuple, Union, Optional
|
|
2
2
|
|
|
3
|
-
from opentrons.types import
|
|
3
|
+
from opentrons.types import (
|
|
4
|
+
Mount,
|
|
5
|
+
DeckLocation,
|
|
6
|
+
Location,
|
|
7
|
+
Point,
|
|
8
|
+
AxisMapType,
|
|
9
|
+
AxisType,
|
|
10
|
+
StringAxisMap,
|
|
11
|
+
)
|
|
4
12
|
from opentrons.legacy_commands import publisher
|
|
5
|
-
from opentrons.hardware_control import SyncHardwareAPI
|
|
13
|
+
from opentrons.hardware_control import SyncHardwareAPI
|
|
14
|
+
from opentrons.protocols.api_support.util import requires_version
|
|
15
|
+
from opentrons.protocols.api_support.types import APIVersion
|
|
16
|
+
from opentrons_shared_data.pipette.types import PipetteNameType
|
|
6
17
|
|
|
7
|
-
from .
|
|
8
|
-
from .core.common import ProtocolCore
|
|
18
|
+
from . import validation
|
|
19
|
+
from .core.common import ProtocolCore, RobotCore
|
|
20
|
+
from .module_contexts import ModuleContext
|
|
21
|
+
from .labware import Labware
|
|
22
|
+
from ._types import PipetteActionTypes, PlungerPositionTypes
|
|
9
23
|
|
|
10
24
|
|
|
11
25
|
class HardwareManager(NamedTuple):
|
|
@@ -34,56 +48,214 @@ class RobotContext(publisher.CommandPublisher):
|
|
|
34
48
|
|
|
35
49
|
"""
|
|
36
50
|
|
|
37
|
-
def __init__(
|
|
38
|
-
self
|
|
51
|
+
def __init__(
|
|
52
|
+
self, core: RobotCore, protocol_core: ProtocolCore, api_version: APIVersion
|
|
53
|
+
) -> None:
|
|
54
|
+
self._hardware = HardwareManager(hardware=protocol_core.get_hardware())
|
|
55
|
+
self._core = core
|
|
56
|
+
self._protocol_core = protocol_core
|
|
57
|
+
self._api_version = api_version
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
@requires_version(2, 22)
|
|
61
|
+
def api_version(self) -> APIVersion:
|
|
62
|
+
return self._api_version
|
|
39
63
|
|
|
40
64
|
@property
|
|
41
65
|
def hardware(self) -> HardwareManager:
|
|
66
|
+
# TODO this hardware attribute should be deprecated
|
|
67
|
+
# in version 3.0+ as we will only support exposed robot
|
|
68
|
+
# context commands.
|
|
42
69
|
return self._hardware
|
|
43
70
|
|
|
71
|
+
@requires_version(2, 22)
|
|
44
72
|
def move_to(
|
|
45
73
|
self,
|
|
46
74
|
mount: Union[Mount, str],
|
|
47
|
-
destination:
|
|
48
|
-
|
|
75
|
+
destination: Location,
|
|
76
|
+
speed: Optional[float] = None,
|
|
49
77
|
) -> None:
|
|
50
|
-
|
|
78
|
+
"""
|
|
79
|
+
Move a specified mount to a destination location on the deck.
|
|
80
|
+
|
|
81
|
+
:param mount: The mount of the instrument you wish to move.
|
|
82
|
+
This can either be an instance of :py:class:`.types.Mount` or one
|
|
83
|
+
of the strings ``"left"``, ``"right"``, ``"extension"``, ``"gripper"``. Note
|
|
84
|
+
that the gripper mount can be referred to either as ``"extension"`` or ``"gripper"``.
|
|
85
|
+
:type mount: types.Mount or str
|
|
86
|
+
:param Location destination:
|
|
87
|
+
:param speed:
|
|
88
|
+
"""
|
|
89
|
+
mount = validation.ensure_instrument_mount(mount)
|
|
90
|
+
self._core.move_to(mount, destination.point, speed)
|
|
51
91
|
|
|
92
|
+
@requires_version(2, 22)
|
|
52
93
|
def move_axes_to(
|
|
53
94
|
self,
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
95
|
+
axis_map: Union[AxisMapType, StringAxisMap],
|
|
96
|
+
critical_point: Optional[Union[AxisMapType, StringAxisMap]] = None,
|
|
97
|
+
speed: Optional[float] = None,
|
|
57
98
|
) -> None:
|
|
58
|
-
|
|
99
|
+
"""
|
|
100
|
+
Move a set of axes to an absolute position on the deck.
|
|
59
101
|
|
|
102
|
+
:param axis_map: A dictionary mapping axes to an absolute position on the deck in mm.
|
|
103
|
+
:param critical_point: The critical point to move the axes with. It should only
|
|
104
|
+
specify the gantry axes (i.e. `x`, `y`, `z`).
|
|
105
|
+
:param float speed: The maximum speed with which you want to move all the axes
|
|
106
|
+
in the axis map.
|
|
107
|
+
"""
|
|
108
|
+
instrument_on_left = self._core.get_pipette_type_from_engine(Mount.LEFT)
|
|
109
|
+
is_96_channel = instrument_on_left == PipetteNameType.P1000_96
|
|
110
|
+
axis_map = validation.ensure_axis_map_type(
|
|
111
|
+
axis_map, self._protocol_core.robot_type, is_96_channel
|
|
112
|
+
)
|
|
113
|
+
if critical_point:
|
|
114
|
+
critical_point = validation.ensure_axis_map_type(
|
|
115
|
+
critical_point, self._protocol_core.robot_type, is_96_channel
|
|
116
|
+
)
|
|
117
|
+
validation.ensure_only_gantry_axis_map_type(
|
|
118
|
+
critical_point, self._protocol_core.robot_type
|
|
119
|
+
)
|
|
120
|
+
else:
|
|
121
|
+
critical_point = None
|
|
122
|
+
self._core.move_axes_to(axis_map, critical_point, speed)
|
|
123
|
+
|
|
124
|
+
@requires_version(2, 22)
|
|
60
125
|
def move_axes_relative(
|
|
61
|
-
self,
|
|
126
|
+
self,
|
|
127
|
+
axis_map: Union[AxisMapType, StringAxisMap],
|
|
128
|
+
speed: Optional[float] = None,
|
|
62
129
|
) -> None:
|
|
63
|
-
|
|
130
|
+
"""
|
|
131
|
+
Move a set of axes to a relative position on the deck.
|
|
132
|
+
|
|
133
|
+
:param axis_map: A dictionary mapping axes to relative movements in mm.
|
|
134
|
+
:type mount: types.Mount or str
|
|
64
135
|
|
|
65
|
-
|
|
66
|
-
|
|
136
|
+
:param float speed: The maximum speed with which you want to move all the axes
|
|
137
|
+
in the axis map.
|
|
138
|
+
"""
|
|
139
|
+
instrument_on_left = self._core.get_pipette_type_from_engine(Mount.LEFT)
|
|
140
|
+
is_96_channel = instrument_on_left == PipetteNameType.P1000_96
|
|
141
|
+
|
|
142
|
+
axis_map = validation.ensure_axis_map_type(
|
|
143
|
+
axis_map, self._protocol_core.robot_type, is_96_channel
|
|
144
|
+
)
|
|
145
|
+
self._core.move_axes_relative(axis_map, speed)
|
|
146
|
+
|
|
147
|
+
def close_gripper_jaw(self, force: Optional[float] = None) -> None:
|
|
148
|
+
"""Command the gripper closed with some force."""
|
|
149
|
+
self._core.close_gripper(force)
|
|
67
150
|
|
|
68
151
|
def open_gripper_jaw(self) -> None:
|
|
69
|
-
|
|
152
|
+
"""Command the gripper open."""
|
|
153
|
+
self._core.release_grip()
|
|
70
154
|
|
|
71
155
|
def axis_coordinates_for(
|
|
72
|
-
self,
|
|
73
|
-
|
|
74
|
-
|
|
156
|
+
self,
|
|
157
|
+
mount: Union[Mount, str],
|
|
158
|
+
location: Union[Location, ModuleContext, DeckLocation],
|
|
159
|
+
) -> AxisMapType:
|
|
160
|
+
"""
|
|
161
|
+
Build a :py:class:`.types.AxisMapType` from a location to be compatible with
|
|
162
|
+
either :py:meth:`.RobotContext.move_axes_to` or :py:meth:`.RobotContext.move_axes_relative`.
|
|
163
|
+
You must provide only one of `location`, `slot`, or `module` to build
|
|
164
|
+
the axis map.
|
|
165
|
+
|
|
166
|
+
:param mount: The mount of the instrument you wish create an axis map for.
|
|
167
|
+
This can either be an instance of :py:class:`.types.Mount` or one
|
|
168
|
+
of the strings ``"left"``, ``"right"``, ``"extension"``, ``"gripper"``. Note
|
|
169
|
+
that the gripper mount can be referred to either as ``"extension"`` or ``"gripper"``.
|
|
170
|
+
:type mount: types.Mount or str
|
|
171
|
+
:param location: The location to format an axis map for.
|
|
172
|
+
:type location: `Well`, `ModuleContext`, `DeckLocation` or `OffDeckType`
|
|
173
|
+
"""
|
|
174
|
+
mount = validation.ensure_instrument_mount(mount)
|
|
175
|
+
|
|
176
|
+
mount_axis = AxisType.axis_for_mount(mount)
|
|
177
|
+
if location:
|
|
178
|
+
loc: Union[Point, Labware, None]
|
|
179
|
+
if isinstance(location, ModuleContext):
|
|
180
|
+
loc = location.labware
|
|
181
|
+
if not loc:
|
|
182
|
+
raise ValueError(f"There must be a labware on {location}")
|
|
183
|
+
top_of_labware = loc.wells()[0].top()
|
|
184
|
+
loc = top_of_labware.point
|
|
185
|
+
return {mount_axis: loc.z, AxisType.X: loc.x, AxisType.Y: loc.y}
|
|
186
|
+
elif location is DeckLocation and not isinstance(location, Location):
|
|
187
|
+
slot_name = validation.ensure_and_convert_deck_slot(
|
|
188
|
+
location,
|
|
189
|
+
api_version=self._api_version,
|
|
190
|
+
robot_type=self._protocol_core.robot_type,
|
|
191
|
+
)
|
|
192
|
+
loc = self._protocol_core.get_slot_center(slot_name)
|
|
193
|
+
return {mount_axis: loc.z, AxisType.X: loc.x, AxisType.Y: loc.y}
|
|
194
|
+
elif isinstance(location, Location):
|
|
195
|
+
assert isinstance(location, Location)
|
|
196
|
+
loc = location.point
|
|
197
|
+
return {mount_axis: loc.z, AxisType.X: loc.x, AxisType.Y: loc.y}
|
|
198
|
+
else:
|
|
199
|
+
raise ValueError(
|
|
200
|
+
"Location parameter must be a Module, Deck Location, or Location type."
|
|
201
|
+
)
|
|
202
|
+
else:
|
|
203
|
+
raise TypeError("You must specify a location to move to.")
|
|
75
204
|
|
|
76
205
|
def plunger_coordinates_for_volume(
|
|
77
|
-
self, mount: Union[Mount, str], volume: float
|
|
78
|
-
) ->
|
|
79
|
-
|
|
206
|
+
self, mount: Union[Mount, str], volume: float, action: PipetteActionTypes
|
|
207
|
+
) -> AxisMapType:
|
|
208
|
+
"""
|
|
209
|
+
Build a :py:class:`.types.AxisMapType` for a pipette plunger motor from volume.
|
|
210
|
+
|
|
211
|
+
"""
|
|
212
|
+
pipette_name = self._core.get_pipette_type_from_engine(mount)
|
|
213
|
+
if not pipette_name:
|
|
214
|
+
raise ValueError(
|
|
215
|
+
f"Expected a pipette to be attached to provided mount {mount}"
|
|
216
|
+
)
|
|
217
|
+
mount = validation.ensure_mount_for_pipette(mount, pipette_name)
|
|
218
|
+
pipette_axis = AxisType.plunger_axis_for_mount(mount)
|
|
219
|
+
|
|
220
|
+
pipette_position = self._core.get_plunger_position_from_volume(
|
|
221
|
+
mount, volume, action, self._protocol_core.robot_type
|
|
222
|
+
)
|
|
223
|
+
return {pipette_axis: pipette_position}
|
|
80
224
|
|
|
81
225
|
def plunger_coordinates_for_named_position(
|
|
82
|
-
self, mount: Union[Mount, str], position_name:
|
|
83
|
-
) ->
|
|
84
|
-
|
|
226
|
+
self, mount: Union[Mount, str], position_name: PlungerPositionTypes
|
|
227
|
+
) -> AxisMapType:
|
|
228
|
+
"""
|
|
229
|
+
Build a :py:class:`.types.AxisMapType` for a pipette plunger motor from position_name.
|
|
85
230
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
231
|
+
"""
|
|
232
|
+
pipette_name = self._core.get_pipette_type_from_engine(mount)
|
|
233
|
+
if not pipette_name:
|
|
234
|
+
raise ValueError(
|
|
235
|
+
f"Expected a pipette to be attached to provided mount {mount}"
|
|
236
|
+
)
|
|
237
|
+
mount = validation.ensure_mount_for_pipette(mount, pipette_name)
|
|
238
|
+
pipette_axis = AxisType.plunger_axis_for_mount(mount)
|
|
239
|
+
pipette_position = self._core.get_plunger_position_from_name(
|
|
240
|
+
mount, position_name
|
|
241
|
+
)
|
|
242
|
+
return {pipette_axis: pipette_position}
|
|
243
|
+
|
|
244
|
+
def build_axis_map(self, axis_map: StringAxisMap) -> AxisMapType:
|
|
245
|
+
"""Take in a :py:class:`.types.StringAxisMap` and output a :py:class:`.types.AxisMapType`.
|
|
246
|
+
A :py:class:`.types.StringAxisMap` is allowed to contain any of the following strings:
|
|
247
|
+
``"x"``, ``"y"``, "``z_l"``, "``z_r"``, "``z_g"``, ``"q"``.
|
|
248
|
+
|
|
249
|
+
An example of a valid axis map could be:
|
|
250
|
+
|
|
251
|
+
{"x": 1, "y": 2} or {"Z_L": 100}
|
|
252
|
+
|
|
253
|
+
Note that capitalization does not matter.
|
|
254
|
+
|
|
255
|
+
"""
|
|
256
|
+
instrument_on_left = self._core.get_pipette_type_from_engine(Mount.LEFT)
|
|
257
|
+
is_96_channel = instrument_on_left == PipetteNameType.P1000_96
|
|
258
|
+
|
|
259
|
+
return validation.ensure_axis_map_type(
|
|
260
|
+
axis_map, self._protocol_core.robot_type, is_96_channel
|
|
261
|
+
)
|
|
@@ -11,7 +11,7 @@ from typing import (
|
|
|
11
11
|
NamedTuple,
|
|
12
12
|
TYPE_CHECKING,
|
|
13
13
|
)
|
|
14
|
-
|
|
14
|
+
from math import isinf, isnan
|
|
15
15
|
from typing_extensions import TypeGuard
|
|
16
16
|
|
|
17
17
|
from opentrons_shared_data.labware.labware_definition import LabwareRole
|
|
@@ -21,7 +21,16 @@ from opentrons_shared_data.robot.types import RobotType
|
|
|
21
21
|
from opentrons.protocols.api_support.types import APIVersion, ThermocyclerStep
|
|
22
22
|
from opentrons.protocols.api_support.util import APIVersionError
|
|
23
23
|
from opentrons.protocols.models import LabwareDefinition
|
|
24
|
-
from opentrons.
|
|
24
|
+
from opentrons.protocols.advanced_control.transfers.common import TransferTipPolicyV2
|
|
25
|
+
from opentrons.types import (
|
|
26
|
+
Mount,
|
|
27
|
+
DeckSlotName,
|
|
28
|
+
StagingSlotName,
|
|
29
|
+
Location,
|
|
30
|
+
AxisType,
|
|
31
|
+
AxisMapType,
|
|
32
|
+
StringAxisMap,
|
|
33
|
+
)
|
|
25
34
|
from opentrons.hardware_control.modules.types import (
|
|
26
35
|
ModuleModel,
|
|
27
36
|
MagneticModuleModel,
|
|
@@ -44,6 +53,9 @@ _COORDINATE_DECK_LABEL_VERSION_GATE = APIVersion(2, 15)
|
|
|
44
53
|
# The first APIVersion where Python protocols can specify staging deck slots (e.g. "D4")
|
|
45
54
|
_STAGING_DECK_SLOT_VERSION_GATE = APIVersion(2, 16)
|
|
46
55
|
|
|
56
|
+
# The first APIVersion where Python protocols can load lids as stacks and treat them as attributes of a parent labware.
|
|
57
|
+
LID_STACK_VERSION_GATE = APIVersion(2, 23)
|
|
58
|
+
|
|
47
59
|
# Mapping of public Python Protocol API pipette load names
|
|
48
60
|
# to names used by the internal Opentrons system
|
|
49
61
|
_PIPETTE_NAMES_MAP = {
|
|
@@ -63,7 +75,9 @@ _PIPETTE_NAMES_MAP = {
|
|
|
63
75
|
"flex_8channel_50": PipetteNameType.P50_MULTI_FLEX,
|
|
64
76
|
"flex_1channel_1000": PipetteNameType.P1000_SINGLE_FLEX,
|
|
65
77
|
"flex_8channel_1000": PipetteNameType.P1000_MULTI_FLEX,
|
|
78
|
+
"flex_8channel_1000_em": PipetteNameType.P1000_MULTI_EM,
|
|
66
79
|
"flex_96channel_1000": PipetteNameType.P1000_96,
|
|
80
|
+
"flex_96channel_200": PipetteNameType.P200_96,
|
|
67
81
|
}
|
|
68
82
|
|
|
69
83
|
|
|
@@ -75,6 +89,14 @@ class PipetteMountTypeError(TypeError):
|
|
|
75
89
|
"""An error raised when an invalid mount type is used for loading pipettes."""
|
|
76
90
|
|
|
77
91
|
|
|
92
|
+
class InstrumentMountTypeError(TypeError):
|
|
93
|
+
"""An error raised when an invalid mount type is used for any available instruments."""
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class IncorrectAxisError(TypeError):
|
|
97
|
+
"""An error raised when an invalid axis key is provided in an axis map."""
|
|
98
|
+
|
|
99
|
+
|
|
78
100
|
class LabwareDefinitionIsNotAdapterError(ValueError):
|
|
79
101
|
"""An error raised when an adapter is attempted to be loaded as a labware."""
|
|
80
102
|
|
|
@@ -95,7 +117,7 @@ def ensure_mount_for_pipette(
|
|
|
95
117
|
mount: Union[str, Mount, None], pipette: PipetteNameType
|
|
96
118
|
) -> Mount:
|
|
97
119
|
"""Ensure that an input value represents a valid mount, and is valid for the given pipette."""
|
|
98
|
-
if pipette
|
|
120
|
+
if pipette in [PipetteNameType.P1000_96, PipetteNameType.P200_96]:
|
|
99
121
|
# Always validate the raw mount input, even if the pipette is a 96-channel and we're not going
|
|
100
122
|
# to use the mount value.
|
|
101
123
|
if mount is not None:
|
|
@@ -146,6 +168,25 @@ def _ensure_mount(mount: Union[str, Mount]) -> Mount:
|
|
|
146
168
|
)
|
|
147
169
|
|
|
148
170
|
|
|
171
|
+
def ensure_instrument_mount(mount: Union[str, Mount]) -> Mount:
|
|
172
|
+
"""Ensure that an input value represents a valid Mount for all instruments."""
|
|
173
|
+
if isinstance(mount, Mount):
|
|
174
|
+
return mount
|
|
175
|
+
|
|
176
|
+
if isinstance(mount, str):
|
|
177
|
+
if mount == "gripper":
|
|
178
|
+
# TODO (lc 08-02-2024) We should decide on the user facing name for
|
|
179
|
+
# the gripper mount axis.
|
|
180
|
+
mount = "extension"
|
|
181
|
+
try:
|
|
182
|
+
return Mount[mount.upper()]
|
|
183
|
+
except KeyError as e:
|
|
184
|
+
raise InstrumentMountTypeError(
|
|
185
|
+
"If mount is specified as a string, it must be 'left', 'right', 'gripper', or 'extension';"
|
|
186
|
+
f" instead, {mount} was given."
|
|
187
|
+
) from e
|
|
188
|
+
|
|
189
|
+
|
|
149
190
|
def ensure_pipette_name(pipette_name: str) -> PipetteNameType:
|
|
150
191
|
"""Ensure that an input value represents a valid pipette name."""
|
|
151
192
|
pipette_name = ensure_lowercase_name(pipette_name)
|
|
@@ -158,6 +199,79 @@ def ensure_pipette_name(pipette_name: str) -> PipetteNameType:
|
|
|
158
199
|
) from None
|
|
159
200
|
|
|
160
201
|
|
|
202
|
+
def _check_ot2_axis_type(
|
|
203
|
+
robot_type: RobotType, axis_map_keys: Union[List[str], List[AxisType]]
|
|
204
|
+
) -> None:
|
|
205
|
+
if robot_type == "OT-2 Standard" and isinstance(axis_map_keys[0], AxisType):
|
|
206
|
+
if any(k not in AxisType.ot2_axes() for k in axis_map_keys):
|
|
207
|
+
raise IncorrectAxisError(
|
|
208
|
+
f"An OT-2 Robot only accepts the following axes {AxisType.ot2_axes()}"
|
|
209
|
+
)
|
|
210
|
+
if robot_type == "OT-2 Standard" and isinstance(axis_map_keys[0], str):
|
|
211
|
+
if any(k.upper() not in [axis.value for axis in AxisType.ot2_axes()] for k in axis_map_keys): # type: ignore [union-attr]
|
|
212
|
+
raise IncorrectAxisError(
|
|
213
|
+
f"An OT-2 Robot only accepts the following axes {AxisType.ot2_axes()}"
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def _check_96_channel_axis_type(
|
|
218
|
+
is_96_channel: bool, axis_map_keys: Union[List[str], List[AxisType]]
|
|
219
|
+
) -> None:
|
|
220
|
+
if is_96_channel and any(
|
|
221
|
+
key_variation in axis_map_keys for key_variation in ["Z_R", "z_r", AxisType.Z_R]
|
|
222
|
+
):
|
|
223
|
+
raise IncorrectAxisError(
|
|
224
|
+
"A 96 channel is attached. You cannot move the `Z_R` mount."
|
|
225
|
+
)
|
|
226
|
+
if not is_96_channel and any(
|
|
227
|
+
key_variation in axis_map_keys for key_variation in ["Q", "q", AxisType.Q]
|
|
228
|
+
):
|
|
229
|
+
raise IncorrectAxisError(
|
|
230
|
+
"A 96 channel is not attached. The clamp `Q` motor does not exist."
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def ensure_axis_map_type(
|
|
235
|
+
axis_map: Union[AxisMapType, StringAxisMap],
|
|
236
|
+
robot_type: RobotType,
|
|
237
|
+
is_96_channel: bool = False,
|
|
238
|
+
) -> AxisMapType:
|
|
239
|
+
"""Ensure that the axis map provided is in the correct shape and contains the correct keys."""
|
|
240
|
+
axis_map_keys: Union[List[str], List[AxisType]] = list(axis_map.keys()) # type: ignore
|
|
241
|
+
key_type = set(type(k) for k in axis_map_keys)
|
|
242
|
+
|
|
243
|
+
if len(key_type) > 1:
|
|
244
|
+
raise IncorrectAxisError(
|
|
245
|
+
"Please provide an `axis_map` with only string or only AxisType keys."
|
|
246
|
+
)
|
|
247
|
+
_check_ot2_axis_type(robot_type, axis_map_keys)
|
|
248
|
+
_check_96_channel_axis_type(is_96_channel, axis_map_keys)
|
|
249
|
+
|
|
250
|
+
if all(isinstance(k, AxisType) for k in axis_map_keys):
|
|
251
|
+
return_map: AxisMapType = axis_map # type: ignore
|
|
252
|
+
return return_map
|
|
253
|
+
try:
|
|
254
|
+
return {AxisType[k.upper()]: v for k, v in axis_map.items()} # type: ignore [union-attr]
|
|
255
|
+
except KeyError as e:
|
|
256
|
+
raise IncorrectAxisError(f"{e} is not a supported `AxisMapType`")
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def ensure_only_gantry_axis_map_type(
|
|
260
|
+
axis_map: AxisMapType, robot_type: RobotType
|
|
261
|
+
) -> None:
|
|
262
|
+
"""Ensure that the axis map provided is in the correct shape and matches the gantry axes for the robot."""
|
|
263
|
+
if robot_type == "OT-2 Standard":
|
|
264
|
+
if any(k not in AxisType.ot2_gantry_axes() for k in axis_map.keys()):
|
|
265
|
+
raise IncorrectAxisError(
|
|
266
|
+
f"A critical point only accepts OT-2 gantry axes which are {AxisType.ot2_gantry_axes()}"
|
|
267
|
+
)
|
|
268
|
+
else:
|
|
269
|
+
if any(k not in AxisType.flex_gantry_axes() for k in axis_map.keys()):
|
|
270
|
+
raise IncorrectAxisError(
|
|
271
|
+
f"A critical point only accepts Flex gantry axes which are {AxisType.flex_gantry_axes()}"
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
|
|
161
275
|
# TODO(jbl 11-17-2023) this function's original purpose was ensure a valid deck slot for a given robot type
|
|
162
276
|
# With deck configuration, the shape of this should change to better represent it checking if a deck slot
|
|
163
277
|
# (and maybe any addressable area) being valid for that deck configuration
|
|
@@ -253,6 +367,27 @@ def ensure_definition_is_labware(definition: LabwareDefinition) -> None:
|
|
|
253
367
|
)
|
|
254
368
|
|
|
255
369
|
|
|
370
|
+
def ensure_definition_is_lid(definition: LabwareDefinition) -> None:
|
|
371
|
+
"""Ensure that one of the definition's allowed roles is `lid` or that that field is empty."""
|
|
372
|
+
if LabwareRole.lid not in definition.allowedRoles:
|
|
373
|
+
raise LabwareDefinitionIsNotLabwareError(
|
|
374
|
+
f"Labware {definition.parameters.loadName} is not a lid."
|
|
375
|
+
)
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
def ensure_definition_is_not_lid_after_api_version(
|
|
379
|
+
api_version: APIVersion, definition: LabwareDefinition
|
|
380
|
+
) -> None:
|
|
381
|
+
"""Ensure that one of the definition's allowed roles is not `lid` or that the API Version is below the release where lid loading was seperated."""
|
|
382
|
+
if (
|
|
383
|
+
LabwareRole.lid in definition.allowedRoles
|
|
384
|
+
and api_version >= LID_STACK_VERSION_GATE
|
|
385
|
+
):
|
|
386
|
+
raise APIVersionError(
|
|
387
|
+
f"Labware Lids cannot be loaded like standard labware in Protocols written with an API version greater than {LID_STACK_VERSION_GATE}."
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
|
|
256
391
|
_MODULE_ALIASES: Dict[str, ModuleModel] = {
|
|
257
392
|
"magdeck": MagneticModuleModel.MAGNETIC_V1,
|
|
258
393
|
"magnetic module": MagneticModuleModel.MAGNETIC_V1,
|
|
@@ -483,3 +618,127 @@ def validate_location(
|
|
|
483
618
|
if well is not None
|
|
484
619
|
else PointTarget(location=target_location, in_place=in_place)
|
|
485
620
|
)
|
|
621
|
+
|
|
622
|
+
|
|
623
|
+
def ensure_boolean(value: bool) -> bool:
|
|
624
|
+
"""Ensure value is a boolean."""
|
|
625
|
+
if not isinstance(value, bool):
|
|
626
|
+
raise ValueError("Value must be a boolean.")
|
|
627
|
+
return value
|
|
628
|
+
|
|
629
|
+
|
|
630
|
+
def ensure_float(value: Union[int, float]) -> float:
|
|
631
|
+
"""Ensure value is a float (or an integer) and return it as a float."""
|
|
632
|
+
if not isinstance(value, (int, float)):
|
|
633
|
+
raise ValueError("Value must be a floating point number.")
|
|
634
|
+
return float(value)
|
|
635
|
+
|
|
636
|
+
|
|
637
|
+
def ensure_positive_float(value: Union[int, float]) -> float:
|
|
638
|
+
"""Ensure value is a positive and real float value."""
|
|
639
|
+
float_value = ensure_float(value)
|
|
640
|
+
if isnan(float_value) or isinf(float_value):
|
|
641
|
+
raise ValueError("Value must be a defined, non-infinite number.")
|
|
642
|
+
if float_value < 0:
|
|
643
|
+
raise ValueError("Value must be a positive float.")
|
|
644
|
+
return float_value
|
|
645
|
+
|
|
646
|
+
|
|
647
|
+
def ensure_positive_int(value: int) -> int:
|
|
648
|
+
"""Ensure value is a positive integer."""
|
|
649
|
+
if not isinstance(value, int):
|
|
650
|
+
raise ValueError("Value must be an integer.")
|
|
651
|
+
if value < 0:
|
|
652
|
+
raise ValueError("Value must be a positive integer.")
|
|
653
|
+
return value
|
|
654
|
+
|
|
655
|
+
|
|
656
|
+
def validate_coordinates(value: Sequence[float]) -> Tuple[float, float, float]:
|
|
657
|
+
"""Ensure value is a valid sequence of 3 floats and return a tuple of 3 floats."""
|
|
658
|
+
if len(value) != 3:
|
|
659
|
+
raise ValueError("Coordinates must be a sequence of exactly three numbers")
|
|
660
|
+
if not all(isinstance(v, (float, int)) for v in value):
|
|
661
|
+
raise ValueError("All values in coordinates must be floats.")
|
|
662
|
+
return float(value[0]), float(value[1]), float(value[2])
|
|
663
|
+
|
|
664
|
+
|
|
665
|
+
def ensure_new_tip_policy(value: str) -> TransferTipPolicyV2:
|
|
666
|
+
"""Ensure that new_tip value is a valid TransferTipPolicy value."""
|
|
667
|
+
try:
|
|
668
|
+
return TransferTipPolicyV2(value.lower())
|
|
669
|
+
except ValueError:
|
|
670
|
+
raise ValueError(
|
|
671
|
+
f"'{value}' is invalid value for 'new_tip'."
|
|
672
|
+
f" Acceptable value is either 'never', 'once', 'always' or 'per source'."
|
|
673
|
+
)
|
|
674
|
+
|
|
675
|
+
|
|
676
|
+
def _verify_each_list_element_is_valid_location(locations: Sequence[Well]) -> None:
|
|
677
|
+
from .labware import Well
|
|
678
|
+
|
|
679
|
+
for loc in locations:
|
|
680
|
+
if not isinstance(loc, Well):
|
|
681
|
+
raise ValueError(
|
|
682
|
+
f"'{loc}' is not a valid location for transfer."
|
|
683
|
+
f" Location should be a well instance."
|
|
684
|
+
)
|
|
685
|
+
|
|
686
|
+
|
|
687
|
+
def ensure_valid_flat_wells_list_for_transfer_v2(
|
|
688
|
+
target: Union[Well, Sequence[Well], Sequence[Sequence[Well]]],
|
|
689
|
+
) -> List[Well]:
|
|
690
|
+
"""Ensure that the given target(s) for a liquid transfer are valid and in a flat list."""
|
|
691
|
+
from .labware import Well
|
|
692
|
+
|
|
693
|
+
if isinstance(target, Well):
|
|
694
|
+
return [target]
|
|
695
|
+
|
|
696
|
+
if isinstance(target, (list, tuple)):
|
|
697
|
+
if len(target) == 0:
|
|
698
|
+
raise ValueError("No target well(s) specified for transfer.")
|
|
699
|
+
if isinstance(target[0], (list, tuple)):
|
|
700
|
+
for sub_sequence in target:
|
|
701
|
+
_verify_each_list_element_is_valid_location(sub_sequence)
|
|
702
|
+
return [loc for sub_sequence in target for loc in sub_sequence]
|
|
703
|
+
else:
|
|
704
|
+
_verify_each_list_element_is_valid_location(target)
|
|
705
|
+
return list(target)
|
|
706
|
+
else:
|
|
707
|
+
raise ValueError(
|
|
708
|
+
f"'{target}' is not a valid location for transfer."
|
|
709
|
+
f" Location should be a well instance, or a 1-dimensional or"
|
|
710
|
+
f" 2-dimensional sequence of well instances."
|
|
711
|
+
)
|
|
712
|
+
|
|
713
|
+
|
|
714
|
+
def ensure_valid_tip_drop_location_for_transfer_v2(
|
|
715
|
+
tip_drop_location: Union[Location, Well, TrashBin, WasteChute]
|
|
716
|
+
) -> Union[Location, Well, TrashBin, WasteChute]:
|
|
717
|
+
"""Ensure that the tip drop location is valid for v2 transfer."""
|
|
718
|
+
from .labware import Well
|
|
719
|
+
|
|
720
|
+
if (
|
|
721
|
+
isinstance(tip_drop_location, Well)
|
|
722
|
+
or isinstance(tip_drop_location, TrashBin)
|
|
723
|
+
or isinstance(tip_drop_location, WasteChute)
|
|
724
|
+
):
|
|
725
|
+
return tip_drop_location
|
|
726
|
+
elif isinstance(tip_drop_location, Location):
|
|
727
|
+
_, maybe_well = tip_drop_location.labware.get_parent_labware_and_well()
|
|
728
|
+
|
|
729
|
+
if maybe_well is None:
|
|
730
|
+
raise TypeError(
|
|
731
|
+
"If a location is specified as a `types.Location`"
|
|
732
|
+
" (for instance, as the result of a call to `Well.top()`),"
|
|
733
|
+
" it must be a location relative to a well,"
|
|
734
|
+
" since that is where a tip is dropped."
|
|
735
|
+
" However, the given location doesn't refer to any well."
|
|
736
|
+
)
|
|
737
|
+
return tip_drop_location
|
|
738
|
+
else:
|
|
739
|
+
raise TypeError(
|
|
740
|
+
f"If specified, location should be an instance of"
|
|
741
|
+
f" `types.Location` (e.g. the return value from `Well.top()`)"
|
|
742
|
+
f" or `Well` (e.g. `reservoir.wells()[0]`) or an instance of `TrashBin` or `WasteChute`."
|
|
743
|
+
f" However, it is '{tip_drop_location}'."
|
|
744
|
+
)
|
|
@@ -57,6 +57,8 @@ from .types import (
|
|
|
57
57
|
ModuleModel,
|
|
58
58
|
ModuleDefinition,
|
|
59
59
|
Liquid,
|
|
60
|
+
LiquidClassRecord,
|
|
61
|
+
LiquidClassRecordWithId,
|
|
60
62
|
AllNozzleLayoutConfiguration,
|
|
61
63
|
SingleNozzleLayoutConfiguration,
|
|
62
64
|
RowNozzleLayoutConfiguration,
|
|
@@ -122,6 +124,8 @@ __all__ = [
|
|
|
122
124
|
"ModuleModel",
|
|
123
125
|
"ModuleDefinition",
|
|
124
126
|
"Liquid",
|
|
127
|
+
"LiquidClassRecord",
|
|
128
|
+
"LiquidClassRecordWithId",
|
|
125
129
|
"AllNozzleLayoutConfiguration",
|
|
126
130
|
"SingleNozzleLayoutConfiguration",
|
|
127
131
|
"RowNozzleLayoutConfiguration",
|
|
@@ -27,7 +27,6 @@ from ..types import (
|
|
|
27
27
|
ModuleDefinition,
|
|
28
28
|
Liquid,
|
|
29
29
|
DeckConfigurationType,
|
|
30
|
-
AddressableAreaLocation,
|
|
31
30
|
)
|
|
32
31
|
|
|
33
32
|
|
|
@@ -235,12 +234,12 @@ class SetDeckConfigurationAction:
|
|
|
235
234
|
class AddAddressableAreaAction:
|
|
236
235
|
"""Add a single addressable area to state.
|
|
237
236
|
|
|
238
|
-
This differs from the deck configuration in
|
|
237
|
+
This differs from the deck configuration in SetDeckConfigurationAction which
|
|
239
238
|
sends over a mapping of cutout fixtures. This action will only load one addressable
|
|
240
239
|
area and that should be pre-validated before being sent via the action.
|
|
241
240
|
"""
|
|
242
241
|
|
|
243
|
-
|
|
242
|
+
addressable_area_name: str
|
|
244
243
|
|
|
245
244
|
|
|
246
245
|
@dataclasses.dataclass(frozen=True)
|
|
@@ -77,6 +77,18 @@ class SyncClient:
|
|
|
77
77
|
) -> commands.LoadPipetteResult:
|
|
78
78
|
pass
|
|
79
79
|
|
|
80
|
+
@overload
|
|
81
|
+
def execute_command_without_recovery(
|
|
82
|
+
self, params: commands.LoadLidStackParams
|
|
83
|
+
) -> commands.LoadLidStackResult:
|
|
84
|
+
pass
|
|
85
|
+
|
|
86
|
+
@overload
|
|
87
|
+
def execute_command_without_recovery(
|
|
88
|
+
self, params: commands.LoadLidParams
|
|
89
|
+
) -> commands.LoadLidResult:
|
|
90
|
+
pass
|
|
91
|
+
|
|
80
92
|
@overload
|
|
81
93
|
def execute_command_without_recovery(
|
|
82
94
|
self, params: commands.LiquidProbeParams
|
|
@@ -89,6 +101,12 @@ class SyncClient:
|
|
|
89
101
|
) -> commands.TryLiquidProbeResult:
|
|
90
102
|
pass
|
|
91
103
|
|
|
104
|
+
@overload
|
|
105
|
+
def execute_command_without_recovery(
|
|
106
|
+
self, params: commands.LoadLiquidClassParams
|
|
107
|
+
) -> commands.LoadLiquidClassResult:
|
|
108
|
+
pass
|
|
109
|
+
|
|
92
110
|
def execute_command_without_recovery(
|
|
93
111
|
self, params: commands.CommandParams
|
|
94
112
|
) -> commands.CommandResult:
|