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,417 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from enum import Enum
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import (
|
|
5
|
+
Dict,
|
|
6
|
+
List,
|
|
7
|
+
NamedTuple,
|
|
8
|
+
Callable,
|
|
9
|
+
Any,
|
|
10
|
+
Tuple,
|
|
11
|
+
Awaitable,
|
|
12
|
+
Union,
|
|
13
|
+
Optional,
|
|
14
|
+
cast,
|
|
15
|
+
TYPE_CHECKING,
|
|
16
|
+
TypeGuard,
|
|
17
|
+
)
|
|
18
|
+
from typing_extensions import TypedDict
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
from opentrons.drivers.flex_stacker.types import (
|
|
22
|
+
LimitSwitchStatus,
|
|
23
|
+
PlatformStatus,
|
|
24
|
+
StackerAxis,
|
|
25
|
+
)
|
|
26
|
+
from opentrons.drivers.rpi_drivers.types import USBPort
|
|
27
|
+
|
|
28
|
+
if TYPE_CHECKING:
|
|
29
|
+
from opentrons_shared_data.module.types import (
|
|
30
|
+
ThermocyclerModuleType,
|
|
31
|
+
MagneticModuleType,
|
|
32
|
+
TemperatureModuleType,
|
|
33
|
+
HeaterShakerModuleType,
|
|
34
|
+
MagneticBlockType,
|
|
35
|
+
AbsorbanceReaderType,
|
|
36
|
+
FlexStackerModuleType,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class ThermocyclerStepBase(TypedDict):
|
|
41
|
+
temperature: float
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class ThermocyclerStep(ThermocyclerStepBase, total=False):
|
|
45
|
+
hold_time_seconds: float
|
|
46
|
+
hold_time_minutes: float
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class ThermocyclerCycle(TypedDict):
|
|
50
|
+
steps: List[ThermocyclerStep]
|
|
51
|
+
repetitions: int
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
UploadFunction = Callable[[str, str, Dict[str, Any]], Awaitable[Tuple[bool, str]]]
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
ModuleDisconnectedCallback = Optional[Callable[[str, str | None], None]]
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class MagneticModuleData(TypedDict):
|
|
61
|
+
engaged: bool
|
|
62
|
+
height: float
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class TemperatureModuleData(TypedDict):
|
|
66
|
+
currentTemp: float
|
|
67
|
+
targetTemp: float | None
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class HeaterShakerData(TypedDict):
|
|
71
|
+
temperatureStatus: str
|
|
72
|
+
speedStatus: str
|
|
73
|
+
labwareLatchStatus: str
|
|
74
|
+
currentTemp: float
|
|
75
|
+
targetTemp: float | None
|
|
76
|
+
currentSpeed: int
|
|
77
|
+
targetSpeed: int | None
|
|
78
|
+
errorDetails: str | None
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class ThermocyclerData(TypedDict):
|
|
82
|
+
lid: str
|
|
83
|
+
lidTarget: float | None
|
|
84
|
+
lidTemp: float
|
|
85
|
+
lidTempStatus: str
|
|
86
|
+
currentTemp: float | None
|
|
87
|
+
targetTemp: float | None
|
|
88
|
+
holdTime: float | None
|
|
89
|
+
rampRate: float | None
|
|
90
|
+
currentCycleIndex: int | None
|
|
91
|
+
totalCycleCount: int | None
|
|
92
|
+
currentStepIndex: int | None
|
|
93
|
+
totalStepCount: int | None
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class AbsorbanceReaderData(TypedDict):
|
|
97
|
+
uptime: int
|
|
98
|
+
deviceStatus: str
|
|
99
|
+
lidStatus: str
|
|
100
|
+
platePresence: str
|
|
101
|
+
measureMode: str
|
|
102
|
+
sampleWavelengths: List[int]
|
|
103
|
+
referenceWavelength: int
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class FlexStackerData(TypedDict):
|
|
107
|
+
latchState: str
|
|
108
|
+
platformState: str
|
|
109
|
+
hopperDoorState: str
|
|
110
|
+
installDetected: bool
|
|
111
|
+
errorDetails: str | None
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
ModuleData = Union[
|
|
115
|
+
Dict[Any, Any], # This allows an empty dict as module data
|
|
116
|
+
MagneticModuleData,
|
|
117
|
+
TemperatureModuleData,
|
|
118
|
+
HeaterShakerData,
|
|
119
|
+
ThermocyclerData,
|
|
120
|
+
AbsorbanceReaderData,
|
|
121
|
+
FlexStackerData,
|
|
122
|
+
]
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class ModuleDataValidator:
|
|
126
|
+
@classmethod
|
|
127
|
+
def is_magnetic_module_data(
|
|
128
|
+
cls, data: ModuleData | None
|
|
129
|
+
) -> TypeGuard[MagneticModuleData]:
|
|
130
|
+
return data is not None and "engaged" in data.keys()
|
|
131
|
+
|
|
132
|
+
@classmethod
|
|
133
|
+
def is_temperature_module_data(
|
|
134
|
+
cls, data: ModuleData | None
|
|
135
|
+
) -> TypeGuard[TemperatureModuleData]:
|
|
136
|
+
return data is not None and "targetTemp" in data.keys()
|
|
137
|
+
|
|
138
|
+
@classmethod
|
|
139
|
+
def is_heater_shaker_data(
|
|
140
|
+
cls, data: ModuleData | None
|
|
141
|
+
) -> TypeGuard[HeaterShakerData]:
|
|
142
|
+
return data is not None and "labwareLatchStatus" in data.keys()
|
|
143
|
+
|
|
144
|
+
@classmethod
|
|
145
|
+
def is_thermocycler_data(
|
|
146
|
+
cls, data: ModuleData | None
|
|
147
|
+
) -> TypeGuard[ThermocyclerData]:
|
|
148
|
+
return data is not None and "lid" in data.keys()
|
|
149
|
+
|
|
150
|
+
@classmethod
|
|
151
|
+
def is_absorbance_reader_data(
|
|
152
|
+
cls, data: ModuleData | None
|
|
153
|
+
) -> TypeGuard[AbsorbanceReaderData]:
|
|
154
|
+
return data is not None and "uptime" in data.keys()
|
|
155
|
+
|
|
156
|
+
@classmethod
|
|
157
|
+
def is_flex_stacker_data(
|
|
158
|
+
cls, data: ModuleData | None
|
|
159
|
+
) -> TypeGuard[FlexStackerData]:
|
|
160
|
+
return data is not None and "platformState" in data.keys()
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
class LiveData(TypedDict):
|
|
164
|
+
status: str
|
|
165
|
+
data: ModuleData | None
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
class ModuleType(str, Enum):
|
|
169
|
+
THERMOCYCLER: ThermocyclerModuleType = "thermocyclerModuleType"
|
|
170
|
+
TEMPERATURE: TemperatureModuleType = "temperatureModuleType"
|
|
171
|
+
MAGNETIC: MagneticModuleType = "magneticModuleType"
|
|
172
|
+
HEATER_SHAKER: HeaterShakerModuleType = "heaterShakerModuleType"
|
|
173
|
+
MAGNETIC_BLOCK: MagneticBlockType = "magneticBlockType"
|
|
174
|
+
ABSORBANCE_READER: AbsorbanceReaderType = "absorbanceReaderType"
|
|
175
|
+
FLEX_STACKER: FlexStackerModuleType = "flexStackerModuleType"
|
|
176
|
+
|
|
177
|
+
@classmethod
|
|
178
|
+
def from_model(cls, model: ModuleModel) -> ModuleType:
|
|
179
|
+
if isinstance(model, MagneticModuleModel):
|
|
180
|
+
return cls.MAGNETIC
|
|
181
|
+
if isinstance(model, TemperatureModuleModel):
|
|
182
|
+
return cls.TEMPERATURE
|
|
183
|
+
if isinstance(model, ThermocyclerModuleModel):
|
|
184
|
+
return cls.THERMOCYCLER
|
|
185
|
+
if isinstance(model, HeaterShakerModuleModel):
|
|
186
|
+
return cls.HEATER_SHAKER
|
|
187
|
+
if isinstance(model, MagneticBlockModel):
|
|
188
|
+
return cls.MAGNETIC_BLOCK
|
|
189
|
+
if isinstance(model, AbsorbanceReaderModel):
|
|
190
|
+
return cls.ABSORBANCE_READER
|
|
191
|
+
if isinstance(model, FlexStackerModuleModel):
|
|
192
|
+
return cls.FLEX_STACKER
|
|
193
|
+
|
|
194
|
+
@classmethod
|
|
195
|
+
def to_module_fixture_id(cls, module_type: ModuleType) -> str:
|
|
196
|
+
if module_type == ModuleType.THERMOCYCLER:
|
|
197
|
+
# Thermocyclers are "loaded" in B1 only
|
|
198
|
+
return "thermocyclerModuleV2Front"
|
|
199
|
+
if module_type == ModuleType.TEMPERATURE:
|
|
200
|
+
return "temperatureModuleV2"
|
|
201
|
+
if module_type == ModuleType.HEATER_SHAKER:
|
|
202
|
+
return "heaterShakerModuleV1"
|
|
203
|
+
if module_type == ModuleType.MAGNETIC_BLOCK:
|
|
204
|
+
return "magneticBlockV1"
|
|
205
|
+
if module_type == ModuleType.ABSORBANCE_READER:
|
|
206
|
+
return "absorbanceReaderV1"
|
|
207
|
+
if module_type == ModuleType.FLEX_STACKER:
|
|
208
|
+
return "flexStackerModuleV1"
|
|
209
|
+
else:
|
|
210
|
+
raise ValueError(
|
|
211
|
+
f"Module Type {module_type} does not have a related fixture ID."
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
class MagneticModuleModel(str, Enum):
|
|
216
|
+
MAGNETIC_V1: str = "magneticModuleV1"
|
|
217
|
+
MAGNETIC_V2: str = "magneticModuleV2"
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
class TemperatureModuleModel(str, Enum):
|
|
221
|
+
TEMPERATURE_V1: str = "temperatureModuleV1"
|
|
222
|
+
TEMPERATURE_V2: str = "temperatureModuleV2"
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
class ThermocyclerModuleModel(str, Enum):
|
|
226
|
+
THERMOCYCLER_V1: str = "thermocyclerModuleV1"
|
|
227
|
+
THERMOCYCLER_V2: str = "thermocyclerModuleV2"
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
class HeaterShakerModuleModel(str, Enum):
|
|
231
|
+
HEATER_SHAKER_V1: str = "heaterShakerModuleV1"
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
class MagneticBlockModel(str, Enum):
|
|
235
|
+
MAGNETIC_BLOCK_V1: str = "magneticBlockV1"
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
class AbsorbanceReaderModel(str, Enum):
|
|
239
|
+
ABSORBANCE_READER_V1: str = "absorbanceReaderV1"
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
class FlexStackerModuleModel(str, Enum):
|
|
243
|
+
FLEX_STACKER_V1: str = "flexStackerModuleV1"
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def module_model_from_string(model_string: str) -> ModuleModel:
|
|
247
|
+
for model_enum in {
|
|
248
|
+
MagneticModuleModel,
|
|
249
|
+
TemperatureModuleModel,
|
|
250
|
+
ThermocyclerModuleModel,
|
|
251
|
+
HeaterShakerModuleModel,
|
|
252
|
+
MagneticBlockModel,
|
|
253
|
+
AbsorbanceReaderModel,
|
|
254
|
+
FlexStackerModuleModel,
|
|
255
|
+
}:
|
|
256
|
+
try:
|
|
257
|
+
return cast(ModuleModel, model_enum(model_string))
|
|
258
|
+
except ValueError:
|
|
259
|
+
pass
|
|
260
|
+
raise ValueError(f"No such module model {model_string}")
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
@dataclass(kw_only=True)
|
|
264
|
+
class ModuleAtPort:
|
|
265
|
+
port: str
|
|
266
|
+
name: str
|
|
267
|
+
serial: Optional[str] = None
|
|
268
|
+
usb_port: USBPort = USBPort(name="", port_number=0)
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
@dataclass(kw_only=True)
|
|
272
|
+
class SimulatingModule:
|
|
273
|
+
serial_number: str
|
|
274
|
+
model: Optional[str]
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
@dataclass(kw_only=True)
|
|
278
|
+
class SimulatingModuleAtPort(ModuleAtPort, SimulatingModule):
|
|
279
|
+
pass
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
class BundledFirmware(NamedTuple):
|
|
283
|
+
"""Represents a versioned firmware file, generally bundled into the fs"""
|
|
284
|
+
|
|
285
|
+
version: str
|
|
286
|
+
path: Path
|
|
287
|
+
|
|
288
|
+
def __repr__(self) -> str:
|
|
289
|
+
return f"<BundledFirmware {self.version}, path={self.path}>"
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
class ModuleInfo(NamedTuple):
|
|
293
|
+
model: str # A module model such as "magneticModuleV2"
|
|
294
|
+
fw_version: str # The version of the firmware
|
|
295
|
+
hw_revision: str # the revision of the hardware
|
|
296
|
+
serial: str # the serial number
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
# TODO(mc, 2022-01-18): replace with enum
|
|
300
|
+
ModuleModel = Union[
|
|
301
|
+
MagneticModuleModel,
|
|
302
|
+
TemperatureModuleModel,
|
|
303
|
+
ThermocyclerModuleModel,
|
|
304
|
+
HeaterShakerModuleModel,
|
|
305
|
+
MagneticBlockModel,
|
|
306
|
+
AbsorbanceReaderModel,
|
|
307
|
+
FlexStackerModuleModel,
|
|
308
|
+
]
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
class MagneticStatus(str, Enum):
|
|
312
|
+
ENGAGED = "engaged"
|
|
313
|
+
DISENGAGED = "disengaged"
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
class TemperatureStatus(str, Enum):
|
|
317
|
+
HOLDING = "holding at target"
|
|
318
|
+
COOLING = "cooling"
|
|
319
|
+
HEATING = "heating"
|
|
320
|
+
IDLE = "idle"
|
|
321
|
+
ERROR = "error"
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
class SpeedStatus(str, Enum):
|
|
325
|
+
HOLDING = "holding at target"
|
|
326
|
+
ACCELERATING = "speeding up"
|
|
327
|
+
DECELERATING = "slowing down"
|
|
328
|
+
IDLE = "idle"
|
|
329
|
+
ERROR = "error"
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
class HeaterShakerStatus(str, Enum):
|
|
333
|
+
IDLE = "idle"
|
|
334
|
+
RUNNING = "running"
|
|
335
|
+
ERROR = "error"
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
class AbsorbanceReaderStatus(str, Enum):
|
|
339
|
+
IDLE = "idle"
|
|
340
|
+
MEASURING = "measuring"
|
|
341
|
+
ERROR = "error"
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
class LidStatus(str, Enum):
|
|
345
|
+
ON = "on"
|
|
346
|
+
OFF = "off"
|
|
347
|
+
UNKNOWN = "unknown"
|
|
348
|
+
ERROR = "error"
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
class FlexStackerStatus(str, Enum):
|
|
352
|
+
IDLE = "idle"
|
|
353
|
+
DISPENSING = "dispensing"
|
|
354
|
+
STORING = "storing"
|
|
355
|
+
ERROR = "error"
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
class PlatformState(str, Enum):
|
|
359
|
+
UNKNOWN = "unknown"
|
|
360
|
+
EXTENDED = "extended"
|
|
361
|
+
RETRACTED = "retracted"
|
|
362
|
+
MISSING = "missing"
|
|
363
|
+
|
|
364
|
+
@classmethod
|
|
365
|
+
def from_status(cls, status: PlatformStatus) -> "PlatformState":
|
|
366
|
+
"""Get the state from the platform status."""
|
|
367
|
+
if status.E and not status.R:
|
|
368
|
+
return cls.EXTENDED
|
|
369
|
+
if status.R and not status.E:
|
|
370
|
+
return cls.RETRACTED
|
|
371
|
+
return cls.UNKNOWN
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
class StackerAxisState(str, Enum):
|
|
375
|
+
UNKNOWN = "unknown"
|
|
376
|
+
EXTENDED = "extended"
|
|
377
|
+
RETRACTED = "retracted"
|
|
378
|
+
|
|
379
|
+
@classmethod
|
|
380
|
+
def from_status(
|
|
381
|
+
cls, status: LimitSwitchStatus, axis: StackerAxis
|
|
382
|
+
) -> "StackerAxisState":
|
|
383
|
+
"""Get the axis state from the limit switch status."""
|
|
384
|
+
match axis:
|
|
385
|
+
case StackerAxis.X:
|
|
386
|
+
if status.XE and not status.XR:
|
|
387
|
+
return cls.EXTENDED
|
|
388
|
+
if status.XR and not status.XE:
|
|
389
|
+
return cls.RETRACTED
|
|
390
|
+
case StackerAxis.Z:
|
|
391
|
+
if status.ZE and not status.ZR:
|
|
392
|
+
return cls.EXTENDED
|
|
393
|
+
if status.ZR and not status.ZE:
|
|
394
|
+
return cls.RETRACTED
|
|
395
|
+
case StackerAxis.L:
|
|
396
|
+
return cls.EXTENDED if status.LR else cls.RETRACTED
|
|
397
|
+
return cls.UNKNOWN
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
class LatchState(str, Enum):
|
|
401
|
+
CLOSED = "closed"
|
|
402
|
+
OPENED = "opened"
|
|
403
|
+
|
|
404
|
+
@classmethod
|
|
405
|
+
def from_state(cls, state: StackerAxisState) -> "LatchState":
|
|
406
|
+
"""Get the latch state from the axis state."""
|
|
407
|
+
return cls.CLOSED if state == StackerAxisState.EXTENDED else cls.OPENED
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
class HopperDoorState(str, Enum):
|
|
411
|
+
CLOSED = "closed"
|
|
412
|
+
OPENED = "opened"
|
|
413
|
+
|
|
414
|
+
@classmethod
|
|
415
|
+
def from_state(cls, state: bool) -> "HopperDoorState":
|
|
416
|
+
"""Get the hopper door state from the door state boolean."""
|
|
417
|
+
return cls.CLOSED if state else cls.OPENED
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import logging
|
|
3
|
+
import os
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from glob import glob
|
|
6
|
+
from typing import Any, AsyncGenerator, Dict, Tuple, Union
|
|
7
|
+
|
|
8
|
+
from .errors import UpdateError
|
|
9
|
+
from .mod_abc import AbstractModule
|
|
10
|
+
from opentrons.hardware_control.threaded_async_lock import ThreadedAsyncLock
|
|
11
|
+
from contextlib import asynccontextmanager
|
|
12
|
+
|
|
13
|
+
log = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
_update_transition_lock = ThreadedAsyncLock()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@asynccontextmanager
|
|
19
|
+
async def protect_update_transition() -> AsyncGenerator[None, None]:
|
|
20
|
+
async with _update_transition_lock.lock():
|
|
21
|
+
yield
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
async def update_firmware(
|
|
25
|
+
module: AbstractModule,
|
|
26
|
+
firmware_file: Union[str, Path],
|
|
27
|
+
) -> None:
|
|
28
|
+
"""Apply update of given firmware file to given module.
|
|
29
|
+
|
|
30
|
+
raises an UpdateError with the reason for the failure.
|
|
31
|
+
"""
|
|
32
|
+
async with protect_update_transition():
|
|
33
|
+
flash_port_or_dfu_serial = await module.prep_for_update()
|
|
34
|
+
kwargs: Dict[str, Any] = {
|
|
35
|
+
"stdout": asyncio.subprocess.PIPE,
|
|
36
|
+
"stderr": asyncio.subprocess.PIPE,
|
|
37
|
+
"module": module,
|
|
38
|
+
}
|
|
39
|
+
successful, res = await module.bootloader()(
|
|
40
|
+
flash_port_or_dfu_serial, str(firmware_file), kwargs
|
|
41
|
+
)
|
|
42
|
+
if not successful:
|
|
43
|
+
log.info(f"Bootloader reponse: {res}")
|
|
44
|
+
raise UpdateError(res)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
async def find_bootloader_port() -> str:
|
|
48
|
+
"""
|
|
49
|
+
Finds the port of an Opentrons Module that has entered its bootloader.
|
|
50
|
+
The bootloader port shows up as 'ot_module_(avrdude|samba)_bootloader'
|
|
51
|
+
on the pi; return found port.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
for attempt in range(3):
|
|
55
|
+
bootloader_ports = glob("/dev/ot_module_*_bootloader*")
|
|
56
|
+
if bootloader_ports:
|
|
57
|
+
if len(bootloader_ports) == 1:
|
|
58
|
+
log.info(f"Found bootloader at port {bootloader_ports[0]}")
|
|
59
|
+
return bootloader_ports[0]
|
|
60
|
+
elif len(bootloader_ports) > 1:
|
|
61
|
+
raise OSError("Multiple new bootloader ports" "found on mode switch")
|
|
62
|
+
await asyncio.sleep(2)
|
|
63
|
+
raise Exception("No ot_module bootloaders found in /dev. Try again")
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
async def find_dfu_device(pid: str, expected_device_count: int) -> str:
|
|
67
|
+
"""
|
|
68
|
+
Find the dfu device and return its serial number (separate from module serial).
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
- pid: The USB Product ID of the device
|
|
72
|
+
- expected_device_count: The expected number of "devices" for dfu-util
|
|
73
|
+
to find for this PID. This is necessary because most STM32 MCU's
|
|
74
|
+
will enumerate with multiple DFU devices, representing the
|
|
75
|
+
separate programmable memory regions on the device. If more than
|
|
76
|
+
this many devices are found, it is assumed that either the wrong
|
|
77
|
+
module is in DFU mode *or* multiple modules are in DFU mode.
|
|
78
|
+
"""
|
|
79
|
+
retries = 5
|
|
80
|
+
log.info(f"Searching for a dfu device with PID {pid}")
|
|
81
|
+
while retries != 0:
|
|
82
|
+
retries -= 1
|
|
83
|
+
await asyncio.sleep(1)
|
|
84
|
+
proc = await asyncio.create_subprocess_exec(
|
|
85
|
+
"dfu-util",
|
|
86
|
+
"-l",
|
|
87
|
+
stdout=asyncio.subprocess.PIPE,
|
|
88
|
+
stderr=asyncio.subprocess.PIPE,
|
|
89
|
+
)
|
|
90
|
+
await proc.wait()
|
|
91
|
+
stdout, stderr = await proc.communicate()
|
|
92
|
+
|
|
93
|
+
if stdout is None and stderr is None:
|
|
94
|
+
continue
|
|
95
|
+
if stderr:
|
|
96
|
+
raise RuntimeError(f"Error finding dfu device: {stderr.decode()}")
|
|
97
|
+
|
|
98
|
+
result = stdout.decode()
|
|
99
|
+
if pid not in result:
|
|
100
|
+
# It could take a few seconds for the device to show up
|
|
101
|
+
continue
|
|
102
|
+
devices_found = 0
|
|
103
|
+
for line in result.splitlines():
|
|
104
|
+
if pid in line:
|
|
105
|
+
log.info(f"Found device with PID {pid}")
|
|
106
|
+
devices_found += 1
|
|
107
|
+
serial = line[(line.find("serial=") + 7) :]
|
|
108
|
+
if devices_found == expected_device_count:
|
|
109
|
+
# Heater-Shaker has 2 unique endpoints, Thermocycler has 3
|
|
110
|
+
return serial
|
|
111
|
+
elif devices_found > expected_device_count:
|
|
112
|
+
raise OSError("Multiple new bootloader devices" "found on mode switch")
|
|
113
|
+
|
|
114
|
+
raise RuntimeError(
|
|
115
|
+
"Could not update firmware via dfu. Possible issues- dfu-util"
|
|
116
|
+
" not working or specified dfu device not found"
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
async def upload_via_avrdude(
|
|
121
|
+
port: str, firmware_file_path: str, kwargs: Dict[str, Any]
|
|
122
|
+
) -> Tuple[bool, str]:
|
|
123
|
+
"""
|
|
124
|
+
Run firmware upload command for hardware module with avrdude bootloader.
|
|
125
|
+
|
|
126
|
+
Returns tuple of success boolean and message from bootloader.
|
|
127
|
+
"""
|
|
128
|
+
# avrdude_options
|
|
129
|
+
PART_NO = "atmega32u4"
|
|
130
|
+
PROGRAMMER_ID = "avr109"
|
|
131
|
+
BAUDRATE = "57600"
|
|
132
|
+
|
|
133
|
+
config_file_path = Path("/etc/avrdude.conf")
|
|
134
|
+
proc = await asyncio.create_subprocess_exec(
|
|
135
|
+
"avrdude",
|
|
136
|
+
"-C{}".format(config_file_path),
|
|
137
|
+
"-v",
|
|
138
|
+
"-p{}".format(PART_NO),
|
|
139
|
+
"-c{}".format(PROGRAMMER_ID),
|
|
140
|
+
"-P{}".format(port),
|
|
141
|
+
"-b{}".format(BAUDRATE),
|
|
142
|
+
"-D",
|
|
143
|
+
"-Uflash:w:{}:i".format(firmware_file_path),
|
|
144
|
+
stdout=kwargs["stdout"],
|
|
145
|
+
stderr=kwargs["stderr"],
|
|
146
|
+
)
|
|
147
|
+
await proc.wait()
|
|
148
|
+
|
|
149
|
+
_result = await proc.communicate()
|
|
150
|
+
result = _result[1].decode()
|
|
151
|
+
avrdude_res = _format_avrdude_response(result)
|
|
152
|
+
if avrdude_res[0]:
|
|
153
|
+
log.debug(result)
|
|
154
|
+
else:
|
|
155
|
+
log.error(
|
|
156
|
+
"Failed to update module firmware for {}: {}".format(port, avrdude_res[1])
|
|
157
|
+
)
|
|
158
|
+
return avrdude_res
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def _format_avrdude_response(raw_response: str) -> Tuple[bool, str]:
|
|
162
|
+
avrdude_log = ""
|
|
163
|
+
for line in raw_response.splitlines():
|
|
164
|
+
if "avrdude:" in line and line != raw_response.splitlines()[1]:
|
|
165
|
+
avrdude_log += line.lstrip("avrdude:") + ".."
|
|
166
|
+
if "flash verified" in line:
|
|
167
|
+
return True, line.lstrip("avrdude: ")
|
|
168
|
+
return False, avrdude_log
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
async def upload_via_bossa(
|
|
172
|
+
port: str, firmware_file_path: str, kwargs: Dict[str, Any]
|
|
173
|
+
) -> Tuple[bool, str]:
|
|
174
|
+
"""
|
|
175
|
+
Run firmware upload command for hardware module with SAMBA bootloader.
|
|
176
|
+
|
|
177
|
+
Returns tuple of success boolean and message from bootloader.
|
|
178
|
+
"""
|
|
179
|
+
# bossac -p/dev/ttyACM1 -e -w -v -R --offset=0x2000
|
|
180
|
+
# modules/thermo-cycler/production/firmware/thermo-cycler-arduino.ino.bin
|
|
181
|
+
# NOTE: bossac cannot traverse symlinks to port,
|
|
182
|
+
# so we resolve to real path
|
|
183
|
+
resolved_symlink = os.path.realpath(port)
|
|
184
|
+
log.info(
|
|
185
|
+
f"device at symlinked port: {port} " f"resolved to path: {resolved_symlink}"
|
|
186
|
+
)
|
|
187
|
+
bossa_args = [
|
|
188
|
+
"bossac",
|
|
189
|
+
f"-p{resolved_symlink}",
|
|
190
|
+
"-e",
|
|
191
|
+
"-w",
|
|
192
|
+
"-v",
|
|
193
|
+
"-R",
|
|
194
|
+
"--offset=0x2000",
|
|
195
|
+
f"{firmware_file_path}",
|
|
196
|
+
]
|
|
197
|
+
proc = await asyncio.create_subprocess_exec(
|
|
198
|
+
*bossa_args, stdout=kwargs["stdout"], stderr=kwargs["stderr"]
|
|
199
|
+
)
|
|
200
|
+
stdout, stderr = await proc.communicate()
|
|
201
|
+
res = stdout.decode()
|
|
202
|
+
if "Verify successful" in res:
|
|
203
|
+
log.debug(res)
|
|
204
|
+
return True, res
|
|
205
|
+
elif stderr:
|
|
206
|
+
log.error(f"Failed to update module firmware for {port}: {res}")
|
|
207
|
+
log.error(f"Error given: {stderr.decode()}")
|
|
208
|
+
return False, res
|
|
209
|
+
return False, ""
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
async def upload_via_dfu(
|
|
213
|
+
dfu_serial: str, firmware_file_path: str, kwargs: Dict[str, Any]
|
|
214
|
+
) -> Tuple[bool, str]:
|
|
215
|
+
"""Run firmware upload command for DFU.
|
|
216
|
+
|
|
217
|
+
Unlike other firmware upload methods, this one doesn't take a `port` argument since
|
|
218
|
+
the module isn't recognized as a cdc device in dfu mode and hence doesn't get
|
|
219
|
+
a port. The firmware upload utility, dfu-util, looks for the specific module
|
|
220
|
+
by searching for available dfu devices. Since we check beforehand that only one
|
|
221
|
+
dfu device is available during the upload process, this check is sufficient for us.
|
|
222
|
+
|
|
223
|
+
In the future, if we want to make sure that the dfu device available is in fact
|
|
224
|
+
the one we seek, then we can ask dfu-util to check for available dfu devices with
|
|
225
|
+
a specific serial number (unrelated to Opentrons' module serial numbers).
|
|
226
|
+
Hence, this method takes a `dfu_serial` argument instead.
|
|
227
|
+
|
|
228
|
+
Returns tuple of success boolean and message from bootloader
|
|
229
|
+
"""
|
|
230
|
+
log.info("Starting firmware upload via dfu util")
|
|
231
|
+
dfu_args = [
|
|
232
|
+
"dfu-util",
|
|
233
|
+
"-a 0",
|
|
234
|
+
"-s 0x08000000:leave",
|
|
235
|
+
f"-D{firmware_file_path}",
|
|
236
|
+
"-R",
|
|
237
|
+
]
|
|
238
|
+
proc = await asyncio.create_subprocess_exec(
|
|
239
|
+
*dfu_args, stdout=kwargs["stdout"], stderr=kwargs["stderr"]
|
|
240
|
+
)
|
|
241
|
+
stdout, stderr = await proc.communicate()
|
|
242
|
+
res = stdout.decode()
|
|
243
|
+
|
|
244
|
+
if "File downloaded successfully" in res:
|
|
245
|
+
log.debug(res)
|
|
246
|
+
log.info("Firmware upload successful")
|
|
247
|
+
return True, res
|
|
248
|
+
else:
|
|
249
|
+
log.error(
|
|
250
|
+
f"Failed to update module firmware for {dfu_serial}. "
|
|
251
|
+
# It isn't easy to decipher the issue from stderror alone
|
|
252
|
+
f"stdout: {res} \n"
|
|
253
|
+
f"stderr: {stderr.decode()}"
|
|
254
|
+
)
|
|
255
|
+
return False, res
|