feat(opcua): write-outcome self-correction — capture prior + compare-and-revert on failure

This commit is contained in:
Joseph Doherty
2026-06-14 01:30:20 -04:00
parent 526ddb6a57
commit 10efcf4517
4 changed files with 224 additions and 133 deletions
@@ -37,22 +37,25 @@ public sealed class OtOpcUaSdkServer : StandardServer
}
/// <summary>
/// Wire the reverse-path sink for inbound operator writes to writable equipment-tag nodes onto the
/// created <see cref="OtOpcUaNodeManager"/>. The host calls this after start with a fire-and-forget
/// lambda that kicks off a bounded <c>Ask</c> of the local <c>DriverHostActor</c>
/// (<c>RouteNodeWrite</c>) and logs failures from a continuation — it must NOT block, because the
/// handler runs under the node-manager <c>Lock</c> (mirrors <see cref="SetAlarmCommandRouter"/>).
/// No-op (returns <c>false</c>) when the node manager has not been created yet, so the caller can
/// detect a too-early call.
/// Wire the reverse-path gateway for inbound operator writes to writable equipment-tag nodes onto the
/// created <see cref="OtOpcUaNodeManager"/>. The host calls this after start with an
/// <c>ActorNodeWriteGateway</c> whose <c>WriteAsync</c> kicks off a bounded <c>Ask</c> of the local
/// <c>DriverHostActor</c> (<c>RouteNodeWrite</c>); the node manager calls it fire-and-forget and uses
/// the resolved <c>NodeWriteOutcome</c> to self-correct the node on a failed device write (mirrors
/// <see cref="SetAlarmCommandRouter"/>). Passing <c>null</c> restores the
/// <c>NullOpcUaNodeWriteGateway</c> default (writes unavailable). No-op (returns <c>false</c>) when the
/// node manager has not been created yet, so the caller can detect a too-early call.
/// </summary>
/// <param name="router">The router invoked by the writable node's <c>OnWriteValue</c> handler once the
/// <c>WriteOperate</c> gate passes; may be <c>null</c> to clear it.</param>
/// <returns><c>true</c> when the router was set on a live node manager; <c>false</c> when no node
/// <param name="gateway">The gateway invoked by the writable node's <c>OnWriteValue</c> handler once the
/// <c>WriteOperate</c> gate passes; may be <c>null</c> to restore the Null default.</param>
/// <returns><c>true</c> when the gateway was set on a live node manager; <c>false</c> when no node
/// manager exists yet.</returns>
public bool SetNodeWriteRouter(Action<string, object?>? router)
public bool SetNodeWriteGateway(IOpcUaNodeWriteGateway? gateway)
{
if (_otOpcUaNodeManager is null) return false;
_otOpcUaNodeManager.NodeWriteRouter = router;
// The NodeWriteGateway setter null-coalesces to the Null default, so a null gateway is intentional
// (restores "writes unavailable"); forgive the nullable-in here.
_otOpcUaNodeManager.NodeWriteGateway = gateway!;
return true;
}