App: Add SerialPortWatcher
Some checks failed
Test / Run tests (push) Has been cancelled

This commit is contained in:
2025-11-09 23:46:19 +01:00
parent 01f4480a08
commit e6367da286
4 changed files with 121 additions and 6 deletions

View File

@@ -26,6 +26,7 @@ namespace Elwig {
public static bool ForceShutdown { get; private set; } = false; public static bool ForceShutdown { get; private set; } = false;
private readonly DispatcherTimer _autoUpdateTimer = new() { Interval = TimeSpan.FromHours(1) }; private readonly DispatcherTimer _autoUpdateTimer = new() { Interval = TimeSpan.FromHours(1) };
public readonly SerialPortWatcher SerialPortWatcher = new();
public static readonly string DataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "Elwig"); public static readonly string DataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "Elwig");
public static readonly string MailsPath = Path.Combine(DataPath, "mails"); public static readonly string MailsPath = Path.Combine(DataPath, "mails");
@@ -48,9 +49,10 @@ namespace Elwig {
public static string? BranchPhoneNr { get; private set; } public static string? BranchPhoneNr { get; private set; }
public static string? BranchFaxNr { get; private set; } public static string? BranchFaxNr { get; private set; }
public static string? BranchMobileNr { get; private set; } public static string? BranchMobileNr { get; private set; }
public static IList<IScale> Scales { get; private set; }
public static IList<ICommandScale> CommandScales => Scales.Where(s => s is ICommandScale).Cast<ICommandScale>().ToList(); public static IList<IScale> Scales { get; private set; } = [];
public static IList<IEventScale> EventScales => Scales.Where(s => s is IEventScale).Cast<IEventScale>().ToList(); public static IList<ICommandScale> CommandScales => [.. Scales.Where(s => s is ICommandScale).Cast<ICommandScale>()];
public static IList<IEventScale> EventScales => [.. Scales.Where(s => s is IEventScale).Cast<IEventScale>()];
public static ClientParameters Client { get; set; } public static ClientParameters Client { get; set; }
public static Dispatcher MainDispatcher { get; private set; } public static Dispatcher MainDispatcher { get; private set; }
@@ -137,6 +139,9 @@ namespace Elwig {
_autoUpdateTimer.Start(); _autoUpdateTimer.Start();
} }
SerialPortWatcher.SerialPortConnected += OnSerialPortConnected;
SerialPortWatcher.SerialPortDisconnected += OnSerialPortDisconnected;
var list = new List<IScale>(); var list = new List<IScale>();
foreach (var s in Config.Scales) { foreach (var s in Config.Scales) {
try { try {
@@ -144,7 +149,7 @@ namespace Elwig {
} catch (Exception e) { } catch (Exception e) {
list.Add(new InvalidScale(s.Id)); list.Add(new InvalidScale(s.Id));
if (s.Required) if (s.Required)
MessageBox.Show($"Unable to create scale {s.Id}:\n\n{e.Message}", "Scale Error", MessageBox.Show($"Verbindung zu Waage {s.Id} konnte nicht hergestellt werden:\n\n{e.Message}", "Waagen-Fehler",
MessageBoxButton.OK, MessageBoxImage.Error); MessageBoxButton.OK, MessageBoxImage.Error);
} }
} }
@@ -152,7 +157,7 @@ namespace Elwig {
if (Config.Branch != null) { if (Config.Branch != null) {
if (!branches.ContainsKey(Config.Branch.ToLower())) { if (!branches.ContainsKey(Config.Branch.ToLower())) {
MessageBox.Show("Invalid branch name in config!", "Invalid Branch Config", MessageBoxButton.OK, MessageBoxImage.Error); MessageBox.Show("Ungültige Zweigstelle in Konfigurationsdatei!", "Ungültige Zweigstelle", MessageBoxButton.OK, MessageBoxImage.Error);
Shutdown(); Shutdown();
} else { } else {
SetBranch(branches[Config.Branch.ToLower()]); SetBranch(branches[Config.Branch.ToLower()]);
@@ -160,7 +165,7 @@ namespace Elwig {
} else if (branches.Count == 1) { } else if (branches.Count == 1) {
SetBranch(branches.First().Value); SetBranch(branches.First().Value);
} else { } else {
MessageBox.Show("Unable to determine local branch!", "Invalid Branch Config", MessageBoxButton.OK, MessageBoxImage.Error); MessageBox.Show("Erkennen der lokalen Zweigstelle nicht möglich!", "Ungültige Zweigstelle", MessageBoxButton.OK, MessageBoxImage.Error);
Shutdown(); Shutdown();
} }
@@ -181,6 +186,7 @@ namespace Elwig {
} }
private async void Application_Exit(object sender, ExitEventArgs evt) { private async void Application_Exit(object sender, ExitEventArgs evt) {
SerialPortWatcher.Dispose();
foreach (var s in EventScales) { foreach (var s in EventScales) {
s.Dispose(); s.Dispose();
} }
@@ -240,6 +246,47 @@ namespace Elwig {
} }
} }
private void OnSerialPortConnected(object? sender, string name) {
for (var i = 0; i < Config.Scales.Count; i++) {
var s = Config.Scales[i];
if ((s.Connection?.StartsWith($"serial://{name}:") ?? false) && Scales[i] is InvalidScale) {
try {
Scales[i] = Scale.FromConfig(s);
MessageBox.Show($"Verbindung zu Waage {s.Id} wieder hergestellt!", $"Waage {s.Id}", MessageBoxButton.OK, MessageBoxImage.Information);
} catch (Exception e) {
Scales[i] = new InvalidScale(s.Id);
MessageBox.Show($"Verbindung zu Waage {s.Id} konnte nicht hergestellt werden:\n\n{e.Message}", "Waagen-Fehler",
MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
UpdateScales();
}
private void OnSerialPortDisconnected(object? sender, string name) {
for (var i = 0; i < Config.Scales.Count; i++) {
var s = Config.Scales[i];
if ((s.Connection?.StartsWith($"serial://{name}:") ?? false) && Scales[i] is not InvalidScale) {
MessageBox.Show($"Verbindung zu Waage {s.Id} unterbrochen!", $"Waagen {s.Id}", MessageBoxButton.OK, MessageBoxImage.Warning);
try {
Scales[i].Dispose();
} catch {
// ignore
}
Scales[i] = new InvalidScale(s.Id);
}
}
UpdateScales();
}
public static void UpdateScales() {
foreach (Window w in CurrentApp.Windows) {
if (w is DeliveryAdminWindow t && t.ViewModel.IsReceipt) {
t.UpdateScales();
}
}
}
public static async Task CheckForUpdates(bool showAlert = false) { public static async Task CheckForUpdates(bool showAlert = false) {
if (Config.UpdateUrl == null) return; if (Config.UpdateUrl == null) return;
var latest = await Utils.GetLatestInstallerUrl(Config.UpdateUrl); var latest = await Utils.GetLatestInstallerUrl(Config.UpdateUrl);

View File

@@ -37,6 +37,7 @@
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="3.0.2" /> <PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="3.0.2" />
<PackageReference Include="System.IO.Hashing" Version="9.0.9" /> <PackageReference Include="System.IO.Hashing" Version="9.0.9" />
<PackageReference Include="System.IO.Ports" Version="9.0.9" /> <PackageReference Include="System.IO.Ports" Version="9.0.9" />
<PackageReference Include="System.Management" Version="9.0.10" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="9.0.9" /> <PackageReference Include="System.Text.Encoding.CodePages" Version="9.0.9" />
</ItemGroup> </ItemGroup>

View File

@@ -0,0 +1,54 @@
using System;
using System.IO.Ports;
using System.Linq;
using System.Management;
namespace Elwig.Helpers {
public sealed class SerialPortWatcher : IDisposable {
private readonly ManagementEventWatcher _deviceArrivalWatcher;
private readonly ManagementEventWatcher _deviceRemovalWatcher;
private string[] _knownPorts;
public event EventHandler<string>? SerialPortConnected;
public event EventHandler<string>? SerialPortDisconnected;
public SerialPortWatcher() {
_knownPorts = SerialPort.GetPortNames();
_deviceArrivalWatcher = new ManagementEventWatcher("SELECT * FROM Win32_DeviceChangeEvent WHERE EventType = 2");
_deviceArrivalWatcher.EventArrived += (s, e) => OnDeviceArrived();
_deviceRemovalWatcher = new ManagementEventWatcher("SELECT * FROM Win32_DeviceChangeEvent WHERE EventType = 3");
_deviceRemovalWatcher.EventArrived += (s, e) => OnDeviceRemoved();
_deviceArrivalWatcher.Start();
_deviceRemovalWatcher.Start();
}
private void OnDeviceArrived() {
string[] currentPorts = SerialPort.GetPortNames();
var newPorts = currentPorts.Except(_knownPorts).ToArray();
foreach (var port in newPorts)
SerialPortConnected?.Invoke(this, port);
_knownPorts = currentPorts;
}
private void OnDeviceRemoved() {
string[] currentPorts = SerialPort.GetPortNames();
var removedPorts = _knownPorts.Except(currentPorts).ToArray();
foreach (var port in removedPorts)
SerialPortDisconnected?.Invoke(this, port);
_knownPorts = currentPorts;
}
public void Dispose() {
try {
_deviceArrivalWatcher?.Stop();
_deviceRemovalWatcher?.Stop();
} finally {
_deviceArrivalWatcher?.Dispose();
_deviceRemovalWatcher?.Dispose();
}
}
}
}

View File

@@ -1168,6 +1168,19 @@ namespace Elwig.Windows {
WeighingDButton.IsEnabled = n > 3 && App.CommandScales[3].IsReady; WeighingDButton.IsEnabled = n > 3 && App.CommandScales[3].IsReady;
} }
public void UpdateScales() {
if (!ViewModel.IsReceipt) return;
foreach (var s in App.EventScales) {
s.WeighingEvent -= Scale_Weighing;
s.WeighingEvent += Scale_Weighing;
}
if (WeighingManualButton.IsEnabled) {
EnableWeighingButtons();
} else {
DisableWeighingButtons();
}
}
private async Task UpdateLsNr() { private async Task UpdateLsNr() {
if (string.IsNullOrEmpty(ViewModel.Date) || ViewModel.Branch == null) { if (string.IsNullOrEmpty(ViewModel.Date) || ViewModel.Branch == null) {
ViewModel.LsNr = ""; ViewModel.LsNr = "";