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,70 @@
|
|
|
1
|
+
"""Command models to open the latch of a Flex Stacker."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
from typing import Optional, Literal, TYPE_CHECKING
|
|
7
|
+
from typing_extensions import Type
|
|
8
|
+
|
|
9
|
+
from pydantic import BaseModel, Field
|
|
10
|
+
|
|
11
|
+
from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
|
|
12
|
+
from ...errors import (
|
|
13
|
+
ErrorOccurrence,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from ...state.state import StateView
|
|
18
|
+
from opentrons.protocol_engine.execution import EquipmentHandler
|
|
19
|
+
|
|
20
|
+
OpenLatchCommandType = Literal["flexStacker/openLatch"]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class OpenLatchParams(BaseModel):
|
|
24
|
+
"""The parameters defining how a stacker should open its latch."""
|
|
25
|
+
|
|
26
|
+
moduleId: str = Field(..., description="Unique ID of the Flex Stacker")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class OpenLatchResult(BaseModel):
|
|
30
|
+
"""Result data from a stacker OpenLatch command."""
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class OpenLatchImpl(AbstractCommandImpl[OpenLatchParams, SuccessData[OpenLatchResult]]):
|
|
34
|
+
"""Implementation of a stacker OpenLatch command."""
|
|
35
|
+
|
|
36
|
+
def __init__(
|
|
37
|
+
self, state_view: StateView, equipment: EquipmentHandler, **kwargs: object
|
|
38
|
+
) -> None:
|
|
39
|
+
self._state_view = state_view
|
|
40
|
+
self._equipment = equipment
|
|
41
|
+
|
|
42
|
+
async def execute(self, params: OpenLatchParams) -> SuccessData[OpenLatchResult]:
|
|
43
|
+
"""Execute the stacker OpenLatch command."""
|
|
44
|
+
stacker_state = self._state_view.modules.get_flex_stacker_substate(
|
|
45
|
+
params.moduleId
|
|
46
|
+
)
|
|
47
|
+
stacker_hw = self._equipment.get_module_hardware_api(stacker_state.module_id)
|
|
48
|
+
|
|
49
|
+
if stacker_hw is not None:
|
|
50
|
+
await stacker_hw.open_latch()
|
|
51
|
+
return SuccessData(public=OpenLatchResult())
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class OpenLatch(BaseCommand[OpenLatchParams, OpenLatchResult, ErrorOccurrence]):
|
|
55
|
+
"""A command to OpenLatch the Flex Stacker of labware."""
|
|
56
|
+
|
|
57
|
+
commandType: OpenLatchCommandType = "flexStacker/openLatch"
|
|
58
|
+
params: OpenLatchParams
|
|
59
|
+
result: Optional[OpenLatchResult] = None
|
|
60
|
+
|
|
61
|
+
_ImplementationCls: Type[OpenLatchImpl] = OpenLatchImpl
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class OpenLatchCreate(BaseCommandCreate[OpenLatchParams]):
|
|
65
|
+
"""A request to execute a Flex Stacker OpenLatch command."""
|
|
66
|
+
|
|
67
|
+
commandType: OpenLatchCommandType = "flexStacker/openLatch"
|
|
68
|
+
params: OpenLatchParams
|
|
69
|
+
|
|
70
|
+
_CommandCls: Type[OpenLatch] = OpenLatch
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"""Command models to prepare the stacker shuttle for movement."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
from typing import Literal, Union, TYPE_CHECKING
|
|
7
|
+
from typing_extensions import Type
|
|
8
|
+
|
|
9
|
+
from pydantic import BaseModel, Field
|
|
10
|
+
|
|
11
|
+
from .common import FlexStackerStallOrCollisionError
|
|
12
|
+
from opentrons_shared_data.errors.exceptions import FlexStackerStallError
|
|
13
|
+
|
|
14
|
+
from ..command import (
|
|
15
|
+
AbstractCommandImpl,
|
|
16
|
+
BaseCommand,
|
|
17
|
+
BaseCommandCreate,
|
|
18
|
+
SuccessData,
|
|
19
|
+
DefinedErrorData,
|
|
20
|
+
)
|
|
21
|
+
from ...errors import ErrorOccurrence
|
|
22
|
+
from ...resources import ModelUtils
|
|
23
|
+
|
|
24
|
+
if TYPE_CHECKING:
|
|
25
|
+
from ...state.state import StateView
|
|
26
|
+
from ...execution import EquipmentHandler
|
|
27
|
+
|
|
28
|
+
PrepareShuttleCommandType = Literal["flexStacker/prepareShuttle"]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class PrepareShuttleParams(BaseModel):
|
|
32
|
+
"""The parameters for a PrepareShuttle command."""
|
|
33
|
+
|
|
34
|
+
moduleId: str = Field(..., description="Unique ID of the Flex Stacker")
|
|
35
|
+
ignoreLatch: bool = Field(
|
|
36
|
+
default=False, description="Ignore the latch state of the shuttle"
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class PrepareShuttleResult(BaseModel):
|
|
41
|
+
"""Result data from a stacker PrepareShuttle command."""
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
_ExecuteReturn = Union[
|
|
45
|
+
SuccessData[PrepareShuttleResult],
|
|
46
|
+
DefinedErrorData[FlexStackerStallOrCollisionError],
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class PrepareShuttleImpl(AbstractCommandImpl[PrepareShuttleParams, _ExecuteReturn]):
|
|
51
|
+
"""Implementation of a stacker prepare shuttle command."""
|
|
52
|
+
|
|
53
|
+
def __init__(
|
|
54
|
+
self,
|
|
55
|
+
state_view: StateView,
|
|
56
|
+
equipment: EquipmentHandler,
|
|
57
|
+
model_utils: ModelUtils,
|
|
58
|
+
**kwargs: object,
|
|
59
|
+
) -> None:
|
|
60
|
+
self._state_view = state_view
|
|
61
|
+
self._equipment = equipment
|
|
62
|
+
self._model_utils = model_utils
|
|
63
|
+
|
|
64
|
+
async def execute(self, params: PrepareShuttleParams) -> _ExecuteReturn:
|
|
65
|
+
"""Execute the stacker prepare shuttle command."""
|
|
66
|
+
stacker_state = self._state_view.modules.get_flex_stacker_substate(
|
|
67
|
+
params.moduleId
|
|
68
|
+
)
|
|
69
|
+
# Allow propagation of ModuleNotAttachedError.
|
|
70
|
+
stacker_hw = self._equipment.get_module_hardware_api(stacker_state.module_id)
|
|
71
|
+
|
|
72
|
+
try:
|
|
73
|
+
if stacker_hw is not None:
|
|
74
|
+
await stacker_hw.home_all(params.ignoreLatch)
|
|
75
|
+
except FlexStackerStallError as e:
|
|
76
|
+
return DefinedErrorData(
|
|
77
|
+
public=FlexStackerStallOrCollisionError(
|
|
78
|
+
id=self._model_utils.generate_id(),
|
|
79
|
+
createdAt=self._model_utils.get_timestamp(),
|
|
80
|
+
wrappedErrors=[
|
|
81
|
+
ErrorOccurrence.from_failed(
|
|
82
|
+
id=self._model_utils.generate_id(),
|
|
83
|
+
createdAt=self._model_utils.get_timestamp(),
|
|
84
|
+
error=e,
|
|
85
|
+
)
|
|
86
|
+
],
|
|
87
|
+
),
|
|
88
|
+
)
|
|
89
|
+
# TODO we should also add a check for shuttle not detected error
|
|
90
|
+
|
|
91
|
+
return SuccessData(public=PrepareShuttleResult())
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class PrepareShuttle(
|
|
95
|
+
BaseCommand[PrepareShuttleParams, PrepareShuttleResult, ErrorOccurrence]
|
|
96
|
+
):
|
|
97
|
+
"""A command to prepare Flex Stacker shuttle."""
|
|
98
|
+
|
|
99
|
+
commandType: PrepareShuttleCommandType = "flexStacker/prepareShuttle"
|
|
100
|
+
params: PrepareShuttleParams
|
|
101
|
+
result: PrepareShuttleResult | None = None
|
|
102
|
+
|
|
103
|
+
_ImplementationCls: Type[PrepareShuttleImpl] = PrepareShuttleImpl
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class PrepareShuttleCreate(BaseCommandCreate[PrepareShuttleParams]):
|
|
107
|
+
"""A request to execute a Flex Stacker PrepareShuttle command."""
|
|
108
|
+
|
|
109
|
+
commandType: PrepareShuttleCommandType = "flexStacker/prepareShuttle"
|
|
110
|
+
params: PrepareShuttleParams
|
|
111
|
+
|
|
112
|
+
_CommandCls: Type[PrepareShuttle] = PrepareShuttle
|
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
"""Command models to retrieve a labware from a Flex Stacker."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
from typing import Literal, TYPE_CHECKING, Any, Union
|
|
5
|
+
from typing_extensions import Type
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel, Field
|
|
8
|
+
from pydantic.json_schema import SkipJsonSchema
|
|
9
|
+
|
|
10
|
+
from ..command import (
|
|
11
|
+
AbstractCommandImpl,
|
|
12
|
+
BaseCommand,
|
|
13
|
+
BaseCommandCreate,
|
|
14
|
+
SuccessData,
|
|
15
|
+
DefinedErrorData,
|
|
16
|
+
)
|
|
17
|
+
from ..flex_stacker.common import FlexStackerStallOrCollisionError
|
|
18
|
+
from ...errors import (
|
|
19
|
+
ErrorOccurrence,
|
|
20
|
+
CannotPerformModuleAction,
|
|
21
|
+
LocationIsOccupiedError,
|
|
22
|
+
FlexStackerLabwarePoolNotYetDefinedError,
|
|
23
|
+
)
|
|
24
|
+
from ...resources import ModelUtils
|
|
25
|
+
from ...state import update_types
|
|
26
|
+
from ...types import (
|
|
27
|
+
ModuleLocation,
|
|
28
|
+
OnLabwareLocation,
|
|
29
|
+
LabwareLocationSequence,
|
|
30
|
+
LabwareLocation,
|
|
31
|
+
LoadedLabware,
|
|
32
|
+
)
|
|
33
|
+
from opentrons_shared_data.labware.labware_definition import LabwareDefinition
|
|
34
|
+
from opentrons_shared_data.errors.exceptions import FlexStackerStallError
|
|
35
|
+
from opentrons.calibration_storage.helpers import uri_from_details
|
|
36
|
+
|
|
37
|
+
if TYPE_CHECKING:
|
|
38
|
+
from opentrons.protocol_engine.state.state import StateView
|
|
39
|
+
from opentrons.protocol_engine.state.module_substates import FlexStackerSubState
|
|
40
|
+
from opentrons.protocol_engine.execution import EquipmentHandler
|
|
41
|
+
|
|
42
|
+
RetrieveCommandType = Literal["flexStacker/retrieve"]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _remove_default(s: dict[str, Any]) -> None:
|
|
46
|
+
s.pop("default", None)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class RetrieveParams(BaseModel):
|
|
50
|
+
"""Input parameters for a labware retrieval command."""
|
|
51
|
+
|
|
52
|
+
moduleId: str = Field(
|
|
53
|
+
...,
|
|
54
|
+
description="Unique ID of the Flex Stacker.",
|
|
55
|
+
)
|
|
56
|
+
labwareId: str | SkipJsonSchema[None] = Field(
|
|
57
|
+
None,
|
|
58
|
+
description="An optional ID to assign to this labware. If None, an ID "
|
|
59
|
+
"will be generated.",
|
|
60
|
+
json_schema_extra=_remove_default,
|
|
61
|
+
)
|
|
62
|
+
displayName: str | SkipJsonSchema[None] = Field(
|
|
63
|
+
None,
|
|
64
|
+
description="An optional user-specified display name "
|
|
65
|
+
"or label for this labware.",
|
|
66
|
+
json_schema_extra=_remove_default,
|
|
67
|
+
)
|
|
68
|
+
adapterId: str | SkipJsonSchema[None] = Field(
|
|
69
|
+
None,
|
|
70
|
+
description="An optional ID to assign to an adapter. If None, an ID "
|
|
71
|
+
"will be generated.",
|
|
72
|
+
json_schema_extra=_remove_default,
|
|
73
|
+
)
|
|
74
|
+
lidId: str | SkipJsonSchema[None] = Field(
|
|
75
|
+
None,
|
|
76
|
+
description="An optional ID to assign to a lid. If None, an ID "
|
|
77
|
+
"will be generated.",
|
|
78
|
+
json_schema_extra=_remove_default,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class RetrieveResult(BaseModel):
|
|
83
|
+
"""Result data from a labware retrieval command."""
|
|
84
|
+
|
|
85
|
+
labwareId: str = Field(
|
|
86
|
+
...,
|
|
87
|
+
description="The labware ID of the primary retrieved labware.",
|
|
88
|
+
)
|
|
89
|
+
adapterId: str | None = Field(
|
|
90
|
+
None,
|
|
91
|
+
description="The optional Adapter Labware ID of the adapter under a primary labware.",
|
|
92
|
+
)
|
|
93
|
+
lidId: str | None = Field(
|
|
94
|
+
None,
|
|
95
|
+
description="The optional Lid Labware ID of the lid on a primary labware.",
|
|
96
|
+
)
|
|
97
|
+
primaryLocationSequence: LabwareLocationSequence = Field(
|
|
98
|
+
..., description="The origin location of the primary labware."
|
|
99
|
+
)
|
|
100
|
+
lidLocationSequence: LabwareLocationSequence | None = Field(
|
|
101
|
+
None,
|
|
102
|
+
description="The origin location of the adapter labware under a primary labware.",
|
|
103
|
+
)
|
|
104
|
+
adapterLocationSequence: LabwareLocationSequence | None = Field(
|
|
105
|
+
None, description="The origin location of the lid labware on a primary labware."
|
|
106
|
+
)
|
|
107
|
+
primaryLabwareURI: str = Field(
|
|
108
|
+
...,
|
|
109
|
+
description="The labware definition URI of the primary labware.",
|
|
110
|
+
)
|
|
111
|
+
adapterLabwareURI: str | None = Field(
|
|
112
|
+
None,
|
|
113
|
+
description="The labware definition URI of the adapter labware.",
|
|
114
|
+
)
|
|
115
|
+
lidLabwareURI: str | None = Field(
|
|
116
|
+
None,
|
|
117
|
+
description="The labware definition URI of the lid labware.",
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
_ExecuteReturn = Union[
|
|
122
|
+
SuccessData[RetrieveResult],
|
|
123
|
+
DefinedErrorData[FlexStackerStallOrCollisionError],
|
|
124
|
+
]
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class RetrieveImpl(AbstractCommandImpl[RetrieveParams, _ExecuteReturn]):
|
|
128
|
+
"""Implementation of a labware retrieval command."""
|
|
129
|
+
|
|
130
|
+
def __init__(
|
|
131
|
+
self,
|
|
132
|
+
state_view: StateView,
|
|
133
|
+
equipment: EquipmentHandler,
|
|
134
|
+
model_utils: ModelUtils,
|
|
135
|
+
**kwargs: object,
|
|
136
|
+
) -> None:
|
|
137
|
+
self._state_view = state_view
|
|
138
|
+
self._equipment = equipment
|
|
139
|
+
self._model_utils = model_utils
|
|
140
|
+
|
|
141
|
+
async def _load_labware_from_pool(
|
|
142
|
+
self, params: RetrieveParams, stacker_state: FlexStackerSubState
|
|
143
|
+
) -> tuple[RetrieveResult, update_types.StateUpdate]:
|
|
144
|
+
state_update = update_types.StateUpdate()
|
|
145
|
+
|
|
146
|
+
# If there is an adapter load it
|
|
147
|
+
adapter_lw = None
|
|
148
|
+
lid_lw = None
|
|
149
|
+
definitions_by_id: dict[str, LabwareDefinition] = {}
|
|
150
|
+
offset_ids_by_id: dict[str, str | None] = {}
|
|
151
|
+
display_names_by_id: dict[str, str | None] = {}
|
|
152
|
+
new_locations_by_id: dict[str, LabwareLocation] = {}
|
|
153
|
+
labware_by_id: dict[str, LoadedLabware] = {}
|
|
154
|
+
adapter_uri: str | None = None
|
|
155
|
+
if stacker_state.pool_adapter_definition is not None:
|
|
156
|
+
adapter_location = ModuleLocation(moduleId=params.moduleId)
|
|
157
|
+
adapter_lw = await self._equipment.load_labware_from_definition(
|
|
158
|
+
definition=stacker_state.pool_adapter_definition,
|
|
159
|
+
location=adapter_location,
|
|
160
|
+
labware_id=params.adapterId,
|
|
161
|
+
labware_pending_load=labware_by_id,
|
|
162
|
+
)
|
|
163
|
+
definitions_by_id[adapter_lw.labware_id] = adapter_lw.definition
|
|
164
|
+
offset_ids_by_id[adapter_lw.labware_id] = adapter_lw.offsetId
|
|
165
|
+
display_names_by_id[
|
|
166
|
+
adapter_lw.labware_id
|
|
167
|
+
] = adapter_lw.definition.metadata.displayName
|
|
168
|
+
new_locations_by_id[adapter_lw.labware_id] = adapter_location
|
|
169
|
+
adapter_uri = str(
|
|
170
|
+
uri_from_details(
|
|
171
|
+
namespace=adapter_lw.definition.namespace,
|
|
172
|
+
load_name=adapter_lw.definition.parameters.loadName,
|
|
173
|
+
version=adapter_lw.definition.version,
|
|
174
|
+
)
|
|
175
|
+
)
|
|
176
|
+
labware_by_id[adapter_lw.labware_id] = LoadedLabware.model_construct(
|
|
177
|
+
id=adapter_lw.labware_id,
|
|
178
|
+
location=adapter_location,
|
|
179
|
+
loadName=adapter_lw.definition.parameters.loadName,
|
|
180
|
+
definitionUri=adapter_uri,
|
|
181
|
+
offsetId=None,
|
|
182
|
+
)
|
|
183
|
+
# Always load the primary labware
|
|
184
|
+
if stacker_state.pool_primary_definition is None:
|
|
185
|
+
raise CannotPerformModuleAction(
|
|
186
|
+
f"Flex Stacker {params.moduleId} has no labware to retrieve"
|
|
187
|
+
)
|
|
188
|
+
primary_location: ModuleLocation | OnLabwareLocation = (
|
|
189
|
+
ModuleLocation(moduleId=params.moduleId)
|
|
190
|
+
if adapter_lw is None
|
|
191
|
+
else OnLabwareLocation(labwareId=adapter_lw.labware_id)
|
|
192
|
+
)
|
|
193
|
+
loaded_labware = await self._equipment.load_labware_from_definition(
|
|
194
|
+
definition=stacker_state.pool_primary_definition,
|
|
195
|
+
location=primary_location,
|
|
196
|
+
labware_id=params.labwareId,
|
|
197
|
+
labware_pending_load={lw_id: lw for lw_id, lw in labware_by_id.items()},
|
|
198
|
+
)
|
|
199
|
+
definitions_by_id[loaded_labware.labware_id] = loaded_labware.definition
|
|
200
|
+
offset_ids_by_id[loaded_labware.labware_id] = loaded_labware.offsetId
|
|
201
|
+
display_names_by_id[
|
|
202
|
+
loaded_labware.labware_id
|
|
203
|
+
] = loaded_labware.definition.metadata.displayName
|
|
204
|
+
new_locations_by_id[loaded_labware.labware_id] = primary_location
|
|
205
|
+
primary_uri = str(
|
|
206
|
+
uri_from_details(
|
|
207
|
+
namespace=loaded_labware.definition.namespace,
|
|
208
|
+
load_name=loaded_labware.definition.parameters.loadName,
|
|
209
|
+
version=loaded_labware.definition.version,
|
|
210
|
+
)
|
|
211
|
+
)
|
|
212
|
+
labware_by_id[loaded_labware.labware_id] = LoadedLabware.model_construct(
|
|
213
|
+
id=loaded_labware.labware_id,
|
|
214
|
+
location=primary_location,
|
|
215
|
+
loadName=loaded_labware.definition.parameters.loadName,
|
|
216
|
+
definitionUri=primary_uri,
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
lid_uri: str | None = None
|
|
220
|
+
# If there is a lid load it
|
|
221
|
+
if stacker_state.pool_lid_definition is not None:
|
|
222
|
+
lid_location = OnLabwareLocation(labwareId=loaded_labware.labware_id)
|
|
223
|
+
lid_lw = await self._equipment.load_labware_from_definition(
|
|
224
|
+
definition=stacker_state.pool_lid_definition,
|
|
225
|
+
location=lid_location,
|
|
226
|
+
labware_id=params.lidId,
|
|
227
|
+
labware_pending_load={lw_id: lw for lw_id, lw in labware_by_id.items()},
|
|
228
|
+
)
|
|
229
|
+
lid_uri = str(
|
|
230
|
+
uri_from_details(
|
|
231
|
+
namespace=lid_lw.definition.namespace,
|
|
232
|
+
load_name=lid_lw.definition.parameters.loadName,
|
|
233
|
+
version=lid_lw.definition.version,
|
|
234
|
+
)
|
|
235
|
+
)
|
|
236
|
+
definitions_by_id[lid_lw.labware_id] = lid_lw.definition
|
|
237
|
+
offset_ids_by_id[lid_lw.labware_id] = lid_lw.offsetId
|
|
238
|
+
display_names_by_id[
|
|
239
|
+
lid_lw.labware_id
|
|
240
|
+
] = lid_lw.definition.metadata.displayName
|
|
241
|
+
new_locations_by_id[lid_lw.labware_id] = lid_location
|
|
242
|
+
labware_by_id[lid_lw.labware_id] = LoadedLabware.model_construct(
|
|
243
|
+
id=lid_lw.labware_id,
|
|
244
|
+
location=lid_location,
|
|
245
|
+
loadName=lid_lw.definition.parameters.loadName,
|
|
246
|
+
definitionUri=lid_uri,
|
|
247
|
+
offsetId=None,
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
# Get the labware dimensions for the labware being retrieved,
|
|
251
|
+
# which is the first one in the hopper labware id list
|
|
252
|
+
primary_location_sequence = (
|
|
253
|
+
self._state_view.geometry.get_predicted_location_sequence(
|
|
254
|
+
new_locations_by_id[loaded_labware.labware_id], labware_by_id
|
|
255
|
+
)
|
|
256
|
+
)
|
|
257
|
+
adapter_location_sequence = (
|
|
258
|
+
self._state_view.geometry.get_predicted_location_sequence(
|
|
259
|
+
new_locations_by_id[adapter_lw.labware_id], labware_by_id
|
|
260
|
+
)
|
|
261
|
+
if adapter_lw is not None
|
|
262
|
+
else None
|
|
263
|
+
)
|
|
264
|
+
lid_location_sequence = (
|
|
265
|
+
self._state_view.geometry.get_predicted_location_sequence(
|
|
266
|
+
new_locations_by_id[lid_lw.labware_id], labware_by_id
|
|
267
|
+
)
|
|
268
|
+
if lid_lw is not None
|
|
269
|
+
else None
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
state_update.set_batch_loaded_labware(
|
|
273
|
+
definitions_by_id=definitions_by_id,
|
|
274
|
+
display_names_by_id=display_names_by_id,
|
|
275
|
+
offset_ids_by_id=offset_ids_by_id,
|
|
276
|
+
new_locations_by_id=new_locations_by_id,
|
|
277
|
+
)
|
|
278
|
+
state_update.update_flex_stacker_labware_pool_count(
|
|
279
|
+
module_id=params.moduleId, count=stacker_state.pool_count - 1
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
if lid_lw is not None:
|
|
283
|
+
state_update.set_lids(
|
|
284
|
+
parent_labware_ids=[loaded_labware.labware_id],
|
|
285
|
+
lid_ids=[lid_lw.labware_id],
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
return (
|
|
289
|
+
RetrieveResult(
|
|
290
|
+
labwareId=loaded_labware.labware_id,
|
|
291
|
+
adapterId=adapter_lw.labware_id if adapter_lw is not None else None,
|
|
292
|
+
lidId=lid_lw.labware_id if lid_lw is not None else None,
|
|
293
|
+
primaryLocationSequence=primary_location_sequence,
|
|
294
|
+
adapterLocationSequence=adapter_location_sequence,
|
|
295
|
+
lidLocationSequence=lid_location_sequence,
|
|
296
|
+
primaryLabwareURI=primary_uri,
|
|
297
|
+
adapterLabwareURI=adapter_uri,
|
|
298
|
+
lidLabwareURI=lid_uri,
|
|
299
|
+
),
|
|
300
|
+
state_update,
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
async def execute(self, params: RetrieveParams) -> _ExecuteReturn:
|
|
304
|
+
"""Execute the labware retrieval command."""
|
|
305
|
+
stacker_state = self._state_view.modules.get_flex_stacker_substate(
|
|
306
|
+
params.moduleId
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
pool_definitions = stacker_state.get_pool_definition_ordered_list()
|
|
310
|
+
if pool_definitions is None:
|
|
311
|
+
location = self._state_view.modules.get_location(params.moduleId)
|
|
312
|
+
raise FlexStackerLabwarePoolNotYetDefinedError(
|
|
313
|
+
message=f"The Flex Stacker in {location} has not been configured yet and cannot be filled."
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
if stacker_state.pool_count == 0:
|
|
317
|
+
raise CannotPerformModuleAction(
|
|
318
|
+
message="Cannot retrieve labware from Flex Stacker because it contains no labware"
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
stacker_loc = ModuleLocation(moduleId=params.moduleId)
|
|
322
|
+
# Allow propagation of ModuleNotAttachedError.
|
|
323
|
+
stacker_hw = self._equipment.get_module_hardware_api(stacker_state.module_id)
|
|
324
|
+
|
|
325
|
+
try:
|
|
326
|
+
self._state_view.labware.raise_if_labware_in_location(stacker_loc)
|
|
327
|
+
except LocationIsOccupiedError:
|
|
328
|
+
raise CannotPerformModuleAction(
|
|
329
|
+
"Cannot retrieve a labware from Flex Stacker if the carriage is occupied"
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
retrieve_result, state_update = await self._load_labware_from_pool(
|
|
333
|
+
params, stacker_state
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
labware_height = self._state_view.geometry.get_height_of_labware_stack(
|
|
337
|
+
definitions=pool_definitions
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
try:
|
|
341
|
+
if stacker_hw is not None:
|
|
342
|
+
await stacker_hw.dispense_labware(labware_height=labware_height)
|
|
343
|
+
except FlexStackerStallError as e:
|
|
344
|
+
return DefinedErrorData(
|
|
345
|
+
public=FlexStackerStallOrCollisionError(
|
|
346
|
+
id=self._model_utils.generate_id(),
|
|
347
|
+
createdAt=self._model_utils.get_timestamp(),
|
|
348
|
+
wrappedErrors=[
|
|
349
|
+
ErrorOccurrence.from_failed(
|
|
350
|
+
id=self._model_utils.generate_id(),
|
|
351
|
+
createdAt=self._model_utils.get_timestamp(),
|
|
352
|
+
error=e,
|
|
353
|
+
)
|
|
354
|
+
],
|
|
355
|
+
),
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
# Update the state to reflect the labware is now in the Flex Stacker slot
|
|
359
|
+
# todo(chb, 2025-02-19): This ModuleLocation piece should probably instead be an AddressableAreaLocation
|
|
360
|
+
# but that has implications for where labware are set by things like module.load_labware(..) and what
|
|
361
|
+
# happens when we move labware.
|
|
362
|
+
stacker_area = (
|
|
363
|
+
self._state_view.modules.ensure_and_convert_module_fixture_location(
|
|
364
|
+
deck_slot=self._state_view.modules.get_location(
|
|
365
|
+
params.moduleId
|
|
366
|
+
).slotName,
|
|
367
|
+
model=self._state_view.modules.get(params.moduleId).model,
|
|
368
|
+
)
|
|
369
|
+
)
|
|
370
|
+
state_update.set_addressable_area_used(stacker_area)
|
|
371
|
+
|
|
372
|
+
return SuccessData(
|
|
373
|
+
public=retrieve_result,
|
|
374
|
+
state_update=state_update,
|
|
375
|
+
)
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
class Retrieve(BaseCommand[RetrieveParams, RetrieveResult, ErrorOccurrence]):
|
|
379
|
+
"""A command to retrieve a labware from a Flex Stacker."""
|
|
380
|
+
|
|
381
|
+
commandType: RetrieveCommandType = "flexStacker/retrieve"
|
|
382
|
+
params: RetrieveParams
|
|
383
|
+
result: RetrieveResult | None = None
|
|
384
|
+
|
|
385
|
+
_ImplementationCls: Type[RetrieveImpl] = RetrieveImpl
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
class RetrieveCreate(BaseCommandCreate[RetrieveParams]):
|
|
389
|
+
"""A request to execute a Flex Stacker retrieve command."""
|
|
390
|
+
|
|
391
|
+
commandType: RetrieveCommandType = "flexStacker/retrieve"
|
|
392
|
+
params: RetrieveParams
|
|
393
|
+
|
|
394
|
+
_CommandCls: Type[Retrieve] = Retrieve
|