feat(alarms): exact OPC UA severity + per-condition quality on native alarm events (#626)
AlarmEventArgs gains optional OpcUaSeverity (supersedes the four-bucket mapping, clamped 1..1000) and QualityStatusCode (projected onto the condition's Quality). An Unspecified-kind event carrying a quality is a quality-only annotation: the host updates the condition but publishes no alerts row. Both default null, so every existing native source is unchanged. Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
@@ -166,6 +166,49 @@ public class NativeAlarmProjectorTests
|
||||
snap.StatusCode.ShouldBe(OpcUaStatus.Good);
|
||||
}
|
||||
|
||||
// Gitea #626 — a source with a finer severity scale than the four buckets (MTConnect Fault = 800) sets
|
||||
// OpcUaSeverity, which supersedes the bucket on the projected condition. Clamped to the Part 9 1..1000.
|
||||
[Theory]
|
||||
[InlineData(800, 800)]
|
||||
[InlineData(500, 500)]
|
||||
[InlineData(0, 1)]
|
||||
[InlineData(5000, 1000)]
|
||||
public void Explicit_opcua_severity_supersedes_the_bucket(int requested, int projected)
|
||||
{
|
||||
var sut = new NativeAlarmProjector();
|
||||
|
||||
var snap = sut.Project("n1", Evt(AlarmTransitionKind.Raise, AlarmSeverity.High) with { OpcUaSeverity = requested });
|
||||
|
||||
snap.Severity.ShouldBe((ushort)projected);
|
||||
}
|
||||
|
||||
// Gitea #626 — a per-condition source quality (MTConnect Unavailable) rides the snapshot's StatusCode, the
|
||||
// same Quality child #477's connectivity annotation writes.
|
||||
[Fact]
|
||||
public void Supplied_quality_is_projected_onto_the_snapshot()
|
||||
{
|
||||
var sut = new NativeAlarmProjector();
|
||||
|
||||
var snap = sut.Project("n1", Evt(AlarmTransitionKind.Clear) with { QualityStatusCode = OpcUaStatus.BadNoCommunication });
|
||||
|
||||
snap.StatusCode.ShouldBe(OpcUaStatus.BadNoCommunication);
|
||||
}
|
||||
|
||||
// Gitea #626 — a quality-only annotation (Unspecified kind + quality) must not move Active/Acked: an
|
||||
// active, unacknowledged condition stays exactly that while only its Quality changes.
|
||||
[Fact]
|
||||
public void Quality_only_annotation_keeps_the_prior_alarm_state()
|
||||
{
|
||||
var sut = new NativeAlarmProjector();
|
||||
sut.Project("n1", Evt(AlarmTransitionKind.Raise));
|
||||
|
||||
var snap = sut.Project("n1", Evt(AlarmTransitionKind.Unspecified) with { QualityStatusCode = OpcUaStatus.BadNoCommunication });
|
||||
|
||||
snap.Active.ShouldBeTrue();
|
||||
snap.Acknowledged.ShouldBeFalse();
|
||||
snap.StatusCode.ShouldBe(OpcUaStatus.BadNoCommunication);
|
||||
}
|
||||
|
||||
private static AlarmEventArgs Evt(
|
||||
AlarmTransitionKind kind,
|
||||
AlarmSeverity sev = AlarmSeverity.High,
|
||||
|
||||
Reference in New Issue
Block a user