docs: backfill XML documentation across 756 files
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.
This commit is contained in:
Joseph Doherty
2026-05-28 08:10:17 -04:00
parent f9fc7dd2e1
commit 64e3fbe035
756 changed files with 9876 additions and 96 deletions
@@ -19,16 +19,19 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Cli.Commands;
[Command("browse", Description = "Enumerate controller symbols via the driver's DiscoverAsync walk.")]
public sealed class BrowseCommand : TwinCATCommandBase
{
/// <summary>Gets or sets the case-sensitive instance-path prefix to filter on.</summary>
[CommandOption("prefix", Description =
"Case-sensitive instance-path prefix to filter on (e.g. 'GVL_Fixture' or " +
"'MAIN.'). Empty (default) prints everything.")]
public string? Prefix { get; init; }
/// <summary>Gets or sets the maximum number of symbols to print.</summary>
[CommandOption("max", Description =
"Maximum number of symbols to print. 0 = unbounded (default 500 for large " +
"controllers — flat-mode symbol counts easily top 10k).")]
public int Max { get; init; } = 500;
/// <inheritdoc />
public override async ValueTask ExecuteAsync(IConsole console)
{
Validate();
@@ -85,6 +88,8 @@ public sealed class BrowseCommand : TwinCATCommandBase
/// <see cref="StringComparison.Ordinal"/> — TwinCAT identifiers are case-sensitive on
/// the wire, so a relaxed match would be misleading.
/// </summary>
/// <param name="source">The source collection to filter.</param>
/// <param name="prefix">The prefix to filter on, or null to keep everything.</param>
internal static List<(string BrowseName, DriverAttributeInfo Info)> FilterByPrefix(
IReadOnlyList<(string BrowseName, DriverAttributeInfo Info)> source, string? prefix)
=> source
@@ -95,6 +100,8 @@ public sealed class BrowseCommand : TwinCATCommandBase
/// Cap-to-max projection. <paramref name="max"/> &lt;= 0 means unbounded, otherwise the
/// min of <paramref name="matchedCount"/> and <paramref name="max"/>.
/// </summary>
/// <param name="matchedCount">The number of matched items.</param>
/// <param name="max">The maximum number to show, or 0 for unbounded.</param>
internal static int PrintLimit(int matchedCount, int max)
=> max <= 0 ? matchedCount : Math.Min(max, matchedCount);
@@ -104,31 +111,42 @@ public sealed class BrowseCommand : TwinCATCommandBase
/// written from at least one ACL tier, so the CLI labels it RW. The real per-tier
/// authorization is enforced server-side.
/// </summary>
/// <param name="info">The attribute info to label.</param>
internal static string AccessTag(DriverAttributeInfo info)
=> info.SecurityClass == SecurityClassification.ViewOnly ? "RO" : "RW";
/// <summary>An address space builder that collects variables for enumeration.</summary>
internal sealed class CollectingAddressSpaceBuilder : IAddressSpaceBuilder
{
/// <summary>Gets the collected variables.</summary>
public List<(string BrowseName, DriverAttributeInfo Info)> Variables { get; } = [];
/// <inheritdoc />
public IAddressSpaceBuilder Folder(string browseName, string displayName) => this;
/// <inheritdoc />
public IVariableHandle Variable(string browseName, string displayName, DriverAttributeInfo info)
{
Variables.Add((browseName, info));
return new Handle(info.FullName);
}
/// <inheritdoc />
public void AddProperty(string name, DriverDataType type, object? value) { }
/// <summary>A variable handle that stores the full reference.</summary>
private sealed class Handle(string fullRef) : IVariableHandle
{
/// <inheritdoc />
public string FullReference => fullRef;
/// <inheritdoc />
public IAlarmConditionSink MarkAsAlarmCondition(AlarmConditionInfo info) => new NullSink();
}
/// <summary>A null sink that ignores alarm condition transitions.</summary>
private sealed class NullSink : IAlarmConditionSink
{
/// <inheritdoc />
public void OnTransition(AlarmEventArgs args) { }
}
}
@@ -13,6 +13,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Cli.Commands;
[Command("probe", Description = "Verify the TwinCAT runtime is reachable and a sample symbol reads.")]
public sealed class ProbeCommand : TwinCATTagCommandBase
{
/// <summary>Gets the symbol path to probe in the TwinCAT runtime.</summary>
[CommandOption("symbol", 's', Description =
"Symbol path to probe. System-global examples: " +
"'TwinCAT_SystemInfoVarList._AppInfo.OnlineChangeCnt', 'MAIN.bRunning'. " +
@@ -20,11 +21,13 @@ public sealed class ProbeCommand : TwinCATTagCommandBase
IsRequired = true)]
public string SymbolPath { get; init; } = default!;
/// <summary>Gets the data type to use for reading the symbol.</summary>
[CommandOption("type", 't', Description =
"Bool / SInt / USInt / Int / UInt / DInt / UDInt / LInt / ULInt / Real / LReal / " +
"String / WString / Time / Date / DateTime / TimeOfDay (default DInt).")]
public TwinCATDataType DataType { get; init; } = TwinCATDataType.DInt;
/// <inheritdoc />
public override async ValueTask ExecuteAsync(IConsole console)
{
Validate();
@@ -11,17 +11,20 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Cli.Commands;
[Command("read", Description = "Read a single TwinCAT symbol.")]
public sealed class ReadCommand : TwinCATTagCommandBase
{
/// <summary>Gets or sets the TwinCAT symbol path to read.</summary>
[CommandOption("symbol", 's', Description =
"Symbol path. Program scope: 'MAIN.bStart'. Global: 'GVL.Counter'. " +
"Nested UDT member: 'Motor1.Status.Running'. Array element: 'Recipe[3]'.",
IsRequired = true)]
public string SymbolPath { get; init; } = default!;
/// <summary>Gets or sets the data type of the symbol being read.</summary>
[CommandOption("type", 't', Description =
"Bool / SInt / USInt / Int / UInt / DInt / UDInt / LInt / ULInt / Real / LReal / " +
"String / WString / Time / Date / DateTime / TimeOfDay (default DInt).")]
public TwinCATDataType DataType { get; init; } = TwinCATDataType.DInt;
/// <inheritdoc />
public override async ValueTask ExecuteAsync(IConsole console)
{
Validate();
@@ -50,6 +53,10 @@ public sealed class ReadCommand : TwinCATTagCommandBase
}
}
/// <summary>Synthesizes an internal tag name from a symbol path and data type.</summary>
/// <param name="symbolPath">The TwinCAT symbol path.</param>
/// <param name="type">The data type of the symbol.</param>
/// <returns>A synthesized tag name combining the symbol path and type.</returns>
internal static string SynthesiseTagName(string symbolPath, TwinCATDataType type)
=> $"{symbolPath}:{type}";
}
@@ -13,17 +13,21 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Cli.Commands;
[Command("subscribe", Description = "Watch a TwinCAT symbol via ADS notification or poll, until Ctrl+C.")]
public sealed class SubscribeCommand : TwinCATTagCommandBase
{
/// <summary>Gets the TwinCAT symbol path to subscribe to.</summary>
[CommandOption("symbol", 's', Description = "Symbol path — same format as `read`.", IsRequired = true)]
public string SymbolPath { get; init; } = default!;
/// <summary>Gets the TwinCAT data type of the symbol.</summary>
[CommandOption("type", 't', Description =
"Bool / SInt / USInt / Int / UInt / DInt / UDInt / LInt / ULInt / Real / LReal / " +
"String / WString / Time / Date / DateTime / TimeOfDay (default DInt).")]
public TwinCATDataType DataType { get; init; } = TwinCATDataType.DInt;
/// <summary>Gets the publishing interval in milliseconds.</summary>
[CommandOption("interval-ms", 'i', Description = "Publishing interval ms (default 1000).")]
public int IntervalMs { get; init; } = 1000;
/// <inheritdoc />
protected override void Validate()
{
base.Validate();
@@ -32,6 +36,7 @@ public sealed class SubscribeCommand : TwinCATTagCommandBase
$"--interval-ms must be greater than 0 (got {IntervalMs}).");
}
/// <inheritdoc />
public override async ValueTask ExecuteAsync(IConsole console)
{
Validate();
@@ -113,6 +118,8 @@ public sealed class SubscribeCommand : TwinCATTagCommandBase
/// different format — anything else means we landed on the poll loop. Internal so the
/// test assembly can cover the mapping without spinning a real driver.
/// </summary>
/// <param name="handle">The subscription handle to describe.</param>
/// <returns>A description of the subscription mechanism ("ADS notification" or "polling").</returns>
internal static string DescribeMechanism(ISubscriptionHandle handle) =>
handle.DiagnosticId.StartsWith("twincat-native-sub-", StringComparison.Ordinal)
? "ADS notification"
@@ -13,20 +13,24 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Cli.Commands;
[Command("write", Description = "Write a single TwinCAT symbol.")]
public sealed class WriteCommand : TwinCATTagCommandBase
{
/// <summary>Gets the symbol path to write.</summary>
[CommandOption("symbol", 's', Description =
"Symbol path — same format as `read`.", IsRequired = true)]
public string SymbolPath { get; init; } = default!;
/// <summary>Gets the data type of the symbol value.</summary>
[CommandOption("type", 't', Description =
"Bool / SInt / USInt / Int / UInt / DInt / UDInt / LInt / ULInt / Real / LReal / " +
"String / WString / Time / Date / DateTime / TimeOfDay (default DInt).")]
public TwinCATDataType DataType { get; init; } = TwinCATDataType.DInt;
/// <summary>Gets the value to write.</summary>
[CommandOption("value", 'v', Description =
"Value to write. Parsed per --type (booleans accept true/false/1/0).",
IsRequired = true)]
public string Value { get; init; } = default!;
/// <inheritdoc />
public override async ValueTask ExecuteAsync(IConsole console)
{
Validate();
@@ -63,6 +67,8 @@ public sealed class WriteCommand : TwinCATTagCommandBase
}
/// <summary>Parse <c>--value</c> per <see cref="TwinCATDataType"/>, invariant culture.</summary>
/// <param name="raw">The raw string value to parse.</param>
/// <param name="type">The target TwinCAT data type.</param>
internal static object ParseValue(string raw, TwinCATDataType type) => type switch
{
TwinCATDataType.Bool => ParseBool(raw),
@@ -13,27 +13,31 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Cli;
/// </summary>
public abstract class TwinCATCommandBase : DriverCommandBase
{
/// <summary>Gets the AMS Net ID of the target runtime.</summary>
[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!;
/// <summary>Gets the AMS port number.</summary>
[CommandOption("ams-port", 'p', Description =
"AMS port. TwinCAT 3 PLC runtime defaults to 851; TwinCAT 2 uses 801.")]
public int AmsPort { get; init; } = 851;
/// <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;
/// <summary>
/// The per-operation timeout, projected from <see cref="TimeoutMs"/>. The CliFx
/// Gets the per-operation timeout, projected from <see cref="TimeoutMs"/>. The CliFx
/// <c>init</c> accessor required by the abstract base property is intentionally a
/// no-op: <see cref="TimeoutMs"/> is the only source of truth, so any value an
/// `init` initialiser supplies to <see cref="Timeout"/> directly is silently
/// `init` initialiser supplies to this property directly is silently
/// dropped. Do NOT add a backing field "fixing" the empty body — it would diverge
/// from <see cref="TimeoutMs"/> and the two would drift on every refactor
/// (Driver.TwinCAT.Cli-007).
/// </summary>
/// <inheritdoc />
public override TimeSpan Timeout
{
get => TimeSpan.FromMilliseconds(TimeoutMs);
@@ -41,11 +45,12 @@ public abstract class TwinCATCommandBase : DriverCommandBase
}
/// <summary>
/// Canonical TwinCAT gateway string the driver's <c>TwinCATAmsAddress.TryParse</c>
/// Gets the canonical TwinCAT gateway string the driver's <c>TwinCATAmsAddress.TryParse</c>
/// consumes — shape <c>ads://{AmsNetId}:{AmsPort}</c>.
/// </summary>
protected string Gateway => $"ads://{AmsNetId}:{AmsPort}";
/// <summary>Gets the driver instance ID for this command.</summary>
protected string DriverInstanceId => $"twincat-cli-{AmsNetId}:{AmsPort}";
/// <summary>
@@ -68,7 +73,12 @@ public abstract class TwinCATCommandBase : DriverCommandBase
// 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).
/// <summary>Gets the gateway string for testing.</summary>
internal string GatewayForTest => Gateway;
/// <summary>Gets the driver instance ID for testing.</summary>
internal string DriverInstanceIdForTest => DriverInstanceId;
/// <summary>Validates the command for testing.</summary>
internal void ValidateForTest() => Validate();
}
@@ -12,6 +12,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Cli;
/// </summary>
public abstract class TwinCATTagCommandBase : TwinCATCommandBase
{
/// <summary>Gets or sets a value indicating whether to use polling instead of native ADS notifications.</summary>
[CommandOption("poll-only", Description =
"Disable native ADS notifications and fall through to the shared PollGroupEngine " +
"(same as setting UseNativeNotifications=false in a real driver config).")]
@@ -22,6 +23,7 @@ public abstract class TwinCATTagCommandBase : TwinCATCommandBase
/// the tag list a subclass supplies. Probe disabled, controller-browse disabled,
/// native notifications toggled by <see cref="PollOnly"/>.
/// </summary>
/// <param name="tags">Tag definitions for the driver.</param>
protected TwinCATDriverOptions BuildOptions(IReadOnlyList<TwinCATTagDefinition> tags) => new()
{
Devices = [new TwinCATDeviceOptions(
@@ -35,6 +37,8 @@ public abstract class TwinCATTagCommandBase : TwinCATCommandBase
};
// ---- Test hook ----
/// <summary>Test hook that exposes BuildOptions for unit testing.</summary>
/// <param name="tags">Tag definitions for the driver.</param>
internal TwinCATDriverOptions BuildOptionsForTest(IReadOnlyList<TwinCATTagDefinition> tags)
=> BuildOptions(tags);
}