using CliFx.Attributes;
using ZB.MOM.WW.OtOpcUa.Driver.Cli.Common;
namespace ZB.MOM.WW.OtOpcUa.Driver.AbCip.Cli;
///
/// Base for every AB CIP CLI command. Carries the libplctag endpoint options
/// (--gateway + --family) and exposes so each
/// command can synthesise an from CLI flags + its own
/// tag list.
///
public abstract class AbCipCommandBase : DriverCommandBase
{
[CommandOption("gateway", 'g', Description =
"Canonical AB CIP gateway: ab://host[:port]/cip-path. Port defaults to 44818 " +
"(EtherNet/IP). cip-path is family-specific: ControlLogix / CompactLogix need " +
"'1,0' to reach slot 0 of the CPU chassis; Micro800 takes an empty path; " +
"GuardLogix typically '1,0' same as ControlLogix.",
IsRequired = true)]
public string Gateway { get; init; } = default!;
[CommandOption("family", 'f', Description =
"ControlLogix / CompactLogix / Micro800 / GuardLogix (default ControlLogix).")]
public AbCipPlcFamily Family { get; init; } = AbCipPlcFamily.ControlLogix;
[CommandOption("timeout-ms", Description = "Per-operation timeout in ms (default 5000).")]
public int TimeoutMs { get; init; } = 5000;
///
/// PR abcip-3.2 — pin the device's CIP addressing mode for this CLI invocation.
/// Auto / Symbolic / Logical. Defaults to (resolves
/// to Symbolic until a future PR plumbs auto-detection). Logical against an
/// unsupported family (Micro800) silently falls back to Symbolic with a logged
/// warning, so passing --addressing-mode Logical across a mixed-family
/// fleet is safe.
///
[CommandOption("addressing-mode", Description =
"CIP addressing mode: Auto / Symbolic / Logical (default Auto, resolves to " +
"Symbolic). Logical uses CIP Symbol Object instance IDs after a one-time @tags " +
"walk; unsupported on Micro800 (silent fallback to Symbolic with warning).")]
public AddressingMode AddressingMode { get; init; } = AddressingMode.Auto;
///
/// PR abcip-5.1 — partner gateway URI for HSBY (Hot-Standby) paired chassis. When
/// supplied, every CLI command auto-enables HSBY role probing on the device options
/// so subcommands like hsby-status + diagnostics surface the active chassis
/// without extra flags. Unset for non-redundant deployments.
///
[CommandOption("partner", Description =
"Partner gateway URI for ControlLogix HSBY pair (e.g. ab://10.0.0.6/1,0). When " +
"set, the driver runs a second role-probe loop and the hsby-status command can " +
"surface which chassis is currently Active. Optional.")]
public string? Partner { get; init; }
///
public override TimeSpan Timeout
{
get => TimeSpan.FromMilliseconds(TimeoutMs);
init { /* driven by TimeoutMs */ }
}
///
/// Build an with the device + tag list a subclass
/// supplies. Probe + alarm projection are disabled — CLI runs are one-shot; the
/// probe loop would race the operator's own reads.
///
protected AbCipDriverOptions BuildOptions(IReadOnlyList tags) => new()
{
Devices = [new AbCipDeviceOptions(
HostAddress: Gateway,
PlcFamily: Family,
DeviceName: $"cli-{Family}",
AddressingMode: AddressingMode,
// PR abcip-5.1 — surface --partner through the device options so commands that
// use BuildOptions can take advantage of HSBY role probing without subclassing.
// Hsby auto-enables only when a partner was actually supplied; pre-5.1 invocations
// (no --partner) see exactly the legacy options shape.
PartnerHostAddress: Partner,
Hsby: string.IsNullOrWhiteSpace(Partner) ? null : new AbCipHsbyOptions
{
Enabled = true,
ProbeInterval = TimeSpan.FromSeconds(2),
})],
Tags = tags,
Timeout = Timeout,
Probe = new AbCipProbeOptions { Enabled = false },
EnableControllerBrowse = false,
EnableAlarmProjection = false,
};
///
/// Short instance id used in Serilog output so operators running the CLI against
/// multiple gateways in parallel can distinguish the logs.
///
protected string DriverInstanceId => $"abcip-cli-{Gateway}";
}