From d6059e724b417ead47ebbc20503ef517c479140f Mon Sep 17 00:00:00 2001
From: Lorenz Stechauner <lorenz.stechauner@necronda.net>
Date: Wed, 26 Apr 2023 21:01:06 +0200
Subject: [PATCH] Add more scale types

---
 Elwig/App.xaml.cs                       |  2 +
 Elwig/Helpers/Utils.cs                  | 52 +++++++++++++++++++-
 Elwig/Helpers/Weighing/GassnerScale.cs  | 63 +++++++++++++++++++++++++
 Elwig/Helpers/Weighing/SchemberScale.cs | 40 ++++++++++++++++
 Elwig/Helpers/Weighing/SystecScale.cs   | 36 +-------------
 5 files changed, 158 insertions(+), 35 deletions(-)
 create mode 100644 Elwig/Helpers/Weighing/GassnerScale.cs
 create mode 100644 Elwig/Helpers/Weighing/SchemberScale.cs

diff --git a/Elwig/App.xaml.cs b/Elwig/App.xaml.cs
index 08d171b..c5f5095 100644
--- a/Elwig/App.xaml.cs
+++ b/Elwig/App.xaml.cs
@@ -50,6 +50,8 @@ namespace Elwig {
                     int? limit = s[6] == null ? null : int.Parse(s[6]);
                     if (type == "systec") {
                         list.AddLast(new SystecScale(scaleNr, model, cnx, empty, filling, limit));
+                    } else {
+                        throw new ArgumentException($"Invalid scale type: \"{type}\"");
                     }
                 } catch (Exception e) {
                     MessageBox.Show($"Unable to create scale {s[0]}:\n\n{e.Message}", "Scale Error", MessageBoxButton.OK, MessageBoxImage.Error);
diff --git a/Elwig/Helpers/Utils.cs b/Elwig/Helpers/Utils.cs
index 3ae33f0..7e06ce6 100644
--- a/Elwig/Helpers/Utils.cs
+++ b/Elwig/Helpers/Utils.cs
@@ -7,12 +7,62 @@ using System.Windows;
 using System.Windows.Controls;
 using System.Diagnostics;
 using System.Windows.Controls.Primitives;
+using System.Text.RegularExpressions;
+using System.IO.Ports;
+using PdfSharp.Charting;
+using System.Net.Sockets;
 
 namespace Elwig.Helpers {
-    public static class Utils {
+    public static partial class Utils {
 
         public static int CurrentSeason => DateTime.Now.Year - (DateTime.Now.Month <= 3 ? 1 : 0);
 
+        public static readonly Regex SerialRegex = GeneratedSerialRegex();
+        public static readonly Regex TcpRegex = GeneratedTcpRegex();
+
+        [GeneratedRegex("^serial://([A-Za-z0-9]+):([0-9]+)(,([5-9]),([NOEMSnoems]),(0|1|1\\.5|2|))?$", RegexOptions.Compiled)]
+        private static partial Regex GeneratedSerialRegex();
+
+        [GeneratedRegex("^tcp://[A-Za-z0-9:._-]+(:[0-9]+)?$", RegexOptions.Compiled)]
+        private static partial Regex GeneratedTcpRegex();
+
+        public static SerialPort OpenSerialConnection(string connection) {
+            var m = Utils.SerialRegex.Match(connection);
+            if (!m.Success)
+                throw new ArgumentException("Invalid connection string for scheme \"serial\"");
+
+            var stop = m.Groups[6].Value;
+            var parity = m.Groups[5].Value.ToUpper();
+            var data = m.Groups[4].Value;
+            var port = new SerialPort() {
+                PortName = m.Groups[1].Value,
+                BaudRate = int.Parse(m.Groups[2].Value),
+                Parity = parity == "E" ? Parity.Even :
+                         parity == "O" ? Parity.Odd :
+                         parity == "M" ? Parity.Mark :
+                         parity == "S" ? Parity.Space :
+                                         Parity.None,
+                DataBits = data == "" ? 8 : int.Parse(data),
+                StopBits = (StopBits)(stop == "" ? 1 : stop == "1.5" ? 3 : stop[0] - '0'),
+                Handshake = Handshake.None,
+            };
+            port.Open();
+            return port;
+        }
+
+        public static TcpClient OpenTcpConnection(string connection) {
+            var m = Utils.TcpRegex.Match(connection);
+            if (!m.Success)
+                throw new ArgumentException("Invalid connection string for scheme \"tcp\"");
+
+            var client = new TcpClient() {
+                SendTimeout = 250,
+                ReceiveTimeout = 250,
+            };
+            client.Connect(m.Groups[1].Value, int.Parse(m.Groups[2].Value));
+            return client;
+        }
+
         public static void SetInputChanged(Control input) {
             var brush = Brushes.Orange;
             if (input is ComboBox cb) {
diff --git a/Elwig/Helpers/Weighing/GassnerScale.cs b/Elwig/Helpers/Weighing/GassnerScale.cs
new file mode 100644
index 0000000..3c30f5c
--- /dev/null
+++ b/Elwig/Helpers/Weighing/GassnerScale.cs
@@ -0,0 +1,63 @@
+using System;
+using System.IO;
+using System.IO.Ports;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Elwig.Helpers.Weighing {
+    public class GassnerScale : IScale {
+
+        protected SerialPort Serial = null;
+        protected StreamReader Reader;
+        protected StreamWriter Writer;
+
+        public string Manufacturer => "Gassner";
+        public int InternalScaleNr => 1;
+        public string Model { get; private set; }
+        public int ScaleNr { get; private set; }
+        public bool IsReady { get; private set; }
+        public bool HasFillingClearance { get; private set; }
+        public int? WeightLimit { get; private set; }
+
+        public GassnerScale(int scaleNr, string model, string connection) {
+            ScaleNr = scaleNr;
+            Model = model;
+            IsReady = true;
+            HasFillingClearance = false;
+
+            if (!connection.StartsWith("serial:"))
+                throw new ArgumentException("Unsupported scheme");
+
+            Serial = Utils.OpenSerialConnection(connection);
+            Writer = new(Serial.BaseStream, Encoding.ASCII, -1, true);
+            Reader = new(Serial.BaseStream, Encoding.ASCII, false, -1, true);
+        }
+
+        public void Dispose() {
+            Writer.Close();
+            Reader.Close();
+            Serial.Close();
+            GC.SuppressFinalize(this);
+        }
+
+        public async Task<WeighingResult> Weigh(bool incIdentNr) {
+            await Writer.WriteAsync(incIdentNr ? "\x05" : "?");
+            // TODO receive response
+            return new();
+        }
+
+        public async Task<WeighingResult> GetCurrentWeight() {
+            return await Weigh(false);
+        }
+
+        public async Task<WeighingResult> Weigh() {
+            return await Weigh(true);
+        }
+
+        public async Task Empty() { }
+
+        public async Task GrantFillingClearance() { }
+
+        public async Task RevokeFillingClearance() { }
+    }
+}
diff --git a/Elwig/Helpers/Weighing/SchemberScale.cs b/Elwig/Helpers/Weighing/SchemberScale.cs
new file mode 100644
index 0000000..e1ffa69
--- /dev/null
+++ b/Elwig/Helpers/Weighing/SchemberScale.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Threading.Tasks;
+
+namespace Elwig.Helpers.Weighing {
+    // TODO implement SchemberScale
+    public class SchemberScale : IScale {
+
+        public string Manufacturer => "Schember";
+        public string Model => throw new NotImplementedException();
+        public int ScaleNr => throw new NotImplementedException();
+        public int InternalScaleNr => throw new NotImplementedException();
+        public bool IsReady => throw new NotImplementedException();
+        public bool HasFillingClearance => throw new NotImplementedException();
+        public int? WeightLimit => throw new NotImplementedException();
+
+        public void Dispose() {
+            throw new NotImplementedException();
+        }
+
+        public Task Empty() {
+            throw new NotImplementedException();
+        }
+
+        public Task<WeighingResult> GetCurrentWeight() {
+            throw new NotImplementedException();
+        }
+
+        public Task GrantFillingClearance() {
+            throw new NotImplementedException();
+        }
+
+        public Task RevokeFillingClearance() {
+            throw new NotImplementedException();
+        }
+
+        public Task<WeighingResult> Weigh() {
+            throw new NotImplementedException();
+        }
+    }
+}
diff --git a/Elwig/Helpers/Weighing/SystecScale.cs b/Elwig/Helpers/Weighing/SystecScale.cs
index b4fcb84..45451d3 100644
--- a/Elwig/Helpers/Weighing/SystecScale.cs
+++ b/Elwig/Helpers/Weighing/SystecScale.cs
@@ -3,7 +3,6 @@ using System.IO;
 using System.IO.Ports;
 using System.Net.Sockets;
 using System.Text;
-using System.Text.RegularExpressions;
 using System.Threading.Tasks;
 
 namespace Elwig.Helpers.Weighing {
@@ -20,9 +19,6 @@ namespace Elwig.Helpers.Weighing {
         protected readonly Output? FillingClearanceMode = null;
         protected readonly int EmptyDelay;
 
-        protected static readonly Regex SerialRe = new(@"^serial://([A-Za-z0-9]+):([0-9]+)(,([5-9]),([NOEMSnoems]),(0|1|1\.5|2|))?$", RegexOptions.Compiled);
-        protected static readonly Regex TcpRe = new(@"^tcp://[A-Za-z0-9:._-]+(:[0-9]+)?$", RegexOptions.Compiled);
-
         public string Manufacturer => "SysTec";
         public int InternalScaleNr => 1;
         public string Model { get; private set; }
@@ -38,39 +34,11 @@ namespace Elwig.Helpers.Weighing {
             HasFillingClearance = false;
 
             Stream stream;
-
             if (connection.StartsWith("serial:")) {
-                var m = SerialRe.Match(connection);
-                if (!m.Success)
-                    throw new ArgumentException("Invalid connection string for scheme \"serial\"");
-
-                var stop = m.Groups[6].Value;
-                var parity = m.Groups[5].Value.ToUpper();
-                var data = m.Groups[4].Value;
-                Serial = new() {
-                    PortName = m.Groups[1].Value,
-                    BaudRate = int.Parse(m.Groups[2].Value),
-                    Parity = parity == "E" ? Parity.Even :
-                             parity == "O" ? Parity.Odd :
-                             parity == "M" ? Parity.Mark :
-                             parity == "S" ? Parity.Space :
-                                             Parity.None,
-                    DataBits = data == "" ? 8 : int.Parse(data),
-                    StopBits = (StopBits)(stop == "" ? 1 : stop == "1.5" ? 3 : stop[0] - '0'),
-                    Handshake = Handshake.None,
-                };
-                Serial.Open();
+                Serial = Utils.OpenSerialConnection(connection);
                 stream = Serial.BaseStream;
             } else if (connection.StartsWith("tcp:")) {
-                var m = TcpRe.Match(connection);
-                if (!m.Success)
-                    throw new ArgumentException("Invalid connection string for scheme \"tcp\"");
-
-                Tcp = new() {
-                    SendTimeout = 250,
-                    ReceiveTimeout = 250,
-                };
-                Tcp.Connect(m.Groups[1].Value, int.Parse(m.Groups[2].Value));
+                Tcp = Utils.OpenTcpConnection(connection);
                 stream = Tcp.GetStream();
             } else {
                 throw new ArgumentException("Unsupported scheme");