feat(opcua): emit Bad blip + AuditWriteUpdateEvent + sync fail-fast on failed device write

This commit is contained in:
Joseph Doherty
2026-06-19 02:14:58 -04:00
parent e047af0553
commit 36eb14e88d
2 changed files with 227 additions and 2 deletions
@@ -1064,6 +1064,8 @@ public sealed class OtOpcUaNodeManager : CustomNodeManager2
/// </list>
/// Silent value-wise — this node manager carries no logger; the gateway logs the underlying write failure
/// and the SDK trace captures any audit-report failure.
/// <para><c>internal</c> (not private) so the behavioural test drives the blip-then-settle continuation
/// against a booted node manager — see <c>NodeManagerWriteRevertTests</c>.</para>
/// </summary>
/// <param name="nodeId">The string id of the written variable node.</param>
/// <param name="outcome">The device-write outcome routed back by the gateway.</param>
@@ -1072,7 +1074,7 @@ public sealed class OtOpcUaNodeManager : CustomNodeManager2
/// <param name="priorStatus">The node's real status captured before the optimistic write.</param>
/// <param name="clientUserId">The writing principal's user-id string (the identity's DisplayName), threaded
/// from <see cref="OnEquipmentTagWrite"/> to populate the audit event's ClientUserId; null when unknown.</param>
private void RevertOptimisticWriteIfNeeded(
internal void RevertOptimisticWriteIfNeeded(
string nodeId, NodeWriteOutcome outcome, object? optimisticValue, object? priorValue, StatusCode priorStatus,
string? clientUserId)
{
@@ -1132,7 +1134,10 @@ public sealed class OtOpcUaNodeManager : CustomNodeManager2
/// <param name="priorValue">The node's real pre-write value (the audit OldValue).</param>
/// <param name="clientUserId">The writing principal's user-id string; null when unknown.</param>
/// <returns>A populated, unreported <see cref="AuditWriteUpdateEventState"/>.</returns>
private AuditWriteUpdateEventState BuildWriteFailureAuditEvent(
/// <remarks><c>internal</c> (not private) so <c>NodeManagerWriteRevertTests</c> can assert the populated
/// audit fields at the nearest deterministic seam (the end-to-end <c>Server.ReportEvent</c> dispatch would
/// need a subscribed event monitored-item to observe).</remarks>
internal AuditWriteUpdateEventState BuildWriteFailureAuditEvent(
BaseDataVariableState variable, NodeWriteOutcome outcome, object? optimisticValue, object? priorValue,
string? clientUserId)
{