using CliFx.Attributes; using ZB.MOM.WW.OtOpcUa.Core.Abstractions; using ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.PlcFamilies; using ZB.MOM.WW.OtOpcUa.Driver.Cli.Common; namespace ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Cli; /// /// Base for every AB Legacy CLI command. Carries the PCCC-specific endpoint options /// (--gateway + --plc-type) on top of 's /// shared verbose + timeout + logging helpers. /// public abstract class AbLegacyCommandBase : DriverCommandBase { /// Gets the gateway endpoint for AB Legacy connection. [CommandOption("gateway", 'g', Description = "Canonical AB Legacy gateway: ab://host[:port]/cip-path. Port defaults to 44818. " + "cip-path depends on the family: SLC 5/05 + PLC-5 typically '1,0'; MicroLogix " + "1100/1400 takes an empty path (direct EIP, no backplane).", IsRequired = true)] public string Gateway { get; init; } = default!; /// Gets the PLC family type. [CommandOption("plc-type", 'P', Description = "Slc500 / MicroLogix / Plc5 / LogixPccc (default Slc500).")] public AbLegacyPlcFamily PlcType { get; init; } = AbLegacyPlcFamily.Slc500; /// Gets the transport stack used to reach the PLC. [CommandOption("transport", Description = "Transport stack: default, native or libplctag. default = libplctag for every --plc-type; native is " + "opt-in (wire-proven only for N-file reads/writes and an F8 read; PLC-5 native is N-file reads only, " + "LogixPccc unverified).")] public AbLegacyCliTransport Transport { get; init; } = AbLegacyCliTransport.Default; /// /// Runs a one-shot command's driver work so a Ctrl-C (cancellation of ) prints /// Cancelled. instead of an unhandled stack trace (review 07 U7). /// Any other exception — and an OCE the operator did not ask for — still propagates. /// /// The CLI console. /// The console's Ctrl-C token. /// The command body. /// A task that completes when the body has finished or was cancelled. internal static async ValueTask RunCancellableAsync(CliFx.Infrastructure.IConsole console, CancellationToken ct, Func body) { try { await body(); } catch (OperationCanceledException) when (ct.IsCancellationRequested) { await console.Error.WriteLineAsync("Cancelled."); } } /// Gets the per-operation timeout in milliseconds. [CommandOption("timeout-ms", Description = "Per-operation timeout in ms (default 5000).")] public int TimeoutMs { get; init; } = 5000; /// public override TimeSpan Timeout { get => TimeSpan.FromMilliseconds(TimeoutMs); init { /* driven by TimeoutMs */ } } /// /// Build an with the device + tag list a subclass /// supplies. The CLI authors typed s from operator flags; /// v3 delivers tags to the driver as records, so each typed def is /// serialised back to its TagConfig blob via /// (RawPath = the def's Name, /// DeviceName = the def's DeviceHostAddress — the CLI's single device). Probe disabled /// for CLI one-shot runs. /// /// The tag definitions to include in the options. /// Configured AB Legacy driver options. protected AbLegacyDriverOptions BuildOptions(IReadOnlyList tags) => new() { Devices = [new AbLegacyDeviceOptions( HostAddress: Gateway, PlcFamily: PlcType, DeviceName: $"cli-{PlcType}")], RawTags = [.. tags.Select(t => new RawTagEntry( t.Name, AbLegacyTagDefinitionFactory.ToTagConfig(t), t.WriteIdempotent, DeviceName: t.DeviceHostAddress))], Timeout = Timeout, Transport = Transport.ToConfigured(), Probe = new AbLegacyProbeOptions { Enabled = false }, }; /// Gets the driver instance identifier for CLI operations. protected string DriverInstanceId => $"ablegacy-cli-{Gateway}"; }