232 lines
9.8 KiB
C#
232 lines
9.8 KiB
C#
using System.IO.Ports;
|
|
|
|
namespace PamhagenSysCtrl.Helpers {
|
|
public class PamhagenPlc : IDisposable {
|
|
|
|
public record struct SensorStates(
|
|
long PressureSensors,
|
|
(FillState A1, FillState A2, FillState Press1, FillState Press2) FillingLevels,
|
|
int TroughSelector,
|
|
double GradationOe,
|
|
FillState[] FillingLevelTanks,
|
|
MotorState PresseQuerSchnecke
|
|
) {
|
|
public readonly bool GetDS(int n) => (PressureSensors & (1L << (n - 1))) != 0;
|
|
}
|
|
|
|
public record struct ActuatorStates(
|
|
long Valves,
|
|
MotorState MP1, MotorState MP2, MotorState P12, MotorState P3, MotorState P4, MotorState P5, MotorState P6,
|
|
bool TroughAuger, bool Rebler, bool StirrerA1, bool StirrerA2, bool Belt, bool Auger1, bool Auger2, bool GradationPump
|
|
) {
|
|
public readonly bool GetV(int n) => (Valves & (1L << (n - 1))) != 0;
|
|
public void SetV(int n, bool val) {
|
|
if (val) {
|
|
Valves |= 1L << (n - 1);
|
|
} else {
|
|
Valves &= ~(1L << (n - 1));
|
|
}
|
|
}
|
|
}
|
|
|
|
protected readonly T1 Plc;
|
|
|
|
public PamhagenPlc(string portName, Parity parity = Parity.Odd) {
|
|
Plc = new T1(portName, parity);
|
|
}
|
|
|
|
public void Dispose() {
|
|
Plc.Dispose();
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
protected static FillState Int3ToFillState(int n) {
|
|
return n switch {
|
|
7 => FillState.Full,
|
|
6 => FillState.Half,
|
|
4 => FillState.Filled,
|
|
0 => FillState.Empty,
|
|
_ => FillState.Invalid,
|
|
};
|
|
}
|
|
|
|
protected static FillState Int2ToFillState(int n) {
|
|
return n switch {
|
|
2 => FillState.Full,
|
|
0 => FillState.Filled,
|
|
1 => FillState.Empty,
|
|
_ => FillState.Invalid,
|
|
};
|
|
}
|
|
|
|
protected static FillState Int1ToFillState(int n) {
|
|
return n switch {
|
|
1 => FillState.Full,
|
|
0 => FillState.Unknown,
|
|
_ => FillState.Invalid,
|
|
};
|
|
}
|
|
|
|
protected static MotorState Int2ToPumpState(int n, bool inverse) {
|
|
return n switch {
|
|
0 => MotorState.Halt,
|
|
1 => inverse ? MotorState.Backward : MotorState.Forward,
|
|
2 => inverse ? MotorState.Forward : MotorState.Backward,
|
|
_ => MotorState.Invalid,
|
|
};
|
|
}
|
|
|
|
protected static MotorState Int1ToPumpState(int n) {
|
|
return n switch {
|
|
1 => MotorState.Forward,
|
|
_ => MotorState.Halt,
|
|
};
|
|
}
|
|
|
|
public async Task EnsureRunning() {
|
|
var s = await Plc.StatusRead();
|
|
if (s.Mode == T1.Mode.RUN || s.Mode == T1.Mode.RUN_F)
|
|
return;
|
|
var error = await Plc.ErrorStatusRead();
|
|
throw new T1Exception($"Interner Fehler: {error} ({(int)error:0000})");
|
|
}
|
|
|
|
protected async Task<(ushort RW010, ushort RW011, ushort RW012, ushort RW013, ushort RW014, ushort RW015, ushort RW016)> ReadInputRegisters() {
|
|
var d = await Plc.DataRead("RW010", 7);
|
|
// RW010, RW011, RW012, RW013 Drucksensoren
|
|
// RW014 Füllstände A1, A2, Presse1, Presse2, Wannenselekt
|
|
// RW015 Messwert von Marselli Gerät
|
|
// RW016 Füllstände der Tanks
|
|
return (d[0], d[1], d[2], d[3], d[4], d[5], d[6]);
|
|
}
|
|
|
|
public async Task<SensorStates> ReadInputs() {
|
|
await EnsureRunning();
|
|
var (r1, r2, r3, r4, f1, v, f2) = await ReadInputRegisters();
|
|
|
|
long pressure = (long)r1 | ((long)r2 << 16) | ((long)r3 << 32) | ((long)r4 << 48);
|
|
pressure = (pressure & 0x01FFFFFFFFFFFFFF) | ((pressure & 0x0600000000000000) << 1);
|
|
|
|
var a1 = Int3ToFillState((f1 ) & 0x7); // 111 011 001 000
|
|
var a2 = Int3ToFillState((f1 >> 3) & 0x7); // 111 011 001 000
|
|
var p1 = Int1ToFillState((f1 >> 6) & 0x1); // 1 0
|
|
var p2 = Int1ToFillState((f1 >> 7) & 0x1); // 1 0
|
|
var wp = ((f1 >> 8) & 0x3); // 10 01
|
|
|
|
// Masseligerät : 4mA => 50 Oe
|
|
// 20mA => 150 Oe
|
|
// ---> 0mA => 25 Oe
|
|
// Oe mA T1Wert
|
|
// 25 0 0
|
|
// 50 4 800
|
|
// 75 8 1600
|
|
// 100 12 2400
|
|
// 125 16 3200
|
|
// 150 20 4000
|
|
// -> factor: 0,03125 + (offset) 25 Oe
|
|
double oe = v * 0.03125 + 25;
|
|
|
|
var t1 = Int2ToFillState((f2 ) & 0x3); // 10 01 00
|
|
var t2 = Int2ToFillState((f2 >> 2) & 0x3); // 10 01 00
|
|
var t3 = Int2ToFillState((f2 >> 4) & 0x3); // 10 01 00
|
|
var t4 = Int2ToFillState((f2 >> 6) & 0x3); // 10 01 00
|
|
var t5 = Int2ToFillState((f2 >> 8) & 0x3); // 10 01 00
|
|
var t6 = Int1ToFillState((f2 >> 10) & 0x1); // 1 0
|
|
var t7 = Int1ToFillState((f2 >> 11) & 0x1); // 1 0
|
|
var t8 = Int1ToFillState((f2 >> 12) & 0x1); // 1 0
|
|
var t9 = Int1ToFillState((f2 >> 13) & 0x1); // 1 0
|
|
var q1 = Int1ToPumpState((f2 >> 15) & 0x1); // 1 0
|
|
|
|
return new SensorStates(pressure, (a1, a2, p1, p2), wp == 3 ? -1 : wp == 1 ? 2 : wp == 2 ? 1 : 0, oe, [t1, t2, t3, t4, t5, t6, t7, t8, t9], q1);
|
|
}
|
|
|
|
protected async Task<(ushort RW000, ushort RW001, ushort RW002, ushort RW003, ushort RW004, ushort RW005, ushort RW006)> ReadOutputRegisters() {
|
|
var d = await Plc.DataRead("RW000", 7);
|
|
// RW000, RW001, RW002, RW003 Ventile
|
|
// RW004 Pumpensteuerung MP1, MP2, P1/2, P3, P4, P5, P6
|
|
// RW005 Steuerung Wannenschnecke, Rebler, Förderung, Rührer
|
|
// RW006 MessungsSteuerung
|
|
return (d[0], d[1], d[2], d[3], d[4], d[5], d[6]);
|
|
}
|
|
|
|
protected async Task WriteOutputRegisters(ushort rw000, ushort rw001, ushort rw002, ushort rw003, ushort rw004, ushort rw005, ushort rw006) {
|
|
// RW000, RW001, RW002, RW003 Ventile
|
|
// RW004 Pumpensteuerung MP1, MP2, P1/2, P3, P4, P5, P6
|
|
// RW005 Steuerung Wannenschnecke, Rebler, Förderung, Rührer
|
|
// RW006 MessungsSteuerung
|
|
await Plc.DataWrite("RW000", rw000, rw001, rw002, rw003, rw004, rw005, rw006);
|
|
}
|
|
|
|
public async Task<ActuatorStates> ReadOutputs() {
|
|
await EnsureRunning();
|
|
var (v1, v2, v3, v4, p1, s1, s2) = await ReadOutputRegisters();
|
|
|
|
long valves = (long)v1 | ((long)v2 << 16) | ((long)v3 << 32) | ((long)v4 << 48);
|
|
|
|
var mp1 = Int2ToPumpState((p1 ) & 0x3, true);
|
|
var mp2 = Int2ToPumpState((p1 >> 2) & 0x3, false);
|
|
var p12 = Int1ToPumpState((p1 >> 4) & 0x1);
|
|
var p3 = Int1ToPumpState((p1 >> 5) & 0x1);
|
|
var p4 = Int1ToPumpState((p1 >> 6) & 0x1);
|
|
var p5 = Int1ToPumpState((p1 >> 7) & 0x1);
|
|
var p6 = Int1ToPumpState((p1 >> 8) & 0x1);
|
|
|
|
var m1 = (s1 & 0x01) != 0;
|
|
var m2 = (s1 & 0x02) != 0;
|
|
var m3 = (s1 & 0x08) != 0;
|
|
var m4 = (s1 & 0x10) != 0;
|
|
var m5 = (s1 & 0x20) != 0;
|
|
var m6 = (s1 & 0x40) != 0;
|
|
var m7 = (s1 & 0x80) != 0;
|
|
|
|
var m8 = (s2 & 0x04) != 0;
|
|
var state = new ActuatorStates(valves, mp1, mp2, p12, p3, p4, p5, p6, m1, m2, m3, m4, m5, m6, m7, m8);
|
|
state.SetV(58, (s1 & 0x04) != 0);
|
|
state.SetV(59, (s2 & 0x01) != 0);
|
|
state.SetV(60, (s2 & 0x02) != 0);
|
|
|
|
return state;
|
|
}
|
|
|
|
public async Task WriteOutputs(ActuatorStates state) {
|
|
await EnsureRunning();
|
|
var v1 = (ushort)((state.Valves ) & 0xFFFF);
|
|
var v2 = (ushort)((state.Valves >> 16) & 0xFFFF);
|
|
var v3 = (ushort)((state.Valves >> 32) & 0xFFFF);
|
|
var v4 = (ushort)((state.Valves >> 48) & 0x01FF);
|
|
|
|
int pumps =
|
|
(state.MP1 == MotorState.Backward ? 0x001 : 0x000) |
|
|
(state.MP1 == MotorState.Forward ? 0x002 : 0x000) |
|
|
(state.MP2 == MotorState.Forward ? 0x004 : 0x000) |
|
|
(state.MP2 == MotorState.Backward ? 0x008 : 0x000) |
|
|
(state.P12 == MotorState.Forward ? 0x010 : 0x000) |
|
|
(state.P3 == MotorState.Forward ? 0x020 : 0x000) |
|
|
(state.P4 == MotorState.Forward ? 0x040 : 0x000) |
|
|
(state.P5 == MotorState.Forward ? 0x080 : 0x000) |
|
|
(state.P6 == MotorState.Forward ? 0x100 : 0x000);
|
|
|
|
int devices =
|
|
(state.TroughAuger ? 0x01 : 0x00) |
|
|
(state.Rebler ? 0x02 : 0x00) |
|
|
(state.GetV(58) ? 0x04 : 0x00) |
|
|
(state.StirrerA1 ? 0x08 : 0x00) |
|
|
(state.StirrerA2 ? 0x10 : 0x00) |
|
|
(state.Belt ? 0x20 : 0x00) |
|
|
(state.Auger1 ? 0x40 : 0x00) |
|
|
(state.Auger2 ? 0x80 : 0x00);
|
|
|
|
int measuring =
|
|
(state.GetV(59) ? 0x01 : 0x00) |
|
|
(state.GetV(60) ? 0x02 : 0x00) |
|
|
(state.GradationPump ? 0x04 : 0x00);
|
|
|
|
await WriteOutputRegisters(v1, v2, v3, v4, (ushort)pumps, (ushort)devices, (ushort)measuring);
|
|
}
|
|
|
|
public async Task TestConnection() {
|
|
await Plc.Test();
|
|
}
|
|
}
|
|
}
|