Three of the messages this program sends were its own invention or in the wrong units, so an N1MM beside it read them wrongly or not at all. | Message | N1MM | What was sent | |---|---|---| | `XMIT` | transmitting, the function key's caption, Run2, F1 pressed, stop sending | transmitting, the radio number | | `PASSFREQ` | the frequency in hundredths of a kilohertz, and nothing else | the frequency in kilohertz, and a callsign | | `FREQ` | the frequency in hundredths of a kilohertz | `BANDMAP`, which N1MM has no such message for | So N1MM showed a radio number where the other operators read what a station is sending, read a pass on 14205.5 as 140.25, and never saw this station move at all. `BANDMAP` is gone: the frequency goes out in `FREQ` when the radio moves and the mode, run and the rest ride in the `STATUS` that follows it, which is where N1MM carries them. The `Msg` column now shows what a station is sending, which is what the caption is for. `PassCall` and `RadioNumber` are gone from the station: neither is on the wire, so neither was ever anything but this program talking to itself. **A station that reconnects resynchronises by itself.** N1MM sends `LASTQAT` — when I last worked anybody — as soon as a connection opens, and a station holding nothing that late asks for the hours in between. An empty log asks for nothing: a program that has just been started has no business pulling the whole contest across, and N1MM suppresses it the same way. The answer goes over the connection this station opens, which is not always up when the message arrives, so a resync that has nothing to ask over waits for it rather than being lost — and one that still cannot send stops and says so instead of leaving the row resynchronising for the rest of the contest. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PdAYHcdRktqKry7nk414TU
137 lines
5.7 KiB
C#
137 lines
5.7 KiB
C#
using Nonemm.Core;
|
|
|
|
namespace Nonemm.Network;
|
|
|
|
/// One computer on the network, as far as this one can tell. The network status
|
|
/// window shows a row per station and these are its columns.
|
|
///
|
|
/// Everything here is what the station last said, with the time it said it.
|
|
/// Nothing is asked for on demand: the beacons and the messages carry it, and a
|
|
/// station that has gone quiet keeps the last thing it said with an older
|
|
/// `LastHeardUtc`, which is how the window shows it as missing rather than
|
|
/// blank.
|
|
public sealed class NetworkedStation
|
|
{
|
|
public NetworkedStation(string computerName, string address, int port)
|
|
{
|
|
ComputerName = computerName;
|
|
Address = address;
|
|
Port = port;
|
|
// now is when it was heard of, whether that was a beacon or the
|
|
// operator naming it. Left at nothing, `StationLink.Forget` reads a
|
|
// station as having been quiet since the beginning of time and drops
|
|
// the connection to it the moment it is opened
|
|
LastHeardUtc = DateTime.UtcNow;
|
|
}
|
|
|
|
/// The name in every message the station sends, and what a contact from it
|
|
/// is stamped with. N1MM uses the computer's own network name.
|
|
public string ComputerName { get; }
|
|
|
|
public string Address { get; internal set; }
|
|
|
|
public int Port { get; internal set; }
|
|
|
|
/// Which station of the entry this is. N1MM numbers them so a contact can
|
|
/// say which position made it; the number arrives in `IAM`.
|
|
public int StationNumber { get; internal set; }
|
|
|
|
/// The version the station broadcast. N1MM refuses to talk to a station
|
|
/// whose version is not its own, so a mismatch here is why nothing works.
|
|
public string Version { get; internal set; } = "";
|
|
|
|
public string Operator { get; internal set; } = "";
|
|
|
|
public string ContestName { get; internal set; } = "";
|
|
|
|
/// Where the station is, from the last `FREQ` or `STATUS` it sent.
|
|
public Frequency Frequency { get; internal set; }
|
|
|
|
public Mode? Mode { get; internal set; }
|
|
|
|
public Band? Band => Bands.ForFrequency(Frequency);
|
|
|
|
/// Contacts an hour over this station's last ten and last hundred
|
|
/// contacts, from its `STATUS` message. The two rate columns of the
|
|
/// network status window.
|
|
public int Last10 { get; internal set; }
|
|
|
|
public int Last100 { get; internal set; }
|
|
|
|
/// True while the station is running rather than searching. In a
|
|
/// multi-single entry this is the run station.
|
|
public bool IsRunning { get; internal set; }
|
|
|
|
public bool IsTransmitting { get; internal set; }
|
|
|
|
/// What the station is sending, which is the caption of the function key
|
|
/// it pressed. N1MM shows it in the `Msg` column while the station is
|
|
/// transmitting and clears it when the station stops.
|
|
public string Message { get; internal set; } = "";
|
|
|
|
/// True when the station is the second run radio of a two-radio position.
|
|
/// N1MM shows Run1 or Run2/Mult in its `R/M` column.
|
|
public bool IsRun2 { get; internal set; }
|
|
|
|
/// A frequency this station has passed, from `PASSFREQ`. N1MM sends the
|
|
/// frequency alone: which station is on it is what the operator is about to
|
|
/// say over the talk line.
|
|
public Frequency PassFrequency { get; internal set; }
|
|
|
|
/// When anything last arrived from the station, and what it was. The window
|
|
/// shows both: a station whose last message is minutes old is not there any
|
|
/// more, whatever its connection says.
|
|
public DateTime LastHeardUtc { get; internal set; }
|
|
|
|
public string LastMessage { get; internal set; } = "";
|
|
|
|
/// How many messages have gone each way since the program started. N1MM
|
|
/// shows the same two numbers, and a Send that climbs while Read stands
|
|
/// still is a station that is not listening.
|
|
public int Sent { get; internal set; }
|
|
|
|
public int Read { get; internal set; }
|
|
|
|
/// True while a connection to the station is open. It is not the same as
|
|
/// the station being there: the connection can stand for a while after the
|
|
/// other program has stopped.
|
|
public bool IsConnected { get; internal set; }
|
|
|
|
/// How long the last echo took to come back, or null when none has.
|
|
public TimeSpan? EchoTime { get; internal set; }
|
|
|
|
/// This computer, which is in the list as well. N1MM shows it too, so an
|
|
/// operator can read its own station number and version off the same
|
|
/// window.
|
|
public bool IsMine { get; internal init; }
|
|
|
|
/// What a resync with this station is doing, and how it went. N1MM counts
|
|
/// the blocks an hour at a time, which is why its columns say hours: `Ok`
|
|
/// is a block both ends already agreed on, `Fixed` is one this station was
|
|
/// told to drop and was sent again, `Failed` is one that still did not
|
|
/// agree after being sent.
|
|
public bool IsResyncing { get; internal set; }
|
|
|
|
public string ResyncStatus { get; internal set; } = "";
|
|
|
|
/// Set when this station said it is ahead of us before there was a
|
|
/// connection to answer over. The resync starts as soon as one opens.
|
|
internal DateTime? ResyncWanted { get; set; }
|
|
|
|
public int HoursOk { get; internal set; }
|
|
|
|
public int HoursFixed { get; internal set; }
|
|
|
|
public int HoursFailed { get; internal set; }
|
|
|
|
/// Set when the operator closed the connection by hand. The beacons keep
|
|
/// arriving and the station stays in the list, but nothing opens a
|
|
/// connection to it again until the operator asks for one.
|
|
public bool IsReconnectPrevented { get; internal set; }
|
|
|
|
/// Set when the station broadcast a version that is not ours. N1MM turns
|
|
/// such a station away, so this program says why rather than failing to
|
|
/// connect for no visible reason.
|
|
public string Refused { get; internal set; } = "";
|
|
}
|