Move path logic into Path class
Test / Run tests (push) Successful in 43s

This commit is contained in:
2026-10-15 23:45:32 +02:00
parent c005378368
commit d8c2292b24
9 changed files with 345 additions and 218 deletions
+89 -17
View File
@@ -10,6 +10,14 @@ namespace PamhagenSysCtrl.Helpers.Plc {
public bool OverridePathClearances { get => _overridePathClearnaces; set { _overridePathClearnaces = value; UpdateInterlocks(); } }
public bool OverrideDryRunClearances { get => _overrideDryRunClearances; set { _overrideDryRunClearances = value; UpdateInterlocks(); } }
internal bool MP1_WantActive { get; private set; }
internal bool MP2_WantActive { get; private set; }
internal bool P12_WantActive { get; private set; }
internal bool P3_WantActive { get; private set; }
internal bool P4_WantActive { get; private set; }
internal bool P5_WantActive { get; private set; }
internal bool P6_WantActive { get; private set; }
protected LegacyPamhagenPlc Plc;
public PamhagenState State { get; private set; }
@@ -17,9 +25,9 @@ namespace PamhagenSysCtrl.Helpers.Plc {
public T1.Status? LastStatus => Plc?.LastStatus;
public T1Error? LastError => Plc?.LastError;
protected LegacyPamhagenPlc.ActuatorStates Actuators;
protected LegacyPamhagenPlc.ActuatorStates LastActuators;
protected LegacyPamhagenPlc.SensorStates Sensors = new(default, default, default, default, default, default, default, [
internal LegacyPamhagenPlc.ActuatorStates Actuators;
internal LegacyPamhagenPlc.ActuatorStates LastActuators;
internal LegacyPamhagenPlc.SensorStates Sensors = new(default, default, default, default, default, default, default, [
FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown
], default);
@@ -37,7 +45,7 @@ namespace PamhagenSysCtrl.Helpers.Plc {
Actuators = await Plc.ReadOutputs();
LastActuators = Actuators;
Sensors = await Plc.ReadInputs();
State = new(Sensors, () => Actuators, OverridePathClearances, OverrideDryRunClearances);
State = new(Sensors, this);
}
public async Task Shutdown() {
@@ -52,7 +60,7 @@ namespace PamhagenSysCtrl.Helpers.Plc {
await Plc.WriteOutputs(LastActuators);
}
Sensors = await Plc.ReadInputs();
State = new(Sensors, () => Actuators, OverridePathClearances, OverrideDryRunClearances);
State = new(Sensors, this);
UpdateInterlocks();
}
@@ -73,6 +81,19 @@ namespace PamhagenSysCtrl.Helpers.Plc {
if (State.MP1_IsActive && !State.MP1_HasClearance) StopMP1();
if (State.MP2_IsActive && !State.MP2_HasClearance) StopMP2();
if (State.P12_IsActive && !State.P12_HasClearance) DisableP12();
if (State.P3_IsActive && !State.P3_HasClearance) DisableP3();
if (State.P4_IsActive && !State.P4_HasClearance) DisableP4();
if (State.P5_IsActive && !State.P5_HasClearance) DisableP5();
if (State.P6_IsActive && !State.P6_HasClearance) DisableP6();
if (MP1_WantActive && State.MP1_HasClearance) StartMP1Forward();
if (MP2_WantActive && State.MP2_HasClearance) StartMP2Forward();
if (P12_WantActive && State.P12_HasClearance) EnableP12();
if (P3_WantActive && State.P3_HasClearance) EnableP3();
if (P4_WantActive && State.P4_HasClearance) EnableP4();
if (P5_WantActive && State.P5_HasClearance) EnableP5();
if (P6_WantActive && State.P6_HasClearance) EnableP6();
if (!ManualControlMode) {
if (State.MP1_State == MotorState.Forward && State.MW1_FillLevel >= FillState.Half && !State.MW1_Stirrer_IsActive) {
@@ -86,12 +107,6 @@ namespace PamhagenSysCtrl.Helpers.Plc {
StopStirrerMW2();
}
}
if (State.P12_IsActive && !State.P12_HasClearance) DisableP12();
if (State.P3_IsActive && !State.P3_HasClearance) DisableP3();
if (State.P4_IsActive && !State.P4_HasClearance) DisableP4();
if (State.P5_IsActive && !State.P5_HasClearance) DisableP5();
if (State.P6_IsActive && !State.P6_HasClearance) DisableP6();
}
public void OpenV(int n) => SetV(n, true);
@@ -128,6 +143,7 @@ namespace PamhagenSysCtrl.Helpers.Plc {
public void StopRebler() {
if (!ManualControlMode) StopTroughAuger();
Actuators.Rebler = false;
if (!ManualControlMode) StopAuger1();
}
public void StartStirrerMW1() {
@@ -154,6 +170,8 @@ namespace PamhagenSysCtrl.Helpers.Plc {
public void StopAuger1() {
if (!ManualControlMode && State.Rebler_IsActive) throw new NoClearanceException("Rebler aktiv");
Actuators.Auger1 = false;
if (!ManualControlMode && !State.PressCrossAuger_IsActive)
StopAuger2();
}
public void StartAuger2() {
@@ -165,6 +183,8 @@ namespace PamhagenSysCtrl.Helpers.Plc {
if (!ManualControlMode && State.PressCrossAuger_IsActive) throw new NoClearanceException("Presse-Querschnecke aktiv");
if (!ManualControlMode && State.Auger1_IsActive) throw new NoClearanceException("Förderschnecke 1 aktiv");
Actuators.Auger2 = false;
if (!ManualControlMode)
StopAuger3();
}
public void StartAuger3() {
@@ -185,83 +205,135 @@ namespace PamhagenSysCtrl.Helpers.Plc {
}
public void StartMP1Forward() {
if (!State.MP1_HasClearance) throw new NoClearanceException("MP1 hat keine Freigabe");
if (!State.MP1_HasClearance) {
if (!State.MP1_HasDryRunClearance || !State.MP1_WantsPathClearance)
throw new NoClearanceException("MP1 hat keine Freigabe");
MP1_WantActive = true;
return;
}
MP1_WantActive = false;
if (Actuators.MP1 == MotorState.Backward) throw new NoClearanceException("MP1 läuft bereits (zurück)");
Actuators.MP1 = MotorState.Forward;
if (State.MW1_FillLevel >= FillState.Half) StartStirrerMW1();
}
public void StartMP1Backward() {
MP1_WantActive = false;
if (!State.MP1_HasClearance) throw new NoClearanceException("MP1 hat keine Freigabe");
if (Actuators.MP1 == MotorState.Forward) throw new NoClearanceException("MP1 läuft bereits (vor)");
Actuators.MP1 = MotorState.Backward;
}
public void StopMP1() {
MP1_WantActive = false;
Actuators.MP1 = MotorState.Halt;
if (!ManualControlMode) StopStirrerMW1();
}
public void StartMP2Forward() {
if (!State.MP2_HasClearance) throw new NoClearanceException("MP2 hat keine Freigabe");
if (!State.MP2_HasClearance) {
if (!State.MP2_HasDryRunClearance || !State.MP2_WantsPathClearance)
throw new NoClearanceException("MP2 hat keine Freigabe");
MP2_WantActive = true;
return;
}
MP2_WantActive = false;
if (Actuators.MP2 == MotorState.Backward) throw new NoClearanceException("MP2 läuft bereits (zurück)");
Actuators.MP2 = MotorState.Forward;
if (State.MW2_FillLevel >= FillState.Half) StartStirrerMW2();
}
public void StartMP2Backward() {
MP2_WantActive = false;
if (!State.MP2_HasClearance) throw new NoClearanceException("MP2 hat keine Freigabe");
if (Actuators.MP2 == MotorState.Forward) throw new NoClearanceException("MP2 läuft bereits (vor)");
Actuators.MP2 = MotorState.Backward;
}
public void StopMP2() {
MP2_WantActive = false;
Actuators.MP2 = MotorState.Halt;
if (!ManualControlMode) StopStirrerMW1();
}
public void EnableP12() {
if (!State.P12_HasClearance) {
if (!State.P12_HasDryRunClearance || !State.P12_WantsPathClearance)
throw new NoClearanceException("P1/2 hat keine Freigabe");
P12_WantActive = true;
return;
}
P12_WantActive = false;
if (!State.P12_HasClearance) throw new NoClearanceException("P1/2 hat keine Freigabe");
Actuators.P12 = MotorState.Forward;
}
public void DisableP12() {
P12_WantActive = false;
Actuators.P12 = MotorState.Halt;
}
public void EnableP3() {
if (!State.P3_HasClearance) throw new NoClearanceException("P3 hat keine Freigabe");
if (!State.P3_HasClearance) {
if (!State.P3_HasDryRunClearance || !State.P3_WantsPathClearance)
throw new NoClearanceException("P3 hat keine Freigabe");
P3_WantActive = true;
return;
}
P3_WantActive = false;
Actuators.P3 = MotorState.Forward;
}
public void DisableP3() {
P3_WantActive = false;
Actuators.P3 = MotorState.Halt;
}
public void EnableP4() {
if (!State.P4_HasClearance) throw new NoClearanceException("P4 hat keine Freigabe");
if (!State.P4_HasClearance) {
if (!State.P4_HasDryRunClearance || !State.P4_WantsPathClearance)
throw new NoClearanceException("P4 hat keine Freigabe");
P4_WantActive = true;
return;
}
P4_WantActive = false;
Actuators.P4 = MotorState.Forward;
}
public void DisableP4() {
P4_WantActive = false;
Actuators.P4 = MotorState.Halt;
}
public void EnableP5() {
if (!State.P5_HasClearance) throw new NoClearanceException("P5 hat keine Freigabe");
if (!State.P5_HasClearance) {
if (!State.P5_HasDryRunClearance || !State.P5_WantsPathClearance)
throw new NoClearanceException("P5 hat keine Freigabe");
P5_WantActive = true;
return;
}
P5_WantActive = false;
Actuators.P5 = MotorState.Forward;
}
public void DisableP5() {
P5_WantActive = false;
Actuators.P5 = MotorState.Halt;
}
public void EnableP6() {
if (!State.P6_HasClearance) throw new NoClearanceException("P6 hat keine Freigabe");
if (!State.P6_HasClearance) {
if (!State.P6_HasDryRunClearance || !State.P6_WantsPathClearance)
throw new NoClearanceException("P6 hat keine Freigabe");
P6_WantActive = true;
return;
}
P6_WantActive = false;
Actuators.P6 = MotorState.Forward;
}
public void DisableP6() {
P6_WantActive = false;
Actuators.P6 = MotorState.Halt;
}
}
+107 -33
View File
@@ -2,8 +2,12 @@ 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(); }
private readonly PamhagenPlc Plc;
private LegacyPamhagenPlc.ActuatorStates Actuators => Plc == null ? default : Plc.Actuators;
public bool ManualControlMode => Plc != null && Plc.ManualControlMode;
public bool OverridePathClearances => Plc != null && Plc.OverridePathClearances;
public bool OverrideDryRunClearances => Plc != null && Plc.OverrideDryRunClearances;
public long Valves => Actuators.Valves;
public long PressureSensors => Sensors.PressureSensors;
@@ -57,20 +61,51 @@ namespace PamhagenSysCtrl.Helpers.Plc {
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; }
private readonly bool MP1_PathClearance;
private readonly bool MP2_PathClearance;
private readonly bool P12_PathClearance;
private readonly bool P3_PathClearance;
private readonly bool P4_PathClearance;
private readonly bool P5_PathClearance;
private readonly bool P6_PathClearance;
private readonly bool MP1_DryRunClearance;
private readonly bool MP2_DryRunClearance;
private readonly bool P12_DryRunClearance;
private readonly bool P3_DryRunClearance;
private readonly bool P4_DryRunClearance;
private readonly bool P5_DryRunClearance;
private readonly bool P6_DryRunClearance;
public readonly bool MP1_WantsPathClearance { get; }
public readonly bool MP2_WantsPathClearance { get; }
public readonly bool P12_WantsPathClearance { get; }
public readonly bool P3_WantsPathClearance { get; }
public readonly bool P4_WantsPathClearance { get; }
public readonly bool P5_WantsPathClearance { get; }
public readonly bool P6_WantsPathClearance { get; }
public bool MP1_WantActive => Plc.MP1_WantActive;
public bool MP2_WantActive => Plc.MP2_WantActive;
public bool P12_WantActive => Plc.P12_WantActive;
public bool P3_WantActive => Plc.P3_WantActive;
public bool P4_WantActive => Plc.P4_WantActive;
public bool P5_WantActive => Plc.P5_WantActive;
public bool P6_WantActive => Plc.P6_WantActive;
public bool MP1_HasPathClearance => OverridePathClearances || MP1_PathClearance;
public bool MP2_HasPathClearance => OverridePathClearances || MP2_PathClearance;
public bool P12_HasPathClearance => OverridePathClearances || P12_PathClearance;
public bool P3_HasPathClearance => OverridePathClearances || P3_PathClearance;
public bool P4_HasPathClearance => OverridePathClearances || P4_PathClearance;
public bool P5_HasPathClearance => OverridePathClearances || P5_PathClearance;
public bool P6_HasPathClearance => OverridePathClearances || P6_PathClearance;
public bool MP1_HasDryRunClearance => OverrideDryRunClearances || MP1_DryRunClearance;
public bool MP2_HasDryRunClearance => OverrideDryRunClearances || MP2_DryRunClearance;
public bool P12_HasDryRunClearance => OverrideDryRunClearances || P12_DryRunClearance;
public bool P3_HasDryRunClearance => OverrideDryRunClearances || P3_DryRunClearance;
public bool P4_HasDryRunClearance => OverrideDryRunClearances || P4_DryRunClearance;
public bool P5_HasDryRunClearance => OverrideDryRunClearances || P5_DryRunClearance;
public bool P6_HasDryRunClearance => OverrideDryRunClearances || P6_DryRunClearance;
public bool MP1_HasClearance => MP1_HasDryRunClearance && MP1_HasPathClearance;
public bool MP2_HasClearance => MP2_HasDryRunClearance && MP2_HasPathClearance;
public bool P12_HasClearance => P12_HasDryRunClearance && P12_HasPathClearance;
@@ -79,25 +114,33 @@ namespace PamhagenSysCtrl.Helpers.Plc {
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) {
public PamhagenState(LegacyPamhagenPlc.SensorStates sensors, PamhagenPlc plc) {
Sensors = sensors;
ActuatorsCb = actuatorsCb;
Plc = plc;
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_WantsPathClearance = WantsPathClearnace(this, Paths.MP1);
MP2_WantsPathClearance = WantsPathClearnace(this, Paths.MP2);
P12_WantsPathClearance = WantsPathClearnace(this, Paths.P12);
P3_WantsPathClearance = WantsPathClearnace(this, Paths.P3);
P4_WantsPathClearance = WantsPathClearnace(this, Paths.P4);
P5_WantsPathClearance = WantsPathClearnace(this, Paths.P5);
P6_WantsPathClearance = WantsPathClearnace(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;
MP1_PathClearance = HasPathClearance(this, Paths.MP1);
MP2_PathClearance = HasPathClearance(this, Paths.MP2);
P12_PathClearance = HasPathClearance(this, Paths.P12);
P3_PathClearance = HasPathClearance(this, Paths.P3);
P4_PathClearance = HasPathClearance(this, Paths.P4);
P5_PathClearance = HasPathClearance(this, Paths.P5);
P6_PathClearance = HasPathClearance(this, Paths.P6);
MP1_DryRunClearance = MW1_FillLevel != FillState.Empty;
MP2_DryRunClearance = MW2_FillLevel != FillState.Empty;
P12_DryRunClearance = T1_FillLevel != FillState.Empty || T2_FillLevel != FillState.Empty;
P3_DryRunClearance = T3_FillLevel != FillState.Empty;
P4_DryRunClearance = T4_FillLevel != FillState.Empty;
P5_DryRunClearance = T5_FillLevel != FillState.Empty;
P6_DryRunClearance = true;
}
public bool Valve_IsClosed(int n) => Sensors.GetDS(n);
@@ -124,10 +167,18 @@ namespace PamhagenSysCtrl.Helpers.Plc {
return masks.Any(m => state.Valves == m && ~state.PressureSensors == m);
}
private static bool HasAnyClearance(PamhagenState state, IEnumerable<ValveStateMask> masks) {
private static bool HasAnyPathClearance(PamhagenState state, IEnumerable<ValveStateMask> masks) {
return masks.Any(m => state.Valves >= m && ~state.PressureSensors >= m);
}
private static bool WantsSinglePathClearance(PamhagenState state, IEnumerable<ValveStateMask> masks) {
return masks.Any(m => state.Valves == m);
}
private static bool WantsAnyPathClearance(PamhagenState state, IEnumerable<ValveStateMask> masks) {
return masks.Any(m => state.Valves >= m);
}
private static bool HasPathClearance(PamhagenState state, (ValveStateMask[] Press1, ValveStateMask[] Press2, ValveStateMask[][] Tanks) masks) {
return
(HasSinglePathClearance(state, masks.Press1) && state.Press1_HasClearance) ||
@@ -144,6 +195,22 @@ namespace PamhagenSysCtrl.Helpers.Plc {
false;
}
private static bool WantsPathClearnace(PamhagenState state, (ValveStateMask[] Press1, ValveStateMask[] Press2, ValveStateMask[][] Tanks) masks) {
return
(WantsSinglePathClearance(state, masks.Press1) && state.Press1_HasClearance) ||
(WantsSinglePathClearance(state, masks.Press2) && state.Press2_HasClearance) ||
(WantsSinglePathClearance(state, masks.Tanks[0]) && state.T1_FillLevel != FillState.Full) ||
(WantsSinglePathClearance(state, masks.Tanks[1]) && state.T2_FillLevel != FillState.Full) ||
(WantsSinglePathClearance(state, masks.Tanks[2]) && state.T3_FillLevel != FillState.Full) ||
(WantsSinglePathClearance(state, masks.Tanks[3]) && state.T4_FillLevel != FillState.Full) ||
(WantsSinglePathClearance(state, masks.Tanks[4]) && state.T5_FillLevel != FillState.Full) ||
(WantsSinglePathClearance(state, masks.Tanks[5]) && state.T6_FillLevel != FillState.Full) ||
(WantsSinglePathClearance(state, masks.Tanks[6]) && state.T7_FillLevel != FillState.Full) ||
(WantsSinglePathClearance(state, masks.Tanks[7]) && state.T8_FillLevel != FillState.Full) ||
(WantsSinglePathClearance(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) ||
@@ -151,6 +218,13 @@ namespace PamhagenSysCtrl.Helpers.Plc {
false;
}
private static bool WantsPathClearnace(PamhagenState state, (ValveStateMask[] Press1, ValveStateMask[] Press2) masks) {
return
(WantsSinglePathClearance(state, masks.Press1) && state.Press1_HasClearance) ||
(WantsSinglePathClearance(state, masks.Press2) && state.Press2_HasClearance) ||
false;
}
private static readonly (
(ValveStateMask[] Press1, ValveStateMask[] Press2, ValveStateMask[][] Tanks) MP1,
(ValveStateMask[] Press1, ValveStateMask[] Press2, ValveStateMask[][] Tanks) MP2,