using System.IO; using System.Windows; namespace PamhagenSysCtrl.Helpers { public class PamhagenPlant : IDisposable { protected PamhagenPlc? Plc; protected bool IsRunning = true; protected Thread? BackgroundThread; public event EventHandler? Update; protected bool ActuatorsChanged = false; protected PamhagenPlc.ActuatorStates Actuators; 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 WantValveClosed(int n) => GetV(n); public bool WantValveOpen(int n) => !WantValveClosed(n); public bool IsA1Selected => Sensors.TroughSelector == 1; public bool IsA2Selected => Sensors.TroughSelector == 2; public FillState FillLevelA1 => Sensors.FillingLevels.A1; public FillState FillLevelA2 => Sensors.FillingLevels.A2; public FillState FillLevelPress1 => Sensors.FillingLevels.Press1; public FillState FillLevelPress2 => Sensors.FillingLevels.Press2; public FillState FillLevelT1 => Sensors.FillingLevelTanks[0]; public FillState FillLevelT2 => Sensors.FillingLevelTanks[1]; public FillState FillLevelT3 => Sensors.FillingLevelTanks[2]; public FillState FillLevelT4 => Sensors.FillingLevelTanks[3]; public FillState FillLevelT5 => Sensors.FillingLevelTanks[4]; public FillState FillLevelT6 => Sensors.FillingLevelTanks[5]; public FillState FillLevelT7 => Sensors.FillingLevelTanks[6]; public FillState FillLevelT8 => Sensors.FillingLevelTanks[7]; public FillState FillLevelT9 => Sensors.FillingLevelTanks[8]; public double GradationOe => Sensors.GradationOe; public MotorState PresseQuerSchnecke => Sensors.PresseQuerSchnecke; public bool WantA1Selected => Actuators.GetV(58); public bool WantA2Selected => !Actuators.GetV(58); public bool IsTroughAugerActive => Actuators.TroughAuger; public bool IsReblerActive => Actuators.Rebler; public bool IsStirrerA1Active => Actuators.StirrerA1; public bool IsStirrerA2Active => Actuators.StirrerA2; public bool IsBeltActive => Actuators.Belt; public bool IsAuger1Active => Actuators.Auger1; public bool IsAuger2Active => Actuators.Auger2; public bool IsGradationPumpActive => Actuators.GradationPump; public PamhagenPlant(string portName) { Plc = new(portName); } protected virtual void RaiseUpdateEvent(EventArgs evt) { App.MainDispatcher.BeginInvoke(() => Update?.Invoke(this, evt)); } public async Task StartThread() { if (Plc == null) throw new Exception(); await Plc.TestConnection(); Actuators = await Plc.ReadOutputs(); BackgroundThread = new Thread(new ParameterizedThreadStart(BackgroundLoop)); BackgroundThread.Start(); } public void Dispose() { IsRunning = false; BackgroundThread?.Interrupt(); BackgroundThread?.Join(); Plc?.Dispose(); GC.SuppressFinalize(this); } protected async void BackgroundLoop(object? parameters) { while (IsRunning && Plc != null) { try { if (ActuatorsChanged) { await Plc.WriteOutputs(Actuators); ActuatorsChanged = false; } Sensors = await Plc.ReadInputs(); RaiseUpdateEvent(EventArgs.Empty); } catch (ThreadInterruptedException) { // ignore } catch (Exception exc) { var str = "Bei der SPS ist ein Fehler aufgetreten:\n\n" + exc.Message; if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message; MessageBox.Show(str, "SPS-Fehler", MessageBoxButton.OK, MessageBoxImage.Error); if (exc is TimeoutException or IOException) { Plc?.Dispose(); Plc = null; IsRunning = false; } } } try { if (Plc != null) await Plc.WriteOutputs(new()); } catch (Exception exc) { var str = "Beim Schließen der Verbindung zur SPS ist ein Fehler aufgetreten:\n\n" + exc.Message; if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message; MessageBox.Show(str, "SPS-Fehler", MessageBoxButton.OK, MessageBoxImage.Error); } } public void CommitChanges() { ActuatorsChanged = true; } public MotorState MP1 { get => Actuators.MP1; set => Actuators.MP1 = (value != MotorState.Halt && value != MotorState.Forward && value != MotorState.Backward) ? throw new ArgumentException("Invalid state for MP1") : value; } public MotorState MP2 { get => Actuators.MP2; set => Actuators.MP2 = (value != MotorState.Halt && value != MotorState.Forward && value != MotorState.Backward) ? throw new ArgumentException("Invalid state for MP2") : value; } public MotorState P12 { get => Actuators.P12; set => Actuators.P12 = (value != MotorState.Halt && value != MotorState.Forward) ? throw new ArgumentException("Invalid state for P12") : value; } public MotorState P3 { get => Actuators.P3; set => Actuators.P3 = (value != MotorState.Halt && value != MotorState.Forward) ? throw new ArgumentException("Invalid state for P3") : value; } public MotorState P4 { get => Actuators.P4; set => Actuators.P4 = (value != MotorState.Halt && value != MotorState.Forward) ? throw new ArgumentException("Invalid state for P4") : value; } public MotorState P5 { get => Actuators.P5; set => Actuators.P5 = (value != MotorState.Halt && value != MotorState.Forward) ? throw new ArgumentException("Invalid state for P5") : value; } public MotorState P6 { get => Actuators.P6; set => Actuators.P6 = (value != MotorState.Halt && value != MotorState.Forward) ? throw new ArgumentException("Invalid state for P6") : value; } public void OpenV(int n) => SetV(n, false); public void CloseV(int n) => SetV(n, true); public void SetV(int n, bool val) { if (n < 1 || n > 60) throw new ArgumentException("Invalid value for n: 1 <= #V <= 60"); Actuators.SetV(n, val); } public void SelectA1() { Actuators.SetV(58, true); //if (IsReblerActive) Actuators.StirrerA1 = true; } public void SelectA2() { Actuators.SetV(58, false); //if (IsReblerActive) Actuators.StirrerA2 = true; } public void StartRebler() { Actuators.Rebler = true; } public void StopRebler() { Actuators.Rebler = false; } } }