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