opentrons 8.1.0a0__py2.py3-none-any.whl → 8.2.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.
- opentrons/cli/analyze.py +71 -7
- opentrons/config/__init__.py +9 -0
- opentrons/config/advanced_settings.py +22 -0
- opentrons/config/defaults_ot3.py +14 -36
- opentrons/config/feature_flags.py +4 -0
- opentrons/config/types.py +6 -17
- opentrons/drivers/absorbance_reader/abstract.py +27 -3
- opentrons/drivers/absorbance_reader/async_byonoy.py +207 -154
- opentrons/drivers/absorbance_reader/driver.py +24 -15
- opentrons/drivers/absorbance_reader/hid_protocol.py +79 -50
- opentrons/drivers/absorbance_reader/simulator.py +32 -6
- opentrons/drivers/types.py +23 -1
- opentrons/execute.py +2 -2
- opentrons/hardware_control/api.py +18 -10
- opentrons/hardware_control/backends/controller.py +3 -2
- opentrons/hardware_control/backends/flex_protocol.py +11 -5
- opentrons/hardware_control/backends/ot3controller.py +18 -50
- opentrons/hardware_control/backends/ot3simulator.py +7 -6
- opentrons/hardware_control/instruments/ot2/pipette_handler.py +22 -82
- opentrons/hardware_control/instruments/ot3/pipette_handler.py +10 -2
- opentrons/hardware_control/module_control.py +43 -2
- opentrons/hardware_control/modules/__init__.py +7 -1
- opentrons/hardware_control/modules/absorbance_reader.py +230 -83
- opentrons/hardware_control/modules/errors.py +7 -0
- opentrons/hardware_control/modules/heater_shaker.py +8 -3
- opentrons/hardware_control/modules/magdeck.py +12 -3
- opentrons/hardware_control/modules/mod_abc.py +27 -2
- opentrons/hardware_control/modules/tempdeck.py +15 -7
- opentrons/hardware_control/modules/thermocycler.py +69 -3
- opentrons/hardware_control/modules/types.py +11 -5
- opentrons/hardware_control/modules/update.py +11 -5
- opentrons/hardware_control/modules/utils.py +3 -1
- opentrons/hardware_control/ot3_calibration.py +6 -6
- opentrons/hardware_control/ot3api.py +126 -89
- opentrons/hardware_control/poller.py +15 -11
- opentrons/hardware_control/protocols/__init__.py +1 -7
- opentrons/hardware_control/protocols/instrument_configurer.py +14 -2
- opentrons/hardware_control/protocols/liquid_handler.py +5 -0
- opentrons/motion_planning/__init__.py +2 -0
- opentrons/motion_planning/waypoints.py +32 -0
- opentrons/protocol_api/__init__.py +2 -1
- opentrons/protocol_api/_liquid.py +87 -1
- opentrons/protocol_api/_parameter_context.py +10 -1
- opentrons/protocol_api/core/engine/deck_conflict.py +0 -297
- opentrons/protocol_api/core/engine/instrument.py +29 -25
- opentrons/protocol_api/core/engine/labware.py +10 -2
- opentrons/protocol_api/core/engine/module_core.py +129 -17
- opentrons/protocol_api/core/engine/pipette_movement_conflict.py +355 -0
- opentrons/protocol_api/core/engine/protocol.py +55 -2
- opentrons/protocol_api/core/instrument.py +2 -0
- opentrons/protocol_api/core/legacy/legacy_instrument_core.py +2 -0
- opentrons/protocol_api/core/legacy/legacy_protocol_core.py +5 -2
- opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +2 -0
- opentrons/protocol_api/core/module.py +22 -4
- opentrons/protocol_api/core/protocol.py +5 -2
- opentrons/protocol_api/instrument_context.py +52 -20
- opentrons/protocol_api/labware.py +13 -1
- opentrons/protocol_api/module_contexts.py +68 -13
- opentrons/protocol_api/protocol_context.py +38 -4
- opentrons/protocol_api/validation.py +5 -3
- opentrons/protocol_engine/__init__.py +10 -9
- opentrons/protocol_engine/actions/__init__.py +5 -0
- opentrons/protocol_engine/actions/actions.py +42 -25
- opentrons/protocol_engine/actions/get_state_update.py +38 -0
- opentrons/protocol_engine/clients/sync_client.py +7 -1
- opentrons/protocol_engine/clients/transports.py +1 -1
- opentrons/protocol_engine/commands/__init__.py +0 -4
- opentrons/protocol_engine/commands/absorbance_reader/__init__.py +41 -11
- opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +161 -0
- opentrons/protocol_engine/commands/absorbance_reader/initialize.py +53 -9
- opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +160 -0
- opentrons/protocol_engine/commands/absorbance_reader/read.py +196 -0
- opentrons/protocol_engine/commands/aspirate.py +29 -16
- opentrons/protocol_engine/commands/aspirate_in_place.py +32 -15
- opentrons/protocol_engine/commands/blow_out.py +63 -14
- opentrons/protocol_engine/commands/blow_out_in_place.py +55 -13
- opentrons/protocol_engine/commands/calibration/calibrate_gripper.py +2 -5
- opentrons/protocol_engine/commands/calibration/calibrate_module.py +3 -4
- opentrons/protocol_engine/commands/calibration/calibrate_pipette.py +2 -5
- opentrons/protocol_engine/commands/calibration/move_to_maintenance_position.py +6 -4
- opentrons/protocol_engine/commands/command.py +28 -17
- opentrons/protocol_engine/commands/command_unions.py +37 -24
- opentrons/protocol_engine/commands/comment.py +5 -3
- opentrons/protocol_engine/commands/configure_for_volume.py +11 -14
- opentrons/protocol_engine/commands/configure_nozzle_layout.py +9 -15
- opentrons/protocol_engine/commands/custom.py +5 -3
- opentrons/protocol_engine/commands/dispense.py +42 -20
- opentrons/protocol_engine/commands/dispense_in_place.py +32 -14
- opentrons/protocol_engine/commands/drop_tip.py +68 -15
- opentrons/protocol_engine/commands/drop_tip_in_place.py +52 -11
- opentrons/protocol_engine/commands/get_tip_presence.py +5 -3
- opentrons/protocol_engine/commands/heater_shaker/close_labware_latch.py +6 -6
- opentrons/protocol_engine/commands/heater_shaker/deactivate_heater.py +6 -6
- opentrons/protocol_engine/commands/heater_shaker/deactivate_shaker.py +6 -6
- opentrons/protocol_engine/commands/heater_shaker/open_labware_latch.py +8 -6
- opentrons/protocol_engine/commands/heater_shaker/set_and_wait_for_shake_speed.py +8 -4
- opentrons/protocol_engine/commands/heater_shaker/set_target_temperature.py +6 -4
- opentrons/protocol_engine/commands/heater_shaker/wait_for_temperature.py +6 -6
- opentrons/protocol_engine/commands/home.py +11 -5
- opentrons/protocol_engine/commands/liquid_probe.py +146 -88
- opentrons/protocol_engine/commands/load_labware.py +19 -5
- opentrons/protocol_engine/commands/load_liquid.py +18 -7
- opentrons/protocol_engine/commands/load_module.py +43 -6
- opentrons/protocol_engine/commands/load_pipette.py +18 -17
- opentrons/protocol_engine/commands/magnetic_module/disengage.py +6 -6
- opentrons/protocol_engine/commands/magnetic_module/engage.py +6 -4
- opentrons/protocol_engine/commands/move_labware.py +106 -19
- opentrons/protocol_engine/commands/move_relative.py +15 -3
- opentrons/protocol_engine/commands/move_to_addressable_area.py +29 -4
- opentrons/protocol_engine/commands/move_to_addressable_area_for_drop_tip.py +13 -4
- opentrons/protocol_engine/commands/move_to_coordinates.py +11 -5
- opentrons/protocol_engine/commands/move_to_well.py +37 -10
- opentrons/protocol_engine/commands/pick_up_tip.py +50 -29
- opentrons/protocol_engine/commands/pipetting_common.py +39 -15
- opentrons/protocol_engine/commands/prepare_to_aspirate.py +62 -15
- opentrons/protocol_engine/commands/reload_labware.py +13 -4
- opentrons/protocol_engine/commands/retract_axis.py +6 -3
- opentrons/protocol_engine/commands/save_position.py +2 -3
- opentrons/protocol_engine/commands/set_rail_lights.py +5 -3
- opentrons/protocol_engine/commands/set_status_bar.py +5 -3
- opentrons/protocol_engine/commands/temperature_module/deactivate.py +6 -4
- opentrons/protocol_engine/commands/temperature_module/set_target_temperature.py +3 -4
- opentrons/protocol_engine/commands/temperature_module/wait_for_temperature.py +6 -6
- opentrons/protocol_engine/commands/thermocycler/__init__.py +19 -0
- opentrons/protocol_engine/commands/thermocycler/close_lid.py +8 -8
- opentrons/protocol_engine/commands/thermocycler/deactivate_block.py +6 -4
- opentrons/protocol_engine/commands/thermocycler/deactivate_lid.py +6 -4
- opentrons/protocol_engine/commands/thermocycler/open_lid.py +8 -4
- opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py +165 -0
- opentrons/protocol_engine/commands/thermocycler/run_profile.py +6 -6
- opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py +3 -4
- opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py +3 -4
- opentrons/protocol_engine/commands/thermocycler/wait_for_block_temperature.py +6 -4
- opentrons/protocol_engine/commands/thermocycler/wait_for_lid_temperature.py +6 -4
- opentrons/protocol_engine/commands/touch_tip.py +19 -7
- opentrons/protocol_engine/commands/unsafe/__init__.py +30 -0
- opentrons/protocol_engine/commands/unsafe/unsafe_blow_out_in_place.py +6 -4
- opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +12 -4
- opentrons/protocol_engine/commands/unsafe/unsafe_engage_axes.py +5 -3
- opentrons/protocol_engine/commands/unsafe/unsafe_place_labware.py +194 -0
- opentrons/protocol_engine/commands/unsafe/unsafe_ungrip_labware.py +75 -0
- opentrons/protocol_engine/commands/unsafe/update_position_estimators.py +5 -3
- opentrons/protocol_engine/commands/verify_tip_presence.py +5 -5
- opentrons/protocol_engine/commands/wait_for_duration.py +5 -3
- opentrons/protocol_engine/commands/wait_for_resume.py +5 -3
- opentrons/protocol_engine/create_protocol_engine.py +41 -8
- opentrons/protocol_engine/engine_support.py +2 -1
- opentrons/protocol_engine/error_recovery_policy.py +14 -3
- opentrons/protocol_engine/errors/__init__.py +18 -0
- opentrons/protocol_engine/errors/exceptions.py +114 -2
- opentrons/protocol_engine/execution/__init__.py +2 -0
- opentrons/protocol_engine/execution/command_executor.py +22 -13
- opentrons/protocol_engine/execution/create_queue_worker.py +5 -1
- opentrons/protocol_engine/execution/door_watcher.py +1 -1
- opentrons/protocol_engine/execution/equipment.py +2 -1
- opentrons/protocol_engine/execution/error_recovery_hardware_state_synchronizer.py +101 -0
- opentrons/protocol_engine/execution/gantry_mover.py +4 -2
- opentrons/protocol_engine/execution/hardware_stopper.py +3 -3
- opentrons/protocol_engine/execution/heater_shaker_movement_flagger.py +1 -4
- opentrons/protocol_engine/execution/labware_movement.py +6 -3
- opentrons/protocol_engine/execution/movement.py +8 -3
- opentrons/protocol_engine/execution/pipetting.py +7 -4
- opentrons/protocol_engine/execution/queue_worker.py +6 -2
- opentrons/protocol_engine/execution/run_control.py +1 -1
- opentrons/protocol_engine/execution/thermocycler_movement_flagger.py +1 -1
- opentrons/protocol_engine/execution/thermocycler_plate_lifter.py +2 -1
- opentrons/protocol_engine/execution/tip_handler.py +77 -43
- opentrons/protocol_engine/notes/__init__.py +14 -2
- opentrons/protocol_engine/notes/notes.py +18 -1
- opentrons/protocol_engine/plugins.py +1 -1
- opentrons/protocol_engine/protocol_engine.py +54 -31
- opentrons/protocol_engine/resources/__init__.py +2 -0
- opentrons/protocol_engine/resources/deck_data_provider.py +58 -5
- opentrons/protocol_engine/resources/file_provider.py +157 -0
- opentrons/protocol_engine/resources/fixture_validation.py +5 -0
- opentrons/protocol_engine/resources/labware_validation.py +10 -0
- opentrons/protocol_engine/state/__init__.py +0 -70
- opentrons/protocol_engine/state/addressable_areas.py +1 -1
- opentrons/protocol_engine/state/command_history.py +21 -2
- opentrons/protocol_engine/state/commands.py +110 -31
- opentrons/protocol_engine/state/files.py +59 -0
- opentrons/protocol_engine/state/frustum_helpers.py +440 -0
- opentrons/protocol_engine/state/geometry.py +359 -15
- opentrons/protocol_engine/state/labware.py +166 -63
- opentrons/protocol_engine/state/liquids.py +1 -1
- opentrons/protocol_engine/state/module_substates/absorbance_reader_substate.py +19 -3
- opentrons/protocol_engine/state/modules.py +167 -85
- opentrons/protocol_engine/state/motion.py +16 -9
- opentrons/protocol_engine/state/pipettes.py +157 -317
- opentrons/protocol_engine/state/state.py +30 -1
- opentrons/protocol_engine/state/state_summary.py +3 -0
- opentrons/protocol_engine/state/tips.py +69 -114
- opentrons/protocol_engine/state/update_types.py +408 -0
- opentrons/protocol_engine/state/wells.py +236 -0
- opentrons/protocol_engine/types.py +90 -0
- opentrons/protocol_reader/file_format_validator.py +83 -15
- opentrons/protocol_runner/json_translator.py +21 -5
- opentrons/protocol_runner/legacy_command_mapper.py +27 -6
- opentrons/protocol_runner/legacy_context_plugin.py +27 -71
- opentrons/protocol_runner/protocol_runner.py +6 -3
- opentrons/protocol_runner/run_orchestrator.py +26 -6
- opentrons/protocols/advanced_control/mix.py +3 -5
- opentrons/protocols/advanced_control/transfers.py +125 -56
- opentrons/protocols/api_support/constants.py +1 -1
- opentrons/protocols/api_support/definitions.py +1 -1
- opentrons/protocols/api_support/labware_like.py +4 -4
- opentrons/protocols/api_support/tip_tracker.py +2 -2
- opentrons/protocols/api_support/types.py +15 -2
- opentrons/protocols/api_support/util.py +30 -42
- opentrons/protocols/duration/errors.py +1 -1
- opentrons/protocols/duration/estimator.py +50 -29
- opentrons/protocols/execution/dev_types.py +2 -2
- opentrons/protocols/execution/execute_json_v4.py +15 -10
- opentrons/protocols/execution/execute_python.py +8 -3
- opentrons/protocols/geometry/planning.py +12 -12
- opentrons/protocols/labware.py +17 -33
- opentrons/simulate.py +3 -3
- opentrons/types.py +30 -3
- opentrons/util/logging_config.py +34 -0
- {opentrons-8.1.0a0.dist-info → opentrons-8.2.0a0.dist-info}/METADATA +5 -4
- {opentrons-8.1.0a0.dist-info → opentrons-8.2.0a0.dist-info}/RECORD +227 -215
- opentrons/protocol_engine/commands/absorbance_reader/measure.py +0 -94
- opentrons/protocol_engine/commands/configuring_common.py +0 -26
- opentrons/protocol_runner/thread_async_queue.py +0 -174
- /opentrons/protocol_engine/state/{abstract_store.py → _abstract_store.py} +0 -0
- /opentrons/protocol_engine/state/{move_types.py → _move_types.py} +0 -0
- {opentrons-8.1.0a0.dist-info → opentrons-8.2.0a0.dist-info}/LICENSE +0 -0
- {opentrons-8.1.0a0.dist-info → opentrons-8.2.0a0.dist-info}/WHEEL +0 -0
- {opentrons-8.1.0a0.dist-info → opentrons-8.2.0a0.dist-info}/entry_points.txt +0 -0
- {opentrons-8.1.0a0.dist-info → opentrons-8.2.0a0.dist-info}/top_level.txt +0 -0
|
@@ -15,9 +15,13 @@ from typing import (
|
|
|
15
15
|
Union,
|
|
16
16
|
)
|
|
17
17
|
|
|
18
|
+
from opentrons.protocol_engine.state import update_types
|
|
18
19
|
from opentrons_shared_data.deck.types import DeckDefinitionV5
|
|
19
20
|
from opentrons_shared_data.gripper.constants import LABWARE_GRIP_FORCE
|
|
20
|
-
from opentrons_shared_data.labware.labware_definition import
|
|
21
|
+
from opentrons_shared_data.labware.labware_definition import (
|
|
22
|
+
LabwareRole,
|
|
23
|
+
InnerWellGeometry,
|
|
24
|
+
)
|
|
21
25
|
from opentrons_shared_data.pipette.types import LabwareUri
|
|
22
26
|
|
|
23
27
|
from opentrons.types import DeckSlotName, StagingSlotName, MountType
|
|
@@ -27,12 +31,6 @@ from opentrons.calibration_storage.helpers import uri_from_details
|
|
|
27
31
|
|
|
28
32
|
from .. import errors
|
|
29
33
|
from ..resources import DeckFixedLabware, labware_validation, fixture_validation
|
|
30
|
-
from ..commands import (
|
|
31
|
-
Command,
|
|
32
|
-
LoadLabwareResult,
|
|
33
|
-
MoveLabwareResult,
|
|
34
|
-
ReloadLabwareResult,
|
|
35
|
-
)
|
|
36
34
|
from ..types import (
|
|
37
35
|
DeckSlotLocation,
|
|
38
36
|
OnLabwareLocation,
|
|
@@ -53,12 +51,12 @@ from ..types import (
|
|
|
53
51
|
)
|
|
54
52
|
from ..actions import (
|
|
55
53
|
Action,
|
|
56
|
-
SucceedCommandAction,
|
|
57
54
|
AddLabwareOffsetAction,
|
|
58
55
|
AddLabwareDefinitionAction,
|
|
56
|
+
get_state_updates,
|
|
59
57
|
)
|
|
60
|
-
from .
|
|
61
|
-
from .
|
|
58
|
+
from ._abstract_store import HasState, HandlesActions
|
|
59
|
+
from ._move_types import EdgePathType
|
|
62
60
|
|
|
63
61
|
|
|
64
62
|
# URIs of labware whose definitions accidentally specify an engage height
|
|
@@ -69,8 +67,6 @@ _MAGDECK_HALF_MM_LABWARE = {
|
|
|
69
67
|
"opentrons/usascientific_96_wellplate_2.4ml_deep/1",
|
|
70
68
|
}
|
|
71
69
|
|
|
72
|
-
_OT3_INSTRUMENT_ATTACH_SLOT = DeckSlotName.SLOT_D1
|
|
73
|
-
|
|
74
70
|
_RIGHT_SIDE_SLOTS = {
|
|
75
71
|
# OT-2:
|
|
76
72
|
DeckSlotName.FIXED_TRASH,
|
|
@@ -153,10 +149,11 @@ class LabwareStore(HasState[LabwareState], HandlesActions):
|
|
|
153
149
|
|
|
154
150
|
def handle_action(self, action: Action) -> None:
|
|
155
151
|
"""Modify state in reaction to an action."""
|
|
156
|
-
|
|
157
|
-
self.
|
|
152
|
+
for state_update in get_state_updates(action):
|
|
153
|
+
self._add_loaded_labware(state_update)
|
|
154
|
+
self._set_labware_location(state_update)
|
|
158
155
|
|
|
159
|
-
|
|
156
|
+
if isinstance(action, AddLabwareOffsetAction):
|
|
160
157
|
labware_offset = LabwareOffset.construct(
|
|
161
158
|
id=action.labware_offset_id,
|
|
162
159
|
createdAt=action.created_at,
|
|
@@ -174,66 +171,71 @@ class LabwareStore(HasState[LabwareState], HandlesActions):
|
|
|
174
171
|
)
|
|
175
172
|
self._state.definitions_by_uri[uri] = action.definition
|
|
176
173
|
|
|
177
|
-
def
|
|
178
|
-
"""
|
|
179
|
-
|
|
174
|
+
def _add_labware_offset(self, labware_offset: LabwareOffset) -> None:
|
|
175
|
+
"""Add a new labware offset to state.
|
|
176
|
+
|
|
177
|
+
`labware_offset.id` must not match any existing labware offset ID.
|
|
178
|
+
`LoadLabwareCommand`s retain references to their corresponding labware offsets
|
|
179
|
+
and expect them to be immutable.
|
|
180
|
+
"""
|
|
181
|
+
assert labware_offset.id not in self._state.labware_offsets_by_id
|
|
182
|
+
|
|
183
|
+
self._state.labware_offsets_by_id[labware_offset.id] = labware_offset
|
|
184
|
+
|
|
185
|
+
def _add_loaded_labware(self, state_update: update_types.StateUpdate) -> None:
|
|
186
|
+
loaded_labware_update = state_update.loaded_labware
|
|
187
|
+
if loaded_labware_update != update_types.NO_CHANGE:
|
|
180
188
|
# If the labware load refers to an offset, that offset must actually exist.
|
|
181
|
-
if
|
|
182
|
-
assert
|
|
189
|
+
if loaded_labware_update.offset_id is not None:
|
|
190
|
+
assert (
|
|
191
|
+
loaded_labware_update.offset_id in self._state.labware_offsets_by_id
|
|
192
|
+
)
|
|
183
193
|
|
|
184
194
|
definition_uri = uri_from_details(
|
|
185
|
-
namespace=
|
|
186
|
-
load_name=
|
|
187
|
-
version=
|
|
195
|
+
namespace=loaded_labware_update.definition.namespace,
|
|
196
|
+
load_name=loaded_labware_update.definition.parameters.loadName,
|
|
197
|
+
version=loaded_labware_update.definition.version,
|
|
188
198
|
)
|
|
189
199
|
|
|
190
|
-
self._state.definitions_by_uri[
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
200
|
+
self._state.definitions_by_uri[
|
|
201
|
+
definition_uri
|
|
202
|
+
] = loaded_labware_update.definition
|
|
203
|
+
|
|
204
|
+
location = loaded_labware_update.new_location
|
|
205
|
+
|
|
206
|
+
display_name = loaded_labware_update.display_name
|
|
195
207
|
|
|
196
208
|
self._state.labware_by_id[
|
|
197
|
-
|
|
209
|
+
loaded_labware_update.labware_id
|
|
198
210
|
] = LoadedLabware.construct(
|
|
199
|
-
id=
|
|
211
|
+
id=loaded_labware_update.labware_id,
|
|
200
212
|
location=location,
|
|
201
|
-
loadName=
|
|
213
|
+
loadName=loaded_labware_update.definition.parameters.loadName,
|
|
202
214
|
definitionUri=definition_uri,
|
|
203
|
-
offsetId=
|
|
204
|
-
displayName=
|
|
215
|
+
offsetId=loaded_labware_update.offset_id,
|
|
216
|
+
displayName=display_name,
|
|
205
217
|
)
|
|
206
218
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
elif isinstance(command.result, MoveLabwareResult):
|
|
213
|
-
labware_id = command.params.labwareId
|
|
214
|
-
new_location = command.params.newLocation
|
|
215
|
-
new_offset_id = command.result.offsetId
|
|
219
|
+
def _set_labware_location(self, state_update: update_types.StateUpdate) -> None:
|
|
220
|
+
labware_location_update = state_update.labware_location
|
|
221
|
+
if labware_location_update != update_types.NO_CHANGE:
|
|
222
|
+
labware_id = labware_location_update.labware_id
|
|
223
|
+
new_offset_id = labware_location_update.offset_id
|
|
216
224
|
|
|
217
225
|
self._state.labware_by_id[labware_id].offsetId = new_offset_id
|
|
218
|
-
if isinstance(
|
|
219
|
-
new_location, AddressableAreaLocation
|
|
220
|
-
) and fixture_validation.is_gripper_waste_chute(
|
|
221
|
-
new_location.addressableAreaName
|
|
222
|
-
):
|
|
223
|
-
# If a labware has been moved into a waste chute it's been chuted away and is now technically off deck
|
|
224
|
-
new_location = OFF_DECK_LOCATION
|
|
225
|
-
self._state.labware_by_id[labware_id].location = new_location
|
|
226
226
|
|
|
227
|
-
|
|
228
|
-
|
|
227
|
+
if labware_location_update.new_location:
|
|
228
|
+
new_location = labware_location_update.new_location
|
|
229
229
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
230
|
+
if isinstance(
|
|
231
|
+
new_location, AddressableAreaLocation
|
|
232
|
+
) and fixture_validation.is_gripper_waste_chute(
|
|
233
|
+
new_location.addressableAreaName
|
|
234
|
+
):
|
|
235
|
+
# If a labware has been moved into a waste chute it's been chuted away and is now technically off deck
|
|
236
|
+
new_location = OFF_DECK_LOCATION
|
|
235
237
|
|
|
236
|
-
|
|
238
|
+
self._state.labware_by_id[labware_id].location = new_location
|
|
237
239
|
|
|
238
240
|
|
|
239
241
|
class LabwareView(HasState[LabwareState]):
|
|
@@ -313,6 +315,22 @@ class LabwareView(HasState[LabwareState]):
|
|
|
313
315
|
|
|
314
316
|
return None
|
|
315
317
|
|
|
318
|
+
def get_by_addressable_area(
|
|
319
|
+
self,
|
|
320
|
+
addressable_area: str,
|
|
321
|
+
) -> Optional[LoadedLabware]:
|
|
322
|
+
"""Get the labware located in a given addressable area, if any."""
|
|
323
|
+
loaded_labware = list(self._state.labware_by_id.values())
|
|
324
|
+
|
|
325
|
+
for labware in loaded_labware:
|
|
326
|
+
if (
|
|
327
|
+
isinstance(labware.location, AddressableAreaLocation)
|
|
328
|
+
and labware.location.addressableAreaName == addressable_area
|
|
329
|
+
):
|
|
330
|
+
return labware
|
|
331
|
+
|
|
332
|
+
return None
|
|
333
|
+
|
|
316
334
|
def get_definition(self, labware_id: str) -> LabwareDefinition:
|
|
317
335
|
"""Get labware definition by the labware's unique identifier."""
|
|
318
336
|
return self.get_definition_by_uri(
|
|
@@ -378,6 +396,16 @@ class LabwareView(HasState[LabwareState]):
|
|
|
378
396
|
return self.get_parent_location(parent.labwareId)
|
|
379
397
|
return parent
|
|
380
398
|
|
|
399
|
+
def get_labware_stack(
|
|
400
|
+
self, labware_stack: List[LoadedLabware]
|
|
401
|
+
) -> List[LoadedLabware]:
|
|
402
|
+
"""Get the a stack of labware starting from a given labware or existing stack."""
|
|
403
|
+
parent = self.get_location(labware_stack[-1].id)
|
|
404
|
+
if isinstance(parent, OnLabwareLocation):
|
|
405
|
+
labware_stack.append(self.get(parent.labwareId))
|
|
406
|
+
return self.get_labware_stack(labware_stack)
|
|
407
|
+
return labware_stack
|
|
408
|
+
|
|
381
409
|
def get_all(self) -> List[LoadedLabware]:
|
|
382
410
|
"""Get a list of all labware entries in state."""
|
|
383
411
|
return list(self._state.labware_by_id.values())
|
|
@@ -402,6 +430,27 @@ class LabwareView(HasState[LabwareState]):
|
|
|
402
430
|
and len(self.get_definition(labware_id).wells) < 96
|
|
403
431
|
)
|
|
404
432
|
|
|
433
|
+
def get_labware_stacking_maximum(self, labware: LabwareDefinition) -> int:
|
|
434
|
+
"""Returns the maximum number of labware allowed in a stack for a given labware definition.
|
|
435
|
+
|
|
436
|
+
If not defined within a labware, defaults to one.
|
|
437
|
+
"""
|
|
438
|
+
stacking_quirks = {
|
|
439
|
+
"stackingMaxFive": 5,
|
|
440
|
+
"stackingMaxFour": 4,
|
|
441
|
+
"stackingMaxThree": 3,
|
|
442
|
+
"stackingMaxTwo": 2,
|
|
443
|
+
"stackingMaxOne": 1,
|
|
444
|
+
"stackingMaxZero": 0,
|
|
445
|
+
}
|
|
446
|
+
for quirk in stacking_quirks.keys():
|
|
447
|
+
if (
|
|
448
|
+
labware.parameters.quirks is not None
|
|
449
|
+
and quirk in labware.parameters.quirks
|
|
450
|
+
):
|
|
451
|
+
return stacking_quirks[quirk]
|
|
452
|
+
return 1
|
|
453
|
+
|
|
405
454
|
def get_should_center_pipette_on_target_well(self, labware_id: str) -> bool:
|
|
406
455
|
"""True if a pipette moving to a well of this labware should center its body on the target.
|
|
407
456
|
|
|
@@ -435,6 +484,29 @@ class LabwareView(HasState[LabwareState]):
|
|
|
435
484
|
f"{well_name} does not exist in {labware_id}."
|
|
436
485
|
) from e
|
|
437
486
|
|
|
487
|
+
def get_well_geometry(
|
|
488
|
+
self, labware_id: str, well_name: Optional[str] = None
|
|
489
|
+
) -> InnerWellGeometry:
|
|
490
|
+
"""Get a well's inner geometry by labware and well name."""
|
|
491
|
+
labware_def = self.get_definition(labware_id)
|
|
492
|
+
if labware_def.innerLabwareGeometry is None:
|
|
493
|
+
raise errors.IncompleteLabwareDefinitionError(
|
|
494
|
+
message=f"No innerLabwareGeometry found in labware definition for labware_id: {labware_id}."
|
|
495
|
+
)
|
|
496
|
+
well_def = self.get_well_definition(labware_id, well_name)
|
|
497
|
+
well_id = well_def.geometryDefinitionId
|
|
498
|
+
if well_id is None:
|
|
499
|
+
raise errors.IncompleteWellDefinitionError(
|
|
500
|
+
message=f"No geometryDefinitionId found in well definition for well: {well_name} in labware_id: {labware_id}"
|
|
501
|
+
)
|
|
502
|
+
else:
|
|
503
|
+
well_geometry = labware_def.innerLabwareGeometry.get(well_id)
|
|
504
|
+
if well_geometry is None:
|
|
505
|
+
raise errors.IncompleteLabwareDefinitionError(
|
|
506
|
+
message=f"No innerLabwareGeometry found in labware definition for well_id: {well_id} in labware_id: {labware_id}"
|
|
507
|
+
)
|
|
508
|
+
return well_geometry
|
|
509
|
+
|
|
438
510
|
def get_well_size(
|
|
439
511
|
self, labware_id: str, well_name: str
|
|
440
512
|
) -> Tuple[float, float, float]:
|
|
@@ -569,9 +641,14 @@ class LabwareView(HasState[LabwareState]):
|
|
|
569
641
|
) -> OverlapOffset:
|
|
570
642
|
"""Get the labware's overlap with requested labware's load name."""
|
|
571
643
|
definition = self.get_definition(labware_id)
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
644
|
+
if below_labware_name in definition.stackingOffsetWithLabware.keys():
|
|
645
|
+
stacking_overlap = definition.stackingOffsetWithLabware.get(
|
|
646
|
+
below_labware_name, OverlapOffset(x=0, y=0, z=0)
|
|
647
|
+
)
|
|
648
|
+
else:
|
|
649
|
+
stacking_overlap = definition.stackingOffsetWithLabware.get(
|
|
650
|
+
"default", OverlapOffset(x=0, y=0, z=0)
|
|
651
|
+
)
|
|
575
652
|
return OverlapOffset(
|
|
576
653
|
x=stacking_overlap.x, y=stacking_overlap.y, z=stacking_overlap.z
|
|
577
654
|
)
|
|
@@ -704,6 +781,12 @@ class LabwareView(HasState[LabwareState]):
|
|
|
704
781
|
"""Check if labware is fixed trash."""
|
|
705
782
|
return self.get_has_quirk(labware_id, "fixedTrash")
|
|
706
783
|
|
|
784
|
+
def is_absorbance_reader_lid(self, labware_id: str) -> bool:
|
|
785
|
+
"""Check if labware is an absorbance reader lid."""
|
|
786
|
+
return labware_validation.is_absorbance_reader_lid(
|
|
787
|
+
self.get(labware_id).loadName
|
|
788
|
+
)
|
|
789
|
+
|
|
707
790
|
def raise_if_labware_inaccessible_by_pipette(self, labware_id: str) -> None:
|
|
708
791
|
"""Raise an error if the specified location cannot be reached via a pipette."""
|
|
709
792
|
labware = self.get(labware_id)
|
|
@@ -734,7 +817,7 @@ class LabwareView(HasState[LabwareState]):
|
|
|
734
817
|
f"Labware {labware.loadName} is already present at {location}."
|
|
735
818
|
)
|
|
736
819
|
|
|
737
|
-
def raise_if_labware_cannot_be_stacked(
|
|
820
|
+
def raise_if_labware_cannot_be_stacked( # noqa: C901
|
|
738
821
|
self, top_labware_definition: LabwareDefinition, bottom_labware_id: str
|
|
739
822
|
) -> None:
|
|
740
823
|
"""Raise if the specified labware definition cannot be placed on top of the bottom labware."""
|
|
@@ -753,17 +836,37 @@ class LabwareView(HasState[LabwareState]):
|
|
|
753
836
|
)
|
|
754
837
|
elif isinstance(below_labware.location, ModuleLocation):
|
|
755
838
|
below_definition = self.get_definition(labware_id=below_labware.id)
|
|
756
|
-
if not labware_validation.validate_definition_is_adapter(
|
|
839
|
+
if not labware_validation.validate_definition_is_adapter(
|
|
840
|
+
below_definition
|
|
841
|
+
) and not labware_validation.validate_definition_is_lid(
|
|
842
|
+
top_labware_definition
|
|
843
|
+
):
|
|
757
844
|
raise errors.LabwareCannotBeStackedError(
|
|
758
845
|
f"Labware {top_labware_definition.parameters.loadName} cannot be loaded"
|
|
759
846
|
f" onto a labware on top of a module"
|
|
760
847
|
)
|
|
761
848
|
elif isinstance(below_labware.location, OnLabwareLocation):
|
|
849
|
+
labware_stack = self.get_labware_stack([below_labware])
|
|
850
|
+
stack_without_adapters = []
|
|
851
|
+
for lw in labware_stack:
|
|
852
|
+
if not labware_validation.validate_definition_is_adapter(
|
|
853
|
+
self.get_definition(lw.id)
|
|
854
|
+
):
|
|
855
|
+
stack_without_adapters.append(lw)
|
|
856
|
+
if len(stack_without_adapters) >= self.get_labware_stacking_maximum(
|
|
857
|
+
top_labware_definition
|
|
858
|
+
):
|
|
859
|
+
raise errors.LabwareCannotBeStackedError(
|
|
860
|
+
f"Labware {top_labware_definition.parameters.loadName} cannot be loaded to stack of more than {self.get_labware_stacking_maximum(top_labware_definition)} labware."
|
|
861
|
+
)
|
|
862
|
+
|
|
762
863
|
further_below_definition = self.get_definition(
|
|
763
864
|
labware_id=below_labware.location.labwareId
|
|
764
865
|
)
|
|
765
866
|
if labware_validation.validate_definition_is_adapter(
|
|
766
867
|
further_below_definition
|
|
868
|
+
) and not labware_validation.validate_definition_is_lid(
|
|
869
|
+
top_labware_definition
|
|
767
870
|
):
|
|
768
871
|
raise errors.LabwareCannotBeStackedError(
|
|
769
872
|
f"Labware {top_labware_definition.parameters.loadName} cannot be loaded"
|
|
@@ -3,7 +3,7 @@ from dataclasses import dataclass
|
|
|
3
3
|
from typing import Dict, List
|
|
4
4
|
from opentrons.protocol_engine.types import Liquid
|
|
5
5
|
|
|
6
|
-
from .
|
|
6
|
+
from ._abstract_store import HasState, HandlesActions
|
|
7
7
|
from ..actions import Action, AddLiquidAction
|
|
8
8
|
from ..errors import LiquidDoesNotExistError
|
|
9
9
|
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
"""Heater-Shaker Module sub-state."""
|
|
2
2
|
from dataclasses import dataclass
|
|
3
|
-
from typing import NewType, Optional,
|
|
3
|
+
from typing import List, NewType, Optional, Dict
|
|
4
4
|
|
|
5
|
+
from opentrons.protocol_engine.errors import CannotPerformModuleAction
|
|
5
6
|
|
|
6
7
|
AbsorbanceReaderId = NewType("AbsorbanceReaderId", str)
|
|
8
|
+
AbsorbanceReaderLidId = NewType("AbsorbanceReaderLidId", str)
|
|
9
|
+
AbsorbanceReaderMeasureMode = NewType("AbsorbanceReaderMeasureMode", str)
|
|
7
10
|
|
|
8
11
|
|
|
9
12
|
@dataclass(frozen=True)
|
|
@@ -13,5 +16,18 @@ class AbsorbanceReaderSubState:
|
|
|
13
16
|
module_id: AbsorbanceReaderId
|
|
14
17
|
configured: bool
|
|
15
18
|
measured: bool
|
|
16
|
-
|
|
17
|
-
|
|
19
|
+
is_lid_on: bool
|
|
20
|
+
data: Optional[Dict[int, Dict[str, float]]]
|
|
21
|
+
configured_wavelengths: Optional[List[int]]
|
|
22
|
+
measure_mode: Optional[AbsorbanceReaderMeasureMode]
|
|
23
|
+
reference_wavelength: Optional[int]
|
|
24
|
+
lid_id: Optional[str]
|
|
25
|
+
|
|
26
|
+
def raise_if_lid_status_not_expected(self, lid_on_expected: bool) -> None:
|
|
27
|
+
"""Raise if the lid status is not correct."""
|
|
28
|
+
match = self.is_lid_on is lid_on_expected
|
|
29
|
+
if not match:
|
|
30
|
+
raise CannotPerformModuleAction(
|
|
31
|
+
"Cannot perform lid action because the lid is already "
|
|
32
|
+
f"{'closed' if self.is_lid_on else 'open'}"
|
|
33
|
+
)
|