opentrons 8.3.1a1__py2.py3-none-any.whl → 8.4.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.
- opentrons/calibration_storage/ot2/mark_bad_calibration.py +2 -0
- opentrons/calibration_storage/ot2/tip_length.py +6 -6
- opentrons/config/advanced_settings.py +9 -11
- opentrons/config/feature_flags.py +0 -4
- opentrons/config/reset.py +7 -2
- opentrons/drivers/asyncio/communication/__init__.py +2 -0
- opentrons/drivers/asyncio/communication/async_serial.py +4 -0
- opentrons/drivers/asyncio/communication/errors.py +41 -8
- opentrons/drivers/asyncio/communication/serial_connection.py +36 -10
- opentrons/drivers/flex_stacker/__init__.py +9 -3
- opentrons/drivers/flex_stacker/abstract.py +140 -15
- opentrons/drivers/flex_stacker/driver.py +593 -47
- opentrons/drivers/flex_stacker/errors.py +64 -0
- opentrons/drivers/flex_stacker/simulator.py +222 -24
- opentrons/drivers/flex_stacker/types.py +211 -15
- opentrons/drivers/flex_stacker/utils.py +19 -0
- opentrons/execute.py +4 -2
- opentrons/hardware_control/api.py +5 -0
- opentrons/hardware_control/backends/flex_protocol.py +4 -0
- opentrons/hardware_control/backends/ot3controller.py +12 -1
- opentrons/hardware_control/backends/ot3simulator.py +3 -0
- opentrons/hardware_control/backends/subsystem_manager.py +8 -4
- opentrons/hardware_control/instruments/ot2/instrument_calibration.py +10 -6
- opentrons/hardware_control/instruments/ot3/pipette_handler.py +59 -6
- opentrons/hardware_control/modules/__init__.py +12 -1
- opentrons/hardware_control/modules/absorbance_reader.py +11 -9
- opentrons/hardware_control/modules/flex_stacker.py +498 -0
- opentrons/hardware_control/modules/heater_shaker.py +12 -10
- opentrons/hardware_control/modules/magdeck.py +5 -1
- opentrons/hardware_control/modules/tempdeck.py +5 -1
- opentrons/hardware_control/modules/thermocycler.py +15 -14
- opentrons/hardware_control/modules/types.py +191 -1
- opentrons/hardware_control/modules/utils.py +3 -0
- opentrons/hardware_control/motion_utilities.py +20 -0
- opentrons/hardware_control/ot3api.py +145 -15
- opentrons/hardware_control/protocols/liquid_handler.py +47 -1
- opentrons/hardware_control/types.py +6 -0
- opentrons/legacy_commands/commands.py +19 -3
- opentrons/legacy_commands/helpers.py +15 -0
- opentrons/legacy_commands/types.py +3 -2
- opentrons/protocol_api/__init__.py +2 -0
- opentrons/protocol_api/_liquid.py +39 -8
- opentrons/protocol_api/_liquid_properties.py +20 -19
- opentrons/protocol_api/_transfer_liquid_validation.py +91 -0
- opentrons/protocol_api/core/common.py +3 -1
- opentrons/protocol_api/core/engine/deck_conflict.py +11 -1
- opentrons/protocol_api/core/engine/instrument.py +1233 -65
- opentrons/protocol_api/core/engine/labware.py +8 -4
- opentrons/protocol_api/core/engine/load_labware_params.py +68 -10
- opentrons/protocol_api/core/engine/module_core.py +118 -2
- opentrons/protocol_api/core/engine/protocol.py +253 -11
- opentrons/protocol_api/core/engine/stringify.py +19 -8
- opentrons/protocol_api/core/engine/transfer_components_executor.py +853 -0
- opentrons/protocol_api/core/engine/well.py +60 -5
- opentrons/protocol_api/core/instrument.py +65 -19
- opentrons/protocol_api/core/labware.py +6 -2
- opentrons/protocol_api/core/legacy/labware_offset_provider.py +7 -3
- opentrons/protocol_api/core/legacy/legacy_instrument_core.py +69 -21
- opentrons/protocol_api/core/legacy/legacy_labware_core.py +8 -4
- opentrons/protocol_api/core/legacy/legacy_protocol_core.py +36 -0
- opentrons/protocol_api/core/legacy/legacy_well_core.py +25 -1
- opentrons/protocol_api/core/legacy/load_info.py +4 -12
- opentrons/protocol_api/core/legacy/module_geometry.py +6 -1
- opentrons/protocol_api/core/legacy/well_geometry.py +3 -3
- opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +67 -21
- opentrons/protocol_api/core/module.py +43 -0
- opentrons/protocol_api/core/protocol.py +33 -0
- opentrons/protocol_api/core/well.py +21 -1
- opentrons/protocol_api/instrument_context.py +246 -123
- opentrons/protocol_api/labware.py +75 -11
- opentrons/protocol_api/module_contexts.py +140 -0
- opentrons/protocol_api/protocol_context.py +156 -16
- opentrons/protocol_api/validation.py +51 -41
- opentrons/protocol_engine/__init__.py +21 -2
- opentrons/protocol_engine/actions/actions.py +5 -5
- opentrons/protocol_engine/clients/sync_client.py +6 -0
- opentrons/protocol_engine/commands/__init__.py +30 -0
- opentrons/protocol_engine/commands/absorbance_reader/__init__.py +0 -1
- opentrons/protocol_engine/commands/air_gap_in_place.py +3 -2
- opentrons/protocol_engine/commands/aspirate.py +6 -2
- opentrons/protocol_engine/commands/aspirate_in_place.py +3 -1
- opentrons/protocol_engine/commands/aspirate_while_tracking.py +237 -0
- opentrons/protocol_engine/commands/blow_out.py +2 -0
- opentrons/protocol_engine/commands/blow_out_in_place.py +4 -1
- opentrons/protocol_engine/commands/command_unions.py +69 -0
- opentrons/protocol_engine/commands/configure_for_volume.py +3 -0
- opentrons/protocol_engine/commands/dispense.py +3 -1
- opentrons/protocol_engine/commands/dispense_in_place.py +3 -0
- opentrons/protocol_engine/commands/dispense_while_tracking.py +240 -0
- opentrons/protocol_engine/commands/drop_tip.py +23 -1
- opentrons/protocol_engine/commands/evotip_dispense.py +6 -7
- opentrons/protocol_engine/commands/evotip_seal_pipette.py +24 -29
- opentrons/protocol_engine/commands/evotip_unseal_pipette.py +1 -7
- opentrons/protocol_engine/commands/flex_stacker/__init__.py +106 -0
- opentrons/protocol_engine/commands/flex_stacker/close_latch.py +72 -0
- opentrons/protocol_engine/commands/flex_stacker/common.py +15 -0
- opentrons/protocol_engine/commands/flex_stacker/empty.py +161 -0
- opentrons/protocol_engine/commands/flex_stacker/fill.py +164 -0
- opentrons/protocol_engine/commands/flex_stacker/open_latch.py +70 -0
- opentrons/protocol_engine/commands/flex_stacker/prepare_shuttle.py +112 -0
- opentrons/protocol_engine/commands/flex_stacker/retrieve.py +394 -0
- opentrons/protocol_engine/commands/flex_stacker/set_stored_labware.py +190 -0
- opentrons/protocol_engine/commands/flex_stacker/store.py +288 -0
- opentrons/protocol_engine/commands/generate_command_schema.py +31 -2
- opentrons/protocol_engine/commands/labware_handling_common.py +24 -0
- opentrons/protocol_engine/commands/liquid_probe.py +21 -12
- opentrons/protocol_engine/commands/load_labware.py +42 -39
- opentrons/protocol_engine/commands/load_lid.py +21 -13
- opentrons/protocol_engine/commands/load_lid_stack.py +130 -47
- opentrons/protocol_engine/commands/load_module.py +18 -17
- opentrons/protocol_engine/commands/load_pipette.py +3 -0
- opentrons/protocol_engine/commands/move_labware.py +139 -20
- opentrons/protocol_engine/commands/pick_up_tip.py +5 -2
- opentrons/protocol_engine/commands/pipetting_common.py +154 -7
- opentrons/protocol_engine/commands/prepare_to_aspirate.py +17 -2
- opentrons/protocol_engine/commands/reload_labware.py +6 -19
- opentrons/protocol_engine/commands/unsafe/unsafe_blow_out_in_place.py +3 -1
- opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +6 -1
- opentrons/protocol_engine/errors/__init__.py +8 -0
- opentrons/protocol_engine/errors/exceptions.py +50 -0
- opentrons/protocol_engine/execution/equipment.py +123 -106
- opentrons/protocol_engine/execution/labware_movement.py +8 -6
- opentrons/protocol_engine/execution/pipetting.py +233 -26
- opentrons/protocol_engine/execution/tip_handler.py +14 -5
- opentrons/protocol_engine/labware_offset_standardization.py +173 -0
- opentrons/protocol_engine/protocol_engine.py +22 -13
- opentrons/protocol_engine/resources/deck_configuration_provider.py +94 -2
- opentrons/protocol_engine/resources/deck_data_provider.py +1 -1
- opentrons/protocol_engine/resources/labware_data_provider.py +32 -12
- opentrons/protocol_engine/resources/labware_validation.py +7 -5
- opentrons/protocol_engine/slot_standardization.py +11 -23
- opentrons/protocol_engine/state/addressable_areas.py +84 -46
- opentrons/protocol_engine/state/frustum_helpers.py +26 -10
- opentrons/protocol_engine/state/geometry.py +683 -100
- opentrons/protocol_engine/state/labware.py +252 -55
- opentrons/protocol_engine/state/module_substates/__init__.py +4 -0
- opentrons/protocol_engine/state/module_substates/flex_stacker_substate.py +68 -0
- opentrons/protocol_engine/state/module_substates/heater_shaker_module_substate.py +22 -0
- opentrons/protocol_engine/state/module_substates/temperature_module_substate.py +13 -0
- opentrons/protocol_engine/state/module_substates/thermocycler_module_substate.py +20 -0
- opentrons/protocol_engine/state/modules.py +178 -52
- opentrons/protocol_engine/state/pipettes.py +54 -0
- opentrons/protocol_engine/state/state.py +1 -1
- opentrons/protocol_engine/state/tips.py +14 -0
- opentrons/protocol_engine/state/update_types.py +180 -25
- opentrons/protocol_engine/state/wells.py +54 -8
- opentrons/protocol_engine/types/__init__.py +292 -0
- opentrons/protocol_engine/types/automatic_tip_selection.py +39 -0
- opentrons/protocol_engine/types/command_annotations.py +53 -0
- opentrons/protocol_engine/types/deck_configuration.py +72 -0
- opentrons/protocol_engine/types/execution.py +96 -0
- opentrons/protocol_engine/types/hardware_passthrough.py +25 -0
- opentrons/protocol_engine/types/instrument.py +47 -0
- opentrons/protocol_engine/types/instrument_sensors.py +47 -0
- opentrons/protocol_engine/types/labware.py +110 -0
- opentrons/protocol_engine/types/labware_movement.py +22 -0
- opentrons/protocol_engine/types/labware_offset_location.py +108 -0
- opentrons/protocol_engine/types/labware_offset_vector.py +33 -0
- opentrons/protocol_engine/types/liquid.py +40 -0
- opentrons/protocol_engine/types/liquid_class.py +59 -0
- opentrons/protocol_engine/types/liquid_handling.py +13 -0
- opentrons/protocol_engine/types/liquid_level_detection.py +137 -0
- opentrons/protocol_engine/types/location.py +193 -0
- opentrons/protocol_engine/types/module.py +269 -0
- opentrons/protocol_engine/types/partial_tip_configuration.py +76 -0
- opentrons/protocol_engine/types/run_time_parameters.py +133 -0
- opentrons/protocol_engine/types/tip.py +18 -0
- opentrons/protocol_engine/types/util.py +21 -0
- opentrons/protocol_engine/types/well_position.py +107 -0
- opentrons/protocol_reader/extract_labware_definitions.py +7 -3
- opentrons/protocol_reader/file_format_validator.py +5 -3
- opentrons/protocol_runner/json_translator.py +4 -2
- opentrons/protocol_runner/legacy_command_mapper.py +6 -2
- opentrons/protocol_runner/run_orchestrator.py +4 -1
- opentrons/protocols/advanced_control/transfers/common.py +48 -1
- opentrons/protocols/advanced_control/transfers/transfer_liquid_utils.py +204 -0
- opentrons/protocols/api_support/definitions.py +1 -1
- opentrons/protocols/api_support/instrument.py +16 -3
- opentrons/protocols/labware.py +5 -6
- opentrons/protocols/models/__init__.py +0 -21
- opentrons/simulate.py +4 -2
- opentrons/types.py +15 -6
- {opentrons-8.3.1a1.dist-info → opentrons-8.4.0a1.dist-info}/METADATA +4 -4
- {opentrons-8.3.1a1.dist-info → opentrons-8.4.0a1.dist-info}/RECORD +188 -148
- opentrons/calibration_storage/ot2/models/defaults.py +0 -0
- opentrons/calibration_storage/ot3/models/defaults.py +0 -0
- opentrons/protocol_api/core/legacy/legacy_robot_core.py +0 -0
- opentrons/protocol_engine/types.py +0 -1311
- {opentrons-8.3.1a1.dist-info → opentrons-8.4.0a1.dist-info}/LICENSE +0 -0
- {opentrons-8.3.1a1.dist-info → opentrons-8.4.0a1.dist-info}/WHEEL +0 -0
- {opentrons-8.3.1a1.dist-info → opentrons-8.4.0a1.dist-info}/entry_points.txt +0 -0
- {opentrons-8.3.1a1.dist-info → opentrons-8.4.0a1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"""Command models to configure the stored labware pool of a Flex Stacker.."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
from typing import Optional, Literal, TYPE_CHECKING
|
|
5
|
+
from typing_extensions import Type
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel, Field
|
|
8
|
+
|
|
9
|
+
from opentrons_shared_data.labware.labware_definition import LabwareDefinition
|
|
10
|
+
|
|
11
|
+
from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
|
|
12
|
+
from ...errors import (
|
|
13
|
+
ErrorOccurrence,
|
|
14
|
+
)
|
|
15
|
+
from ...errors.exceptions import FlexStackerNotLogicallyEmptyError
|
|
16
|
+
from ...state import update_types
|
|
17
|
+
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from opentrons.protocol_engine.state.state import StateView
|
|
20
|
+
from opentrons.protocol_engine.execution import EquipmentHandler
|
|
21
|
+
|
|
22
|
+
SetStoredLabwareCommandType = Literal["flexStacker/setStoredLabware"]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class StackerStoredLabwareDetails(BaseModel):
|
|
26
|
+
"""The parameters defining a labware to be stored in the stacker."""
|
|
27
|
+
|
|
28
|
+
loadName: str = Field(
|
|
29
|
+
..., description="Name used to reference the definition of this labware."
|
|
30
|
+
)
|
|
31
|
+
namespace: str = Field(
|
|
32
|
+
..., description="Namespace of the definition of this labware."
|
|
33
|
+
)
|
|
34
|
+
version: int = Field(..., description="Version of the definition of this labware.")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class SetStoredLabwareParams(BaseModel):
|
|
38
|
+
"""Input parameters for a setStoredLabware command."""
|
|
39
|
+
|
|
40
|
+
moduleId: str = Field(
|
|
41
|
+
...,
|
|
42
|
+
description="Unique ID of the Flex Stacker.",
|
|
43
|
+
)
|
|
44
|
+
primaryLabware: StackerStoredLabwareDetails = Field(
|
|
45
|
+
...,
|
|
46
|
+
description="The details of the primary labware (i.e. not the lid or adapter, if any) stored in the stacker.",
|
|
47
|
+
)
|
|
48
|
+
lidLabware: StackerStoredLabwareDetails | None = Field(
|
|
49
|
+
default=None,
|
|
50
|
+
description="The details of the lid on the primary labware, if any.",
|
|
51
|
+
)
|
|
52
|
+
adapterLabware: StackerStoredLabwareDetails | None = Field(
|
|
53
|
+
default=None,
|
|
54
|
+
description="The details of the adapter under the primary labware, if any.",
|
|
55
|
+
)
|
|
56
|
+
initialCount: int | None = Field(
|
|
57
|
+
None,
|
|
58
|
+
description=(
|
|
59
|
+
"The number of labware that should be initially stored in the stacker. This number will be silently clamped to "
|
|
60
|
+
"the maximum number of labware that will fit; do not rely on the parameter to know how many labware are in the stacker."
|
|
61
|
+
),
|
|
62
|
+
ge=0,
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class SetStoredLabwareResult(BaseModel):
|
|
67
|
+
"""Result data from a setstoredlabware command."""
|
|
68
|
+
|
|
69
|
+
primaryLabwareDefinition: LabwareDefinition = Field(
|
|
70
|
+
..., description="The definition of the primary labware."
|
|
71
|
+
)
|
|
72
|
+
lidLabwareDefinition: LabwareDefinition | None = Field(
|
|
73
|
+
..., description="The definition of the lid on the primary labware, if any."
|
|
74
|
+
)
|
|
75
|
+
adapterLabwareDefinition: LabwareDefinition | None = Field(
|
|
76
|
+
...,
|
|
77
|
+
description="The definition of the adapter under the primary labware, if any.",
|
|
78
|
+
)
|
|
79
|
+
count: int = Field(
|
|
80
|
+
..., description="The number of labware loaded into the stacker labware pool."
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class SetStoredLabwareImpl(
|
|
85
|
+
AbstractCommandImpl[SetStoredLabwareParams, SuccessData[SetStoredLabwareResult]]
|
|
86
|
+
):
|
|
87
|
+
"""Implementation of a setstoredlabware command."""
|
|
88
|
+
|
|
89
|
+
def __init__(
|
|
90
|
+
self,
|
|
91
|
+
state_view: StateView,
|
|
92
|
+
equipment: EquipmentHandler,
|
|
93
|
+
**kwargs: object,
|
|
94
|
+
) -> None:
|
|
95
|
+
self._state_view = state_view
|
|
96
|
+
self._equipment = equipment
|
|
97
|
+
|
|
98
|
+
async def execute(
|
|
99
|
+
self, params: SetStoredLabwareParams
|
|
100
|
+
) -> SuccessData[SetStoredLabwareResult]:
|
|
101
|
+
"""Execute the setstoredlabwarecommand."""
|
|
102
|
+
stacker_state = self._state_view.modules.get_flex_stacker_substate(
|
|
103
|
+
params.moduleId
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
if stacker_state.pool_count != 0:
|
|
107
|
+
# Note: this error catches if the protocol tells us the stacker is not empty, making this command
|
|
108
|
+
# invalid at this point in the protocol. This error is not recoverable and should occur during
|
|
109
|
+
# analysis; the protocol must be changed.
|
|
110
|
+
|
|
111
|
+
raise FlexStackerNotLogicallyEmptyError(
|
|
112
|
+
message=(
|
|
113
|
+
"The Flex Stacker must be known to be empty before reconfiguring its labware pool, but it has "
|
|
114
|
+
f"a pool of {stacker_state.pool_count} labware"
|
|
115
|
+
)
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
labware_def, _ = await self._equipment.load_definition_for_details(
|
|
119
|
+
load_name=params.primaryLabware.loadName,
|
|
120
|
+
namespace=params.primaryLabware.namespace,
|
|
121
|
+
version=params.primaryLabware.version,
|
|
122
|
+
)
|
|
123
|
+
lid_def: LabwareDefinition | None = None
|
|
124
|
+
if params.lidLabware:
|
|
125
|
+
lid_def, _ = await self._equipment.load_definition_for_details(
|
|
126
|
+
load_name=params.lidLabware.loadName,
|
|
127
|
+
namespace=params.lidLabware.namespace,
|
|
128
|
+
version=params.lidLabware.version,
|
|
129
|
+
)
|
|
130
|
+
adapter_def: LabwareDefinition | None = None
|
|
131
|
+
if params.adapterLabware:
|
|
132
|
+
adapter_def, _ = await self._equipment.load_definition_for_details(
|
|
133
|
+
load_name=params.adapterLabware.loadName,
|
|
134
|
+
namespace=params.adapterLabware.namespace,
|
|
135
|
+
version=params.adapterLabware.version,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
self._state_view.labware.raise_if_stacker_labware_pool_is_not_valid(
|
|
139
|
+
labware_def, lid_def, adapter_def
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
pool_height = self._state_view.geometry.get_height_of_labware_stack(
|
|
143
|
+
[x for x in [lid_def, labware_def, adapter_def] if x is not None]
|
|
144
|
+
)
|
|
145
|
+
max_pool_count = self._state_view.modules.stacker_max_pool_count_by_height(
|
|
146
|
+
params.moduleId, pool_height
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
initial_count = (
|
|
150
|
+
params.initialCount if params.initialCount is not None else max_pool_count
|
|
151
|
+
)
|
|
152
|
+
count = min(initial_count, max_pool_count)
|
|
153
|
+
|
|
154
|
+
state_update = (
|
|
155
|
+
update_types.StateUpdate()
|
|
156
|
+
.update_flex_stacker_labware_pool_definition(
|
|
157
|
+
params.moduleId, max_pool_count, labware_def, adapter_def, lid_def
|
|
158
|
+
)
|
|
159
|
+
.update_flex_stacker_labware_pool_count(params.moduleId, count)
|
|
160
|
+
)
|
|
161
|
+
return SuccessData(
|
|
162
|
+
public=SetStoredLabwareResult.model_construct(
|
|
163
|
+
primaryLabwareDefinition=labware_def,
|
|
164
|
+
lidLabwareDefinition=lid_def,
|
|
165
|
+
adapterLabwareDefinition=adapter_def,
|
|
166
|
+
count=count,
|
|
167
|
+
),
|
|
168
|
+
state_update=state_update,
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
class SetStoredLabware(
|
|
173
|
+
BaseCommand[SetStoredLabwareParams, SetStoredLabwareResult, ErrorOccurrence]
|
|
174
|
+
):
|
|
175
|
+
"""A command to setstoredlabware the Flex Stacker."""
|
|
176
|
+
|
|
177
|
+
commandType: SetStoredLabwareCommandType = "flexStacker/setStoredLabware"
|
|
178
|
+
params: SetStoredLabwareParams
|
|
179
|
+
result: Optional[SetStoredLabwareResult] = None
|
|
180
|
+
|
|
181
|
+
_ImplementationCls: Type[SetStoredLabwareImpl] = SetStoredLabwareImpl
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
class SetStoredLabwareCreate(BaseCommandCreate[SetStoredLabwareParams]):
|
|
185
|
+
"""A request to execute a Flex Stacker SetStoredLabware command."""
|
|
186
|
+
|
|
187
|
+
commandType: SetStoredLabwareCommandType = "flexStacker/setStoredLabware"
|
|
188
|
+
params: SetStoredLabwareParams
|
|
189
|
+
|
|
190
|
+
_CommandCls: Type[SetStoredLabware] = SetStoredLabware
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
"""Command models to retrieve a labware from a Flex Stacker."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
from typing import Optional, Literal, TYPE_CHECKING, Type, Union
|
|
5
|
+
|
|
6
|
+
from pydantic import BaseModel, Field
|
|
7
|
+
|
|
8
|
+
from ..command import (
|
|
9
|
+
AbstractCommandImpl,
|
|
10
|
+
BaseCommand,
|
|
11
|
+
BaseCommandCreate,
|
|
12
|
+
SuccessData,
|
|
13
|
+
DefinedErrorData,
|
|
14
|
+
)
|
|
15
|
+
from ..flex_stacker.common import FlexStackerStallOrCollisionError
|
|
16
|
+
from ...errors import (
|
|
17
|
+
ErrorOccurrence,
|
|
18
|
+
CannotPerformModuleAction,
|
|
19
|
+
LabwareNotLoadedOnModuleError,
|
|
20
|
+
FlexStackerLabwarePoolNotYetDefinedError,
|
|
21
|
+
)
|
|
22
|
+
from ...resources import ModelUtils
|
|
23
|
+
from ...state import update_types
|
|
24
|
+
from ...types import (
|
|
25
|
+
LabwareLocationSequence,
|
|
26
|
+
InStackerHopperLocation,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
from opentrons_shared_data.errors.exceptions import FlexStackerStallError
|
|
30
|
+
from opentrons.calibration_storage.helpers import uri_from_details
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
if TYPE_CHECKING:
|
|
34
|
+
from opentrons.protocol_engine.state.state import StateView
|
|
35
|
+
from opentrons.protocol_engine.state.module_substates import FlexStackerSubState
|
|
36
|
+
from opentrons.protocol_engine.execution import EquipmentHandler
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
StoreCommandType = Literal["flexStacker/store"]
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class StoreParams(BaseModel):
|
|
43
|
+
"""Input parameters for a labware storage command."""
|
|
44
|
+
|
|
45
|
+
moduleId: str = Field(
|
|
46
|
+
...,
|
|
47
|
+
description="Unique ID of the flex stacker.",
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class StoreResult(BaseModel):
|
|
52
|
+
"""Result data from a labware storage command."""
|
|
53
|
+
|
|
54
|
+
eventualDestinationLocationSequence: LabwareLocationSequence | None = Field(
|
|
55
|
+
None,
|
|
56
|
+
description=(
|
|
57
|
+
"The full location in which all labware moved by this command will eventually reside."
|
|
58
|
+
),
|
|
59
|
+
)
|
|
60
|
+
primaryOriginLocationSequence: LabwareLocationSequence | None = Field(
|
|
61
|
+
None, description=("The origin location of the primary labware.")
|
|
62
|
+
)
|
|
63
|
+
primaryLabwareId: str | None = Field(
|
|
64
|
+
None, description="The primary labware in the stack that was stored."
|
|
65
|
+
)
|
|
66
|
+
adapterOriginLocationSequence: LabwareLocationSequence | None = Field(
|
|
67
|
+
None, description=("The origin location of the adapter labware, if any.")
|
|
68
|
+
)
|
|
69
|
+
adapterLabwareId: str | None = Field(
|
|
70
|
+
None, description="The adapter in the stack that was stored, if any."
|
|
71
|
+
)
|
|
72
|
+
lidOriginLocationSequence: LabwareLocationSequence | None = Field(
|
|
73
|
+
None, description=("The origin location of the lid labware, if any.")
|
|
74
|
+
)
|
|
75
|
+
lidLabwareId: str | None = Field(
|
|
76
|
+
None, description="The lid in the stack that was stored, if any."
|
|
77
|
+
)
|
|
78
|
+
primaryLabwareURI: str = Field(
|
|
79
|
+
...,
|
|
80
|
+
description="The labware definition URI of the primary labware.",
|
|
81
|
+
)
|
|
82
|
+
adapterLabwareURI: str | None = Field(
|
|
83
|
+
None,
|
|
84
|
+
description="The labware definition URI of the adapter labware.",
|
|
85
|
+
)
|
|
86
|
+
lidLabwareURI: str | None = Field(
|
|
87
|
+
None,
|
|
88
|
+
description="The labware definition URI of the lid labware.",
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
_ExecuteReturn = Union[
|
|
93
|
+
SuccessData[StoreResult],
|
|
94
|
+
DefinedErrorData[FlexStackerStallOrCollisionError],
|
|
95
|
+
]
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class StoreImpl(AbstractCommandImpl[StoreParams, _ExecuteReturn]):
|
|
99
|
+
"""Implementation of a labware storage command."""
|
|
100
|
+
|
|
101
|
+
def __init__(
|
|
102
|
+
self,
|
|
103
|
+
state_view: StateView,
|
|
104
|
+
equipment: EquipmentHandler,
|
|
105
|
+
model_utils: ModelUtils,
|
|
106
|
+
**kwargs: object,
|
|
107
|
+
) -> None:
|
|
108
|
+
self._state_view = state_view
|
|
109
|
+
self._equipment = equipment
|
|
110
|
+
self._model_utils = model_utils
|
|
111
|
+
|
|
112
|
+
def _verify_labware_to_store(
|
|
113
|
+
self, params: StoreParams, stacker_state: FlexStackerSubState
|
|
114
|
+
) -> tuple[str, str | None, str | None]:
|
|
115
|
+
try:
|
|
116
|
+
bottom_id = self._state_view.labware.get_id_by_module(params.moduleId)
|
|
117
|
+
except LabwareNotLoadedOnModuleError:
|
|
118
|
+
raise CannotPerformModuleAction(
|
|
119
|
+
"Cannot store labware if Flex Stacker carriage is empty"
|
|
120
|
+
)
|
|
121
|
+
labware_ids = self._state_view.labware.get_labware_stack_from_parent(bottom_id)
|
|
122
|
+
labware_defs = [
|
|
123
|
+
self._state_view.labware.get_definition(id) for id in labware_ids
|
|
124
|
+
]
|
|
125
|
+
|
|
126
|
+
lid_id: str | None = None
|
|
127
|
+
|
|
128
|
+
pool_list = stacker_state.get_pool_definition_ordered_list()
|
|
129
|
+
assert pool_list is not None
|
|
130
|
+
if len(labware_ids) != len(pool_list):
|
|
131
|
+
raise CannotPerformModuleAction(
|
|
132
|
+
"Cannot store labware stack that does not correspond with Flex Stacker configuration"
|
|
133
|
+
)
|
|
134
|
+
if stacker_state.pool_lid_definition is not None:
|
|
135
|
+
if labware_defs[-1] != stacker_state.pool_lid_definition:
|
|
136
|
+
raise CannotPerformModuleAction(
|
|
137
|
+
"Cannot store labware stack that does not correspond with Flex Stacker configuration"
|
|
138
|
+
)
|
|
139
|
+
lid_id = labware_ids[-1]
|
|
140
|
+
|
|
141
|
+
if stacker_state.pool_adapter_definition is not None:
|
|
142
|
+
if (
|
|
143
|
+
labware_defs[0] != stacker_state.pool_adapter_definition
|
|
144
|
+
or labware_defs[1] != stacker_state.pool_primary_definition
|
|
145
|
+
):
|
|
146
|
+
raise CannotPerformModuleAction(
|
|
147
|
+
"Cannot store labware stack that does not correspond with Flex Stacker configuration"
|
|
148
|
+
)
|
|
149
|
+
else:
|
|
150
|
+
return labware_ids[1], labware_ids[0], lid_id
|
|
151
|
+
else:
|
|
152
|
+
if labware_defs[0] != stacker_state.pool_primary_definition:
|
|
153
|
+
raise CannotPerformModuleAction(
|
|
154
|
+
"Cannot store labware stack that does not correspond with Flex Stacker configuration"
|
|
155
|
+
)
|
|
156
|
+
return labware_ids[0], None, lid_id
|
|
157
|
+
|
|
158
|
+
async def execute(self, params: StoreParams) -> _ExecuteReturn:
|
|
159
|
+
"""Execute the labware storage command."""
|
|
160
|
+
stacker_state = self._state_view.modules.get_flex_stacker_substate(
|
|
161
|
+
params.moduleId
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
if stacker_state.pool_count == stacker_state.max_pool_count:
|
|
165
|
+
raise CannotPerformModuleAction(
|
|
166
|
+
"Cannot store labware in Flex Stacker while it is full"
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
pool_definitions = stacker_state.get_pool_definition_ordered_list()
|
|
170
|
+
if pool_definitions is None:
|
|
171
|
+
raise FlexStackerLabwarePoolNotYetDefinedError(
|
|
172
|
+
message="The Flex Stacker has not been configured yet and cannot be filled."
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
primary_id, maybe_adapter_id, maybe_lid_id = self._verify_labware_to_store(
|
|
176
|
+
params, stacker_state
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
# Allow propagation of ModuleNotAttachedError.
|
|
180
|
+
stacker_hw = self._equipment.get_module_hardware_api(stacker_state.module_id)
|
|
181
|
+
|
|
182
|
+
eventual_target_location_sequence = (
|
|
183
|
+
self._state_view.geometry.get_predicted_location_sequence(
|
|
184
|
+
InStackerHopperLocation(moduleId=params.moduleId)
|
|
185
|
+
)
|
|
186
|
+
)
|
|
187
|
+
stack_height = self._state_view.geometry.get_height_of_labware_stack(
|
|
188
|
+
pool_definitions
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
state_update = update_types.StateUpdate()
|
|
192
|
+
try:
|
|
193
|
+
if stacker_hw is not None:
|
|
194
|
+
await stacker_hw.store_labware(labware_height=stack_height)
|
|
195
|
+
except FlexStackerStallError as e:
|
|
196
|
+
return DefinedErrorData(
|
|
197
|
+
public=FlexStackerStallOrCollisionError(
|
|
198
|
+
id=self._model_utils.generate_id(),
|
|
199
|
+
createdAt=self._model_utils.get_timestamp(),
|
|
200
|
+
wrappedErrors=[
|
|
201
|
+
ErrorOccurrence.from_failed(
|
|
202
|
+
id=self._model_utils.generate_id(),
|
|
203
|
+
createdAt=self._model_utils.get_timestamp(),
|
|
204
|
+
error=e,
|
|
205
|
+
)
|
|
206
|
+
],
|
|
207
|
+
),
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
id_list = [
|
|
211
|
+
id for id in (primary_id, maybe_adapter_id, maybe_lid_id) if id is not None
|
|
212
|
+
]
|
|
213
|
+
|
|
214
|
+
state_update.set_batch_labware_location(
|
|
215
|
+
new_locations_by_id={
|
|
216
|
+
id: InStackerHopperLocation(moduleId=params.moduleId) for id in id_list
|
|
217
|
+
},
|
|
218
|
+
new_offset_ids_by_id={id: None for id in id_list},
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
state_update.update_flex_stacker_labware_pool_count(
|
|
222
|
+
module_id=params.moduleId, count=stacker_state.pool_count + 1
|
|
223
|
+
)
|
|
224
|
+
if stacker_state.pool_primary_definition is None:
|
|
225
|
+
raise FlexStackerLabwarePoolNotYetDefinedError(
|
|
226
|
+
"The Primary Labware must be defined in the stacker pool."
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
return SuccessData(
|
|
230
|
+
public=StoreResult(
|
|
231
|
+
eventualDestinationLocationSequence=eventual_target_location_sequence,
|
|
232
|
+
primaryOriginLocationSequence=self._state_view.geometry.get_location_sequence(
|
|
233
|
+
primary_id
|
|
234
|
+
),
|
|
235
|
+
primaryLabwareId=primary_id,
|
|
236
|
+
adapterOriginLocationSequence=(
|
|
237
|
+
self._state_view.geometry.get_location_sequence(maybe_adapter_id)
|
|
238
|
+
if maybe_adapter_id is not None
|
|
239
|
+
else None
|
|
240
|
+
),
|
|
241
|
+
adapterLabwareId=maybe_adapter_id,
|
|
242
|
+
lidOriginLocationSequence=(
|
|
243
|
+
self._state_view.geometry.get_location_sequence(maybe_lid_id)
|
|
244
|
+
if maybe_lid_id is not None
|
|
245
|
+
else None
|
|
246
|
+
),
|
|
247
|
+
lidLabwareId=maybe_lid_id,
|
|
248
|
+
primaryLabwareURI=uri_from_details(
|
|
249
|
+
stacker_state.pool_primary_definition.namespace,
|
|
250
|
+
stacker_state.pool_primary_definition.parameters.loadName,
|
|
251
|
+
stacker_state.pool_primary_definition.version,
|
|
252
|
+
),
|
|
253
|
+
adapterLabwareURI=uri_from_details(
|
|
254
|
+
stacker_state.pool_adapter_definition.namespace,
|
|
255
|
+
stacker_state.pool_adapter_definition.parameters.loadName,
|
|
256
|
+
stacker_state.pool_adapter_definition.version,
|
|
257
|
+
)
|
|
258
|
+
if stacker_state.pool_adapter_definition is not None
|
|
259
|
+
else None,
|
|
260
|
+
lidLabwareURI=uri_from_details(
|
|
261
|
+
stacker_state.pool_lid_definition.namespace,
|
|
262
|
+
stacker_state.pool_lid_definition.parameters.loadName,
|
|
263
|
+
stacker_state.pool_lid_definition.version,
|
|
264
|
+
)
|
|
265
|
+
if stacker_state.pool_lid_definition is not None
|
|
266
|
+
else None,
|
|
267
|
+
),
|
|
268
|
+
state_update=state_update,
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
class Store(BaseCommand[StoreParams, StoreResult, ErrorOccurrence]):
|
|
273
|
+
"""A command to store a labware in a Flex Stacker."""
|
|
274
|
+
|
|
275
|
+
commandType: StoreCommandType = "flexStacker/store"
|
|
276
|
+
params: StoreParams
|
|
277
|
+
result: Optional[StoreResult] = None
|
|
278
|
+
|
|
279
|
+
_ImplementationCls: Type[StoreImpl] = StoreImpl
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
class StoreCreate(BaseCommandCreate[StoreParams]):
|
|
283
|
+
"""A request to execute a Flex Stacker store command."""
|
|
284
|
+
|
|
285
|
+
commandType: StoreCommandType = "flexStacker/store"
|
|
286
|
+
params: StoreParams
|
|
287
|
+
|
|
288
|
+
_CommandCls: Type[Store] = Store
|
|
@@ -5,6 +5,9 @@ import argparse
|
|
|
5
5
|
import sys
|
|
6
6
|
from opentrons.protocol_engine.commands.command_unions import CommandCreateAdapter
|
|
7
7
|
|
|
8
|
+
from opentrons_shared_data.command import get_newest_schema_version
|
|
9
|
+
from opentrons_shared_data.load import get_shared_data_root
|
|
10
|
+
|
|
8
11
|
|
|
9
12
|
def generate_command_schema(version: str) -> str:
|
|
10
13
|
"""Generate a JSON Schema that all valid create commands can validate against."""
|
|
@@ -14,6 +17,13 @@ def generate_command_schema(version: str) -> str:
|
|
|
14
17
|
return json.dumps(schema_as_dict, indent=2, sort_keys=True)
|
|
15
18
|
|
|
16
19
|
|
|
20
|
+
def write_command_schema(json_string: str, version: str) -> None:
|
|
21
|
+
"""Write a JSON command schema to the shared-data command schema directory."""
|
|
22
|
+
path = get_shared_data_root() / "command" / "schemas" / f"{version}.json"
|
|
23
|
+
with open(path, "w") as schema_file:
|
|
24
|
+
schema_file.write(json_string)
|
|
25
|
+
|
|
26
|
+
|
|
17
27
|
if __name__ == "__main__":
|
|
18
28
|
parser = argparse.ArgumentParser(
|
|
19
29
|
prog="generate_command_schema",
|
|
@@ -22,10 +32,29 @@ if __name__ == "__main__":
|
|
|
22
32
|
parser.add_argument(
|
|
23
33
|
"version",
|
|
24
34
|
type=str,
|
|
25
|
-
|
|
35
|
+
nargs="?",
|
|
36
|
+
help="The command schema version. This is a single integer (e.g. 7) that will be used to name the generated"
|
|
37
|
+
" schema file. If not included, it will automatically use the latest version in shared-data.",
|
|
38
|
+
)
|
|
39
|
+
parser.add_argument(
|
|
40
|
+
"--overwrite-shared-data",
|
|
41
|
+
action="store_true",
|
|
42
|
+
help="If used, overwrites the specified or automatically chosen command schema version in shared-data."
|
|
43
|
+
" If not included, the generated schema will be printed to stdout.",
|
|
26
44
|
)
|
|
27
45
|
args = parser.parse_args()
|
|
28
|
-
|
|
46
|
+
|
|
47
|
+
if args.version is None:
|
|
48
|
+
version_string = get_newest_schema_version()
|
|
49
|
+
else:
|
|
50
|
+
version_string = args.version
|
|
51
|
+
|
|
52
|
+
command_schema = generate_command_schema(version_string)
|
|
53
|
+
|
|
54
|
+
if args.overwrite_shared_data:
|
|
55
|
+
write_command_schema(command_schema, version_string)
|
|
56
|
+
else:
|
|
57
|
+
print(command_schema)
|
|
29
58
|
|
|
30
59
|
sys.exit()
|
|
31
60
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Helpers for commands that alter the position of labware."""
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, Field
|
|
4
|
+
|
|
5
|
+
from ..types import LabwareLocationSequence
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class LabwareHandlingResultMixin(BaseModel):
|
|
9
|
+
"""A result for commands that create a labware entity."""
|
|
10
|
+
|
|
11
|
+
labwareId: str = Field(..., description="The id of the labware.")
|
|
12
|
+
locationSequence: LabwareLocationSequence | None = Field(
|
|
13
|
+
None,
|
|
14
|
+
description="The full location down to the deck on which this labware exists.",
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class LabwarePositionResultMixin(LabwareHandlingResultMixin):
|
|
19
|
+
"""A result for commands that create an offsetable labware entity."""
|
|
20
|
+
|
|
21
|
+
offsetId: str | None = Field(
|
|
22
|
+
None,
|
|
23
|
+
description="An ID referencing the labware offset that will apply to this labware in this location.",
|
|
24
|
+
)
|
|
@@ -23,6 +23,7 @@ from opentrons_shared_data.errors.exceptions import (
|
|
|
23
23
|
)
|
|
24
24
|
|
|
25
25
|
from ..types import DeckPoint
|
|
26
|
+
from ..types.liquid_level_detection import LiquidTrackingType
|
|
26
27
|
from .pipetting_common import (
|
|
27
28
|
LiquidNotFoundError,
|
|
28
29
|
PipetteIdMixin,
|
|
@@ -80,7 +81,7 @@ class TryLiquidProbeParams(_CommonParams):
|
|
|
80
81
|
class LiquidProbeResult(DestinationPositionResult):
|
|
81
82
|
"""Result data from the execution of a `liquidProbe` command."""
|
|
82
83
|
|
|
83
|
-
z_position:
|
|
84
|
+
z_position: LiquidTrackingType = Field(
|
|
84
85
|
..., description="The Z coordinate, in mm, of the found liquid in deck space."
|
|
85
86
|
)
|
|
86
87
|
# New fields should use camelCase. z_position is snake_case for historical reasons.
|
|
@@ -89,7 +90,7 @@ class LiquidProbeResult(DestinationPositionResult):
|
|
|
89
90
|
class TryLiquidProbeResult(DestinationPositionResult):
|
|
90
91
|
"""Result data from the execution of a `tryLiquidProbe` command."""
|
|
91
92
|
|
|
92
|
-
z_position:
|
|
93
|
+
z_position: Union[LiquidTrackingType, SkipJsonSchema[None]] = Field(
|
|
93
94
|
...,
|
|
94
95
|
description=(
|
|
95
96
|
"The Z coordinate, in mm, of the found liquid in deck space."
|
|
@@ -116,8 +117,7 @@ class _ExecuteCommonResult(NamedTuple):
|
|
|
116
117
|
# If the probe succeeded, the z_pos that it returned.
|
|
117
118
|
# Or, if the probe found no liquid, the error representing that,
|
|
118
119
|
# so calling code can propagate those details up.
|
|
119
|
-
z_pos_or_error:
|
|
120
|
-
|
|
120
|
+
z_pos_or_error: LiquidTrackingType | PipetteLiquidNotFoundError | PipetteOverpressureError
|
|
121
121
|
state_update: update_types.StateUpdate
|
|
122
122
|
deck_point: DeckPoint
|
|
123
123
|
|
|
@@ -190,6 +190,9 @@ async def _execute_common( # noqa: C901
|
|
|
190
190
|
well_location=params.wellLocation,
|
|
191
191
|
)
|
|
192
192
|
except PipetteLiquidNotFoundError as exception:
|
|
193
|
+
move_result.state_update.set_pipette_ready_to_aspirate(
|
|
194
|
+
pipette_id=pipette_id, ready_to_aspirate=True
|
|
195
|
+
)
|
|
193
196
|
return _ExecuteCommonResult(
|
|
194
197
|
z_pos_or_error=exception,
|
|
195
198
|
state_update=move_result.state_update,
|
|
@@ -223,6 +226,9 @@ async def _execute_common( # noqa: C901
|
|
|
223
226
|
),
|
|
224
227
|
)
|
|
225
228
|
else:
|
|
229
|
+
move_result.state_update.set_pipette_ready_to_aspirate(
|
|
230
|
+
pipette_id=pipette_id, ready_to_aspirate=True
|
|
231
|
+
)
|
|
226
232
|
return _ExecuteCommonResult(
|
|
227
233
|
z_pos_or_error=z_pos,
|
|
228
234
|
state_update=move_result.state_update,
|
|
@@ -303,12 +309,13 @@ class LiquidProbeImplementation(
|
|
|
303
309
|
)
|
|
304
310
|
else:
|
|
305
311
|
try:
|
|
306
|
-
well_volume:
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
+
well_volume: Union[
|
|
313
|
+
LiquidTrackingType,
|
|
314
|
+
update_types.ClearType,
|
|
315
|
+
] = self._state_view.geometry.get_well_volume_at_height(
|
|
316
|
+
labware_id=params.labwareId,
|
|
317
|
+
well_name=params.wellName,
|
|
318
|
+
height=z_pos_or_error,
|
|
312
319
|
)
|
|
313
320
|
except IncompleteLabwareDefinitionError:
|
|
314
321
|
well_volume = update_types.CLEAR
|
|
@@ -370,7 +377,10 @@ class TryLiquidProbeImplementation(
|
|
|
370
377
|
z_pos_or_error, (PipetteLiquidNotFoundError, PipetteOverpressureError)
|
|
371
378
|
):
|
|
372
379
|
z_pos = None
|
|
373
|
-
well_volume:
|
|
380
|
+
well_volume: Union[
|
|
381
|
+
LiquidTrackingType,
|
|
382
|
+
update_types.ClearType,
|
|
383
|
+
] = update_types.CLEAR
|
|
374
384
|
else:
|
|
375
385
|
z_pos = z_pos_or_error
|
|
376
386
|
try:
|
|
@@ -387,7 +397,6 @@ class TryLiquidProbeImplementation(
|
|
|
387
397
|
volume=well_volume,
|
|
388
398
|
last_probed=self._model_utils.get_timestamp(),
|
|
389
399
|
)
|
|
390
|
-
|
|
391
400
|
return SuccessData(
|
|
392
401
|
public=TryLiquidProbeResult(
|
|
393
402
|
z_position=z_pos,
|