feat(protocol): add FlexWaveformModel for firmware v4.2.18 waveform status (#2136)#2759
Conversation
…tatus FlexRadio firmware v4.2.18 introduced three new waveform status sub-shapes that AetherSDR had no handler for — messages silently fell through. Add FlexWaveformModel to parse and store all three shapes: - "waveform" installed_list — legacy waveform list (comma + DEL-separated) - "waveform container" — Docker container add/remove (bare-word removed key) - "waveform wfp_status" — Waveform Processor power/ready/ipaddr Wire into RadioModel: sub waveform all subscription, commandReady forwarding, clear() on disconnect, and dispatch in onStatusReceived(). Named FlexWaveformModel to avoid confusion with the audio waveform visualization classes (WaveformWidget, StripWaveform) in src/gui/. Protocol verified against FlexLib v4.2.18.41174 Radio.cs lines 11162–11321. All 27 unit tests pass. Fixes aethersdr#2136 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Claude here — landed at 23:14 UTC. Fifth contribution today, Ryan! 🎉 This is the kind of PR that makes a maintainer's life easy. Every wire shape cited to a FlexLib v4.2.18.41174 line number, three sub-shapes disambiguated cleanly via the existing parser (no parser changes needed), the bare-word `removed` handling matches the existing `display pan removed` idiom at `RadioModel.cpp:3577` so future contributors can pattern-match, the `FlexWaveform*` naming explicitly avoids confusion with the audio-render `WaveformWidget`/`StripWaveform` classes, and defensive parsing logs and skips rather than crashing on malformed input. 27-assertion test suite covers all three shapes plus edge cases. The feasibility report and implementation plan are appreciated — they're consistent with the project's "consult FlexLib first" convention and they'll be valuable reference for the next firmware-version bump that adds new status object shapes. Five PRs today (#2745, #2747, #2752, #2754, #2759), five distinct code paths (RADE UI → RADE model → CWX UI → CWX core → SmartSDR protocol). All five clean diffs that landed without rework. Genuinely remarkable. 73, Jeremy KK7GWY & Claude (AI dev partner) |
…ment (#2779) ## Summary Surfaces the data already collected by `FlexWaveformModel` (merged in #2759) in a non-modal, geometry-persistent dialog at **File → Waveforms**, mirroring SmartSDR's layout and location. - **WFP status bar** at the top: power indicator (●), ready indicator (●), and IP address — all live-updating via `FlexWaveformModel::wfpStatusChanged` - **Waveform list**: one row per installed waveform or Docker container, with name, version, and type badge — live-updating via `FlexWaveformModel::waveformsChanged` - **Restart** button per row — fire-and-forget, no confirmation; sends `waveform restart <name>` - **Remove** (container) / **Uninstall** (legacy .wfp) button per row — `QMessageBox::question` confirmation before sending `waveform remove_container <name>` or `waveform uninstall <name>` - Install omitted intentionally — Docker containers are managed at the radio/firmware level, not from the client ## Implementation `WaveformsDialog` inherits `PersistentDialog` (geometry-persist + frameless-chrome handling) and takes a `FlexWaveformModel*` in its constructor. No refresh logic in `MainWindow` — the dialog wires `FlexWaveformModel` signals directly. `showOrRaisePersistent` handles lazy-construct, raise-if-open, and frameless toggle registration automatically. ## Test plan - [x] **File → Waveforms** opens dialog; re-triggering raises it rather than opening a second - [x] WFP status shows correct power/ready state and IP address - [x] `freedv-flex` (or other container) appears in the list with version and `[Container]` badge - [ ] **Restart** sends `waveform restart freedv-flex` — visible in radio log - [ ] **Remove** → cancel → no command sent; **Remove** → confirm → sends `waveform remove_container freedv-flex` - [ ] Disconnect from radio → list empties, status clears (driven by `FlexWaveformModel::clear()`) - [x] Frameless mode toggle applies correctly 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
… PR 8) (#2953) Eight docs leave the top-level docs/ tree, partitioned by purpose: docs/archive/ — historical planning/session notes, kept for grep context: - issue-2136-feasibility-report.md (PR #2759, shipped) - issue-2136-implementation-plan.md (PR #2759, shipped) - TX_SYNC_FIX_REPORT.md (PR #353, RadioModel.cpp still references the report by name) - aether-dsp-session-notes.md (#796 maintainer notebook) docs/data/ — reference fixtures (not bundled, not runtime): - SSDR.settings + SmartSDR.exe.config (SmartSDR parity reference) - rxapplet_mode_settings.csv (per-mode RX control research) - vfo_mode_filters.csv (VFO filter-preset research; VfoWidget.cpp comment updated to new path) Kept in docs/ (still active): - audio_test_plan.md (17 KB, edited 2026-04-29, ongoing test plan) - profile-import-export-manual-checklist.md (PR #2641 QA checklist) Both new dirs include a one-paragraph README explaining the convention so a future maintainer doesn't have to guess what belongs where. Cross-references audited: - AppSettings.h:9 mentions "SmartSDR's SSDR.settings" as a concept, not a path — no change. - RadioModel.cpp:3991 references TX_SYNC_FIX_REPORT by name — no change, file is findable via grep/find. - VfoWidget.cpp:3324 carried a literal "docs/vfo_mode_filters.csv" path — updated to docs/data/. Verified with a clean local Release build (cmake --build … --target AetherSDR exits 0). After this PR, docs/ contains 18 markdown files (architecture/user docs), the assets/ subdir from PR 5, and the new archive/ + data/ subdirs — matches the acceptance criteria in the cleanup plan. Principle I. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…sdr#2933, PR 8) (aethersdr#2953) Eight docs leave the top-level docs/ tree, partitioned by purpose: docs/archive/ — historical planning/session notes, kept for grep context: - issue-2136-feasibility-report.md (PR aethersdr#2759, shipped) - issue-2136-implementation-plan.md (PR aethersdr#2759, shipped) - TX_SYNC_FIX_REPORT.md (PR aethersdr#353, RadioModel.cpp still references the report by name) - aether-dsp-session-notes.md (aethersdr#796 maintainer notebook) docs/data/ — reference fixtures (not bundled, not runtime): - SSDR.settings + SmartSDR.exe.config (SmartSDR parity reference) - rxapplet_mode_settings.csv (per-mode RX control research) - vfo_mode_filters.csv (VFO filter-preset research; VfoWidget.cpp comment updated to new path) Kept in docs/ (still active): - audio_test_plan.md (17 KB, edited 2026-04-29, ongoing test plan) - profile-import-export-manual-checklist.md (PR aethersdr#2641 QA checklist) Both new dirs include a one-paragraph README explaining the convention so a future maintainer doesn't have to guess what belongs where. Cross-references audited: - AppSettings.h:9 mentions "SmartSDR's SSDR.settings" as a concept, not a path — no change. - RadioModel.cpp:3991 references TX_SYNC_FIX_REPORT by name — no change, file is findable via grep/find. - VfoWidget.cpp:3324 carried a literal "docs/vfo_mode_filters.csv" path — updated to docs/data/. Verified with a clean local Release build (cmake --build … --target AetherSDR exits 0). After this PR, docs/ contains 18 markdown files (architecture/user docs), the assets/ subdir from PR 5, and the new archive/ + data/ subdirs — matches the acceptance criteria in the cleanup plan. Principle I. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
waveformstatus sub-shapes that AetherSDR previously had no handler for — messages fell through silently inonStatusReceived().FlexWaveformModel(new files) to parse and store all three shapes, wired intoRadioModelwith subscription, command forwarding, and disconnect cleanup.FlexWaveform*to avoid collision with the existing audio waveform visualization classes (WaveformWidget,StripWaveform).Fixes #2136
Protocol Details
Wire shapes verified against FlexLib v4.2.18.41174
Radio.cslines 11162–11321:object"waveform",-delimited; name/version:\x7F-delimited"waveform container"removedis a bare-word key (no=value)"waveform wfp_status"Subscription:
sub waveform all(FlexLib Radio.cs:2301) added to connection sequence.Commands stubbed:
waveform uninstall,waveform remove_container,waveform restart(FlexLib Radio.cs:8484–8499).Files Changed
src/models/FlexWaveformModel.hsrc/models/FlexWaveformModel.cppsrc/models/RadioModel.hsrc/models/RadioModel.cppCMakeLists.txttests/flex_waveform_model_test.cppdocs/issue-2136-feasibility-report.mddocs/issue-2136-implementation-plan.mdTest Plan
./build/flex_waveform_model_test— 27/27 pass (verified locally)waveformstatus messagessub waveform all(radio ignores unknown sub topics)Notes for Reviewers
CMakeLists.txtis Tier 3 (maintainer-only:@ten9876)@ten9876or@jensenpat)aethersdr-agentattempt (see issue comments) failed its CI gate due to path restrictions — this is a human-initiated PR with no such constraint🤖 Generated with Claude Code