Fixes after testing live
This commit is contained in:
@@ -1,64 +0,0 @@
|
|||||||
using System.IO;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace PamhagenSysCtrl.Helpers {
|
|
||||||
public static partial class Extensions {
|
|
||||||
|
|
||||||
[LibraryImport("msvcrt.dll")]
|
|
||||||
[UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
|
|
||||||
private static unsafe partial int memcmp(void* b1, void* b2, long count);
|
|
||||||
|
|
||||||
public static unsafe int CompareBuffers(char[] buffer1, int offset1, char[] buffer2, int offset2, int count) {
|
|
||||||
fixed (char* b1 = buffer1, b2 = buffer2) {
|
|
||||||
return memcmp(b1 + offset1, b2 + offset2, count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static string? ReadUntil(this StreamReader reader, string delimiter) {
|
|
||||||
return ReadUntil(reader, delimiter.ToCharArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string? ReadUntil(this StreamReader reader, char delimiter) {
|
|
||||||
return ReadUntil(reader, [delimiter]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string? ReadUntil(this StreamReader reader, char[] delimiter) {
|
|
||||||
var buf = new char[512];
|
|
||||||
int bufSize = 0, ret;
|
|
||||||
while (!reader.EndOfStream && bufSize < buf.Length - 1) {
|
|
||||||
if ((ret = reader.Read()) == -1)
|
|
||||||
return null;
|
|
||||||
buf[bufSize++] = (char)ret;
|
|
||||||
|
|
||||||
if (bufSize >= delimiter.Length && CompareBuffers(buf, bufSize - delimiter.Length, delimiter, 0, delimiter.Length) == 0)
|
|
||||||
return new string(buf, 0, bufSize);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Task<string?> ReadUntilAsync(this StreamReader reader, string delimiter) {
|
|
||||||
return ReadUntilAsync(reader, delimiter.ToCharArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Task<string?> ReadUntilAsync(this StreamReader reader, char delimiter) {
|
|
||||||
return ReadUntilAsync(reader, [delimiter]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task<string?> ReadUntilAsync(this StreamReader reader, char[] delimiter) {
|
|
||||||
var buf = new char[512];
|
|
||||||
int bufSize = 0;
|
|
||||||
var tmpBuf = new char[1];
|
|
||||||
while (!reader.EndOfStream && bufSize < buf.Length - 1) {
|
|
||||||
if ((await reader.ReadAsync(tmpBuf, 0, 1)) != 1)
|
|
||||||
return null;
|
|
||||||
buf[bufSize++] = tmpBuf[0];
|
|
||||||
|
|
||||||
if (bufSize >= delimiter.Length && CompareBuffers(buf, bufSize - delimiter.Length, delimiter, 0, delimiter.Length) == 0)
|
|
||||||
return new string(buf, 0, bufSize);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -15,13 +15,10 @@ namespace PamhagenSysCtrl.Helpers {
|
|||||||
protected PamhagenPlc.ActuatorStates Actuators;
|
protected PamhagenPlc.ActuatorStates Actuators;
|
||||||
protected PamhagenPlc.SensorStates Sensors;
|
protected PamhagenPlc.SensorStates Sensors;
|
||||||
|
|
||||||
protected bool GetDS(int n) => Sensors.GetDS(n >= 1 && n <= 60 ? n : throw new ArgumentException("Invalid value for n: 1 <= #DS <= 60"));
|
|
||||||
protected bool GetV(int n) => Actuators.GetV(n >= 1 && n <= 60 ? n : throw new ArgumentException("Invalid value for n: 1 <= #V <= 60"));
|
|
||||||
|
|
||||||
public bool IsValveClosed(int n) => GetDS(n);
|
|
||||||
public bool IsValveOpen(int n) => !IsValveClosed(n);
|
public bool IsValveOpen(int n) => !IsValveClosed(n);
|
||||||
public bool WantValveClosed(int n) => GetV(n);
|
public bool IsValveClosed(int n) => Sensors.GetDS(n);
|
||||||
public bool WantValveOpen(int n) => !WantValveClosed(n);
|
public bool WantValveClosed(int n) => !WantValveOpen(n);
|
||||||
|
public bool WantValveOpen(int n) => Actuators.GetV(n);
|
||||||
|
|
||||||
public bool IsA1Selected => Sensors.TroughSelector == 1;
|
public bool IsA1Selected => Sensors.TroughSelector == 1;
|
||||||
public bool IsA2Selected => Sensors.TroughSelector == 2;
|
public bool IsA2Selected => Sensors.TroughSelector == 2;
|
||||||
@@ -149,10 +146,10 @@ namespace PamhagenSysCtrl.Helpers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void OpenV(int n) => SetV(n, false);
|
public void OpenV(int n) => SetV(n, true);
|
||||||
public void CloseV(int n) => SetV(n, true);
|
public void CloseV(int n) => SetV(n, false);
|
||||||
|
|
||||||
public void SetV(int n, bool val) {
|
protected void SetV(int n, bool val) {
|
||||||
if (n < 1 || n > 60) throw new ArgumentException("Invalid value for n: 1 <= #V <= 60");
|
if (n < 1 || n > 60) throw new ArgumentException("Invalid value for n: 1 <= #V <= 60");
|
||||||
Actuators.SetV(n, val);
|
Actuators.SetV(n, val);
|
||||||
|
|
||||||
|
|||||||
@@ -27,8 +27,6 @@ namespace PamhagenSysCtrl.Helpers {
|
|||||||
Valves &= ~(1L << (n - 1));
|
Valves &= ~(1L << (n - 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public void CloseV(int n) => SetV(n, true);
|
|
||||||
public void OpenV(int n) => SetV(n, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected readonly T1 Plc;
|
protected readonly T1 Plc;
|
||||||
|
|||||||
@@ -9,58 +9,59 @@ namespace PamhagenSysCtrl.Helpers {
|
|||||||
public readonly record struct Status(Mode Mode, int Flags);
|
public readonly record struct Status(Mode Mode, int Flags);
|
||||||
|
|
||||||
protected SerialPort Serial;
|
protected SerialPort Serial;
|
||||||
protected Stream Stream;
|
|
||||||
protected StreamReader Reader;
|
|
||||||
|
|
||||||
public T1(string portName, Parity parity = Parity.Odd) {
|
public T1(string portName, Parity parity = Parity.Odd) {
|
||||||
Serial = new SerialPort(portName, 9600, parity, 8, StopBits.One);
|
Serial = new SerialPort(portName, 9600, parity, 8, StopBits.One) {
|
||||||
|
Handshake = Handshake.None,
|
||||||
|
NewLine = "\r",
|
||||||
|
ReadTimeout = 1000,
|
||||||
|
WriteTimeout = 1000,
|
||||||
|
DtrEnable = true,
|
||||||
|
RtsEnable = true,
|
||||||
|
Encoding = Encoding.ASCII,
|
||||||
|
};
|
||||||
Serial.Open();
|
Serial.Open();
|
||||||
Stream = Serial.BaseStream ?? Stream.Null;
|
Serial.DiscardInBuffer();
|
||||||
Stream.ReadTimeout = 250;
|
Serial.DiscardOutBuffer();
|
||||||
Stream.WriteTimeout = 250;
|
|
||||||
Reader = new(Stream, Encoding.ASCII, false, 256);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose() {
|
public void Dispose() {
|
||||||
Reader.Dispose();
|
|
||||||
Stream.Dispose();
|
|
||||||
Serial.Dispose();
|
Serial.Dispose();
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async Task SendCommand(string cmd, string? data = null, bool checksum = true) {
|
protected async Task SendCommand(string cmd, string? data = null, bool checksum = true) {
|
||||||
// max 255 bytes
|
// max 255 bytes
|
||||||
byte[] d = new byte[255];
|
var line = new StringBuilder("(A", 256);
|
||||||
int n = Encoding.ASCII.GetBytes($"(A{1:00}{cmd}", d);
|
line.AppendFormat("{0:00}{1}", 1, cmd);
|
||||||
if (data != null) {
|
if (data != null) {
|
||||||
if (data.Length > 244) throw new ArgumentException("T1 playload too long");
|
if (data.Length > 244) throw new ArgumentException("T1 playload too long");
|
||||||
n += Encoding.ASCII.GetBytes(data, 0, data.Length, d, n);
|
line.Append(data);
|
||||||
}
|
}
|
||||||
if (checksum) {
|
if (checksum) {
|
||||||
d[n++] = (byte)'&';
|
line.Append('&');
|
||||||
byte chk = d.Take(n).Aggregate((s, c) => (byte)((s + c) & 0xFF));
|
var chk = Enumerable.Range(0, line.Length).Aggregate(0, (c, i) => c + line[i]) & 0xFF;
|
||||||
n += Encoding.ASCII.GetBytes($"{chk:X2}", 0, 2, d, n);
|
line.AppendFormat("{0:X2}", chk);
|
||||||
}
|
}
|
||||||
d[n++] = (byte)')';
|
line.Append(')');
|
||||||
d[n++] = (byte)'\r';
|
Serial.WriteLine(line.ToString());
|
||||||
await Stream.WriteAsync(d.AsMemory(0, n));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async Task<(string, string)> ReceiveResponse(string? expectedCmd = null) {
|
protected async Task<(string, string)> ReceiveResponse(string? expectedCmd = null) {
|
||||||
var line = await Reader.ReadUntilAsync('\r');
|
var line = Serial.ReadLine();
|
||||||
if (line == null) {
|
if (line == null) {
|
||||||
throw new IOException("Verbindung zu SPS (T1) verloren");
|
throw new IOException("Verbindung zu SPS (T1) verloren");
|
||||||
} else if (line.Length < 9 || !line.StartsWith("(A") || !line.EndsWith(")\r") || line[^5] != '&') {
|
} else if (line.Length < 8 || !line.StartsWith("(A") || !line.EndsWith(')') || line[^4] != '&') {
|
||||||
throw new FormatException("Invalid response from PLC (T1)");
|
throw new FormatException("Invalid response from PLC (T1)");
|
||||||
}
|
}
|
||||||
|
|
||||||
var chk1 = (byte)Convert.ToInt32(line[^4..^2], 16);
|
var chk1 = Convert.ToInt32(line[^3..^1], 16);
|
||||||
var chk2 = Encoding.ASCII.GetBytes(line).SkipLast(4).Aggregate((s, c) => (byte)((s + c) & 0xFF));
|
var chk2 = Enumerable.Range(0, line.Length - 3).Aggregate(0, (c, i) => c + line[i]) & 0xFF;
|
||||||
if (chk1 != chk2) {
|
if (chk1 != chk2) {
|
||||||
throw new IOException($"Ungültige Prüfsumme von SPS (T1)");
|
throw new IOException($"Ungültige Prüfsumme von SPS (T1)");
|
||||||
}
|
}
|
||||||
|
|
||||||
var (cmd, data) = (line[4..6], line[6..^5]);
|
var (cmd, data) = (line[4..6], line[6..^4]);
|
||||||
if (cmd == "ST" && data[3] == '6') {
|
if (cmd == "ST" && data[3] == '6') {
|
||||||
// TODO check for other error bits?
|
// TODO check for other error bits?
|
||||||
await SendCommand("ER");
|
await SendCommand("ER");
|
||||||
@@ -89,7 +90,7 @@ namespace PamhagenSysCtrl.Helpers {
|
|||||||
return (cmd, data);
|
return (cmd, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Test(string data = "PINGPONG") {
|
public async Task Test(string data = "_Ping_Pong_0123456789_abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ_") {
|
||||||
await SendCommand("TS", data);
|
await SendCommand("TS", data);
|
||||||
var (_, res) = await ReceiveResponse("TS");
|
var (_, res) = await ReceiveResponse("TS");
|
||||||
if (data.Replace(" ", "") != res) throw new T1Exception("Wrong test response from PLC (T1)");
|
if (data.Replace(" ", "") != res) throw new T1Exception("Wrong test response from PLC (T1)");
|
||||||
|
|||||||
@@ -46,8 +46,8 @@ namespace PamhagenSysCtrl {
|
|||||||
} catch (Exception exc) {
|
} catch (Exception exc) {
|
||||||
App.Plant?.Dispose();
|
App.Plant?.Dispose();
|
||||||
App.Plant = null;
|
App.Plant = null;
|
||||||
var str = "Beim Aufbauen einer Verbindung zur SPS ist ein Fehler aufgetreten:\n\n" + exc.Message;
|
var str = $"Beim Aufbauen einer Verbindung zur SPS ist ein Fehler aufgetreten:\n\n{exc.GetType().Name}: {exc.Message}";
|
||||||
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
|
if (exc.InnerException != null) str += $"\n\n{exc.InnerException.GetType().Name}: {exc.InnerException.Message}";
|
||||||
MessageBox.Show(str, "SPS-Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
MessageBox.Show(str, "SPS-Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user