From 69bfda54e1e84f89a4638a17738c765bfdf4e7bf Mon Sep 17 00:00:00 2001 From: LucianoDev Date: Mon, 20 Apr 2026 17:45:59 +0200 Subject: [PATCH] feat: redesign Bench page with Fluent card layout and radial advance monitor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three-column layout replacing the old HMI grid: - BenchRpmCommandCard: inline numeric input, 2×4 preset grid, Start/Stop - BenchActuatorsCard: direction toggle, oil pump, temperature PID, misc relays with FluentStateToggle showing checked state via AccentFillColor - BenchLiveDataCard: 2×5 KPI tiles (RPM, P1, P2, Q-Delivery, Q-Over, temps) - BenchChartsCard: 2×2 compact chart grid (Delivery, Over, P1, P2) - AdvanceMonitorCard: RadialAngleGauge custom FrameworkElement + PSG/INJ readouts, Δ° lock offset input, Zero PSG / Zero INJ buttons Supporting changes: - AngleDisplayViewModel: promote _currentManualDegrees, _isLockSet to [ObservableProperty]; add PsgRelativeDegrees, InjEncoderDegreesValue, TargetLockAngle, IsRunningMode (29/31 hysteresis); computed PrimaryGaugeAngle, TargetAngleForGauge, SecondaryGaugeAngle - BenchControlViewModel: add IsDirectionLeft computed property, SetDirectionRightCommand, SetDirectionLeftCommand, ApplyRpmCommand - FlowmeterChartView: add IsCompact DP (false default) for 90px compact height - Styles.xaml: add IsChecked trigger to FluentStateToggle (accent fill + white text) - Strings.en/es.xaml: add all new card and actuator string keys Co-Authored-By: Claude Sonnet 4.6 --- Resources/Strings.en.xaml | 46 ++++ Resources/Strings.es.xaml | 46 ++++ Resources/Styles.xaml | 5 + ViewModels/AngleDisplayViewModel.cs | 60 ++++- ViewModels/BenchControlViewModel.cs | 22 +- Views/Controls/RadialAngleGauge.cs | 228 +++++++++++++++++ Views/Pages/BenchPage.xaml | 97 ++++---- Views/UserControls/AdvanceMonitorCard.xaml | 125 ++++++++++ Views/UserControls/AdvanceMonitorCard.xaml.cs | 14 ++ Views/UserControls/BenchActuatorsCard.xaml | 219 ++++++++++++++++ Views/UserControls/BenchActuatorsCard.xaml.cs | 14 ++ Views/UserControls/BenchChartsCard.xaml | 93 +++++++ Views/UserControls/BenchChartsCard.xaml.cs | 14 ++ Views/UserControls/BenchLiveDataCard.xaml | 235 ++++++++++++++++++ Views/UserControls/BenchLiveDataCard.xaml.cs | 14 ++ Views/UserControls/BenchRpmCommandCard.xaml | 145 +++++++++++ .../UserControls/BenchRpmCommandCard.xaml.cs | 14 ++ Views/UserControls/FlowmeterChartView.xaml | 3 +- Views/UserControls/FlowmeterChartView.xaml.cs | 26 ++ 19 files changed, 1361 insertions(+), 59 deletions(-) create mode 100644 Views/Controls/RadialAngleGauge.cs create mode 100644 Views/UserControls/AdvanceMonitorCard.xaml create mode 100644 Views/UserControls/AdvanceMonitorCard.xaml.cs create mode 100644 Views/UserControls/BenchActuatorsCard.xaml create mode 100644 Views/UserControls/BenchActuatorsCard.xaml.cs create mode 100644 Views/UserControls/BenchChartsCard.xaml create mode 100644 Views/UserControls/BenchChartsCard.xaml.cs create mode 100644 Views/UserControls/BenchLiveDataCard.xaml create mode 100644 Views/UserControls/BenchLiveDataCard.xaml.cs create mode 100644 Views/UserControls/BenchRpmCommandCard.xaml create mode 100644 Views/UserControls/BenchRpmCommandCard.xaml.cs diff --git a/Resources/Strings.en.xaml b/Resources/Strings.en.xaml index 0b08b10..893e835 100644 --- a/Resources/Strings.en.xaml +++ b/Resources/Strings.en.xaml @@ -240,6 +240,52 @@ Set PSG zero reference Set INJ zero reference + + RPM Command + Actual + Target + Apply + Actuators & Relays + Direction + Right + Left + Oil Pump + Counter + Set + Temperature + Setpoint °C + Tolerance ±°C + Heater + Dep. Cool. + T-In Cool. + Misc Relays + Electronic + Flasher + Pulse 4 + Live Data + Live Charts + Advance Monitor + Lock Offset Δ° + Zero PSG + Zero INJ + Lock + Bench RPM + Pressure P1 + Pressure P2 + Q Delivery + Q Over + T-In + T-Out + T4 + T-Tank + Bench Temp + bar + cc/s + Q Delivery + Q Over + Pressure P1 + Pressure P2 + ▶ START TEST ■ STOP diff --git a/Resources/Strings.es.xaml b/Resources/Strings.es.xaml index d38262b..2456e50 100644 --- a/Resources/Strings.es.xaml +++ b/Resources/Strings.es.xaml @@ -240,6 +240,52 @@ Fijar referencia cero PSG Fijar referencia cero INJ + + Mando RPM + Actual + Objetivo + Aplicar + Actuadores y Relés + Dirección + Derecha + Izquierda + Bomba de Aceite + Contador + Fijar + Temperatura + Consigna °C + Tolerancia ±°C + Calefactor + Refr. Dep. + Refr. T-In + Relés Misc. + Electrónico + Flasher + Pulso 4 + Datos en Vivo + Gráficos + Monitor de Avance + Offset Bloqueo Δ° + Cero PSG + Cero INJ + Bloqueo + RPM Banco + Presión P1 + Presión P2 + Q Entrega + Q Derrame + T-Ent. + T-Sal. + T4 + T-Tanque + T. Banco + bar + cc/iny. + Q Entrega + Q Derrame + Presión P1 + Presión P2 + ▶ INICIAR TEST ■ PARAR diff --git a/Resources/Styles.xaml b/Resources/Styles.xaml index c806b27..1eb08af 100644 --- a/Resources/Styles.xaml +++ b/Resources/Styles.xaml @@ -93,6 +93,11 @@ IsHitTestVisible="False"/> + + + + + diff --git a/ViewModels/AngleDisplayViewModel.cs b/ViewModels/AngleDisplayViewModel.cs index 8d10eaa..5f0e4ec 100644 --- a/ViewModels/AngleDisplayViewModel.cs +++ b/ViewModels/AngleDisplayViewModel.cs @@ -31,10 +31,15 @@ namespace HC_APTBS.ViewModels private double _zeroPsgEncoder; private double _zeroInjDegrees; private double _injEncoderDegrees; + [ObservableProperty] + [NotifyPropertyChangedFor(nameof(PrimaryGaugeAngle))] + [NotifyPropertyChangedFor(nameof(SecondaryGaugeAngle))] private double _currentManualDegrees; private double _lockAngleValue; - private bool _isLockSet; - private bool _isManualVisible; + [ObservableProperty] + [NotifyPropertyChangedFor(nameof(TargetAngleForGauge))] + private bool _isLockSet; + private bool _isManualVisible; // ── Observable properties (bound by View) ───────────────────────────────── @@ -68,6 +73,27 @@ namespace HC_APTBS.ViewModels /// Foreground brush for the lock angle display (Green/Red/White). [ObservableProperty] private Brush _lockAngleForeground = Brushes.White; + // ── Numeric doubles for radial advance-monitor control ──────────────────── + + /// PSG relative angle as a double for gauge binding. + [ObservableProperty] + [NotifyPropertyChangedFor(nameof(PrimaryGaugeAngle))] + private double _psgRelativeDegrees; + + /// INJ encoder absolute degrees (0–360) as a double. + [ObservableProperty] private double _injEncoderDegreesValue; + + /// Lock angle target (0–360 degrees) as a double for gauge binding. + [ObservableProperty] + [NotifyPropertyChangedFor(nameof(TargetAngleForGauge))] + private double _targetLockAngle; + + /// True when bench RPM ≥ 31, hysteresis-cleared below 29. + [ObservableProperty] + [NotifyPropertyChangedFor(nameof(PrimaryGaugeAngle))] + [NotifyPropertyChangedFor(nameof(SecondaryGaugeAngle))] + private bool _isRunningMode; + // ── Constructor ─────────────────────────────────────────────────────────── /// @@ -78,6 +104,17 @@ namespace HC_APTBS.ViewModels _encoderResolution = configService.Settings.EncoderResolution; } + // ── Computed for radial gauge ───────────────────────────────────────────── + + /// Primary thumb angle: manual-wheel angle in hand-mode, PSG relative angle when running. + public double PrimaryGaugeAngle => IsRunningMode ? PsgRelativeDegrees : CurrentManualDegrees; + + /// Target thumb angle for the radial gauge, or null when no lock has been set yet. + public double? TargetAngleForGauge => IsLockSet ? TargetLockAngle : (double?)null; + + /// Ghost secondary thumb angle: manual-wheel position shown when running, null in hand-mode (primary already shows it). + public double? SecondaryGaugeAngle => IsRunningMode ? CurrentManualDegrees : (double?)null; + // ── Public update (called from MainViewModel.OnRefreshTick) ─────────────── /// @@ -98,6 +135,11 @@ namespace HC_APTBS.ViewModels _currentRpm = rpm; _isDirectionRight = isDirectionRight; + if (!IsRunningMode && rpm >= 31.0) + IsRunningMode = true; + else if (IsRunningMode && rpm < 29.0) + IsRunningMode = false; + RecalculatePsg(); RecalculateInj(); RecalculateManual(); @@ -150,7 +192,8 @@ namespace HC_APTBS.ViewModels if (double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out double delta)) { _lockAngleValue = CalculateLockAngle(delta); - _isLockSet = true; + TargetLockAngle = _lockAngleValue; + IsLockSet = true; LockAngleDisplay = FormatAngle(_lockAngleValue); } @@ -183,8 +226,9 @@ namespace HC_APTBS.ViewModels if (relDeg > 180) relDeg = 360 - relDeg; relDeg *= sign; - PsgEncoderAngle = FormatAngle(rawDeg); - PsgRelativeAngle = FormatAngle(relDeg); + PsgRelativeDegrees = relDeg; + PsgEncoderAngle = FormatAngle(rawDeg); + PsgRelativeAngle = FormatAngle(relDeg); } private void RecalculateInj() @@ -202,7 +246,8 @@ namespace HC_APTBS.ViewModels double sign = DirectionSign; // INJ encoder → degrees (full 0–360, NO 180 normalization). - _injEncoderDegrees = _injRaw * (360.0 / _encoderResolution); + _injEncoderDegrees = _injRaw * (360.0 / _encoderResolution); + InjEncoderDegreesValue = _injEncoderDegrees; // Relative angle from zero reference. double relDeg = (_injEncoderDegrees - _zeroInjDegrees) * sign; @@ -216,13 +261,14 @@ namespace HC_APTBS.ViewModels CultureInfo.InvariantCulture, out double delta)) { _lockAngleValue = CalculateLockAngle(delta); + TargetLockAngle = _lockAngleValue; LockAngleDisplay = FormatAngle(_lockAngleValue); } } private void RecalculateManual() { - _currentManualDegrees = _manualRaw * (360.0 / _encoderResolution); + CurrentManualDegrees = _manualRaw * (360.0 / _encoderResolution); if (_currentRpm < 30) { diff --git a/ViewModels/BenchControlViewModel.cs b/ViewModels/BenchControlViewModel.cs index 9205aa6..0731c4c 100644 --- a/ViewModels/BenchControlViewModel.cs +++ b/ViewModels/BenchControlViewModel.cs @@ -22,7 +22,12 @@ namespace HC_APTBS.ViewModels // ── Direction ───────────────────────────────────────────────────────────── /// True when the bench rotates clockwise (right). False for counter-clockwise (left). - [ObservableProperty] private bool _isDirectionRight = true; + [ObservableProperty] + [NotifyPropertyChangedFor(nameof(IsDirectionLeft))] + private bool _isDirectionRight = true; + + /// True when the bench rotates counter-clockwise (left). Computed inverse of . + public bool IsDirectionLeft => !IsDirectionRight; // ── Oil pump ────────────────────────────────────────────────────────────── @@ -85,6 +90,14 @@ namespace HC_APTBS.ViewModels _bench.SetRelay(RelayNames.DirectionLeft, !value); } + /// Sets the bench rotation direction to clockwise (right). + [RelayCommand] + private void SetDirectionRight() => IsDirectionRight = true; + + /// Sets the bench rotation direction to counter-clockwise (left). + [RelayCommand] + private void SetDirectionLeft() => IsDirectionRight = false; + // ── Oil pump toggle ─────────────────────────────────────────────────────── partial void OnIsOilPumpOnChanged(bool value) @@ -167,6 +180,13 @@ namespace HC_APTBS.ViewModels IsBenchRunning = false; } + /// + /// Applies the RPM value from and starts the bench. + /// Bound to the inline Apply button in BenchRpmCommandCard. + /// + [RelayCommand] + private void ApplyRpm() => StartBench(); + /// /// Quick-select button handler: sets the RPM input and starts the bench. /// diff --git a/Views/Controls/RadialAngleGauge.cs b/Views/Controls/RadialAngleGauge.cs new file mode 100644 index 0000000..857c10e --- /dev/null +++ b/Views/Controls/RadialAngleGauge.cs @@ -0,0 +1,228 @@ +using System; +using System.Globalization; +using System.Windows; +using System.Windows.Media; + +namespace HC_APTBS.Views.Controls +{ + /// + /// Geometry-rendered circular angle gauge for the bench advance-monitoring display. + /// Shows a 360° dial with 5° tick steps, major labels every 45°, and up to three + /// directional thumb markers: a primary thumb (manual wheel or PSG), an optional + /// target thumb (lock-angle target, colour-coded for tolerance), and an optional + /// ghost thumb (secondary / de-emphasised context indicator). + /// + public sealed class RadialAngleGauge : FrameworkElement + { + // ── Dependency properties ───────────────────────────────────────────────── + + public static readonly DependencyProperty PrimaryAngleProperty = + DependencyProperty.Register(nameof(PrimaryAngle), typeof(double), typeof(RadialAngleGauge), + new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender)); + + public static readonly DependencyProperty TargetAngleProperty = + DependencyProperty.Register(nameof(TargetAngle), typeof(double?), typeof(RadialAngleGauge), + new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender)); + + public static readonly DependencyProperty SecondaryAngleProperty = + DependencyProperty.Register(nameof(SecondaryAngle), typeof(double?), typeof(RadialAngleGauge), + new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender)); + + public static readonly DependencyProperty PrimaryBrushProperty = + DependencyProperty.Register(nameof(PrimaryBrush), typeof(Brush), typeof(RadialAngleGauge), + new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender)); + + public static readonly DependencyProperty TargetBrushProperty = + DependencyProperty.Register(nameof(TargetBrush), typeof(Brush), typeof(RadialAngleGauge), + new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender)); + + public static readonly DependencyProperty IsRunningModeProperty = + DependencyProperty.Register(nameof(IsRunningMode), typeof(bool), typeof(RadialAngleGauge), + new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsRender)); + + // ── Property accessors ──────────────────────────────────────────────────── + + /// Primary thumb angle in degrees (0 = top, clockwise positive). + public double PrimaryAngle + { + get => (double)GetValue(PrimaryAngleProperty); + set => SetValue(PrimaryAngleProperty, value); + } + + /// Target thumb angle in degrees, or null to hide it. + public double? TargetAngle + { + get => (double?)GetValue(TargetAngleProperty); + set => SetValue(TargetAngleProperty, value); + } + + /// Ghost secondary thumb angle in degrees, or null to hide it. + public double? SecondaryAngle + { + get => (double?)GetValue(SecondaryAngleProperty); + set => SetValue(SecondaryAngleProperty, value); + } + + /// Fill brush for the primary thumb. Falls back to the accent brush when null. + public Brush? PrimaryBrush + { + get => (Brush?)GetValue(PrimaryBrushProperty); + set => SetValue(PrimaryBrushProperty, value); + } + + /// Fill brush for the target thumb (green = within tolerance, red = off-target). + public Brush? TargetBrush + { + get => (Brush?)GetValue(TargetBrushProperty); + set => SetValue(TargetBrushProperty, value); + } + + /// When true the mode label switches to PSG context; adjusts visual emphasis. + public bool IsRunningMode + { + get => (bool)GetValue(IsRunningModeProperty); + set => SetValue(IsRunningModeProperty, value); + } + + // ── Layout ──────────────────────────────────────────────────────────────── + + protected override Size MeasureOverride(Size availableSize) + { + double side = double.IsInfinity(availableSize.Width) ? 240.0 : availableSize.Width; + if (!double.IsInfinity(availableSize.Height)) + side = Math.Min(side, availableSize.Height); + side = Math.Max(side, 120.0); + return new Size(side, side); + } + + // ── Render ──────────────────────────────────────────────────────────────── + + protected override void OnRender(DrawingContext dc) + { + double w = ActualWidth; + double h = ActualHeight; + double side = Math.Min(w, h); + double cx = w / 2.0; + double cy = h / 2.0; + + // Radii as fractions of half-side + double halfS = side / 2.0; + double rOuter = halfS * 0.88; + double rInner = halfS * 0.82; + double rTickOut = halfS * 0.86; + double rMinorIn = halfS * 0.80; + double rMajorIn = halfS * 0.74; + double rLabel = halfS * 0.68; + double rThumbTip = halfS * 0.88; + double rThumbBase= halfS * 0.72; + double thumbHalf = halfS * 0.06; // half-width of triangle base + + // Brushes — use WPF-UI resources when available, safe hard-coded fallbacks otherwise + Brush ringBrush = GetThemeBrush("ControlStrokeColorDefaultBrush", new SolidColorBrush(Color.FromArgb(64, 200, 200, 200))); + Brush tickBrush = GetThemeBrush("TextFillColorSecondaryBrush", new SolidColorBrush(Color.FromArgb(120, 180, 180, 180))); + Brush majorBrush = GetThemeBrush("TextFillColorPrimaryBrush", Brushes.WhiteSmoke); + Brush accentBrush = GetThemeBrush("AccentFillColorPrimaryBrush", Brushes.DodgerBlue); + Brush primaryFill = PrimaryBrush ?? accentBrush; + Brush targetFill = TargetBrush ?? Brushes.White; + Brush ghostFill = new SolidColorBrush(Color.FromArgb(80, 200, 200, 200)); + + var ringPen = new Pen(ringBrush, 1.0); + var minorPen = new Pen(tickBrush, 1.0); + var majorPen = new Pen(majorBrush, 2.0); + + // Background ring (outer circle) + dc.DrawEllipse(null, ringPen, new Point(cx, cy), rOuter, rOuter); + dc.DrawEllipse(null, new Pen(ringBrush, 0.5), new Point(cx, cy), rInner, rInner); + + // Tick marks and labels + var labelTypeface = new Typeface("Segoe UI"); + double dpi = VisualTreeHelper.GetDpi(this).PixelsPerDip; + + for (int deg = 0; deg < 360; deg += 5) + { + bool isMajor = (deg % 45 == 0); + double rad = deg * Math.PI / 180.0; + double sinA = Math.Sin(rad); + double cosA = Math.Cos(rad); + + double oR = rTickOut; + double iR = isMajor ? rMajorIn : rMinorIn; + var p1 = new Point(cx + oR * sinA, cy - oR * cosA); + var p2 = new Point(cx + iR * sinA, cy - iR * cosA); + dc.DrawLine(isMajor ? majorPen : minorPen, p1, p2); + + if (isMajor) + { + string label = deg.ToString(CultureInfo.InvariantCulture); + var ft = new FormattedText(label, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, + labelTypeface, 10, majorBrush, dpi); + var labelPt = new Point( + cx + rLabel * sinA - ft.Width / 2, + cy - rLabel * cosA - ft.Height / 2); + dc.DrawText(ft, labelPt); + } + } + + // Ghost / secondary thumb + if (SecondaryAngle.HasValue) + DrawThumb(dc, cx, cy, SecondaryAngle.Value, rThumbTip, rThumbBase, thumbHalf, ghostFill, null); + + // Target thumb (outline + thin fill) + if (TargetAngle.HasValue) + { + var targetOutline = new Pen(targetFill, 1.5); + DrawThumb(dc, cx, cy, TargetAngle.Value, rThumbTip + halfS * 0.03, rThumbBase - halfS * 0.03, thumbHalf * 1.3, null, targetOutline); + } + + // Primary thumb (filled) + DrawThumb(dc, cx, cy, PrimaryAngle, rThumbTip, rThumbBase, thumbHalf, primaryFill, null); + + // Centre readout + string centre = $"{PrimaryAngle:F1}°"; + var cft = new FormattedText(centre, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, + new Typeface("Consolas"), 14, primaryFill, dpi); + dc.DrawText(cft, new Point(cx - cft.Width / 2, cy - cft.Height / 2)); + + // Mode label (tiny) + string modeLabel = IsRunningMode ? "PSG" : "MAN"; + var mft = new FormattedText(modeLabel, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, + new Typeface("Segoe UI"), 9, tickBrush, dpi); + dc.DrawText(mft, new Point(cx - mft.Width / 2, cy + 12)); + } + + // ── Helpers ─────────────────────────────────────────────────────────────── + + /// Draws an inward-pointing triangle thumb at the given dial angle. + private static void DrawThumb( + DrawingContext dc, double cx, double cy, + double angleDeg, double tipR, double baseR, double halfWidth, + Brush? fill, Pen? outline) + { + double rad = angleDeg * Math.PI / 180.0; + double sinA = Math.Sin(rad); + double cosA = Math.Cos(rad); + double perpSin = Math.Sin(rad + Math.PI / 2.0); + double perpCos = Math.Cos(rad + Math.PI / 2.0); + + var tip = new Point(cx + tipR * sinA, cy - tipR * cosA); + var left = new Point(cx + baseR * sinA + halfWidth * perpSin, + cy - baseR * cosA - halfWidth * perpCos); + var right = new Point(cx + baseR * sinA - halfWidth * perpSin, + cy - baseR * cosA + halfWidth * perpCos); + + var geo = new StreamGeometry(); + using (var ctx = geo.Open()) + { + ctx.BeginFigure(tip, true, true); + ctx.LineTo(left, true, false); + ctx.LineTo(right, true, false); + } + geo.Freeze(); + dc.DrawGeometry(fill, outline, geo); + } + + /// Resolves a WPF-UI theme brush by key with a safe fallback. + private Brush GetThemeBrush(string key, Brush fallback) + => TryFindResource(key) as Brush ?? fallback; + } +} diff --git a/Views/Pages/BenchPage.xaml b/Views/Pages/BenchPage.xaml index 46a19f8..d99feee 100644 --- a/Views/Pages/BenchPage.xaml +++ b/Views/Pages/BenchPage.xaml @@ -5,62 +5,59 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:uc="clr-namespace:HC_APTBS.Views.UserControls" mc:Ignorable="d" - d:DesignHeight="900" d:DesignWidth="1280"> + d:DesignHeight="900" d:DesignWidth="1400"> - - - - - - + + + + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + diff --git a/Views/UserControls/AdvanceMonitorCard.xaml b/Views/UserControls/AdvanceMonitorCard.xaml new file mode 100644 index 0000000..ff574c2 --- /dev/null +++ b/Views/UserControls/AdvanceMonitorCard.xaml @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Views/UserControls/AdvanceMonitorCard.xaml.cs b/Views/UserControls/AdvanceMonitorCard.xaml.cs new file mode 100644 index 0000000..8693bdc --- /dev/null +++ b/Views/UserControls/AdvanceMonitorCard.xaml.cs @@ -0,0 +1,14 @@ +using System.Windows.Controls; + +namespace HC_APTBS.Views.UserControls +{ + /// + /// Fluent card hosting the radial advance angle gauge, PSG/INJ readouts, + /// lock-offset input, and set-zero buttons. + /// DataContext = . + /// + public partial class AdvanceMonitorCard : UserControl + { + public AdvanceMonitorCard() => InitializeComponent(); + } +} diff --git a/Views/UserControls/BenchActuatorsCard.xaml b/Views/UserControls/BenchActuatorsCard.xaml new file mode 100644 index 0000000..eec3136 --- /dev/null +++ b/Views/UserControls/BenchActuatorsCard.xaml @@ -0,0 +1,219 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Views/UserControls/BenchActuatorsCard.xaml.cs b/Views/UserControls/BenchActuatorsCard.xaml.cs new file mode 100644 index 0000000..d8e62a7 --- /dev/null +++ b/Views/UserControls/BenchActuatorsCard.xaml.cs @@ -0,0 +1,14 @@ +using System.Windows.Controls; + +namespace HC_APTBS.Views.UserControls +{ + /// + /// Fluent card grouping all bench actuator controls: oil pump, counter, direction, + /// temperature PID, and auxiliary relays. + /// DataContext = . + /// + public partial class BenchActuatorsCard : UserControl + { + public BenchActuatorsCard() => InitializeComponent(); + } +} diff --git a/Views/UserControls/BenchChartsCard.xaml b/Views/UserControls/BenchChartsCard.xaml new file mode 100644 index 0000000..48189a9 --- /dev/null +++ b/Views/UserControls/BenchChartsCard.xaml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Views/UserControls/BenchChartsCard.xaml.cs b/Views/UserControls/BenchChartsCard.xaml.cs new file mode 100644 index 0000000..c4a7aa0 --- /dev/null +++ b/Views/UserControls/BenchChartsCard.xaml.cs @@ -0,0 +1,14 @@ +using System.Windows.Controls; + +namespace HC_APTBS.Views.UserControls +{ + /// + /// Fluent card with a 2×2 grid of compact real-time bench charts + /// (Q-Delivery, Q-Over, P1, P2). + /// DataContext = . + /// + public partial class BenchChartsCard : UserControl + { + public BenchChartsCard() => InitializeComponent(); + } +} diff --git a/Views/UserControls/BenchLiveDataCard.xaml b/Views/UserControls/BenchLiveDataCard.xaml new file mode 100644 index 0000000..d97e4be --- /dev/null +++ b/Views/UserControls/BenchLiveDataCard.xaml @@ -0,0 +1,235 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Views/UserControls/BenchLiveDataCard.xaml.cs b/Views/UserControls/BenchLiveDataCard.xaml.cs new file mode 100644 index 0000000..85826f6 --- /dev/null +++ b/Views/UserControls/BenchLiveDataCard.xaml.cs @@ -0,0 +1,14 @@ +using System.Windows.Controls; + +namespace HC_APTBS.Views.UserControls +{ + /// + /// Fluent card with two rows of KPI tiles showing bench live readings + /// (RPM, pressures, flow, temperatures). + /// DataContext = . + /// + public partial class BenchLiveDataCard : UserControl + { + public BenchLiveDataCard() => InitializeComponent(); + } +} diff --git a/Views/UserControls/BenchRpmCommandCard.xaml b/Views/UserControls/BenchRpmCommandCard.xaml new file mode 100644 index 0000000..45833ae --- /dev/null +++ b/Views/UserControls/BenchRpmCommandCard.xaml @@ -0,0 +1,145 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Views/UserControls/BenchRpmCommandCard.xaml.cs b/Views/UserControls/BenchRpmCommandCard.xaml.cs new file mode 100644 index 0000000..0fd491c --- /dev/null +++ b/Views/UserControls/BenchRpmCommandCard.xaml.cs @@ -0,0 +1,14 @@ +using System.Windows.Controls; + +namespace HC_APTBS.Views.UserControls +{ + /// + /// Fluent card providing RPM numeric input, preset buttons, Start/Stop and + /// voltage readout for manual bench motor control. + /// DataContext = . + /// + public partial class BenchRpmCommandCard : UserControl + { + public BenchRpmCommandCard() => InitializeComponent(); + } +} diff --git a/Views/UserControls/FlowmeterChartView.xaml b/Views/UserControls/FlowmeterChartView.xaml index db41982..9cdc113 100644 --- a/Views/UserControls/FlowmeterChartView.xaml +++ b/Views/UserControls/FlowmeterChartView.xaml @@ -18,7 +18,8 @@ VerticalAlignment="Center"/> - /// UserControl hosting a single real-time flowmeter chart. /// DataContext is expected to be a . + /// Set to true to reduce chart height to 90 px + /// (used in the 2×2 bench chart grid). /// public partial class FlowmeterChartView : UserControl { + /// When true the chart height shrinks from 120 to 90 px. + public static readonly DependencyProperty IsCompactProperty = + DependencyProperty.Register(nameof(IsCompact), typeof(bool), typeof(FlowmeterChartView), + new FrameworkPropertyMetadata(false, + (d, e) => ((FlowmeterChartView)d).ChartHeight = (bool)e.NewValue ? 90.0 : 120.0)); + + public bool IsCompact + { + get => (bool)GetValue(IsCompactProperty); + set => SetValue(IsCompactProperty, value); + } + + /// Derived chart height (120 or 90); bound in XAML. + public static readonly DependencyProperty ChartHeightProperty = + DependencyProperty.Register(nameof(ChartHeight), typeof(double), typeof(FlowmeterChartView), + new FrameworkPropertyMetadata(120.0)); + + public double ChartHeight + { + get => (double)GetValue(ChartHeightProperty); + set => SetValue(ChartHeightProperty, value); + } + public FlowmeterChartView() { InitializeComponent();