diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/OpcUa/IOpcUaNodeWriteGateway.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/OpcUa/IOpcUaNodeWriteGateway.cs new file mode 100644 index 00000000..4469005f --- /dev/null +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/OpcUa/IOpcUaNodeWriteGateway.cs @@ -0,0 +1,36 @@ +namespace ZB.MOM.WW.OtOpcUa.Commons.OpcUa; + +/// +/// Reverse-path gateway for an inbound OPC UA operator write to a writable equipment-tag node. +/// The node manager calls fire-and-forget from its OnWriteValue handler +/// (which runs under the node-manager Lock, so the call MUST return promptly — it kicks off an +/// asynchronous route and the Task completes later) and uses the resolved +/// to self-correct the node on failure. +/// +public interface IOpcUaNodeWriteGateway +{ + /// Route a write of to the driver backing node + /// ; the returned task resolves with the device-write outcome. + /// The folder-scoped equipment-variable node id being written. + /// The value the client wrote. + /// Cancellation token. + /// A task resolving to the device-write outcome. + Task WriteAsync(string nodeId, object? value, CancellationToken ct); +} + +/// Outcome of routing an inbound node write to the backing driver. +/// True when the driver accepted the write. +/// Failure detail when is false; null on success. +public readonly record struct NodeWriteOutcome(bool Success, string? Reason); + +/// No-op gateway: every write resolves to "writes unavailable" (matches the legacy +/// no-router-wired BadNotWritable). The default before the host wires the real gateway. +public sealed class NullOpcUaNodeWriteGateway : IOpcUaNodeWriteGateway +{ + /// The shared singleton instance. + public static readonly NullOpcUaNodeWriteGateway Instance = new(); + private NullOpcUaNodeWriteGateway() { } + /// + public Task WriteAsync(string nodeId, object? value, CancellationToken ct) => + Task.FromResult(new NodeWriteOutcome(false, "writes unavailable")); +} diff --git a/tests/Core/ZB.MOM.WW.OtOpcUa.Commons.Tests/OpcUa/NullOpcUaNodeWriteGatewayTests.cs b/tests/Core/ZB.MOM.WW.OtOpcUa.Commons.Tests/OpcUa/NullOpcUaNodeWriteGatewayTests.cs new file mode 100644 index 00000000..dc7a2899 --- /dev/null +++ b/tests/Core/ZB.MOM.WW.OtOpcUa.Commons.Tests/OpcUa/NullOpcUaNodeWriteGatewayTests.cs @@ -0,0 +1,16 @@ +using Shouldly; +using Xunit; +using ZB.MOM.WW.OtOpcUa.Commons.OpcUa; + +namespace ZB.MOM.WW.OtOpcUa.Commons.Tests.OpcUa; + +public class NullOpcUaNodeWriteGatewayTests +{ + [Fact] + public async Task NullGateway_returns_writes_unavailable() + { + var outcome = await NullOpcUaNodeWriteGateway.Instance.WriteAsync("ns=2;s=x", 1, TestContext.Current.CancellationToken); + outcome.Success.ShouldBeFalse(); + outcome.Reason.ShouldBe("writes unavailable"); + } +}