fix(tci): a refused un-key no longer leaves the rig keyed for good
The trx handler stamped its PTT cache BEFORE commanding the radio and left it in place when the command failed. An operator running JTDX over TCI with an Icom on CI-V lost an un-key to a lost acknowledgement: the cache recorded "off" regardless, and from then on every trx:0,false was dismissed as a repeat of a state the radio had never reached. The cache is per-server, not per-connection, so reconnecting JTDX changed nothing either -- the transmitter stayed keyed into the amplifier, with no drive, until the radio was switched off by hand. The cache is now written only on success, and a failure clears "known" outright so the next command reaches the radio whatever it is. Second guard: releasePTT drops a PTT this server asserted when the client disconnects, and when the server stops -- before the CAT backend goes down, while the rig is still reachable. rigctld has had that since a K3 sat in transmit for 29 s; the TCI server was written without it, so an operator moving from Hamlib to TCI silently lost the protection. A later log shows the rig keyed for 40 s across a JTDX reconnect for exactly that reason.
This commit is contained in:
@@ -16,6 +16,7 @@ type fakeRig struct {
|
||||
txHz int64
|
||||
ptt bool
|
||||
splitErr error
|
||||
pttErr error
|
||||
calls []string
|
||||
}
|
||||
|
||||
@@ -34,6 +35,10 @@ func (r *fakeRig) SetMode(m string) error {
|
||||
return nil
|
||||
}
|
||||
func (r *fakeRig) SetPTT(on bool) error {
|
||||
// Refused BEFORE the state moves, like a radio that never got the frame.
|
||||
if r.pttErr != nil {
|
||||
return r.pttErr
|
||||
}
|
||||
r.calls = append(r.calls, fmt.Sprintf("ptt=%v", on))
|
||||
r.ptt = on
|
||||
return nil
|
||||
@@ -328,3 +333,54 @@ func TestRepeatedPTTIsNotResentToTheRadio(t *testing.T) {
|
||||
t.Errorf("the radio was told %v, want the change through and the repeat dropped", r.calls)
|
||||
}
|
||||
}
|
||||
|
||||
// A refused un-key must never be remembered as done.
|
||||
//
|
||||
// The failure an operator hit running JTDX over TCI with an Icom on CI-V: the
|
||||
// rig went to transmit, the un-key was refused on a lost acknowledgement, and
|
||||
// from then on NOTHING could take it out of transmit. The cache had stamped
|
||||
// "off" before the radio was even commanded and kept it after the refusal, so
|
||||
// every later trx:0,false was dismissed as a repeat of a state the radio had
|
||||
// never reached. It is per-server, not per-connection, so reconnecting the
|
||||
// client changed nothing either — the transmitter stayed keyed into the
|
||||
// amplifier, with no drive, until the radio was switched off by hand.
|
||||
func TestARefusedUnkeyIsNotRememberedAsDone(t *testing.T) {
|
||||
r := &fakeRig{freq: 14074000, rxFreq: 14074000, mode: "USB"}
|
||||
s := srv(r)
|
||||
ask(t, s, "trx:0,true")
|
||||
if !r.ptt {
|
||||
t.Fatal("the rig was never keyed — the test would prove nothing")
|
||||
}
|
||||
|
||||
r.pttErr = fmt.Errorf("icom: timeout waiting for response")
|
||||
ask(t, s, "trx:0,false")
|
||||
if !r.ptt {
|
||||
t.Fatal("the fake rig un-keyed on a refusal — the test would prove nothing")
|
||||
}
|
||||
|
||||
// The client asks again, and this time the radio answers. It MUST be told.
|
||||
r.pttErr = nil
|
||||
ask(t, s, "trx:0,false")
|
||||
if r.ptt {
|
||||
t.Error("still keyed: the refused un-key was cached as done and the retry was dropped as a repeat")
|
||||
}
|
||||
}
|
||||
|
||||
// A client that walks away mid-over must not leave the rig transmitting, and
|
||||
// the release must be once-only — a second call has nothing to un-key and must
|
||||
// not re-command a radio that is already receiving.
|
||||
func TestReleasePTTUnkeysOnceWhenTheClientLeaves(t *testing.T) {
|
||||
r := &fakeRig{freq: 14074000, rxFreq: 14074000, mode: "USB"}
|
||||
s := srv(r)
|
||||
ask(t, s, "trx:0,true")
|
||||
|
||||
s.releasePTT("client left")
|
||||
if r.ptt {
|
||||
t.Error("the rig is still keyed after the client left")
|
||||
}
|
||||
n := len(r.calls)
|
||||
s.releasePTT("client left")
|
||||
if len(r.calls) != n {
|
||||
t.Errorf("released twice — the radio was told %v", r.calls)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user