using Microsoft.Extensions.Logging; using MxGateway.Client; using MxGateway.Contracts.Proto; namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Runtime; /// /// Production backed by the /// MxGatewayClient.AcknowledgeAlarmAsync 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. /// 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); } } }