fix: parse hex meter num field and handle M-prefix radio messages#2771
Conversation
RadioModel::handleMeterStatus used toInt() without a base, returning 0 for hex values like 0x3B26ABBA from EXT_WVF meters (FreeDV_SNR). Fix uses toInt(nullptr, 0) for auto-base detection. M-prefix radio messages (waveform conflict warnings, etc.) were parsed by CommandParser but silently discarded in onMessageReceived. Now logged via qCWarning and surfaced to the user as a QMessageBox::warning dialog. Wire format confirmed from pcap: plain text body, no KV structure. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Claude here — landed at 00:25 UTC. Six clean PRs in a single day, Ryan! 🎉 Both fixes have that same "trace it to the actual root" quality your previous five had — the hex meter The modal-dialog choice is right for Phase 0 — M-messages are rare and the current FreeDV conflict warning is exactly the kind of thing operators want to see surface immediately. If we ever see spam patterns from new firmware versions we can add throttling or a status-bar surface as a follow-up. Not a problem to solve today. Today's tally for you: #2745, #2747, #2752, #2754, #2759, #2771. Six PRs, six distinct code paths (RADE UI → RADE model → CWX UI → CWX core → SmartSDR protocol v4.2.18 → meter parse + M-dispatch). All clean diffs, all landed without rework. Outstanding work — looking forward to more. 73, Jeremy KK7GWY & Claude (AI dev partner) |
…odals (#2785) ## Summary PR #2771 (merged today) added user-facing dialogs for M-prefix radio messages. In practice the radio sends an **Info-severity** M-message on every client connect/disconnect (\"Client connected from IP …\" etc.) — routine multi-client notices that SmartSDR silently logs. AetherSDR was popping a modal warning dialog on every connect as a result. The fix extracts the severity bits that FlexLib documents but AetherSDR's parser was discarding, then surfaces messages appropriately by severity. ## Root cause FlexLib's wire format ([Radio.cs:4498-4516](https://github.com/FlexRadioSystems/FlexLib/blob/main/FlexLib/Radio.cs#L4498)) encodes severity in bits 24-25 of the M-message number: \`\`\` M<8-hex-digits>|<text> └── (num >> 24) & 0x3 → Info=0, Warning=1, Error=2, Fatal=3 \`\`\` AetherSDR's \`CommandParser\` was extracting the handle as a single hex value but **discarding the severity bits**. PR #2771's MainWindow handler then treated all M-messages identically as \`QMessageBox::warning\`. ## What changes - \`CommandParser\`: new \`MessageSeverity\` enum + \`severity\` field on \`ParsedMessage\`; \`'M':\` case now extracts \`(handle >> 24) & 0x3\` into it - \`RadioModel::radioMessageReceived\` signal extended to \`(QString, MessageSeverity)\` - \`RadioModel::onMessageReceived\` logs at the matching protocol level (\`qCInfo\` / \`qCWarning\` / \`qCCritical\`) — log path and UI path now both switch on the same enum so they can't disagree - \`MainWindow::onRadioMessage\` switches on severity: | Severity | Behavior | |---|---| | **Info** | \`statusBar()->showMessage(text, 5000)\` — 5-second toast, no modal | | **Warning** | \`QMessageBox::warning\` (PR #2771's intent preserved) | | **Error** | \`QMessageBox::critical\` with \"Radio — Error\" title | | **Fatal** | \`QMessageBox::critical\` with \"Radio — Fatal\" title | ## Behavior changes - \"Client connected from IP …\" → status-bar toast (was modal popup) ✅ - \"Client disconnected from IP …\" → status-bar toast (was modal popup) ✅ - FreeDV mode-conflict warning from #2771 — unchanged, still modal ✅ - Future ATU / interlock / calibration errors — modal with \"Error\"/\"Fatal\" title prefix instead of \"Radio\" generic ## Test plan - [x] Local build clean (402/402 link, only pre-existing warnings) - [x] Connect to FLEX-8400 — IP notice appears in status bar for 5 seconds, no modal interrupts connection flow. **Confirmed by @ten9876.** - [ ] Trigger a Warning-severity M-message (e.g. set two slices to FDVU when FreeDV is running) — modal warning still appears - [ ] No regression in other M-message consumers (none exist in current codebase per grep) ## Files | File | Change | |---|---| | \`src/core/CommandParser.h\` | \`MessageSeverity\` enum + \`ParsedMessage::severity\` field + protocol-format comment refresh | | \`src/core/CommandParser.cpp\` | Extract severity bits in the \`'M':\` case | | \`src/models/RadioModel.h\` | Signal signature + \`CommandParser.h\` include | | \`src/models/RadioModel.cpp\` | Switch on severity for log level; emit with severity | | \`src/gui/MainWindow.h\` | Slot signature + \`CommandParser.h\` include | | \`src/gui/MainWindow.cpp\` | Switch on severity for log + dialog/toast | Net: **83 insertions / 16 deletions** across 6 files. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
Hex meter
numparse fix —RadioModel::handleMeterStatusused.toInt()without a base argument, silently returning 0 for hex values like0x3B26ABBAfromEXT_WVFmeters (e.g. FreeDV_SNR). Changed to.toInt(nullptr, 0)for auto-base detection.M-prefix message handler — M-prefix radio messages (e.g. FreeDV Docker waveform conflict warnings) were fully parsed by
CommandParserbut silently discarded inRadioModel::onMessageReceiveddue to theMessageType::Status-only guard. They are now logged viaqCWarning(lcProtocol)and surfaced to the user as aQMessageBox::warningdialog inMainWindow. Wire format confirmed from pcap capture: plain text body, no KV structure.Test plan
QMessageBox::warningappears with: "Only one FDVU or FDVL slice can be active at a time. Non-TX slices have been set to USB and/or LSB."qCWarning(lcProtocol)log entry for the M-messagenumfield parses non-zero (verify via debug log thatsourceIndexis non-zero forEXT_WVFmeters)🤖 Generated with Claude Code