using Nonemm.Core; namespace Nonemm.Contests; /// One station of the entry with its key down, as the others see it. public sealed record Transmitter(string ComputerName, Band? Band, ModeCategory? Mode); /// How many signals a multi-operator entry may have on the air at once, and on /// which bands. /// /// N1MM blocks the transmitter when the entry is about to break these. This /// program does not touch the transmitter — a logger that decides for itself /// when the operator may not send is a logger that loses a contact when it is /// wrong — so it says what is happening and leaves it to the operator. /// /// | Entry | What is allowed | /// |---|---| /// | one transmitter | one signal on the air | /// | two transmitters | two signals, on different bands | /// | anything else | as many as the sponsor allows, one per band | /// /// Two signals on one band is wrong in every multi-operator class, which is why /// it is checked whatever the category says. An entry that is neither one nor /// two transmitters — multi-unlimited, and a single operator with two radios — /// is only held to that. public static class TransmitRules { /// What is wrong with this station transmitting now, or null when nothing /// is. `others` is every other station of the entry that has its key down. public static string? Violation( ContestEntry entry, Transmitter me, IReadOnlyList others) { if (others.Count == 0) { return null; } if (others.FirstOrDefault(o => SameBand(o, me) && o.Mode == me.Mode) is { } sameMode) { return $"{sameMode.ComputerName} is transmitting on {Where(me)} as well"; } if (others.FirstOrDefault(o => SameBand(o, me)) is { } sameBand) { return $"{sameBand.ComputerName} is transmitting on {me.Band?.Name} as well"; } int allowed = Transmitters(entry); if (allowed > 0 && others.Count + 1 > allowed) { string signals = $"{others.Count + 1} signals"; string on = string.Join(", ", others.Select(Where).Prepend(Where(me))); return allowed == 1 ? $"{signals} on the air — a one-transmitter entry may have one: {on}" : $"{signals} on the air — a two-transmitter entry may have two: {on}"; } return null; } /// How many signals the entry may have at once, or zero when the category /// does not say. N1MM reads the number off the operator category on /// Cabrillo 2.0 and off the transmitter category after it, and so does /// this. private static int Transmitters(ContestEntry entry) { if (!entry.OperatorCategory.StartsWith("MULTI", StringComparison.OrdinalIgnoreCase)) { return 0; } if (Is(entry, "MULTI-ONE", "ONE")) { return 1; } return Is(entry, "MULTI-TWO", "TWO") ? 2 : 0; } private static bool Is(ContestEntry entry, string operatorCategory, string transmitterCategory) => entry.OperatorCategory.Equals(operatorCategory, StringComparison.OrdinalIgnoreCase) || entry.TransmitterCategory.Equals(transmitterCategory, StringComparison.OrdinalIgnoreCase); /// Two stations are on the same band only when both say which band they are /// on. A station that has not said counts as somewhere else: a guess here /// puts a red box in front of an operator who is doing nothing wrong. private static bool SameBand(Transmitter one, Transmitter other) => one.Band is not null && other.Band is not null && one.Band == other.Band; private static string Where(Transmitter station) => $"{station.Band?.Name ?? "somewhere"} {Named(station.Mode)}".TrimEnd(); private static string Named(ModeCategory? mode) => mode switch { ModeCategory.Cw => "CW", ModeCategory.Phone => "phone", ModeCategory.Digital => "digital", _ => "", }; }