fix(runtime): #869 review round 3 — key a native-ack revert to the ack that failed
A pending local ack can outlive its own delivery (sources that send no Acknowledge back; a Clear now only carries it through), so a later failed re-ack — or a stale Galaxy confirmation timing out after the alarm re-occurred and was acked again — could revert an ack it does not own. MarkAcknowledged now issues a sequence (0 when the ack changed nothing), carried on RouteAlarmAck and echoed on AlarmAckFailed; TryRevertAcknowledge reverts only on a matching sequence. Claude-Session: https://claude.ai/code/session_01GT67AD99zQQqpLCVm3p7xk
This commit is contained in:
@@ -90,8 +90,11 @@ public sealed class DriverInstanceActor : ReceiveActor, IWithTimers
|
||||
/// acknowledge that does not reach the upstream alarm system (refused, failed, or dropped because the driver
|
||||
/// is not connected) is reported back to the sender as an <see cref="AlarmAckFailed"/> so the host can revert
|
||||
/// the locally-applied acknowledge (lmxopcua#760). Null ⇒ no report.</param>
|
||||
/// <param name="AckSequence">The host projector's sequence for this acknowledge (lmxopcua#869), echoed on
|
||||
/// <see cref="AlarmAckFailed"/> so only a failure of THIS ack reverts it; 0 ⇒ the ack changed nothing.</param>
|
||||
public sealed record RouteAlarmAck(
|
||||
string ConditionId, string? Comment, string OperatorUser, string? ApiKeyId = null, string? ConditionNodeId = null);
|
||||
string ConditionId, string? Comment, string OperatorUser, string? ApiKeyId = null, string? ConditionNodeId = null,
|
||||
long AckSequence = 0);
|
||||
|
||||
/// <summary>
|
||||
/// Reply to a <see cref="RouteAlarmAck"/> carrying a <see cref="RouteAlarmAck.ConditionNodeId"/> whose
|
||||
@@ -99,7 +102,8 @@ public sealed class DriverInstanceActor : ReceiveActor, IWithTimers
|
||||
/// </summary>
|
||||
/// <param name="ConditionNodeId">The OPC UA condition NodeId the client acknowledged.</param>
|
||||
/// <param name="Reason">Why the acknowledge did not go through; never contains a secret.</param>
|
||||
public sealed record AlarmAckFailed(string ConditionNodeId, string Reason);
|
||||
/// <param name="AckSequence">The failed acknowledge's <see cref="RouteAlarmAck.AckSequence"/> (lmxopcua#869).</param>
|
||||
public sealed record AlarmAckFailed(string ConditionNodeId, string Reason, long AckSequence = 0);
|
||||
public sealed record Subscribe(IReadOnlyList<string> FullReferences, TimeSpan PublishingInterval);
|
||||
/// <summary>
|
||||
/// Sets the set of references this driver should keep subscribed for the lifetime of the
|
||||
@@ -1451,7 +1455,7 @@ public sealed class DriverInstanceActor : ReceiveActor, IWithTimers
|
||||
private static void ReportAckFailed(IActorRef replyTo, RouteAlarmAck msg, string reason)
|
||||
{
|
||||
if (string.IsNullOrEmpty(msg.ConditionNodeId) || replyTo.IsNobody()) return;
|
||||
replyTo.Tell(new AlarmAckFailed(msg.ConditionNodeId, reason));
|
||||
replyTo.Tell(new AlarmAckFailed(msg.ConditionNodeId, reason, msg.AckSequence));
|
||||
}
|
||||
|
||||
private async Task HandleSubscribeAsync(Subscribe msg)
|
||||
|
||||
Reference in New Issue
Block a user