Files
lmxopcua/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/IAlarmAcknowledgeConfirmation.cs
T
Joseph Doherty 4e492e6550 fix(galaxy): confirm unconfirmed native alarm acks against Galaxy's acknowledged transition (#869)
mxaccessgw answers AcknowledgeAlarm OK both when the AckMsg write was
applied and when it was only sent (no OnWriteComplete; the reply then
carries a diagnostic). EvaluateReply treated both as success, so the H6
live gate saw a condition go Acknowledged while Galaxy never applied it.

- EvaluateReply returns Applied (no diagnostic) or Unconfirmed (diagnostic)
  and logs the gateway diagnostic on every branch that carries one.
- GalaxyAckConfirmationTracker watches the alarm feed from before the RPC
  and, for an unconfirmed reply, waits up to AlarmAck.ConfirmTimeoutMs
  (default 10 s, 0 = old behaviour) for an Acknowledge/ActiveAcknowledged
  transition; on timeout it refuses (BadTimeout + diagnostic).
- New optional IAlarmAcknowledgeConfirmation: DriverInstanceActor awaits it
  after AcknowledgeAsync, off its mailbox and outside the resilience
  pipeline; a failure is the existing #760 AlarmAckFailed revert.
- AdminUI Galaxy form carries the new knob; docs updated.

Claude-Session: https://claude.ai/code/session_01GT67AD99zQQqpLCVm3p7xk
2026-11-11 13:03:04 -05:00

34 lines
2.2 KiB
C#

namespace ZB.MOM.WW.OtOpcUa.Core.Abstractions;
/// <summary>
/// Optional companion to <see cref="IAlarmSource"/> for a driver whose upstream can ACCEPT an acknowledge
/// without CONFIRMING it was applied (lmxopcua#869). mxaccessgw answers its <c>AcknowledgeAlarm</c> RPC OK
/// both when the <c>AckMsg</c> write was applied and when it was only sent with no completion observed; the
/// truth arrives later, as the alarm's own acknowledged transition on the alarm stream.
/// </summary>
/// <remarks>
/// <para>
/// The runtime calls <see cref="ConfirmAcknowledgedAsync"/> after <see cref="IAlarmSource.AcknowledgeAsync"/>
/// returned normally, with the SAME request instances, OUTSIDE the resilience pipeline (it is a wait on an
/// upstream event, not I/O, so it must neither be cut by the capability timeout nor count toward the
/// breaker) and OFF the driver actor's mailbox (the wait is seconds long).
/// </para>
/// <para>
/// It completes normally when every request is confirmed, or never needed confirming (the upstream
/// reported it applied). It throws — normally a <see cref="DriverOperationRefusedException"/> naming why —
/// when an acknowledge is not confirmed within the driver's own bounded window; the runtime then reverts
/// the condition to unacknowledged exactly as it does for an <see cref="IAlarmSource.AcknowledgeAsync"/>
/// failure (lmxopcua#760). The window is the driver's to own and bound.
/// </para>
/// </remarks>
public interface IAlarmAcknowledgeConfirmation
{
/// <summary>Waits for the upstream to confirm the acknowledges <see cref="IAlarmSource.AcknowledgeAsync"/> sent.</summary>
/// <param name="acknowledgements">The request instances previously passed to <see cref="IAlarmSource.AcknowledgeAsync"/>.</param>
/// <param name="cancellationToken">Abandons the wait (the caller then treats the acknowledge as unconfirmed).</param>
/// <returns>A task that completes when every acknowledge is confirmed or needed no confirmation.</returns>
Task ConfirmAcknowledgedAsync(
IReadOnlyList<AlarmAcknowledgeRequest> acknowledgements,
CancellationToken cancellationToken);
}