benchlab-pytools 3.3.0__tar.gz → 3.4.0__tar.gz
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.
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/CHANGELOG.md +35 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/PKG-INFO +22 -11
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/README.md +16 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/__init__.py +1 -1
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/core/datasource.py +13 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/flash/README.md +20 -4
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/flash/flash_manager.py +24 -9
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/flash/flash_tool.py +27 -4
- benchlab_pytools-3.4.0/benchlab/flash/requirements.txt +11 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/flash/test_flash_manager.py +43 -5
- benchlab_pytools-3.4.0/benchlab/link/requirements.txt +3 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/main.py +24 -8
- benchlab_pytools-3.4.0/benchlab/mqtt/requirements.txt +3 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/tools.py +3 -1
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/tui/README.md +7 -2
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/tui/config.py +14 -1
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/tui/test_tui_core.py +53 -4
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/tui/tui_core.py +111 -2
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/pyproject.toml +19 -7
- benchlab_pytools-3.3.0/benchlab/flash/requirements.txt +0 -7
- benchlab_pytools-3.3.0/benchlab/mqtt/requirements.txt +0 -3
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/.flake8 +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/.gitignore +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/CONTRIBUTING.md +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/LICENSE +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/__main__.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/bootstrap.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/config/README.md +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/config/__init__.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/config/config_client.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/config/config_manager.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/config/config_tool.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/config/diff.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/config/examples/complete_config.json +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/config/examples/rgb_lighting.json +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/config/examples/simple_fan.json +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/config/http_dto.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/config/schema.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/config/test_config_client.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/config/test_config_manager.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/config/test_diff.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/config/test_http_config_client.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/config/test_http_dto.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/core/README.md +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/core/__init__.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/core/config.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/core/datasource_manager.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/core/device_registry.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/core/discovery.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/core/infrastructure.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/core/process_manager.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/core/retry.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/core/shared_serial.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/core/statistics.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/csv_log/README.md +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/csv_log/__init__.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/csv_log/csv_logger_enhanced.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/csv_log/message_batcher.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/csv_log/requirements.txt +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/csv_log/smart_retry.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/flash/__init__.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/flash/dfu.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/flash/image.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/flash/test_dfu.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/flash/test_flash_tool.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/flash/test_image.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/graph/README.md +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/graph/__init__.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/graph/app.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/graph/requirements.txt +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/graph/runner.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/graph/test_graph.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/graph/ui.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/hwinfo/README.md +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/hwinfo/__init__.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/hwinfo/hwinfo_export.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/hwinfo/requirements.txt +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/hwinfo/test_hwinfo_export.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/launcher.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/link/.env.EXAMPLE +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/link/README.md +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/link/__init__.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/link/link_main.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/link/test_link_main.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/menu.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/menu_classic.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/mqtt/README.md +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/mqtt/__init__.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/mqtt/mqtt.config_template +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/mqtt/mqtt_publisher.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/mqtt/test_mqtt_publisher.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/prefs.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/requirements.txt +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/restapi/.env.example +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/restapi/__init__.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/restapi/favicon.ico +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/restapi/readme.md +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/restapi/requirements.txt +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/restapi/telemetry_api.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/restapi/test_read_recovery.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/restapi/test_server.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/sources.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/tui/__init__.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/tui/requirements.txt +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/tui/test_tui_main.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/tui/tui_main.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/README.md +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/NOTICE.md +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/README.md +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/Running_from_source_on_linux.md +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/__init__.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/assets/vu1_hello_world.png +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/community_applications.md +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/config.yaml +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/database.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/dial_driver.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/dials/Comms_Hub_Gauge.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/dials/Comms_Hub_Server.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/dials/__init__.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/dials/base_logger.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/installer/inc/icon.ico +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/installer/install.nsi +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/make_version.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/pylintrc +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/requirements.txt +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/serial_driver.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/server.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/server_config.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/server_dial_handler.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/vu_notifications.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/android-chrome-192x192.png +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/android-chrome-512x512.png +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/apple-touch-icon.png +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/assets/css/demo.css +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/assets/css/demo.min.css +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/assets/css/demo.rtl.css +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/assets/css/demo.rtl.min.css +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/assets/css/tabler-flags.css +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/assets/css/tabler-flags.min.css +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/assets/css/tabler-flags.rtl.css +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/assets/css/tabler-flags.rtl.min.css +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/assets/css/tabler-payments.css +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/assets/css/tabler-payments.min.css +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/assets/css/tabler-payments.rtl.css +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/assets/css/tabler-payments.rtl.min.css +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/assets/css/tabler-social.css +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/assets/css/tabler-social.min.css +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/assets/css/tabler-social.rtl.css +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/assets/css/tabler-social.rtl.min.css +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/assets/css/tabler-vendors.css +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/assets/css/tabler-vendors.min.css +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/assets/css/tabler-vendors.rtl.css +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/assets/css/tabler-vendors.rtl.min.css +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/assets/css/tabler.css +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/assets/css/tabler.min.css +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/assets/css/tabler.rtl.css +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/assets/css/tabler.rtl.min.css +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/assets/css/theme.min.css +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/assets/img/header.png +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/assets/img/logo.png +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/assets/js/dark-theme.js +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/assets/js/dark-theme.min.js +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/assets/js/demo.js +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/assets/js/demo.min.js +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/assets/js/jquery.min.js +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/assets/js/tabler.esm.js +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/assets/js/tabler.esm.min.js +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/assets/js/tabler.js +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/assets/js/tabler.min.js +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/assets/js/vu1_api.js +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/assets/js/vu1_gui_api_key_settings.js +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/assets/js/vu1_gui_api_keys.js +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/assets/js/vu1_gui_dial.js +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/assets/js/vu1_gui_home.js +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/assets/js/vu1_gui_root.js +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/favicon-16x16.png +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/favicon-32x32.png +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/favicon.ico +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/favicon.png +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/index.html +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/views/api_keys.html +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/views/dial.html +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/views/footer.html +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/views/header.html +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/views/key_settings.html +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/VU-Server/www/views/start.html +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/__init__.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/assets/Barlow-Bold.ttf +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/assets/bl_dial_144x200.png +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/assets/bl_logo_144x200.png +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/devices.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/requirements.txt +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/sensors.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/test_devices.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/test_vu_server_config.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/test_vu_server_manager.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/test_vu_tui.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/vu_dial.config_template +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/vu_logo_gen.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/vu_server.config_template +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/vu_server_config.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/vu_server_manager.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/vu_tui.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/vu/vu_updater.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/wigidash/README.md +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/wigidash/__init__.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/wigidash/assets/Inter-Bold.ttf +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/wigidash/assets/Inter.ttf +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/wigidash/assets/benchlab.png +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/wigidash/benchlab_fleet.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/wigidash/benchlab_graph.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/wigidash/benchlab_overview.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/wigidash/benchlab_telemetry.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/wigidash/benchlab_ui.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/wigidash/benchlab_utils.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/wigidash/requirements.txt +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/wigidash/test_wigidash.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/wigidash/wigidash_device.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/wigidash/wigidash_manager.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/wigidash/wigidash_session.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/wigidash/wigidash_usb.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab/wigidash/wigidash_widget.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/benchlab.py +0 -0
- {benchlab_pytools-3.3.0 → benchlab_pytools-3.4.0}/pytest.ini +0 -0
|
@@ -4,6 +4,40 @@ All notable changes to BENCHLAB PyTools are documented here. Format follows
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versioning follows
|
|
5
5
|
[SemVer](https://semver.org/).
|
|
6
6
|
|
|
7
|
+
## [3.4.0] - 2026-10-22
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- TUI: BENCHLAB2 fault status is now surfaced. The Device tab shows a
|
|
11
|
+
quick-glance warning when a fault is currently active, and a new Faults
|
|
12
|
+
tab lists all 34 known BL2 faults with their live (`FaultStatus`) and
|
|
13
|
+
latched (`FaultLog`) state. Requires BL2 firmware 05+; ORIGINAL devices
|
|
14
|
+
and earlier BL2 firmware show an explanatory message instead. (#95)
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
- `-flash`: confirmed against real hardware that a device doesn't always
|
|
18
|
+
re-enumerate as CDC on its own after leaving DFU mode, even though the
|
|
19
|
+
flash and verify already succeeded. The tool now says so plainly instead
|
|
20
|
+
of a vague "may just be slow re-enumeration" warning, and the
|
|
21
|
+
interactive flow prompts to replug the USB cable and re-checks the
|
|
22
|
+
device before exiting instead of leaving the user to guess. (#97)
|
|
23
|
+
|
|
24
|
+
## [3.3.1] - 2026-10-17
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
- Packaging: `benchlab-pytools[flash]` was missing `pydantic` and
|
|
28
|
+
`requests` (`flash_manager` imports `config_manager` -> `config.schema`
|
|
29
|
+
-> `pydantic`; `--source service_http` needs `requests`), so a clean
|
|
30
|
+
`[flash]`-only install crashed with `ModuleNotFoundError` on first use.
|
|
31
|
+
The launcher also reported `ModuleNotFoundError` as "Flash tool module
|
|
32
|
+
not available in this build" regardless of cause; it now prints the
|
|
33
|
+
real missing module and the install command. (#90)
|
|
34
|
+
- `check_bootloader_supported()` only gated BENCHLAB1 firmware against
|
|
35
|
+
the CMD_BOOTLOADER version floor; a BENCHLAB2 unit on firmware below
|
|
36
|
+
`0x05` (which predates CMD_BOOTLOADER on BL2, per the firmware repo's
|
|
37
|
+
protocol contract freeze history) passed the check and then hit a 5s
|
|
38
|
+
timeout in `enter_dfu_mode()` instead of being told up front to use a
|
|
39
|
+
physical BOOT0/jumper bootloader entry. (#94)
|
|
40
|
+
|
|
7
41
|
## [3.3.0] - 2026-10-01
|
|
8
42
|
|
|
9
43
|
### Added
|
|
@@ -193,6 +227,7 @@ export, MQTT publisher, VU dials, WigiDash, and config import/export tools,
|
|
|
193
227
|
sharing a common data-source layer (direct serial, FastAPI, MQTT, named
|
|
194
228
|
pipe, service HTTP, service WebSocket).
|
|
195
229
|
|
|
230
|
+
[3.3.1]: https://github.com/BenchLab-io/benchlab-pytools/compare/v3.3.0...v3.3.1
|
|
196
231
|
[3.3.0]: https://github.com/BenchLab-io/benchlab-pytools/compare/v3.2.1...v3.3.0
|
|
197
232
|
[3.2.1]: https://github.com/BenchLab-io/benchlab-pytools/compare/v3.2.0...v3.2.1
|
|
198
233
|
[3.2.0]: https://github.com/BenchLab-io/benchlab-pytools/compare/v3.1.0...v3.2.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: benchlab-pytools
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.4.0
|
|
4
4
|
Summary: Python control suite for BENCHLAB telemetry devices
|
|
5
5
|
Project-URL: Homepage, https://benchlab.io/
|
|
6
6
|
Project-URL: Repository, https://github.com/BenchLab-io/benchlab-pytools
|
|
@@ -24,13 +24,14 @@ Classifier: Topic :: System :: Monitoring
|
|
|
24
24
|
Requires-Python: >=3.10
|
|
25
25
|
Requires-Dist: benchlab-pycore>=0.8.0
|
|
26
26
|
Requires-Dist: packaging<26.0,>=23.0
|
|
27
|
+
Requires-Dist: pydantic>=2.0.0
|
|
27
28
|
Requires-Dist: python-dotenv<2.0,>=1.2.2
|
|
28
29
|
Requires-Dist: pywin32>=306; platform_system == 'Windows'
|
|
30
|
+
Requires-Dist: requests>=2.33.1
|
|
29
31
|
Requires-Dist: textual<9.0,>=0.60
|
|
30
32
|
Requires-Dist: windows-curses>=2.4.1; platform_system == 'Windows'
|
|
31
33
|
Provides-Extra: all
|
|
32
34
|
Requires-Dist: amqtt<0.13.0,>=0.12.0; extra == 'all'
|
|
33
|
-
Requires-Dist: benchlab-pycore>=0.8.0; extra == 'all'
|
|
34
35
|
Requires-Dist: blessed>=1.20.0; extra == 'all'
|
|
35
36
|
Requires-Dist: dearpygui>=2.1.1; extra == 'all'
|
|
36
37
|
Requires-Dist: fastapi==0.111.1; extra == 'all'
|
|
@@ -42,25 +43,20 @@ Requires-Dist: numpy>=2.2.0; extra == 'all'
|
|
|
42
43
|
Requires-Dist: paho-mqtt<3.0,>=2.0; extra == 'all'
|
|
43
44
|
Requires-Dist: pillow>=10.0.0; extra == 'all'
|
|
44
45
|
Requires-Dist: pillow>=11.3.0; extra == 'all'
|
|
45
|
-
Requires-Dist: pydantic>=2.0.0; extra == 'all'
|
|
46
46
|
Requires-Dist: pyelftools>=0.31; extra == 'all'
|
|
47
47
|
Requires-Dist: pyserial>=3.5; extra == 'all'
|
|
48
48
|
Requires-Dist: python-dotenv==1.2.2; extra == 'all'
|
|
49
49
|
Requires-Dist: pyusb>=1.3.1; extra == 'all'
|
|
50
50
|
Requires-Dist: pyyaml<7.0,>=6.0; extra == 'all'
|
|
51
51
|
Requires-Dist: pyyaml>=6.0; extra == 'all'
|
|
52
|
-
Requires-Dist: requests>=2.31.0; extra == 'all'
|
|
53
|
-
Requires-Dist: requests>=2.33.1; extra == 'all'
|
|
54
52
|
Requires-Dist: ruamel-yaml; extra == 'all'
|
|
55
53
|
Requires-Dist: tornado; extra == 'all'
|
|
56
54
|
Requires-Dist: tqdm>=4.66; extra == 'all'
|
|
57
55
|
Requires-Dist: uvicorn[standard]==0.24.0; extra == 'all'
|
|
58
56
|
Requires-Dist: websockets<16.0,>=13.0; extra == 'all'
|
|
59
57
|
Provides-Extra: csv-log
|
|
60
|
-
Requires-Dist: benchlab-pycore>=0.8.0; extra == 'csv-log'
|
|
61
58
|
Requires-Dist: pyserial>=3.5; extra == 'csv-log'
|
|
62
59
|
Provides-Extra: flash
|
|
63
|
-
Requires-Dist: benchlab-pycore>=0.8.0; extra == 'flash'
|
|
64
60
|
Requires-Dist: intelhex>=2.3.0; extra == 'flash'
|
|
65
61
|
Requires-Dist: libusb-package>=1.0.26.1; (sys_platform == 'win32') and extra == 'flash'
|
|
66
62
|
Requires-Dist: pyelftools>=0.31; extra == 'flash'
|
|
@@ -72,29 +68,28 @@ Requires-Dist: dearpygui>=2.1.1; extra == 'graph'
|
|
|
72
68
|
Requires-Dist: pyserial>=3.5; extra == 'graph'
|
|
73
69
|
Provides-Extra: hwinfo
|
|
74
70
|
Requires-Dist: pyserial>=3.5; extra == 'hwinfo'
|
|
71
|
+
Provides-Extra: link
|
|
72
|
+
Requires-Dist: paho-mqtt<3.0,>=2.0; extra == 'link'
|
|
73
|
+
Requires-Dist: pyserial>=3.5; extra == 'link'
|
|
75
74
|
Provides-Extra: mqtt
|
|
76
75
|
Requires-Dist: amqtt<0.13.0,>=0.12.0; extra == 'mqtt'
|
|
77
76
|
Requires-Dist: paho-mqtt<3.0,>=2.0; extra == 'mqtt'
|
|
78
77
|
Requires-Dist: pyyaml<7.0,>=6.0; extra == 'mqtt'
|
|
79
78
|
Provides-Extra: restapi
|
|
80
79
|
Requires-Dist: fastapi==0.111.1; extra == 'restapi'
|
|
81
|
-
Requires-Dist: pydantic>=2.0.0; extra == 'restapi'
|
|
82
80
|
Requires-Dist: pyserial>=3.5; extra == 'restapi'
|
|
83
81
|
Requires-Dist: python-dotenv==1.2.2; extra == 'restapi'
|
|
84
82
|
Requires-Dist: uvicorn[standard]==0.24.0; extra == 'restapi'
|
|
85
83
|
Provides-Extra: service-ws
|
|
86
84
|
Requires-Dist: websockets<16.0,>=13.0; extra == 'service-ws'
|
|
87
85
|
Provides-Extra: tui
|
|
88
|
-
Requires-Dist: pydantic>=2.0.0; extra == 'tui'
|
|
89
86
|
Requires-Dist: pyserial>=3.5; extra == 'tui'
|
|
90
|
-
Requires-Dist: requests>=2.33.1; extra == 'tui'
|
|
91
87
|
Provides-Extra: vu
|
|
92
88
|
Requires-Dist: blessed>=1.20.0; extra == 'vu'
|
|
93
89
|
Requires-Dist: numpy; extra == 'vu'
|
|
94
90
|
Requires-Dist: pillow>=10.0.0; extra == 'vu'
|
|
95
91
|
Requires-Dist: pyserial>=3.5; extra == 'vu'
|
|
96
92
|
Requires-Dist: pyyaml>=6.0; extra == 'vu'
|
|
97
|
-
Requires-Dist: requests>=2.31.0; extra == 'vu'
|
|
98
93
|
Requires-Dist: ruamel-yaml; extra == 'vu'
|
|
99
94
|
Requires-Dist: tornado; extra == 'vu'
|
|
100
95
|
Provides-Extra: wigidash
|
|
@@ -191,6 +186,22 @@ python -m benchlab -config ... # Device configuration import/export
|
|
|
191
186
|
python -m benchlab -flash ... # Firmware flash tool (STM32 ROM DFU)
|
|
192
187
|
```
|
|
193
188
|
|
|
189
|
+
The flash tool needs its extra: `pip install "benchlab-pytools[flash]"`. It takes a firmware
|
|
190
|
+
image with `--file` and does not download one; releases live in
|
|
191
|
+
[BENCHLAB-FW](https://github.com/BenchLab-io/BENCHLAB-FW/releases). Start with `--list` to see what
|
|
192
|
+
is connected, and `--verify-only` to check an image against a device without erasing or writing:
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
python -m benchlab -flash --list
|
|
196
|
+
python -m benchlab -flash --file benchlab2-fw07-v0.7.2.bin --verify-only
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
**On Windows, flashing needs a one-time driver step.** In DFU mode the board enumerates as
|
|
200
|
+
ST's ROM bootloader (VID `0483`, PID `DF11`), which does not bind to a driver libusb can use,
|
|
201
|
+
so the tool cannot see a device that is plugged in and sitting in its bootloader. Bind WinUSB
|
|
202
|
+
to it with [Zadig](https://zadig.akeo.ie/) first -- see
|
|
203
|
+
[Windows: USB driver setup](benchlab/flash/README.md#windows-usb-driver-setup-zadig).
|
|
204
|
+
|
|
194
205
|
Running with no flags is equivalent to launching the interactive menu.
|
|
195
206
|
|
|
196
207
|
### Data Sources
|
|
@@ -83,6 +83,22 @@ python -m benchlab -config ... # Device configuration import/export
|
|
|
83
83
|
python -m benchlab -flash ... # Firmware flash tool (STM32 ROM DFU)
|
|
84
84
|
```
|
|
85
85
|
|
|
86
|
+
The flash tool needs its extra: `pip install "benchlab-pytools[flash]"`. It takes a firmware
|
|
87
|
+
image with `--file` and does not download one; releases live in
|
|
88
|
+
[BENCHLAB-FW](https://github.com/BenchLab-io/BENCHLAB-FW/releases). Start with `--list` to see what
|
|
89
|
+
is connected, and `--verify-only` to check an image against a device without erasing or writing:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
python -m benchlab -flash --list
|
|
93
|
+
python -m benchlab -flash --file benchlab2-fw07-v0.7.2.bin --verify-only
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**On Windows, flashing needs a one-time driver step.** In DFU mode the board enumerates as
|
|
97
|
+
ST's ROM bootloader (VID `0483`, PID `DF11`), which does not bind to a driver libusb can use,
|
|
98
|
+
so the tool cannot see a device that is plugged in and sitting in its bootloader. Bind WinUSB
|
|
99
|
+
to it with [Zadig](https://zadig.akeo.ie/) first -- see
|
|
100
|
+
[Windows: USB driver setup](benchlab/flash/README.md#windows-usb-driver-setup-zadig).
|
|
101
|
+
|
|
86
102
|
Running with no flags is equivalent to launching the interactive menu.
|
|
87
103
|
|
|
88
104
|
### Data Sources
|
|
@@ -312,6 +312,15 @@ class DirectDataSource(DataSource):
|
|
|
312
312
|
ser, product_id=product_id)
|
|
313
313
|
if sensors:
|
|
314
314
|
data = self._pycore['translate_sensor_struct'](sensors)
|
|
315
|
+
# translate_sensor_struct_bl2() doesn't read these
|
|
316
|
+
# off the raw struct (BL2 fw05+ only), so pull them
|
|
317
|
+
# directly here.
|
|
318
|
+
fault_status = getattr(sensors, 'FaultStatus', None)
|
|
319
|
+
fault_log = getattr(sensors, 'FaultLog', None)
|
|
320
|
+
if fault_status is not None:
|
|
321
|
+
data['FaultStatus'] = fault_status
|
|
322
|
+
if fault_log is not None:
|
|
323
|
+
data['FaultLog'] = fault_log
|
|
315
324
|
data['timestamp'] = datetime.now(UTC).isoformat()
|
|
316
325
|
with self._lock:
|
|
317
326
|
self._latest_data[uid] = data
|
|
@@ -722,6 +731,10 @@ CS_SHORT_NAME_MAP: Dict[str, str] = {
|
|
|
722
731
|
**{f"FAN{i}_T": f"Fan{i}_RPM" for i in range(1, 10)},
|
|
723
732
|
**{f"FAN{i}_D": f"Fan{i}_Duty" for i in range(1, 10)},
|
|
724
733
|
"FAN_EXT": "FanExtDuty",
|
|
734
|
+
|
|
735
|
+
# BL2 fault bitmasks (fw05+ only) — see Fault enum in benchlab_pycore
|
|
736
|
+
"FAULT_STATUS": "FaultStatus",
|
|
737
|
+
"FAULT_LOG": "FaultLog",
|
|
725
738
|
}
|
|
726
739
|
|
|
727
740
|
|
|
@@ -178,10 +178,26 @@ WinUSB (or libusbK) driver to it using
|
|
|
178
178
|
|
|
179
179
|
1. Start the flash operation so the device enters DFU mode (or put it into
|
|
180
180
|
DFU mode and let it stay there briefly).
|
|
181
|
-
2. Open Zadig
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
181
|
+
2. Open Zadig **as Administrator** and find the device -- it should show up
|
|
182
|
+
as `STM32 BOOTLOADER`. A driverless device is listed by default; only
|
|
183
|
+
enable "List All Devices" if it does not appear.
|
|
184
|
+
3. **Check the USB ID reads `0483 DF11`** before continuing, so the driver
|
|
185
|
+
cannot go onto the wrong device.
|
|
186
|
+
4. Select the WinUSB driver and click "Install Driver" / "Replace Driver".
|
|
187
|
+
This takes around 30 seconds and the window may appear frozen.
|
|
188
|
+
5. Re-run the flash command.
|
|
189
|
+
|
|
190
|
+
To confirm the bind before re-running, on Windows:
|
|
191
|
+
|
|
192
|
+
```powershell
|
|
193
|
+
Get-PnpDevice | Where-Object { $_.InstanceId -like '*VID_0483&PID_DF11*' } |
|
|
194
|
+
Select-Object Status, FriendlyName, Service
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
`Service : WinUSB` with `Status : OK` means libusb can now see it. An
|
|
198
|
+
unbound device instead reports `Status : Error` and
|
|
199
|
+
`ConfigManagerErrorCode : 28` ("no driver installed"), which is the
|
|
200
|
+
underlying cause of the timeout above.
|
|
185
201
|
|
|
186
202
|
This is a one-time setup per machine/USB port combination. This is a
|
|
187
203
|
Windows driver-binding limitation, independent of this tool being pure
|
|
@@ -32,6 +32,11 @@ logger = logging.getLogger("benchlab.flash.manager")
|
|
|
32
32
|
# ignore the opcode).
|
|
33
33
|
MIN_BOOTLOADER_FW_VERSION = 0x04
|
|
34
34
|
|
|
35
|
+
# Same opcode, same commit (c722244), but BL2's frozen protocol contracts
|
|
36
|
+
# show it landed between the fw04 and fw05 freezes -- benchlab2-fw03.json/
|
|
37
|
+
# benchlab2-fw04.json have no BOOTLOADER entry, benchlab2-fw05.json does.
|
|
38
|
+
MIN_BOOTLOADER_FW_VERSION_BL2 = 0x05
|
|
39
|
+
|
|
35
40
|
_KNOWN_PRODUCT_IDS = (BENCHLAB_ORIGINAL_PRODUCT_ID, BENCHLAB_BL2_PRODUCT_ID)
|
|
36
41
|
_BENCHLAB_VENDOR_ID = 0xEE
|
|
37
42
|
|
|
@@ -155,16 +160,20 @@ class FlashManager:
|
|
|
155
160
|
|
|
156
161
|
def check_bootloader_supported(
|
|
157
162
|
self, device_info: dict, product_id: int) -> None:
|
|
158
|
-
if
|
|
159
|
-
|
|
160
|
-
|
|
163
|
+
if product_id == BENCHLAB_ORIGINAL_PRODUCT_ID:
|
|
164
|
+
min_fw = MIN_BOOTLOADER_FW_VERSION
|
|
165
|
+
elif product_id == BENCHLAB_BL2_PRODUCT_ID:
|
|
166
|
+
min_fw = MIN_BOOTLOADER_FW_VERSION_BL2
|
|
167
|
+
else:
|
|
168
|
+
return
|
|
169
|
+
|
|
170
|
+
if device_info.get("FwVersion", 0) < min_fw:
|
|
161
171
|
raise UnsupportedFirmwareError(
|
|
162
172
|
"This device's firmware "
|
|
163
173
|
f"(0x{device_info.get('FwVersion', 0):02X}) predates "
|
|
164
|
-
f"CMD_BOOTLOADER support (requires "
|
|
165
|
-
|
|
166
|
-
"
|
|
167
|
-
"bootloader entry instead.")
|
|
174
|
+
f"CMD_BOOTLOADER support (requires 0x{min_fw:02X}+). It "
|
|
175
|
+
"cannot be put into DFU mode via software -- use a "
|
|
176
|
+
"physical BOOT0/jumper bootloader entry instead.")
|
|
168
177
|
|
|
169
178
|
def enter_dfu_mode(
|
|
170
179
|
self, identifier: str, port: Optional[str] = None,
|
|
@@ -316,7 +325,9 @@ class FlashManager:
|
|
|
316
325
|
|
|
317
326
|
logger.warning(
|
|
318
327
|
"%s did not reappear within %.0fs after leaving DFU mode -- "
|
|
319
|
-
"
|
|
328
|
+
"flash/verify already succeeded, but it didn't re-enumerate "
|
|
329
|
+
"as CDC on its own. Unplug and replug the USB cable to "
|
|
330
|
+
"reconnect it.",
|
|
320
331
|
port, timeout)
|
|
321
332
|
return None
|
|
322
333
|
|
|
@@ -344,7 +355,11 @@ class FlashManager:
|
|
|
344
355
|
"Flash completed but verification failed")
|
|
345
356
|
|
|
346
357
|
if port is not None:
|
|
347
|
-
self.leave_and_reboot(usb_dev, port)
|
|
358
|
+
reboot_info = self.leave_and_reboot(usb_dev, port)
|
|
359
|
+
if reboot_info is None and self.source == "direct":
|
|
360
|
+
message += (
|
|
361
|
+
f" -- {port} did not come back on its own; unplug and "
|
|
362
|
+
"replug the USB cable to reconnect it")
|
|
348
363
|
else:
|
|
349
364
|
# No CDC port to wait for -- just trigger the DfuSe leave
|
|
350
365
|
# sequence so the device reboots into the newly-flashed app.
|
|
@@ -226,8 +226,9 @@ def cmd_flash(args):
|
|
|
226
226
|
|
|
227
227
|
def progress_cb(port, written, total):
|
|
228
228
|
pct = (written / total * 100) if total else 100
|
|
229
|
+
done = total and written >= total
|
|
229
230
|
print(f"\r {port}: {written}/{total} bytes ({pct:.0f}%)",
|
|
230
|
-
end="", flush=True)
|
|
231
|
+
end="\n" if done else "", flush=True)
|
|
231
232
|
|
|
232
233
|
results = manager.flash_many(
|
|
233
234
|
[port for port, _ in targets], args.file,
|
|
@@ -316,8 +317,9 @@ def cmd_flash_bare_dfu(args):
|
|
|
316
317
|
|
|
317
318
|
def progress_cb(written, total):
|
|
318
319
|
pct = (written / total * 100) if total else 100
|
|
320
|
+
done = total and written >= total
|
|
319
321
|
print(f"\r {written}/{total} bytes ({pct:.0f}%)",
|
|
320
|
-
end="", flush=True)
|
|
322
|
+
end="\n" if done else "", flush=True)
|
|
321
323
|
|
|
322
324
|
result = manager.flash_bare_dfu(
|
|
323
325
|
product_id, data, verify_only=args.verify_only,
|
|
@@ -453,24 +455,45 @@ def interactive_mode(args):
|
|
|
453
455
|
|
|
454
456
|
def progress_cb(port, written, total):
|
|
455
457
|
pct = (written / total * 100) if total else 100
|
|
458
|
+
done = total and written >= total
|
|
456
459
|
print(f"\r {port}: {written}/{total} bytes ({pct:.0f}%)",
|
|
457
|
-
end="", flush=True)
|
|
460
|
+
end="\n" if done else "", flush=True)
|
|
458
461
|
|
|
459
462
|
print()
|
|
460
463
|
results = manager.flash_many(
|
|
461
464
|
[_device_identifier(args, device) for device in targets],
|
|
462
465
|
file_path, progress_cb=progress_cb,
|
|
463
466
|
on_unsupported_firmware=_make_boot0_fallback_prompt())
|
|
464
|
-
print()
|
|
465
467
|
|
|
466
468
|
print()
|
|
467
469
|
print("Results:")
|
|
468
470
|
any_failed = False
|
|
471
|
+
needs_reconnect_check = []
|
|
469
472
|
for result in results:
|
|
470
473
|
status = "OK" if result.ok else "FAILED"
|
|
471
474
|
if not result.ok:
|
|
472
475
|
any_failed = True
|
|
473
476
|
print(f" {result.port} [{status}] {result.message}")
|
|
477
|
+
if result.ok and "unplug and replug" in result.message:
|
|
478
|
+
needs_reconnect_check.append(result.port)
|
|
479
|
+
|
|
480
|
+
for port in needs_reconnect_check:
|
|
481
|
+
print()
|
|
482
|
+
while True:
|
|
483
|
+
choice = input(
|
|
484
|
+
f"Replug {port}'s USB cable, then press Enter to check "
|
|
485
|
+
"it's back (or type 'skip' to continue without "
|
|
486
|
+
"checking): ").strip().lower()
|
|
487
|
+
if choice == "skip":
|
|
488
|
+
break
|
|
489
|
+
info = manager.identify_device(port)
|
|
490
|
+
if info is not None:
|
|
491
|
+
fw = info.get('FwVersion', 0)
|
|
492
|
+
print(f" {port} is back (FW: 0x{fw:02X})")
|
|
493
|
+
break
|
|
494
|
+
print(f" {port} not detected yet -- check the cable and try "
|
|
495
|
+
"again.")
|
|
496
|
+
|
|
474
497
|
print()
|
|
475
498
|
input("Press Enter to exit...")
|
|
476
499
|
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
pyserial>=3.5
|
|
2
|
+
pyusb>=1.3.1
|
|
3
|
+
intelhex>=2.3.0
|
|
4
|
+
pyelftools>=0.31
|
|
5
|
+
tqdm>=4.66
|
|
6
|
+
benchlab-pycore>=0.8.0
|
|
7
|
+
libusb-package>=1.0.26.1; sys_platform == 'win32'
|
|
8
|
+
# flash_manager -> config_manager -> config.schema (see #90)
|
|
9
|
+
pydantic>=2.0.0
|
|
10
|
+
# needed by --source service_http (see #90)
|
|
11
|
+
requests>=2.33.1
|
|
@@ -108,11 +108,22 @@ def test_check_bootloader_supported_bl1_version_gate(
|
|
|
108
108
|
info, BENCHLAB_ORIGINAL_PRODUCT_ID)
|
|
109
109
|
|
|
110
110
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
111
|
+
@pytest.mark.parametrize("fw,should_raise", [
|
|
112
|
+
(0x03, True),
|
|
113
|
+
(0x04, True),
|
|
114
|
+
(0x05, False),
|
|
115
|
+
(0x07, False),
|
|
116
|
+
])
|
|
117
|
+
def test_check_bootloader_supported_bl2_version_gate(
|
|
118
|
+
manager, fw, should_raise):
|
|
119
|
+
info = _device_info(product_id=BENCHLAB_BL2_PRODUCT_ID, fw=fw)
|
|
120
|
+
if should_raise:
|
|
121
|
+
with pytest.raises(UnsupportedFirmwareError):
|
|
122
|
+
manager.check_bootloader_supported(
|
|
123
|
+
info, BENCHLAB_BL2_PRODUCT_ID)
|
|
124
|
+
else:
|
|
125
|
+
manager.check_bootloader_supported(
|
|
126
|
+
info, BENCHLAB_BL2_PRODUCT_ID)
|
|
116
127
|
|
|
117
128
|
|
|
118
129
|
# -- enter_dfu_mode / wait_for_dfu_device ----------------------------------
|
|
@@ -354,6 +365,33 @@ def test_flash_bare_dfu_flashes_without_cdc_round_trip(
|
|
|
354
365
|
assert "Flashed and verified successfully" in result.message
|
|
355
366
|
|
|
356
367
|
|
|
368
|
+
def test_erase_verify_leave_hints_replug_when_port_does_not_reappear(
|
|
369
|
+
manager, monkeypatch):
|
|
370
|
+
"""leave_and_reboot() returning None for a 'direct' flash (the CDC
|
|
371
|
+
port never came back on its own) must not be silently swallowed --
|
|
372
|
+
append an actionable unplug/replug hint to the result message rather
|
|
373
|
+
than reporting a bare success. Confirmed against real hardware: the
|
|
374
|
+
flash+verify succeed but the device needs a manual USB replug to
|
|
375
|
+
re-enumerate as CDC (see PR discussion)."""
|
|
376
|
+
fake_usb_dev = MagicMock()
|
|
377
|
+
monkeypatch.setattr(
|
|
378
|
+
manager, "verify_image", lambda usb_dev, d, pid: True)
|
|
379
|
+
monkeypatch.setattr(
|
|
380
|
+
manager, "erase_and_flash",
|
|
381
|
+
lambda usb_dev, d, pid, progress_cb=None: None)
|
|
382
|
+
monkeypatch.setattr(
|
|
383
|
+
manager, "leave_and_reboot", lambda usb_dev, port: None)
|
|
384
|
+
|
|
385
|
+
ok, message = manager._erase_verify_leave(
|
|
386
|
+
fake_usb_dev, b"\x00" * 16, BENCHLAB_ORIGINAL_PRODUCT_ID,
|
|
387
|
+
"COM3", verify_only=False)
|
|
388
|
+
|
|
389
|
+
assert ok
|
|
390
|
+
assert "Flashed and verified successfully" in message
|
|
391
|
+
assert "unplug and replug" in message.lower()
|
|
392
|
+
assert "COM3" in message
|
|
393
|
+
|
|
394
|
+
|
|
357
395
|
def test_flash_bare_dfu_catches_exceptions(manager, monkeypatch):
|
|
358
396
|
monkeypatch.setattr(
|
|
359
397
|
manager, "wait_for_dfu_device",
|
|
@@ -402,34 +402,50 @@ def launch_mode() -> None:
|
|
|
402
402
|
try:
|
|
403
403
|
sys.argv.remove('-config')
|
|
404
404
|
from benchlab.config.config_tool import main as config_main
|
|
405
|
+
except ModuleNotFoundError as exc:
|
|
406
|
+
# A missing transitive dependency is not a missing build: name the
|
|
407
|
+
# real cause. There is no install hint because config's only
|
|
408
|
+
# third-party import (pydantic) is a core dependency, so a miss
|
|
409
|
+
# here means a broken install rather than a missing extra (#90,
|
|
410
|
+
# #92).
|
|
411
|
+
print(f"Config tool unavailable: {exc}")
|
|
412
|
+
else:
|
|
405
413
|
sys.exit(config_main())
|
|
406
|
-
except ModuleNotFoundError:
|
|
407
|
-
print("Config tool module not available in this build.")
|
|
408
414
|
|
|
409
415
|
elif args.flash:
|
|
410
416
|
# Flash tool handles its own argument parsing
|
|
411
417
|
try:
|
|
412
418
|
sys.argv.remove('-flash')
|
|
413
419
|
from benchlab.flash.flash_tool import main as flash_main
|
|
420
|
+
except ModuleNotFoundError as exc:
|
|
421
|
+
# A missing transitive dependency is not a missing build: name
|
|
422
|
+
# it, and point at the extra that supplies it (see #90).
|
|
423
|
+
print(f"Flash tool unavailable: {exc}")
|
|
424
|
+
print('Install it with: pip install "benchlab-pytools[flash]"')
|
|
425
|
+
else:
|
|
414
426
|
sys.exit(flash_main())
|
|
415
|
-
except ModuleNotFoundError:
|
|
416
|
-
print("Flash tool module not available in this build.")
|
|
417
427
|
|
|
418
428
|
elif args.fastapi:
|
|
419
429
|
try:
|
|
420
430
|
from benchlab.restapi.telemetry_api import run_server
|
|
431
|
+
except ModuleNotFoundError as exc:
|
|
432
|
+
# Same reasoning as -flash: a missing dependency is not a missing
|
|
433
|
+
# build, so report which module is absent (see #90).
|
|
434
|
+
print(f"FastAPI / Uvicorn unavailable: {exc}")
|
|
435
|
+
print('Install it with: pip install "benchlab-pytools[restapi]"')
|
|
436
|
+
else:
|
|
421
437
|
run_server()
|
|
422
|
-
except ModuleNotFoundError:
|
|
423
|
-
print("FastAPI / Uvicorn not available in this build.")
|
|
424
438
|
|
|
425
439
|
elif args.mqtt:
|
|
426
440
|
try:
|
|
427
441
|
from benchlab.mqtt.mqtt_publisher import run_mqtt_mode
|
|
442
|
+
except ModuleNotFoundError as exc:
|
|
443
|
+
print(f"MQTT module unavailable: {exc}")
|
|
444
|
+
print('Install it with: pip install "benchlab-pytools[mqtt]"')
|
|
445
|
+
else:
|
|
428
446
|
broker = args.mqtt if args.mqtt else "localhost"
|
|
429
447
|
os.environ.setdefault("MQTT_BROKER", broker)
|
|
430
448
|
run_mqtt_mode(broker)
|
|
431
|
-
except ModuleNotFoundError:
|
|
432
|
-
print("MQTT module not available in this build.")
|
|
433
449
|
|
|
434
450
|
elif args.link:
|
|
435
451
|
_export_link_env(args)
|
|
@@ -25,7 +25,9 @@ CONSUMER_TOOLS = {
|
|
|
25
25
|
"flag": "-config",
|
|
26
26
|
"module": "benchlab.config.config_tool",
|
|
27
27
|
"function": "main",
|
|
28
|
-
|
|
28
|
+
# No requirements file: config's only third-party import is pydantic,
|
|
29
|
+
# which is a core dependency, so there is nothing tool-specific to
|
|
30
|
+
# install (see #92).
|
|
29
31
|
"supported_sources": ["direct", "named_pipe"],
|
|
30
32
|
},
|
|
31
33
|
"flash": {
|
|
@@ -33,7 +33,7 @@ The BENCHLAB Enhanced TUI provides a modern, feature-rich curses-based interface
|
|
|
33
33
|
|
|
34
34
|
## 📊 Telemetry Tabs
|
|
35
35
|
|
|
36
|
-
The TUI has
|
|
36
|
+
The TUI has 9 tabs: `Fleet`, `Device`, `System`, `Motherboard`, `12VHPWR`, `Voltage`, `Temperature`, `Fans`, `Faults` (in this order; see `Config.TAB_NAMES` in `benchlab/tui/config.py`). Tabs can also be jumped to directly with the number keys `0`-`8`.
|
|
37
37
|
|
|
38
38
|
### **Fleet Tab (Tab 0)**
|
|
39
39
|
- Device list with connection status indicators
|
|
@@ -46,6 +46,7 @@ The TUI has 8 tabs: `Fleet`, `Device`, `System`, `Motherboard`, `12VHPWR`, `Volt
|
|
|
46
46
|
- Device identification (Model, Vendor ID, Product ID, UID)
|
|
47
47
|
- Firmware version display
|
|
48
48
|
- TUI refresh interval
|
|
49
|
+
- Quick-glance active-fault warning (BL2 fw05+ only); see the Faults tab for full detail
|
|
49
50
|
|
|
50
51
|
### **System / Motherboard / 12VHPWR Tabs (Tabs 2-4)**
|
|
51
52
|
- Real-time power consumption with progress bars
|
|
@@ -76,12 +77,16 @@ The TUI has 8 tabs: `Fleet`, `Device`, `System`, `Motherboard`, `12VHPWR`, `Volt
|
|
|
76
77
|
- External fan duty monitoring
|
|
77
78
|
- Fan statistics (average duty, max RPM, active count)
|
|
78
79
|
|
|
80
|
+
### **Faults Tab (Tab 8)**
|
|
81
|
+
- Per-fault Active/Logged status for all BENCHLAB 2 fault conditions
|
|
82
|
+
- BL2 firmware 05+ only; shows an unavailable message on ORIGINAL devices or older BL2 firmware
|
|
83
|
+
|
|
79
84
|
## 🎮 Keyboard Shortcuts
|
|
80
85
|
|
|
81
86
|
### **Navigation**
|
|
82
87
|
- `h`, `←` - Move to previous tab
|
|
83
88
|
- `l`, `→` - Move to next tab
|
|
84
|
-
- `0`-`
|
|
89
|
+
- `0`-`8` - Jump directly to a tab by index
|
|
85
90
|
- `q`, `Q` - Quit application
|
|
86
91
|
- `?` - Show/hide help modal
|
|
87
92
|
|
|
@@ -29,7 +29,8 @@ TAB_NAMES = [
|
|
|
29
29
|
"12VHPWR",
|
|
30
30
|
"Voltage",
|
|
31
31
|
"Temperature",
|
|
32
|
-
"Fans"
|
|
32
|
+
"Fans",
|
|
33
|
+
"Faults"]
|
|
33
34
|
|
|
34
35
|
# Statistics display width
|
|
35
36
|
STAT_COLUMN_WIDTH = 10
|
|
@@ -323,6 +324,18 @@ class Layout:
|
|
|
323
324
|
'STATS': 52, # Statistics column
|
|
324
325
|
}
|
|
325
326
|
|
|
327
|
+
# Faults tab column positions — two-column grid (17 rows each) so all
|
|
328
|
+
# 34 Fault enum members fit on one screen under MIN_TERMINAL_ROWS/COLS.
|
|
329
|
+
FAULT_COLS = {
|
|
330
|
+
'NAME': 2, # Fault name column (left grid column)
|
|
331
|
+
'ACTIVE': 26, # Active status column (left grid column)
|
|
332
|
+
'LOGGED': 36, # Logged status column (left grid column)
|
|
333
|
+
'NAME2': 48, # Fault name column (right grid column)
|
|
334
|
+
'ACTIVE2': 72, # Active status column (right grid column)
|
|
335
|
+
'LOGGED2': 82, # Logged status column (right grid column)
|
|
336
|
+
}
|
|
337
|
+
FAULT_ROWS_PER_COL = 17 # Rows per grid column (34 faults / 2)
|
|
338
|
+
|
|
326
339
|
|
|
327
340
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
328
341
|
# Help Text
|