opentrons 8.6.0__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/__init__.py +150 -0
- opentrons/_version.py +34 -0
- opentrons/calibration_storage/__init__.py +54 -0
- opentrons/calibration_storage/deck_configuration.py +62 -0
- opentrons/calibration_storage/encoder_decoder.py +31 -0
- opentrons/calibration_storage/file_operators.py +142 -0
- opentrons/calibration_storage/helpers.py +103 -0
- opentrons/calibration_storage/ot2/__init__.py +34 -0
- opentrons/calibration_storage/ot2/deck_attitude.py +85 -0
- opentrons/calibration_storage/ot2/mark_bad_calibration.py +27 -0
- opentrons/calibration_storage/ot2/models/__init__.py +0 -0
- opentrons/calibration_storage/ot2/models/v1.py +149 -0
- opentrons/calibration_storage/ot2/pipette_offset.py +129 -0
- opentrons/calibration_storage/ot2/tip_length.py +281 -0
- opentrons/calibration_storage/ot3/__init__.py +31 -0
- opentrons/calibration_storage/ot3/deck_attitude.py +83 -0
- opentrons/calibration_storage/ot3/gripper_offset.py +156 -0
- opentrons/calibration_storage/ot3/models/__init__.py +0 -0
- opentrons/calibration_storage/ot3/models/v1.py +122 -0
- opentrons/calibration_storage/ot3/module_offset.py +138 -0
- opentrons/calibration_storage/ot3/pipette_offset.py +95 -0
- opentrons/calibration_storage/types.py +45 -0
- opentrons/cli/__init__.py +21 -0
- opentrons/cli/__main__.py +5 -0
- opentrons/cli/analyze.py +557 -0
- opentrons/config/__init__.py +631 -0
- opentrons/config/advanced_settings.py +871 -0
- opentrons/config/defaults_ot2.py +214 -0
- opentrons/config/defaults_ot3.py +499 -0
- opentrons/config/feature_flags.py +86 -0
- opentrons/config/gripper_config.py +55 -0
- opentrons/config/reset.py +203 -0
- opentrons/config/robot_configs.py +187 -0
- opentrons/config/types.py +183 -0
- opentrons/drivers/__init__.py +0 -0
- opentrons/drivers/absorbance_reader/__init__.py +11 -0
- opentrons/drivers/absorbance_reader/abstract.py +72 -0
- opentrons/drivers/absorbance_reader/async_byonoy.py +352 -0
- opentrons/drivers/absorbance_reader/driver.py +81 -0
- opentrons/drivers/absorbance_reader/hid_protocol.py +161 -0
- opentrons/drivers/absorbance_reader/simulator.py +84 -0
- opentrons/drivers/asyncio/__init__.py +0 -0
- opentrons/drivers/asyncio/communication/__init__.py +22 -0
- opentrons/drivers/asyncio/communication/async_serial.py +187 -0
- opentrons/drivers/asyncio/communication/errors.py +88 -0
- opentrons/drivers/asyncio/communication/serial_connection.py +557 -0
- opentrons/drivers/command_builder.py +102 -0
- opentrons/drivers/flex_stacker/__init__.py +13 -0
- opentrons/drivers/flex_stacker/abstract.py +214 -0
- opentrons/drivers/flex_stacker/driver.py +768 -0
- opentrons/drivers/flex_stacker/errors.py +68 -0
- opentrons/drivers/flex_stacker/simulator.py +309 -0
- opentrons/drivers/flex_stacker/types.py +367 -0
- opentrons/drivers/flex_stacker/utils.py +19 -0
- opentrons/drivers/heater_shaker/__init__.py +5 -0
- opentrons/drivers/heater_shaker/abstract.py +76 -0
- opentrons/drivers/heater_shaker/driver.py +204 -0
- opentrons/drivers/heater_shaker/simulator.py +94 -0
- opentrons/drivers/mag_deck/__init__.py +6 -0
- opentrons/drivers/mag_deck/abstract.py +44 -0
- opentrons/drivers/mag_deck/driver.py +208 -0
- opentrons/drivers/mag_deck/simulator.py +63 -0
- opentrons/drivers/rpi_drivers/__init__.py +33 -0
- opentrons/drivers/rpi_drivers/dev_types.py +94 -0
- opentrons/drivers/rpi_drivers/gpio.py +282 -0
- opentrons/drivers/rpi_drivers/gpio_simulator.py +127 -0
- opentrons/drivers/rpi_drivers/interfaces.py +15 -0
- opentrons/drivers/rpi_drivers/types.py +364 -0
- opentrons/drivers/rpi_drivers/usb.py +102 -0
- opentrons/drivers/rpi_drivers/usb_simulator.py +22 -0
- opentrons/drivers/serial_communication.py +151 -0
- opentrons/drivers/smoothie_drivers/__init__.py +4 -0
- opentrons/drivers/smoothie_drivers/connection.py +51 -0
- opentrons/drivers/smoothie_drivers/constants.py +121 -0
- opentrons/drivers/smoothie_drivers/driver_3_0.py +1933 -0
- opentrons/drivers/smoothie_drivers/errors.py +49 -0
- opentrons/drivers/smoothie_drivers/parse_utils.py +143 -0
- opentrons/drivers/smoothie_drivers/simulator.py +99 -0
- opentrons/drivers/smoothie_drivers/types.py +16 -0
- opentrons/drivers/temp_deck/__init__.py +10 -0
- opentrons/drivers/temp_deck/abstract.py +54 -0
- opentrons/drivers/temp_deck/driver.py +197 -0
- opentrons/drivers/temp_deck/simulator.py +57 -0
- opentrons/drivers/thermocycler/__init__.py +12 -0
- opentrons/drivers/thermocycler/abstract.py +99 -0
- opentrons/drivers/thermocycler/driver.py +395 -0
- opentrons/drivers/thermocycler/simulator.py +126 -0
- opentrons/drivers/types.py +107 -0
- opentrons/drivers/utils.py +222 -0
- opentrons/execute.py +742 -0
- opentrons/hardware_control/__init__.py +65 -0
- opentrons/hardware_control/__main__.py +77 -0
- opentrons/hardware_control/adapters.py +98 -0
- opentrons/hardware_control/api.py +1347 -0
- opentrons/hardware_control/backends/__init__.py +7 -0
- opentrons/hardware_control/backends/controller.py +400 -0
- opentrons/hardware_control/backends/errors.py +9 -0
- opentrons/hardware_control/backends/estop_state.py +164 -0
- opentrons/hardware_control/backends/flex_protocol.py +497 -0
- opentrons/hardware_control/backends/ot3controller.py +1930 -0
- opentrons/hardware_control/backends/ot3simulator.py +900 -0
- opentrons/hardware_control/backends/ot3utils.py +664 -0
- opentrons/hardware_control/backends/simulator.py +442 -0
- opentrons/hardware_control/backends/status_bar_state.py +240 -0
- opentrons/hardware_control/backends/subsystem_manager.py +431 -0
- opentrons/hardware_control/backends/tip_presence_manager.py +173 -0
- opentrons/hardware_control/backends/types.py +14 -0
- opentrons/hardware_control/constants.py +6 -0
- opentrons/hardware_control/dev_types.py +125 -0
- opentrons/hardware_control/emulation/__init__.py +0 -0
- opentrons/hardware_control/emulation/abstract_emulator.py +21 -0
- opentrons/hardware_control/emulation/app.py +56 -0
- opentrons/hardware_control/emulation/connection_handler.py +38 -0
- opentrons/hardware_control/emulation/heater_shaker.py +150 -0
- opentrons/hardware_control/emulation/magdeck.py +60 -0
- opentrons/hardware_control/emulation/module_server/__init__.py +8 -0
- opentrons/hardware_control/emulation/module_server/client.py +78 -0
- opentrons/hardware_control/emulation/module_server/helpers.py +130 -0
- opentrons/hardware_control/emulation/module_server/models.py +31 -0
- opentrons/hardware_control/emulation/module_server/server.py +110 -0
- opentrons/hardware_control/emulation/parser.py +74 -0
- opentrons/hardware_control/emulation/proxy.py +241 -0
- opentrons/hardware_control/emulation/run_emulator.py +68 -0
- opentrons/hardware_control/emulation/scripts/__init__.py +0 -0
- opentrons/hardware_control/emulation/scripts/run_app.py +54 -0
- opentrons/hardware_control/emulation/scripts/run_module_emulator.py +72 -0
- opentrons/hardware_control/emulation/scripts/run_smoothie.py +37 -0
- opentrons/hardware_control/emulation/settings.py +119 -0
- opentrons/hardware_control/emulation/simulations.py +133 -0
- opentrons/hardware_control/emulation/smoothie.py +192 -0
- opentrons/hardware_control/emulation/tempdeck.py +69 -0
- opentrons/hardware_control/emulation/thermocycler.py +128 -0
- opentrons/hardware_control/emulation/types.py +10 -0
- opentrons/hardware_control/emulation/util.py +38 -0
- opentrons/hardware_control/errors.py +43 -0
- opentrons/hardware_control/execution_manager.py +164 -0
- opentrons/hardware_control/instruments/__init__.py +5 -0
- opentrons/hardware_control/instruments/instrument_abc.py +39 -0
- opentrons/hardware_control/instruments/ot2/__init__.py +0 -0
- opentrons/hardware_control/instruments/ot2/instrument_calibration.py +152 -0
- opentrons/hardware_control/instruments/ot2/pipette.py +777 -0
- opentrons/hardware_control/instruments/ot2/pipette_handler.py +995 -0
- opentrons/hardware_control/instruments/ot3/__init__.py +0 -0
- opentrons/hardware_control/instruments/ot3/gripper.py +420 -0
- opentrons/hardware_control/instruments/ot3/gripper_handler.py +173 -0
- opentrons/hardware_control/instruments/ot3/instrument_calibration.py +214 -0
- opentrons/hardware_control/instruments/ot3/pipette.py +858 -0
- opentrons/hardware_control/instruments/ot3/pipette_handler.py +1030 -0
- opentrons/hardware_control/module_control.py +332 -0
- opentrons/hardware_control/modules/__init__.py +69 -0
- opentrons/hardware_control/modules/absorbance_reader.py +373 -0
- opentrons/hardware_control/modules/errors.py +7 -0
- opentrons/hardware_control/modules/flex_stacker.py +948 -0
- opentrons/hardware_control/modules/heater_shaker.py +426 -0
- opentrons/hardware_control/modules/lid_temp_status.py +35 -0
- opentrons/hardware_control/modules/magdeck.py +233 -0
- opentrons/hardware_control/modules/mod_abc.py +245 -0
- opentrons/hardware_control/modules/module_calibration.py +93 -0
- opentrons/hardware_control/modules/plate_temp_status.py +61 -0
- opentrons/hardware_control/modules/tempdeck.py +299 -0
- opentrons/hardware_control/modules/thermocycler.py +731 -0
- opentrons/hardware_control/modules/types.py +417 -0
- opentrons/hardware_control/modules/update.py +255 -0
- opentrons/hardware_control/modules/utils.py +73 -0
- opentrons/hardware_control/motion_utilities.py +318 -0
- opentrons/hardware_control/nozzle_manager.py +422 -0
- opentrons/hardware_control/ot3_calibration.py +1171 -0
- opentrons/hardware_control/ot3api.py +3227 -0
- opentrons/hardware_control/pause_manager.py +31 -0
- opentrons/hardware_control/poller.py +112 -0
- opentrons/hardware_control/protocols/__init__.py +106 -0
- opentrons/hardware_control/protocols/asyncio_configurable.py +11 -0
- opentrons/hardware_control/protocols/calibratable.py +45 -0
- opentrons/hardware_control/protocols/chassis_accessory_manager.py +90 -0
- opentrons/hardware_control/protocols/configurable.py +48 -0
- opentrons/hardware_control/protocols/event_sourcer.py +18 -0
- opentrons/hardware_control/protocols/execution_controllable.py +33 -0
- opentrons/hardware_control/protocols/flex_calibratable.py +96 -0
- opentrons/hardware_control/protocols/flex_instrument_configurer.py +52 -0
- opentrons/hardware_control/protocols/gripper_controller.py +55 -0
- opentrons/hardware_control/protocols/hardware_manager.py +51 -0
- opentrons/hardware_control/protocols/identifiable.py +16 -0
- opentrons/hardware_control/protocols/instrument_configurer.py +206 -0
- opentrons/hardware_control/protocols/liquid_handler.py +266 -0
- opentrons/hardware_control/protocols/module_provider.py +16 -0
- opentrons/hardware_control/protocols/motion_controller.py +243 -0
- opentrons/hardware_control/protocols/position_estimator.py +45 -0
- opentrons/hardware_control/protocols/simulatable.py +10 -0
- opentrons/hardware_control/protocols/stoppable.py +9 -0
- opentrons/hardware_control/protocols/types.py +27 -0
- opentrons/hardware_control/robot_calibration.py +224 -0
- opentrons/hardware_control/scripts/README.md +28 -0
- opentrons/hardware_control/scripts/__init__.py +1 -0
- opentrons/hardware_control/scripts/gripper_control.py +208 -0
- opentrons/hardware_control/scripts/ot3gripper +7 -0
- opentrons/hardware_control/scripts/ot3repl +7 -0
- opentrons/hardware_control/scripts/repl.py +187 -0
- opentrons/hardware_control/scripts/tc_control.py +97 -0
- opentrons/hardware_control/scripts/update_module_fw.py +274 -0
- opentrons/hardware_control/simulator_setup.py +260 -0
- opentrons/hardware_control/thread_manager.py +431 -0
- opentrons/hardware_control/threaded_async_lock.py +97 -0
- opentrons/hardware_control/types.py +792 -0
- opentrons/hardware_control/util.py +234 -0
- opentrons/legacy_broker.py +53 -0
- opentrons/legacy_commands/__init__.py +1 -0
- opentrons/legacy_commands/commands.py +483 -0
- opentrons/legacy_commands/helpers.py +153 -0
- opentrons/legacy_commands/module_commands.py +276 -0
- opentrons/legacy_commands/protocol_commands.py +54 -0
- opentrons/legacy_commands/publisher.py +155 -0
- opentrons/legacy_commands/robot_commands.py +51 -0
- opentrons/legacy_commands/types.py +1186 -0
- opentrons/motion_planning/__init__.py +32 -0
- opentrons/motion_planning/adjacent_slots_getters.py +168 -0
- opentrons/motion_planning/deck_conflict.py +501 -0
- opentrons/motion_planning/errors.py +35 -0
- opentrons/motion_planning/types.py +42 -0
- opentrons/motion_planning/waypoints.py +218 -0
- opentrons/ordered_set.py +138 -0
- opentrons/protocol_api/__init__.py +105 -0
- opentrons/protocol_api/_liquid.py +157 -0
- opentrons/protocol_api/_liquid_properties.py +814 -0
- opentrons/protocol_api/_nozzle_layout.py +31 -0
- opentrons/protocol_api/_parameter_context.py +300 -0
- opentrons/protocol_api/_parameters.py +31 -0
- opentrons/protocol_api/_transfer_liquid_validation.py +108 -0
- opentrons/protocol_api/_types.py +43 -0
- opentrons/protocol_api/config.py +23 -0
- opentrons/protocol_api/core/__init__.py +23 -0
- opentrons/protocol_api/core/common.py +33 -0
- opentrons/protocol_api/core/core_map.py +74 -0
- opentrons/protocol_api/core/engine/__init__.py +22 -0
- opentrons/protocol_api/core/engine/_default_labware_versions.py +179 -0
- opentrons/protocol_api/core/engine/deck_conflict.py +400 -0
- opentrons/protocol_api/core/engine/exceptions.py +19 -0
- opentrons/protocol_api/core/engine/instrument.py +2391 -0
- opentrons/protocol_api/core/engine/labware.py +238 -0
- opentrons/protocol_api/core/engine/load_labware_params.py +73 -0
- opentrons/protocol_api/core/engine/module_core.py +1027 -0
- opentrons/protocol_api/core/engine/overlap_versions.py +20 -0
- opentrons/protocol_api/core/engine/pipette_movement_conflict.py +358 -0
- opentrons/protocol_api/core/engine/point_calculations.py +64 -0
- opentrons/protocol_api/core/engine/protocol.py +1153 -0
- opentrons/protocol_api/core/engine/robot.py +139 -0
- opentrons/protocol_api/core/engine/stringify.py +74 -0
- opentrons/protocol_api/core/engine/transfer_components_executor.py +1006 -0
- opentrons/protocol_api/core/engine/well.py +241 -0
- opentrons/protocol_api/core/instrument.py +459 -0
- opentrons/protocol_api/core/labware.py +151 -0
- opentrons/protocol_api/core/legacy/__init__.py +11 -0
- opentrons/protocol_api/core/legacy/_labware_geometry.py +37 -0
- opentrons/protocol_api/core/legacy/deck.py +369 -0
- opentrons/protocol_api/core/legacy/labware_offset_provider.py +108 -0
- opentrons/protocol_api/core/legacy/legacy_instrument_core.py +709 -0
- opentrons/protocol_api/core/legacy/legacy_labware_core.py +235 -0
- opentrons/protocol_api/core/legacy/legacy_module_core.py +592 -0
- opentrons/protocol_api/core/legacy/legacy_protocol_core.py +612 -0
- opentrons/protocol_api/core/legacy/legacy_well_core.py +162 -0
- opentrons/protocol_api/core/legacy/load_info.py +67 -0
- opentrons/protocol_api/core/legacy/module_geometry.py +547 -0
- opentrons/protocol_api/core/legacy/well_geometry.py +148 -0
- opentrons/protocol_api/core/legacy_simulator/__init__.py +16 -0
- opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +624 -0
- opentrons/protocol_api/core/legacy_simulator/legacy_protocol_core.py +85 -0
- opentrons/protocol_api/core/module.py +484 -0
- opentrons/protocol_api/core/protocol.py +311 -0
- opentrons/protocol_api/core/robot.py +51 -0
- opentrons/protocol_api/core/well.py +116 -0
- opentrons/protocol_api/core/well_grid.py +45 -0
- opentrons/protocol_api/create_protocol_context.py +177 -0
- opentrons/protocol_api/deck.py +223 -0
- opentrons/protocol_api/disposal_locations.py +244 -0
- opentrons/protocol_api/instrument_context.py +3272 -0
- opentrons/protocol_api/labware.py +1579 -0
- opentrons/protocol_api/module_contexts.py +1447 -0
- opentrons/protocol_api/module_validation_and_errors.py +61 -0
- opentrons/protocol_api/protocol_context.py +1688 -0
- opentrons/protocol_api/robot_context.py +303 -0
- opentrons/protocol_api/validation.py +761 -0
- opentrons/protocol_engine/__init__.py +155 -0
- opentrons/protocol_engine/actions/__init__.py +65 -0
- opentrons/protocol_engine/actions/action_dispatcher.py +30 -0
- opentrons/protocol_engine/actions/action_handler.py +13 -0
- opentrons/protocol_engine/actions/actions.py +302 -0
- opentrons/protocol_engine/actions/get_state_update.py +38 -0
- opentrons/protocol_engine/clients/__init__.py +5 -0
- opentrons/protocol_engine/clients/sync_client.py +174 -0
- opentrons/protocol_engine/clients/transports.py +197 -0
- opentrons/protocol_engine/commands/__init__.py +757 -0
- opentrons/protocol_engine/commands/absorbance_reader/__init__.py +61 -0
- opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +154 -0
- opentrons/protocol_engine/commands/absorbance_reader/common.py +6 -0
- opentrons/protocol_engine/commands/absorbance_reader/initialize.py +151 -0
- opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +154 -0
- opentrons/protocol_engine/commands/absorbance_reader/read.py +226 -0
- opentrons/protocol_engine/commands/air_gap_in_place.py +162 -0
- opentrons/protocol_engine/commands/aspirate.py +244 -0
- opentrons/protocol_engine/commands/aspirate_in_place.py +184 -0
- opentrons/protocol_engine/commands/aspirate_while_tracking.py +211 -0
- opentrons/protocol_engine/commands/blow_out.py +146 -0
- opentrons/protocol_engine/commands/blow_out_in_place.py +119 -0
- opentrons/protocol_engine/commands/calibration/__init__.py +60 -0
- opentrons/protocol_engine/commands/calibration/calibrate_gripper.py +166 -0
- opentrons/protocol_engine/commands/calibration/calibrate_module.py +117 -0
- opentrons/protocol_engine/commands/calibration/calibrate_pipette.py +96 -0
- opentrons/protocol_engine/commands/calibration/move_to_maintenance_position.py +156 -0
- opentrons/protocol_engine/commands/command.py +308 -0
- opentrons/protocol_engine/commands/command_unions.py +974 -0
- opentrons/protocol_engine/commands/comment.py +57 -0
- opentrons/protocol_engine/commands/configure_for_volume.py +108 -0
- opentrons/protocol_engine/commands/configure_nozzle_layout.py +115 -0
- opentrons/protocol_engine/commands/custom.py +67 -0
- opentrons/protocol_engine/commands/dispense.py +194 -0
- opentrons/protocol_engine/commands/dispense_in_place.py +179 -0
- opentrons/protocol_engine/commands/dispense_while_tracking.py +204 -0
- opentrons/protocol_engine/commands/drop_tip.py +232 -0
- opentrons/protocol_engine/commands/drop_tip_in_place.py +205 -0
- opentrons/protocol_engine/commands/flex_stacker/__init__.py +64 -0
- opentrons/protocol_engine/commands/flex_stacker/common.py +900 -0
- opentrons/protocol_engine/commands/flex_stacker/empty.py +293 -0
- opentrons/protocol_engine/commands/flex_stacker/fill.py +281 -0
- opentrons/protocol_engine/commands/flex_stacker/retrieve.py +339 -0
- opentrons/protocol_engine/commands/flex_stacker/set_stored_labware.py +328 -0
- opentrons/protocol_engine/commands/flex_stacker/store.py +339 -0
- opentrons/protocol_engine/commands/generate_command_schema.py +61 -0
- opentrons/protocol_engine/commands/get_next_tip.py +134 -0
- opentrons/protocol_engine/commands/get_tip_presence.py +87 -0
- opentrons/protocol_engine/commands/hash_command_params.py +38 -0
- opentrons/protocol_engine/commands/heater_shaker/__init__.py +102 -0
- opentrons/protocol_engine/commands/heater_shaker/close_labware_latch.py +83 -0
- opentrons/protocol_engine/commands/heater_shaker/deactivate_heater.py +82 -0
- opentrons/protocol_engine/commands/heater_shaker/deactivate_shaker.py +84 -0
- opentrons/protocol_engine/commands/heater_shaker/open_labware_latch.py +110 -0
- opentrons/protocol_engine/commands/heater_shaker/set_and_wait_for_shake_speed.py +125 -0
- opentrons/protocol_engine/commands/heater_shaker/set_target_temperature.py +90 -0
- opentrons/protocol_engine/commands/heater_shaker/wait_for_temperature.py +102 -0
- opentrons/protocol_engine/commands/home.py +100 -0
- opentrons/protocol_engine/commands/identify_module.py +86 -0
- opentrons/protocol_engine/commands/labware_handling_common.py +29 -0
- opentrons/protocol_engine/commands/liquid_probe.py +464 -0
- opentrons/protocol_engine/commands/load_labware.py +210 -0
- opentrons/protocol_engine/commands/load_lid.py +154 -0
- opentrons/protocol_engine/commands/load_lid_stack.py +272 -0
- opentrons/protocol_engine/commands/load_liquid.py +95 -0
- opentrons/protocol_engine/commands/load_liquid_class.py +144 -0
- opentrons/protocol_engine/commands/load_module.py +223 -0
- opentrons/protocol_engine/commands/load_pipette.py +167 -0
- opentrons/protocol_engine/commands/magnetic_module/__init__.py +32 -0
- opentrons/protocol_engine/commands/magnetic_module/disengage.py +97 -0
- opentrons/protocol_engine/commands/magnetic_module/engage.py +119 -0
- opentrons/protocol_engine/commands/move_labware.py +546 -0
- opentrons/protocol_engine/commands/move_relative.py +102 -0
- opentrons/protocol_engine/commands/move_to_addressable_area.py +176 -0
- opentrons/protocol_engine/commands/move_to_addressable_area_for_drop_tip.py +198 -0
- opentrons/protocol_engine/commands/move_to_coordinates.py +107 -0
- opentrons/protocol_engine/commands/move_to_well.py +119 -0
- opentrons/protocol_engine/commands/movement_common.py +338 -0
- opentrons/protocol_engine/commands/pick_up_tip.py +241 -0
- opentrons/protocol_engine/commands/pipetting_common.py +443 -0
- opentrons/protocol_engine/commands/prepare_to_aspirate.py +121 -0
- opentrons/protocol_engine/commands/pressure_dispense.py +155 -0
- opentrons/protocol_engine/commands/reload_labware.py +90 -0
- opentrons/protocol_engine/commands/retract_axis.py +75 -0
- opentrons/protocol_engine/commands/robot/__init__.py +70 -0
- opentrons/protocol_engine/commands/robot/close_gripper_jaw.py +96 -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 +86 -0
- opentrons/protocol_engine/commands/save_position.py +109 -0
- opentrons/protocol_engine/commands/seal_pipette_to_tip.py +353 -0
- opentrons/protocol_engine/commands/set_rail_lights.py +67 -0
- opentrons/protocol_engine/commands/set_status_bar.py +89 -0
- opentrons/protocol_engine/commands/temperature_module/__init__.py +46 -0
- opentrons/protocol_engine/commands/temperature_module/deactivate.py +86 -0
- opentrons/protocol_engine/commands/temperature_module/set_target_temperature.py +97 -0
- opentrons/protocol_engine/commands/temperature_module/wait_for_temperature.py +104 -0
- opentrons/protocol_engine/commands/thermocycler/__init__.py +152 -0
- opentrons/protocol_engine/commands/thermocycler/close_lid.py +87 -0
- opentrons/protocol_engine/commands/thermocycler/deactivate_block.py +80 -0
- opentrons/protocol_engine/commands/thermocycler/deactivate_lid.py +80 -0
- opentrons/protocol_engine/commands/thermocycler/open_lid.py +87 -0
- opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py +171 -0
- opentrons/protocol_engine/commands/thermocycler/run_profile.py +124 -0
- opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py +140 -0
- opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py +100 -0
- opentrons/protocol_engine/commands/thermocycler/wait_for_block_temperature.py +93 -0
- opentrons/protocol_engine/commands/thermocycler/wait_for_lid_temperature.py +89 -0
- opentrons/protocol_engine/commands/touch_tip.py +189 -0
- opentrons/protocol_engine/commands/unsafe/__init__.py +161 -0
- opentrons/protocol_engine/commands/unsafe/unsafe_blow_out_in_place.py +100 -0
- opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +121 -0
- opentrons/protocol_engine/commands/unsafe/unsafe_engage_axes.py +82 -0
- opentrons/protocol_engine/commands/unsafe/unsafe_place_labware.py +208 -0
- opentrons/protocol_engine/commands/unsafe/unsafe_stacker_close_latch.py +94 -0
- opentrons/protocol_engine/commands/unsafe/unsafe_stacker_manual_retrieve.py +295 -0
- opentrons/protocol_engine/commands/unsafe/unsafe_stacker_open_latch.py +91 -0
- opentrons/protocol_engine/commands/unsafe/unsafe_stacker_prepare_shuttle.py +136 -0
- opentrons/protocol_engine/commands/unsafe/unsafe_ungrip_labware.py +77 -0
- opentrons/protocol_engine/commands/unsafe/update_position_estimators.py +90 -0
- opentrons/protocol_engine/commands/unseal_pipette_from_tip.py +153 -0
- opentrons/protocol_engine/commands/verify_tip_presence.py +100 -0
- opentrons/protocol_engine/commands/wait_for_duration.py +76 -0
- opentrons/protocol_engine/commands/wait_for_resume.py +75 -0
- opentrons/protocol_engine/create_protocol_engine.py +193 -0
- opentrons/protocol_engine/engine_support.py +28 -0
- opentrons/protocol_engine/error_recovery_policy.py +81 -0
- opentrons/protocol_engine/errors/__init__.py +191 -0
- opentrons/protocol_engine/errors/error_occurrence.py +182 -0
- opentrons/protocol_engine/errors/exceptions.py +1308 -0
- opentrons/protocol_engine/execution/__init__.py +50 -0
- opentrons/protocol_engine/execution/command_executor.py +216 -0
- opentrons/protocol_engine/execution/create_queue_worker.py +102 -0
- opentrons/protocol_engine/execution/door_watcher.py +119 -0
- opentrons/protocol_engine/execution/equipment.py +819 -0
- opentrons/protocol_engine/execution/error_recovery_hardware_state_synchronizer.py +101 -0
- opentrons/protocol_engine/execution/gantry_mover.py +686 -0
- opentrons/protocol_engine/execution/hardware_stopper.py +147 -0
- opentrons/protocol_engine/execution/heater_shaker_movement_flagger.py +207 -0
- opentrons/protocol_engine/execution/labware_movement.py +297 -0
- opentrons/protocol_engine/execution/movement.py +350 -0
- opentrons/protocol_engine/execution/pipetting.py +607 -0
- opentrons/protocol_engine/execution/queue_worker.py +86 -0
- opentrons/protocol_engine/execution/rail_lights.py +25 -0
- opentrons/protocol_engine/execution/run_control.py +33 -0
- opentrons/protocol_engine/execution/status_bar.py +34 -0
- opentrons/protocol_engine/execution/thermocycler_movement_flagger.py +188 -0
- opentrons/protocol_engine/execution/thermocycler_plate_lifter.py +81 -0
- opentrons/protocol_engine/execution/tip_handler.py +550 -0
- opentrons/protocol_engine/labware_offset_standardization.py +194 -0
- opentrons/protocol_engine/notes/__init__.py +17 -0
- opentrons/protocol_engine/notes/notes.py +59 -0
- opentrons/protocol_engine/plugins.py +104 -0
- opentrons/protocol_engine/protocol_engine.py +683 -0
- opentrons/protocol_engine/resources/__init__.py +26 -0
- opentrons/protocol_engine/resources/deck_configuration_provider.py +232 -0
- opentrons/protocol_engine/resources/deck_data_provider.py +94 -0
- opentrons/protocol_engine/resources/file_provider.py +161 -0
- opentrons/protocol_engine/resources/fixture_validation.py +68 -0
- opentrons/protocol_engine/resources/labware_data_provider.py +106 -0
- opentrons/protocol_engine/resources/labware_validation.py +73 -0
- opentrons/protocol_engine/resources/model_utils.py +32 -0
- opentrons/protocol_engine/resources/module_data_provider.py +44 -0
- opentrons/protocol_engine/resources/ot3_validation.py +21 -0
- opentrons/protocol_engine/resources/pipette_data_provider.py +379 -0
- opentrons/protocol_engine/slot_standardization.py +128 -0
- opentrons/protocol_engine/state/__init__.py +1 -0
- opentrons/protocol_engine/state/_abstract_store.py +27 -0
- opentrons/protocol_engine/state/_axis_aligned_bounding_box.py +50 -0
- opentrons/protocol_engine/state/_labware_origin_math.py +636 -0
- opentrons/protocol_engine/state/_move_types.py +83 -0
- opentrons/protocol_engine/state/_well_math.py +193 -0
- opentrons/protocol_engine/state/addressable_areas.py +699 -0
- opentrons/protocol_engine/state/command_history.py +309 -0
- opentrons/protocol_engine/state/commands.py +1164 -0
- opentrons/protocol_engine/state/config.py +39 -0
- opentrons/protocol_engine/state/files.py +57 -0
- opentrons/protocol_engine/state/fluid_stack.py +138 -0
- opentrons/protocol_engine/state/geometry.py +2408 -0
- opentrons/protocol_engine/state/inner_well_math_utils.py +548 -0
- opentrons/protocol_engine/state/labware.py +1432 -0
- opentrons/protocol_engine/state/liquid_classes.py +82 -0
- opentrons/protocol_engine/state/liquids.py +73 -0
- opentrons/protocol_engine/state/module_substates/__init__.py +45 -0
- opentrons/protocol_engine/state/module_substates/absorbance_reader_substate.py +35 -0
- opentrons/protocol_engine/state/module_substates/flex_stacker_substate.py +112 -0
- opentrons/protocol_engine/state/module_substates/heater_shaker_module_substate.py +115 -0
- opentrons/protocol_engine/state/module_substates/magnetic_block_substate.py +17 -0
- opentrons/protocol_engine/state/module_substates/magnetic_module_substate.py +65 -0
- opentrons/protocol_engine/state/module_substates/temperature_module_substate.py +67 -0
- opentrons/protocol_engine/state/module_substates/thermocycler_module_substate.py +163 -0
- opentrons/protocol_engine/state/modules.py +1515 -0
- opentrons/protocol_engine/state/motion.py +373 -0
- opentrons/protocol_engine/state/pipettes.py +905 -0
- opentrons/protocol_engine/state/state.py +421 -0
- opentrons/protocol_engine/state/state_summary.py +36 -0
- opentrons/protocol_engine/state/tips.py +420 -0
- opentrons/protocol_engine/state/update_types.py +904 -0
- opentrons/protocol_engine/state/wells.py +290 -0
- opentrons/protocol_engine/types/__init__.py +310 -0
- opentrons/protocol_engine/types/automatic_tip_selection.py +39 -0
- opentrons/protocol_engine/types/command_annotations.py +53 -0
- opentrons/protocol_engine/types/deck_configuration.py +81 -0
- opentrons/protocol_engine/types/execution.py +96 -0
- opentrons/protocol_engine/types/hardware_passthrough.py +25 -0
- opentrons/protocol_engine/types/instrument.py +47 -0
- opentrons/protocol_engine/types/instrument_sensors.py +47 -0
- opentrons/protocol_engine/types/labware.py +131 -0
- opentrons/protocol_engine/types/labware_movement.py +22 -0
- opentrons/protocol_engine/types/labware_offset_location.py +111 -0
- opentrons/protocol_engine/types/labware_offset_vector.py +16 -0
- opentrons/protocol_engine/types/liquid.py +40 -0
- opentrons/protocol_engine/types/liquid_class.py +59 -0
- opentrons/protocol_engine/types/liquid_handling.py +13 -0
- opentrons/protocol_engine/types/liquid_level_detection.py +191 -0
- opentrons/protocol_engine/types/location.py +194 -0
- opentrons/protocol_engine/types/module.py +310 -0
- opentrons/protocol_engine/types/partial_tip_configuration.py +76 -0
- opentrons/protocol_engine/types/run_time_parameters.py +133 -0
- opentrons/protocol_engine/types/tip.py +18 -0
- opentrons/protocol_engine/types/util.py +21 -0
- opentrons/protocol_engine/types/well_position.py +124 -0
- opentrons/protocol_reader/__init__.py +37 -0
- opentrons/protocol_reader/extract_labware_definitions.py +66 -0
- opentrons/protocol_reader/file_format_validator.py +152 -0
- opentrons/protocol_reader/file_hasher.py +27 -0
- opentrons/protocol_reader/file_identifier.py +284 -0
- opentrons/protocol_reader/file_reader_writer.py +90 -0
- opentrons/protocol_reader/input_file.py +16 -0
- opentrons/protocol_reader/protocol_files_invalid_error.py +6 -0
- opentrons/protocol_reader/protocol_reader.py +188 -0
- opentrons/protocol_reader/protocol_source.py +124 -0
- opentrons/protocol_reader/role_analyzer.py +86 -0
- opentrons/protocol_runner/__init__.py +26 -0
- opentrons/protocol_runner/create_simulating_orchestrator.py +118 -0
- opentrons/protocol_runner/json_file_reader.py +55 -0
- opentrons/protocol_runner/json_translator.py +314 -0
- opentrons/protocol_runner/legacy_command_mapper.py +852 -0
- opentrons/protocol_runner/legacy_context_plugin.py +116 -0
- opentrons/protocol_runner/protocol_runner.py +530 -0
- opentrons/protocol_runner/python_protocol_wrappers.py +179 -0
- opentrons/protocol_runner/run_orchestrator.py +496 -0
- opentrons/protocol_runner/task_queue.py +95 -0
- opentrons/protocols/__init__.py +6 -0
- opentrons/protocols/advanced_control/__init__.py +0 -0
- opentrons/protocols/advanced_control/common.py +38 -0
- opentrons/protocols/advanced_control/mix.py +60 -0
- opentrons/protocols/advanced_control/transfers/__init__.py +0 -0
- opentrons/protocols/advanced_control/transfers/common.py +180 -0
- opentrons/protocols/advanced_control/transfers/transfer.py +972 -0
- opentrons/protocols/advanced_control/transfers/transfer_liquid_utils.py +231 -0
- opentrons/protocols/api_support/__init__.py +0 -0
- opentrons/protocols/api_support/constants.py +8 -0
- opentrons/protocols/api_support/deck_type.py +110 -0
- opentrons/protocols/api_support/definitions.py +18 -0
- opentrons/protocols/api_support/instrument.py +151 -0
- opentrons/protocols/api_support/labware_like.py +233 -0
- opentrons/protocols/api_support/tip_tracker.py +175 -0
- opentrons/protocols/api_support/types.py +32 -0
- opentrons/protocols/api_support/util.py +403 -0
- opentrons/protocols/bundle.py +89 -0
- opentrons/protocols/duration/__init__.py +4 -0
- opentrons/protocols/duration/errors.py +5 -0
- opentrons/protocols/duration/estimator.py +628 -0
- opentrons/protocols/execution/__init__.py +0 -0
- opentrons/protocols/execution/dev_types.py +181 -0
- opentrons/protocols/execution/errors.py +40 -0
- opentrons/protocols/execution/execute.py +84 -0
- opentrons/protocols/execution/execute_json_v3.py +275 -0
- opentrons/protocols/execution/execute_json_v4.py +359 -0
- opentrons/protocols/execution/execute_json_v5.py +28 -0
- opentrons/protocols/execution/execute_python.py +169 -0
- opentrons/protocols/execution/json_dispatchers.py +87 -0
- opentrons/protocols/execution/types.py +7 -0
- opentrons/protocols/geometry/__init__.py +0 -0
- opentrons/protocols/geometry/planning.py +297 -0
- opentrons/protocols/labware.py +312 -0
- opentrons/protocols/models/__init__.py +0 -0
- opentrons/protocols/models/json_protocol.py +679 -0
- opentrons/protocols/parameters/__init__.py +0 -0
- opentrons/protocols/parameters/csv_parameter_definition.py +77 -0
- opentrons/protocols/parameters/csv_parameter_interface.py +96 -0
- opentrons/protocols/parameters/exceptions.py +34 -0
- opentrons/protocols/parameters/parameter_definition.py +272 -0
- opentrons/protocols/parameters/types.py +17 -0
- opentrons/protocols/parameters/validation.py +267 -0
- opentrons/protocols/parse.py +671 -0
- opentrons/protocols/types.py +159 -0
- opentrons/py.typed +0 -0
- opentrons/resources/scripts/lpc21isp +0 -0
- opentrons/resources/smoothie-edge-8414642.hex +23010 -0
- opentrons/simulate.py +1065 -0
- opentrons/system/__init__.py +6 -0
- opentrons/system/camera.py +51 -0
- opentrons/system/log_control.py +59 -0
- opentrons/system/nmcli.py +856 -0
- opentrons/system/resin.py +24 -0
- opentrons/system/smoothie_update.py +15 -0
- opentrons/system/wifi.py +204 -0
- opentrons/tools/__init__.py +0 -0
- opentrons/tools/args_handler.py +22 -0
- opentrons/tools/write_pipette_memory.py +157 -0
- opentrons/types.py +618 -0
- opentrons/util/__init__.py +1 -0
- opentrons/util/async_helpers.py +166 -0
- opentrons/util/broker.py +84 -0
- opentrons/util/change_notifier.py +47 -0
- opentrons/util/entrypoint_util.py +278 -0
- opentrons/util/get_union_elements.py +26 -0
- opentrons/util/helpers.py +6 -0
- opentrons/util/linal.py +178 -0
- opentrons/util/logging_config.py +265 -0
- opentrons/util/logging_queue_handler.py +61 -0
- opentrons/util/performance_helpers.py +157 -0
- opentrons-8.6.0.dist-info/METADATA +37 -0
- opentrons-8.6.0.dist-info/RECORD +601 -0
- opentrons-8.6.0.dist-info/WHEEL +4 -0
- opentrons-8.6.0.dist-info/entry_points.txt +3 -0
- opentrons-8.6.0.dist-info/licenses/LICENSE +202 -0
|
@@ -0,0 +1,1006 @@
|
|
|
1
|
+
"""Executor for liquid class based complex commands."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import logging
|
|
5
|
+
from copy import deepcopy
|
|
6
|
+
from enum import Enum
|
|
7
|
+
from typing import TYPE_CHECKING, Optional, Union, Literal
|
|
8
|
+
from dataclasses import dataclass, field, replace
|
|
9
|
+
|
|
10
|
+
from opentrons_shared_data.liquid_classes.liquid_class_definition import (
|
|
11
|
+
PositionReference,
|
|
12
|
+
Coordinate,
|
|
13
|
+
BlowoutLocation,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
from opentrons.protocol_api._liquid_properties import (
|
|
17
|
+
Submerge,
|
|
18
|
+
TransferProperties,
|
|
19
|
+
MixProperties,
|
|
20
|
+
SingleDispenseProperties,
|
|
21
|
+
MultiDispenseProperties,
|
|
22
|
+
TouchTipProperties,
|
|
23
|
+
)
|
|
24
|
+
from opentrons.types import Location, Point, Mount
|
|
25
|
+
from opentrons.protocols.advanced_control.transfers.transfer_liquid_utils import (
|
|
26
|
+
LocationCheckDescriptors,
|
|
27
|
+
check_current_volume_before_dispensing,
|
|
28
|
+
)
|
|
29
|
+
from opentrons.protocols.advanced_control.transfers import (
|
|
30
|
+
transfer_liquid_utils as tx_utils,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
if TYPE_CHECKING:
|
|
34
|
+
from .well import WellCore
|
|
35
|
+
from .instrument import InstrumentCore
|
|
36
|
+
from ... import TrashBin, WasteChute
|
|
37
|
+
|
|
38
|
+
log = logging.getLogger(__name__)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
AIR_GAP_LOC_Z_OFFSET_FROM_WELL_TOP = 2
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass
|
|
45
|
+
class LiquidAndAirGapPair:
|
|
46
|
+
"""Pairing of a liquid and air gap in a tip, with air gap below the liquid in a tip."""
|
|
47
|
+
|
|
48
|
+
liquid: float = 0
|
|
49
|
+
air_gap: float = 0
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@dataclass
|
|
53
|
+
class TipState:
|
|
54
|
+
"""Carrier of the state of the pipette tip in use.
|
|
55
|
+
|
|
56
|
+
Properties:
|
|
57
|
+
last_liquid_and_air_gap_in_tip: The last liquid + air_gap combo in the tip.
|
|
58
|
+
This will only include the existing liquid and air gap in the tip that
|
|
59
|
+
an aspirate/ dispense interacts with. For example, the air gap from
|
|
60
|
+
a previous step that needs to be removed, or the liquid from a previous
|
|
61
|
+
aspirate that needs to be dispensed or the liquid that needs to be added to
|
|
62
|
+
during a consolidation.
|
|
63
|
+
ready_to_aspirate: Whether the pipette plunger is in a position that allows
|
|
64
|
+
correct aspiration. The starting state for the pipette at initialization of
|
|
65
|
+
`TransferComponentsExecutor`s should be ready_to_aspirate == True.
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
ready_to_aspirate: bool = True
|
|
69
|
+
# TODO: maybe use the tip contents from engine state instead.
|
|
70
|
+
last_liquid_and_air_gap_in_tip: LiquidAndAirGapPair = field(
|
|
71
|
+
default_factory=LiquidAndAirGapPair
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
def append_liquid(self, volume: float) -> None:
|
|
75
|
+
# Neither aspirate nor a dispense process should be adding liquid
|
|
76
|
+
# when there is an air gap present.
|
|
77
|
+
assert (
|
|
78
|
+
self.last_liquid_and_air_gap_in_tip.air_gap == 0
|
|
79
|
+
), "Air gap present in the tip."
|
|
80
|
+
self.last_liquid_and_air_gap_in_tip.liquid += volume
|
|
81
|
+
|
|
82
|
+
def delete_liquid(self, volume: float) -> None:
|
|
83
|
+
# Neither aspirate nor a dispense process should be removing liquid
|
|
84
|
+
# when there is an air gap present.
|
|
85
|
+
assert (
|
|
86
|
+
self.last_liquid_and_air_gap_in_tip.air_gap == 0
|
|
87
|
+
), "Air gap present in the tip."
|
|
88
|
+
self.last_liquid_and_air_gap_in_tip.liquid -= volume
|
|
89
|
+
|
|
90
|
+
def append_air_gap(self, volume: float) -> None:
|
|
91
|
+
# Neither aspirate nor a dispense process should be adding air gaps
|
|
92
|
+
# when there is already an air gap present.
|
|
93
|
+
assert (
|
|
94
|
+
self.last_liquid_and_air_gap_in_tip.air_gap == 0
|
|
95
|
+
), "Air gap already present in the tip."
|
|
96
|
+
self.last_liquid_and_air_gap_in_tip.air_gap = volume
|
|
97
|
+
|
|
98
|
+
def delete_air_gap(self, volume: float) -> None:
|
|
99
|
+
assert (
|
|
100
|
+
self.last_liquid_and_air_gap_in_tip.air_gap == volume
|
|
101
|
+
), "Last air gap volume doe not match the volume being removed"
|
|
102
|
+
self.last_liquid_and_air_gap_in_tip.air_gap = 0
|
|
103
|
+
|
|
104
|
+
def delete_last_air_gap_and_liquid(self) -> None:
|
|
105
|
+
air_gap_in_tip = self.last_liquid_and_air_gap_in_tip.air_gap
|
|
106
|
+
liquid_in_tip = self.last_liquid_and_air_gap_in_tip.liquid
|
|
107
|
+
if air_gap_in_tip:
|
|
108
|
+
self.delete_air_gap(air_gap_in_tip)
|
|
109
|
+
if liquid_in_tip:
|
|
110
|
+
self.delete_liquid(volume=liquid_in_tip)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class TransferType(Enum):
|
|
114
|
+
ONE_TO_ONE = "one_to_one"
|
|
115
|
+
MANY_TO_ONE = "many_to_one"
|
|
116
|
+
ONE_TO_MANY = "one_to_many"
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class TransferComponentsExecutor:
|
|
120
|
+
def __init__(
|
|
121
|
+
self,
|
|
122
|
+
instrument_core: InstrumentCore,
|
|
123
|
+
transfer_properties: TransferProperties,
|
|
124
|
+
target_location: Union[Location, TrashBin, WasteChute],
|
|
125
|
+
target_well: Optional[WellCore],
|
|
126
|
+
tip_state: TipState,
|
|
127
|
+
transfer_type: TransferType,
|
|
128
|
+
) -> None:
|
|
129
|
+
"""Create a TransferComponentsExecutor instance.
|
|
130
|
+
|
|
131
|
+
One instance should be created to execute all the steps inside each of the
|
|
132
|
+
liquid class' transfer components- aspirate, dispense and multi-dispense.
|
|
133
|
+
The state of the TransferComponentsExecutor instance is expected to be valid
|
|
134
|
+
only for the component it was created.
|
|
135
|
+
|
|
136
|
+
For example, if we want to execute all the steps (submerge, dispense, retract, etc)
|
|
137
|
+
related to the 'dispense' component of a liquid-class based transfer, the class
|
|
138
|
+
will be used to initialize info about the dispense by assigning values
|
|
139
|
+
to class attributes as follows-
|
|
140
|
+
- target_location: the dispense location
|
|
141
|
+
- target_well: the well associated with dispense location, will be None when the
|
|
142
|
+
target_location argument is a TrashBin or WasteChute
|
|
143
|
+
- tip_state: the state of the tip before dispense component steps are executed
|
|
144
|
+
- transfer_type: whether the dispense component is being called as a part of a
|
|
145
|
+
1-to-1 transfer or a consolidation or a distribution
|
|
146
|
+
|
|
147
|
+
These attributes will remain the same throughout the component's execution,
|
|
148
|
+
except `tip_state`, which will keep updating as fluids are handled.
|
|
149
|
+
"""
|
|
150
|
+
self._instrument = instrument_core
|
|
151
|
+
self._transfer_properties = transfer_properties
|
|
152
|
+
self._target_location = target_location
|
|
153
|
+
self._target_well = target_well
|
|
154
|
+
self._tip_state: TipState = deepcopy(tip_state) # don't modify caller's object
|
|
155
|
+
self._transfer_type: TransferType = transfer_type
|
|
156
|
+
|
|
157
|
+
@property
|
|
158
|
+
def tip_state(self) -> TipState:
|
|
159
|
+
"""Return the tip state."""
|
|
160
|
+
return self._tip_state
|
|
161
|
+
|
|
162
|
+
def submerge(
|
|
163
|
+
self,
|
|
164
|
+
submerge_properties: Submerge,
|
|
165
|
+
post_submerge_action: Literal["aspirate", "dispense"],
|
|
166
|
+
) -> None:
|
|
167
|
+
"""Execute submerge steps.
|
|
168
|
+
|
|
169
|
+
1. move to position shown by positionReference + offset (should practically be a point outside/above the liquid).
|
|
170
|
+
Should raise an error if this point is inside the liquid?
|
|
171
|
+
For liquid meniscus this is easy to tell. Can’t be below meniscus
|
|
172
|
+
For reference pos of anything else, do not allow submerge position to be below aspirate position
|
|
173
|
+
2. move to aspirate/dispense position at desired speed
|
|
174
|
+
3. delay
|
|
175
|
+
|
|
176
|
+
If target location is a trash bin or waste chute, the pipette will move to the disposal location given,
|
|
177
|
+
remove air gap and delay
|
|
178
|
+
"""
|
|
179
|
+
submerge_start_location: Union[Location, TrashBin, WasteChute]
|
|
180
|
+
if isinstance(self._target_location, Location):
|
|
181
|
+
assert self._target_well is not None
|
|
182
|
+
submerge_start_point = absolute_point_from_position_reference_and_offset(
|
|
183
|
+
well=self._target_well,
|
|
184
|
+
well_volume_difference=0,
|
|
185
|
+
position_reference=submerge_properties.start_position.position_reference,
|
|
186
|
+
offset=submerge_properties.start_position.offset,
|
|
187
|
+
mount=self._instrument.get_mount(),
|
|
188
|
+
)
|
|
189
|
+
submerge_start_location = Location(
|
|
190
|
+
point=submerge_start_point, labware=self._target_location.labware
|
|
191
|
+
)
|
|
192
|
+
tx_utils.raise_if_location_inside_liquid(
|
|
193
|
+
location=submerge_start_location,
|
|
194
|
+
well_core=self._target_well,
|
|
195
|
+
location_check_descriptors=LocationCheckDescriptors(
|
|
196
|
+
location_type="submerge start",
|
|
197
|
+
pipetting_action=post_submerge_action,
|
|
198
|
+
),
|
|
199
|
+
logger=log,
|
|
200
|
+
)
|
|
201
|
+
else:
|
|
202
|
+
submerge_start_location = self._target_location
|
|
203
|
+
|
|
204
|
+
self._instrument.move_to(
|
|
205
|
+
location=submerge_start_location,
|
|
206
|
+
well_core=self._target_well,
|
|
207
|
+
force_direct=False,
|
|
208
|
+
minimum_z_height=None,
|
|
209
|
+
speed=None,
|
|
210
|
+
)
|
|
211
|
+
self._remove_air_gap(location=submerge_start_location)
|
|
212
|
+
if isinstance(self._target_location, Location):
|
|
213
|
+
self._instrument.move_to(
|
|
214
|
+
location=self._target_location,
|
|
215
|
+
well_core=self._target_well,
|
|
216
|
+
force_direct=True,
|
|
217
|
+
minimum_z_height=None,
|
|
218
|
+
speed=submerge_properties.speed,
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
if submerge_properties.delay.enabled and submerge_properties.delay.duration:
|
|
222
|
+
self._instrument.delay(submerge_properties.delay.duration)
|
|
223
|
+
|
|
224
|
+
def aspirate_and_wait(self, volume: float) -> None:
|
|
225
|
+
"""Aspirate according to aspirate properties and wait if enabled."""
|
|
226
|
+
# TODO: handle volume correction
|
|
227
|
+
assert (
|
|
228
|
+
isinstance(self._target_location, Location)
|
|
229
|
+
and self._target_well is not None
|
|
230
|
+
)
|
|
231
|
+
aspirate_props = self._transfer_properties.aspirate
|
|
232
|
+
correction_volume = aspirate_props.correction_by_volume.get_for_volume(
|
|
233
|
+
self._instrument.get_current_volume() + volume
|
|
234
|
+
)
|
|
235
|
+
self._instrument.aspirate(
|
|
236
|
+
location=self._target_location,
|
|
237
|
+
well_core=None,
|
|
238
|
+
volume=volume,
|
|
239
|
+
rate=1,
|
|
240
|
+
flow_rate=aspirate_props.flow_rate_by_volume.get_for_volume(volume),
|
|
241
|
+
in_place=True,
|
|
242
|
+
correction_volume=correction_volume,
|
|
243
|
+
)
|
|
244
|
+
self._tip_state.append_liquid(volume)
|
|
245
|
+
delay_props = aspirate_props.delay
|
|
246
|
+
if delay_props.enabled and delay_props.duration:
|
|
247
|
+
self._instrument.delay(delay_props.duration)
|
|
248
|
+
|
|
249
|
+
def dispense_and_wait(
|
|
250
|
+
self,
|
|
251
|
+
dispense_properties: Union[SingleDispenseProperties, MultiDispenseProperties],
|
|
252
|
+
volume: float,
|
|
253
|
+
push_out_override: Optional[float],
|
|
254
|
+
) -> None:
|
|
255
|
+
"""Dispense according to dispense properties and wait if enabled."""
|
|
256
|
+
current_vol = self._instrument.get_current_volume()
|
|
257
|
+
check_current_volume_before_dispensing(
|
|
258
|
+
current_volume=current_vol, dispense_volume=volume
|
|
259
|
+
)
|
|
260
|
+
correction_volume = dispense_properties.correction_by_volume.get_for_volume(
|
|
261
|
+
current_vol - volume
|
|
262
|
+
)
|
|
263
|
+
self._instrument.dispense(
|
|
264
|
+
location=self._target_location,
|
|
265
|
+
well_core=None,
|
|
266
|
+
volume=volume,
|
|
267
|
+
rate=1,
|
|
268
|
+
flow_rate=dispense_properties.flow_rate_by_volume.get_for_volume(volume),
|
|
269
|
+
in_place=True,
|
|
270
|
+
push_out=push_out_override,
|
|
271
|
+
correction_volume=correction_volume,
|
|
272
|
+
)
|
|
273
|
+
if push_out_override:
|
|
274
|
+
# If a push out was performed, we need to reset the plunger before we can aspirate again
|
|
275
|
+
self._tip_state.ready_to_aspirate = False
|
|
276
|
+
self._tip_state.delete_liquid(volume)
|
|
277
|
+
dispense_delay = dispense_properties.delay
|
|
278
|
+
if dispense_delay.enabled and dispense_delay.duration:
|
|
279
|
+
self._instrument.delay(dispense_delay.duration)
|
|
280
|
+
|
|
281
|
+
def mix(self, mix_properties: MixProperties, last_dispense_push_out: bool) -> None:
|
|
282
|
+
"""Execute mix steps.
|
|
283
|
+
|
|
284
|
+
1. Use same flow rates and delays as aspirate and dispense
|
|
285
|
+
2. Do [(aspirate + dispense) x repetitions] at the same position
|
|
286
|
+
3. Do NOT push out at the end of dispense
|
|
287
|
+
4. USE the delay property from aspirate & dispense during mix as well (flow rate and delay are coordinated with each other)
|
|
288
|
+
5. Do not mix during consolidation
|
|
289
|
+
NOTE: For most of our built-in definitions, we will keep _mix_ off because it is a very application specific thing.
|
|
290
|
+
We should mention in our docs that users should adjust this property according to their application.
|
|
291
|
+
"""
|
|
292
|
+
if not mix_properties.enabled or not isinstance(
|
|
293
|
+
self._target_location, Location
|
|
294
|
+
):
|
|
295
|
+
return
|
|
296
|
+
# Assertion only for mypy purposes
|
|
297
|
+
assert (
|
|
298
|
+
mix_properties.repetitions is not None
|
|
299
|
+
and mix_properties.volume is not None
|
|
300
|
+
and self._target_well is not None
|
|
301
|
+
)
|
|
302
|
+
push_out_vol = (
|
|
303
|
+
self._transfer_properties.dispense.push_out_by_volume.get_for_volume(
|
|
304
|
+
mix_properties.volume
|
|
305
|
+
)
|
|
306
|
+
)
|
|
307
|
+
for n in range(mix_properties.repetitions, 0, -1):
|
|
308
|
+
self.aspirate_and_wait(volume=mix_properties.volume)
|
|
309
|
+
self.dispense_and_wait(
|
|
310
|
+
dispense_properties=self._transfer_properties.dispense, # TODO: check that using single-dispense props during mix is correct
|
|
311
|
+
volume=mix_properties.volume,
|
|
312
|
+
push_out_override=push_out_vol
|
|
313
|
+
if last_dispense_push_out is True and n == 1
|
|
314
|
+
else 0,
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
def pre_wet(
|
|
318
|
+
self,
|
|
319
|
+
volume: float,
|
|
320
|
+
) -> None:
|
|
321
|
+
"""Do a pre-wet.
|
|
322
|
+
|
|
323
|
+
- 1 combo of aspirate + dispense at the same flow rate as specified in asp & disp and the delays in asp & disp
|
|
324
|
+
- Use the target volume/ volume we will be aspirating
|
|
325
|
+
- No push out
|
|
326
|
+
- No pre-wet for consolidation
|
|
327
|
+
"""
|
|
328
|
+
if not self._transfer_properties.aspirate.pre_wet:
|
|
329
|
+
return
|
|
330
|
+
mix_props = MixProperties(_enabled=True, _repetitions=1, _volume=volume)
|
|
331
|
+
self.mix(mix_properties=mix_props, last_dispense_push_out=False)
|
|
332
|
+
|
|
333
|
+
def retract_after_aspiration(
|
|
334
|
+
self, volume: float, add_air_gap: Optional[bool] = True
|
|
335
|
+
) -> None:
|
|
336
|
+
"""Execute post-aspiration retraction steps.
|
|
337
|
+
|
|
338
|
+
1. Move TO the position reference+offset AT the specified speed
|
|
339
|
+
Raise error if retract is below aspirate position or below the meniscus
|
|
340
|
+
2. Delay
|
|
341
|
+
3. Touch tip
|
|
342
|
+
- Move to the Z offset position
|
|
343
|
+
- Touch tip to the sides at the specified speed (tip moves back to the center as part of touch tip)
|
|
344
|
+
- Return back to the retract position
|
|
345
|
+
4. Air gap
|
|
346
|
+
- If the retract location is at or above the safe location of
|
|
347
|
+
AIR_GAP_LOC_Z_OFFSET_FROM_WELL_TOP, then add the air gap at the
|
|
348
|
+
retract location (where the pipette is already assumed to be).
|
|
349
|
+
- If the retract location is below the safe location, then move to
|
|
350
|
+
the safe location and then add the air gap.
|
|
351
|
+
- Air gap volume depends on the amount of liquid in the pipette.
|
|
352
|
+
So, if the total aspirated volume is 20, use the value for airGapByVolume[20]
|
|
353
|
+
Flow rate = max(aspirateFlowRate, (airGapByVolume)/sec)
|
|
354
|
+
- Use post-aspirate delay
|
|
355
|
+
|
|
356
|
+
Args:
|
|
357
|
+
volume: dispense volume
|
|
358
|
+
add_air_gap: whether to add an air gap before moving away from the current well.
|
|
359
|
+
This value is True for all retractions, except when retracting
|
|
360
|
+
during a multi-dispense. Value of add_air_gap during multi-dispense
|
|
361
|
+
will depend on whether a conditioning volume is used.
|
|
362
|
+
"""
|
|
363
|
+
assert (
|
|
364
|
+
isinstance(self._target_location, Location)
|
|
365
|
+
and self._target_well is not None
|
|
366
|
+
)
|
|
367
|
+
retract_props = self._transfer_properties.aspirate.retract
|
|
368
|
+
retract_point = absolute_point_from_position_reference_and_offset(
|
|
369
|
+
well=self._target_well,
|
|
370
|
+
well_volume_difference=0,
|
|
371
|
+
position_reference=retract_props.end_position.position_reference,
|
|
372
|
+
offset=retract_props.end_position.offset,
|
|
373
|
+
mount=self._instrument.get_mount(),
|
|
374
|
+
)
|
|
375
|
+
retract_location = Location(
|
|
376
|
+
retract_point, labware=self._target_location.labware
|
|
377
|
+
)
|
|
378
|
+
tx_utils.raise_if_location_inside_liquid(
|
|
379
|
+
location=retract_location,
|
|
380
|
+
well_core=self._target_well,
|
|
381
|
+
location_check_descriptors=LocationCheckDescriptors(
|
|
382
|
+
location_type="retract end",
|
|
383
|
+
pipetting_action="aspirate",
|
|
384
|
+
),
|
|
385
|
+
logger=log,
|
|
386
|
+
)
|
|
387
|
+
self._instrument.move_to(
|
|
388
|
+
location=retract_location,
|
|
389
|
+
well_core=self._target_well,
|
|
390
|
+
force_direct=True,
|
|
391
|
+
minimum_z_height=None,
|
|
392
|
+
speed=retract_props.speed,
|
|
393
|
+
)
|
|
394
|
+
retract_delay = retract_props.delay
|
|
395
|
+
if retract_delay.enabled and retract_delay.duration:
|
|
396
|
+
self._instrument.delay(retract_delay.duration)
|
|
397
|
+
touch_tip_props = retract_props.touch_tip
|
|
398
|
+
if touch_tip_props.enabled:
|
|
399
|
+
assert (
|
|
400
|
+
touch_tip_props.speed is not None
|
|
401
|
+
and touch_tip_props.z_offset is not None
|
|
402
|
+
and touch_tip_props.mm_from_edge is not None
|
|
403
|
+
)
|
|
404
|
+
self._instrument.touch_tip(
|
|
405
|
+
location=retract_location,
|
|
406
|
+
well_core=self._target_well,
|
|
407
|
+
radius=1,
|
|
408
|
+
z_offset=touch_tip_props.z_offset,
|
|
409
|
+
speed=touch_tip_props.speed,
|
|
410
|
+
mm_from_edge=touch_tip_props.mm_from_edge,
|
|
411
|
+
)
|
|
412
|
+
self._instrument.move_to(
|
|
413
|
+
location=retract_location,
|
|
414
|
+
well_core=self._target_well,
|
|
415
|
+
force_direct=True,
|
|
416
|
+
minimum_z_height=None,
|
|
417
|
+
# Full speed because the tip will already be out of the liquid
|
|
418
|
+
speed=None,
|
|
419
|
+
)
|
|
420
|
+
# For consolidate, we need to know the total amount that is in the pipette
|
|
421
|
+
# since this may not be the first aspirate
|
|
422
|
+
if self._transfer_type == TransferType.MANY_TO_ONE:
|
|
423
|
+
volume_for_air_gap = self._instrument.get_current_volume()
|
|
424
|
+
else:
|
|
425
|
+
volume_for_air_gap = volume
|
|
426
|
+
if add_air_gap:
|
|
427
|
+
# If we need to add air gap, move to a safe location above the well if
|
|
428
|
+
# the retract location is not already at or above this safe location
|
|
429
|
+
if (
|
|
430
|
+
retract_location.point.z
|
|
431
|
+
< self._target_well.get_top(AIR_GAP_LOC_Z_OFFSET_FROM_WELL_TOP).z
|
|
432
|
+
):
|
|
433
|
+
self._instrument.move_to(
|
|
434
|
+
location=Location(
|
|
435
|
+
point=Point(
|
|
436
|
+
retract_location.point.x,
|
|
437
|
+
retract_location.point.y,
|
|
438
|
+
self._target_well.get_top(
|
|
439
|
+
AIR_GAP_LOC_Z_OFFSET_FROM_WELL_TOP
|
|
440
|
+
).z,
|
|
441
|
+
),
|
|
442
|
+
labware=retract_location.labware,
|
|
443
|
+
),
|
|
444
|
+
well_core=self._target_well,
|
|
445
|
+
force_direct=True,
|
|
446
|
+
minimum_z_height=None,
|
|
447
|
+
# Full speed because the tip will already be out of the liquid
|
|
448
|
+
speed=None,
|
|
449
|
+
)
|
|
450
|
+
self._add_air_gap(
|
|
451
|
+
air_gap_volume=self._transfer_properties.aspirate.retract.air_gap_by_volume.get_for_volume(
|
|
452
|
+
volume_for_air_gap
|
|
453
|
+
)
|
|
454
|
+
)
|
|
455
|
+
|
|
456
|
+
def retract_after_dispensing(
|
|
457
|
+
self,
|
|
458
|
+
trash_location: Union[Location, TrashBin, WasteChute],
|
|
459
|
+
source_location: Optional[Location],
|
|
460
|
+
source_well: Optional[WellCore],
|
|
461
|
+
add_final_air_gap: bool,
|
|
462
|
+
) -> None:
|
|
463
|
+
"""Execute post-dispense retraction steps.
|
|
464
|
+
1. Position ref+offset is the ending position. Move to this position using specified speed
|
|
465
|
+
2. If blowout is enabled and “destination”
|
|
466
|
+
- Do blow-out (at the retract position)
|
|
467
|
+
- Leave plunger down
|
|
468
|
+
3. Touch-tip in the destination well.
|
|
469
|
+
4. If not ready-to-aspirate
|
|
470
|
+
- Prepare-to-aspirate (at the retract position)
|
|
471
|
+
5. Air-gap (at the retract position)
|
|
472
|
+
- This air gap is for preventing any stray droplets from falling while moving the pipette.
|
|
473
|
+
It will be performed out of caution even if we just did a blow_out and should *hypothetically*
|
|
474
|
+
have no liquid left in the tip.
|
|
475
|
+
- This air gap will be removed at the next aspirate.
|
|
476
|
+
If this is the last step of the transfer, and we aren't dropping the tip off,
|
|
477
|
+
then the air gap will be left as is(?).
|
|
478
|
+
6. If blowout is “source” or “trash”
|
|
479
|
+
- Move to location (top of Well)
|
|
480
|
+
- Do blow-out (top of well)
|
|
481
|
+
- Do touch-tip AGAIN at the source well (if blowout in a non-trash location)
|
|
482
|
+
- Prepare-to-aspirate (top of well)
|
|
483
|
+
- Do air-gap (top of well)
|
|
484
|
+
7. If drop tip, move to drop tip location, drop tip
|
|
485
|
+
|
|
486
|
+
If target location is a trash bin or waste chute, the retract movement step is skipped along with touch tip,
|
|
487
|
+
even if it is enabled.
|
|
488
|
+
"""
|
|
489
|
+
retract_props = self._transfer_properties.dispense.retract
|
|
490
|
+
|
|
491
|
+
retract_location: Union[Location, TrashBin, WasteChute]
|
|
492
|
+
if isinstance(self._target_location, Location):
|
|
493
|
+
assert self._target_well is not None
|
|
494
|
+
retract_point = absolute_point_from_position_reference_and_offset(
|
|
495
|
+
well=self._target_well,
|
|
496
|
+
well_volume_difference=0,
|
|
497
|
+
position_reference=retract_props.end_position.position_reference,
|
|
498
|
+
offset=retract_props.end_position.offset,
|
|
499
|
+
mount=self._instrument.get_mount(),
|
|
500
|
+
)
|
|
501
|
+
retract_location = Location(
|
|
502
|
+
retract_point, labware=self._target_location.labware
|
|
503
|
+
)
|
|
504
|
+
tx_utils.raise_if_location_inside_liquid(
|
|
505
|
+
location=retract_location,
|
|
506
|
+
well_core=self._target_well,
|
|
507
|
+
location_check_descriptors=LocationCheckDescriptors(
|
|
508
|
+
location_type="retract end",
|
|
509
|
+
pipetting_action="dispense",
|
|
510
|
+
),
|
|
511
|
+
logger=log,
|
|
512
|
+
)
|
|
513
|
+
self._instrument.move_to(
|
|
514
|
+
location=retract_location,
|
|
515
|
+
well_core=self._target_well,
|
|
516
|
+
force_direct=True,
|
|
517
|
+
minimum_z_height=None,
|
|
518
|
+
speed=retract_props.speed,
|
|
519
|
+
)
|
|
520
|
+
else:
|
|
521
|
+
retract_location = self._target_location
|
|
522
|
+
|
|
523
|
+
# TODO should we delay here for a trash despite not having a "retract"?
|
|
524
|
+
retract_delay = retract_props.delay
|
|
525
|
+
if retract_delay.enabled and retract_delay.duration:
|
|
526
|
+
self._instrument.delay(retract_delay.duration)
|
|
527
|
+
|
|
528
|
+
blowout_props = retract_props.blowout
|
|
529
|
+
if (
|
|
530
|
+
blowout_props.enabled
|
|
531
|
+
and blowout_props.location == BlowoutLocation.DESTINATION
|
|
532
|
+
):
|
|
533
|
+
assert blowout_props.flow_rate is not None
|
|
534
|
+
self._instrument.set_flow_rate(blow_out=blowout_props.flow_rate)
|
|
535
|
+
self._instrument.blow_out(
|
|
536
|
+
location=retract_location,
|
|
537
|
+
well_core=None,
|
|
538
|
+
in_place=True,
|
|
539
|
+
)
|
|
540
|
+
self._tip_state.ready_to_aspirate = False
|
|
541
|
+
is_final_air_gap = (
|
|
542
|
+
blowout_props.enabled
|
|
543
|
+
and blowout_props.location == BlowoutLocation.DESTINATION
|
|
544
|
+
) or not blowout_props.enabled
|
|
545
|
+
|
|
546
|
+
if is_final_air_gap and not add_final_air_gap:
|
|
547
|
+
air_gap_volume = 0.0
|
|
548
|
+
else:
|
|
549
|
+
air_gap_volume = retract_props.air_gap_by_volume.get_for_volume(0)
|
|
550
|
+
# Regardless of the blowout location, do touch tip and air gap
|
|
551
|
+
# when leaving the dispense well. If this will be the final air gap, i.e,
|
|
552
|
+
# we won't be moving to a Trash or a Source for Blowout after this air gap,
|
|
553
|
+
# then skip the final air gap if we have been told to do so.
|
|
554
|
+
self._do_touch_tip_and_air_gap_after_dispense(
|
|
555
|
+
touch_tip_properties=retract_props.touch_tip,
|
|
556
|
+
location=retract_location,
|
|
557
|
+
well=self._target_well,
|
|
558
|
+
air_gap_volume=air_gap_volume,
|
|
559
|
+
)
|
|
560
|
+
|
|
561
|
+
if (
|
|
562
|
+
blowout_props.enabled
|
|
563
|
+
and blowout_props.location != BlowoutLocation.DESTINATION
|
|
564
|
+
):
|
|
565
|
+
assert blowout_props.flow_rate is not None
|
|
566
|
+
self._instrument.set_flow_rate(blow_out=blowout_props.flow_rate)
|
|
567
|
+
blowout_touch_tip_props = retract_props.touch_tip
|
|
568
|
+
touch_tip_and_air_gap_location: Union[Location, TrashBin, WasteChute]
|
|
569
|
+
if blowout_props.location == BlowoutLocation.SOURCE:
|
|
570
|
+
if source_location is None or source_well is None:
|
|
571
|
+
raise RuntimeError(
|
|
572
|
+
"Blowout location is 'source' but source location &/or well is not provided."
|
|
573
|
+
)
|
|
574
|
+
# TODO: check if we should add a blowout location z-offset in liq class definition
|
|
575
|
+
self._instrument.blow_out(
|
|
576
|
+
location=Location(
|
|
577
|
+
source_well.get_top(0), labware=source_location.labware
|
|
578
|
+
),
|
|
579
|
+
well_core=source_well,
|
|
580
|
+
in_place=False,
|
|
581
|
+
)
|
|
582
|
+
touch_tip_and_air_gap_location = Location(
|
|
583
|
+
source_well.get_top(0), labware=source_location.labware
|
|
584
|
+
)
|
|
585
|
+
touch_tip_and_air_gap_well = source_well
|
|
586
|
+
# Skip touch tip if blowing out at the SOURCE and it's untouchable:
|
|
587
|
+
if (
|
|
588
|
+
"touchTipDisabled"
|
|
589
|
+
in source_location.labware.quirks_from_any_parent()
|
|
590
|
+
):
|
|
591
|
+
blowout_touch_tip_props = replace(blowout_touch_tip_props)
|
|
592
|
+
blowout_touch_tip_props.enabled = False
|
|
593
|
+
else:
|
|
594
|
+
self._instrument.blow_out(
|
|
595
|
+
location=trash_location,
|
|
596
|
+
well_core=None,
|
|
597
|
+
in_place=False,
|
|
598
|
+
)
|
|
599
|
+
touch_tip_and_air_gap_location = trash_location
|
|
600
|
+
touch_tip_and_air_gap_well = (
|
|
601
|
+
# We have already established that trash location of `Location` type
|
|
602
|
+
# has its `labware` as `Well` type.
|
|
603
|
+
trash_location.labware.as_well()._core # type: ignore[assignment]
|
|
604
|
+
if isinstance(trash_location, Location)
|
|
605
|
+
else None
|
|
606
|
+
)
|
|
607
|
+
# A non-multi-dispense blowout will only have air and maybe droplets in the tip
|
|
608
|
+
# since we only blowout after dispensing the full tip contents.
|
|
609
|
+
# So delete the air gap from tip state
|
|
610
|
+
last_air_gap = self._tip_state.last_liquid_and_air_gap_in_tip.air_gap
|
|
611
|
+
self._tip_state.delete_air_gap(last_air_gap)
|
|
612
|
+
self._tip_state.ready_to_aspirate = False
|
|
613
|
+
|
|
614
|
+
air_gap_volume = (
|
|
615
|
+
retract_props.air_gap_by_volume.get_for_volume(0)
|
|
616
|
+
if add_final_air_gap
|
|
617
|
+
else 0.0
|
|
618
|
+
)
|
|
619
|
+
# Do touch tip and air gap again after blowing out into source well or trash
|
|
620
|
+
self._do_touch_tip_and_air_gap_after_dispense(
|
|
621
|
+
touch_tip_properties=blowout_touch_tip_props,
|
|
622
|
+
location=touch_tip_and_air_gap_location,
|
|
623
|
+
well=touch_tip_and_air_gap_well,
|
|
624
|
+
air_gap_volume=air_gap_volume,
|
|
625
|
+
)
|
|
626
|
+
|
|
627
|
+
def retract_during_multi_dispensing( # noqa: C901
|
|
628
|
+
self,
|
|
629
|
+
trash_location: Union[Location, TrashBin, WasteChute],
|
|
630
|
+
source_location: Optional[Location],
|
|
631
|
+
source_well: Optional[WellCore],
|
|
632
|
+
conditioning_volume: float,
|
|
633
|
+
add_final_air_gap: bool,
|
|
634
|
+
is_last_retract: bool,
|
|
635
|
+
) -> None:
|
|
636
|
+
"""Execute post-dispense retraction steps when the dispense is a part of a multi-dispense.
|
|
637
|
+
|
|
638
|
+
Args:
|
|
639
|
+
trash_location: Location where we can drop tips or blowout, if set to do so
|
|
640
|
+
source_location: Location where we can blowout, if set to do so
|
|
641
|
+
source_well: Well where we can blowout, if set to do so
|
|
642
|
+
conditioning_volume: Conditioning volume used for this multi-dispense. Can be 0
|
|
643
|
+
add_final_air_gap: Whether we should add the final air gap of the step
|
|
644
|
+
is_last_retract: Whether this is the last retract of the multi-dispense steps,
|
|
645
|
+
i.e., this is part of the last dispense in the series of consecutive dispenses.
|
|
646
|
+
This dispense might not be the last dispense of the entire distribution.
|
|
647
|
+
|
|
648
|
+
This function is mostly similar to the single-dispense retract function except
|
|
649
|
+
that it handles air gaps differently based on the disposal volume, conditioning volume
|
|
650
|
+
and whether we are moving to another dispense or going back to the source.
|
|
651
|
+
"""
|
|
652
|
+
assert (
|
|
653
|
+
isinstance(self._target_location, Location)
|
|
654
|
+
and self._target_well is not None
|
|
655
|
+
)
|
|
656
|
+
assert self._transfer_properties.multi_dispense is not None
|
|
657
|
+
|
|
658
|
+
retract_props = self._transfer_properties.multi_dispense.retract
|
|
659
|
+
retract_point = absolute_point_from_position_reference_and_offset(
|
|
660
|
+
well=self._target_well,
|
|
661
|
+
well_volume_difference=0,
|
|
662
|
+
position_reference=retract_props.end_position.position_reference,
|
|
663
|
+
offset=retract_props.end_position.offset,
|
|
664
|
+
mount=self._instrument.get_mount(),
|
|
665
|
+
)
|
|
666
|
+
retract_location = Location(
|
|
667
|
+
retract_point, labware=self._target_location.labware
|
|
668
|
+
)
|
|
669
|
+
tx_utils.raise_if_location_inside_liquid(
|
|
670
|
+
location=retract_location,
|
|
671
|
+
well_core=self._target_well,
|
|
672
|
+
location_check_descriptors=LocationCheckDescriptors(
|
|
673
|
+
location_type="retract end",
|
|
674
|
+
pipetting_action="dispense",
|
|
675
|
+
),
|
|
676
|
+
logger=log,
|
|
677
|
+
)
|
|
678
|
+
self._instrument.move_to(
|
|
679
|
+
location=retract_location,
|
|
680
|
+
well_core=self._target_well,
|
|
681
|
+
force_direct=True,
|
|
682
|
+
minimum_z_height=None,
|
|
683
|
+
speed=retract_props.speed,
|
|
684
|
+
)
|
|
685
|
+
retract_delay = retract_props.delay
|
|
686
|
+
if retract_delay.enabled and retract_delay.duration:
|
|
687
|
+
self._instrument.delay(retract_delay.duration)
|
|
688
|
+
|
|
689
|
+
blowout_props = retract_props.blowout
|
|
690
|
+
if (
|
|
691
|
+
is_last_retract
|
|
692
|
+
and blowout_props.enabled
|
|
693
|
+
and blowout_props.location == BlowoutLocation.DESTINATION
|
|
694
|
+
):
|
|
695
|
+
assert blowout_props.flow_rate is not None
|
|
696
|
+
self._instrument.set_flow_rate(blow_out=blowout_props.flow_rate)
|
|
697
|
+
self._instrument.blow_out(
|
|
698
|
+
location=retract_location,
|
|
699
|
+
well_core=None,
|
|
700
|
+
in_place=True,
|
|
701
|
+
)
|
|
702
|
+
# A blowout will remove all air gap and liquid (disposal volume) from the tip
|
|
703
|
+
# so delete them from tip state (although practically, there will not be
|
|
704
|
+
# any air gaps in the tip before blowing out in the destination well)
|
|
705
|
+
self._tip_state.delete_last_air_gap_and_liquid()
|
|
706
|
+
self._tip_state.ready_to_aspirate = False
|
|
707
|
+
|
|
708
|
+
# A retract will perform total of two air gaps if we need to blow out in source or trash:
|
|
709
|
+
# - 1st air gap: added before leaving the destination volume to go to src/ trash
|
|
710
|
+
# - 2nd air gap: added before leaving the blowout location to go to src or tip drop location
|
|
711
|
+
# But if blowout is disabled or is set to Destination well, then only one air gap
|
|
712
|
+
# will be added after retracting, before moving to src or tip drop location.
|
|
713
|
+
# `is_final_air_gap_of_current_retract` tells us whether the next air gap
|
|
714
|
+
# we will be adding, is going to be the last air gap of this step.
|
|
715
|
+
is_final_air_gap_of_current_retract = (
|
|
716
|
+
blowout_props.enabled
|
|
717
|
+
and blowout_props.location == BlowoutLocation.DESTINATION
|
|
718
|
+
) or not blowout_props.enabled
|
|
719
|
+
|
|
720
|
+
# Whether we should add the next air gap depends on the cases as shown below.
|
|
721
|
+
# The main points when deciding this-
|
|
722
|
+
# - When we have used a conditioning volume, we do not want to add air gaps
|
|
723
|
+
# while there's still liquid in tip for dispensing
|
|
724
|
+
# - If we are not using conditioning volume then we want to add gaps just like
|
|
725
|
+
# we do during the one-to-one transfers
|
|
726
|
+
# - If this will be the last air gap of the step, if the above two conditions
|
|
727
|
+
# indicate that we should be adding an air gap, use `add_final_air_gap` as
|
|
728
|
+
# the final decider of whether to add the air gap.
|
|
729
|
+
if is_final_air_gap_of_current_retract:
|
|
730
|
+
if conditioning_volume > 0:
|
|
731
|
+
add_air_gap = is_last_retract and add_final_air_gap
|
|
732
|
+
else:
|
|
733
|
+
add_air_gap = add_final_air_gap
|
|
734
|
+
else:
|
|
735
|
+
if conditioning_volume > 0:
|
|
736
|
+
add_air_gap = is_last_retract
|
|
737
|
+
else:
|
|
738
|
+
add_air_gap = True
|
|
739
|
+
|
|
740
|
+
air_gap_volume = (
|
|
741
|
+
retract_props.air_gap_by_volume.get_for_volume(
|
|
742
|
+
self.tip_state.last_liquid_and_air_gap_in_tip.liquid
|
|
743
|
+
)
|
|
744
|
+
if add_air_gap
|
|
745
|
+
else 0.0
|
|
746
|
+
)
|
|
747
|
+
|
|
748
|
+
# Regardless of the blowout location, do touch tip
|
|
749
|
+
# when leaving the dispense well.
|
|
750
|
+
# Add an air gap depending on conditioning volume + whether this is
|
|
751
|
+
# the last step of a multi-dispense sequence + whether this is the last step
|
|
752
|
+
# of the entire liquid distribution.
|
|
753
|
+
self._do_touch_tip_and_air_gap_after_dispense(
|
|
754
|
+
touch_tip_properties=retract_props.touch_tip,
|
|
755
|
+
location=retract_location,
|
|
756
|
+
well=self._target_well,
|
|
757
|
+
air_gap_volume=air_gap_volume,
|
|
758
|
+
)
|
|
759
|
+
|
|
760
|
+
if (
|
|
761
|
+
is_last_retract # We can do a blowout only on the last multi-dispense step
|
|
762
|
+
and blowout_props.enabled
|
|
763
|
+
and blowout_props.location != BlowoutLocation.DESTINATION
|
|
764
|
+
):
|
|
765
|
+
assert blowout_props.flow_rate is not None
|
|
766
|
+
self._instrument.set_flow_rate(blow_out=blowout_props.flow_rate)
|
|
767
|
+
blowout_touch_tip_props = retract_props.touch_tip
|
|
768
|
+
touch_tip_and_air_gap_location: Union[Location, TrashBin, WasteChute]
|
|
769
|
+
if blowout_props.location == BlowoutLocation.SOURCE:
|
|
770
|
+
if source_location is None or source_well is None:
|
|
771
|
+
raise RuntimeError(
|
|
772
|
+
"Blowout location is 'source' but source location &/or well is not provided."
|
|
773
|
+
)
|
|
774
|
+
# TODO: check if we should add a blowout location z-offset in liq class definition
|
|
775
|
+
self._instrument.blow_out(
|
|
776
|
+
location=Location(
|
|
777
|
+
source_well.get_top(0), labware=source_location.labware
|
|
778
|
+
),
|
|
779
|
+
well_core=source_well,
|
|
780
|
+
in_place=False,
|
|
781
|
+
)
|
|
782
|
+
touch_tip_and_air_gap_location = Location(
|
|
783
|
+
source_well.get_top(0), labware=source_location.labware
|
|
784
|
+
)
|
|
785
|
+
touch_tip_and_air_gap_well = source_well
|
|
786
|
+
# Skip touch tip if blowing out at the SOURCE and it's untouchable:
|
|
787
|
+
if (
|
|
788
|
+
"touchTipDisabled"
|
|
789
|
+
in source_location.labware.quirks_from_any_parent()
|
|
790
|
+
):
|
|
791
|
+
blowout_touch_tip_props = replace(blowout_touch_tip_props)
|
|
792
|
+
blowout_touch_tip_props.enabled = False
|
|
793
|
+
else:
|
|
794
|
+
self._instrument.blow_out(
|
|
795
|
+
location=trash_location,
|
|
796
|
+
well_core=None,
|
|
797
|
+
in_place=False,
|
|
798
|
+
)
|
|
799
|
+
touch_tip_and_air_gap_location = trash_location
|
|
800
|
+
touch_tip_and_air_gap_well = (
|
|
801
|
+
# We have already established that trash location of `Location` type
|
|
802
|
+
# has its `labware` as `Well` type.
|
|
803
|
+
trash_location.labware.as_well()._core # type: ignore[assignment]
|
|
804
|
+
if isinstance(trash_location, Location)
|
|
805
|
+
else None
|
|
806
|
+
)
|
|
807
|
+
# A blowout will remove all air gap and liquid (disposal volume) from the tip
|
|
808
|
+
# so delete them from tip state
|
|
809
|
+
self._tip_state.delete_last_air_gap_and_liquid()
|
|
810
|
+
self._tip_state.ready_to_aspirate = False
|
|
811
|
+
|
|
812
|
+
if (
|
|
813
|
+
# Same check as before for when it's the final air gap of current retract
|
|
814
|
+
conditioning_volume > 0
|
|
815
|
+
and is_last_retract
|
|
816
|
+
and add_final_air_gap
|
|
817
|
+
):
|
|
818
|
+
# The volume in tip at this point should be 0uL
|
|
819
|
+
air_gap_volume = retract_props.air_gap_by_volume.get_for_volume(0)
|
|
820
|
+
else:
|
|
821
|
+
air_gap_volume = 0
|
|
822
|
+
# Do touch tip and air gap again after blowing out into source well or trash
|
|
823
|
+
self._do_touch_tip_and_air_gap_after_dispense(
|
|
824
|
+
touch_tip_properties=blowout_touch_tip_props,
|
|
825
|
+
location=touch_tip_and_air_gap_location,
|
|
826
|
+
well=touch_tip_and_air_gap_well,
|
|
827
|
+
air_gap_volume=air_gap_volume,
|
|
828
|
+
)
|
|
829
|
+
|
|
830
|
+
def _do_touch_tip_and_air_gap_after_dispense(
|
|
831
|
+
self,
|
|
832
|
+
touch_tip_properties: TouchTipProperties,
|
|
833
|
+
location: Union[Location, TrashBin, WasteChute],
|
|
834
|
+
well: Optional[WellCore],
|
|
835
|
+
air_gap_volume: float,
|
|
836
|
+
) -> None:
|
|
837
|
+
"""Perform touch tip and air gap as part of post-dispense retract.
|
|
838
|
+
|
|
839
|
+
This function can be invoked up to 2 times for each dispense:
|
|
840
|
+
1) Once for touching tip at the dispense location.
|
|
841
|
+
2) Then again in the blowout location if it is not the dispense location.
|
|
842
|
+
For case (2), the caller should disable touch-tip in touch_tip_properties
|
|
843
|
+
if the blowout location is not touchable (such as reservoirs).
|
|
844
|
+
|
|
845
|
+
If the retract location is at or above the safe location of
|
|
846
|
+
AIR_GAP_LOC_Z_OFFSET_FROM_WELL_TOP, then add the air gap at the retract location
|
|
847
|
+
(where the pipette is already assumed to be at).
|
|
848
|
+
|
|
849
|
+
If the retract location is below the safe location, then move to the safe location
|
|
850
|
+
and then add the air gap.
|
|
851
|
+
|
|
852
|
+
Note: if the plunger needs to be adjusted to prepare for aspirate, it will be done
|
|
853
|
+
at the same location where the air gap will be added.
|
|
854
|
+
"""
|
|
855
|
+
if touch_tip_properties.enabled:
|
|
856
|
+
assert (
|
|
857
|
+
touch_tip_properties.speed is not None
|
|
858
|
+
and touch_tip_properties.z_offset is not None
|
|
859
|
+
and touch_tip_properties.mm_from_edge is not None
|
|
860
|
+
)
|
|
861
|
+
# TODO:, check that when blow out is a non-dest-well,
|
|
862
|
+
# whether the touch tip params from transfer props should be used for
|
|
863
|
+
# both dest-well touch tip and non-dest-well touch tip.
|
|
864
|
+
if isinstance(location, Location) and well is not None:
|
|
865
|
+
self._instrument.touch_tip(
|
|
866
|
+
location=location,
|
|
867
|
+
well_core=well,
|
|
868
|
+
radius=1,
|
|
869
|
+
z_offset=touch_tip_properties.z_offset,
|
|
870
|
+
speed=touch_tip_properties.speed,
|
|
871
|
+
mm_from_edge=touch_tip_properties.mm_from_edge,
|
|
872
|
+
)
|
|
873
|
+
|
|
874
|
+
# Move back to the 'retract' position
|
|
875
|
+
self._instrument.move_to(
|
|
876
|
+
location=location,
|
|
877
|
+
well_core=well,
|
|
878
|
+
force_direct=True,
|
|
879
|
+
minimum_z_height=None,
|
|
880
|
+
# Full speed because the tip will already be out of the liquid
|
|
881
|
+
speed=None,
|
|
882
|
+
)
|
|
883
|
+
if air_gap_volume > 0 or not self._tip_state.ready_to_aspirate:
|
|
884
|
+
# If we need to move the plunger up either to prepare for aspirate or to add air gap,
|
|
885
|
+
# move to a safe location above the well if the retract location is not already
|
|
886
|
+
# at or above this safe location
|
|
887
|
+
if isinstance(location, Location):
|
|
888
|
+
assert well is not None # For mypy purposes only
|
|
889
|
+
if (
|
|
890
|
+
location.point.z
|
|
891
|
+
< well.get_top(AIR_GAP_LOC_Z_OFFSET_FROM_WELL_TOP).z
|
|
892
|
+
):
|
|
893
|
+
self._instrument.move_to(
|
|
894
|
+
location=Location(
|
|
895
|
+
point=Point(
|
|
896
|
+
location.point.x,
|
|
897
|
+
location.point.y,
|
|
898
|
+
well.get_top(AIR_GAP_LOC_Z_OFFSET_FROM_WELL_TOP).z,
|
|
899
|
+
),
|
|
900
|
+
labware=location.labware,
|
|
901
|
+
),
|
|
902
|
+
well_core=well,
|
|
903
|
+
force_direct=True,
|
|
904
|
+
minimum_z_height=None,
|
|
905
|
+
speed=None,
|
|
906
|
+
)
|
|
907
|
+
else:
|
|
908
|
+
if (
|
|
909
|
+
location.offset.z
|
|
910
|
+
< location.top(
|
|
911
|
+
x=0, y=0, z=AIR_GAP_LOC_Z_OFFSET_FROM_WELL_TOP
|
|
912
|
+
).offset.z
|
|
913
|
+
):
|
|
914
|
+
self._instrument.move_to(
|
|
915
|
+
location=location.top(
|
|
916
|
+
x=location.offset.x,
|
|
917
|
+
y=location.offset.y,
|
|
918
|
+
z=AIR_GAP_LOC_Z_OFFSET_FROM_WELL_TOP,
|
|
919
|
+
),
|
|
920
|
+
well_core=None,
|
|
921
|
+
force_direct=True,
|
|
922
|
+
minimum_z_height=None,
|
|
923
|
+
speed=None,
|
|
924
|
+
)
|
|
925
|
+
|
|
926
|
+
if not self._tip_state.ready_to_aspirate:
|
|
927
|
+
self._instrument.prepare_to_aspirate()
|
|
928
|
+
self._tip_state.ready_to_aspirate = True
|
|
929
|
+
if air_gap_volume > 0:
|
|
930
|
+
self._add_air_gap(air_gap_volume=air_gap_volume)
|
|
931
|
+
|
|
932
|
+
def _add_air_gap(
|
|
933
|
+
self,
|
|
934
|
+
air_gap_volume: float,
|
|
935
|
+
) -> None:
|
|
936
|
+
"""Add an air gap."""
|
|
937
|
+
if air_gap_volume == 0:
|
|
938
|
+
return
|
|
939
|
+
aspirate_props = self._transfer_properties.aspirate
|
|
940
|
+
correction_volume = aspirate_props.correction_by_volume.get_for_volume(
|
|
941
|
+
self._instrument.get_current_volume() + air_gap_volume
|
|
942
|
+
)
|
|
943
|
+
# The minimum flow rate should be air_gap_volume per second
|
|
944
|
+
flow_rate = max(
|
|
945
|
+
aspirate_props.flow_rate_by_volume.get_for_volume(air_gap_volume),
|
|
946
|
+
air_gap_volume,
|
|
947
|
+
)
|
|
948
|
+
self._instrument.air_gap_in_place(
|
|
949
|
+
volume=air_gap_volume,
|
|
950
|
+
flow_rate=flow_rate,
|
|
951
|
+
correction_volume=correction_volume,
|
|
952
|
+
)
|
|
953
|
+
delay_props = aspirate_props.delay
|
|
954
|
+
if delay_props.enabled and delay_props.duration:
|
|
955
|
+
self._instrument.delay(delay_props.duration)
|
|
956
|
+
self._tip_state.append_air_gap(air_gap_volume)
|
|
957
|
+
|
|
958
|
+
def _remove_air_gap(self, location: Union[Location, TrashBin, WasteChute]) -> None:
|
|
959
|
+
"""Remove a previously added air gap."""
|
|
960
|
+
last_air_gap = self._tip_state.last_liquid_and_air_gap_in_tip.air_gap
|
|
961
|
+
dispense_props = self._transfer_properties.dispense
|
|
962
|
+
self._instrument.remove_air_gap_during_transfer_with_liquid_class(
|
|
963
|
+
last_air_gap=last_air_gap,
|
|
964
|
+
dispense_props=dispense_props,
|
|
965
|
+
location=location,
|
|
966
|
+
)
|
|
967
|
+
self._tip_state.delete_air_gap(last_air_gap)
|
|
968
|
+
|
|
969
|
+
|
|
970
|
+
def absolute_point_from_position_reference_and_offset(
|
|
971
|
+
well: WellCore,
|
|
972
|
+
well_volume_difference: float,
|
|
973
|
+
position_reference: PositionReference,
|
|
974
|
+
offset: Coordinate,
|
|
975
|
+
mount: Mount,
|
|
976
|
+
) -> Point:
|
|
977
|
+
"""Return the absolute point, given the well, the position reference and offset.
|
|
978
|
+
|
|
979
|
+
If using meniscus as the position reference, well_volume_difference should be specified.
|
|
980
|
+
`well_volume_difference` is the expected *difference* in well volume we want to consider
|
|
981
|
+
when estimating the height of the liquid meniscus after an aspirate/ dispense.
|
|
982
|
+
So, for liquid height estimation after an aspirate, well_volume_difference is
|
|
983
|
+
expected to be a -ve value while for a dispense, it will be a +ve value.
|
|
984
|
+
"""
|
|
985
|
+
match position_reference:
|
|
986
|
+
case PositionReference.WELL_TOP:
|
|
987
|
+
reference_point = well.get_top(0)
|
|
988
|
+
case PositionReference.WELL_BOTTOM:
|
|
989
|
+
reference_point = well.get_bottom(0)
|
|
990
|
+
case PositionReference.WELL_CENTER:
|
|
991
|
+
reference_point = well.get_center()
|
|
992
|
+
case PositionReference.LIQUID_MENISCUS:
|
|
993
|
+
estimated_liquid_height = well.estimate_liquid_height_after_pipetting(
|
|
994
|
+
mount=mount,
|
|
995
|
+
operation_volume=well_volume_difference,
|
|
996
|
+
)
|
|
997
|
+
if isinstance(estimated_liquid_height, (float, int)):
|
|
998
|
+
reference_point = well.get_bottom(z_offset=estimated_liquid_height)
|
|
999
|
+
else:
|
|
1000
|
+
# If estimated liquid height gives a SimulatedProbeResult then
|
|
1001
|
+
# assume meniscus is at well center.
|
|
1002
|
+
# Will this cause more harm than good? Is there a better alternative to this?
|
|
1003
|
+
reference_point = well.get_center()
|
|
1004
|
+
case _:
|
|
1005
|
+
raise ValueError(f"Unknown position reference {position_reference}")
|
|
1006
|
+
return reference_point + Point(offset.x, offset.y, offset.z)
|