Files
pamhagen-sysctrl/PamhagenSysCtrl/Helpers/Plc/PamhagenState.cs
T
2026-10-13 17:26:35 +02:00

263 lines
17 KiB
C#

namespace PamhagenSysCtrl.Helpers.Plc {
public readonly record struct PamhagenState {
private readonly LegacyPamhagenPlc.SensorStates Sensors;
private readonly Func<LegacyPamhagenPlc.ActuatorStates> ActuatorsCb;
private LegacyPamhagenPlc.ActuatorStates Actuators { get => ActuatorsCb == null ? default : ActuatorsCb(); }
public long Valves => Actuators.Valves;
public long PressureSensors => Sensors.PressureSensors;
public bool MW1_IsSelected => Sensors.TroughSelector == 1;
public bool MW2_IsSelected => Sensors.TroughSelector == 2;
public bool MW1_WantSelected => Actuators.GetV(58);
public bool MW2_WantSelected => !Actuators.GetV(58);
public FillState MW1_FillLevel => Sensors.FillLevelMW1;
public FillState MW2_FillLevel => Sensors.FillLevelMW2;
public FillState[] Tanks_FillLevel => Sensors.FillLevelTanks ?? [FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown];
public FillState T1_FillLevel => Sensors.FillLevelTanks[0];
public FillState T2_FillLevel => Sensors.FillLevelTanks[1];
public FillState T3_FillLevel => Sensors.FillLevelTanks[2];
public FillState T4_FillLevel => Sensors.FillLevelTanks[3];
public FillState T5_FillLevel => Sensors.FillLevelTanks[4];
public FillState T6_FillLevel => Sensors.FillLevelTanks[5];
public FillState T7_FillLevel => Sensors.FillLevelTanks[6];
public FillState T8_FillLevel => Sensors.FillLevelTanks[7];
public FillState T9_FillLevel => Sensors.FillLevelTanks[8];
public MotorState PressCrossAuger_State => Sensors.PressCrossAuger;
public bool PressCrossAuger_IsActive => PressCrossAuger_State == MotorState.Forward;
public bool TroughAuger_IsActive => Actuators.TroughAuger;
public bool Rebler_IsActive => Actuators.Rebler;
public bool MW1_Stirrer_IsActive => Actuators.StirrerMW1;
public bool MW2_Stirrer_IsActive => Actuators.StirrerMW2;
public bool Auger1_IsActive => Actuators.Auger1;
public bool Auger2_IsActive => Actuators.Auger2;
public bool Auger3_IsActive => Actuators.Auger3;
public bool WasteDisposal_IsActive => Auger1_IsActive || Auger2_IsActive || Auger3_IsActive;
public MotorState MP1_State => Actuators.MP1;
public MotorState MP2_State => Actuators.MP2;
public MotorState P12_State => Actuators.P12;
public MotorState P3_State => Actuators.P3;
public MotorState P4_State => Actuators.P4;
public MotorState P5_State => Actuators.P5;
public MotorState P6_State => Actuators.P6;
public bool MP1_IsActive => Actuators.MP1 == MotorState.Forward || Actuators.MP1 == MotorState.Backward;
public bool MP2_IsActive => Actuators.MP2 == MotorState.Forward || Actuators.MP2 == MotorState.Backward;
public bool P12_IsActive => Actuators.P12 == MotorState.Forward;
public bool P3_IsActive => Actuators.P3 == MotorState.Forward;
public bool P4_IsActive => Actuators.P4 == MotorState.Forward;
public bool P5_IsActive => Actuators.P5 == MotorState.Forward;
public bool P6_IsActive => Actuators.P6 == MotorState.Forward;
public bool Press1_HasClearance => Sensors.ClearancePress1;
public bool Press2_HasClearance => Sensors.ClearancePress2;
public bool TroughAuger_HasClearance => Rebler_IsActive && !(MW1_IsSelected && MW1_FillLevel == FillState.Full) && !(MW2_IsSelected && MW2_FillLevel == FillState.Full);
public readonly bool MP1_HasPathClearance { get; }
public readonly bool MP2_HasPathClearance { get; }
public readonly bool P12_HasPathClearance { get; }
public readonly bool P3_HasPathClearance { get; }
public readonly bool P4_HasPathClearance { get; }
public readonly bool P5_HasPathClearance { get; }
public readonly bool P6_HasPathClearance { get; }
public readonly bool MP1_HasDryRunClearance { get; }
public readonly bool MP2_HasDryRunClearance { get; }
public readonly bool P12_HasDryRunClearance { get; }
public readonly bool P3_HasDryRunClearance { get; }
public readonly bool P4_HasDryRunClearance { get; }
public readonly bool P5_HasDryRunClearance { get; }
public readonly bool P6_HasDryRunClearance { get; }
public bool MP1_HasClearance => MP1_HasDryRunClearance && MP1_HasPathClearance;
public bool MP2_HasClearance => MP2_HasDryRunClearance && MP2_HasPathClearance;
public bool P12_HasClearance => P12_HasDryRunClearance && P12_HasPathClearance;
public bool P3_HasClearance => P3_HasDryRunClearance && P3_HasPathClearance;
public bool P4_HasClearance => P4_HasDryRunClearance && P4_HasPathClearance;
public bool P5_HasClearance => P4_HasDryRunClearance && P5_HasPathClearance;
public bool P6_HasClearance => P6_HasDryRunClearance && P6_HasPathClearance;
public PamhagenState(LegacyPamhagenPlc.SensorStates sensors, Func<LegacyPamhagenPlc.ActuatorStates> actuatorsCb, bool overridePathClearances = false, bool overrideDryRunClearances = false) {
Sensors = sensors;
ActuatorsCb = actuatorsCb;
MP1_HasPathClearance = overridePathClearances || HasPathClearance(this, Paths.MP1);
MP2_HasPathClearance = overridePathClearances || HasPathClearance(this, Paths.MP2);
P12_HasPathClearance = overridePathClearances || HasPathClearance(this, Paths.P12);
P3_HasPathClearance = overridePathClearances || HasPathClearance(this, Paths.P3);
P4_HasPathClearance = overridePathClearances || HasPathClearance(this, Paths.P4);
P5_HasPathClearance = overridePathClearances || HasPathClearance(this, Paths.P5);
P6_HasPathClearance = overridePathClearances || HasPathClearance(this, Paths.P6);
MP1_HasDryRunClearance = overrideDryRunClearances || MW1_FillLevel != FillState.Empty;
MP2_HasDryRunClearance = overrideDryRunClearances || MW2_FillLevel != FillState.Empty;
P12_HasDryRunClearance = overrideDryRunClearances || T1_FillLevel != FillState.Empty || T2_FillLevel != FillState.Empty;
P3_HasDryRunClearance = overrideDryRunClearances || T3_FillLevel != FillState.Empty;
P4_HasDryRunClearance = overrideDryRunClearances || T4_FillLevel != FillState.Empty;
P5_HasDryRunClearance = overrideDryRunClearances || T5_FillLevel != FillState.Empty;
P6_HasDryRunClearance = true;
}
public bool Valve_IsClosed(int n) => Sensors.GetDS(n);
public bool Valve_IsOpen(int n) => !Valve_IsClosed(n);
public bool Valve_WantOpen(int n) => Actuators.GetV(n);
public bool Valve_WantClosed(int n) => !Valve_WantOpen(n);
private record struct ValveStates(int[] Open, int[] Closed) {
public readonly ValveStateMask ToMask() => new(Open.Aggregate(0L, (m, n) => m | (1L << (n - 1))), Open.Concat(Closed).Aggregate(0L, (m, n) => m | (1L << (n - 1))));
public static ValveStates operator +(ValveStates a, ValveStates b) {
return new([.. a.Open, .. b.Open], [.. a.Closed, .. b.Closed]);
}
}
private record struct ValveStateMask(long States, long Mask) {
public static ValveStateMask[] From(IEnumerable<ValveStates> states) => [.. states.Select(s => s.ToMask())];
public static bool operator ==(long a, ValveStateMask b) => (a & b.Mask) == b.States;
public static bool operator >=(long a, ValveStateMask b) => (a & b.States) == b.States;
public static bool operator !=(long a, ValveStateMask b) => !(a == b);
public static bool operator <=(long a, ValveStateMask b) => !(a >= b);
}
private static bool HasSinglePathClearance(PamhagenState state, IEnumerable<ValveStateMask> masks) {
return masks.Any(m => state.Valves == m && ~state.PressureSensors == m);
}
private static bool HasAnyClearance(PamhagenState state, IEnumerable<ValveStateMask> masks) {
return masks.Any(m => state.Valves >= m && ~state.PressureSensors >= m);
}
private static bool HasPathClearance(PamhagenState state, (ValveStateMask[] Press1, ValveStateMask[] Press2, ValveStateMask[][] Tanks) masks) {
return
(HasSinglePathClearance(state, masks.Press1) && state.Press1_HasClearance) ||
(HasSinglePathClearance(state, masks.Press2) && state.Press2_HasClearance) ||
(HasSinglePathClearance(state, masks.Tanks[0]) && state.T1_FillLevel != FillState.Full) ||
(HasSinglePathClearance(state, masks.Tanks[1]) && state.T2_FillLevel != FillState.Full) ||
(HasSinglePathClearance(state, masks.Tanks[2]) && state.T3_FillLevel != FillState.Full) ||
(HasSinglePathClearance(state, masks.Tanks[3]) && state.T4_FillLevel != FillState.Full) ||
(HasSinglePathClearance(state, masks.Tanks[4]) && state.T5_FillLevel != FillState.Full) ||
(HasSinglePathClearance(state, masks.Tanks[5]) && state.T6_FillLevel != FillState.Full) ||
(HasSinglePathClearance(state, masks.Tanks[6]) && state.T7_FillLevel != FillState.Full) ||
(HasSinglePathClearance(state, masks.Tanks[7]) && state.T8_FillLevel != FillState.Full) ||
(HasSinglePathClearance(state, masks.Tanks[8]) && state.T9_FillLevel != FillState.Full) ||
false;
}
private static bool HasPathClearance(PamhagenState state, (ValveStateMask[] Press1, ValveStateMask[] Press2) masks) {
return
(HasSinglePathClearance(state, masks.Press1) && state.Press1_HasClearance) ||
(HasSinglePathClearance(state, masks.Press2) && state.Press2_HasClearance) ||
false;
}
private static readonly (
(ValveStateMask[] Press1, ValveStateMask[] Press2, ValveStateMask[][] Tanks) MP1,
(ValveStateMask[] Press1, ValveStateMask[] Press2, ValveStateMask[][] Tanks) MP2,
(ValveStateMask[] Press1, ValveStateMask[] Press2, ValveStateMask[][] Tanks) P12,
(ValveStateMask[] Press1, ValveStateMask[] Press2, ValveStateMask[][] Tanks) P3,
(ValveStateMask[] Press1, ValveStateMask[] Press2, ValveStateMask[][] Tanks) P4,
(ValveStateMask[] Press1, ValveStateMask[] Press2, ValveStateMask[][] Tanks) P5,
(ValveStateMask[] Press1, ValveStateMask[] Press2) P6
) Paths;
static PamhagenState() {
ValveStates[] wt_aa = [
new([12], [15, 16, 20]), // bypass
new([15, 18, 16], [11, 12, 14, 17, 19, 20]), // WT1
new([15, 17, 20], [11, 12, 18, 19, 21]), // WT2
new([15, 18, 19, 20], [11, 12, 14, 16, 17, 21]), // WT1+WT2
];
ValveStates[] wt_ab = [
new([15, 18, 14], [11, 12, 13, 16, 17, 19, 21]), // WT1
new([15, 17, 21], [11, 12, 13, 18, 19, 20]), // WT2
new([15, 18, 19, 21], [11, 12, 13, 14, 16, 17, 20]), // WT1+WT2
];
ValveStates[] wt_ba = [
new([11, 18, 16], [15, 12, 13, 14, 17, 19, 20]), // WT1
new([11, 17, 20], [15, 12, 13, 18, 19, 21]), // WT2
new([11, 18, 19, 20], [15, 12, 13, 14, 16, 17, 21]), // WT1+WT2
];
ValveStates[] wt_bb = [
new([13], [11, 14, 21]), // bypass
new([11, 18, 14], [15, 13, 16, 17, 19, 21]), // WT1
new([11, 17, 21], [15, 13, 18, 19, 20]), // WT2
new([11, 18, 19, 21], [15, 13, 14, 16, 17, 20]), // WT1+WT2
];
ValveStates[][] tanks = [
[new([22], [24, 26, 28]), new([23], [25, 27, 29])],
[new([24], [22, 26, 28]), new([25], [23, 27, 29])],
[new([26], [22, 24, 28]), new([27], [23, 25, 29])],
[new([28, 30], [22, 24, 26, 32, 34]), new([29, 31], [23, 25, 27, 33, 35])],
[new([28, 32], [22, 24, 26, 30, 34]), new([29, 33], [23, 25, 27, 31, 35])],
[new([28, 34, 37], [22, 24, 26, 30, 32, 39, 41, 43]), new([29, 35, 36], [23, 25, 27, 31, 33, 38, 40, 42])],
[new([28, 34, 39], [22, 24, 26, 30, 32, 37, 41, 43]), new([29, 35, 38], [23, 25, 27, 31, 33, 36, 40, 42])],
[new([28, 34, 41], [22, 24, 26, 30, 32, 37, 39, 43]), new([29, 35, 40], [23, 25, 27, 31, 33, 36, 38, 42])],
[new([28, 34, 43], [22, 24, 26, 30, 32, 37, 39, 41]), new([29, 35, 42], [23, 25, 27, 31, 33, 36, 38, 40])],
];
ValveStates[] mp1_pr1 = [
new([1, 6, 51], [3, 4, 5, 50, 52, 53, 55]),
new([4, 8, 52], [1, 2, 7, 50, 51, 53, 56]),
];
ValveStates[] mp1_pr2 = [
new([1, 6, 55], [3, 4, 5, 51, 54, 56, 57]),
new([4, 8, 56], [1, 2, 7, 52, 54, 55, 57]),
];
ValveStates mp1_wt_a = new([1, 5], [3, 4, 6]);
ValveStates mp1_wt_b = new([4, 7, 9], [1, 2, 8, 10]);
ValveStates[] mp1_a = [.. wt_aa.Select(m => mp1_wt_a + m), .. wt_ba.Select(m => mp1_wt_b + m)];
ValveStates[] mp1_b = [.. wt_ab.Select(m => mp1_wt_a + m), .. wt_bb.Select(m => mp1_wt_b + m)];
ValveStates[][] mp1_t = [.. tanks.Select<ValveStates[], ValveStates[]>(t => [.. mp1_a.Select(m => m + t[0]), .. mp1_b.Select(m => m + t[1])])];
ValveStates[] mp2_pr1 = [
new([2, 8, 52], [3, 4, 7, 50, 51, 53, 56]),
new([3, 6, 51], [1, 2, 5, 50, 52, 53, 55]),
];
ValveStates[] mp2_pr2 = [
new([2, 8, 56], [3, 4, 7, 52, 54, 55, 57]),
new([3, 6, 55], [2, 1, 5, 51, 54, 56, 57]),
];
ValveStates mp2_wt_a = new([3, 5], [1, 2, 6]);
ValveStates mp2_wt_b = new([2, 7, 9], [3, 4, 8, 10]);
ValveStates[] mp2_a = [.. wt_aa.Select(m => mp2_wt_a + m), .. wt_ba.Select(m => mp2_wt_b + m)];
ValveStates[] mp2_b = [.. wt_ab.Select(m => mp2_wt_a + m), .. wt_bb.Select(m => mp2_wt_b + m)];
ValveStates[][] mp2_t = [.. tanks.Select<ValveStates[], ValveStates[]>(t => [.. mp2_a.Select(m => m + t[0]), .. mp2_b.Select(m => m + t[1])])];
ValveStates[] p12_pr1 = [new([44, 46, 49, 50], [10, 45, 47, 48, 54, 51, 52, 53])];
ValveStates[] p12_pr2 = [new([44, 46, 49, 54], [10, 45, 47, 48, 50, 55, 56, 57])];
ValveStates[] p12_b = [new([44, 10], [9, 45, 46])];
ValveStates[][] p12_t = [.. tanks.Select<ValveStates[], ValveStates[]>(t => [.. p12_b.Select(m => m + t[1])])];
ValveStates[] p3_pr1 = [new([45, 46, 49, 50], [10, 44, 47, 48, 51, 52, 53, 54])];
ValveStates[] p3_pr2 = [new([45, 46, 49, 54], [10, 44, 47, 48, 50, 55, 56, 57])];
ValveStates[] p3_b = [new([45, 10], [9, 44, 46])];
ValveStates[][] p3_t = [.. tanks.Select<ValveStates[], ValveStates[]>(t => [.. p3_b.Select(m => m + t[1])])];
ValveStates[] p4_pr1 = [new([47, 49, 50], [46, 48, 51, 52, 53, 54])];
ValveStates[] p4_pr2 = [new([47, 49, 54], [46, 48, 50, 55, 56, 57])];
ValveStates[] p4_b = [new([47, 46, 10], [9, 44, 45, 48, 49])];
ValveStates[][] p4_t = [.. tanks.Select<ValveStates[], ValveStates[]>(t => [.. p4_b.Select(m => m + t[1])])];
ValveStates[] p5_pr1 = [new([48, 49, 50], [46, 47, 51, 52, 53, 54])];
ValveStates[] p5_pr2 = [new([48, 49, 54], [46, 47, 50, 55, 56, 57])];
ValveStates[] p5_b = [new([48, 46, 10], [9, 44, 45, 47, 49])];
ValveStates[][] p5_t = [.. tanks.Select<ValveStates[], ValveStates[]>(t => [.. p5_b.Select(m => m + t[1])])];
ValveStates[] p6_pr1 = [new([53], [50, 51, 52, 57])];
ValveStates[] p6_pr2 = [new([57], [53, 54, 55, 56])];
Paths = (
(ValveStateMask.From(mp1_pr1), ValveStateMask.From(mp1_pr2), [.. mp1_t.Select(t => ValveStateMask.From(t))]),
(ValveStateMask.From(mp2_pr1), ValveStateMask.From(mp2_pr2), [.. mp2_t.Select(t => ValveStateMask.From(t))]),
(ValveStateMask.From(p12_pr1), ValveStateMask.From(p12_pr2), [.. p12_t.Select(t => ValveStateMask.From(t))]),
(ValveStateMask.From(p3_pr1), ValveStateMask.From(p3_pr2), [.. p3_t.Select(t => ValveStateMask.From(t))]),
(ValveStateMask.From(p4_pr1), ValveStateMask.From(p4_pr2), [.. p4_t.Select(t => ValveStateMask.From(t))]),
(ValveStateMask.From(p5_pr1), ValveStateMask.From(p5_pr2), [.. p5_t.Select(t => ValveStateMask.From(t))]),
(ValveStateMask.From(p6_pr1), ValveStateMask.From(p6_pr2))
);
}
}
}