Files
Nonemm/src/Nonemm.App/Windows/EntryWindow.Esm.cs
ericek111 8956c2fd22 Take the rest of what reading N1MM turned up
The call frame. Above the callsign box, N1MM writes which spotted station the
radio is sitting on, coloured like the bandmap colours it, or CQ-Frequency when
this is the frequency the last CQ went out on. Space with an empty callsign box
takes the call out of the frame and into the box, but only while searching:
TabPressed does that when the run box is unchecked, because a running station is
answering callers rather than chasing the one under its VFO. How close counts as
sitting on it is N1MM's tuning tolerance, 300 Hz, now a setting.

Sh/dx while unassisted. TelnetClient.Output refuses any command holding SH/DX
when the entry is not assisted, and the telnet window does the same now.
Whether an entry is assisted follows N1MM's own rule: assisted, multi-operator
or checklog may use spots, a single operator who did not say assisted may not.

The node's flavour. Click_LogonButton reads DXSpider, AR-Cluster, CC Cluster or
GoCluster out of the banner; ClusterClient reads the same words and the window
shows what it found. It changes nothing yet — the commands it would change are
ones this does not send — but the node says so and now we know.

CQ WW RTTY's state box. NextTab only stops on SectionText when the station is
US or Canadian, because nobody else sends a state, so a contest can now say a
box is not for this station and the walk steps over it. The box is still there
to type in.

The telnet button editor shows as many buttons as are defined, with an empty row
to write the next one in, rather than N1MM's twelve.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 10:23:30 +00:00

142 lines
4.2 KiB
C#

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<int> 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<int> 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<int> next = IsEsmOn ? NextEsmAction().Keys : [];
IReadOnlyList<FunctionKey> 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));
}
}
}