Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
9ebabf1
chore: stage SCL integration patch 1
masarray Jul 14, 2026
85ded8d
chore: stage SCL integration patch 2
masarray Jul 14, 2026
5f10973
chore: stage SCL integration patch 3
masarray Jul 14, 2026
a3ed9de
chore: stage SCL integration patch 4
masarray Jul 14, 2026
2a1f433
chore: stage SCL model patch 1
masarray Jul 14, 2026
29eca4d
chore: stage SCL model patch 2
masarray Jul 14, 2026
6d46058
chore: stage SCL signal mapper
masarray Jul 14, 2026
a995294
chore: stage live model projection
masarray Jul 14, 2026
df66b58
chore: stage integration build workflow
masarray Jul 14, 2026
f115411
chore: stage SCL integration documentation
masarray Jul 14, 2026
199df7b
chore: apply staged SCL integration
masarray Jul 14, 2026
cc01ab8
ci: apply SCL integration from pull request
masarray Jul 14, 2026
0db954e
ci: separate source apply from workflow updates
masarray Jul 14, 2026
47861c7
Integrate engine-owned SCL workspace [applied]
github-actions[bot] Jul 14, 2026
4aa89c8
ci: validate SCL workspace integration against engine branch
masarray Jul 14, 2026
b6357c1
ci: remove temporary integration apply workflow
masarray Jul 14, 2026
99590b3
ci: add focused SCL integration compile gate
masarray Jul 14, 2026
46bc699
fix: complete SCL live model projection source
masarray Jul 14, 2026
ea73781
ci: remove focused compile gate after successful validation
masarray Jul 14, 2026
1217451
chore: bump SCL workspace integration to 1.6.6
masarray Jul 14, 2026
093895a
chore: align portable publish default with 1.6.6
masarray Jul 14, 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
22 changes: 17 additions & 5 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ on:
pull_request:
workflow_dispatch:

env:
# Temporary integration pin. Switch this to main after ARIEC61850 PR #25 is merged.
ARIEC61850_REF: agent/scl-workspace-api

jobs:
build-windows:
runs-on: windows-latest
Expand Down Expand Up @@ -36,21 +40,29 @@ jobs:

- name: Checkout ARIEC61850 engine
shell: powershell
run: git clone --quiet --depth 1 https://github.com/masarray/ARIEC61850.git ARIEC61850
run: git clone --quiet --depth 1 --branch $env:ARIEC61850_REF https://github.com/masarray/ARIEC61850.git ARIEC61850

- name: Verify ARIEC61850 Smart Control API
- name: Verify required ARIEC61850 APIs
shell: powershell
run: |
$control = ".\ARIEC61850\src\AR.Iec61850\Control\Iec61850ControlService.cs"
$models = ".\ARIEC61850\src\AR.Iec61850\Control\Iec61850ControlModels.cs"
$workspace = ".\ARIEC61850\src\AR.Iec61850\Scl\Workspace\SclWorkspaceService.cs"
$workspaceModels = ".\ARIEC61850\src\AR.Iec61850\Scl\Workspace\SclWorkspaceModels.cs"
if (!(Test-Path $control) -or !(Test-Path $models)) {
throw "ARIEC61850 Smart Control source was not found. ArIED 1.6.5 requires the native Control namespace."
throw "ARIEC61850 Smart Control source was not found. ArIED requires the native Control namespace."
}
if (!(Select-String -Path $models -Pattern "interface IIec61850ControlService" -Quiet)) {
throw "ARIEC61850 does not expose IIec61850ControlService."
}
if (!(Select-String -Path $models -Pattern "CommandTerminationReceived" -Quiet)) {
throw "ARIEC61850 Smart Control result contract is too old for ArIED 1.6.5."
throw "ARIEC61850 Smart Control result contract is too old for ArIED."
}
if (!(Test-Path $workspace) -or !(Test-Path $workspaceModels)) {
throw "ARIEC61850 SCL Workspace API was not found. ArIED Open SCL must use the engine-owned workspace service."
}
if (!(Select-String -Path $workspace -Pattern "CompareLive" -Quiet)) {
throw "ARIEC61850 SCL Workspace API does not expose design-versus-live comparison."
}

- name: Setup .NET 8
Expand All @@ -66,7 +78,7 @@ jobs:

- name: Publish portable x64
shell: powershell
run: .\ArIED61850Tester\scripts\publish-windows-portable.ps1 -Version 1.6.5 -EngineProject "$env:GITHUB_WORKSPACE\ARIEC61850\src\AR.Iec61850\AR.Iec61850.csproj"
run: .\ArIED61850Tester\scripts\publish-windows-portable.ps1 -Version 1.6.6 -EngineProject "$env:GITHUB_WORKSPACE\ARIEC61850\src\AR.Iec61850\AR.Iec61850.csproj"

- name: Upload portable package
uses: actions/upload-artifact@v4
Expand Down
10 changes: 5 additions & 5 deletions ArIED61850Tester.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@
<Copyright>Copyright (C) 2026 Mas Ari / masarray</Copyright>
<Product>ArIED 61850</Product>
<AssemblyTitle>ArIED 61850</AssemblyTitle>
<Description>Smart IED Explorer &amp; Monitor for live IEC 61850 discovery, multi-IED monitoring, reporting, control-object inspection, quality, timestamps, and event logging.</Description>
<Version>1.6.5</Version>
<AssemblyVersion>1.6.5.0</AssemblyVersion>
<FileVersion>1.6.5.0</FileVersion>
<PackageTags>iec61850;iec-61850;mms;ied;relay-testing;substation-automation;digital-substation;report-control-block;wpf;dotnet</PackageTags>
<Description>Smart IED Explorer &amp; Monitor for SCL-based offline engineering, live IEC 61850 discovery, multi-IED monitoring, reporting, control-object inspection, quality, timestamps, and event logging.</Description>
<Version>1.6.6</Version>
<AssemblyVersion>1.6.6.0</AssemblyVersion>
<FileVersion>1.6.6.0</FileVersion>
<PackageTags>iec61850;iec-61850;mms;scl;ied;relay-testing;substation-automation;digital-substation;report-control-block;wpf;dotnet</PackageTags>
<PackageLicenseExpression>GPL-3.0-or-later</PackageLicenseExpression>
<ArIec61850Project Condition="'$(ArIec61850Project)' == '' and '$(ARIEC61850_PROJECT)' != ''">$(ARIEC61850_PROJECT)</ArIec61850Project>
<ArIec61850Project Condition="'$(ArIec61850Project)' == ''">..\ARIEC61850\src\AR.Iec61850\AR.Iec61850.csproj</ArIec61850Project>
Expand Down
335 changes: 262 additions & 73 deletions MainWindow.xaml.cs

Large diffs are not rendered by default.

119 changes: 105 additions & 14 deletions Models/MonitorModels.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using AR.Iec61850.Scl.Workspace;

namespace ArIED61850Tester.Models;

public sealed class Iec61850MonitorDevice : ObservableObject
Expand Down Expand Up @@ -28,13 +30,76 @@ public sealed class Iec61850MonitorDevice : ObservableObject
private bool _hasDiscoveryCache;
private string _busyTitle = "Discovering IEC 61850 IED";
private int _unreadEventCount;
private SclIedWorkspace? _sclWorkspace;
private SclLiveModelComparisonResult? _sclComparison;
private string _sclSourcePath = string.Empty;
private string _sclSourceSha256 = string.Empty;
private string _sclIedName = string.Empty;
private string _sclAccessPointName = string.Empty;

public string DeviceId { get; set; } = Guid.NewGuid().ToString("N");
public BulkObservableCollection<SignalDefinition> Signals { get; } = new();
public BulkObservableCollection<Iec61850MonitorPoint> Points { get; } = new();
public BulkObservableCollection<SignalDefinition> CommandSignals { get; } = new();
public Iec61850DeviceDiagnosticSnapshot LastDiagnosticSnapshot { get; set; } = new();

public SclIedWorkspace? SclWorkspace
{
get => _sclWorkspace;
set
{
if (ReferenceEquals(_sclWorkspace, value)) return;
_sclWorkspace = value;
RefreshComputed();
}
}

public SclLiveModelComparisonResult? SclComparison
{
get => _sclComparison;
set
{
if (ReferenceEquals(_sclComparison, value)) return;
_sclComparison = value;
RefreshComputed();
}
}

public string SclSourcePath
{
get => _sclSourcePath;
set => Set(ref _sclSourcePath, value?.Trim() ?? string.Empty);
}

public string SclSourceSha256
{
get => _sclSourceSha256;
set => Set(ref _sclSourceSha256, value?.Trim() ?? string.Empty);
}

public string SclIedName
{
get => _sclIedName;
set => Set(ref _sclIedName, value?.Trim() ?? string.Empty);
}

public string SclAccessPointName
{
get => _sclAccessPointName;
set => Set(ref _sclAccessPointName, value?.Trim() ?? string.Empty);
}

public bool HasSclDesignModel => SclWorkspace != null || !string.IsNullOrWhiteSpace(SclSourceSha256);
public bool RequiresEndpointBinding => HasSclDesignModel && string.IsNullOrWhiteSpace(IpAddress);
public bool HasSclConfigurationDrift => SclComparison?.RequiresFullDiscovery == true;
public string SclVerificationText => !HasSclDesignModel
? string.Empty
: SclComparison == null
? IsConnected ? "SCL associated • full model unverified" : "SCL offline model"
: SclComparison.IsCompatible
? "SCL verified against live model"
: $"SCL drift • {SclComparison.BlockingFindingCount} blocking finding(s)";

// Smart Auto reporting: existing static RCB/DataSet first, temporary association-
// scoped dynamic DataSet/URCB second, MMS polling only when reporting cannot be armed.
public bool AllowDynamicDataSetWrites { get; set; } = true;
Expand Down Expand Up @@ -320,28 +385,42 @@ public void MarkDiscoveryFailed(string message)
public bool CanPlayAction => !IsBusy && (!IsConnected || (!IsMonitoring && SelectedLiveSignalCount > 0));
public bool CanStopAction => !IsBusy && (IsConnected || IsMonitoring);
public bool CanRescan => !IsBusy && !IsMonitoring;
public string CacheStateText => HasDiscoveryCache
? $"Saved model ready • {SignalCount:N0} signals"
: "No saved model • discovery required";
public string ReadyWorkspaceTitle => HasDiscoveryCache ? "Saved IED model is ready" : "IED is ready";
public string ReadyWorkspaceMessage => HasDiscoveryCache && SelectedLiveSignalCount > 0
? $"{SelectedLiveSignalCount:N0} saved live signal(s) are selected. Use Play or Connect All for immediate live values without a full discovery scan."
public string CacheStateText => HasSclDesignModel
? $"SCL design model • {SignalCount:N0} signals"
: HasDiscoveryCache
? "The complete signal model is restored. Choose signals offline; Apply & Start Live connects and starts monitoring automatically."
: "Choose signals in the wizard; Apply & Start Live begins monitoring automatically.";
? $"Saved live model • {SignalCount:N0} signals"
: "No cached model • discovery required";
public string ReadyWorkspaceTitle => HasSclDesignModel
? RequiresEndpointBinding ? "SCL model ready — endpoint required" : "SCL design model is ready"
: HasDiscoveryCache ? "Saved IED model is ready" : "IED is ready";
public string ReadyWorkspaceMessage => HasSclDesignModel
? RequiresEndpointBinding
? "The LD/LN/DO/DA model is available offline. Press Play to bind an MMS endpoint before connecting."
: SelectedLiveSignalCount > 0
? $"{SelectedLiveSignalCount:N0} SCL signal(s) are selected. Play performs a fast MMS association; Re-scan compares the complete live model."
: "Browse and choose signals offline. Play associates without repeating full discovery; Re-scan performs design-versus-live verification."
: HasDiscoveryCache && SelectedLiveSignalCount > 0
? $"{SelectedLiveSignalCount:N0} saved live signal(s) are selected. Use Play or Connect All for immediate live values without a full discovery scan."
: HasDiscoveryCache
? "The complete signal model is restored. Choose signals offline; Apply & Start Live connects and starts monitoring automatically."
: "Choose signals in the wizard; Apply & Start Live begins monitoring automatically.";
public string ConnectionActionLabel => IsBusy ? "Working…" : IsConnected ? "Disconnect" : "Connect";
public string MonitorActionLabel => IsBusy ? "Working…" : IsMonitoring ? "Stop Monitor" : "Start Monitor";
public string ActivityText => IsBusy ? "Working…" : IsMonitoring ? "Monitoring" : Status;
public string SummaryText => $"{SignalCount} scanned • {SelectedSignalCount} selected • {PointCount} live";
public string IdentityText => string.IsNullOrWhiteSpace(LogicalDeviceSummary)
? EndpointText
: $"{EndpointText} • LD {LogicalDeviceSummary}";
: HasSclDesignModel
? $"{EndpointText} • {LogicalDeviceSummary}"
: $"{EndpointText} • LD {LogicalDeviceSummary}";
public string ConnectionGlyph => IsBusy ? "…" : IsConnected ? "⏻" : "↗";
public string ConnectionToolTip => IsBusy
? "IED connection operation is running"
: IsConnected
? $"Disconnect {Name}"
: $"Connect and discover {EndpointText}";
: HasSclDesignModel
? $"Connect {Name} using the SCL design model"
: $"Connect and discover {EndpointText}";
public string MonitorGlyph => IsBusy ? "…" : IsMonitoring ? "■" : "▶";
public string MonitorToolTip => IsBusy
? "IED session operation is running"
Expand All @@ -354,9 +433,13 @@ public void MarkDiscoveryFailed(string message)
? $"Stop monitoring {Name} before changing its signal selection"
: $"Open signal selection wizard for {Name}";
public string PlayToolTip => !IsConnected
? HasDiscoveryCache
? $"Fast-connect {Name} from the saved model and start its selected live values"
: $"Connect and discover {EndpointText}"
? RequiresEndpointBinding
? $"Bind an MMS endpoint for {Name}, then fast-connect from the SCL design model"
: HasSclDesignModel
? $"Fast-connect {Name} from the SCL design model; use Re-scan for full live comparison"
: HasDiscoveryCache
? $"Fast-connect {Name} from the saved model and start its selected live values"
: $"Connect and discover {EndpointText}"
: IsMonitoring
? $"{Name} is already monitoring"
: SelectedLiveSignalCount == 0
Expand Down Expand Up @@ -436,6 +519,10 @@ public void RefreshComputed()
Raise(nameof(CanStopAction));
Raise(nameof(CanRescan));
Raise(nameof(CacheStateText));
Raise(nameof(HasSclDesignModel));
Raise(nameof(RequiresEndpointBinding));
Raise(nameof(HasSclConfigurationDrift));
Raise(nameof(SclVerificationText));
Raise(nameof(ReadyWorkspaceTitle));
Raise(nameof(ReadyWorkspaceMessage));
Raise(nameof(ConnectionActionLabel));
Expand Down Expand Up @@ -681,7 +768,7 @@ private static bool TryParseBinaryState(string? value, out bool state)

public sealed class Iec61850TesterProject
{
public int SchemaVersion { get; set; } = 2;
public int SchemaVersion { get; set; } = 3;
public string ProjectName { get; set; } = "ArIED 61850 Session";
public int DefaultPollingIntervalMs { get; set; } = 1000;
public List<Iec61850TesterDeviceProfile> Devices { get; set; } = new();
Expand All @@ -697,6 +784,10 @@ public sealed class Iec61850TesterDeviceProfile
public int Port { get; set; } = 102;
public bool AllowDynamicDataSetWrites { get; set; } = true;
public bool DiscoverySucceeded { get; set; }
public string SclSourcePath { get; set; } = string.Empty;
public string SclSourceSha256 { get; set; } = string.Empty;
public string SclIedName { get; set; } = string.Empty;
public string SclAccessPointName { get; set; } = string.Empty;
public List<string> SelectedReferences { get; set; } = new();
public List<Iec61850CachedSignalProfile> CachedSignals { get; set; } = new();
}
Expand Down
Loading
Loading