Add ReconnectThread to reconnect to PLC and Keyboard on the fly
Test / Run tests (push) Successful in 26s
Test / Run tests (push) Successful in 26s
This commit is contained in:
@@ -37,12 +37,6 @@ namespace PamhagenSysCtrl {
|
||||
Version = new Version(typeof(App).GetTypeInfo().Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion.Split('+')[0] ?? "0.0.0");
|
||||
|
||||
Plant = new(Config.PlcPort);
|
||||
try {
|
||||
await Plant.StartThread();
|
||||
} catch (Exception exc) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
base.OnStartup(evt);
|
||||
|
||||
if (Config.UpdateUrl != null && Config.UpdateAuto) {
|
||||
|
||||
@@ -32,40 +32,79 @@ namespace PamhagenSysCtrl.Helpers {
|
||||
protected Xbe24Keyboard? Keyboard;
|
||||
public PamhagenGraph Graph;
|
||||
|
||||
public bool IsPlcConnected => Plc != null;
|
||||
public bool IsKeyboardConnected => Keyboard != null;
|
||||
|
||||
protected bool IsRunning = true;
|
||||
protected Thread? BackgroundThread;
|
||||
protected Thread? ReconnectThread;
|
||||
protected Thread? PlcThread;
|
||||
|
||||
public event EventHandler<PlantEventArgs>? Update;
|
||||
|
||||
public string? PlcPortName => Plc?.PortName;
|
||||
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; // FIXME
|
||||
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 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);
|
||||
}
|
||||
public PamhagenPlant(string comPort) {
|
||||
PlcComPort = comPort;
|
||||
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 += 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);
|
||||
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.KeyDown += Keyboard_KeyDown;
|
||||
Keyboard.KeysChanged += Keyboard_KeysChanged;
|
||||
Keyboard.SetFashFrequency(250);
|
||||
} catch { }
|
||||
}
|
||||
|
||||
await Task.Delay(100);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void RaiseUpdateEvent(PlantEventArgs evt) {
|
||||
@@ -132,22 +171,17 @@ namespace PamhagenSysCtrl.Helpers {
|
||||
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.Initialize();
|
||||
BackgroundThread = new Thread(new ParameterizedThreadStart(BackgroundLoop));
|
||||
BackgroundThread.Start();
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
IsRunning = false;
|
||||
BackgroundThread?.Join();
|
||||
ReconnectThread?.Join();
|
||||
PlcThread?.Join();
|
||||
Plc?.Dispose();
|
||||
Keyboard?.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected async void BackgroundLoop(object? parameters) {
|
||||
protected async void PlcLoop(object? parameters) {
|
||||
App.MainDispatcher?.Invoke(InitializePaths);
|
||||
while (IsRunning && Plc != null) {
|
||||
var start = DateTime.Now;
|
||||
try {
|
||||
@@ -160,22 +194,24 @@ namespace PamhagenSysCtrl.Helpers {
|
||||
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;
|
||||
IsRunning = false;
|
||||
} else {
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
if (Plc != null)
|
||||
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);
|
||||
} 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() {
|
||||
@@ -219,69 +255,67 @@ namespace PamhagenSysCtrl.Helpers {
|
||||
}
|
||||
|
||||
private void Keyboard_KeyDown(object? sender, int keyIndex) {
|
||||
if (Plc == null) return;
|
||||
var key = (Xbe24Key)keyIndex;
|
||||
App.MainDispatcher?.BeginInvoke(() => {
|
||||
if (Plc == null) return;
|
||||
try {
|
||||
if (key == Xbe24Key.START_REBLER) {
|
||||
Plc.StartRebler();
|
||||
} else if (key == Xbe24Key.STOP_REBLER) {
|
||||
if (State.Rebler_IsActive) {
|
||||
Plc.StopRebler();
|
||||
} else {
|
||||
Plc.StopAuger1();
|
||||
if (!State.PressCrossAuger_IsActive) {
|
||||
Plc.StopAuger2();
|
||||
Plc.StopAuger3();
|
||||
}
|
||||
try {
|
||||
if (key == Xbe24Key.START_REBLER) {
|
||||
Plc.StartRebler();
|
||||
} else if (key == Xbe24Key.STOP_REBLER) {
|
||||
if (State.Rebler_IsActive) {
|
||||
Plc.StopRebler();
|
||||
} else {
|
||||
Plc.StopAuger1();
|
||||
if (!State.PressCrossAuger_IsActive) {
|
||||
Plc.StopAuger2();
|
||||
Plc.StopAuger3();
|
||||
}
|
||||
} else if (key == Xbe24Key.START_TROUGH_AUGER) {
|
||||
Plc.StartTroughAuger();
|
||||
} else if (key == Xbe24Key.STOP_TROUGH_AUGER) {
|
||||
Plc.StopTroughAuger();
|
||||
} else if (key == Xbe24Key.SELECT_MW2) {
|
||||
Plc.SelectMW2();
|
||||
} else if (key == Xbe24Key.SELECT_MW1) {
|
||||
Plc.SelectMW1();
|
||||
} else if (key == Xbe24Key.START_MP2) {
|
||||
Plc.StartMP2Forward();
|
||||
} else if (key == Xbe24Key.STOP_MP2) {
|
||||
Plc.StopMP2();
|
||||
} else if (key == Xbe24Key.START_MP1) {
|
||||
Plc.StartMP1Forward();
|
||||
} else if (key == Xbe24Key.STOP_MP1) {
|
||||
Plc.StopMP1();
|
||||
} else if (key == Xbe24Key.FAST_SELECT_MW1_1) {
|
||||
FastSelect(Graph.MW1, Graph.Press1);
|
||||
} else if (key == Xbe24Key.FAST_SELECT_MW1_2) {
|
||||
FastSelect(Graph.MW1, Graph.Press2);
|
||||
} else if (key == Xbe24Key.FAST_SELECT_MW1_3) {
|
||||
FastSelect(Graph.MW1, Graph.Tank1);
|
||||
} else if (key == Xbe24Key.FAST_SELECT_MW1_4) {
|
||||
FastSelect(Graph.MW1, Graph.Tank2);
|
||||
} else if (key == Xbe24Key.FAST_SELECT_MW1_5) {
|
||||
FastSelect(Graph.MW1, Graph.Tank3);
|
||||
} else if (key == Xbe24Key.FAST_SELECT_MW1_6) {
|
||||
FastSelect(Graph.MW1, Graph.Tank4);
|
||||
} else if (key == Xbe24Key.FAST_SELECT_MW2_1) {
|
||||
FastSelect(Graph.MW2, Graph.Press1);
|
||||
} else if (key == Xbe24Key.FAST_SELECT_MW2_2) {
|
||||
FastSelect(Graph.MW2, Graph.Press2);
|
||||
} else if (key == Xbe24Key.FAST_SELECT_MW2_3) {
|
||||
FastSelect(Graph.MW2, Graph.Tank1);
|
||||
} else if (key == Xbe24Key.FAST_SELECT_MW2_4) {
|
||||
FastSelect(Graph.MW2, Graph.Tank2);
|
||||
} else if (key == Xbe24Key.FAST_SELECT_MW2_5) {
|
||||
FastSelect(Graph.MW2, Graph.Tank3);
|
||||
} else if (key == Xbe24Key.FAST_SELECT_MW2_6) {
|
||||
FastSelect(Graph.MW2, Graph.Tank4);
|
||||
}
|
||||
} catch (NoClearanceException) {
|
||||
// Keyboard?.SetBacklight(keyIndex, Color.FromRgb(255, 0, 0));
|
||||
} catch (Exception) {
|
||||
// ignore
|
||||
} else if (key == Xbe24Key.START_TROUGH_AUGER) {
|
||||
Plc.StartTroughAuger();
|
||||
} else if (key == Xbe24Key.STOP_TROUGH_AUGER) {
|
||||
Plc.StopTroughAuger();
|
||||
} else if (key == Xbe24Key.SELECT_MW2) {
|
||||
Plc.SelectMW2();
|
||||
} else if (key == Xbe24Key.SELECT_MW1) {
|
||||
Plc.SelectMW1();
|
||||
} else if (key == Xbe24Key.START_MP2) {
|
||||
Plc.StartMP2Forward();
|
||||
} else if (key == Xbe24Key.STOP_MP2) {
|
||||
Plc.StopMP2();
|
||||
} else if (key == Xbe24Key.START_MP1) {
|
||||
Plc.StartMP1Forward();
|
||||
} else if (key == Xbe24Key.STOP_MP1) {
|
||||
Plc.StopMP1();
|
||||
} else if (key == Xbe24Key.FAST_SELECT_MW1_1) {
|
||||
FastSelect(Graph.MW1, Graph.Press1);
|
||||
} else if (key == Xbe24Key.FAST_SELECT_MW1_2) {
|
||||
FastSelect(Graph.MW1, Graph.Press2);
|
||||
} else if (key == Xbe24Key.FAST_SELECT_MW1_3) {
|
||||
FastSelect(Graph.MW1, Graph.Tank1);
|
||||
} else if (key == Xbe24Key.FAST_SELECT_MW1_4) {
|
||||
FastSelect(Graph.MW1, Graph.Tank2);
|
||||
} else if (key == Xbe24Key.FAST_SELECT_MW1_5) {
|
||||
FastSelect(Graph.MW1, Graph.Tank3);
|
||||
} else if (key == Xbe24Key.FAST_SELECT_MW1_6) {
|
||||
FastSelect(Graph.MW1, Graph.Tank4);
|
||||
} else if (key == Xbe24Key.FAST_SELECT_MW2_1) {
|
||||
FastSelect(Graph.MW2, Graph.Press1);
|
||||
} else if (key == Xbe24Key.FAST_SELECT_MW2_2) {
|
||||
FastSelect(Graph.MW2, Graph.Press2);
|
||||
} else if (key == Xbe24Key.FAST_SELECT_MW2_3) {
|
||||
FastSelect(Graph.MW2, Graph.Tank1);
|
||||
} else if (key == Xbe24Key.FAST_SELECT_MW2_4) {
|
||||
FastSelect(Graph.MW2, Graph.Tank2);
|
||||
} else if (key == Xbe24Key.FAST_SELECT_MW2_5) {
|
||||
FastSelect(Graph.MW2, Graph.Tank3);
|
||||
} else if (key == Xbe24Key.FAST_SELECT_MW2_6) {
|
||||
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) {
|
||||
|
||||
@@ -34,12 +34,12 @@ namespace PamhagenSysCtrl.Helpers.Plc {
|
||||
|
||||
protected readonly T1 Plc;
|
||||
|
||||
public string PortName => Plc.PortName;
|
||||
public string ComPort => Plc.PortName;
|
||||
public T1.Status LastStatus => Plc.LastStatus;
|
||||
public T1Error LastError => Plc.LastError;
|
||||
|
||||
public LegacyPamhagenPlc(string portName, Parity parity = Parity.Odd) {
|
||||
Plc = new T1(portName, parity);
|
||||
public LegacyPamhagenPlc(string comPort, Parity parity = Parity.Odd) {
|
||||
Plc = new T1(comPort, parity);
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace PamhagenSysCtrl.Helpers.Plc {
|
||||
protected LegacyPamhagenPlc Plc;
|
||||
public PamhagenState State { get; private set; }
|
||||
|
||||
public string? PortName => Plc?.PortName;
|
||||
public string? ComPort => Plc?.ComPort;
|
||||
public T1.Status? LastStatus => Plc?.LastStatus;
|
||||
public T1Error? LastError => Plc?.LastError;
|
||||
|
||||
@@ -23,8 +23,8 @@ namespace PamhagenSysCtrl.Helpers.Plc {
|
||||
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 PamhagenPlc(string comPort, Parity parity = Parity.Odd) {
|
||||
Plc = new(comPort, parity);
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
@@ -36,6 +36,8 @@ namespace PamhagenSysCtrl.Helpers.Plc {
|
||||
await Plc.TestConnection();
|
||||
Actuators = await Plc.ReadOutputs();
|
||||
LastActuators = Actuators;
|
||||
Sensors = await Plc.ReadInputs();
|
||||
State = new(Sensors, () => Actuators, OverridePathClearances, OverrideDryRunClearances);
|
||||
}
|
||||
|
||||
public async Task Shutdown() {
|
||||
@@ -46,9 +48,8 @@ namespace PamhagenSysCtrl.Helpers.Plc {
|
||||
UpdateInterlocks();
|
||||
|
||||
if (Actuators != LastActuators) {
|
||||
// FIXME race condition!
|
||||
await Plc.WriteOutputs(Actuators);
|
||||
LastActuators = Actuators;
|
||||
await Plc.WriteOutputs(LastActuators);
|
||||
}
|
||||
Sensors = await Plc.ReadInputs();
|
||||
State = new(Sensors, () => Actuators, OverridePathClearances, OverrideDryRunClearances);
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace PamhagenSysCtrl.Helpers.Plc {
|
||||
public FillState MW1_FillLevel => Sensors.FillLevelMW1;
|
||||
public FillState MW2_FillLevel => Sensors.FillLevelMW2;
|
||||
|
||||
public FillState[] Tanks_FillLevel => Sensors.FillLevelTanks;
|
||||
public FillState[] Tanks_FillLevel => Sensors.FillLevelTanks ?? [FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown];
|
||||
public FillState T1_FillLevel => Sensors.FillLevelTanks[0];
|
||||
public FillState T2_FillLevel => Sensors.FillLevelTanks[1];
|
||||
public FillState T3_FillLevel => Sensors.FillLevelTanks[2];
|
||||
|
||||
@@ -39,9 +39,13 @@ namespace PamhagenSysCtrl.Helpers {
|
||||
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}");
|
||||
|
||||
SetBacklight(Color.FromRgb(1, 1, 1));
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
GC.SuppressFinalize(this);
|
||||
SetBacklight(Color.FromRgb(1, 1, 1));
|
||||
XbeDevice?.CloseInterface();
|
||||
}
|
||||
|
||||
|
||||
@@ -68,8 +68,10 @@ namespace PamhagenSysCtrl.Windows {
|
||||
[FastSelectButton_MW1_Tank5] = (Graph.MW1, Graph.MP1, Graph.Tank5),
|
||||
[FastSelectButton_MW2_Tank5] = (Graph.MW2, Graph.MP2, Graph.Tank5),
|
||||
};
|
||||
App.Plant?.Update += OnUpdate;
|
||||
App.Plant?.InitializePaths();
|
||||
if (App.Plant != null) {
|
||||
Status_Connection.Text = $"Nein ({App.Plant.PlcComPort})";
|
||||
App.Plant.Update += OnUpdate;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnClosed(object sender, EventArgs evt) {
|
||||
@@ -584,27 +586,21 @@ namespace PamhagenSysCtrl.Windows {
|
||||
|
||||
public void OnUpdate(object? sender, PlantEventArgs? evt) {
|
||||
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.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";
|
||||
if (p.IsPlcConnected) {
|
||||
Status_Connection.Text = $"Ja ({p.PlcComPort}" + (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.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";
|
||||
} else {
|
||||
Status_Connection.Text = p.PlcConnectionError is string err ? $"Nein ({p.PlcComPort}, {err})" : $"Nein ({p.PlcComPort})";
|
||||
Status_Plc.Text = "-";
|
||||
Status_CompressedAir.Text = "-";
|
||||
Status_ValvesOpen.Text = "-";
|
||||
}
|
||||
}
|
||||
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 ControlledValve v && v.IsLocked)) continue;
|
||||
Graph.AddPath(p);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<Path> ReconstructSelectedPaths() {
|
||||
if (App.Plant is not PamhagenPlant p) yield break;
|
||||
var sources = Graph.Nodes.Where(n => n is ISource).ToList();
|
||||
|
||||
Reference in New Issue
Block a user