opentrons 8.1.0__py2.py3-none-any.whl → 8.2.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.
- 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 +208 -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/backends/ot3utils.py +1 -0
- 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 +232 -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 +131 -94
- 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/hardware_control/protocols/position_estimator.py +3 -1
- opentrons/hardware_control/types.py +2 -0
- opentrons/legacy_commands/helpers.py +8 -2
- 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 +20 -4
- opentrons/protocol_api/core/engine/module_core.py +166 -17
- opentrons/protocol_api/core/engine/pipette_movement_conflict.py +362 -0
- opentrons/protocol_api/core/engine/protocol.py +30 -2
- opentrons/protocol_api/core/instrument.py +2 -0
- opentrons/protocol_api/core/labware.py +4 -0
- opentrons/protocol_api/core/legacy/legacy_instrument_core.py +2 -0
- opentrons/protocol_api/core/legacy/legacy_labware_core.py +5 -0
- opentrons/protocol_api/core/legacy/legacy_protocol_core.py +6 -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 +6 -2
- opentrons/protocol_api/instrument_context.py +52 -20
- opentrons/protocol_api/labware.py +13 -1
- opentrons/protocol_api/module_contexts.py +115 -17
- opentrons/protocol_api/protocol_context.py +49 -5
- opentrons/protocol_api/validation.py +5 -3
- opentrons/protocol_engine/__init__.py +10 -9
- opentrons/protocol_engine/actions/__init__.py +3 -0
- opentrons/protocol_engine/actions/actions.py +30 -25
- opentrons/protocol_engine/actions/get_state_update.py +38 -0
- opentrons/protocol_engine/clients/sync_client.py +1 -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 +148 -0
- opentrons/protocol_engine/commands/absorbance_reader/initialize.py +65 -9
- opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +148 -0
- opentrons/protocol_engine/commands/absorbance_reader/read.py +200 -0
- opentrons/protocol_engine/commands/aspirate.py +29 -16
- opentrons/protocol_engine/commands/aspirate_in_place.py +33 -16
- 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 +31 -18
- 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 +70 -16
- opentrons/protocol_engine/commands/drop_tip_in_place.py +59 -13
- 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 +28 -5
- opentrons/protocol_engine/commands/load_liquid.py +18 -7
- opentrons/protocol_engine/commands/load_module.py +4 -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 +155 -23
- 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 +51 -30
- opentrons/protocol_engine/commands/pipetting_common.py +47 -16
- 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 +208 -0
- opentrons/protocol_engine/commands/unsafe/unsafe_ungrip_labware.py +77 -0
- opentrons/protocol_engine/commands/unsafe/update_position_estimators.py +10 -4
- 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 +60 -10
- opentrons/protocol_engine/engine_support.py +2 -1
- opentrons/protocol_engine/error_recovery_policy.py +14 -3
- opentrons/protocol_engine/errors/__init__.py +20 -0
- opentrons/protocol_engine/errors/error_occurrence.py +8 -3
- opentrons/protocol_engine/errors/exceptions.py +127 -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 +73 -22
- opentrons/protocol_engine/execution/movement.py +17 -7
- 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 +47 -31
- opentrons/protocol_engine/resources/__init__.py +2 -0
- opentrons/protocol_engine/resources/deck_data_provider.py +19 -5
- opentrons/protocol_engine/resources/file_provider.py +161 -0
- opentrons/protocol_engine/resources/fixture_validation.py +11 -1
- 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 +445 -59
- opentrons/protocol_engine/state/labware.py +264 -84
- opentrons/protocol_engine/state/liquids.py +1 -1
- opentrons/protocol_engine/state/module_substates/absorbance_reader_substate.py +21 -3
- opentrons/protocol_engine/state/modules.py +145 -90
- opentrons/protocol_engine/state/motion.py +33 -14
- 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 +424 -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 +41 -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/protocols/parameters/csv_parameter_interface.py +3 -1
- opentrons/simulate.py +3 -3
- opentrons/types.py +30 -3
- opentrons/util/logging_config.py +34 -0
- {opentrons-8.1.0.dist-info → opentrons-8.2.0.dist-info}/METADATA +5 -4
- {opentrons-8.1.0.dist-info → opentrons-8.2.0.dist-info}/RECORD +235 -223
- 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.0.dist-info → opentrons-8.2.0.dist-info}/LICENSE +0 -0
- {opentrons-8.1.0.dist-info → opentrons-8.2.0.dist-info}/WHEEL +0 -0
- {opentrons-8.1.0.dist-info → opentrons-8.2.0.dist-info}/entry_points.txt +0 -0
- {opentrons-8.1.0.dist-info → opentrons-8.2.0.dist-info}/top_level.txt +0 -0
|
@@ -24,11 +24,17 @@ from opentrons.hardware_control.modules.types import LiveData
|
|
|
24
24
|
from opentrons.motion_planning.adjacent_slots_getters import (
|
|
25
25
|
get_east_slot,
|
|
26
26
|
get_west_slot,
|
|
27
|
+
get_adjacent_staging_slot,
|
|
27
28
|
)
|
|
29
|
+
from opentrons.protocol_engine.actions.get_state_update import get_state_updates
|
|
28
30
|
from opentrons.protocol_engine.commands.calibration.calibrate_module import (
|
|
29
31
|
CalibrateModuleResult,
|
|
30
32
|
)
|
|
31
|
-
from opentrons.
|
|
33
|
+
from opentrons.protocol_engine.state import update_types
|
|
34
|
+
from opentrons.protocol_engine.state.module_substates.absorbance_reader_substate import (
|
|
35
|
+
AbsorbanceReaderMeasureMode,
|
|
36
|
+
)
|
|
37
|
+
from opentrons.types import DeckSlotName, MountType, StagingSlotName
|
|
32
38
|
from ..errors import ModuleNotConnectedError
|
|
33
39
|
|
|
34
40
|
from ..types import (
|
|
@@ -45,7 +51,10 @@ from ..types import (
|
|
|
45
51
|
HeaterShakerMovementRestrictors,
|
|
46
52
|
DeckType,
|
|
47
53
|
LabwareMovementOffsetData,
|
|
54
|
+
AddressableAreaLocation,
|
|
48
55
|
)
|
|
56
|
+
|
|
57
|
+
from ..resources import DeckFixedLabware
|
|
49
58
|
from .addressable_areas import AddressableAreaView
|
|
50
59
|
from .. import errors
|
|
51
60
|
from ..commands import (
|
|
@@ -56,8 +65,12 @@ from ..commands import (
|
|
|
56
65
|
thermocycler,
|
|
57
66
|
absorbance_reader,
|
|
58
67
|
)
|
|
59
|
-
from ..actions import
|
|
60
|
-
|
|
68
|
+
from ..actions import (
|
|
69
|
+
Action,
|
|
70
|
+
SucceedCommandAction,
|
|
71
|
+
AddModuleAction,
|
|
72
|
+
)
|
|
73
|
+
from ._abstract_store import HasState, HandlesActions
|
|
61
74
|
from .module_substates import (
|
|
62
75
|
MagneticModuleSubState,
|
|
63
76
|
HeaterShakerModuleSubState,
|
|
@@ -174,6 +187,15 @@ class ModuleState:
|
|
|
174
187
|
deck_type: DeckType
|
|
175
188
|
"""Type of deck that the modules are on."""
|
|
176
189
|
|
|
190
|
+
deck_fixed_labware: Sequence[DeckFixedLabware]
|
|
191
|
+
"""Fixed labware from the deck which may be assigned to a module.
|
|
192
|
+
|
|
193
|
+
The Opentrons Plate Reader module makes use of an electronic Lid labware which moves
|
|
194
|
+
between the Reader and Dock positions, and is pre-loaded into the engine as to persist
|
|
195
|
+
even when not in use. For this reason, we inject it here when an appropriate match
|
|
196
|
+
is identified.
|
|
197
|
+
"""
|
|
198
|
+
|
|
177
199
|
|
|
178
200
|
class ModuleStore(HasState[ModuleState], HandlesActions):
|
|
179
201
|
"""Module state container."""
|
|
@@ -183,6 +205,7 @@ class ModuleStore(HasState[ModuleState], HandlesActions):
|
|
|
183
205
|
def __init__(
|
|
184
206
|
self,
|
|
185
207
|
config: Config,
|
|
208
|
+
deck_fixed_labware: Sequence[DeckFixedLabware],
|
|
186
209
|
module_calibration_offsets: Optional[Dict[str, ModuleOffsetData]] = None,
|
|
187
210
|
) -> None:
|
|
188
211
|
"""Initialize a ModuleStore and its state."""
|
|
@@ -194,6 +217,7 @@ class ModuleStore(HasState[ModuleState], HandlesActions):
|
|
|
194
217
|
substate_by_module_id={},
|
|
195
218
|
module_offset_by_serial=module_calibration_offsets or {},
|
|
196
219
|
deck_type=config.deck_type,
|
|
220
|
+
deck_fixed_labware=deck_fixed_labware,
|
|
197
221
|
)
|
|
198
222
|
self._robot_type = config.robot_type
|
|
199
223
|
|
|
@@ -212,7 +236,13 @@ class ModuleStore(HasState[ModuleState], HandlesActions):
|
|
|
212
236
|
module_live_data=action.module_live_data,
|
|
213
237
|
)
|
|
214
238
|
|
|
239
|
+
for state_update in get_state_updates(action):
|
|
240
|
+
self._handle_state_update(state_update)
|
|
241
|
+
|
|
215
242
|
def _handle_command(self, command: Command) -> None:
|
|
243
|
+
# todo(mm, 2024-11-04): Delete this function. Port these isinstance()
|
|
244
|
+
# checks to the update_types.StateUpdate mechanism.
|
|
245
|
+
|
|
216
246
|
if isinstance(command.result, LoadModuleResult):
|
|
217
247
|
slot_name = command.params.location.slotName
|
|
218
248
|
self._add_module_substate(
|
|
@@ -270,11 +300,38 @@ class ModuleStore(HasState[ModuleState], HandlesActions):
|
|
|
270
300
|
command.result,
|
|
271
301
|
(
|
|
272
302
|
absorbance_reader.InitializeResult,
|
|
273
|
-
absorbance_reader.
|
|
303
|
+
absorbance_reader.ReadAbsorbanceResult,
|
|
274
304
|
),
|
|
275
305
|
):
|
|
276
306
|
self._handle_absorbance_reader_commands(command)
|
|
277
307
|
|
|
308
|
+
def _handle_state_update(self, state_update: update_types.StateUpdate) -> None:
|
|
309
|
+
if state_update.absorbance_reader_lid != update_types.NO_CHANGE:
|
|
310
|
+
module_id = state_update.absorbance_reader_lid.module_id
|
|
311
|
+
is_lid_on = state_update.absorbance_reader_lid.is_lid_on
|
|
312
|
+
|
|
313
|
+
# Get current values:
|
|
314
|
+
absorbance_reader_substate = self._state.substate_by_module_id[module_id]
|
|
315
|
+
assert isinstance(
|
|
316
|
+
absorbance_reader_substate, AbsorbanceReaderSubState
|
|
317
|
+
), f"{module_id} is not an absorbance plate reader."
|
|
318
|
+
configured = absorbance_reader_substate.configured
|
|
319
|
+
measure_mode = absorbance_reader_substate.measure_mode
|
|
320
|
+
configured_wavelengths = absorbance_reader_substate.configured_wavelengths
|
|
321
|
+
reference_wavelength = absorbance_reader_substate.reference_wavelength
|
|
322
|
+
data = absorbance_reader_substate.data
|
|
323
|
+
|
|
324
|
+
self._state.substate_by_module_id[module_id] = AbsorbanceReaderSubState(
|
|
325
|
+
module_id=AbsorbanceReaderId(module_id),
|
|
326
|
+
configured=configured,
|
|
327
|
+
measured=True,
|
|
328
|
+
is_lid_on=is_lid_on,
|
|
329
|
+
measure_mode=measure_mode,
|
|
330
|
+
configured_wavelengths=configured_wavelengths,
|
|
331
|
+
reference_wavelength=reference_wavelength,
|
|
332
|
+
data=data,
|
|
333
|
+
)
|
|
334
|
+
|
|
278
335
|
def _add_module_substate(
|
|
279
336
|
self,
|
|
280
337
|
module_id: str,
|
|
@@ -338,8 +395,11 @@ class ModuleStore(HasState[ModuleState], HandlesActions):
|
|
|
338
395
|
module_id=AbsorbanceReaderId(module_id),
|
|
339
396
|
configured=False,
|
|
340
397
|
measured=False,
|
|
398
|
+
is_lid_on=True,
|
|
341
399
|
data=None,
|
|
342
|
-
|
|
400
|
+
measure_mode=None,
|
|
401
|
+
configured_wavelengths=None,
|
|
402
|
+
reference_wavelength=None,
|
|
343
403
|
)
|
|
344
404
|
|
|
345
405
|
def _update_additional_slots_occupied_by_thermocycler(
|
|
@@ -513,7 +573,6 @@ class ModuleStore(HasState[ModuleState], HandlesActions):
|
|
|
513
573
|
target_block_temperature=block_temperature,
|
|
514
574
|
target_lid_temperature=None,
|
|
515
575
|
)
|
|
516
|
-
# TODO (spp, 2022-08-01): set is_lid_open to False upon lid commands' failure
|
|
517
576
|
elif isinstance(command.result, thermocycler.OpenLidResult):
|
|
518
577
|
self._state.substate_by_module_id[module_id] = ThermocyclerModuleSubState(
|
|
519
578
|
module_id=ThermocyclerModuleId(module_id),
|
|
@@ -533,7 +592,7 @@ class ModuleStore(HasState[ModuleState], HandlesActions):
|
|
|
533
592
|
self,
|
|
534
593
|
command: Union[
|
|
535
594
|
absorbance_reader.Initialize,
|
|
536
|
-
absorbance_reader.
|
|
595
|
+
absorbance_reader.ReadAbsorbance,
|
|
537
596
|
],
|
|
538
597
|
) -> None:
|
|
539
598
|
module_id = command.params.moduleId
|
|
@@ -544,22 +603,31 @@ class ModuleStore(HasState[ModuleState], HandlesActions):
|
|
|
544
603
|
|
|
545
604
|
# Get current values
|
|
546
605
|
configured = absorbance_reader_substate.configured
|
|
547
|
-
|
|
606
|
+
measure_mode = absorbance_reader_substate.measure_mode
|
|
607
|
+
configured_wavelengths = absorbance_reader_substate.configured_wavelengths
|
|
608
|
+
reference_wavelength = absorbance_reader_substate.reference_wavelength
|
|
609
|
+
is_lid_on = absorbance_reader_substate.is_lid_on
|
|
548
610
|
|
|
549
611
|
if isinstance(command.result, absorbance_reader.InitializeResult):
|
|
550
612
|
self._state.substate_by_module_id[module_id] = AbsorbanceReaderSubState(
|
|
551
613
|
module_id=AbsorbanceReaderId(module_id),
|
|
552
614
|
configured=True,
|
|
553
615
|
measured=False,
|
|
616
|
+
is_lid_on=is_lid_on,
|
|
617
|
+
measure_mode=AbsorbanceReaderMeasureMode(command.params.measureMode),
|
|
618
|
+
configured_wavelengths=command.params.sampleWavelengths,
|
|
619
|
+
reference_wavelength=command.params.referenceWavelength,
|
|
554
620
|
data=None,
|
|
555
|
-
configured_wavelength=command.params.sampleWavelength,
|
|
556
621
|
)
|
|
557
|
-
elif isinstance(command.result, absorbance_reader.
|
|
622
|
+
elif isinstance(command.result, absorbance_reader.ReadAbsorbanceResult):
|
|
558
623
|
self._state.substate_by_module_id[module_id] = AbsorbanceReaderSubState(
|
|
559
624
|
module_id=AbsorbanceReaderId(module_id),
|
|
560
625
|
configured=configured,
|
|
561
|
-
configured_wavelength=configured_wavelength,
|
|
562
626
|
measured=True,
|
|
627
|
+
is_lid_on=is_lid_on,
|
|
628
|
+
measure_mode=measure_mode,
|
|
629
|
+
configured_wavelengths=configured_wavelengths,
|
|
630
|
+
reference_wavelength=reference_wavelength,
|
|
563
631
|
data=command.result.data,
|
|
564
632
|
)
|
|
565
633
|
|
|
@@ -711,7 +779,7 @@ class ModuleView(HasState[ModuleState]):
|
|
|
711
779
|
return self._get_module_substate(
|
|
712
780
|
module_id=module_id,
|
|
713
781
|
expected_type=AbsorbanceReaderSubState,
|
|
714
|
-
expected_name="
|
|
782
|
+
expected_name="Absorbance Reader",
|
|
715
783
|
)
|
|
716
784
|
|
|
717
785
|
def get_location(self, module_id: str) -> DeckSlotLocation:
|
|
@@ -776,12 +844,21 @@ class ModuleView(HasState[ModuleState]):
|
|
|
776
844
|
"""Get the specified module's dimensions."""
|
|
777
845
|
return self.get_definition(module_id).dimensions
|
|
778
846
|
|
|
779
|
-
def
|
|
847
|
+
def get_nominal_offset_to_child(
|
|
780
848
|
self,
|
|
781
849
|
module_id: str,
|
|
850
|
+
# todo(mm, 2024-11-07): A method of one view taking a sibling view as an argument
|
|
851
|
+
# is unusual, and may be bug-prone if the order in which the views are updated
|
|
852
|
+
# matters. If we need to compute something that depends on module info and
|
|
853
|
+
# addressable area info, can we do that computation in GeometryView instead of
|
|
854
|
+
# here?
|
|
782
855
|
addressable_areas: AddressableAreaView,
|
|
783
856
|
) -> LabwareOffsetVector:
|
|
784
|
-
"""Get the module's
|
|
857
|
+
"""Get the nominal offset from a module's location to its child labware's location.
|
|
858
|
+
|
|
859
|
+
Includes the slot-specific transform. Does not include the child's
|
|
860
|
+
Labware Position Check offset.
|
|
861
|
+
"""
|
|
785
862
|
if (
|
|
786
863
|
self.state.deck_type == DeckType.OT2_STANDARD
|
|
787
864
|
or self.state.deck_type == DeckType.OT2_SHORT_TRASH
|
|
@@ -889,7 +966,7 @@ class ModuleView(HasState[ModuleState]):
|
|
|
889
966
|
default_lw_offset_point = self.get_definition(module_id).labwareOffset.z
|
|
890
967
|
z_difference = module_height - default_lw_offset_point
|
|
891
968
|
|
|
892
|
-
nominal_transformed_lw_offset_z = self.
|
|
969
|
+
nominal_transformed_lw_offset_z = self.get_nominal_offset_to_child(
|
|
893
970
|
module_id=module_id, addressable_areas=addressable_areas
|
|
894
971
|
).z
|
|
895
972
|
calibration_offset = self.get_module_calibration_offset(module_id)
|
|
@@ -1017,8 +1094,8 @@ class ModuleView(HasState[ModuleState]):
|
|
|
1017
1094
|
|
|
1018
1095
|
def should_dodge_thermocycler(
|
|
1019
1096
|
self,
|
|
1020
|
-
from_slot: DeckSlotName,
|
|
1021
|
-
to_slot: DeckSlotName,
|
|
1097
|
+
from_slot: Union[DeckSlotName, StagingSlotName],
|
|
1098
|
+
to_slot: Union[DeckSlotName, StagingSlotName],
|
|
1022
1099
|
) -> bool:
|
|
1023
1100
|
"""Decide if the requested path would cross the thermocycler, if installed.
|
|
1024
1101
|
|
|
@@ -1179,6 +1256,28 @@ class ModuleView(HasState[ModuleState]):
|
|
|
1179
1256
|
else:
|
|
1180
1257
|
return False
|
|
1181
1258
|
|
|
1259
|
+
def convert_absorbance_reader_data_points(
|
|
1260
|
+
self, data: List[float]
|
|
1261
|
+
) -> Dict[str, float]:
|
|
1262
|
+
"""Return the data from the Absorbance Reader module in a map of wells for each read value."""
|
|
1263
|
+
if len(data) == 96:
|
|
1264
|
+
# We have to reverse the reader values because the Opentrons Absorbance Reader is rotated 180 degrees on the deck
|
|
1265
|
+
data.reverse()
|
|
1266
|
+
well_map: Dict[str, float] = {}
|
|
1267
|
+
for i, value in enumerate(data):
|
|
1268
|
+
row = chr(ord("A") + i // 12) # Convert index to row (A-H)
|
|
1269
|
+
col = (i % 12) + 1 # Convert index to column (1-12)
|
|
1270
|
+
well_key = f"{row}{col}"
|
|
1271
|
+
truncated_value = float(
|
|
1272
|
+
"{:.5}".format(str(value))
|
|
1273
|
+
) # Truncate the returned value to the third decimal place
|
|
1274
|
+
well_map[well_key] = truncated_value
|
|
1275
|
+
return well_map
|
|
1276
|
+
else:
|
|
1277
|
+
raise ValueError(
|
|
1278
|
+
"Only readings of 96 Well labware are supported for conversion to map of values by well."
|
|
1279
|
+
)
|
|
1280
|
+
|
|
1182
1281
|
def ensure_and_convert_module_fixture_location(
|
|
1183
1282
|
self,
|
|
1184
1283
|
deck_slot: DeckSlotName,
|
|
@@ -1194,84 +1293,40 @@ class ModuleView(HasState[ModuleState]):
|
|
|
1194
1293
|
f"Invalid Deck Type: {deck_type.name} - Does not support modules as fixtures."
|
|
1195
1294
|
)
|
|
1196
1295
|
|
|
1296
|
+
assert deck_slot in DeckSlotName.ot3_slots()
|
|
1197
1297
|
if model == ModuleModel.MAGNETIC_BLOCK_V1:
|
|
1198
|
-
|
|
1199
|
-
slot
|
|
1200
|
-
for slot in [
|
|
1201
|
-
"A1",
|
|
1202
|
-
"B1",
|
|
1203
|
-
"C1",
|
|
1204
|
-
"D1",
|
|
1205
|
-
"A2",
|
|
1206
|
-
"B2",
|
|
1207
|
-
"C2",
|
|
1208
|
-
"D2",
|
|
1209
|
-
"A3",
|
|
1210
|
-
"B3",
|
|
1211
|
-
"C3",
|
|
1212
|
-
"D3",
|
|
1213
|
-
]
|
|
1214
|
-
]
|
|
1215
|
-
addressable_areas = [
|
|
1216
|
-
"magneticBlockV1A1",
|
|
1217
|
-
"magneticBlockV1B1",
|
|
1218
|
-
"magneticBlockV1C1",
|
|
1219
|
-
"magneticBlockV1D1",
|
|
1220
|
-
"magneticBlockV1A2",
|
|
1221
|
-
"magneticBlockV1B2",
|
|
1222
|
-
"magneticBlockV1C2",
|
|
1223
|
-
"magneticBlockV1D2",
|
|
1224
|
-
"magneticBlockV1A3",
|
|
1225
|
-
"magneticBlockV1B3",
|
|
1226
|
-
"magneticBlockV1C3",
|
|
1227
|
-
"magneticBlockV1D3",
|
|
1228
|
-
]
|
|
1298
|
+
return f"magneticBlockV1{deck_slot.value}"
|
|
1229
1299
|
|
|
1230
1300
|
elif model == ModuleModel.HEATER_SHAKER_MODULE_V1:
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
"heaterShakerV1A1",
|
|
1236
|
-
"heaterShakerV1B1",
|
|
1237
|
-
"heaterShakerV1C1",
|
|
1238
|
-
"heaterShakerV1D1",
|
|
1239
|
-
"heaterShakerV1A3",
|
|
1240
|
-
"heaterShakerV1B3",
|
|
1241
|
-
"heaterShakerV1C3",
|
|
1242
|
-
"heaterShakerV1D3",
|
|
1243
|
-
]
|
|
1301
|
+
# only allowed in column 1 & 3
|
|
1302
|
+
assert deck_slot.value[-1] in ("1", "3")
|
|
1303
|
+
return f"heaterShakerV1{deck_slot.value}"
|
|
1304
|
+
|
|
1244
1305
|
elif model == ModuleModel.TEMPERATURE_MODULE_V2:
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
"temperatureModuleV2A1",
|
|
1250
|
-
"temperatureModuleV2B1",
|
|
1251
|
-
"temperatureModuleV2C1",
|
|
1252
|
-
"temperatureModuleV2D1",
|
|
1253
|
-
"temperatureModuleV2A3",
|
|
1254
|
-
"temperatureModuleV2B3",
|
|
1255
|
-
"temperatureModuleV2C3",
|
|
1256
|
-
"temperatureModuleV2D3",
|
|
1257
|
-
]
|
|
1306
|
+
# only allowed in column 1 & 3
|
|
1307
|
+
assert deck_slot.value[-1] in ("1", "3")
|
|
1308
|
+
return f"temperatureModuleV2{deck_slot.value}"
|
|
1309
|
+
|
|
1258
1310
|
elif model == ModuleModel.THERMOCYCLER_MODULE_V2:
|
|
1259
1311
|
return "thermocyclerModuleV2"
|
|
1312
|
+
|
|
1260
1313
|
elif model == ModuleModel.ABSORBANCE_READER_V1:
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
"absorbanceReaderV1B3",
|
|
1265
|
-
"absorbanceReaderV1C3",
|
|
1266
|
-
"absorbanceReaderV1D3",
|
|
1267
|
-
]
|
|
1268
|
-
else:
|
|
1269
|
-
raise ValueError(
|
|
1270
|
-
f"Unknown module {model.name} has no addressable areas to provide."
|
|
1271
|
-
)
|
|
1314
|
+
# only allowed in column 3
|
|
1315
|
+
assert deck_slot.value[-1] == "3"
|
|
1316
|
+
return f"absorbanceReaderV1{deck_slot.value}"
|
|
1272
1317
|
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1318
|
+
raise ValueError(
|
|
1319
|
+
f"Unknown module {model.name} has no addressable areas to provide."
|
|
1320
|
+
)
|
|
1321
|
+
|
|
1322
|
+
def absorbance_reader_dock_location(
|
|
1323
|
+
self, module_id: str
|
|
1324
|
+
) -> AddressableAreaLocation:
|
|
1325
|
+
"""Get the addressable area for the absorbance reader dock."""
|
|
1326
|
+
reader_slot = self.get_location(module_id)
|
|
1327
|
+
lid_doc_slot = get_adjacent_staging_slot(reader_slot.slotName)
|
|
1328
|
+
assert lid_doc_slot is not None
|
|
1329
|
+
lid_dock_area = AddressableAreaLocation(
|
|
1330
|
+
addressableAreaName="absorbanceReaderV1LidDock" + lid_doc_slot.value
|
|
1331
|
+
)
|
|
1332
|
+
return lid_dock_area
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"""Motion state store and getters."""
|
|
2
2
|
from dataclasses import dataclass
|
|
3
|
-
from typing import List, Optional
|
|
3
|
+
from typing import List, Optional, Union
|
|
4
4
|
|
|
5
|
-
from opentrons.types import MountType, Point
|
|
5
|
+
from opentrons.types import MountType, Point, StagingSlotName
|
|
6
6
|
from opentrons.hardware_control.types import CriticalPoint
|
|
7
7
|
from opentrons.motion_planning.adjacent_slots_getters import (
|
|
8
8
|
get_east_west_slots,
|
|
@@ -10,11 +10,12 @@ from opentrons.motion_planning.adjacent_slots_getters import (
|
|
|
10
10
|
)
|
|
11
11
|
from opentrons import motion_planning
|
|
12
12
|
|
|
13
|
-
from . import
|
|
13
|
+
from . import _move_types
|
|
14
14
|
from .. import errors
|
|
15
15
|
from ..types import (
|
|
16
16
|
MotorAxis,
|
|
17
17
|
WellLocation,
|
|
18
|
+
LiquidHandlingWellLocation,
|
|
18
19
|
CurrentWell,
|
|
19
20
|
CurrentPipetteLocation,
|
|
20
21
|
AddressableOffsetVector,
|
|
@@ -89,13 +90,14 @@ class MotionView:
|
|
|
89
90
|
pipette_id: str,
|
|
90
91
|
labware_id: str,
|
|
91
92
|
well_name: str,
|
|
92
|
-
well_location: Optional[WellLocation],
|
|
93
|
+
well_location: Optional[Union[WellLocation, LiquidHandlingWellLocation]],
|
|
93
94
|
origin: Point,
|
|
94
95
|
origin_cp: Optional[CriticalPoint],
|
|
95
96
|
max_travel_z: float,
|
|
96
97
|
current_well: Optional[CurrentWell] = None,
|
|
97
98
|
force_direct: bool = False,
|
|
98
99
|
minimum_z_height: Optional[float] = None,
|
|
100
|
+
operation_volume: Optional[float] = None,
|
|
99
101
|
) -> List[motion_planning.Waypoint]:
|
|
100
102
|
"""Calculate waypoints to a destination that's specified as a well."""
|
|
101
103
|
location = current_well or self._pipettes.get_current_location()
|
|
@@ -107,12 +109,14 @@ class MotionView:
|
|
|
107
109
|
destination_cp = CriticalPoint.XY_CENTER
|
|
108
110
|
|
|
109
111
|
destination = self._geometry.get_well_position(
|
|
110
|
-
labware_id,
|
|
111
|
-
well_name,
|
|
112
|
-
well_location,
|
|
112
|
+
labware_id=labware_id,
|
|
113
|
+
well_name=well_name,
|
|
114
|
+
well_location=well_location,
|
|
115
|
+
operation_volume=operation_volume,
|
|
116
|
+
pipette_id=pipette_id,
|
|
113
117
|
)
|
|
114
118
|
|
|
115
|
-
move_type =
|
|
119
|
+
move_type = _move_types.get_move_type_to_well(
|
|
116
120
|
pipette_id, labware_id, well_name, location, force_direct
|
|
117
121
|
)
|
|
118
122
|
min_travel_z = self._geometry.get_min_travel_z(
|
|
@@ -151,6 +155,7 @@ class MotionView:
|
|
|
151
155
|
minimum_z_height: Optional[float] = None,
|
|
152
156
|
stay_at_max_travel_z: bool = False,
|
|
153
157
|
ignore_tip_configuration: Optional[bool] = True,
|
|
158
|
+
max_travel_z_extra_margin: Optional[float] = None,
|
|
154
159
|
) -> List[motion_planning.Waypoint]:
|
|
155
160
|
"""Calculate waypoints to a destination that's specified as an addressable area."""
|
|
156
161
|
location = self._pipettes.get_current_location()
|
|
@@ -169,7 +174,9 @@ class MotionView:
|
|
|
169
174
|
# beneath max_travel_z. Investigate why motion_planning.get_waypoints() does not
|
|
170
175
|
# let us travel at max_travel_z, and whether it's safe to make it do that.
|
|
171
176
|
# Possibly related: https://github.com/Opentrons/opentrons/pull/6882#discussion_r514248062
|
|
172
|
-
max_travel_z
|
|
177
|
+
max_travel_z
|
|
178
|
+
- motion_planning.waypoints.MINIMUM_Z_MARGIN
|
|
179
|
+
- (max_travel_z_extra_margin or 0.0),
|
|
173
180
|
)
|
|
174
181
|
destination = base_destination_at_max_z + Point(
|
|
175
182
|
offset.x, offset.y, offset.z
|
|
@@ -270,9 +277,13 @@ class MotionView:
|
|
|
270
277
|
current_location = self._pipettes.get_current_location()
|
|
271
278
|
if current_location is not None:
|
|
272
279
|
if isinstance(current_location, CurrentWell):
|
|
273
|
-
|
|
280
|
+
ancestor = self._geometry.get_ancestor_slot_name(
|
|
274
281
|
current_location.labware_id
|
|
275
|
-
)
|
|
282
|
+
)
|
|
283
|
+
if isinstance(ancestor, StagingSlotName):
|
|
284
|
+
# Staging Area Slots cannot intersect with the h/s
|
|
285
|
+
return False
|
|
286
|
+
pipette_deck_slot = ancestor.as_int()
|
|
276
287
|
else:
|
|
277
288
|
pipette_deck_slot = (
|
|
278
289
|
self._addressable_areas.get_addressable_area_base_slot(
|
|
@@ -292,9 +303,13 @@ class MotionView:
|
|
|
292
303
|
current_location = self._pipettes.get_current_location()
|
|
293
304
|
if current_location is not None:
|
|
294
305
|
if isinstance(current_location, CurrentWell):
|
|
295
|
-
|
|
306
|
+
ancestor = self._geometry.get_ancestor_slot_name(
|
|
296
307
|
current_location.labware_id
|
|
297
|
-
)
|
|
308
|
+
)
|
|
309
|
+
if isinstance(ancestor, StagingSlotName):
|
|
310
|
+
# Staging Area Slots cannot intersect with the h/s
|
|
311
|
+
return False
|
|
312
|
+
pipette_deck_slot = ancestor.as_int()
|
|
298
313
|
else:
|
|
299
314
|
pipette_deck_slot = (
|
|
300
315
|
self._addressable_areas.get_addressable_area_base_slot(
|
|
@@ -317,6 +332,10 @@ class MotionView:
|
|
|
317
332
|
"""Get a list of touch points for a touch tip operation."""
|
|
318
333
|
mount = self._pipettes.get_mount(pipette_id)
|
|
319
334
|
labware_slot = self._geometry.get_ancestor_slot_name(labware_id)
|
|
335
|
+
if isinstance(labware_slot, StagingSlotName):
|
|
336
|
+
raise errors.LocationIsStagingSlotError(
|
|
337
|
+
"Cannot perform Touch Tip on labware in Staging Area Slot."
|
|
338
|
+
)
|
|
320
339
|
next_to_module = self._modules.is_edge_move_unsafe(mount, labware_slot)
|
|
321
340
|
edge_path_type = self._labware.get_edge_path_type(
|
|
322
341
|
labware_id, well_name, mount, labware_slot, next_to_module
|
|
@@ -326,7 +345,7 @@ class MotionView:
|
|
|
326
345
|
labware_id, well_name, radius
|
|
327
346
|
)
|
|
328
347
|
|
|
329
|
-
positions =
|
|
348
|
+
positions = _move_types.get_edge_point_list(
|
|
330
349
|
center_point, x_offset, y_offset, edge_path_type
|
|
331
350
|
)
|
|
332
351
|
critical_point: Optional[CriticalPoint] = None
|