feat(alarms): route inbound Part 9 alarm methods through AlarmAck gate (T18)
Wire the materialised AlarmConditionState method handlers so a client calling Acknowledge/Confirm/Shelve/AddComment is gated on the AlarmAck data-plane role and, when allowed, routed back to the scripted-alarm engine via a new `alarm-commands` DistributedPubSub topic. - Commons: new AlarmCommand DTO (AlarmId/Operation/User/Comment/UnshelveAtUtc). - ScriptedAlarmHostActor: add AlarmCommandsTopic const. - OtOpcUaNodeManager: settable AlarmCommandRouter + wire OnAcknowledge/OnConfirm/ OnAddComment/OnShelve/OnTimedUnshelve. Each resolves the principal off ISessionOperationContext.UserIdentity as RoleCarryingUserIdentity, fails closed (BadUserAccessDenied) when the AlarmAck role is absent or no identity, else maps + routes an AlarmCommand and returns Good. OnShelve discriminates OneShotShelve/ TimedShelve/Unshelve from the SDK flags; TimedShelve expiry = UtcNow + ms. No Akka/IActorRef handle — only the Action<AlarmCommand> delegate. T20 de-dup note left; WriteAlarmCondition untouched. - OpcUaServer.Security: OpcUaDataPlaneRoles.AlarmAck shared const (the role was a bare string everywhere; introduced one symbol for the gate + tests). - OtOpcUaSdkServer: SetAlarmCommandRouter pass-through. - Host: boot wiring publishes each command via mediator.Tell(Publish(...)) using a lazy ActorSystem accessor (mirrors DpsScriptLogPublisher). - Tests: 11 new gate + mapping tests (OpcUaServer.Tests 88->99, all green).
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
namespace ZB.MOM.WW.OtOpcUa.Commons.OpcUa;
|
||||
|
||||
/// <summary>
|
||||
/// Commons-level command carried from an inbound OPC UA Part 9 alarm method call
|
||||
/// (Acknowledge / Confirm / Shelve / AddComment …) back to the scripted-alarm engine. The SDK
|
||||
/// node manager builds one of these in its condition method-handler delegates after the
|
||||
/// <c>AlarmAck</c> role gate passes, then the host routes it onto the cluster
|
||||
/// <c>alarm-commands</c> DistributedPubSub topic; T19's engine-side subscriber consumes it and
|
||||
/// drives the matching <c>Part9StateMachine.Apply*</c> transition. This is a pure DTO — it makes
|
||||
/// no auth decision and holds no SDK/Akka handle.
|
||||
/// </summary>
|
||||
/// <param name="AlarmId">
|
||||
/// The alarm's ScriptedAlarmId — equal to the materialised condition node's NodeId identifier
|
||||
/// (T14 aligned the condition NodeId to the ScriptedAlarmId). The engine keys its domain state by
|
||||
/// this id.
|
||||
/// </param>
|
||||
/// <param name="Operation">
|
||||
/// The Part 9 operation, one of: <c>Acknowledge</c>, <c>Confirm</c>, <c>OneShotShelve</c>,
|
||||
/// <c>TimedShelve</c>, <c>Unshelve</c>, <c>Enable</c>, <c>Disable</c>, <c>AddComment</c>. These map
|
||||
/// 1:1 onto the engine's <c>Part9StateMachine.Apply*</c> calls on the consuming side (T19).
|
||||
/// </param>
|
||||
/// <param name="User">The acting user — the authenticated session identity's display/name.</param>
|
||||
/// <param name="Comment">
|
||||
/// The free-text comment supplied with the call (the OPC UA <c>LocalizedText</c> payload's text),
|
||||
/// or <c>null</c> when none was provided.
|
||||
/// </param>
|
||||
/// <param name="UnshelveAtUtc">
|
||||
/// For <c>TimedShelve</c>, the absolute UTC instant the shelve auto-expires
|
||||
/// (<c>DateTime.UtcNow + shelvingTime</c>, where the OPC UA <c>Duration</c> <c>shelvingTime</c> is
|
||||
/// in <b>milliseconds</b>); <c>null</c> for every other operation.
|
||||
/// </param>
|
||||
public sealed record AlarmCommand(
|
||||
string AlarmId,
|
||||
string Operation,
|
||||
string User,
|
||||
string? Comment,
|
||||
DateTime? UnshelveAtUtc);
|
||||
Reference in New Issue
Block a user