Separates ApplicationUri from namespace identity so each instance in a redundant pair has a unique server URI while sharing the same Galaxy namespace. Exposes RedundancySupport, ServerUriArray, and dynamic ServiceLevel through the standard OPC UA server object. ServiceLevel is computed from role (Primary/Secondary) and runtime health (MXAccess and DB connectivity). Adds CLI redundancy command, second deployed service instance, and 31 new tests including paired-server integration. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
71 lines
2.7 KiB
C#
71 lines
2.7 KiB
C#
using CliFx;
|
|
using CliFx.Attributes;
|
|
using CliFx.Infrastructure;
|
|
using Opc.Ua;
|
|
using Opc.Ua.Client;
|
|
|
|
namespace OpcUaCli.Commands;
|
|
|
|
[Command("redundancy", Description = "Read redundancy state from an OPC UA server")]
|
|
public class RedundancyCommand : ICommand
|
|
{
|
|
[CommandOption("url", 'u', Description = "OPC UA server endpoint URL", IsRequired = true)]
|
|
public string Url { get; init; } = default!;
|
|
|
|
[CommandOption("username", 'U', Description = "Username for authentication")]
|
|
public string? Username { get; init; }
|
|
|
|
[CommandOption("password", 'P', Description = "Password for authentication")]
|
|
public string? Password { get; init; }
|
|
|
|
[CommandOption("security", 'S', Description = "Transport security: none, sign, encrypt (default: none)")]
|
|
public string Security { get; init; } = "none";
|
|
|
|
public async ValueTask ExecuteAsync(IConsole console)
|
|
{
|
|
using var session = await OpcUaHelper.ConnectAsync(Url, Username, Password, Security);
|
|
|
|
// Read RedundancySupport
|
|
var redundancySupportValue = await session.ReadValueAsync(VariableIds.Server_ServerRedundancy_RedundancySupport);
|
|
var redundancyMode = (RedundancySupport)(int)redundancySupportValue.Value;
|
|
await console.Output.WriteLineAsync($"Redundancy Mode: {redundancyMode}");
|
|
|
|
// Read ServiceLevel
|
|
var serviceLevelValue = await session.ReadValueAsync(VariableIds.Server_ServiceLevel);
|
|
var serviceLevel = (byte)serviceLevelValue.Value;
|
|
await console.Output.WriteLineAsync($"Service Level: {serviceLevel}");
|
|
|
|
// Read ServerUriArray (only present for non-transparent redundancy)
|
|
try
|
|
{
|
|
var serverUriArrayValue = await session.ReadValueAsync(VariableIds.Server_ServerRedundancy_ServerUriArray);
|
|
if (serverUriArrayValue.Value is string[] uris && uris.Length > 0)
|
|
{
|
|
await console.Output.WriteLineAsync("Server URIs:");
|
|
foreach (var uri in uris)
|
|
{
|
|
await console.Output.WriteLineAsync($" - {uri}");
|
|
}
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// ServerUriArray may not be present when RedundancySupport is None
|
|
}
|
|
|
|
// Read ServerArray for the local server's ApplicationUri
|
|
try
|
|
{
|
|
var serverArrayValue = await session.ReadValueAsync(VariableIds.Server_ServerArray);
|
|
if (serverArrayValue.Value is string[] serverArray && serverArray.Length > 0)
|
|
{
|
|
await console.Output.WriteLineAsync($"Application URI: {serverArray[0]}");
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// Informational only
|
|
}
|
|
}
|
|
}
|