driver-galaxy: GalaxyDriver implements IAlarmSource (PR B.2)
Twelfth PR of the alarms-over-gateway epic (docs/plans/alarms-over-gateway.md). Depends on PR B.1 (EventPump dispatch, merged) and PR E.2 (.NET SDK alarm methods, merged). Restores the v1 IAlarmSource capability that PR 7.2 retired with the legacy Galaxy.Host / Galaxy.Proxy projects. GalaxyDriver gains: - IAlarmSource on the class declaration → eight capabilities total (IDriver / ITagDiscovery / IReadable / IWritable / ISubscribable / IRediscoverable / IHostConnectivityProbe / IAlarmSource). - SubscribeAlarmsAsync — returns a sentinel handle and starts the shared EventPump (alarm wiring is lazy on first sub). Multiple handles share the same gateway stream; the server-side AlarmConditionService dispatches per-source-node downstream. - UnsubscribeAlarmsAsync — symmetric handle removal; rejects handles not issued by this driver. - AcknowledgeAsync — issues one gateway RPC per acknowledgement through IGalaxyAlarmAcknowledger. ConditionId carries the alarm full reference; falls back to SourceNodeId when empty. - OnAlarmEvent — bridges EventPump.OnAlarmTransition (B.1) onto AlarmEventArgs. Suppressed when no alarm subscription is active so untracked transitions don't leak through. New runtime types: - IGalaxyAlarmAcknowledger — test seam. - GatewayGalaxyAlarmAcknowledger — production wrapper around MxGatewayClient.AcknowledgeAlarmAsync (PR E.2). Maps native MxStatus failures to a logged warning rather than a thrown exception so a transient MxAccess hiccup doesn't fail the operator's Acknowledge. - GalaxyAlarmSubscriptionHandle — driver-side IAlarmSubscriptionHandle. Production runtime construction in BuildProductionRuntimeAsync wires the acknowledger when not pre-injected; tests inject a fake via the internal ctor. Tests: - 7 new tests in GalaxyDriverAlarmSourceTests — subscribe → event fire path, suppress without subscription, unsubscribe stops flow, foreign-handle rejection, ack routes per-request, ack falls back to SourceNodeId, ack throws NotSupported without acknowledger. - Full Driver.Galaxy.Tests: 203 passed (was 196; 7 new). Operates as a "stub-ready" surface — runtime ack calls will return PERMISSION_DENIED until A.3 ships the gateway-side dispatch, and no alarm transitions will arrive until A.2 adds the worker MxAccess subscription. Both will activate this code path automatically when the gateway side lands. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MxGateway.Client;
|
||||
using MxGateway.Contracts.Proto;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Runtime;
|
||||
|
||||
/// <summary>
|
||||
/// Production <see cref="IGalaxyAlarmAcknowledger"/> backed by the
|
||||
/// <c>MxGatewayClient.AcknowledgeAlarmAsync</c> RPC (PR E.2). Maps the
|
||||
/// reply's protocol status into a thrown exception when the gateway
|
||||
/// reports a non-OK condition; native MxStatus failures inside the reply
|
||||
/// surface as a logged warning so operator workflows aren't blocked by a
|
||||
/// transient MxAccess hiccup.
|
||||
/// </summary>
|
||||
internal sealed class GatewayGalaxyAlarmAcknowledger : IGalaxyAlarmAcknowledger
|
||||
{
|
||||
private readonly MxGatewayClient _client;
|
||||
private readonly GalaxyMxSession _session;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public GatewayGalaxyAlarmAcknowledger(
|
||||
MxGatewayClient client,
|
||||
GalaxyMxSession session,
|
||||
ILogger logger)
|
||||
{
|
||||
_client = client ?? throw new ArgumentNullException(nameof(client));
|
||||
_session = session ?? throw new ArgumentNullException(nameof(session));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public async Task AcknowledgeAsync(
|
||||
string alarmFullReference,
|
||||
string comment,
|
||||
string operatorUser,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrEmpty(alarmFullReference);
|
||||
|
||||
var session = _session.Session
|
||||
?? throw new InvalidOperationException(
|
||||
"GatewayGalaxyAlarmAcknowledger requires a connected GalaxyMxSession; underlying gateway session is null.");
|
||||
var sessionId = session.SessionId;
|
||||
|
||||
var reply = await _client.AcknowledgeAlarmAsync(
|
||||
new AcknowledgeAlarmRequest
|
||||
{
|
||||
SessionId = sessionId,
|
||||
ClientCorrelationId = Guid.NewGuid().ToString("N"),
|
||||
AlarmFullReference = alarmFullReference,
|
||||
Comment = comment ?? string.Empty,
|
||||
OperatorUser = operatorUser ?? string.Empty,
|
||||
},
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (reply.Status is { Success: 0 } status)
|
||||
{
|
||||
// Native MxAccess rejected the ack — log but don't throw. Treat as a
|
||||
// best-effort operator workflow; the operator can retry via the OPC UA
|
||||
// session if necessary.
|
||||
_logger.LogWarning(
|
||||
"Galaxy AcknowledgeAlarm for {AlarmRef} returned MxStatus failure: category={Category} detail={Detail} text={Text}",
|
||||
alarmFullReference, status.Category, status.Detail, status.DiagnosticText);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user