From 49bdd5d4c00013cf2dc5b824637563ba918a8ac5 Mon Sep 17 00:00:00 2001 From: Lorenz Stechauner Date: Tue, 13 Oct 2026 12:45:33 +0200 Subject: [PATCH] Refactor PamhagenPlc --- PamhagenSysCtrl/Helpers/PamhagenGraph.cs | 40 +- PamhagenSysCtrl/Helpers/PamhagenPlant.cs | 650 ++---------------- .../LegacyPamhagenPlc.cs} | 20 +- PamhagenSysCtrl/Helpers/Plc/PamhagenPlc.cs | 267 +++++++ PamhagenSysCtrl/Helpers/Plc/PamhagenState.cs | 262 +++++++ PamhagenSysCtrl/Helpers/{ => Plc}/T1.cs | 4 +- PamhagenSysCtrl/Helpers/{ => Plc}/T1Error.cs | 2 +- .../Helpers/{ => Plc}/T1Exception.cs | 2 +- .../Windows/ManualControlWindow.xaml.cs | 189 +++-- .../Windows/PlantSchemeWindow.xaml.cs | 45 +- 10 files changed, 754 insertions(+), 727 deletions(-) rename PamhagenSysCtrl/Helpers/{PamhagenPlc.cs => Plc/LegacyPamhagenPlc.cs} (94%) create mode 100644 PamhagenSysCtrl/Helpers/Plc/PamhagenPlc.cs create mode 100644 PamhagenSysCtrl/Helpers/Plc/PamhagenState.cs rename PamhagenSysCtrl/Helpers/{ => Plc}/T1.cs (97%) rename PamhagenSysCtrl/Helpers/{ => Plc}/T1Error.cs (99%) rename PamhagenSysCtrl/Helpers/{ => Plc}/T1Exception.cs (62%) diff --git a/PamhagenSysCtrl/Helpers/PamhagenGraph.cs b/PamhagenSysCtrl/Helpers/PamhagenGraph.cs index 9d68498..49274d3 100644 --- a/PamhagenSysCtrl/Helpers/PamhagenGraph.cs +++ b/PamhagenSysCtrl/Helpers/PamhagenGraph.cs @@ -66,26 +66,26 @@ namespace PamhagenSysCtrl.Helpers { var pr1X = -15; var pr2X = 23; - var trough = new EntryTrough("Auffangwanne", mw1X + 14, rebY - 20, () => App.Plant?.IsTroughAugerActive ?? false); + var trough = new EntryTrough("Auffangwanne", mw1X + 14, rebY - 20, () => App.Plant?.State.TroughAuger_IsActive ?? false); Nodes.Add(trough); - Auger1 = new EncasedAuger("", rebX - 19, rebY, () => App.Plant?.IsAuger1Active ?? false); - var auger0 = new EncasedAuger("Pressen-\nQuerschnecke", rebX - 39, rebY, () => App.Plant?.IsPresseQuerSchneckeActive ?? false, 180, clickable: false); - Auger3 = new EncasedAuger("", rebX - 23, rebY - 24, () => App.Plant?.IsAuger3Active ?? false, 135); - var belt = new ConveyorBelt("", rebX - 29, rebY - 8, () => App.Plant?.IsAuger2Active ?? false); + Auger1 = new EncasedAuger("", rebX - 19, rebY, () => App.Plant?.State.Auger1_IsActive ?? false); + var auger0 = new EncasedAuger("Pressen-\nQuerschnecke", rebX - 39, rebY, () => App.Plant?.State.PressCrossAuger_IsActive ?? false, 180, clickable: false); + Auger3 = new EncasedAuger("", rebX - 23, rebY - 24, () => App.Plant?.State.Auger3_IsActive ?? false, 135); + var belt = new ConveyorBelt("", rebX - 29, rebY - 8, () => App.Plant?.State.Auger2_IsActive ?? false); Nodes.Add(belt); Nodes.Add(Auger1); Nodes.Add(auger0); Nodes.Add(Auger3); - var rebler = new Rebler("Rebler", rebX, rebY, () => App.Plant?.IsReblerActive ?? false); + var rebler = new Rebler("Rebler", rebX, rebY, () => App.Plant?.State.Rebler_IsActive ?? false); Nodes.Add(rebler); - var switcher = new Switcher("", rebX, rebY + 12, () => App.Plant?.TroughSelector ?? 0, () => App.Plant == null ? 0 : App.Plant.WantMW1Selected ? 1 : 2); + var switcher = new Switcher("", rebX, rebY + 12, () => App.Plant?.State.MW1_IsSelected == true ? 1 : App.Plant?.State.MW2_IsSelected == true ? 2 : 0, () => App.Plant?.State.MW1_WantSelected == true ? 1 : App.Plant?.State.MW2_WantSelected == true ? 2 : 0); Nodes.Add(switcher); - MW1 = new Trough("MW1", mw1X, mpY - 14, () => App.Plant?.FillLevelMW1 ?? FillState.Unknown); - MW2 = new Trough("MW2", mw2X, mpY - 14, () => App.Plant?.FillLevelMW2 ?? FillState.Unknown); - MP1 = new Pump("MP1", mw1X, mpY, () => App.Plant?.MP1 ?? default, () => App.Plant?.MP1HasClearance ?? false, (x) => SelectedPaths.Any(p => p.Hops.Any(h => h.Node == x))); - MP2 = new Pump("MP2", mw2X, mpY, () => App.Plant?.MP2 ?? default, () => App.Plant?.MP2HasClearance ?? false, (x) => SelectedPaths.Any(p => p.Hops.Any(h => h.Node == x))); + MW1 = new Trough("MW1", mw1X, mpY - 14, () => App.Plant?.State.MW1_FillLevel ?? FillState.Unknown); + MW2 = new Trough("MW2", mw2X, mpY - 14, () => App.Plant?.State.MW2_FillLevel ?? FillState.Unknown); + MP1 = new Pump("MP1", mw1X, mpY, () => App.Plant?.State.MP1_State ?? default, () => App.Plant?.State.MP1_HasClearance ?? false, (x) => SelectedPaths.Any(p => p.Hops.Any(h => h.Node == x))); + MP2 = new Pump("MP2", mw2X, mpY, () => App.Plant?.State.MP2_State ?? default, () => App.Plant?.State.MP2_HasClearance ?? false, (x) => SelectedPaths.Any(p => p.Hops.Any(h => h.Node == x))); var x1 = new PipeJoin(mw1X, mpY + 8); var x2 = new PipeJoin(mw2X, mpY + 8); @@ -309,14 +309,14 @@ namespace PamhagenSysCtrl.Helpers { CreateInlet(v43, Tank9); var xp12 = new PipeJoin((t1X + t2X) / 2, tanksY + 16); - P12 = new Pump("P1/2", (t1X + t2X) / 2, tanksY + 22, () => App.Plant?.P12 ?? default, () => App.Plant?.P12HasClearance ?? false, (x) => SelectedPaths.Any(p => p.Hops.Any(h => h.Node == x))); + P12 = new Pump("P1/2", (t1X + t2X) / 2, tanksY + 22, () => App.Plant?.State.P12_State ?? default, () => App.Plant?.State.P12_HasClearance ?? false, (x) => SelectedPaths.Any(p => p.Hops.Any(h => h.Node == x))); CreateOutlet(Tank1, xp12); CreateOutlet(Tank2, xp12); CreatePipe(xp12, P12); - P3 = new Pump("P3", t3X, tanksY + 22, () => App.Plant?.P3 ?? default, () => App.Plant?.P3HasClearance ?? false, (x) => SelectedPaths.Any(p => p.Hops.Any(h => h.Node == x))); - P4 = new Pump("P4", t4X, tanksY + 22, () => App.Plant?.P4 ?? default, () => App.Plant?.P4HasClearance ?? false, (x) => SelectedPaths.Any(p => p.Hops.Any(h => h.Node == x))); - P5 = new Pump("P5", t5X, tanksY + 22, () => App.Plant?.P5 ?? default, () => App.Plant?.P5HasClearance ?? false, (x) => SelectedPaths.Any(p => p.Hops.Any(h => h.Node == x))); + P3 = new Pump("P3", t3X, tanksY + 22, () => App.Plant?.State.P3_State ?? default, () => App.Plant?.State.P3_HasClearance ?? false, (x) => SelectedPaths.Any(p => p.Hops.Any(h => h.Node == x))); + P4 = new Pump("P4", t4X, tanksY + 22, () => App.Plant?.State.P4_State ?? default, () => App.Plant?.State.P4_HasClearance ?? false, (x) => SelectedPaths.Any(p => p.Hops.Any(h => h.Node == x))); + P5 = new Pump("P5", t5X, tanksY + 22, () => App.Plant?.State.P5_State ?? default, () => App.Plant?.State.P5_HasClearance ?? false, (x) => SelectedPaths.Any(p => p.Hops.Any(h => h.Node == x))); CreateOutlet(Tank3, P3); CreateOutlet(Tank4, P4); CreateOutlet(Tank5, P5); @@ -346,7 +346,7 @@ namespace PamhagenSysCtrl.Helpers { var xt6 = new PipeJoin(t6X, tanksY + 22); var xt7 = new PipeJoin(t7X, tanksY + 22); var xt8 = new PipeJoin(t8X, tanksY + 22); - P6 = new Pump("P6", t6X, tanksY + 30, () => App.Plant?.P6 ?? default, () => App.Plant?.P6HasClearance ?? false, (x) => SelectedPaths.Any(p => p.Hops.Any(h => h.Node == x))); + P6 = new Pump("P6", t6X, tanksY + 30, () => App.Plant?.State.P6_State ?? default, () => App.Plant?.State.P6_HasClearance ?? false, (x) => SelectedPaths.Any(p => p.Hops.Any(h => h.Node == x))); CreateOutlet(Tank6, xt6); CreateOutlet(Tank7, xt7); CreateOutlet(Tank8, xt8); @@ -404,8 +404,8 @@ namespace PamhagenSysCtrl.Helpers { CreatePipe(xp5, xp6); CreatePipe(xp7, xp8); - var zv1 = new ExternalValve("ZV1", pr1cX, prY - 8, 0, () => App.Plant?.Press1HasClearance ?? false); - var zv2 = new ExternalValve("ZV2", pr2cX, prY - 8, 2, () => App.Plant?.Press2HasClearance ?? false); + var zv1 = new ExternalValve("ZV1", pr1cX, prY - 8, 0, () => App.Plant?.State.Press1_HasClearance ?? false); + var zv2 = new ExternalValve("ZV2", pr2cX, prY - 8, 2, () => App.Plant?.State.Press2_HasClearance ?? false); CreatePipe(xp6, zv1, true); CreatePipe(xp8, zv2, true); CreateInlet(zv1, Press1, true); @@ -413,11 +413,11 @@ namespace PamhagenSysCtrl.Helpers { } protected ControlledValve CreateValve(int n, double x, double y, int o) { - return new(n, x, y, o, () => App.Plant?.IsValveOpen(n) ?? false, (v) => App.Plant?.ManualControlMode ?? false ? App.Plant.WantValveOpen(n) : SelectedPaths.Any(p => p.Hops.Any(h => h.Node == v))); + return new(n, x, y, o, () => App.Plant?.State.Valve_IsOpen(n) ?? false, (v) => App.Plant?.ManualControlMode ?? false ? App.Plant.State.Valve_WantOpen(n) : SelectedPaths.Any(p => p.Hops.Any(h => h.Node == v))); } protected static Tank CreateTank(int n, double x, double y, int capacity) { - return new($"Tank {n}", x, y, capacity, () => App.Plant?.TankFillLevels[n - 1] ?? FillState.Unknown); + return new($"Tank {n}", x, y, capacity, () => App.Plant?.State.Tanks_FillLevel[n - 1] ?? FillState.Unknown); } private int PathCost(Path p) { diff --git a/PamhagenSysCtrl/Helpers/PamhagenPlant.cs b/PamhagenSysCtrl/Helpers/PamhagenPlant.cs index 37b91d7..6f8a54c 100644 --- a/PamhagenSysCtrl/Helpers/PamhagenPlant.cs +++ b/PamhagenSysCtrl/Helpers/PamhagenPlant.cs @@ -1,4 +1,5 @@ using PamhagenSysCtrl.Helpers.Pipeline; +using PamhagenSysCtrl.Helpers.Plc; using System.IO; using System.Windows; using System.Windows.Media; @@ -34,73 +35,17 @@ namespace PamhagenSysCtrl.Helpers { protected bool IsRunning = true; protected Thread? BackgroundThread; - public bool ManualControlMode { get; set; } - public bool OverridePathClearances { get; set; } - public bool OverrideDryRunClearances { get; set; } - public event EventHandler? Update; - protected bool ActuatorsChanged = false; - protected PamhagenPlc.ActuatorStates Actuators; - protected PamhagenPlc.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); - public string? PlcPortName => Plc?.PortName; - public T1.Status? LastPlcStatus => Plc?.LastPlcStatus; - public T1Error? LastPlcError => Plc?.LastPlcError; + public T1.Status? LastPlcStatus => Plc?.LastStatus; + public T1Error? LastPlcError => Plc?.LastError; public long? LastRttNs { get; private set; } - public bool IsValveOpen(int n) => !IsValveClosed(n); - public bool IsValveClosed(int n) => Sensors.GetDS(n); - public bool WantValveClosed(int n) => !WantValveOpen(n); - public bool WantValveOpen(int n) => Actuators.GetV(n); - - public bool AreValvesOpen(params int[] ns) => ns.All(IsValveOpen) && ns.All(WantValveOpen); - public bool AreValvesClosed(params int[] ns) => ns.All(IsValveClosed) && ns.All(WantValveClosed); - - public int TroughSelector => Sensors.TroughSelector; - public bool IsMW1Selected => Sensors.TroughSelector == 1; - public bool IsMW2Selected => Sensors.TroughSelector == 2; - public FillState FillLevelMW1 => Sensors.FillingLevelMW1; - public FillState FillLevelMW2 => Sensors.FillingLevelMW2; - public bool Press1HasClearance => Sensors.ClearancePress1; - public bool Press2HasClearance => Sensors.ClearancePress2; - 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 FillState[] TankFillLevels => Sensors.FillingLevelTanks; - public double GradationOe => Sensors.GradationOe; - public MotorState PresseQuerSchnecke => Sensors.PresseQuerSchnecke; - public bool IsPresseQuerSchneckeActive => PresseQuerSchnecke == MotorState.Forward; - - public bool WantMW1Selected => Actuators.GetV(58); - public bool WantMW2Selected => !Actuators.GetV(58); - - public bool IsTroughAugerActive => Actuators.TroughAuger; - public bool TroughAugerHasClearance => IsReblerActive && !(IsMW1Selected && FillLevelMW1 == FillState.Full) && !(IsMW2Selected && FillLevelMW2 == FillState.Full); - public bool IsReblerActive => Actuators.Rebler; - public bool IsStirrerMW1Active => Actuators.StirrerMW1; - public bool IsStirrerMW2Active => Actuators.StirrerMW2; - public bool IsAuger1Active => Actuators.Auger1; - public bool IsAuger2Active => Actuators.Auger2; - public bool IsAuger3Active => Actuators.Auger3; - public bool IsWasteDisposalActive => IsAuger1Active || IsAuger2Active || IsAuger3Active; - public bool IsGradationPumpActive => Actuators.GradationPump; - - public bool IsMP1Active => Actuators.MP1 == MotorState.Forward || Actuators.MP1 == MotorState.Backward; - public bool IsMP2Active => Actuators.MP2 == MotorState.Forward || Actuators.MP2 == MotorState.Backward; - public bool IsP12Active => Actuators.P12 == MotorState.Forward; - public bool IsP3Active => Actuators.P3 == MotorState.Forward; - public bool IsP4Active => Actuators.P4 == MotorState.Forward; - public bool IsP5Active => Actuators.P5 == MotorState.Forward; - public bool IsP6Active => Actuators.P6 == MotorState.Forward; + public PamhagenState State => Plc?.State ?? default; // FIXME + public bool ManualControlMode { get => Plc?.ManualControlMode ?? false; set => Plc?.ManualControlMode = value; } + public bool OverridePathClearances { get => Plc?.OverridePathClearances ?? false; set => Plc?.OverridePathClearances = value; } + public bool OverrideDryRunClearances { get => Plc?.OverrideDryRunClearances ?? false; set => Plc?.OverrideDryRunClearances = value; } public PamhagenPlant(string portName) { try { @@ -120,7 +65,6 @@ namespace PamhagenSysCtrl.Helpers { Keyboard.SetBacklight(Color.FromRgb(4, 4, 4)); } catch { } - Update += OnUpdate; Update += UpdateKeyboardBacklights; } @@ -128,56 +72,17 @@ namespace PamhagenSysCtrl.Helpers { App.MainDispatcher?.BeginInvoke(() => Update?.Invoke(this, evt)); } - protected static void OnUpdate(object? sender, PlantEventArgs evt) { - if (sender is not PamhagenPlant p) return; - - if (!p.ManualControlMode) { - if (p.IsPresseQuerSchneckeActive && !p.IsAuger2Active) p.StartAuger2(); - if (p.IsReblerActive && !p.IsAuger1Active) p.StartAuger1(); - if (p.IsAuger1Active && !p.IsAuger2Active) p.StartAuger2(); - if (p.IsAuger2Active && !p.IsAuger3Active) p.StartAuger3(); - if (p.IsTroughAugerActive && !p.TroughAugerHasClearance) - p.StopTroughAuger(); - if (!p.IsPresseQuerSchneckeActive && !p.IsAuger1Active) { - p.StopAuger2(); - p.StopAuger3(); - } - } - - if (p.IsMP1Active && !p.MP1HasClearance) p.StopMP1(); - if (p.IsMP2Active && !p.MP2HasClearance) p.StopMP2(); - - if (!p.ManualControlMode) { - if (p.MP1 == MotorState.Forward && p.FillLevelMW1 >= FillState.Half && !p.IsStirrerMW1Active) { - p.StartStirrerMW1(); - } else if (p.IsStirrerMW1Active && (!p.IsMP1Active || p.FillLevelMW1 < FillState.Half)) { - p.StopStirrerMW1(); - } - if (p.MP2 == MotorState.Forward && p.FillLevelMW2 >= FillState.Half && !p.IsStirrerMW2Active) { - p.StartStirrerMW2(); - } else if (p.IsStirrerMW2Active && (!p.IsMP2Active || p.FillLevelMW2 < FillState.Half)) { - p.StopStirrerMW2(); - } - } - - if (p.IsP12Active && !p.P12HasClearance) p.StopP12(); - if (p.IsP3Active && !p.P3HasClearance) p.StopP3(); - if (p.IsP4Active && !p.P4HasClearance) p.StopP4(); - if (p.IsP5Active && !p.P5HasClearance) p.StopP5(); - if (p.IsP6Active && !p.P6HasClearance) p.StopP6(); - } - protected void UpdateKeyboardBacklights(object? sender, PlantEventArgs evt) { if (sender is not PamhagenPlant plant || plant.Keyboard is not Xbe24Keyboard k) return; - k.SetBacklight((int)Xbe24Key.START_REBLER, plant.IsReblerActive ? Color.FromRgb(0, 255, 0) : plant.IsAuger1Active ? Color.FromRgb(255, 255, 0) : plant.IsAuger2Active || plant.IsAuger3Active ? Color.FromRgb(32, 32, 4) : Color.FromRgb(4, 32, 4)); - k.SetBacklight((int)Xbe24Key.STOP_REBLER, !plant.IsReblerActive ? Color.FromRgb(255, 0, 0) : Color.FromRgb(32, 4, 4)); + k.SetBacklight((int)Xbe24Key.START_REBLER, State.Rebler_IsActive ? Color.FromRgb(0, 255, 0) : State.Auger1_IsActive ? Color.FromRgb(255, 255, 0) : State.WasteDisposal_IsActive ? Color.FromRgb(32, 32, 4) : Color.FromRgb(4, 32, 4)); + k.SetBacklight((int)Xbe24Key.STOP_REBLER, !State.Rebler_IsActive ? Color.FromRgb(255, 0, 0) : Color.FromRgb(32, 4, 4)); - k.SetBacklight((int)Xbe24Key.START_TROUGH_AUGER, !plant.TroughAugerHasClearance ? k.IsKeyPressed((int)Xbe24Key.START_TROUGH_AUGER) ? Color.FromRgb(255, 32, 0) : Color.FromRgb(32, 4, 4) : plant.IsTroughAugerActive ? Color.FromRgb(0, 255, 0) : Color.FromRgb(4, 32, 4)); - k.SetBacklight((int)Xbe24Key.STOP_TROUGH_AUGER, !plant.IsTroughAugerActive ? Color.FromRgb(255, 0, 0) : Color.FromRgb(32, 4, 4)); + k.SetBacklight((int)Xbe24Key.START_TROUGH_AUGER, !State.TroughAuger_HasClearance ? k.IsKeyPressed((int)Xbe24Key.START_TROUGH_AUGER) ? Color.FromRgb(255, 32, 0) : Color.FromRgb(32, 4, 4) : State.TroughAuger_IsActive ? Color.FromRgb(0, 255, 0) : Color.FromRgb(4, 32, 4)); + k.SetBacklight((int)Xbe24Key.STOP_TROUGH_AUGER, !State.TroughAuger_IsActive ? Color.FromRgb(255, 0, 0) : Color.FromRgb(32, 4, 4)); - k.SetBacklight((int)Xbe24Key.SELECT_MW2, plant.WantMW2Selected || plant.IsMW2Selected ? Color.FromRgb(0, 0, 255) : Color.FromRgb(4, 4, 16), plant.WantMW2Selected && !plant.IsMW2Selected); - k.SetBacklight((int)Xbe24Key.SELECT_MW1, plant.WantMW1Selected || plant.IsMW1Selected ? Color.FromRgb(0, 0, 255) : Color.FromRgb(4, 4, 16), plant.WantMW1Selected && !plant.IsMW1Selected); + k.SetBacklight((int)Xbe24Key.SELECT_MW2, State.MW2_WantSelected || State.MW2_IsSelected ? Color.FromRgb(0, 0, 255) : Color.FromRgb(4, 4, 16), State.MW2_WantSelected && !State.MW2_IsSelected); + k.SetBacklight((int)Xbe24Key.SELECT_MW1, State.MW1_WantSelected || State.MW1_IsSelected ? Color.FromRgb(0, 0, 255) : Color.FromRgb(4, 4, 16), State.MW1_WantSelected && !State.MW1_IsSelected); var buttons = new Dictionary { [Xbe24Key.FAST_SELECT_MW1_1] = (Graph.MW1, Graph.MP1, Graph.Press1), @@ -213,24 +118,23 @@ namespace PamhagenSysCtrl.Helpers { } } - if (!plant.MP2HasPathClearance && plant.MP2HasDryRunClearance && preparingMP2) { + if (!plant.State.MP2_HasPathClearance && plant.State.MP2_HasDryRunClearance && preparingMP2) { k.SetBacklight((int)Xbe24Key.START_MP2, Color.FromRgb(4, 32, 4), true); } else { - k.SetBacklight((int)Xbe24Key.START_MP2, !plant.MP2HasClearance ? k.IsKeyPressed((int)Xbe24Key.START_MP2) ? Color.FromRgb(255, 32, 0) : Color.FromRgb(32, 4, 4) : plant.IsMP2Active ? Color.FromRgb(0, 255, 0) : Color.FromRgb(4, 32, 4)); + k.SetBacklight((int)Xbe24Key.START_MP2, !State.MP2_HasClearance ? k.IsKeyPressed((int)Xbe24Key.START_MP2) ? Color.FromRgb(255, 32, 0) : Color.FromRgb(32, 4, 4) : State.MP2_IsActive ? Color.FromRgb(0, 255, 0) : Color.FromRgb(4, 32, 4)); } - if (!plant.MP1HasPathClearance && plant.MP1HasDryRunClearance && preparingMP1) { + if (!plant.State.MP1_HasPathClearance && plant.State.MP1_HasDryRunClearance && preparingMP1) { k.SetBacklight((int)Xbe24Key.START_MP1, Color.FromRgb(4, 32, 4), true); } else { - k.SetBacklight((int)Xbe24Key.START_MP1, !plant.MP1HasClearance ? k.IsKeyPressed((int)Xbe24Key.START_MP1) ? Color.FromRgb(255, 32, 0) : Color.FromRgb(32, 4, 4) : plant.IsMP1Active ? Color.FromRgb(0, 255, 0) : Color.FromRgb(4, 32, 4)); + k.SetBacklight((int)Xbe24Key.START_MP1, !State.MP1_HasClearance ? k.IsKeyPressed((int)Xbe24Key.START_MP1) ? Color.FromRgb(255, 32, 0) : Color.FromRgb(32, 4, 4) : State.MP1_IsActive ? Color.FromRgb(0, 255, 0) : Color.FromRgb(4, 32, 4)); } - k.SetBacklight((int)Xbe24Key.STOP_MP2, highlightOffMP2 ? Color.FromRgb(255, 32, 0) : !plant.IsMP2Active ? Color.FromRgb(255, 0, 0) : Color.FromRgb(32, 4, 4)); - k.SetBacklight((int)Xbe24Key.STOP_MP1, highlightOffMP1 ? Color.FromRgb(255, 32, 0) : !plant.IsMP1Active ? Color.FromRgb(255, 0, 0) : Color.FromRgb(32, 4, 4)); + k.SetBacklight((int)Xbe24Key.STOP_MP2, highlightOffMP2 ? Color.FromRgb(255, 32, 0) : !State.MP2_IsActive ? Color.FromRgb(255, 0, 0) : Color.FromRgb(32, 4, 4)); + k.SetBacklight((int)Xbe24Key.STOP_MP1, highlightOffMP1 ? Color.FromRgb(255, 32, 0) : !State.MP1_IsActive ? Color.FromRgb(255, 0, 0) : Color.FromRgb(32, 4, 4)); } public async Task StartThread() { if (Plc == null) throw new Exception(); - await Plc.TestConnection(); - Actuators = await Plc.ReadOutputs(); + await Plc.Initialize(); BackgroundThread = new Thread(new ParameterizedThreadStart(BackgroundLoop)); BackgroundThread.Start(); } @@ -247,11 +151,7 @@ namespace PamhagenSysCtrl.Helpers { while (IsRunning && Plc != null) { var start = DateTime.Now; try { - if (ActuatorsChanged) { - await Plc.WriteOutputs(Actuators); - ActuatorsChanged = false; - } - Sensors = await Plc.ReadInputs(); + await Plc.Update(); LastRttNs = (long)(DateTime.Now - start).TotalNanoseconds; RaiseUpdateEvent(new()); } catch (Exception exc) { @@ -270,7 +170,7 @@ namespace PamhagenSysCtrl.Helpers { } try { if (Plc != null) - await Plc.WriteOutputs(new() { Valves = Actuators.Valves }); + await Plc.Shutdown(); } 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; @@ -283,7 +183,7 @@ namespace PamhagenSysCtrl.Helpers { var remove = Graph.SelectedPaths.ToList(); foreach (var p in remove) Graph.RemovePath(p); for (int i = 1; i <= 57; i++) { - App.Plant?.CloseV(i); + App.Plant?.Plc?.CloseV(i); } foreach (var p in add) { if (p.Hops.Any(h => h.Node is ControlledValve v && v.IsLocked)) continue; @@ -298,7 +198,7 @@ namespace PamhagenSysCtrl.Helpers { foreach (var src in sources) { var paths = new List(); foreach (var sink in sinks) { - paths.AddRange(Graph.GetPaths(src, sink, null, (n) => n is not ISink && (n is not ControlledValve v || p.WantValveOpen(v.Nr)))); + paths.AddRange(Graph.GetPaths(src, sink, null, (n) => n is not ISink && (n is not ControlledValve v || State.Valve_WantOpen(v.Nr)))); } if (paths.Count == 1) yield return paths.First(); @@ -321,35 +221,36 @@ namespace PamhagenSysCtrl.Helpers { private void Keyboard_KeyDown(object? sender, int keyIndex) { var key = (Xbe24Key)keyIndex; App.MainDispatcher?.BeginInvoke(() => { + if (Plc == null) return; try { if (key == Xbe24Key.START_REBLER) { - StartRebler(); + Plc.StartRebler(); } else if (key == Xbe24Key.STOP_REBLER) { - if (IsReblerActive) { - StopRebler(); + if (State.Rebler_IsActive) { + Plc.StopRebler(); } else { - StopAuger1(); - if (!IsPresseQuerSchneckeActive) { - StopAuger2(); - StopAuger3(); + Plc.StopAuger1(); + if (!State.PressCrossAuger_IsActive) { + Plc.StopAuger2(); + Plc.StopAuger3(); } } } else if (key == Xbe24Key.START_TROUGH_AUGER) { - StartTroughAuger(); + Plc.StartTroughAuger(); } else if (key == Xbe24Key.STOP_TROUGH_AUGER) { - StopTroughAuger(); + Plc.StopTroughAuger(); } else if (key == Xbe24Key.SELECT_MW2) { - SelectMW2(); + Plc.SelectMW2(); } else if (key == Xbe24Key.SELECT_MW1) { - SelectMW1(); + Plc.SelectMW1(); } else if (key == Xbe24Key.START_MP2) { - StartMP2Forward(); + Plc.StartMP2Forward(); } else if (key == Xbe24Key.STOP_MP2) { - StopMP2(); + Plc.StopMP2(); } else if (key == Xbe24Key.START_MP1) { - StartMP1Forward(); + Plc.StartMP1Forward(); } else if (key == Xbe24Key.STOP_MP1) { - StopMP1(); + Plc.StopMP1(); } else if (key == Xbe24Key.FAST_SELECT_MW1_1) { FastSelect(Graph.MW1, Graph.Press1); } else if (key == Xbe24Key.FAST_SELECT_MW1_2) { @@ -387,442 +288,39 @@ namespace PamhagenSysCtrl.Helpers { RaiseUpdateEvent(new()); } - protected bool HasWTClearanceAA => - (AreValvesOpen(12) && AreValvesClosed(15, 16, 20)) || // bypass - (AreValvesOpen(15, 18, 16) && AreValvesClosed(11, 12, 14, 17, 19, 20)) || // WT1 - (AreValvesOpen(15, 17, 20) && AreValvesClosed(11, 12, 18, 19, 21)) || // WT2 - (AreValvesOpen(15, 18, 19, 20) && AreValvesClosed(11, 12, 14, 16, 17, 21)) || // WT1+WT2 - false; - protected bool HasWTClearanceAB => - (AreValvesOpen(15, 18, 14) && AreValvesClosed(11, 12, 13, 16, 17, 19, 21)) || // WT1 - (AreValvesOpen(15, 17, 21) && AreValvesClosed(11, 12, 13, 18, 19, 20)) || // WT2 - (AreValvesOpen(15, 18, 19, 21) && AreValvesClosed(11, 12, 13, 14, 16, 17, 20)) || // WT1+WT2 - false; - protected bool HasWTClearanceBA => - (AreValvesOpen(11, 18, 16) && AreValvesClosed(15, 12, 13, 14, 17, 19, 20)) || // WT1 - (AreValvesOpen(11, 17, 20) && AreValvesClosed(15, 12, 13, 18, 19, 21)) || // WT2 - (AreValvesOpen(11, 18, 19, 20) && AreValvesClosed(15, 12, 13, 14, 16, 17, 21)) || // WT1+WT2 - false; - protected bool HasWTClearanceBB => - (AreValvesOpen(13) && AreValvesClosed(11, 14, 21)) || // bypass - (AreValvesOpen(11, 18, 14) && AreValvesClosed(15, 13, 16, 17, 19, 21)) || // WT1 - (AreValvesOpen(11, 17, 21) && AreValvesClosed(15, 13, 18, 19, 20)) || // WT2 - (AreValvesOpen(11, 18, 19, 21) && AreValvesClosed(15, 13, 14, 16, 17, 20)) || // WT1+WT2 - false; - - protected bool HasT1ClearanceA => AreValvesOpen(22) && AreValvesClosed(24, 26, 28); - protected bool HasT1ClearanceB => AreValvesOpen(23) && AreValvesClosed(25, 27, 29); - protected bool HasT2ClearanceA => AreValvesOpen(24) && AreValvesClosed(22, 26, 28); - protected bool HasT2ClearanceB => AreValvesOpen(25) && AreValvesClosed(23, 27, 29); - protected bool HasT3ClearanceA => AreValvesOpen(26) && AreValvesClosed(22, 24, 28); - protected bool HasT3ClearanceB => AreValvesOpen(27) && AreValvesClosed(23, 25, 29); - protected bool HasT4ClearanceA => AreValvesOpen(28, 30) && AreValvesClosed(22, 24, 26, 32, 34); - protected bool HasT4ClearanceB => AreValvesOpen(29, 31) && AreValvesClosed(23, 25, 27, 33, 35); - protected bool HasT5ClearanceA => AreValvesOpen(28, 32) && AreValvesClosed(22, 24, 26, 30, 34); - protected bool HasT5ClearanceB => AreValvesOpen(29, 33) && AreValvesClosed(23, 25, 27, 31, 35); - protected bool HasT6ClearanceA => AreValvesOpen(28, 34, 37) && AreValvesClosed(22, 24, 26, 30, 32, 39, 41, 43); - protected bool HasT6ClearanceB => AreValvesOpen(29, 35, 36) && AreValvesClosed(23, 25, 27, 31, 33, 38, 40, 42); - protected bool HasT7ClearanceA => AreValvesOpen(28, 34, 39) && AreValvesClosed(22, 24, 26, 30, 32, 37, 41, 43); - protected bool HasT7ClearanceB => AreValvesOpen(29, 35, 38) && AreValvesClosed(23, 25, 27, 31, 33, 36, 40, 42); - protected bool HasT8ClearanceA => AreValvesOpen(28, 34, 41) && AreValvesClosed(22, 24, 26, 30, 32, 37, 39, 43); - protected bool HasT8ClearanceB => AreValvesOpen(29, 35, 40) && AreValvesClosed(23, 25, 27, 31, 33, 36, 38, 42); - protected bool HasT9ClearanceA => AreValvesOpen(28, 34, 43) && AreValvesClosed(22, 24, 26, 30, 32, 37, 39, 41); - protected bool HasT9ClearanceB => AreValvesOpen(29, 35, 42) && AreValvesClosed(23, 25, 27, 31, 33, 36, 38, 40); - - protected bool HasClearanceTank1(bool a, bool b) => - (a && ((HasWTClearanceAA && HasT1ClearanceA) || (HasWTClearanceAB && HasT1ClearanceB))) || - (b && ((HasWTClearanceBA && HasT1ClearanceA) || (HasWTClearanceBB && HasT1ClearanceB))); - protected bool HasClearanceTank2(bool a, bool b) => - (a && ((HasWTClearanceAA && HasT2ClearanceA) || (HasWTClearanceAB && HasT2ClearanceB))) || - (b && ((HasWTClearanceBA && HasT2ClearanceA) || (HasWTClearanceBB && HasT2ClearanceB))); - protected bool HasClearanceTank3(bool a, bool b) => - (a && ((HasWTClearanceAA && HasT3ClearanceA) || (HasWTClearanceAB && HasT3ClearanceB))) || - (b && ((HasWTClearanceBA && HasT3ClearanceA) || (HasWTClearanceBB && HasT3ClearanceB))); - protected bool HasClearanceTank4(bool a, bool b) => - (a && ((HasWTClearanceAA && HasT4ClearanceA) || (HasWTClearanceAB && HasT4ClearanceB))) || - (b && ((HasWTClearanceBA && HasT4ClearanceA) || (HasWTClearanceBB && HasT4ClearanceB))); - protected bool HasClearanceTank5(bool a, bool b) => - (a && ((HasWTClearanceAA && HasT5ClearanceA) || (HasWTClearanceAB && HasT5ClearanceB))) || - (b && ((HasWTClearanceBA && HasT5ClearanceA) || (HasWTClearanceBB && HasT5ClearanceB))); - protected bool HasClearanceTank6(bool a, bool b) => - (a && ((HasWTClearanceAA && HasT6ClearanceA) || (HasWTClearanceAB && HasT6ClearanceB))) || - (b && ((HasWTClearanceBA && HasT6ClearanceA) || (HasWTClearanceBB && HasT6ClearanceB))); - protected bool HasClearanceTank7(bool a, bool b) => - (a && ((HasWTClearanceAA && HasT7ClearanceA) || (HasWTClearanceAB && HasT7ClearanceB))) || - (b && ((HasWTClearanceBA && HasT7ClearanceA) || (HasWTClearanceBB && HasT7ClearanceB))); - protected bool HasClearanceTank8(bool a, bool b) => - (a && ((HasWTClearanceAA && HasT8ClearanceA) || (HasWTClearanceAB && HasT8ClearanceB))) || - (b && ((HasWTClearanceBA && HasT8ClearanceA) || (HasWTClearanceBB && HasT8ClearanceB))); - protected bool HasClearanceTank9(bool a, bool b) => - (a && ((HasWTClearanceAA && HasT9ClearanceA) || (HasWTClearanceAB && HasT9ClearanceB))) || - (b && ((HasWTClearanceBA && HasT9ClearanceA) || (HasWTClearanceBB && HasT9ClearanceB))); - - public bool MP1HasClearance => MP1HasDryRunClearance && MP1HasPathClearance; - public bool MP1HasDryRunClearance => (OverrideDryRunClearances || FillLevelMW1 != FillState.Empty); - public bool MP1HasPathClearance => ( - OverridePathClearances || - (MP1HasClearancePress1 && Press1HasClearance) || - (MP1HasClearancePress2 && Press2HasClearance) || - (HasClearanceTank1(MP1HasClearanceTanksA, MP1HasClearanceTanksB) && FillLevelT1 != FillState.Full) || - (HasClearanceTank2(MP1HasClearanceTanksA, MP1HasClearanceTanksB) && FillLevelT2 != FillState.Full) || - (HasClearanceTank3(MP1HasClearanceTanksA, MP1HasClearanceTanksB) && FillLevelT3 != FillState.Full) || - (HasClearanceTank4(MP1HasClearanceTanksA, MP1HasClearanceTanksB) && FillLevelT4 != FillState.Full) || - (HasClearanceTank5(MP1HasClearanceTanksA, MP1HasClearanceTanksB) && FillLevelT5 != FillState.Full) || - (HasClearanceTank6(MP1HasClearanceTanksA, MP1HasClearanceTanksB) && FillLevelT6 != FillState.Full) || - (HasClearanceTank7(MP1HasClearanceTanksA, MP1HasClearanceTanksB) && FillLevelT7 != FillState.Full) || - (HasClearanceTank8(MP1HasClearanceTanksA, MP1HasClearanceTanksB) && FillLevelT8 != FillState.Full) || - (HasClearanceTank9(MP1HasClearanceTanksA, MP1HasClearanceTanksB) && FillLevelT9 != FillState.Full) || - false); - protected bool MP1HasClearancePress1 => - (AreValvesOpen(1, 6, 51) && AreValvesClosed(3, 4, 5, 50, 52, 53, 55)) || - (AreValvesOpen(4, 8, 52) && AreValvesClosed(1, 2, 7, 50, 51, 53, 56)); - protected bool MP1HasClearancePress2 => - (AreValvesOpen(1, 6, 55) && AreValvesClosed(3, 4, 5, 51, 54, 56, 57)) || - (AreValvesOpen(4, 8, 56) && AreValvesClosed(1, 2, 7, 52, 54, 55, 57)); - protected bool MP1HasClearanceTanksA => AreValvesOpen(1, 5) && AreValvesClosed(3, 4, 6); - protected bool MP1HasClearanceTanksB => AreValvesOpen(4, 7, 9) && AreValvesClosed(1, 2, 8, 10); - - 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 bool MP2HasClearance => MP2HasDryRunClearance && MP2HasPathClearance; - public bool MP2HasDryRunClearance => (OverrideDryRunClearances || FillLevelMW2 != FillState.Empty); - public bool MP2HasPathClearance => ( - OverridePathClearances || - (MP2HasClearancePress1 && Press1HasClearance) || - (MP2HasClearancePress2 && Press2HasClearance) || - (HasClearanceTank1(MP2HasClearanceTanksA, MP2HasClearanceTanksB) && FillLevelT1 != FillState.Full) || - (HasClearanceTank2(MP2HasClearanceTanksA, MP2HasClearanceTanksB) && FillLevelT2 != FillState.Full) || - (HasClearanceTank3(MP2HasClearanceTanksA, MP2HasClearanceTanksB) && FillLevelT3 != FillState.Full) || - (HasClearanceTank4(MP2HasClearanceTanksA, MP2HasClearanceTanksB) && FillLevelT4 != FillState.Full) || - (HasClearanceTank5(MP2HasClearanceTanksA, MP2HasClearanceTanksB) && FillLevelT5 != FillState.Full) || - (HasClearanceTank6(MP2HasClearanceTanksA, MP2HasClearanceTanksB) && FillLevelT6 != FillState.Full) || - (HasClearanceTank7(MP2HasClearanceTanksA, MP2HasClearanceTanksB) && FillLevelT7 != FillState.Full) || - (HasClearanceTank8(MP2HasClearanceTanksA, MP2HasClearanceTanksB) && FillLevelT8 != FillState.Full) || - (HasClearanceTank9(MP2HasClearanceTanksA, MP2HasClearanceTanksB) && FillLevelT9 != FillState.Full) || - false); - protected bool MP2HasClearancePress1 => - (AreValvesOpen(2, 8, 52) && AreValvesClosed(3, 4, 7, 50, 51, 53, 56)) || - (AreValvesOpen(3, 6, 51) && AreValvesClosed(1, 2, 5, 50, 52, 53, 55)); - protected bool MP2HasClearancePress2 => - (AreValvesOpen(2, 8, 56) && AreValvesClosed(3, 4, 7, 52, 54, 55, 57)) || - (AreValvesOpen(3, 6, 55) && AreValvesClosed(2, 1, 5, 51, 54, 56, 57)); - protected bool MP2HasClearanceTanksA => AreValvesOpen(3, 5) && AreValvesClosed(1, 2, 6); - protected bool MP2HasClearanceTanksB => AreValvesOpen(2, 7, 9) && AreValvesClosed(3, 4, 8, 10); - - 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 bool P12HasClearance => (OverrideDryRunClearances || FillLevelT1 != FillState.Empty || FillLevelT2 != FillState.Empty) && ( - OverridePathClearances || - (P12HasClearancePress1 && Press1HasClearance) || - (P12HasClearancePress2 && Press2HasClearance) || - (HasClearanceTank1(false, P12HasClearanceTanksB) && FillLevelT1 != FillState.Full) || - (HasClearanceTank2(false, P12HasClearanceTanksB) && FillLevelT2 != FillState.Full) || - (HasClearanceTank3(false, P12HasClearanceTanksB) && FillLevelT3 != FillState.Full) || - (HasClearanceTank4(false, P12HasClearanceTanksB) && FillLevelT4 != FillState.Full) || - (HasClearanceTank5(false, P12HasClearanceTanksB) && FillLevelT5 != FillState.Full) || - (HasClearanceTank6(false, P12HasClearanceTanksB) && FillLevelT6 != FillState.Full) || - (HasClearanceTank7(false, P12HasClearanceTanksB) && FillLevelT7 != FillState.Full) || - (HasClearanceTank8(false, P12HasClearanceTanksB) && FillLevelT8 != FillState.Full) || - (HasClearanceTank9(false, P12HasClearanceTanksB) && FillLevelT9 != FillState.Full) || - false); - protected bool P12HasClearancePress1 => AreValvesOpen(44, 46, 49, 50) && AreValvesClosed(10, 45, 47, 48, 54, 51, 52, 53); - protected bool P12HasClearancePress2 => AreValvesOpen(44, 46, 49, 54) && AreValvesClosed(10, 45, 47, 48, 50, 55, 56, 57); - protected bool P12HasClearanceTanksB => AreValvesOpen(44, 10) && AreValvesClosed(9, 45, 46); - - public MotorState P12 { - get => Actuators.P12; - set => Actuators.P12 = (value != MotorState.Halt && value != MotorState.Forward) ? throw new ArgumentException("Invalid state for P12") : value; - } - - public bool P3HasClearance => (OverrideDryRunClearances || FillLevelT3 != FillState.Empty) && ( - OverridePathClearances || - (P3HasClearancePress1 && Press1HasClearance) || - (P3HasClearancePress2 && Press2HasClearance) || - (HasClearanceTank1(false, P3HasClearanceTanksB) && FillLevelT1 != FillState.Full) || - (HasClearanceTank2(false, P3HasClearanceTanksB) && FillLevelT2 != FillState.Full) || - (HasClearanceTank3(false, P3HasClearanceTanksB) && FillLevelT3 != FillState.Full) || - (HasClearanceTank4(false, P3HasClearanceTanksB) && FillLevelT4 != FillState.Full) || - (HasClearanceTank5(false, P3HasClearanceTanksB) && FillLevelT5 != FillState.Full) || - (HasClearanceTank6(false, P3HasClearanceTanksB) && FillLevelT6 != FillState.Full) || - (HasClearanceTank7(false, P3HasClearanceTanksB) && FillLevelT7 != FillState.Full) || - (HasClearanceTank8(false, P3HasClearanceTanksB) && FillLevelT8 != FillState.Full) || - (HasClearanceTank9(false, P3HasClearanceTanksB) && FillLevelT9 != FillState.Full) || - false); - protected bool P3HasClearancePress1 => AreValvesOpen(45, 46, 49, 50) && AreValvesClosed(10, 44, 47, 48, 51, 52, 53, 54); - protected bool P3HasClearancePress2 => AreValvesOpen(45, 46, 49, 54) && AreValvesClosed(10, 44, 47, 48, 50, 55, 56, 57); - protected bool P3HasClearanceTanksB => AreValvesOpen(45, 10) && AreValvesClosed(9, 44, 46); - - public MotorState P3 { - get => Actuators.P3; - set => Actuators.P3 = (value != MotorState.Halt && value != MotorState.Forward) ? throw new ArgumentException("Invalid state for P3") : value; - } - - public bool P4HasClearance => (OverrideDryRunClearances || FillLevelT4 != FillState.Empty) && ( - OverridePathClearances || - (P4HasClearancePress1 && Press1HasClearance) || - (P4HasClearancePress2 && Press2HasClearance) || - (HasClearanceTank1(false, P4HasClearanceTanksB) && FillLevelT1 != FillState.Full) || - (HasClearanceTank2(false, P4HasClearanceTanksB) && FillLevelT2 != FillState.Full) || - (HasClearanceTank3(false, P4HasClearanceTanksB) && FillLevelT3 != FillState.Full) || - (HasClearanceTank4(false, P4HasClearanceTanksB) && FillLevelT4 != FillState.Full) || - (HasClearanceTank5(false, P4HasClearanceTanksB) && FillLevelT5 != FillState.Full) || - (HasClearanceTank6(false, P4HasClearanceTanksB) && FillLevelT6 != FillState.Full) || - (HasClearanceTank7(false, P4HasClearanceTanksB) && FillLevelT7 != FillState.Full) || - (HasClearanceTank8(false, P4HasClearanceTanksB) && FillLevelT8 != FillState.Full) || - (HasClearanceTank9(false, P4HasClearanceTanksB) && FillLevelT9 != FillState.Full) || - false); - protected bool P4HasClearancePress1 => AreValvesOpen(47, 49, 50) && AreValvesClosed(46, 48, 51, 52, 53, 54); - protected bool P4HasClearancePress2 => AreValvesOpen(47, 49, 54) && AreValvesClosed(46, 48, 50, 55, 56, 57); - protected bool P4HasClearanceTanksB => AreValvesOpen(47, 46, 10) && AreValvesClosed(9, 44, 45, 48, 49); - - public MotorState P4 { - get => Actuators.P4; - set => Actuators.P4 = (value != MotorState.Halt && value != MotorState.Forward) ? throw new ArgumentException("Invalid state for P4") : value; - } - - public bool P5HasClearance => (OverrideDryRunClearances || FillLevelT5 != FillState.Empty) && ( - OverridePathClearances || - (P5HasClearancePress1 && Press1HasClearance) || - (P5HasClearancePress2 && Press2HasClearance) || - (HasClearanceTank1(false, P5HasClearanceTanksB) && FillLevelT1 != FillState.Full) || - (HasClearanceTank2(false, P5HasClearanceTanksB) && FillLevelT2 != FillState.Full) || - (HasClearanceTank3(false, P5HasClearanceTanksB) && FillLevelT3 != FillState.Full) || - (HasClearanceTank4(false, P5HasClearanceTanksB) && FillLevelT4 != FillState.Full) || - (HasClearanceTank5(false, P5HasClearanceTanksB) && FillLevelT5 != FillState.Full) || - (HasClearanceTank6(false, P5HasClearanceTanksB) && FillLevelT6 != FillState.Full) || - (HasClearanceTank7(false, P5HasClearanceTanksB) && FillLevelT7 != FillState.Full) || - (HasClearanceTank8(false, P5HasClearanceTanksB) && FillLevelT8 != FillState.Full) || - (HasClearanceTank9(false, P5HasClearanceTanksB) && FillLevelT9 != FillState.Full) || - false); - protected bool P5HasClearancePress1 => AreValvesOpen(48, 49, 50) && AreValvesClosed(46, 47, 51, 52, 53, 54); - protected bool P5HasClearancePress2 => AreValvesOpen(48, 49, 54) && AreValvesClosed(46, 47, 50, 55, 56, 57); - protected bool P5HasClearanceTanksB => AreValvesOpen(48, 46, 10) && AreValvesClosed(9, 44, 45, 47, 49); - - public MotorState P5 { - get => Actuators.P5; - set => Actuators.P5 = (value != MotorState.Halt && value != MotorState.Forward) ? throw new ArgumentException("Invalid state for P5") : value; - } - - public bool P6HasClearance => OverridePathClearances || - (AreValvesOpen(53) && AreValvesClosed(50, 51, 52, 57) && Press1HasClearance) || - (AreValvesOpen(57) && AreValvesClosed(53, 54, 55, 56) && Press2HasClearance); - - 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, 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); - ActuatorsChanged = true; - } - - public void SelectMW1() { - Actuators.SetV(58, true); - ActuatorsChanged = true; - } - - public void SelectMW2() { - Actuators.SetV(58, false); - ActuatorsChanged = true; - } - - public void StartTroughAuger() { - if (!ManualControlMode && !TroughAugerHasClearance) - throw new NoClearanceException("Schnecke Auffangwanne hat keine Freigabe"); - Actuators.TroughAuger = true; - ActuatorsChanged = true; - } - - public void StopTroughAuger() { - Actuators.TroughAuger = false; - ActuatorsChanged = true; - } - - public void StartRebler() { - Actuators.Rebler = true; - if (!ManualControlMode) StartAuger1(); - ActuatorsChanged = true; - } - - public void StopRebler() { - if (!ManualControlMode) StopTroughAuger(); - Actuators.Rebler = false; - ActuatorsChanged = true; - } - - public void StartStirrerMW1() { - Actuators.StirrerMW1 = true; - ActuatorsChanged = true; - } - - public void StopStirrerMW1() { - Actuators.StirrerMW1 = false; - ActuatorsChanged = true; - } - - public void StartStirrerMW2() { - Actuators.StirrerMW2 = true; - ActuatorsChanged = true; - } - - public void StopStirrerMW2() { - Actuators.StirrerMW2 = false; - ActuatorsChanged = true; - } - - public void StartAuger1() { - Actuators.Auger1 = true; - if (!ManualControlMode) StartAuger2(); - ActuatorsChanged = true; - } - - public void StopAuger1() { - if (!ManualControlMode && IsReblerActive) throw new NoClearanceException("Rebler aktiv"); - Actuators.Auger1 = false; - ActuatorsChanged = true; - } - - public void StartAuger2() { - Actuators.Auger2 = true; - if (!ManualControlMode) StartAuger3(); - ActuatorsChanged = true; - } - - public void StopAuger2() { - if (!ManualControlMode && IsPresseQuerSchneckeActive) throw new NoClearanceException("Presse-Querschnecke aktiv"); - if (!ManualControlMode && IsAuger1Active) throw new NoClearanceException("Förderschnecke 1 aktiv"); - Actuators.Auger2 = false; - ActuatorsChanged = true; - } - - public void StartAuger3() { - Actuators.Auger3 = true; - ActuatorsChanged = true; - } - - public void StopAuger3() { - if (!ManualControlMode && IsAuger2Active) throw new NoClearanceException("Förderschnecke 2 aktiv"); - Actuators.Auger3 = false; - ActuatorsChanged = true; - } - - public void StartGradationPump() { - Actuators.GradationPump = true; - ActuatorsChanged = true; - } - - public void StopGradationPump() { - Actuators.GradationPump = false; - ActuatorsChanged = true; - } - - public void StartMP1Forward() { - if (!MP1HasClearance) throw new NoClearanceException("MP1 hat keine Freigabe"); - if (Actuators.MP1 == MotorState.Backward) throw new NoClearanceException("MP1 läuft bereits (zurück)"); - Actuators.MP1 = MotorState.Forward; - if (FillLevelMW1 >= FillState.Half) StartStirrerMW1(); - ActuatorsChanged = true; - } - - public void StartMP1Backward() { - if (!MP1HasClearance) throw new NoClearanceException("MP1 hat keine Freigabe"); - if (Actuators.MP1 == MotorState.Forward) throw new NoClearanceException("MP1 läuft bereits (vor)"); - Actuators.MP1 = MotorState.Backward; - ActuatorsChanged = true; - } - - public void StopMP1() { - Actuators.MP1 = MotorState.Halt; - if (!ManualControlMode) StopStirrerMW1(); - ActuatorsChanged = true; - } - - public void StartMP2Forward() { - if (!MP2HasClearance) throw new NoClearanceException("MP2 hat keine Freigabe"); - if (Actuators.MP2 == MotorState.Backward) throw new NoClearanceException("MP2 läuft bereits (zurück)"); - Actuators.MP2 = MotorState.Forward; - if (FillLevelMW2 >= FillState.Half) StartStirrerMW2(); - ActuatorsChanged = true; - } - - public void StartMP2Backward() { - if (!MP2HasClearance) throw new NoClearanceException("MP2 hat keine Freigabe"); - if (Actuators.MP2 == MotorState.Forward) throw new NoClearanceException("MP2 läuft bereits (vor)"); - Actuators.MP2 = MotorState.Backward; - ActuatorsChanged = true; - } - - public void StopMP2() { - Actuators.MP2 = MotorState.Halt; - if (!ManualControlMode) StopStirrerMW1(); - ActuatorsChanged = true; - } - - public void StartP12() { - if (!P12HasClearance) throw new NoClearanceException("P1/2 hat keine Freigabe"); - Actuators.P12 = MotorState.Forward; - ActuatorsChanged = true; - } - - public void StopP12() { - Actuators.P12 = MotorState.Halt; - ActuatorsChanged = true; - } - - public void StartP3() { - if (!P3HasClearance) throw new NoClearanceException("P3 hat keine Freigabe"); - Actuators.P3 = MotorState.Forward; - ActuatorsChanged = true; - } - - public void StopP3() { - Actuators.P3 = MotorState.Halt; - ActuatorsChanged = true; - } - - public void StartP4() { - if (!P4HasClearance) throw new NoClearanceException("P4 hat keine Freigabe"); - Actuators.P4 = MotorState.Forward; - ActuatorsChanged = true; - } - - public void StopP4() { - Actuators.P4 = MotorState.Halt; - ActuatorsChanged = true; - } - - public void StartP5() { - if (!P5HasClearance) throw new NoClearanceException("P5 hat keine Freigabe"); - Actuators.P5 = MotorState.Forward; - ActuatorsChanged = true; - } - - public void StopP5() { - Actuators.P5 = MotorState.Halt; - ActuatorsChanged = true; - } - - public void StartP6() { - if (!P6HasClearance) throw new NoClearanceException("P6 hat keine Freigabe"); - Actuators.P6 = MotorState.Forward; - ActuatorsChanged = true; - } - - public void StopP6() { - Actuators.P6 = MotorState.Halt; - ActuatorsChanged = true; - } + public void OpenV(int n) => Plc?.OpenV(n); + public void CloseV(int n) => Plc?.CloseV(n); + public void SelectMW1() => Plc?.SelectMW1(); + public void SelectMW2() => Plc?.SelectMW2(); + public void StartTroughAuger() => Plc?.StartTroughAuger(); + public void StopTroughAuger() => Plc?.StopTroughAuger(); + public void StartRebler() => Plc?.StartRebler(); + public void StopRebler() => Plc?.StopRebler(); + public void StartStirrerMW1() => Plc?.StartStirrerMW1(); + public void StopStirrerMW1() => Plc?.StopStirrerMW1(); + public void StartStirrerMW2() => Plc?.StartStirrerMW2(); + public void StopStirrerMW2() => Plc?.StopStirrerMW2(); + public void StartAuger1() => Plc?.StartAuger1(); + public void StopAuger1() => Plc?.StopAuger1(); + public void StartAuger2() => Plc?.StartAuger2(); + public void StopAuger2() => Plc?.StopAuger2(); + public void StartAuger3() => Plc?.StartAuger3(); + public void StopAuger3() => Plc?.StopAuger3(); + public void StartMP1Forward() => Plc?.StartMP1Forward(); + public void StartMP1Backward() => Plc?.StartMP1Backward(); + public void StopMP1() => Plc?.StopMP1(); + public void StartMP2Forward() => Plc?.StartMP2Forward(); + public void StartMP2Backward() => Plc?.StartMP2Backward(); + public void StopMP2() => Plc?.StopMP2(); + public void EnableP12() => Plc?.EnableP12(); + public void DisableP12() => Plc?.DisableP12(); + public void EnableP3() => Plc?.EnableP3(); + public void DisableP3() => Plc?.DisableP3(); + public void EnableP4() => Plc?.EnableP4(); + public void DisableP4() => Plc?.DisableP4(); + public void EnableP5() => Plc?.EnableP5(); + public void DisableP5() => Plc?.DisableP5(); + public void EnableP6() => Plc?.EnableP6(); + public void DisableP6() => Plc?.DisableP6(); } } diff --git a/PamhagenSysCtrl/Helpers/PamhagenPlc.cs b/PamhagenSysCtrl/Helpers/Plc/LegacyPamhagenPlc.cs similarity index 94% rename from PamhagenSysCtrl/Helpers/PamhagenPlc.cs rename to PamhagenSysCtrl/Helpers/Plc/LegacyPamhagenPlc.cs index 70f8107..5b68d2a 100644 --- a/PamhagenSysCtrl/Helpers/PamhagenPlc.cs +++ b/PamhagenSysCtrl/Helpers/Plc/LegacyPamhagenPlc.cs @@ -1,18 +1,18 @@ using System.IO.Ports; -namespace PamhagenSysCtrl.Helpers { - public class PamhagenPlc : IDisposable { +namespace PamhagenSysCtrl.Helpers.Plc { + public class LegacyPamhagenPlc : IDisposable { public record struct SensorStates( long PressureSensors, - FillState FillingLevelMW1, - FillState FillingLevelMW2, + FillState FillLevelMW1, + FillState FillLevelMW2, bool ClearancePress1, bool ClearancePress2, int TroughSelector, double GradationOe, - FillState[] FillingLevelTanks, - MotorState PresseQuerSchnecke + FillState[] FillLevelTanks, + MotorState PressCrossAuger ) { public readonly bool GetDS(int n) => (PressureSensors & (1L << (n - 1))) != 0; } @@ -35,10 +35,10 @@ namespace PamhagenSysCtrl.Helpers { protected readonly T1 Plc; public string PortName => Plc.PortName; - public T1.Status LastPlcStatus => Plc.LastStatus; - public T1Error LastPlcError => Plc.LastError; + public T1.Status LastStatus => Plc.LastStatus; + public T1Error LastError => Plc.LastError; - public PamhagenPlc(string portName, Parity parity = Parity.Odd) { + public LegacyPamhagenPlc(string portName, Parity parity = Parity.Odd) { Plc = new T1(portName, parity); } @@ -95,7 +95,7 @@ namespace PamhagenSysCtrl.Helpers { 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() { + public 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 diff --git a/PamhagenSysCtrl/Helpers/Plc/PamhagenPlc.cs b/PamhagenSysCtrl/Helpers/Plc/PamhagenPlc.cs new file mode 100644 index 0000000..ae7a216 --- /dev/null +++ b/PamhagenSysCtrl/Helpers/Plc/PamhagenPlc.cs @@ -0,0 +1,267 @@ +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(); } } + + protected LegacyPamhagenPlc Plc; + public PamhagenState State { get; private set; } + + public string? PortName => Plc?.PortName; + 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, [ + FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown + ], default); + + public PamhagenPlc(string portName, Parity parity = Parity.Odd) { + Plc = new(portName, parity); + } + + public void Dispose() { + GC.SuppressFinalize(this); + Plc.Dispose(); + } + + public async Task Initialize() { + await Plc.TestConnection(); + Actuators = await Plc.ReadOutputs(); + LastActuators = Actuators; + } + + public async Task Shutdown() { + await Plc.WriteOutputs(new() { Valves = Actuators.Valves }); + } + + public async Task Update() { + UpdateInterlocks(); + + if (Actuators != LastActuators) { + // FIXME race condition! + await Plc.WriteOutputs(Actuators); + LastActuators = Actuators; + } + Sensors = await Plc.ReadInputs(); + State = new(Sensors, () => Actuators, OverridePathClearances, OverrideDryRunClearances); + + 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 (!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(); + } + } + + 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); + 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; + } + + 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; + } + + 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; + } + + 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) throw new NoClearanceException("MP1 hat keine Freigabe"); + 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() { + 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() { + Actuators.MP1 = MotorState.Halt; + if (!ManualControlMode) StopStirrerMW1(); + } + + public void StartMP2Forward() { + if (!State.MP2_HasClearance) throw new NoClearanceException("MP2 hat keine Freigabe"); + 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() { + 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() { + Actuators.MP2 = MotorState.Halt; + if (!ManualControlMode) StopStirrerMW1(); + } + + public void EnableP12() { + if (!State.P12_HasClearance) throw new NoClearanceException("P1/2 hat keine Freigabe"); + Actuators.P12 = MotorState.Forward; + } + + public void DisableP12() { + Actuators.P12 = MotorState.Halt; + } + + public void EnableP3() { + if (!State.P3_HasClearance) throw new NoClearanceException("P3 hat keine Freigabe"); + Actuators.P3 = MotorState.Forward; + } + + public void DisableP3() { + Actuators.P3 = MotorState.Halt; + } + + public void EnableP4() { + if (!State.P4_HasClearance) throw new NoClearanceException("P4 hat keine Freigabe"); + Actuators.P4 = MotorState.Forward; + } + + public void DisableP4() { + Actuators.P4 = MotorState.Halt; + } + + public void EnableP5() { + if (!State.P5_HasClearance) throw new NoClearanceException("P5 hat keine Freigabe"); + Actuators.P5 = MotorState.Forward; + } + + public void DisableP5() { + Actuators.P5 = MotorState.Halt; + } + + public void EnableP6() { + if (!State.P6_HasClearance) throw new NoClearanceException("P6 hat keine Freigabe"); + Actuators.P6 = MotorState.Forward; + } + + public void DisableP6() { + Actuators.P6 = MotorState.Halt; + } + } +} diff --git a/PamhagenSysCtrl/Helpers/Plc/PamhagenState.cs b/PamhagenSysCtrl/Helpers/Plc/PamhagenState.cs new file mode 100644 index 0000000..174f382 --- /dev/null +++ b/PamhagenSysCtrl/Helpers/Plc/PamhagenState.cs @@ -0,0 +1,262 @@ +namespace PamhagenSysCtrl.Helpers.Plc { + public readonly record struct PamhagenState { + + private readonly LegacyPamhagenPlc.SensorStates Sensors; + private readonly Func 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; + 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 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 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 masks) { + return masks.Any(m => state.Valves == m && ~state.PressureSensors == m); + } + + private static bool HasAnyClearance(PamhagenState state, IEnumerable 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(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(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(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(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(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(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)) + ); + } + } +} diff --git a/PamhagenSysCtrl/Helpers/T1.cs b/PamhagenSysCtrl/Helpers/Plc/T1.cs similarity index 97% rename from PamhagenSysCtrl/Helpers/T1.cs rename to PamhagenSysCtrl/Helpers/Plc/T1.cs index 9a3a6c1..ca66a9c 100644 --- a/PamhagenSysCtrl/Helpers/T1.cs +++ b/PamhagenSysCtrl/Helpers/Plc/T1.cs @@ -2,7 +2,7 @@ using System.IO; using System.IO.Ports; using System.Text; -namespace PamhagenSysCtrl.Helpers { +namespace PamhagenSysCtrl.Helpers.Plc { public class T1 : IDisposable { public enum Mode { HALT = 1, RUN = 2, RUN_F = 3, HOLD = 4, ERROR = 6 } @@ -51,7 +51,7 @@ namespace PamhagenSysCtrl.Helpers { Serial.WriteLine(line.ToString()); } - protected async Task<(string, string)> ReceiveResponse(string? expectedCmd = null) { + protected async Task<(string Command, string Data)> ReceiveResponse(string? expectedCmd = null) { var line = Serial.ReadLine(); if (line.Length < 8 || !line.StartsWith("(A") || !line.EndsWith(')') || line[^4] != '&') { throw new FormatException("Invalid response from PLC (T1)"); diff --git a/PamhagenSysCtrl/Helpers/T1Error.cs b/PamhagenSysCtrl/Helpers/Plc/T1Error.cs similarity index 99% rename from PamhagenSysCtrl/Helpers/T1Error.cs rename to PamhagenSysCtrl/Helpers/Plc/T1Error.cs index 75805ad..7e2f7b9 100644 --- a/PamhagenSysCtrl/Helpers/T1Error.cs +++ b/PamhagenSysCtrl/Helpers/Plc/T1Error.cs @@ -1,4 +1,4 @@ -namespace PamhagenSysCtrl.Helpers { +namespace PamhagenSysCtrl.Helpers.Plc { public enum T1Error { UNKNOWN_ERROR = -1, NO_ERROR = 0, diff --git a/PamhagenSysCtrl/Helpers/T1Exception.cs b/PamhagenSysCtrl/Helpers/Plc/T1Exception.cs similarity index 62% rename from PamhagenSysCtrl/Helpers/T1Exception.cs rename to PamhagenSysCtrl/Helpers/Plc/T1Exception.cs index eb9ec9e..a63facc 100644 --- a/PamhagenSysCtrl/Helpers/T1Exception.cs +++ b/PamhagenSysCtrl/Helpers/Plc/T1Exception.cs @@ -1,4 +1,4 @@ -namespace PamhagenSysCtrl.Helpers { +namespace PamhagenSysCtrl.Helpers.Plc { public class T1Exception(string msg) : Exception(msg) { } } diff --git a/PamhagenSysCtrl/Windows/ManualControlWindow.xaml.cs b/PamhagenSysCtrl/Windows/ManualControlWindow.xaml.cs index a000cc1..8f55bf9 100644 --- a/PamhagenSysCtrl/Windows/ManualControlWindow.xaml.cs +++ b/PamhagenSysCtrl/Windows/ManualControlWindow.xaml.cs @@ -29,14 +29,14 @@ namespace PamhagenSysCtrl { if (App.Plant == null || sender is not Button b) return; var i = int.Parse($"{b.Content}"[1..]); if (i == 58) { - if (App.Plant.WantMW1Selected) { + if (App.Plant.State.MW1_WantSelected) { App.Plant.SelectMW2(); } else { App.Plant.SelectMW1(); } } else if (i > 57) { return; - } else if (App.Plant.WantValveOpen(i)) { + } else if (App.Plant.State.Valve_WantOpen(i)) { App.Plant.CloseV(i); } else { App.Plant.OpenV(i); @@ -60,8 +60,8 @@ namespace PamhagenSysCtrl { if (sender is not PamhagenPlant plant) return; for (int i = 1; i <= 58; i++) { - var target = plant.WantValveClosed(i); - var actual = plant.IsValveClosed(i); + var target = plant.State.Valve_WantClosed(i); + var actual = plant.State.Valve_IsClosed(i); if (ValvePanel.Children[i - 1] is not Button b) continue; b.BorderBrush = target ? Brushes.DarkRed : Brushes.DarkGreen; @@ -69,98 +69,97 @@ namespace PamhagenSysCtrl { b.Background = actual ? Brushes.MistyRose : Brushes.MintCream; } - ReblerButton.BorderBrush = plant.IsReblerActive ? Brushes.DarkGreen : Brushes.DarkRed; - ReblerButton.Foreground = plant.IsReblerActive ? Brushes.DarkGreen : Brushes.DarkRed; - ReblerButton.Background = plant.IsReblerActive ? Brushes.MintCream: Brushes.MistyRose; + ReblerButton.BorderBrush = plant.State.Rebler_IsActive ? Brushes.DarkGreen : Brushes.DarkRed; + ReblerButton.Foreground = plant.State.Rebler_IsActive ? Brushes.DarkGreen : Brushes.DarkRed; + ReblerButton.Background = plant.State.Rebler_IsActive ? Brushes.MintCream: Brushes.MistyRose; - TroughAugerButton.BorderBrush = plant.IsTroughAugerActive ? Brushes.DarkGreen : Brushes.DarkRed; - TroughAugerButton.Foreground = plant.IsTroughAugerActive ? Brushes.DarkGreen : Brushes.DarkRed; - TroughAugerButton.Background = plant.IsTroughAugerActive ? Brushes.MintCream : Brushes.MistyRose; + TroughAugerButton.BorderBrush = plant.State.TroughAuger_IsActive ? Brushes.DarkGreen : Brushes.DarkRed; + TroughAugerButton.Foreground = plant.State.TroughAuger_IsActive ? Brushes.DarkGreen : Brushes.DarkRed; + TroughAugerButton.Background = plant.State.TroughAuger_IsActive ? Brushes.MintCream : Brushes.MistyRose; - StirrerMW1Button.BorderBrush = plant.IsStirrerMW1Active ? Brushes.DarkGreen : Brushes.DarkRed; - StirrerMW1Button.Foreground = plant.IsStirrerMW1Active ? Brushes.DarkGreen : Brushes.DarkRed; - StirrerMW1Button.Background = plant.IsStirrerMW1Active ? Brushes.MintCream : Brushes.MistyRose; + StirrerMW1Button.BorderBrush = plant.State.MW1_Stirrer_IsActive ? Brushes.DarkGreen : Brushes.DarkRed; + StirrerMW1Button.Foreground = plant.State.MW1_Stirrer_IsActive ? Brushes.DarkGreen : Brushes.DarkRed; + StirrerMW1Button.Background = plant.State.MW1_Stirrer_IsActive ? Brushes.MintCream : Brushes.MistyRose; - StirrerMW2Button.BorderBrush = plant.IsStirrerMW2Active ? Brushes.DarkGreen : Brushes.DarkRed; - StirrerMW2Button.Foreground = plant.IsStirrerMW2Active ? Brushes.DarkGreen : Brushes.DarkRed; - StirrerMW2Button.Background = plant.IsStirrerMW2Active ? Brushes.MintCream : Brushes.MistyRose; + StirrerMW2Button.BorderBrush = plant.State.MW2_Stirrer_IsActive ? Brushes.DarkGreen : Brushes.DarkRed; + StirrerMW2Button.Foreground = plant.State.MW2_Stirrer_IsActive ? Brushes.DarkGreen : Brushes.DarkRed; + StirrerMW2Button.Background = plant.State.MW2_Stirrer_IsActive ? Brushes.MintCream : Brushes.MistyRose; - Auger1Button.BorderBrush = plant.IsAuger1Active ? Brushes.DarkGreen : Brushes.DarkRed; - Auger1Button.Foreground = plant.IsAuger1Active ? Brushes.DarkGreen : Brushes.DarkRed; - Auger1Button.Background = plant.IsAuger1Active ? Brushes.MintCream : Brushes.MistyRose; + Auger1Button.BorderBrush = plant.State.Auger1_IsActive ? Brushes.DarkGreen : Brushes.DarkRed; + Auger1Button.Foreground = plant.State.Auger1_IsActive ? Brushes.DarkGreen : Brushes.DarkRed; + Auger1Button.Background = plant.State.Auger1_IsActive ? Brushes.MintCream : Brushes.MistyRose; - Auger2Button.BorderBrush = plant.IsAuger2Active ? Brushes.DarkGreen : Brushes.DarkRed; - Auger2Button.Foreground = plant.IsAuger2Active ? Brushes.DarkGreen : Brushes.DarkRed; - Auger2Button.Background = plant.IsAuger2Active ? Brushes.MintCream : Brushes.MistyRose; + Auger2Button.BorderBrush = plant.State.Auger2_IsActive ? Brushes.DarkGreen : Brushes.DarkRed; + Auger2Button.Foreground = plant.State.Auger2_IsActive ? Brushes.DarkGreen : Brushes.DarkRed; + Auger2Button.Background = plant.State.Auger2_IsActive ? Brushes.MintCream : Brushes.MistyRose; - Auger3Button.BorderBrush = plant.IsAuger3Active ? Brushes.DarkGreen : Brushes.DarkRed; - Auger3Button.Foreground = plant.IsAuger3Active ? Brushes.DarkGreen : Brushes.DarkRed; - Auger3Button.Background = plant.IsAuger3Active ? Brushes.MintCream : Brushes.MistyRose; + Auger3Button.BorderBrush = plant.State.Auger3_IsActive ? Brushes.DarkGreen : Brushes.DarkRed; + Auger3Button.Foreground = plant.State.Auger3_IsActive ? Brushes.DarkGreen : Brushes.DarkRed; + Auger3Button.Background = plant.State.Auger3_IsActive ? Brushes.MintCream : Brushes.MistyRose; - MP1BackwardButton.BorderBrush = plant.MP1 == MotorState.Backward ? Brushes.DarkGreen : Brushes.SlateGray; - MP1BackwardButton.Foreground = plant.MP1 == MotorState.Backward ? Brushes.DarkGreen : Brushes.SlateGray; - MP1BackwardButton.Background = plant.MP1 == MotorState.Backward ? Brushes.MintCream : Brushes.WhiteSmoke; - MP1HaltButton.BorderBrush = plant.MP1 == MotorState.Halt ? Brushes.DarkRed : Brushes.SlateGray; - MP1HaltButton.Foreground = plant.MP1 == MotorState.Halt ? Brushes.DarkRed : Brushes.SlateGray; - MP1HaltButton.Background = plant.MP1 == MotorState.Halt ? Brushes.MistyRose : Brushes.WhiteSmoke; - MP1ForwardButton.BorderBrush = plant.MP1 == MotorState.Forward ? Brushes.DarkGreen : Brushes.SlateGray; - MP1ForwardButton.Foreground = plant.MP1 == MotorState.Forward ? Brushes.DarkGreen : Brushes.SlateGray; - MP1ForwardButton.Background = plant.MP1 == MotorState.Forward ? Brushes.MintCream: Brushes.WhiteSmoke; + MP1BackwardButton.BorderBrush = plant.State.MP1_State == MotorState.Backward ? Brushes.DarkGreen : Brushes.SlateGray; + MP1BackwardButton.Foreground = plant.State.MP1_State == MotorState.Backward ? Brushes.DarkGreen : Brushes.SlateGray; + MP1BackwardButton.Background = plant.State.MP1_State == MotorState.Backward ? Brushes.MintCream : Brushes.WhiteSmoke; + MP1HaltButton.BorderBrush = plant.State.MP1_State == MotorState.Halt ? Brushes.DarkRed : Brushes.SlateGray; + MP1HaltButton.Foreground = plant.State.MP1_State == MotorState.Halt ? Brushes.DarkRed : Brushes.SlateGray; + MP1HaltButton.Background = plant.State.MP1_State == MotorState.Halt ? Brushes.MistyRose : Brushes.WhiteSmoke; + MP1ForwardButton.BorderBrush = plant.State.MP1_State == MotorState.Forward ? Brushes.DarkGreen : Brushes.SlateGray; + MP1ForwardButton.Foreground = plant.State.MP1_State == MotorState.Forward ? Brushes.DarkGreen : Brushes.SlateGray; + MP1ForwardButton.Background = plant.State.MP1_State == MotorState.Forward ? Brushes.MintCream: Brushes.WhiteSmoke; - MP2BackwardButton.BorderBrush = plant.MP2 == MotorState.Backward ? Brushes.DarkGreen : Brushes.SlateGray; - MP2BackwardButton.Foreground = plant.MP2 == MotorState.Backward ? Brushes.DarkGreen : Brushes.SlateGray; - MP2BackwardButton.Background = plant.MP2 == MotorState.Backward ? Brushes.MintCream : Brushes.WhiteSmoke; - MP2HaltButton.BorderBrush = plant.MP2 == MotorState.Halt ? Brushes.DarkRed : Brushes.SlateGray; - MP2HaltButton.Foreground = plant.MP2 == MotorState.Halt ? Brushes.DarkRed : Brushes.SlateGray; - MP2HaltButton.Background = plant.MP2 == MotorState.Halt ? Brushes.MistyRose : Brushes.WhiteSmoke; - MP2ForwardButton.BorderBrush = plant.MP2 == MotorState.Forward ? Brushes.DarkGreen : Brushes.SlateGray; - MP2ForwardButton.Foreground = plant.MP2 == MotorState.Forward ? Brushes.DarkGreen : Brushes.SlateGray; - MP2ForwardButton.Background = plant.MP2 == MotorState.Forward ? Brushes.MintCream : Brushes.WhiteSmoke; + MP2BackwardButton.BorderBrush = plant.State.MP2_State == MotorState.Backward ? Brushes.DarkGreen : Brushes.SlateGray; + MP2BackwardButton.Foreground = plant.State.MP2_State == MotorState.Backward ? Brushes.DarkGreen : Brushes.SlateGray; + MP2BackwardButton.Background = plant.State.MP2_State == MotorState.Backward ? Brushes.MintCream : Brushes.WhiteSmoke; + MP2HaltButton.BorderBrush = plant.State.MP2_State == MotorState.Halt ? Brushes.DarkRed : Brushes.SlateGray; + MP2HaltButton.Foreground = plant.State.MP2_State == MotorState.Halt ? Brushes.DarkRed : Brushes.SlateGray; + MP2HaltButton.Background = plant.State.MP2_State == MotorState.Halt ? Brushes.MistyRose : Brushes.WhiteSmoke; + MP2ForwardButton.BorderBrush = plant.State.MP2_State == MotorState.Forward ? Brushes.DarkGreen : Brushes.SlateGray; + MP2ForwardButton.Foreground = plant.State.MP2_State == MotorState.Forward ? Brushes.DarkGreen : Brushes.SlateGray; + MP2ForwardButton.Background = plant.State.MP2_State == MotorState.Forward ? Brushes.MintCream : Brushes.WhiteSmoke; - P12Button.BorderBrush = plant.P12 == MotorState.Forward ? Brushes.DarkGreen : Brushes.DarkRed; - P12Button.Foreground = plant.P12 == MotorState.Forward ? Brushes.DarkGreen : Brushes.DarkRed; - P12Button.Background = plant.P12 == MotorState.Forward ? Brushes.MintCream : Brushes.MistyRose; + P12Button.BorderBrush = plant.State.P12_State == MotorState.Forward ? Brushes.DarkGreen : Brushes.DarkRed; + P12Button.Foreground = plant.State.P12_State == MotorState.Forward ? Brushes.DarkGreen : Brushes.DarkRed; + P12Button.Background = plant.State.P12_State == MotorState.Forward ? Brushes.MintCream : Brushes.MistyRose; - P3Button.BorderBrush = plant.P3 == MotorState.Forward ? Brushes.DarkGreen : Brushes.DarkRed; - P3Button.Foreground = plant.P3 == MotorState.Forward ? Brushes.DarkGreen : Brushes.DarkRed; - P3Button.Background = plant.P3 == MotorState.Forward ? Brushes.MintCream : Brushes.MistyRose; + P3Button.BorderBrush = plant.State.P3_State == MotorState.Forward ? Brushes.DarkGreen : Brushes.DarkRed; + P3Button.Foreground = plant.State.P3_State == MotorState.Forward ? Brushes.DarkGreen : Brushes.DarkRed; + P3Button.Background = plant.State.P3_State == MotorState.Forward ? Brushes.MintCream : Brushes.MistyRose; - P4Button.BorderBrush = plant.P4 == MotorState.Forward ? Brushes.DarkGreen : Brushes.DarkRed; - P4Button.Foreground = plant.P4 == MotorState.Forward ? Brushes.DarkGreen : Brushes.DarkRed; - P4Button.Background = plant.P4 == MotorState.Forward ? Brushes.MintCream : Brushes.MistyRose; + P4Button.BorderBrush = plant.State.P4_State == MotorState.Forward ? Brushes.DarkGreen : Brushes.DarkRed; + P4Button.Foreground = plant.State.P4_State == MotorState.Forward ? Brushes.DarkGreen : Brushes.DarkRed; + P4Button.Background = plant.State.P4_State == MotorState.Forward ? Brushes.MintCream : Brushes.MistyRose; - P5Button.BorderBrush = plant.P5 == MotorState.Forward ? Brushes.DarkGreen : Brushes.DarkRed; - P5Button.Foreground = plant.P5 == MotorState.Forward ? Brushes.DarkGreen : Brushes.DarkRed; - P5Button.Background = plant.P5 == MotorState.Forward ? Brushes.MintCream : Brushes.MistyRose; + P5Button.BorderBrush = plant.State.P5_State == MotorState.Forward ? Brushes.DarkGreen : Brushes.DarkRed; + P5Button.Foreground = plant.State.P5_State == MotorState.Forward ? Brushes.DarkGreen : Brushes.DarkRed; + P5Button.Background = plant.State.P5_State == MotorState.Forward ? Brushes.MintCream : Brushes.MistyRose; - P6Button.BorderBrush = plant.P6 == MotorState.Forward ? Brushes.DarkGreen : Brushes.DarkRed; - P6Button.Foreground = plant.P6 == MotorState.Forward ? Brushes.DarkGreen : Brushes.DarkRed; - P6Button.Background = plant.P6 == MotorState.Forward ? Brushes.MintCream : Brushes.MistyRose; + P6Button.BorderBrush = plant.State.P6_State == MotorState.Forward ? Brushes.DarkGreen : Brushes.DarkRed; + P6Button.Foreground = plant.State.P6_State == MotorState.Forward ? Brushes.DarkGreen : Brushes.DarkRed; + P6Button.Background = plant.State.P6_State == MotorState.Forward ? Brushes.MintCream : Brushes.MistyRose; Status.Text = $""" - Gradation °Oe: {plant.GradationOe:N2} - Presse Querschnecke: {plant.PresseQuerSchnecke} + Presse Querschnecke: {plant.State.PressCrossAuger_State} Füllstände: - Maischewanne 1: {plant.FillLevelMW1} (ausgewählt: {plant.WantMW1Selected}, Status: {plant.IsMW1Selected}) - Maischewanne 2: {plant.FillLevelMW2} (ausgewählt: {plant.WantMW2Selected}, Status: {plant.IsMW2Selected}) - Presse 1: {plant.Press1HasClearance} - Presse 2: {plant.Press2HasClearance} - Tank 1: {plant.FillLevelT1} - Tank 2: {plant.FillLevelT2} - Tank 3: {plant.FillLevelT3} - Tank 4: {plant.FillLevelT4} - Tank 5: {plant.FillLevelT5} - Tank 6: {plant.FillLevelT6} - Tank 7: {plant.FillLevelT7} - Tank 8: {plant.FillLevelT8} - Tank 9: {plant.FillLevelT9} + Maischewanne 1: {plant.State.MW2_FillLevel} (ausgewählt: {plant.State.MW1_WantSelected}, Status: {plant.State.MW1_IsSelected}) + Maischewanne 2: {plant.State.MW2_FillLevel} (ausgewählt: {plant.State.MW2_WantSelected}, Status: {plant.State.MW2_IsSelected}) + Presse 1: {plant.State.Press1_HasClearance} + Presse 2: {plant.State.Press2_HasClearance} + Tank 1: {plant.State.T1_FillLevel} + Tank 2: {plant.State.T2_FillLevel} + Tank 3: {plant.State.T3_FillLevel} + Tank 4: {plant.State.T4_FillLevel} + Tank 5: {plant.State.T5_FillLevel} + Tank 6: {plant.State.T6_FillLevel} + Tank 7: {plant.State.T7_FillLevel} + Tank 8: {plant.State.T8_FillLevel} + Tank 9: {plant.State.T9_FillLevel} """; } private void TroughAugerButton_Click(object? sender, RoutedEventArgs? evt) { if (App.Plant == null) return; try { - if (App.Plant.IsTroughAugerActive) { + if (App.Plant.State.TroughAuger_IsActive) { App.Plant.StopTroughAuger(); } else { App.Plant.StartTroughAuger(); @@ -173,7 +172,7 @@ namespace PamhagenSysCtrl { private void ReblerButton_Click(object? sender, RoutedEventArgs? evt) { if (App.Plant == null) return; try { - if (App.Plant.IsReblerActive) { + if (App.Plant.State.Rebler_IsActive) { App.Plant.StopRebler(); } else { App.Plant.StartRebler(); @@ -186,7 +185,7 @@ namespace PamhagenSysCtrl { private void StirrerMW1Button_Click(object? sender, RoutedEventArgs? evt) { if (App.Plant == null) return; try { - if (App.Plant.IsStirrerMW1Active) { + if (App.Plant.State.MW1_Stirrer_IsActive) { App.Plant.StopStirrerMW1(); } else { App.Plant.StartStirrerMW1(); @@ -199,7 +198,7 @@ namespace PamhagenSysCtrl { private void StirrerMW2Button_Click(object? sender, RoutedEventArgs? evt) { if (App.Plant == null) return; try { - if (App.Plant.IsStirrerMW2Active) { + if (App.Plant.State.MW2_Stirrer_IsActive) { App.Plant.StopStirrerMW2(); } else { App.Plant.StartStirrerMW2(); @@ -212,7 +211,7 @@ namespace PamhagenSysCtrl { private void Auger1Button_Click(object? sender, RoutedEventArgs? evt) { if (App.Plant == null) return; try { - if (App.Plant.IsAuger1Active) { + if (App.Plant.State.Auger1_IsActive) { App.Plant.StopAuger1(); } else { App.Plant.StartAuger1(); @@ -225,7 +224,7 @@ namespace PamhagenSysCtrl { private void Auger2Button_Click(object? sender, RoutedEventArgs? evt) { if (App.Plant == null) return; try { - if (App.Plant.IsAuger2Active) { + if (App.Plant.State.Auger2_IsActive) { App.Plant.StopAuger2(); } else { App.Plant.StartAuger2(); @@ -238,7 +237,7 @@ namespace PamhagenSysCtrl { private void Auger3Button_Click(object? sender, RoutedEventArgs? evt) { if (App.Plant == null) return; try { - if (App.Plant.IsAuger3Active) { + if (App.Plant.State.Auger3_IsActive) { App.Plant.StopAuger3(); } else { App.Plant.StartAuger3(); @@ -306,10 +305,10 @@ namespace PamhagenSysCtrl { private void P12Button_Click(object? sender, RoutedEventArgs? evt) { if (App.Plant == null) return; try { - if (App.Plant.P12 == MotorState.Forward) { - App.Plant.StopP12(); + if (App.Plant.State.P12_IsActive) { + App.Plant.DisableP12(); } else { - App.Plant.StartP12(); + App.Plant.EnableP12(); } } catch (NoClearanceException exc) { MessageBox.Show($"{exc.Message}", "Keine Freigabe", MessageBoxButton.OK, MessageBoxImage.Error); @@ -319,10 +318,10 @@ namespace PamhagenSysCtrl { private void P3Button_Click(object? sender, RoutedEventArgs? evt) { if (App.Plant == null) return; try { - if (App.Plant.P3 == MotorState.Forward) { - App.Plant.StopP3(); + if (App.Plant.State.P3_IsActive) { + App.Plant.DisableP3(); } else { - App.Plant.StartP3(); + App.Plant.EnableP3(); } } catch (NoClearanceException exc) { MessageBox.Show($"{exc.Message}", "Keine Freigabe", MessageBoxButton.OK, MessageBoxImage.Error); @@ -332,10 +331,10 @@ namespace PamhagenSysCtrl { private void P4Button_Click(object? sender, RoutedEventArgs? evt) { if (App.Plant == null) return; try { - if (App.Plant.P4 == MotorState.Forward) { - App.Plant.StopP4(); + if (App.Plant.State.P4_IsActive) { + App.Plant.DisableP4(); } else { - App.Plant.StartP4(); + App.Plant.EnableP4(); } } catch (NoClearanceException exc) { MessageBox.Show($"{exc.Message}", "Keine Freigabe", MessageBoxButton.OK, MessageBoxImage.Error); @@ -345,10 +344,10 @@ namespace PamhagenSysCtrl { private void P5Button_Click(object? sender, RoutedEventArgs? evt) { if (App.Plant == null) return; try { - if (App.Plant.P5 == MotorState.Forward) { - App.Plant.StopP5(); + if (App.Plant.State.P5_IsActive) { + App.Plant.DisableP5(); } else { - App.Plant.StartP5(); + App.Plant.EnableP5(); } } catch (NoClearanceException exc) { MessageBox.Show($"{exc.Message}", "Keine Freigabe", MessageBoxButton.OK, MessageBoxImage.Error); @@ -358,10 +357,10 @@ namespace PamhagenSysCtrl { private void P6Button_Click(object? sender, RoutedEventArgs? evt) { if (App.Plant == null) return; try { - if (App.Plant.P6 == MotorState.Forward) { - App.Plant.StopP6(); + if (App.Plant.State.P6_IsActive) { + App.Plant.DisableP6(); } else { - App.Plant.StartP6(); + App.Plant.EnableP6(); } } catch (NoClearanceException exc) { MessageBox.Show($"{exc.Message}", "Keine Freigabe", MessageBoxButton.OK, MessageBoxImage.Error); diff --git a/PamhagenSysCtrl/Windows/PlantSchemeWindow.xaml.cs b/PamhagenSysCtrl/Windows/PlantSchemeWindow.xaml.cs index 52e0509..6ec87b2 100644 --- a/PamhagenSysCtrl/Windows/PlantSchemeWindow.xaml.cs +++ b/PamhagenSysCtrl/Windows/PlantSchemeWindow.xaml.cs @@ -1,5 +1,6 @@ using PamhagenSysCtrl.Helpers; using PamhagenSysCtrl.Helpers.Pipeline; +using PamhagenSysCtrl.Helpers.Plc; using System.Diagnostics; using System.Windows; using System.Windows.Controls; @@ -385,7 +386,7 @@ namespace PamhagenSysCtrl.Windows { App.Plant?.StartRebler(); } } else if (c is Switcher switcher) { - if (App.Plant?.WantMW1Selected ?? false) { + if (App.Plant?.State.MW1_WantSelected ?? false) { App.Plant?.SelectMW2(); } else { App.Plant?.SelectMW1(); @@ -425,33 +426,33 @@ namespace PamhagenSysCtrl.Windows { } } else if (pump == Graph.P12) { if (pump.IsActive) { - App.Plant?.StopP12(); + App.Plant?.DisableP12(); } else { - App.Plant?.StartP12(); + App.Plant?.EnableP12(); } } else if (pump == Graph.P3) { if (pump.IsActive) { - App.Plant?.StopP3(); + App.Plant?.DisableP3(); } else { - App.Plant?.StartP3(); + App.Plant?.EnableP3(); } } else if (pump == Graph.P4) { if (pump.IsActive) { - App.Plant?.StopP4(); + App.Plant?.DisableP4(); } else { - App.Plant?.StartP4(); + App.Plant?.EnableP4(); } } else if (pump == Graph.P5) { if (pump.IsActive) { - App.Plant?.StopP5(); + App.Plant?.DisableP5(); } else { - App.Plant?.StartP5(); + App.Plant?.EnableP5(); } } else if (pump == Graph.P6) { if (pump.IsActive) { - App.Plant?.StopP6(); + App.Plant?.DisableP6(); } else { - App.Plant?.StartP6(); + App.Plant?.EnableP6(); } } } @@ -497,14 +498,14 @@ namespace PamhagenSysCtrl.Windows { } } - SetFastSelectButton(FastSelectButton_EntryTrough_Start, App.Plant?.IsTroughAugerActive ?? false ? 1 : 0, !(App.Plant?.TroughAugerHasClearance ?? false)); - SetFastSelectButton(FastSelectButton_EntryTrough_Stop, App.Plant?.IsTroughAugerActive ?? false ? 0 : 2); - SetFastSelectButton(FastSelectButton_MW1, App.Plant?.IsMW1Selected ?? false ? 1 : 0); - SetFastSelectButton(FastSelectButton_MW2, App.Plant?.IsMW2Selected ?? false ? 1 : 0); - SetFastSelectButton(FastSelectButton_MP1_Forward, App.Plant?.MP1 == MotorState.Forward ? 1 : 0, !(App.Plant?.MP1HasClearance ?? false)); - SetFastSelectButton(FastSelectButton_MP1_Stop, App.Plant?.MP1 == MotorState.Halt ? 2 : 0); - SetFastSelectButton(FastSelectButton_MP2_Forward, App.Plant?.MP2 == MotorState.Forward ? 1 : 0, !(App.Plant?.MP2HasClearance ?? false)); - SetFastSelectButton(FastSelectButton_MP2_Stop, App.Plant?.MP2 == MotorState.Halt ? 2 : 0); + SetFastSelectButton(FastSelectButton_EntryTrough_Start, App.Plant?.State.TroughAuger_IsActive ?? false ? 1 : 0, !(App.Plant?.State.TroughAuger_HasClearance ?? false)); + SetFastSelectButton(FastSelectButton_EntryTrough_Stop, App.Plant?.State.TroughAuger_IsActive ?? false ? 0 : 2); + SetFastSelectButton(FastSelectButton_MW1, App.Plant?.State.MW1_IsSelected?? false ? 1 : 0); + SetFastSelectButton(FastSelectButton_MW2, App.Plant?.State.MW2_IsSelected ?? false ? 1 : 0); + SetFastSelectButton(FastSelectButton_MP1_Forward, App.Plant?.State.MP1_State == MotorState.Forward ? 1 : 0, !(App.Plant?.State.MP1_HasClearance ?? false)); + SetFastSelectButton(FastSelectButton_MP1_Stop, App.Plant?.State.MP1_State == MotorState.Halt ? 2 : 0); + SetFastSelectButton(FastSelectButton_MP2_Forward, App.Plant?.State.MP2_State == MotorState.Forward ? 1 : 0, !(App.Plant?.State.MP2_HasClearance ?? false)); + SetFastSelectButton(FastSelectButton_MP2_Stop, App.Plant?.State.MP2_State == MotorState.Halt ? 2 : 0); _hover = Graph.GetHover(pos.X, pos.Y, RoundedScale); var hoverButtons = FastSelectPaths.Where(b => b.Key.IsMouseOver).ToDictionary(); @@ -585,8 +586,8 @@ namespace PamhagenSysCtrl.Windows { if (App.Plant is PamhagenPlant p) { Status_Connection.Text = $"Ja ({p.PlcPortName}" + (p.LastRttNs != null ? $", {p.LastRttNs / 1000000.0:N0} ms" : "") + ")"; Status_Plc.Text = $"{p.LastPlcStatus?.Mode} ({p.LastPlcStatus?.Flags:X3}) / {p.LastPlcError?.GetName()} ({(int?)p.LastPlcError:0000})"; - Status_CompressedAir.Text = Enumerable.Range(1, 57).Any(p.IsValveClosed) ? "Ein" : Enumerable.Range(1, 57).Any(p.WantValveClosed) ? "Aus" : "?"; - Status_ValvesOpen.Text = Enumerable.Range(1, 57).Count(p.IsValveOpen) + " / " + Enumerable.Range(1, 57).Count(p.WantValveOpen) + " / 57"; + Status_CompressedAir.Text = Enumerable.Range(1, 57).Any(p.State.Valve_IsClosed) ? "Ein" : Enumerable.Range(1, 57).Any(p.State.Valve_WantClosed) ? "Aus" : "?"; + Status_ValvesOpen.Text = Enumerable.Range(1, 57).Count(p.State.Valve_IsOpen) + " / " + Enumerable.Range(1, 57).Count(p.State.Valve_WantOpen) + " / 57"; } UpdateScheme(Mouse.GetPosition(SchemeCanvas), Mouse.LeftButton == MouseButtonState.Pressed); } @@ -611,7 +612,7 @@ namespace PamhagenSysCtrl.Windows { foreach (var src in sources) { var paths = new List(); foreach (var sink in sinks) { - paths.AddRange(Graph.GetPaths(src, sink, null, (n) => n is not ISink && (n is not ControlledValve v || p.WantValveOpen(v.Nr)))); + paths.AddRange(Graph.GetPaths(src, sink, null, (n) => n is not ISink && (n is not ControlledValve v || p.State.Valve_WantOpen(v.Nr)))); } if (paths.Count == 1) yield return paths.First();