Files
lmxopcua/tools/opcuacli-dotnet/Commands/RedundancyCommand.cs
Joseph Doherty a55153d7d5 Add configurable non-transparent OPC UA server redundancy
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>
2026-03-28 13:32:17 -04:00

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
}
}
}