Resynchronize a log with the other stations, and rescore it
A station that was away — a laptop that lost the network, one that was switched off — comes back missing whatever the others logged while it was gone. N1MM fills that in by walking the log an hour at a time, and this is that walk, on the same messages: | Message | What it says | |---|---| | `CHECKSUM` | here is what I hold for your contacts in this hour, and the contest and callsign I am logging | | `DELETEQS` | drop your copies of my contacts in that hour, I am sending them again | | `RESYNCQSO` | one of those contacts | | `CONFIRMED` | here is what the hour hashes to at my end — which also asks for the next hour | Each station is the authority on the contacts it made, so an hour that disagrees is replaced rather than merged. The answer to one block asks for the next, and the walk ends when the block starts after the end of the period. The Resynchronize tab counts the hours that agreed, the hours that were sent again and the hours that still disagreed, which are N1MM's columns. **`LogChecksum`** is N1MM's: a CRC32 over the `DXLOG` columns of the block, ordered by time and then callsign, leaving out what each station works out for itself — points, the three multiplier flags, the country prefix, the continent, the WPX prefix and `IsOriginal`. Two stations may disagree about those and still hold the same log. A checksum that disagrees for any other reason costs one block of traffic, which is what a resync is for. **The link still holds no log.** It asks one through `ContactLog` for the contacts of a computer in a block of time, and `SessionContactLog` answers on the window thread, because that is where the log is touched. **Every contact logged here now carries the computer that made it.** N1MM stamps `NetBiosName` and `NetworkedCompNr` on every row and this program did not, so its own contacts belonged to no station and a resync could not ask for them. **Rescore** writes the points and multiplier flags of a period back to the database. The log in memory scores itself whenever it changes; the stored rows do not, and N1MM reads them rather than working them out. A contact that arrives from another station after the one that took a multiplier leaves those columns behind. A station can also ask the others to rescore when its resync finishes, which is the flag on the end of `CHECKSUM`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PdAYHcdRktqKry7nk414TU
This commit is contained in:
@@ -56,6 +56,14 @@ public sealed class ContestSession
|
||||
|
||||
public string Operator { get; set; } = "";
|
||||
|
||||
/// The computer this station logs as, and which station of the entry it is.
|
||||
/// N1MM stamps every contact with both — `NetBiosName` and
|
||||
/// `NetworkedCompNr` — and a resync asks for the contacts of one computer
|
||||
/// by name, so a contact logged without it belongs to no station.
|
||||
public string StationName { get; set; } = "";
|
||||
|
||||
public int StationNumber { get; set; } = 1;
|
||||
|
||||
public event EventHandler? Changed;
|
||||
|
||||
public event EventHandler<Qso>? Logged;
|
||||
@@ -71,7 +79,7 @@ public sealed class ContestSession
|
||||
/// already holds it.
|
||||
public Qso Add(Qso qso)
|
||||
{
|
||||
Qso scored = Log.Add(qso);
|
||||
Qso scored = Log.Add(Stamped(qso));
|
||||
Qso stored = store.Add(scored);
|
||||
if (stored.TimestampUtc != scored.TimestampUtc)
|
||||
{
|
||||
@@ -83,6 +91,56 @@ public sealed class ContestSession
|
||||
return stored;
|
||||
}
|
||||
|
||||
/// Drops every contact one computer made inside a block of time, which is
|
||||
/// what a resync asks for before it sends the block again. Returns how many
|
||||
/// went.
|
||||
public int ClearFromNetwork(string computerName, DateTime fromUtc, TimeSpan length)
|
||||
{
|
||||
List<Qso> gone = Log.Qsos
|
||||
.Where(q =>
|
||||
string.Equals(q.StationName, computerName, StringComparison.OrdinalIgnoreCase)
|
||||
&& q.TimestampUtc >= fromUtc
|
||||
&& q.TimestampUtc < fromUtc + length)
|
||||
.ToList();
|
||||
foreach (Qso qso in gone)
|
||||
{
|
||||
store.Delete(qso.Id);
|
||||
Log.Remove(qso.Id);
|
||||
}
|
||||
if (gone.Count > 0)
|
||||
{
|
||||
Changed?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
return gone.Count;
|
||||
}
|
||||
|
||||
/// Scores the log again and writes the points and multiplier flags of the
|
||||
/// contacts from `fromUtc` on back to storage. Returns how many rows were
|
||||
/// written.
|
||||
///
|
||||
/// The log in memory scores itself whenever it changes, so this is for the
|
||||
/// database: N1MM reads the stored points and flags rather than working
|
||||
/// them out, and a contact that arrived from another station after the one
|
||||
/// that took a multiplier leaves those columns behind.
|
||||
public int Rescore(DateTime fromUtc)
|
||||
{
|
||||
Log.Rescore();
|
||||
int written = 0;
|
||||
foreach (Qso qso in Log.Qsos.Where(q => q.TimestampUtc >= fromUtc))
|
||||
{
|
||||
store.Update(qso);
|
||||
written++;
|
||||
}
|
||||
Changed?.Invoke(this, EventArgs.Empty);
|
||||
return written;
|
||||
}
|
||||
|
||||
/// Whose contact this is. A contact that already names a station keeps it.
|
||||
private Qso Stamped(Qso qso) =>
|
||||
StationName.Length == 0 || qso.StationName.Length > 0
|
||||
? qso
|
||||
: qso with { StationName = StationName, NetworkedComputerNumber = StationNumber };
|
||||
|
||||
public void Delete(string id)
|
||||
{
|
||||
Qso? gone = Log.Qsos.FirstOrDefault(q => q.Id == id);
|
||||
|
||||
Reference in New Issue
Block a user