using System; using CommunityToolkit.Mvvm.ComponentModel; namespace HC_APTBS.ViewModels { /// /// Represents the vertical graphic result indicator for a single receive parameter /// within a phase card. Displays expected value, tolerance bounds, and live/final /// measurement result as a vertical progress bar. /// public sealed partial class GraphicIndicatorViewModel : ObservableObject { /// CAN parameter name (e.g. "QDelivery", "QOver"). [ObservableProperty] private string _parameterName = string.Empty; /// Target/expected measurement value. [ObservableProperty] private double _expectedValue; /// Acceptable deviation from the expected value. [ObservableProperty] private double _tolerance; /// /// Current live measurement value, updated in real-time during the measurement phase. /// Triggers recalculation of and . /// [ObservableProperty] private double _currentValue; /// /// Vertical progress bar fill percentage (0-100), computed from /// relative to the display range that includes tolerance margins. /// [ObservableProperty] private double _progressPercent; /// True when falls within the tolerance window. [ObservableProperty] private bool _isWithinTolerance = true; /// True once a measurement has been recorded for this indicator. [ObservableProperty] private bool _hasValue; /// Lower tolerance bound: - . public double MinBound => ExpectedValue - Tolerance; /// Upper tolerance bound: + . public double MaxBound => ExpectedValue + Tolerance; /// Formatted display string for the current value. public string DisplayValue => HasValue ? CurrentValue.ToString("F1") : "---"; // ── Recalculation on value change ───────────────────────────────────────── partial void OnCurrentValueChanged(double value) { HasValue = true; RecalculateProgress(value); IsWithinTolerance = Math.Abs(value - ExpectedValue) <= Tolerance; OnPropertyChanged(nameof(DisplayValue)); } partial void OnExpectedValueChanged(double value) { OnPropertyChanged(nameof(MinBound)); OnPropertyChanged(nameof(MaxBound)); if (HasValue) RecalculateProgress(CurrentValue); } partial void OnToleranceChanged(double value) { OnPropertyChanged(nameof(MinBound)); OnPropertyChanged(nameof(MaxBound)); if (HasValue) RecalculateProgress(CurrentValue); } /// /// Computes the progress bar fill percentage using the same algorithm as the /// original GraphicResultDisplay. The display range extends 20% beyond the /// tolerance bounds on each side so that out-of-tolerance readings are still visible. /// private void RecalculateProgress(double value) { double range = 2.0 * Tolerance; if (range <= 0) { ProgressPercent = 50; return; } // The tolerance band occupies the middle 60% of the bar. // Add 20% margin above and below. double margin = ((100.0 * range / 60.0) - range) / 2.0; double bottom = ExpectedValue - Tolerance - margin; double top = ExpectedValue + Tolerance + margin; double span = top - bottom; if (span <= 0) { ProgressPercent = 50; return; } double pct = (value - bottom) / span * 100.0; ProgressPercent = Math.Clamp(pct, 5.0, 100.0); } /// Resets the indicator to its initial state for a new test run. public void Reset() { CurrentValue = 0; ProgressPercent = 0; IsWithinTolerance = true; HasValue = false; OnPropertyChanged(nameof(DisplayValue)); } } }