fix(winkeyer): send the probe the way a working client sends it
Logger32's WinKeyer debug against the K3NG that will not answer OpsLog is a
capture of the exchange working, on the same keyer and the same port:
Sent: 13 13 13 00 04 55
Rcvd: 55 (72 ms)
Sent: 00 02 Host open
Rcvd: 23 (WK2 v23)
That is the sequence I already send. The difference is the grouping: Logger32
puts the three nulls and the echo probe in ONE write, and we split them with a
50 ms pause and a buffer purge in between. On a keyer that reboots when the
port opens, that pause is a window for it to come up mid-sequence and swallow
half of it — and there was nothing to wait for, since a null produces no reply.
The handshake bytes are now logged unconditionally, not behind the diagnostic
option. "No WinKeyer answered" cannot be told apart from a wrong port, a wrong
baud rate, a keyer still booting, or another program holding the line. The
bytes can, and it is four lines per connect attempt.
This commit is contained in:
+6
-2
@@ -2,8 +2,12 @@
|
||||
{
|
||||
"version": "0.25.4",
|
||||
"date": "",
|
||||
"en": [],
|
||||
"fr": []
|
||||
"en": [
|
||||
"WinKeyer: the opening probe goes out as one write, matching a capture of a client that talks to the same K3NG keyer, and the handshake bytes are always logged so a keyer that stays silent can be diagnosed."
|
||||
],
|
||||
"fr": [
|
||||
"WinKeyer : la sonde d’ouverture part en un seul envoi, calquée sur la capture d’un client qui dialogue avec le même manipulateur K3NG, et les octets de la poignée de main sont toujours journalisés pour diagnostiquer un manipulateur muet."
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "0.25.3",
|
||||
|
||||
@@ -30,10 +30,10 @@ import (
|
||||
// the one to fail on: everything after it assumes a listener.
|
||||
// open 0x00 0x02, and the keyer returns its firmware version.
|
||||
const (
|
||||
cmdNull = 0x13
|
||||
cmdAdmin = 0x00
|
||||
adminOpen = 0x02
|
||||
adminEcho = 0x04
|
||||
cmdNull = 0x13
|
||||
cmdAdmin = 0x00
|
||||
adminOpen = 0x02
|
||||
adminEcho = 0x04
|
||||
echoProbe = 0x55 // K1EL's own choice; any byte works, this one is 0b01010101
|
||||
bootDelay = 400 * time.Millisecond
|
||||
echoTimeout = 2 * time.Second // K1EL: "if a WK doesn't respond within 2 seconds abort"
|
||||
@@ -95,18 +95,25 @@ func hostOpenOnce(p serial.Port, boot time.Duration) (int, error) {
|
||||
time.Sleep(boot)
|
||||
drain(p)
|
||||
|
||||
// Resync the command parser before asking it anything.
|
||||
if _, err := p.Write([]byte{cmdNull, cmdNull, cmdNull}); err != nil {
|
||||
return 0, fmt.Errorf("resync: %w", err)
|
||||
}
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
drain(p)
|
||||
|
||||
// Is anything actually there?
|
||||
if _, err := p.Write([]byte{cmdAdmin, adminEcho, echoProbe}); err != nil {
|
||||
// The resync nulls and the echo probe go out as ONE write.
|
||||
//
|
||||
// Copied byte for byte from a Logger32 capture against the K3NG keyer that
|
||||
// would not answer OpsLog: "Sent: 13 13 13 00 04 55 / Rcvd: 55". Same keyer,
|
||||
// same port, same six bytes — the only difference was that we sent them as
|
||||
// two writes with a pause and a buffer purge in between, and Logger32 sends
|
||||
// them as one. On a keyer that reboots when the port opens, that pause is a
|
||||
// window for it to come up mid-sequence and swallow half of it.
|
||||
//
|
||||
// There is nothing to wait for between the two halves anyway: a null produces
|
||||
// no reply, so the pause was only ever giving the keyer a chance to change
|
||||
// its mind.
|
||||
probe := []byte{cmdNull, cmdNull, cmdNull, cmdAdmin, adminEcho, echoProbe}
|
||||
traceHandshake("TX", probe, 0, false)
|
||||
if _, err := p.Write(probe); err != nil {
|
||||
return 0, fmt.Errorf("echo test: %w", err)
|
||||
}
|
||||
b, ok := readByte(p, echoTimeout)
|
||||
traceHandshake("RX", nil, b, ok)
|
||||
if !ok {
|
||||
return 0, errNoKeyer
|
||||
}
|
||||
@@ -116,16 +123,37 @@ func hostOpenOnce(p serial.Port, boot time.Duration) (int, error) {
|
||||
return 0, fmt.Errorf("echo test: expected 0x%02X, got 0x%02X — is this the keyer's port?", echoProbe, b)
|
||||
}
|
||||
|
||||
if _, err := p.Write([]byte{cmdAdmin, adminOpen}); err != nil {
|
||||
open := []byte{cmdAdmin, adminOpen}
|
||||
traceHandshake("TX", open, 0, false)
|
||||
if _, err := p.Write(open); err != nil {
|
||||
return 0, fmt.Errorf("host open: %w", err)
|
||||
}
|
||||
ver, ok := readByte(p, openTimeout)
|
||||
traceHandshake("RX", nil, ver, ok)
|
||||
if !ok {
|
||||
return 0, errors.New("host open: the keyer echoed but did not return its firmware version")
|
||||
}
|
||||
return int(ver), nil
|
||||
}
|
||||
|
||||
// traceHandshake puts the opening exchange in the log, ALWAYS — unlike the
|
||||
// running trace beside it, which is behind the diagnostic option.
|
||||
//
|
||||
// A failure that says only "no WinKeyer answered" cannot be told apart from a
|
||||
// wrong port, a wrong baud rate, a keyer still rebooting, or another program
|
||||
// holding the line. The bytes can. It is four lines per connect, and only when
|
||||
// the connect is attempted.
|
||||
func traceHandshake(dir string, b []byte, got byte, ok bool) {
|
||||
switch {
|
||||
case dir == "TX":
|
||||
applog.Printf("winkeyer: handshake TX % 02X", b)
|
||||
case ok:
|
||||
applog.Printf("winkeyer: handshake RX %02X", got)
|
||||
default:
|
||||
applog.Printf("winkeyer: handshake RX — nothing came back")
|
||||
}
|
||||
}
|
||||
|
||||
// readByte waits up to d for one byte. The serial read timeout is per-call and
|
||||
// can return 0 bytes without an error, so this loops until the deadline rather
|
||||
// than trusting a single Read.
|
||||
|
||||
@@ -102,6 +102,8 @@ func TestHostOpenFollowsK1ELSequence(t *testing.T) {
|
||||
if ver != 23 {
|
||||
t.Errorf("version = %d, want 23", ver)
|
||||
}
|
||||
// One write for the six probe bytes, then Host Open — the order and the
|
||||
// grouping of a Logger32 capture against a real K3NG.
|
||||
want := []byte{
|
||||
cmdNull, cmdNull, cmdNull,
|
||||
cmdAdmin, adminEcho, echoProbe,
|
||||
|
||||
Reference in New Issue
Block a user