From 25f79f92bb5cd640098a61562ba7b0b83df5b3dd Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 24 Oct 2026 08:28:28 +0700 Subject: [PATCH 01/12] style(smv-p1): concise measurement and reference labels, short sync HOLD tooltip, compact publisher summary --- Views/SmvInlineViewer.xaml.cs | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/Views/SmvInlineViewer.xaml.cs b/Views/SmvInlineViewer.xaml.cs index bf979d767..187f9dbd7 100644 --- a/Views/SmvInlineViewer.xaml.cs +++ b/Views/SmvInlineViewer.xaml.cs @@ -84,7 +84,8 @@ private sealed record PendingAnalysis(SmvSnapshotResult Snapshot, string? Select $"{phase.Name}: P {phase.PText} Q₁ {phase.QText} S {phase.SText} PF {phase.PfText}")); public string SyncLabel => SmvSyncPresentation.Label(_visibleSnapshot); public string SyncDetail => _awaitingPublisherSwitch && _visibleSnapshot is not null - ? $"HOLD: sync evidence belongs to last displayed publisher ({_lastDisplayedPublisherKey}), not the newly selected publisher. " + SmvSyncPresentation.Detail(_visibleSnapshot) + ? "HOLD · Previous publisher sync shown.\nNew publisher sync not yet known.\n" + + SmvSyncPresentation.Detail(_visibleSnapshot) : SmvSyncPresentation.Detail(_visibleSnapshot); public Brush SyncBackground => SmvSyncPresentation.Background(_visibleSnapshot); public Brush SyncForeground => SmvSyncPresentation.Foreground(_visibleSnapshot); @@ -94,10 +95,12 @@ private sealed record PendingAnalysis(SmvSnapshotResult Snapshot, string? Select public ICollectionView VoltageMeasurements { get; } public ICollectionView CurrentMeasurements { get; } + // The data grid's column header already supplies RMS and Angle; + // mapping and scale remain accessible by hovering either title. public string VoltageGroupHeading => ActiveUnits == SmvDisplayUnits.NineTwoLe - ? "Voltage · Ch5–8 (V)" : "RAW Group A · Ch1–4 (counts)"; + ? "Voltage (V)" : "RAW · Ch1–4"; public string CurrentGroupHeading => ActiveUnits == SmvDisplayUnits.NineTwoLe - ? "Current · Ch1–4 (A)" : "RAW Group B · Ch5–8 (counts)"; + ? "Current (A)" : "RAW · Ch5–8"; // The original DigSub WPF controls repaint immutable models, not packet work. public WaveformSnapshot? CurrentWaveform { get; private set; } @@ -117,18 +120,18 @@ public string TimeAxisCaption get { if (CurrentWaveform is not { } wave || wave.SampleRateHz <= 0) - return "2 cycles · timebase pending"; + return "Timebase —"; var count = Math.Min(2 * Math.Max(1, wave.SamplesPerCycle), SmvWaveformTimeAxis.AvailableSamples(wave)); return ActiveUnits == SmvDisplayUnits.NineTwoLe && SmvWaveformTimeAxis.ReferenceSample(wave, count) > 0.01 - ? "2 cycles · Ua↑ = 0 ms" - : "2 cycles · first sample = 0 ms"; + ? "Ua = 0 ms" + : "Start = 0 ms"; } } public string PhaseReferenceText => ActiveUnits == SmvDisplayUnits.NineTwoLe - ? "Ua (Ch5) · ∠0° reference" : "Ch1 · ∠0° reference"; + ? "Ref: Ua" : "Ref: Ch1"; public string UnitStatus => RequestedUnits switch { @@ -142,6 +145,8 @@ public string TimeAxisCaption public string CaptureStatus { get => _captureStatus; private set => Change(ref _captureStatus, value, nameof(CaptureStatus)); } public string WaveformStatus { get => _waveformStatus; private set => Change(ref _waveformStatus, value, nameof(WaveformStatus)); } public string StreamDetail { get => _streamDetail; private set => Change(ref _streamDetail, value, nameof(StreamDetail)); } + private string _streamTechnicalDetail = "Choose an SV stream to inspect its publisher metadata."; + public string StreamTechnicalDetail { get => _streamTechnicalDetail; private set => Change(ref _streamTechnicalDetail, value, nameof(StreamTechnicalDetail)); } public SmvInlineViewer() { @@ -534,10 +539,13 @@ private void Freshness_Tick(object? sender, EventArgs e) private void UpdateStreamDetail(SmvLiveStreamSummary? summary) { StreamDetail = summary is null - ? "Passive discovery • no selected publisher. No MMS connection needed." - : $"{summary.SvId} • APPID 0x{summary.AppId:X4}\n" + + ? "Select an SV publisher" + : $"APPID 0x{summary.AppId:X4} · VLAN {summary.Vlan}"; + StreamTechnicalDetail = summary is null + ? "Passive discovery; no MMS connection required." + : $"{summary.SvId} · APPID 0x{summary.AppId:X4}\n" + $"{summary.SourceMac} → {summary.DestinationMac}\n" + - $"VLAN {summary.Vlan} • gap {summary.Gaps} • restart {summary.Restarts}"; + $"VLAN {summary.Vlan} · gap {summary.Gaps} · restart {summary.Restarts}"; } private void ClearPresentation(string message) From 63b68e6f51a7e98ed79771d30ec5b3a3022d86bf Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 24 Oct 2026 08:29:04 +0700 Subject: [PATCH 02/12] style(smv-p1): remove duplicate captions, wrap sync tooltip, keep technical metadata on hover and simplify Power headings --- Views/SmvInlineViewer.xaml | 52 +++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/Views/SmvInlineViewer.xaml b/Views/SmvInlineViewer.xaml index 37d7efbb2..11f94eb53 100644 --- a/Views/SmvInlineViewer.xaml +++ b/Views/SmvInlineViewer.xaml @@ -295,12 +295,12 @@ BorderBrush="{StaticResource BorderSubtle}" BorderThickness="1" CornerRadius="8"> - + ToolTip="{Binding StreamTechnicalDetail}" MaxHeight="95"/> @@ -323,10 +323,18 @@ - + + Padding="8,3" Margin="0,0,9,0"> + + + + + @@ -366,9 +374,10 @@ IsStreamLive="{Binding IsLiveSnapshot}" TimebaseMode="2 cycles" VoltageScale="Auto Peak Hold" CurrentScale="Auto Peak Hold" ShowMode="Both" LayoutMode="Overlay" MarkersEnabled="True"/> - + + @@ -423,9 +432,7 @@ - + @@ -543,12 +550,11 @@ - - + + + From e8e509916efbc7ef6530fcb3fe9cbc911b2c7edd Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 24 Oct 2026 08:30:44 +0700 Subject: [PATCH 03/12] style(smv-p1): remove redundant group mapping and pending frequency labels, render one concise sample-rate footer --- .../Controls/WaveformPanelControl.cs | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/Ports/DigSubAnalyzer/Controls/WaveformPanelControl.cs b/Ports/DigSubAnalyzer/Controls/WaveformPanelControl.cs index 20b293a42..363b14cd4 100644 --- a/Ports/DigSubAnalyzer/Controls/WaveformPanelControl.cs +++ b/Ports/DigSubAnalyzer/Controls/WaveformPanelControl.cs @@ -253,9 +253,9 @@ protected override void OnRender(DrawingContext dc) if (nineTwoLe) { if (showCurrent) - visibleLanes.Add(new LaneDefinition("VOLTAGE · Ch5–8", "V", snapshot.CurrentSeries)); + visibleLanes.Add(new LaneDefinition("VOLTAGE", "V", snapshot.CurrentSeries)); if (showVoltage) - visibleLanes.Add(new LaneDefinition("CURRENT · Ch1–4", "A", snapshot.VoltageSeries)); + visibleLanes.Add(new LaneDefinition("CURRENT", "A", snapshot.VoltageSeries)); } else { @@ -363,7 +363,8 @@ private void DrawLane(DrawingContext dc, Rect rect, LaneDefinition lane, Wavefor if (MarkersEnabled && _cursorVisible && _cursorLane == laneIndex) DrawCursor(dc, innerRect, plotSeries, amplitudeMax, stacked, snapshot); - DrawTimebaseLabel(dc, innerRect, snapshot); + if (laneIndex == 0) + DrawTimebaseLabel(dc, innerRect, snapshot); } private IReadOnlyList BuildVisibleSeries( @@ -1158,10 +1159,21 @@ private void DrawMillisecondsAxis(DrawingContext dc, Rect plot, WaveformSnapshot private static void DrawTimebaseLabel(DrawingContext dc, Rect rect, WaveformSnapshot snapshot) { - var sampleRateText = snapshot.SampleRateHz > 0 ? $"{snapshot.SampleRateHz:0.#} Hz sample" : "Sample rate pending"; - var frequencyText = snapshot.MeasuredFrequencyHz > 0 ? $"{snapshot.MeasuredFrequencyHz:0.###} Hz signal" : "Signal frequency pending"; - dc.DrawText(CreateText(sampleRateText, 10.3, MutedBrush, LabelTypeface), new Point(rect.Left, rect.Bottom + 23)); - dc.DrawText(CreateText(frequencyText, 10.3, MutedBrush, LabelTypeface), new Point(rect.Right - 106, rect.Bottom + 23)); + // Share this technical caption once, never duplicate pending text. + if (snapshot.SampleRateHz > 0 && double.IsFinite(snapshot.SampleRateHz)) + { + var rate = snapshot.SampleRateHz >= 1000 + ? (snapshot.SampleRateHz / 1000.0).ToString("0.###", CultureInfo.InvariantCulture) + " kS/s" + : snapshot.SampleRateHz.ToString("0.#", CultureInfo.InvariantCulture) + " S/s"; + dc.DrawText(CreateText(rate, 10.3, MutedBrush, LabelTypeface), + new Point(rect.Left, rect.Bottom + 23)); + } + if (snapshot.MeasuredFrequencyHz > 0 && double.IsFinite(snapshot.MeasuredFrequencyHz)) + { + var label = CreateText(snapshot.MeasuredFrequencyHz.ToString("0.##", + CultureInfo.InvariantCulture) + " Hz", 10.3, MutedBrush, LabelTypeface); + dc.DrawText(label, new Point(rect.Right - label.Width, rect.Bottom + 23)); + } } // Presentation only: no fade animations/timers or per-sample state. From 5dafbe89c103cf2f74cf05a58bbbec9cbebb1877 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 24 Oct 2026 08:30:56 +0700 Subject: [PATCH 04/12] style(smv-p1): use one concise phasor line, preserve separate-scale distinction without duplicate numeric footer --- .../Controls/PhasorDiagramControl.cs | 23 +++++++------------ 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/Ports/DigSubAnalyzer/Controls/PhasorDiagramControl.cs b/Ports/DigSubAnalyzer/Controls/PhasorDiagramControl.cs index ea253c6ae..4b4a56391 100644 --- a/Ports/DigSubAnalyzer/Controls/PhasorDiagramControl.cs +++ b/Ports/DigSubAnalyzer/Controls/PhasorDiagramControl.cs @@ -273,21 +273,14 @@ protected override void OnRender(DrawingContext dc) } dc.Pop(); - // Numeric scale evidence comes from the SAME six vectors as arrows. - // This footer is a visual reference (per-family maximum), not a - // calibration claim or one shared physical voltage/current scale. - var voltageUnit = _visualItems.FirstOrDefault(item => - item.Family == PhasorFamily.Voltage && IsRenderable(item))?.Unit ?? "counts"; - var currentUnit = _visualItems.FirstOrDefault(item => - item.Family == PhasorFamily.Current && IsRenderable(item))?.Unit ?? "counts"; - var voltageScaleText = PhasorScaleCaption.Format(voltageUnit == "V" ? "U" : "A", voltageMax, voltageUnit); - var currentScaleText = PhasorScaleCaption.Format(currentUnit == "A" ? "I" : "B", currentMax, currentUnit); - DrawText(dc, $"{voltageScaleText} · {currentScaleText}", - 10.0, new Point(6, height - 31), TextSecondaryBrush, BodyTypeface); - DrawText(dc, voltageUnit == "V" && currentUnit == "A" - ? "9-2LE · U solid / I dashed · independent scales" - : "RAW A solid · RAW B dashed · independent count scales", - 9.5, new Point(6, height - 15), TextMutedBrush, BodyTypeface); + // The six callouts already display magnitudes. Avoid two further + // lines repeating normalization/calibration explanations below them. + // Still distinguish the independently normalized vector families. + var mapped = _visualItems.Any(item => item.Unit is "V" or "A"); + DrawText(dc, mapped + ? "U solid · I dashed · separate scales" + : "RAW A solid · B dashed · separate scales", + 9.8, new Point(6, height - 17), TextMutedBrush, BodyTypeface); if (!IsStreamLive) DrawText(dc, "STALE · last valid samples", 11, new Point(7, 5), StaleBrush, LabelTypeface); } From 3c709a828907d412c91a8b4cd614b58e2a394e2b Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 24 Oct 2026 08:31:11 +0700 Subject: [PATCH 05/12] style(smv-p1): short safety-aware SV capture readout with full diagnostic details on hover --- Views/SmvInlineViewer.xaml.cs | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/Views/SmvInlineViewer.xaml.cs b/Views/SmvInlineViewer.xaml.cs index 187f9dbd7..f2381c42c 100644 --- a/Views/SmvInlineViewer.xaml.cs +++ b/Views/SmvInlineViewer.xaml.cs @@ -142,7 +142,33 @@ public string TimeAxisCaption }; public string StreamCountText => $"{Streams.Count:N0} detected"; - public string CaptureStatus { get => _captureStatus; private set => Change(ref _captureStatus, value, nameof(CaptureStatus)); } + + // Instrument status stays concise; original diagnostics remain in tooltip + // and in the dedicated Diagnostics tab. Never conceal an actual HOLD/error. + public string CaptureBrief => _captureStatus switch + { + var status when status.StartsWith("HOLD", StringComparison.OrdinalIgnoreCase) => + "HOLD · stream interrupted", + var status when status.Contains("failed", StringComparison.OrdinalIgnoreCase) || + status.Contains("error", StringComparison.OrdinalIgnoreCase) => + "SV capture error", + _ when _capturing && Streams.Count > 0 => $"LIVE · {Streams.Count} SV stream(s)", + _ when _capturing => "Searching for SV streams", + _ => "SMV idle" + }; + private string? _lastCaptureBrief; + public string CaptureStatus + { + get => _captureStatus; + private set + { + if (_captureStatus == value) return; + Change(ref _captureStatus, value, nameof(CaptureStatus)); + if (_lastCaptureBrief == CaptureBrief) return; + _lastCaptureBrief = CaptureBrief; + Notify(nameof(CaptureBrief)); + } + } public string WaveformStatus { get => _waveformStatus; private set => Change(ref _waveformStatus, value, nameof(WaveformStatus)); } public string StreamDetail { get => _streamDetail; private set => Change(ref _streamDetail, value, nameof(StreamDetail)); } private string _streamTechnicalDetail = "Choose an SV stream to inspect its publisher metadata."; From c69a34129ae6161574c2ce3286c9898683f032f7 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 24 Oct 2026 08:31:26 +0700 Subject: [PATCH 06/12] style(smv-p1): one-line live status, phasor calibration details on hover --- Views/SmvInlineViewer.xaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Views/SmvInlineViewer.xaml b/Views/SmvInlineViewer.xaml index 11f94eb53..13559d76f 100644 --- a/Views/SmvInlineViewer.xaml +++ b/Views/SmvInlineViewer.xaml @@ -410,7 +410,8 @@ + IsStreamLive="{Binding IsLiveSnapshot}" + ToolTip="Voltage/current (or RAW A/B) arrows use separate magnitude scales. Full measurement precision is shown below."/> @@ -619,7 +620,7 @@ - From 4525a88e1313a14a026a7cb9ca21a6a3de4548a3 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 24 Oct 2026 08:31:40 +0700 Subject: [PATCH 07/12] test(smv-p1): align source guards with contextual metadata and compact instrument labels --- tests/ARSAS.Tests/SmvP3PresentationSourceRegressionTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ARSAS.Tests/SmvP3PresentationSourceRegressionTests.cs b/tests/ARSAS.Tests/SmvP3PresentationSourceRegressionTests.cs index 065f3359c..a03d14754 100644 --- a/tests/ARSAS.Tests/SmvP3PresentationSourceRegressionTests.cs +++ b/tests/ARSAS.Tests/SmvP3PresentationSourceRegressionTests.cs @@ -9,7 +9,7 @@ public void P3ReadoutsAreVisibleAndProfileUnverifiedPowerIsNotInvented() { var xaml = Source("Views/SmvInlineViewer.xaml"); Assert.Contains("Text=\"{Binding SyncLabel}\"", xaml); - Assert.Contains("ToolTip=\"{Binding SyncDetail}\"", xaml); + Assert.Contains("PlacementTarget.DataContext.SyncDetail", xaml); Assert.Contains("Text=\"{Binding TimeAxisCaption}\"", xaml); Assert.Contains("Text=\"{Binding PowerP}\"", xaml); Assert.Contains("Text=\"{Binding PowerQ}\"", xaml); From 6d12fdd466742a9f1809b521b5248a3b81355017 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 24 Oct 2026 08:31:43 +0700 Subject: [PATCH 08/12] test(smv-p1): align source guards with contextual metadata and compact instrument labels --- tests/ARSAS.Tests/SmvP3FatUxRegressionTests.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/ARSAS.Tests/SmvP3FatUxRegressionTests.cs b/tests/ARSAS.Tests/SmvP3FatUxRegressionTests.cs index 8d91384c0..3ecd034d2 100644 --- a/tests/ARSAS.Tests/SmvP3FatUxRegressionTests.cs +++ b/tests/ARSAS.Tests/SmvP3FatUxRegressionTests.cs @@ -33,7 +33,8 @@ public void MeasurementsPanel_HasSevenDefinedRows_PowerAndFooterCannotShareOneSl (string?)x.Attribute("Text") == "{Binding UnitStatus}"); Assert.Equal("6", (string?)footer.Attribute("Grid.Row")); Assert.DoesNotContain("IsExpanded=\"False\"", power.ToString()); - Assert.Contains("Text=\"{Binding PowerStatusShort}\"", power.ToString()); + Assert.Contains("Text=\"3φ Power\"", power.ToString()); + Assert.Contains("ToolTip=\"{Binding PowerStatus}\"", power.ToString()); Assert.Equal(2, xaml.Descendants().Count(x => x.Name.LocalName == "DataGrid")); } From 526c0e863574f7d03aba0d3f45ccc95b838151a7 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 24 Oct 2026 08:31:46 +0700 Subject: [PATCH 09/12] test(smv-p1): align source guards with contextual metadata and compact instrument labels --- tests/ARSAS.Tests/SmvP1VisualDensityTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ARSAS.Tests/SmvP1VisualDensityTests.cs b/tests/ARSAS.Tests/SmvP1VisualDensityTests.cs index ad73a4bc0..62507e394 100644 --- a/tests/ARSAS.Tests/SmvP1VisualDensityTests.cs +++ b/tests/ARSAS.Tests/SmvP1VisualDensityTests.cs @@ -93,7 +93,7 @@ public void CompactPowerAndMeasurementPanel_AreContentSized_WithoutBlankBottomAr Assert.Equal("{Binding PowerVisibility}", (string?)power.Attribute("Visibility")); var powerLabels = power.Descendants().Where(n => n.Name.LocalName == "TextBlock") .Select(n => (string?)n.Attribute("Text")).ToArray(); - foreach (var field in new[] { "ACTIVE · P", "REACTIVE · Q₁", "APPARENT · S", "POWER FACTOR", + foreach (var field in new[] { "P", "Q₁", "S", "PF", "{Binding PowerP}", "{Binding PowerQ}", "{Binding PowerS}", "{Binding PowerPf}" }) Assert.Contains(field, powerLabels); Assert.DoesNotContain(power.Descendants(), n => n.Name.LocalName == "UniformGrid"); From 79f0e464c44011d672b4ed8d18bb124dd72e70c3 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 24 Oct 2026 08:32:23 +0700 Subject: [PATCH 10/12] test(smv-p1): mapped waveform titles are concise voltage/current while raw retains channel groups --- tests/ARSAS.Tests/SmvP3FatUxRegressionTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ARSAS.Tests/SmvP3FatUxRegressionTests.cs b/tests/ARSAS.Tests/SmvP3FatUxRegressionTests.cs index 3ecd034d2..33d16a6c5 100644 --- a/tests/ARSAS.Tests/SmvP3FatUxRegressionTests.cs +++ b/tests/ARSAS.Tests/SmvP3FatUxRegressionTests.cs @@ -44,8 +44,8 @@ public void TrueVoltageIsFirstPlotInMappedProfile_AndRawOriginalOrderIsUnchanged var wave = Read("Ports/DigSubAnalyzer/Controls/WaveformPanelControl.cs"); var mapped = wave.IndexOf("if (nineTwoLe)", StringComparison.Ordinal); Assert.True(mapped >= 0); - var voltage = wave.IndexOf("visibleLanes.Add(new LaneDefinition(\"VOLTAGE · Ch5–8\"", mapped, StringComparison.Ordinal); - var current = wave.IndexOf("visibleLanes.Add(new LaneDefinition(\"CURRENT · Ch1–4\"", mapped, StringComparison.Ordinal); + var voltage = wave.IndexOf("visibleLanes.Add(new LaneDefinition(\"VOLTAGE\"", mapped, StringComparison.Ordinal); + var current = wave.IndexOf("visibleLanes.Add(new LaneDefinition(\"CURRENT\"", mapped, StringComparison.Ordinal); Assert.True(voltage > mapped && current > voltage); var raw = wave.IndexOf("visibleLanes.Add(new LaneDefinition(\"RAW Ch1–4\"", current, StringComparison.Ordinal); Assert.True(raw > current); From 2671ca1ddd1003196164db3388d09837183e7e1e Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 24 Oct 2026 08:32:46 +0700 Subject: [PATCH 11/12] test(smv-p1): instrument-first visibility with preserved bounded diagnostic hover, sync semantics, RAW/V-A fidelity --- .../SmvP1InstrumentNoiseRegressionTests.cs | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 tests/ARSAS.Tests/SmvP1InstrumentNoiseRegressionTests.cs diff --git a/tests/ARSAS.Tests/SmvP1InstrumentNoiseRegressionTests.cs b/tests/ARSAS.Tests/SmvP1InstrumentNoiseRegressionTests.cs new file mode 100644 index 000000000..6c3074f38 --- /dev/null +++ b/tests/ARSAS.Tests/SmvP1InstrumentNoiseRegressionTests.cs @@ -0,0 +1,96 @@ +using System.IO; +using System.Linq; +using System.Xml.Linq; + +namespace ARSAS.Tests; + +/// +/// Windows FAT P1 lock: instrument-first surface remains quiet while protocol +/// and provenance evidence stays reachable through bounded hover details. +/// This is presentation-only; do not change sync, timebase or measured values. +/// +public sealed class SmvP1InstrumentNoiseRegressionTests +{ + [Fact] + public void SyncHover_IsBoundedAndWrapped_WhileLiveBadgeRemains() + { + var xaml = XDocument.Parse(Read("Views/SmvInlineViewer.xaml")); + var tooltip = xaml.Descendants().Single(x => x.Name.LocalName == "ToolTip" && + (string?)x.Attribute("MaxWidth") == "330"); + Assert.Contains(tooltip.Descendants(), x => x.Name.LocalName == "TextBlock" && + (string?)x.Attribute("TextWrapping") == "Wrap" && + ((string?)x.Attribute("Text"))!.Contains("SyncDetail", StringComparison.Ordinal)); + Assert.Contains("Text=\"{Binding SyncLabel}\"", Read("Views/SmvInlineViewer.xaml")); + var vm = Read("Views/SmvInlineViewer.xaml.cs"); + Assert.Contains("Previous publisher sync shown.", vm); + Assert.Contains("New publisher sync not yet known.", vm); + Assert.Contains("SmvSyncPresentation.Detail(_visibleSnapshot)", vm); + } + + [Fact] + public void MeasurementSurface_FavorsLiveElectricalValues_AndRetainsOnDemandMetadata() + { + var xaml = Read("Views/SmvInlineViewer.xaml"); + var vm = Read("Views/SmvInlineViewer.xaml.cs"); + Assert.DoesNotContain("Text=\"RMS · phase\"", xaml); + Assert.DoesNotContain("ACTIVE · P", xaml); + Assert.DoesNotContain("REACTIVE · Q₁", xaml); + Assert.DoesNotContain("APPARENT · S", xaml); + Assert.DoesNotContain("POWER FACTOR", xaml); + Assert.Contains("Text=\"3φ Power\"", xaml); + Assert.Contains("ToolTip=\"{Binding PowerDetails}\"", xaml); + Assert.Contains("Text=\"{Binding UnitStatus}\" Visibility=\"Collapsed\"", xaml); + Assert.Contains("ToolTip=\"{Binding UnitStatus}\"", xaml); + Assert.Contains("\"Voltage (V)\"", vm); + Assert.Contains("\"Current (A)\"", vm); + Assert.Contains("\"Ref: Ua\"", vm); + Assert.Contains("\"Ua = 0 ms\"", vm); + Assert.Contains("SmvEngineeringDisplay.Effective(_visibleSnapshot, RequestedUnits)", vm); + } + + [Fact] + public void PublisherAndCapture_StayConciseButFullEvidenceIsStillAvailable() + { + var xaml = Read("Views/SmvInlineViewer.xaml"); + var vm = Read("Views/SmvInlineViewer.xaml.cs"); + Assert.Contains("Text=\"{Binding StreamDetail}\"", xaml); + Assert.Contains("ToolTip=\"{Binding StreamTechnicalDetail}\"", xaml); + Assert.Contains("SourceMac", vm); + Assert.Contains("DestinationMac", vm); + Assert.Contains("summary.Gaps", vm); + Assert.Contains("summary.Restarts", vm); + Assert.Contains("Text=\"{Binding CaptureBrief}\"", xaml); + Assert.Contains("ToolTip=\"{Binding CaptureStatus}\"", xaml); + Assert.Contains("\"SV capture error\"", vm); + Assert.Contains("\"HOLD · stream interrupted\"", vm); + } + + [Fact] + public void WaveformAndPhasor_AvoidRepeatedPendingAndScaleExplanations() + { + var wave = Read("Ports/DigSubAnalyzer/Controls/WaveformPanelControl.cs"); + var phasor = Read("Ports/DigSubAnalyzer/Controls/PhasorDiagramControl.cs"); + Assert.DoesNotContain("Signal frequency pending", wave); + Assert.DoesNotContain("Sample rate pending", wave); + Assert.Contains("if (laneIndex == 0)", wave); + Assert.Contains("DrawTimebaseLabel(dc, innerRect, snapshot);", wave); + Assert.Contains("\"VOLTAGE\"", wave); + Assert.Contains("\"CURRENT\"", wave); + Assert.Contains("U solid · I dashed · separate scales", phasor); + Assert.DoesNotContain("9-2LE · U solid / I dashed · independent scales", phasor); + var xaml = Read("Views/SmvInlineViewer.xaml"); + Assert.Contains("Visibility=\"Collapsed\" ToolTip=\"{Binding WaveformStatus}\"", xaml); + Assert.Contains("ToolTip=\"{Binding WaveformStatus}\"", xaml); + Assert.Contains("Full measurement precision is shown below.", xaml); + } + + private static string Read(string path) + { + for (DirectoryInfo? d = new(AppContext.BaseDirectory); d is not null; d = d.Parent) + { + var resolved = Path.Combine(d.FullName, path); + if (File.Exists(resolved)) return File.ReadAllText(resolved); + } + throw new FileNotFoundException(path); + } +} From 4b2b62f882a81988a39a2f82d1ac8ff67592be02 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 24 Oct 2026 08:38:40 +0700 Subject: [PATCH 12/12] test(smv-p1): align RAW group source assertions with approved concise captions, keep numeric conversion checks --- tests/ARSAS.Tests/SmvP3FatUxRegressionTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ARSAS.Tests/SmvP3FatUxRegressionTests.cs b/tests/ARSAS.Tests/SmvP3FatUxRegressionTests.cs index 33d16a6c5..b5eb93465 100644 --- a/tests/ARSAS.Tests/SmvP3FatUxRegressionTests.cs +++ b/tests/ARSAS.Tests/SmvP3FatUxRegressionTests.cs @@ -87,8 +87,8 @@ public void RawAndNineTwoLe_AreSameWireMagnitudesButExplicitlyDifferentUnitsAndG Assert.Equal("A", current.Unit); Assert.Equal("V", voltage.Unit); var view = Read("Views/SmvInlineViewer.xaml.cs"); - Assert.Contains("RAW Group A · Ch1–4 (counts)", view); - Assert.Contains("RAW Group B · Ch5–8 (counts)", view); + Assert.Contains("RAW · Ch1–4", view); + Assert.Contains("RAW · Ch5–8", view); Assert.Contains("Voltage/Current grouping is unverified", view); }