Weighing: Restructure class structure

This commit is contained in:
2024-02-21 12:57:55 +01:00
parent 7ff069d068
commit 99ca12b276
6 changed files with 161 additions and 104 deletions

View File

@ -63,7 +63,7 @@ namespace Elwig {
Directory.CreateDirectory(TempPath); Directory.CreateDirectory(TempPath);
Directory.CreateDirectory(DataPath); Directory.CreateDirectory(DataPath);
MainDispatcher = Dispatcher; MainDispatcher = Dispatcher;
Scales = Array.Empty<IScale>(); Scales = [];
CurrentApp = this; CurrentApp = this;
OverrideCulture(); OverrideCulture();
} }
@ -96,7 +96,7 @@ namespace Elwig {
return; return;
} }
Dictionary<string, (string, string, int?, string?, string?, string?, string?, string?)> branches = new(); Dictionary<string, (string, string, int?, string?, string?, string?, string?, string?)> branches = [];
using (var ctx = new AppDbContext()) { 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)); 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 { try {
@ -115,23 +115,11 @@ namespace Elwig {
var list = new List<IScale>(); var list = new List<IScale>();
foreach (var s in Config.Scales) { foreach (var s in Config.Scales) {
var id = s[0];
try { try {
var type = s[1]?.ToLower(); list.Add(Scale.FromConfig(s));
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}\"");
}
} catch (Exception e) { } catch (Exception e) {
list.Add(new InvalidScale(id)); list.Add(new InvalidScale(s.Id));
MessageBox.Show($"Unable to create scale {s[0]}:\n\n{e.Message}", "Scale Error", MessageBoxButton.OK, MessageBoxImage.Error); MessageBox.Show($"Unable to create scale {s.Id}:\n\n{e.Message}", "Scale Error", MessageBoxButton.OK, MessageBoxImage.Error);
} }
} }
Scales = list; Scales = list;

View File

@ -4,6 +4,31 @@ using System.Linq;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
namespace Elwig.Helpers { 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 { public class Config {
private readonly string FileName; private readonly string FileName;
@ -11,8 +36,8 @@ namespace Elwig.Helpers {
public string DatabaseFile = App.DataPath + "database.sqlite3"; public string DatabaseFile = App.DataPath + "database.sqlite3";
public string? DatabaseLog = null; public string? DatabaseLog = null;
public string? Branch = null; public string? Branch = null;
public IList<string?[]> Scales; public IList<ScaleConfig> Scales;
private readonly List<string?[]> ScaleList = []; private readonly List<ScaleConfig> ScaleList = [];
private static readonly string[] trueValues = ["1", "true", "yes", "on"]; private static readonly string[] trueValues = ["1", "true", "yes", "on"];
public Config(string filename) { public Config(string filename) {
@ -34,12 +59,10 @@ namespace Elwig.Helpers {
ScaleList.Clear(); ScaleList.Clear();
Scales = ScaleList; Scales = ScaleList;
foreach (var s in scales) { foreach (var s in scales) {
string? scaleLog = config[$"scale.{s}:log"]; ScaleList.Add(new(
if (scaleLog != null) scaleLog = Path.Combine(App.DataPath, scaleLog);
ScaleList.Add([
s, config[$"scale.{s}:type"], config[$"scale.{s}:model"], config[$"scale.{s}:connection"], 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"); file.Write($"\r\n[database]\r\nfile = {DatabaseFile}\r\n");
if (DatabaseLog != null) file.Write($"log = {DatabaseLog}\r\n"); if (DatabaseLog != null) file.Write($"log = {DatabaseLog}\r\n");
foreach (var s in ScaleList) { 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"); file.Write($"\r\n[scale.{s.Id}]\r\ntype = {s.Type}\r\nmodel = {s.Model}\r\nconnection = {s.Connection}\r\n");
if (s[4] != null) file.Write($"empty = {s[4]}\r\n"); if (s.Empty != null) file.Write($"empty = {s.Empty}\r\n");
if (s[5] != null) file.Write($"filling = {s[5]}\r\n"); if (s.Filling != null) file.Write($"filling = {s.Filling}\r\n");
if (s[6] != null) file.Write($"limit = {s[6]}\r\n"); if (s.Limit != null) file.Write($"limit = {s.Limit}\r\n");
if (s[7] != null) file.Write($"log = {s[7]}\r\n"); if (s._Log != null) file.Write($"log = {s._Log}\r\n");
} }
} }
} }

View File

@ -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}'"),
};
}
}
}

View File

@ -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;
}
}
}

View File

@ -1,23 +1,11 @@
using System; using System;
using System.IO; using System.IO;
using System.IO.Ports; using System.IO.Ports;
using System.Net.Sockets;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Elwig.Helpers.Weighing { namespace Elwig.Helpers.Weighing {
public class SystecScale : ICommandScale { public class SysTecITScale : Scale, 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 string Manufacturer => "SysTec"; public string Manufacturer => "SysTec";
public int InternalScaleNr => 1; public int InternalScaleNr => 1;
@ -25,74 +13,15 @@ namespace Elwig.Helpers.Weighing {
public string ScaleId { get; private set; } public string ScaleId { get; private set; }
public bool IsReady { get; private set; } public bool IsReady { get; private set; }
public bool HasFillingClearance { 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; ScaleId = id;
Model = model; Model = model;
IsReady = true; IsReady = true;
HasFillingClearance = false; 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.WriteTimeout = 250;
Stream.ReadTimeout = 11000; 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) { protected async Task SendCommand(string command) {

View File

@ -5,7 +5,7 @@ namespace Tests.WeighingTests {
class ScaleTestMatzen { class ScaleTestMatzen {
private MockScale? Mock; private MockScale? Mock;
private SystecScale? Scale; private SysTecITScale? Scale;
private static (string, bool) ScaleHandler(string req, int weight, string? error, int identNr) { private static (string, bool) ScaleHandler(string req, int weight, string? error, int identNr) {
var modes = error?.Split(';') ?? []; var modes = error?.Split(';') ?? [];