opentrons 8.3.2a0__py2.py3-none-any.whl → 8.4.0__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 +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 +102 -5
- opentrons/legacy_commands/helpers.py +74 -1
- opentrons/legacy_commands/types.py +33 -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 +1356 -107
- 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/pipette_movement_conflict.py +6 -14
- 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 +858 -0
- opentrons/protocol_api/core/engine/well.py +73 -5
- opentrons/protocol_api/core/instrument.py +71 -21
- 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 +76 -49
- 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 +27 -2
- 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 +73 -23
- opentrons/protocol_api/core/module.py +43 -0
- opentrons/protocol_api/core/protocol.py +33 -0
- opentrons/protocol_api/core/well.py +23 -2
- opentrons/protocol_api/instrument_context.py +454 -150
- opentrons/protocol_api/labware.py +98 -50
- opentrons/protocol_api/module_contexts.py +140 -0
- opentrons/protocol_api/protocol_context.py +163 -19
- 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 +66 -36
- 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 +210 -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 +102 -33
- 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 +204 -0
- opentrons/protocol_engine/commands/drop_tip.py +23 -1
- 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 +291 -0
- opentrons/protocol_engine/commands/generate_command_schema.py +31 -2
- opentrons/protocol_engine/commands/labware_handling_common.py +29 -0
- opentrons/protocol_engine/commands/liquid_probe.py +27 -13
- 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/move_to_well.py +5 -11
- opentrons/protocol_engine/commands/pick_up_tip.py +5 -2
- opentrons/protocol_engine/commands/pipetting_common.py +159 -8
- opentrons/protocol_engine/commands/prepare_to_aspirate.py +15 -5
- opentrons/protocol_engine/commands/{evotip_dispense.py → pressure_dispense.py} +33 -34
- opentrons/protocol_engine/commands/reload_labware.py +6 -19
- opentrons/protocol_engine/commands/{evotip_seal_pipette.py → seal_pipette_to_tip.py} +97 -76
- 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/commands/{evotip_unseal_pipette.py → unseal_pipette_from_tip.py} +31 -40
- opentrons/protocol_engine/errors/__init__.py +10 -0
- opentrons/protocol_engine/errors/exceptions.py +62 -0
- opentrons/protocol_engine/execution/equipment.py +123 -106
- opentrons/protocol_engine/execution/labware_movement.py +8 -6
- opentrons/protocol_engine/execution/pipetting.py +235 -25
- opentrons/protocol_engine/execution/tip_handler.py +82 -32
- opentrons/protocol_engine/labware_offset_standardization.py +194 -0
- opentrons/protocol_engine/protocol_engine.py +22 -13
- opentrons/protocol_engine/resources/deck_configuration_provider.py +98 -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 +36 -14
- opentrons/protocol_engine/state/geometry.py +892 -227
- 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 +210 -67
- 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 +55 -9
- opentrons/protocol_engine/types/__init__.py +300 -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 +111 -0
- opentrons/protocol_engine/types/labware_movement.py +22 -0
- opentrons/protocol_engine/types/labware_offset_location.py +111 -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 +131 -0
- opentrons/protocol_engine/types/location.py +194 -0
- opentrons/protocol_engine/types/module.py +301 -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 +124 -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 +27 -23
- opentrons/protocols/models/__init__.py +0 -21
- opentrons/simulate.py +4 -2
- opentrons/types.py +20 -7
- opentrons/util/logging_config.py +94 -25
- opentrons/util/logging_queue_handler.py +61 -0
- {opentrons-8.3.2a0.dist-info → opentrons-8.4.0.dist-info}/METADATA +4 -4
- {opentrons-8.3.2a0.dist-info → opentrons-8.4.0.dist-info}/RECORD +192 -151
- 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.2a0.dist-info → opentrons-8.4.0.dist-info}/LICENSE +0 -0
- {opentrons-8.3.2a0.dist-info → opentrons-8.4.0.dist-info}/WHEEL +0 -0
- {opentrons-8.3.2a0.dist-info → opentrons-8.4.0.dist-info}/entry_points.txt +0 -0
- {opentrons-8.3.2a0.dist-info → opentrons-8.4.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
"""Convert labware offset creation requests and stored elements between legacy and new."""
|
|
2
|
+
|
|
3
|
+
from opentrons_shared_data.robot.types import RobotType
|
|
4
|
+
from opentrons_shared_data.deck.types import DeckDefinitionV5
|
|
5
|
+
from .errors import (
|
|
6
|
+
OffsetLocationInvalidError,
|
|
7
|
+
FixtureDoesNotExistError,
|
|
8
|
+
)
|
|
9
|
+
from .types import (
|
|
10
|
+
LabwareOffsetCreate,
|
|
11
|
+
LegacyLabwareOffsetCreate,
|
|
12
|
+
LabwareOffsetCreateInternal,
|
|
13
|
+
LegacyLabwareOffsetLocation,
|
|
14
|
+
LabwareOffsetLocationSequence,
|
|
15
|
+
OnLabwareOffsetLocationSequenceComponent,
|
|
16
|
+
OnAddressableAreaOffsetLocationSequenceComponent,
|
|
17
|
+
OnModuleOffsetLocationSequenceComponent,
|
|
18
|
+
ModuleModel,
|
|
19
|
+
)
|
|
20
|
+
from .resources import deck_configuration_provider
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def standardize_labware_offset_create(
|
|
24
|
+
request: LabwareOffsetCreate | LegacyLabwareOffsetCreate,
|
|
25
|
+
robot_type: RobotType,
|
|
26
|
+
deck_definition: DeckDefinitionV5,
|
|
27
|
+
) -> LabwareOffsetCreateInternal:
|
|
28
|
+
"""Turn a union of old and new labware offset create requests into a new one."""
|
|
29
|
+
location_sequence, legacy_location = _locations_for_create(
|
|
30
|
+
request, robot_type, deck_definition
|
|
31
|
+
)
|
|
32
|
+
return LabwareOffsetCreateInternal(
|
|
33
|
+
definitionUri=request.definitionUri,
|
|
34
|
+
locationSequence=location_sequence,
|
|
35
|
+
legacyLocation=legacy_location,
|
|
36
|
+
vector=request.vector,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def legacy_offset_location_to_offset_location_sequence(
|
|
41
|
+
location: LegacyLabwareOffsetLocation, deck_definition: DeckDefinitionV5
|
|
42
|
+
) -> LabwareOffsetLocationSequence:
|
|
43
|
+
"""Convert a legacy location to a new-style sequence."""
|
|
44
|
+
sequence: LabwareOffsetLocationSequence = []
|
|
45
|
+
if location.definitionUri:
|
|
46
|
+
sequence.append(
|
|
47
|
+
OnLabwareOffsetLocationSequenceComponent(labwareUri=location.definitionUri)
|
|
48
|
+
)
|
|
49
|
+
if location.moduleModel:
|
|
50
|
+
sequence.append(
|
|
51
|
+
OnModuleOffsetLocationSequenceComponent(moduleModel=location.moduleModel)
|
|
52
|
+
)
|
|
53
|
+
cutout_id = deck_configuration_provider.get_cutout_id_by_deck_slot_name(
|
|
54
|
+
location.slotName
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
# Given a module model, try to figure out the equivalent cutout fixture.
|
|
58
|
+
#
|
|
59
|
+
# The Thermocycler is special. A single Thermocycler is represented in a deck
|
|
60
|
+
# configuration as two separate cutout fixtures, because it spans two separate
|
|
61
|
+
# cutouts. This makes it the only module whose module model string does not map
|
|
62
|
+
# 1:1 with a cutout fixture ID string.
|
|
63
|
+
#
|
|
64
|
+
# TODO(mm, 2025-04-11): This is fragile, and the failure mode when it does the
|
|
65
|
+
# wrong thing can mean labware offsets don't apply, which is pretty bad. We
|
|
66
|
+
# either need a more explicit module<->cutout-fixture mapping, or we need to
|
|
67
|
+
# avoid this mapping entirely.
|
|
68
|
+
if (
|
|
69
|
+
# Check for v2 specifically because v1 is OT-2-only and OT-2s don't have
|
|
70
|
+
# modules in their deck definitions; and v3 does not exist at the time of writing.
|
|
71
|
+
location.moduleModel
|
|
72
|
+
== ModuleModel.THERMOCYCLER_MODULE_V2
|
|
73
|
+
):
|
|
74
|
+
possible_cutout_fixture_id = "thermocyclerModuleV2Front"
|
|
75
|
+
else:
|
|
76
|
+
possible_cutout_fixture_id = location.moduleModel.value
|
|
77
|
+
|
|
78
|
+
try:
|
|
79
|
+
addressable_area = deck_configuration_provider.get_labware_hosting_addressable_area_name_for_cutout_and_cutout_fixture(
|
|
80
|
+
cutout_id, possible_cutout_fixture_id, deck_definition
|
|
81
|
+
)
|
|
82
|
+
sequence.append(
|
|
83
|
+
OnAddressableAreaOffsetLocationSequenceComponent(
|
|
84
|
+
addressableAreaName=addressable_area
|
|
85
|
+
)
|
|
86
|
+
)
|
|
87
|
+
except FixtureDoesNotExistError:
|
|
88
|
+
# this is an OT-2 (or this module isn't supported in the deck definition) and we should use a
|
|
89
|
+
# slot addressable area name
|
|
90
|
+
sequence.append(
|
|
91
|
+
OnAddressableAreaOffsetLocationSequenceComponent(
|
|
92
|
+
addressableAreaName=location.slotName.value
|
|
93
|
+
)
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
else:
|
|
97
|
+
# Slight hack: we should have a more formal association here. However, since the slot
|
|
98
|
+
# name is already standardized, and since the addressable areas for slots are just the
|
|
99
|
+
# name of the slots, we can rely on this.
|
|
100
|
+
sequence.append(
|
|
101
|
+
OnAddressableAreaOffsetLocationSequenceComponent(
|
|
102
|
+
addressableAreaName=location.slotName.value
|
|
103
|
+
)
|
|
104
|
+
)
|
|
105
|
+
return sequence
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _offset_location_sequence_head_to_labware_and_module(
|
|
109
|
+
location_sequence: LabwareOffsetLocationSequence,
|
|
110
|
+
) -> tuple[ModuleModel | None, str | None]:
|
|
111
|
+
labware_uri: str | None = None
|
|
112
|
+
module_model: ModuleModel | None = None
|
|
113
|
+
for location in location_sequence:
|
|
114
|
+
if isinstance(location, OnAddressableAreaOffsetLocationSequenceComponent):
|
|
115
|
+
raise OffsetLocationInvalidError(
|
|
116
|
+
"Addressable areas may only be the final element of an offset location."
|
|
117
|
+
)
|
|
118
|
+
elif isinstance(location, OnLabwareOffsetLocationSequenceComponent):
|
|
119
|
+
if labware_uri is not None:
|
|
120
|
+
# We only take the first location
|
|
121
|
+
continue
|
|
122
|
+
if module_model is not None:
|
|
123
|
+
# Labware can't be underneath modules
|
|
124
|
+
raise OffsetLocationInvalidError(
|
|
125
|
+
"Labware must not be underneath a module."
|
|
126
|
+
)
|
|
127
|
+
labware_uri = location.labwareUri
|
|
128
|
+
elif isinstance(location, OnModuleOffsetLocationSequenceComponent):
|
|
129
|
+
if module_model is not None:
|
|
130
|
+
# Bad, somebody put more than one module in here
|
|
131
|
+
raise OffsetLocationInvalidError(
|
|
132
|
+
"Only one module location may exist in an offset location."
|
|
133
|
+
)
|
|
134
|
+
module_model = location.moduleModel
|
|
135
|
+
else:
|
|
136
|
+
raise OffsetLocationInvalidError(
|
|
137
|
+
f"Invalid location component in offset location: {repr(location)}"
|
|
138
|
+
)
|
|
139
|
+
return module_model, labware_uri
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _offset_location_sequence_to_legacy_offset_location(
|
|
143
|
+
location_sequence: LabwareOffsetLocationSequence, deck_definition: DeckDefinitionV5
|
|
144
|
+
) -> LegacyLabwareOffsetLocation:
|
|
145
|
+
if len(location_sequence) == 0:
|
|
146
|
+
raise OffsetLocationInvalidError(
|
|
147
|
+
"Offset locations must contain at least one component."
|
|
148
|
+
)
|
|
149
|
+
last_element = location_sequence[-1]
|
|
150
|
+
if not isinstance(last_element, OnAddressableAreaOffsetLocationSequenceComponent):
|
|
151
|
+
raise OffsetLocationInvalidError(
|
|
152
|
+
"Offset locations must end with an addressable area."
|
|
153
|
+
)
|
|
154
|
+
module_model, labware_uri = _offset_location_sequence_head_to_labware_and_module(
|
|
155
|
+
location_sequence[:-1]
|
|
156
|
+
)
|
|
157
|
+
(
|
|
158
|
+
cutout_id,
|
|
159
|
+
_cutout_fixtures,
|
|
160
|
+
) = deck_configuration_provider.get_potential_cutout_fixtures(
|
|
161
|
+
last_element.addressableAreaName, deck_definition
|
|
162
|
+
)
|
|
163
|
+
slot_name = deck_configuration_provider.get_deck_slot_for_cutout_id(cutout_id)
|
|
164
|
+
return LegacyLabwareOffsetLocation(
|
|
165
|
+
slotName=slot_name, moduleModel=module_model, definitionUri=labware_uri
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def _locations_for_create(
|
|
170
|
+
request: LabwareOffsetCreate | LegacyLabwareOffsetCreate,
|
|
171
|
+
robot_type: RobotType,
|
|
172
|
+
deck_definition: DeckDefinitionV5,
|
|
173
|
+
) -> tuple[LabwareOffsetLocationSequence, LegacyLabwareOffsetLocation]:
|
|
174
|
+
if isinstance(request, LabwareOffsetCreate):
|
|
175
|
+
return (
|
|
176
|
+
request.locationSequence,
|
|
177
|
+
_offset_location_sequence_to_legacy_offset_location(
|
|
178
|
+
request.locationSequence, deck_definition
|
|
179
|
+
),
|
|
180
|
+
)
|
|
181
|
+
else:
|
|
182
|
+
normalized = request.location.model_copy(
|
|
183
|
+
update={
|
|
184
|
+
"slotName": request.location.slotName.to_equivalent_for_robot_type(
|
|
185
|
+
robot_type
|
|
186
|
+
)
|
|
187
|
+
}
|
|
188
|
+
)
|
|
189
|
+
return (
|
|
190
|
+
legacy_offset_location_to_offset_location_sequence(
|
|
191
|
+
normalized, deck_definition
|
|
192
|
+
),
|
|
193
|
+
normalized,
|
|
194
|
+
)
|
|
@@ -1,29 +1,32 @@
|
|
|
1
1
|
"""ProtocolEngine class definition."""
|
|
2
|
+
|
|
2
3
|
from contextlib import AsyncExitStack
|
|
3
4
|
from logging import getLogger
|
|
4
5
|
from typing import Dict, Optional, Union, AsyncGenerator, Callable
|
|
5
|
-
from opentrons.protocol_engine.actions.actions import (
|
|
6
|
-
ResumeFromRecoveryAction,
|
|
7
|
-
SetErrorRecoveryPolicyAction,
|
|
8
|
-
)
|
|
9
6
|
|
|
10
|
-
from opentrons.protocols.models import LabwareDefinition
|
|
11
|
-
from opentrons.hardware_control import HardwareControlAPI
|
|
12
|
-
from opentrons.hardware_control.modules import AbstractModule as HardwareModuleAPI
|
|
13
|
-
from opentrons.hardware_control.types import PauseType as HardwarePauseType
|
|
14
7
|
from opentrons_shared_data.errors import (
|
|
15
8
|
ErrorCodes,
|
|
16
9
|
EnumeratedError,
|
|
17
10
|
)
|
|
11
|
+
from opentrons_shared_data.labware.labware_definition import LabwareDefinition
|
|
18
12
|
|
|
13
|
+
from opentrons.hardware_control import HardwareControlAPI
|
|
14
|
+
from opentrons.hardware_control.modules import AbstractModule as HardwareModuleAPI
|
|
15
|
+
from opentrons.hardware_control.types import PauseType as HardwarePauseType
|
|
16
|
+
|
|
17
|
+
from .actions.actions import (
|
|
18
|
+
ResumeFromRecoveryAction,
|
|
19
|
+
SetErrorRecoveryPolicyAction,
|
|
20
|
+
)
|
|
19
21
|
from .errors import ProtocolCommandFailedError, ErrorOccurrence, CommandNotAllowedError
|
|
20
22
|
from .errors.exceptions import EStopActivatedError
|
|
21
23
|
from .error_recovery_policy import ErrorRecoveryPolicy
|
|
22
|
-
from . import commands, slot_standardization
|
|
24
|
+
from . import commands, slot_standardization, labware_offset_standardization
|
|
23
25
|
from .resources import ModelUtils, ModuleDataProvider, FileProvider
|
|
24
26
|
from .types import (
|
|
25
27
|
LabwareOffset,
|
|
26
28
|
LabwareOffsetCreate,
|
|
29
|
+
LegacyLabwareOffsetCreate,
|
|
27
30
|
LabwareUri,
|
|
28
31
|
ModuleModel,
|
|
29
32
|
Liquid,
|
|
@@ -516,15 +519,21 @@ class ProtocolEngine:
|
|
|
516
519
|
)
|
|
517
520
|
)
|
|
518
521
|
|
|
519
|
-
def add_labware_offset(
|
|
522
|
+
def add_labware_offset(
|
|
523
|
+
self, request: LabwareOffsetCreate | LegacyLabwareOffsetCreate
|
|
524
|
+
) -> LabwareOffset:
|
|
520
525
|
"""Add a new labware offset and return it.
|
|
521
526
|
|
|
522
527
|
The added offset will apply to subsequent `LoadLabwareCommand`s.
|
|
523
528
|
|
|
524
529
|
To retrieve offsets later, see `.state_view.labware`.
|
|
525
530
|
"""
|
|
526
|
-
|
|
527
|
-
|
|
531
|
+
internal_request = (
|
|
532
|
+
labware_offset_standardization.standardize_labware_offset_create(
|
|
533
|
+
request,
|
|
534
|
+
self.state_view.config.robot_type,
|
|
535
|
+
self.state_view.addressable_areas.deck_definition,
|
|
536
|
+
)
|
|
528
537
|
)
|
|
529
538
|
|
|
530
539
|
labware_offset_id = self._model_utils.generate_id()
|
|
@@ -533,7 +542,7 @@ class ProtocolEngine:
|
|
|
533
542
|
AddLabwareOffsetAction(
|
|
534
543
|
labware_offset_id=labware_offset_id,
|
|
535
544
|
created_at=created_at,
|
|
536
|
-
request=
|
|
545
|
+
request=internal_request,
|
|
537
546
|
)
|
|
538
547
|
)
|
|
539
548
|
return self.state_view.labware.get_labware_offset(
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
"""Deck configuration resource provider."""
|
|
2
|
+
|
|
2
3
|
from typing import List, Set, Tuple
|
|
3
4
|
|
|
4
|
-
from opentrons_shared_data.deck.types import
|
|
5
|
+
from opentrons_shared_data.deck.types import (
|
|
6
|
+
DeckDefinitionV5,
|
|
7
|
+
CutoutFixture,
|
|
8
|
+
)
|
|
5
9
|
|
|
6
10
|
from opentrons.types import DeckSlotName
|
|
7
11
|
|
|
@@ -17,6 +21,7 @@ from ..errors import (
|
|
|
17
21
|
CutoutDoesNotExistError,
|
|
18
22
|
FixtureDoesNotExistError,
|
|
19
23
|
AddressableAreaDoesNotExistError,
|
|
24
|
+
SlotDoesNotExistError,
|
|
20
25
|
)
|
|
21
26
|
|
|
22
27
|
|
|
@@ -98,12 +103,15 @@ def get_potential_cutout_fixtures(
|
|
|
98
103
|
def get_addressable_area_from_name(
|
|
99
104
|
addressable_area_name: str,
|
|
100
105
|
cutout_position: DeckPoint,
|
|
101
|
-
base_slot: DeckSlotName,
|
|
102
106
|
deck_definition: DeckDefinitionV5,
|
|
103
107
|
) -> AddressableArea:
|
|
104
108
|
"""Given a name and a cutout position, get an addressable area on the deck."""
|
|
105
109
|
for addressable_area in deck_definition["locations"]["addressableAreas"]:
|
|
106
110
|
if addressable_area["id"] == addressable_area_name:
|
|
111
|
+
cutout_id, _ = get_potential_cutout_fixtures(
|
|
112
|
+
addressable_area_name, deck_definition
|
|
113
|
+
)
|
|
114
|
+
base_slot = get_deck_slot_for_cutout_id(cutout_id)
|
|
107
115
|
area_offset = addressable_area["offsetFromCutoutFixture"]
|
|
108
116
|
position = AddressableOffsetVector(
|
|
109
117
|
x=area_offset[0] + cutout_position.x,
|
|
@@ -130,3 +138,91 @@ def get_addressable_area_from_name(
|
|
|
130
138
|
raise AddressableAreaDoesNotExistError(
|
|
131
139
|
f"Could not find addressable area with name {addressable_area_name}"
|
|
132
140
|
)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def get_deck_slot_for_cutout_id(cutout_id: str) -> DeckSlotName:
|
|
144
|
+
"""Get the corresponding deck slot for an addressable area."""
|
|
145
|
+
try:
|
|
146
|
+
return CUTOUT_TO_DECK_SLOT_MAP[cutout_id]
|
|
147
|
+
except KeyError as e:
|
|
148
|
+
raise CutoutDoesNotExistError(
|
|
149
|
+
f"Could not find data for cutout {cutout_id}"
|
|
150
|
+
) from e
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def get_cutout_id_by_deck_slot_name(slot_name: DeckSlotName) -> str:
|
|
154
|
+
"""Get the Cutout ID of a given Deck Slot by Deck Slot Name."""
|
|
155
|
+
try:
|
|
156
|
+
return DECK_SLOT_TO_CUTOUT_MAP[slot_name]
|
|
157
|
+
except KeyError as e:
|
|
158
|
+
raise SlotDoesNotExistError(
|
|
159
|
+
f"Could not find data for slot {slot_name.value}"
|
|
160
|
+
) from e
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def get_labware_hosting_addressable_area_name_for_cutout_and_cutout_fixture(
|
|
164
|
+
cutout_id: str, cutout_fixture_id: str, deck_definition: DeckDefinitionV5
|
|
165
|
+
) -> str:
|
|
166
|
+
"""Get the first addressable area that can contain labware for a cutout and fixture.
|
|
167
|
+
|
|
168
|
+
This probably isn't relevant outside of labware offset locations, where (for now) nothing
|
|
169
|
+
provides more than one labware-containing addressable area.
|
|
170
|
+
"""
|
|
171
|
+
for cutoutFixture in deck_definition["cutoutFixtures"]:
|
|
172
|
+
if cutoutFixture["id"] != cutout_fixture_id:
|
|
173
|
+
continue
|
|
174
|
+
provided_aas = cutoutFixture["providesAddressableAreas"].get(cutout_id, None)
|
|
175
|
+
if provided_aas is None:
|
|
176
|
+
raise CutoutDoesNotExistError(
|
|
177
|
+
f"{cutout_fixture_id} does not go in {cutout_id}"
|
|
178
|
+
)
|
|
179
|
+
for aa_id in provided_aas:
|
|
180
|
+
for addressable_area in deck_definition["locations"]["addressableAreas"]:
|
|
181
|
+
if addressable_area["id"] != aa_id:
|
|
182
|
+
continue
|
|
183
|
+
# TODO: In deck def v6 this will be easier, but as of right now there isn't really
|
|
184
|
+
# a way to tell from an addressable area whether it takes labware so let's take the
|
|
185
|
+
# first one
|
|
186
|
+
return aa_id
|
|
187
|
+
raise AddressableAreaDoesNotExistError(
|
|
188
|
+
f"Could not find an addressable area that allows labware from cutout fixture {cutout_fixture_id} in cutout {cutout_id}"
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
raise FixtureDoesNotExistError(f"Could not find entry for {cutout_fixture_id}")
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
# This is a temporary shim while Protocol Engine's conflict-checking code
|
|
195
|
+
# can only take deck slots as input.
|
|
196
|
+
# Long-term solution: Check for conflicts based on bounding boxes, not slot adjacencies.
|
|
197
|
+
# Shorter-term: Change the conflict-checking code to take cutouts instead of deck slots.
|
|
198
|
+
CUTOUT_TO_DECK_SLOT_MAP: dict[str, DeckSlotName] = {
|
|
199
|
+
# OT-2
|
|
200
|
+
"cutout1": DeckSlotName.SLOT_1,
|
|
201
|
+
"cutout2": DeckSlotName.SLOT_2,
|
|
202
|
+
"cutout3": DeckSlotName.SLOT_3,
|
|
203
|
+
"cutout4": DeckSlotName.SLOT_4,
|
|
204
|
+
"cutout5": DeckSlotName.SLOT_5,
|
|
205
|
+
"cutout6": DeckSlotName.SLOT_6,
|
|
206
|
+
"cutout7": DeckSlotName.SLOT_7,
|
|
207
|
+
"cutout8": DeckSlotName.SLOT_8,
|
|
208
|
+
"cutout9": DeckSlotName.SLOT_9,
|
|
209
|
+
"cutout10": DeckSlotName.SLOT_10,
|
|
210
|
+
"cutout11": DeckSlotName.SLOT_11,
|
|
211
|
+
"cutout12": DeckSlotName.FIXED_TRASH,
|
|
212
|
+
# Flex
|
|
213
|
+
"cutoutA1": DeckSlotName.SLOT_A1,
|
|
214
|
+
"cutoutA2": DeckSlotName.SLOT_A2,
|
|
215
|
+
"cutoutA3": DeckSlotName.SLOT_A3,
|
|
216
|
+
"cutoutB1": DeckSlotName.SLOT_B1,
|
|
217
|
+
"cutoutB2": DeckSlotName.SLOT_B2,
|
|
218
|
+
"cutoutB3": DeckSlotName.SLOT_B3,
|
|
219
|
+
"cutoutC1": DeckSlotName.SLOT_C1,
|
|
220
|
+
"cutoutC2": DeckSlotName.SLOT_C2,
|
|
221
|
+
"cutoutC3": DeckSlotName.SLOT_C3,
|
|
222
|
+
"cutoutD1": DeckSlotName.SLOT_D1,
|
|
223
|
+
"cutoutD2": DeckSlotName.SLOT_D2,
|
|
224
|
+
"cutoutD3": DeckSlotName.SLOT_D3,
|
|
225
|
+
}
|
|
226
|
+
DECK_SLOT_TO_CUTOUT_MAP = {
|
|
227
|
+
deck_slot: cutout for cutout, deck_slot in CUTOUT_TO_DECK_SLOT_MAP.items()
|
|
228
|
+
}
|
|
@@ -10,7 +10,7 @@ from opentrons_shared_data.deck import (
|
|
|
10
10
|
DEFAULT_DECK_DEFINITION_VERSION,
|
|
11
11
|
)
|
|
12
12
|
from opentrons_shared_data.deck.types import DeckDefinitionV5
|
|
13
|
-
from
|
|
13
|
+
from opentrons_shared_data.labware.labware_definition import LabwareDefinition
|
|
14
14
|
from opentrons.types import DeckSlotName
|
|
15
15
|
|
|
16
16
|
from ..types import (
|
|
@@ -6,7 +6,12 @@ abstract away rough edges until we can improve those underlying interfaces.
|
|
|
6
6
|
import logging
|
|
7
7
|
from anyio import to_thread
|
|
8
8
|
|
|
9
|
-
from
|
|
9
|
+
from opentrons_shared_data.labware.labware_definition import (
|
|
10
|
+
LabwareDefinition,
|
|
11
|
+
LabwareDefinition3,
|
|
12
|
+
labware_definition_type_adapter,
|
|
13
|
+
)
|
|
14
|
+
|
|
10
15
|
from opentrons.protocols.labware import get_labware_definition
|
|
11
16
|
|
|
12
17
|
# TODO (lc 09-26-2022) We should conditionally import ot2 or ot3 calibration
|
|
@@ -44,7 +49,7 @@ class LabwareDataProvider:
|
|
|
44
49
|
def _get_labware_definition_sync(
|
|
45
50
|
load_name: str, namespace: str, version: int
|
|
46
51
|
) -> LabwareDefinition:
|
|
47
|
-
return
|
|
52
|
+
return labware_definition_type_adapter.validate_python(
|
|
48
53
|
get_labware_definition(load_name, namespace, version)
|
|
49
54
|
)
|
|
50
55
|
|
|
@@ -72,15 +77,30 @@ class LabwareDataProvider:
|
|
|
72
77
|
labware_definition: LabwareDefinition,
|
|
73
78
|
nominal_fallback: float,
|
|
74
79
|
) -> float:
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
80
|
+
if isinstance(labware_definition, LabwareDefinition3):
|
|
81
|
+
# FIXME(mm, 2025-02-19): This needs to be resolved for v8.4.0.
|
|
82
|
+
# Tip length calibration internals don't yet support schema 3 because
|
|
83
|
+
# it's probably an incompatible change at the filesystem level
|
|
84
|
+
# (not downgrade-safe), and because robot-server's calibration sessions
|
|
85
|
+
# are built atop opentrons.protocol_api.core.legacy, which does not (yet?)
|
|
86
|
+
# support labware schema 3.
|
|
87
|
+
# https://opentrons.atlassian.net/browse/EXEC-1230
|
|
88
|
+
log.warning(
|
|
89
|
+
f"Tip rack"
|
|
90
|
+
f" {labware_definition.namespace}/{labware_definition.parameters.loadName}/{labware_definition.version}"
|
|
91
|
+
f" has schema 3, so tip length calibration is currently unsupported."
|
|
92
|
+
f" Using nominal fallback of {nominal_fallback}."
|
|
84
93
|
)
|
|
85
|
-
log.debug(message, exc_info=e)
|
|
86
94
|
return nominal_fallback
|
|
95
|
+
else:
|
|
96
|
+
try:
|
|
97
|
+
return instr_cal.load_tip_length_for_pipette(
|
|
98
|
+
pipette_serial, labware_definition
|
|
99
|
+
).tip_length
|
|
100
|
+
except TipLengthCalNotFound as e:
|
|
101
|
+
message = (
|
|
102
|
+
f"No calibrated tip length found for {pipette_serial},"
|
|
103
|
+
f" using nominal fallback value of {nominal_fallback}"
|
|
104
|
+
)
|
|
105
|
+
log.debug(message, exc_info=e)
|
|
106
|
+
return nominal_fallback
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"""Validation file for labware role and location checking functions."""
|
|
2
2
|
|
|
3
|
-
from opentrons_shared_data.labware.labware_definition import
|
|
4
|
-
|
|
3
|
+
from opentrons_shared_data.labware.labware_definition import (
|
|
4
|
+
LabwareDefinition,
|
|
5
|
+
LabwareRole,
|
|
6
|
+
)
|
|
5
7
|
|
|
6
8
|
|
|
7
9
|
def is_flex_trash(load_name: str) -> bool:
|
|
@@ -14,9 +16,9 @@ def is_absorbance_reader_lid(load_name: str) -> bool:
|
|
|
14
16
|
return load_name == "opentrons_flex_lid_absorbance_plate_reader_module"
|
|
15
17
|
|
|
16
18
|
|
|
17
|
-
def
|
|
18
|
-
"""Check if a labware is
|
|
19
|
-
return load_name == "
|
|
19
|
+
def is_lid_stack(load_name: str) -> bool:
|
|
20
|
+
"""Check if a labware object is a system lid stack object."""
|
|
21
|
+
return load_name == "protocol_engine_lid_stack_object"
|
|
20
22
|
|
|
21
23
|
|
|
22
24
|
def validate_definition_is_labware(definition: LabwareDefinition) -> bool:
|
|
@@ -14,7 +14,6 @@ This module does that conversion, for any Protocol Engine input that contains a
|
|
|
14
14
|
deck slot.
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
|
-
|
|
18
17
|
from typing import Any, Callable, Dict, Type
|
|
19
18
|
|
|
20
19
|
from opentrons_shared_data.robot.types import RobotType
|
|
@@ -22,32 +21,15 @@ from opentrons_shared_data.robot.types import RobotType
|
|
|
22
21
|
from . import commands
|
|
23
22
|
from .types import (
|
|
24
23
|
OFF_DECK_LOCATION,
|
|
24
|
+
SYSTEM_LOCATION,
|
|
25
25
|
DeckSlotLocation,
|
|
26
|
-
|
|
26
|
+
LoadableLabwareLocation,
|
|
27
27
|
AddressableAreaLocation,
|
|
28
|
-
LabwareOffsetCreate,
|
|
29
28
|
ModuleLocation,
|
|
30
29
|
OnLabwareLocation,
|
|
31
30
|
)
|
|
32
31
|
|
|
33
32
|
|
|
34
|
-
def standardize_labware_offset(
|
|
35
|
-
original: LabwareOffsetCreate, robot_type: RobotType
|
|
36
|
-
) -> LabwareOffsetCreate:
|
|
37
|
-
"""Convert the deck slot in the given `LabwareOffsetCreate` to match the given robot type."""
|
|
38
|
-
return original.model_copy(
|
|
39
|
-
update={
|
|
40
|
-
"location": original.location.model_copy(
|
|
41
|
-
update={
|
|
42
|
-
"slotName": original.location.slotName.to_equivalent_for_robot_type(
|
|
43
|
-
robot_type
|
|
44
|
-
)
|
|
45
|
-
}
|
|
46
|
-
)
|
|
47
|
-
}
|
|
48
|
-
)
|
|
49
|
-
|
|
50
|
-
|
|
51
33
|
def standardize_command(
|
|
52
34
|
original: commands.CommandCreate, robot_type: RobotType
|
|
53
35
|
) -> commands.CommandCreate:
|
|
@@ -119,15 +101,21 @@ _standardize_command_functions: Dict[
|
|
|
119
101
|
|
|
120
102
|
|
|
121
103
|
def _standardize_labware_location(
|
|
122
|
-
original:
|
|
123
|
-
) ->
|
|
104
|
+
original: LoadableLabwareLocation, robot_type: RobotType
|
|
105
|
+
) -> LoadableLabwareLocation:
|
|
124
106
|
if isinstance(original, DeckSlotLocation):
|
|
125
107
|
return _standardize_deck_slot_location(original, robot_type)
|
|
126
108
|
elif (
|
|
127
109
|
isinstance(
|
|
128
|
-
original,
|
|
110
|
+
original,
|
|
111
|
+
(
|
|
112
|
+
ModuleLocation,
|
|
113
|
+
OnLabwareLocation,
|
|
114
|
+
AddressableAreaLocation,
|
|
115
|
+
),
|
|
129
116
|
)
|
|
130
117
|
or original == OFF_DECK_LOCATION
|
|
118
|
+
or original == SYSTEM_LOCATION
|
|
131
119
|
):
|
|
132
120
|
return original
|
|
133
121
|
|