Files
pamhagen-sysctrl/PamhagenSysCtrl/Helpers/Plc/PamhagenPlc.cs
T
2026-10-16 10:26:17 +02:00

348 lines
13 KiB
C#

using System.IO.Ports;
namespace PamhagenSysCtrl.Helpers.Plc {
public class PamhagenPlc : IDisposable {
private bool _manualControlMode = false;
private bool _overridePathClearnaces = false;
private bool _overrideDryRunClearances = false;
public bool ManualControlMode { get => _manualControlMode; set { _manualControlMode = value; UpdateInterlocks(); } }
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; }
public string? ComPort => Plc?.ComPort;
public T1.Status? LastStatus => Plc?.LastStatus;
public T1Error? LastError => Plc?.LastError;
internal LegacyPamhagenPlc.ActuatorStates Actuators;
internal LegacyPamhagenPlc.ActuatorStates LastActuators;
internal LegacyPamhagenPlc.SensorStates[] LastSensors = [];
internal readonly int ConsiderLastSensorsOpen;
internal readonly int ConsiderLastSensorsClose;
public PamhagenPlc(string comPort, int sensorDelayOpenMs, int sensorDelayCloseMs, Parity parity = Parity.Odd) {
Plc = new(comPort, parity);
ConsiderLastSensorsOpen = sensorDelayOpenMs / 125 + 1;
ConsiderLastSensorsClose = sensorDelayCloseMs / 125 + 1;
LastSensors = new LegacyPamhagenPlc.SensorStates[Math.Max(ConsiderLastSensorsOpen, ConsiderLastSensorsClose)];
}
public void Dispose() {
GC.SuppressFinalize(this);
Plc.Dispose();
}
public async Task Initialize() {
await Plc.TestConnection();
Actuators = await Plc.ReadOutputs();
LastActuators = Actuators;
var sensors = await Plc.ReadInputs();
LastSensors[0] = sensors;
State = new(LastSensors, this);
}
public async Task Shutdown() {
await Plc.WriteOutputs(new() { Valves = Actuators.Valves });
}
public async Task Update() {
UpdateInterlocks();
if (Actuators != LastActuators) {
LastActuators = Actuators;
await Plc.WriteOutputs(LastActuators);
}
var sensors = await Plc.ReadInputs();
Array.Copy(LastSensors, 0, LastSensors, 1, LastSensors.Length - 1);
LastSensors[0] = sensors;
State = new(LastSensors, this);
UpdateInterlocks();
}
protected void UpdateInterlocks() {
if (!ManualControlMode) {
if (State.PressCrossAuger_IsActive && !State.Auger2_IsActive) StartAuger2();
if (State.Rebler_IsActive && !State.Auger1_IsActive) StartAuger1();
if (State.Auger1_IsActive && !State.Auger2_IsActive) StartAuger2();
if (State.Auger2_IsActive && !State.Auger3_IsActive) StartAuger3();
if (State.TroughAuger_IsActive && !State.TroughAuger_HasClearance)
StopTroughAuger();
if (!State.PressCrossAuger_IsActive && !State.Auger1_IsActive) {
StopAuger2();
StopAuger3();
}
}
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) {
StartStirrerMW1();
} else if (State.MW2_Stirrer_IsActive && (!State.MP1_IsActive || State.MW1_FillLevel < FillState.Half)) {
StopStirrerMW1();
}
if (State.MP2_State == MotorState.Forward && State.MW2_FillLevel >= FillState.Half && !State.MW2_Stirrer_IsActive) {
StartStirrerMW2();
} else if (State.MW2_Stirrer_IsActive && (!State.MP2_IsActive || State.MW2_FillLevel < FillState.Half)) {
StopStirrerMW2();
}
}
}
public void OpenV(int n) => SetV(n, true);
public void CloseV(int n) => SetV(n, false);
protected void SetV(int n, bool val) {
if (n < 1 || n > 57) throw new ArgumentException("Invalid value for n: 1 <= #V <= 57");
Actuators.SetV(n, val);
}
public void SelectMW1() {
Actuators.SetV(58, true);
}
public void SelectMW2() {
Actuators.SetV(58, false);
}
public void StartTroughAuger() {
if (!ManualControlMode && !State.TroughAuger_HasClearance)
throw new NoClearanceException("Schnecke Auffangwanne hat keine Freigabe");
Actuators.TroughAuger = true;
}
public void StopTroughAuger() {
Actuators.TroughAuger = false;
}
public void StartRebler() {
Actuators.Rebler = true;
if (!ManualControlMode) StartAuger1();
}
public void StopRebler() {
if (!ManualControlMode) StopTroughAuger();
Actuators.Rebler = false;
if (!ManualControlMode) StopAuger1();
}
public void StartStirrerMW1() {
Actuators.StirrerMW1 = true;
}
public void StopStirrerMW1() {
Actuators.StirrerMW1 = false;
}
public void StartStirrerMW2() {
Actuators.StirrerMW2 = true;
}
public void StopStirrerMW2() {
Actuators.StirrerMW2 = false;
}
public void StartAuger1() {
Actuators.Auger1 = true;
if (!ManualControlMode) StartAuger2();
}
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() {
Actuators.Auger2 = true;
if (!ManualControlMode) StartAuger3();
}
public void StopAuger2() {
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() {
Actuators.Auger3 = true;
}
public void StopAuger3() {
if (!ManualControlMode && State.Auger2_IsActive) throw new NoClearanceException("Förderschnecke 2 aktiv");
Actuators.Auger3 = false;
}
public void StartGradationPump() {
Actuators.GradationPump = true;
}
public void StopGradationPump() {
Actuators.GradationPump = false;
}
public void StartMP1Forward() {
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) {
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) {
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) {
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) {
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) {
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;
}
}
}