Files
pamhagen-sysctrl/PamhagenSysCtrl/Helpers/PamhagenPlant.cs
T
lorenz.stechauner 1e31dafce5
Test / Run tests (push) Successful in 15s
Xbe24Keyboard: Update colors
2026-11-06 10:19:24 +01:00

631 lines
32 KiB
C#

using PamhagenSysCtrl.Helpers.Pipeline;
using PamhagenSysCtrl.Helpers.Plc;
using System.IO;
using System.Windows;
namespace PamhagenSysCtrl.Helpers {
public class PamhagenPlant : IDisposable {
public enum ShortcutKey {
START_REBLER, STOP_REBLER, TOGGLE_REBLER, INDICATE_REBLER,
START_WASTE_DISPOSAL, STOP_WASTE_DISPOSAL, TOGGLE_WASTE_DISPOSAL, INDICATE_WASTE_DISPOSAL,
START_AUGER1, STOP_AUGER1, TOGGLE_AUGER1, INDICATE_AUGER1,
START_AUGER2, STOP_AUGER2, TOGGLE_AUGER2, INDICATE_AUGER2,
START_AUGER3, STOP_AUGER3, TOGGLE_AUGER3, INDICATE_AUGER3,
INDICATE_PRESS_CROSS_AUGER,
START_TROUGH_AUGER, STOP_TROUGH_AUGER, TOGGLE_TROUGH_AUGER, INDICATE_TROUGH_AUGER,
SELECT_MW1, INDICATE_MW1_SELECTED,
SELECT_MW2, INDICATE_MW2_SELECTED,
START_STIRRER_MW1, STOP_STIRRER_MW1, TOGGLE_STIRRER_MW1, INDICATE_STIRRER_MW1,
START_STIRRER_MW2, STOP_STIRRER_MW2, TOGGLE_STIRRER_MW2, INDICATE_STIRRER_MW2,
START_MP1, STOP_MP1, TOGGLE_MP1, INDICATE_MP1,
START_MP2, STOP_MP2, TOGGLE_MP2, INDICATE_MP2,
ENABLE_P12, DISABLE_P12, TOGGLE_P12, INDICATE_P12,
ENABLE_P3, DISABLE_P3, TOGGLE_P3, INDICATE_P3,
ENABLE_P4, DISABLE_P4, TOGGLE_P4, INDICATE_P4,
ENABLE_P5, DISABLE_P5, TOGGLE_P5, INDICATE_P5,
ENABLE_P6, DISABLE_P6, TOGGLE_P6, INDICATE_P6,
INDICATE_PRESS1_CLEARANCE,
INDICATE_PRESS2_CLEARANCE,
PATH_MW1_PRESS1, PATH_MW1_LTG3_PRESS1, PATH_MW1_LTG4_PRESS1,
PATH_MW1_PRESS2, PATH_MW1_LTG3_PRESS2, PATH_MW1_LTG4_PRESS2,
PATH_MW1_T1, PATH_MW1_LTG1_T1, PATH_MW1_LTG2_T1,
PATH_MW1_T2, PATH_MW1_LTG1_T2, PATH_MW1_LTG2_T2,
PATH_MW1_T3, PATH_MW1_LTG1_T3, PATH_MW1_LTG2_T3,
PATH_MW1_T4, PATH_MW1_LTG1_T4, PATH_MW1_LTG2_T4,
PATH_MW1_T5, PATH_MW1_LTG1_T5, PATH_MW1_LTG2_T5,
PATH_MW1_T6, PATH_MW1_LTG1_T6, PATH_MW1_LTG2_T6,
PATH_MW1_T7, PATH_MW1_LTG1_T7, PATH_MW1_LTG2_T7,
PATH_MW1_T8, PATH_MW1_LTG1_T8, PATH_MW1_LTG2_T8,
PATH_MW1_T9, PATH_MW1_LTG1_T9, PATH_MW1_LTG2_T9,
PATH_MW2_PRESS1, PATH_MW2_LTG3_PRESS1, PATH_MW2_LTG4_PRESS1,
PATH_MW2_PRESS2, PATH_MW2_LTG3_PRESS2, PATH_MW2_LTG4_PRESS2,
PATH_MW2_T1, PATH_MW2_LTG1_T1, PATH_MW2_LTG2_T1,
PATH_MW2_T2, PATH_MW2_LTG1_T2, PATH_MW2_LTG2_T2,
PATH_MW2_T3, PATH_MW2_LTG1_T3, PATH_MW2_LTG2_T3,
PATH_MW2_T4, PATH_MW2_LTG1_T4, PATH_MW2_LTG2_T4,
PATH_MW2_T5, PATH_MW2_LTG1_T5, PATH_MW2_LTG2_T5,
PATH_MW2_T6, PATH_MW2_LTG1_T6, PATH_MW2_LTG2_T6,
PATH_MW2_T7, PATH_MW2_LTG1_T7, PATH_MW2_LTG2_T7,
PATH_MW2_T8, PATH_MW2_LTG1_T8, PATH_MW2_LTG2_T8,
PATH_MW2_T9, PATH_MW2_LTG1_T9, PATH_MW2_LTG2_T9,
PATH_T12_PRESS1, PATH_T12_PRESS2, PATH_T12_T1, PATH_T12_T2, PATH_T12_T3, PATH_T12_T4, PATH_T12_T5, PATH_T12_T6, PATH_T12_T7, PATH_T12_T8, PATH_T12_T9,
PATH_T3_PRESS1, PATH_T3_PRESS2, PATH_T3_T1, PATH_T3_T2, PATH_T3_T3, PATH_T3_T4, PATH_T3_T5, PATH_T3_T6, PATH_T3_T7, PATH_T3_T8, PATH_T3_T9,
PATH_T4_PRESS1, PATH_T4_PRESS2, PATH_T4_T1, PATH_T4_T2, PATH_T4_T3, PATH_T4_T4, PATH_T4_T5, PATH_T4_T6, PATH_T4_T7, PATH_T4_T8, PATH_T4_T9,
PATH_T5_PRESS1, PATH_T5_PRESS2, PATH_T5_T1, PATH_T5_T2, PATH_T5_T3, PATH_T5_T4, PATH_T5_T5, PATH_T5_T6, PATH_T5_T7, PATH_T5_T8, PATH_T5_T9,
PATH_P6_PRESS1, PATH_P6_PRESS2,
}
protected PamhagenPlc? Plc;
protected Xbe24Keyboard? Keyboard;
public PamhagenGraph Graph;
public bool IsPlcConnected => Plc != null;
public bool IsKeyboardConnected => Keyboard != null;
protected bool IsRunning = true;
protected Thread? ReconnectThread;
protected Thread? PlcThread;
public event EventHandler<PlantEventArgs>? Update;
public readonly string PlcComPort;
public string? PlcConnectionError { get; private set; }
public T1.Status? LastPlcStatus => Plc?.LastStatus;
public T1Error? LastPlcError => Plc?.LastError;
public long? LastRttNs { get; private set; }
public PamhagenState State => Plc?.State ?? default;
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 comPort) {
PlcComPort = comPort;
Graph = new();
Update += UpdateKeyboardBacklights;
ReconnectThread = new Thread(new ParameterizedThreadStart(ReconnectLoop));
ReconnectThread.Start();
}
protected async void ReconnectLoop(object? parameters) {
while (IsRunning) {
if (Plc == null) {
PlcThread?.Join();
PlcThread = null;
PamhagenPlc? tmp = null;
try {
tmp = new PamhagenPlc(PlcComPort, App.Config.PlcSensorDelayOpen * 1000, App.Config.PlcSensorDelayClose * 1000, App.Config.PlcSensorDelayMW * 1000);
await tmp.Initialize();
Plc = tmp;
PlcConnectionError = null;
PlcThread = new Thread(new ParameterizedThreadStart(PlcLoop));
PlcThread.Start();
} catch (Exception exc) {
tmp?.Dispose();
PlcConnectionError = $"{exc.GetType().Name}: {exc.Message}";
if (exc is FileNotFoundException) {
// COM port does not exist - ignore
PlcConnectionError = "Port existiert nicht";
} else if (exc is UnauthorizedAccessException) {
// COM port exists, but is used by other process - ignore
PlcConnectionError = "Port wird bereits verwendet";
} else if (exc is TimeoutException) {
// COM port exists, but PLC not responding - ignore
PlcConnectionError = "Keine Rückmeldung von SPS";
} else {
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);
}
}
RaiseUpdateEvent(new());
}
if (Keyboard == null) {
try {
Keyboard = new();
Keyboard.Error += (s, e) => {
try {
Keyboard.Dispose();
} catch { }
Keyboard = null;
};
Keyboard.KeyDown += Keyboard_KeyDown;
Keyboard.KeysChanged += Keyboard_KeysChanged;
Keyboard.SetFashFrequency(250);
} catch { }
}
await Task.Delay(100);
}
}
protected virtual void RaiseUpdateEvent(PlantEventArgs evt) {
App.MainDispatcher?.BeginInvoke(() => Update?.Invoke(this, evt));
}
private static void UpdatePumpKeys(Xbe24Keyboard k, ShortcutKey start, ShortcutKey stop, ShortcutKey toggle, ShortcutKey indicate, PamhagenState.PumpState state, bool highlightOff, bool directControl) {
var waiting = !state.HasPathClearance && state.HasDryRunClearance && state.WantsPathClearance;
k.SetBacklight(start,
state.State == MotorState.Forward ? (Xbe24Keyboard.Green, false) :
state.State == MotorState.Backward ? (Xbe24Keyboard.Lime, true) :
waiting && state.WantActive ? (Xbe24Keyboard.Green, true) :
waiting && !state.WantActive ? (Xbe24Keyboard.LightGreen, true) :
state.HasClearance ? (Xbe24Keyboard.LightGreen, false) :
k.IsKeyPressed(start) ? (Xbe24Keyboard.Orange, false) :
(Xbe24Keyboard.LightRed, false));
k.SetBacklight(stop,
highlightOff ? Xbe24Keyboard.Orange :
!state.IsActive ? Xbe24Keyboard.Red :
Xbe24Keyboard.LightRed);
k.SetBacklight(toggle,
highlightOff ? (Xbe24Keyboard.Orange, false) :
state.State == MotorState.Forward ? (Xbe24Keyboard.Green, false) :
state.State == MotorState.Backward ? (Xbe24Keyboard.Lime, true) :
waiting && state.WantActive ? (Xbe24Keyboard.Green, true) :
waiting && !state.WantActive ? (Xbe24Keyboard.Yellow, true) :
state.HasClearance ? (Xbe24Keyboard.Yellow, false) :
k.IsKeyPressed(toggle) ? (Xbe24Keyboard.Orange, false) :
(Xbe24Keyboard.Red, false));
k.SetBacklight(indicate,
state.State == MotorState.Forward ? (Xbe24Keyboard.Green, false) :
state.State == MotorState.Backward ? (Xbe24Keyboard.Lime, true) :
waiting && state.WantActive ? (Xbe24Keyboard.Green, true) :
waiting && !state.WantActive ? (Xbe24Keyboard.Yellow, true) :
state.HasClearance ? (Xbe24Keyboard.Yellow, false) :
(Xbe24Keyboard.Red, false));
}
private static void UpdateMotorKeys(Xbe24Keyboard k, ShortcutKey start, ShortcutKey stop, ShortcutKey toggle, ShortcutKey indicate, bool active, bool clearance, bool consistent = true) {
k.SetBacklight(start,
active && consistent ? Xbe24Keyboard.Green :
active ? Xbe24Keyboard.Yellow :
clearance ? Xbe24Keyboard.LightGreen :
k.IsKeyPressed(start) ? Xbe24Keyboard.Orange :
Xbe24Keyboard.LightRed);
k.SetBacklight(stop,
!active && consistent ? Xbe24Keyboard.Red :
Xbe24Keyboard.LightRed);
k.SetBacklight(toggle,
active && consistent ? Xbe24Keyboard.Green :
active ? Xbe24Keyboard.Yellow :
!clearance && k.IsKeyPressed(toggle) ? Xbe24Keyboard.Orange :
Xbe24Keyboard.Red);
k.SetBacklight(indicate,
active && consistent ? Xbe24Keyboard.Green :
active ? Xbe24Keyboard.Yellow :
Xbe24Keyboard.Red);
}
private static void UpdateMwSelectKeys(Xbe24Keyboard k, ShortcutKey select, ShortcutKey indicate, bool selected, bool wantSelected, bool clearance) {
k.SetBacklight(select,
selected && !clearance ? (Xbe24Keyboard.Red, true) :
selected ? (Xbe24Keyboard.Blue, false) :
!clearance && k.IsKeyPressed(select) ? (Xbe24Keyboard.Orange, false) :
wantSelected && !clearance ? (Xbe24Keyboard.Red, true) :
wantSelected ? (Xbe24Keyboard.Blue, true) :
clearance ? (Xbe24Keyboard.LightBlue, false) :
(Xbe24Keyboard.LightRed, false));
k.SetBacklight(indicate,
selected && !clearance ? (Xbe24Keyboard.Red, true) :
selected ? (Xbe24Keyboard.Blue, false) :
wantSelected && !clearance ? (Xbe24Keyboard.Red, true) :
wantSelected ? (Xbe24Keyboard.Blue, true) :
clearance ? (Xbe24Keyboard.LightBlue, false) :
(Xbe24Keyboard.LightRed, false));
}
private static void UpdateMwStirrerKeys(Xbe24Keyboard k, FillState auto, ShortcutKey start, ShortcutKey stop, ShortcutKey toggle, ShortcutKey indicate, bool active, bool pumping, FillState filling) {
k.SetBacklight(start,
active && pumping && filling >= auto ? (Xbe24Keyboard.Green, false) :
active ? (Xbe24Keyboard.Green, true) :
pumping && filling >= auto ? (Xbe24Keyboard.LightGreen, true) :
filling >= auto ? (Xbe24Keyboard.LightGreen, false) :
(Xbe24Keyboard.LightRed, false));
k.SetBacklight(stop,
active && pumping && filling >= auto ? (Xbe24Keyboard.LightRed, false) :
active ? (Xbe24Keyboard.LightRed, true) :
pumping && filling >= auto ? (Xbe24Keyboard.Red, true) :
(Xbe24Keyboard.Red, false));
k.SetBacklight(toggle,
active && pumping && filling >= auto ? (Xbe24Keyboard.Green, false) :
active ? (Xbe24Keyboard.Green, true) :
pumping && filling >= auto ? (Xbe24Keyboard.Red, true) :
filling >= auto ? (Xbe24Keyboard.Yellow, false) :
(Xbe24Keyboard.Red, false));
k.SetBacklight(indicate,
active && pumping && filling >= auto ? (Xbe24Keyboard.Green, false) :
active ? (Xbe24Keyboard.Green, true) :
pumping && filling >= auto ? (Xbe24Keyboard.Red, true) :
filling >= auto ? (Xbe24Keyboard.Yellow, false) :
(Xbe24Keyboard.Red, false));
}
protected void UpdateKeyboardBacklights(object? sender, PlantEventArgs evt) {
if (sender is not PamhagenPlant plant || plant.Keyboard is not Xbe24Keyboard k) return;
k.SetBacklight(ShortcutKey.INDICATE_PRESS_CROSS_AUGER, State.PressCrossAuger_IsActive ? Xbe24Keyboard.Green : Xbe24Keyboard.Red);
k.SetBacklight(ShortcutKey.INDICATE_PRESS1_CLEARANCE, State.Press1_HasClearance ? Xbe24Keyboard.Green : Xbe24Keyboard.Red);
k.SetBacklight(ShortcutKey.INDICATE_PRESS2_CLEARANCE, State.Press2_HasClearance ? Xbe24Keyboard.Green : Xbe24Keyboard.Red);
UpdateMotorKeys(k, ShortcutKey.START_AUGER1, ShortcutKey.STOP_AUGER1, ShortcutKey.TOGGLE_AUGER1, ShortcutKey.INDICATE_AUGER1,
State.Auger1_IsActive, true);
UpdateMotorKeys(k, ShortcutKey.START_AUGER2, ShortcutKey.STOP_AUGER2, ShortcutKey.TOGGLE_AUGER2, ShortcutKey.INDICATE_AUGER2,
State.Auger2_IsActive, true);
UpdateMotorKeys(k, ShortcutKey.START_AUGER3, ShortcutKey.STOP_AUGER3, ShortcutKey.TOGGLE_AUGER3, ShortcutKey.INDICATE_AUGER3,
State.Auger3_IsActive, true);
UpdateMotorKeys(k, ShortcutKey.START_REBLER, ShortcutKey.STOP_REBLER, ShortcutKey.TOGGLE_REBLER, ShortcutKey.INDICATE_REBLER,
State.Rebler_IsActive, true);
UpdateMotorKeys(k, ShortcutKey.START_TROUGH_AUGER, ShortcutKey.STOP_TROUGH_AUGER, ShortcutKey.TOGGLE_TROUGH_AUGER, ShortcutKey.INDICATE_TROUGH_AUGER,
State.TroughAuger_IsActive, State.TroughAuger_HasClearance);
UpdateMotorKeys(k, ShortcutKey.START_WASTE_DISPOSAL, ShortcutKey.STOP_WASTE_DISPOSAL, ShortcutKey.TOGGLE_WASTE_DISPOSAL, ShortcutKey.INDICATE_WASTE_DISPOSAL,
true, State.WasteDisposal_IsActive, State.WasteDisposal_IsConsistent);
UpdateMwSelectKeys(k, ShortcutKey.SELECT_MW1, ShortcutKey.INDICATE_MW1_SELECTED,
State.MW1_IsSelected, State.MW1_WantSelected, State.MW1_FillLevel != FillState.Full);
UpdateMwSelectKeys(k, ShortcutKey.SELECT_MW2, ShortcutKey.INDICATE_MW2_SELECTED,
State.MW2_IsSelected, State.MW2_WantSelected, State.MW2_FillLevel != FillState.Full);
UpdateMwStirrerKeys(k, Plc?.AutoStirrer ?? App.Config.PlcAutoStirrer,
ShortcutKey.START_STIRRER_MW1, ShortcutKey.STOP_STIRRER_MW1, ShortcutKey.TOGGLE_STIRRER_MW1, ShortcutKey.INDICATE_STIRRER_MW1,
State.MW1_Stirrer_IsActive, State.MP1_State == MotorState.Forward, State.MW1_FillLevel);
UpdateMwStirrerKeys(k, Plc?.AutoStirrer ?? App.Config.PlcAutoStirrer,
ShortcutKey.START_STIRRER_MW2, ShortcutKey.STOP_STIRRER_MW2, ShortcutKey.TOGGLE_STIRRER_MW2, ShortcutKey.INDICATE_STIRRER_MW2,
State.MW2_Stirrer_IsActive, State.MP2_State == MotorState.Forward, State.MW2_FillLevel);
Dictionary<Pump, bool> pumpHighlightOff = [];
foreach (var (key, d) in Graph.PathShortcuts) {
if (App.Config.MapKey(key) is not int idx)
continue;
var isActive = d.Pump.IsActive;
var hasClearnace = d.Sink.HasFillingClearance;
var paths = plant.Graph.SelectedPaths.Select(p => (Pipeline.Path?)p)
.Where(p => p?.Matches(d.Source, d.Intermediate, d.Sink) ?? false);
if (paths.FirstOrDefault() is Pipeline.Path path) {
var isReady = path.IsReady;
var isDefault = !path.Hops.SkipLast(1).Any(h => h.Node is not Valve && h.Node is not PipeJoin && h.Node is not Pump);
k.SetBacklight(idx,
!hasClearnace && k.IsKeyPressed(idx) ? Xbe24Keyboard.Orange :
!hasClearnace ? Xbe24Keyboard.Red :
isActive && isDefault ? Xbe24Keyboard.Cyan :
isActive ? Xbe24Keyboard.Purple :
isDefault ? Xbe24Keyboard.Blue :
Xbe24Keyboard.Magenta, !hasClearnace || !isReady);
} else {
var isFree = Graph.GetFreePath([d.Source, d.Sink], overrideStart: true) != null;
if (isActive && k.IsKeyPressed(idx)) pumpHighlightOff[d.Pump] = true;
k.SetBacklight(idx,
(!isFree || !hasClearnace) && k.IsKeyPressed(idx) ? Xbe24Keyboard.Orange :
(!isFree || !hasClearnace) ? Xbe24Keyboard.LightRed :
isActive && k.IsKeyPressed(idx) ? Xbe24Keyboard.Orange :
isActive ? Xbe24Keyboard.Neutral :
Xbe24Keyboard.LightBlue);
}
}
UpdatePumpKeys(k, ShortcutKey.START_MP1, ShortcutKey.STOP_MP1, ShortcutKey.TOGGLE_MP1, ShortcutKey.INDICATE_MP1,
plant.State.MP1, pumpHighlightOff.GetValueOrDefault(Graph.MP1), true);
UpdatePumpKeys(k, ShortcutKey.START_MP2, ShortcutKey.STOP_MP2, ShortcutKey.TOGGLE_MP2, ShortcutKey.INDICATE_MP2,
plant.State.MP2, pumpHighlightOff.GetValueOrDefault(Graph.MP2), true);
UpdatePumpKeys(k, ShortcutKey.ENABLE_P12, ShortcutKey.DISABLE_P12, ShortcutKey.TOGGLE_P12, ShortcutKey.INDICATE_P12,
plant.State.P12, pumpHighlightOff.GetValueOrDefault(Graph.P12), false);
UpdatePumpKeys(k, ShortcutKey.ENABLE_P3, ShortcutKey.DISABLE_P3, ShortcutKey.TOGGLE_P3, ShortcutKey.INDICATE_P3,
plant.State.P3, pumpHighlightOff.GetValueOrDefault(Graph.P3), false);
UpdatePumpKeys(k, ShortcutKey.ENABLE_P4, ShortcutKey.DISABLE_P4, ShortcutKey.TOGGLE_P4, ShortcutKey.INDICATE_P4,
plant.State.P4, pumpHighlightOff.GetValueOrDefault(Graph.P4), false);
UpdatePumpKeys(k, ShortcutKey.ENABLE_P5, ShortcutKey.DISABLE_P5, ShortcutKey.TOGGLE_P5, ShortcutKey.INDICATE_P5,
plant.State.P5, pumpHighlightOff.GetValueOrDefault(Graph.P5), false);
UpdatePumpKeys(k, ShortcutKey.ENABLE_P6, ShortcutKey.DISABLE_P6, ShortcutKey.TOGGLE_P6, ShortcutKey.INDICATE_P6,
plant.State.P6, pumpHighlightOff.GetValueOrDefault(Graph.P6), false);
}
public void Dispose() {
IsRunning = false;
ReconnectThread?.Join();
PlcThread?.Join();
Plc?.Dispose();
Keyboard?.Dispose();
GC.SuppressFinalize(this);
}
protected async void PlcLoop(object? parameters) {
App.MainDispatcher?.Invoke(InitializePaths);
while (IsRunning && Plc != null) {
var start = DateTime.Now;
try {
await Plc.Update();
LastRttNs = (long)(DateTime.Now - start).TotalNanoseconds;
RaiseUpdateEvent(new());
} catch (Exception exc) {
LastRttNs = null;
var str = "Bei der SPS ist ein Fehler aufgetreten:\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "SPS-Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
if (exc is TimeoutException or IOException) {
PlcConnectionError = $"{exc.GetType().Name}: {exc.Message}";
Plc?.Dispose();
Plc = null;
} else {
await Task.Delay(1000);
}
}
}
if (Plc != null) {
try {
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;
MessageBox.Show(str, "SPS-Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
RaiseUpdateEvent(new());
}
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?.Plc?.CloseV(i);
}
foreach (var p in add) {
if (p.InnerValves.Any(v => v.IsLocked)) continue;
Graph.AddPath(p);
}
}
public IEnumerable<Pipeline.Path> 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<Pipeline.Path>();
foreach (var sink in sinks) {
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();
}
}
public void FastSelect(ISource source, INode? intermediate, ISink sink) {
if (Graph.SelectedPaths.Select(p => (Pipeline.Path?)p).FirstOrDefault(p => p?.Start == source, null) is Pipeline.Path p1) {
if (p1.HasActivePump)
throw new NoClearanceException("Pumpe aktiv");
Graph.RemovePath(p1);
if (p1.End == sink && (intermediate == null || p1.Contains(intermediate)))
return;
}
if (Graph.GetFreePath(intermediate == null ? [source, sink] : [source, intermediate, sink]) is Pipeline.Path p2) {
Graph.AddPath(p2);
}
}
private void Keyboard_KeyDown(object? sender, int keyIndex) {
if (Plc == null || App.Config.MapKey(keyIndex) is not ShortcutKey key) return;
try {
if (Graph.PathShortcuts.TryGetValue(key, out var path)) {
FastSelect(path.Source, path.Intermediate, path.Sink);
} else {
switch (key) {
case ShortcutKey.START_REBLER: StartRebler(); break;
case ShortcutKey.STOP_REBLER: StopRebler(); break;
case ShortcutKey.TOGGLE_REBLER: ToggleRebler(); break;
case ShortcutKey.START_WASTE_DISPOSAL: StartWasteDisposal(); break;
case ShortcutKey.STOP_WASTE_DISPOSAL: StopWasteDisposal(); break;
case ShortcutKey.TOGGLE_WASTE_DISPOSAL: ToggleWasteDisposal(); break;
case ShortcutKey.START_AUGER1: StartAuger1(); break;
case ShortcutKey.STOP_AUGER1: StopAuger1(); break;
case ShortcutKey.TOGGLE_AUGER1: ToggleAuger1(); break;
case ShortcutKey.START_AUGER2: StartAuger2(); break;
case ShortcutKey.STOP_AUGER2: StopAuger2(); break;
case ShortcutKey.TOGGLE_AUGER2: ToggleAuger2(); break;
case ShortcutKey.START_AUGER3: StartAuger3(); break;
case ShortcutKey.STOP_AUGER3: StopAuger3(); break;
case ShortcutKey.TOGGLE_AUGER3: ToggleAuger3(); break;
case ShortcutKey.START_TROUGH_AUGER: StartTroughAuger(); break;
case ShortcutKey.STOP_TROUGH_AUGER: StopTroughAuger(); break;
case ShortcutKey.TOGGLE_TROUGH_AUGER: ToggleTroughAuger(); break;
case ShortcutKey.SELECT_MW1: SelectMW1(); break;
case ShortcutKey.SELECT_MW2: SelectMW2(); break;
case ShortcutKey.START_STIRRER_MW1: StartStirrerMW1(); break;
case ShortcutKey.STOP_STIRRER_MW1: StopStirrerMW1(); break;
case ShortcutKey.TOGGLE_STIRRER_MW1: ToggleStirrerMW1(); break;
case ShortcutKey.START_STIRRER_MW2: StartStirrerMW2(); break;
case ShortcutKey.STOP_STIRRER_MW2: StopStirrerMW2(); break;
case ShortcutKey.TOGGLE_STIRRER_MW2: ToggleStirrerMW2(); break;
case ShortcutKey.START_MP1: StartMP1Forward(); break;
case ShortcutKey.STOP_MP1: StopMP1(); break;
case ShortcutKey.TOGGLE_MP1: ToggleMP1(); break;
case ShortcutKey.START_MP2: StartMP2Forward(); break;
case ShortcutKey.STOP_MP2: StopMP2(); break;
case ShortcutKey.TOGGLE_MP2: ToggleMP2(); break;
case ShortcutKey.ENABLE_P12: EnableP12(); break;
case ShortcutKey.DISABLE_P12: DisableP12(); break;
case ShortcutKey.TOGGLE_P12: ToggleP12(); break;
case ShortcutKey.ENABLE_P3: EnableP3(); break;
case ShortcutKey.DISABLE_P3: DisableP3(); break;
case ShortcutKey.TOGGLE_P3: ToggleP3(); break;
case ShortcutKey.ENABLE_P4: EnableP4(); break;
case ShortcutKey.DISABLE_P4: DisableP4(); break;
case ShortcutKey.TOGGLE_P4: ToggleP4(); break;
case ShortcutKey.ENABLE_P5: EnableP5(); break;
case ShortcutKey.DISABLE_P5: DisableP5(); break;
case ShortcutKey.TOGGLE_P5: ToggleP5(); break;
case ShortcutKey.ENABLE_P6: EnableP6(); break;
case ShortcutKey.DISABLE_P6: DisableP6(); break;
case ShortcutKey.TOGGLE_P6: ToggleP6(); break;
default: return;
}
}
RaiseUpdateEvent(new());
} catch (Exception) {
// ignore
}
}
private void Keyboard_KeysChanged(object? sender, Xbe24Keyboard.KeyState state) {
UpdateKeyboardBacklights(this, new());
}
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 StartWasteDisposal() => Plc?.StartWasteDisposal();
public void StopWasteDisposal() => Plc?.StopWasteDisposal();
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();
public void ToggleTroughAuger() {
if (State.TroughAuger_IsActive) {
StopTroughAuger();
} else {
StartTroughAuger();
}
}
public void ToggleRebler() {
if (State.Rebler_IsActive) {
StopRebler();
} else {
StartRebler();
}
}
public void ToggleAuger1() {
if (State.Auger1_IsActive) {
StopAuger1();
} else {
StartAuger1();
}
}
public void ToggleAuger2() {
if (State.Auger2_IsActive) {
StopAuger2();
} else {
StartAuger2();
}
}
public void ToggleAuger3() {
if (State.Auger3_IsActive) {
StopAuger3();
} else {
StartAuger3();
}
}
public void ToggleWasteDisposal() {
if (State.WasteDisposal_IsActive) {
StopWasteDisposal();
} else {
StartWasteDisposal();
}
}
public void ToggleStirrerMW1() {
if (State.MW1_Stirrer_IsActive) {
StopStirrerMW1();
} else {
StartStirrerMW1();
}
}
public void ToggleStirrerMW2() {
if (State.MW2_Stirrer_IsActive) {
StopStirrerMW2();
} else {
StartStirrerMW2();
}
}
public void ToggleMP1() {
if (State.MP1_IsActive || State.MP1_WantActive) {
StopMP1();
} else {
StartMP1Forward();
}
}
public void ToggleMP2() {
if (State.MP2_IsActive || State.MP2_WantActive) {
StopMP2();
} else {
StartMP2Forward();
}
}
public void ToggleP12() {
if (State.P12_IsActive || State.P12_WantActive) {
EnableP12();
} else {
DisableP12();
}
}
public void ToggleP3() {
if (State.P3_IsActive || State.P3_WantActive) {
EnableP3();
} else {
DisableP3();
}
}
public void ToggleP4() {
if (State.P4_IsActive || State.P4_WantActive) {
EnableP4();
} else {
DisableP4();
}
}
public void ToggleP5() {
if (State.P5_IsActive || State.P5_WantActive) {
EnableP5();
} else {
DisableP5();
}
}
public void ToggleP6() {
if (State.P6_IsActive || State.P6_WantActive) {
EnableP6();
} else {
DisableP6();
}
}
}
}