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
@@ -10,7 +10,14 @@ namespace ZB.MOM.WW.OtOpcUa.Commons.Engines;
/// </summary>
public interface IAlarmActorStateStore
{
/// <summary>Loads the persisted state snapshot for an alarm actor.</summary>
/// <param name="alarmId">The alarm identifier.</param>
/// <param name="ct">Cancellation token.</param>
/// <returns>The alarm state snapshot if found; null if the alarm has no persisted state.</returns>
Task<AlarmActorStateSnapshot?> LoadAsync(string alarmId, CancellationToken ct);
/// <summary>Saves the alarm actor state snapshot.</summary>
/// <param name="snapshot">The state snapshot to persist.</param>
/// <param name="ct">Cancellation token.</param>
Task SaveAsync(AlarmActorStateSnapshot snapshot, CancellationToken ct);
}
@@ -34,8 +41,14 @@ public sealed class NullAlarmActorStateStore : IAlarmActorStateStore
{
public static readonly NullAlarmActorStateStore Instance = new();
private NullAlarmActorStateStore() { }
/// <summary>Always returns null, indicating no persisted state.</summary>
/// <param name="alarmId">The alarm identifier (unused).</param>
/// <param name="ct">Cancellation token (unused).</param>
public Task<AlarmActorStateSnapshot?> LoadAsync(string alarmId, CancellationToken ct) =>
Task.FromResult<AlarmActorStateSnapshot?>(null);
/// <summary>Completes immediately without persisting anything.</summary>
/// <param name="snapshot">The state snapshot (ignored).</param>
/// <param name="ct">Cancellation token (unused).</param>
public Task SaveAsync(AlarmActorStateSnapshot snapshot, CancellationToken ct) =>
Task.CompletedTask;
}
@@ -8,6 +8,11 @@ namespace ZB.MOM.WW.OtOpcUa.Commons.Engines;
/// </summary>
public interface IScriptedAlarmEvaluator
{
/// <summary>Evaluates an alarm predicate against the provided dependencies.</summary>
/// <param name="alarmId">The unique identifier of the alarm being evaluated.</param>
/// <param name="predicate">The predicate expression to evaluate.</param>
/// <param name="dependencies">Read-only dictionary of variable names to values for predicate evaluation.</param>
/// <returns>Result containing success flag, alarm active state, and optional failure reason.</returns>
ScriptedAlarmEvalResult Evaluate(string alarmId, string predicate, IReadOnlyDictionary<string, object?> dependencies);
}
@@ -15,7 +20,14 @@ public interface IScriptedAlarmEvaluator
/// <c>Success</c> is true; on failure the caller should keep the prior state and log Reason.</summary>
public sealed record ScriptedAlarmEvalResult(bool Success, bool Active, string? Reason)
{
/// <summary>Creates a successful alarm evaluation result with the given active state.</summary>
/// <param name="active">Whether the alarm condition is active.</param>
/// <returns>A successful evaluation result.</returns>
public static ScriptedAlarmEvalResult Ok(bool active) => new(true, active, null);
/// <summary>Creates a failed alarm evaluation result with the given reason.</summary>
/// <param name="reason">Description of the evaluation failure cause.</param>
/// <returns>A failed evaluation result.</returns>
public static ScriptedAlarmEvalResult Failure(string reason) => new(false, false, reason);
}
@@ -25,6 +37,11 @@ public sealed class NullScriptedAlarmEvaluator : IScriptedAlarmEvaluator
{
public static readonly NullScriptedAlarmEvaluator Instance = new();
private NullScriptedAlarmEvaluator() { }
/// <summary>Returns an inactive alarm result for every evaluation (safe no-op behavior).</summary>
/// <param name="alarmId">The alarm identifier (ignored).</param>
/// <param name="predicate">The predicate expression (ignored).</param>
/// <param name="dependencies">The variable dependencies (ignored).</param>
/// <returns>Always returns an inactive alarm result.</returns>
public ScriptedAlarmEvalResult Evaluate(string alarmId, string predicate, IReadOnlyDictionary<string, object?> dependencies)
=> ScriptedAlarmEvalResult.Ok(active: false);
}
@@ -13,6 +13,10 @@ public interface IVirtualTagEvaluator
/// <paramref name="dependencies"/>. Implementations must not throw — script failures
/// are reported via <see cref="VirtualTagEvalResult.Failure"/>.
/// </summary>
/// <param name="virtualTagId">The unique identifier of the virtual tag being evaluated.</param>
/// <param name="expression">The expression string to evaluate.</param>
/// <param name="dependencies">Read-only dictionary of variable names to values for expression evaluation.</param>
/// <returns>Result containing success flag, evaluated value, and optional failure reason.</returns>
VirtualTagEvalResult Evaluate(string virtualTagId, string expression, IReadOnlyDictionary<string, object?> dependencies);
}
@@ -21,7 +25,15 @@ public interface IVirtualTagEvaluator
public sealed record VirtualTagEvalResult(bool Success, object? Value, string? Reason)
{
public static readonly VirtualTagEvalResult NoChange = new(true, null, "no-change");
/// <summary>Creates a successful evaluation result with the given value.</summary>
/// <param name="value">The evaluated value.</param>
/// <returns>A successful evaluation result.</returns>
public static VirtualTagEvalResult Ok(object? value) => new(true, value, null);
/// <summary>Creates a failed evaluation result with the given reason.</summary>
/// <param name="reason">Description of the failure cause.</param>
/// <returns>A failed evaluation result.</returns>
public static VirtualTagEvalResult Failure(string reason) => new(false, null, reason);
}
@@ -31,6 +43,11 @@ public sealed class NullVirtualTagEvaluator : IVirtualTagEvaluator
{
public static readonly NullVirtualTagEvaluator Instance = new();
private NullVirtualTagEvaluator() { }
/// <summary>Returns <see cref="VirtualTagEvalResult.NoChange"/> for every evaluation.</summary>
/// <param name="virtualTagId">The virtual tag identifier (ignored).</param>
/// <param name="expression">The expression string (ignored).</param>
/// <param name="dependencies">The variable dependencies (ignored).</param>
/// <returns>Always returns <see cref="VirtualTagEvalResult.NoChange"/>.</returns>
public VirtualTagEvalResult Evaluate(string virtualTagId, string expression, IReadOnlyDictionary<string, object?> dependencies)
=> VirtualTagEvalResult.NoChange;
}
@@ -9,5 +9,9 @@ namespace ZB.MOM.WW.OtOpcUa.Commons.Interfaces;
/// </summary>
public interface IAdminOperationsClient
{
/// <summary>Starts a new deployment on the cluster-singleton admin operations actor.</summary>
/// <param name="createdBy">The user or system identifier triggering the deployment.</param>
/// <param name="ct">The cancellation token.</param>
/// <returns>A task representing the asynchronous operation containing the deployment start result.</returns>
Task<StartDeploymentResult> StartDeploymentAsync(string createdBy, CancellationToken ct);
}
@@ -10,11 +10,23 @@ namespace ZB.MOM.WW.OtOpcUa.Commons.Interfaces;
/// </summary>
public interface IClusterRoleInfo
{
/// <summary>Gets the local cluster node identifier.</summary>
NodeId LocalNode { get; }
/// <summary>Gets the set of roles assigned to the local node.</summary>
IReadOnlySet<string> LocalRoles { get; }
/// <summary>Checks if the local node has the specified role.</summary>
/// <param name="role">Role name to check.</param>
/// <returns>True if the local node has the role; otherwise, false.</returns>
bool HasRole(string role);
/// <summary>Gets all nodes assigned to the specified role.</summary>
/// <param name="role">Role name to query.</param>
/// <returns>List of node identifiers with the role.</returns>
IReadOnlyList<NodeId> MembersWithRole(string role);
/// <summary>Gets the leader node for the specified role, or null if no leader is elected.</summary>
/// <param name="role">Role name to query.</param>
/// <returns>The leader node identifier, or null if no leader exists.</returns>
NodeId? RoleLeader(string role);
/// <summary>Occurs when the leader of a role changes.</summary>
event EventHandler<RoleLeaderChangedEventArgs>? RoleLeaderChanged;
}
@@ -8,5 +8,8 @@ namespace ZB.MOM.WW.OtOpcUa.Commons.Interfaces;
/// </summary>
public interface IFleetDiagnosticsClient
{
/// <summary>Gets diagnostics for the specified node.</summary>
/// <param name="nodeId">The node ID to retrieve diagnostics for.</param>
/// <param name="ct">The cancellation token.</param>
Task<NodeDiagnosticsSnapshot> GetDiagnosticsAsync(NodeId nodeId, CancellationToken ct);
}
@@ -2,9 +2,15 @@ using ZB.MOM.WW.OtOpcUa.Commons.Types;
namespace ZB.MOM.WW.OtOpcUa.Commons.Interfaces;
/// <summary>Event arguments for role leader change notifications.</summary>
public sealed class RoleLeaderChangedEventArgs : EventArgs
{
/// <summary>Gets the role name that changed leadership.</summary>
public required string Role { get; init; }
/// <summary>Gets the previous leader node ID, or null if there was no previous leader.</summary>
public required NodeId? PreviousLeader { get; init; }
/// <summary>Gets the new leader node ID, or null if the role is now leaderless.</summary>
public required NodeId? NewLeader { get; init; }
}
@@ -68,6 +68,7 @@ public static class OtOpcUaTelemetry
/// Starts a deploy span tagged with the deployment id. Caller disposes to close. Returns
/// null when no listener is attached so the call site stays cheap on undecorated builds.
/// </summary>
/// <param name="deploymentId">The deployment identifier to tag the span with.</param>
public static Activity? StartDeployApplySpan(string deploymentId)
{
var activity = ActivitySource.StartActivity("otopcua.deploy.apply", ActivityKind.Internal);
@@ -18,20 +18,41 @@ public sealed class DeferredAddressSpaceSink : IOpcUaAddressSpaceSink
/// <summary>Swap in the production sink. Pass <c>null</c> to revert to the null sink
/// (used during graceful shutdown so post-stop writes don't hit a half-disposed manager).</summary>
/// <param name="sink">The sink implementation to use, or null to use the null sink.</param>
public void SetSink(IOpcUaAddressSpaceSink? sink) =>
_inner = sink ?? NullOpcUaAddressSpaceSink.Instance;
/// <summary>Writes a value to the OPC UA address space through the inner sink.</summary>
/// <param name="nodeId">The node ID of the variable.</param>
/// <param name="value">The value to write.</param>
/// <param name="quality">The OPC UA quality value.</param>
/// <param name="sourceTimestampUtc">The source timestamp in UTC.</param>
public void WriteValue(string nodeId, object? value, OpcUaQuality quality, DateTime sourceTimestampUtc)
=> _inner.WriteValue(nodeId, value, quality, sourceTimestampUtc);
/// <summary>Writes an alarm state through the inner sink.</summary>
/// <param name="alarmNodeId">The node ID of the alarm condition.</param>
/// <param name="active">Whether the alarm is active.</param>
/// <param name="acknowledged">Whether the alarm has been acknowledged.</param>
/// <param name="sourceTimestampUtc">The source timestamp in UTC.</param>
public void WriteAlarmState(string alarmNodeId, bool active, bool acknowledged, DateTime sourceTimestampUtc)
=> _inner.WriteAlarmState(alarmNodeId, active, acknowledged, sourceTimestampUtc);
/// <summary>Ensures a folder exists in the address space through the inner sink.</summary>
/// <param name="folderNodeId">The node ID of the folder.</param>
/// <param name="parentNodeId">The node ID of the parent folder, or null for root.</param>
/// <param name="displayName">The display name of the folder.</param>
public void EnsureFolder(string folderNodeId, string? parentNodeId, string displayName)
=> _inner.EnsureFolder(folderNodeId, parentNodeId, displayName);
/// <summary>Ensures a variable exists in the address space through the inner sink.</summary>
/// <param name="variableNodeId">The node ID of the variable.</param>
/// <param name="parentFolderNodeId">The node ID of the parent folder, or null for root.</param>
/// <param name="displayName">The display name of the variable.</param>
/// <param name="dataType">The OPC UA data type of the variable.</param>
public void EnsureVariable(string variableNodeId, string? parentFolderNodeId, string displayName, string dataType)
=> _inner.EnsureVariable(variableNodeId, parentFolderNodeId, displayName, dataType);
/// <summary>Rebuilds the address space through the inner sink.</summary>
public void RebuildAddressSpace() => _inner.RebuildAddressSpace();
}
@@ -12,8 +12,11 @@ public sealed class DeferredServiceLevelPublisher : IServiceLevelPublisher
private volatile IServiceLevelPublisher _inner = NullServiceLevelPublisher.Instance;
/// <summary>Swap the underlying publisher. Pass null to revert to the Null no-op.</summary>
/// <param name="inner">The publisher implementation to use, or null to use the null publisher.</param>
public void SetInner(IServiceLevelPublisher? inner) =>
_inner = inner ?? NullServiceLevelPublisher.Instance;
/// <summary>Publishes a service level value to the inner publisher.</summary>
/// <param name="serviceLevel">The service level to publish.</param>
public void Publish(byte serviceLevel) => _inner.Publish(serviceLevel);
}
@@ -9,9 +9,17 @@ namespace ZB.MOM.WW.OtOpcUa.Commons.OpcUa;
public interface IOpcUaAddressSpaceSink
{
/// <summary>Write a Variable node's current value + quality + source timestamp.</summary>
/// <param name="nodeId">The OPC UA node ID of the variable.</param>
/// <param name="value">The value to write.</param>
/// <param name="quality">The quality status of the value.</param>
/// <param name="sourceTimestampUtc">The source timestamp in UTC.</param>
void WriteValue(string nodeId, object? value, OpcUaQuality quality, DateTime sourceTimestampUtc);
/// <summary>Write an alarm-condition Variable's active/acknowledged state.</summary>
/// <param name="alarmNodeId">The OPC UA node ID of the alarm.</param>
/// <param name="active">Whether the alarm is active.</param>
/// <param name="acknowledged">Whether the alarm has been acknowledged.</param>
/// <param name="sourceTimestampUtc">The source timestamp in UTC.</param>
void WriteAlarmState(string alarmNodeId, bool active, bool acknowledged, DateTime sourceTimestampUtc);
/// <summary>
@@ -20,6 +28,9 @@ public interface IOpcUaAddressSpaceSink
/// <paramref name="parentNodeId"/> is null the folder is parented under the namespace
/// root. Idempotent: calling twice with the same id is safe.
/// </summary>
/// <param name="folderNodeId">The OPC UA node ID for the folder.</param>
/// <param name="parentNodeId">The parent folder node ID, or null for namespace root.</param>
/// <param name="displayName">The display name for the folder.</param>
void EnsureFolder(string folderNodeId, string? parentNodeId, string displayName);
/// <summary>
@@ -29,6 +40,9 @@ public interface IOpcUaAddressSpaceSink
/// Used by <c>Phase7Applier</c> to materialise Galaxy / SystemPlatform tags ahead of any
/// driver-side subscribe so OPC UA clients can browse them. Idempotent.
/// </summary>
/// <param name="variableNodeId">The OPC UA node ID for the variable.</param>
/// <param name="parentFolderNodeId">The parent folder node ID, or null for namespace root.</param>
/// <param name="displayName">The display name for the variable.</param>
/// <param name="dataType">OPC UA built-in type name ("Boolean" / "Int32" / "Float" / etc.).</param>
void EnsureVariable(string variableNodeId, string? parentFolderNodeId, string displayName, string dataType);
@@ -49,9 +63,19 @@ public sealed class NullOpcUaAddressSpaceSink : IOpcUaAddressSpaceSink
{
public static readonly NullOpcUaAddressSpaceSink Instance = new();
private NullOpcUaAddressSpaceSink() { }
/// <inheritdoc />
public void WriteValue(string nodeId, object? value, OpcUaQuality quality, DateTime sourceTimestampUtc) { }
/// <inheritdoc />
public void WriteAlarmState(string alarmNodeId, bool active, bool acknowledged, DateTime sourceTimestampUtc) { }
/// <inheritdoc />
public void EnsureFolder(string folderNodeId, string? parentNodeId, string displayName) { }
/// <inheritdoc />
public void EnsureVariable(string variableNodeId, string? parentFolderNodeId, string displayName, string dataType) { }
/// <inheritdoc />
public void RebuildAddressSpace() { }
}
@@ -8,6 +8,8 @@ namespace ZB.MOM.WW.OtOpcUa.Commons.OpcUa;
/// </summary>
public interface IServiceLevelPublisher
{
/// <summary>Publishes the service level value to the OPC UA Server object.</summary>
/// <param name="serviceLevel">The service level value (0-255).</param>
void Publish(byte serviceLevel);
}
@@ -17,6 +19,11 @@ public sealed class NullServiceLevelPublisher : IServiceLevelPublisher
{
public static readonly NullServiceLevelPublisher Instance = new();
private NullServiceLevelPublisher() { }
/// <summary>Gets the last published service level value.</summary>
public byte LastPublished { get; private set; }
/// <summary>Records the service level value without publishing.</summary>
/// <param name="serviceLevel">The service level value (0-255).</param>
public void Publish(byte serviceLevel) => LastPublished = serviceLevel;
}
@@ -2,9 +2,16 @@ namespace ZB.MOM.WW.OtOpcUa.Commons.Types;
public readonly record struct CorrelationId(Guid Value)
{
/// <summary>Creates a new CorrelationId with a randomly generated GUID.</summary>
public static CorrelationId NewId() => new(Guid.NewGuid());
/// <inheritdoc />
public override string ToString() => Value.ToString("N");
/// <summary>Parses a lowercase hex string without hyphens into a CorrelationId.</summary>
/// <param name="s">The string to parse.</param>
public static CorrelationId Parse(string s) => new(Guid.ParseExact(s, "N"));
/// <summary>Attempts to parse a lowercase hex string without hyphens into a CorrelationId.</summary>
/// <param name="s">The string to parse, or null.</param>
/// <param name="id">The resulting CorrelationId if parsing succeeds.</param>
public static bool TryParse(string? s, out CorrelationId id)
{
if (Guid.TryParseExact(s, "N", out var g)) { id = new CorrelationId(g); return true; }
@@ -2,9 +2,22 @@ namespace ZB.MOM.WW.OtOpcUa.Commons.Types;
public readonly record struct DeploymentId(Guid Value)
{
/// <summary>Creates a new deployment ID with a random GUID.</summary>
/// <returns>A new DeploymentId.</returns>
public static DeploymentId NewId() => new(Guid.NewGuid());
/// <inheritdoc />
public override string ToString() => Value.ToString("N");
/// <summary>Parses a deployment ID from a hex string without hyphens.</summary>
/// <param name="s">The hex string to parse.</param>
/// <returns>The parsed DeploymentId.</returns>
public static DeploymentId Parse(string s) => new(Guid.ParseExact(s, "N"));
/// <summary>Attempts to parse a deployment ID from a hex string without hyphens.</summary>
/// <param name="s">The hex string to parse, or null.</param>
/// <param name="id">The parsed DeploymentId if successful, or default.</param>
/// <returns>True if parsing succeeded; false otherwise.</returns>
public static bool TryParse(string? s, out DeploymentId id)
{
if (Guid.TryParseExact(s, "N", out var g)) { id = new DeploymentId(g); return true; }
@@ -2,9 +2,22 @@ namespace ZB.MOM.WW.OtOpcUa.Commons.Types;
public readonly record struct ExecutionId(Guid Value)
{
/// <summary>Creates a new execution ID with a randomly generated GUID.</summary>
/// <returns>A new ExecutionId instance.</returns>
public static ExecutionId NewId() => new(Guid.NewGuid());
/// <inheritdoc />
public override string ToString() => Value.ToString("N");
/// <summary>Parses the specified string into an ExecutionId in format N.</summary>
/// <param name="s">The string to parse.</param>
/// <returns>The parsed ExecutionId.</returns>
public static ExecutionId Parse(string s) => new(Guid.ParseExact(s, "N"));
/// <summary>Tries to parse the specified string into an ExecutionId in format N.</summary>
/// <param name="s">The string to parse, or null.</param>
/// <param name="id">The parsed ExecutionId, or default if parsing fails.</param>
/// <returns>true if parsing succeeded; otherwise, false.</returns>
public static bool TryParse(string? s, out ExecutionId id)
{
if (Guid.TryParseExact(s, "N", out var g)) { id = new ExecutionId(g); return true; }
@@ -7,11 +7,22 @@ namespace ZB.MOM.WW.OtOpcUa.Commons.Types;
/// </summary>
public readonly record struct NodeId(string Value)
{
/// <inheritdoc />
public override string ToString() => Value;
/// <summary>Parses a string into a NodeId.</summary>
/// <param name="s">The string to parse.</param>
/// <returns>A new NodeId instance.</returns>
/// <exception cref="ArgumentException">Thrown when the string is null, empty, or whitespace.</exception>
public static NodeId Parse(string s) =>
string.IsNullOrWhiteSpace(s)
? throw new ArgumentException("NodeId value cannot be empty.", nameof(s))
: new NodeId(s);
/// <summary>Attempts to parse a string into a NodeId.</summary>
/// <param name="s">The string to parse.</param>
/// <param name="id">The parsed NodeId if successful.</param>
/// <returns>True if the parse succeeded; otherwise false.</returns>
public static bool TryParse(string? s, out NodeId id)
{
if (!string.IsNullOrWhiteSpace(s)) { id = new NodeId(s); return true; }
@@ -6,11 +6,24 @@ namespace ZB.MOM.WW.OtOpcUa.Commons.Types;
/// </summary>
public readonly record struct RevisionHash(string Value)
{
/// <inheritdoc />
public override string ToString() => Value;
/// <summary>
/// Parses a string into a <see cref="RevisionHash"/>.
/// </summary>
/// <param name="s">The string to parse.</param>
/// <returns>A <see cref="RevisionHash"/> instance.</returns>
/// <exception cref="ArgumentException">Thrown if the input is null, empty, or whitespace.</exception>
public static RevisionHash Parse(string s) =>
string.IsNullOrWhiteSpace(s)
? throw new ArgumentException("RevisionHash value cannot be empty.", nameof(s))
: new RevisionHash(s);
/// <summary>
/// Attempts to parse a string into a <see cref="RevisionHash"/>.
/// </summary>
/// <param name="s">The string to parse.</param>
/// <param name="hash">The parsed hash, or default if parsing fails.</param>
/// <returns>True if parsing succeeded; otherwise false.</returns>
public static bool TryParse(string? s, out RevisionHash hash)
{
if (!string.IsNullOrWhiteSpace(s)) { hash = new RevisionHash(s); return true; }