opentrons 8.2.0a4__py2.py3-none-any.whl → 8.3.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/deck_configuration.py +3 -3
- opentrons/calibration_storage/file_operators.py +3 -3
- opentrons/calibration_storage/helpers.py +3 -1
- opentrons/calibration_storage/ot2/models/v1.py +16 -29
- opentrons/calibration_storage/ot2/tip_length.py +7 -4
- opentrons/calibration_storage/ot3/models/v1.py +14 -23
- opentrons/cli/analyze.py +18 -6
- opentrons/config/defaults_ot3.py +1 -0
- opentrons/drivers/asyncio/communication/__init__.py +2 -0
- opentrons/drivers/asyncio/communication/errors.py +16 -3
- opentrons/drivers/asyncio/communication/serial_connection.py +24 -9
- opentrons/drivers/command_builder.py +2 -2
- opentrons/drivers/flex_stacker/__init__.py +9 -0
- opentrons/drivers/flex_stacker/abstract.py +89 -0
- opentrons/drivers/flex_stacker/driver.py +260 -0
- opentrons/drivers/flex_stacker/simulator.py +109 -0
- opentrons/drivers/flex_stacker/types.py +138 -0
- opentrons/drivers/heater_shaker/driver.py +18 -3
- opentrons/drivers/temp_deck/driver.py +13 -3
- opentrons/drivers/thermocycler/driver.py +17 -3
- opentrons/execute.py +3 -1
- opentrons/hardware_control/__init__.py +1 -2
- opentrons/hardware_control/api.py +33 -21
- opentrons/hardware_control/backends/flex_protocol.py +17 -7
- opentrons/hardware_control/backends/ot3controller.py +213 -63
- opentrons/hardware_control/backends/ot3simulator.py +18 -9
- opentrons/hardware_control/backends/ot3utils.py +43 -15
- opentrons/hardware_control/dev_types.py +4 -0
- opentrons/hardware_control/emulation/heater_shaker.py +4 -0
- opentrons/hardware_control/emulation/module_server/client.py +1 -1
- opentrons/hardware_control/emulation/module_server/server.py +5 -3
- opentrons/hardware_control/emulation/settings.py +3 -4
- opentrons/hardware_control/instruments/ot2/instrument_calibration.py +2 -1
- opentrons/hardware_control/instruments/ot2/pipette.py +15 -22
- opentrons/hardware_control/instruments/ot2/pipette_handler.py +8 -1
- opentrons/hardware_control/instruments/ot3/gripper.py +2 -2
- opentrons/hardware_control/instruments/ot3/pipette.py +23 -22
- opentrons/hardware_control/instruments/ot3/pipette_handler.py +10 -1
- opentrons/hardware_control/modules/mod_abc.py +2 -2
- opentrons/hardware_control/motion_utilities.py +68 -0
- opentrons/hardware_control/nozzle_manager.py +39 -41
- opentrons/hardware_control/ot3_calibration.py +1 -1
- opentrons/hardware_control/ot3api.py +78 -31
- opentrons/hardware_control/protocols/gripper_controller.py +3 -0
- opentrons/hardware_control/protocols/hardware_manager.py +5 -1
- opentrons/hardware_control/protocols/liquid_handler.py +22 -1
- opentrons/hardware_control/protocols/motion_controller.py +7 -0
- opentrons/hardware_control/robot_calibration.py +1 -1
- opentrons/hardware_control/types.py +61 -0
- opentrons/legacy_commands/commands.py +37 -0
- opentrons/legacy_commands/types.py +39 -0
- opentrons/protocol_api/__init__.py +20 -1
- opentrons/protocol_api/_liquid.py +24 -49
- opentrons/protocol_api/_liquid_properties.py +754 -0
- opentrons/protocol_api/_types.py +24 -0
- opentrons/protocol_api/core/common.py +2 -0
- opentrons/protocol_api/core/engine/instrument.py +191 -10
- opentrons/protocol_api/core/engine/labware.py +29 -7
- opentrons/protocol_api/core/engine/protocol.py +130 -5
- opentrons/protocol_api/core/engine/robot.py +139 -0
- opentrons/protocol_api/core/engine/well.py +4 -1
- opentrons/protocol_api/core/instrument.py +73 -4
- opentrons/protocol_api/core/labware.py +13 -4
- opentrons/protocol_api/core/legacy/legacy_instrument_core.py +87 -3
- opentrons/protocol_api/core/legacy/legacy_labware_core.py +13 -4
- opentrons/protocol_api/core/legacy/legacy_protocol_core.py +32 -1
- opentrons/protocol_api/core/legacy/legacy_robot_core.py +0 -0
- opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +61 -3
- opentrons/protocol_api/core/protocol.py +34 -1
- opentrons/protocol_api/core/robot.py +51 -0
- opentrons/protocol_api/instrument_context.py +299 -44
- opentrons/protocol_api/labware.py +248 -9
- opentrons/protocol_api/module_contexts.py +21 -17
- opentrons/protocol_api/protocol_context.py +125 -4
- opentrons/protocol_api/robot_context.py +204 -32
- opentrons/protocol_api/validation.py +262 -3
- opentrons/protocol_engine/__init__.py +4 -0
- opentrons/protocol_engine/actions/actions.py +2 -3
- opentrons/protocol_engine/clients/sync_client.py +18 -0
- opentrons/protocol_engine/commands/__init__.py +121 -0
- opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +1 -3
- opentrons/protocol_engine/commands/absorbance_reader/initialize.py +20 -6
- opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +1 -2
- opentrons/protocol_engine/commands/absorbance_reader/read.py +36 -10
- opentrons/protocol_engine/commands/air_gap_in_place.py +160 -0
- opentrons/protocol_engine/commands/aspirate.py +103 -53
- opentrons/protocol_engine/commands/aspirate_in_place.py +55 -51
- opentrons/protocol_engine/commands/blow_out.py +44 -39
- opentrons/protocol_engine/commands/blow_out_in_place.py +21 -32
- opentrons/protocol_engine/commands/calibration/calibrate_gripper.py +13 -6
- opentrons/protocol_engine/commands/calibration/calibrate_module.py +1 -1
- opentrons/protocol_engine/commands/calibration/calibrate_pipette.py +3 -3
- opentrons/protocol_engine/commands/calibration/move_to_maintenance_position.py +1 -1
- opentrons/protocol_engine/commands/command.py +73 -66
- opentrons/protocol_engine/commands/command_unions.py +140 -1
- opentrons/protocol_engine/commands/comment.py +1 -1
- opentrons/protocol_engine/commands/configure_for_volume.py +10 -3
- opentrons/protocol_engine/commands/configure_nozzle_layout.py +6 -4
- opentrons/protocol_engine/commands/custom.py +6 -12
- opentrons/protocol_engine/commands/dispense.py +82 -48
- opentrons/protocol_engine/commands/dispense_in_place.py +71 -51
- opentrons/protocol_engine/commands/drop_tip.py +52 -31
- opentrons/protocol_engine/commands/drop_tip_in_place.py +79 -8
- opentrons/protocol_engine/commands/evotip_dispense.py +156 -0
- opentrons/protocol_engine/commands/evotip_seal_pipette.py +331 -0
- opentrons/protocol_engine/commands/evotip_unseal_pipette.py +160 -0
- opentrons/protocol_engine/commands/generate_command_schema.py +4 -11
- opentrons/protocol_engine/commands/get_next_tip.py +134 -0
- opentrons/protocol_engine/commands/get_tip_presence.py +1 -1
- opentrons/protocol_engine/commands/heater_shaker/close_labware_latch.py +1 -1
- opentrons/protocol_engine/commands/heater_shaker/deactivate_heater.py +1 -1
- opentrons/protocol_engine/commands/heater_shaker/deactivate_shaker.py +1 -1
- opentrons/protocol_engine/commands/heater_shaker/open_labware_latch.py +1 -1
- opentrons/protocol_engine/commands/heater_shaker/set_and_wait_for_shake_speed.py +1 -1
- opentrons/protocol_engine/commands/heater_shaker/set_target_temperature.py +1 -1
- opentrons/protocol_engine/commands/heater_shaker/wait_for_temperature.py +10 -4
- opentrons/protocol_engine/commands/home.py +13 -4
- opentrons/protocol_engine/commands/liquid_probe.py +125 -31
- opentrons/protocol_engine/commands/load_labware.py +33 -6
- opentrons/protocol_engine/commands/load_lid.py +146 -0
- opentrons/protocol_engine/commands/load_lid_stack.py +189 -0
- opentrons/protocol_engine/commands/load_liquid.py +12 -4
- opentrons/protocol_engine/commands/load_liquid_class.py +144 -0
- opentrons/protocol_engine/commands/load_module.py +31 -10
- opentrons/protocol_engine/commands/load_pipette.py +19 -8
- opentrons/protocol_engine/commands/magnetic_module/disengage.py +1 -1
- opentrons/protocol_engine/commands/magnetic_module/engage.py +1 -1
- opentrons/protocol_engine/commands/move_labware.py +28 -6
- opentrons/protocol_engine/commands/move_relative.py +35 -25
- opentrons/protocol_engine/commands/move_to_addressable_area.py +40 -27
- opentrons/protocol_engine/commands/move_to_addressable_area_for_drop_tip.py +53 -32
- opentrons/protocol_engine/commands/move_to_coordinates.py +36 -22
- opentrons/protocol_engine/commands/move_to_well.py +40 -24
- opentrons/protocol_engine/commands/movement_common.py +338 -0
- opentrons/protocol_engine/commands/pick_up_tip.py +49 -27
- opentrons/protocol_engine/commands/pipetting_common.py +169 -87
- opentrons/protocol_engine/commands/prepare_to_aspirate.py +24 -33
- opentrons/protocol_engine/commands/reload_labware.py +1 -1
- opentrons/protocol_engine/commands/retract_axis.py +1 -1
- opentrons/protocol_engine/commands/robot/__init__.py +69 -0
- opentrons/protocol_engine/commands/robot/close_gripper_jaw.py +86 -0
- opentrons/protocol_engine/commands/robot/common.py +18 -0
- opentrons/protocol_engine/commands/robot/move_axes_relative.py +101 -0
- opentrons/protocol_engine/commands/robot/move_axes_to.py +100 -0
- opentrons/protocol_engine/commands/robot/move_to.py +94 -0
- opentrons/protocol_engine/commands/robot/open_gripper_jaw.py +77 -0
- opentrons/protocol_engine/commands/save_position.py +14 -5
- opentrons/protocol_engine/commands/set_rail_lights.py +1 -1
- opentrons/protocol_engine/commands/set_status_bar.py +1 -1
- opentrons/protocol_engine/commands/temperature_module/deactivate.py +1 -1
- opentrons/protocol_engine/commands/temperature_module/set_target_temperature.py +1 -1
- opentrons/protocol_engine/commands/temperature_module/wait_for_temperature.py +10 -4
- opentrons/protocol_engine/commands/thermocycler/close_lid.py +1 -1
- opentrons/protocol_engine/commands/thermocycler/deactivate_block.py +1 -1
- opentrons/protocol_engine/commands/thermocycler/deactivate_lid.py +1 -1
- opentrons/protocol_engine/commands/thermocycler/open_lid.py +1 -1
- opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py +9 -3
- opentrons/protocol_engine/commands/thermocycler/run_profile.py +9 -3
- opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py +11 -4
- opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py +1 -1
- opentrons/protocol_engine/commands/thermocycler/wait_for_block_temperature.py +1 -1
- opentrons/protocol_engine/commands/thermocycler/wait_for_lid_temperature.py +1 -1
- opentrons/protocol_engine/commands/touch_tip.py +65 -16
- opentrons/protocol_engine/commands/unsafe/unsafe_blow_out_in_place.py +5 -2
- opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +13 -4
- opentrons/protocol_engine/commands/unsafe/unsafe_engage_axes.py +2 -5
- opentrons/protocol_engine/commands/unsafe/unsafe_place_labware.py +1 -1
- opentrons/protocol_engine/commands/unsafe/unsafe_ungrip_labware.py +1 -1
- opentrons/protocol_engine/commands/unsafe/update_position_estimators.py +2 -5
- opentrons/protocol_engine/commands/verify_tip_presence.py +11 -4
- opentrons/protocol_engine/commands/wait_for_duration.py +10 -3
- opentrons/protocol_engine/commands/wait_for_resume.py +10 -3
- opentrons/protocol_engine/errors/__init__.py +12 -0
- opentrons/protocol_engine/errors/error_occurrence.py +19 -20
- opentrons/protocol_engine/errors/exceptions.py +76 -0
- opentrons/protocol_engine/execution/command_executor.py +1 -1
- opentrons/protocol_engine/execution/equipment.py +73 -5
- opentrons/protocol_engine/execution/gantry_mover.py +369 -8
- opentrons/protocol_engine/execution/hardware_stopper.py +7 -7
- opentrons/protocol_engine/execution/movement.py +27 -0
- opentrons/protocol_engine/execution/pipetting.py +5 -1
- opentrons/protocol_engine/execution/tip_handler.py +34 -15
- opentrons/protocol_engine/notes/notes.py +1 -1
- opentrons/protocol_engine/protocol_engine.py +7 -6
- opentrons/protocol_engine/resources/labware_data_provider.py +1 -1
- opentrons/protocol_engine/resources/labware_validation.py +18 -0
- opentrons/protocol_engine/resources/module_data_provider.py +1 -1
- opentrons/protocol_engine/resources/pipette_data_provider.py +26 -0
- opentrons/protocol_engine/slot_standardization.py +9 -9
- opentrons/protocol_engine/state/_move_types.py +9 -5
- opentrons/protocol_engine/state/_well_math.py +193 -0
- opentrons/protocol_engine/state/addressable_areas.py +25 -61
- opentrons/protocol_engine/state/command_history.py +12 -0
- opentrons/protocol_engine/state/commands.py +22 -14
- opentrons/protocol_engine/state/files.py +10 -12
- opentrons/protocol_engine/state/fluid_stack.py +138 -0
- opentrons/protocol_engine/state/frustum_helpers.py +63 -69
- opentrons/protocol_engine/state/geometry.py +47 -1
- opentrons/protocol_engine/state/labware.py +92 -26
- opentrons/protocol_engine/state/liquid_classes.py +82 -0
- opentrons/protocol_engine/state/liquids.py +16 -4
- opentrons/protocol_engine/state/modules.py +52 -70
- opentrons/protocol_engine/state/motion.py +6 -1
- opentrons/protocol_engine/state/pipettes.py +149 -58
- opentrons/protocol_engine/state/state.py +21 -2
- opentrons/protocol_engine/state/state_summary.py +4 -2
- opentrons/protocol_engine/state/tips.py +11 -44
- opentrons/protocol_engine/state/update_types.py +343 -48
- opentrons/protocol_engine/state/wells.py +19 -11
- opentrons/protocol_engine/types.py +176 -28
- opentrons/protocol_reader/extract_labware_definitions.py +5 -2
- opentrons/protocol_reader/file_format_validator.py +5 -5
- opentrons/protocol_runner/json_file_reader.py +9 -3
- opentrons/protocol_runner/json_translator.py +51 -25
- opentrons/protocol_runner/legacy_command_mapper.py +66 -64
- opentrons/protocol_runner/protocol_runner.py +35 -4
- opentrons/protocol_runner/python_protocol_wrappers.py +1 -1
- opentrons/protocol_runner/run_orchestrator.py +13 -3
- opentrons/protocols/advanced_control/common.py +38 -0
- opentrons/protocols/advanced_control/mix.py +1 -1
- opentrons/protocols/advanced_control/transfers/__init__.py +0 -0
- opentrons/protocols/advanced_control/transfers/common.py +56 -0
- opentrons/protocols/advanced_control/{transfers.py → transfers/transfer.py} +10 -85
- opentrons/protocols/api_support/definitions.py +1 -1
- opentrons/protocols/api_support/instrument.py +1 -1
- opentrons/protocols/api_support/util.py +10 -0
- opentrons/protocols/labware.py +70 -8
- opentrons/protocols/models/json_protocol.py +5 -9
- opentrons/simulate.py +3 -1
- opentrons/types.py +162 -2
- opentrons/util/entrypoint_util.py +2 -5
- opentrons/util/logging_config.py +1 -1
- {opentrons-8.2.0a4.dist-info → opentrons-8.3.0.dist-info}/METADATA +16 -15
- {opentrons-8.2.0a4.dist-info → opentrons-8.3.0.dist-info}/RECORD +238 -208
- {opentrons-8.2.0a4.dist-info → opentrons-8.3.0.dist-info}/WHEEL +1 -1
- {opentrons-8.2.0a4.dist-info → opentrons-8.3.0.dist-info}/LICENSE +0 -0
- {opentrons-8.2.0a4.dist-info → opentrons-8.3.0.dist-info}/entry_points.txt +0 -0
- {opentrons-8.2.0a4.dist-info → opentrons-8.3.0.dist-info}/top_level.txt +0 -0
|
@@ -35,6 +35,7 @@ from opentrons.protocol_engine.state.module_substates.absorbance_reader_substate
|
|
|
35
35
|
AbsorbanceReaderMeasureMode,
|
|
36
36
|
)
|
|
37
37
|
from opentrons.types import DeckSlotName, MountType, StagingSlotName
|
|
38
|
+
from .update_types import AbsorbanceReaderStateUpdate
|
|
38
39
|
from ..errors import ModuleNotConnectedError
|
|
39
40
|
|
|
40
41
|
from ..types import (
|
|
@@ -63,7 +64,6 @@ from ..commands import (
|
|
|
63
64
|
heater_shaker,
|
|
64
65
|
temperature_module,
|
|
65
66
|
thermocycler,
|
|
66
|
-
absorbance_reader,
|
|
67
67
|
)
|
|
68
68
|
from ..actions import (
|
|
69
69
|
Action,
|
|
@@ -296,40 +296,10 @@ class ModuleStore(HasState[ModuleState], HandlesActions):
|
|
|
296
296
|
):
|
|
297
297
|
self._handle_thermocycler_module_commands(command)
|
|
298
298
|
|
|
299
|
-
if isinstance(
|
|
300
|
-
command.result,
|
|
301
|
-
(
|
|
302
|
-
absorbance_reader.InitializeResult,
|
|
303
|
-
absorbance_reader.ReadAbsorbanceResult,
|
|
304
|
-
),
|
|
305
|
-
):
|
|
306
|
-
self._handle_absorbance_reader_commands(command)
|
|
307
|
-
|
|
308
299
|
def _handle_state_update(self, state_update: update_types.StateUpdate) -> None:
|
|
309
|
-
if state_update.
|
|
310
|
-
|
|
311
|
-
|
|
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,
|
|
300
|
+
if state_update.absorbance_reader_state_update != update_types.NO_CHANGE:
|
|
301
|
+
self._handle_absorbance_reader_commands(
|
|
302
|
+
state_update.absorbance_reader_state_update
|
|
333
303
|
)
|
|
334
304
|
|
|
335
305
|
def _add_module_substate(
|
|
@@ -589,50 +559,61 @@ class ModuleStore(HasState[ModuleState], HandlesActions):
|
|
|
589
559
|
)
|
|
590
560
|
|
|
591
561
|
def _handle_absorbance_reader_commands(
|
|
592
|
-
self,
|
|
593
|
-
command: Union[
|
|
594
|
-
absorbance_reader.Initialize,
|
|
595
|
-
absorbance_reader.ReadAbsorbance,
|
|
596
|
-
],
|
|
562
|
+
self, absorbance_reader_state_update: AbsorbanceReaderStateUpdate
|
|
597
563
|
) -> None:
|
|
598
|
-
|
|
564
|
+
# Get current values:
|
|
565
|
+
module_id = absorbance_reader_state_update.module_id
|
|
599
566
|
absorbance_reader_substate = self._state.substate_by_module_id[module_id]
|
|
600
567
|
assert isinstance(
|
|
601
568
|
absorbance_reader_substate, AbsorbanceReaderSubState
|
|
602
569
|
), f"{module_id} is not an absorbance plate reader."
|
|
603
|
-
|
|
604
|
-
|
|
570
|
+
is_lid_on = absorbance_reader_substate.is_lid_on
|
|
571
|
+
measured = True
|
|
605
572
|
configured = absorbance_reader_substate.configured
|
|
606
573
|
measure_mode = absorbance_reader_substate.measure_mode
|
|
607
574
|
configured_wavelengths = absorbance_reader_substate.configured_wavelengths
|
|
608
575
|
reference_wavelength = absorbance_reader_substate.reference_wavelength
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
576
|
+
data = absorbance_reader_substate.data
|
|
577
|
+
if (
|
|
578
|
+
absorbance_reader_state_update.absorbance_reader_lid
|
|
579
|
+
!= update_types.NO_CHANGE
|
|
580
|
+
):
|
|
581
|
+
is_lid_on = absorbance_reader_state_update.absorbance_reader_lid.is_lid_on
|
|
582
|
+
elif (
|
|
583
|
+
absorbance_reader_state_update.initialize_absorbance_reader_update
|
|
584
|
+
!= update_types.NO_CHANGE
|
|
585
|
+
):
|
|
586
|
+
configured = True
|
|
587
|
+
measured = False
|
|
588
|
+
is_lid_on = is_lid_on
|
|
589
|
+
measure_mode = AbsorbanceReaderMeasureMode(
|
|
590
|
+
absorbance_reader_state_update.initialize_absorbance_reader_update.measure_mode
|
|
621
591
|
)
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
is_lid_on=is_lid_on,
|
|
628
|
-
measure_mode=measure_mode,
|
|
629
|
-
configured_wavelengths=configured_wavelengths,
|
|
630
|
-
reference_wavelength=reference_wavelength,
|
|
631
|
-
data=command.result.data,
|
|
592
|
+
configured_wavelengths = (
|
|
593
|
+
absorbance_reader_state_update.initialize_absorbance_reader_update.sample_wave_lengths
|
|
594
|
+
)
|
|
595
|
+
reference_wavelength = (
|
|
596
|
+
absorbance_reader_state_update.initialize_absorbance_reader_update.reference_wave_length
|
|
632
597
|
)
|
|
598
|
+
data = None
|
|
599
|
+
elif (
|
|
600
|
+
absorbance_reader_state_update.absorbance_reader_data
|
|
601
|
+
!= update_types.NO_CHANGE
|
|
602
|
+
):
|
|
603
|
+
data = absorbance_reader_state_update.absorbance_reader_data.read_result
|
|
604
|
+
self._state.substate_by_module_id[module_id] = AbsorbanceReaderSubState(
|
|
605
|
+
module_id=AbsorbanceReaderId(module_id),
|
|
606
|
+
configured=configured,
|
|
607
|
+
measured=measured,
|
|
608
|
+
is_lid_on=is_lid_on,
|
|
609
|
+
measure_mode=measure_mode,
|
|
610
|
+
configured_wavelengths=configured_wavelengths,
|
|
611
|
+
reference_wavelength=reference_wavelength,
|
|
612
|
+
data=data,
|
|
613
|
+
)
|
|
633
614
|
|
|
634
615
|
|
|
635
|
-
class ModuleView
|
|
616
|
+
class ModuleView:
|
|
636
617
|
"""Read-only view of computed module state."""
|
|
637
618
|
|
|
638
619
|
_state: ModuleState
|
|
@@ -654,7 +635,7 @@ class ModuleView(HasState[ModuleState]):
|
|
|
654
635
|
DeckSlotLocation(slotName=slot_name) if slot_name is not None else None
|
|
655
636
|
)
|
|
656
637
|
|
|
657
|
-
return LoadedModule.
|
|
638
|
+
return LoadedModule.model_construct(
|
|
658
639
|
id=module_id,
|
|
659
640
|
location=location,
|
|
660
641
|
model=attached_module.definition.model,
|
|
@@ -860,8 +841,8 @@ class ModuleView(HasState[ModuleState]):
|
|
|
860
841
|
Labware Position Check offset.
|
|
861
842
|
"""
|
|
862
843
|
if (
|
|
863
|
-
self.
|
|
864
|
-
or self.
|
|
844
|
+
self._state.deck_type == DeckType.OT2_STANDARD
|
|
845
|
+
or self._state.deck_type == DeckType.OT2_SHORT_TRASH
|
|
865
846
|
):
|
|
866
847
|
definition = self.get_definition(module_id)
|
|
867
848
|
slot = self.get_location(module_id).slotName.id
|
|
@@ -908,7 +889,7 @@ class ModuleView(HasState[ModuleState]):
|
|
|
908
889
|
"Module location invalid for nominal module offset calculation."
|
|
909
890
|
)
|
|
910
891
|
module_addressable_area = self.ensure_and_convert_module_fixture_location(
|
|
911
|
-
location,
|
|
892
|
+
location, module.model
|
|
912
893
|
)
|
|
913
894
|
module_addressable_area_position = (
|
|
914
895
|
addressable_areas.get_addressable_area_offsets_from_cutout(
|
|
@@ -1281,13 +1262,14 @@ class ModuleView(HasState[ModuleState]):
|
|
|
1281
1262
|
def ensure_and_convert_module_fixture_location(
|
|
1282
1263
|
self,
|
|
1283
1264
|
deck_slot: DeckSlotName,
|
|
1284
|
-
deck_type: DeckType,
|
|
1285
1265
|
model: ModuleModel,
|
|
1286
1266
|
) -> str:
|
|
1287
1267
|
"""Ensure module fixture load location is valid.
|
|
1288
1268
|
|
|
1289
1269
|
Also, convert the deck slot to a valid module fixture addressable area.
|
|
1290
1270
|
"""
|
|
1271
|
+
deck_type = self._state.deck_type
|
|
1272
|
+
|
|
1291
1273
|
if deck_type == DeckType.OT2_STANDARD or deck_type == DeckType.OT2_SHORT_TRASH:
|
|
1292
1274
|
raise ValueError(
|
|
1293
1275
|
f"Invalid Deck Type: {deck_type.name} - Does not support modules as fixtures."
|
|
@@ -327,6 +327,7 @@ class MotionView:
|
|
|
327
327
|
labware_id: str,
|
|
328
328
|
well_name: str,
|
|
329
329
|
center_point: Point,
|
|
330
|
+
mm_from_edge: float = 0,
|
|
330
331
|
radius: float = 1.0,
|
|
331
332
|
) -> List[motion_planning.Waypoint]:
|
|
332
333
|
"""Get a list of touch points for a touch tip operation."""
|
|
@@ -346,7 +347,11 @@ class MotionView:
|
|
|
346
347
|
)
|
|
347
348
|
|
|
348
349
|
positions = _move_types.get_edge_point_list(
|
|
349
|
-
center_point,
|
|
350
|
+
center=center_point,
|
|
351
|
+
x_radius=x_offset,
|
|
352
|
+
y_radius=y_offset,
|
|
353
|
+
mm_from_edge=mm_from_edge,
|
|
354
|
+
edge_path_type=edge_path_type,
|
|
350
355
|
)
|
|
351
356
|
critical_point: Optional[CriticalPoint] = None
|
|
352
357
|
|
|
@@ -1,28 +1,33 @@
|
|
|
1
1
|
"""Basic pipette data state and store."""
|
|
2
|
+
|
|
2
3
|
from __future__ import annotations
|
|
3
4
|
|
|
4
5
|
import dataclasses
|
|
6
|
+
from logging import getLogger
|
|
5
7
|
from typing import (
|
|
6
8
|
Dict,
|
|
7
9
|
List,
|
|
8
10
|
Mapping,
|
|
9
11
|
Optional,
|
|
10
12
|
Tuple,
|
|
11
|
-
|
|
13
|
+
cast,
|
|
12
14
|
)
|
|
13
15
|
|
|
16
|
+
from typing_extensions import assert_never
|
|
17
|
+
|
|
14
18
|
from opentrons_shared_data.pipette import pipette_definition
|
|
19
|
+
from opentrons_shared_data.pipette.ul_per_mm import calculate_ul_per_mm
|
|
20
|
+
from opentrons_shared_data.pipette.types import UlPerMmAction
|
|
21
|
+
|
|
15
22
|
from opentrons.config.defaults_ot2 import Z_RETRACT_DISTANCE
|
|
16
23
|
from opentrons.hardware_control.dev_types import PipetteDict
|
|
17
24
|
from opentrons.hardware_control import CriticalPoint
|
|
18
25
|
from opentrons.hardware_control.nozzle_manager import (
|
|
19
|
-
NozzleConfigurationType,
|
|
20
26
|
NozzleMap,
|
|
21
27
|
)
|
|
22
|
-
from opentrons.types import MountType, Mount as HwMount, Point
|
|
28
|
+
from opentrons.types import MountType, Mount as HwMount, Point, NozzleConfigurationType
|
|
23
29
|
|
|
24
|
-
from . import update_types
|
|
25
|
-
from .. import commands
|
|
30
|
+
from . import update_types, fluid_stack
|
|
26
31
|
from .. import errors
|
|
27
32
|
from ..types import (
|
|
28
33
|
LoadedPipette,
|
|
@@ -36,13 +41,13 @@ from ..types import (
|
|
|
36
41
|
)
|
|
37
42
|
from ..actions import (
|
|
38
43
|
Action,
|
|
39
|
-
FailCommandAction,
|
|
40
44
|
SetPipetteMovementSpeedAction,
|
|
41
|
-
SucceedCommandAction,
|
|
42
45
|
get_state_updates,
|
|
43
46
|
)
|
|
44
47
|
from ._abstract_store import HasState, HandlesActions
|
|
45
48
|
|
|
49
|
+
LOG = getLogger(__name__)
|
|
50
|
+
|
|
46
51
|
|
|
47
52
|
@dataclasses.dataclass(frozen=True)
|
|
48
53
|
class HardwarePipette:
|
|
@@ -98,6 +103,9 @@ class StaticPipetteConfig:
|
|
|
98
103
|
bounding_nozzle_offsets: BoundingNozzlesOffsets
|
|
99
104
|
default_nozzle_map: NozzleMap # todo(mm, 2024-10-14): unused, remove?
|
|
100
105
|
lld_settings: Optional[Dict[str, Dict[str, float]]]
|
|
106
|
+
plunger_positions: Dict[str, float]
|
|
107
|
+
shaft_ul_per_mm: float
|
|
108
|
+
available_sensors: pipette_definition.AvailableSensorDefinition
|
|
101
109
|
|
|
102
110
|
|
|
103
111
|
@dataclasses.dataclass
|
|
@@ -108,7 +116,7 @@ class PipetteState:
|
|
|
108
116
|
# attributes are populated at the appropriate times. Refactor to a
|
|
109
117
|
# single dict-of-many-things instead of many dicts-of-single-things.
|
|
110
118
|
pipettes_by_id: Dict[str, LoadedPipette]
|
|
111
|
-
|
|
119
|
+
pipette_contents_by_id: Dict[str, Optional[fluid_stack.FluidStack]]
|
|
112
120
|
current_location: Optional[CurrentPipetteLocation]
|
|
113
121
|
current_deck_point: CurrentDeckPoint
|
|
114
122
|
attached_tip_by_id: Dict[str, Optional[TipGeometry]]
|
|
@@ -128,7 +136,7 @@ class PipetteStore(HasState[PipetteState], HandlesActions):
|
|
|
128
136
|
"""Initialize a PipetteStore and its state."""
|
|
129
137
|
self._state = PipetteState(
|
|
130
138
|
pipettes_by_id={},
|
|
131
|
-
|
|
139
|
+
pipette_contents_by_id={},
|
|
132
140
|
attached_tip_by_id={},
|
|
133
141
|
current_location=None,
|
|
134
142
|
current_deck_point=CurrentDeckPoint(mount=None, deck_point=None),
|
|
@@ -147,11 +155,9 @@ class PipetteStore(HasState[PipetteState], HandlesActions):
|
|
|
147
155
|
self._update_pipette_config(state_update)
|
|
148
156
|
self._update_pipette_nozzle_map(state_update)
|
|
149
157
|
self._update_tip_state(state_update)
|
|
158
|
+
self._update_volumes(state_update)
|
|
150
159
|
|
|
151
|
-
if isinstance(action,
|
|
152
|
-
self._update_volumes(action)
|
|
153
|
-
|
|
154
|
-
elif isinstance(action, SetPipetteMovementSpeedAction):
|
|
160
|
+
if isinstance(action, SetPipetteMovementSpeedAction):
|
|
155
161
|
self._state.movement_speed_by_id[action.pipette_id] = action.speed
|
|
156
162
|
|
|
157
163
|
def _set_load_pipette(self, state_update: update_types.StateUpdate) -> None:
|
|
@@ -166,7 +172,6 @@ class PipetteStore(HasState[PipetteState], HandlesActions):
|
|
|
166
172
|
self._state.liquid_presence_detection_by_id[pipette_id] = (
|
|
167
173
|
state_update.loaded_pipette.liquid_presence_detection or False
|
|
168
174
|
)
|
|
169
|
-
self._state.aspirated_volume_by_id[pipette_id] = None
|
|
170
175
|
self._state.movement_speed_by_id[pipette_id] = None
|
|
171
176
|
self._state.attached_tip_by_id[pipette_id] = None
|
|
172
177
|
|
|
@@ -177,7 +182,6 @@ class PipetteStore(HasState[PipetteState], HandlesActions):
|
|
|
177
182
|
attached_tip = state_update.pipette_tip_state.tip_geometry
|
|
178
183
|
|
|
179
184
|
self._state.attached_tip_by_id[pipette_id] = attached_tip
|
|
180
|
-
self._state.aspirated_volume_by_id[pipette_id] = 0
|
|
181
185
|
|
|
182
186
|
static_config = self._state.static_config_by_id.get(pipette_id)
|
|
183
187
|
if static_config:
|
|
@@ -204,7 +208,6 @@ class PipetteStore(HasState[PipetteState], HandlesActions):
|
|
|
204
208
|
|
|
205
209
|
else:
|
|
206
210
|
pipette_id = state_update.pipette_tip_state.pipette_id
|
|
207
|
-
self._state.aspirated_volume_by_id[pipette_id] = None
|
|
208
211
|
self._state.attached_tip_by_id[pipette_id] = None
|
|
209
212
|
|
|
210
213
|
static_config = self._state.static_config_by_id.get(pipette_id)
|
|
@@ -292,6 +295,9 @@ class PipetteStore(HasState[PipetteState], HandlesActions):
|
|
|
292
295
|
),
|
|
293
296
|
default_nozzle_map=config.nozzle_map,
|
|
294
297
|
lld_settings=config.pipette_lld_settings,
|
|
298
|
+
plunger_positions=config.plunger_positions,
|
|
299
|
+
shaft_ul_per_mm=config.shaft_ul_per_mm,
|
|
300
|
+
available_sensors=config.available_sensors,
|
|
295
301
|
)
|
|
296
302
|
self._state.flow_rates_by_id[
|
|
297
303
|
state_update.pipette_config.pipette_id
|
|
@@ -308,54 +314,48 @@ class PipetteStore(HasState[PipetteState], HandlesActions):
|
|
|
308
314
|
state_update.pipette_nozzle_map.pipette_id
|
|
309
315
|
] = state_update.pipette_nozzle_map.nozzle_map
|
|
310
316
|
|
|
311
|
-
def _update_volumes(
|
|
312
|
-
|
|
317
|
+
def _update_volumes(self, state_update: update_types.StateUpdate) -> None:
|
|
318
|
+
if state_update.pipette_aspirated_fluid == update_types.NO_CHANGE:
|
|
319
|
+
return
|
|
320
|
+
if state_update.pipette_aspirated_fluid.type == "aspirated":
|
|
321
|
+
self._update_aspirated(state_update.pipette_aspirated_fluid)
|
|
322
|
+
elif state_update.pipette_aspirated_fluid.type == "ejected":
|
|
323
|
+
self._update_ejected(state_update.pipette_aspirated_fluid)
|
|
324
|
+
elif state_update.pipette_aspirated_fluid.type == "empty":
|
|
325
|
+
self._update_empty(state_update.pipette_aspirated_fluid)
|
|
326
|
+
elif state_update.pipette_aspirated_fluid.type == "unknown":
|
|
327
|
+
self._update_unknown(state_update.pipette_aspirated_fluid)
|
|
328
|
+
else:
|
|
329
|
+
assert_never(state_update.pipette_aspirated_fluid.type)
|
|
330
|
+
|
|
331
|
+
def _update_aspirated(
|
|
332
|
+
self, update: update_types.PipetteAspiratedFluidUpdate
|
|
313
333
|
) -> None:
|
|
314
|
-
|
|
315
|
-
|
|
334
|
+
if self._state.pipette_contents_by_id[update.pipette_id] is None:
|
|
335
|
+
self._state.pipette_contents_by_id[
|
|
336
|
+
update.pipette_id
|
|
337
|
+
] = fluid_stack.FluidStack()
|
|
316
338
|
|
|
317
|
-
|
|
318
|
-
action.command.result,
|
|
319
|
-
(commands.AspirateResult, commands.AspirateInPlaceResult),
|
|
320
|
-
):
|
|
321
|
-
pipette_id = action.command.params.pipetteId
|
|
322
|
-
previous_volume = self._state.aspirated_volume_by_id[pipette_id] or 0
|
|
323
|
-
# PipetteHandler will have clamped action.command.result.volume for us, so
|
|
324
|
-
# next_volume should always be in bounds.
|
|
325
|
-
next_volume = previous_volume + action.command.result.volume
|
|
339
|
+
self._fluid_stack_log_if_empty(update.pipette_id).add_fluid(update.fluid)
|
|
326
340
|
|
|
327
|
-
|
|
341
|
+
def _update_ejected(self, update: update_types.PipetteEjectedFluidUpdate) -> None:
|
|
342
|
+
self._fluid_stack_log_if_empty(update.pipette_id).remove_fluid(update.volume)
|
|
328
343
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
(commands.DispenseResult, commands.DispenseInPlaceResult),
|
|
332
|
-
):
|
|
333
|
-
pipette_id = action.command.params.pipetteId
|
|
334
|
-
previous_volume = self._state.aspirated_volume_by_id[pipette_id] or 0
|
|
335
|
-
# PipetteHandler will have clamped action.command.result.volume for us, so
|
|
336
|
-
# next_volume should always be in bounds.
|
|
337
|
-
next_volume = previous_volume - action.command.result.volume
|
|
338
|
-
self._state.aspirated_volume_by_id[pipette_id] = next_volume
|
|
339
|
-
|
|
340
|
-
elif isinstance(action, SucceedCommandAction) and isinstance(
|
|
341
|
-
action.command.result,
|
|
342
|
-
(
|
|
343
|
-
commands.BlowOutResult,
|
|
344
|
-
commands.BlowOutInPlaceResult,
|
|
345
|
-
commands.unsafe.UnsafeBlowOutInPlaceResult,
|
|
346
|
-
),
|
|
347
|
-
):
|
|
348
|
-
pipette_id = action.command.params.pipetteId
|
|
349
|
-
self._state.aspirated_volume_by_id[pipette_id] = None
|
|
344
|
+
def _update_empty(self, update: update_types.PipetteEmptyFluidUpdate) -> None:
|
|
345
|
+
self._state.pipette_contents_by_id[update.pipette_id] = fluid_stack.FluidStack()
|
|
350
346
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
):
|
|
354
|
-
pipette_id = action.command.params.pipetteId
|
|
355
|
-
self._state.aspirated_volume_by_id[pipette_id] = 0
|
|
347
|
+
def _update_unknown(self, update: update_types.PipetteUnknownFluidUpdate) -> None:
|
|
348
|
+
self._state.pipette_contents_by_id[update.pipette_id] = None
|
|
356
349
|
|
|
350
|
+
def _fluid_stack_log_if_empty(self, pipette_id: str) -> fluid_stack.FluidStack:
|
|
351
|
+
stack = self._state.pipette_contents_by_id[pipette_id]
|
|
352
|
+
if stack is None:
|
|
353
|
+
LOG.error("Pipette state tried to alter an unknown-contents pipette")
|
|
354
|
+
return fluid_stack.FluidStack()
|
|
355
|
+
return stack
|
|
357
356
|
|
|
358
|
-
|
|
357
|
+
|
|
358
|
+
class PipetteView:
|
|
359
359
|
"""Read-only view of computed pipettes state."""
|
|
360
360
|
|
|
361
361
|
_state: PipetteState
|
|
@@ -457,6 +457,10 @@ class PipetteView(HasState[PipetteState]):
|
|
|
457
457
|
def get_aspirated_volume(self, pipette_id: str) -> Optional[float]:
|
|
458
458
|
"""Get the currently aspirated volume of a pipette by ID.
|
|
459
459
|
|
|
460
|
+
This is the volume currently displaced by the plunger relative to its bottom position,
|
|
461
|
+
regardless of whether that volume likely contains liquid or air. This makes it the right
|
|
462
|
+
function to call to know how much more volume the plunger may displace.
|
|
463
|
+
|
|
460
464
|
Returns:
|
|
461
465
|
The volume the pipette has aspirated.
|
|
462
466
|
None, after blow-out and the plunger is in an unsafe position.
|
|
@@ -468,13 +472,50 @@ class PipetteView(HasState[PipetteState]):
|
|
|
468
472
|
self.validate_tip_state(pipette_id, True)
|
|
469
473
|
|
|
470
474
|
try:
|
|
471
|
-
|
|
475
|
+
stack = self._state.pipette_contents_by_id[pipette_id]
|
|
476
|
+
if stack is None:
|
|
477
|
+
return None
|
|
478
|
+
return stack.aspirated_volume()
|
|
472
479
|
|
|
473
480
|
except KeyError as e:
|
|
474
481
|
raise errors.PipetteNotLoadedError(
|
|
475
482
|
f"Pipette {pipette_id} not found; unable to get current volume."
|
|
476
483
|
) from e
|
|
477
484
|
|
|
485
|
+
def get_liquid_dispensed_by_ejecting_volume(
|
|
486
|
+
self, pipette_id: str, volume: float
|
|
487
|
+
) -> Optional[float]:
|
|
488
|
+
"""Get the amount of liquid (not air) that will be dispensed if the pipette ejects a specified volume.
|
|
489
|
+
|
|
490
|
+
For instance, if the pipette contains, in vertical order,
|
|
491
|
+
10 ul air
|
|
492
|
+
80 ul liquid
|
|
493
|
+
5 ul air
|
|
494
|
+
|
|
495
|
+
then dispensing 10ul would result in 5ul of liquid; dispensing 85 ul would result in 80ul liquid; dispensing
|
|
496
|
+
95ul would result in 80ul liquid.
|
|
497
|
+
|
|
498
|
+
Returns:
|
|
499
|
+
The volume of liquid that would be dispensed by the requested volume.
|
|
500
|
+
None, after blow-out or when the plunger is in an unsafe position.
|
|
501
|
+
|
|
502
|
+
Raises:
|
|
503
|
+
PipetteNotLoadedError: pipette ID does not exist.
|
|
504
|
+
TipnotAttachedError: No tip is attached to the pipette.
|
|
505
|
+
"""
|
|
506
|
+
self.validate_tip_state(pipette_id, True)
|
|
507
|
+
|
|
508
|
+
try:
|
|
509
|
+
stack = self._state.pipette_contents_by_id[pipette_id]
|
|
510
|
+
if stack is None:
|
|
511
|
+
return None
|
|
512
|
+
return stack.liquid_part_of_dispense_volume(volume)
|
|
513
|
+
|
|
514
|
+
except KeyError as e:
|
|
515
|
+
raise errors.PipetteNotLoadedError(
|
|
516
|
+
f"Pipette {pipette_id} not found; unable to get current liquid volume."
|
|
517
|
+
) from e
|
|
518
|
+
|
|
478
519
|
def get_working_volume(self, pipette_id: str) -> float:
|
|
479
520
|
"""Get the working maximum volume of a pipette by ID.
|
|
480
521
|
|
|
@@ -641,6 +682,10 @@ class PipetteView(HasState[PipetteState]):
|
|
|
641
682
|
nozzle_map = self._state.nozzle_configuration_by_id[pipette_id]
|
|
642
683
|
return nozzle_map.starting_nozzle
|
|
643
684
|
|
|
685
|
+
def get_nozzle_configuration(self, pipette_id: str) -> NozzleMap:
|
|
686
|
+
"""Get the nozzle map of the pipette."""
|
|
687
|
+
return self._state.nozzle_configuration_by_id[pipette_id]
|
|
688
|
+
|
|
644
689
|
def _get_critical_point_offset_without_tip(
|
|
645
690
|
self, pipette_id: str, critical_point: Optional[CriticalPoint]
|
|
646
691
|
) -> Point:
|
|
@@ -723,6 +768,13 @@ class PipetteView(HasState[PipetteState]):
|
|
|
723
768
|
pip_front_left_bound,
|
|
724
769
|
)
|
|
725
770
|
|
|
771
|
+
def get_pipette_supports_pressure(self, pipette_id: str) -> bool:
|
|
772
|
+
"""Return if this pipette supports a pressure sensor."""
|
|
773
|
+
return (
|
|
774
|
+
"pressure"
|
|
775
|
+
in self._state.static_config_by_id[pipette_id].available_sensors.sensors
|
|
776
|
+
)
|
|
777
|
+
|
|
726
778
|
def get_liquid_presence_detection(self, pipette_id: str) -> bool:
|
|
727
779
|
"""Determine if liquid presence detection is enabled for this pipette."""
|
|
728
780
|
try:
|
|
@@ -731,3 +783,42 @@ class PipetteView(HasState[PipetteState]):
|
|
|
731
783
|
raise errors.PipetteNotLoadedError(
|
|
732
784
|
f"Pipette {pipette_id} not found; unable to determine if pipette liquid presence detection enabled."
|
|
733
785
|
) from e
|
|
786
|
+
|
|
787
|
+
def get_nozzle_configuration_supports_lld(self, pipette_id: str) -> bool:
|
|
788
|
+
"""Determine if the current partial tip configuration supports LLD."""
|
|
789
|
+
nozzle_map = self.get_nozzle_configuration(pipette_id)
|
|
790
|
+
if (
|
|
791
|
+
nozzle_map.physical_nozzle_count == 96
|
|
792
|
+
and nozzle_map.back_left != nozzle_map.full_instrument_back_left
|
|
793
|
+
and nozzle_map.front_right != nozzle_map.full_instrument_front_right
|
|
794
|
+
):
|
|
795
|
+
return False
|
|
796
|
+
return True
|
|
797
|
+
|
|
798
|
+
def lookup_volume_to_mm_conversion(
|
|
799
|
+
self, pipette_id: str, volume: float, action: str
|
|
800
|
+
) -> float:
|
|
801
|
+
"""Get the volumn to mm conversion for a pipette."""
|
|
802
|
+
try:
|
|
803
|
+
lookup_volume = self.get_working_volume(pipette_id)
|
|
804
|
+
except errors.TipNotAttachedError:
|
|
805
|
+
lookup_volume = self.get_maximum_volume(pipette_id)
|
|
806
|
+
|
|
807
|
+
pipette_config = self.get_config(pipette_id)
|
|
808
|
+
lookup_table_from_config = pipette_config.tip_configuration_lookup_table
|
|
809
|
+
try:
|
|
810
|
+
tip_settings = lookup_table_from_config[lookup_volume]
|
|
811
|
+
except KeyError:
|
|
812
|
+
tip_settings = list(lookup_table_from_config.values())[0]
|
|
813
|
+
return calculate_ul_per_mm(
|
|
814
|
+
volume,
|
|
815
|
+
cast(UlPerMmAction, action),
|
|
816
|
+
tip_settings,
|
|
817
|
+
shaft_ul_per_mm=pipette_config.shaft_ul_per_mm,
|
|
818
|
+
)
|
|
819
|
+
|
|
820
|
+
def lookup_plunger_position_name(
|
|
821
|
+
self, pipette_id: str, position_name: str
|
|
822
|
+
) -> float:
|
|
823
|
+
"""Get the plunger position provided for the given pipette id."""
|
|
824
|
+
return self.get_config(pipette_id).plunger_positions[position_name]
|
|
@@ -9,7 +9,7 @@ from opentrons_shared_data.deck.types import DeckDefinitionV5
|
|
|
9
9
|
from opentrons_shared_data.robot.types import RobotDefinition
|
|
10
10
|
|
|
11
11
|
from opentrons.protocol_engine.error_recovery_policy import ErrorRecoveryPolicy
|
|
12
|
-
from opentrons.protocol_engine.types import ModuleOffsetData
|
|
12
|
+
from opentrons.protocol_engine.types import LiquidClassRecordWithId, ModuleOffsetData
|
|
13
13
|
from opentrons.util.change_notifier import ChangeNotifier
|
|
14
14
|
|
|
15
15
|
from ..resources import DeckFixedLabware
|
|
@@ -25,6 +25,7 @@ from .labware import LabwareState, LabwareStore, LabwareView
|
|
|
25
25
|
from .pipettes import PipetteState, PipetteStore, PipetteView
|
|
26
26
|
from .modules import ModuleState, ModuleStore, ModuleView
|
|
27
27
|
from .liquids import LiquidState, LiquidView, LiquidStore
|
|
28
|
+
from .liquid_classes import LiquidClassState, LiquidClassStore, LiquidClassView
|
|
28
29
|
from .tips import TipState, TipView, TipStore
|
|
29
30
|
from .wells import WellState, WellView, WellStore
|
|
30
31
|
from .geometry import GeometryView
|
|
@@ -49,6 +50,7 @@ class State:
|
|
|
49
50
|
pipettes: PipetteState
|
|
50
51
|
modules: ModuleState
|
|
51
52
|
liquids: LiquidState
|
|
53
|
+
liquid_classes: LiquidClassState
|
|
52
54
|
tips: TipState
|
|
53
55
|
wells: WellState
|
|
54
56
|
files: FileState
|
|
@@ -64,6 +66,7 @@ class StateView(HasState[State]):
|
|
|
64
66
|
_pipettes: PipetteView
|
|
65
67
|
_modules: ModuleView
|
|
66
68
|
_liquid: LiquidView
|
|
69
|
+
_liquid_classes: LiquidClassView
|
|
67
70
|
_tips: TipView
|
|
68
71
|
_wells: WellView
|
|
69
72
|
_geometry: GeometryView
|
|
@@ -101,6 +104,11 @@ class StateView(HasState[State]):
|
|
|
101
104
|
"""Get state view selectors for liquid state."""
|
|
102
105
|
return self._liquid
|
|
103
106
|
|
|
107
|
+
@property
|
|
108
|
+
def liquid_classes(self) -> LiquidClassView:
|
|
109
|
+
"""Get state view selectors for liquid class state."""
|
|
110
|
+
return self._liquid_classes
|
|
111
|
+
|
|
104
112
|
@property
|
|
105
113
|
def tips(self) -> TipView:
|
|
106
114
|
"""Get state view selectors for tip state."""
|
|
@@ -135,7 +143,7 @@ class StateView(HasState[State]):
|
|
|
135
143
|
"""Get protocol run data."""
|
|
136
144
|
error = self._commands.get_error()
|
|
137
145
|
# TODO maybe add summary here for AA
|
|
138
|
-
return StateSummary.
|
|
146
|
+
return StateSummary.model_construct(
|
|
139
147
|
status=self._commands.get_status(),
|
|
140
148
|
errors=[] if error is None else [error],
|
|
141
149
|
pipettes=self._pipettes.get_all(),
|
|
@@ -148,6 +156,12 @@ class StateView(HasState[State]):
|
|
|
148
156
|
wells=self._wells.get_all(),
|
|
149
157
|
hasEverEnteredErrorRecovery=self._commands.get_has_entered_recovery_mode(),
|
|
150
158
|
files=self._state.files.file_ids,
|
|
159
|
+
liquidClasses=[
|
|
160
|
+
LiquidClassRecordWithId(
|
|
161
|
+
liquidClassId=liquid_class_id, **dict(liquid_class_record)
|
|
162
|
+
)
|
|
163
|
+
for liquid_class_id, liquid_class_record in self._liquid_classes.get_all().items()
|
|
164
|
+
],
|
|
151
165
|
)
|
|
152
166
|
|
|
153
167
|
|
|
@@ -213,6 +227,7 @@ class StateStore(StateView, ActionHandler):
|
|
|
213
227
|
module_calibration_offsets=module_calibration_offsets,
|
|
214
228
|
)
|
|
215
229
|
self._liquid_store = LiquidStore()
|
|
230
|
+
self._liquid_class_store = LiquidClassStore()
|
|
216
231
|
self._tip_store = TipStore()
|
|
217
232
|
self._well_store = WellStore()
|
|
218
233
|
self._file_store = FileStore()
|
|
@@ -224,6 +239,7 @@ class StateStore(StateView, ActionHandler):
|
|
|
224
239
|
self._labware_store,
|
|
225
240
|
self._module_store,
|
|
226
241
|
self._liquid_store,
|
|
242
|
+
self._liquid_class_store,
|
|
227
243
|
self._tip_store,
|
|
228
244
|
self._well_store,
|
|
229
245
|
self._file_store,
|
|
@@ -342,6 +358,7 @@ class StateStore(StateView, ActionHandler):
|
|
|
342
358
|
pipettes=self._pipette_store.state,
|
|
343
359
|
modules=self._module_store.state,
|
|
344
360
|
liquids=self._liquid_store.state,
|
|
361
|
+
liquid_classes=self._liquid_class_store.state,
|
|
345
362
|
tips=self._tip_store.state,
|
|
346
363
|
wells=self._well_store.state,
|
|
347
364
|
files=self._file_store.state,
|
|
@@ -359,6 +376,7 @@ class StateStore(StateView, ActionHandler):
|
|
|
359
376
|
self._pipettes = PipetteView(state.pipettes)
|
|
360
377
|
self._modules = ModuleView(state.modules)
|
|
361
378
|
self._liquid = LiquidView(state.liquids)
|
|
379
|
+
self._liquid_classes = LiquidClassView(state.liquid_classes)
|
|
362
380
|
self._tips = TipView(state.tips)
|
|
363
381
|
self._wells = WellView(state.wells)
|
|
364
382
|
self._files = FileView(state.files)
|
|
@@ -391,6 +409,7 @@ class StateStore(StateView, ActionHandler):
|
|
|
391
409
|
self._pipettes._state = next_state.pipettes
|
|
392
410
|
self._modules._state = next_state.modules
|
|
393
411
|
self._liquid._state = next_state.liquids
|
|
412
|
+
self._liquid_classes._state = next_state.liquid_classes
|
|
394
413
|
self._tips._state = next_state.tips
|
|
395
414
|
self._wells._state = next_state.wells
|
|
396
415
|
self._change_notifier.notify()
|