Speak telnet properly to the cluster node

The old client read the socket with a StreamReader, so the option negotiation a
node sends on connect landed in the text as control bytes and made a mess of
the first lines. TelnetStream now answers it: it agrees to ECHO and
SUPPRESS-GO-AHEAD, refuses everything else, swallows subnegotiations, and
tracks what it already answered so the two ends do not reply to each other for
ever. A command split across two reads is still understood, because a socket
hands over whatever has turned up rather than whole messages.

LineAssembler keeps the tail that has no line ending yet. Nodes write their
login prompt as "login: " with nothing after it, so the old ReadLineAsync sat
waiting for a line that never came and only got through because the greeting
happened to mention a matching word.

Login now follows N1MM: it looks for LOGON, ENTER CALL, LOG IN and the rest,
and sends the callsign anyway after ten seconds if no prompt turns up. A
password is sent when the node asks for one and the operator configured one.
The commands go out after that, not straight after the call. A connection that
has heard nothing for four minutes gets a blank line so the node does not drop
it as idle.

The old prompt check matched "call" anywhere in a line, which any spot comment
could trigger.

SendSpotAsync sends the node's dx command. Alt+P, or Edit / Spot It, spots the
call being typed at the current frequency, or the last contact logged when
nothing is typed, and puts it on our own bandmap without waiting for it to come
back round from the node.

The spot parser now takes a line with no colon after the spotter, and finds the
time when DXSpider has put the spotter's grid after it.

The cluster tests run over a real socket against a node fake that sends the
negotiation, the prompt and spot lines.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-27 15:46:19 +00:00
parent b355b9b6bf
commit 2b8033ccdd
19 changed files with 918 additions and 66 deletions

View File

@@ -47,7 +47,7 @@ scorer: red for a dupe, green for a new multiplier, blue for points.
| Windows | entry, log, check, bandmap, score summary, packet |
| Editing | double-click a cell in the log, or open the whole contact with Ctrl+Y; Delete removes it. All of it goes out to the other stations |
| Radio | hamlib `rigctld`, reconnecting on its own |
| Cluster | DX cluster over telnet, spots feeding the bandmap |
| Cluster | DX cluster over telnet, spots feeding the bandmap, Alt+P to spot a station |
| Network | contacts shared with the other stations of a multi-operator entry, in N1MM's own contact message |
| Keying | CW through `cwdaemon` or a WinKeyer, with N1MM's message macros |
@@ -119,6 +119,25 @@ Correcting the country prefix by hand changes the score. The country file is a
best guess for calls it has no rule for, so what the contact says now wins over
what the file says.
### The DX cluster
**Config → Cluster** takes the node's address, a password for the few nodes that
ask for one, and the commands to send after login. Filters are the node's
business, so whatever goes in the command box is sent as typed and left alone.
The client speaks telnet properly: it answers the option negotiation instead of
letting the control bytes turn up in the first lines of text, and it reads the
login prompt out of a partial line, because nodes write `login: ` with no line
ending. If no prompt arrives within ten seconds the callsign goes out anyway,
which is what N1MM does. A connection that has heard nothing for four minutes
gets a blank line, so the node does not drop it as idle. A dropped connection is
retried.
Alt+P, or **Edit → Spot It**, puts the call being typed on the cluster at the
current frequency; with nothing typed it spots the last contact logged. The spot
also goes straight onto our own bandmap rather than waiting to come back round
from the node.
### Networked stations
**Config → Network** names this station and lists the others. Each contact is
@@ -167,6 +186,8 @@ Not yet: voice keying, QTC handling for WAE, call history files, digital modes
beyond logging them, and the check window's Call History and Exchange columns,
which are left out rather than shown empty.
The radio, cluster, network and keyer clients are tested against fakes that
speak the documented protocols. None has yet been run against a real radio, a
live cluster node or a keyer.
The radio, network and keyer clients are tested against fakes that speak the
documented protocols. None has been run against a real radio or keyer. The
cluster client is tested over a real socket against a node fake that sends the
telnet negotiation, the login prompt and spot lines, but not against a live
node.