diff --git a/engines/ARIEC61850.lock.json b/engines/ARIEC61850.lock.json index 51f5571ba..e0c3fa6fc 100644 --- a/engines/ARIEC61850.lock.json +++ b/engines/ARIEC61850.lock.json @@ -2,7 +2,7 @@ "schemaVersion": 1, "repository": "masarray/ARIEC61850", "ref": "main", - "commit": "becda399b4a3ae34831215fc915798b4f846c1be", - "sourcePullRequest": 81, - "purpose": "Pins the ARIEC61850 engine used by ARSAS. PR #76 preserves unresolved static DataSet members, PR #77 canonicalizes cross-logical-device SCL references, PR #78 keeps one descriptor per static DataSet member while separating resolved runtime primary leaves from the original FCDA/FCD identity, PR #79 projects generic Boolean status structures to scalar stVal while preserving quality/timestamp, PR #80 normalizes validated DataRef-enabled InformationReport ordering, and PR #81 accepts valid zero OptFlds reports while quarantining unmapped canonical report metadata so OptFlds/inclusion/reason fields can never leak into process values." + "commit": "9ca4ee490a27046d5d2ffdc21f3614b2d7d58725", + "sourcePullRequest": 84, + "purpose": "Pins the ARIEC61850 engine used by ARSAS. PR #76 preserves unresolved static DataSet members, PR #77 canonicalizes cross-logical-device SCL references, PR #78 keeps one descriptor per static DataSet member while separating resolved runtime primary leaves from the original FCDA/FCD identity, PR #79 projects generic Boolean status structures to scalar stVal while preserving quality/timestamp, PR #80 normalizes validated DataRef-enabled InformationReport ordering, PR #81 accepts valid zero OptFlds reports while quarantining unmapped canonical report metadata so OptFlds/inclusion/reason fields can never leak into process values, and PR #84 routes exact PrimaryValue residuals through dynamic reporting before MMS polling while preventing quality/timestamp descriptors from hijacking the primary-value fallback." } diff --git a/tests/ARSAS.Tests/Iec61850ReportPlannerRegressionTests.cs b/tests/ARSAS.Tests/Iec61850ReportPlannerRegressionTests.cs index f66b44bc3..65cb08e4f 100644 --- a/tests/ARSAS.Tests/Iec61850ReportPlannerRegressionTests.cs +++ b/tests/ARSAS.Tests/Iec61850ReportPlannerRegressionTests.cs @@ -35,6 +35,36 @@ public void BuildPlans_FastPointWithoutStaticCoverage_BecomesDynamicCandidate() Assert.Contains(point, plan.Bindings); } + [Fact] + public void BuildPlans_SlowMeasurementWithoutStaticCoverage_BecomesDynamicCandidate() + { + var device = BuildDevice(); + var point = BuildSlowMeasurementPoint(device); + + var plans = Iec61850ReportPlanner.BuildPlans(device, new[] { point }); + + var plan = Assert.Single(plans); + Assert.Equal("Dynamic candidate", plan.Status); + Assert.True(plan.AllowDynamicDataSetWrites); + Assert.Contains(point, plan.Bindings); + } + + [Fact] + public void BuildPlans_AllUncoveredPoints_AppearExactlyOnceInDynamicCandidates() + { + var device = BuildDevice(); + var fast = BuildFastStatusPoint(device); + var slow = BuildSlowMeasurementPoint(device); + + var plans = Iec61850ReportPlanner.BuildPlans(device, new[] { fast, slow }); + + Assert.All(plans, plan => Assert.Equal("Dynamic candidate", plan.Status)); + var bindings = plans.SelectMany(plan => plan.Bindings).ToArray(); + Assert.Equal(2, bindings.Length); + Assert.Single(bindings, point => point.PointKey == fast.PointKey); + Assert.Single(bindings, point => point.PointKey == slow.PointKey); + } + [Fact] public void BuildDynamicFallbackPlans_FastPoint_IsNotExcludedByPollingInterval() { @@ -70,4 +100,18 @@ private static Iec61850MonitorPoint BuildFastStatusPoint(Iec61850MonitorDevice d Category = "Position", PollingIntervalMs = 250 }; + + private static Iec61850MonitorPoint BuildSlowMeasurementPoint(Iec61850MonitorDevice device) + => new() + { + DeviceId = device.DeviceId, + DeviceName = device.Name, + IpAddress = device.IpAddress, + SignalName = "Phase A current", + IecReference = "IED1LD0/MMXU1.A.phsA.cVal.mag.f", + FunctionalConstraint = "MX", + IecDataType = "Float32", + Category = "Measurement", + PollingIntervalMs = 2000 + }; } diff --git a/tests/ARSAS.Tests/OfflineDataSetSignalSelectionRegressionTests.cs b/tests/ARSAS.Tests/OfflineDataSetSignalSelectionRegressionTests.cs index 5cb5c3e37..97dc1b38a 100644 --- a/tests/ARSAS.Tests/OfflineDataSetSignalSelectionRegressionTests.cs +++ b/tests/ARSAS.Tests/OfflineDataSetSignalSelectionRegressionTests.cs @@ -72,13 +72,14 @@ public void EngineLock_PinsMergedReportProjectionEngineWithoutLosingMemberCentri { var source = File.ReadAllText(FindRepoFile("engines/ARIEC61850.lock.json")); - Assert.Contains("becda399b4a3ae34831215fc915798b4f846c1be", source, StringComparison.OrdinalIgnoreCase); - Assert.Contains("\"sourcePullRequest\": 81", source, StringComparison.Ordinal); + Assert.Contains("9ca4ee490a27046d5d2ffdc21f3614b2d7d58725", source, StringComparison.OrdinalIgnoreCase); + Assert.Contains("\"sourcePullRequest\": 84", source, StringComparison.Ordinal); Assert.Contains("one descriptor per static DataSet member", source, StringComparison.OrdinalIgnoreCase); Assert.Contains("generic Boolean status structures", source, StringComparison.OrdinalIgnoreCase); Assert.Contains("DataRef-enabled InformationReport ordering", source, StringComparison.OrdinalIgnoreCase); Assert.Contains("zero OptFlds", source, StringComparison.OrdinalIgnoreCase); Assert.Contains("quarantining unmapped canonical report metadata", source, StringComparison.OrdinalIgnoreCase); + Assert.Contains("dynamic reporting before MMS polling", source, StringComparison.OrdinalIgnoreCase); } [Fact] diff --git a/tests/ARSAS.Tests/VisualSystemP0RegressionTests.cs b/tests/ARSAS.Tests/VisualSystemP0RegressionTests.cs index 8b8f10c96..8a1e76d97 100644 --- a/tests/ARSAS.Tests/VisualSystemP0RegressionTests.cs +++ b/tests/ARSAS.Tests/VisualSystemP0RegressionTests.cs @@ -86,8 +86,8 @@ public void P0_KeepsTheReviewedAriecEnginePinUntouched() var testDirectory = Path.GetDirectoryName(FindRepoFile("MainWindow.xaml"))!; var engineLock = File.ReadAllText(Path.Combine(testDirectory, "engines", "ARIEC61850.lock.json")); - Assert.Contains("becda399b4a3ae34831215fc915798b4f846c1be", engineLock, StringComparison.OrdinalIgnoreCase); - Assert.Contains("\"sourcePullRequest\": 81", engineLock, StringComparison.Ordinal); + Assert.Contains("9ca4ee490a27046d5d2ffdc21f3614b2d7d58725", engineLock, StringComparison.OrdinalIgnoreCase); + Assert.Contains("\"sourcePullRequest\": 84", engineLock, StringComparison.Ordinal); } private static string Slice(string source, string start, string end) diff --git a/tests/ARSAS.Tests/WorkspaceNoiseP1RegressionTests.cs b/tests/ARSAS.Tests/WorkspaceNoiseP1RegressionTests.cs index 9c4e4a366..30bc9842c 100644 --- a/tests/ARSAS.Tests/WorkspaceNoiseP1RegressionTests.cs +++ b/tests/ARSAS.Tests/WorkspaceNoiseP1RegressionTests.cs @@ -87,8 +87,8 @@ public void P1_KeepsReviewedAriecEnginePinUntouched() var root = Path.GetDirectoryName(FindRepoFile("MainWindow.xaml"))!; var engineLock = File.ReadAllText(Path.Combine(root, "engines", "ARIEC61850.lock.json")); - Assert.Contains("becda399b4a3ae34831215fc915798b4f846c1be", engineLock, StringComparison.OrdinalIgnoreCase); - Assert.Contains("\"sourcePullRequest\": 81", engineLock, StringComparison.Ordinal); + Assert.Contains("9ca4ee490a27046d5d2ffdc21f3614b2d7d58725", engineLock, StringComparison.OrdinalIgnoreCase); + Assert.Contains("\"sourcePullRequest\": 84", engineLock, StringComparison.Ordinal); } private static string Slice(string source, string start, string end)