diff --git a/Elwig/App.xaml.cs b/Elwig/App.xaml.cs index 54d6490..08be304 100644 --- a/Elwig/App.xaml.cs +++ b/Elwig/App.xaml.cs @@ -63,7 +63,7 @@ namespace Elwig { Directory.CreateDirectory(TempPath); Directory.CreateDirectory(DataPath); MainDispatcher = Dispatcher; - Scales = Array.Empty(); + Scales = []; CurrentApp = this; OverrideCulture(); } @@ -96,7 +96,7 @@ namespace Elwig { return; } - Dictionary branches = new(); + Dictionary branches = []; using (var ctx = new AppDbContext()) { branches = ctx.Branches.ToDictionary(b => b.Name.ToLower(), b => (b.ZwstId, b.Name, b.PostalDest?.AtPlz?.Plz, b.PostalDest?.AtPlz?.Ort.Name, b.Address, b.PhoneNr, b.FaxNr, b.MobileNr)); try { @@ -115,23 +115,11 @@ namespace Elwig { var list = new List(); foreach (var s in Config.Scales) { - var id = s[0]; try { - var type = s[1]?.ToLower(); - var model = s[2]; - var cnx = s[3]; - var empty = s[4]; - var filling = s[5]; - int? limit = s[6] == null ? null : int.Parse(s[6]); - var log = s[7]; - if (type == "systec") { - list.Add(new SystecScale(id, model, cnx, empty, filling, limit, log)); - } else { - throw new ArgumentException($"Invalid scale type: \"{type}\""); - } + list.Add(Scale.FromConfig(s)); } catch (Exception e) { - list.Add(new InvalidScale(id)); - MessageBox.Show($"Unable to create scale {s[0]}:\n\n{e.Message}", "Scale Error", MessageBoxButton.OK, MessageBoxImage.Error); + list.Add(new InvalidScale(s.Id)); + MessageBox.Show($"Unable to create scale {s.Id}:\n\n{e.Message}", "Scale Error", MessageBoxButton.OK, MessageBoxImage.Error); } } Scales = list; diff --git a/Elwig/Helpers/Config.cs b/Elwig/Helpers/Config.cs index 8cf601f..f967105 100644 --- a/Elwig/Helpers/Config.cs +++ b/Elwig/Helpers/Config.cs @@ -4,6 +4,31 @@ using System.Linq; using Microsoft.Extensions.Configuration; namespace Elwig.Helpers { + + public record struct ScaleConfig { + public string Id; + public string? Type; + public string? Model; + public string? Connection; + public string? Empty; + public string? Filling; + public string? Limit; + public string? Log; + public string? _Log; + + public ScaleConfig(string id, string? type, string? model, string? cnx, string? empty, string? filling, string? limit, string? log) { + Id = id; + Type = type; + Model = model; + Connection = cnx; + Empty = empty; + Filling = filling; + Limit = limit; + _Log = log; + Log = log != null ? Path.Combine(App.DataPath, log) : null; + } + } + public class Config { private readonly string FileName; @@ -11,8 +36,8 @@ namespace Elwig.Helpers { public string DatabaseFile = App.DataPath + "database.sqlite3"; public string? DatabaseLog = null; public string? Branch = null; - public IList Scales; - private readonly List ScaleList = []; + public IList Scales; + private readonly List ScaleList = []; private static readonly string[] trueValues = ["1", "true", "yes", "on"]; public Config(string filename) { @@ -34,12 +59,10 @@ namespace Elwig.Helpers { ScaleList.Clear(); Scales = ScaleList; foreach (var s in scales) { - string? scaleLog = config[$"scale.{s}:log"]; - if (scaleLog != null) scaleLog = Path.Combine(App.DataPath, scaleLog); - ScaleList.Add([ + ScaleList.Add(new( s, config[$"scale.{s}:type"], config[$"scale.{s}:model"], config[$"scale.{s}:connection"], - config[$"scale.{s}:empty"], config[$"scale.{s}:filling"], config[$"scale.{s}:limit"], scaleLog - ]); + config[$"scale.{s}:empty"], config[$"scale.{s}:filling"], config[$"scale.{s}:limit"], config[$"scale.{s}:log"] + )); } } @@ -51,11 +74,11 @@ namespace Elwig.Helpers { file.Write($"\r\n[database]\r\nfile = {DatabaseFile}\r\n"); if (DatabaseLog != null) file.Write($"log = {DatabaseLog}\r\n"); foreach (var s in ScaleList) { - file.Write($"\r\n[scale.{s[0]}]\r\ntype = {s[1]}\r\nmodel = {s[2]}\r\nconnection = {s[3]}\r\n"); - if (s[4] != null) file.Write($"empty = {s[4]}\r\n"); - if (s[5] != null) file.Write($"filling = {s[5]}\r\n"); - if (s[6] != null) file.Write($"limit = {s[6]}\r\n"); - if (s[7] != null) file.Write($"log = {s[7]}\r\n"); + file.Write($"\r\n[scale.{s.Id}]\r\ntype = {s.Type}\r\nmodel = {s.Model}\r\nconnection = {s.Connection}\r\n"); + if (s.Empty != null) file.Write($"empty = {s.Empty}\r\n"); + if (s.Filling != null) file.Write($"filling = {s.Filling}\r\n"); + if (s.Limit != null) file.Write($"limit = {s.Limit}\r\n"); + if (s._Log != null) file.Write($"log = {s._Log}\r\n"); } } } diff --git a/Elwig/Helpers/Weighing/Scale.cs b/Elwig/Helpers/Weighing/Scale.cs new file mode 100644 index 0000000..88c446b --- /dev/null +++ b/Elwig/Helpers/Weighing/Scale.cs @@ -0,0 +1,95 @@ +using System.IO.Ports; +using System.IO; +using System.Net.Sockets; +using System; + +namespace Elwig.Helpers.Weighing { + public abstract class Scale : IDisposable { + + protected enum Output { RTS, DTR, OUT1, OUT2 }; + + protected SerialPort? Serial = null; + protected SerialPort? ControlSerialEmpty = null, ControlSerialFilling = null; + protected TcpClient? Tcp = null; + protected Stream Stream; + + protected readonly Output? EmptyMode = null; + protected readonly Output? FillingClearanceMode = null; + protected readonly int EmptyDelay; + + public int? WeightLimit { get; private set; } + public string? LogPath { get; private set; } + + public static IScale FromConfig(ScaleConfig config) { + int? limit = config.Limit != null ? int.Parse(config.Limit) : null; + if (config.Type == "SysTec-IT") { + return new SysTecITScale(config.Id, config.Model!, config.Connection!, config.Empty, config.Filling, limit, config.Log); + } else if (config.Type == "Schember-evt") { + return new SchemberEventScale(config.Id, config.Model!, config.Connection!, config.Empty, config.Filling, limit, config.Log); + } else { + throw new ArgumentException($"Invalid scale type: \"{config.Type}\""); + } + } + + protected Scale(string cnx, string? empty, string? filling, int? limit, string? log) { + if (cnx.StartsWith("serial:")) { + Serial = Utils.OpenSerialConnection(cnx); + Stream = Serial.BaseStream; + } else if (cnx.StartsWith("tcp:")) { + Tcp = Utils.OpenTcpConnection(cnx); + Stream = Tcp.GetStream(); + } else { + throw new ArgumentException("Unsupported scheme"); + } + + LogPath = log; + + if (empty != null) { + var parts = empty.Split(':'); + if (parts.Length == 3) { + if (parts[0] != Serial?.PortName) + ControlSerialEmpty = Utils.OpenSerialConnection($"serial://{parts[0]}:9600"); + } else if (parts.Length != 2) { + throw new ArgumentException("Invalid value for 'empty'"); + } + EmptyMode = ConvertOutput(parts[^2]); + EmptyDelay = int.Parse(parts[^1]); + } + + WeightLimit = limit; + if (filling != null) { + var parts = filling.Split(':'); + if (parts.Length == 2) { + if (parts[0] != Serial?.PortName) + ControlSerialFilling = parts[0] != ControlSerialEmpty?.PortName ? Utils.OpenSerialConnection($"serial://{parts[0]}:9600") : ControlSerialEmpty; + } else if (parts.Length != 1) { + throw new ArgumentException("Invalid value for 'filling'"); + } + FillingClearanceMode = ConvertOutput(parts[^1]); + } + + if (FillingClearanceMode != null && WeightLimit == null) + throw new ArgumentException("Weight limit has to be set, if filling clearance supervision is enabled"); + } + + public void Dispose() { + Stream.Close(); + Serial?.Close(); + ControlSerialEmpty?.Close(); + ControlSerialFilling?.Close(); + Tcp?.Close(); + GC.SuppressFinalize(this); + } + + protected static Output? ConvertOutput(string? value) { + return value switch { + null => null, + "RTS" => Output.RTS, + "DTR" => Output.DTR, + "OUT1" => Output.OUT1, + "OUT2" => Output.OUT2, + _ => throw new ArgumentException($"Invalid value for argument: '{value}'"), + }; + } + } +} diff --git a/Elwig/Helpers/Weighing/SchemberEventScale.cs b/Elwig/Helpers/Weighing/SchemberEventScale.cs new file mode 100644 index 0000000..88559e3 --- /dev/null +++ b/Elwig/Helpers/Weighing/SchemberEventScale.cs @@ -0,0 +1,22 @@ +namespace Elwig.Helpers.Weighing { + public class SchemberEventScale : Scale, IEventScale { + + public string Manufacturer => "Schember"; + public int InternalScaleNr => 1; + public string Model { get; private set; } + public string ScaleId { get; private set; } + public bool IsReady { get; private set; } + public bool HasFillingClearance { get; private set; } + + public SchemberEventScale(string id, string model, string cnx, string? empty = null, string? filling = null, int? limit = null, string? log = null) : + base(cnx, empty, filling, limit, log) { + ScaleId = id; + Model = model; + IsReady = true; + HasFillingClearance = false; + Stream.WriteTimeout = 250; + Stream.ReadTimeout = 6000; + } + + } +} diff --git a/Elwig/Helpers/Weighing/SystecScale.cs b/Elwig/Helpers/Weighing/SystecItScale.cs similarity index 67% rename from Elwig/Helpers/Weighing/SystecScale.cs rename to Elwig/Helpers/Weighing/SystecItScale.cs index ec34e09..ec0b96b 100644 --- a/Elwig/Helpers/Weighing/SystecScale.cs +++ b/Elwig/Helpers/Weighing/SystecItScale.cs @@ -1,23 +1,11 @@ using System; using System.IO; using System.IO.Ports; -using System.Net.Sockets; using System.Text; using System.Threading.Tasks; namespace Elwig.Helpers.Weighing { - public class SystecScale : ICommandScale { - - protected enum Output { RTS, DTR, OUT1, OUT2 }; - - protected SerialPort? Serial = null; - protected SerialPort? ControlSerialEmpty = null, ControlSerialFilling = null; - protected TcpClient? Tcp = null; - protected Stream Stream; - - protected readonly Output? EmptyMode = null; - protected readonly Output? FillingClearanceMode = null; - protected readonly int EmptyDelay; + public class SysTecITScale : Scale, ICommandScale { public string Manufacturer => "SysTec"; public int InternalScaleNr => 1; @@ -25,74 +13,15 @@ namespace Elwig.Helpers.Weighing { public string ScaleId { get; private set; } public bool IsReady { get; private set; } public bool HasFillingClearance { get; private set; } - public int? WeightLimit { get; private set; } - public string? LogPath { get; private set; } - public SystecScale(string id, string model, string connection, string? empty = null, string? filling = null, int? limit = null, string? log = null) { + public SysTecITScale(string id, string model, string cnx, string? empty = null, string? filling = null, int? limit = null, string? log = null) : + base(cnx, empty, filling, limit, log) { ScaleId = id; Model = model; IsReady = true; HasFillingClearance = false; - LogPath = log; - - if (connection.StartsWith("serial:")) { - Serial = Utils.OpenSerialConnection(connection); - Stream = Serial.BaseStream; - } else if (connection.StartsWith("tcp:")) { - Tcp = Utils.OpenTcpConnection(connection); - Stream = Tcp.GetStream(); - } else { - throw new ArgumentException("Unsupported scheme"); - } Stream.WriteTimeout = 250; Stream.ReadTimeout = 11000; - - if (empty != null) { - var parts = empty.Split(':'); - if (parts.Length == 3) { - if (parts[0] != Serial?.PortName) - ControlSerialEmpty = Utils.OpenSerialConnection($"serial://{parts[0]}:9600"); - } else if (parts.Length != 2) { - throw new ArgumentException("Invalid value for 'empty'"); - } - EmptyMode = ConvertOutput(parts[^2]); - EmptyDelay = int.Parse(parts[^1]); - } - - WeightLimit = limit; - if (filling != null) { - var parts = filling.Split(':'); - if (parts.Length == 2) { - if (parts[0] != Serial?.PortName) - ControlSerialFilling = parts[0] != ControlSerialEmpty?.PortName ? Utils.OpenSerialConnection($"serial://{parts[0]}:9600") : ControlSerialEmpty; - } else if (parts.Length != 1) { - throw new ArgumentException("Invalid value for 'filling'"); - } - FillingClearanceMode = ConvertOutput(parts[^1]); - } - - if (FillingClearanceMode != null && WeightLimit == null) - throw new ArgumentException("Weight limit has to be set, if filling clearance supervision is enabled"); - } - - public void Dispose() { - Stream.Close(); - Serial?.Close(); - ControlSerialEmpty?.Close(); - ControlSerialFilling?.Close(); - Tcp?.Close(); - GC.SuppressFinalize(this); - } - - protected static Output? ConvertOutput(string? value) { - return value switch { - null => null, - "RTS" => Output.RTS, - "DTR" => Output.DTR, - "OUT1" => Output.OUT1, - "OUT2" => Output.OUT2, - _ => throw new ArgumentException($"Invalid value for argument: '{value}'"), - }; } protected async Task SendCommand(string command) { diff --git a/Tests/WeighingTests/ScaleTestMatzen.cs b/Tests/WeighingTests/ScaleTestMatzen.cs index 3ef4f8a..dfc7d32 100644 --- a/Tests/WeighingTests/ScaleTestMatzen.cs +++ b/Tests/WeighingTests/ScaleTestMatzen.cs @@ -5,7 +5,7 @@ namespace Tests.WeighingTests { class ScaleTestMatzen { private MockScale? Mock; - private SystecScale? Scale; + private SysTecITScale? Scale; private static (string, bool) ScaleHandler(string req, int weight, string? error, int identNr) { var modes = error?.Split(';') ?? [];