Wulpus web gui - #47
Merged
Merged
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
… only sending ws-message on change) + adding warning about offline-server to ui
(+ fix build by removing unnecessary import)
+ Add Matlab import script + make logfiles more tracable to wulpus by appending connection details
Add support for multiple WULPUS in parallel
Merge dev into main for v1.2.3 release
update fork with upstream
move from pip to uv and clean up READMEs
There was a problem hiding this comment.
Pull request overview
Adds a web-based WULPUS GUI (React/Vite) and a FastAPI backend with WebSocket streaming, configuration endpoints, logging/replay, plus build/release packaging updates and refreshed documentation.
Changes:
- Introduce a FastAPI backend (
sw/wulpus/) with typed config/models, dongle interfaces (USB/BLE/mock), data processing, and WebSocket broadcasting. - Add a React/Vite frontend (
sw/wulpus-frontend/) for connection management, live plotting (A-mode/B-mode), configuration editing, series execution, and log replay/download. - Update packaging and docs (PyInstaller spec, GitHub Actions release workflow, changelogs, READMEs, ignores).
Reviewed changes
Copilot reviewed 80 out of 97 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| sw/wulpus/wulpus.py | Core device controller: connect/start/measure/save log frames. |
| sw/wulpus/wulpus_model.py | Shared status + measurement response models. |
| sw/wulpus/wulpus_mock.py | Mock/replay-capable Wulpus implementation. |
| sw/wulpus/wulpus_config_models.py | Pydantic configuration models + validation. |
| sw/wulpus/wulpus_api.py | Byte-package generation for HW config/restart. |
| sw/wulpus/wulpus_api_helper.py | Helper constants + TX/RX bitmask builder. |
| sw/wulpus/websocket_manager.py | WebSocket connection management + broadcasts. |
| sw/wulpus/series.py | Series scheduling models + loop. |
| sw/wulpus/production-frontend/.gitignore | Keep production frontend dir but ignore built assets. |
| sw/wulpus/plot_helpers.py | Plotting helpers for loading/formatting measurement logs. |
| sw/wulpus/measurements/.gitignore | Ignore recorded measurement outputs. |
| sw/wulpus/main.spec | PyInstaller packaging (includes frontend assets). |
| sw/wulpus/interface.py | Async dongle interface contract + connection types. |
| sw/wulpus/interface_usb.py | Serial (USB dongle) implementation. |
| sw/wulpus/interface_mock.py | Mock dongle implementation for simulation. |
| sw/wulpus/interface_direct.py | Direct BLE implementation (Nordic UART Service). |
| sw/wulpus/helper.py | IO/helpers: zip log load, config load, misc utilities. |
| sw/wulpus/data_processing.py | Peak detection + wavelet/envelope processing pipeline. |
| sw/wulpus/configs/.gitignore | Ignore saved config files output. |
| sw/wulpus/config-analysis/.gitignore | Ignore analysis config output. |
| sw/wulpus/.gitignore | Ignore backend build/dist artifacts. |
| sw/wulpus-frontend/vite.config.ts | Dev proxy for API/logs/configs/ws. |
| sw/wulpus-frontend/tsconfig.node.json | TS config for Vite/node tooling. |
| sw/wulpus-frontend/tsconfig.json | TS project references. |
| sw/wulpus-frontend/tsconfig.app.json | TS config for the app bundle. |
| sw/wulpus-frontend/src/websocket-types.ts | Frontend types mirroring backend payloads. |
| sw/wulpus-frontend/src/vite-env.d.ts | Vite type declarations. |
| sw/wulpus-frontend/src/UsConfig.tsx | UI panel for ultrasound config (basic/advanced). |
| sw/wulpus-frontend/src/TxRxConfig.tsx | UI for editing TX/RX channel configs. |
| sw/wulpus-frontend/src/StatusView.tsx | Connection/status dropdown + connect/disconnect actions. |
| sw/wulpus-frontend/src/SeriesPanel.tsx | UI to start/stop repeated measurement series. |
| sw/wulpus-frontend/src/MultiNumField.tsx | Input widget for comma-separated channel lists + toggles. |
| sw/wulpus-frontend/src/main.tsx | App bootstrap + routing. |
| sw/wulpus-frontend/src/LogsPage.tsx | Recorded logs listing + replay/download UI. |
| sw/wulpus-frontend/src/index.css | Tailwind base + icon font + slider styling. |
| sw/wulpus-frontend/src/helper.ts | Config defaults + DSP helpers + fullscreen helper. |
| sw/wulpus-frontend/src/Graph.tsx | Plotly rendering for signal + B-mode heatmap + filter controls. |
| sw/wulpus-frontend/src/Fields.tsx | Small form field components. |
| sw/wulpus-frontend/src/ConnectionPanel.tsx | Connection orchestration (scan/mock/start/stop). |
| sw/wulpus-frontend/src/ConfigFilesPanel.tsx | Save/load/delete configs via API + local upload. |
| sw/wulpus-frontend/src/assets/react.svg | Frontend asset. |
| sw/wulpus-frontend/src/App.tsx | Main dashboard layout + WS integration + state handling. |
| sw/wulpus-frontend/src/api.ts | Frontend API client functions + shared types. |
| sw/wulpus-frontend/src/AnalysisConfigPanel.tsx | UI for peak/spacer analysis configuration. |
| sw/wulpus-frontend/README.md | Frontend dev/build instructions. |
| sw/wulpus-frontend/public/vite.svg | Frontend asset. |
| sw/wulpus-frontend/package.json | Frontend dependencies + scripts. |
| sw/wulpus-frontend/index.html | Frontend HTML entry. |
| sw/wulpus-frontend/eslint.config.js | ESLint config for TS/React setup. |
| sw/wulpus-frontend/build.py | Build+copy script into backend-served production directory. |
| sw/wulpus-frontend/.gitignore | Ignore node/build/editor artifacts. |
| sw/uss_config.json | Example ultrasound config JSON. |
| sw/uss_config_hand_tests.json | Example config for hand tests. |
| sw/tx_rx_configs.json | Example TX/RX config JSON. |
| sw/README.md | Updated software usage instructions (web UI + source build). |
| sw/pyproject.toml | Python project deps (FastAPI, BLE, parquet, etc.). |
| sw/MATLAB_load_wulpus_log.m | MATLAB loader updated for new zip/parquet log format. |
| sw/LICENSE | License header adjustment. |
| sw/jupyter notebook (legacy)/wulpus_jptnbk/uss_conf.py | Legacy notebook import path + formatting updates. |
| sw/jupyter notebook (legacy)/wulpus_jptnbk/uss_conf_gui.py | Legacy notebook import path + formatting updates. |
| sw/jupyter notebook (legacy)/wulpus_jptnbk/test_rx_tx_conf.py | Added tests for legacy rx/tx logic. |
| sw/jupyter notebook (legacy)/wulpus_jptnbk/rx_tx_conf.py | Added helper to build tx/rx configs from a config object. |
| sw/jupyter notebook (legacy)/wulpus_jptnbk/dongle.py | Legacy dongle module moved under legacy package. |
| sw/jupyter notebook (legacy)/wulpus_jptnbk/config_package.py | Legacy config package moved under legacy package. |
| sw/jupyter notebook (legacy)/wulpus_jptnbk/init.py | Legacy package marker. |
| sw/jupyter notebook (legacy)/wulpus_gui.ipynb | Updated imports to legacy package namespace. |
| sw/jupyter notebook (legacy)/LICENSE | Added legacy license file. |
| sw/jupyter notebook (legacy)/examples/uss_config.json | Legacy example config. |
| sw/jupyter notebook (legacy)/examples/tx_rx_configs.json | Legacy example TX/RX configs. |
| sw/jupyter notebook (legacy)/.gitignore | Ignore user outputs except examples. |
| sw/how_to_install_dependencies.md | Removed obsolete conda-based instructions. |
| sw/convert_measurements.ipynb | Notebook to convert old measurements into new log format. |
| sw/CHANGELOG.md | Changelog entries for web UI/backend release. |
| README.md | Root README updates (typos + software instructions + attribution). |
| fw/nrf52/README.md | Path fixes + wording for firmware docs. |
| fw/nrf52/how_to_setup_nRF52_toolchain.md | Minor wording fix. |
| fw/nrf52/how_to_flash_nrf52840_dongle.md | Corrected dongle flashing doc + paths. |
| fw/msp430/wulpus_msp430_firmware/wulpus/wulpus_sys.h | Add new-config condition helper + comments. |
| fw/msp430/wulpus_msp430_firmware/wulpus/wulpus_sys.c | Use new-config condition helper + cleanup. |
| fw/msp430/wulpus_msp430_firmware/main.c | Refactor acquisition prep/power sequencing for new config flow. |
| fw/msp430/README.md | Path fix in firmware README. |
| docs/CHANGELOG.md | Docs changelog updated for new GUI/API. |
| AGENTS.md | Repo agent guidance (commands/architecture/style). |
| .gitignore | Ignore uv/venv/build artifacts and local dev dirs. |
| .github/workflows/release-gui.yml | Windows build workflow: build UI, PyInstaller, artifact + release. |
Suppressed comments (3)
sw/wulpus/interface_direct.py:169
- Same issue as above: returning
(None, None, None)makes the caller think a frame was received.
sw/wulpus/interface_direct.py:224 - The function also returns
(None, None, None)when acquisition ends, which should beNoneso callers can reliably useif data is None:.
sw/wulpus-frontend/src/App.tsx:138 - React Query's
onErrorcallback signature is(error, variables, contextFromOnMutate); the current implementation uses a non-existentcontext.clientand won't restore the previous cached value.
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+95
to
+100
| 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}") | ||
| return channels |
Comment on lines
+98
to
+99
| def set_config(self, config: WulpusConfig) -> bytes: | ||
| self._config = config |
| from wulpus.interface_mock import WulpusDongleMock | ||
| from wulpus.helper import zip_to_dataframe | ||
|
|
||
| from wulpus.wulpus import Status, Wulpus |
Comment on lines
+90
to
+94
| 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] |
Comment on lines
+163
to
+165
| if not self._bleak_client or not self._bleak_client.is_connected: | ||
| print("Error: BLE client is not connected.") | ||
| return None, None, None |
| export type DataFrame = { | ||
| measurement: { | ||
| data: number[] | ||
| time: number[] |
Comment on lines
+129
to
+135
| onMutate: async (newData, context) => { | ||
| // Cancel any outgoing refetches (so they don't overwrite our optimistic update) | ||
| await context.client.cancelQueries({ queryKey: ['fetchAnalyzeConfig'] }) | ||
| const previousVal = context.client.getQueryData(['fetchAnalyzeConfig']) | ||
| context.client.setQueryData(['fetchAnalyzeConfig'], () => newData) | ||
| return { previousVal } | ||
| }, |
Comment on lines
+111
to
+125
| try { | ||
| const reader = new FileReader(); | ||
| reader.onload = async (e) => { | ||
| const content = e.target?.result; | ||
| if (typeof content === 'string') { | ||
| const config = JSON.parse(content); | ||
| handleApplyConfig(config); | ||
| } | ||
| }; | ||
| reader.readAsText(file); | ||
| } catch (e) { | ||
| setError(String(e)); | ||
| } finally { | ||
| setUploading(false); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.