Compare commits
4
Commits
3c97d974b5
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9da5863136 | ||
|
|
d950e3511f | ||
|
|
3d24c65758 | ||
|
|
f4ef8d2f16 |
@@ -36,15 +36,11 @@ namespace PamhagenSysCtrl {
|
||||
protected override async void OnStartup(StartupEventArgs evt) {
|
||||
Version = new Version(typeof(App).GetTypeInfo().Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion.Split('+')[0] ?? "0.0.0");
|
||||
|
||||
try {
|
||||
Plant = new(Config.PlcPort);
|
||||
try {
|
||||
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);
|
||||
|
||||
@@ -94,10 +94,10 @@ namespace PamhagenSysCtrl.Helpers {
|
||||
CreatePipe(MP1, x1);
|
||||
CreatePipe(MP2, x2);
|
||||
|
||||
var v1 = CreateValve(1, mw1X, mpY + 16);
|
||||
var v2 = CreateValve(2, mw2X, mpY + 16);
|
||||
var v3 = CreateValve(3, mw2X + 6, mpY + 16);
|
||||
var v4 = CreateValve(4, mw1X - 6, mpY + 16);
|
||||
var v1 = CreateValve(1, mw1X, mpY + 16, 2);
|
||||
var v2 = CreateValve(2, mw2X, mpY + 16, 0);
|
||||
var v3 = CreateValve(3, mw2X + 6, mpY + 16, 2);
|
||||
var v4 = CreateValve(4, mw1X - 6, mpY + 16, 0);
|
||||
|
||||
CreatePipe(x1, v1);
|
||||
CreatePipe(x1, v4);
|
||||
@@ -116,11 +116,11 @@ namespace PamhagenSysCtrl.Helpers {
|
||||
CreatePipe(v4, x5, true);
|
||||
CreatePipe(x5, x6, true);
|
||||
|
||||
var v5 = CreateValve(5, x1X + 6, ltg1Y);
|
||||
var v6 = CreateValve(6, x1X + 6, ltg2Y + 8);
|
||||
var v7 = CreateValve(7, x1X + 6, ltg2Y);
|
||||
var v8 = CreateValve(8, x1X - 6, ltg2Y + 8);
|
||||
var v9 = CreateValve(9, ltg2X, ltg5Y + 8);
|
||||
var v5 = CreateValve(5, x1X + 6, ltg1Y, 1);
|
||||
var v6 = CreateValve(6, x1X + 6, ltg2Y + 8, 3);
|
||||
var v7 = CreateValve(7, x1X + 6, ltg2Y, 3);
|
||||
var v8 = CreateValve(8, x1X - 6, ltg2Y + 8, 0);
|
||||
var v9 = CreateValve(9, ltg2X, ltg5Y + 8, 2);
|
||||
|
||||
CreatePipe(x4, v5);
|
||||
CreatePipe(x4, v6, true);
|
||||
@@ -136,13 +136,13 @@ namespace PamhagenSysCtrl.Helpers {
|
||||
CreatePipe(v9, x7);
|
||||
CreatePipe(x7, xv11);
|
||||
|
||||
var v10 = CreateValve(10, ltg2X + 8, ltg5Y);
|
||||
var v11 = CreateValve(11, ltg1X - 6, wt1Y + 14);
|
||||
var v12 = CreateValve(12, ltg1X, wt1Y);
|
||||
var v13 = CreateValve(13, ltg2X, wt1Y);
|
||||
var v14 = CreateValve(14, ltg1X - 6, wt1Y - 8);
|
||||
var v15 = CreateValve(15, ltg1X - 6, wt1Y + 8);
|
||||
var v16 = CreateValve(16, ltg1X - 6, wt1Y - 14);
|
||||
var v10 = CreateValve(10, ltg2X + 8, ltg5Y, 3);
|
||||
var v11 = CreateValve(11, ltg1X - 6, wt1Y + 14, 3);
|
||||
var v12 = CreateValve(12, ltg1X, wt1Y, 0);
|
||||
var v13 = CreateValve(13, ltg2X, wt1Y, 2);
|
||||
var v14 = CreateValve(14, ltg1X - 6, wt1Y - 8, 3);
|
||||
var v15 = CreateValve(15, ltg1X - 6, wt1Y + 8, 1);
|
||||
var v16 = CreateValve(16, ltg1X - 6, wt1Y - 14, 1);
|
||||
CreatePipe(v10, x7);
|
||||
CreatePipe(xv11, v11);
|
||||
CreatePipe(xv15, v15);
|
||||
@@ -156,8 +156,8 @@ namespace PamhagenSysCtrl.Helpers {
|
||||
CreatePipe(v16, xv16);
|
||||
CreatePipe(v14, xv14);
|
||||
|
||||
var v20 = CreateValve(20, ltg1X - 6, wt2Y - 14);
|
||||
var v21 = CreateValve(21, ltg1X - 6, wt2Y - 8);
|
||||
var v20 = CreateValve(20, ltg1X - 6, wt2Y - 14, 1);
|
||||
var v21 = CreateValve(21, ltg1X - 6, wt2Y - 8, 3);
|
||||
var xv20 = new PipeJoin(ltg1X, wt2Y - 14);
|
||||
var xv21 = new PipeJoin(ltg2X, wt2Y - 8);
|
||||
CreatePipe(xv16, xv20);
|
||||
@@ -167,9 +167,9 @@ namespace PamhagenSysCtrl.Helpers {
|
||||
|
||||
var wt1 = new HeatExchanger("WT Most", wtX, wt1Y);
|
||||
var wt2 = new HeatExchanger("WT Warmwasser", wtX, wt2Y);
|
||||
var v17 = CreateValve(17, wtX + 14, wt1Y);
|
||||
var v18 = CreateValve(18, wtX + 8, wt1Y + 8);
|
||||
var v19 = CreateValve(19, wtX + 8, (wt1Y + wt2Y) / 2);
|
||||
var v17 = CreateValve(17, wtX + 14, wt1Y, 2);
|
||||
var v18 = CreateValve(18, wtX + 8, wt1Y + 8, 3);
|
||||
var v19 = CreateValve(19, wtX + 8, (wt1Y + wt2Y) / 2, 0);
|
||||
var xwt1 = new PipeJoin(wtX + 24, wt1Y + 8);
|
||||
var xwt2 = new PipeJoin(wtX + 14, wt1Y + 8);
|
||||
var xwt3 = new PipeJoin(wtX + 8, wt1Y - 8);
|
||||
@@ -196,8 +196,8 @@ namespace PamhagenSysCtrl.Helpers {
|
||||
|
||||
var xv22 = new PipeJoin(t1X - 3, ltg7Y);
|
||||
var xv23 = new PipeJoin(t1X + 3, ltg6Y);
|
||||
var v22 = CreateValve(22, t1X - 3, tValveY);
|
||||
var v23 = CreateValve(23, t1X + 3, tValveY);
|
||||
var v22 = CreateValve(22, t1X - 3, tValveY, 0);
|
||||
var v23 = CreateValve(23, t1X + 3, tValveY, 2);
|
||||
Tank1 = CreateTank(1, t1X, tanksY, 35000);
|
||||
CreatePipe(xv20, xv22, true, label: "Ltg. 7", labelOffsetX: 10);
|
||||
CreatePipe(xv21, xv23, true, label: "Ltg. 6", labelOffsetX: 4);
|
||||
@@ -208,8 +208,8 @@ namespace PamhagenSysCtrl.Helpers {
|
||||
|
||||
var xv24 = new PipeJoin(t2X - 3, ltg7Y);
|
||||
var xv25 = new PipeJoin(t2X + 3, ltg6Y);
|
||||
var v24 = CreateValve(24, t2X - 3, tValveY);
|
||||
var v25 = CreateValve(25, t2X + 3, tValveY);
|
||||
var v24 = CreateValve(24, t2X - 3, tValveY, 0);
|
||||
var v25 = CreateValve(25, t2X + 3, tValveY, 2);
|
||||
Tank2 = CreateTank(2, t2X, tanksY, 35000);
|
||||
CreatePipe(xv22, xv24);
|
||||
CreatePipe(xv23, xv25);
|
||||
@@ -220,8 +220,8 @@ namespace PamhagenSysCtrl.Helpers {
|
||||
|
||||
var xv26 = new PipeJoin(t3X - 3, ltg7Y);
|
||||
var xv27 = new PipeJoin(t3X + 3, ltg6Y);
|
||||
var v26 = CreateValve(26, t3X - 3, tValveY);
|
||||
var v27 = CreateValve(27, t3X + 3, tValveY);
|
||||
var v26 = CreateValve(26, t3X - 3, tValveY, 0);
|
||||
var v27 = CreateValve(27, t3X + 3, tValveY, 2);
|
||||
Tank3 = CreateTank(3, t3X, tanksY, 35000);
|
||||
CreatePipe(xv24, xv26);
|
||||
CreatePipe(xv25, xv27);
|
||||
@@ -230,15 +230,15 @@ namespace PamhagenSysCtrl.Helpers {
|
||||
CreateInlet(v26, Tank3);
|
||||
CreateInlet(v27, Tank3);
|
||||
|
||||
var v28 = CreateValve(28, (t3X + t4X) / 2, ltg7Y);
|
||||
var v29 = CreateValve(29, (t3X + t4X) / 2, ltg6Y);
|
||||
var v28 = CreateValve(28, (t3X + t4X) / 2, ltg7Y, 1);
|
||||
var v29 = CreateValve(29, (t3X + t4X) / 2, ltg6Y, 3);
|
||||
CreatePipe(xv26, v28);
|
||||
CreatePipe(xv27, v29);
|
||||
|
||||
var xv30 = new PipeJoin(t4X - 3, ltg7Y);
|
||||
var xv31 = new PipeJoin(t4X + 3, ltg6Y);
|
||||
var v30 = CreateValve(30, t4X - 3, tValveY);
|
||||
var v31 = CreateValve(31, t4X + 3, tValveY);
|
||||
var v30 = CreateValve(30, t4X - 3, tValveY, 0);
|
||||
var v31 = CreateValve(31, t4X + 3, tValveY, 2);
|
||||
Tank4 = CreateTank(4, t4X, tanksY, 35000);
|
||||
CreatePipe(v28, xv30);
|
||||
CreatePipe(v29, xv31);
|
||||
@@ -249,8 +249,8 @@ namespace PamhagenSysCtrl.Helpers {
|
||||
|
||||
var xv32 = new PipeJoin(t5X - 3, ltg7Y);
|
||||
var xv33 = new PipeJoin(t5X + 3, ltg6Y);
|
||||
var v32 = CreateValve(32, t5X - 3, tValveY);
|
||||
var v33 = CreateValve(33, t5X + 3, tValveY);
|
||||
var v32 = CreateValve(32, t5X - 3, tValveY, 0);
|
||||
var v33 = CreateValve(33, t5X + 3, tValveY, 2);
|
||||
Tank5 = CreateTank(5, t5X, tanksY, 35000);
|
||||
CreatePipe(xv30, xv32, label: "Ltg. 7", labelOffsetX: 6);
|
||||
CreatePipe(xv31, xv33, label: "Ltg. 6", labelOffsetX: 0);
|
||||
@@ -259,15 +259,15 @@ namespace PamhagenSysCtrl.Helpers {
|
||||
CreateInlet(v32, Tank5);
|
||||
CreateInlet(v33, Tank5);
|
||||
|
||||
var v34 = CreateValve(34, (t5X + t6X) / 2, ltg7Y);
|
||||
var v35 = CreateValve(35, (t5X + t6X) / 2, ltg6Y);
|
||||
var v34 = CreateValve(34, (t5X + t6X) / 2, ltg7Y, 1);
|
||||
var v35 = CreateValve(35, (t5X + t6X) / 2, ltg6Y, 3);
|
||||
CreatePipe(xv32, v34);
|
||||
CreatePipe(xv33, v35);
|
||||
|
||||
var xv36 = new PipeJoin(t6X + 3, ltg6Y);
|
||||
var xv37 = new PipeJoin(t6X - 3, ltg7Y);
|
||||
var v36 = CreateValve(36, t6X + 3, tValveY);
|
||||
var v37 = CreateValve(37, t6X - 3, tValveY);
|
||||
var v36 = CreateValve(36, t6X + 3, tValveY, 2);
|
||||
var v37 = CreateValve(37, t6X - 3, tValveY, 0);
|
||||
Tank6 = CreateTank(6, t6X, tanksY, 35000);
|
||||
CreatePipe(v34, xv37);
|
||||
CreatePipe(v35, xv36);
|
||||
@@ -278,8 +278,8 @@ namespace PamhagenSysCtrl.Helpers {
|
||||
|
||||
var xv38 = new PipeJoin(t7X + 3, ltg6Y);
|
||||
var xv39 = new PipeJoin(t7X - 3, ltg7Y);
|
||||
var v38 = CreateValve(38, t7X + 3, tValveY);
|
||||
var v39 = CreateValve(39, t7X - 3, tValveY);
|
||||
var v38 = CreateValve(38, t7X + 3, tValveY, 2);
|
||||
var v39 = CreateValve(39, t7X - 3, tValveY, 0);
|
||||
Tank7 = CreateTank(7, t7X, tanksY, 35000);
|
||||
CreatePipe(xv37, xv39);
|
||||
CreatePipe(xv36, xv38);
|
||||
@@ -290,8 +290,8 @@ namespace PamhagenSysCtrl.Helpers {
|
||||
|
||||
var xv40 = new PipeJoin(t8X + 3, ltg6Y);
|
||||
var xv41 = new PipeJoin(t8X - 3, ltg7Y);
|
||||
var v40 = CreateValve(40, t8X + 3, tValveY);
|
||||
var v41 = CreateValve(41, t8X - 3, tValveY);
|
||||
var v40 = CreateValve(40, t8X + 3, tValveY, 2);
|
||||
var v41 = CreateValve(41, t8X - 3, tValveY, 0);
|
||||
Tank8 = CreateTank(8, t8X, tanksY, 35000);
|
||||
CreatePipe(xv38, xv40, label: "Ltg. 6", labelOffsetX: 0);
|
||||
CreatePipe(xv39, xv41, label: "Ltg. 7", labelOffsetX: 6);
|
||||
@@ -300,8 +300,8 @@ namespace PamhagenSysCtrl.Helpers {
|
||||
CreateInlet(v40, Tank8);
|
||||
CreateInlet(v41, Tank8);
|
||||
|
||||
var v42 = CreateValve(42, t9X + 3, tValveY);
|
||||
var v43 = CreateValve(43, t9X - 3, tValveY);
|
||||
var v42 = CreateValve(42, t9X + 3, tValveY, 2);
|
||||
var v43 = CreateValve(43, t9X - 3, tValveY, 0);
|
||||
Tank9 = CreateTank(9, t9X, tanksY, 35000);
|
||||
CreatePipe(xv40, v42);
|
||||
CreatePipe(xv41, v43);
|
||||
@@ -321,11 +321,11 @@ namespace PamhagenSysCtrl.Helpers {
|
||||
CreateOutlet(Tank4, P4);
|
||||
CreateOutlet(Tank5, P5);
|
||||
|
||||
var v44 = CreateValve(44, (t1X + t2X) / 2, tanksY + 30);
|
||||
var v45 = CreateValve(45, t3X, tanksY + 30);
|
||||
var v46 = CreateValve(46, (t3X + t4X) / 2, ltg5Y);
|
||||
var v47 = CreateValve(47, t4X, tanksY + 30);
|
||||
var v48 = CreateValve(48, t5X, tanksY + 30);
|
||||
var v44 = CreateValve(44, (t1X + t2X) / 2, tanksY + 30, 2);
|
||||
var v45 = CreateValve(45, t3X, tanksY + 30, 2);
|
||||
var v46 = CreateValve(46, (t3X + t4X) / 2, ltg5Y, 3);
|
||||
var v47 = CreateValve(47, t4X, tanksY + 30, 2);
|
||||
var v48 = CreateValve(48, t5X, tanksY + 30, 2);
|
||||
var xv44 = new PipeJoin((t1X + t2X) / 2, tanksY + 36);
|
||||
var xv45 = new PipeJoin(t3X, tanksY + 36);
|
||||
var xv47 = new PipeJoin(t4X, tanksY + 36);
|
||||
@@ -355,7 +355,7 @@ namespace PamhagenSysCtrl.Helpers {
|
||||
CreatePipe(xt7, xt6);
|
||||
CreatePipe(xt6, P6);
|
||||
|
||||
var v49 = CreateValve(49, t4X, tanksY + 44);
|
||||
var v49 = CreateValve(49, t4X, tanksY + 44, 2);
|
||||
CreatePipe(xv47, v49);
|
||||
|
||||
var prX = (pr1X + pr2X) / 2;
|
||||
@@ -363,14 +363,14 @@ namespace PamhagenSysCtrl.Helpers {
|
||||
var pr2cX = prX + 3;
|
||||
Press1 = new Press("Presse 1", pr1X, prY, 20000, () => FillState.Unknown);
|
||||
Press2 = new Press("Presse 2", pr2X, prY, 15000, () => FillState.Unknown);
|
||||
var v50 = CreateValve(50, pr2cX + 6, ltg3Y);
|
||||
var v51 = CreateValve(51, pr1cX - 6, ltg3Y);
|
||||
var v52 = CreateValve(52, pr1cX - 6, ltg4Y);
|
||||
var v53 = CreateValve(53, pr2cX + 6, ltg4Y);
|
||||
var v54 = CreateValve(54, pr2cX + 6, ltg8Y);
|
||||
var v55 = CreateValve(55, pr1cX - 6, ltg8Y);
|
||||
var v56 = CreateValve(56, pr1cX - 6, ltg9Y);
|
||||
var v57 = CreateValve(57, pr2cX + 6, ltg9Y);
|
||||
var v50 = CreateValve(50, pr2cX + 6, ltg3Y, 1);
|
||||
var v51 = CreateValve(51, pr1cX - 6, ltg3Y, 1);
|
||||
var v52 = CreateValve(52, pr1cX - 6, ltg4Y, 3);
|
||||
var v53 = CreateValve(53, pr2cX + 6, ltg4Y, 3);
|
||||
var v54 = CreateValve(54, pr2cX + 6, ltg8Y, 1);
|
||||
var v55 = CreateValve(55, pr1cX - 6, ltg8Y, 1);
|
||||
var v56 = CreateValve(56, pr1cX - 6, ltg9Y, 3);
|
||||
var v57 = CreateValve(57, pr2cX + 6, ltg9Y, 3);
|
||||
var xp1 = new PipeJoin(pr1cX - 14, ltg3Y);
|
||||
var xp2 = new PipeJoin(pr1cX - 14, ltg4Y);
|
||||
var xp3 = new PipeJoin(pr2cX + 14, ltg8Y);
|
||||
@@ -404,16 +404,16 @@ namespace PamhagenSysCtrl.Helpers {
|
||||
CreatePipe(xp5, xp6);
|
||||
CreatePipe(xp7, xp8);
|
||||
|
||||
var zv1 = new CenterValve("ZV1", pr1cX, prY - 8, () => App.Plant?.Press1HasClearance ?? false);
|
||||
var zv2 = new CenterValve("ZV2", pr2cX, prY - 8, () => App.Plant?.Press2HasClearance ?? false);
|
||||
var zv1 = new ExternalValve("ZV1", pr1cX, prY - 8, 0, () => App.Plant?.Press1HasClearance ?? false);
|
||||
var zv2 = new ExternalValve("ZV2", pr2cX, prY - 8, 2, () => App.Plant?.Press2HasClearance ?? false);
|
||||
CreatePipe(xp6, zv1, true);
|
||||
CreatePipe(xp8, zv2, true);
|
||||
CreateInlet(zv1, Press1, true);
|
||||
CreateInlet(zv2, Press2, true);
|
||||
}
|
||||
|
||||
protected Valve CreateValve(int n, double x, double y) {
|
||||
return new(n, x, y, () => App.Plant?.IsValveOpen(n) ?? false, (v) => App.Plant?.ManualControlMode ?? false ? App.Plant.WantValveOpen(n) : SelectedPaths.Any(p => p.Hops.Any(h => h.Node == v)));
|
||||
protected ControlledValve CreateValve(int n, double x, double y, int o) {
|
||||
return new(n, x, y, o, () => App.Plant?.IsValveOpen(n) ?? false, (v) => App.Plant?.ManualControlMode ?? false ? App.Plant.WantValveOpen(n) : SelectedPaths.Any(p => p.Hops.Any(h => h.Node == v)));
|
||||
}
|
||||
|
||||
protected static Tank CreateTank(int n, double x, double y, int capacity) {
|
||||
@@ -423,23 +423,23 @@ namespace PamhagenSysCtrl.Helpers {
|
||||
private int PathCost(Path p) {
|
||||
return
|
||||
1000 * p.Hops.Count(h => h.Node is Valve) +
|
||||
(p.Hops.Any(h => h.Node is Valve v && v.Nr == 15) && !p.Hops.Any(h => h.Node is Valve v && (v.Nr == 16 || v.Nr == 20)) ? 10 : 0) +
|
||||
(p.Hops.Any(h => h.Node is Valve v && v.Nr == 11) && !p.Hops.Any(h => h.Node is Valve v && (v.Nr == 14 || v.Nr == 21)) ? 10 : 0) +
|
||||
(p.Hops.Any(h => h.Node is Valve v && v.Nr == 6) ? 10 : 0) +
|
||||
(p.Hops.Any(h => h.Node is ControlledValve v && v.Nr == 15) && !p.Hops.Any(h => h.Node is ControlledValve v && (v.Nr == 16 || v.Nr == 20)) ? 10 : 0) +
|
||||
(p.Hops.Any(h => h.Node is ControlledValve v && v.Nr == 11) && !p.Hops.Any(h => h.Node is ControlledValve v && (v.Nr == 14 || v.Nr == 21)) ? 10 : 0) +
|
||||
(p.Hops.Any(h => h.Node is ControlledValve v && v.Nr == 6) ? 10 : 0) +
|
||||
p.Bends;
|
||||
}
|
||||
|
||||
public Path? GetFreePath(IEnumerable<INode> points, bool overrideStart = false) {
|
||||
if (overrideStart) {
|
||||
return GetPath(points, (n) => n is not ISink && (n is not Valve v || !v.LockedForPaths.Any(p => p.Start != points.First())), PathCost);
|
||||
return GetPath(points, (n) => n is not ISink && (n is not ControlledValve v || !v.LockedForPaths.Any(p => p.Start != points.First())), PathCost);
|
||||
} else {
|
||||
return GetPath(points, (n) => n is not ISink && (n is not Valve v || !v.IsLocked), PathCost);
|
||||
return GetPath(points, (n) => n is not ISink && (n is not ControlledValve v || !v.IsLocked), PathCost);
|
||||
}
|
||||
}
|
||||
|
||||
public void TraverseSubgraph(INode start, Func<INode, bool> invalid, HashSet<IEdge>? _visited = null, Action<INode, bool, IEdge>? edgeAction = null, Action<Valve>? valveAction = null, bool reverse = false) {
|
||||
public void TraverseSubgraph(INode start, Func<INode, bool> invalid, HashSet<IEdge>? _visited = null, Action<INode, bool, IEdge>? edgeAction = null, Action<ControlledValve>? valveAction = null, bool reverse = false) {
|
||||
var visited = _visited ?? [];
|
||||
if (start is Valve v) {
|
||||
if (start is ControlledValve v) {
|
||||
valveAction?.Invoke(v);
|
||||
}
|
||||
if (invalid(start)) {
|
||||
@@ -467,7 +467,7 @@ namespace PamhagenSysCtrl.Helpers {
|
||||
UpdateValvesAdjacientToPath(path, true, false);
|
||||
if (App.Plant is PamhagenPlant plant) {
|
||||
foreach (var (_, node) in path.Hops) {
|
||||
if (node is Valve v) {
|
||||
if (node is ControlledValve v) {
|
||||
plant.OpenV(v.Nr);
|
||||
}
|
||||
}
|
||||
@@ -480,7 +480,7 @@ namespace PamhagenSysCtrl.Helpers {
|
||||
foreach (var p in _selectedPaths) UpdateValvesAdjacientToPath(p, true);
|
||||
if (App.Plant is PamhagenPlant plant) {
|
||||
foreach (var (_, node) in path.Hops) {
|
||||
if (node is Valve v) {
|
||||
if (node is ControlledValve v) {
|
||||
plant.CloseV(v.Nr);
|
||||
}
|
||||
}
|
||||
@@ -489,7 +489,7 @@ namespace PamhagenSysCtrl.Helpers {
|
||||
|
||||
private void UpdateValvesAdjacientToPath(Path path, bool locked, bool? wantOpen = null) {
|
||||
foreach (var (edge, node) in path.Hops) {
|
||||
TraverseSubgraph(node, (n) => n is Valve || n is ISink || n is Pump, valveAction: (v) => {
|
||||
TraverseSubgraph(node, (n) => n is ControlledValve || n is ISink || n is Pump, valveAction: (v) => {
|
||||
if (locked) {
|
||||
v.LockedForPaths.Add(path);
|
||||
} else {
|
||||
@@ -526,7 +526,7 @@ namespace PamhagenSysCtrl.Helpers {
|
||||
|
||||
public void ColorPipesByValves(INode start, Brush? color1, MotorState flowArrows, Brush? color2) {
|
||||
var flowPathOffset = 0.0;
|
||||
TraverseSubgraph(start, (n) => n is ISink || n is Outlet || !((n as Valve)?.IsOpen ?? (n as CenterValve)?.IsOpen ?? true), null,
|
||||
TraverseSubgraph(start, (n) => n is ISink || n is Outlet || !((n as Valve)?.IsOpen ?? (n as ExternalValve)?.IsOpen ?? true), null,
|
||||
edgeAction: (s, r, e) => {
|
||||
if (((e.Start as Valve)?.IsOpen ?? true) && ((e.End as Valve)?.IsOpen ?? true)) {
|
||||
e.Highlight = color1;
|
||||
|
||||
@@ -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;
|
||||
@@ -21,6 +26,11 @@ namespace PamhagenSysCtrl.Helpers {
|
||||
FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown
|
||||
], default);
|
||||
|
||||
public string? PlcPortName => Plc?.PortName;
|
||||
public T1.Status? LastPlcStatus => Plc?.LastPlcStatus;
|
||||
public T1Error? LastPlcError => Plc?.LastPlcError;
|
||||
public long? LastRttNs { get; private set; }
|
||||
|
||||
public bool IsValveOpen(int n) => !IsValveClosed(n);
|
||||
public bool IsValveClosed(int n) => Sensors.GetDS(n);
|
||||
public bool WantValveClosed(int n) => !WantValveOpen(n);
|
||||
@@ -73,8 +83,25 @@ namespace PamhagenSysCtrl.Helpers {
|
||||
public bool IsP6Active => Actuators.P6 == MotorState.Forward;
|
||||
|
||||
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);
|
||||
}
|
||||
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) {
|
||||
@@ -91,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();
|
||||
@@ -116,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<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 ControlledValve 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);
|
||||
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();
|
||||
@@ -128,19 +219,23 @@ namespace PamhagenSysCtrl.Helpers {
|
||||
IsRunning = false;
|
||||
BackgroundThread?.Join();
|
||||
Plc?.Dispose();
|
||||
Keyboard?.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected async void BackgroundLoop(object? parameters) {
|
||||
while (IsRunning && Plc != null) {
|
||||
var start = DateTime.Now;
|
||||
try {
|
||||
if (ActuatorsChanged) {
|
||||
await Plc.WriteOutputs(Actuators);
|
||||
ActuatorsChanged = false;
|
||||
}
|
||||
Sensors = await Plc.ReadInputs();
|
||||
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);
|
||||
@@ -163,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 ControlledValve 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 || 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
|
||||
@@ -233,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) ||
|
||||
@@ -261,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) ||
|
||||
|
||||
@@ -34,6 +34,10 @@ namespace PamhagenSysCtrl.Helpers {
|
||||
|
||||
protected readonly T1 Plc;
|
||||
|
||||
public string PortName => Plc.PortName;
|
||||
public T1.Status LastPlcStatus => Plc.LastStatus;
|
||||
public T1Error LastPlcError => Plc.LastError;
|
||||
|
||||
public PamhagenPlc(string portName, Parity parity = Parity.Odd) {
|
||||
Plc = new T1(portName, parity);
|
||||
}
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace PamhagenSysCtrl.Helpers.Pipeline {
|
||||
public class CenterValve : INode {
|
||||
|
||||
public const double DIAMETER = 4.8;
|
||||
|
||||
public double CenterX { get; set; }
|
||||
public double CenterY { get; set; }
|
||||
public string Label { get; set; }
|
||||
public Brush? Highlight { get; set; }
|
||||
|
||||
public bool IsOpen => CallbackIsOpen();
|
||||
public ISet<IEdge> Inputs { get; init; }
|
||||
public ISet<IEdge> Outputs { get; init; }
|
||||
|
||||
protected Func<bool> CallbackIsOpen;
|
||||
|
||||
private Shape? _circle;
|
||||
private TextBlock? _text;
|
||||
|
||||
public CenterValve(string label, double x, double y, Func<bool> cbOpen) {
|
||||
CenterX = x;
|
||||
CenterY = y;
|
||||
Label = label;
|
||||
CallbackIsOpen = cbOpen;
|
||||
Inputs = new HashSet<IEdge>();
|
||||
Outputs = new HashSet<IEdge>();
|
||||
}
|
||||
|
||||
public void Draw(Canvas canvas) {
|
||||
_circle = new Ellipse() {
|
||||
Fill = Brushes.DarkGray,
|
||||
Stroke = Brushes.Black,
|
||||
};
|
||||
_text = new() {
|
||||
Text = Label,
|
||||
TextAlignment = TextAlignment.Center,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
SnapsToDevicePixels = true,
|
||||
};
|
||||
canvas.Children.Add(_circle);
|
||||
canvas.Children.Add(_text);
|
||||
}
|
||||
|
||||
public void Scale(double scale) {
|
||||
var border = Graph.ToBorder(scale);
|
||||
Graph.SetCenter(_circle, CenterX, CenterY, DIAMETER, DIAMETER, scale);
|
||||
_circle?.StrokeThickness = border;
|
||||
_text?.Width = DIAMETER * scale;
|
||||
_text?.Height = DIAMETER * scale;
|
||||
_text?.FontSize = 2 * scale;
|
||||
_text?.SetValue(Canvas.LeftProperty, (CenterX - DIAMETER / 2 + 0.1) * scale);
|
||||
_text?.SetValue(Canvas.TopProperty, (CenterY - DIAMETER / 2 + 1.1) * scale);
|
||||
}
|
||||
|
||||
public bool IsInside(double x, double y) {
|
||||
return Math.Sqrt(Math.Pow(CenterX - x, 2) + Math.Pow(CenterY - y, 2)) <= DIAMETER / 2;
|
||||
}
|
||||
|
||||
public void Update(bool isHovering, bool isPressed) {
|
||||
_circle?.Fill =
|
||||
!IsOpen ? PamhagenBrushes.Red :
|
||||
IsOpen ? PamhagenBrushes.Green :
|
||||
Highlight ?? Brushes.DarkGray;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace PamhagenSysCtrl.Helpers.Pipeline {
|
||||
public class ControlledValve : Valve {
|
||||
|
||||
public int Nr { get; set; }
|
||||
public override string Label => $"V{Nr}";
|
||||
|
||||
public readonly ISet<Path> LockedForPaths = new HashSet<Path>();
|
||||
public bool IsLocked => LockedForPaths.Any();
|
||||
public bool WantOpen => CallbackWantOpen(this);
|
||||
|
||||
protected Func<Valve, bool> CallbackWantOpen;
|
||||
|
||||
public ControlledValve(int nr, double x, double y, int orientation, Func<bool> cbOpen, Func<Valve, bool> cbWant) :
|
||||
base(x, y, orientation, cbOpen) {
|
||||
Nr = nr;
|
||||
CallbackWantOpen = cbWant;
|
||||
}
|
||||
|
||||
public override void Update(bool isHovering, bool isPressed) {
|
||||
if (App.Plant?.ManualControlMode ?? false) {
|
||||
_text?.Visibility = Visibility.Visible;
|
||||
_triangles?.Fill =
|
||||
!WantOpen && !IsOpen && !isHovering ? PamhagenBrushes.Red :
|
||||
!WantOpen && !IsOpen ? PamhagenBrushes.Green :
|
||||
!WantOpen && IsOpen ? PamhagenBrushes.DimRed :
|
||||
WantOpen && !IsOpen ? PamhagenBrushes.DimGreen :
|
||||
WantOpen && IsOpen && !isHovering ? PamhagenBrushes.Green :
|
||||
WantOpen && IsOpen ? PamhagenBrushes.Red :
|
||||
Highlight ?? (isHovering ? Brushes.LightGray : Brushes.DarkGray);
|
||||
} else {
|
||||
_text?.Visibility = isHovering ? Visibility.Visible : Visibility.Hidden;
|
||||
_triangles?.Fill =
|
||||
IsLocked && !WantOpen && !IsOpen ? PamhagenBrushes.DimOrange :
|
||||
IsLocked && !WantOpen && IsOpen ? PamhagenBrushes.Red :
|
||||
IsLocked && WantOpen && !IsOpen ? PamhagenBrushes.Yellow :
|
||||
IsLocked && WantOpen && IsOpen ? PamhagenBrushes.Green :
|
||||
Highlight ?? (isHovering ? Brushes.LightGray : Brushes.DarkGray);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace PamhagenSysCtrl.Helpers.Pipeline {
|
||||
public class ExternalValve : Valve {
|
||||
|
||||
public override string Label { get; }
|
||||
|
||||
public ExternalValve(string label, double x, double y, int orientation, Func<bool> cbOpen) :
|
||||
base(x, y, orientation, cbOpen) {
|
||||
Label = label;
|
||||
}
|
||||
|
||||
public override void Update(bool isHovering, bool isPressed) {
|
||||
_text?.Visibility = isHovering || (App.Plant?.ManualControlMode ?? false) ? Visibility.Visible : Visibility.Hidden;
|
||||
_triangles?.Fill =
|
||||
!IsOpen ? PamhagenBrushes.Red :
|
||||
IsOpen ? PamhagenBrushes.Green :
|
||||
Highlight ?? Brushes.DarkGray;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ namespace PamhagenSysCtrl.Helpers.Pipeline {
|
||||
protected Func<Pump, bool> CallbackSelected;
|
||||
|
||||
private Ellipse? _circle;
|
||||
private Polygon? _triangle;
|
||||
private Polyline? _triangle;
|
||||
private TextBlock? _text;
|
||||
|
||||
public Pump(string label, double x, double y, Func<MotorState> cbState, Func<bool> cbClearance, Func<Pump, bool> cbSelected) {
|
||||
@@ -45,9 +45,11 @@ namespace PamhagenSysCtrl.Helpers.Pipeline {
|
||||
Fill = Brushes.WhiteSmoke,
|
||||
Stroke = Brushes.Black,
|
||||
};
|
||||
_triangle = new Polygon() {
|
||||
Fill = Brushes.Transparent,
|
||||
_triangle = new Polyline() {
|
||||
Stroke = Brushes.Black,
|
||||
StrokeStartLineCap = PenLineCap.Square,
|
||||
StrokeLineJoin = PenLineJoin.Round,
|
||||
StrokeEndLineCap = PenLineCap.Square,
|
||||
};
|
||||
_text = new() {
|
||||
Text = Label,
|
||||
@@ -63,12 +65,12 @@ namespace PamhagenSysCtrl.Helpers.Pipeline {
|
||||
var border = Graph.ToBorder(scale);
|
||||
_circle?.StrokeThickness = border;
|
||||
Graph.SetCenter(_circle, CenterX, CenterY, DIAMETER, DIAMETER, scale);
|
||||
var dx = Math.Cos(Math.PI / 6) * (DIAMETER / 2);
|
||||
var dy = Math.Sin(Math.PI / 6) * (DIAMETER / 2);
|
||||
var dx = Math.Cos(Math.PI / 9.5) * (DIAMETER / 2);
|
||||
var dy = Math.Sin(Math.PI / 9.5) * (DIAMETER / 2);
|
||||
_triangle?.Points = [
|
||||
Graph.ToPx(CenterX - dx, CenterY - dy, scale, dxpx: border, dypx: border / 2, round: false),
|
||||
Graph.ToPx(CenterX + dx, CenterY - dy, scale, dxpx: -border, dypx: border / 2, round: false),
|
||||
Graph.ToPx(CenterX, CenterY + DIAMETER / 2, scale, dypx: -border, round: false),
|
||||
Graph.ToPx(CenterX + dx, CenterY - dy, scale, dxpx: -border, dypx: border / 2, round: false),
|
||||
];
|
||||
_triangle?.StrokeThickness = border;
|
||||
_text?.FontSize = 14;
|
||||
|
||||
@@ -4,88 +4,134 @@ using System.Windows.Media;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace PamhagenSysCtrl.Helpers.Pipeline {
|
||||
public class Valve : INode {
|
||||
public abstract class Valve : INode {
|
||||
|
||||
public const double DIAMETER = 4.8;
|
||||
public const double LENGTH = 4.8;
|
||||
public const double WIDTH = 2.6;
|
||||
|
||||
public double CenterX { get; set; }
|
||||
public double CenterY { get; set; }
|
||||
public int Nr { get; set; }
|
||||
public string Label => $"V{Nr}";
|
||||
public abstract string Label { get; }
|
||||
public Brush? Highlight { get; set; }
|
||||
|
||||
public readonly ISet<Path> LockedForPaths = new HashSet<Path>();
|
||||
public int Orientation { get; set; }
|
||||
|
||||
public bool IsLocked => LockedForPaths.Any();
|
||||
public bool IsOpen => CallbackIsOpen();
|
||||
public bool WantOpen => CallbackWantOpen(this);
|
||||
public ISet<IEdge> Inputs { get; init; }
|
||||
public ISet<IEdge> Outputs { get; init; }
|
||||
|
||||
protected Func<bool> CallbackIsOpen;
|
||||
protected Func<Valve, bool> CallbackWantOpen;
|
||||
|
||||
private Shape? _circle;
|
||||
private TextBlock? _text;
|
||||
protected Rectangle? _background;
|
||||
protected Polygon? _triangles;
|
||||
protected Polyline? _topLine;
|
||||
protected Polyline? _bottomLine;
|
||||
protected TextBlock? _text;
|
||||
|
||||
public Valve(int nr, double x, double y, Func<bool> cbOpen, Func<Valve, bool> cbWant) {
|
||||
public Valve(double x, double y, int orientation, Func<bool> cbOpen) {
|
||||
CenterX = x;
|
||||
CenterY = y;
|
||||
Nr = nr;
|
||||
CallbackIsOpen = cbOpen;
|
||||
CallbackWantOpen = cbWant;
|
||||
Inputs = new HashSet<IEdge>();
|
||||
Outputs = new HashSet<IEdge>();
|
||||
Orientation = orientation;
|
||||
}
|
||||
|
||||
public void Draw(Canvas canvas) {
|
||||
_circle = new Ellipse() {
|
||||
_background = new Rectangle() {
|
||||
Fill = Brushes.White,
|
||||
};
|
||||
_triangles = new Polygon() {
|
||||
Fill = Brushes.DarkGray,
|
||||
Stroke = Brushes.Black,
|
||||
};
|
||||
_topLine = new Polyline() {
|
||||
Stroke = Brushes.Black,
|
||||
};
|
||||
_bottomLine = new Polyline() {
|
||||
Stroke = Brushes.Black,
|
||||
};
|
||||
_text = new() {
|
||||
Text = Label,
|
||||
TextAlignment = TextAlignment.Center,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
SnapsToDevicePixels = true,
|
||||
};
|
||||
canvas.Children.Add(_circle);
|
||||
canvas.Children.Add(_background);
|
||||
canvas.Children.Add(_triangles);
|
||||
canvas.Children.Add(_topLine);
|
||||
canvas.Children.Add(_bottomLine);
|
||||
canvas.Children.Add(_text);
|
||||
}
|
||||
|
||||
public void Scale(double scale) {
|
||||
var border = Graph.ToBorder(scale);
|
||||
Graph.SetCenter(_circle, CenterX, CenterY, DIAMETER, DIAMETER, scale);
|
||||
_circle?.StrokeThickness = border;
|
||||
_text?.Width = DIAMETER * scale;
|
||||
_text?.Height = DIAMETER * scale;
|
||||
_text?.FontSize = 2 * scale;
|
||||
_text?.SetValue(Canvas.LeftProperty, (CenterX - DIAMETER / 2 + 0.1) * scale);
|
||||
_text?.SetValue(Canvas.TopProperty, (CenterY - DIAMETER / 2 + 1.1) * scale);
|
||||
if (Orientation == 0 || Orientation == 2) {
|
||||
Graph.SetCenter(_background, CenterX, CenterY, LENGTH / 2, LENGTH, scale);
|
||||
_triangles?.Points = [
|
||||
Graph.ToPx(CenterX - WIDTH / 2, CenterY - LENGTH / 2, scale),
|
||||
Graph.ToPx(CenterX + WIDTH / 2, CenterY + LENGTH / 2, scale),
|
||||
Graph.ToPx(CenterX - WIDTH / 2, CenterY + LENGTH / 2, scale),
|
||||
Graph.ToPx(CenterX + WIDTH / 2, CenterY - LENGTH / 2, scale),
|
||||
];
|
||||
_topLine?.Points = [
|
||||
Graph.ToPx(CenterX - WIDTH / 2, CenterY - LENGTH / 4, scale),
|
||||
Graph.ToPx(CenterX - WIDTH / 4, CenterY, scale),
|
||||
Graph.ToPx(CenterX - WIDTH / 2, CenterY + LENGTH / 4, scale),
|
||||
];
|
||||
_bottomLine?.Points = [
|
||||
Graph.ToPx(CenterX + WIDTH / 2, CenterY - LENGTH / 4, scale),
|
||||
Graph.ToPx(CenterX + WIDTH / 4, CenterY, scale),
|
||||
Graph.ToPx(CenterX + WIDTH / 2, CenterY + LENGTH / 4, scale),
|
||||
];
|
||||
} else {
|
||||
Graph.SetCenter(_background, CenterX, CenterY, LENGTH, LENGTH / 2, scale);
|
||||
_triangles?.Points = [
|
||||
Graph.ToPx(CenterX - LENGTH / 2, CenterY - WIDTH / 2, scale),
|
||||
Graph.ToPx(CenterX + LENGTH / 2, CenterY + WIDTH / 2, scale),
|
||||
Graph.ToPx(CenterX + LENGTH / 2, CenterY - WIDTH / 2, scale),
|
||||
Graph.ToPx(CenterX - LENGTH / 2, CenterY + WIDTH / 2, scale),
|
||||
];
|
||||
_topLine?.Points = [
|
||||
Graph.ToPx(CenterX - LENGTH / 4, CenterY - WIDTH / 2, scale),
|
||||
Graph.ToPx(CenterX, CenterY - WIDTH / 4, scale),
|
||||
Graph.ToPx(CenterX + LENGTH / 4, CenterY - WIDTH / 2, scale),
|
||||
];
|
||||
_bottomLine?.Points = [
|
||||
Graph.ToPx(CenterX - LENGTH / 4, CenterY + WIDTH / 2, scale),
|
||||
Graph.ToPx(CenterX, CenterY + WIDTH / 4, scale),
|
||||
Graph.ToPx(CenterX + LENGTH / 4, CenterY + WIDTH / 2, scale),
|
||||
];
|
||||
}
|
||||
_triangles?.StrokeThickness = border;
|
||||
_topLine?.StrokeThickness = border;
|
||||
_bottomLine?.StrokeThickness = border;
|
||||
_text?.Width = 40;
|
||||
_text?.Height = 16;
|
||||
_text?.FontSize = 12;
|
||||
_text?.TextAlignment = Orientation == 0 ? TextAlignment.Right : Orientation == 2 ? TextAlignment.Left : TextAlignment.Center;
|
||||
if (Orientation == 0) {
|
||||
_text?.SetValue(Canvas.LeftProperty, (CenterX - WIDTH / 1.5) * scale - 40);
|
||||
_text?.SetValue(Canvas.TopProperty, CenterY * scale - 8);
|
||||
} else if (Orientation == 2) {
|
||||
_text?.SetValue(Canvas.LeftProperty, (CenterX + WIDTH / 1.5) * scale);
|
||||
_text?.SetValue(Canvas.TopProperty, CenterY * scale - 8 );
|
||||
} else if (Orientation == 1) {
|
||||
_text?.SetValue(Canvas.LeftProperty, CenterX * scale - 20);
|
||||
_text?.SetValue(Canvas.TopProperty, (CenterY - WIDTH / 2) * scale - 16);
|
||||
} else {
|
||||
_text?.SetValue(Canvas.LeftProperty, CenterX * scale - 20);
|
||||
_text?.SetValue(Canvas.TopProperty, (CenterY + WIDTH / 2) * scale);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsInside(double x, double y) {
|
||||
return Math.Sqrt(Math.Pow(CenterX - x, 2) + Math.Pow(CenterY - y, 2)) <= DIAMETER / 2;
|
||||
if (Orientation == 0 || Orientation == 2) {
|
||||
return Math.Abs(x - CenterX) <= WIDTH / 2 && Math.Abs(y - CenterY) <= LENGTH / 2;
|
||||
} else {
|
||||
return Math.Abs(x - CenterX) <= LENGTH / 2 && Math.Abs(y - CenterY) <= WIDTH / 2;
|
||||
}
|
||||
}
|
||||
|
||||
public void Update(bool isHovering, bool isPressed) {
|
||||
if (App.Plant?.ManualControlMode ?? false) {
|
||||
_circle?.Fill =
|
||||
!WantOpen && !IsOpen && !isHovering ? PamhagenBrushes.Red :
|
||||
!WantOpen && !IsOpen ? PamhagenBrushes.Green :
|
||||
!WantOpen && IsOpen ? PamhagenBrushes.DimRed :
|
||||
WantOpen && !IsOpen ? PamhagenBrushes.DimGreen :
|
||||
WantOpen && IsOpen && !isHovering ? PamhagenBrushes.Green :
|
||||
WantOpen && IsOpen ? PamhagenBrushes.Red :
|
||||
Highlight ?? (isHovering ? Brushes.LightGray : Brushes.DarkGray);
|
||||
} else {
|
||||
_circle?.Fill =
|
||||
IsLocked && !WantOpen && !IsOpen ? PamhagenBrushes.DimOrange :
|
||||
IsLocked && !WantOpen && IsOpen ? PamhagenBrushes.Red :
|
||||
IsLocked && WantOpen && !IsOpen ? PamhagenBrushes.Yellow :
|
||||
IsLocked && WantOpen && IsOpen ? PamhagenBrushes.Green :
|
||||
Highlight ?? (isHovering ? Brushes.LightGray : Brushes.DarkGray);
|
||||
}
|
||||
}
|
||||
public abstract void Update(bool isHovering, bool isPressed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,12 +10,16 @@ namespace PamhagenSysCtrl.Helpers {
|
||||
|
||||
protected SerialPort Serial;
|
||||
|
||||
public string PortName => Serial.PortName;
|
||||
public Status LastStatus { get; private set; }
|
||||
public T1Error LastError { get; private set; }
|
||||
|
||||
public T1(string portName, Parity parity = Parity.Odd) {
|
||||
Serial = new SerialPort(portName, 9600, parity, 8, StopBits.One) {
|
||||
Handshake = Handshake.None,
|
||||
NewLine = "\r",
|
||||
ReadTimeout = 1000,
|
||||
WriteTimeout = 1000,
|
||||
ReadTimeout = 500,
|
||||
WriteTimeout = 500,
|
||||
DtrEnable = true,
|
||||
RtsEnable = true,
|
||||
Encoding = Encoding.ASCII,
|
||||
@@ -97,14 +101,16 @@ namespace PamhagenSysCtrl.Helpers {
|
||||
public async Task<Status> StatusRead() {
|
||||
await SendCommand("ST", checksum: false);
|
||||
var (_, res) = await ReceiveResponse("ST");
|
||||
return new Status((Mode)(res[3] - '0'), ((res[0] - '0') << 4) | (res[1] - '0'));
|
||||
LastStatus = new Status((Mode)(res[3] - '0'), ((res[0] - '0') << 4) | (res[1] - '0'));
|
||||
return LastStatus;
|
||||
}
|
||||
|
||||
public async Task<T1Error> ErrorStatusRead() {
|
||||
await SendCommand("ER", checksum: false);
|
||||
var (_, res) = await ReceiveResponse("ER");
|
||||
var code = Convert.ToInt32(res, 10);
|
||||
return Enum.IsDefined(typeof(T1Error), code) ? (T1Error)code : T1Error.UNKNOWN_ERROR;
|
||||
LastError = Enum.IsDefined(typeof(T1Error), code) ? (T1Error)code : T1Error.UNKNOWN_ERROR;
|
||||
return LastError;
|
||||
}
|
||||
|
||||
public Task<ushort[]> DataRead(string register, int length) {
|
||||
|
||||
@@ -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>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="10.0.11" />
|
||||
<PackageReference Include="System.IO.Ports" Version="10.0.11" />
|
||||
<Reference Include="PIEHidNetCore">
|
||||
<HintPath>PIEHidNetCore.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -227,7 +227,44 @@
|
||||
</Grid>
|
||||
|
||||
<StatusBar Grid.Row="2" BorderThickness="0,1,0,0" BorderBrush="Gray">
|
||||
|
||||
<StatusBar.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="2*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="3*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="1*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="2*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
</Grid>
|
||||
</ItemsPanelTemplate>
|
||||
</StatusBar.ItemsPanel>
|
||||
<StatusBarItem>
|
||||
<TextBlock>
|
||||
SPS-Verbindung: <Run x:Name="Status_Connection" Text="-"/>
|
||||
</TextBlock>
|
||||
</StatusBarItem>
|
||||
<Separator Grid.Column="1"/>
|
||||
<StatusBarItem Grid.Column="2">
|
||||
<TextBlock>
|
||||
SPS-Status: <Run x:Name="Status_Plc" Text="-"/>
|
||||
</TextBlock>
|
||||
</StatusBarItem>
|
||||
<Separator Grid.Column="3"/>
|
||||
<StatusBarItem Grid.Column="4">
|
||||
<TextBlock>
|
||||
Druckluft: <Run x:Name="Status_CompressedAir" Text="-"/>
|
||||
</TextBlock>
|
||||
</StatusBarItem>
|
||||
<Separator Grid.Column="5"/>
|
||||
<StatusBarItem Grid.Column="6">
|
||||
<TextBlock>
|
||||
Ventile offen: <Run x:Name="Status_ValvesOpen" Text="-"/>
|
||||
</TextBlock>
|
||||
</StatusBarItem>
|
||||
</StatusBar>
|
||||
</Grid>
|
||||
</Window>
|
||||
|
||||
@@ -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,7 +50,6 @@ namespace PamhagenSysCtrl.Windows {
|
||||
public PlantSchemeWindow() {
|
||||
InitializeComponent();
|
||||
Menu_Help_CheckForUpdates.IsEnabled = App.Config.UpdateUrl != null;
|
||||
Graph = new();
|
||||
Graph.Draw(SchemeCanvas, RoundedScale);
|
||||
FastSelectPaths = new Dictionary<Button, (ISource, Pump, ISink)> {
|
||||
[FastSelectButton_MW1_Press1] = (Graph.MW1, Graph.MP1, Graph.Press1),
|
||||
@@ -69,7 +68,7 @@ namespace PamhagenSysCtrl.Windows {
|
||||
[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,20 +210,13 @@ 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);
|
||||
App.Plant?.FastSelect(val.Source, val.Sink);
|
||||
OnUpdate(null, null);
|
||||
_lastSource = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSizeChanged(object sender, SizeChangedEventArgs evt) {
|
||||
var rx = evt.NewSize.Width / 1550.0;
|
||||
@@ -349,7 +341,7 @@ namespace PamhagenSysCtrl.Windows {
|
||||
}
|
||||
foreach (var c in clicked) {
|
||||
if (App.Plant?.ManualControlMode ?? false) {
|
||||
if (c is Valve v) {
|
||||
if (c is ControlledValve v) {
|
||||
if (v.WantOpen) {
|
||||
App.Plant.CloseV(v.Nr);
|
||||
} else {
|
||||
@@ -513,9 +505,6 @@ namespace PamhagenSysCtrl.Windows {
|
||||
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();
|
||||
@@ -564,14 +553,14 @@ namespace PamhagenSysCtrl.Windows {
|
||||
|
||||
if (Graph.SelectedPaths.Select(p => (Path?)p).FirstOrDefault(p => p?.Start == Graph.MW1) is Path pmw1) {
|
||||
MP1_Target.Text = "\u2b9e " + string.Join(" \u2b9e ", pmw1.Hops
|
||||
.Select(h => h.Node is Valve || h.Node is Pump || h.Node is CenterValve ? null : h.Node.Label)
|
||||
.Select(h => h.Node is Valve || h.Node is Pump ? null : h.Node.Label)
|
||||
.Where(l => !string.IsNullOrWhiteSpace(l)));
|
||||
} else {
|
||||
MP1_Target.Text = "Vor";
|
||||
}
|
||||
if (Graph.SelectedPaths.Select(p => (Path?)p).FirstOrDefault(p => p?.Start == Graph.MW2) is Path pmw2) {
|
||||
MP2_Target.Text = "\u2b9e " + string.Join(" \u2b9e ", pmw2.Hops
|
||||
.Select(h => h.Node is Valve || h.Node is Pump || h.Node is CenterValve ? null : h.Node.Label)
|
||||
.Select(h => h.Node is Valve || h.Node is Pump ? null : h.Node.Label)
|
||||
.Where(l => !string.IsNullOrWhiteSpace(l)));
|
||||
} else {
|
||||
MP2_Target.Text = "Vor";
|
||||
@@ -584,8 +573,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);
|
||||
@@ -593,7 +582,12 @@ namespace PamhagenSysCtrl.Windows {
|
||||
}
|
||||
|
||||
public void OnUpdate(object? sender, PlantEventArgs? evt) {
|
||||
if (sender is not PamhagenPlant plant) return;
|
||||
if (App.Plant is PamhagenPlant p) {
|
||||
Status_Connection.Text = $"Ja ({p.PlcPortName}" + (p.LastRttNs != null ? $", {p.LastRttNs / 1000000.0:N0} ms" : "") + ")";
|
||||
Status_Plc.Text = $"{p.LastPlcStatus?.Mode} ({p.LastPlcStatus?.Flags:X3}) / {p.LastPlcError?.GetName()} ({(int?)p.LastPlcError:0000})";
|
||||
Status_CompressedAir.Text = Enumerable.Range(1, 57).Any(p.IsValveClosed) ? "Ein" : Enumerable.Range(1, 57).Any(p.WantValveClosed) ? "Aus" : "?";
|
||||
Status_ValvesOpen.Text = Enumerable.Range(1, 57).Count(p.IsValveOpen) + " / " + Enumerable.Range(1, 57).Count(p.WantValveOpen) + " / 57";
|
||||
}
|
||||
UpdateScheme(Mouse.GetPosition(SchemeCanvas), Mouse.LeftButton == MouseButtonState.Pressed);
|
||||
}
|
||||
|
||||
@@ -605,7 +599,7 @@ namespace PamhagenSysCtrl.Windows {
|
||||
App.Plant?.CloseV(i);
|
||||
}
|
||||
foreach (var p in add) {
|
||||
if (p.Hops.Any(h => h.Node is Valve v && v.IsLocked)) continue;
|
||||
if (p.Hops.Any(h => h.Node is ControlledValve v && v.IsLocked)) continue;
|
||||
Graph.AddPath(p);
|
||||
}
|
||||
}
|
||||
@@ -617,7 +611,7 @@ namespace PamhagenSysCtrl.Windows {
|
||||
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))));
|
||||
paths.AddRange(Graph.GetPaths(src, sink, null, (n) => n is not ISink && (n is not ControlledValve v || p.WantValveOpen(v.Nr))));
|
||||
}
|
||||
if (paths.Count == 1)
|
||||
yield return paths.First();
|
||||
|
||||
Reference in New Issue
Block a user