fix: Save and close taking time when Flex or Icom is not connected

This commit is contained in:
2026-07-07 11:07:16 +02:00
parent 1184a675c2
commit 5b37397a64
3 changed files with 76 additions and 9 deletions
+35 -4
View File
@@ -4,6 +4,7 @@ package cat
import (
"bufio"
"context"
"encoding/binary"
"fmt"
"math"
@@ -25,9 +26,10 @@ type Flex struct {
host string
port int
mu sync.Mutex
conn net.Conn
wmu sync.Mutex // serialises writes to conn
mu sync.Mutex
conn net.Conn
dialCancel context.CancelFunc // cancels an in-flight Connect dial (Interrupt/Stop); nil when not dialing
wmu sync.Mutex // serialises writes to conn
seq int
handle string
model string
@@ -171,7 +173,19 @@ func (f *Flex) Connect() error {
if host == "" {
return fmt.Errorf("flex: no radio IP configured")
}
conn, err := net.DialTimeout("tcp", net.JoinHostPort(host, strconv.Itoa(port)), 5*time.Second)
// Cancellable dial: Interrupt() (called by Stop/Start) aborts it at once so a
// dead radio's 5 s dial timeout doesn't make Stop / Settings "Save & Close"
// wait several seconds for the poll goroutine to give up.
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
f.mu.Lock()
f.dialCancel = cancel
f.mu.Unlock()
var d net.Dialer
conn, err := d.DialContext(ctx, "tcp", net.JoinHostPort(host, strconv.Itoa(port)))
cancel()
f.mu.Lock()
f.dialCancel = nil
f.mu.Unlock()
if err != nil {
return fmt.Errorf("flex: connect %s:%d: %w", host, port, err)
}
@@ -224,6 +238,23 @@ func (f *Flex) Disconnect() {
}
}
// Interrupt aborts an in-flight Connect dial so Stop()/Start() (Settings
// "Save & Close", CAT backend switch) don't block on a dead radio's 5 s dial
// timeout. Satisfies the Manager's optional interruptible interface. Safe to call
// anytime and from another goroutine; a no-op when not dialing.
func (f *Flex) Interrupt() {
f.mu.Lock()
cancel := f.dialCancel
c := f.conn
f.mu.Unlock()
if cancel != nil {
cancel()
}
if c != nil {
_ = c.Close() // unblock the reader if we're already past the dial
}
}
// send writes a sequenced command (C<seq>|<cmd>) to the radio and returns the
// sequence number (so the caller can match the R<seq> response, e.g. to learn a
// new spot's index). Returns 0 when not connected. Best effort.