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,364 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
import enum
|
|
3
|
+
import re
|
|
4
|
+
from itertools import groupby
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import List, Optional, Tuple
|
|
7
|
+
from opentrons.hardware_control.types import BoardRevision
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class PinDir(enum.Enum):
|
|
11
|
+
rev_input = enum.auto()
|
|
12
|
+
input = enum.auto()
|
|
13
|
+
output = enum.auto()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class PortGroup:
|
|
17
|
+
MAIN = "main"
|
|
18
|
+
LEFT = "left"
|
|
19
|
+
RIGHT = "right"
|
|
20
|
+
FRONT = "front"
|
|
21
|
+
UNKNOWN = "unknown"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
REV_OG_USB_PORTS = {"3": 1, "5": 2}
|
|
25
|
+
REV_A_USB_HUB = 3
|
|
26
|
+
FLEX_B2_USB_PORT_GROUP_LEFT = 4
|
|
27
|
+
FLEX_B2_USB_PORT_GROUP_RIGHT = 3
|
|
28
|
+
FLEX_B2_USB_PORT_GROUP_FRONT = 7
|
|
29
|
+
FLEX_B2_USB_PORTS = {"4": 1, "3": 2, "2": 3, "1": 4}
|
|
30
|
+
|
|
31
|
+
BUS_PATH = "/sys/bus/usb/devices/usb1/"
|
|
32
|
+
|
|
33
|
+
# Example usb path might look like:
|
|
34
|
+
# '/sys/bus/usb/devices/usb1/1-1/1-1.3/1-1.3:1.0/tty/ttyACM1/dev'.
|
|
35
|
+
# Example hid device path might look like:
|
|
36
|
+
# '/sys/bus/usb/devices/usb1/1-1/1-1.3/1-1.3:1.0/0003:16D0:1199.0002/hidraw/hidraw0/dev'
|
|
37
|
+
# There is only 1 bus that supports USB on the raspberry pi.
|
|
38
|
+
USB_PORT_INFO = re.compile(
|
|
39
|
+
r"""
|
|
40
|
+
(?P<port_path>(\d[\d]*-\d[\d\.]*[/]?)+):
|
|
41
|
+
(?P<device_path>
|
|
42
|
+
\d.\d/
|
|
43
|
+
(tty/tty(\w{4})/dev | [\w:\.]+?/hidraw/hidraw\d/dev)
|
|
44
|
+
)
|
|
45
|
+
""",
|
|
46
|
+
re.VERBOSE,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
HUB_PATTERN = re.compile(r"(\d-[\d.]+\d?)[\/:]")
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@dataclass(frozen=True)
|
|
53
|
+
class USBPort:
|
|
54
|
+
name: str
|
|
55
|
+
port_number: int
|
|
56
|
+
port_group: str = PortGroup.UNKNOWN
|
|
57
|
+
hub: bool = False
|
|
58
|
+
hub_port: Optional[int] = None
|
|
59
|
+
device_path: str = ""
|
|
60
|
+
|
|
61
|
+
@classmethod
|
|
62
|
+
def build(
|
|
63
|
+
cls, full_path: str, board_revision: BoardRevision
|
|
64
|
+
) -> Optional["USBPort"]:
|
|
65
|
+
"""
|
|
66
|
+
Build a USBPort dataclass.
|
|
67
|
+
|
|
68
|
+
An example port path:
|
|
69
|
+
`1-1.3/1-1.3:1.0/tty/ttyACM1/dev`
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
port_path: Full path of a usb device
|
|
73
|
+
board_revision: Board revision
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
Tuple of the port number, port group, hub, hub port, device path, and name
|
|
77
|
+
"""
|
|
78
|
+
match = USB_PORT_INFO.search(full_path)
|
|
79
|
+
if not match:
|
|
80
|
+
return None
|
|
81
|
+
|
|
82
|
+
port_path = match.group("port_path")
|
|
83
|
+
device_path = match.group("device_path")
|
|
84
|
+
port_nodes = cls.get_unique_nodes(port_path)
|
|
85
|
+
hub, port, hub_port, name = cls.find_hub(port_nodes, board_revision)
|
|
86
|
+
hub, port_group, port, hub_port = cls.map_to_revision(
|
|
87
|
+
board_revision,
|
|
88
|
+
(
|
|
89
|
+
hub,
|
|
90
|
+
port,
|
|
91
|
+
hub_port,
|
|
92
|
+
),
|
|
93
|
+
)
|
|
94
|
+
return cls(
|
|
95
|
+
name=name,
|
|
96
|
+
port_number=port,
|
|
97
|
+
port_group=port_group,
|
|
98
|
+
hub=hub,
|
|
99
|
+
hub_port=hub_port,
|
|
100
|
+
device_path=device_path,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
@staticmethod
|
|
104
|
+
def find_hub(
|
|
105
|
+
port_nodes: List[str],
|
|
106
|
+
board_revision: BoardRevision,
|
|
107
|
+
) -> Tuple[Optional[int], int, Optional[int], str]:
|
|
108
|
+
"""
|
|
109
|
+
Find the hub, port, and hub_port data for a USB port from
|
|
110
|
+
the port nodes.
|
|
111
|
+
|
|
112
|
+
The last item of the port nodes list is parsed by splitting
|
|
113
|
+
the item at each period. It is parsed as follows:
|
|
114
|
+
'bus.hub.port.hub_port'. The USB bus data is unused. The
|
|
115
|
+
hub_port data is only populated if a USB hub is connected
|
|
116
|
+
to that port. The Flex FRONT USB port is parsed as:
|
|
117
|
+
'bus.hub.hub_port'.
|
|
118
|
+
|
|
119
|
+
:param port_nodes: A list of unique port id(s)
|
|
120
|
+
:returns: Tuple of the hub, port number, hub_port and name
|
|
121
|
+
"""
|
|
122
|
+
if len(port_nodes) == 1 and "." not in port_nodes[0]:
|
|
123
|
+
# if no hub is attached, such as on a dev kit, the port
|
|
124
|
+
# nodes available will be 1-1
|
|
125
|
+
port = int(port_nodes[0].split("-")[1])
|
|
126
|
+
hub = None
|
|
127
|
+
hub_port = None
|
|
128
|
+
name = port_nodes[0]
|
|
129
|
+
else:
|
|
130
|
+
port_nodes = [node for node in port_nodes if "." in node]
|
|
131
|
+
if len(port_nodes) > 2:
|
|
132
|
+
port_info = port_nodes[2].split(".")
|
|
133
|
+
hub = int(port_info[1])
|
|
134
|
+
port = int(port_info[2])
|
|
135
|
+
hub_port = int(port_info[3])
|
|
136
|
+
name = port_nodes[2]
|
|
137
|
+
elif len(port_nodes) > 1:
|
|
138
|
+
if board_revision == BoardRevision.OG:
|
|
139
|
+
port_info = port_nodes[1].split(".")
|
|
140
|
+
hub = int(port_info[1])
|
|
141
|
+
port = int(port_info[1])
|
|
142
|
+
hub_port = int(port_info[2])
|
|
143
|
+
name = port_nodes[1]
|
|
144
|
+
else:
|
|
145
|
+
port_info = port_nodes[1].split(".")
|
|
146
|
+
hub = int(port_info[1])
|
|
147
|
+
name = port_nodes[1]
|
|
148
|
+
if (board_revision == BoardRevision.FLEX_B2) and (
|
|
149
|
+
hub == FLEX_B2_USB_PORT_GROUP_FRONT
|
|
150
|
+
):
|
|
151
|
+
port = 9
|
|
152
|
+
hub_port = int(port_info[2])
|
|
153
|
+
else:
|
|
154
|
+
port = int(port_info[2])
|
|
155
|
+
hub_port = None
|
|
156
|
+
else:
|
|
157
|
+
if board_revision == BoardRevision.FLEX_B2:
|
|
158
|
+
port_info = port_nodes[0].split(".")
|
|
159
|
+
hub = int(port_info[1])
|
|
160
|
+
port = 9
|
|
161
|
+
hub_port = None
|
|
162
|
+
name = port_nodes[0]
|
|
163
|
+
else:
|
|
164
|
+
port = int(port_nodes[0].split(".")[1])
|
|
165
|
+
hub = None
|
|
166
|
+
hub_port = None
|
|
167
|
+
name = port_nodes[0]
|
|
168
|
+
return hub, port, hub_port, name
|
|
169
|
+
|
|
170
|
+
@staticmethod
|
|
171
|
+
def get_unique_nodes(full_name: str) -> List[str]:
|
|
172
|
+
"""
|
|
173
|
+
Get unique port nodes for a USB port from the USB port path.
|
|
174
|
+
|
|
175
|
+
For a Flex or OT-2_R with a USB hub connected to a port,
|
|
176
|
+
the USB port path will look like:
|
|
177
|
+
`1-1.3/1-1.3.2/1-1.3.2.4/1-1.3.2.4`. This will become the
|
|
178
|
+
following 3 port nodes: ['1-1.3', '1-1.3.2', '1-1.3.2.4'].
|
|
179
|
+
The Flex FRONT USB port with a hub connected will look like:
|
|
180
|
+
`1-1.7/1-1.7.4/1-1.7.4` and become ['1-1.7', '1-1.7.4'].
|
|
181
|
+
|
|
182
|
+
For a Flex or OT-2_R without a USB hub connected to a port,
|
|
183
|
+
the USB port path will look like: `1-1.3/1-1.3.4/1-1.3.4`.
|
|
184
|
+
This will become the follwing 2 port nodes: ['1-1.3', '1-1.3.4'].
|
|
185
|
+
The Flex FRONT USB port without a hub connected will look like:
|
|
186
|
+
`1-1.7/1-1.7` and become ['1-1.7'].
|
|
187
|
+
|
|
188
|
+
For a OT-2_OG with a USB hub connected to a port, the USB
|
|
189
|
+
port path will look like: `1-1.3/1-1.3/1-1.3.3/1-1.3.3`. This will
|
|
190
|
+
become the following 2 port nodes: ['1-1.3', '1-1.3.3'].
|
|
191
|
+
|
|
192
|
+
For a OT-2_OG without a USB hub connected to a port, the USB
|
|
193
|
+
port path will look like: `1-1.3/1-1.3`. This will become the
|
|
194
|
+
following 1 port node: ['1-1.3'].
|
|
195
|
+
|
|
196
|
+
We only need one unique id here, so we will filter out duplicates.
|
|
197
|
+
|
|
198
|
+
:param full_name: Full path of the physical USB Path
|
|
199
|
+
:returns: List of separated USB port paths
|
|
200
|
+
"""
|
|
201
|
+
all_match = HUB_PATTERN.findall(full_name)
|
|
202
|
+
match_set = []
|
|
203
|
+
for match in all_match:
|
|
204
|
+
if match not in match_set:
|
|
205
|
+
match_set.append(match)
|
|
206
|
+
return match_set
|
|
207
|
+
|
|
208
|
+
@staticmethod
|
|
209
|
+
def map_to_revision(
|
|
210
|
+
board_revision: BoardRevision,
|
|
211
|
+
port_info: Tuple[Optional[int], int, Optional[int]],
|
|
212
|
+
) -> Tuple[bool, str, int, Optional[int]]:
|
|
213
|
+
"""
|
|
214
|
+
Synthesize the hub, port_group, and hub_port data for a USB port
|
|
215
|
+
from the hub and hub_port data.
|
|
216
|
+
|
|
217
|
+
If a USB hub is connected to a port, the hub data field is True
|
|
218
|
+
and the hub_port data field is populated. Otherwise, the hub
|
|
219
|
+
data field is False and the hub_port data field is None.
|
|
220
|
+
|
|
221
|
+
For the OT-2, there is only one bank of USB ports, so the
|
|
222
|
+
port_group is always MAIN. For the Flex, there are LEFT, RIGHT,
|
|
223
|
+
and FRONT USB port banks, which map to specific hub values.
|
|
224
|
+
|
|
225
|
+
For the Flex, the RIGHT port values are increased by 4 to match
|
|
226
|
+
the physical hardware labeling (USB5 - USB8).
|
|
227
|
+
|
|
228
|
+
:param port_info: Tuple of the hub, port number, and hub port
|
|
229
|
+
:returns: Tuple of the hub, port group, port number, and hub port
|
|
230
|
+
"""
|
|
231
|
+
hub, port, hub_port = port_info
|
|
232
|
+
if board_revision == BoardRevision.OG:
|
|
233
|
+
if hub:
|
|
234
|
+
return (
|
|
235
|
+
True,
|
|
236
|
+
PortGroup.MAIN,
|
|
237
|
+
REV_OG_USB_PORTS.get(str(port), port),
|
|
238
|
+
hub_port,
|
|
239
|
+
)
|
|
240
|
+
else:
|
|
241
|
+
return (
|
|
242
|
+
False,
|
|
243
|
+
PortGroup.MAIN,
|
|
244
|
+
REV_OG_USB_PORTS.get(str(port), port),
|
|
245
|
+
None,
|
|
246
|
+
)
|
|
247
|
+
elif board_revision == BoardRevision.FLEX_B2:
|
|
248
|
+
if hub == FLEX_B2_USB_PORT_GROUP_LEFT:
|
|
249
|
+
port_group = PortGroup.LEFT
|
|
250
|
+
port = FLEX_B2_USB_PORTS.get(str(port), port)
|
|
251
|
+
elif hub == FLEX_B2_USB_PORT_GROUP_RIGHT:
|
|
252
|
+
port_group = PortGroup.RIGHT
|
|
253
|
+
port = FLEX_B2_USB_PORTS.get(str(port), port)
|
|
254
|
+
port = port + 4
|
|
255
|
+
elif hub == FLEX_B2_USB_PORT_GROUP_FRONT:
|
|
256
|
+
port_group = PortGroup.FRONT
|
|
257
|
+
else:
|
|
258
|
+
port_group = PortGroup.UNKNOWN
|
|
259
|
+
if hub_port:
|
|
260
|
+
return True, port_group, port, hub_port
|
|
261
|
+
else:
|
|
262
|
+
return False, port_group, port, None
|
|
263
|
+
else: # any variant of OT2-Refresh
|
|
264
|
+
if hub_port:
|
|
265
|
+
return True, PortGroup.MAIN, port, hub_port
|
|
266
|
+
else:
|
|
267
|
+
return False, PortGroup.MAIN, port, None
|
|
268
|
+
|
|
269
|
+
def __hash__(self) -> int:
|
|
270
|
+
"""
|
|
271
|
+
Hash function.
|
|
272
|
+
|
|
273
|
+
To have a unique set of nodes, they must
|
|
274
|
+
all have a unique hash. Lists are not
|
|
275
|
+
hashable which is why we need to unpack
|
|
276
|
+
the list here.
|
|
277
|
+
"""
|
|
278
|
+
return hash(self.name)
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
class GPIOPin:
|
|
282
|
+
@classmethod
|
|
283
|
+
def build(cls, name: str, in_out: PinDir, pin: int) -> GPIOPin:
|
|
284
|
+
# use this method if the pin number is the same
|
|
285
|
+
# across all board revisions
|
|
286
|
+
return cls(name, in_out, rev_og=pin, rev_a=pin, rev_b=pin, rev_c=pin)
|
|
287
|
+
|
|
288
|
+
@classmethod
|
|
289
|
+
def build_with_rev(
|
|
290
|
+
cls, name: str, in_out: PinDir, **kwargs: Optional[int]
|
|
291
|
+
) -> GPIOPin:
|
|
292
|
+
return cls(name, in_out, **kwargs)
|
|
293
|
+
|
|
294
|
+
def __init__(
|
|
295
|
+
self,
|
|
296
|
+
name: str,
|
|
297
|
+
in_out: PinDir,
|
|
298
|
+
rev_og: Optional[int] = None,
|
|
299
|
+
rev_a: Optional[int] = None,
|
|
300
|
+
rev_b: Optional[int] = None,
|
|
301
|
+
rev_c: Optional[int] = None,
|
|
302
|
+
):
|
|
303
|
+
self.name = name
|
|
304
|
+
self.in_out = in_out
|
|
305
|
+
self.rev_og = rev_og
|
|
306
|
+
self.rev_a = rev_a
|
|
307
|
+
self.rev_b = rev_b
|
|
308
|
+
self.rev_c = rev_c
|
|
309
|
+
|
|
310
|
+
def by_board_rev(self, board_rev: BoardRevision) -> Optional[int]:
|
|
311
|
+
ref = {
|
|
312
|
+
BoardRevision.OG: self.rev_og,
|
|
313
|
+
BoardRevision.A: self.rev_a,
|
|
314
|
+
BoardRevision.B: self.rev_b,
|
|
315
|
+
BoardRevision.C: self.rev_c,
|
|
316
|
+
BoardRevision.UNKNOWN: self.rev_og,
|
|
317
|
+
}
|
|
318
|
+
return ref[board_rev]
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
class GPIOGroup:
|
|
322
|
+
def __init__(self, pins: List[GPIOPin]):
|
|
323
|
+
self.pins = pins
|
|
324
|
+
|
|
325
|
+
def __getattr__(self, item: str) -> GPIOPin:
|
|
326
|
+
res = next(filter(lambda x: x.name is item, self.pins), None)
|
|
327
|
+
assert res, f"Failed to find GPIOPin named: {item}"
|
|
328
|
+
return res
|
|
329
|
+
|
|
330
|
+
def by_type(self, pin_dir: PinDir) -> GPIOGroup:
|
|
331
|
+
return GPIOGroup(list(filter(lambda x: x.in_out is pin_dir, self.pins)))
|
|
332
|
+
|
|
333
|
+
def by_names(self, names: List[str]) -> GPIOGroup:
|
|
334
|
+
return GPIOGroup(list(filter(lambda x: x.name in names, self.pins)))
|
|
335
|
+
|
|
336
|
+
def group_by_pins(self, board_rev: BoardRevision) -> List[List[GPIOPin]]:
|
|
337
|
+
c = groupby(self.pins, key=lambda x: x.by_board_rev(board_rev))
|
|
338
|
+
l: List[List[GPIOPin]] = []
|
|
339
|
+
for k, v in c:
|
|
340
|
+
l.append(list(v))
|
|
341
|
+
return l
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
gpio_group = GPIOGroup(
|
|
345
|
+
[
|
|
346
|
+
# revision pins (input)
|
|
347
|
+
GPIOPin.build("rev_0", PinDir.rev_input, 17),
|
|
348
|
+
GPIOPin.build("rev_1", PinDir.rev_input, 27),
|
|
349
|
+
# output pins
|
|
350
|
+
GPIOPin.build("frame_leds", PinDir.output, 6),
|
|
351
|
+
GPIOPin.build("blue_button", PinDir.output, 13),
|
|
352
|
+
GPIOPin.build("halt", PinDir.output, 18),
|
|
353
|
+
GPIOPin.build("green_button", PinDir.output, 19),
|
|
354
|
+
GPIOPin.build("audio_enable", PinDir.output, 21),
|
|
355
|
+
GPIOPin.build("isp", PinDir.output, 23),
|
|
356
|
+
GPIOPin.build("reset", PinDir.output, 24),
|
|
357
|
+
GPIOPin.build("red_button", PinDir.output, 26),
|
|
358
|
+
# input pins
|
|
359
|
+
GPIOPin.build("button_input", PinDir.input, 5),
|
|
360
|
+
GPIOPin.build_with_rev("door_sw_filt", PinDir.input, rev_og=20, rev_a=12),
|
|
361
|
+
GPIOPin.build_with_rev("window_sw_filt", PinDir.input, rev_og=20, rev_a=16),
|
|
362
|
+
GPIOPin.build("window_door_sw", PinDir.input, 20),
|
|
363
|
+
]
|
|
364
|
+
)
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""
|
|
2
|
+
USB Driver.
|
|
3
|
+
|
|
4
|
+
A class to convert info from the usb bus into a
|
|
5
|
+
more readable format.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import subprocess
|
|
9
|
+
import os
|
|
10
|
+
from typing import List, Union
|
|
11
|
+
|
|
12
|
+
from opentrons.hardware_control.modules.types import (
|
|
13
|
+
ModuleAtPort,
|
|
14
|
+
SimulatingModuleAtPort,
|
|
15
|
+
)
|
|
16
|
+
from opentrons.hardware_control.types import BoardRevision
|
|
17
|
+
|
|
18
|
+
from .interfaces import USBDriverInterface
|
|
19
|
+
from .types import BUS_PATH, USBPort
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class USBBus(USBDriverInterface):
|
|
23
|
+
def __init__(self, board_revision: BoardRevision):
|
|
24
|
+
self._board_revision = board_revision
|
|
25
|
+
|
|
26
|
+
@staticmethod
|
|
27
|
+
def _read_bus() -> List[str]:
|
|
28
|
+
"""
|
|
29
|
+
Read the USB Bus information.
|
|
30
|
+
|
|
31
|
+
Use the sys bus path to find all of the USBs with
|
|
32
|
+
active devices connected to them.
|
|
33
|
+
"""
|
|
34
|
+
read = [""]
|
|
35
|
+
try:
|
|
36
|
+
read = (
|
|
37
|
+
subprocess.check_output(["find", BUS_PATH, "-name", "dev"])
|
|
38
|
+
.decode()
|
|
39
|
+
.splitlines()
|
|
40
|
+
)
|
|
41
|
+
except Exception:
|
|
42
|
+
pass
|
|
43
|
+
return read
|
|
44
|
+
|
|
45
|
+
@staticmethod
|
|
46
|
+
def _read_symlink(virtual_port: str) -> str:
|
|
47
|
+
""" """
|
|
48
|
+
symlink = ""
|
|
49
|
+
try:
|
|
50
|
+
symlink = os.readlink(virtual_port)
|
|
51
|
+
except OSError:
|
|
52
|
+
pass
|
|
53
|
+
return symlink
|
|
54
|
+
|
|
55
|
+
def _read_usb_bus(self) -> List[USBPort]:
|
|
56
|
+
"""
|
|
57
|
+
Read usb bus
|
|
58
|
+
|
|
59
|
+
Take the value returned from the USB bus and match
|
|
60
|
+
the paths to the expected port paths for modules.
|
|
61
|
+
:returns: A list of matching ports as dataclasses
|
|
62
|
+
"""
|
|
63
|
+
active_ports = self._read_bus()
|
|
64
|
+
port_matches = []
|
|
65
|
+
for port in active_ports:
|
|
66
|
+
usb_port = USBPort.build(port.strip("/"), self._board_revision)
|
|
67
|
+
if usb_port:
|
|
68
|
+
port_matches.append(usb_port)
|
|
69
|
+
return port_matches
|
|
70
|
+
|
|
71
|
+
def match_virtual_ports(
|
|
72
|
+
self,
|
|
73
|
+
virtual_ports: Union[List[ModuleAtPort], List[SimulatingModuleAtPort]],
|
|
74
|
+
) -> Union[List[ModuleAtPort], List[SimulatingModuleAtPort]]:
|
|
75
|
+
"""
|
|
76
|
+
Match Virtual Ports
|
|
77
|
+
|
|
78
|
+
Given a list of virtual module ports, look up the
|
|
79
|
+
symlink to find the device path and link that to
|
|
80
|
+
the physical usb port information.
|
|
81
|
+
The virtual port path looks something like:
|
|
82
|
+
dev/ot_module_[MODULE NAME]
|
|
83
|
+
:param virtual_ports: A list of ModuleAtPort or SimulatingModuleAtPort objects
|
|
84
|
+
that hold the name and virtual port of each module
|
|
85
|
+
connected to the robot.
|
|
86
|
+
|
|
87
|
+
:returns: The updated list of ModuleAtPort
|
|
88
|
+
dataclasses with the physical usb port
|
|
89
|
+
information updated.
|
|
90
|
+
"""
|
|
91
|
+
actual_ports = self._read_usb_bus()
|
|
92
|
+
sorted_virtual_ports = []
|
|
93
|
+
|
|
94
|
+
for p in actual_ports:
|
|
95
|
+
for vp in virtual_ports:
|
|
96
|
+
serial_port = self._read_symlink(vp.port)
|
|
97
|
+
if serial_port in p.device_path:
|
|
98
|
+
vp.usb_port = p
|
|
99
|
+
sorted_virtual_ports.append(vp)
|
|
100
|
+
break
|
|
101
|
+
|
|
102
|
+
return sorted_virtual_ports or virtual_ports
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""
|
|
2
|
+
USB Simulating Driver.
|
|
3
|
+
|
|
4
|
+
A class to convert info from the usb bus into a
|
|
5
|
+
more readable format.
|
|
6
|
+
"""
|
|
7
|
+
from typing import List, Union
|
|
8
|
+
|
|
9
|
+
from opentrons.hardware_control.modules.types import (
|
|
10
|
+
ModuleAtPort,
|
|
11
|
+
SimulatingModuleAtPort,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
from .interfaces import USBDriverInterface
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class USBBusSimulator(USBDriverInterface):
|
|
18
|
+
def match_virtual_ports(
|
|
19
|
+
self,
|
|
20
|
+
virtual_port: Union[List[ModuleAtPort], List[SimulatingModuleAtPort]],
|
|
21
|
+
) -> Union[List[ModuleAtPort], List[SimulatingModuleAtPort]]:
|
|
22
|
+
return virtual_port
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
from typing import List, Optional, Iterator
|
|
2
|
+
|
|
3
|
+
import serial # type: ignore[import-untyped]
|
|
4
|
+
from serial import Serial
|
|
5
|
+
from serial.tools import list_ports # type: ignore[import-untyped]
|
|
6
|
+
import contextlib
|
|
7
|
+
import logging
|
|
8
|
+
|
|
9
|
+
from serial.tools.list_ports_common import ListPortInfo # type: ignore[import-untyped]
|
|
10
|
+
|
|
11
|
+
log = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
RECOVERY_TIMEOUT = 10
|
|
14
|
+
DEFAULT_SERIAL_TIMEOUT = 5
|
|
15
|
+
DEFAULT_WRITE_TIMEOUT = 30
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class SerialNoResponse(Exception):
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def get_ports_by_name(device_name: str) -> List[str]:
|
|
23
|
+
"""Returns all serial devices with a given name"""
|
|
24
|
+
filtered_devices = filter(
|
|
25
|
+
lambda device: device_name in device[1], list_ports.comports()
|
|
26
|
+
)
|
|
27
|
+
device_ports = [device[0] for device in filtered_devices]
|
|
28
|
+
return device_ports
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def get_port_by_VID(vid: str) -> Optional[ListPortInfo]:
|
|
32
|
+
"""Returns first serial device with a given VID"""
|
|
33
|
+
for d in list_ports.comports():
|
|
34
|
+
if d.vid == vid:
|
|
35
|
+
return d[0]
|
|
36
|
+
return None
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@contextlib.contextmanager
|
|
40
|
+
def serial_with_temp_timeout(
|
|
41
|
+
serial_connection: Serial, timeout: float
|
|
42
|
+
) -> Iterator[Serial]:
|
|
43
|
+
"""Implements a temporary timeout for a serial connection"""
|
|
44
|
+
saved_timeout = serial_connection.timeout
|
|
45
|
+
if timeout is not None:
|
|
46
|
+
serial_connection.timeout = timeout
|
|
47
|
+
try:
|
|
48
|
+
yield serial_connection
|
|
49
|
+
finally:
|
|
50
|
+
serial_connection.timeout = saved_timeout
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _parse_serial_response(response: bytes, ack: bytes) -> Optional[bytes]:
|
|
54
|
+
if ack in response:
|
|
55
|
+
parsed_response = response.split(ack)[0]
|
|
56
|
+
return parsed_response.strip()
|
|
57
|
+
else:
|
|
58
|
+
return None
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def clear_buffer(serial_connection: Serial) -> None:
|
|
62
|
+
serial_connection.reset_input_buffer()
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _write_to_device_and_return(
|
|
66
|
+
cmd: str, ack: str, device_connection: Serial, tag: Optional[str] = None
|
|
67
|
+
) -> str:
|
|
68
|
+
"""Writes to a serial device.
|
|
69
|
+
- Formats command
|
|
70
|
+
- Wait for ack return
|
|
71
|
+
- return parsed response"""
|
|
72
|
+
|
|
73
|
+
if not tag:
|
|
74
|
+
tag = device_connection.port
|
|
75
|
+
|
|
76
|
+
encoded_write = cmd.encode()
|
|
77
|
+
encoded_ack = ack.encode()
|
|
78
|
+
log.debug(f"{tag}: Write -> {encoded_write!r}")
|
|
79
|
+
device_connection.write(encoded_write)
|
|
80
|
+
response = device_connection.read_until(encoded_ack)
|
|
81
|
+
log.debug(f"{tag}: Read <- {response}")
|
|
82
|
+
if encoded_ack not in response:
|
|
83
|
+
log.warning(f"{tag}: timed out after {device_connection.timeout}")
|
|
84
|
+
raise SerialNoResponse(
|
|
85
|
+
"No response from serial port after {} second(s)".format(
|
|
86
|
+
device_connection.timeout
|
|
87
|
+
)
|
|
88
|
+
)
|
|
89
|
+
clean_response = _parse_serial_response(response, encoded_ack)
|
|
90
|
+
if clean_response:
|
|
91
|
+
return clean_response.decode()
|
|
92
|
+
return ""
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _connect(port_name: str, baudrate: int) -> Serial:
|
|
96
|
+
ser = serial.serial_for_url(
|
|
97
|
+
url=port_name, baudrate=baudrate, timeout=DEFAULT_SERIAL_TIMEOUT
|
|
98
|
+
)
|
|
99
|
+
log.debug(ser)
|
|
100
|
+
return ser
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _attempt_command_recovery(
|
|
104
|
+
command: str, ack: str, serial_conn: Serial, tag: Optional[str] = None
|
|
105
|
+
) -> str:
|
|
106
|
+
"""Recovery after following a failed write_and_return() attempt"""
|
|
107
|
+
if not tag:
|
|
108
|
+
tag = serial_conn.port
|
|
109
|
+
with serial_with_temp_timeout(serial_conn, RECOVERY_TIMEOUT) as device:
|
|
110
|
+
response = _write_to_device_and_return(command, ack, device, tag=tag)
|
|
111
|
+
if response is None:
|
|
112
|
+
log.debug(f"{tag}: No valid response during _attempt_command_recovery")
|
|
113
|
+
raise RuntimeError(
|
|
114
|
+
"Recovery attempted - no valid serial response "
|
|
115
|
+
"for command: {} in {} seconds".format(command.encode(), RECOVERY_TIMEOUT)
|
|
116
|
+
)
|
|
117
|
+
return response
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def write_and_return(
|
|
121
|
+
command: str,
|
|
122
|
+
ack: str,
|
|
123
|
+
serial_connection: Serial,
|
|
124
|
+
timeout: int = DEFAULT_WRITE_TIMEOUT,
|
|
125
|
+
tag: Optional[str] = None,
|
|
126
|
+
) -> str:
|
|
127
|
+
"""Write a command and return the response"""
|
|
128
|
+
clear_buffer(serial_connection)
|
|
129
|
+
with serial_with_temp_timeout(serial_connection, timeout) as device_connection:
|
|
130
|
+
response = _write_to_device_and_return(command, ack, device_connection, tag)
|
|
131
|
+
return response
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def connect(
|
|
135
|
+
device_name: str, port: Optional[str] = None, baudrate: int = 115200
|
|
136
|
+
) -> Serial:
|
|
137
|
+
"""
|
|
138
|
+
Creates a serial connection.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
device_name: defaults to 'Smoothieboard'
|
|
142
|
+
port: the optional port
|
|
143
|
+
baudrate: integer frequency for serial communication
|
|
144
|
+
|
|
145
|
+
Returns:
|
|
146
|
+
serial.Serial connection
|
|
147
|
+
"""
|
|
148
|
+
if not port:
|
|
149
|
+
port = get_ports_by_name(device_name=device_name)[0]
|
|
150
|
+
log.debug("Device name: {}, Port: {}".format(device_name, port))
|
|
151
|
+
return _connect(port_name=port, baudrate=baudrate)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from opentrons.drivers.asyncio.communication import SerialConnection
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
log = logging.getLogger(__name__)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class RemoveUnwantedCharactersMixin:
|
|
10
|
+
"""Mixin that removes echoed command and other unwanted characters
|
|
11
|
+
from the response."""
|
|
12
|
+
|
|
13
|
+
def process_raw_response(self, command: str, response: str) -> str:
|
|
14
|
+
"""Process the raw response."""
|
|
15
|
+
return self._remove_unwanted_characters(command=command, response=response)
|
|
16
|
+
|
|
17
|
+
@staticmethod
|
|
18
|
+
def _remove_unwanted_characters(command: str, response: str) -> str:
|
|
19
|
+
# smoothieware can enter a weird state, where it repeats back
|
|
20
|
+
# the sent command at the beginning of its response.
|
|
21
|
+
# Check for this echo, and strips the command from the response
|
|
22
|
+
def _is_token_command(_s: str) -> bool:
|
|
23
|
+
"""check if token is a command"""
|
|
24
|
+
# A single letter token cannot be assumed to be a command.
|
|
25
|
+
# For example: "M369 L" response is "L:2132121212".
|
|
26
|
+
return len(_s) > 1
|
|
27
|
+
|
|
28
|
+
# Split at spaces.
|
|
29
|
+
tokens = (c.strip() for c in command.strip().split(" "))
|
|
30
|
+
# A list of commands to remove from response. Including the entire
|
|
31
|
+
# command.
|
|
32
|
+
remove_from_response = [command] + [c for c in tokens if _is_token_command(c)]
|
|
33
|
+
|
|
34
|
+
# also removing any inadvertent newline/return characters
|
|
35
|
+
# this is ok because all data we need from Smoothie is returned on
|
|
36
|
+
# the first line in the response
|
|
37
|
+
remove_from_response += ["\r", "\n"]
|
|
38
|
+
modified_response = str(response)
|
|
39
|
+
|
|
40
|
+
for cmd in remove_from_response:
|
|
41
|
+
modified_response = modified_response.replace(cmd, "")
|
|
42
|
+
|
|
43
|
+
if modified_response != response:
|
|
44
|
+
log.debug(f"Removed characters from response: {response}")
|
|
45
|
+
log.debug(f"Newly formatted response: {modified_response}")
|
|
46
|
+
|
|
47
|
+
return modified_response.strip()
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class SmoothieConnection(RemoveUnwantedCharactersMixin, SerialConnection):
|
|
51
|
+
pass
|