[cwx] Make CWX availability follow the TX slice#4269
Conversation
Keep F1-F12 shortcut routing tied to the selected slice while deriving CWX availability from the radio TX slice. Refresh the availability gate when TX assignment or either relevant slice mode changes. Fixes aethersdr#4173
There was a problem hiding this comment.
Clean, correct, well-scoped fix. Splitting the two policies — shortcut routing keeps following the selected slice while CWX availability now derives from the TX slice — matches how FlexLib CWX::getTXFrequency() behaves, and the split DIGU-selected / CW-TX case is exactly the reported trap. The re-evaluation triggers line up with the three cases the summary claims: TX reassignment via txSliceChanged, and mode changes on either the active or the TX slice via the widened guard in the modeChanged lambda. Behavior in the common single-slice case is unchanged (selected == TX). Nothing blocking.
Non-blocking notes
updateKeyerAvailabilityopen-codes a "find the TX slice's mode" loop that already exists verbatim in thesetTxModeGetterlambda (MainWindow.cpp:1094) and asisTxSlice()scans in several other spots — a small shared helper would avoid the third copy. Purely a cleanup; fine to leave for a follow-up.
Thanks for the thorough write-up and the before/after proof.
🤖 aethersdr-agent · cost: $6.5320 · model: claude-opus-4-8
NF0T
left a comment
There was a problem hiding this comment.
Correct root-cause diagnosis — splitting "shortcut routing follows the selected slice" from "CWX availability follows the TX slice" is the right conceptual fix, and the split-DIGU-selected/CW-TX repro in the automation bridge proof is exactly the reported trap. CI is green and the commit is signed.
Two issues before this can merge, both surfacing in the exact split-slice scenario this PR targets:
1. The CWX panel can auto-close and never reopen during a TX-slice handoff. SliceModel::setTxSlice() only sends a wire command — each slice's TX flag updates independently via its own async status echo, so a TX handoff between two slices (e.g. the adjacent "#1351 Active follows TX slice" external-client case) fires two unsynchronized txSliceChanged signals. If the old slice's tx=false lands before the new slice's tx=true arrives, the new txMode scan momentarily finds no TX slice at all, txIsCw goes false, and updateKeyerAvailability calls m_cwxPanel->hide() — even though CW TX capability was never actually lost. When the corrective call fires moments later with txIsCw true again, the panel is already hidden and there's no re-show path anywhere in the function. The panel silently disappears mid-session until the operator manually reopens it.
2. CwxPanel's F1-F12 macro shortcuts still gate on the selected slice, contradicting the fix. CwxPanel's shortcut handler reads m_activeModeProvider() (still wired to activeSlice()->mode(), untouched by this PR) and silently no-ops if that's not CW — while the macro button click handler right next to it sends unconditionally. So after this fix, with selected=USB/TX=CW: the indicator now correctly shows CWX available and the panel opens, but pressing F1 does nothing, because the keyboard-shortcut gate is still selected-slice-based. That's the exact bug class this PR fixes, one hop away, in the same panel.
Non-blocking, worth doing in the same pass since you're already touching this code:
RadioModel::txSlice()(RadioModel.cpp:1355) already implements the "scan for the TX slice" loop this PR adds a copy of — it's currently unused anywhere in the codebase. Making it public and calling it here (and fromsetTxModeGetter/updateDaxTxMode, which duplicate the same scan) would collapse several hand-rolled copies into one.SliceModel* active = activeSlice(); updateKeyerAvailability(...)is pasted at both new call sites — a small no-arg helper would remove the duplication.- The new protocol-behavior comment ("CWX sends on the radio's TX slice... FlexLib
CWX::getTXFrequency()") doesn't cite a FlexLib file/line, pcap, or firmware version — worth pinning down given Principle I's evidence bar, since right now it's an asserted claim rather than a demonstrated one. - DVK has the identical structural gap (slice-agnostic playback, availability still on the selected slice) — same bug shape, worth a follow-up issue rather than blocking this one.
Happy to re-review quickly once the panel re-show and the shortcut gate are addressed — the core direction here is right.
… slice The initial fix made the CWX *indicator* follow the TX slice but left the F1-F12 shortcut set and the whole DVK path on the *selected* RX slice, which produced two defects: 1. Split VFO (selected=USB, TX=CW): the CWX indicator lit and the panel opened, but F1-F12 no-oped — the CwxPanel handler and setShortcutsEnabled still gated on the selected slice. And because CWX shortcuts derived from the TX slice's mode while DVK shortcuts derived from the selected slice's mode, both F1-F12 sets could be enabled at once (TX=CW + selected=USB), re-opening the activatedAmbiguously hole aethersdr#2464/aethersdr#2582 closed. 2. A momentary "no TX slice" — during a TX handoff between slices, or a band-recall that drops+recreates the TX slice — made txIsCw false and hid an open CWX panel with no re-show path, so the panel silently vanished until the operator reopened it. Fix: drive both keyers' availability *and* their F1-F12 shortcuts from the single TX slice. One source keeps the two shortcut sets mutually exclusive (a slice can't be CW and voice at once), and only auto-hide a panel when a TX slice actually exists and is in the wrong mode — a transient absence no longer yanks the panel. Also: - Reuse RadioModel::txSlice() (promoted from private) instead of the hand-rolled slice scan the fix added — dedupes a 3rd copy of the loop. - Point CwxPanel's F1-F12 mode guard at the TX slice (setTxModeProvider). - Cite the Principle I evidence for TX-slice targeting: reference/FlexLib_API_v4.1.5.39794/FlexLib/CWX.cs:186-199 getTXFrequency(). - Collapse the redundant activeMode/txMode split; updateKeyerAvailability() now takes no argument and reads the TX slice itself. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ten9876
left a comment
There was a problem hiding this comment.
Approving with a follow-up commit (66a72d07) that resolves both of @NF0T's blocking findings plus the dedup. Built clean (1476/1476) and full suite green (114/114, 1 quarantined skip).
What the follow-up changes
The initial commit moved the CWX indicator to the TX slice but left the F1-F12 shortcut set and the entire DVK path on the selected RX slice. That's not just incomplete — it reopens a closed bug:
- F1-F12 no-op in the split case (NF0T #2).
CwxPanel's handler andsetShortcutsEnabledboth gated on the selected slice, so with selected=USB / TX=CW the indicator lit and the panel opened but the macros did nothing. - Ambiguous-shortcut regression. Worse, once CWX shortcuts derive from the TX slice's mode while DVK shortcuts derive from the selected slice's mode, both F1-F12
ApplicationShortcutsets can be enabled simultaneously (TX=CW and selected=USB) — exactly theactivatedAmbiguouslyhole #2464/#2582 closed. Mutual exclusion only holds if both keyers derive from the same slice. - Panel vanish on transient (NF0T #1). A momentary "no TX slice" (TX handoff between slices, or a band-recall that drops+recreates the TX slice) made
txIsCwfalse and hid an open panel with no re-show path.
Fix: drive both keyers' availability and their F1-F12 shortcuts from the single TX slice, and only auto-hide a panel when a TX slice actually exists and is in the wrong mode (a transient absence no longer yanks the panel).
Also addressed
- Reuses
RadioModel::txSlice()(promoted fromprivate→public) instead of the hand-rolled scan — removes the 3rd copy of that loop. Note for the record:txSlice()was not unused; it's called ~8× insideRadioModel.cpp. It just wasn't reachable fromMainWindowbecause it was private. - Points
CwxPanel's F1-F12 mode guard at the TX slice (setActiveModeProvider→setTxModeProvider). - Principle I: cites
reference/FlexLib_API_v4.1.5.39794/FlexLib/CWX.cs:186-199getTXFrequency()— the FlexLib scan that returns theIsTransmitSlice's freq. - Collapses the
activeMode/txModesplit;updateKeyerAvailability()is now argument-free and reads the TX slice itself, simplifying all 6 call sites.
@NF0T — this expands your #2 into "shortcuts follow the TX slice" (not the selected slice) because keeping them split would re-introduce the ambiguous-key bug. If you'd rather keep DVK/selected-slice semantics, that's a design call worth a thread. Filed the DVK "same structural gap" note you raised as a follow-up.
Summary
Root cause
MainWindow::updateKeyerAvailability()used one selected-slice mode value for two different policies. Selected-slice mode is correct for shortcut routing, but CWX itself targets the radio's TX slice (matching FlexLibCWX::getTXFrequency()). In a split setup with a selected DIGU slice and a CW TX slice, that coupling incorrectly disabled CWX.Agent automation bridge proof
The freshly built app was driven into the reported two-slice state without keying:
active=true,mode=DIGU,txSlice=falseactive=false,mode=CW,txSlice=truedumpTree:enabled=true,visible=truetransmitting=false,mox=false,tuning=falseThe bridge sequence used
slice add,slice mode,slice tx,slice select,get slices,get transmit,dumpTree, andgrab MainWindow. After capture, slice A was restored to USB/TX, temporary slice B was removed, and the radio returned to its original one-slice state.Proof
Reporter confirmation before the fix: CWX is unavailable with the multi-slice setup.
Fixed build: selected slice A remains DIGU while slice B is the CW TX slice and CWX is available.
Testing
cmake --build build -j8— passed (765/765 build steps)ctest --test-dir build --output-on-failure -j8— 113/113 runnable tests passed; one quarantined test skipped as designedpython3 tools/check_engine_boundary.py --strict— zero blocking findingspython3 tools/check_a11y.py— no findings in changed linesBridge note
The bridge can inspect the CWX label structurally, but
invoke/targetedclickAtcannot resolve it because the interactiveQLabelhas neither an object name nor an accessibility identifier. This did not block the exact enabled-state proof, but assigning a stable target or converting the interactive label to a keyboard-accessible control would improve future activation testing.Fixes #4173
👨🏼💻 Generated with OpenAI Codex (GPT-5.6 Sol) and tested by @jensenpat