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");
}
}
}