diff --git a/PamhagenSysCtrl/App.xaml.cs b/PamhagenSysCtrl/App.xaml.cs index d5d1b79..d250919 100644 --- a/PamhagenSysCtrl/App.xaml.cs +++ b/PamhagenSysCtrl/App.xaml.cs @@ -36,15 +36,11 @@ namespace PamhagenSysCtrl { protected override async void OnStartup(StartupEventArgs evt) { Version = new Version(typeof(App).GetTypeInfo().Assembly.GetCustomAttribute()?.InformationalVersion.Split('+')[0] ?? "0.0.0"); + Plant = new(Config.PlcPort); try { - Plant = new(Config.PlcPort); await Plant.StartThread(); } catch (Exception exc) { - Plant?.Dispose(); - Plant = null; - var str = $"Beim Aufbauen einer Verbindung zur SPS ist ein Fehler aufgetreten:\n\n{exc.GetType().Name}: {exc.Message}"; - if (exc.InnerException != null) str += $"\n\n{exc.InnerException.GetType().Name}: {exc.InnerException.Message}"; - MessageBox.Show(str, "SPS-Fehler", MessageBoxButton.OK, MessageBoxImage.Error); + // TODO } base.OnStartup(evt); diff --git a/PamhagenSysCtrl/Helpers/PamhagenPlant.cs b/PamhagenSysCtrl/Helpers/PamhagenPlant.cs index a2d633e..24c051e 100644 --- a/PamhagenSysCtrl/Helpers/PamhagenPlant.cs +++ b/PamhagenSysCtrl/Helpers/PamhagenPlant.cs @@ -1,10 +1,15 @@ +using PamhagenSysCtrl.Helpers.Pipeline; using System.IO; using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; namespace PamhagenSysCtrl.Helpers { public class PamhagenPlant : IDisposable { protected PamhagenPlc? Plc; + protected Xbe24Keyboard? Keyboard; + public PamhagenGraph Graph; protected bool IsRunning = true; protected Thread? BackgroundThread; @@ -78,8 +83,25 @@ namespace PamhagenSysCtrl.Helpers { public bool IsP6Active => Actuators.P6 == MotorState.Forward; public PamhagenPlant(string portName) { - Plc = new(portName); + try { + Plc = new(portName); + } catch (Exception exc) { + var str = $"Beim Aufbauen einer Verbindung zur SPS ist ein Fehler aufgetreten:\n\n{exc.GetType().Name}: {exc.Message}"; + if (exc.InnerException != null) str += $"\n\n{exc.InnerException.GetType().Name}: {exc.InnerException.Message}"; + MessageBox.Show(str, "SPS-Fehler", MessageBoxButton.OK, MessageBoxImage.Error); + } + Graph = new(); + + try { + Keyboard = new(); + Keyboard.KeyDown += Keyboard_KeyDown; + Keyboard.KeysChanged += Keyboard_KeysChanged; + Keyboard.SetFashFrequency(250); + Keyboard.SetBacklight(Color.FromRgb(4, 4, 4)); + } catch { } + Update += OnUpdate; + Update += UpdateKeyboardBacklights; } protected virtual void RaiseUpdateEvent(PlantEventArgs evt) { @@ -96,6 +118,10 @@ namespace PamhagenSysCtrl.Helpers { 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(); @@ -121,6 +147,66 @@ namespace PamhagenSysCtrl.Helpers { 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(0, 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(1, !plant.IsReblerActive ? Color.FromRgb(255, 0, 0) : Color.FromRgb(32, 4, 4)); + + k.SetBacklight(6, !plant.TroughAugerHasClearance ? k.IsKeyPressed(6) ? Color.FromRgb(255, 0, 0) : Color.FromRgb(32, 4, 4) : plant.IsTroughAugerActive ? Color.FromRgb(0, 255, 0) : Color.FromRgb(4, 32, 4)); + k.SetBacklight(7, !plant.IsTroughAugerActive ? Color.FromRgb(255, 0, 0) : Color.FromRgb(32, 4, 4)); + + k.SetBacklight(2, plant.WantMW2Selected || plant.IsMW2Selected ? Color.FromRgb(0, 0, 255) : Color.FromRgb(4, 4, 16), plant.WantMW2Selected && !plant.IsMW2Selected); + k.SetBacklight(8, plant.WantMW1Selected || plant.IsMW1Selected ? Color.FromRgb(0, 0, 255) : Color.FromRgb(4, 4, 16), plant.WantMW1Selected && !plant.IsMW1Selected); + + var buttons = new Dictionary { + [12] = (Graph.MW1, Graph.MP1, Graph.Press1), + [18] = (Graph.MW2, Graph.MP2, Graph.Press1), + [13] = (Graph.MW1, Graph.MP1, Graph.Press2), + [19] = (Graph.MW2, Graph.MP2, Graph.Press2), + [14] = (Graph.MW1, Graph.MP1, Graph.Tank1), + [20] = (Graph.MW2, Graph.MP2, Graph.Tank1), + [15] = (Graph.MW1, Graph.MP1, Graph.Tank2), + [21] = (Graph.MW2, Graph.MP2, Graph.Tank2), + [16] = (Graph.MW1, Graph.MP1, Graph.Tank3), + [22] = (Graph.MW2, Graph.MP2, Graph.Tank3), + [17] = (Graph.MW1, Graph.MP1, Graph.Tank4), + [23] = (Graph.MW2, Graph.MP2, Graph.Tank4), + }; + + bool preparingMP1 = false, preparingMP2 = false; + bool highlightOffMP1 = false, highlightOffMP2 = false; + foreach (var (idx, d) in buttons) { + var isRunning = d.Pump.IsActive; + if (plant.Graph.SelectedPaths.Select(p => (Pipeline.Path?)p).FirstOrDefault(p => p?.Start == d.Source && p?.Hops.Last().Node == d.Sink) is Pipeline.Path path) { + // TODO + var isReady = path.Hops.All(h => h.Node is not Valve v || v.IsOpen == v.WantOpen); + if (!isReady && d.Pump == Graph.MP1) preparingMP1 = true; + if (!isReady && d.Pump == Graph.MP2) preparingMP2 = true; + var isDefault = !path.Hops.SkipLast(1).Any(h => h.Node is not Valve && h.Node is not PipeJoin && h.Node is not Pump && h.Node is not CenterValve); + k.SetBacklight(idx, isRunning ? isDefault ? Color.FromRgb(0, 255, 255) : Color.FromRgb(255, 64, 255) : isDefault ? Color.FromRgb(0, 0, 255) : Color.FromRgb(128, 0, 128), !isReady); + } else { + var isFree = Graph.GetFreePath([d.Source, d.Sink], overrideStart: true) != null; + if (isRunning && k.IsKeyPressed(idx) && d.Pump == Graph.MP1) highlightOffMP1 = true; + if (isRunning && k.IsKeyPressed(idx) && d.Pump == Graph.MP2) highlightOffMP2 = true; + k.SetBacklight(idx, isRunning ? k.IsKeyPressed(idx) ? Color.FromRgb(255, 0, 0) : Color.FromRgb(4, 4, 4) : isFree ? Color.FromRgb(8, 8, 32) : k.IsKeyPressed(idx) ? Color.FromRgb(255, 0, 0) : Color.FromRgb(4, 4, 4)); + } + } + + if (!plant.MP2HasPathClearance && plant.MP2HasDryRunClearance && preparingMP2) { + k.SetBacklight(4, Color.FromRgb(4, 32, 4), true); + } else { + k.SetBacklight(4, !plant.MP2HasClearance ? k.IsKeyPressed(4) ? Color.FromRgb(255, 0, 0) : Color.FromRgb(32, 4, 4) : plant.IsMP2Active ? Color.FromRgb(0, 255, 0) : Color.FromRgb(4, 32, 4)); + } + if (!plant.MP1HasPathClearance && plant.MP1HasDryRunClearance && preparingMP1) { + k.SetBacklight(10, Color.FromRgb(4, 32, 4), true); + } else { + k.SetBacklight(10, !plant.MP1HasClearance ? k.IsKeyPressed(10) ? Color.FromRgb(255, 0, 0) : Color.FromRgb(32, 4, 4) : plant.IsMP1Active ? Color.FromRgb(0, 255, 0) : Color.FromRgb(4, 32, 4)); + } + k.SetBacklight(5, highlightOffMP2 ? Color.FromRgb(255, 64, 0) : !plant.IsMP2Active ? Color.FromRgb(255, 0, 0) : Color.FromRgb(32, 4, 4)); + k.SetBacklight(11, highlightOffMP1 ? Color.FromRgb(255, 64, 0) : !plant.IsMP1Active ? Color.FromRgb(255, 0, 0) : Color.FromRgb(32, 4, 4)); + } + public async Task StartThread() { if (Plc == null) throw new Exception(); await Plc.TestConnection(); @@ -133,6 +219,7 @@ namespace PamhagenSysCtrl.Helpers { IsRunning = false; BackgroundThread?.Join(); Plc?.Dispose(); + Keyboard?.Dispose(); GC.SuppressFinalize(this); } @@ -171,6 +258,114 @@ namespace PamhagenSysCtrl.Helpers { } } + public void InitializePaths() { + var add = ReconstructSelectedPaths().ToList(); + var remove = Graph.SelectedPaths.ToList(); + foreach (var p in remove) Graph.RemovePath(p); + for (int i = 1; i <= 57; i++) { + App.Plant?.CloseV(i); + } + foreach (var p in add) { + if (p.Hops.Any(h => h.Node is Valve v && v.IsLocked)) continue; + Graph.AddPath(p); + } + } + + public IEnumerable ReconstructSelectedPaths() { + if (App.Plant is not PamhagenPlant p) yield break; + var sources = Graph.Nodes.Where(n => n is ISource).ToList(); + var sinks = Graph.Nodes.Where(n => n is ISink).ToList(); + 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 Valve v || p.WantValveOpen(v.Nr)))); + } + if (paths.Count == 1) + yield return paths.First(); + } + } + + public void FastSelect(ISource source, ISink sink) { + if (Graph.SelectedPaths.Select(p => (Pipeline.Path?)p).FirstOrDefault(p => p?.Start == source, null) is Pipeline.Path p1) { + if (p1.Hops.Any(h => h.Node is Pump p && p.IsActive)) + throw new NoClearanceException("Pumpe aktiv"); + Graph.RemovePath(p1); + if (p1.Hops.Last().Node == sink) + return; + } + if (Graph.GetFreePath([source, sink]) is Pipeline.Path p2) { + Graph.AddPath(p2); + } + } + + private void Keyboard_KeyDown(object? sender, int keyIndex) { + App.MainDispatcher?.BeginInvoke(() => { + try { + if (keyIndex == 0) { + StartRebler(); + } else if (keyIndex == 1) { + if (IsReblerActive) { + StopRebler(); + } else { + StopAuger1(); + if (!IsPresseQuerSchneckeActive) { + StopAuger2(); + StopAuger3(); + } + } + } else if (keyIndex == 6) { + StartTroughAuger(); + } else if (keyIndex == 7) { + StopTroughAuger(); + } else if (keyIndex == 2) { + SelectMW2(); + } else if (keyIndex == 8) { + SelectMW1(); + } else if (keyIndex == 4) { + StartMP2Forward(); + } else if (keyIndex == 5) { + StopMP2(); + } else if (keyIndex == 10) { + StartMP1Forward(); + } else if (keyIndex == 11) { + StopMP1(); + } else if (keyIndex == 12) { + FastSelect(Graph.MW1, Graph.Press1); + } else if (keyIndex == 13) { + FastSelect(Graph.MW1, Graph.Press2); + } else if (keyIndex == 14) { + FastSelect(Graph.MW1, Graph.Tank1); + } else if (keyIndex == 15) { + FastSelect(Graph.MW1, Graph.Tank2); + } else if (keyIndex == 16) { + FastSelect(Graph.MW1, Graph.Tank3); + } else if (keyIndex == 17) { + FastSelect(Graph.MW1, Graph.Tank4); + } else if (keyIndex == 18) { + FastSelect(Graph.MW2, Graph.Press1); + } else if (keyIndex == 19) { + FastSelect(Graph.MW2, Graph.Press2); + } else if (keyIndex == 20) { + FastSelect(Graph.MW2, Graph.Tank1); + } else if (keyIndex == 21) { + FastSelect(Graph.MW2, Graph.Tank2); + } else if (keyIndex == 22) { + FastSelect(Graph.MW2, Graph.Tank3); + } else if (keyIndex == 23) { + FastSelect(Graph.MW2, Graph.Tank4); + } + } catch (NoClearanceException) { + // Keyboard?.SetBacklight(keyIndex, Color.FromRgb(255, 0, 0)); + } catch (Exception) { + // ignore + } + }); + } + + private void Keyboard_KeysChanged(object? sender, Xbe24Keyboard.KeyState state) { + RaiseUpdateEvent(new()); + } + protected bool HasWTClearanceAA => (AreValvesOpen(12) && AreValvesClosed(15, 16, 20)) || // bypass (AreValvesOpen(15, 18, 16) && AreValvesClosed(11, 12, 14, 17, 19, 20)) || // WT1 @@ -241,7 +436,9 @@ namespace PamhagenSysCtrl.Helpers { (a && ((HasWTClearanceAA && HasT9ClearanceA) || (HasWTClearanceAB && HasT9ClearanceB))) || (b && ((HasWTClearanceBA && HasT9ClearanceA) || (HasWTClearanceBB && HasT9ClearanceB))); - public bool MP1HasClearance => (OverrideDryRunClearances || FillLevelMW1 != FillState.Empty) && ( + public bool MP1HasClearance => MP1HasDryRunClearance && MP1HasPathClearance; + public bool MP1HasDryRunClearance => (OverrideDryRunClearances || FillLevelMW1 != FillState.Empty); + public bool MP1HasPathClearance => ( OverridePathClearances || (MP1HasClearancePress1 && Press1HasClearance) || (MP1HasClearancePress2 && Press2HasClearance) || @@ -269,7 +466,9 @@ namespace PamhagenSysCtrl.Helpers { set => Actuators.MP1 = (value != MotorState.Halt && value != MotorState.Forward && value != MotorState.Backward) ? throw new ArgumentException("Invalid state for MP1") : value; } - public bool MP2HasClearance => (OverrideDryRunClearances || FillLevelMW2 != FillState.Empty) && ( + public bool MP2HasClearance => MP2HasDryRunClearance && MP2HasPathClearance; + public bool MP2HasDryRunClearance => (OverrideDryRunClearances || FillLevelMW2 != FillState.Empty); + public bool MP2HasPathClearance => ( OverridePathClearances || (MP2HasClearancePress1 && Press1HasClearance) || (MP2HasClearancePress2 && Press2HasClearance) || diff --git a/PamhagenSysCtrl/Helpers/Xbe24Keyboard.cs b/PamhagenSysCtrl/Helpers/Xbe24Keyboard.cs new file mode 100644 index 0000000..0f41513 --- /dev/null +++ b/PamhagenSysCtrl/Helpers/Xbe24Keyboard.cs @@ -0,0 +1,114 @@ +using PIEHidNetCore; +using System.IO; +using System.Windows.Media; + +namespace PamhagenSysCtrl.Helpers { + public class Xbe24Keyboard : IDisposable, PIEDataHandler, PIEErrorHandler { + + public enum LedBank { UPPER = 0, LOWER = 1 } + + public record struct KeyState(byte[] Rows) { + public readonly bool GetKey(int n) => (Rows[n / 6] & (1 << (n % 6))) != 0; + } + + protected readonly PIEDevice XbeDevice; + + private KeyState LastKeyState = new(new byte[4]); + private readonly (Color Color, bool Flashing)[] LastStates = new (Color, bool)[48]; + + public bool IsKeyPressed(int keyIndex) => LastKeyState.GetKey(keyIndex); + + public event EventHandler? KeyDown; + public event EventHandler? KeyUp; + public event EventHandler? KeysChanged; + + public Xbe24Keyboard(int pid = 0x0555) { + foreach (var device in PIEDevice.EnumeratePIE()) { + if (device.Pid == pid && device.HidUsagePage == 0x0C && device.HidUsage == 1 && device.WriteLength > 1) { + XbeDevice = device; + break; + } + } + if (XbeDevice == null) + throw new ArgumentException($"No XBE keyboard with id 05f3:{pid:x4}"); + + int res; + if ((res = XbeDevice.SetupInterface()) != 0) + throw new IOException($"Unable to SetupInterface for XBE keyboard: {res}"); + if ((res = XbeDevice.SetDataCallback(this)) != 0) + throw new IOException($"Unable to SetDataCallback for XBE keyboard: {res}"); + if ((res = XbeDevice.SetErrorCallback(this)) != 0) + throw new IOException($"Unable to SetErrorCallback for XBE keyboard: {res}"); + } + + public void Dispose() { + XbeDevice?.CloseInterface(); + } + + public void HandlePIEHidData(byte[] data, PIEDevice sourceDevice, int error) { + if (sourceDevice != XbeDevice) + return; + var current = new KeyState(data[3..8]); + var last = LastKeyState; + LastKeyState = current; + HandleKeyStateChange(current, last); + } + + public void HandlePIEHidError(PIEDevice sourceDevice, int error) { + if (sourceDevice != XbeDevice) + return; + // TODO + } + + protected void HandleKeyStateChange(KeyState current, KeyState last) { + for (int i = 0; i < 24; i++) { + if (!last.GetKey(i) && current.GetKey(i)) { + KeyDown?.Invoke(this, i); + } else if (last.GetKey(i) && !current.GetKey(i)) { + KeyUp?.Invoke(this, i); + } + } + KeysChanged?.Invoke(this, current); + } + + protected void SendCommand(byte command, byte[] payload) { + byte[] data = new byte[XbeDevice.WriteLength]; + data[1] = command; + Array.Copy(payload, 0, data, 2, Math.Min(payload.Length, data.Length - 2)); + + int res; + do { + res = XbeDevice.WriteData(data); + } while (res == 404); + if (res != 0) + throw new IOException($"Unable to WriteData for XBE keyboard: {res}"); + } + + public void SetBacklight(int keyIndex, Color color, bool flashing = false) { + SetBacklight(keyIndex, LedBank.UPPER, color, flashing); + SetBacklight(keyIndex, LedBank.LOWER, color, flashing); + } + + public void SetBacklight(int keyIndex, LedBank bank, Color color, bool flashing = false) { + if (LastStates[keyIndex * 2 + (int)bank] == (color, flashing)) + return; + SendCommand(165, [(byte)keyIndex, (byte)bank, color.R, color.G, color.B, (byte)(flashing ? 1 : 0)]); + LastStates[keyIndex * 2 + (int)bank] = (color, flashing); + } + + public void SetBacklight(Color color) { + SetBacklight(LedBank.UPPER, color); + SetBacklight(LedBank.LOWER, color); + } + + public void SetBacklight(LedBank bank, Color color) { + SendCommand(166, [(byte)bank, color.R, color.G, color.B]); + for (int i = 0; i < LastStates.Length; i++) + LastStates[i] = (color, false); + } + + public void SetFashFrequency(int ms) { + SendCommand(180, [(byte)(ms / 16)]); + } + } +} diff --git a/PamhagenSysCtrl/PIEHidNetCore.dll b/PamhagenSysCtrl/PIEHidNetCore.dll new file mode 100644 index 0000000..3dab6e1 Binary files /dev/null and b/PamhagenSysCtrl/PIEHidNetCore.dll differ diff --git a/PamhagenSysCtrl/PamhagenSysCtrl.csproj b/PamhagenSysCtrl/PamhagenSysCtrl.csproj index 02df3e7..213dda0 100644 --- a/PamhagenSysCtrl/PamhagenSysCtrl.csproj +++ b/PamhagenSysCtrl/PamhagenSysCtrl.csproj @@ -18,6 +18,9 @@ + + PIEHidNetCore.dll + diff --git a/PamhagenSysCtrl/Windows/PlantSchemeWindow.xaml.cs b/PamhagenSysCtrl/Windows/PlantSchemeWindow.xaml.cs index 2eec3fb..ee02a54 100644 --- a/PamhagenSysCtrl/Windows/PlantSchemeWindow.xaml.cs +++ b/PamhagenSysCtrl/Windows/PlantSchemeWindow.xaml.cs @@ -11,7 +11,7 @@ namespace PamhagenSysCtrl.Windows { private const double _minMove = 50; - private readonly PamhagenGraph Graph; + private static PamhagenGraph Graph => App.Plant!.Graph; private Point? _lastMousePos; private bool _mouseDragging; @@ -50,26 +50,25 @@ namespace PamhagenSysCtrl.Windows { public PlantSchemeWindow() { InitializeComponent(); Menu_Help_CheckForUpdates.IsEnabled = App.Config.UpdateUrl != null; - Graph = new(); Graph.Draw(SchemeCanvas, RoundedScale); FastSelectPaths = new Dictionary { [FastSelectButton_MW1_Press1] = (Graph.MW1, Graph.MP1, Graph.Press1), [FastSelectButton_MW2_Press1] = (Graph.MW2, Graph.MP2, Graph.Press1), [FastSelectButton_MW1_Press2] = (Graph.MW1, Graph.MP1, Graph.Press2), [FastSelectButton_MW2_Press2] = (Graph.MW2, Graph.MP2, Graph.Press2), - [FastSelectButton_MW1_Tank1] = (Graph.MW1, Graph.MP1, Graph.Tank1), - [FastSelectButton_MW2_Tank1] = (Graph.MW2, Graph.MP2, Graph.Tank1), - [FastSelectButton_MW1_Tank2] = (Graph.MW1, Graph.MP1, Graph.Tank2), - [FastSelectButton_MW2_Tank2] = (Graph.MW2, Graph.MP2, Graph.Tank2), - [FastSelectButton_MW1_Tank3] = (Graph.MW1, Graph.MP1, Graph.Tank3), - [FastSelectButton_MW2_Tank3] = (Graph.MW2, Graph.MP2, Graph.Tank3), - [FastSelectButton_MW1_Tank4] = (Graph.MW1, Graph.MP1, Graph.Tank4), - [FastSelectButton_MW2_Tank4] = (Graph.MW2, Graph.MP2, Graph.Tank4), - [FastSelectButton_MW1_Tank5] = (Graph.MW1, Graph.MP1, Graph.Tank5), - [FastSelectButton_MW2_Tank5] = (Graph.MW2, Graph.MP2, Graph.Tank5), + [FastSelectButton_MW1_Tank1] = (Graph.MW1, Graph.MP1, Graph.Tank1), + [FastSelectButton_MW2_Tank1] = (Graph.MW2, Graph.MP2, Graph.Tank1), + [FastSelectButton_MW1_Tank2] = (Graph.MW1, Graph.MP1, Graph.Tank2), + [FastSelectButton_MW2_Tank2] = (Graph.MW2, Graph.MP2, Graph.Tank2), + [FastSelectButton_MW1_Tank3] = (Graph.MW1, Graph.MP1, Graph.Tank3), + [FastSelectButton_MW2_Tank3] = (Graph.MW2, Graph.MP2, Graph.Tank3), + [FastSelectButton_MW1_Tank4] = (Graph.MW1, Graph.MP1, Graph.Tank4), + [FastSelectButton_MW2_Tank4] = (Graph.MW2, Graph.MP2, Graph.Tank4), + [FastSelectButton_MW1_Tank5] = (Graph.MW1, Graph.MP1, Graph.Tank5), + [FastSelectButton_MW2_Tank5] = (Graph.MW2, Graph.MP2, Graph.Tank5), }; App.Plant?.Update += OnUpdate; - InitializePaths(); + App.Plant?.InitializePaths(); } private void OnClosed(object sender, EventArgs evt) { @@ -96,7 +95,7 @@ namespace PamhagenSysCtrl.Windows { Menu_Settings_OverridePathClearances.IsChecked = false; Menu_Settings_OverrideDryRunClearances.IsChecked = false; - InitializePaths(); + App.Plant?.InitializePaths(); } OnUpdate(null, null); } @@ -211,19 +210,12 @@ namespace PamhagenSysCtrl.Windows { UpdateScheme(evt.GetPosition(SchemeCanvas), evt.LeftButton == MouseButtonState.Pressed); } - private void FastSelectButton_Click(object sender, RoutedEventArgs evt) { + private void FastSelectButton_Click(object sender, RoutedEventArgs? evt) { if (sender is not Button b || !FastSelectPaths.TryGetValue(b, out var val)) return; - - if (Graph.SelectedPaths.Select(p => (Path?)p).FirstOrDefault(p => p?.Start == val.Source, null) is Path p1) { - Graph.RemovePath(p1); - if (p1.Hops.Last().Node == val.Sink) - return; - } - if (Graph.GetFreePath([val.Source, val.Sink]) is Path p2) { - Graph.AddPath(p2); - _lastSource = null; - } + App.Plant?.FastSelect(val.Source, val.Sink); + OnUpdate(null, null); + _lastSource = null; } private void OnSizeChanged(object sender, SizeChangedEventArgs evt) { @@ -492,6 +484,7 @@ namespace PamhagenSysCtrl.Windows { SetFastSelectButton(b, paths.Any(p => p.Start == e.Source && p.Hops.Last().Node == e.Sink) ? (hoverPathDeactivate?.Start == e.Source && hoverPathDeactivate?.Hops.Last().Node == e.Sink) ? 2 : 1 : 0, e.Pump.IsActive || Graph.GetFreePath([e.Source, e.Sink], overrideStart: true) == null); + } private ISet UpdateScheme(Point pos, bool down) { @@ -511,13 +504,12 @@ namespace PamhagenSysCtrl.Windows { 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); - foreach (var b in FastSelectPaths) { - SetFastSelectButton(b.Key, 0, false); - } _hover = Graph.GetHover(pos.X, pos.Y, RoundedScale); var hoverButtons = FastSelectPaths.Where(b => b.Key.IsMouseOver).ToDictionary(); @@ -586,8 +578,8 @@ namespace PamhagenSysCtrl.Windows { var paths = Graph.SelectedPaths; if (HoveringPath is Path hp) paths = paths.Append(hp); - foreach (var b in FastSelectPaths.Keys) { - UpdateFastSelectPathButton(paths, hoverPathDeactivate, b); + foreach (var b in FastSelectPaths) { + UpdateFastSelectPathButton(paths, hoverPathDeactivate, b.Key); } Graph.Update(_hover, down); @@ -603,32 +595,5 @@ namespace PamhagenSysCtrl.Windows { } UpdateScheme(Mouse.GetPosition(SchemeCanvas), Mouse.LeftButton == MouseButtonState.Pressed); } - - public void InitializePaths() { - var add = ReconstructSelectedPaths().ToList(); - var remove = Graph.SelectedPaths.ToList(); - foreach (var p in remove) Graph.RemovePath(p); - for (int i = 1; i <= 57; i++) { - App.Plant?.CloseV(i); - } - foreach (var p in add) { - if (p.Hops.Any(h => h.Node is Valve v && v.IsLocked)) continue; - Graph.AddPath(p); - } - } - - public IEnumerable ReconstructSelectedPaths() { - if (App.Plant is not PamhagenPlant p) yield break; - var sources = Graph.Nodes.Where(n => n is ISource).ToList(); - var sinks = Graph.Nodes.Where(n => n is ISink).ToList(); - 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 Valve v || p.WantValveOpen(v.Nr)))); - } - if (paths.Count == 1) - yield return paths.First(); - } - } } }