using CliFx; using CliFx.Attributes; using CliFx.Infrastructure; using Serilog; using ZB.MOM.WW.OtOpcUa.Client.Shared; using ZB.MOM.WW.OtOpcUa.Client.Shared.Helpers; using ZB.MOM.WW.OtOpcUa.Client.Shared.Models; namespace ZB.MOM.WW.OtOpcUa.Client.CLI; /// /// Abstract base class for all CLI commands providing common connection options and helpers. /// public abstract class CommandBase : ICommand { internal static readonly IOpcUaClientServiceFactory DefaultFactory = new OpcUaClientServiceFactory(); private readonly IOpcUaClientServiceFactory _factory; /// /// Initializes a CLI command with the shared OPC UA client factory used to create per-command sessions. /// /// The factory that creates the shared client service for the command execution. protected CommandBase(IOpcUaClientServiceFactory factory) { _factory = factory; } /// /// Gets the primary OPC UA endpoint URL the command should connect to. /// [CommandOption("url", 'u', Description = "OPC UA server endpoint URL", IsRequired = true)] public string Url { get; init; } = default!; /// /// Gets the optional username used for authenticated OPC UA sessions. /// [CommandOption("username", 'U', Description = "Username for authentication")] public string? Username { get; init; } /// /// Gets the optional password paired with for authenticated OPC UA sessions. /// [CommandOption("password", 'P', Description = "Password for authentication")] public string? Password { get; init; } /// /// Gets the transport security mode requested by the operator for this CLI session. /// [CommandOption("security", 'S', Description = "Transport security: none, sign, encrypt, signandencrypt (default: none)")] public string Security { get; init; } = "none"; /// /// Gets the optional comma-separated failover endpoints that should be tried if the primary endpoint is unavailable. /// [CommandOption("failover-urls", 'F', Description = "Comma-separated failover endpoint URLs for redundancy")] public string? FailoverUrls { get; init; } /// /// Gets a value indicating whether verbose Serilog output should be enabled for troubleshooting. /// [CommandOption("verbose", Description = "Enable verbose/debug logging")] public bool Verbose { get; init; } /// /// Executes the command-specific workflow against the configured OPC UA endpoint. /// /// The CLI console used for output and cancellation handling. public abstract ValueTask ExecuteAsync(IConsole console); /// /// Creates a from the common command options. /// protected ConnectionSettings CreateConnectionSettings() { var securityMode = SecurityModeMapper.FromString(Security); var failoverUrls = !string.IsNullOrWhiteSpace(FailoverUrls) ? FailoverUrlParser.Parse(Url, FailoverUrls) : null; var settings = new ConnectionSettings { EndpointUrl = Url, FailoverUrls = failoverUrls, Username = Username, Password = Password, SecurityMode = securityMode, AutoAcceptCertificates = true }; return settings; } /// /// Creates a new , connects it using the common options, /// and returns both the service and the connection info. /// /// The cancellation token that aborts connection setup for the command. protected async Task<(IOpcUaClientService Service, ConnectionInfo Info)> CreateServiceAndConnectAsync( CancellationToken ct) { var service = _factory.Create(); var settings = CreateConnectionSettings(); var info = await service.ConnectAsync(settings, ct); return (service, info); } /// /// Configures Serilog based on the verbose flag. /// protected void ConfigureLogging() { var config = new LoggerConfiguration(); if (Verbose) config.MinimumLevel.Debug() .WriteTo.Console(); else config.MinimumLevel.Warning() .WriteTo.Console(); Log.Logger = config.CreateLogger(); } }