Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
bdea756
Fix report-backed FAT analog evidence latency
masarray Sep 6, 2026
4007aeb
Remove per-event FAT evidence disk barriers
masarray Sep 6, 2026
318771d
Guard relay-bench FAT immediate evidence behavior
masarray Sep 6, 2026
74289c3
Keep final FAT evidence durability without per-event fsync
masarray Sep 6, 2026
8e2429f
Preserve FAT capture status contract for immediate report evidence
masarray Sep 6, 2026
db81148
Align FAT V2 regressions with immediate report evidence
masarray Sep 6, 2026
ad2c4b1
Fast-path already-live Engineering FAT signal matching
masarray Sep 6, 2026
a2592af
Guard already-live Engineering FAT fast attach
masarray Sep 6, 2026
992de77
P0: project FAT command live values from shared process image
masarray Sep 6, 2026
c39ec8c
P0: keep FAT command rows stable during live updates
masarray Sep 6, 2026
2fd1426
test: complete relay-bench fast-path fixture
masarray Sep 6, 2026
c1063c7
test: lock stable FAT command live projection
masarray Sep 6, 2026
90c62d7
fix(fat): keep Value1/Value2 rolling with latest process change
masarray Sep 6, 2026
cb79103
fix(fat): advance completed evidence pair on every new condition
masarray Sep 6, 2026
6716fce
test(fat): lock rolling Value1/Value2 regression
masarray Sep 6, 2026
0f20e27
fix(fat): remove preparation progress visual-tree hot loop
masarray Sep 6, 2026
f3a122d
test(fat): lock preparation progress off hot visual loop
masarray Sep 6, 2026
c56a945
fix(fat-report): remove optional download gate wording
masarray Sep 6, 2026
478909f
test(fat-report): lock concise COMTRADE evidence layout
masarray Sep 6, 2026
d89c61c
Fix FAT rolling capture operator override regression
masarray Sep 6, 2026
ef168e6
Align FAT progress regression with P0 responsiveness guard
masarray Sep 6, 2026
38cf760
Defer FAT close journal sealing off UI thread
masarray Sep 6, 2026
ff77630
Keep FAT close responsive while sealing journals
masarray Sep 6, 2026
f89a2c5
Guard responsive FAT close journal sealing
masarray Sep 6, 2026
991ef5b
Fix FAT P0 first-frame canonical templates
masarray Sep 6, 2026
3352a7f
Lock P0 first-frame and SCL connect-only regressions
masarray Sep 6, 2026
1340a0b
Fix FAT close lifecycle compile guard
masarray Sep 6, 2026
484c5a5
Fix deferred FAT seal record count typing
masarray Sep 6, 2026
612e1b5
Test Engineering Play static dataset connect-only path
masarray Sep 6, 2026
31b8c1b
P0: coalesce FAT journal flushes during UI resume
masarray Sep 6, 2026
cb0d411
P0: keep FAT resume and stop responsive
masarray Sep 6, 2026
33aa8b5
P0: cache FAT live projection index
masarray Sep 6, 2026
3e281d7
P0: project realtime IED connection health into FAT cards
masarray Sep 6, 2026
7b73e23
Fix P0 runtime action model import
masarray Sep 6, 2026
1c5f2a3
P0: roll composite FAT Value2 on any phase change
masarray Sep 6, 2026
0698f3c
Test composite FAT Value2 rolling
masarray Sep 6, 2026
b1c0bcb
Lock P0 FAT responsiveness and connection-health contracts
masarray Sep 6, 2026
4e86a61
P0: paint Start Continue busy state before FAT work
masarray Sep 6, 2026
4c07a3c
P0: instrument FAT command latency
masarray Sep 6, 2026
a53cc38
Test Start Continue responsive button path
masarray Sep 6, 2026
728404e
P0: align FAT command bridge regression with timing instrumentation
masarray Sep 6, 2026
4d87db7
P0: suppress stale poll flicker after confirmed control feedback
masarray Sep 6, 2026
520dac9
P0: cover command feedback freshness fence
masarray Sep 6, 2026
fd928d8
P0: tighten freshness fence source contract
masarray Sep 6, 2026
d84bcca
P0: keep stale command verification out of SOE
masarray Sep 6, 2026
38d9b1d
P0: cover freshness fence SOE integrity
masarray Sep 6, 2026
7158412
P0: preserve exact report provenance through command freshness fence
masarray Sep 6, 2026
a1dad1e
P0: lock exact SOE origin through freshness fence
masarray Sep 6, 2026
9eb8d1d
P0: behavior-test command freshness and SOE authority
masarray Sep 6, 2026
e35f420
P0: bind freshness fence to active control transaction
masarray Sep 6, 2026
35734a0
P0: behavior-test active command transaction fence
masarray Sep 6, 2026
f91abd0
P0: lock freshness fence to active command scope
masarray Sep 6, 2026
6d6628e
fix(fat): keep coherent completed transitions PASS across resume
masarray Sep 6, 2026
e6937db
fix(fat): keep current Value2 rolling after snapshot and recapture
masarray Sep 6, 2026
a435743
fix(fat): move evidence journal I/O off WPF hot path
masarray Sep 6, 2026
da7ff2e
fix(fat): remove FAT tooltips and compact actions to one row
masarray Sep 6, 2026
26f88c8
perf(fat): remove progress tooltip work from dispatcher ticks
masarray Sep 6, 2026
ef221e3
fix(fat): wrap full IEC references in report preview
masarray Sep 6, 2026
0307c08
feat(fat): append final tested witnessed approved sign-off page
masarray Sep 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
287 changes: 223 additions & 64 deletions IoListTestingWindow.CommandPanel.cs

Large diffs are not rendered by default.

64 changes: 21 additions & 43 deletions IoListTestingWindow.P0BenchUx.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,14 @@
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Threading;
using ArIED61850Tester.Models.IoTesting;

namespace ArIED61850Tester;

/// <summary>
/// Bench-facing P0 UX corrections that are intentionally presentation-only.
///
/// - LIVE/VALUE1/VALUE2 always render canonical Boolean text without mutating raw evidence.
/// - Normal per-cell Capture buttons are removed from the template; Recapture remains the
/// explicit operator correction path while FatAutoCaptureCoordinator owns normal capture.
/// - Primary session actions and secondary evidence/status actions use two adaptive rows.
/// Bench-facing FAT UX corrections. FAT deliberately disables ToolTips so hover creation
/// cannot compete with report-backed updates on relay benches; Engineering ToolTips live in
/// MainWindow and are untouched. The FAT action strip is kept in one compact adaptive row.
/// </summary>
public partial class IoListTestingWindow
{
Expand All @@ -38,22 +34,19 @@ private static void P0BenchUxLoaded(object sender, RoutedEventArgs e)
return;

window._p0BenchUxInstalled = true;
window.ContentRendered += window.P0BenchUxContentRendered;
window.Closed += window.P0BenchUxClosed;
window.Dispatcher.BeginInvoke(
new Action(window.ApplyP0BenchUx),
DispatcherPriority.ContextIdle);
}

private void P0BenchUxContentRendered(object? sender, EventArgs e)
=> Dispatcher.BeginInvoke(new Action(ApplyP0BenchUx), DispatcherPriority.ContextIdle);
// FAT-only. Do not touch MainWindow/Engineering ToolTips.
ToolTipService.SetIsEnabled(window, false);

private void P0BenchUxClosed(object? sender, EventArgs e)
{
ContentRendered -= P0BenchUxContentRendered;
Closed -= P0BenchUxClosed;
// Final FAT V2 schema is installed before first visible render.
window.InstallFatV2WorkspaceUx();
window.ApplyP0BenchUx();
}

private void P0BenchUxClosed(object? sender, EventArgs e)
=> Closed -= P0BenchUxClosed;

private void ApplyP0BenchUx()
{
ConfigureP0StableFatColumns();
Expand Down Expand Up @@ -129,9 +122,6 @@ private static DataTemplate BuildP0EvidenceValueTemplate(FatValueSlot slot)
value.SetValue(TextBlock.FontWeightProperty, FontWeights.SemiBold);
value.SetValue(TextBlock.FontSizeProperty, 11.4);
value.SetValue(TextBlock.TextTrimmingProperty, TextTrimming.CharacterEllipsis);
value.SetBinding(FrameworkElement.ToolTipProperty, new Binding(isValue1
? nameof(IoTestPointPlan.Value1EvidenceToolTip)
: nameof(IoTestPointPlan.Value2EvidenceToolTip)));
panel.AppendChild(value);

var timestamp = new FrameworkElementFactory(typeof(TextBlock));
Expand All @@ -143,8 +133,6 @@ private static DataTemplate BuildP0EvidenceValueTemplate(FatValueSlot slot)
timestamp.SetValue(TextBlock.ForegroundProperty, new SolidColorBrush(Color.FromRgb(112, 126, 145)));
panel.AppendChild(timestamp);

// Intentionally no normal Capture button. Automatic capture is the normal path;
// multi-row/context Recapture remains available for explicit evidence correction.
return new DataTemplate { VisualTree = panel };
}

Expand All @@ -166,36 +154,26 @@ private void ConfigureP0AdaptiveHeaderActions()
{
Orientation = Orientation.Horizontal,
HorizontalAlignment = HorizontalAlignment.Right,
VerticalAlignment = VerticalAlignment.Center
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(0)
};

// Retained as an empty compatibility panel because P2 helpers reference it. All FAT
// actions/status now share the one visible compact row instead of forcing a second row.
_p0SecondaryHeaderActions = new WrapPanel
{
Orientation = Orientation.Horizontal,
HorizontalAlignment = HorizontalAlignment.Right,
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(0, 5, 0, 0)
Visibility = Visibility.Collapsed,
Margin = new Thickness(0)
};

foreach (var child in children)
{
if (IsP0SecondaryHeaderAction(child))
_p0SecondaryHeaderActions.Children.Add(child);
else
_p0PrimaryHeaderActions.Children.Add(child);
}
_p0PrimaryHeaderActions.Children.Add(child);

actionPanel.Children.Add(_p0PrimaryHeaderActions);
if (_p0SecondaryHeaderActions.Children.Count > 0)
actionPanel.Children.Add(_p0SecondaryHeaderActions);
}

private bool IsP0SecondaryHeaderAction(UIElement element)
=> ReferenceEquals(element, WorkspacePreviewToggle) ||
ReferenceEquals(element, _timeSyncEvidenceButton) ||
ReferenceEquals(element, _comtradeEvidenceButton) ||
ReferenceEquals(element, _cleanSessionButton) ||
ReferenceEquals(element, _clockSyncGlobalStatusText) ||
ReferenceEquals(element, _clockSyncEvidenceText);
private bool IsP0SecondaryHeaderAction(UIElement element) => false;
}

public sealed class P0FatCanonicalValueConverter : IValueConverter
Expand All @@ -207,4 +185,4 @@ public object Convert(object value, Type targetType, object parameter, CultureIn

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
=> Binding.DoNothing;
}
}
46 changes: 30 additions & 16 deletions IoListTestingWindow.P0Lifecycle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@
using System.IO;
using System.Windows;
using System.Windows.Threading;
using ArIED61850Tester.Services.IoTesting;

namespace ArIED61850Tester;

/// <summary>
/// P0 responsiveness guard for the FAT workspace lifecycle.
///
/// Confirmation and evidence-session state transitions remain on the WPF Dispatcher, but
/// the high-rate FAT live projection is quiesced before sealing/saving and durable workspace
/// persistence runs on a worker thread. This prevents a closing window from competing with
/// incoming report traffic while preserving UI-bound session state safety.
/// the high-rate FAT live projection is quiesced before sealing/saving. Close-only durable
/// journal flush/read-back verification and workspace persistence run on worker threads so
/// a closing window never blocks the Dispatcher on disk work.
/// </summary>
public partial class IoListTestingWindow
{
Expand Down Expand Up @@ -91,33 +92,46 @@ private async void P0Window_Closing(object? sender, CancelEventArgs e)
engineeringWindow.SuspendIoFatRuntimeProjection(this);

IsEnabled = false;
// Let the disabled/closing visual state paint before journal sealing. The actual
// session mutation remains on Dispatcher; only durable workspace I/O is offloaded.
// Let the disabled/closing visual state paint before journal sealing. Session state
// remains Dispatcher-owned; only the physical seal/read-back work is deferred.
await Dispatcher.Yield(DispatcherPriority.Render);

try
{
if (Session.HasActiveSessions)
{
// Session.StopAll mutates controller/project state and raises UI-bound
// PropertyChanged notifications. Keep that state transition on Dispatcher;
// offloading the whole coordinator would create a cross-thread WPF defect.
var stopAll = Session.StopAll(
"Workspace closed by operator; per-IED evidence journal sealed.");
if (!stopAll.Succeeded)
var stopSucceeded = false;
var stopMessage = string.Empty;
// StopAll still performs controller/project state mutation and UI-bound
// PropertyChanged notifications synchronously on this Dispatcher. The scope
// changes only production journal Dispose(): its durable flush + read-back
// verification are queued to a worker and awaited immediately afterwards.
using (IoTestEvidenceJournal.BeginDeferredSealScope())
{
var stopAll = Session.StopAll(
"Workspace closed by operator; per-IED evidence journal sealed.");
stopSucceeded = stopAll.Succeeded;
stopMessage = stopAll.Message;
}

// Do not allow project save or window close until every queued journal has
// completed its physical disk barrier and full hash-chain read-back.
await IoTestEvidenceJournal.AwaitDeferredSealsAsync();

if (!stopSucceeded)
{
MessageBox.Show(
this,
stopAll.Message,
stopMessage,
"Evidence journals could not be sealed",
MessageBoxButton.OK,
MessageBoxImage.Error);
return;
}
}

// Persistence is pure durable I/O after the session state is sealed and is the
// portion that must not occupy the WPF Dispatcher.
// Persistence is pure durable I/O after every session journal is actually sealed
// and verified, so it also stays away from the WPF Dispatcher.
if (Storage != null)
await Task.Run(Storage.SaveNow);

Expand All @@ -128,8 +142,8 @@ private async void P0Window_Closing(object? sender, CancelEventArgs e)
{
var answer = MessageBox.Show(
this,
$"ARSAS could not save the latest IO FAT progress.\n\n{ex.Message}\n\nClose the workspace anyway?",
"Progress save failed",
$"ARSAS could not seal the FAT evidence or save the latest IO FAT progress.\n\n{ex.Message}\n\nClose the workspace anyway?",
"FAT close failed",
MessageBoxButton.YesNo,
MessageBoxImage.Error,
MessageBoxResult.No);
Expand Down
Loading
Loading