fix(icom): re-open the CI-V flow when the rig goes quiet mid-session

The IC-7760 recurrently stops answering CI-V a few minutes into a network
session while the transport keeps chatting — pings answered, session alive,
commands unanswered — and the only recovery was the 30 s watchdog's full
teardown. At 10 s of CI-V silence the pump now says 'open' again on the
existing stream, which is all it should take if the rig quietly closed the
data flow; the watchdog remains as the backstop. The send counters gain a
mutex since the pump now transmits too.
This commit is contained in:
2026-08-29 16:56:06 +02:00
parent a2b019b280
commit 2c0158b75c
2 changed files with 25 additions and 4 deletions
+21 -2
View File
@@ -97,6 +97,7 @@ type icomNet struct {
// CAT goroutine) and during dial — never by the pump — so no lock is needed.
vTracked uint16
vCivSeq uint16
seqMu sync.Mutex // guards vTracked/vCivSeq: the command loop AND the pump's quiet-recovery both send
rx chan []byte // CI-V byte chunks from civPump → Read (control replies)
scopeRx chan []byte // scope (0x27) frames, kept off rx so the panadapter
@@ -227,10 +228,12 @@ func (n *icomNet) Write(p []byte) (int, error) {
if icnTrace {
debugLog.Printf("icom net TX: % X", p)
}
seq := n.vTracked
pkt := icnCivData(seq, n.vID, n.vRemote, n.vCivSeq, p)
n.seqMu.Lock()
seq, civSeq := n.vTracked, n.vCivSeq
n.vTracked++
n.vCivSeq++
n.seqMu.Unlock()
pkt := icnCivData(seq, n.vID, n.vRemote, civSeq, p)
n.sentMu.Lock()
n.sentBuf[seq] = pkt
delete(n.sentBuf, seq-1024) // keep the buffer bounded (~last 1024 packets) so
@@ -441,6 +444,22 @@ func (n *icomNet) civPump() {
}
debugLog.Printf("icom net: no CI-V DATA for 10 s (transport last heard %s ago; last scope frame %s ago; last socket error: %v; missing-seq backlog: %d)",
time.Since(lastPkt).Round(time.Second), scopeAge, lastErr, len(n.rxMissing))
// And try the gentle repair before the 30 s watchdog tears the whole
// session down: if the rig quietly closed the CI-V data flow (the
// transport is still chatting, so the session itself stands), saying
// "open" again on the same stream is all it should take. Harmless
// when the cause is elsewhere — the watchdog still fires at 30 s.
n.seqMu.Lock()
seq, civSeq := n.vTracked, n.vCivSeq
n.vTracked++
n.vCivSeq++
n.seqMu.Unlock()
ocPkt := icnOpenClose(seq, n.vID, n.vRemote, civSeq, 0x04)
n.sentMu.Lock()
n.sentBuf[seq] = ocPkt
n.sentMu.Unlock()
_, _ = n.civ.Write(ocPkt)
debugLog.Printf("icom net: re-sent the CI-V open on the existing stream")
}
if time.Since(lastIdle) > 150*time.Millisecond {
_, _ = n.civ.Write(icnCtrl(0x00, 0, n.vID, n.vRemote))