using Microsoft.Extensions.Logging; using Opc.Ua; using Opc.Ua.Server; using ZB.MOM.WW.OtOpcUa.Commons.OpcUa; namespace ZB.MOM.WW.OtOpcUa.OpcUaServer; /// /// Production that writes the OPC UA Server object's /// ServiceLevel Variable through the SDK. Clients reading /// VariableIds.Server_ServiceLevel see the live value updated whenever the redundancy /// state changes — that's the standard OPC UA non-transparent-redundancy signal callers use /// to pick a primary. /// /// Uses (a ) and /// its child variable, which the SDK populates /// automatically during initialization. Writes are /// guarded by so concurrent diagnostics scans /// from the SDK don't fight with our update. /// public sealed class SdkServiceLevelPublisher : IServiceLevelPublisher { private readonly IServerInternal _serverInternal; private readonly ILogger _logger; /// Initializes a new instance of the SdkServiceLevelPublisher class. /// The OPC UA server internal interface. /// The logger instance. public SdkServiceLevelPublisher(IServerInternal serverInternal, ILogger logger) { _serverInternal = serverInternal; _logger = logger; } /// Publishes the service level to the OPC UA Server object. /// The service level value to publish. public void Publish(byte serviceLevel) { var node = _serverInternal.ServerObject?.ServiceLevel; if (node is null) { _logger.LogWarning("SdkServiceLevelPublisher: ServerObject.ServiceLevel unavailable; skipping write"); return; } try { lock (_serverInternal.DiagnosticsLock) { node.Value = serviceLevel; node.Timestamp = DateTime.UtcNow; node.StatusCode = StatusCodes.Good; node.ClearChangeMasks(_serverInternal.DefaultSystemContext, includeChildren: false); } } catch (Exception ex) { _logger.LogWarning(ex, "SdkServiceLevelPublisher: write to Server.ServiceLevel threw"); } } }