[codex] Defer ping watchdog during Multi-Flex joins#3570
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adjusts RadioModel’s TCP ping watchdog to avoid false disconnects during Multi-Flex join bursts, where the radio may temporarily delay ping replies while replaying status/stream ownership to another newly connected client.
Changes:
- Track the timestamp of recent foreign
client ... connectedstatus messages. - Add a single 5-second grace window that defers the forced-disconnect decision if the missed-ping threshold coincides with a recent Multi-Flex client-connect burst.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| src/models/RadioModel.h | Adds state and constants to support a Multi-Flex-specific ping grace period. |
| src/models/RadioModel.cpp | Implements the grace-window logic in the ping watchdog and records foreign client-connect timestamps from status messages. |
There was a problem hiding this comment.
Thanks for this, @rfoust — it's a tidy, well-scoped fix backed by a real reproduction log. I traced the logic against RadioModel.cpp and it holds up. All 6 CI checks are green.
What I verified
- Grace state is reset on (re)connect — both
m_lastMultiFlexClientConnectMsandm_multiFlexPingGraceUntilMsare cleared instartNetworkMonitor(), so a stale timestamp can't carry across sessions. - The arming site is correctly gated — the
m_lastMultiFlexClientConnectMswrite sits inside theconnectedbranch ofonStatusReceived()and is guarded byhandle != clientHandle(), so our own connect echo doesn't trigger it. - The grace is bounded for a single connect burst — detection window is
(missThreshold + 1) * interval(~6 s at the 1 s ping cadence) measured from the connect, and grace itself is 5 s; once the connect is older than the window, the normalforceDisconnect()path runs. So a genuinely-dead link after a join still disconnects, just ~5 s later. Good. sendCmd("ping")is kept alive during the deferral — so a recovered link resetsm_pingMissCountviapingRttMeasuredand the watchdog returns to normal. Thenow >= m_lastMultiFlexClientConnectMsclock-rewind guard is a nice defensive touch.
One minor observation (non-blocking)
The PR describes "one 5-second grace period," but the mechanism actually grants grace per foreign client-connect event, not once globally. If the radio replays multiple client … connected status messages (e.g. several SmartSDR clients joining, or repeated status replays), each refreshes m_lastMultiFlexClientConnectMs and can re-arm the 5 s window. In practice this is fine — and arguably what you'd want, since each real join deserves grace — but it does mean deferral can extend past a single 5 s window during a sustained connect storm. Worth a one-line comment noting the per-event semantics so a future reader doesn't assume it's strictly one-shot. No change required.
Nice work — the defensive resets and the bounded-window design are exactly right for a watchdog override.
🤖 aethersdr-agent · cost: $2.9577 · model: claude-opus-4-8
Fixes a Multi-Flex reconnect false-positive: the 5-miss TCP ping watchdog was force-disconnecting while another SmartSDR client joined and the radio replayed status/stream ownership. Records foreign 'client ... connected' status timestamps and grants a bounded 5s grace when the missed-ping window overlaps the connect burst. Watchdog stays safe-by-bound (grace expires; a genuinely dead radio still disconnects ~5s later), state reset on reconnect, own-echo excluded. Verified on a real radio via reproduction log (Principle VIII). Co-authored-by: rfoust <rfoust@users.noreply.github.com>
Summary
Fixes a Multi-Flex reconnect false-positive where AetherSDR could force-disconnect after 5 unanswered pings while another SmartSDR client was joining and the radio was replaying status/stream ownership. The ping watchdog now records recent foreign
client ... connectedstatus messages and grants one 5-second grace period if the missed-ping threshold overlaps that client-connect burst. This keeps the normal watchdog behavior unchanged outside the Multi-Flex connect window.Constitution principle honored
Principle VIII — Evidence Over Assertion. The fix is based on logs showing the prior failure path (
5 consecutive pings unanswered — forcing disconnect) immediately after SmartSDR connected, and was verified with a reproduction log where the watchdog reached 5 misses but deferred instead of disconnecting.Test plan
cmake --build build --parallel)Reproduction verification:
Observed in
aethersdr-20260614-001547.log:0x106E3651.noted Multi-Flex client connect for ping grace.misses: 5.deferring ping disconnect after recent Multi-Flex client connect ... grace_ms: 5000.remote_audio_rx 0x04000008and ignored SmartSDR'sremote_audio_rx 0x04000009.Checklist
docs/COMMIT-SIGNING.md)AppSettingscalls — use nested-JSON-under-one-key (Principle V)MeterSmoother(Principle II)