feat(scripted-alarms): richer AlarmConditionState bridge to the OPC UA node (T15)
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
namespace ZB.MOM.WW.OtOpcUa.Commons.OpcUa;
|
||||
|
||||
/// <summary>
|
||||
/// Commons-level projection of an alarm's full OPC UA Part 9 condition state, carried from the
|
||||
/// Runtime engine to the SDK sink. Commons cannot reference <c>Core.ScriptedAlarms</c> (its
|
||||
/// domain enums live there), so this DTO is deliberately primitive: every field is a
|
||||
/// <see cref="bool"/>, the Commons-local <see cref="AlarmShelvingKind"/> enum, a
|
||||
/// <see cref="ushort"/> severity, or a <see cref="string"/>. The Runtime host maps its Core
|
||||
/// <c>AlarmConditionState</c> + <c>AlarmSeverity</c> down to this shape; the SDK
|
||||
/// <c>OtOpcUaNodeManager</c> projects it back up onto a real <c>AlarmConditionState</c> node.
|
||||
/// </summary>
|
||||
/// <param name="Active">Whether the alarm condition is currently active (ActiveState).</param>
|
||||
/// <param name="Acknowledged">Whether the active transition has been acknowledged (AckedState).</param>
|
||||
/// <param name="Confirmed">Whether the clear transition has been confirmed (ConfirmedState).</param>
|
||||
/// <param name="Enabled">Whether the alarm is enabled (EnabledState); a disabled alarm reports no events.</param>
|
||||
/// <param name="Shelving">The shelving mode (ShelvingState): unshelved, one-shot, or timed.</param>
|
||||
/// <param name="Severity">OPC UA severity on the 1..1000 scale (the SDK <c>SetSeverity</c> input).</param>
|
||||
/// <param name="Message">The human-readable condition message (LocalizedText payload).</param>
|
||||
public sealed record AlarmConditionSnapshot(
|
||||
bool Active,
|
||||
bool Acknowledged,
|
||||
bool Confirmed,
|
||||
bool Enabled,
|
||||
AlarmShelvingKind Shelving,
|
||||
ushort Severity,
|
||||
string Message);
|
||||
|
||||
/// <summary>
|
||||
/// Commons-local mirror of the Core <c>ShelvingKind</c> enum so this assembly carries no
|
||||
/// dependency on <c>Core.ScriptedAlarms</c>. <see cref="Unshelved"/> = no suppression,
|
||||
/// <see cref="OneShot"/> = suppress the next active transition, <see cref="Timed"/> = suppress
|
||||
/// until a configured expiry. The Runtime host maps the Core enum onto these members; the SDK
|
||||
/// sink maps them onto the Part 9 <c>SetShelvingState(shelved, oneShot, shelvingTime)</c> flags.
|
||||
/// </summary>
|
||||
public enum AlarmShelvingKind
|
||||
{
|
||||
/// <summary>No shelving — the alarm behaves normally.</summary>
|
||||
Unshelved,
|
||||
|
||||
/// <summary>One-shot shelve — suppresses the next active transition, then expires.</summary>
|
||||
OneShot,
|
||||
|
||||
/// <summary>Timed shelve — suppresses until a configured expiry timestamp passes.</summary>
|
||||
Timed,
|
||||
}
|
||||
@@ -30,13 +30,12 @@ public sealed class DeferredAddressSpaceSink : IOpcUaAddressSpaceSink
|
||||
public void WriteValue(string nodeId, object? value, OpcUaQuality quality, DateTime sourceTimestampUtc)
|
||||
=> _inner.WriteValue(nodeId, value, quality, sourceTimestampUtc);
|
||||
|
||||
/// <summary>Writes an alarm state through the inner sink.</summary>
|
||||
/// <summary>Writes a full alarm-condition state through the inner sink.</summary>
|
||||
/// <param name="alarmNodeId">The node ID of the alarm condition.</param>
|
||||
/// <param name="active">Whether the alarm is active.</param>
|
||||
/// <param name="acknowledged">Whether the alarm has been acknowledged.</param>
|
||||
/// <param name="state">The full condition state to project onto the node.</param>
|
||||
/// <param name="sourceTimestampUtc">The source timestamp in UTC.</param>
|
||||
public void WriteAlarmState(string alarmNodeId, bool active, bool acknowledged, DateTime sourceTimestampUtc)
|
||||
=> _inner.WriteAlarmState(alarmNodeId, active, acknowledged, sourceTimestampUtc);
|
||||
public void WriteAlarmCondition(string alarmNodeId, AlarmConditionSnapshot state, DateTime sourceTimestampUtc)
|
||||
=> _inner.WriteAlarmCondition(alarmNodeId, state, sourceTimestampUtc);
|
||||
|
||||
/// <summary>Materialises a real Part 9 alarm-condition node through the inner sink.</summary>
|
||||
/// <param name="alarmNodeId">The alarm node ID (== ScriptedAlarmId).</param>
|
||||
|
||||
@@ -15,20 +15,21 @@ public interface IOpcUaAddressSpaceSink
|
||||
/// <param name="sourceTimestampUtc">The source timestamp in UTC.</param>
|
||||
void WriteValue(string nodeId, object? value, OpcUaQuality quality, DateTime sourceTimestampUtc);
|
||||
|
||||
/// <summary>Write an alarm-condition's active/acknowledged state. When a real Part 9 condition
|
||||
/// node has been materialised for <paramref name="alarmNodeId"/> via
|
||||
/// <see cref="MaterialiseAlarmCondition"/>, this projects onto its ActiveState/AckedState/Retain;
|
||||
/// otherwise it falls back to the legacy two-element placeholder variable.</summary>
|
||||
/// <summary>Write an alarm-condition's full Part 9 state. When a real condition node has been
|
||||
/// materialised for <paramref name="alarmNodeId"/> via <see cref="MaterialiseAlarmCondition"/>,
|
||||
/// this projects the whole <see cref="AlarmConditionSnapshot"/>
|
||||
/// (Enabled/Active/Acked/Confirmed/Shelving/Severity/Message) onto it and recomputes Retain;
|
||||
/// otherwise it falls back to the legacy two-element <c>[Active, Acknowledged]</c> placeholder
|
||||
/// variable. No OPC UA event is fired — that is T16's responsibility.</summary>
|
||||
/// <param name="alarmNodeId">The OPC UA node ID of the alarm (== ScriptedAlarmId for materialised conditions).</param>
|
||||
/// <param name="active">Whether the alarm is active.</param>
|
||||
/// <param name="acknowledged">Whether the alarm has been acknowledged.</param>
|
||||
/// <param name="state">The full condition state to project onto the node.</param>
|
||||
/// <param name="sourceTimestampUtc">The source timestamp in UTC.</param>
|
||||
void WriteAlarmState(string alarmNodeId, bool active, bool acknowledged, DateTime sourceTimestampUtc);
|
||||
void WriteAlarmCondition(string alarmNodeId, AlarmConditionSnapshot state, DateTime sourceTimestampUtc);
|
||||
|
||||
/// <summary>
|
||||
/// Materialise a real OPC UA Part 9 alarm-condition node under its equipment folder so clients
|
||||
/// can browse it as a proper condition (with basic Active/Ack state). The node id equals the
|
||||
/// alarm node id (the ScriptedAlarmId) so subsequent <see cref="WriteAlarmState"/> calls update
|
||||
/// alarm node id (the ScriptedAlarmId) so subsequent <see cref="WriteAlarmCondition"/> calls update
|
||||
/// it. Used by <c>Phase7Applier.MaterialiseScriptedAlarms</c>. Idempotent.
|
||||
/// </summary>
|
||||
/// <param name="alarmNodeId">The alarm node ID (== ScriptedAlarmId); becomes the condition's NodeId.</param>
|
||||
@@ -84,7 +85,7 @@ public sealed class NullOpcUaAddressSpaceSink : IOpcUaAddressSpaceSink
|
||||
public void WriteValue(string nodeId, object? value, OpcUaQuality quality, DateTime sourceTimestampUtc) { }
|
||||
|
||||
/// <inheritdoc />
|
||||
public void WriteAlarmState(string alarmNodeId, bool active, bool acknowledged, DateTime sourceTimestampUtc) { }
|
||||
public void WriteAlarmCondition(string alarmNodeId, AlarmConditionSnapshot state, DateTime sourceTimestampUtc) { }
|
||||
|
||||
/// <inheritdoc />
|
||||
public void MaterialiseAlarmCondition(string alarmNodeId, string equipmentNodeId, string displayName, string alarmType, int severity) { }
|
||||
|
||||
@@ -90,42 +90,69 @@ public sealed class OtOpcUaNodeManager : CustomNodeManager2
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply an alarm-state write. When a real Part 9 <see cref="AlarmConditionState"/> has been
|
||||
/// materialised for <paramref name="alarmNodeId"/> (via <see cref="MaterialiseAlarmCondition"/>),
|
||||
/// this projects <paramref name="active"/>/<paramref name="acknowledged"/> onto the live
|
||||
/// condition node's ActiveState/AckedState/Retain (T14 — basic active/ack only; <b>no event
|
||||
/// firing yet</b>, that lands in T16). Otherwise it falls back to the legacy two-element
|
||||
/// <c>[active, acknowledged]</c> <see cref="BaseDataVariableState"/> placeholder so callers
|
||||
/// whose alarm node hasn't been materialised (and the existing unit tests) keep working.
|
||||
/// Apply a full Part 9 alarm-condition write. When a real <see cref="AlarmConditionState"/> has
|
||||
/// been materialised for <paramref name="alarmNodeId"/> (via <see cref="MaterialiseAlarmCondition"/>),
|
||||
/// this projects the whole <paramref name="state"/> snapshot
|
||||
/// (Enabled / Active / Acked / Confirmed / Shelving / Severity / Message) onto the live condition
|
||||
/// node and recomputes Retain (T15 — richer state; <b>still no event firing</b>, that lands in T16).
|
||||
/// Otherwise it falls back to the legacy two-element <c>[Active, Acknowledged]</c>
|
||||
/// <see cref="BaseDataVariableState"/> placeholder so callers whose alarm node hasn't been
|
||||
/// materialised (and the existing unit tests) keep working.
|
||||
/// </summary>
|
||||
/// <param name="alarmNodeId">The node identifier of the alarm (== ScriptedAlarmId for materialised conditions).</param>
|
||||
/// <param name="active">Whether the alarm is currently active.</param>
|
||||
/// <param name="acknowledged">Whether the alarm has been acknowledged.</param>
|
||||
/// <param name="state">The full condition state to project onto the node.</param>
|
||||
/// <param name="sourceTimestampUtc">The timestamp of the alarm state change in UTC.</param>
|
||||
public void WriteAlarmState(string alarmNodeId, bool active, bool acknowledged, DateTime sourceTimestampUtc)
|
||||
public void WriteAlarmCondition(string alarmNodeId, AlarmConditionSnapshot state, DateTime sourceTimestampUtc)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrEmpty(alarmNodeId);
|
||||
ArgumentNullException.ThrowIfNull(state);
|
||||
|
||||
if (_alarmConditions.TryGetValue(alarmNodeId, out var condition))
|
||||
{
|
||||
lock (Lock)
|
||||
{
|
||||
condition.SetActiveState(SystemContext, active);
|
||||
condition.SetAcknowledgedState(SystemContext, acknowledged);
|
||||
// EnabledState / AckedState / ActiveState are mandatory children — always present after
|
||||
// Create. Confirm + Shelving are optional Part 9 children: T14's real-server finding is
|
||||
// that Create auto-builds them for our subtypes, but a base AlarmConditionState (or a
|
||||
// future SDK that builds a leaner child set) may leave them null. Null-guard each optional
|
||||
// child so projecting Confirmed/Shelving onto a node that lacks the sub-state machine is a
|
||||
// no-op rather than an NRE.
|
||||
condition.SetEnableState(SystemContext, state.Enabled);
|
||||
condition.SetActiveState(SystemContext, state.Active);
|
||||
condition.SetAcknowledgedState(SystemContext, state.Acknowledged);
|
||||
if (condition.ConfirmedState is not null)
|
||||
{
|
||||
condition.SetConfirmedState(SystemContext, state.Confirmed);
|
||||
}
|
||||
if (condition.ShelvingState is not null)
|
||||
{
|
||||
// SetShelvingState(shelved, oneShot, shelvingTime): map our 3-way kind onto the SDK's
|
||||
// (shelved, oneShot) flag pair. Timed shelving's expiry is owned by the engine, not the
|
||||
// SDK timer, so we pass shelvingTime=0 (no SDK-managed auto-unshelve).
|
||||
condition.SetShelvingState(
|
||||
SystemContext,
|
||||
shelved: state.Shelving != AlarmShelvingKind.Unshelved,
|
||||
oneShot: state.Shelving == AlarmShelvingKind.OneShot,
|
||||
shelvingTime: 0);
|
||||
}
|
||||
condition.SetSeverity(SystemContext, MapSeverity(state.Severity));
|
||||
condition.Message.Value = new LocalizedText(state.Message);
|
||||
|
||||
// Part 9: retain the condition while it is active OR unacknowledged so a client's
|
||||
// ConditionRefresh replays it. T16's event firing will also drive Retain; here we keep
|
||||
// it correct for the basic projection.
|
||||
condition.Retain.Value = active || !acknowledged;
|
||||
// it correct for the projection.
|
||||
condition.Retain.Value = state.Active || !state.Acknowledged;
|
||||
condition.Time.Value = sourceTimestampUtc;
|
||||
condition.ReceiveTime.Value = sourceTimestampUtc;
|
||||
// NO ReportEvent here — T16 owns event firing. ClearChangeMasks just notifies any
|
||||
// attribute (not event) subscribers watching ActiveState/AckedState/Retain directly.
|
||||
// attribute (not event) subscribers watching the condition's children directly.
|
||||
condition.ClearChangeMasks(SystemContext, includeChildren: true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Fallback: alarm not materialised as a real condition — keep the legacy bool[2] variable.
|
||||
// Fallback: alarm not materialised as a real condition — keep the legacy bool[2] variable so
|
||||
// un-materialised callers (and the existing unit tests) keep working.
|
||||
lock (Lock)
|
||||
{
|
||||
// CreateVariable mutates the SDK address space, so it MUST run under Lock (see WriteValue).
|
||||
@@ -135,7 +162,7 @@ public sealed class OtOpcUaNodeManager : CustomNodeManager2
|
||||
_variables[alarmNodeId] = variable;
|
||||
}
|
||||
|
||||
variable.Value = new[] { active, acknowledged };
|
||||
variable.Value = new[] { state.Active, state.Acknowledged };
|
||||
variable.StatusCode = StatusCodes.Good;
|
||||
variable.Timestamp = sourceTimestampUtc;
|
||||
variable.ClearChangeMasks(SystemContext, includeChildren: false);
|
||||
@@ -146,7 +173,7 @@ public sealed class OtOpcUaNodeManager : CustomNodeManager2
|
||||
/// Materialise a real OPC UA Part 9 <see cref="AlarmConditionState"/> node under its equipment
|
||||
/// folder so clients can browse it as a proper condition (and, once T16 lands, subscribe to its
|
||||
/// events). The node id is the alarm node id (the ScriptedAlarmId) so subsequent
|
||||
/// <see cref="WriteAlarmState"/> calls — which target that same id — update this node.
|
||||
/// <see cref="WriteAlarmCondition"/> calls — which target that same id — update this node.
|
||||
/// <para>
|
||||
/// This is the T14 production replacement for the <c>bool[2]</c> placeholder: it creates
|
||||
/// node + basic Active/Ack state + the notifier wiring needed for T16 events, but fires
|
||||
|
||||
@@ -59,12 +59,12 @@ public sealed class Phase7Applier
|
||||
var removedCount = 0;
|
||||
foreach (var eq in plan.RemovedEquipment)
|
||||
{
|
||||
SafeWriteAlarmState(eq.EquipmentId, active: false, acknowledged: false, ts);
|
||||
SafeWriteAlarmCondition(eq.EquipmentId, RemovedConditionState, ts);
|
||||
removedCount++;
|
||||
}
|
||||
foreach (var alarm in plan.RemovedAlarms)
|
||||
{
|
||||
SafeWriteAlarmState(alarm.ScriptedAlarmId, active: false, acknowledged: false, ts);
|
||||
SafeWriteAlarmCondition(alarm.ScriptedAlarmId, RemovedConditionState, ts);
|
||||
removedCount++;
|
||||
}
|
||||
|
||||
@@ -332,10 +332,22 @@ public sealed class Phase7Applier
|
||||
catch (Exception ex) { _logger.LogWarning(ex, "Phase7Applier: EnsureVariable threw for {Node}", nodeId); }
|
||||
}
|
||||
|
||||
private void SafeWriteAlarmState(string nodeId, bool active, bool acknowledged, DateTime ts)
|
||||
/// <summary>The "no-event" condition state written to a removed equipment / alarm node before the
|
||||
/// rebuild tears it down: inactive, acked, confirmed, enabled, unshelved, severity 0, empty message.
|
||||
/// Drives Retain to false so a removed condition stops replaying on ConditionRefresh.</summary>
|
||||
private static readonly AlarmConditionSnapshot RemovedConditionState = new(
|
||||
Active: false,
|
||||
Acknowledged: true,
|
||||
Confirmed: true,
|
||||
Enabled: true,
|
||||
Shelving: AlarmShelvingKind.Unshelved,
|
||||
Severity: 0,
|
||||
Message: string.Empty);
|
||||
|
||||
private void SafeWriteAlarmCondition(string nodeId, AlarmConditionSnapshot state, DateTime ts)
|
||||
{
|
||||
try { _sink.WriteAlarmState(nodeId, active, acknowledged, ts); }
|
||||
catch (Exception ex) { _logger.LogWarning(ex, "Phase7Applier: WriteAlarmState threw for {Node}", nodeId); }
|
||||
try { _sink.WriteAlarmCondition(nodeId, state, ts); }
|
||||
catch (Exception ex) { _logger.LogWarning(ex, "Phase7Applier: WriteAlarmCondition threw for {Node}", nodeId); }
|
||||
}
|
||||
|
||||
private void SafeMaterialiseAlarmCondition(string alarmNodeId, string equipmentNodeId, string displayName, string alarmType, int severity)
|
||||
|
||||
@@ -28,13 +28,12 @@ public sealed class SdkAddressSpaceSink : IOpcUaAddressSpaceSink
|
||||
public void WriteValue(string nodeId, object? value, OpcUaQuality quality, DateTime sourceTimestampUtc)
|
||||
=> _nodeManager.WriteValue(nodeId, value, quality, sourceTimestampUtc);
|
||||
|
||||
/// <summary>Writes alarm state to the OPC UA address space.</summary>
|
||||
/// <summary>Writes the full Part 9 alarm-condition state to the OPC UA address space.</summary>
|
||||
/// <param name="alarmNodeId">The alarm node identifier.</param>
|
||||
/// <param name="active">Whether the alarm is active.</param>
|
||||
/// <param name="acknowledged">Whether the alarm is acknowledged.</param>
|
||||
/// <param name="state">The full condition state to project onto the node.</param>
|
||||
/// <param name="sourceTimestampUtc">The source timestamp in UTC.</param>
|
||||
public void WriteAlarmState(string alarmNodeId, bool active, bool acknowledged, DateTime sourceTimestampUtc)
|
||||
=> _nodeManager.WriteAlarmState(alarmNodeId, active, acknowledged, sourceTimestampUtc);
|
||||
public void WriteAlarmCondition(string alarmNodeId, AlarmConditionSnapshot state, DateTime sourceTimestampUtc)
|
||||
=> _nodeManager.WriteAlarmCondition(alarmNodeId, state, sourceTimestampUtc);
|
||||
|
||||
/// <summary>Materialises a real Part 9 alarm-condition node in the address space.</summary>
|
||||
/// <param name="alarmNodeId">The alarm node identifier (== ScriptedAlarmId).</param>
|
||||
|
||||
@@ -30,7 +30,15 @@ public sealed class OpcUaPublishActor : ReceiveActor
|
||||
public const string RedundancyStateTopic = "redundancy-state";
|
||||
|
||||
public sealed record AttributeValueUpdate(string NodeId, object? Value, OpcUaQuality Quality, DateTime TimestampUtc);
|
||||
public sealed record AlarmStateUpdate(string AlarmNodeId, bool Active, bool Acknowledged, DateTime TimestampUtc);
|
||||
|
||||
/// <summary>Carries the full Part 9 condition state for a scripted alarm to the sink. The
|
||||
/// <paramref name="State"/> snapshot is the Commons projection the Runtime host maps from the engine's
|
||||
/// Core <c>AlarmConditionState</c> + severity/message — the actor stays decoupled from
|
||||
/// <c>Core.ScriptedAlarms</c>.</summary>
|
||||
/// <param name="AlarmNodeId">The alarm node id (== ScriptedAlarmId for materialised conditions).</param>
|
||||
/// <param name="State">The full condition state to project onto the node.</param>
|
||||
/// <param name="TimestampUtc">The source timestamp of the transition in UTC.</param>
|
||||
public sealed record AlarmStateUpdate(string AlarmNodeId, AlarmConditionSnapshot State, DateTime TimestampUtc);
|
||||
/// <summary>
|
||||
/// Triggers an address-space rebuild. <paramref name="DeploymentId"/> is the deployment
|
||||
/// just applied by the host; the rebuild loads THAT artifact so materialisation matches the
|
||||
@@ -167,13 +175,13 @@ public sealed class OpcUaPublishActor : ReceiveActor
|
||||
{
|
||||
try
|
||||
{
|
||||
_sink.WriteAlarmState(msg.AlarmNodeId, msg.Active, msg.Acknowledged, msg.TimestampUtc);
|
||||
_sink.WriteAlarmCondition(msg.AlarmNodeId, msg.State, msg.TimestampUtc);
|
||||
Interlocked.Increment(ref _writes);
|
||||
OtOpcUaTelemetry.OpcUaSinkWrite.Add(1, new KeyValuePair<string, object?>("kind", "alarm"));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Warning(ex, "OpcUaPublish: sink.WriteAlarmState threw for {Node}", msg.AlarmNodeId);
|
||||
_log.Warning(ex, "OpcUaPublish: sink.WriteAlarmCondition threw for {Node}", msg.AlarmNodeId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ using Akka.Actor;
|
||||
using Akka.Cluster.Tools.PublishSubscribe;
|
||||
using Akka.Event;
|
||||
using ZB.MOM.WW.OtOpcUa.Commons.Messages.Alerts;
|
||||
using ZB.MOM.WW.OtOpcUa.Commons.OpcUa;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.ScriptedAlarms;
|
||||
using ZB.MOM.WW.OtOpcUa.OpcUaServer;
|
||||
@@ -234,13 +235,12 @@ public sealed class ScriptedAlarmHostActor : ReceiveActor
|
||||
return;
|
||||
}
|
||||
|
||||
// Bridge to OPC UA: drive the alarm node's Active / Acknowledged sub-vars. We use e.AlarmId as
|
||||
// the node id for now — T14 will materialise the real condition node at an aligned NodeId and
|
||||
// this id will line up with it.
|
||||
// Bridge to OPC UA: project the FULL Part 9 condition state (enabled/active/acked/confirmed/
|
||||
// shelving/severity/message) onto the materialised condition node via the Commons snapshot.
|
||||
// e.AlarmId is the materialised condition's NodeId (T14 aligned it to the ScriptedAlarmId).
|
||||
_publishActor.Tell(new OpcUaPublishActor.AlarmStateUpdate(
|
||||
AlarmNodeId: e.AlarmId,
|
||||
Active: e.Condition.Active == AlarmActiveState.Active,
|
||||
Acknowledged: e.Condition.Acked == AlarmAckedState.Acknowledged,
|
||||
State: ToSnapshot(e),
|
||||
TimestampUtc: e.TimestampUtc));
|
||||
|
||||
// Publish the transition to the cluster `alerts` topic — the single historization + live
|
||||
@@ -297,6 +297,29 @@ public sealed class ScriptedAlarmHostActor : ReceiveActor
|
||||
HistorizeToAveva: p.HistorizeToAveva,
|
||||
Retain: p.Retain);
|
||||
|
||||
/// <summary>Maps a <see cref="ScriptedAlarmEvent"/>'s Core <see cref="AlarmConditionState"/> +
|
||||
/// severity/message down to the Commons <see cref="AlarmConditionSnapshot"/> the SDK sink projects.
|
||||
/// Severity is the OPC UA 1..1000 value <see cref="SeverityToInt"/> derives from the coarse engine
|
||||
/// bucket, cast to the <c>ushort</c> the SDK <c>SetSeverity</c> expects. Shelving's 3-way Core kind
|
||||
/// maps 1:1 onto the Commons <see cref="AlarmShelvingKind"/>.</summary>
|
||||
private static AlarmConditionSnapshot ToSnapshot(ScriptedAlarmEvent e) => new(
|
||||
Active: e.Condition.Active == AlarmActiveState.Active,
|
||||
Acknowledged: e.Condition.Acked == AlarmAckedState.Acknowledged,
|
||||
Confirmed: e.Condition.Confirmed == AlarmConfirmedState.Confirmed,
|
||||
Enabled: e.Condition.Enabled == AlarmEnabledState.Enabled,
|
||||
Shelving: MapShelving(e.Condition.Shelving.Kind),
|
||||
Severity: (ushort)SeverityToInt(e.Severity),
|
||||
Message: e.Message);
|
||||
|
||||
/// <summary>Maps the Core <see cref="ShelvingKind"/> onto the Commons <see cref="AlarmShelvingKind"/>
|
||||
/// mirror (the Commons assembly can't see the Core enum).</summary>
|
||||
private static AlarmShelvingKind MapShelving(ShelvingKind kind) => kind switch
|
||||
{
|
||||
ShelvingKind.OneShot => AlarmShelvingKind.OneShot,
|
||||
ShelvingKind.Timed => AlarmShelvingKind.Timed,
|
||||
_ => AlarmShelvingKind.Unshelved,
|
||||
};
|
||||
|
||||
/// <summary>The acting user for an <see cref="AlarmTransitionEvent"/>. Engine-driven
|
||||
/// Activated / Cleared transitions are <c>"system"</c>; operator Acknowledged / Confirmed carry the
|
||||
/// recorded user from the condition state, falling back to <c>"system"</c> when none was recorded.</summary>
|
||||
|
||||
Reference in New Issue
Block a user