Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
23 changes: 20 additions & 3 deletions engines/ARIEC61850.lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
"schemaVersion": 1,
"repository": "masarray/ARIEC61850",
"ref": "main",
"commit": "51c2eefd61d6bcbe1116fda6b781b74bb9cf06da",
"sourcePullRequest": 173,
"purpose": "M2.17-K isolated C264 acceptance trial: ARIEC typed ST enumerated Mod/Beh/Health/PhyHealth Report FCD projection with exact ST/quality/timestamp checks, preserving P10.9V control and fail-closed unknowns. ARIEC #173 Windows engine CI passed; integrated consumer CI/physical C264 replay pending. Do not promote or merge into main without acceptance.",
"commit": "1ed776a689c8707be5db09a6318c4a7613229f95",
"sourcePullRequest": 175,
"purpose": "P0-A/B isolated global interoperability identity + exact-target adaptive RCB planner trial. Engine PR #175 Windows CI 39216835641 passed 1145/1145 at immutable SHA. Full ARSAS integrated Windows CI and physical Siemens/BCUGE verification pending. Keep draft; do not merge or promote without end-to-end acceptance.",
"previousTrialPin": {
"commit": "4467124775d8d9d76f3db194f9fbfd97144767a8",
"sourcePullRequest": 134,
Expand Down Expand Up @@ -270,5 +270,22 @@
"physicalQualificationRequired": true,
"mainlinePromotionForbidden": true,
"cyclicMmsProcessPollingForbidden": true
},
"p0GlobalInteroperabilityTrial": {
"status": "engine-windows-ci-passed-not-physical",
"sourcePullRequest": 175,
"consumerPullRequest": 622,
"baseEngineCommit": "51c2eefd61d6bcbe1116fda6b781b74bb9cf06da",
"exactCommit": "1ed776a689c8707be5db09a6318c4a7613229f95",
"engineCiRun": 39216835641,
"verifiedEngineTests": 1145,
"identityEvidence": "synthetic-five-ld",
"rcbEvidence": "synthetic-66-300-rcb-target-scoped-resumable",
"physicalQualificationRequired": true,
"mainlinePromotionForbidden": true,
"vendorBranchingForbidden": true,
"readOnlyDiscoveryRequired": true,
"rcbOwnershipProtectionRequired": true,
"cyclicMmsProcessPollingForbidden": true
}
}
33 changes: 33 additions & 0 deletions scripts/test-p0-global-interoperability-trial.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Private physical acceptance remains independent of a Windows-green engine code trial.
# This checker is intentionally a strict allowlist for one immutable P0 trial SHA.
function Test-P0GlobalInteroperabilityTrial {
[CmdletBinding()]
param([Parameter(Mandatory = $true)] $Lock)

$trial = $Lock.p0GlobalInteroperabilityTrial
$ancestor = $Lock.m217KTypedStatusReportTrial
if ($null -eq $trial -or $null -eq $ancestor) {
return $false
}

return (
$trial.status -eq 'engine-windows-ci-passed-not-physical' -and
[int]$trial.sourcePullRequest -eq 175 -and
[int]$trial.consumerPullRequest -eq 622 -and
$trial.exactCommit -ceq '1ed776a689c8707be5db09a6318c4a7613229f95' -and
$trial.baseEngineCommit -ceq $ancestor.exactCommit -and
$ancestor.exactCommit -ceq '51c2eefd61d6bcbe1116fda6b781b74bb9cf06da' -and
[int64]$trial.engineCiRun -eq 39216835641 -and
[int]$trial.verifiedEngineTests -eq 1145 -and
$trial.identityEvidence -eq 'synthetic-five-ld' -and
$trial.rcbEvidence -eq 'synthetic-66-300-rcb-target-scoped-resumable' -and
[bool]$trial.physicalQualificationRequired -and
[bool]$trial.mainlinePromotionForbidden -and
[bool]$trial.vendorBranchingForbidden -and
[bool]$trial.readOnlyDiscoveryRequired -and
[bool]$trial.rcbOwnershipProtectionRequired -and
[bool]$trial.cyclicMmsProcessPollingForbidden -and
$Lock.commit -ceq $trial.exactCommit -and
[int]$Lock.sourcePullRequest -eq [int]$trial.sourcePullRequest
)
}
80 changes: 80 additions & 0 deletions tests/ARSAS.Tests/P0GlobalIdentityConsumerRegressionTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
using AR.Iec61850.Discovery;
using ArIED61850Tester.Models;
using ArIED61850Tester.Services;
using System.Security.Cryptography;

namespace ARSAS.Tests;

public sealed class P0GlobalIdentityConsumerRegressionTests
{
private static readonly string[] Domains =
[
"SIEBCUCTRL", "SIEBCUMEAS", "SIEBCUPROT", "SIEBCUUD1", "SIEBCUUD2"
];

[Fact]
public void UnverifiedLiveDomains_PreserveIdentityCandidateWithoutWireConfidence()
{
var inferred = LiveIedIdentityResolver.Resolve(Domains, "192.0.2.20");

Assert.Equal("SIEBCU", inferred.IedName);
Assert.Equal(LiveIedDiscoveryConfidenceLevel.Medium, inferred.Confidence);
Assert.Equal("CTRL", inferred.LogicalDeviceAliases["SIEBCUCTRL"]);
Assert.Equal(5, inferred.LogicalDeviceAliases.Count);
}

[Fact]
public async Task TrustedPinnedScl_ProjectsExactIdentityThroughConsumerWithoutChangingRawDomains()
{
var folder = Path.Combine(Path.GetTempPath(), "arsas-p0-identity-" + Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(folder);
var path = Path.Combine(folder, "synthetic.iid");
try
{
var ldXml = string.Join("", new[] { "CTRL", "MEAS", "PROT", "UD1", "UD2" }
.Select(inst => $"<LDevice inst=\"{inst}\"/>"));
var scl = $"""
<SCL xmlns="http://www.iec.ch/61850/2003/SCL">
<Communication><SubNetwork name="Test"><ConnectedAP iedName="SIEBCU" apName="P1"><Address><P type="IP">192.0.2.20</P></Address></ConnectedAP></SubNetwork></Communication>
<IED name="SIEBCU"><AccessPoint name="P1"><Server>{ldXml}</Server></AccessPoint></IED>
</SCL>
""";
await File.WriteAllTextAsync(path, scl);
var device = new Iec61850MonitorDevice
{
IpAddress = "192.0.2.20",
SclSourcePath = path,
SclSourceSha256 = Convert.ToHexString(SHA256.HashData(await File.ReadAllBytesAsync(path))),
SclIedName = "SIEBCU",
SclAccessPointName = "P1"
};
var live = new LiveIedModelDiscoveryDocument
{
Host = "192.0.2.20",
IedName = "192.0.2.20",
LogicalDevices = Domains.Select(domain => new LiveIedLogicalDeviceModel
{
MmsDomain = domain,
Inst = domain
}).ToArray()
};

var verified = await TrustedSclIdentityAuthority.TryMatchAsync(device, live);
Assert.NotNull(verified);
Assert.Equal("SIEBCU", verified.IedName);
Assert.Equal(LiveIedDiscoveryConfidenceLevel.High, verified.Confidence);
var projection = TrustedSclIdentityAuthority.ToConsumerIdentity(verified, Domains);
Assert.Equal("SIEBCU", projection.IedName);
Assert.Equal(5, projection.LogicalDevices.Count);
Assert.Contains(projection.LogicalDevices,
ld => ld.Domain == "SIEBCUPROT" && ld.Instance == "PROT");

device.IpAddress = "192.0.2.21";
Assert.Null(await TrustedSclIdentityAuthority.TryMatchAsync(device, live));
}
finally
{
Directory.Delete(folder, recursive: true);
}
}
}
Loading