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
@@ -22,10 +22,16 @@ public enum OpcUaSecurityProfile
Basic256Sha256SignAndEncrypt,
}
/// <summary>Configuration options for OPC UA application hosting.</summary>
public sealed class OpcUaApplicationHostOptions
{
/// <summary>Gets or sets the application name (default "OtOpcUa").</summary>
public string ApplicationName { get; set; } = "OtOpcUa";
/// <summary>Gets or sets the application URI (default "urn:OtOpcUa").</summary>
public string ApplicationUri { get; set; } = "urn:OtOpcUa";
/// <summary>Gets or sets the product URI (default "https://zb.com/otopcua").</summary>
public string ProductUri { get; set; } = "https://zb.com/otopcua";
/// <summary>Listening port for the binary endpoint (default 4840).</summary>
@@ -93,6 +99,10 @@ public sealed class OpcUaApplicationHost : IAsyncDisposable
private StandardServer? _server;
private ImpersonateEventHandler? _impersonateHandler;
/// <summary>Initializes a new instance of the OPC UA application host.</summary>
/// <param name="options">The host configuration options.</param>
/// <param name="logger">The logger for diagnostic output.</param>
/// <param name="userAuthenticator">An optional user authenticator for UserName tokens; uses null implementation if not provided.</param>
public OpcUaApplicationHost(
OpcUaApplicationHostOptions options,
ILogger<OpcUaApplicationHost> logger,
@@ -103,9 +113,15 @@ public sealed class OpcUaApplicationHost : IAsyncDisposable
_userAuthenticator = userAuthenticator ?? NullOpcUaUserAuthenticator.Instance;
}
/// <summary>Gets the OPC Foundation application instance, or null if not yet started.</summary>
public ApplicationInstance? ApplicationInstance => _application;
/// <summary>Gets the OPC UA server instance, or null if not yet started.</summary>
public StandardServer? Server => _server;
/// <summary>Starts the OPC UA application and server.</summary>
/// <param name="server">The standard server instance to start.</param>
/// <param name="cancellationToken">A cancellation token for the operation.</param>
public async Task StartAsync(StandardServer server, CancellationToken cancellationToken)
{
_server = server;
@@ -215,6 +231,9 @@ public sealed class OpcUaApplicationHost : IAsyncDisposable
/// the full SDK. Side-effects are confined to mutating <see cref="ImpersonateEventArgs"/>
/// and logging.
/// </summary>
/// <param name="authenticator">The user authenticator to validate credentials.</param>
/// <param name="args">The impersonation event arguments to process.</param>
/// <param name="logger">The logger for diagnostic output.</param>
internal static void HandleImpersonation(
IOpcUaUserAuthenticator authenticator,
ImpersonateEventArgs args,
@@ -351,6 +370,7 @@ public sealed class OpcUaApplicationHost : IAsyncDisposable
/// falls back to a single None entry so the server doesn't refuse to start with no
/// listening endpoints — the misconfiguration is logged and very visible.
/// </summary>
/// <param name="profiles">The security profiles to build policies for.</param>
internal static IEnumerable<ServerSecurityPolicy> BuildSecurityPolicies(IEnumerable<OpcUaSecurityProfile> profiles)
{
var seen = new HashSet<OpcUaSecurityProfile>();
@@ -409,6 +429,7 @@ public sealed class OpcUaApplicationHost : IAsyncDisposable
};
}
/// <summary>Disposes the application host and cleans up resources.</summary>
public ValueTask DisposeAsync()
{
if (_impersonateHandler is not null && _server?.CurrentInstance?.SessionManager is { } sessionManager)
@@ -30,13 +30,18 @@ public sealed class OtOpcUaNodeManager : CustomNodeManager2
private readonly ConcurrentDictionary<string, FolderState> _folders = new(StringComparer.Ordinal);
private FolderState? _root;
/// <summary>Initializes a new instance of the <see cref="OtOpcUaNodeManager"/> class with the OPC UA server and configuration.</summary>
/// <param name="server">The OPC UA server instance.</param>
/// <param name="configuration">The application configuration.</param>
public OtOpcUaNodeManager(IServerInternal server, ApplicationConfiguration configuration)
: base(server, configuration, DefaultNamespaceUri)
{
// SystemContext is initialised by the base ctor.
}
/// <summary>Gets the count of variable nodes currently managed.</summary>
public int VariableCount => _variables.Count;
/// <summary>Gets the count of folder nodes currently managed.</summary>
public int FolderCount => _folders.Count;
/// <summary>
@@ -44,6 +49,10 @@ public sealed class OtOpcUaNodeManager : CustomNodeManager2
/// variable node on first call; subsequent calls update Value + StatusCode +
/// SourceTimestamp and call <c>ClearChangeMasks</c> so subscribed clients see the change.
/// </summary>
/// <param name="nodeId">The node identifier of the variable.</param>
/// <param name="value">The new value to write.</param>
/// <param name="quality">The OPC UA quality status code.</param>
/// <param name="sourceTimestampUtc">The timestamp of the value in UTC.</param>
public void WriteValue(string nodeId, object? value, OpcUaQuality quality, DateTime sourceTimestampUtc)
{
ArgumentException.ThrowIfNullOrEmpty(nodeId);
@@ -61,6 +70,10 @@ public sealed class OtOpcUaNodeManager : CustomNodeManager2
/// <summary>Apply an alarm-state write. Surfaced as a two-element Variable carrying
/// <c>[active, acknowledged]</c> — proper <c>AlarmConditionState</c> + event firing
/// comes when the F14b walker integration lands and registers real condition nodes.</summary>
/// <param name="alarmNodeId">The node identifier of the alarm variable.</param>
/// <param name="active">Whether the alarm is currently active.</param>
/// <param name="acknowledged">Whether the alarm has been acknowledged.</param>
/// <param name="sourceTimestampUtc">The timestamp of the alarm state change in UTC.</param>
public void WriteAlarmState(string alarmNodeId, bool active, bool acknowledged, DateTime sourceTimestampUtc)
{
ArgumentException.ThrowIfNullOrEmpty(alarmNodeId);
@@ -82,6 +95,9 @@ public sealed class OtOpcUaNodeManager : CustomNodeManager2
/// folder hierarchy. Idempotent: the second call with the same id returns the cached
/// folder so adding child variables under it still works.
/// </summary>
/// <param name="folderNodeId">The node identifier of the folder.</param>
/// <param name="parentNodeId">The node identifier of the parent folder; null to use the namespace root.</param>
/// <param name="displayName">The display name of the folder.</param>
public void EnsureFolder(string folderNodeId, string? parentNodeId, string displayName)
{
ArgumentException.ThrowIfNullOrEmpty(folderNodeId);
@@ -116,6 +132,10 @@ public sealed class OtOpcUaNodeManager : CustomNodeManager2
/// Idempotent. Materialises Galaxy / SystemPlatform tags so they're browseable before the
/// Galaxy driver issues SubscribeBulk.
/// </summary>
/// <param name="variableNodeId">The node identifier of the variable.</param>
/// <param name="parentFolderNodeId">The node identifier of the parent folder; null to use the namespace root.</param>
/// <param name="displayName">The display name of the variable.</param>
/// <param name="dataType">The OPC UA data type name (e.g., "Boolean", "Int32", "String").</param>
public void EnsureVariable(string variableNodeId, string? parentFolderNodeId, string displayName, string dataType)
{
ArgumentException.ThrowIfNullOrEmpty(variableNodeId);
@@ -28,6 +28,9 @@ public sealed class Phase7Applier
private readonly IOpcUaAddressSpaceSink _sink;
private readonly ILogger<Phase7Applier> _logger;
/// <summary>Initializes a new instance of the Phase7Applier class.</summary>
/// <param name="sink">The OPC UA address space sink to apply changes to.</param>
/// <param name="logger">The logger instance.</param>
public Phase7Applier(IOpcUaAddressSpaceSink sink, ILogger<Phase7Applier> logger)
{
ArgumentNullException.ThrowIfNull(sink);
@@ -40,6 +43,8 @@ public sealed class Phase7Applier
/// Apply <paramref name="plan"/> to the sink. Returns a summary of what was applied so
/// callers (OpcUaPublishActor) can correlate the work back to the originating deployment.
/// </summary>
/// <param name="plan">The plan to apply.</param>
/// <returns>A Phase7ApplyOutcome summarizing the applied changes.</returns>
public Phase7ApplyOutcome Apply(Phase7Plan plan)
{
ArgumentNullException.ThrowIfNull(plan);
@@ -104,6 +109,7 @@ public sealed class Phase7Applier
/// Idempotent: each <c>EnsureFolder</c> call returns the existing folder if already
/// present, so re-applies are cheap.
/// </summary>
/// <param name="composition">The composition result containing the hierarchy to materialise.</param>
public void MaterialiseHierarchy(Phase7CompositionResult composition)
{
ArgumentNullException.ThrowIfNull(composition);
@@ -136,6 +142,7 @@ public sealed class Phase7Applier
/// the Galaxy driver's <c>OnDataChange</c> path fills the value in once SubscribeBulk lands.
/// Idempotent.
/// </summary>
/// <param name="composition">The composition result containing the Galaxy tags to materialise.</param>
public void MaterialiseGalaxyTags(Phase7CompositionResult composition)
{
ArgumentNullException.ThrowIfNull(composition);
@@ -18,6 +18,9 @@ public sealed record Phase7CompositionResult(
IReadOnlyList<GalaxyTagPlan> GalaxyTags)
{
/// <summary>Convenience constructor for tests + earlier callers that don't carry UNS or Galaxy data.</summary>
/// <param name="equipmentNodes">The equipment nodes.</param>
/// <param name="driverInstancePlans">The driver instance plans.</param>
/// <param name="scriptedAlarmPlans">The scripted alarm plans.</param>
public Phase7CompositionResult(
IReadOnlyList<EquipmentNode> equipmentNodes,
IReadOnlyList<DriverInstancePlan> driverInstancePlans,
@@ -28,6 +31,11 @@ public sealed record Phase7CompositionResult(
}
/// <summary>Convenience constructor for callers carrying UNS but not Galaxy data.</summary>
/// <param name="unsAreas">The UNS areas.</param>
/// <param name="unsLines">The UNS lines.</param>
/// <param name="equipmentNodes">The equipment nodes.</param>
/// <param name="driverInstancePlans">The driver instance plans.</param>
/// <param name="scriptedAlarmPlans">The scripted alarm plans.</param>
public Phase7CompositionResult(
IReadOnlyList<UnsAreaProjection> unsAreas,
IReadOnlyList<UnsLineProjection> unsLines,
@@ -74,6 +82,10 @@ public sealed record GalaxyTagPlan(
public static class Phase7Composer
{
/// <summary>Convenience overload for legacy callers + tests that don't yet supply UNS / Galaxy data.</summary>
/// <param name="equipment">The equipment.</param>
/// <param name="driverInstances">The driver instances.</param>
/// <param name="scriptedAlarms">The scripted alarms.</param>
/// <returns>The composition result.</returns>
public static Phase7CompositionResult Compose(
IReadOnlyList<Equipment> equipment,
IReadOnlyList<DriverInstance> driverInstances,
@@ -82,6 +94,12 @@ public static class Phase7Composer
Array.Empty<Tag>(), Array.Empty<Namespace>());
/// <summary>UNS-aware overload that doesn't yet supply Galaxy tags.</summary>
/// <param name="unsAreas">The UNS areas.</param>
/// <param name="unsLines">The UNS lines.</param>
/// <param name="equipment">The equipment.</param>
/// <param name="driverInstances">The driver instances.</param>
/// <param name="scriptedAlarms">The scripted alarms.</param>
/// <returns>The composition result.</returns>
public static Phase7CompositionResult Compose(
IReadOnlyList<UnsArea> unsAreas,
IReadOnlyList<UnsLine> unsLines,
@@ -91,6 +109,17 @@ public static class Phase7Composer
Compose(unsAreas, unsLines, equipment, driverInstances, scriptedAlarms,
Array.Empty<Tag>(), Array.Empty<Namespace>());
/// <summary>
/// Composes the address space build plan from the configuration entities.
/// </summary>
/// <param name="unsAreas">The UNS areas.</param>
/// <param name="unsLines">The UNS lines.</param>
/// <param name="equipment">The equipment.</param>
/// <param name="driverInstances">The driver instances.</param>
/// <param name="scriptedAlarms">The scripted alarms.</param>
/// <param name="tags">The tags.</param>
/// <param name="namespaces">The namespaces.</param>
/// <returns>The composition result.</returns>
public static Phase7CompositionResult Compose(
IReadOnlyList<UnsArea> unsAreas,
IReadOnlyList<UnsLine> unsLines,
@@ -26,6 +26,7 @@ public sealed record Phase7Plan(
IReadOnlyList<GalaxyTagPlan> RemovedGalaxyTags,
IReadOnlyList<Phase7Plan.GalaxyTagDelta> ChangedGalaxyTags)
{
/// <summary>Gets a value indicating whether the composition plan contains no changes.</summary>
public bool IsEmpty =>
AddedEquipment.Count == 0 && RemovedEquipment.Count == 0 && ChangedEquipment.Count == 0 &&
AddedDrivers.Count == 0 && RemovedDrivers.Count == 0 && ChangedDrivers.Count == 0 &&
@@ -46,6 +47,8 @@ public static class Phase7Planner
/// Element equality on the projection records doubles as the "did this change" check,
/// so any field difference moves an item from "stable" to ChangedX.
/// </summary>
/// <param name="previous">The previous composition result.</param>
/// <param name="next">The new composition result.</param>
public static Phase7Plan Compute(Phase7CompositionResult previous, Phase7CompositionResult next)
{
ArgumentNullException.ThrowIfNull(previous);
@@ -12,23 +12,45 @@ public sealed class SdkAddressSpaceSink : IOpcUaAddressSpaceSink
{
private readonly OtOpcUaNodeManager _nodeManager;
/// <summary>Initializes a new instance of the SdkAddressSpaceSink class.</summary>
/// <param name="nodeManager">The OPC UA node manager.</param>
public SdkAddressSpaceSink(OtOpcUaNodeManager nodeManager)
{
ArgumentNullException.ThrowIfNull(nodeManager);
_nodeManager = nodeManager;
}
/// <summary>Writes a value to the OPC UA address space.</summary>
/// <param name="nodeId">The OPC UA node identifier.</param>
/// <param name="value">The value being written.</param>
/// <param name="quality">The OPC UA quality status.</param>
/// <param name="sourceTimestampUtc">The source timestamp in UTC.</param>
public void WriteValue(string nodeId, object? value, OpcUaQuality quality, DateTime sourceTimestampUtc)
=> _nodeManager.WriteValue(nodeId, value, quality, sourceTimestampUtc);
/// <summary>Writes alarm state to the OPC UA address space.</summary>
/// <param name="alarmNodeId">The alarm node identifier.</param>
/// <param name="active">Whether the alarm is active.</param>
/// <param name="acknowledged">Whether the alarm is acknowledged.</param>
/// <param name="sourceTimestampUtc">The source timestamp in UTC.</param>
public void WriteAlarmState(string alarmNodeId, bool active, bool acknowledged, DateTime sourceTimestampUtc)
=> _nodeManager.WriteAlarmState(alarmNodeId, active, acknowledged, sourceTimestampUtc);
/// <summary>Ensures a folder node exists in the address space.</summary>
/// <param name="folderNodeId">The folder node identifier.</param>
/// <param name="parentNodeId">The parent folder node identifier.</param>
/// <param name="displayName">The display name for the folder.</param>
public void EnsureFolder(string folderNodeId, string? parentNodeId, string displayName)
=> _nodeManager.EnsureFolder(folderNodeId, parentNodeId, displayName);
/// <summary>Ensures a variable node exists in the address space.</summary>
/// <param name="variableNodeId">The variable node identifier.</param>
/// <param name="parentFolderNodeId">The parent folder node identifier.</param>
/// <param name="displayName">The display name for the variable.</param>
/// <param name="dataType">The OPC UA data type.</param>
public void EnsureVariable(string variableNodeId, string? parentFolderNodeId, string displayName, string dataType)
=> _nodeManager.EnsureVariable(variableNodeId, parentFolderNodeId, displayName, dataType);
/// <summary>Rebuilds the entire OPC UA address space.</summary>
public void RebuildAddressSpace() => _nodeManager.RebuildAddressSpace();
}
@@ -23,12 +23,17 @@ public sealed class SdkServiceLevelPublisher : IServiceLevelPublisher
private readonly IServerInternal _serverInternal;
private readonly ILogger<SdkServiceLevelPublisher> _logger;
/// <summary>Initializes a new instance of the SdkServiceLevelPublisher class.</summary>
/// <param name="serverInternal">The OPC UA server internal interface.</param>
/// <param name="logger">The logger instance.</param>
public SdkServiceLevelPublisher(IServerInternal serverInternal, ILogger<SdkServiceLevelPublisher> logger)
{
_serverInternal = serverInternal;
_logger = logger;
}
/// <summary>Publishes the service level to the OPC UA Server object.</summary>
/// <param name="serviceLevel">The service level value to publish.</param>
public void Publish(byte serviceLevel)
{
var node = _serverInternal.ServerObject?.ServiceLevel;
@@ -17,6 +17,10 @@ public interface IOpcUaUserAuthenticator
/// reject codes, and a thrown exception escapes into the OPC UA SDK's session-activation
/// path where it surfaces as a generic <c>BadInternalError</c>.
/// </summary>
/// <param name="username">The username to authenticate.</param>
/// <param name="password">The cleartext password to authenticate.</param>
/// <param name="ct">The cancellation token.</param>
/// <returns>An OpcUaUserAuthResult indicating success or failure.</returns>
Task<OpcUaUserAuthResult> AuthenticateUserNameAsync(string username, string password, CancellationToken ct);
}
@@ -27,9 +31,16 @@ public sealed record OpcUaUserAuthResult(
IReadOnlyList<string> Roles,
string? Error)
{
/// <summary>Creates a successful authentication result with the specified display name and roles.</summary>
/// <param name="displayName">The display name for the authenticated user.</param>
/// <param name="roles">The roles to assign to the authenticated user.</param>
/// <returns>A successful OpcUaUserAuthResult.</returns>
public static OpcUaUserAuthResult Allow(string displayName, IReadOnlyList<string> roles) =>
new(true, displayName, roles, null);
/// <summary>Creates a failed authentication result with the specified error message.</summary>
/// <param name="error">The error message describing why authentication failed.</param>
/// <returns>A failed OpcUaUserAuthResult.</returns>
public static OpcUaUserAuthResult Deny(string error) =>
new(false, null, Array.Empty<string>(), error);
}
@@ -44,6 +55,11 @@ public sealed class NullOpcUaUserAuthenticator : IOpcUaUserAuthenticator
public static readonly NullOpcUaUserAuthenticator Instance = new();
private NullOpcUaUserAuthenticator() { }
/// <summary>Authenticates a username (always denies in this null implementation).</summary>
/// <param name="username">The username to authenticate.</param>
/// <param name="password">The cleartext password to authenticate.</param>
/// <param name="ct">The cancellation token.</param>
/// <returns>A task that returns a denial result indicating no authenticator is configured.</returns>
public Task<OpcUaUserAuthResult> AuthenticateUserNameAsync(string username, string password, CancellationToken ct) =>
Task.FromResult(OpcUaUserAuthResult.Deny("No UserName authenticator is configured on this server."));
}