namespace ZB.MOM.WW.OtOpcUa.Commons.OpcUa; /// /// Wrapper that defers to an inner sink swapped in at /// runtime. Needed because the production sink (SdkAddressSpaceSink) wraps an /// OtOpcUaNodeManager that only exists after the SDK StandardServer has /// started — but Akka actors resolve their sink dependency at construction time, before /// the hosted service has booted the SDK. /// /// Bound as a singleton in DI on driver-role hosts; the OPC UA hosted service calls /// once the server is up. Until that swap happens, every call is a /// no-op against , so the actor stays safe to /// receive messages from the moment it boots. /// public sealed class DeferredAddressSpaceSink : IOpcUaAddressSpaceSink { private volatile IOpcUaAddressSpaceSink _inner = NullOpcUaAddressSpaceSink.Instance; /// Swap in the production sink. Pass null to revert to the null sink /// (used during graceful shutdown so post-stop writes don't hit a half-disposed manager). public void SetSink(IOpcUaAddressSpaceSink? sink) => _inner = sink ?? NullOpcUaAddressSpaceSink.Instance; public void WriteValue(string nodeId, object? value, OpcUaQuality quality, DateTime sourceTimestampUtc) => _inner.WriteValue(nodeId, value, quality, sourceTimestampUtc); public void WriteAlarmState(string alarmNodeId, bool active, bool acknowledged, DateTime sourceTimestampUtc) => _inner.WriteAlarmState(alarmNodeId, active, acknowledged, sourceTimestampUtc); public void EnsureFolder(string folderNodeId, string? parentNodeId, string displayName) => _inner.EnsureFolder(folderNodeId, parentNodeId, displayName); public void RebuildAddressSpace() => _inner.RebuildAddressSpace(); }