diff --git a/README.md b/README.md index 7c3ce0c..d160480 100644 --- a/README.md +++ b/README.md @@ -933,6 +933,12 @@ Most contests run one sequence for the whole station. CQ WPX runs one per band for a multi-two and a multi-multi, which is the class the entry declares in contest setup, and N1MM decides it the same way. +**Reset Serial Numbers at all Stations** is for a pool that has gone wrong — a +station that was logging another contest, or numbers claimed and never used. +Every station counts from its own log again, so the next number is the highest +one logged plus one and no number that has gone out is sent twice. The contacts +already logged keep the numbers they were sent with. + A contact that arrives is put into the log where its timestamp says it belongs and the log is scored again, so of two stations that worked the same multiplier the one that worked it first keeps it, however late the message turns up. diff --git a/docs/unfinished.md b/docs/unfinished.md index a4ce8f8..6d42c3b 100644 --- a/docs/unfinished.md +++ b/docs/unfinished.md @@ -451,11 +451,14 @@ the station list, and what arrives goes on the call stack of the radio the operator is on, sorted by whether it is a multiplier here. **Serial numbers.** Reset Serial Numbers at all Stations sends `RESETQSONRS` -and every station starts again at one. The contacts already logged keep the -numbers they were sent with; the reset lasts as long as the program is open, -the way N1MM's does. The shared number pool — `RESERVENR`, `QSONRS`, `REJECTNR`, -where stations claim numbers in advance so two positions cannot send the same -one — is not written. +and every station counts from its own log again: the next number is the highest +one in it plus one, which is what the N1MM manual says the button does. The +contacts already logged keep the numbers they were sent with. + +The shared pool is written: `ReserveNr` claims a number at the first character +of the callsign and `QSONRS` carries the whole pool when a station has fallen +behind. `RejectNr` is not — N1MM sends it and reads nothing, so there is +nothing on the other end to agree with. **The ten-minute rule.** `BandChangeRules` counts band changes and the stay on a band, and the entry window shows the countdown. What was missing was the rule diff --git a/src/Nonemm.App/AppSession.cs b/src/Nonemm.App/AppSession.cs index 5696f73..cd42c93 100644 --- a/src/Nonemm.App/AppSession.cs +++ b/src/Nonemm.App/AppSession.cs @@ -411,14 +411,15 @@ public sealed class AppSession : IDisposable } } - /// Starts the serial numbers again at one. The contacts already logged keep - /// the numbers they were sent with; it is the next number that changes. + /// Starts the serial numbers again from the log: the next number becomes + /// the highest one in it plus one. The contacts already logged keep the + /// numbers they were sent with. N1MM sends no pool with this — every + /// station works the same number out of its own copy of the log. public void ResetSerialNumbers() { if (Logging is not null) { - Logging.SerialNumbersFrom = DateTime.UtcNow; - _ = link?.SendSerialNumbersAsync(Logging.Numbers.Snapshot()); + Logging.RestartNumbers(); Changed?.Invoke(this, EventArgs.Empty); } } diff --git a/src/Nonemm.App/Windows/NetworkStatusWindow.axaml.cs b/src/Nonemm.App/Windows/NetworkStatusWindow.axaml.cs index 785583a..ff2d591 100644 --- a/src/Nonemm.App/Windows/NetworkStatusWindow.axaml.cs +++ b/src/Nonemm.App/Windows/NetworkStatusWindow.axaml.cs @@ -378,7 +378,8 @@ public sealed partial class NetworkStatusWindow : RefreshableWindow } } - /// Starts the serial numbers again at one, here and at every station. + /// Counts the serial numbers from the log again, here and at every + /// station: the next number is the highest one logged plus one. private async void OnResetSerialNumbers(object? sender, RoutedEventArgs e) { if (link is null) @@ -387,7 +388,7 @@ public sealed partial class NetworkStatusWindow : RefreshableWindow } session.ResetSerialNumbers(); await link.SendResetSerialNumbersAsync(); - Say("serial numbers start again at one"); + Say($"serial numbers start again at {session.Logging?.NextNumberOn(null)}"); } private void OnLogEverywhereChanged(object? sender, RoutedEventArgs e) diff --git a/src/Nonemm.Network/StationLink.cs b/src/Nonemm.Network/StationLink.cs index bde8634..234a4ba 100644 --- a/src/Nonemm.Network/StationLink.cs +++ b/src/Nonemm.Network/StationLink.cs @@ -456,7 +456,7 @@ public sealed class StationLink : IDisposable computerName), cancellation); - /// Tells every station to start its serial numbers again at one. + /// Tells every station to count its serial numbers from the log again. public Task SendResetSerialNumbersAsync(CancellationToken cancellation = default) => SendAsync(StationMessages.ResetSerialNumbers(StationNumber, computerName), cancellation); diff --git a/src/Nonemm.Network/StationMessages.cs b/src/Nonemm.Network/StationMessages.cs index 2d29069..eb988f0 100644 --- a/src/Nonemm.Network/StationMessages.cs +++ b/src/Nonemm.Network/StationMessages.cs @@ -26,7 +26,7 @@ namespace Nonemm.Network; /// | `CONTESTNAME` | which contest the sender is logging | yes | yes | /// | `TIME` | the sender's clock | yes | yes | /// | `MASTER` | which station is the master for the cluster and the time | yes | yes | -/// | `RESETQSONRS` | start the serial numbers again at one | yes | yes | +/// | `RESETQSONRS` | count the serial numbers from the log again | yes | yes | /// | `ReserveNr` | a serial number the sender has taken for a contact | yes | yes | /// | `QSONRS` | the highest serial number taken on each band | yes | yes | /// | `STACKCALL` | a contact for another station's call stack | yes | yes | @@ -304,8 +304,10 @@ public static class StationMessages public static StationRecord Master(string masterName, int stationNumber, string computerName) => Record("MASTER", stationNumber, computerName, [masterName]); - /// Start the serial numbers again at one. It is sent when an operator finds - /// the entry has been logging with numbers from a contest before this one. + /// Count the serial numbers from the log again, so the next one is the + /// highest logged plus one. Each station works that out from its own copy + /// of the log, so the message carries nothing. N1MM's manual calls it "only + /// for use when you have screwed up". public static StationRecord ResetSerialNumbers(int stationNumber, string computerName) => Record("RESETQSONRS", stationNumber, computerName, []); diff --git a/src/Nonemm.Session/ContestSession.cs b/src/Nonemm.Session/ContestSession.cs index 9f531a0..f42d40f 100644 --- a/src/Nonemm.Session/ContestSession.cs +++ b/src/Nonemm.Session/ContestSession.cs @@ -68,19 +68,15 @@ public sealed class ContestSession public int StationNumber { get; set; } = 1; - /// Contacts logged before this are not counted when the next serial number - /// is worked out, so Reset Serial Numbers starts them again at one. The - /// contacts already logged keep the numbers they were sent with. It lasts - /// as long as the program is open, the way N1MM's does. - public DateTime SerialNumbersFrom + /// Starts the serial numbers again: the pool is filled from the log, so the + /// next number is the highest number in it plus one. That is what N1MM's + /// Reset Serial Numbers at all Stations does, at every station of the + /// entry, and it leaves the contacts already logged with the numbers they + /// were sent with. + public void RestartNumbers() { - get; - set - { - field = value; - SeedNumbers(); - NumbersRestarted?.Invoke(this, EventArgs.Empty); - } + SeedNumbers(); + NumbersRestarted?.Invoke(this, EventArgs.Empty); } /// The numbers have been started again, so a position holding one it took @@ -278,6 +274,6 @@ public sealed class ContestSession ? Log.Qsos.Where(q => Contest.SessionOf(q.TimestampUtc) == Contest.SessionOf(DateTime.UtcNow)) : Log.Qsos; - Numbers.SeedFrom(counted.Where(q => q.TimestampUtc >= SerialNumbersFrom)); + Numbers.SeedFrom(counted); } } diff --git a/tests/Nonemm.Session.Tests/NetworkedContactsTests.cs b/tests/Nonemm.Session.Tests/NetworkedContactsTests.cs index 9ceb0c8..dac479f 100644 --- a/tests/Nonemm.Session.Tests/NetworkedContactsTests.cs +++ b/tests/Nonemm.Session.Tests/NetworkedContactsTests.cs @@ -182,19 +182,25 @@ public class NetworkedContactsTests Assert.Equal(2, store.Qsos(session.Instance.ContestNumber).Count); } - /// Reset Serial Numbers at all Stations starts the numbers again at one. - /// The contacts already logged keep the numbers they were sent with. + /// Reset Serial Numbers at all Stations puts the next number back to the + /// highest in the log plus one, which is what the N1MM manual says it does: + /// every station lands on the same number without repeating one that has + /// gone out. The contacts already logged keep the numbers they were sent + /// with. [Fact] - public void SerialNumbersCanBeStartedAgainAtOne() + public void ResettingTheSerialNumbersCountsFromTheLogAgain() { (ContestSession session, _) = Station(); session.Add(Arriving("JA1XYZ", 1) with { Id = Qso.NewId(), SentNumber = 1 }); session.Add(Arriving("JA2XYZ", 2) with { Id = Qso.NewId(), SentNumber = 2 }); + // a number claimed by a station that was logging something else, which + // is the mess the reset is for + session.Numbers.Note(400, Bands.Band20M); + Assert.Equal(401, session.NextNumberOn(null)); + + session.RestartNumbers(); + Assert.Equal(3, session.NextNumberOn(null)); - - session.SerialNumbersFrom = DateTime.UtcNow; - - Assert.Equal(1, session.NextNumberOn(null)); Assert.Equal([1, 2], session.Log.Qsos.Select(q => q.SentNumber)); }