Give the network status window N1MM's tabs and columns
The window was one flat table of columns this program made up. N1MM's has tabs and a fixed set of columns, and an operator who knows one has to be able to read the other. **The window** now has Stations, Actions and Messages. Stations is a grid with N1MM's headers in N1MM's order — Computer, IP Address, Pass, Run, 10, 100, Freq, Op, Msg, Send, Receive, Band, R/M, Mode — and a row per station, this computer included. Right-click a row to pass the contact being worked to that station, to talk, to show the connection log, or to close and open its connection. Actions holds the talk line, the pass frequency, the echo and the networking switch; Messages holds the chat, the connection failures, the echo round trips and a trace of every message that arrives. **`STATUS`** is what fills those columns. N1MM sends it every few seconds whether anything has changed or not — the pass frequency, the frequency, run or search and pounce, the operator, the rates over the last ten and the last hundred contacts, the mode, the categories and the version — so a station sitting on one frequency for an hour still has a current row. `BANDMAP` only says a station moved. The five transmit-interlock flags are sent as false: this program does not block a transmitter. **`Rates.PerHour`** reads the two rate columns out of the log. Count back over the last N contacts, stop at a gap longer than the contest's minimum off time, and treat a span under a minute as a minute, so three contacts in five seconds read as 180 an hour rather than 2160. A connection closed by hand stays closed. Without that flag the next beacon opens it again within ten seconds, which makes the menu item look broken. Left out, because there is nothing behind them: N1MM's Resynchronize and Rescore tabs, the transmit interlocks of its Options tab, the Master column and the call-stacking target tab. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PdAYHcdRktqKry7nk414TU
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
using Avalonia.Media;
|
||||
using Nonemm.App.Theming;
|
||||
using Nonemm.Network;
|
||||
|
||||
namespace Nonemm.App.Windows;
|
||||
|
||||
/// One line of the network status window, in N1MM's columns. The station
|
||||
/// itself holds the raw values; this turns them into the text of a cell.
|
||||
public sealed class StationRow
|
||||
{
|
||||
private readonly NetworkedStation station;
|
||||
private readonly DateTime now;
|
||||
|
||||
public StationRow(NetworkedStation station, DateTime now)
|
||||
{
|
||||
this.station = station;
|
||||
this.now = now;
|
||||
}
|
||||
|
||||
public NetworkedStation Station => station;
|
||||
|
||||
public string Computer => station.StationNumber > 0
|
||||
? $"{station.ComputerName} ({station.StationNumber})"
|
||||
: station.ComputerName;
|
||||
|
||||
public string IpAddress => station.Port > 0 && station.Address.Length > 0
|
||||
? $"{station.Address}:{station.Port}"
|
||||
: station.Address;
|
||||
|
||||
/// The frequency this station is passing contacts on, and who is on it.
|
||||
public string Pass => station.PassFrequency.Hertz > 0
|
||||
? $"{station.PassFrequency.Kilohertz:0.0} {station.PassCall}".TrimEnd()
|
||||
: "";
|
||||
|
||||
/// Run or search and pounce, which is N1MM's Run column.
|
||||
public string Run => station.IsRunning ? "Run" : "S&P";
|
||||
|
||||
public string Last10 => station.Last10 > 0 ? station.Last10.ToString() : "";
|
||||
|
||||
public string Last100 => station.Last100 > 0 ? station.Last100.ToString() : "";
|
||||
|
||||
public string Freq => station.Frequency.Hertz > 0 ? $"{station.Frequency.Kilohertz:0.0}" : "";
|
||||
|
||||
public string Op => station.Operator;
|
||||
|
||||
/// The last message this station sent, and TX while it is transmitting.
|
||||
/// Two stations keying at once is what an operator watches this column for.
|
||||
public string Msg => station.IsTransmitting
|
||||
? $"TX {station.LastMessage}".TrimEnd()
|
||||
: station.LastMessage;
|
||||
|
||||
/// Whether this program can write to the station. N1MM prints OK and so
|
||||
/// does this.
|
||||
public string Send => station.IsMine ? "" : station.IsConnected ? "OK" : "no";
|
||||
|
||||
/// Whether anything is arriving. A station that has said nothing for a
|
||||
/// while reads as how long ago that was, because a connection that stands
|
||||
/// open is not the same as a station that is still there.
|
||||
public string Receive
|
||||
{
|
||||
get
|
||||
{
|
||||
if (station.IsMine)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
TimeSpan since = now - station.LastHeardUtc;
|
||||
return since < StationLink.Patience ? "OK" : Ago(since);
|
||||
}
|
||||
}
|
||||
|
||||
public string Band => station.Band?.Name ?? "";
|
||||
|
||||
/// Run1 or Run2, which N1MM heads `R/M`.
|
||||
public string RunMult => station.IsRun2 ? "Run2" : "Run1";
|
||||
|
||||
public string Mode => station.Mode?.Name ?? "";
|
||||
|
||||
/// Red for a station that is not talking, and for the version mismatch that
|
||||
/// leaves everything else looking right. Every other row takes the theme's
|
||||
/// own text colour: the grid reuses its rows, so a row that was red has to
|
||||
/// be painted back.
|
||||
public IBrush Colour => !station.IsMine && (station.Refused.Length > 0 || !station.IsConnected)
|
||||
? Themes.Brush(Themes.Current.BadBackground)
|
||||
: Themes.Brush(Themes.Current.FieldForeground);
|
||||
|
||||
/// How long ago, in the shortest form that says it.
|
||||
private static string Ago(TimeSpan since) => since switch
|
||||
{
|
||||
{ TotalMinutes: < 1 } => $"{since.Seconds} s",
|
||||
{ TotalHours: < 1 } => $"{since.Minutes} min",
|
||||
_ => "over an hour",
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user