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