refactor(winkeyer): learn the slow boot instead of naming the keyer

The K3NG entry added an hour ago is gone. It named one clone among many —
WKmini, home-built Arduinos, unbranded boxes — for hardware that speaks
exactly the same protocol, and it was the only line in the engine list that
picked a boot delay rather than a protocol. An operator with an unlabelled
clone would have had to guess.

The delay is now learnt per port. The first connect finds out by failing the
quick attempt and succeeding on the slow one; that fact is written to a
global setting keyed by the port, and every connect afterwards goes straight
to the slow attempt. Global rather than per profile on purpose: which keyer
is plugged into COM3 belongs to the computer, and switching profiles for a
different rig does not change the keyer on the desk.

Two tests hold the contract from both sides — a slow keyer must be reported
as slow, and a keyer that answers at once must not be, or every K1EL connect
would inherit seconds it never needed.
This commit is contained in:
2026-08-14 13:05:13 +02:00
parent aca4dc5678
commit 3ce930e9cc
7 changed files with 94 additions and 30 deletions
+23 -8
View File
@@ -95,7 +95,7 @@ func (f *fakeKeyer) SetMode(*serial.Mode) error { return nil }
// left mid-command simply absorbed.
func TestHostOpenFollowsK1ELSequence(t *testing.T) {
f := &fakeKeyer{version: 23}
ver, err := hostOpen(f, false)
ver, _, err := hostOpen(f, false)
if err != nil {
t.Fatalf("hostOpen: %v", err)
}
@@ -123,7 +123,7 @@ func TestHostOpenFollowsK1ELSequence(t *testing.T) {
// having to unplug anything.
func TestHostOpenRecoversAConfusedParser(t *testing.T) {
f := &fakeKeyer{version: 30, needsResync: true}
ver, err := hostOpen(f, false)
ver, _, err := hostOpen(f, false)
if err != nil {
t.Fatalf("hostOpen: %v", err)
}
@@ -137,7 +137,7 @@ func TestHostOpenRecoversAConfusedParser(t *testing.T) {
// commands and a keyer that never made a sound.
func TestHostOpenFailsWhenNothingAnswers(t *testing.T) {
f := &fakeKeyer{deaf: true}
if _, err := hostOpen(f, false); !errors.Is(err, errNoKeyer) {
if _, _, err := hostOpen(f, false); !errors.Is(err, errNoKeyer) {
t.Fatalf("want errNoKeyer, got %v", err)
}
}
@@ -146,7 +146,7 @@ func TestHostOpenFailsWhenNothingAnswers(t *testing.T) {
// reported as "no keyer".
func TestHostOpenReportsMissingVersion(t *testing.T) {
f := &fakeKeyer{mute: true}
_, err := hostOpen(f, false)
_, _, err := hostOpen(f, false)
if err == nil {
t.Fatal("want an error")
}
@@ -176,21 +176,36 @@ func (s *slowKeyer) Write(p []byte) (int, error) {
func TestHostOpenWaitsOutAnArduinoReboot(t *testing.T) {
f := &slowKeyer{ready: time.Now().Add(1500 * time.Millisecond)}
f.version = 23
ver, err := hostOpen(f, false)
ver, slow, err := hostOpen(f, false)
if err != nil {
t.Fatalf("hostOpen: %v", err)
}
if ver != 23 {
t.Errorf("version = %d, want 23", ver)
}
// This second value is what gets remembered for the port, and it is the
// whole reason the operator is never asked what kind of keyer they own.
// Lose it and every later connect pays the same doomed quick attempt.
if !slow {
t.Error("the long wait is what worked, but it was not reported as needed")
}
}
// Telling OpsLog the keyer is a K3NG must skip the doomed fast attempt, so the
// first try already allows for the reboot.
// A keyer that answers straight away must NOT be remembered as slow — that
// would add seconds to every connect for a K1EL that never needed them.
func TestHostOpenDoesNotMarkAFastKeyerSlow(t *testing.T) {
f := &fakeKeyer{version: 23}
if _, slow, err := hostOpen(f, false); err != nil || slow {
t.Fatalf("hostOpen = slow %v, err %v — want a fast keyer left alone", slow, err)
}
}
// A port already known to hold a slow keyer skips the doomed fast attempt, so
// the second connect is as quick as a K1EL's.
func TestHostOpenSlowBootSucceedsFirstTry(t *testing.T) {
f := &slowKeyer{ready: time.Now().Add(1500 * time.Millisecond)}
f.version = 23
if _, err := hostOpen(f, true); err != nil {
if _, _, err := hostOpen(f, true); err != nil {
t.Fatalf("hostOpen: %v", err)
}
// One attempt: exactly one handshake on the wire, not two.