opentrons 8.3.1a1__py2.py3-none-any.whl → 8.4.0a0__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of opentrons might be problematic. Click here for more details.
- opentrons/calibration_storage/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 +7 -2
- 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 +245 -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 +2 -9
- 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 +3 -1
- 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/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.0a0.dist-info}/METADATA +4 -4
- {opentrons-8.3.1a1.dist-info → opentrons-8.4.0a0.dist-info}/RECORD +187 -147
- 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.0a0.dist-info}/LICENSE +0 -0
- {opentrons-8.3.1a1.dist-info → opentrons-8.4.0a0.dist-info}/WHEEL +0 -0
- {opentrons-8.3.1a1.dist-info → opentrons-8.4.0a0.dist-info}/entry_points.txt +0 -0
- {opentrons-8.3.1a1.dist-info → opentrons-8.4.0a0.dist-info}/top_level.txt +0 -0
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
from typing import List, Optional, cast, Dict
|
|
4
4
|
|
|
5
5
|
from opentrons_shared_data.labware.types import (
|
|
6
|
-
|
|
6
|
+
LabwareParameters2 as LabwareParameters2Dict,
|
|
7
|
+
LabwareParameters3 as LabwareParameters3Dict,
|
|
7
8
|
LabwareDefinition as LabwareDefinitionDict,
|
|
8
9
|
)
|
|
9
10
|
|
|
@@ -29,6 +30,9 @@ from ..labware import AbstractLabware, LabwareLoadParams
|
|
|
29
30
|
from .well import WellCore
|
|
30
31
|
|
|
31
32
|
|
|
33
|
+
_LabwareParametersDict = LabwareParameters2Dict | LabwareParameters3Dict
|
|
34
|
+
|
|
35
|
+
|
|
32
36
|
class LabwareCore(AbstractLabware[WellCore]):
|
|
33
37
|
"""Labware API core using a ProtocolEngine.
|
|
34
38
|
|
|
@@ -96,9 +100,9 @@ class LabwareCore(AbstractLabware[WellCore]):
|
|
|
96
100
|
LabwareDefinitionDict, self._definition.model_dump(exclude_none=True)
|
|
97
101
|
)
|
|
98
102
|
|
|
99
|
-
def get_parameters(self) ->
|
|
103
|
+
def get_parameters(self) -> _LabwareParametersDict:
|
|
100
104
|
return cast(
|
|
101
|
-
|
|
105
|
+
_LabwareParametersDict,
|
|
102
106
|
self._definition.parameters.model_dump(exclude_none=True),
|
|
103
107
|
)
|
|
104
108
|
|
|
@@ -122,7 +126,7 @@ class LabwareCore(AbstractLabware[WellCore]):
|
|
|
122
126
|
|
|
123
127
|
request = LabwareOffsetCreate.model_construct(
|
|
124
128
|
definitionUri=self.get_uri(),
|
|
125
|
-
|
|
129
|
+
locationSequence=offset_location,
|
|
126
130
|
vector=LabwareOffsetVector(x=delta.x, y=delta.y, z=delta.z),
|
|
127
131
|
)
|
|
128
132
|
self._engine_client.add_labware_offset(request)
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
from typing import Dict, Tuple, List, Optional
|
|
2
|
-
|
|
3
1
|
from opentrons.protocols.api_support.constants import OPENTRONS_NAMESPACE
|
|
4
2
|
from opentrons.protocol_engine.state.labware import LabwareLoadParams
|
|
3
|
+
from opentrons.protocols.api_support.types import APIVersion
|
|
5
4
|
|
|
6
5
|
|
|
7
6
|
# Default versions of Opentrons standard labware definitions in Python Protocol API
|
|
@@ -9,7 +8,7 @@ from opentrons.protocol_engine.state.labware import LabwareLoadParams
|
|
|
9
8
|
#
|
|
10
9
|
# TODO(jbl 2023-08-01) this needs to be done more holistically, both to find the version and make sure that
|
|
11
10
|
# it corresponds to the API level is was released with
|
|
12
|
-
_APILEVEL_2_14_OT_DEFAULT_VERSIONS:
|
|
11
|
+
_APILEVEL_2_14_OT_DEFAULT_VERSIONS: dict[str, int] = {
|
|
13
12
|
# v1 of many labware definitions have wrong `zDimension`s. (Jira RSS-202.)
|
|
14
13
|
# For "opentrons_96_aluminumblock_generic_pcr_strip_200ul" and
|
|
15
14
|
# "opentrons_24_aluminumblock_generic_2ml_screwcap", they're wrong enough to
|
|
@@ -34,6 +33,54 @@ _APILEVEL_2_14_OT_DEFAULT_VERSIONS: Dict[str, int] = {
|
|
|
34
33
|
"corning_24_wellplate_3.4ml_flat": 2,
|
|
35
34
|
}
|
|
36
35
|
|
|
36
|
+
_APILEVEL_2_23_OT_DEFAULT_VERSIONS: dict[str, int] = {
|
|
37
|
+
"agilent_1_reservoir_290ml": 2,
|
|
38
|
+
"appliedbiosystemsmicroamp_384_wellplate_40ul": 2,
|
|
39
|
+
"armadillo_96_wellplate_200ul_pcr_full_skirt": 3,
|
|
40
|
+
"axygen_1_reservoir_90ml": 2,
|
|
41
|
+
"biorad_384_wellplate_50ul": 3,
|
|
42
|
+
"biorad_96_wellplate_200ul_pcr": 3,
|
|
43
|
+
"corning_12_wellplate_6.9ml_flat": 3,
|
|
44
|
+
"corning_24_wellplate_3.4ml_flat": 3,
|
|
45
|
+
"corning_384_wellplate_112ul_flat": 3,
|
|
46
|
+
"corning_48_wellplate_1.6ml_flat": 3,
|
|
47
|
+
"corning_6_wellplate_16.8ml_flat": 3,
|
|
48
|
+
"corning_96_wellplate_360ul_flat": 3,
|
|
49
|
+
"nest_12_reservoir_15ml": 2,
|
|
50
|
+
"nest_1_reservoir_195ml": 3,
|
|
51
|
+
"nest_1_reservoir_290ml": 2,
|
|
52
|
+
"nest_96_wellplate_100ul_pcr_full_skirt": 3,
|
|
53
|
+
"nest_96_wellplate_200ul_flat": 3,
|
|
54
|
+
"nest_96_wellplate_2ml_deep": 3,
|
|
55
|
+
"opentrons_10_tuberack_falcon_4x50ml_6x15ml_conical": 2,
|
|
56
|
+
"opentrons_10_tuberack_nest_4x50ml_6x15ml_conical": 2,
|
|
57
|
+
"opentrons_15_tuberack_falcon_15ml_conical": 2,
|
|
58
|
+
"opentrons_15_tuberack_nest_15ml_conical": 2,
|
|
59
|
+
"opentrons_24_aluminumblock_generic_2ml_screwcap": 3,
|
|
60
|
+
"opentrons_24_aluminumblock_nest_0.5ml_screwcap": 2,
|
|
61
|
+
"opentrons_24_aluminumblock_nest_1.5ml_screwcap": 2,
|
|
62
|
+
"opentrons_24_aluminumblock_nest_1.5ml_snapcap": 2,
|
|
63
|
+
"opentrons_24_aluminumblock_nest_2ml_screwcap": 2,
|
|
64
|
+
"opentrons_24_aluminumblock_nest_2ml_snapcap": 2,
|
|
65
|
+
"opentrons_24_tuberack_eppendorf_1.5ml_safelock_snapcap": 2,
|
|
66
|
+
"opentrons_24_tuberack_eppendorf_2ml_safelock_snapcap": 2,
|
|
67
|
+
"opentrons_24_tuberack_generic_2ml_screwcap": 2,
|
|
68
|
+
"opentrons_24_tuberack_nest_0.5ml_screwcap": 2,
|
|
69
|
+
"opentrons_24_tuberack_nest_1.5ml_screwcap": 2,
|
|
70
|
+
"opentrons_24_tuberack_nest_1.5ml_snapcap": 2,
|
|
71
|
+
"opentrons_24_tuberack_nest_2ml_screwcap": 2,
|
|
72
|
+
"opentrons_24_tuberack_nest_2ml_snapcap": 2,
|
|
73
|
+
"opentrons_6_tuberack_falcon_50ml_conical": 2,
|
|
74
|
+
"opentrons_6_tuberack_nest_50ml_conical": 2,
|
|
75
|
+
"opentrons_96_aluminumblock_generic_pcr_strip_200ul": 3,
|
|
76
|
+
"opentrons_96_wellplate_200ul_pcr_full_skirt": 3,
|
|
77
|
+
"opentrons_tough_pcr_auto_sealing_lid": 2,
|
|
78
|
+
"thermoscientificnunc_96_wellplate_1300ul": 2,
|
|
79
|
+
"thermoscientificnunc_96_wellplate_2000ul": 2,
|
|
80
|
+
"usascientific_12_reservoir_22ml": 2,
|
|
81
|
+
"usascientific_96_wellplate_2.4ml_deep": 2,
|
|
82
|
+
}
|
|
83
|
+
|
|
37
84
|
|
|
38
85
|
class AmbiguousLoadLabwareParamsError(RuntimeError):
|
|
39
86
|
"""Error raised when specific labware parameters cannot be found due to multiple matching labware definitions."""
|
|
@@ -41,10 +88,11 @@ class AmbiguousLoadLabwareParamsError(RuntimeError):
|
|
|
41
88
|
|
|
42
89
|
def resolve(
|
|
43
90
|
load_name: str,
|
|
44
|
-
namespace:
|
|
45
|
-
version:
|
|
46
|
-
custom_load_labware_params:
|
|
47
|
-
|
|
91
|
+
namespace: str | None,
|
|
92
|
+
version: int | None,
|
|
93
|
+
custom_load_labware_params: list[LabwareLoadParams],
|
|
94
|
+
api_version: APIVersion,
|
|
95
|
+
) -> tuple[str, int]:
|
|
48
96
|
"""Resolve the load labware parameters that best matches any custom labware, or default to opentrons standards
|
|
49
97
|
|
|
50
98
|
Args:
|
|
@@ -82,7 +130,9 @@ def resolve(
|
|
|
82
130
|
resolved_version = (
|
|
83
131
|
version
|
|
84
132
|
if version is not None
|
|
85
|
-
else _get_default_version_for_standard_labware(
|
|
133
|
+
else _get_default_version_for_standard_labware(
|
|
134
|
+
load_name=load_name, api_version=api_version
|
|
135
|
+
)
|
|
86
136
|
)
|
|
87
137
|
|
|
88
138
|
elif len(filtered_custom_params) > 1:
|
|
@@ -99,7 +149,15 @@ def resolve(
|
|
|
99
149
|
return resolved_namespace, resolved_version
|
|
100
150
|
|
|
101
151
|
|
|
102
|
-
def _get_default_version_for_standard_labware(
|
|
152
|
+
def _get_default_version_for_standard_labware(
|
|
153
|
+
load_name: str, api_version: APIVersion
|
|
154
|
+
) -> int:
|
|
103
155
|
# We know the protocol is running at least apiLevel 2.14 by this point because
|
|
104
156
|
# apiLevel 2.13 and below has its own separate code path for resolving labware.
|
|
105
|
-
|
|
157
|
+
if (
|
|
158
|
+
api_version >= APIVersion(2, 23)
|
|
159
|
+
and load_name in _APILEVEL_2_23_OT_DEFAULT_VERSIONS
|
|
160
|
+
):
|
|
161
|
+
return _APILEVEL_2_23_OT_DEFAULT_VERSIONS[load_name]
|
|
162
|
+
else:
|
|
163
|
+
return _APILEVEL_2_14_OT_DEFAULT_VERSIONS.get(load_name, 1)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""Protocol API module implementation logic."""
|
|
2
|
+
|
|
2
3
|
from __future__ import annotations
|
|
3
4
|
|
|
4
5
|
from typing import Optional, List, Dict, Union
|
|
@@ -17,7 +18,7 @@ from opentrons.drivers.types import (
|
|
|
17
18
|
)
|
|
18
19
|
|
|
19
20
|
from opentrons.protocol_engine import commands as cmd
|
|
20
|
-
from opentrons.protocol_engine.types import ABSMeasureMode
|
|
21
|
+
from opentrons.protocol_engine.types import ABSMeasureMode, StackerFillEmptyStrategy
|
|
21
22
|
from opentrons.types import DeckSlotName
|
|
22
23
|
from opentrons.protocol_engine.clients import SyncClient as ProtocolEngineClient
|
|
23
24
|
from opentrons.protocol_engine.errors.exceptions import (
|
|
@@ -37,9 +38,10 @@ from ..module import (
|
|
|
37
38
|
AbstractHeaterShakerCore,
|
|
38
39
|
AbstractMagneticBlockCore,
|
|
39
40
|
AbstractAbsorbanceReaderCore,
|
|
41
|
+
AbstractFlexStackerCore,
|
|
40
42
|
)
|
|
41
43
|
from .exceptions import InvalidMagnetEngageHeightError
|
|
42
|
-
|
|
44
|
+
from . import load_labware_params
|
|
43
45
|
|
|
44
46
|
# Valid wavelength range for absorbance reader
|
|
45
47
|
ABS_WAVELENGTH_MIN = 350
|
|
@@ -692,3 +694,117 @@ class AbsorbanceReaderCore(ModuleCore, AbstractAbsorbanceReaderCore):
|
|
|
692
694
|
self.module_id
|
|
693
695
|
)
|
|
694
696
|
return abs_state.is_lid_on
|
|
697
|
+
|
|
698
|
+
|
|
699
|
+
class FlexStackerCore(ModuleCore, AbstractFlexStackerCore):
|
|
700
|
+
"""Flex Stacker core logic implementation for Python protocols."""
|
|
701
|
+
|
|
702
|
+
_sync_module_hardware: SynchronousAdapter[hw_modules.FlexStacker]
|
|
703
|
+
|
|
704
|
+
def retrieve(self) -> None:
|
|
705
|
+
"""Retrieve a labware from the Flex Stacker's hopper."""
|
|
706
|
+
self._engine_client.execute_command(
|
|
707
|
+
cmd.flex_stacker.RetrieveParams(
|
|
708
|
+
moduleId=self.module_id,
|
|
709
|
+
)
|
|
710
|
+
)
|
|
711
|
+
|
|
712
|
+
def store(self) -> None:
|
|
713
|
+
"""Store a labware into Flex Stacker's hopper."""
|
|
714
|
+
self._engine_client.execute_command(
|
|
715
|
+
cmd.flex_stacker.StoreParams(
|
|
716
|
+
moduleId=self.module_id,
|
|
717
|
+
)
|
|
718
|
+
)
|
|
719
|
+
|
|
720
|
+
def fill(self, message: str, count: int | None) -> None:
|
|
721
|
+
"""Pause the protocol to add more labware to the Flex Stacker's hopper."""
|
|
722
|
+
self._engine_client.execute_command(
|
|
723
|
+
cmd.flex_stacker.FillParams(
|
|
724
|
+
moduleId=self.module_id,
|
|
725
|
+
strategy=StackerFillEmptyStrategy.MANUAL_WITH_PAUSE,
|
|
726
|
+
message=message,
|
|
727
|
+
count=count,
|
|
728
|
+
)
|
|
729
|
+
)
|
|
730
|
+
|
|
731
|
+
def empty(self, message: str) -> None:
|
|
732
|
+
"""Pause the protocol to remove labware from the Flex Stacker's hopper."""
|
|
733
|
+
self._engine_client.execute_command(
|
|
734
|
+
cmd.flex_stacker.EmptyParams(
|
|
735
|
+
moduleId=self.module_id,
|
|
736
|
+
strategy=StackerFillEmptyStrategy.MANUAL_WITH_PAUSE,
|
|
737
|
+
message=message,
|
|
738
|
+
count=0,
|
|
739
|
+
)
|
|
740
|
+
)
|
|
741
|
+
|
|
742
|
+
def set_stored_labware(
|
|
743
|
+
self,
|
|
744
|
+
main_load_name: str,
|
|
745
|
+
main_namespace: str | None,
|
|
746
|
+
main_version: int | None,
|
|
747
|
+
lid_load_name: str | None,
|
|
748
|
+
lid_namespace: str | None,
|
|
749
|
+
lid_version: int | None,
|
|
750
|
+
adapter_load_name: str | None,
|
|
751
|
+
adapter_namespace: str | None,
|
|
752
|
+
adapter_version: int | None,
|
|
753
|
+
count: int | None,
|
|
754
|
+
) -> None:
|
|
755
|
+
"""Configure the kind of labware that the stacker stores."""
|
|
756
|
+
|
|
757
|
+
custom_labware_params = (
|
|
758
|
+
self._engine_client.state.labware.find_custom_labware_load_params()
|
|
759
|
+
)
|
|
760
|
+
|
|
761
|
+
main_namespace, main_version = load_labware_params.resolve(
|
|
762
|
+
main_load_name,
|
|
763
|
+
main_namespace,
|
|
764
|
+
main_version,
|
|
765
|
+
custom_labware_params,
|
|
766
|
+
self._api_version,
|
|
767
|
+
)
|
|
768
|
+
main_labware = cmd.flex_stacker.StackerStoredLabwareDetails(
|
|
769
|
+
loadName=main_load_name, namespace=main_namespace, version=main_version
|
|
770
|
+
)
|
|
771
|
+
|
|
772
|
+
lid_labware: cmd.flex_stacker.StackerStoredLabwareDetails | None = None
|
|
773
|
+
|
|
774
|
+
if lid_load_name:
|
|
775
|
+
lid_namespace, lid_version = load_labware_params.resolve(
|
|
776
|
+
lid_load_name,
|
|
777
|
+
lid_namespace,
|
|
778
|
+
lid_version,
|
|
779
|
+
custom_labware_params,
|
|
780
|
+
self._api_version,
|
|
781
|
+
)
|
|
782
|
+
lid_labware = cmd.flex_stacker.StackerStoredLabwareDetails(
|
|
783
|
+
loadName=lid_load_name, namespace=lid_namespace, version=lid_version
|
|
784
|
+
)
|
|
785
|
+
|
|
786
|
+
adapter_labware: cmd.flex_stacker.StackerStoredLabwareDetails | None = None
|
|
787
|
+
|
|
788
|
+
if adapter_load_name:
|
|
789
|
+
adapter_namespace, adapter_version = load_labware_params.resolve(
|
|
790
|
+
adapter_load_name,
|
|
791
|
+
adapter_namespace,
|
|
792
|
+
adapter_version,
|
|
793
|
+
custom_labware_params,
|
|
794
|
+
self._api_version,
|
|
795
|
+
)
|
|
796
|
+
adapter_labware = cmd.flex_stacker.StackerStoredLabwareDetails(
|
|
797
|
+
loadName=adapter_load_name,
|
|
798
|
+
namespace=adapter_namespace,
|
|
799
|
+
version=adapter_version,
|
|
800
|
+
)
|
|
801
|
+
|
|
802
|
+
self._engine_client.execute_command(
|
|
803
|
+
cmd.flex_stacker.SetStoredLabwareParams(
|
|
804
|
+
moduleId=self.module_id,
|
|
805
|
+
initialCount=count,
|
|
806
|
+
primaryLabware=main_labware,
|
|
807
|
+
lidLabware=lid_labware,
|
|
808
|
+
adapterLabware=adapter_labware,
|
|
809
|
+
)
|
|
810
|
+
)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""ProtocolEngine-based Protocol API core implementation."""
|
|
2
|
+
|
|
2
3
|
from __future__ import annotations
|
|
3
4
|
from typing import Dict, Optional, Type, Union, List, Tuple, TYPE_CHECKING
|
|
4
5
|
|
|
@@ -8,7 +9,9 @@ from opentrons.protocol_engine import commands as cmd
|
|
|
8
9
|
from opentrons.protocol_engine.commands import LoadModuleResult
|
|
9
10
|
|
|
10
11
|
from opentrons_shared_data.deck.types import DeckDefinitionV5, SlotDefV3
|
|
11
|
-
from opentrons_shared_data.labware.labware_definition import
|
|
12
|
+
from opentrons_shared_data.labware.labware_definition import (
|
|
13
|
+
labware_definition_type_adapter,
|
|
14
|
+
)
|
|
12
15
|
from opentrons_shared_data.labware.types import LabwareDefinition as LabwareDefDict
|
|
13
16
|
from opentrons_shared_data import liquid_classes
|
|
14
17
|
from opentrons_shared_data.liquid_classes.liquid_class_definition import (
|
|
@@ -47,7 +50,8 @@ from opentrons.protocol_engine import (
|
|
|
47
50
|
from opentrons.protocol_engine.types import (
|
|
48
51
|
ModuleModel as ProtocolEngineModuleModel,
|
|
49
52
|
OFF_DECK_LOCATION,
|
|
50
|
-
|
|
53
|
+
SYSTEM_LOCATION,
|
|
54
|
+
LoadableLabwareLocation,
|
|
51
55
|
NonStackedLocation,
|
|
52
56
|
)
|
|
53
57
|
from opentrons.protocol_engine.clients import SyncClient as ProtocolEngineClient
|
|
@@ -74,9 +78,11 @@ from .module_core import (
|
|
|
74
78
|
NonConnectedModuleCore,
|
|
75
79
|
MagneticBlockCore,
|
|
76
80
|
AbsorbanceReaderCore,
|
|
81
|
+
FlexStackerCore,
|
|
77
82
|
)
|
|
78
83
|
from .exceptions import InvalidModuleLocationError
|
|
79
84
|
from . import load_labware_params, deck_conflict, overlap_versions
|
|
85
|
+
from opentrons.protocol_engine.resources import labware_validation
|
|
80
86
|
|
|
81
87
|
if TYPE_CHECKING:
|
|
82
88
|
from ...labware import Labware
|
|
@@ -193,7 +199,7 @@ class ProtocolCore(
|
|
|
193
199
|
) -> LabwareLoadParams:
|
|
194
200
|
"""Add a labware definition to the set of loadable definitions."""
|
|
195
201
|
uri = self._engine_client.add_labware_definition(
|
|
196
|
-
|
|
202
|
+
labware_definition_type_adapter.validate_python(definition)
|
|
197
203
|
)
|
|
198
204
|
return LabwareLoadParams.from_uri(uri)
|
|
199
205
|
|
|
@@ -219,7 +225,7 @@ class ProtocolCore(
|
|
|
219
225
|
self._engine_client.state.labware.find_custom_labware_load_params()
|
|
220
226
|
)
|
|
221
227
|
namespace, version = load_labware_params.resolve(
|
|
222
|
-
load_name, namespace, version, custom_labware_params
|
|
228
|
+
load_name, namespace, version, custom_labware_params, self._api_version
|
|
223
229
|
)
|
|
224
230
|
|
|
225
231
|
load_result = self._engine_client.execute_command_without_recovery(
|
|
@@ -290,7 +296,7 @@ class ProtocolCore(
|
|
|
290
296
|
self._engine_client.state.labware.find_custom_labware_load_params()
|
|
291
297
|
)
|
|
292
298
|
namespace, version = load_labware_params.resolve(
|
|
293
|
-
load_name, namespace, version, custom_labware_params
|
|
299
|
+
load_name, namespace, version, custom_labware_params, self._api_version
|
|
294
300
|
)
|
|
295
301
|
load_result = self._engine_client.execute_command_without_recovery(
|
|
296
302
|
cmd.LoadLabwareParams(
|
|
@@ -338,7 +344,7 @@ class ProtocolCore(
|
|
|
338
344
|
self._engine_client.state.labware.find_custom_labware_load_params()
|
|
339
345
|
)
|
|
340
346
|
namespace, version = load_labware_params.resolve(
|
|
341
|
-
load_name, namespace, version, custom_labware_params
|
|
347
|
+
load_name, namespace, version, custom_labware_params, self._api_version
|
|
342
348
|
)
|
|
343
349
|
load_result = self._engine_client.execute_command_without_recovery(
|
|
344
350
|
cmd.LoadLidParams(
|
|
@@ -371,6 +377,34 @@ class ProtocolCore(
|
|
|
371
377
|
self._labware_cores_by_id[labware_core.labware_id] = labware_core
|
|
372
378
|
return labware_core
|
|
373
379
|
|
|
380
|
+
def load_labware_to_flex_stacker_hopper(
|
|
381
|
+
self,
|
|
382
|
+
module_core: Union[ModuleCore, NonConnectedModuleCore],
|
|
383
|
+
load_name: str,
|
|
384
|
+
quantity: int,
|
|
385
|
+
label: Optional[str],
|
|
386
|
+
namespace: Optional[str],
|
|
387
|
+
version: Optional[int],
|
|
388
|
+
lid: Optional[str],
|
|
389
|
+
) -> None:
|
|
390
|
+
"""Load one or more labware with or without a lid to the flex stacker hopper."""
|
|
391
|
+
assert isinstance(module_core, FlexStackerCore)
|
|
392
|
+
for _ in range(quantity):
|
|
393
|
+
labware_core = self.load_labware(
|
|
394
|
+
load_name=load_name,
|
|
395
|
+
location=module_core,
|
|
396
|
+
label=label,
|
|
397
|
+
namespace=namespace,
|
|
398
|
+
version=version,
|
|
399
|
+
)
|
|
400
|
+
if lid is not None:
|
|
401
|
+
self.load_lid(
|
|
402
|
+
load_name=lid,
|
|
403
|
+
location=labware_core,
|
|
404
|
+
namespace=namespace,
|
|
405
|
+
version=version,
|
|
406
|
+
)
|
|
407
|
+
|
|
374
408
|
def move_labware(
|
|
375
409
|
self,
|
|
376
410
|
labware_core: LabwareCore,
|
|
@@ -442,6 +476,203 @@ class ProtocolCore(
|
|
|
442
476
|
existing_module_ids=list(self._module_cores_by_id.keys()),
|
|
443
477
|
)
|
|
444
478
|
|
|
479
|
+
def move_lid( # noqa: C901
|
|
480
|
+
self,
|
|
481
|
+
source_location: Union[DeckSlotName, StagingSlotName, LabwareCore],
|
|
482
|
+
new_location: Union[
|
|
483
|
+
DeckSlotName,
|
|
484
|
+
StagingSlotName,
|
|
485
|
+
LabwareCore,
|
|
486
|
+
OffDeckType,
|
|
487
|
+
WasteChute,
|
|
488
|
+
TrashBin,
|
|
489
|
+
],
|
|
490
|
+
use_gripper: bool,
|
|
491
|
+
pause_for_manual_move: bool,
|
|
492
|
+
pick_up_offset: Optional[Tuple[float, float, float]],
|
|
493
|
+
drop_offset: Optional[Tuple[float, float, float]],
|
|
494
|
+
) -> LabwareCore | None:
|
|
495
|
+
"""Move the given lid to a new location."""
|
|
496
|
+
if use_gripper:
|
|
497
|
+
strategy = LabwareMovementStrategy.USING_GRIPPER
|
|
498
|
+
elif pause_for_manual_move:
|
|
499
|
+
strategy = LabwareMovementStrategy.MANUAL_MOVE_WITH_PAUSE
|
|
500
|
+
else:
|
|
501
|
+
strategy = LabwareMovementStrategy.MANUAL_MOVE_WITHOUT_PAUSE
|
|
502
|
+
|
|
503
|
+
if isinstance(source_location, DeckSlotName) or isinstance(
|
|
504
|
+
source_location, StagingSlotName
|
|
505
|
+
):
|
|
506
|
+
# Find the source labware at the provided deck slot
|
|
507
|
+
labware_in_slot = self._engine_client.state.labware.get_by_slot(
|
|
508
|
+
source_location
|
|
509
|
+
)
|
|
510
|
+
if labware_in_slot is None:
|
|
511
|
+
raise LabwareNotLoadedOnLabwareError(
|
|
512
|
+
"Lid cannot be loaded on non-labware position."
|
|
513
|
+
)
|
|
514
|
+
else:
|
|
515
|
+
labware = LabwareCore(labware_in_slot.id, self._engine_client)
|
|
516
|
+
else:
|
|
517
|
+
labware = source_location
|
|
518
|
+
|
|
519
|
+
# if this is a labware stack, we need to find the labware at the top of the stack
|
|
520
|
+
if labware_validation.is_lid_stack(labware.load_name):
|
|
521
|
+
lid_id = self._engine_client.state.labware.get_highest_child_labware(
|
|
522
|
+
labware.labware_id
|
|
523
|
+
)
|
|
524
|
+
# if this is a labware with a lid, we just need to find its lid_id
|
|
525
|
+
else:
|
|
526
|
+
lid = self._engine_client.state.labware.get_lid_by_labware_id(
|
|
527
|
+
labware.labware_id
|
|
528
|
+
)
|
|
529
|
+
if lid is not None:
|
|
530
|
+
lid_id = lid.id
|
|
531
|
+
else:
|
|
532
|
+
raise ValueError("Cannot move a lid off of a labware with no lid.")
|
|
533
|
+
|
|
534
|
+
_pick_up_offset = (
|
|
535
|
+
LabwareOffsetVector(
|
|
536
|
+
x=pick_up_offset[0], y=pick_up_offset[1], z=pick_up_offset[2]
|
|
537
|
+
)
|
|
538
|
+
if pick_up_offset
|
|
539
|
+
else None
|
|
540
|
+
)
|
|
541
|
+
_drop_offset = (
|
|
542
|
+
LabwareOffsetVector(x=drop_offset[0], y=drop_offset[1], z=drop_offset[2])
|
|
543
|
+
if drop_offset
|
|
544
|
+
else None
|
|
545
|
+
)
|
|
546
|
+
|
|
547
|
+
create_new_lid_stack = False
|
|
548
|
+
|
|
549
|
+
if isinstance(new_location, DeckSlotName) or isinstance(
|
|
550
|
+
new_location, StagingSlotName
|
|
551
|
+
):
|
|
552
|
+
# Find the destination labware at the provided deck slot
|
|
553
|
+
destination_labware_in_slot = self._engine_client.state.labware.get_by_slot(
|
|
554
|
+
new_location
|
|
555
|
+
)
|
|
556
|
+
if destination_labware_in_slot is None:
|
|
557
|
+
to_location = self._convert_labware_location(location=new_location)
|
|
558
|
+
# absolutely must make a new lid stack
|
|
559
|
+
create_new_lid_stack = True
|
|
560
|
+
else:
|
|
561
|
+
highest_child_location = (
|
|
562
|
+
self._engine_client.state.labware.get_highest_child_labware(
|
|
563
|
+
destination_labware_in_slot.id
|
|
564
|
+
)
|
|
565
|
+
)
|
|
566
|
+
if labware_validation.validate_definition_is_adapter(
|
|
567
|
+
self._engine_client.state.labware.get_definition(
|
|
568
|
+
highest_child_location
|
|
569
|
+
)
|
|
570
|
+
):
|
|
571
|
+
# absolutely must make a new lid stack
|
|
572
|
+
create_new_lid_stack = True
|
|
573
|
+
|
|
574
|
+
to_location = self._convert_labware_location(
|
|
575
|
+
location=LabwareCore(highest_child_location, self._engine_client)
|
|
576
|
+
)
|
|
577
|
+
elif isinstance(new_location, LabwareCore):
|
|
578
|
+
highest_child_location = (
|
|
579
|
+
self._engine_client.state.labware.get_highest_child_labware(
|
|
580
|
+
new_location.labware_id
|
|
581
|
+
)
|
|
582
|
+
)
|
|
583
|
+
if labware_validation.validate_definition_is_adapter(
|
|
584
|
+
self._engine_client.state.labware.get_definition(highest_child_location)
|
|
585
|
+
):
|
|
586
|
+
# absolutely must make a new lid stack
|
|
587
|
+
create_new_lid_stack = True
|
|
588
|
+
to_location = self._convert_labware_location(
|
|
589
|
+
location=LabwareCore(highest_child_location, self._engine_client)
|
|
590
|
+
)
|
|
591
|
+
else:
|
|
592
|
+
to_location = self._convert_labware_location(location=new_location)
|
|
593
|
+
|
|
594
|
+
output_result = None
|
|
595
|
+
if create_new_lid_stack:
|
|
596
|
+
# Make a new lid stack object that is empty
|
|
597
|
+
result = self._engine_client.execute_command_without_recovery(
|
|
598
|
+
cmd.LoadLidStackParams(
|
|
599
|
+
location=SYSTEM_LOCATION,
|
|
600
|
+
loadName="empty",
|
|
601
|
+
version=1,
|
|
602
|
+
namespace="empty",
|
|
603
|
+
quantity=0,
|
|
604
|
+
)
|
|
605
|
+
)
|
|
606
|
+
|
|
607
|
+
# Move the lid stack object from the SYSTEM_LOCATION space to the desired deck location
|
|
608
|
+
self._engine_client.execute_command(
|
|
609
|
+
cmd.MoveLabwareParams(
|
|
610
|
+
labwareId=result.stackLabwareId,
|
|
611
|
+
newLocation=to_location,
|
|
612
|
+
strategy=LabwareMovementStrategy.MANUAL_MOVE_WITHOUT_PAUSE,
|
|
613
|
+
pickUpOffset=None,
|
|
614
|
+
dropOffset=None,
|
|
615
|
+
)
|
|
616
|
+
)
|
|
617
|
+
|
|
618
|
+
output_result = LabwareCore(
|
|
619
|
+
labware_id=result.stackLabwareId, engine_client=self._engine_client
|
|
620
|
+
)
|
|
621
|
+
destination = self._convert_labware_location(location=output_result)
|
|
622
|
+
else:
|
|
623
|
+
destination = to_location
|
|
624
|
+
|
|
625
|
+
self._engine_client.execute_command(
|
|
626
|
+
cmd.MoveLabwareParams(
|
|
627
|
+
labwareId=lid_id,
|
|
628
|
+
newLocation=destination,
|
|
629
|
+
strategy=strategy,
|
|
630
|
+
pickUpOffset=_pick_up_offset,
|
|
631
|
+
dropOffset=_drop_offset,
|
|
632
|
+
)
|
|
633
|
+
)
|
|
634
|
+
|
|
635
|
+
# Handle leftover empty lid stack if there is one
|
|
636
|
+
if (
|
|
637
|
+
labware_validation.is_lid_stack(labware.load_name)
|
|
638
|
+
and self._engine_client.state.labware.get_highest_child_labware(
|
|
639
|
+
labware_id=labware.labware_id
|
|
640
|
+
)
|
|
641
|
+
== labware.labware_id
|
|
642
|
+
):
|
|
643
|
+
# The originating lid stack is now empty, so we need to move it to the SYSTEM_LOCATION
|
|
644
|
+
self._engine_client.execute_command(
|
|
645
|
+
cmd.MoveLabwareParams(
|
|
646
|
+
labwareId=labware.labware_id,
|
|
647
|
+
newLocation=SYSTEM_LOCATION,
|
|
648
|
+
strategy=LabwareMovementStrategy.MANUAL_MOVE_WITHOUT_PAUSE,
|
|
649
|
+
pickUpOffset=None,
|
|
650
|
+
dropOffset=None,
|
|
651
|
+
)
|
|
652
|
+
)
|
|
653
|
+
|
|
654
|
+
if strategy == LabwareMovementStrategy.USING_GRIPPER:
|
|
655
|
+
# Clear out last location since it is not relevant to pipetting
|
|
656
|
+
# and we only use last location for in-place pipetting commands
|
|
657
|
+
self.set_last_location(location=None, mount=Mount.EXTENSION)
|
|
658
|
+
|
|
659
|
+
# FIXME(jbl, 2024-01-04) deck conflict after execution logic issue, read notes in load_labware for more info:
|
|
660
|
+
deck_conflict.check(
|
|
661
|
+
engine_state=self._engine_client.state,
|
|
662
|
+
new_labware_id=lid_id,
|
|
663
|
+
existing_disposal_locations=self._disposal_locations,
|
|
664
|
+
# TODO: We can now fetch these IDs from engine too.
|
|
665
|
+
# See comment in self.load_labware().
|
|
666
|
+
existing_labware_ids=[
|
|
667
|
+
labware_id
|
|
668
|
+
for labware_id in self._labware_cores_by_id
|
|
669
|
+
if labware_id != labware_id
|
|
670
|
+
],
|
|
671
|
+
existing_module_ids=list(self._module_cores_by_id.keys()),
|
|
672
|
+
)
|
|
673
|
+
|
|
674
|
+
return output_result
|
|
675
|
+
|
|
445
676
|
def _resolve_module_hardware(
|
|
446
677
|
self, serial_number: str, model: ModuleModel
|
|
447
678
|
) -> AbstractModule:
|
|
@@ -527,6 +758,7 @@ class ProtocolCore(
|
|
|
527
758
|
ModuleType.THERMOCYCLER: ThermocyclerModuleCore,
|
|
528
759
|
ModuleType.HEATER_SHAKER: HeaterShakerModuleCore,
|
|
529
760
|
ModuleType.ABSORBANCE_READER: AbsorbanceReaderCore,
|
|
761
|
+
ModuleType.FLEX_STACKER: FlexStackerCore,
|
|
530
762
|
}
|
|
531
763
|
|
|
532
764
|
module_type = load_module_result.model.as_type()
|
|
@@ -557,6 +789,15 @@ class ProtocolCore(
|
|
|
557
789
|
load_module_result=load_module_result, model=model
|
|
558
790
|
)
|
|
559
791
|
|
|
792
|
+
def add_or_get_labware_core(self, labware_id: str) -> LabwareCore:
|
|
793
|
+
"""Create a LabwareCore and add it to the map or return one if it exists."""
|
|
794
|
+
if labware_id in self._labware_cores_by_id:
|
|
795
|
+
return self._labware_cores_by_id[labware_id]
|
|
796
|
+
else:
|
|
797
|
+
core = LabwareCore(labware_id, self._engine_client)
|
|
798
|
+
self._labware_cores_by_id[labware_id] = core
|
|
799
|
+
return core
|
|
800
|
+
|
|
560
801
|
def load_robot(self) -> RobotCore:
|
|
561
802
|
"""Load a robot core into the RobotContext."""
|
|
562
803
|
return RobotCore(
|
|
@@ -720,7 +961,7 @@ class ProtocolCore(
|
|
|
720
961
|
self._engine_client.state.labware.find_custom_labware_load_params()
|
|
721
962
|
)
|
|
722
963
|
namespace, version = load_labware_params.resolve(
|
|
723
|
-
load_name, namespace, version, custom_labware_params
|
|
964
|
+
load_name, namespace, version, custom_labware_params, self._api_version
|
|
724
965
|
)
|
|
725
966
|
|
|
726
967
|
load_result = self._engine_client.execute_command_without_recovery(
|
|
@@ -734,6 +975,7 @@ class ProtocolCore(
|
|
|
734
975
|
)
|
|
735
976
|
|
|
736
977
|
# FIXME(CHB, 2024-12-04) just like load labware and load adapter we have a validating after loading the object issue
|
|
978
|
+
assert load_result.definition is not None
|
|
737
979
|
validation.ensure_definition_is_lid(load_result.definition)
|
|
738
980
|
|
|
739
981
|
deck_conflict.check(
|
|
@@ -803,9 +1045,9 @@ class ProtocolCore(
|
|
|
803
1045
|
labware_id = self._engine_client.state.labware.get_id_by_module(
|
|
804
1046
|
module_core.module_id
|
|
805
1047
|
)
|
|
806
|
-
return self._labware_cores_by_id[labware_id]
|
|
807
1048
|
except LabwareNotLoadedOnModuleError:
|
|
808
1049
|
return None
|
|
1050
|
+
return self.add_or_get_labware_core(labware_id)
|
|
809
1051
|
|
|
810
1052
|
def get_labware_on_labware(
|
|
811
1053
|
self, labware_core: LabwareCore
|
|
@@ -815,9 +1057,9 @@ class ProtocolCore(
|
|
|
815
1057
|
labware_id = self._engine_client.state.labware.get_id_by_labware(
|
|
816
1058
|
labware_core.labware_id
|
|
817
1059
|
)
|
|
818
|
-
return self._labware_cores_by_id[labware_id]
|
|
819
1060
|
except LabwareNotLoadedOnLabwareError:
|
|
820
1061
|
return None
|
|
1062
|
+
return self.add_or_get_labware_core(labware_id)
|
|
821
1063
|
|
|
822
1064
|
def get_slot_center(self, slot_name: Union[DeckSlotName, StagingSlotName]) -> Point:
|
|
823
1065
|
"""Get the absolute coordinate of a slot's center."""
|
|
@@ -905,7 +1147,7 @@ class ProtocolCore(
|
|
|
905
1147
|
WasteChute,
|
|
906
1148
|
TrashBin,
|
|
907
1149
|
],
|
|
908
|
-
) ->
|
|
1150
|
+
) -> LoadableLabwareLocation:
|
|
909
1151
|
if isinstance(location, LabwareCore):
|
|
910
1152
|
return OnLabwareLocation(labwareId=location.labware_id)
|
|
911
1153
|
else:
|
|
@@ -921,7 +1163,7 @@ class ProtocolCore(
|
|
|
921
1163
|
OffDeckType,
|
|
922
1164
|
WasteChute,
|
|
923
1165
|
TrashBin,
|
|
924
|
-
]
|
|
1166
|
+
],
|
|
925
1167
|
) -> NonStackedLocation:
|
|
926
1168
|
if isinstance(location, (ModuleCore, NonConnectedModuleCore)):
|
|
927
1169
|
return ModuleLocation(moduleId=location.module_id)
|