Add the Avalonia application and the station network

The entry window types contacts and colours them as they are typed; the log,
check, bandmap, score and packet windows read the same session. Contacts are
shared with the other stations of a multi-operator entry in N1MM's contact
message, so an N1MM station on the same network sees them too.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Erik
2026-08-27 10:56:35 +00:00
parent ffeeb2cdc1
commit 2834f63c8e
45 changed files with 2667 additions and 0 deletions

View File

@@ -0,0 +1,117 @@
using System.Text.Json;
using System.Text.Json.Serialization;
using Nonemm.Core;
namespace Nonemm.App.Configuration;
/// What the program remembers between runs.
public sealed record Settings
{
public string DatabasePath { get; init; } = "";
public int ContestNumber { get; init; }
public StoredStation Station { get; init; } = new();
public string ClusterHost { get; init; } = "";
public int ClusterPort { get; init; } = 7373;
public IReadOnlyList<string> ClusterCommands { get; init; } = [];
public string RigctldHost { get; init; } = "127.0.0.1";
public int RigctldPort { get; init; } = 4532;
public bool RadioEnabled { get; init; }
public bool ClusterEnabled { get; init; }
public string NetworkStationName { get; init; } = "";
public int NetworkPort { get; init; } = 12060;
public bool NetworkEnabled { get; init; }
public IReadOnlyList<string> NetworkPeers { get; init; } = [];
public static Settings Load(string path)
{
if (!File.Exists(path))
{
return new Settings();
}
try
{
return JsonSerializer.Deserialize(File.ReadAllText(path), SettingsJson.Default.Settings)
?? new Settings();
}
catch (JsonException)
{
// a settings file that will not parse is replaced rather than
// stopping the program before a contest
return new Settings();
}
}
public void Save(string path) =>
File.WriteAllText(path, JsonSerializer.Serialize(this, SettingsJson.Default.Settings));
}
/// The operator's station as it is stored, kept separate from `StationInfo` so
/// the file format does not follow every change to the domain type.
public sealed record StoredStation
{
public string Callsign { get; init; } = "";
public int CqZone { get; init; }
public int ItuZone { get; init; }
public string Continent { get; init; } = "";
public string CountryPrefix { get; init; } = "";
public string State { get; init; } = "";
public string Province { get; init; } = "";
public string ArrlSection { get; init; } = "";
public string GridSquare { get; init; } = "";
public string County { get; init; } = "";
public string Name { get; init; } = "";
public string Power { get; init; } = "";
public string Club { get; init; } = "";
public int Check { get; init; }
public string Precedence { get; init; } = "A";
public StationInfo ToStationInfo() => new()
{
Callsign = Callsign,
CqZone = CqZone,
ItuZone = ItuZone,
Continent = Continent,
CountryPrefix = CountryPrefix,
State = State,
Province = Province,
ArrlSection = ArrlSection,
GridSquare = GridSquare,
County = County,
Name = Name,
Power = Power,
Club = Club,
Check = Check,
Precedence = Precedence,
};
}
[JsonSerializable(typeof(Settings))]
[JsonSourceGenerationOptions(WriteIndented = true)]
internal sealed partial class SettingsJson : JsonSerializerContext;