Skip to content
Closed
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
4 changes: 2 additions & 2 deletions src/AR.Iec61850/Discovery/Iec61850StandardModelRegistry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,13 @@ public static class Iec61850StandardModelRegistry
[Key("MMXU", "PF")] = Def("MMXU", "PF", "WYE", 0.84, "power factor"),

// Harmonic measurements use phase-group CDCs. Physical AA1E1F06R4
// plus IEDScout SCL prove that MHAI ThdA / ThdPhV are WYE objects whose
// plus independent engineering SCL evidence proves that MHAI ThdA / ThdPhV are WYE objects whose
// phase children are CMV SDOs, not ACD indication structures.
[Key("MHAI", "ThdA")] = Def("MHAI", "ThdA", "WYE", 0.98, "three-phase current THD"),
[Key("MHAI", "ThdPhV")] = Def("MHAI", "ThdPhV", "WYE", 0.98, "three-phase voltage THD"),

// Instrument-transformer semantics are standardized and were also observed in
// the AA1E1F06R4 IEDScout engineering model. Exact registry entries prevent
// the AA1E1F06R4 reference engineering model. Exact registry entries prevent
// live discovery from dropping these configuration DOs when attribute-name
// heuristics alone cannot infer their CDC safely.
[Key("TCTR", "Mod")] = Def("TCTR", "Mod", "ENC", 0.98, "mode control"),
Expand Down
32 changes: 21 additions & 11 deletions src/AR.Iec61850/Mms/MmsClientSession.SclStaticReport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,25 @@ public static IReadOnlyList<SclStaticReportActivationStepKind> BuildBrcbEnableFa
public sealed partial class MmsClientSession
{
/// <summary>
/// Starts a persistent monitor for a static DataSet already trusted from SCL.
/// Backward-compatible SCL entry point. Static report activation is source-neutral once
/// the caller has supplied an authoritative ordered DataSet and configured RCB.
/// </summary>
public Task<MmsPersistentReportMonitorStartResult> StartStaticSclReportMonitorAsync(
MmsReportSubscriptionPlan plan,
bool triggerGeneralInterrogation = false,
CancellationToken cancellationToken = default)
=> StartConfiguredStaticReportMonitorAsync(plan, triggerGeneralInterrogation, cancellationToken);

/// <summary>
/// Starts a persistent monitor for an authoritative configured static DataSet.
/// The primary wire sequence is deliberately minimal: whole-RCB Read, optional
/// URCB Resv=true, RptEna=true, then two whole-RCB verification Reads. BRCB
/// ResvTms is a retry-only compatibility fallback when direct RptEna is rejected.
/// No DataSet browsing/creation and no GI are performed unless explicitly requested.
/// When explicit GI is requested, GI acceptance is part of startup success so a
/// caller cannot report an active initial-image monitor when the GI write was rejected.
/// No DataSet browsing/creation is performed here. When explicit GI is requested,
/// GI acceptance is part of startup success so a caller cannot report an active
/// initial-image monitor when the GI write was rejected.
/// </summary>
public async Task<MmsPersistentReportMonitorStartResult> StartStaticSclReportMonitorAsync(
public async Task<MmsPersistentReportMonitorStartResult> StartConfiguredStaticReportMonitorAsync(
MmsReportSubscriptionPlan plan,
bool triggerGeneralInterrogation = false,
CancellationToken cancellationToken = default)
Expand All @@ -66,7 +76,7 @@ public async Task<MmsPersistentReportMonitorStartResult> StartStaticSclReportMon
return new MmsPersistentReportMonitorStartResult
{
IsSuccess = false,
Message = "Trusted-SCL static report activation requires a ready plan with selected RCB."
Message = "Configured static report activation requires a ready plan with selected RCB."
};
}

Expand All @@ -75,7 +85,7 @@ public async Task<MmsPersistentReportMonitorStartResult> StartStaticSclReportMon
return new MmsPersistentReportMonitorStartResult
{
IsSuccess = false,
Message = "Trusted-SCL static report activation never creates or mutates a dynamic DataSet."
Message = "Configured static report activation never creates or mutates a dynamic DataSet."
};
}

Expand Down Expand Up @@ -179,7 +189,7 @@ public async Task<MmsPersistentReportMonitorStartResult> StartStaticSclReportMon
WriteSteps = writes,
Warnings = warnings,
RcbSnapshots = snapshots,
Message = "RptEna=true was not accepted; trusted-SCL static report monitor was not started."
Message = "RptEna=true was not accepted; configured static report monitor was not started."
};
}

Expand Down Expand Up @@ -252,8 +262,8 @@ public async Task<MmsPersistentReportMonitorStartResult> StartStaticSclReportMon
Warnings = warnings,
RcbSnapshots = snapshots,
Message = triggerGeneralInterrogation
? $"Trusted-SCL static report monitor armed for {rcb.Reference}; explicit one-shot GI=true was accepted after activation."
: $"Trusted-SCL static report monitor armed for {rcb.Reference}; no DataSet discovery/mutation or GI was performed."
? $"Configured static report monitor armed for {rcb.Reference}; explicit one-shot GI=true was accepted after activation."
: $"Configured static report monitor armed for {rcb.Reference}; no DataSet discovery/mutation or GI was performed."
};
}
catch (Exception ex) when (ex is IOException or InvalidDataException or ObjectDisposedException or InvalidOperationException)
Expand Down Expand Up @@ -291,7 +301,7 @@ public async Task<MmsPersistentReportMonitorStartResult> StartStaticSclReportMon
WriteSteps = writes,
Warnings = warnings,
RcbSnapshots = snapshots,
Message = $"Trusted-SCL static report activation failed: {ex.GetType().Name}: {ex.Message}"
Message = $"Configured static report activation failed: {ex.GetType().Name}: {ex.Message}"
};
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,17 @@ public void Brcb_Explicit_Reservation_Is_Retry_Only()
fallback);
}

[Fact]
public void ConfiguredStaticActivation_Is_SourceNeutral_PublicEntryPoint()
{
var configured = typeof(MmsClientSession).GetMethod(nameof(MmsClientSession.StartConfiguredStaticReportMonitorAsync));
var scl = typeof(MmsClientSession).GetMethod(nameof(MmsClientSession.StartStaticSclReportMonitorAsync));

Assert.NotNull(configured);
Assert.NotNull(scl);
Assert.Equal(configured!.ReturnType, scl!.ReturnType);
}

[Fact]
public void Urcb_Has_No_Brcb_Reservation_Fallback()
{
Expand Down