using CliFx.Attributes;
using CliFx.Exceptions;
using ZB.MOM.WW.OtOpcUa.Driver.Cli.Common;
namespace ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Cli;
///
/// Base for every TwinCAT CLI command. Carries the AMS target options
/// (--ams-net-id + --ams-port) + the per-call timeout. Commands that build
/// a single-device / single-tag from flag input inherit
/// from instead — that intermediate adds the
/// --poll-only flag and the BuildOptions helper.
///
public abstract class TwinCATCommandBase : DriverCommandBase
{
/// Gets the AMS Net ID of the target runtime.
[CommandOption("ams-net-id", 'n', Description =
"AMS Net ID of the target runtime (e.g. '192.168.1.40.1.1' or '127.0.0.1.1.1' for local).",
IsRequired = true)]
public string AmsNetId { get; init; } = default!;
/// Gets the AMS port number.
[CommandOption("ams-port", 'p', Description =
"AMS port. TwinCAT 3 PLC runtime defaults to 851; TwinCAT 2 uses 801.")]
public int AmsPort { get; init; } = 851;
/// Gets the per-operation timeout in milliseconds.
[CommandOption("timeout-ms", Description = "Per-operation timeout in ms (default 5000).")]
public int TimeoutMs { get; init; } = 5000;
///
/// Gets the per-operation timeout, projected from . The CliFx
/// init accessor required by the abstract base property is intentionally a
/// no-op: is the only source of truth, so any value an
/// `init` initialiser supplies to this property directly is silently
/// dropped. Do NOT add a backing field "fixing" the empty body — it would diverge
/// from and the two would drift on every refactor
/// (Driver.TwinCAT.Cli-007).
///
///
public override TimeSpan Timeout
{
get => TimeSpan.FromMilliseconds(TimeoutMs);
init { /* see XML summary — driven by TimeoutMs */ }
}
///
/// Gets the canonical TwinCAT gateway string the driver's TwinCATAmsAddress.TryParse
/// consumes — shape ads://{AmsNetId}:{AmsPort}.
///
protected string Gateway => $"ads://{AmsNetId}:{AmsPort}";
/// Gets the driver instance ID for this command.
protected string DriverInstanceId => $"twincat-cli-{AmsNetId}:{AmsPort}";
///
/// Validates the numeric options every TwinCAT CLI command shares (timeout + AMS port).
/// Subclasses override and call base.Validate() first to add their own range
/// checks. Throwing here surfaces a clean CliFx one-line error before the driver gets
/// a chance to fail with an opaque transport error (Driver.TwinCAT.Cli-001).
///
protected virtual void Validate()
{
if (TimeoutMs <= 0)
throw new CommandException(
$"--timeout-ms must be greater than 0 (got {TimeoutMs}).");
if (AmsPort is <= 0 or > 65535)
throw new CommandException(
$"--ams-port must be in the range 1..65535 (got {AmsPort}).");
}
// ---- Test hooks ----
// Protected members are exposed to the test assembly through these internal accessors so the
// test project can cover Gateway / DriverInstanceId composition + range validation without
// needing reflection on every assertion (Driver.TwinCAT.Cli-006).
/// Gets the gateway string for testing.
internal string GatewayForTest => Gateway;
/// Gets the driver instance ID for testing.
internal string DriverInstanceIdForTest => DriverInstanceId;
/// Validates the command for testing.
internal void ValidateForTest() => Validate();
}