opentrons 8.6.0a1__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 +501 -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 +183 -0
- opentrons/drivers/asyncio/communication/errors.py +88 -0
- opentrons/drivers/asyncio/communication/serial_connection.py +552 -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/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 +215 -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 +1115 -0
- opentrons/motion_planning/__init__.py +32 -0
- opentrons/motion_planning/adjacent_slots_getters.py +168 -0
- opentrons/motion_planning/deck_conflict.py +396 -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 +348 -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 +1025 -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 +990 -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 +3212 -0
- opentrons/protocol_api/labware.py +1579 -0
- opentrons/protocol_api/module_contexts.py +1425 -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 +326 -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 +349 -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 +58 -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 +1158 -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 +2359 -0
- opentrons/protocol_engine/state/inner_well_math_utils.py +548 -0
- opentrons/protocol_engine/state/labware.py +1459 -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 +1500 -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 +308 -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 +303 -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 +848 -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.0a1.dist-info/METADATA +37 -0
- opentrons-8.6.0a1.dist-info/RECORD +600 -0
- opentrons-8.6.0a1.dist-info/WHEEL +4 -0
- opentrons-8.6.0a1.dist-info/entry_points.txt +3 -0
- opentrons-8.6.0a1.dist-info/licenses/LICENSE +202 -0
|
@@ -0,0 +1,995 @@
|
|
|
1
|
+
"""Shared code for managing pipette configuration and storage."""
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
import logging
|
|
4
|
+
from typing import (
|
|
5
|
+
Callable,
|
|
6
|
+
Dict,
|
|
7
|
+
Generic,
|
|
8
|
+
Optional,
|
|
9
|
+
Tuple,
|
|
10
|
+
Any,
|
|
11
|
+
cast,
|
|
12
|
+
List,
|
|
13
|
+
Sequence,
|
|
14
|
+
Iterator,
|
|
15
|
+
TypeVar,
|
|
16
|
+
)
|
|
17
|
+
import numpy
|
|
18
|
+
|
|
19
|
+
from opentrons_shared_data.errors.exceptions import (
|
|
20
|
+
UnexpectedTipRemovalError,
|
|
21
|
+
UnexpectedTipAttachError,
|
|
22
|
+
)
|
|
23
|
+
from opentrons_shared_data.pipette.types import UlPerMmAction
|
|
24
|
+
from opentrons_shared_data.pipette.types import Quirks
|
|
25
|
+
from opentrons_shared_data.errors.exceptions import CommandPreconditionViolated
|
|
26
|
+
|
|
27
|
+
from opentrons import types as top_types
|
|
28
|
+
from opentrons.hardware_control.types import (
|
|
29
|
+
CriticalPoint,
|
|
30
|
+
HardwareAction,
|
|
31
|
+
Axis,
|
|
32
|
+
OT3Mount,
|
|
33
|
+
)
|
|
34
|
+
from opentrons.hardware_control.constants import (
|
|
35
|
+
SHAKE_OFF_TIPS_SPEED,
|
|
36
|
+
SHAKE_OFF_TIPS_PICKUP_DISTANCE,
|
|
37
|
+
DROP_TIP_RELEASE_DISTANCE,
|
|
38
|
+
SHAKE_OFF_TIPS_DROP_DISTANCE,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
from opentrons.hardware_control.dev_types import PipetteDict
|
|
42
|
+
from .pipette import Pipette
|
|
43
|
+
|
|
44
|
+
# TODO both pipette handlers should be combined once the pipette configurations
|
|
45
|
+
# are unified AND we separate out the concept of changing pipette state versus static state
|
|
46
|
+
|
|
47
|
+
MountType = TypeVar("MountType", top_types.Mount, OT3Mount)
|
|
48
|
+
|
|
49
|
+
InstrumentsByMount = Dict[MountType, Optional[Pipette]]
|
|
50
|
+
PipetteHandlingData = Tuple[Pipette, MountType]
|
|
51
|
+
|
|
52
|
+
MOD_LOG = logging.getLogger(__name__)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@dataclass(frozen=True)
|
|
56
|
+
class LiquidActionSpec:
|
|
57
|
+
axis: Axis
|
|
58
|
+
volume: float
|
|
59
|
+
plunger_distance: float
|
|
60
|
+
speed: float
|
|
61
|
+
instr: Pipette
|
|
62
|
+
current: float
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@dataclass(frozen=True)
|
|
66
|
+
class PickUpTipPressSpec:
|
|
67
|
+
relative_down: top_types.Point
|
|
68
|
+
relative_up: top_types.Point
|
|
69
|
+
current: Dict[Axis, float]
|
|
70
|
+
speed: float
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@dataclass(frozen=True)
|
|
74
|
+
class PickUpTipSpec:
|
|
75
|
+
plunger_prep_pos: float
|
|
76
|
+
plunger_currents: Dict[Axis, float]
|
|
77
|
+
presses: List[PickUpTipPressSpec]
|
|
78
|
+
shake_off_list: List[Tuple[top_types.Point, Optional[float]]]
|
|
79
|
+
retract_target: float
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@dataclass(frozen=True)
|
|
83
|
+
class DropTipMove:
|
|
84
|
+
target_position: float
|
|
85
|
+
current: Dict[Axis, float]
|
|
86
|
+
speed: Optional[float]
|
|
87
|
+
home_after: bool = False
|
|
88
|
+
home_after_safety_margin: float = 0
|
|
89
|
+
home_axes: Sequence[Axis] = tuple()
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@dataclass(frozen=True)
|
|
93
|
+
class DropTipSpec:
|
|
94
|
+
drop_moves: List[DropTipMove]
|
|
95
|
+
shake_moves: List[Tuple[top_types.Point, Optional[float]]]
|
|
96
|
+
ending_current: Dict[Axis, float]
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class PipetteHandlerProvider(Generic[MountType]):
|
|
100
|
+
IHP_LOG = MOD_LOG.getChild("InstrumentHandler")
|
|
101
|
+
|
|
102
|
+
def __init__(self, attached_instruments: InstrumentsByMount[MountType]):
|
|
103
|
+
assert attached_instruments
|
|
104
|
+
self._attached_instruments: InstrumentsByMount[MountType] = attached_instruments
|
|
105
|
+
self._ihp_log = PipetteHandlerProvider.IHP_LOG.getChild(str(id(self)))
|
|
106
|
+
|
|
107
|
+
def reset_instrument(self, mount: Optional[MountType] = None) -> None:
|
|
108
|
+
"""
|
|
109
|
+
Reset the internal state of a pipette by its mount, without doing
|
|
110
|
+
any lower level reconfiguration. This is useful to make sure that no
|
|
111
|
+
settings changes from a protocol persist.
|
|
112
|
+
|
|
113
|
+
:param mount: If specified, reset that mount. If not specified,
|
|
114
|
+
reset both
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
def _reset(m: MountType) -> None:
|
|
118
|
+
if isinstance(m, top_types.Mount) and m not in top_types.Mount.ot2_mounts():
|
|
119
|
+
self._ihp_log.warning(
|
|
120
|
+
"Received a non OT2 mount for resetting. Skipping"
|
|
121
|
+
)
|
|
122
|
+
return
|
|
123
|
+
self._ihp_log.info(f"Resetting configuration for {m}")
|
|
124
|
+
p = self._attached_instruments[m]
|
|
125
|
+
if not p:
|
|
126
|
+
return
|
|
127
|
+
if isinstance(m, OT3Mount):
|
|
128
|
+
# This is to satisfy lint. Code will be cleaner once
|
|
129
|
+
# we can combine the pipette handler for OT2 and OT3
|
|
130
|
+
# pipettes again.
|
|
131
|
+
p.reset_pipette_offset(m.to_mount(), to_default=False)
|
|
132
|
+
else:
|
|
133
|
+
p.reset_pipette_offset(m, to_default=False)
|
|
134
|
+
p.reload_configurations()
|
|
135
|
+
p.reset_state()
|
|
136
|
+
|
|
137
|
+
if not mount:
|
|
138
|
+
for m in type(list(self._attached_instruments.keys())[0]):
|
|
139
|
+
_reset(m)
|
|
140
|
+
else:
|
|
141
|
+
_reset(mount)
|
|
142
|
+
|
|
143
|
+
def reset_instrument_offset(self, mount: MountType, to_default: bool) -> None:
|
|
144
|
+
"""
|
|
145
|
+
Temporarily reset the pipette offset to default values.
|
|
146
|
+
:param mount: Modify the given mount.
|
|
147
|
+
"""
|
|
148
|
+
if isinstance(mount, OT3Mount):
|
|
149
|
+
# This is to satisfy lint. Code will be cleaner once
|
|
150
|
+
# we can combine the pipette handler for OT2 and OT3
|
|
151
|
+
# pipettes again.
|
|
152
|
+
pipette = self.get_pipette(mount)
|
|
153
|
+
pipette.reset_pipette_offset(mount.to_mount(), to_default)
|
|
154
|
+
else:
|
|
155
|
+
pipette = self.get_pipette(mount)
|
|
156
|
+
pipette.reset_pipette_offset(mount, to_default)
|
|
157
|
+
|
|
158
|
+
def save_instrument_offset(self, mount: MountType, delta: top_types.Point) -> None:
|
|
159
|
+
"""
|
|
160
|
+
Save a new instrument offset the pipette offset to a particular value.
|
|
161
|
+
:param mount: Modify the given mount.
|
|
162
|
+
:param delta: The offset to set for the pipette.
|
|
163
|
+
"""
|
|
164
|
+
if isinstance(mount, OT3Mount):
|
|
165
|
+
# This is to satisfy lint. Code will be cleaner once
|
|
166
|
+
# we can combine the pipette handler for OT2 and OT3
|
|
167
|
+
# pipettes again.
|
|
168
|
+
pipette = self.get_pipette(mount)
|
|
169
|
+
pipette.save_pipette_offset(mount.to_mount(), delta)
|
|
170
|
+
else:
|
|
171
|
+
pipette = self.get_pipette(mount)
|
|
172
|
+
pipette.save_pipette_offset(mount, delta)
|
|
173
|
+
|
|
174
|
+
# TODO(mc, 2022-01-11): change returned map value type to `Optional[PipetteDict]`
|
|
175
|
+
# instead of potentially returning an empty dict
|
|
176
|
+
def get_attached_instruments(self) -> Dict[MountType, PipetteDict]:
|
|
177
|
+
"""Get the status dicts of the cached attached instruments.
|
|
178
|
+
|
|
179
|
+
Also available as :py:meth:`get_attached_instruments`.
|
|
180
|
+
|
|
181
|
+
This returns a dictified version of the
|
|
182
|
+
:py:class:`hardware_control.instruments.pipette.Pipette` as a dict keyed by
|
|
183
|
+
the :py:class:`top_types.Mount` to which the pipette is attached.
|
|
184
|
+
If no pipette is attached on a given mount, the mount key will
|
|
185
|
+
still be present but will have the value ``None``.
|
|
186
|
+
|
|
187
|
+
Note that this is only a query of a cached value; to actively scan
|
|
188
|
+
for changes, use :py:meth:`cache_instruments`. This process deactivates
|
|
189
|
+
the motors and should be used sparingly.
|
|
190
|
+
"""
|
|
191
|
+
return {
|
|
192
|
+
m: self.get_attached_instrument(m)
|
|
193
|
+
for m in self._attached_instruments.keys()
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
# TODO(mc, 2022-01-11): change return type to `Optional[PipetteDict]` instead
|
|
197
|
+
# of potentially returning an empty dict
|
|
198
|
+
def get_attached_instrument(self, mount: MountType) -> PipetteDict:
|
|
199
|
+
instr = self._attached_instruments[mount]
|
|
200
|
+
result: Dict[str, Any] = {}
|
|
201
|
+
if instr:
|
|
202
|
+
configs = [
|
|
203
|
+
"name",
|
|
204
|
+
"aspirate_flow_rate",
|
|
205
|
+
"dispense_flow_rate",
|
|
206
|
+
"pipette_id",
|
|
207
|
+
"current_volume",
|
|
208
|
+
"display_name",
|
|
209
|
+
"tip_length",
|
|
210
|
+
"model",
|
|
211
|
+
"blow_out_flow_rate",
|
|
212
|
+
"working_volume",
|
|
213
|
+
"tip_overlap",
|
|
214
|
+
"versioned_tip_overlap",
|
|
215
|
+
"available_volume",
|
|
216
|
+
"return_tip_height",
|
|
217
|
+
"default_aspirate_flow_rates",
|
|
218
|
+
"default_blow_out_flow_rates",
|
|
219
|
+
"default_dispense_flow_rates",
|
|
220
|
+
"back_compat_names",
|
|
221
|
+
"supported_tips",
|
|
222
|
+
"lld_settings",
|
|
223
|
+
]
|
|
224
|
+
|
|
225
|
+
instr_dict = instr.as_dict()
|
|
226
|
+
# TODO (spp, 2021-08-27): Revisit this logic. Why do we need to build
|
|
227
|
+
# this dict newly every time? Any why only a few items are being updated?
|
|
228
|
+
for key in configs:
|
|
229
|
+
result[key] = instr_dict[key]
|
|
230
|
+
result["current_nozzle_map"] = instr.nozzle_manager.current_configuration
|
|
231
|
+
result["min_volume"] = instr.liquid_class.min_volume
|
|
232
|
+
result["max_volume"] = instr.liquid_class.max_volume
|
|
233
|
+
result["channels"] = instr._max_channels.value
|
|
234
|
+
result["has_tip"] = instr.has_tip
|
|
235
|
+
result["tip_length"] = instr.current_tip_length
|
|
236
|
+
result["aspirate_speed"] = self.plunger_speed(
|
|
237
|
+
instr, instr.aspirate_flow_rate, "aspirate"
|
|
238
|
+
)
|
|
239
|
+
result["dispense_speed"] = self.plunger_speed(
|
|
240
|
+
instr, instr.dispense_flow_rate, "dispense"
|
|
241
|
+
)
|
|
242
|
+
result["blow_out_speed"] = self.plunger_speed(
|
|
243
|
+
instr, instr.blow_out_flow_rate, "dispense"
|
|
244
|
+
)
|
|
245
|
+
result["ready_to_aspirate"] = instr.ready_to_aspirate
|
|
246
|
+
result["default_blow_out_speeds"] = {
|
|
247
|
+
alvl: self.plunger_speed(instr, fr, "blowout")
|
|
248
|
+
for alvl, fr in instr.blow_out_flow_rates_lookup.items()
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
result["default_dispense_speeds"] = {
|
|
252
|
+
alvl: self.plunger_speed(instr, fr, "dispense")
|
|
253
|
+
for alvl, fr in instr.dispense_flow_rates_lookup.items()
|
|
254
|
+
}
|
|
255
|
+
result["default_aspirate_speeds"] = {
|
|
256
|
+
alvl: self.plunger_speed(instr, fr, "aspirate")
|
|
257
|
+
for alvl, fr in instr.aspirate_flow_rates_lookup.items()
|
|
258
|
+
}
|
|
259
|
+
result[
|
|
260
|
+
"pipette_bounding_box_offsets"
|
|
261
|
+
] = instr.config.pipette_bounding_box_offsets
|
|
262
|
+
result["lld_settings"] = instr.config.lld_settings
|
|
263
|
+
result["plunger_positions"] = {
|
|
264
|
+
"top": instr.plunger_positions.top,
|
|
265
|
+
"bottom": instr.plunger_positions.bottom,
|
|
266
|
+
"blow_out": instr.plunger_positions.blow_out,
|
|
267
|
+
"drop_tip": instr.plunger_positions.drop_tip,
|
|
268
|
+
}
|
|
269
|
+
result["shaft_ul_per_mm"] = instr.config.shaft_ul_per_mm
|
|
270
|
+
return cast(PipetteDict, result)
|
|
271
|
+
|
|
272
|
+
@property
|
|
273
|
+
def attached_instruments(self) -> Dict[MountType, PipetteDict]:
|
|
274
|
+
return self.get_attached_instruments()
|
|
275
|
+
|
|
276
|
+
@property
|
|
277
|
+
def attached_pipettes(self) -> Dict[MountType, PipetteDict]:
|
|
278
|
+
return self.get_attached_instruments()
|
|
279
|
+
|
|
280
|
+
@property
|
|
281
|
+
def get_attached_pipettes(self) -> Dict[MountType, PipetteDict]:
|
|
282
|
+
return self.get_attached_instruments()
|
|
283
|
+
|
|
284
|
+
@property
|
|
285
|
+
def hardware_instruments(self) -> InstrumentsByMount[MountType]:
|
|
286
|
+
"""Do not write new code that uses this."""
|
|
287
|
+
return self._attached_instruments
|
|
288
|
+
|
|
289
|
+
def set_current_tiprack_diameter(
|
|
290
|
+
self, mount: MountType, tiprack_diameter: float
|
|
291
|
+
) -> None:
|
|
292
|
+
instr = self.get_pipette(mount)
|
|
293
|
+
self._ihp_log.info(
|
|
294
|
+
"Updating tip rack diameter on pipette mount: "
|
|
295
|
+
f"{mount}, tip diameter: {tiprack_diameter} mm"
|
|
296
|
+
)
|
|
297
|
+
instr.current_tiprack_diameter = tiprack_diameter
|
|
298
|
+
|
|
299
|
+
def set_working_volume(self, mount: MountType, tip_volume: float) -> None:
|
|
300
|
+
instr = self.get_pipette(mount)
|
|
301
|
+
if not instr:
|
|
302
|
+
raise top_types.PipetteNotAttachedError(
|
|
303
|
+
"No pipette attached to {} mount".format(mount.name)
|
|
304
|
+
)
|
|
305
|
+
self._ihp_log.info(
|
|
306
|
+
"Updating working volume on pipette mount:"
|
|
307
|
+
f"{mount}, tip volume: {tip_volume} ul"
|
|
308
|
+
)
|
|
309
|
+
instr.working_volume = tip_volume
|
|
310
|
+
|
|
311
|
+
def calibrate_plunger(
|
|
312
|
+
self,
|
|
313
|
+
mount: MountType,
|
|
314
|
+
top: Optional[float] = None,
|
|
315
|
+
bottom: Optional[float] = None,
|
|
316
|
+
blow_out: Optional[float] = None,
|
|
317
|
+
drop_tip: Optional[float] = None,
|
|
318
|
+
) -> None:
|
|
319
|
+
"""
|
|
320
|
+
Set calibration values for the pipette plunger.
|
|
321
|
+
This can be called multiple times as the user sets each value,
|
|
322
|
+
or you can set them all at once.
|
|
323
|
+
:param top: Touching but not engaging the plunger.
|
|
324
|
+
:param bottom: Must be above the pipette's physical hard-stop, while
|
|
325
|
+
still leaving enough room for 'blow_out'
|
|
326
|
+
:param blow_out: Plunger is pushed down enough to expel all liquids.
|
|
327
|
+
:param drop_tip: Position that causes the tip to be released from the
|
|
328
|
+
pipette
|
|
329
|
+
"""
|
|
330
|
+
instr = self.get_pipette(mount)
|
|
331
|
+
pos_dict: Dict[str, float] = {
|
|
332
|
+
"top": instr.plunger_positions.top,
|
|
333
|
+
"bottom": instr.plunger_positions.bottom,
|
|
334
|
+
"blow_out": instr.plunger_positions.blow_out,
|
|
335
|
+
"drop_tip": instr.plunger_positions.drop_tip,
|
|
336
|
+
}
|
|
337
|
+
if top is not None:
|
|
338
|
+
pos_dict["top"] = top
|
|
339
|
+
if bottom is not None:
|
|
340
|
+
pos_dict["bottom"] = bottom
|
|
341
|
+
if blow_out is not None:
|
|
342
|
+
pos_dict["blow_out"] = blow_out
|
|
343
|
+
if drop_tip is not None:
|
|
344
|
+
pos_dict["drop_tip"] = drop_tip
|
|
345
|
+
instr.update_config_item(pos_dict)
|
|
346
|
+
|
|
347
|
+
def set_flow_rate(
|
|
348
|
+
self,
|
|
349
|
+
mount: MountType,
|
|
350
|
+
aspirate: Optional[float] = None,
|
|
351
|
+
dispense: Optional[float] = None,
|
|
352
|
+
blow_out: Optional[float] = None,
|
|
353
|
+
) -> None:
|
|
354
|
+
this_pipette = self.get_pipette(mount)
|
|
355
|
+
if aspirate:
|
|
356
|
+
this_pipette.aspirate_flow_rate = aspirate
|
|
357
|
+
if dispense:
|
|
358
|
+
this_pipette.dispense_flow_rate = dispense
|
|
359
|
+
if blow_out:
|
|
360
|
+
this_pipette.blow_out_flow_rate = blow_out
|
|
361
|
+
|
|
362
|
+
def set_pipette_speed(
|
|
363
|
+
self,
|
|
364
|
+
mount: MountType,
|
|
365
|
+
aspirate: Optional[float] = None,
|
|
366
|
+
dispense: Optional[float] = None,
|
|
367
|
+
blow_out: Optional[float] = None,
|
|
368
|
+
) -> None:
|
|
369
|
+
this_pipette = self.get_pipette(mount)
|
|
370
|
+
if aspirate:
|
|
371
|
+
this_pipette.aspirate_flow_rate = self.plunger_flowrate(
|
|
372
|
+
this_pipette, aspirate, "aspirate"
|
|
373
|
+
)
|
|
374
|
+
if dispense:
|
|
375
|
+
this_pipette.dispense_flow_rate = self.plunger_flowrate(
|
|
376
|
+
this_pipette, dispense, "dispense"
|
|
377
|
+
)
|
|
378
|
+
if blow_out:
|
|
379
|
+
this_pipette.blow_out_flow_rate = self.plunger_flowrate(
|
|
380
|
+
this_pipette, blow_out, "dispense"
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
def instrument_max_height(
|
|
384
|
+
self,
|
|
385
|
+
mount: MountType,
|
|
386
|
+
retract_distance: float,
|
|
387
|
+
critical_point: Optional[CriticalPoint],
|
|
388
|
+
) -> float:
|
|
389
|
+
"""Return max achievable height of the attached instrument
|
|
390
|
+
based on the current critical point
|
|
391
|
+
"""
|
|
392
|
+
pip = self.get_pipette(mount)
|
|
393
|
+
cp = self.critical_point_for(mount, critical_point)
|
|
394
|
+
|
|
395
|
+
max_height = (
|
|
396
|
+
pip.config.mount_configurations.homePosition - retract_distance + cp.z
|
|
397
|
+
)
|
|
398
|
+
|
|
399
|
+
return max_height
|
|
400
|
+
|
|
401
|
+
async def reset(self) -> None:
|
|
402
|
+
self._attached_instruments = {
|
|
403
|
+
k: None for k in self._attached_instruments.keys()
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
async def update_nozzle_configuration(
|
|
407
|
+
self,
|
|
408
|
+
mount: MountType,
|
|
409
|
+
back_left_nozzle: str,
|
|
410
|
+
front_right_nozzle: str,
|
|
411
|
+
starting_nozzle: Optional[str] = None,
|
|
412
|
+
) -> None:
|
|
413
|
+
instr = self._attached_instruments[mount]
|
|
414
|
+
if instr:
|
|
415
|
+
instr.update_nozzle_configuration(
|
|
416
|
+
back_left_nozzle, front_right_nozzle, starting_nozzle
|
|
417
|
+
)
|
|
418
|
+
|
|
419
|
+
async def reset_nozzle_configuration(self, mount: MountType) -> None:
|
|
420
|
+
instr = self._attached_instruments[mount]
|
|
421
|
+
if instr:
|
|
422
|
+
instr.reset_nozzle_configuration()
|
|
423
|
+
|
|
424
|
+
def add_tip(self, mount: MountType, tip_length: float) -> None:
|
|
425
|
+
instr = self._attached_instruments[mount]
|
|
426
|
+
attached = self.attached_instruments
|
|
427
|
+
instr_dict = attached[mount]
|
|
428
|
+
if instr and not instr.has_tip:
|
|
429
|
+
instr.add_tip(tip_length=tip_length)
|
|
430
|
+
# TODO (spp, 2021-08-27): These items are being updated in a local copy
|
|
431
|
+
# of the PipetteDict, which gets thrown away. Fix this.
|
|
432
|
+
instr_dict["has_tip"] = True
|
|
433
|
+
instr_dict["tip_length"] = tip_length
|
|
434
|
+
else:
|
|
435
|
+
self._ihp_log.warning(
|
|
436
|
+
f"attach tip called while tip already attached to {instr}"
|
|
437
|
+
)
|
|
438
|
+
|
|
439
|
+
def cache_tip(self, mount: MountType, tip_length: float) -> None:
|
|
440
|
+
instrument = self.get_pipette(mount)
|
|
441
|
+
if instrument.has_tip:
|
|
442
|
+
# instrument.add_tip() would raise an AssertionError if we tried to overwrite an existing tip.
|
|
443
|
+
instrument.remove_tip()
|
|
444
|
+
instrument.add_tip(tip_length=tip_length)
|
|
445
|
+
instrument.set_current_volume(0)
|
|
446
|
+
|
|
447
|
+
def remove_tip(self, mount: MountType) -> None:
|
|
448
|
+
instr = self._attached_instruments[mount]
|
|
449
|
+
attached = self.attached_instruments
|
|
450
|
+
instr_dict = attached[mount]
|
|
451
|
+
if instr and instr.has_tip:
|
|
452
|
+
instr.remove_tip()
|
|
453
|
+
# TODO (spp, 2021-08-27): These items are being updated in a local copy
|
|
454
|
+
# of the PipetteDict, which gets thrown away. Fix this.
|
|
455
|
+
instr_dict["has_tip"] = False
|
|
456
|
+
instr_dict["tip_length"] = 0.0
|
|
457
|
+
else:
|
|
458
|
+
self._ihp_log.warning("detach tip called with no tip")
|
|
459
|
+
|
|
460
|
+
def critical_point_for(
|
|
461
|
+
self, mount: MountType, cp_override: Optional[CriticalPoint] = None
|
|
462
|
+
) -> top_types.Point:
|
|
463
|
+
"""Return the current critical point of the specified mount.
|
|
464
|
+
|
|
465
|
+
The mount's critical point is the position of the mount itself, if no
|
|
466
|
+
pipette is attached, or the pipette's critical point (which depends on
|
|
467
|
+
tip status).
|
|
468
|
+
|
|
469
|
+
If `cp_override` is specified, and that critical point actually exists,
|
|
470
|
+
it will be used instead. Invalid `cp_override`s are ignored.
|
|
471
|
+
"""
|
|
472
|
+
pip = self._attached_instruments[mount]
|
|
473
|
+
if pip is not None and cp_override != CriticalPoint.MOUNT:
|
|
474
|
+
return pip.critical_point(cp_override)
|
|
475
|
+
else:
|
|
476
|
+
# This offset is required because the motor driver coordinate system is
|
|
477
|
+
# configured such that the end of a p300 single gen1's tip is 0.
|
|
478
|
+
return top_types.Point(0, 0, 30)
|
|
479
|
+
|
|
480
|
+
def ready_for_tip_action(
|
|
481
|
+
self, target: Pipette, action: HardwareAction, mount: MountType
|
|
482
|
+
) -> None:
|
|
483
|
+
if not target.has_tip:
|
|
484
|
+
raise UnexpectedTipRemovalError(action.name, target.name, mount.name)
|
|
485
|
+
if (
|
|
486
|
+
action == HardwareAction.ASPIRATE
|
|
487
|
+
and target.current_volume == 0
|
|
488
|
+
and not target.ready_to_aspirate
|
|
489
|
+
):
|
|
490
|
+
raise RuntimeError("Pipette not ready to aspirate")
|
|
491
|
+
self._ihp_log.debug(f"{action} on {target.name}")
|
|
492
|
+
|
|
493
|
+
def plunger_position(
|
|
494
|
+
self, instr: Pipette, ul: float, action: "UlPerMmAction"
|
|
495
|
+
) -> float:
|
|
496
|
+
mm = ul / instr.ul_per_mm(ul, action)
|
|
497
|
+
position = mm + instr.plunger_positions.bottom
|
|
498
|
+
return round(position, 6)
|
|
499
|
+
|
|
500
|
+
def plunger_speed(
|
|
501
|
+
self, instr: Pipette, ul_per_s: float, action: "UlPerMmAction"
|
|
502
|
+
) -> float:
|
|
503
|
+
mm_per_s = ul_per_s / instr.ul_per_mm(instr.liquid_class.max_volume, action)
|
|
504
|
+
return round(mm_per_s, 6)
|
|
505
|
+
|
|
506
|
+
def plunger_flowrate(
|
|
507
|
+
self, instr: Pipette, mm_per_s: float, action: "UlPerMmAction"
|
|
508
|
+
) -> float:
|
|
509
|
+
ul_per_s = mm_per_s * instr.ul_per_mm(instr.liquid_class.max_volume, action)
|
|
510
|
+
return round(ul_per_s, 6)
|
|
511
|
+
|
|
512
|
+
def plan_check_aspirate(
|
|
513
|
+
self,
|
|
514
|
+
mount: MountType,
|
|
515
|
+
volume: Optional[float],
|
|
516
|
+
rate: float,
|
|
517
|
+
) -> Optional[LiquidActionSpec]:
|
|
518
|
+
"""Check preconditions for aspirate, parse args, and calculate positions.
|
|
519
|
+
|
|
520
|
+
While the mechanics of issuing an aspirate move itself are left to child
|
|
521
|
+
classes, determining things like aspiration volume from the allowed argument
|
|
522
|
+
types is invariant between machines, and this method gathers that functionality.
|
|
523
|
+
|
|
524
|
+
Coalesce
|
|
525
|
+
- Optional volumes
|
|
526
|
+
|
|
527
|
+
Check
|
|
528
|
+
- Aspiration volumes compared to max and remaining
|
|
529
|
+
|
|
530
|
+
Calculate
|
|
531
|
+
- Plunger distances (possibly calling an overridden plunger_volume)
|
|
532
|
+
"""
|
|
533
|
+
instrument = self.get_pipette(mount)
|
|
534
|
+
self.ready_for_tip_action(instrument, HardwareAction.ASPIRATE, mount)
|
|
535
|
+
if volume is None:
|
|
536
|
+
self._ihp_log.debug(
|
|
537
|
+
"No aspirate volume defined. Aspirating up to "
|
|
538
|
+
"max_volume for the pipette"
|
|
539
|
+
)
|
|
540
|
+
asp_vol = instrument.available_volume
|
|
541
|
+
else:
|
|
542
|
+
asp_vol = volume
|
|
543
|
+
|
|
544
|
+
if asp_vol == 0:
|
|
545
|
+
return None
|
|
546
|
+
|
|
547
|
+
assert instrument.ok_to_add_volume(
|
|
548
|
+
asp_vol
|
|
549
|
+
), "Cannot aspirate more than pipette max volume"
|
|
550
|
+
|
|
551
|
+
dist = self.plunger_position(
|
|
552
|
+
instrument, instrument.current_volume + asp_vol, "aspirate"
|
|
553
|
+
)
|
|
554
|
+
speed = self.plunger_speed(
|
|
555
|
+
instrument, instrument.aspirate_flow_rate * rate, "aspirate"
|
|
556
|
+
)
|
|
557
|
+
if isinstance(mount, OT3Mount):
|
|
558
|
+
return LiquidActionSpec(
|
|
559
|
+
axis=Axis.of_main_tool_actuator(mount),
|
|
560
|
+
volume=asp_vol,
|
|
561
|
+
plunger_distance=dist,
|
|
562
|
+
speed=speed,
|
|
563
|
+
instr=instrument,
|
|
564
|
+
current=instrument.plunger_motor_current.run,
|
|
565
|
+
)
|
|
566
|
+
else:
|
|
567
|
+
return LiquidActionSpec(
|
|
568
|
+
axis=Axis.of_plunger(mount),
|
|
569
|
+
volume=asp_vol,
|
|
570
|
+
plunger_distance=dist,
|
|
571
|
+
speed=speed,
|
|
572
|
+
instr=instrument,
|
|
573
|
+
current=instrument.plunger_motor_current.run,
|
|
574
|
+
)
|
|
575
|
+
|
|
576
|
+
def plan_check_dispense(
|
|
577
|
+
self,
|
|
578
|
+
mount: MountType,
|
|
579
|
+
volume: Optional[float],
|
|
580
|
+
rate: float,
|
|
581
|
+
push_out: Optional[float],
|
|
582
|
+
) -> Optional[LiquidActionSpec]:
|
|
583
|
+
"""Check preconditions for dispense, parse args, and calculate positions.
|
|
584
|
+
|
|
585
|
+
While the mechanics of issuing a dispense move itself are left to child
|
|
586
|
+
classes, determining things like dispense volume from the allowed argument
|
|
587
|
+
types is invariant between machines, and this method gathers that functionality.
|
|
588
|
+
|
|
589
|
+
Coalesce
|
|
590
|
+
- Optional volumes
|
|
591
|
+
|
|
592
|
+
Check
|
|
593
|
+
- Dispense volumes compared to max and remaining
|
|
594
|
+
|
|
595
|
+
Calculate
|
|
596
|
+
- Plunger distances (possibly calling an overridden plunger_volume)
|
|
597
|
+
"""
|
|
598
|
+
|
|
599
|
+
instrument = self.get_pipette(mount)
|
|
600
|
+
self.ready_for_tip_action(instrument, HardwareAction.DISPENSE, mount)
|
|
601
|
+
|
|
602
|
+
if volume is None:
|
|
603
|
+
disp_vol = instrument.current_volume
|
|
604
|
+
self._ihp_log.debug(
|
|
605
|
+
"No dispense volume specified. Dispensing all "
|
|
606
|
+
"remaining liquid ({}uL) from pipette".format(disp_vol)
|
|
607
|
+
)
|
|
608
|
+
else:
|
|
609
|
+
disp_vol = volume
|
|
610
|
+
|
|
611
|
+
# Ensure we don't dispense more than the current volume.
|
|
612
|
+
#
|
|
613
|
+
# This clamping is inconsistent with plan_check_aspirate(), which asserts
|
|
614
|
+
# that its input is in bounds instead of clamping it. This is left to avoid
|
|
615
|
+
# disturbing Python protocols with apiLevel <= 2.13. In newer Python protocols,
|
|
616
|
+
# the Protocol Engine layer applies its own bounds checking.
|
|
617
|
+
disp_vol = min(instrument.current_volume, disp_vol)
|
|
618
|
+
|
|
619
|
+
if disp_vol == 0:
|
|
620
|
+
return None
|
|
621
|
+
|
|
622
|
+
is_full_dispense = numpy.isclose(instrument.current_volume - disp_vol, 0)
|
|
623
|
+
|
|
624
|
+
if is_full_dispense:
|
|
625
|
+
if push_out is None:
|
|
626
|
+
push_out_ul = instrument.push_out_volume
|
|
627
|
+
else:
|
|
628
|
+
push_out_ul = push_out
|
|
629
|
+
else:
|
|
630
|
+
if push_out is not None and push_out != 0:
|
|
631
|
+
raise CommandPreconditionViolated(
|
|
632
|
+
message="Cannot push_out on a dispense that does not leave the pipette empty",
|
|
633
|
+
detail={
|
|
634
|
+
"command": "dispense",
|
|
635
|
+
"remaining-volume": str(instrument.current_volume - disp_vol),
|
|
636
|
+
},
|
|
637
|
+
)
|
|
638
|
+
push_out_ul = 0
|
|
639
|
+
|
|
640
|
+
push_out_dist_mm = push_out_ul / instrument.ul_per_mm(push_out_ul, "blowout")
|
|
641
|
+
|
|
642
|
+
if not instrument.ok_to_push_out(push_out_dist_mm):
|
|
643
|
+
raise CommandPreconditionViolated(
|
|
644
|
+
message="Cannot push_out more than pipette max blowout volume.",
|
|
645
|
+
detail={
|
|
646
|
+
"command": "dispense",
|
|
647
|
+
},
|
|
648
|
+
)
|
|
649
|
+
|
|
650
|
+
dist = self.plunger_position(
|
|
651
|
+
instrument, instrument.current_volume - disp_vol, "dispense"
|
|
652
|
+
)
|
|
653
|
+
speed = self.plunger_speed(
|
|
654
|
+
instrument, instrument.dispense_flow_rate * rate, "dispense"
|
|
655
|
+
)
|
|
656
|
+
if isinstance(mount, top_types.Mount):
|
|
657
|
+
return LiquidActionSpec(
|
|
658
|
+
axis=Axis.of_plunger(mount),
|
|
659
|
+
volume=disp_vol,
|
|
660
|
+
plunger_distance=dist + push_out_dist_mm,
|
|
661
|
+
speed=speed,
|
|
662
|
+
instr=instrument,
|
|
663
|
+
current=instrument.plunger_motor_current.run,
|
|
664
|
+
)
|
|
665
|
+
else:
|
|
666
|
+
return LiquidActionSpec(
|
|
667
|
+
axis=Axis.of_main_tool_actuator(mount),
|
|
668
|
+
volume=disp_vol,
|
|
669
|
+
plunger_distance=dist,
|
|
670
|
+
speed=speed,
|
|
671
|
+
instr=instrument,
|
|
672
|
+
current=instrument.plunger_motor_current.run,
|
|
673
|
+
)
|
|
674
|
+
|
|
675
|
+
def plan_check_blow_out(self, mount: MountType) -> LiquidActionSpec:
|
|
676
|
+
"""Check preconditions and calculate values for blowout."""
|
|
677
|
+
instrument = self.get_pipette(mount)
|
|
678
|
+
speed = self.plunger_speed(
|
|
679
|
+
instrument, instrument.blow_out_flow_rate, "dispense"
|
|
680
|
+
)
|
|
681
|
+
|
|
682
|
+
if isinstance(mount, top_types.Mount):
|
|
683
|
+
return LiquidActionSpec(
|
|
684
|
+
axis=Axis.of_plunger(mount),
|
|
685
|
+
volume=0,
|
|
686
|
+
plunger_distance=instrument.plunger_positions.blow_out,
|
|
687
|
+
speed=speed,
|
|
688
|
+
instr=instrument,
|
|
689
|
+
current=instrument.plunger_motor_current.run,
|
|
690
|
+
)
|
|
691
|
+
else:
|
|
692
|
+
return LiquidActionSpec(
|
|
693
|
+
axis=Axis.of_main_tool_actuator(mount),
|
|
694
|
+
volume=0,
|
|
695
|
+
plunger_distance=instrument.plunger_positions.blow_out,
|
|
696
|
+
speed=speed,
|
|
697
|
+
instr=instrument,
|
|
698
|
+
current=instrument.plunger_motor_current.run,
|
|
699
|
+
)
|
|
700
|
+
|
|
701
|
+
@staticmethod
|
|
702
|
+
def _build_pickup_shakes(
|
|
703
|
+
instrument: Pipette,
|
|
704
|
+
) -> List[Tuple[top_types.Point, Optional[float]]]:
|
|
705
|
+
def build_one_shake() -> List[Tuple[top_types.Point, Optional[float]]]:
|
|
706
|
+
shake_dist = float(SHAKE_OFF_TIPS_PICKUP_DISTANCE)
|
|
707
|
+
shake_speed = float(SHAKE_OFF_TIPS_SPEED)
|
|
708
|
+
return [
|
|
709
|
+
(top_types.Point(-shake_dist, 0, 0), shake_speed), # left
|
|
710
|
+
(top_types.Point(2 * shake_dist, 0, 0), shake_speed), # right
|
|
711
|
+
(top_types.Point(-shake_dist, 0, 0), shake_speed), # center
|
|
712
|
+
(top_types.Point(0, -shake_dist, 0), shake_speed), # front
|
|
713
|
+
(top_types.Point(0, 2 * shake_dist, 0), shake_speed), # back
|
|
714
|
+
(top_types.Point(0, -shake_dist, 0), shake_speed), # center
|
|
715
|
+
(top_types.Point(0, 0, DROP_TIP_RELEASE_DISTANCE), None), # up
|
|
716
|
+
]
|
|
717
|
+
|
|
718
|
+
if Quirks.pickupTipShake in instrument.config.quirks:
|
|
719
|
+
return build_one_shake() + build_one_shake()
|
|
720
|
+
else:
|
|
721
|
+
return []
|
|
722
|
+
|
|
723
|
+
def plan_check_pick_up_tip(
|
|
724
|
+
self,
|
|
725
|
+
mount: MountType,
|
|
726
|
+
presses: Optional[int],
|
|
727
|
+
increment: Optional[float],
|
|
728
|
+
tip_length: float = 0,
|
|
729
|
+
) -> Tuple[PickUpTipSpec, Callable[[], None]]:
|
|
730
|
+
# Prechecks: ready for pickup tip and press/increment are valid
|
|
731
|
+
instrument = self.get_pipette(mount)
|
|
732
|
+
if instrument.has_tip:
|
|
733
|
+
raise UnexpectedTipAttachError("pick_up_tip", instrument.name, mount.name)
|
|
734
|
+
self._ihp_log.debug(f"Picking up tip on {mount.name}")
|
|
735
|
+
|
|
736
|
+
if presses is None or presses < 0:
|
|
737
|
+
checked_presses = instrument.pick_up_configurations.press_fit.presses
|
|
738
|
+
else:
|
|
739
|
+
checked_presses = presses
|
|
740
|
+
|
|
741
|
+
if not increment or increment < 0:
|
|
742
|
+
check_incr = instrument.pick_up_configurations.press_fit.increment
|
|
743
|
+
else:
|
|
744
|
+
check_incr = increment
|
|
745
|
+
|
|
746
|
+
pick_up_speed = instrument.get_pick_up_speed_by_configuration(
|
|
747
|
+
instrument.pick_up_configurations.press_fit
|
|
748
|
+
)
|
|
749
|
+
|
|
750
|
+
pick_up_distance = instrument.get_pick_up_distance_by_configuration(
|
|
751
|
+
instrument.pick_up_configurations.press_fit
|
|
752
|
+
)
|
|
753
|
+
|
|
754
|
+
def build_presses() -> Iterator[Tuple[float, float]]:
|
|
755
|
+
# Press the nozzle into the tip <presses> number of times,
|
|
756
|
+
# moving further by <increment> mm after each press
|
|
757
|
+
for i in range(checked_presses):
|
|
758
|
+
# move nozzle down into the tip
|
|
759
|
+
|
|
760
|
+
press_dist = -1.0 * pick_up_distance + -1.0 * check_incr * i
|
|
761
|
+
# move nozzle back up
|
|
762
|
+
backup_dist = -press_dist
|
|
763
|
+
yield (press_dist, backup_dist)
|
|
764
|
+
|
|
765
|
+
def add_tip_to_instr() -> None:
|
|
766
|
+
instrument.add_tip(tip_length=tip_length)
|
|
767
|
+
instrument.set_current_volume(0)
|
|
768
|
+
|
|
769
|
+
if isinstance(mount, top_types.Mount):
|
|
770
|
+
return (
|
|
771
|
+
PickUpTipSpec(
|
|
772
|
+
plunger_prep_pos=instrument.plunger_positions.bottom,
|
|
773
|
+
plunger_currents={
|
|
774
|
+
Axis.of_plunger(mount): instrument.plunger_motor_current.run
|
|
775
|
+
},
|
|
776
|
+
presses=[
|
|
777
|
+
PickUpTipPressSpec(
|
|
778
|
+
current={
|
|
779
|
+
Axis.by_mount(
|
|
780
|
+
mount
|
|
781
|
+
): instrument.get_pick_up_current_by_configuration(
|
|
782
|
+
instrument.pick_up_configurations.press_fit
|
|
783
|
+
)
|
|
784
|
+
},
|
|
785
|
+
speed=pick_up_speed,
|
|
786
|
+
relative_down=top_types.Point(0, 0, press_dist),
|
|
787
|
+
relative_up=top_types.Point(0, 0, backup_dist),
|
|
788
|
+
)
|
|
789
|
+
for press_dist, backup_dist in build_presses()
|
|
790
|
+
],
|
|
791
|
+
shake_off_list=self._build_pickup_shakes(instrument),
|
|
792
|
+
retract_target=pick_up_distance + check_incr * checked_presses + 2,
|
|
793
|
+
),
|
|
794
|
+
add_tip_to_instr,
|
|
795
|
+
)
|
|
796
|
+
else:
|
|
797
|
+
return (
|
|
798
|
+
PickUpTipSpec(
|
|
799
|
+
plunger_prep_pos=instrument.plunger_positions.bottom,
|
|
800
|
+
plunger_currents={
|
|
801
|
+
Axis.of_main_tool_actuator(
|
|
802
|
+
mount
|
|
803
|
+
): instrument.plunger_motor_current.run
|
|
804
|
+
},
|
|
805
|
+
presses=[
|
|
806
|
+
PickUpTipPressSpec(
|
|
807
|
+
current={
|
|
808
|
+
Axis.by_mount(
|
|
809
|
+
mount
|
|
810
|
+
): instrument.get_pick_up_current_by_configuration(
|
|
811
|
+
instrument.pick_up_configurations.press_fit
|
|
812
|
+
)
|
|
813
|
+
},
|
|
814
|
+
speed=pick_up_speed,
|
|
815
|
+
relative_down=top_types.Point(0, 0, press_dist),
|
|
816
|
+
relative_up=top_types.Point(0, 0, backup_dist),
|
|
817
|
+
)
|
|
818
|
+
for press_dist, backup_dist in build_presses()
|
|
819
|
+
],
|
|
820
|
+
shake_off_list=self._build_pickup_shakes(instrument),
|
|
821
|
+
retract_target=pick_up_distance + check_incr * checked_presses + 2,
|
|
822
|
+
),
|
|
823
|
+
add_tip_to_instr,
|
|
824
|
+
)
|
|
825
|
+
|
|
826
|
+
@staticmethod
|
|
827
|
+
def _shake_off_tips_drop(
|
|
828
|
+
tiprack_diameter: float,
|
|
829
|
+
) -> List[Tuple[top_types.Point, Optional[float]]]:
|
|
830
|
+
# tips don't always fall off, especially if resting against
|
|
831
|
+
# tiprack or other tips below it. To ensure the tip has fallen
|
|
832
|
+
# first, shake the pipette to dislodge partially-sealed tips,
|
|
833
|
+
# then second, raise the pipette so loosened tips have room to fall
|
|
834
|
+
shake_off_dist = SHAKE_OFF_TIPS_DROP_DISTANCE
|
|
835
|
+
if tiprack_diameter > 0.0:
|
|
836
|
+
shake_off_dist = min(shake_off_dist, tiprack_diameter / 4)
|
|
837
|
+
shake_off_dist = max(shake_off_dist, 1.0)
|
|
838
|
+
speed = SHAKE_OFF_TIPS_SPEED
|
|
839
|
+
return [
|
|
840
|
+
(top_types.Point(-shake_off_dist, 0, 0), speed), # left
|
|
841
|
+
(top_types.Point(2 * shake_off_dist, 0, 0), speed), # right
|
|
842
|
+
(top_types.Point(-shake_off_dist, 0, 0), speed), # center
|
|
843
|
+
(top_types.Point(0, 0, DROP_TIP_RELEASE_DISTANCE), None), # top
|
|
844
|
+
]
|
|
845
|
+
|
|
846
|
+
def _droptip_sequence_builder(
|
|
847
|
+
self,
|
|
848
|
+
bottom_pos: float,
|
|
849
|
+
droptip_pos: float,
|
|
850
|
+
plunger_currents: Dict[Axis, float],
|
|
851
|
+
drop_tip_currents: Dict[Axis, float],
|
|
852
|
+
speed: float,
|
|
853
|
+
home_after: bool,
|
|
854
|
+
home_axes: Sequence[Axis],
|
|
855
|
+
) -> Callable[[], List[DropTipMove]]:
|
|
856
|
+
def build() -> List[DropTipMove]:
|
|
857
|
+
base = [
|
|
858
|
+
DropTipMove(
|
|
859
|
+
target_position=bottom_pos, current=plunger_currents, speed=None
|
|
860
|
+
),
|
|
861
|
+
DropTipMove(
|
|
862
|
+
target_position=droptip_pos,
|
|
863
|
+
current=drop_tip_currents,
|
|
864
|
+
speed=speed,
|
|
865
|
+
home_after=home_after,
|
|
866
|
+
home_after_safety_margin=abs(bottom_pos - droptip_pos),
|
|
867
|
+
home_axes=home_axes,
|
|
868
|
+
),
|
|
869
|
+
DropTipMove( # always finish drop-tip at a known safe plunger position
|
|
870
|
+
target_position=bottom_pos, current=plunger_currents, speed=None
|
|
871
|
+
),
|
|
872
|
+
]
|
|
873
|
+
return base
|
|
874
|
+
|
|
875
|
+
return build
|
|
876
|
+
|
|
877
|
+
# todo(mm, 2024-10-17): The returned _remove_tips() callable is not used by anything
|
|
878
|
+
# anymore. Delete it.
|
|
879
|
+
def plan_check_drop_tip(
|
|
880
|
+
self,
|
|
881
|
+
mount: MountType,
|
|
882
|
+
home_after: bool,
|
|
883
|
+
) -> Tuple[DropTipSpec, Callable[[], None]]:
|
|
884
|
+
instrument = self.get_pipette(mount)
|
|
885
|
+
|
|
886
|
+
if not instrument.drop_configurations.plunger_eject:
|
|
887
|
+
raise CommandPreconditionViolated(
|
|
888
|
+
f"Pipette {instrument.name} on {mount.name} has no plunger eject configuration"
|
|
889
|
+
)
|
|
890
|
+
bottom = instrument.plunger_positions.bottom
|
|
891
|
+
droptip = instrument.plunger_positions.drop_tip
|
|
892
|
+
speed = instrument.drop_configurations.plunger_eject.speed
|
|
893
|
+
shakes: List[Tuple[top_types.Point, Optional[float]]] = []
|
|
894
|
+
if Quirks.dropTipShake in instrument.config.quirks:
|
|
895
|
+
diameter = instrument.current_tiprack_diameter
|
|
896
|
+
shakes = self._shake_off_tips_drop(diameter)
|
|
897
|
+
|
|
898
|
+
def _remove_tips() -> None:
|
|
899
|
+
instrument.set_current_volume(0)
|
|
900
|
+
instrument.current_tiprack_diameter = 0.0
|
|
901
|
+
instrument.remove_tip()
|
|
902
|
+
|
|
903
|
+
if isinstance(mount, top_types.Mount):
|
|
904
|
+
seq_builder_ot2 = self._droptip_sequence_builder(
|
|
905
|
+
bottom,
|
|
906
|
+
droptip,
|
|
907
|
+
{Axis.of_plunger(mount): instrument.plunger_motor_current.run},
|
|
908
|
+
{
|
|
909
|
+
Axis.of_plunger(
|
|
910
|
+
mount
|
|
911
|
+
): instrument.drop_configurations.plunger_eject.current
|
|
912
|
+
},
|
|
913
|
+
speed,
|
|
914
|
+
home_after,
|
|
915
|
+
(Axis.of_plunger(mount),),
|
|
916
|
+
)
|
|
917
|
+
seq_ot2 = seq_builder_ot2()
|
|
918
|
+
if Quirks.doubleDropTip in instrument.config.quirks:
|
|
919
|
+
seq_ot2 = seq_ot2 + seq_builder_ot2()
|
|
920
|
+
return (
|
|
921
|
+
DropTipSpec(
|
|
922
|
+
drop_moves=seq_ot2,
|
|
923
|
+
shake_moves=shakes,
|
|
924
|
+
ending_current={
|
|
925
|
+
Axis.of_plunger(mount): instrument.plunger_motor_current.run
|
|
926
|
+
},
|
|
927
|
+
),
|
|
928
|
+
_remove_tips,
|
|
929
|
+
)
|
|
930
|
+
else:
|
|
931
|
+
seq_builder_ot3 = self._droptip_sequence_builder(
|
|
932
|
+
bottom,
|
|
933
|
+
droptip,
|
|
934
|
+
{
|
|
935
|
+
Axis.of_main_tool_actuator(
|
|
936
|
+
mount
|
|
937
|
+
): instrument.plunger_motor_current.run
|
|
938
|
+
},
|
|
939
|
+
{
|
|
940
|
+
Axis.of_main_tool_actuator(
|
|
941
|
+
mount
|
|
942
|
+
): instrument.drop_configurations.plunger_eject.current
|
|
943
|
+
},
|
|
944
|
+
speed,
|
|
945
|
+
home_after,
|
|
946
|
+
(Axis.of_main_tool_actuator(mount),),
|
|
947
|
+
)
|
|
948
|
+
|
|
949
|
+
seq_ot3 = seq_builder_ot3()
|
|
950
|
+
if Quirks.doubleDropTip in instrument.config.quirks:
|
|
951
|
+
seq_ot3 = seq_ot3 + seq_builder_ot3()
|
|
952
|
+
return (
|
|
953
|
+
DropTipSpec(
|
|
954
|
+
drop_moves=seq_ot3,
|
|
955
|
+
shake_moves=shakes,
|
|
956
|
+
ending_current={
|
|
957
|
+
Axis.of_main_tool_actuator(
|
|
958
|
+
mount
|
|
959
|
+
): instrument.plunger_motor_current.run
|
|
960
|
+
},
|
|
961
|
+
),
|
|
962
|
+
_remove_tips,
|
|
963
|
+
)
|
|
964
|
+
|
|
965
|
+
def get_pipette(self, mount: MountType) -> Pipette:
|
|
966
|
+
pip = self._attached_instruments[mount]
|
|
967
|
+
if not pip:
|
|
968
|
+
raise top_types.PipetteNotAttachedError(
|
|
969
|
+
f"No pipette attached to {mount.name} mount"
|
|
970
|
+
)
|
|
971
|
+
return pip
|
|
972
|
+
|
|
973
|
+
async def set_liquid_class(self, mount: MountType, liquid_class: str) -> None:
|
|
974
|
+
pip = self.get_pipette(mount)
|
|
975
|
+
pip.set_liquid_class_by_name(liquid_class)
|
|
976
|
+
|
|
977
|
+
|
|
978
|
+
class OT3PipetteHandler(PipetteHandlerProvider[OT3Mount]):
|
|
979
|
+
"""Override for correct plunger_position."""
|
|
980
|
+
|
|
981
|
+
def plunger_position(
|
|
982
|
+
self, instr: Pipette, ul: float, action: "UlPerMmAction"
|
|
983
|
+
) -> float:
|
|
984
|
+
mm = ul / instr.ul_per_mm(ul, action)
|
|
985
|
+
position = instr.plunger_positions.bottom - mm
|
|
986
|
+
return round(position, 6)
|
|
987
|
+
|
|
988
|
+
def critical_point_for(
|
|
989
|
+
self, mount: MountType, cp_override: Optional[CriticalPoint] = None
|
|
990
|
+
) -> top_types.Point:
|
|
991
|
+
pip = self._attached_instruments[OT3Mount.from_mount(mount)]
|
|
992
|
+
if pip is not None and cp_override != CriticalPoint.MOUNT:
|
|
993
|
+
return pip.critical_point(cp_override)
|
|
994
|
+
else:
|
|
995
|
+
return top_types.Point(0, 0, 0)
|