64e3fbe035
v2-ci / build (push) Failing after 1m43s
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests) (push) Has been skipped
Adds <summary>, <param>, <typeparam>, and <inheritdoc/> tags to public members surfaced by commentchecker — resolves 5,847 of 5,869 issues (99.6%) across three /fixdocs passes.
98 lines
4.6 KiB
C#
98 lines
4.6 KiB
C#
using CliFx.Attributes;
|
|
using ZB.MOM.WW.OtOpcUa.Driver.Cli.Common;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Driver.AbCip.Cli;
|
|
|
|
/// <summary>
|
|
/// Base for every AB CIP CLI command. Carries the libplctag endpoint options
|
|
/// (<c>--gateway</c> + <c>--family</c>) and exposes <see cref="BuildOptions"/> so each
|
|
/// command can synthesise an <see cref="AbCipDriverOptions"/> from CLI flags + its own
|
|
/// tag list.
|
|
/// </summary>
|
|
public abstract class AbCipCommandBase : DriverCommandBase
|
|
{
|
|
/// <summary>Gets the canonical AB CIP gateway address.</summary>
|
|
[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!;
|
|
|
|
/// <summary>Gets the PLC family type.</summary>
|
|
[CommandOption("family", 'f', Description =
|
|
"ControlLogix / CompactLogix / Micro800 / GuardLogix (default ControlLogix).")]
|
|
public AbCipPlcFamily Family { get; init; } = AbCipPlcFamily.ControlLogix;
|
|
|
|
/// <summary>Gets the per-operation timeout in milliseconds.</summary>
|
|
[CommandOption("timeout-ms", Description = "Per-operation timeout in ms (default 5000).")]
|
|
public int TimeoutMs { get; init; } = 5000;
|
|
|
|
/// <inheritdoc />
|
|
/// <remarks>
|
|
/// The getter validates <see cref="TimeoutMs"/> (Driver.AbCip.Cli-004) — a zero or
|
|
/// negative <c>--timeout-ms</c> would otherwise propagate as a non-positive
|
|
/// <see cref="TimeSpan"/> into the driver. The <c>init</c> accessor is unreachable
|
|
/// because CliFx binds <see cref="TimeoutMs"/> rather than <c>Timeout</c>; it throws
|
|
/// <see cref="NotSupportedException"/> so an object-initializer assignment
|
|
/// (<c>new ReadCommand { Timeout = ... }</c>) fails fast instead of being silently
|
|
/// discarded (Driver.AbCip.Cli-006).
|
|
/// </remarks>
|
|
public override TimeSpan Timeout
|
|
{
|
|
get
|
|
{
|
|
if (TimeoutMs <= 0)
|
|
throw new CliFx.Exceptions.CommandException(
|
|
$"--timeout-ms must be > 0 (got {TimeoutMs}). " +
|
|
"Pick a positive number of milliseconds for the per-operation timeout.");
|
|
return TimeSpan.FromMilliseconds(TimeoutMs);
|
|
}
|
|
init => throw new NotSupportedException(
|
|
$"{nameof(AbCipCommandBase)}.{nameof(Timeout)} is derived from {nameof(TimeoutMs)} " +
|
|
"and cannot be assigned directly. Set TimeoutMs instead.");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Build an <see cref="AbCipDriverOptions"/> 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.
|
|
/// </summary>
|
|
/// <param name="tags">The list of tag definitions to include in the options.</param>
|
|
protected AbCipDriverOptions BuildOptions(IReadOnlyList<AbCipTagDefinition> tags) => new()
|
|
{
|
|
Devices = [new AbCipDeviceOptions(
|
|
HostAddress: Gateway,
|
|
PlcFamily: Family,
|
|
DeviceName: $"cli-{Family}")],
|
|
Tags = tags,
|
|
Timeout = Timeout,
|
|
Probe = new AbCipProbeOptions { Enabled = false },
|
|
EnableControllerBrowse = false,
|
|
EnableAlarmProjection = false,
|
|
};
|
|
|
|
/// <summary>
|
|
/// Short instance id used in Serilog output so operators running the CLI against
|
|
/// multiple gateways in parallel can distinguish the logs.
|
|
/// </summary>
|
|
protected string DriverInstanceId => $"abcip-cli-{Gateway}";
|
|
|
|
/// <summary>
|
|
/// Guards against <see cref="AbCipDataType.Structure"/> being passed to a command
|
|
/// that does not support UDT layouts. Call at the top of <c>ExecuteAsync</c> for any
|
|
/// command that accepts <c>--type</c> but cannot handle memberless Structure tags.
|
|
/// Throws a <see cref="CliFx.Exceptions.CommandException"/> if <paramref name="type"/>
|
|
/// is <see cref="AbCipDataType.Structure"/>.
|
|
/// </summary>
|
|
/// <param name="type">The data type to validate.</param>
|
|
protected static void RejectStructure(AbCipDataType type)
|
|
{
|
|
if (type == AbCipDataType.Structure)
|
|
throw new CliFx.Exceptions.CommandException(
|
|
"Structure (UDT) reads are out of scope for this command — those need an explicit " +
|
|
"member layout, which belongs in a real driver config.");
|
|
}
|
|
}
|