using Avalonia.Controls; using Nonemm.Session; namespace Nonemm.App.Windows; /// Enter sends the message the contact has got to, which is how most operators /// run N1MM. What goes out is decided by `Esm`; this keeps the state that /// decision needs and sends what it asks for. public sealed partial class EntryWindow { private bool callSent; private bool exchangeSent; /// The call as it was when it last went out, for the corrected call. private string sentCall = ""; /// What the last Enter sent, so `=` can send it again. private IReadOnlyList lastKeys = []; private bool IsEsmOn => session.Settings.EsmEnabled; /// The keys the next Enter would send. The entry window highlights them, so /// the operator can see what is about to happen. private EsmAction NextEsmAction() { if (Logging is null) { return EsmAction.Nothing; } return Esm.Decide(new EsmSituation { IsRunning = Logging.IsRunning, HasCall = Logging.Entry.Call.Trim().Length > 0, IsDupe = Logging.Verdict()?.IsDupe == true, HasExchange = Logging.Entry.IsComplete, CallSent = callSent, ExchangeSent = exchangeSent, SendsCallOnce = session.Settings.EsmSendsCallOnce, WorksDupes = session.Settings.EsmWorksDupes, }); } private async Task RunEsmAsync() { EsmAction action = NextEsmAction(); if (action.IsNothing) { Status("nothing to send — the station is already in the log"); return; } await SendKeysAsync(action.Keys); if (action.Logs) { LogContact(); } Refresh(); } private async Task SendKeysAsync(IReadOnlyList keys) { if (Logging is null) { return; } lastKeys = keys; foreach (int key in keys) { await SendKeyAsync(key, CorrectedCall(key)); if (key == Esm.CallCq) { RememberCqFrequency(); } Remember(key); } } /// N1MM's "send corrected call": while running, a call that has changed /// since it went out is sent again in front of the message that ends the /// contact, so the station hears the call we are logging. private string CorrectedCall(int key) { if (Logging is null || key != Esm.EndQso || !Logging.IsRunning || !session.Settings.EsmSendsCorrectedCall) { return ""; } string call = Logging.Entry.Call.Trim(); return call.Length > 0 && sentCall.Length > 0 && call != sentCall ? call + " " : ""; } private void Remember(int key) { if (key is Esm.MyCall or Esm.HisCall) { callSent = true; sentCall = Logging is null ? "" : Logging.Entry.Call.Trim(); } if (key == Esm.Exchange) { exchangeSent = true; } } /// The contact is over, or the boxes have been cleared: the next station /// starts from nothing sent. private void ResetEsm() { callSent = false; exchangeSent = false; sentCall = ""; lastKeys = []; } /// `=` sends whatever Enter last sent, without deciding again. private void RepeatLastMessage() { if (lastKeys.Count == 0) { Status("nothing has been sent yet"); return; } _ = SendKeysAsync(lastKeys); } /// The buttons say what the file says, which is a different twelve labels /// while running and while searching, and the ones ESM would send next are /// highlighted. private void ShowFunctionKeys() { EsmItem.IsChecked = IsEsmOn; IReadOnlyList next = IsEsmOn ? NextEsmAction().Keys : []; IReadOnlyList keys = Keys(); for (int at = 0; at < functionButtons.Count; at++) { functionButtons[at].Content = keys[at].Label.Length > 0 ? keys[at].Label : $"F{at + 1}"; functionButtons[at].Classes.Set("esm", next.Contains(at)); } } }