Refactor GUI into a webpage using a HTTP-API#40
Conversation
= switch to compressed log
…was not thought of)
+ Add stop-button + Add fullscreen-mode for graph
…oints for logs and configs and a new ConfigFilesPanel
…one. This allowed the fix of B-Mode
- Add documentation for new GUI / wulpus controller
Add gitlab runner build of a all-in-one exe using pyinstaller
|
@13Bytes , thank you for the contribution to the WULPUS project. It is great to see your alternative GUI. Our team is currently in a conference trip, we will review your pull request proposal in early October. |
+ Add Matlab import script + make logfiles more tracable to wulpus by appending connection details
Add support for multiple WULPUS in parallel
|
@13Bytes , sorry for the delayed reply on this pull request. Unfortunately, I could not review it on time, and since late November I am no longer a member of the PULP-BIO organization nor a maintainer of the WULPUS repository. I hope others can take it over and merge your contributions. Thank you again for your hard work. Best, |
There was a problem hiding this comment.
Pull request overview
Refactors the legacy Python/Jupyter GUI into a browser-based UI backed by a FastAPI HTTP/WebSocket API, adds logging/replay functionality, and introduces a Windows build pipeline via PyInstaller + GitHub Actions.
Changes:
- Added a FastAPI backend + WebSocket streaming layer with a new core
Wulpusdevice/state class and typed config/models. - Introduced a new React/Vite frontend (config editing, live plots, logs/replay, series mode).
- Added packaging/build automation (PyInstaller spec + GitHub Actions artifact/release flow) and updated docs/changelogs.
Reviewed changes
Copilot reviewed 77 out of 94 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| sw/wulpus/wulpus.py | New core device/state class (connect/start/measure/save). |
| sw/wulpus/wulpus_model.py | Adds typed status/measurement + HTTP response model. |
| sw/wulpus/wulpus_mock.py | Adds mock/replay implementation for backend. |
| sw/wulpus/wulpus_config_models.py | Adds Pydantic config models for US + TX/RX configs. |
| sw/wulpus/wulpus_api.py | Generates HW config/restart byte packages. |
| sw/wulpus/wulpus_api_helper.py | Helpers for package building and TX/RX bitmask building. |
| sw/wulpus/websocket_manager.py | Manages WS clients + status/data broadcast. |
| sw/wulpus/series.py | Implements measurement series scheduling loop + models. |
| sw/wulpus/plot_helpers.py | Adds plotting utilities for saved log format. |
| sw/wulpus/helper.py | Adds zip/parquet log helpers, config persistence helpers, misc utilities. |
| sw/wulpus/data_processing.py | Adds DSP/peak detection processor + analysis config model. |
| sw/wulpus/interface.py | Introduces async dongle interface contract + connection types. |
| sw/wulpus/interface_usb.py | Implements serial dongle backend under new interface. |
| sw/wulpus/interface_direct.py | Implements direct BLE backend using Bleak + frame assembly. |
| sw/wulpus/interface_mock.py | Implements mock dongle backend for simulation. |
| sw/wulpus/main.spec | PyInstaller spec for building backend + bundled frontend assets. |
| sw/wulpus/.gitignore | Ignores backend build artifacts. |
| sw/wulpus/configs/.gitignore | Keeps configs dir tracked but empty by default. |
| sw/wulpus/config-analysis/.gitignore | Keeps analysis-config dir tracked but empty by default. |
| sw/wulpus/measurements/.gitignore | Keeps measurements dir tracked but empty by default. |
| sw/wulpus/production-frontend/.gitignore | Keeps production frontend dir tracked but empty by default. |
| sw/wulpus-frontend/package.json | Defines frontend dependencies/scripts (Vite/React/Tailwind/Plotly/etc.). |
| sw/wulpus-frontend/vite.config.ts | Adds dev proxy for API/log/config/ws endpoints. |
| sw/wulpus-frontend/tsconfig.json | TS project references setup. |
| sw/wulpus-frontend/tsconfig.app.json | App TS config (strict, bundler mode). |
| sw/wulpus-frontend/tsconfig.node.json | Node/Vite TS config. |
| sw/wulpus-frontend/eslint.config.js | ESLint configuration for TS/React hooks/refresh. |
| sw/wulpus-frontend/index.html | Frontend HTML entrypoint. |
| sw/wulpus-frontend/README.md | Frontend dev/build instructions. |
| sw/wulpus-frontend/.gitignore | Ignores node/dist/editor artifacts. |
| sw/wulpus-frontend/build.py | Builds frontend and copies into backend production dir. |
| sw/wulpus-frontend/public/vite.svg | Adds Vite logo asset. |
| sw/wulpus-frontend/src/main.tsx | React app bootstrap + router + query client. |
| sw/wulpus-frontend/src/App.tsx | Main dashboard wiring (WS, config state, panels, graph). |
| sw/wulpus-frontend/src/api.ts | Frontend API client for backend endpoints. |
| sw/wulpus-frontend/src/websocket-types.ts | Frontend types mirroring backend models. |
| sw/wulpus-frontend/src/ConnectionPanel.tsx | Connection management UI + mock toggle + start/stop controls. |
| sw/wulpus-frontend/src/StatusView.tsx | Per-device status + connect/disconnect controls. |
| sw/wulpus-frontend/src/UsConfig.tsx | US config panel UI (basic/advanced fields). |
| sw/wulpus-frontend/src/TxRxConfig.tsx | TX/RX config editor UI. |
| sw/wulpus-frontend/src/SeriesPanel.tsx | Series mode UI (interval/number/progress). |
| sw/wulpus-frontend/src/AnalysisConfigPanel.tsx | Analysis/peak detection config UI. |
| sw/wulpus-frontend/src/ConfigFilesPanel.tsx | Save/load/delete config files via backend endpoints. |
| sw/wulpus-frontend/src/LogsPage.tsx | Log browser UI (list/download/replay). |
| sw/wulpus-frontend/src/Graph.tsx | Live plotting (Plotly) including B-mode and filters/fullscreen. |
| sw/wulpus-frontend/src/helper.ts | Frontend helper utilities (default config, DSP helpers, fullscreen). |
| sw/wulpus-frontend/src/Fields.tsx | Small form-field components. |
| sw/wulpus-frontend/src/MultiNumField.tsx | Multi-number input with channel toggles. |
| sw/wulpus-frontend/src/index.css | Tailwind + Material Symbols + slider styling. |
| sw/wulpus-frontend/src/vite-env.d.ts | Vite types reference. |
| sw/wulpus-frontend/src/assets/react.svg | Adds React logo asset. |
| sw/pyproject.toml | Defines Python project deps (FastAPI, pandas/pyarrow, bleak, etc.). |
| sw/README.md | Updates software run/build instructions for web UI + artifacts. |
| sw/CHANGELOG.md | Documents web UI refactor release notes. |
| sw/LICENSE | Updates license header content (Apache 2.0 text retained). |
| sw/MATLAB_load_wulpus_log.m | Adds MATLAB helper to load the new zip/parquet log format. |
| sw/convert_measurements.ipynb | Adds notebook to convert legacy measurements into new format. |
| sw/how_to_install_dependencies.md | Removes legacy conda-based dependency instructions. |
| sw/jupyter notebook (legacy)/.gitignore | Ignores user outputs/configs except examples. |
| sw/jupyter notebook (legacy)/LICENSE | Adds legacy notebook license file. |
| sw/jupyter notebook (legacy)/wulpus_gui.ipynb | Updates imports to new legacy package path. |
| sw/jupyter notebook (legacy)/wulpus_jptnbk/init.py | Adds package init for legacy notebook code. |
| sw/jupyter notebook (legacy)/wulpus_jptnbk/config_package.py | Moves legacy config package code under legacy namespace. |
| sw/jupyter notebook (legacy)/wulpus_jptnbk/dongle.py | Moves legacy dongle implementation under legacy namespace. |
| sw/jupyter notebook (legacy)/wulpus_jptnbk/rx_tx_conf.py | Adds helper to build TX/RX configs from WulpusConfig in legacy code. |
| sw/jupyter notebook (legacy)/wulpus_jptnbk/rx_tx_conf_gui.py | Updates legacy GUI code imports/formatting under legacy namespace. |
| sw/jupyter notebook (legacy)/wulpus_jptnbk/test_rx_tx_conf.py | Adds/updates tests for legacy TX/RX config generation. |
| sw/jupyter notebook (legacy)/wulpus_jptnbk/uss_conf.py | Updates legacy US config imports/formatting under legacy namespace. |
| sw/jupyter notebook (legacy)/wulpus_jptnbk/uss_conf_gui.py | Updates legacy US config GUI imports/formatting under legacy namespace. |
| sw/jupyter notebook (legacy)/examples/uss_config.json | Adds example legacy US config JSON. |
| sw/jupyter notebook (legacy)/examples/tx_rx_configs.json | Adds example legacy TX/RX configs JSON. |
| README.md | Updates top-level README to reference new software flow. |
| docs/CHANGELOG.md | Documents documentation-side release notes for new GUI/API. |
| AGENTS.md | Adds developer commands/architecture/style guidelines. |
| .gitignore | Expands ignore rules (uv/venv/build artifacts, etc.). |
| .github/workflows/release-gui.yml | Adds CI workflow to build frontend, package exe, and publish artifacts/releases. |
| fw/nrf52/README.md | Fixes paths/wording in firmware docs. |
| fw/nrf52/how_to_setup_nRF52_toolchain.md | Fixes wording in setup doc. |
| fw/nrf52/how_to_flash_nrf52840_dongle.md | Fixes title/paths/wording for dongle flashing doc. |
| fw/msp430/wulpus_msp430_firmware/wulpus/wulpus_sys.h | Adds “new config” condition helper prototype. |
| fw/msp430/wulpus_msp430_firmware/wulpus/wulpus_sys.c | Refactors restart/config checks; minor cleanup. |
| fw/msp430/wulpus_msp430_firmware/main.c | Refactors acquisition loop flow; adds preparation function. |
| fw/msp430/README.md | Fixes paths/wording in MSP430 firmware docs. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| def validate_channel_ids(cls, channels): | ||
| for ch in channels: | ||
| if not (0 <= ch <= MAX_CH_ID): | ||
| raise ValueError( | ||
| f"Channel ID {ch} must be between 0 and {MAX_CH_ID}") |
| if not self._bleak_client or not self._bleak_client.is_connected: | ||
| print("Error: BLE client is not connected.") | ||
| return None, None, None | ||
|
|
||
| if self._data_queue is None: | ||
| print("Error: Data queue not initialized.") | ||
| return None, None, None |
| f"Warning: Oversized frame discarded ({len(frame_buffer)} bytes)") | ||
| frame_buffer = bytearray() | ||
|
|
||
| return None, None, None |
| from wulpus.interface_mock import WulpusDongleMock | ||
| from wulpus.helper import zip_to_dataframe | ||
|
|
||
| from wulpus.wulpus import Status, Wulpus |
| zip_files = glob.glob(os.path.join(measurement_dir, '*.zip')) | ||
| if not zip_files: | ||
| raise FileNotFoundError(f"No zip files found in {measurement_dir}") | ||
| zip_files.sort() | ||
| return zip_files[:n] |
| def set_config(self, config: WulpusConfig) -> bytes: | ||
| self._config = config |
| while data_cnt < number_of_acq and self._acquisition_running: | ||
| # need to be a object so it gets passes by ref | ||
| data = await current_intf.receive_data(self, self._config.us_config.num_samples) | ||
| timestamp = int(time.time_ns()/1e3) |
| class AnalysisConfig(BaseModel): | ||
| spacers: List[Dict[str, Union[float, str]]] = [ | ||
| {"thickness": 0.6, "note": "PDMS", "speedOfSound": 1030}, | ||
| {"thickness": 8, "note": "PEEK spacer", "speedOfSound": 2500} | ||
| ] |
| export type DataFrame = { | ||
| measurement: { | ||
| data: number[] | ||
| time: number[] | ||
| tx: number[] | ||
| rx: number[] | ||
| } | ||
| peaks: number[] | ||
| wavelet: number[] | ||
| spacer_region: [number, number] | ||
| wulpus_id?: number; | ||
| } No newline at end of file |
update fork with upstream
To increase the usability of the UI and also make it easier to extend the code, I created a big refactor of the python-part of wulpus.

It has feature-parity with some quality-of-life improvements.
The code-structure is as following:
Ther's now a main
Wulpus-class, which only represents a wulpus-board and its states, without any user-interface.The user-facing API is served via HTTP using FastAPI in
main.py, which gets used by the react frontend.The frontend can therefore take advantage of all the benefits of a browsers rendering engine.
This also increased the overall reliability of the program (at least on my system) a bunch.
It uses the same config-structure, but allows quick editing of all parameters in parallel without any reload.
It also includes an integrated log-browser with replay-functionality.
I added support for integrated bluetooth-adapters and extended the log-format so it also stores e.g. the capture-time and the used config, which allows for easier reconstruction and synchronization between different measurements.
Ther's now also an automatic build of the application (into an .exe) of every commit, which allows users who don't need the dev-environment to just run it. (Can be found as an artifact on the summary-page of every workflow run)