fix(opcua): forward ISurgicalAddressSpaceSink through DeferredAddressSpaceSink (F10b surgical path was inert in prod)

This commit is contained in:
Joseph Doherty
2026-06-18 13:57:53 -04:00
parent b472bba384
commit 22b0611fb9
2 changed files with 89 additions and 1 deletions
@@ -12,7 +12,7 @@ namespace ZB.MOM.WW.OtOpcUa.Commons.OpcUa;
/// no-op against <see cref="NullOpcUaAddressSpaceSink"/>, so the actor stays safe to
/// receive messages from the moment it boots.
/// </summary>
public sealed class DeferredAddressSpaceSink : IOpcUaAddressSpaceSink
public sealed class DeferredAddressSpaceSink : IOpcUaAddressSpaceSink, ISurgicalAddressSpaceSink
{
private volatile IOpcUaAddressSpaceSink _inner = NullOpcUaAddressSpaceSink.Instance;
@@ -69,4 +69,17 @@ public sealed class DeferredAddressSpaceSink : IOpcUaAddressSpaceSink
/// <summary>Rebuilds the address space through the inner sink.</summary>
public void RebuildAddressSpace() => _inner.RebuildAddressSpace();
/// <summary>Forwards an in-place tag-attribute update (F10b) to the inner sink when it supports the
/// surgical capability. Returns false otherwise — before the real <c>SdkAddressSpaceSink</c> is
/// swapped in (inner is still the null sink), or any inner sink that isn't surgical — so the caller
/// (Phase7Applier) falls back to a full rebuild. Without this forward the surgical optimization is
/// inert on every driver-role host, because actors inject THIS wrapper, not the inner sink.</summary>
/// <param name="variableNodeId">The node ID of the variable to update in place.</param>
/// <param name="writable">Whether the node should be read/write.</param>
/// <param name="historianTagname">null ⇒ not historized; non-null ⇒ Historizing + historian binding.</param>
/// <returns>True when the inner sink applied the update; false when it lacks the capability or the node is missing.</returns>
public bool UpdateTagAttributes(string variableNodeId, bool writable, string? historianTagname)
=> _inner is ISurgicalAddressSpaceSink surgical
&& surgical.UpdateTagAttributes(variableNodeId, writable, historianTagname);
}