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
@@ -14,11 +14,13 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Modbus.Cli.Commands;
[Command("probe", Description = "Verify the Modbus-TCP endpoint is reachable and speaks Modbus.")]
public sealed class ProbeCommand : ModbusCommandBase
{
/// <summary>Gets the holding-register address to use for the probe read.</summary>
[CommandOption("probe-address", Description =
"Holding-register address used as the cheap-read probe (default 0). Some PLCs lock " +
"register 0 — set this to a known-good address on your device.")]
public ushort ProbeAddress { get; init; }
/// <inheritdoc />
public override async ValueTask ExecuteAsync(IConsole console)
{
ConfigureLogging();
@@ -80,6 +82,9 @@ public sealed class ProbeCommand : ModbusCommandBase
/// <item><c>OK</c> — driver Healthy and snapshot Good.</item>
/// </list>
/// </summary>
/// <param name="state">The driver's health state.</param>
/// <param name="statusCode">The OPC UA status code from the probe read.</param>
/// <returns>A string describing the verdict.</returns>
public static string ComputeVerdict(DriverState state, uint statusCode)
{
// OPC UA StatusCode top 2 bits encode the quality class:
@@ -14,35 +14,43 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Modbus.Cli.Commands;
[Command("read", Description = "Read a single Modbus register or coil.")]
public sealed class ReadCommand : ModbusCommandBase
{
/// <summary>Gets the Modbus region to read from.</summary>
[CommandOption("region", 'r', Description =
"Coils / DiscreteInputs / InputRegisters / HoldingRegisters", IsRequired = true)]
public ModbusRegion Region { get; init; }
/// <summary>Gets the zero-based address within the region.</summary>
[CommandOption("address", 'a', Description =
"Zero-based address within the region.", IsRequired = true)]
public ushort Address { get; init; }
/// <summary>Gets the data type to read.</summary>
[CommandOption("type", 't', Description =
"Bool / Int16 / UInt16 / Int32 / UInt32 / Int64 / UInt64 / Float32 / Float64 / " +
"BitInRegister / String / Bcd16 / Bcd32", IsRequired = true)]
public ModbusDataType DataType { get; init; }
/// <summary>Gets the byte order for multi-register types.</summary>
[CommandOption("byte-order", Description =
"BigEndian (default, spec ABCD) or WordSwap (CDAB). Ignored for single-register types.")]
public ModbusByteOrder ByteOrder { get; init; } = ModbusByteOrder.BigEndian;
/// <summary>Gets the bit index for BitInRegister type.</summary>
[CommandOption("bit-index", Description =
"For type=BitInRegister: bit 0-15 LSB-first.")]
public byte BitIndex { get; init; }
/// <summary>Gets the string length for String type.</summary>
[CommandOption("string-length", Description =
"For type=String: character count (2 per register, rounded up).")]
public ushort StringLength { get; init; }
/// <summary>Gets the byte order for string values.</summary>
[CommandOption("string-byte-order", Description =
"For type=String: HighByteFirst (standard) or LowByteFirst (DirectLOGIC et al).")]
public ModbusStringByteOrder StringByteOrder { get; init; } = ModbusStringByteOrder.HighByteFirst;
/// <inheritdoc />
public override async ValueTask ExecuteAsync(IConsole console)
{
ConfigureLogging();
@@ -86,6 +94,10 @@ public sealed class ReadCommand : ModbusCommandBase
/// (<c>HR[100]</c>, <c>Coil[5]</c>, <c>IR[42]</c>) — the driver treats the name
/// purely as a lookup key, so any stable string works.
/// </summary>
/// <param name="region">The Modbus region (Coils, DiscreteInputs, InputRegisters, or HoldingRegisters).</param>
/// <param name="address">The zero-based address within the region.</param>
/// <param name="type">The Modbus data type being read.</param>
/// <returns>A human-readable tag name.</returns>
internal static string SynthesiseTagName(
ModbusRegion region, ushort address, ModbusDataType type)
{
@@ -14,23 +14,28 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Modbus.Cli.Commands;
[Command("subscribe", Description = "Watch a Modbus register via polled subscription until Ctrl+C.")]
public sealed class SubscribeCommand : ModbusCommandBase
{
/// <summary>Gets or sets the Modbus register region (Coils, DiscreteInputs, InputRegisters, or HoldingRegisters).</summary>
[CommandOption("region", 'r', Description =
"Coils / DiscreteInputs / InputRegisters / HoldingRegisters", IsRequired = true)]
public ModbusRegion Region { get; init; }
/// <summary>Gets or sets the zero-based address within the region.</summary>
[CommandOption("address", 'a', Description = "Zero-based address within the region.", IsRequired = true)]
public ushort Address { get; init; }
/// <summary>Gets or sets the data type (Bool, Int16, UInt16, Int32, UInt32, Int64, UInt64, Float32, Float64, BitInRegister, String, Bcd16, Bcd32).</summary>
[CommandOption("type", 't', Description =
"Bool / Int16 / UInt16 / Int32 / UInt32 / Int64 / UInt64 / Float32 / Float64 / " +
"BitInRegister / String / Bcd16 / Bcd32", IsRequired = true)]
public ModbusDataType DataType { get; init; }
/// <summary>Gets or sets the publishing interval in milliseconds.</summary>
[CommandOption("interval-ms", 'i', Description =
"Publishing interval in milliseconds (default 1000). The PollGroupEngine enforces " +
"a floor of ~250ms; values below it get rounded up.")]
public int IntervalMs { get; init; } = 1000;
/// <summary>Gets or sets the byte order (BigEndian or WordSwap).</summary>
[CommandOption("byte-order", Description =
"BigEndian (default) or WordSwap.")]
public ModbusByteOrder ByteOrder { get; init; } = ModbusByteOrder.BigEndian;
@@ -38,18 +43,22 @@ public sealed class SubscribeCommand : ModbusCommandBase
// Driver.Modbus.Cli-001: subscribe previously lacked these three options that read and
// write both expose. Without them, BitInRegister always watches bit 0 and String runs with
// StringLength=0, silently producing wrong results for any subscriber using those types.
/// <summary>Gets or sets the bit index for type=BitInRegister (0-15, LSB-first).</summary>
[CommandOption("bit-index", Description =
"For type=BitInRegister: which bit of the holding register (0-15, LSB-first).")]
public byte BitIndex { get; init; }
/// <summary>Gets or sets the string length for type=String (character count, 2 per register).</summary>
[CommandOption("string-length", Description =
"For type=String: character count (2 per register, rounded up).")]
public ushort StringLength { get; init; }
/// <summary>Gets or sets the string byte order for type=String (HighByteFirst or LowByteFirst).</summary>
[CommandOption("string-byte-order", Description =
"For type=String: HighByteFirst (standard) or LowByteFirst (DirectLOGIC).")]
public ModbusStringByteOrder StringByteOrder { get; init; } = ModbusStringByteOrder.HighByteFirst;
/// <inheritdoc />
public override async ValueTask ExecuteAsync(IConsole console)
{
ConfigureLogging();
@@ -16,41 +16,51 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Modbus.Cli.Commands;
[Command("write", Description = "Write a single Modbus coil or holding register.")]
public sealed class WriteCommand : ModbusCommandBase
{
/// <summary>Gets the Modbus region to write to.</summary>
[CommandOption("region", 'r', Description =
"Coils or HoldingRegisters (the only writable regions per the protocol spec).",
IsRequired = true)]
public ModbusRegion Region { get; init; }
/// <summary>Gets the zero-based address within the region.</summary>
[CommandOption("address", 'a', Description =
"Zero-based address within the region.", IsRequired = true)]
public ushort Address { get; init; }
/// <summary>Gets the data type of the value to write.</summary>
[CommandOption("type", 't', Description =
"Bool / Int16 / UInt16 / Int32 / UInt32 / Int64 / UInt64 / Float32 / Float64 / " +
"BitInRegister / String / Bcd16 / Bcd32", IsRequired = true)]
public ModbusDataType DataType { get; init; }
/// <summary>Gets the value string to write and parse.</summary>
[CommandOption("value", 'v', Description =
"Value to write. Parsed per --type (booleans accept true/false/0/1).",
IsRequired = true)]
public string Value { get; init; } = default!;
/// <summary>Gets the byte order for multi-register values.</summary>
[CommandOption("byte-order", Description =
"BigEndian (default, ABCD) or WordSwap (CDAB). Ignored for single-register types.")]
public ModbusByteOrder ByteOrder { get; init; } = ModbusByteOrder.BigEndian;
/// <summary>Gets the bit index for BitInRegister type.</summary>
[CommandOption("bit-index", Description =
"For type=BitInRegister: which bit of the holding register (0-15, LSB-first).")]
public byte BitIndex { get; init; }
/// <summary>Gets the string length for String type.</summary>
[CommandOption("string-length", Description =
"For type=String: character count (2 per register, rounded up).")]
public ushort StringLength { get; init; }
/// <summary>Gets the byte order for string characters.</summary>
[CommandOption("string-byte-order", Description =
"For type=String: HighByteFirst (standard) or LowByteFirst (DirectLOGIC).")]
public ModbusStringByteOrder StringByteOrder { get; init; } = ModbusStringByteOrder.HighByteFirst;
/// <summary>Executes the write command.</summary>
/// <inheritdoc />
public override async ValueTask ExecuteAsync(IConsole console)
{
ConfigureLogging();
@@ -106,10 +116,13 @@ public sealed class WriteCommand : ModbusCommandBase
}
/// <summary>
/// Parse the operator's <c>--value</c> string into the CLR type the driver expects
/// for the declared <see cref="ModbusDataType"/>. Uses invariant culture everywhere
/// Parses the operator's <c>--value</c> string into the CLR type the driver expects
/// for the declared data type. Uses invariant culture everywhere
/// so <c>3.14</c> and <c>3,14</c> don't swap meaning between runs.
/// </summary>
/// <param name="raw">The raw value string from the command line.</param>
/// <param name="type">The data type to parse into.</param>
/// <returns>The parsed value in the appropriate CLR type.</returns>
internal static object ParseValue(string raw, ModbusDataType type) => type switch
{
ModbusDataType.Bool or ModbusDataType.BitInRegister => ParseBool(raw),
@@ -12,18 +12,23 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Modbus.Cli;
/// </summary>
public abstract class ModbusCommandBase : DriverCommandBase
{
/// <summary>Gets the Modbus-TCP server hostname or IP address.</summary>
[CommandOption("host", 'h', Description = "Modbus-TCP server hostname or IP", IsRequired = true)]
public string Host { get; init; } = default!;
/// <summary>Gets the Modbus-TCP port number.</summary>
[CommandOption("port", 'p', Description = "Modbus-TCP port (default 502)")]
public int Port { get; init; } = 502;
/// <summary>Gets the Modbus unit ID (slave ID).</summary>
[CommandOption("unit-id", 'U', Description = "Modbus unit / slave ID (1-247, default 1)")]
public byte UnitId { get; init; } = 1;
/// <summary>Gets the per-PDU timeout in milliseconds.</summary>
[CommandOption("timeout-ms", Description = "Per-PDU timeout in milliseconds (default 2000)")]
public int TimeoutMs { get; init; } = 2000;
/// <summary>Gets a value indicating whether to disable automatic reconnection.</summary>
[CommandOption("disable-reconnect", Description =
"Disable the built-in mid-transaction reconnect-and-retry. Matches the driver's " +
"AutoReconnect=false setting — use when diagnosing socket teardown behaviour.")]
@@ -42,6 +47,7 @@ public abstract class ModbusCommandBase : DriverCommandBase
/// disabled — CLI runs are one-shot, the probe loop would race the operator's
/// command against its own keep-alive reads.
/// </summary>
/// <param name="tags">The tag definitions to include in the options.</param>
protected ModbusDriverOptions BuildOptions(IReadOnlyList<ModbusTagDefinition> tags) => new()
{
Host = Host,