diff --git a/PamhagenSysCtrl/Helpers/PamhagenPlant.cs b/PamhagenSysCtrl/Helpers/PamhagenPlant.cs index f93cbeb..474a5d6 100644 --- a/PamhagenSysCtrl/Helpers/PamhagenPlant.cs +++ b/PamhagenSysCtrl/Helpers/PamhagenPlant.cs @@ -35,6 +35,7 @@ namespace PamhagenSysCtrl.Helpers { public FillState FillLevelT7 => Sensors.FillingLevelTanks[6]; public FillState FillLevelT8 => Sensors.FillingLevelTanks[7]; public FillState FillLevelT9 => Sensors.FillingLevelTanks[8]; + public FillState[] TankFillLevels => Sensors.FillingLevelTanks; public double GradationOe => Sensors.GradationOe; public MotorState PresseQuerSchnecke => Sensors.PresseQuerSchnecke; diff --git a/PamhagenSysCtrl/Helpers/Pipeline/Graph.cs b/PamhagenSysCtrl/Helpers/Pipeline/Graph.cs index 8ba99ee..85609ee 100644 --- a/PamhagenSysCtrl/Helpers/Pipeline/Graph.cs +++ b/PamhagenSysCtrl/Helpers/Pipeline/Graph.cs @@ -22,6 +22,16 @@ namespace PamhagenSysCtrl.Helpers.Pipeline { return p; } + public Inlet CreateInlet(INode start, ISink end) { + Nodes.Add(start); + Nodes.Add(end); + var p = new Inlet(start, end, start.CenterX > end.CenterX); + start.Outputs.Add(p); + end.Inputs.Add(p); + Edges.Add(p); + return p; + } + public Path? GetPath(INode start, INode end, Func? valid = null) { if (!Nodes.Contains(start) || !Nodes.Contains(end)) throw new ArgumentException("Start/end node not contained in graph"); valid ??= a => true; @@ -35,8 +45,11 @@ namespace PamhagenSysCtrl.Helpers.Pipeline { } var p = GetPath(o.End, end, valid); - if (p != null && (path == null || p.Value.Hops.Count() + 1 < path.Value.Hops.Count())) { - path = new(start, [(o, o.End), ..p.Value.Hops]); + if (p != null) { + var p2 = new Path(start, [(o, o.End), .. p.Value.Hops]); + if (path == null || p2.Length < path.Value.Length) { + path = p2; + } } } return path != null ? new(start, path.Value.Hops) : null; diff --git a/PamhagenSysCtrl/Helpers/Pipeline/ISink.cs b/PamhagenSysCtrl/Helpers/Pipeline/ISink.cs index 8c3b158..4a6fbbf 100644 --- a/PamhagenSysCtrl/Helpers/Pipeline/ISink.cs +++ b/PamhagenSysCtrl/Helpers/Pipeline/ISink.cs @@ -1,5 +1,6 @@ namespace PamhagenSysCtrl.Helpers.Pipeline { public interface ISink : INode { + public double TopY { get; } public bool IsFull { get; } } } diff --git a/PamhagenSysCtrl/Helpers/Pipeline/Inlet.cs b/PamhagenSysCtrl/Helpers/Pipeline/Inlet.cs new file mode 100644 index 0000000..b2d18df --- /dev/null +++ b/PamhagenSysCtrl/Helpers/Pipeline/Inlet.cs @@ -0,0 +1,84 @@ +using System.Windows.Controls; +using System.Windows.Media; +using System.Windows.Shapes; + +namespace PamhagenSysCtrl.Helpers.Pipeline { + public class Inlet : IEdge { + + public INode Start { get; set; } + public INode End => Sink; + public ISink Sink { get; set; } + public bool Orientation { get; set; } + public bool Highlight { get; set; } + + private Line? _outer1; + private Line? _outer2; + private Line? _inner1; + private Line? _inner2; + + public Inlet(INode start, ISink end, bool orientation = false) { + Start = start; + Sink = end; + Orientation = orientation; + } + + public void Draw(Canvas canvas) { + var x1 = Start.CenterX; + var y1 = Start.CenterY; + var x2 = Start.CenterX; + var y2 = Sink.TopY - 10; + var x3 = Start.CenterX + (Orientation ? -10 : 10); + var y3 = Sink.TopY + 10; + _outer1 = new Line() { + X1 = x1, + Y1 = y1, + X2 = x2, + Y2 = y2, + StrokeThickness = 10, + Stroke = Brushes.Black, + StrokeStartLineCap = PenLineCap.Square, + StrokeEndLineCap = PenLineCap.Round, + }; + _inner1 = new Line() { + X1 = x1, + Y1 = y1, + X2 = x2, + Y2 = y2, + StrokeThickness = 6, + Stroke = Brushes.DarkGray, + StrokeStartLineCap = PenLineCap.Square, + StrokeEndLineCap = PenLineCap.Round, + }; + _outer2 = new Line() { + X1 = x2, + Y1 = y2, + X2 = x3, + Y2 = y3, + StrokeThickness = 10, + Stroke = Brushes.Black, + StrokeStartLineCap = PenLineCap.Round, + StrokeEndLineCap = PenLineCap.Flat, + }; + _inner2 = new Line() { + X1 = x2, + Y1 = y2, + X2 = x3, + Y2 = y3, + StrokeThickness = 6, + Stroke = Brushes.DarkGray, + StrokeStartLineCap = PenLineCap.Round, + StrokeEndLineCap = PenLineCap.Flat, + }; + canvas.Children.Add(_outer1); + canvas.Children.Add(_outer2); + canvas.Children.Add(_inner1); + canvas.Children.Add(_inner2); + } + + public bool Update(double x, double y) { + _inner1?.Stroke = Highlight ? Brushes.Green : Brushes.DarkGray; + _inner2?.Stroke = Highlight ? Brushes.Green : Brushes.DarkGray; + return false; + } + } +} diff --git a/PamhagenSysCtrl/Helpers/Pipeline/Path.cs b/PamhagenSysCtrl/Helpers/Pipeline/Path.cs index cea4efa..165f778 100644 --- a/PamhagenSysCtrl/Helpers/Pipeline/Path.cs +++ b/PamhagenSysCtrl/Helpers/Pipeline/Path.cs @@ -1,3 +1,8 @@ namespace PamhagenSysCtrl.Helpers.Pipeline { - public record struct Path(INode Start, IEnumerable<(IEdge Edge, INode Node)> Hops); + public record struct Path( + INode Start, + IEnumerable<(IEdge Edge, INode Node)> Hops) + { + public readonly double Length => Hops.Sum(h => Math.Abs(h.Edge.Start.CenterX - h.Edge.End.CenterX) + Math.Abs(h.Edge.Start.CenterY - h.Edge.End.CenterY)); + } } diff --git a/PamhagenSysCtrl/Helpers/Pipeline/Tank.cs b/PamhagenSysCtrl/Helpers/Pipeline/Tank.cs index a24fb2d..da13c0d 100644 --- a/PamhagenSysCtrl/Helpers/Pipeline/Tank.cs +++ b/PamhagenSysCtrl/Helpers/Pipeline/Tank.cs @@ -1,10 +1,16 @@ using System.Windows.Controls; +using System.Windows.Media; +using System.Windows.Shapes; namespace PamhagenSysCtrl.Helpers.Pipeline { class Tank : INode, ISink, ISource { + public const double WIDTH = 60; + public const double HEIGHT = 100; + public double CenterX { get; set; } public double CenterY { get; set; } + public double TopY => CenterY - HEIGHT / 2; public string Label { get; set; } public bool IsFull => CallbackIsFull(); public bool IsFilled => CallbackIsFilled(); @@ -26,11 +32,21 @@ namespace PamhagenSysCtrl.Helpers.Pipeline { } public void Draw(Canvas canvas) { - + var rect = new Rectangle() { + Width = WIDTH, + Height = HEIGHT, + Stroke = Brushes.Black, + StrokeThickness = 2, + Fill = Brushes.White, + }; + rect.SetValue(Canvas.LeftProperty, CenterX - WIDTH / 2); + rect.SetValue(Canvas.TopProperty, CenterY - HEIGHT / 2); + canvas.Children.Add(rect); } public bool Update(double x, double y) { - return false; + var hover = Math.Abs(x - CenterX) <= WIDTH / 2 && Math.Abs(y - CenterY) <= HEIGHT / 2; + return hover; } } } diff --git a/PamhagenSysCtrl/Windows/PlantSchemeWindow.xaml.cs b/PamhagenSysCtrl/Windows/PlantSchemeWindow.xaml.cs index 8dbc0f5..523ee26 100644 --- a/PamhagenSysCtrl/Windows/PlantSchemeWindow.xaml.cs +++ b/PamhagenSysCtrl/Windows/PlantSchemeWindow.xaml.cs @@ -62,13 +62,13 @@ namespace PamhagenSysCtrl.Windows { Graph.CreatePipe(x6, v8); Graph.CreatePipe(v7, v9); - var x7 = new PipeJoin(390, 210); - var x8 = new PipeJoin(360, 240); - var x9 = new PipeJoin(320, 240); + var xv15 = new PipeJoin(390, 210); + var xv11 = new PipeJoin(360, 240); + var x7 = new PipeJoin(320, 240); - Graph.CreatePipe(v5, x7); - Graph.CreatePipe(v9, x9); - Graph.CreatePipe(x9, x8); + Graph.CreatePipe(v5, xv15); + Graph.CreatePipe(v9, x7); + Graph.CreatePipe(x7, xv11); var v10 = CreateValve(10, 320, 280); var v11 = CreateValve(11, 360, 180); @@ -78,40 +78,105 @@ namespace PamhagenSysCtrl.Windows { var v15 = CreateValve(15, 390, 180); var v16 = CreateValve(16, 520, 180); - Graph.CreatePipe(v10, x9); - Graph.CreatePipe(x8, v11); - Graph.CreatePipe(x7, v15); - Graph.CreatePipe(x7, v12); - Graph.CreatePipe(x8, v13); + Graph.CreatePipe(v10, x7); + Graph.CreatePipe(xv11, v11); + Graph.CreatePipe(xv15, v15); + Graph.CreatePipe(xv15, v12); + Graph.CreatePipe(xv11, v13); - var x10 = new PipeJoin(520, 210); - var x11 = new PipeJoin(490, 240); + var xv16 = new PipeJoin(520, 210); + var xv14 = new PipeJoin(490, 240); - Graph.CreatePipe(v12, x10); - Graph.CreatePipe(v13, x11); - Graph.CreatePipe(x10, v16); - Graph.CreatePipe(x11, v14); + Graph.CreatePipe(v12, xv16); + Graph.CreatePipe(v13, xv14); + Graph.CreatePipe(v16, xv16); + Graph.CreatePipe(v14, xv14); var v20 = CreateValve(20, 650, 180); var v21 = CreateValve(21, 620, 180); - var x12 = new PipeJoin(650, 210); - var x13 = new PipeJoin(620, 240); + var xv20 = new PipeJoin(650, 210); + var xv21 = new PipeJoin(620, 240); - Graph.CreatePipe(x10, x12); - Graph.CreatePipe(x11, x13); - Graph.CreatePipe(x12, v20); - Graph.CreatePipe(x13, v21); + Graph.CreatePipe(xv16, xv20); + Graph.CreatePipe(xv14, xv21); + Graph.CreatePipe(v20, xv20); + Graph.CreatePipe(v21, xv21); - var x14 = new PipeJoin(390, 140); - var x15 = new PipeJoin(490, 140); - var x16 = new PipeJoin(620, 140); + var x8 = new PipeJoin(390, 140); + var x9 = new PipeJoin(490, 140); + var x10 = new PipeJoin(620, 140); - Graph.CreatePipe(v11, x14, true); - Graph.CreatePipe(v15, x14, true); - Graph.CreatePipe(v14, x15, true); - Graph.CreatePipe(v16, x15, true); - Graph.CreatePipe(v20, x16, true); - Graph.CreatePipe(v21, x16, true); + Graph.CreatePipe(v11, x8, true); + Graph.CreatePipe(v15, x8, true); + Graph.CreatePipe(x9, v14); + Graph.CreatePipe(x9, v16); + Graph.CreatePipe(x10, v20); + Graph.CreatePipe(x10, v21); + + var xv22 = new PipeJoin(720, 210); + var xv23 = new PipeJoin(750, 240); + var v22 = CreateValve(22, 720, 280); + var v23 = CreateValve(23, 750, 280); + var t1 = CreateTank(1, 735, 360); + Graph.CreatePipe(xv20, xv22); + Graph.CreatePipe(xv21, xv23); + Graph.CreatePipe(xv22, v22); + Graph.CreatePipe(xv23, v23); + Graph.CreateInlet(v22, t1); + Graph.CreateInlet(v23, t1); + + var xv24 = new PipeJoin(800, 210); + var xv25 = new PipeJoin(830, 240); + var v24 = CreateValve(24, 800, 280); + var v25 = CreateValve(25, 830, 280); + var t2 = CreateTank(2, 815, 360); + Graph.CreatePipe(xv22, xv24); + Graph.CreatePipe(xv23, xv25); + Graph.CreatePipe(xv24, v24); + Graph.CreatePipe(xv25, v25); + Graph.CreateInlet(v24, t2); + Graph.CreateInlet(v25, t2); + + var xv26 = new PipeJoin(880, 210); + var xv27 = new PipeJoin(910, 240); + var v26 = CreateValve(26, 880, 280); + var v27 = CreateValve(27, 910, 280); + var t3 = CreateTank(3, 895, 360); + Graph.CreatePipe(xv24, xv26); + Graph.CreatePipe(xv25, xv27); + Graph.CreatePipe(xv26, v26); + Graph.CreatePipe(xv27, v27); + Graph.CreateInlet(v26, t3); + Graph.CreateInlet(v27, t3); + + var v28 = CreateValve(28, 950, 210); + var v29 = CreateValve(29, 950, 240); + Graph.CreatePipe(xv26, v28); + Graph.CreatePipe(xv27, v29); + + var xv30 = new PipeJoin(990, 210); + var xv31 = new PipeJoin(1020, 240); + var v30 = CreateValve(30, 990, 280); + var v31 = CreateValve(31, 1020, 280); + var t4 = CreateTank(4, 1005, 360); + Graph.CreatePipe(v28, xv30); + Graph.CreatePipe(v29, xv31); + Graph.CreatePipe(xv30, v30); + Graph.CreatePipe(xv31, v31); + Graph.CreateInlet(v30, t4); + Graph.CreateInlet(v31, t4); + + var xv32 = new PipeJoin(1070, 210); + var xv33 = new PipeJoin(1100, 240); + var v32 = CreateValve(32, 1070, 280); + var v33 = CreateValve(33, 1100, 280); + var t5 = CreateTank(5, 1085, 360); + Graph.CreatePipe(xv30, xv32); + Graph.CreatePipe(xv31, xv33); + Graph.CreatePipe(xv32, v32); + Graph.CreatePipe(xv33, v33); + Graph.CreateInlet(v32, t5); + Graph.CreateInlet(v33, t5); Graph.Draw(SchemeCanvas); } @@ -120,6 +185,10 @@ namespace PamhagenSysCtrl.Windows { return new($"V{n}", x, y, () => App.Plant?.IsValveOpen(4) ?? false, () => App.Plant?.WantValveOpen(4) ?? false); } + private static Tank CreateTank(int n, double x, double y) { + return new($"T{n}", x, y, () => App.Plant?.TankFillLevels[n - 1] != FillState.Empty, () => App.Plant?.TankFillLevels[n - 1] != FillState.Full); + } + private void SchemeCanvas_MouseMove(object sender, MouseEventArgs evt) { var p = evt.GetPosition(SchemeCanvas); foreach (var e in Graph.Edges) {