Show the other computers, and apply the ten-minute rule

Three pieces on top of the link:

**The network status window**, on the entry window's Window menu. A row
per station, this computer included the way N1MM shows it: number,
address, operator, band, mode, run, transmit, pass frequency, the last
message type, how long ago it arrived, the counts each way and the echo
round trip. A station that broadcast the wrong version is named under the
table in red — that is the one fault where everything looks connected
and nothing arrives. The box at the bottom sends a line of chat, and
Echo asks every station whether it is there.

**Where this station is** goes out once a second when it has changed.
The frequency, the mode and run have a dozen places they can change from
— the radio moving, a band button, a QSY typed into the callsign box —
so it is read and compared rather than announced from each of them.

**The ten-minute rule.** `BandChangeRules` already counted changes and
the stay on a band, and the entry window already showed the countdown,
but only a user-defined contest carried a rule, so every built-in
contest allowed anything. `ForCategory` gives a multi-operator entry
with one or two transmitters a ten-minute stay, which is N1MM's fallback
in `ContestInstance.BandChangeTimerDuration` for every contest that does
not name its own. Its per-contest table is a few hundred cases in a
decompiled hash switch and is not repeated.

Config ▸ Edit Networked-Computer Names now covers both networks: the
12060 broadcast to other programs and the 12070 link, with the station
number, the port, the version to claim and stations named by address for
a network where a broadcast does not reach.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PdAYHcdRktqKry7nk414TU
This commit is contained in:
2026-09-03 15:26:42 +00:00
parent c0e1bc1e1b
commit 6f2f39768c
12 changed files with 588 additions and 3 deletions

View File

@@ -22,6 +22,48 @@ public sealed record BandChangeRules(
/// A contest that does not limit band changes at all.
public static readonly BandChangeRules None = new(0);
/// How long a multi-operator entry with one transmitter has to stay on a
/// band. It is the ten-minute rule, and it is N1MM's fallback for every
/// contest that does not name a number of its own.
public static readonly TimeSpan MultiOneStay = TimeSpan.FromMinutes(10);
/// What a contest allows an entry in this category, before the contest has
/// its say. These are N1MM's defaults, from
/// `ContestInstance.BandChangeTimerDuration` and `BandChangeCountMax`:
///
/// | Category | Changes counted | Stay |
/// |---|---|---|
/// | single operator | no | none |
/// | multi-operator, one transmitter | no | ten minutes |
/// | multi-operator, two transmitters | no | ten minutes |
/// | multi-operator, more | no | none |
///
/// The count is off by default because N1MM's own fallback for it is zero:
/// the contests that cap band changes per hour name the cap themselves, and
/// a contest class that does so overrides `Contest.BandChangesFor`. The
/// stay is the other way round — ten minutes is the fallback, and a contest
/// with no rule of its own gets it.
///
/// A station with a transmitter per band changes band by moving to another
/// radio, so nothing is counted for it.
public static BandChangeRules ForCategory(ContestEntry entry)
{
if (!entry.OperatorCategory.StartsWith("MULTI", StringComparison.OrdinalIgnoreCase))
{
return None;
}
// N1MM reads the number of transmitters off the operator category on
// Cabrillo 2.0 and off the transmitter category after it. Both are
// checked, so an entry written either way is read
bool one = Is(entry, "MULTI-ONE", "ONE");
bool two = Is(entry, "MULTI-TWO", "TWO");
return one || two ? None with { MinimumStay = MultiOneStay } : None;
}
private static bool Is(ContestEntry entry, string operatorCategory, string transmitterCategory) =>
entry.OperatorCategory.Equals(operatorCategory, StringComparison.OrdinalIgnoreCase)
|| entry.TransmitterCategory.Equals(transmitterCategory, StringComparison.OrdinalIgnoreCase);
public bool IsCounted => Max > 0;
/// True for a contest that says how long a station has to stay on a band.