From d9c1b79563fa635c15ce58be635a4be1cfaae376 Mon Sep 17 00:00:00 2001 From: Lorenz Stechauner Date: Tue, 4 Aug 2026 23:49:12 +0200 Subject: [PATCH] Add switcher --- PamhagenSysCtrl/Helpers/PamhagenGraph.cs | 14 +-- PamhagenSysCtrl/Helpers/PamhagenPlant.cs | 24 +++-- .../Helpers/Pipeline/ConveyorBelt.cs | 13 ++- .../Helpers/Pipeline/EncasedAuger.cs | 5 +- PamhagenSysCtrl/Helpers/Pipeline/Switcher.cs | 87 +++++++++++++++++++ PamhagenSysCtrl/Windows/MainWindow.xaml | 16 ++-- .../Windows/PlantSchemeWindow.xaml.cs | 20 ++++- 7 files changed, 143 insertions(+), 36 deletions(-) create mode 100644 PamhagenSysCtrl/Helpers/Pipeline/Switcher.cs diff --git a/PamhagenSysCtrl/Helpers/PamhagenGraph.cs b/PamhagenSysCtrl/Helpers/PamhagenGraph.cs index ffd7912..5e3c03c 100644 --- a/PamhagenSysCtrl/Helpers/PamhagenGraph.cs +++ b/PamhagenSysCtrl/Helpers/PamhagenGraph.cs @@ -16,6 +16,8 @@ namespace PamhagenSysCtrl.Helpers { public readonly Pump P4; public readonly Pump P5; public readonly Pump P6; + public readonly EncasedAuger Auger1; + public readonly EncasedAuger Auger2; public PamhagenGraph() { double mw2X = 180; @@ -48,16 +50,18 @@ namespace PamhagenSysCtrl.Helpers { var trough = new EntryTrough("Auffangwanne", mw1X + 70, 100, () => App.Plant?.IsTroughAugerActive ?? false); Nodes.Add(trough); - var auger1 = new EncasedAuger("", (mw1X + mw2X) / 2 - 100, 200, () => App.Plant?.IsAuger1Active ?? false); - var auger2 = new EncasedAuger("Pressen-\nQuerschnecke", 40, 200, () => App.Plant?.IsPresseQuerSchneckeActive ?? false, 180); - var auger3 = new EncasedAuger("", 120, 80, () => App.Plant?.IsAuger2Active ?? false, 135); + Auger1 = new EncasedAuger("", (mw1X + mw2X) / 2 - 100, 200, () => App.Plant?.IsAuger1Active ?? false); + var auger3 = new EncasedAuger("Pressen-\nQuerschnecke", 40, 200, () => App.Plant?.IsPresseQuerSchneckeActive ?? false, 180, clickable: false); + Auger2 = new EncasedAuger("", 120, 80, () => App.Plant?.IsAuger2Active ?? false, 135); var belt = new ConveyorBelt("", 90, 160, () => App.Plant?.IsBeltActive ?? false); Nodes.Add(belt); - Nodes.Add(auger1); - Nodes.Add(auger2); + Nodes.Add(Auger1); Nodes.Add(auger3); + Nodes.Add(Auger2); var rebler = new Rebler("Rebler", (mw1X + mw2X) / 2, 200, () => App.Plant?.IsReblerActive ?? false); Nodes.Add(rebler); + var switcher = new Switcher("", (mw1X + mw2X) / 2, 260, () => App.Plant?.TroughSelector ?? 0, () => App.Plant == null ? 0 : App.Plant.WantA1Selected ? 1 : 2); + Nodes.Add(switcher); MW1 = new Trough("MW1", mw1X, mpY - 70, () => App.Plant?.FillLevelA1 ?? FillState.Unknown); MW2 = new Trough("MW2", mw2X, mpY - 70, () => App.Plant?.FillLevelA2 ?? FillState.Unknown); diff --git a/PamhagenSysCtrl/Helpers/PamhagenPlant.cs b/PamhagenSysCtrl/Helpers/PamhagenPlant.cs index f6e6923..9195900 100644 --- a/PamhagenSysCtrl/Helpers/PamhagenPlant.cs +++ b/PamhagenSysCtrl/Helpers/PamhagenPlant.cs @@ -25,6 +25,7 @@ namespace PamhagenSysCtrl.Helpers { public bool AreValvesOpen(params int[] ns) => ns.All(IsValveOpen); public bool AreValvesClosed(params int[] ns) => ns.All(IsValveClosed); + public int TroughSelector => Sensors.TroughSelector; public bool IsA1Selected => Sensors.TroughSelector == 1; public bool IsA2Selected => Sensors.TroughSelector == 2; public FillState FillLevelA1 => Sensors.FillingLevels.A1; @@ -329,7 +330,7 @@ namespace PamhagenSysCtrl.Helpers { set => Actuators.P4 = (value != MotorState.Halt && value != MotorState.Forward) ? throw new ArgumentException("Invalid state for P4") : value; } - public bool P5HasClearance => FillLevelT4 != FillState.Empty && ( + public bool P5HasClearance => FillLevelT5 != FillState.Empty && ( (P5HasClearancePress1 && FillLevelPress1 != FillState.Full) || (P5HasClearancePress2 && FillLevelPress2 != FillState.Full) || (HasClearanceTank1(false, P5HasClearanceTanksB) && FillLevelT1 != FillState.Full) || @@ -383,7 +384,7 @@ namespace PamhagenSysCtrl.Helpers { } public void StartTroughAuger() { - if (!IsReblerActive || !((IsA1Selected && FillLevelA1 != FillState.Full) || (IsA2Selected && FillLevelA2 != FillState.Full))) + if (!IsReblerActive || (IsA1Selected && FillLevelA1 == FillState.Full) || (IsA2Selected && FillLevelA2 == FillState.Full)) throw new NoClearanceException("Schnecke Auffangwanne hat keine Freigabe"); Actuators.TroughAuger = true; ActuatorsChanged = true; @@ -396,6 +397,7 @@ namespace PamhagenSysCtrl.Helpers { public void StartRebler() { Actuators.Rebler = true; + StartAuger1(); ActuatorsChanged = true; } @@ -427,20 +429,25 @@ namespace PamhagenSysCtrl.Helpers { public void StartBelt() { Actuators.Belt = true; + StartAuger2(); ActuatorsChanged = true; } public void StopBelt() { + if (IsPresseQuerSchneckeActive) throw new NoClearanceException("Presse-Querschnecke aktiv"); + if (IsAuger1Active) throw new NoClearanceException("Förderschnecke 1 aktiv"); Actuators.Belt = false; ActuatorsChanged = true; } public void StartAuger1() { Actuators.Auger1 = true; + StartBelt(); ActuatorsChanged = true; } public void StopAuger1() { + if (IsReblerActive) throw new NoClearanceException("Rebler aktiv"); Actuators.Auger1 = false; ActuatorsChanged = true; } @@ -451,22 +458,11 @@ namespace PamhagenSysCtrl.Helpers { } public void StopAuger2() { + if (IsBeltActive) throw new NoClearanceException("Förderband aktiv"); Actuators.Auger2 = false; ActuatorsChanged = true; } - public void StartWasteDisposal() { - StartBelt(); - StartAuger1(); - StartAuger2(); - } - - public void StopWasteDisposal() { - StopBelt(); - StopAuger1(); - StopAuger2(); - } - public void StartGradationPump() { Actuators.GradationPump = true; ActuatorsChanged = true; diff --git a/PamhagenSysCtrl/Helpers/Pipeline/ConveyorBelt.cs b/PamhagenSysCtrl/Helpers/Pipeline/ConveyorBelt.cs index 3094c44..85b8c39 100644 --- a/PamhagenSysCtrl/Helpers/Pipeline/ConveyorBelt.cs +++ b/PamhagenSysCtrl/Helpers/Pipeline/ConveyorBelt.cs @@ -2,13 +2,12 @@ using System.Windows.Controls; using System.Windows.Media; using System.Windows.Media.Animation; -using System.Windows.Media.Media3D; using System.Windows.Shapes; namespace PamhagenSysCtrl.Helpers.Pipeline { public class ConveyorBelt : INode { - public const double WIDTH = 35; + public const double WIDTH = 30; public const double HEIGHT = 120; public double CenterX { get; set; } @@ -58,7 +57,7 @@ namespace PamhagenSysCtrl.Helpers.Pipeline { _rect.SetValue(Canvas.LeftProperty, CenterX - WIDTH / 2); _rect.SetValue(Canvas.TopProperty, CenterY - HEIGHT / 2); - var d = 20; + var d = 15; int n = (int)(HEIGHT / d) + 2; _polyline = new Polyline() { Points = [.. Enumerable.Range(0, n).SelectMany(i => new List() { @@ -81,12 +80,12 @@ namespace PamhagenSysCtrl.Helpers.Pipeline { Storyboard.SetTarget(slideUp, _polyline); Storyboard.SetTargetProperty(slideUp, new PropertyPath(Canvas.TopProperty)); var clipCanvas = new Canvas() { - Width = WIDTH - 20, - Height = HEIGHT - 2, + Width = WIDTH - 15, + Height = HEIGHT - 8, ClipToBounds = true, }; - clipCanvas.SetValue(Canvas.TopProperty, CenterY - HEIGHT / 2 + 2); - clipCanvas.SetValue(Canvas.LeftProperty, CenterX - WIDTH / 2 + 10); + clipCanvas.SetValue(Canvas.TopProperty, CenterY - HEIGHT / 2 + 4); + clipCanvas.SetValue(Canvas.LeftProperty, CenterX - WIDTH / 2 + 7.5); clipCanvas.Children.Add(_polyline); canvas.Children.Add(_rect); diff --git a/PamhagenSysCtrl/Helpers/Pipeline/EncasedAuger.cs b/PamhagenSysCtrl/Helpers/Pipeline/EncasedAuger.cs index cf87c0e..8f45389 100644 --- a/PamhagenSysCtrl/Helpers/Pipeline/EncasedAuger.cs +++ b/PamhagenSysCtrl/Helpers/Pipeline/EncasedAuger.cs @@ -16,6 +16,7 @@ namespace PamhagenSysCtrl.Helpers.Pipeline { public ISet Inputs { get; init; } public ISet Outputs { get; init; } + public bool IsClickable { get; private set; } public bool IsActive => CallbackActive(); public bool IsAnimationActive { get => _animationActive; @@ -33,12 +34,13 @@ namespace PamhagenSysCtrl.Helpers.Pipeline { private Auger? _auger; private TextBlock? _text; - public EncasedAuger(string label, double x, double y, Func cbActive, double angle = 0) { + public EncasedAuger(string label, double x, double y, Func cbActive, double angle = 0, bool clickable = true) { CenterX = x; CenterY = y; Label = label; Angle = angle; CallbackActive = cbActive; + IsClickable = clickable; Inputs = new HashSet(); Outputs = new HashSet(); } @@ -84,6 +86,7 @@ namespace PamhagenSysCtrl.Helpers.Pipeline { } public void Update(bool isHovering, bool isPressed) { + isHovering = isHovering && IsClickable; IsAnimationActive = IsActive; _inner?.Fill = IsAnimationActive && isHovering && isPressed ? PamhagenBrushes.Red : diff --git a/PamhagenSysCtrl/Helpers/Pipeline/Switcher.cs b/PamhagenSysCtrl/Helpers/Pipeline/Switcher.cs new file mode 100644 index 0000000..1210d70 --- /dev/null +++ b/PamhagenSysCtrl/Helpers/Pipeline/Switcher.cs @@ -0,0 +1,87 @@ +using System.Windows.Controls; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; + +namespace PamhagenSysCtrl.Helpers.Pipeline { + public class Switcher : INode { + + public const double WIDTH = 100; + public const double HEIGHT = 50; + + public double CenterX { get; set; } + public double CenterY { get; set; } + public string Label { get; set; } + public ISet Inputs { get; init; } + public ISet Outputs { get; init; } + + private Line? _main; + private Line? _shadow; + private RotateTransform? _mainTransform; + private RotateTransform? _shadowTransform; + + protected Func CallbackIsPosition; + protected Func CallbackWantPosition; + + public Switcher(string label, double x, double y, Func cbIsPosition, Func cbWantPosition) { + CenterX = x; + CenterY = y; + Label = label; + CallbackIsPosition = cbIsPosition; + CallbackWantPosition = cbWantPosition; + Inputs = new HashSet(); + Outputs = new HashSet(); + } + + public void Draw(Canvas canvas) { + _mainTransform = new RotateTransform(0, CenterX, CenterY); + _shadowTransform = new RotateTransform(0, CenterX, CenterY); + _main = new Line() { + X1 = CenterX - WIDTH / 2, Y1 = CenterY, + X2 = CenterX + WIDTH / 2, Y2 = CenterY, + StrokeThickness = 5, + Stroke = Brushes.Black, + StrokeStartLineCap = PenLineCap.Square, + StrokeEndLineCap = PenLineCap.Square, + RenderTransform = _mainTransform, + }; + _shadow = new Line() { + X1 = CenterX - WIDTH / 2, Y1 = CenterY, + X2 = CenterX + WIDTH / 2, Y2 = CenterY, + StrokeThickness = 5, + Stroke = Brushes.DarkGray, + StrokeStartLineCap = PenLineCap.Square, + StrokeEndLineCap = PenLineCap.Square, + RenderTransform = _shadowTransform, + }; + canvas.Children.Add(_shadow); + canvas.Children.Add(_main); + } + + public bool IsInside(double x, double y) { + return Math.Abs(x - CenterX) <= WIDTH / 2 && Math.Abs(y - CenterY) <= HEIGHT / 2; + } + + public void Update(bool isHovering, bool isPressed) { + var isPos = CallbackIsPosition(); + var wantPos = CallbackWantPosition(); + if (isHovering && wantPos == isPos) wantPos = wantPos == 1 ? 2 : 1; + var target1 = isPos == 0 ? 0 : isPos == 1 ? 15 : -15; + var target2 = wantPos == 0 ? 0 : wantPos == 1 ? 15 : -15; + _mainTransform?.BeginAnimation( + RotateTransform.AngleProperty, + new DoubleAnimation { + From = _mainTransform.Angle, + To = target1, + Duration = TimeSpan.FromSeconds(Math.Abs(_mainTransform.Angle - target1) / 120.0) + }); + _shadowTransform?.BeginAnimation( + RotateTransform.AngleProperty, + new DoubleAnimation { + From = _shadowTransform.Angle, + To = target2, + Duration = TimeSpan.FromSeconds(Math.Abs(_shadowTransform.Angle - target2) / 120.0) + }); + } + } +} diff --git a/PamhagenSysCtrl/Windows/MainWindow.xaml b/PamhagenSysCtrl/Windows/MainWindow.xaml index 02d3c6c..c192b88 100644 --- a/PamhagenSysCtrl/Windows/MainWindow.xaml +++ b/PamhagenSysCtrl/Windows/MainWindow.xaml @@ -61,31 +61,31 @@ Margin="135,115,10,10" VerticalAlignment="Top" HorizontalAlignment="Left" Click="GradationPumpButton_Click"/> -