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,731 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import logging
|
|
5
|
+
from typing import Callable, Optional, List, Dict, Mapping, Union, cast
|
|
6
|
+
from opentrons.drivers.rpi_drivers.types import USBPort
|
|
7
|
+
from opentrons.drivers.types import ThermocyclerLidStatus, Temperature, PlateTemperature
|
|
8
|
+
from opentrons.hardware_control.modules.lid_temp_status import LidTemperatureStatus
|
|
9
|
+
from opentrons.hardware_control.modules.plate_temp_status import PlateTemperatureStatus
|
|
10
|
+
from opentrons.hardware_control.modules.types import (
|
|
11
|
+
ModuleDisconnectedCallback,
|
|
12
|
+
TemperatureStatus,
|
|
13
|
+
)
|
|
14
|
+
from opentrons.hardware_control.poller import Reader, Poller
|
|
15
|
+
|
|
16
|
+
from ..execution_manager import ExecutionManager
|
|
17
|
+
from . import types, update, mod_abc
|
|
18
|
+
from opentrons.drivers.thermocycler import (
|
|
19
|
+
AbstractThermocyclerDriver,
|
|
20
|
+
SimulatingDriver,
|
|
21
|
+
ThermocyclerDriverV2,
|
|
22
|
+
ThermocyclerDriverFactory,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
log = logging.getLogger(__name__)
|
|
27
|
+
|
|
28
|
+
POLLING_FREQUENCY_SEC = 1.0
|
|
29
|
+
SIM_POLLING_FREQUENCY_SEC = POLLING_FREQUENCY_SEC / 50.0
|
|
30
|
+
|
|
31
|
+
V1_MODULE_STRING = "thermocyclerModuleV1"
|
|
32
|
+
V2_MODULE_STRING = "thermocyclerModuleV2"
|
|
33
|
+
|
|
34
|
+
DFU_PID = "df11"
|
|
35
|
+
|
|
36
|
+
_TC_PLATE_LIFT_OPEN_DEGREES = 20
|
|
37
|
+
_TC_PLATE_LIFT_RETURN_DEGREES = 23
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class ThermocyclerError(Exception):
|
|
41
|
+
pass
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _temperature_is_holding(status: Optional[TemperatureStatus]) -> bool:
|
|
45
|
+
if status in (TemperatureStatus.HOLDING, TemperatureStatus.IDLE):
|
|
46
|
+
return True
|
|
47
|
+
|
|
48
|
+
if status == TemperatureStatus.ERROR:
|
|
49
|
+
raise ThermocyclerError("Error occurred while waiting for temperature")
|
|
50
|
+
|
|
51
|
+
return False
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class Thermocycler(mod_abc.AbstractModule):
|
|
55
|
+
"""Hardware control interface for an attached Thermocycler."""
|
|
56
|
+
|
|
57
|
+
MODULE_TYPE = types.ModuleType.THERMOCYCLER
|
|
58
|
+
|
|
59
|
+
@classmethod
|
|
60
|
+
async def build(
|
|
61
|
+
cls,
|
|
62
|
+
port: str,
|
|
63
|
+
usb_port: USBPort,
|
|
64
|
+
hw_control_loop: asyncio.AbstractEventLoop,
|
|
65
|
+
execution_manager: Optional[ExecutionManager] = None,
|
|
66
|
+
poll_interval_seconds: Optional[float] = None,
|
|
67
|
+
simulating: bool = False,
|
|
68
|
+
sim_model: Optional[str] = None,
|
|
69
|
+
sim_serial_number: Optional[str] = None,
|
|
70
|
+
disconnected_callback: ModuleDisconnectedCallback = None,
|
|
71
|
+
) -> "Thermocycler":
|
|
72
|
+
"""
|
|
73
|
+
Build and connect to a Thermocycler
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
port: The port to connect to
|
|
77
|
+
usb_port: USB Port
|
|
78
|
+
hw_control_loop: The event loop running in the hardware control thread.
|
|
79
|
+
execution_manager: Execution manager.
|
|
80
|
+
poll_interval_seconds: Poll interval override.
|
|
81
|
+
simulating: whether to build a simulating driver
|
|
82
|
+
loop: Loop
|
|
83
|
+
sim_model: The model name used by simulator
|
|
84
|
+
disconnected_callback: Callback to inform the module controller that the device was disconnected
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
Thermocycler instance.
|
|
88
|
+
"""
|
|
89
|
+
driver: AbstractThermocyclerDriver
|
|
90
|
+
if not simulating:
|
|
91
|
+
driver = await ThermocyclerDriverFactory.create(
|
|
92
|
+
port=port, loop=hw_control_loop
|
|
93
|
+
)
|
|
94
|
+
poll_interval_seconds = poll_interval_seconds or POLLING_FREQUENCY_SEC
|
|
95
|
+
else:
|
|
96
|
+
driver = SimulatingDriver(model=sim_model, serial_number=sim_serial_number)
|
|
97
|
+
poll_interval_seconds = poll_interval_seconds or SIM_POLLING_FREQUENCY_SEC
|
|
98
|
+
|
|
99
|
+
reader = ThermocyclerReader(driver=driver)
|
|
100
|
+
poller = Poller(reader=reader, interval=poll_interval_seconds)
|
|
101
|
+
module = cls(
|
|
102
|
+
port=port,
|
|
103
|
+
usb_port=usb_port,
|
|
104
|
+
driver=driver,
|
|
105
|
+
reader=reader,
|
|
106
|
+
poller=poller,
|
|
107
|
+
device_info=await driver.get_device_info(),
|
|
108
|
+
hw_control_loop=hw_control_loop,
|
|
109
|
+
execution_manager=execution_manager,
|
|
110
|
+
disconnected_callback=disconnected_callback,
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
try:
|
|
114
|
+
await poller.start()
|
|
115
|
+
except Exception:
|
|
116
|
+
log.exception(f"First read of Thermocycler on port {port} failed")
|
|
117
|
+
|
|
118
|
+
return module
|
|
119
|
+
|
|
120
|
+
def __init__(
|
|
121
|
+
self,
|
|
122
|
+
port: str,
|
|
123
|
+
usb_port: USBPort,
|
|
124
|
+
driver: AbstractThermocyclerDriver,
|
|
125
|
+
reader: ThermocyclerReader,
|
|
126
|
+
poller: Poller,
|
|
127
|
+
device_info: Dict[str, str],
|
|
128
|
+
hw_control_loop: asyncio.AbstractEventLoop,
|
|
129
|
+
execution_manager: Optional[ExecutionManager] = None,
|
|
130
|
+
disconnected_callback: ModuleDisconnectedCallback = None,
|
|
131
|
+
) -> None:
|
|
132
|
+
"""
|
|
133
|
+
Constructor
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
port: The port the thermocycler is connected to.
|
|
137
|
+
usb_port: The USB port.
|
|
138
|
+
driver: The thermocycler driver.
|
|
139
|
+
reader: An interface to read data from the Thermocycler.
|
|
140
|
+
poller: A poll controller for reads.
|
|
141
|
+
device_info: The thermocycler device info.
|
|
142
|
+
hw_control_loop: The event loop running in the hardware control thread.
|
|
143
|
+
execution_manager: The hardware execution manager.
|
|
144
|
+
disconnected_callback: Callback to inform the module controller that the device was disconnected
|
|
145
|
+
"""
|
|
146
|
+
self._driver = driver
|
|
147
|
+
super().__init__(
|
|
148
|
+
port=port,
|
|
149
|
+
usb_port=usb_port,
|
|
150
|
+
hw_control_loop=hw_control_loop,
|
|
151
|
+
execution_manager=execution_manager,
|
|
152
|
+
disconnected_callback=disconnected_callback,
|
|
153
|
+
)
|
|
154
|
+
self._device_info = device_info
|
|
155
|
+
self._reader = reader
|
|
156
|
+
self._poller = poller
|
|
157
|
+
self._total_cycle_count: Optional[int] = None
|
|
158
|
+
self._current_cycle_index: Optional[int] = None
|
|
159
|
+
self._total_step_count: Optional[int] = None
|
|
160
|
+
self._current_step_index: Optional[int] = None
|
|
161
|
+
self._error: Optional[str] = None
|
|
162
|
+
self._reader.register_error_handler(self._enter_error_state)
|
|
163
|
+
|
|
164
|
+
async def cleanup(self) -> None:
|
|
165
|
+
"""Stop the poller task."""
|
|
166
|
+
await self._poller.stop()
|
|
167
|
+
await self._driver.disconnect()
|
|
168
|
+
|
|
169
|
+
@classmethod
|
|
170
|
+
def name(cls) -> str:
|
|
171
|
+
return "thermocycler"
|
|
172
|
+
|
|
173
|
+
def firmware_prefix(self) -> str:
|
|
174
|
+
"""The prefix used for looking up firmware"""
|
|
175
|
+
if self.model() == V1_MODULE_STRING:
|
|
176
|
+
return "thermocycler"
|
|
177
|
+
else:
|
|
178
|
+
return "thermocycler-gen2"
|
|
179
|
+
|
|
180
|
+
def model(self) -> str:
|
|
181
|
+
if isinstance(self._driver, SimulatingDriver):
|
|
182
|
+
return self._driver.model()
|
|
183
|
+
elif isinstance(self._driver, ThermocyclerDriverV2):
|
|
184
|
+
return V2_MODULE_STRING
|
|
185
|
+
else:
|
|
186
|
+
# Real module that is not a V2
|
|
187
|
+
return V1_MODULE_STRING
|
|
188
|
+
|
|
189
|
+
def bootloader(self) -> types.UploadFunction:
|
|
190
|
+
if self.model() == V2_MODULE_STRING:
|
|
191
|
+
return update.upload_via_dfu
|
|
192
|
+
else:
|
|
193
|
+
return update.upload_via_bossa
|
|
194
|
+
|
|
195
|
+
def _clear_cycle_counters(self) -> None:
|
|
196
|
+
"""Clear the cycle counters."""
|
|
197
|
+
self._total_cycle_count = None
|
|
198
|
+
self._current_cycle_index = None
|
|
199
|
+
self._total_step_count = None
|
|
200
|
+
self._current_step_index = None
|
|
201
|
+
|
|
202
|
+
async def deactivate_lid(self, must_be_running: bool = True) -> None:
|
|
203
|
+
"""Deactivate the lid heating pad"""
|
|
204
|
+
if must_be_running:
|
|
205
|
+
await self.wait_for_is_running()
|
|
206
|
+
await self._driver.deactivate_lid()
|
|
207
|
+
await self._reader.read_lid_temperature()
|
|
208
|
+
|
|
209
|
+
async def deactivate_block(self, must_be_running: bool = True) -> None:
|
|
210
|
+
"""Deactivate the block peltiers"""
|
|
211
|
+
if must_be_running:
|
|
212
|
+
await self.wait_for_is_running()
|
|
213
|
+
self._clear_cycle_counters()
|
|
214
|
+
await self._driver.deactivate_block()
|
|
215
|
+
await self._reader.read_block_temperature()
|
|
216
|
+
|
|
217
|
+
async def deactivate(self, must_be_running: bool = True) -> None:
|
|
218
|
+
"""Deactivate the block peltiers and lid heating pad"""
|
|
219
|
+
if must_be_running:
|
|
220
|
+
await self.wait_for_is_running()
|
|
221
|
+
self._clear_cycle_counters()
|
|
222
|
+
await self._driver.deactivate_all()
|
|
223
|
+
await self._reader.read()
|
|
224
|
+
|
|
225
|
+
async def open(self) -> str:
|
|
226
|
+
"""Open the lid if it is closed"""
|
|
227
|
+
await self.wait_for_is_running()
|
|
228
|
+
await self._driver.open_lid()
|
|
229
|
+
await self._wait_for_lid_status(ThermocyclerLidStatus.OPEN)
|
|
230
|
+
return ThermocyclerLidStatus.OPEN
|
|
231
|
+
|
|
232
|
+
async def close(self) -> str:
|
|
233
|
+
"""Close the lid if it is open"""
|
|
234
|
+
await self.wait_for_is_running()
|
|
235
|
+
await self._driver.close_lid()
|
|
236
|
+
await self._wait_for_lid_status(ThermocyclerLidStatus.CLOSED)
|
|
237
|
+
return ThermocyclerLidStatus.CLOSED
|
|
238
|
+
|
|
239
|
+
async def lift_plate(self) -> str:
|
|
240
|
+
"""If the lid is open, lift the plate.
|
|
241
|
+
|
|
242
|
+
Note that this should always be preceded by an `open` call. If the
|
|
243
|
+
lid is not open, the Thermocycler will respond with an error message.
|
|
244
|
+
|
|
245
|
+
Returns: Thermocycler lid status after running the command.
|
|
246
|
+
"""
|
|
247
|
+
await self.wait_for_is_running()
|
|
248
|
+
await self._driver.lift_plate()
|
|
249
|
+
await self._wait_for_lid_status(ThermocyclerLidStatus.OPEN)
|
|
250
|
+
return ThermocyclerLidStatus.OPEN
|
|
251
|
+
|
|
252
|
+
async def raise_plate(self) -> None:
|
|
253
|
+
"""Move lid an extra bit."""
|
|
254
|
+
if not self.lid_status == ThermocyclerLidStatus.OPEN:
|
|
255
|
+
raise RuntimeError("Lid must be open")
|
|
256
|
+
await self.wait_for_is_running()
|
|
257
|
+
await self._driver.jog_lid(_TC_PLATE_LIFT_OPEN_DEGREES)
|
|
258
|
+
# There is NO WAIT for the updated lid stats, because it will always
|
|
259
|
+
# remain at "open"
|
|
260
|
+
|
|
261
|
+
async def return_from_raise_plate(self) -> None:
|
|
262
|
+
"""Return lid back to normal open position."""
|
|
263
|
+
await self.wait_for_is_running()
|
|
264
|
+
await self._driver.jog_lid(-_TC_PLATE_LIFT_RETURN_DEGREES)
|
|
265
|
+
await self.open()
|
|
266
|
+
await self._wait_for_lid_status(ThermocyclerLidStatus.OPEN)
|
|
267
|
+
|
|
268
|
+
async def set_temperature(
|
|
269
|
+
self,
|
|
270
|
+
temperature: float,
|
|
271
|
+
hold_time_seconds: Optional[float] = None,
|
|
272
|
+
hold_time_minutes: Optional[float] = None,
|
|
273
|
+
ramp_rate: Optional[float] = None,
|
|
274
|
+
volume: Optional[float] = None,
|
|
275
|
+
) -> None:
|
|
276
|
+
"""
|
|
277
|
+
Set the temperature and wait.
|
|
278
|
+
|
|
279
|
+
If hold time is set this function will return after
|
|
280
|
+
the hold time expires.
|
|
281
|
+
|
|
282
|
+
Otherwise it will return when the target temperature is reached.
|
|
283
|
+
|
|
284
|
+
Args:
|
|
285
|
+
temperature: The target temperature.
|
|
286
|
+
hold_time_seconds: Optional number of seconds to wait.
|
|
287
|
+
hold_time_minutes: Optional number of minutes to wait.
|
|
288
|
+
ramp_rate: Optional ramp rate.
|
|
289
|
+
volume: Optional volume.
|
|
290
|
+
|
|
291
|
+
Returns: None
|
|
292
|
+
"""
|
|
293
|
+
await self.wait_for_is_running()
|
|
294
|
+
await self._set_temperature_no_pause(
|
|
295
|
+
temperature=temperature,
|
|
296
|
+
hold_time_seconds=hold_time_seconds,
|
|
297
|
+
hold_time_minutes=hold_time_minutes,
|
|
298
|
+
ramp_rate=ramp_rate,
|
|
299
|
+
volume=volume,
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
async def _set_temperature_no_pause(
|
|
303
|
+
self,
|
|
304
|
+
temperature: float,
|
|
305
|
+
hold_time_seconds: Optional[float],
|
|
306
|
+
hold_time_minutes: Optional[float],
|
|
307
|
+
ramp_rate: Optional[float],
|
|
308
|
+
volume: Optional[float],
|
|
309
|
+
) -> None:
|
|
310
|
+
seconds = hold_time_seconds if hold_time_seconds is not None else 0
|
|
311
|
+
minutes = hold_time_minutes if hold_time_minutes is not None else 0
|
|
312
|
+
total_seconds = seconds + (minutes * 60)
|
|
313
|
+
hold_time = total_seconds if total_seconds > 0 else 0
|
|
314
|
+
|
|
315
|
+
if ramp_rate is not None:
|
|
316
|
+
await self._driver.set_ramp_rate(ramp_rate=ramp_rate)
|
|
317
|
+
|
|
318
|
+
await self._driver.set_plate_temperature(
|
|
319
|
+
temp=temperature, hold_time=hold_time, volume=volume
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
task = self._loop.create_task(self._wait_for_block_target())
|
|
323
|
+
self.make_cancellable(task)
|
|
324
|
+
await task
|
|
325
|
+
|
|
326
|
+
async def wait_for_block_target(self) -> None:
|
|
327
|
+
"""
|
|
328
|
+
Wait for thermocycler to reach given temperature.
|
|
329
|
+
|
|
330
|
+
Will return when the target temperature is reached.
|
|
331
|
+
|
|
332
|
+
Args:
|
|
333
|
+
temperature: The target temperature.
|
|
334
|
+
|
|
335
|
+
Returns: None
|
|
336
|
+
"""
|
|
337
|
+
await self.wait_for_is_running()
|
|
338
|
+
task = self._loop.create_task(self._wait_for_block_target())
|
|
339
|
+
self.make_cancellable(task)
|
|
340
|
+
await task
|
|
341
|
+
|
|
342
|
+
async def cycle_temperatures(
|
|
343
|
+
self,
|
|
344
|
+
steps: List[types.ThermocyclerStep],
|
|
345
|
+
repetitions: int,
|
|
346
|
+
volume: Optional[float] = None,
|
|
347
|
+
) -> None:
|
|
348
|
+
"""
|
|
349
|
+
Begin a set temperature cycle.
|
|
350
|
+
|
|
351
|
+
Args:
|
|
352
|
+
steps: The set temperature steps.
|
|
353
|
+
repetitions: Number of repetitions.
|
|
354
|
+
volume: Optional volume.
|
|
355
|
+
|
|
356
|
+
Returns: None
|
|
357
|
+
"""
|
|
358
|
+
await self.wait_for_is_running()
|
|
359
|
+
self._total_cycle_count = repetitions
|
|
360
|
+
self._total_step_count = len(steps)
|
|
361
|
+
|
|
362
|
+
task = self._loop.create_task(self._execute_cycles(steps, repetitions, volume))
|
|
363
|
+
self.make_cancellable(task)
|
|
364
|
+
await task
|
|
365
|
+
|
|
366
|
+
async def execute_profile(
|
|
367
|
+
self,
|
|
368
|
+
profile: List[Union[types.ThermocyclerCycle, types.ThermocyclerStep]],
|
|
369
|
+
volume: Optional[float] = None,
|
|
370
|
+
) -> None:
|
|
371
|
+
"""Begin a set temperature profile, with both repeating and non-repeating steps.
|
|
372
|
+
|
|
373
|
+
Args:
|
|
374
|
+
profile: The temperature profile to follow.
|
|
375
|
+
volume: Optional volume
|
|
376
|
+
|
|
377
|
+
Returns: None
|
|
378
|
+
"""
|
|
379
|
+
await self.wait_for_is_running()
|
|
380
|
+
self._total_cycle_count = 0
|
|
381
|
+
self._total_step_count = 0
|
|
382
|
+
self._current_cycle_index = 0
|
|
383
|
+
self._current_step_index = 0
|
|
384
|
+
for step_or_cycle in profile:
|
|
385
|
+
if "steps" in step_or_cycle:
|
|
386
|
+
# basically https://github.com/python/mypy/issues/14766
|
|
387
|
+
this_cycle = cast(types.ThermocyclerCycle, step_or_cycle)
|
|
388
|
+
self._total_cycle_count += this_cycle["repetitions"]
|
|
389
|
+
self._total_step_count += (
|
|
390
|
+
len(this_cycle["steps"]) * this_cycle["repetitions"]
|
|
391
|
+
)
|
|
392
|
+
else:
|
|
393
|
+
self._total_step_count += 1
|
|
394
|
+
self._total_cycle_count += 1
|
|
395
|
+
task = self._loop.create_task(self._execute_profile(profile, volume))
|
|
396
|
+
self.make_cancellable(task)
|
|
397
|
+
await task
|
|
398
|
+
|
|
399
|
+
async def set_lid_temperature(self, temperature: float) -> None:
|
|
400
|
+
"""Set the lid temperature in degrees Celsius"""
|
|
401
|
+
await self.wait_for_is_running()
|
|
402
|
+
await self._driver.set_lid_temperature(temp=temperature)
|
|
403
|
+
|
|
404
|
+
task = self._loop.create_task(self._wait_for_lid_target())
|
|
405
|
+
self.make_cancellable(task)
|
|
406
|
+
await task
|
|
407
|
+
|
|
408
|
+
async def wait_for_lid_target(self) -> None:
|
|
409
|
+
"""Set the lid temperature in degrees Celsius"""
|
|
410
|
+
await self.wait_for_is_running()
|
|
411
|
+
|
|
412
|
+
task = self._loop.create_task(self._wait_for_lid_target())
|
|
413
|
+
self.make_cancellable(task)
|
|
414
|
+
await task
|
|
415
|
+
|
|
416
|
+
# TODO(mc, 2022-04-25): de-duplicate with `set_temperature`
|
|
417
|
+
async def set_target_block_temperature(
|
|
418
|
+
self,
|
|
419
|
+
celsius: float,
|
|
420
|
+
hold_time_seconds: Optional[float] = None,
|
|
421
|
+
volume: Optional[float] = None,
|
|
422
|
+
) -> None:
|
|
423
|
+
"""Set the Thermocycler's target block temperature.
|
|
424
|
+
|
|
425
|
+
Does not wait for the target temperature to be reached.
|
|
426
|
+
|
|
427
|
+
Args:
|
|
428
|
+
celsius: The target block temperature, in degrees celsius.
|
|
429
|
+
"""
|
|
430
|
+
await self.wait_for_is_running()
|
|
431
|
+
await self._driver.set_plate_temperature(
|
|
432
|
+
temp=celsius,
|
|
433
|
+
hold_time=hold_time_seconds,
|
|
434
|
+
volume=volume,
|
|
435
|
+
)
|
|
436
|
+
await self._reader.read_block_temperature()
|
|
437
|
+
|
|
438
|
+
# TODO(mc, 2022-04-26): de-duplicate with `set_lid_temperature`
|
|
439
|
+
async def set_target_lid_temperature(self, celsius: float) -> None:
|
|
440
|
+
"""Set the Thermocycler's target lid temperature.
|
|
441
|
+
|
|
442
|
+
Does not wait for the target temperature to be reached.
|
|
443
|
+
|
|
444
|
+
Args:
|
|
445
|
+
celsius: The target lid temperature, in degrees celsius.
|
|
446
|
+
"""
|
|
447
|
+
await self.wait_for_is_running()
|
|
448
|
+
await self._driver.set_lid_temperature(temp=celsius)
|
|
449
|
+
await self._reader.read_lid_temperature()
|
|
450
|
+
|
|
451
|
+
async def _wait_for_lid_target(self) -> None:
|
|
452
|
+
"""
|
|
453
|
+
This method only exits if lid target temperature has been reached.
|
|
454
|
+
|
|
455
|
+
Subject to change without a version bump.
|
|
456
|
+
"""
|
|
457
|
+
await self._reader.read_lid_temperature()
|
|
458
|
+
|
|
459
|
+
while not _temperature_is_holding(self.lid_temp_status):
|
|
460
|
+
await self._poller.wait_next_poll()
|
|
461
|
+
|
|
462
|
+
async def _wait_for_block_target(self) -> None:
|
|
463
|
+
"""
|
|
464
|
+
This method only exits if set temperature has been reached.
|
|
465
|
+
|
|
466
|
+
Subject to change without a version bump.
|
|
467
|
+
"""
|
|
468
|
+
await self._reader.read_block_temperature()
|
|
469
|
+
|
|
470
|
+
while not _temperature_is_holding(self.status):
|
|
471
|
+
await self._poller.wait_next_poll()
|
|
472
|
+
|
|
473
|
+
while self.hold_time is not None and self.hold_time > 0:
|
|
474
|
+
await self._poller.wait_next_poll()
|
|
475
|
+
|
|
476
|
+
async def _wait_for_lid_status(self, status: ThermocyclerLidStatus) -> None:
|
|
477
|
+
"""Wait for lid status to be status."""
|
|
478
|
+
await self._reader.read_lid_status()
|
|
479
|
+
|
|
480
|
+
while self.lid_status != status:
|
|
481
|
+
await self._poller.wait_next_poll()
|
|
482
|
+
|
|
483
|
+
@property
|
|
484
|
+
def lid_target(self) -> Optional[float]:
|
|
485
|
+
return self._reader.lid_temperature.target
|
|
486
|
+
|
|
487
|
+
@property
|
|
488
|
+
def lid_temp(self) -> float:
|
|
489
|
+
return self._reader.lid_temperature.current
|
|
490
|
+
|
|
491
|
+
@property
|
|
492
|
+
def lid_status(self) -> ThermocyclerLidStatus:
|
|
493
|
+
return self._reader.lid_status
|
|
494
|
+
|
|
495
|
+
@property
|
|
496
|
+
def lid_temp_status(self) -> TemperatureStatus:
|
|
497
|
+
return self._reader.lid_temperature_status
|
|
498
|
+
|
|
499
|
+
# TODO(mc, 2022-10-13): remove
|
|
500
|
+
@property
|
|
501
|
+
def ramp_rate(self) -> Optional[float]:
|
|
502
|
+
"""Not supported."""
|
|
503
|
+
return None
|
|
504
|
+
|
|
505
|
+
@property
|
|
506
|
+
def hold_time(self) -> Optional[float]:
|
|
507
|
+
return self._reader.block_temperature.hold
|
|
508
|
+
|
|
509
|
+
@property
|
|
510
|
+
def temperature(self) -> Optional[float]:
|
|
511
|
+
return self._reader.block_temperature.current
|
|
512
|
+
|
|
513
|
+
@property
|
|
514
|
+
def target(self) -> Optional[float]:
|
|
515
|
+
return self._reader.block_temperature.target
|
|
516
|
+
|
|
517
|
+
@property
|
|
518
|
+
def status(self) -> TemperatureStatus:
|
|
519
|
+
return (
|
|
520
|
+
self._reader.block_temperature_status
|
|
521
|
+
if self._error is None
|
|
522
|
+
else TemperatureStatus.ERROR
|
|
523
|
+
)
|
|
524
|
+
|
|
525
|
+
@property
|
|
526
|
+
def device_info(self) -> Mapping[str, str]:
|
|
527
|
+
return self._device_info
|
|
528
|
+
|
|
529
|
+
@property
|
|
530
|
+
def total_cycle_count(self) -> Optional[int]:
|
|
531
|
+
return self._total_cycle_count
|
|
532
|
+
|
|
533
|
+
@property
|
|
534
|
+
def current_cycle_index(self) -> Optional[int]:
|
|
535
|
+
return self._current_cycle_index
|
|
536
|
+
|
|
537
|
+
@property
|
|
538
|
+
def total_step_count(self) -> Optional[int]:
|
|
539
|
+
return self._total_step_count
|
|
540
|
+
|
|
541
|
+
@property
|
|
542
|
+
def current_step_index(self) -> Optional[int]:
|
|
543
|
+
return self._current_step_index
|
|
544
|
+
|
|
545
|
+
@property
|
|
546
|
+
def live_data(self) -> types.LiveData:
|
|
547
|
+
data: types.ThermocyclerData = {
|
|
548
|
+
"lid": self.lid_status,
|
|
549
|
+
"lidTarget": self.lid_target,
|
|
550
|
+
"lidTemp": self.lid_temp,
|
|
551
|
+
"lidTempStatus": self.lid_temp_status,
|
|
552
|
+
"currentTemp": self.temperature,
|
|
553
|
+
"targetTemp": self.target,
|
|
554
|
+
"holdTime": self.hold_time,
|
|
555
|
+
"rampRate": self.ramp_rate,
|
|
556
|
+
"currentCycleIndex": self.current_cycle_index,
|
|
557
|
+
"totalCycleCount": self.total_cycle_count,
|
|
558
|
+
"currentStepIndex": self.current_step_index,
|
|
559
|
+
"totalStepCount": self.total_step_count,
|
|
560
|
+
}
|
|
561
|
+
return {
|
|
562
|
+
"status": self.status,
|
|
563
|
+
"data": data,
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
@property
|
|
567
|
+
def is_simulated(self) -> bool:
|
|
568
|
+
return isinstance(self._driver, SimulatingDriver)
|
|
569
|
+
|
|
570
|
+
async def prep_for_update(self) -> str:
|
|
571
|
+
await self._poller.stop()
|
|
572
|
+
await self._driver.enter_programming_mode()
|
|
573
|
+
|
|
574
|
+
if self.model() == V2_MODULE_STRING:
|
|
575
|
+
# TC2 has three unique "devices" over DFU
|
|
576
|
+
new_port = await update.find_dfu_device(
|
|
577
|
+
pid=DFU_PID, expected_device_count=3
|
|
578
|
+
)
|
|
579
|
+
else:
|
|
580
|
+
new_port = await update.find_bootloader_port()
|
|
581
|
+
|
|
582
|
+
return new_port or self.port
|
|
583
|
+
|
|
584
|
+
async def _execute_cycle_step(
|
|
585
|
+
self, step: types.ThermocyclerStep, volume: Optional[float]
|
|
586
|
+
) -> None:
|
|
587
|
+
"""
|
|
588
|
+
Execute a thermocycler step.
|
|
589
|
+
|
|
590
|
+
Args:
|
|
591
|
+
step: The set temperature parameters.
|
|
592
|
+
volume: The volume
|
|
593
|
+
|
|
594
|
+
Returns: None
|
|
595
|
+
"""
|
|
596
|
+
temperature = step.get("temperature")
|
|
597
|
+
hold_time_minutes = step.get("hold_time_minutes", None)
|
|
598
|
+
hold_time_seconds = step.get("hold_time_seconds", None)
|
|
599
|
+
await self._set_temperature_no_pause(
|
|
600
|
+
temperature=temperature, # type: ignore
|
|
601
|
+
hold_time_minutes=hold_time_minutes,
|
|
602
|
+
hold_time_seconds=hold_time_seconds,
|
|
603
|
+
ramp_rate=None,
|
|
604
|
+
volume=volume,
|
|
605
|
+
)
|
|
606
|
+
|
|
607
|
+
async def _execute_cycles(
|
|
608
|
+
self,
|
|
609
|
+
steps: List[types.ThermocyclerStep],
|
|
610
|
+
repetitions: int,
|
|
611
|
+
volume: Optional[float],
|
|
612
|
+
) -> None:
|
|
613
|
+
"""
|
|
614
|
+
Execute cycles.
|
|
615
|
+
|
|
616
|
+
Args:
|
|
617
|
+
steps: The set temperature steps.
|
|
618
|
+
repetitions: The number of repetitions
|
|
619
|
+
volume: The optional volume.
|
|
620
|
+
|
|
621
|
+
Returns: None
|
|
622
|
+
"""
|
|
623
|
+
for rep in range(repetitions):
|
|
624
|
+
self._current_cycle_index = rep + 1 # science starts at 1
|
|
625
|
+
for step_idx, step in enumerate(steps):
|
|
626
|
+
self._current_step_index = step_idx + 1 # science starts at 1
|
|
627
|
+
await self._execute_cycle_step(step, volume)
|
|
628
|
+
|
|
629
|
+
async def _execute_profile(
|
|
630
|
+
self,
|
|
631
|
+
profile: List[Union[types.ThermocyclerCycle, types.ThermocyclerStep]],
|
|
632
|
+
volume: Optional[float],
|
|
633
|
+
) -> None:
|
|
634
|
+
"""
|
|
635
|
+
Execute profiles.
|
|
636
|
+
|
|
637
|
+
Profiles command a thermocycler pattern that can contain multiple cycles and out-of-cycle steps.
|
|
638
|
+
"""
|
|
639
|
+
self._current_cycle_index = 0
|
|
640
|
+
self._current_step_index = 0
|
|
641
|
+
for step_or_cycle in profile:
|
|
642
|
+
self._current_cycle_index += 1
|
|
643
|
+
if "repetitions" in step_or_cycle:
|
|
644
|
+
# basically https://github.com/python/mypy/issues/14766
|
|
645
|
+
this_cycle = cast(types.ThermocyclerCycle, step_or_cycle)
|
|
646
|
+
for rep in range(this_cycle["repetitions"]):
|
|
647
|
+
for step in this_cycle["steps"]:
|
|
648
|
+
self._current_step_index += 1
|
|
649
|
+
await self._execute_cycle_step(step, volume)
|
|
650
|
+
else:
|
|
651
|
+
await self._execute_cycle_step(step_or_cycle, volume)
|
|
652
|
+
|
|
653
|
+
# TODO(mc, 2022-10-13): why does this exist?
|
|
654
|
+
# Do the driver and poller really need to be disconnected?
|
|
655
|
+
# Could we accomplish the same thing by latching the error state
|
|
656
|
+
# and allowing the driver and poller to continue?
|
|
657
|
+
def _enter_error_state(self, error: Exception) -> None:
|
|
658
|
+
"""Enter into an error state.
|
|
659
|
+
|
|
660
|
+
The Thermocycler will not be accessible in this state.
|
|
661
|
+
|
|
662
|
+
Args:
|
|
663
|
+
cause: The cause of the exception.
|
|
664
|
+
|
|
665
|
+
Returns:
|
|
666
|
+
None
|
|
667
|
+
"""
|
|
668
|
+
self._error = str(error)
|
|
669
|
+
log.error(
|
|
670
|
+
f"Thermocycler has encountered an unrecoverable error: {self._error}. "
|
|
671
|
+
f"Please refer to support article at "
|
|
672
|
+
f"https://support.opentrons.com/en/articles/3469797-thermocycler-module"
|
|
673
|
+
f" for troubleshooting."
|
|
674
|
+
)
|
|
675
|
+
asyncio.run_coroutine_threadsafe(self.cleanup(), self._loop)
|
|
676
|
+
|
|
677
|
+
|
|
678
|
+
class ThermocyclerReader(Reader):
|
|
679
|
+
"""Read data from the thermocycler.
|
|
680
|
+
|
|
681
|
+
Args:
|
|
682
|
+
driver: A connected Thermocycler driver.
|
|
683
|
+
"""
|
|
684
|
+
|
|
685
|
+
lid_status: ThermocyclerLidStatus
|
|
686
|
+
lid_temperature: Temperature
|
|
687
|
+
block_temperature: PlateTemperature
|
|
688
|
+
|
|
689
|
+
def __init__(
|
|
690
|
+
self,
|
|
691
|
+
driver: AbstractThermocyclerDriver,
|
|
692
|
+
) -> None:
|
|
693
|
+
self.lid_status = ThermocyclerLidStatus.UNKNOWN
|
|
694
|
+
self.lid_temperature = Temperature(current=25.0, target=None)
|
|
695
|
+
self.block_temperature = PlateTemperature(current=25.0, target=None, hold=None)
|
|
696
|
+
self._lid_temperature_status = LidTemperatureStatus()
|
|
697
|
+
self._block_temperature_status = PlateTemperatureStatus()
|
|
698
|
+
self._driver = driver
|
|
699
|
+
self._handle_error: Optional[Callable[[Exception], None]] = None
|
|
700
|
+
|
|
701
|
+
@property
|
|
702
|
+
def block_temperature_status(self) -> TemperatureStatus:
|
|
703
|
+
return self._block_temperature_status.status
|
|
704
|
+
|
|
705
|
+
@property
|
|
706
|
+
def lid_temperature_status(self) -> TemperatureStatus:
|
|
707
|
+
return self._lid_temperature_status.status
|
|
708
|
+
|
|
709
|
+
def on_error(self, exception: Exception) -> None:
|
|
710
|
+
if self._handle_error is not None:
|
|
711
|
+
self._handle_error(exception)
|
|
712
|
+
|
|
713
|
+
def register_error_handler(self, handle_error: Callable[[Exception], None]) -> None:
|
|
714
|
+
self._handle_error = handle_error
|
|
715
|
+
|
|
716
|
+
async def read(self) -> None:
|
|
717
|
+
"""Poll the thermocycler."""
|
|
718
|
+
await self.read_lid_status()
|
|
719
|
+
await self.read_lid_temperature()
|
|
720
|
+
await self.read_block_temperature()
|
|
721
|
+
|
|
722
|
+
async def read_lid_status(self) -> None:
|
|
723
|
+
self.lid_status = await self._driver.get_lid_status()
|
|
724
|
+
|
|
725
|
+
async def read_block_temperature(self) -> None:
|
|
726
|
+
self.block_temperature = await self._driver.get_plate_temperature()
|
|
727
|
+
self._block_temperature_status.update(self.block_temperature)
|
|
728
|
+
|
|
729
|
+
async def read_lid_temperature(self) -> None:
|
|
730
|
+
self.lid_temperature = await self._driver.get_lid_temperature()
|
|
731
|
+
self._lid_temperature_status.update(self.lid_temperature)
|