@@ -36,15 +36,11 @@ namespace PamhagenSysCtrl {
|
|||||||
protected override async void OnStartup(StartupEventArgs evt) {
|
protected override async void OnStartup(StartupEventArgs evt) {
|
||||||
Version = new Version(typeof(App).GetTypeInfo().Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion.Split('+')[0] ?? "0.0.0");
|
Version = new Version(typeof(App).GetTypeInfo().Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion.Split('+')[0] ?? "0.0.0");
|
||||||
|
|
||||||
|
Plant = new(Config.PlcPort);
|
||||||
try {
|
try {
|
||||||
Plant = new(Config.PlcPort);
|
|
||||||
await Plant.StartThread();
|
await Plant.StartThread();
|
||||||
} catch (Exception exc) {
|
} catch (Exception exc) {
|
||||||
Plant?.Dispose();
|
// TODO
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
base.OnStartup(evt);
|
base.OnStartup(evt);
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
|
using PamhagenSysCtrl.Helpers.Pipeline;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Media;
|
||||||
|
|
||||||
namespace PamhagenSysCtrl.Helpers {
|
namespace PamhagenSysCtrl.Helpers {
|
||||||
public class PamhagenPlant : IDisposable {
|
public class PamhagenPlant : IDisposable {
|
||||||
|
|
||||||
protected PamhagenPlc? Plc;
|
protected PamhagenPlc? Plc;
|
||||||
|
protected Xbe24Keyboard? Keyboard;
|
||||||
|
public PamhagenGraph Graph;
|
||||||
|
|
||||||
protected bool IsRunning = true;
|
protected bool IsRunning = true;
|
||||||
protected Thread? BackgroundThread;
|
protected Thread? BackgroundThread;
|
||||||
@@ -78,8 +83,25 @@ namespace PamhagenSysCtrl.Helpers {
|
|||||||
public bool IsP6Active => Actuators.P6 == MotorState.Forward;
|
public bool IsP6Active => Actuators.P6 == MotorState.Forward;
|
||||||
|
|
||||||
public PamhagenPlant(string portName) {
|
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 += OnUpdate;
|
||||||
|
Update += UpdateKeyboardBacklights;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void RaiseUpdateEvent(PlantEventArgs evt) {
|
protected virtual void RaiseUpdateEvent(PlantEventArgs evt) {
|
||||||
@@ -96,6 +118,10 @@ namespace PamhagenSysCtrl.Helpers {
|
|||||||
if (p.IsAuger2Active && !p.IsAuger3Active) p.StartAuger3();
|
if (p.IsAuger2Active && !p.IsAuger3Active) p.StartAuger3();
|
||||||
if (p.IsTroughAugerActive && !p.TroughAugerHasClearance)
|
if (p.IsTroughAugerActive && !p.TroughAugerHasClearance)
|
||||||
p.StopTroughAuger();
|
p.StopTroughAuger();
|
||||||
|
if (!p.IsPresseQuerSchneckeActive && !p.IsAuger1Active) {
|
||||||
|
p.StopAuger2();
|
||||||
|
p.StopAuger3();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p.IsMP1Active && !p.MP1HasClearance) p.StopMP1();
|
if (p.IsMP1Active && !p.MP1HasClearance) p.StopMP1();
|
||||||
@@ -121,6 +147,66 @@ namespace PamhagenSysCtrl.Helpers {
|
|||||||
if (p.IsP6Active && !p.P6HasClearance) p.StopP6();
|
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<int, (ISource Source, Pump Pump, ISink Sink)> {
|
||||||
|
[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() {
|
public async Task StartThread() {
|
||||||
if (Plc == null) throw new Exception();
|
if (Plc == null) throw new Exception();
|
||||||
await Plc.TestConnection();
|
await Plc.TestConnection();
|
||||||
@@ -133,6 +219,7 @@ namespace PamhagenSysCtrl.Helpers {
|
|||||||
IsRunning = false;
|
IsRunning = false;
|
||||||
BackgroundThread?.Join();
|
BackgroundThread?.Join();
|
||||||
Plc?.Dispose();
|
Plc?.Dispose();
|
||||||
|
Keyboard?.Dispose();
|
||||||
GC.SuppressFinalize(this);
|
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<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 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 =>
|
protected bool HasWTClearanceAA =>
|
||||||
(AreValvesOpen(12) && AreValvesClosed(15, 16, 20)) || // bypass
|
(AreValvesOpen(12) && AreValvesClosed(15, 16, 20)) || // bypass
|
||||||
(AreValvesOpen(15, 18, 16) && AreValvesClosed(11, 12, 14, 17, 19, 20)) || // WT1
|
(AreValvesOpen(15, 18, 16) && AreValvesClosed(11, 12, 14, 17, 19, 20)) || // WT1
|
||||||
@@ -241,7 +436,9 @@ namespace PamhagenSysCtrl.Helpers {
|
|||||||
(a && ((HasWTClearanceAA && HasT9ClearanceA) || (HasWTClearanceAB && HasT9ClearanceB))) ||
|
(a && ((HasWTClearanceAA && HasT9ClearanceA) || (HasWTClearanceAB && HasT9ClearanceB))) ||
|
||||||
(b && ((HasWTClearanceBA && HasT9ClearanceA) || (HasWTClearanceBB && 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 ||
|
OverridePathClearances ||
|
||||||
(MP1HasClearancePress1 && Press1HasClearance) ||
|
(MP1HasClearancePress1 && Press1HasClearance) ||
|
||||||
(MP1HasClearancePress2 && Press2HasClearance) ||
|
(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;
|
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 ||
|
OverridePathClearances ||
|
||||||
(MP2HasClearancePress1 && Press1HasClearance) ||
|
(MP2HasClearancePress1 && Press1HasClearance) ||
|
||||||
(MP2HasClearancePress2 && Press2HasClearance) ||
|
(MP2HasClearancePress2 && Press2HasClearance) ||
|
||||||
|
|||||||
@@ -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<int>? KeyDown;
|
||||||
|
public event EventHandler<int>? KeyUp;
|
||||||
|
public event EventHandler<KeyState>? 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)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -18,6 +18,9 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="10.0.11" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="10.0.11" />
|
||||||
<PackageReference Include="System.IO.Ports" Version="10.0.11" />
|
<PackageReference Include="System.IO.Ports" Version="10.0.11" />
|
||||||
|
<Reference Include="PIEHidNetCore">
|
||||||
|
<HintPath>PIEHidNetCore.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ namespace PamhagenSysCtrl.Windows {
|
|||||||
|
|
||||||
private const double _minMove = 50;
|
private const double _minMove = 50;
|
||||||
|
|
||||||
private readonly PamhagenGraph Graph;
|
private static PamhagenGraph Graph => App.Plant!.Graph;
|
||||||
|
|
||||||
private Point? _lastMousePos;
|
private Point? _lastMousePos;
|
||||||
private bool _mouseDragging;
|
private bool _mouseDragging;
|
||||||
@@ -50,26 +50,25 @@ namespace PamhagenSysCtrl.Windows {
|
|||||||
public PlantSchemeWindow() {
|
public PlantSchemeWindow() {
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
Menu_Help_CheckForUpdates.IsEnabled = App.Config.UpdateUrl != null;
|
Menu_Help_CheckForUpdates.IsEnabled = App.Config.UpdateUrl != null;
|
||||||
Graph = new();
|
|
||||||
Graph.Draw(SchemeCanvas, RoundedScale);
|
Graph.Draw(SchemeCanvas, RoundedScale);
|
||||||
FastSelectPaths = new Dictionary<Button, (ISource, Pump, ISink)> {
|
FastSelectPaths = new Dictionary<Button, (ISource, Pump, ISink)> {
|
||||||
[FastSelectButton_MW1_Press1] = (Graph.MW1, Graph.MP1, Graph.Press1),
|
[FastSelectButton_MW1_Press1] = (Graph.MW1, Graph.MP1, Graph.Press1),
|
||||||
[FastSelectButton_MW2_Press1] = (Graph.MW2, Graph.MP2, Graph.Press1),
|
[FastSelectButton_MW2_Press1] = (Graph.MW2, Graph.MP2, Graph.Press1),
|
||||||
[FastSelectButton_MW1_Press2] = (Graph.MW1, Graph.MP1, Graph.Press2),
|
[FastSelectButton_MW1_Press2] = (Graph.MW1, Graph.MP1, Graph.Press2),
|
||||||
[FastSelectButton_MW2_Press2] = (Graph.MW2, Graph.MP2, Graph.Press2),
|
[FastSelectButton_MW2_Press2] = (Graph.MW2, Graph.MP2, Graph.Press2),
|
||||||
[FastSelectButton_MW1_Tank1] = (Graph.MW1, Graph.MP1, Graph.Tank1),
|
[FastSelectButton_MW1_Tank1] = (Graph.MW1, Graph.MP1, Graph.Tank1),
|
||||||
[FastSelectButton_MW2_Tank1] = (Graph.MW2, Graph.MP2, Graph.Tank1),
|
[FastSelectButton_MW2_Tank1] = (Graph.MW2, Graph.MP2, Graph.Tank1),
|
||||||
[FastSelectButton_MW1_Tank2] = (Graph.MW1, Graph.MP1, Graph.Tank2),
|
[FastSelectButton_MW1_Tank2] = (Graph.MW1, Graph.MP1, Graph.Tank2),
|
||||||
[FastSelectButton_MW2_Tank2] = (Graph.MW2, Graph.MP2, Graph.Tank2),
|
[FastSelectButton_MW2_Tank2] = (Graph.MW2, Graph.MP2, Graph.Tank2),
|
||||||
[FastSelectButton_MW1_Tank3] = (Graph.MW1, Graph.MP1, Graph.Tank3),
|
[FastSelectButton_MW1_Tank3] = (Graph.MW1, Graph.MP1, Graph.Tank3),
|
||||||
[FastSelectButton_MW2_Tank3] = (Graph.MW2, Graph.MP2, Graph.Tank3),
|
[FastSelectButton_MW2_Tank3] = (Graph.MW2, Graph.MP2, Graph.Tank3),
|
||||||
[FastSelectButton_MW1_Tank4] = (Graph.MW1, Graph.MP1, Graph.Tank4),
|
[FastSelectButton_MW1_Tank4] = (Graph.MW1, Graph.MP1, Graph.Tank4),
|
||||||
[FastSelectButton_MW2_Tank4] = (Graph.MW2, Graph.MP2, Graph.Tank4),
|
[FastSelectButton_MW2_Tank4] = (Graph.MW2, Graph.MP2, Graph.Tank4),
|
||||||
[FastSelectButton_MW1_Tank5] = (Graph.MW1, Graph.MP1, Graph.Tank5),
|
[FastSelectButton_MW1_Tank5] = (Graph.MW1, Graph.MP1, Graph.Tank5),
|
||||||
[FastSelectButton_MW2_Tank5] = (Graph.MW2, Graph.MP2, Graph.Tank5),
|
[FastSelectButton_MW2_Tank5] = (Graph.MW2, Graph.MP2, Graph.Tank5),
|
||||||
};
|
};
|
||||||
App.Plant?.Update += OnUpdate;
|
App.Plant?.Update += OnUpdate;
|
||||||
InitializePaths();
|
App.Plant?.InitializePaths();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnClosed(object sender, EventArgs evt) {
|
private void OnClosed(object sender, EventArgs evt) {
|
||||||
@@ -96,7 +95,7 @@ namespace PamhagenSysCtrl.Windows {
|
|||||||
Menu_Settings_OverridePathClearances.IsChecked = false;
|
Menu_Settings_OverridePathClearances.IsChecked = false;
|
||||||
Menu_Settings_OverrideDryRunClearances.IsChecked = false;
|
Menu_Settings_OverrideDryRunClearances.IsChecked = false;
|
||||||
|
|
||||||
InitializePaths();
|
App.Plant?.InitializePaths();
|
||||||
}
|
}
|
||||||
OnUpdate(null, null);
|
OnUpdate(null, null);
|
||||||
}
|
}
|
||||||
@@ -211,19 +210,12 @@ namespace PamhagenSysCtrl.Windows {
|
|||||||
UpdateScheme(evt.GetPosition(SchemeCanvas), evt.LeftButton == MouseButtonState.Pressed);
|
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))
|
if (sender is not Button b || !FastSelectPaths.TryGetValue(b, out var val))
|
||||||
return;
|
return;
|
||||||
|
App.Plant?.FastSelect(val.Source, val.Sink);
|
||||||
if (Graph.SelectedPaths.Select(p => (Path?)p).FirstOrDefault(p => p?.Start == val.Source, null) is Path p1) {
|
OnUpdate(null, null);
|
||||||
Graph.RemovePath(p1);
|
_lastSource = null;
|
||||||
if (p1.Hops.Last().Node == val.Sink)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (Graph.GetFreePath([val.Source, val.Sink]) is Path p2) {
|
|
||||||
Graph.AddPath(p2);
|
|
||||||
_lastSource = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnSizeChanged(object sender, SizeChangedEventArgs evt) {
|
private void OnSizeChanged(object sender, SizeChangedEventArgs evt) {
|
||||||
@@ -492,6 +484,7 @@ namespace PamhagenSysCtrl.Windows {
|
|||||||
SetFastSelectButton(b,
|
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,
|
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);
|
e.Pump.IsActive || Graph.GetFreePath([e.Source, e.Sink], overrideStart: true) == null);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ISet<INode> UpdateScheme(Point pos, bool down) {
|
private ISet<INode> UpdateScheme(Point pos, bool down) {
|
||||||
@@ -511,13 +504,12 @@ namespace PamhagenSysCtrl.Windows {
|
|||||||
SetFastSelectButton(FastSelectButton_EntryTrough_Stop, App.Plant?.IsTroughAugerActive ?? false ? 0 : 2);
|
SetFastSelectButton(FastSelectButton_EntryTrough_Stop, App.Plant?.IsTroughAugerActive ?? false ? 0 : 2);
|
||||||
SetFastSelectButton(FastSelectButton_MW1, App.Plant?.IsMW1Selected ?? false ? 1 : 0);
|
SetFastSelectButton(FastSelectButton_MW1, App.Plant?.IsMW1Selected ?? false ? 1 : 0);
|
||||||
SetFastSelectButton(FastSelectButton_MW2, App.Plant?.IsMW2Selected ?? 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_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_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_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_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);
|
_hover = Graph.GetHover(pos.X, pos.Y, RoundedScale);
|
||||||
var hoverButtons = FastSelectPaths.Where(b => b.Key.IsMouseOver).ToDictionary();
|
var hoverButtons = FastSelectPaths.Where(b => b.Key.IsMouseOver).ToDictionary();
|
||||||
@@ -586,8 +578,8 @@ namespace PamhagenSysCtrl.Windows {
|
|||||||
|
|
||||||
var paths = Graph.SelectedPaths;
|
var paths = Graph.SelectedPaths;
|
||||||
if (HoveringPath is Path hp) paths = paths.Append(hp);
|
if (HoveringPath is Path hp) paths = paths.Append(hp);
|
||||||
foreach (var b in FastSelectPaths.Keys) {
|
foreach (var b in FastSelectPaths) {
|
||||||
UpdateFastSelectPathButton(paths, hoverPathDeactivate, b);
|
UpdateFastSelectPathButton(paths, hoverPathDeactivate, b.Key);
|
||||||
}
|
}
|
||||||
|
|
||||||
Graph.Update(_hover, down);
|
Graph.Update(_hover, down);
|
||||||
@@ -603,32 +595,5 @@ namespace PamhagenSysCtrl.Windows {
|
|||||||
}
|
}
|
||||||
UpdateScheme(Mouse.GetPosition(SchemeCanvas), Mouse.LeftButton == MouseButtonState.Pressed);
|
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<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<Path>();
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user