using System; using CommunityToolkit.Mvvm.ComponentModel; namespace HC_APTBS.ViewModels { /// /// Vertical min/max/target gauge for a single receive parameter on a phase card. /// Ticks continuously while the phase is active (conditioning + measurement) and /// locks to the final pass/fail colour via once the /// phase ends. /// 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 every refresh tick by /// so the bar moves /// through conditioning as well as measurement. /// [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; /// True after the owning phase completes; freezes the fill colour. [ObservableProperty] private bool _isPhaseCompleted; /// Pass/fail outcome of the owning phase. Only meaningful when is true. [ObservableProperty] private bool _phasePassed; /// 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") : "---"; /// Top of the in-tolerance band, as a percent of the bar height (0 = top, 100 = bottom). public double ToleranceBandTopPercent => Tolerance > 0 ? 20.0 : 50.0; /// Height of the in-tolerance band, as a percent of the bar height. public double ToleranceBandHeightPercent => Tolerance > 0 ? 60.0 : 0.0; /// Position of the target line, as a percent of the bar height (symmetric around expected). public double ExpectedMarkerPercent => 50.0; // ── 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)); OnPropertyChanged(nameof(ToleranceBandTopPercent)); OnPropertyChanged(nameof(ToleranceBandHeightPercent)); if (HasValue) RecalculateProgress(CurrentValue); } /// /// Applies a runtime tolerance update (e.g. after DFI auto-adjust) without /// touching the live . Raises change notifications /// for all dependent computed properties. /// public void ApplyTolerance(double expected, double tolerance) { ExpectedValue = expected; Tolerance = tolerance; } /// /// 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; IsPhaseCompleted = false; PhasePassed = false; OnPropertyChanged(nameof(DisplayValue)); } } }