Skip to content

FAT P2: embed production FAT workspace in Engineering tab - #290

Open
masarray wants to merge 34 commits into
mainfrom
feat/native-fat-workspace
Open

FAT P2: embed production FAT workspace in Engineering tab#290
masarray wants to merge 34 commits into
mainfrom
feat/native-fat-workspace

Conversation

@masarray

@masarray masarray commented Sep 9, 2026

Copy link
Copy Markdown
Owner

Milestone

Move the proven production FAT workspace into the persistent Engineering FAT tab and make Engineering/Explorer the single SCL/static-DataSet/live-value authority.

Production FAT tab pivot

  • keeps the P0/P1 Engineering shell already merged to main
  • keeps the global persistent IED Explorer visible as selected-device/navigation authority
  • keeps the shared Command Dock as the only command UI/runtime owner
  • embeds the existing IoListTestingWindow production central workspace into the FAT tab
  • reuses the existing IoTestProject, IoTestMultiSessionCoordinator, IoTestSessionController, persistence, auto-capture and FAT result lifecycle instead of implementing a second FAT state machine
  • keeps the production FAT Window loaded-but-hidden as the existing controller/lifecycle owner while its central visual surface is hosted by MainWindow
  • removes the legacy FAT window's duplicate IED Explorer and suppresses its duplicate command panel in embedded mode

Engineering-owned automatic FAT bootstrap

  • opening SCL once in Engineering is now sufficient; clicking FAT no longer requires a second Open SCL for FAT
  • when the selected Engineering IED already owns a parsed SclIedWorkspace with static DataSet members, FAT automatically builds the production project from those exact in-memory ARIEC workspaces
  • IoFatEngineeringWorkspaceProjectionService consumes the existing Engineering SclIedWorkspace objects and FatSclWorkspaceImportService static-DataSet projection; it does not invoke SclWorkspaceService.OpenAsync or parse XML again
  • source bytes are only re-described/hashed for provenance and package/persistence integrity; the IEC 61850 model itself is not reopened
  • the exact Engineering runtime workspaces are registered into the production FAT SCL runtime cache so existing shared acquisition/session ownership is preserved
  • project identity/schema remains compatible with the production ARSAS-FAT-SCL-1.0 persistence path, so saved FAT progress can continue using the same source identity
  • if no selected Engineering IED/static DataSet authority exists, the fallback launcher remains available rather than fabricating FAT rows

P0 field fixes

  • automatic Engineering -> FAT bootstrap no longer allows the historical MainWindow.Hide() / child-Window Show() hand-off to blank the already-visible Engineering shell; the compatibility hide path still delegates to WPF outside that narrowly-scoped embedded bootstrap
  • automatic Engineering FAT is explicitly a Static DataSet workflow: after shared selection synchronization, any persisted or newly synthesized scl-manual-* workspace overlays are retired from active WorkspaceSelected scope when authoritative static DataSet rows exist
  • retiring a manual overlay does not delete it and does not alter TestEnabled, FAT disposition or captured evidence; saved audit/evidence continuity is preserved
  • the duplicate-live-reference session preflight remains strict and unchanged; the fix removes the invalid duplicate scope instead of weakening the one-edge/one-result safety guard
  • regression coverage reproduces the field failure where a static SCL member and a different manual scalar reference both resolve to the same primary live leaf, proves preflight blocks before cleanup, then proves automatic static cleanup makes the valid static scope pass

FAT workflow

  • primary workflow remains the proven Start / Pause / Resume / Stop production session flow
  • Value 1 / Value 2 evidence capture and completion/result semantics remain production FAT controller behavior
  • experimental top-level Capture V1, Capture V2, PASS, REVIEW, FAIL, Reset current workflow is retired from the visible FAT tab
  • manual capture/recapture remains only where the existing production workspace already exposes it as an override

Report Preview

  • reuses the existing IoListTestingWindow.PrintPreview implementation
  • Print Preview switches the entire FAT center between signal/testing view and the native paged report preview
  • no right-side pseudo-preview is used
  • existing page/zoom/print/PDF preview behavior remains owned by the production report code

Navigation / visual language

  • FAT remains the seventh Engineering workspace
  • added a seven-slot late navigation parity correction because legacy MainWindow navigation clamps selection index 6 to Diagnostics/index 5
  • the same workflow selection pill now moves into the seventh equal-width cell
  • Diagnostics no longer remains visually active while FAT is selected
  • FAT inherits the same segmented-button sizing, alignment, selected foreground and pill geometry as the other Engineering tabs
  • final canonical seven-destination navigation cleanup remains a separate visual P1 field item

Engineering selection bridge

  • when FAT is idle, changing the persistent Engineering IED Explorer retargets the embedded production FAT context using stable live DeviceId / IP / SCL IED identity
  • an active FAT session is not retargeted by navigation changes; existing production session ownership remains latched
  • explicit manual SCL workflows retain manual-row behavior; only the automatic Engineering Static DataSet bootstrap retires manual overlays from active FAT workspace scope

Excel intentionally deferred

  • no new Excel import/export flow is added to the FAT tab
  • legacy Excel compatibility remains available only through the existing compatibility path

Safety boundary

No Select/SBO/Operate sequencing, ctlModel, confirmation, interlock/synchrocheck/Test, CommandTermination, RCB runtime behavior, GOOSE capture, MMS acquisition, live-monitor acquisition, or production FAT evidence semantics are changed.

Regression coverage

  • automatic FAT bootstrap must consume device.SclWorkspace and must not call an SCL/XML open/parser path
  • selected Engineering static DataSet triggers production workspace bootstrap
  • exact Engineering runtime workspaces are adopted by production FAT
  • automatic static scope retires manual aliases before the production FAT window/session is exposed
  • manual-only projects are not altered by static-scope cleanup
  • embedded automatic bootstrap keeps Engineering visible instead of exposing the legacy window hand-off
  • seventh FAT destination uses 7-cell pill geometry and correct active foreground

Validation

Latest head 4dfc8344f0838f489b735d3045ae71ec2abec89e:

  • Build ARSAS #2346 ✅ source/invariant checks, restore, compile, full application regression tests, portable publish, portable EXE smoke test
  • Validate IO List Testing #1059 ✅ focused build + IO/FAT regression tests
  • Validate SV evidence bundles #1361 ✅

PR remains unmerged pending physical-IED field verification of the P0 transition/preflight fixes and the separate P1 FAT navigation visual cleanup.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2d5a962c93

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Models/NativeFatModels.cs
Comment on lines +164 to +169
public bool CaptureValue(int slot)
{
if (!CanCapture || slot is < 1 or > 2)
return false;

var capture = CaptureCurrent();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Require a live sample before capturing FAT evidence

When the selected IED comes from an offline SCL or cached project, a SignalDefinition exists with its default value/quality ("-" and "Unknown") even though there is no active live point. Because CanCapture only checks for the existence of either source object, CaptureValue records that placeholder as commissioning evidence and the row can subsequently be marked PASS. Require an active, initialized, sufficiently fresh live observation rather than merely an engineering-model source.

Useful? React with 👍 / 👎.

Comment on lines +114 to +115
File.Move(tempPath, path, overwrite: true);
state.StoragePath = path;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep unreadable state files from being overwritten

When the preferred JSON is damaged, TryReadAsync returns null and LoadAsync creates a new state whose StoragePath is that same file; the automatic save queued after reconciliation then replaces it here. This contradicts the recovery behavior promised by TryReadAsync and permanently destroys the old commissioning evidence before an operator can recover it. Quarantine the damaged file or choose a different path for the replacement state.

Useful? React with 👍 / 👎.

Comment thread Services/NativeFatStateStore.cs Outdated
Comment on lines +329 to +330
private string GetPreferredPath(string? iedName)
=> Path.Combine(_rootDirectory, SanitizeFileStem(iedName) + ".json");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Include device identity in each state filename

When two active devices have the same display name, or names that sanitize to the same stem, both states resolve to the same JSON path even though their DeviceId values differ. Saving the second state uses overwrite mode and destroys the first device's FAT evidence; duplicate IED names are possible across separate SCL sources and endpoints. Derive the path from a collision-resistant device identity in addition to the display name.

Useful? React with 👍 / 👎.

Comment on lines +305 to +310
if (!string.IsNullOrWhiteSpace(state.DeviceId) &&
state.DeviceId.Equals(device.DeviceId, StringComparison.OrdinalIgnoreCase))
return true;

return string.IsNullOrWhiteSpace(state.DeviceId) &&
state.IedName.Equals(device.Name, StringComparison.OrdinalIgnoreCase);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Use an identity that survives reopening an endpoint

When an operator re-adds the same endpoint in a later application launch without reopening a saved project, Iec61850MonitorDevice.DeviceId is a new random GUID. This check therefore rejects the prior JSON even when the discovered IED name and preferred path are identical, after which a blank reconciled state overwrites that file. Base persisted matching on a durable endpoint/SCL/device identity, or persist the generated ID independently of project files, so the advertised resumable state works across ordinary launches.

Useful? React with 👍 / 👎.

Comment on lines +960 to +963
_nativeFatInstallRetry?.Stop();
_nativeFatReconcileTimer?.Stop();
_nativeFatSaveTimer?.Stop();
_nativeFatLoadCts?.Cancel();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Flush pending FAT edits during shutdown

When the user closes the application within the 350 ms autosave debounce after a capture or result change, shutdown has already cancelled _applicationCancellation and this handler simply stops the pending timer without saving its state. The last commissioning action is therefore lost permanently despite the UI saying autosave was queued. Flush the pending state before cancellation/closure and await that save before disposing the gate.

Useful? React with 👍 / 👎.

Comment on lines +869 to +873
private async void NativeFatSaveTimer_Tick(object? sender, EventArgs e)
{
_nativeFatSaveTimer?.Stop();
if (_nativeFatCurrentState != null)
await SaveNativeFatStateAsync(_nativeFatCurrentState);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Bind each queued save to the state that was edited

When an operator edits FAT data and selects another IED before the debounce expires, loading the new IED replaces _nativeFatCurrentState; the timer then dereferences that field and saves the new state instead of the departing edited state. The old edits remain only in the cache and are lost if that IED is not revisited before exit. Capture the state when scheduling the save or maintain independent pending saves per device.

Useful? React with 👍 / 👎.

Comment on lines +642 to +646
if (!forceReconcile && _nativeFatCurrentState != null &&
_nativeFatCurrentState.DeviceId.Equals(device.DeviceId, StringComparison.OrdinalIgnoreCase))
{
UpdateNativeFatSummary();
return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reconcile after Explorer selection changes

When Explorer signal selections change while FAT is not selected, the FAT collection-change handler ignores any resulting point changes and does not observe SignalDefinition.IsSelected changes at all. Re-entering FAT with the same device then returns here without reconciliation, so added selections remain absent and removed selections remain current until the user manually clicks Refresh. Track selection changes or force reconciliation whenever the workspace is entered.

Useful? React with 👍 / 👎.

masarray added a commit that referenced this pull request Sep 9, 2026
Merge the validated P0/P1 Engineering shell: persistent IED Explorer, shared resizable Command Dock, observation scope refinements, panel geometry, loading feedback, and safer RCB export guidance. Native FAT tab work remains isolated in PR #290.
@masarray
masarray changed the base branch from ux/p0-persistent-workbench-shell to main September 9, 2026 10:18
@masarray masarray changed the title FAT P2: native continuous testing workspace FAT P2: embed production FAT workspace in Engineering tab Sep 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant