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
|
@@ -459,6 +459,19 @@ class ModuleNotConnectedError(ProtocolEngineError):
|
|
|
459
459
|
super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
|
|
460
460
|
|
|
461
461
|
|
|
462
|
+
class OffsetLocationInvalidError(ProtocolEngineError):
|
|
463
|
+
"""Raised when encountering an invalid labware offset location sequence."""
|
|
464
|
+
|
|
465
|
+
def __init__(
|
|
466
|
+
self,
|
|
467
|
+
message: Optional[str] = None,
|
|
468
|
+
details: Optional[Dict[str, Any]] = None,
|
|
469
|
+
wrapping: Optional[Sequence[EnumeratedError]] = None,
|
|
470
|
+
) -> None:
|
|
471
|
+
"""Build an OffsetLocationSequenceDoesNotTerminateAtAnAddressableAreaError."""
|
|
472
|
+
super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
|
|
473
|
+
|
|
474
|
+
|
|
462
475
|
class SlotDoesNotExistError(ProtocolEngineError):
|
|
463
476
|
"""Raised when referencing a deck slot that does not exist."""
|
|
464
477
|
|
|
@@ -1114,6 +1127,19 @@ class LiquidHeightUnknownError(ProtocolEngineError):
|
|
|
1114
1127
|
super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
|
|
1115
1128
|
|
|
1116
1129
|
|
|
1130
|
+
class LiquidVolumeUnknownError(ProtocolEngineError):
|
|
1131
|
+
"""Raised when attempting to report an unknown liquid volume."""
|
|
1132
|
+
|
|
1133
|
+
def __init__(
|
|
1134
|
+
self,
|
|
1135
|
+
message: Optional[str] = None,
|
|
1136
|
+
details: Optional[Dict[str, Any]] = None,
|
|
1137
|
+
wrapping: Optional[Sequence[EnumeratedError]] = None,
|
|
1138
|
+
) -> None:
|
|
1139
|
+
"""Build a LiquidVolumeUnknownError."""
|
|
1140
|
+
super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
|
|
1141
|
+
|
|
1142
|
+
|
|
1117
1143
|
class EStopActivatedError(ProtocolEngineError):
|
|
1118
1144
|
"""Represents an E-stop event."""
|
|
1119
1145
|
|
|
@@ -1231,3 +1257,27 @@ class LiquidClassRedefinitionError(ProtocolEngineError):
|
|
|
1231
1257
|
wrapping: Optional[Sequence[EnumeratedError]] = None,
|
|
1232
1258
|
) -> None:
|
|
1233
1259
|
super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
|
|
1260
|
+
|
|
1261
|
+
|
|
1262
|
+
class FlexStackerNotLogicallyEmptyError(ProtocolEngineError):
|
|
1263
|
+
"""Raised when attempting a stacker operation that requires it to be empty when it is known from the protocol that it is not."""
|
|
1264
|
+
|
|
1265
|
+
def __init__(
|
|
1266
|
+
self,
|
|
1267
|
+
message: Optional[str] = None,
|
|
1268
|
+
details: Optional[dict[str, Any]] = None,
|
|
1269
|
+
wrapping: Optional[Sequence[EnumeratedError]] = None,
|
|
1270
|
+
) -> None:
|
|
1271
|
+
super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
|
|
1272
|
+
|
|
1273
|
+
|
|
1274
|
+
class FlexStackerLabwarePoolNotYetDefinedError(ProtocolEngineError):
|
|
1275
|
+
"""Raised when attempting to modify labware in a stacker whose labware pool is not yet defined."""
|
|
1276
|
+
|
|
1277
|
+
def __init__(
|
|
1278
|
+
self,
|
|
1279
|
+
message: Optional[str] = None,
|
|
1280
|
+
details: Optional[dict[str, Any]] = None,
|
|
1281
|
+
wrapping: Optional[Sequence[EnumeratedError]] = None,
|
|
1282
|
+
) -> None:
|
|
1283
|
+
super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
"""Equipment command side-effect logic."""
|
|
2
|
+
|
|
2
3
|
from dataclasses import dataclass
|
|
3
|
-
from typing import Optional, overload,
|
|
4
|
+
from typing import Optional, overload, List
|
|
4
5
|
|
|
6
|
+
from opentrons_shared_data.labware.labware_definition import LabwareDefinition
|
|
5
7
|
from opentrons_shared_data.pipette.types import PipetteNameType
|
|
6
8
|
|
|
7
9
|
from opentrons.calibration_storage.helpers import uri_from_details
|
|
8
|
-
from opentrons.protocols.models import LabwareDefinition
|
|
9
10
|
from opentrons.types import MountType
|
|
10
11
|
from opentrons.hardware_control import HardwareControlAPI
|
|
11
12
|
from opentrons.hardware_control.modules import (
|
|
@@ -15,6 +16,7 @@ from opentrons.hardware_control.modules import (
|
|
|
15
16
|
TempDeck,
|
|
16
17
|
Thermocycler,
|
|
17
18
|
AbsorbanceReader,
|
|
19
|
+
FlexStacker,
|
|
18
20
|
)
|
|
19
21
|
from opentrons.hardware_control.nozzle_manager import NozzleMap
|
|
20
22
|
from opentrons.protocol_engine.state.module_substates import (
|
|
@@ -23,6 +25,7 @@ from opentrons.protocol_engine.state.module_substates import (
|
|
|
23
25
|
TemperatureModuleId,
|
|
24
26
|
ThermocyclerModuleId,
|
|
25
27
|
AbsorbanceReaderId,
|
|
28
|
+
FlexStackerId,
|
|
26
29
|
)
|
|
27
30
|
from ..errors import (
|
|
28
31
|
FailedToLoadPipetteError,
|
|
@@ -40,13 +43,11 @@ from ..state.modules import HardwareModule
|
|
|
40
43
|
from ..types import (
|
|
41
44
|
LabwareLocation,
|
|
42
45
|
DeckSlotLocation,
|
|
43
|
-
ModuleLocation,
|
|
44
|
-
OnLabwareLocation,
|
|
45
46
|
LabwareOffset,
|
|
46
|
-
LabwareOffsetLocation,
|
|
47
47
|
ModuleModel,
|
|
48
48
|
ModuleDefinition,
|
|
49
49
|
AddressableAreaLocation,
|
|
50
|
+
LoadedLabware,
|
|
50
51
|
)
|
|
51
52
|
|
|
52
53
|
|
|
@@ -127,30 +128,18 @@ class EquipmentHandler:
|
|
|
127
128
|
or pipette_data_provider.VirtualPipetteDataProvider()
|
|
128
129
|
)
|
|
129
130
|
|
|
130
|
-
async def
|
|
131
|
-
self,
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
version: int,
|
|
135
|
-
location: LabwareLocation,
|
|
136
|
-
labware_id: Optional[str],
|
|
137
|
-
) -> LoadedLabwareData:
|
|
138
|
-
"""Load labware by assigning an identifier and pulling required data.
|
|
131
|
+
async def load_definition_for_details(
|
|
132
|
+
self, load_name: str, namespace: str, version: int
|
|
133
|
+
) -> tuple[LabwareDefinition, str]:
|
|
134
|
+
"""Load the definition for a labware from the parameters passed for it to a command.
|
|
139
135
|
|
|
140
136
|
Args:
|
|
141
137
|
load_name: The labware's load name.
|
|
142
138
|
namespace: The labware's namespace.
|
|
143
139
|
version: The labware's version.
|
|
144
|
-
location: The deck location at which labware is placed.
|
|
145
|
-
labware_id: An optional identifier to assign the labware. If None, an
|
|
146
|
-
identifier will be generated.
|
|
147
|
-
|
|
148
|
-
Raises:
|
|
149
|
-
ModuleNotLoadedError: If `location` references a module ID
|
|
150
|
-
that doesn't point to a valid loaded module.
|
|
151
140
|
|
|
152
141
|
Returns:
|
|
153
|
-
A
|
|
142
|
+
A tuple of the loaded LabwareDefinition object and its definition URI.
|
|
154
143
|
"""
|
|
155
144
|
definition_uri = uri_from_details(
|
|
156
145
|
load_name=load_name,
|
|
@@ -160,14 +149,43 @@ class EquipmentHandler:
|
|
|
160
149
|
|
|
161
150
|
try:
|
|
162
151
|
# Try to use existing definition in state.
|
|
163
|
-
|
|
152
|
+
return (
|
|
153
|
+
self._state_store.labware.get_definition_by_uri(definition_uri),
|
|
154
|
+
definition_uri,
|
|
155
|
+
)
|
|
164
156
|
except LabwareDefinitionDoesNotExistError:
|
|
165
157
|
definition = await self._labware_data_provider.get_labware_definition(
|
|
166
158
|
load_name=load_name,
|
|
167
159
|
namespace=namespace,
|
|
168
160
|
version=version,
|
|
169
161
|
)
|
|
162
|
+
return definition, definition_uri
|
|
163
|
+
|
|
164
|
+
async def load_labware_from_definition(
|
|
165
|
+
self,
|
|
166
|
+
definition: LabwareDefinition,
|
|
167
|
+
location: LabwareLocation,
|
|
168
|
+
labware_id: Optional[str],
|
|
169
|
+
labware_pending_load: dict[str, LoadedLabware] | None = None,
|
|
170
|
+
) -> LoadedLabwareData:
|
|
171
|
+
"""Load labware from already-found definition."""
|
|
172
|
+
definition_uri = uri_from_details(
|
|
173
|
+
load_name=definition.parameters.loadName,
|
|
174
|
+
namespace=definition.namespace,
|
|
175
|
+
version=definition.version,
|
|
176
|
+
)
|
|
177
|
+
return await self._load_labware_from_def_and_uri(
|
|
178
|
+
definition, definition_uri, location, labware_id, labware_pending_load
|
|
179
|
+
)
|
|
170
180
|
|
|
181
|
+
async def _load_labware_from_def_and_uri(
|
|
182
|
+
self,
|
|
183
|
+
definition: LabwareDefinition,
|
|
184
|
+
definition_uri: str,
|
|
185
|
+
location: LabwareLocation,
|
|
186
|
+
labware_id: str | None,
|
|
187
|
+
labware_pending_load: dict[str, LoadedLabware] | None,
|
|
188
|
+
) -> LoadedLabwareData:
|
|
171
189
|
labware_id = (
|
|
172
190
|
labware_id if labware_id is not None else self._model_utils.generate_id()
|
|
173
191
|
)
|
|
@@ -176,12 +194,45 @@ class EquipmentHandler:
|
|
|
176
194
|
offset_id = self.find_applicable_labware_offset_id(
|
|
177
195
|
labware_definition_uri=definition_uri,
|
|
178
196
|
labware_location=location,
|
|
197
|
+
labware_pending_load=labware_pending_load,
|
|
179
198
|
)
|
|
180
199
|
|
|
181
200
|
return LoadedLabwareData(
|
|
182
201
|
labware_id=labware_id, definition=definition, offsetId=offset_id
|
|
183
202
|
)
|
|
184
203
|
|
|
204
|
+
async def load_labware(
|
|
205
|
+
self,
|
|
206
|
+
load_name: str,
|
|
207
|
+
namespace: str,
|
|
208
|
+
version: int,
|
|
209
|
+
location: LabwareLocation,
|
|
210
|
+
labware_id: Optional[str],
|
|
211
|
+
) -> LoadedLabwareData:
|
|
212
|
+
"""Load labware by assigning an identifier and pulling required data.
|
|
213
|
+
|
|
214
|
+
Args:
|
|
215
|
+
load_name: The labware's load name.
|
|
216
|
+
namespace: The labware's namespace.
|
|
217
|
+
version: The labware's version.
|
|
218
|
+
location: The deck location at which labware is placed.
|
|
219
|
+
labware_id: An optional identifier to assign the labware. If None, an
|
|
220
|
+
identifier will be generated.
|
|
221
|
+
|
|
222
|
+
Raises:
|
|
223
|
+
ModuleNotLoadedError: If `location` references a module ID
|
|
224
|
+
that doesn't point to a valid loaded module.
|
|
225
|
+
|
|
226
|
+
Returns:
|
|
227
|
+
A LoadedLabwareData object.
|
|
228
|
+
"""
|
|
229
|
+
definition, definition_uri = await self.load_definition_for_details(
|
|
230
|
+
load_name, namespace, version
|
|
231
|
+
)
|
|
232
|
+
return await self._load_labware_from_def_and_uri(
|
|
233
|
+
definition, definition_uri, location, labware_id, None
|
|
234
|
+
)
|
|
235
|
+
|
|
185
236
|
async def reload_labware(self, labware_id: str) -> ReloadedLabwareData:
|
|
186
237
|
"""Reload an already-loaded labware. This cannot change the labware location.
|
|
187
238
|
|
|
@@ -290,7 +341,7 @@ class EquipmentHandler:
|
|
|
290
341
|
async def load_magnetic_block(
|
|
291
342
|
self,
|
|
292
343
|
model: ModuleModel,
|
|
293
|
-
location:
|
|
344
|
+
location: AddressableAreaLocation,
|
|
294
345
|
module_id: Optional[str],
|
|
295
346
|
) -> LoadedModuleData:
|
|
296
347
|
"""Ensure the required magnetic block is attached.
|
|
@@ -321,7 +372,7 @@ class EquipmentHandler:
|
|
|
321
372
|
async def load_module(
|
|
322
373
|
self,
|
|
323
374
|
model: ModuleModel,
|
|
324
|
-
location:
|
|
375
|
+
location: AddressableAreaLocation,
|
|
325
376
|
module_id: Optional[str],
|
|
326
377
|
) -> LoadedModuleData:
|
|
327
378
|
"""Ensure the required module is attached.
|
|
@@ -355,12 +406,18 @@ class EquipmentHandler:
|
|
|
355
406
|
for hw_mod in self._hardware_api.attached_modules
|
|
356
407
|
]
|
|
357
408
|
|
|
358
|
-
serial_number_at_locaiton = self._state_store.geometry._addressable_areas.
|
|
359
|
-
location.
|
|
409
|
+
serial_number_at_locaiton = self._state_store.geometry._addressable_areas.get_fixture_serial_from_deck_configuration_by_addressable_area(
|
|
410
|
+
addressable_area_name=location.addressableAreaName
|
|
411
|
+
)
|
|
412
|
+
cutout_id = self._state_store.geometry._addressable_areas.get_cutout_id_by_deck_slot_name(
|
|
413
|
+
slot_name=self._state_store.geometry._addressable_areas.get_addressable_area_base_slot(
|
|
414
|
+
location.addressableAreaName
|
|
415
|
+
)
|
|
360
416
|
)
|
|
417
|
+
|
|
361
418
|
attached_module = self._state_store.modules.select_hardware_module_to_load(
|
|
362
419
|
model=model,
|
|
363
|
-
location=
|
|
420
|
+
location=cutout_id,
|
|
364
421
|
attached_modules=attached_modules,
|
|
365
422
|
expected_serial_number=serial_number_at_locaiton,
|
|
366
423
|
)
|
|
@@ -386,6 +443,7 @@ class EquipmentHandler:
|
|
|
386
443
|
version: int,
|
|
387
444
|
location: LabwareLocation,
|
|
388
445
|
quantity: int,
|
|
446
|
+
labware_ids: Optional[List[str]] = None,
|
|
389
447
|
) -> List[LoadedLabwareData]:
|
|
390
448
|
"""Load one or many lid labware by assigning an identifier and pulling required data.
|
|
391
449
|
|
|
@@ -394,6 +452,7 @@ class EquipmentHandler:
|
|
|
394
452
|
namespace: The lid labware's namespace.
|
|
395
453
|
version: The lid labware's version.
|
|
396
454
|
location: The deck location at which lid(s) will be placed.
|
|
455
|
+
quantity: The quantity of lids to load at a location.
|
|
397
456
|
labware_ids: An optional list of identifiers to assign the labware. If None,
|
|
398
457
|
an identifier will be generated.
|
|
399
458
|
|
|
@@ -425,21 +484,32 @@ class EquipmentHandler:
|
|
|
425
484
|
f"Requested quantity {quantity} is greater than the stack limit of {stack_limit} provided by definition for {load_name}."
|
|
426
485
|
)
|
|
427
486
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
487
|
+
is_deck_slot_compatible = (
|
|
488
|
+
True
|
|
489
|
+
if definition.parameters.isDeckSlotCompatible is None
|
|
490
|
+
else definition.parameters.isDeckSlotCompatible
|
|
491
|
+
)
|
|
492
|
+
|
|
493
|
+
if isinstance(location, DeckSlotLocation) and not is_deck_slot_compatible:
|
|
434
494
|
raise ValueError(
|
|
435
495
|
f"Lid Labware {load_name} cannot be loaded onto a Deck Slot."
|
|
436
496
|
)
|
|
437
497
|
|
|
438
|
-
load_labware_data_list = []
|
|
439
|
-
|
|
498
|
+
load_labware_data_list: list[LoadedLabwareData] = []
|
|
499
|
+
ids: list[str] = []
|
|
500
|
+
if labware_ids is not None:
|
|
501
|
+
if len(labware_ids) < quantity:
|
|
502
|
+
raise ValueError(
|
|
503
|
+
f"Requested quantity {quantity} is greater than the number of labware lid IDs provided for {load_name}."
|
|
504
|
+
)
|
|
505
|
+
ids = labware_ids
|
|
506
|
+
else:
|
|
507
|
+
for i in range(quantity):
|
|
508
|
+
ids.append(self._model_utils.generate_id())
|
|
509
|
+
for id in ids:
|
|
440
510
|
load_labware_data_list.append(
|
|
441
511
|
LoadedLabwareData(
|
|
442
|
-
labware_id=
|
|
512
|
+
labware_id=id,
|
|
443
513
|
definition=definition,
|
|
444
514
|
offsetId=None,
|
|
445
515
|
)
|
|
@@ -581,6 +651,13 @@ class EquipmentHandler:
|
|
|
581
651
|
) -> Optional[AbsorbanceReader]:
|
|
582
652
|
...
|
|
583
653
|
|
|
654
|
+
@overload
|
|
655
|
+
def get_module_hardware_api(
|
|
656
|
+
self,
|
|
657
|
+
module_id: FlexStackerId,
|
|
658
|
+
) -> Optional[FlexStacker]:
|
|
659
|
+
...
|
|
660
|
+
|
|
584
661
|
def get_module_hardware_api(self, module_id: str) -> Optional[AbstractModule]:
|
|
585
662
|
"""Get the hardware API for a given module."""
|
|
586
663
|
use_virtual_modules = self._state_store.config.use_virtual_modules
|
|
@@ -599,8 +676,11 @@ class EquipmentHandler:
|
|
|
599
676
|
)
|
|
600
677
|
|
|
601
678
|
def find_applicable_labware_offset_id(
|
|
602
|
-
self,
|
|
603
|
-
|
|
679
|
+
self,
|
|
680
|
+
labware_definition_uri: str,
|
|
681
|
+
labware_location: LabwareLocation,
|
|
682
|
+
labware_pending_load: dict[str, LoadedLabware] | None = None,
|
|
683
|
+
) -> str | None:
|
|
604
684
|
"""Figure out what offset would apply to a labware in the given location.
|
|
605
685
|
|
|
606
686
|
Raises:
|
|
@@ -612,8 +692,11 @@ class EquipmentHandler:
|
|
|
612
692
|
or None if no labware offset will apply.
|
|
613
693
|
"""
|
|
614
694
|
labware_offset_location = (
|
|
615
|
-
self.
|
|
695
|
+
self._state_store.geometry.get_projected_offset_location(
|
|
696
|
+
labware_location, labware_pending_load
|
|
697
|
+
)
|
|
616
698
|
)
|
|
699
|
+
|
|
617
700
|
if labware_offset_location is None:
|
|
618
701
|
# No offset for off-deck location.
|
|
619
702
|
# Returning None instead of raising an exception allows loading a labware
|
|
@@ -626,72 +709,6 @@ class EquipmentHandler:
|
|
|
626
709
|
)
|
|
627
710
|
return self._get_id_from_offset(offset)
|
|
628
711
|
|
|
629
|
-
def _get_labware_offset_location_from_labware_location(
|
|
630
|
-
self, labware_location: LabwareLocation
|
|
631
|
-
) -> Optional[LabwareOffsetLocation]:
|
|
632
|
-
if isinstance(labware_location, DeckSlotLocation):
|
|
633
|
-
return LabwareOffsetLocation(slotName=labware_location.slotName)
|
|
634
|
-
elif isinstance(labware_location, ModuleLocation):
|
|
635
|
-
module_id = labware_location.moduleId
|
|
636
|
-
# Allow ModuleNotLoadedError to propagate.
|
|
637
|
-
# Note also that we match based on the module's requested model, not its
|
|
638
|
-
# actual model, to implement robot-server's documented HTTP API semantics.
|
|
639
|
-
module_model = self._state_store.modules.get_requested_model(
|
|
640
|
-
module_id=module_id
|
|
641
|
-
)
|
|
642
|
-
|
|
643
|
-
# If `module_model is None`, it probably means that this module was added by
|
|
644
|
-
# `ProtocolEngine.use_attached_modules()`, instead of an explicit
|
|
645
|
-
# `loadModule` command.
|
|
646
|
-
#
|
|
647
|
-
# This assert should never raise in practice because:
|
|
648
|
-
# 1. `ProtocolEngine.use_attached_modules()` is only used by
|
|
649
|
-
# robot-server's "stateless command" endpoints, under `/commands`.
|
|
650
|
-
# 2. Those endpoints don't support loading labware, so this code will
|
|
651
|
-
# never run.
|
|
652
|
-
#
|
|
653
|
-
# Nevertheless, if it does happen somehow, we do NOT want to pass the
|
|
654
|
-
# `None` value along to `LabwareView.find_applicable_labware_offset()`.
|
|
655
|
-
# `None` means something different there, which will cause us to return
|
|
656
|
-
# wrong results.
|
|
657
|
-
assert module_model is not None, (
|
|
658
|
-
"Can't find offsets for labware"
|
|
659
|
-
" that are loaded on modules"
|
|
660
|
-
" that were loaded with ProtocolEngine.use_attached_modules()."
|
|
661
|
-
)
|
|
662
|
-
|
|
663
|
-
module_location = self._state_store.modules.get_location(
|
|
664
|
-
module_id=module_id
|
|
665
|
-
)
|
|
666
|
-
slot_name = module_location.slotName
|
|
667
|
-
return LabwareOffsetLocation(slotName=slot_name, moduleModel=module_model)
|
|
668
|
-
elif isinstance(labware_location, OnLabwareLocation):
|
|
669
|
-
parent_labware_id = labware_location.labwareId
|
|
670
|
-
parent_labware_uri = self._state_store.labware.get_definition_uri(
|
|
671
|
-
parent_labware_id
|
|
672
|
-
)
|
|
673
|
-
|
|
674
|
-
base_location = self._state_store.labware.get_parent_location(
|
|
675
|
-
parent_labware_id
|
|
676
|
-
)
|
|
677
|
-
base_labware_offset_location = (
|
|
678
|
-
self._get_labware_offset_location_from_labware_location(base_location)
|
|
679
|
-
)
|
|
680
|
-
if base_labware_offset_location is None:
|
|
681
|
-
# No offset for labware sitting on labware off-deck
|
|
682
|
-
return None
|
|
683
|
-
|
|
684
|
-
# If labware is being stacked on itself, all labware in the stack will share a labware offset due to
|
|
685
|
-
# them sharing the same definitionUri in `LabwareOffsetLocation`. This will not be true for the
|
|
686
|
-
# bottom-most labware, which will have a `DeckSlotLocation` and have its definitionUri field empty.
|
|
687
|
-
return LabwareOffsetLocation(
|
|
688
|
-
slotName=base_labware_offset_location.slotName,
|
|
689
|
-
moduleModel=base_labware_offset_location.moduleModel,
|
|
690
|
-
definitionUri=parent_labware_uri,
|
|
691
|
-
)
|
|
692
|
-
else: # Off deck
|
|
693
|
-
return None
|
|
694
|
-
|
|
695
712
|
@staticmethod
|
|
696
713
|
def _get_id_from_offset(labware_offset: Optional[LabwareOffset]) -> Optional[str]:
|
|
697
714
|
return None if labware_offset is None else labware_offset.id
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""Labware movement command handling."""
|
|
2
|
+
|
|
2
3
|
from __future__ import annotations
|
|
3
4
|
|
|
4
5
|
from typing import Optional, TYPE_CHECKING, overload
|
|
@@ -128,6 +129,13 @@ class LabwareMovementHandler:
|
|
|
128
129
|
assert labware_id is not None # From this method's @typing.overloads.
|
|
129
130
|
labware_definition = self._state_store.labware.get_definition(labware_id)
|
|
130
131
|
|
|
132
|
+
from_labware_center = self._state_store.geometry.get_labware_grip_point(
|
|
133
|
+
labware_definition=labware_definition, location=current_location
|
|
134
|
+
)
|
|
135
|
+
to_labware_center = self._state_store.geometry.get_labware_grip_point(
|
|
136
|
+
labware_definition=labware_definition, location=new_location
|
|
137
|
+
)
|
|
138
|
+
|
|
131
139
|
if use_virtual_gripper:
|
|
132
140
|
# todo(mm, 2024-11-07): We should do this collision checking even when we
|
|
133
141
|
# only have a `labware_definition`, not a `labware_id`. Resolve when
|
|
@@ -182,12 +190,6 @@ class LabwareMovementHandler:
|
|
|
182
190
|
current_labware=labware_definition,
|
|
183
191
|
)
|
|
184
192
|
)
|
|
185
|
-
from_labware_center = self._state_store.geometry.get_labware_grip_point(
|
|
186
|
-
labware_definition=labware_definition, location=current_location
|
|
187
|
-
)
|
|
188
|
-
to_labware_center = self._state_store.geometry.get_labware_grip_point(
|
|
189
|
-
labware_definition=labware_definition, location=new_location
|
|
190
|
-
)
|
|
191
193
|
movement_waypoints = get_gripper_labware_movement_waypoints(
|
|
192
194
|
from_labware_center=from_labware_center,
|
|
193
195
|
to_labware_center=to_labware_center,
|