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
@@ -28,6 +28,7 @@ public static class DeploymentArtifact
/// Parse a deployment artifact blob into the list of driver-instance specs to spawn.
/// Empty / malformed blobs return an empty list — callers log + treat as "no drivers".
/// </summary>
/// <param name="blob">The deployment artifact blob to parse.</param>
public static IReadOnlyList<DriverInstanceSpec> ParseDriverInstances(ReadOnlySpan<byte> blob)
{
if (blob.IsEmpty) return Array.Empty<DriverInstanceSpec>();
@@ -87,6 +88,7 @@ public static class DeploymentArtifact
/// subset of fields per entity class to drive the address-space rebuild on driver-role
/// nodes.
/// </summary>
/// <param name="blob">The deployment artifact blob to parse.</param>
public static Phase7CompositionResult ParseComposition(ReadOnlySpan<byte> blob)
{
if (blob.IsEmpty) return Empty();
@@ -54,6 +54,7 @@ public sealed class DriverHostActor : ReceiveActor, IWithTimers
private sealed record ChildEntry(IActorRef Actor, string DriverType, string LastConfigJson, bool Stubbed);
/// <inheritdoc />
public ITimerScheduler Timers { get; set; } = null!;
public sealed class RetryConfigDbConnection
@@ -62,6 +63,14 @@ public sealed class DriverHostActor : ReceiveActor, IWithTimers
private RetryConfigDbConnection() { }
}
/// <summary>Creates props for a DriverHostActor with the specified dependencies.</summary>
/// <param name="dbFactory">Database context factory for configuration database access.</param>
/// <param name="localNode">The local cluster node identifier.</param>
/// <param name="coordinator">Optional coordinator actor reference for deployment coordination.</param>
/// <param name="driverFactory">Optional driver factory; defaults to null factory if not provided.</param>
/// <param name="localRoles">Optional set of roles assigned to the local node.</param>
/// <param name="dependencyMux">Optional actor reference for dependency multiplexing.</param>
/// <param name="opcUaPublishActor">Optional actor reference for OPC UA publishing.</param>
public static Props Props(
IDbContextFactory<OtOpcUaConfigDbContext> dbFactory,
CommonsNodeId localNode,
@@ -73,6 +82,14 @@ public sealed class DriverHostActor : ReceiveActor, IWithTimers
Akka.Actor.Props.Create(() => new DriverHostActor(
dbFactory, localNode, coordinator, driverFactory, localRoles, dependencyMux, opcUaPublishActor));
/// <summary>Initializes a new DriverHostActor with the specified dependencies.</summary>
/// <param name="dbFactory">Database context factory for configuration database access.</param>
/// <param name="localNode">The local cluster node identifier.</param>
/// <param name="coordinator">Optional coordinator actor reference for deployment coordination.</param>
/// <param name="driverFactory">Optional driver factory; defaults to null factory if not provided.</param>
/// <param name="localRoles">Optional set of roles assigned to the local node.</param>
/// <param name="dependencyMux">Optional actor reference for dependency multiplexing.</param>
/// <param name="opcUaPublishActor">Optional actor reference for OPC UA publishing.</param>
public DriverHostActor(
IDbContextFactory<OtOpcUaConfigDbContext> dbFactory,
CommonsNodeId localNode,
@@ -94,6 +111,7 @@ public sealed class DriverHostActor : ReceiveActor, IWithTimers
Become(Steady);
}
/// <inheritdoc />
protected override void PreStart()
{
// Subscribe to deployments topic so the coordinator's broadcast lands here.
@@ -389,14 +407,25 @@ public sealed class DriverHostActor : ReceiveActor, IWithTimers
/// </summary>
private sealed class StubbedDriver : IDriver
{
/// <inheritdoc />
public string DriverInstanceId { get; }
/// <inheritdoc />
public string DriverType { get; }
/// <summary>Initializes a new stubbed driver with the specified ID and type.</summary>
/// <param name="id">The driver instance identifier.</param>
/// <param name="type">The driver type name.</param>
public StubbedDriver(string id, string type) { DriverInstanceId = id; DriverType = type; }
/// <inheritdoc />
public Task InitializeAsync(string driverConfigJson, CancellationToken cancellationToken) => Task.CompletedTask;
/// <inheritdoc />
public Task ReinitializeAsync(string driverConfigJson, CancellationToken cancellationToken) => Task.CompletedTask;
/// <inheritdoc />
public Task ShutdownAsync(CancellationToken cancellationToken) => Task.CompletedTask;
/// <inheritdoc />
public DriverHealth GetHealth() => new(DriverState.Healthy, DateTime.UtcNow, LastError: null);
/// <inheritdoc />
public long GetMemoryFootprint() => 0;
/// <inheritdoc />
public Task FlushOptionalCachesAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}
@@ -59,8 +59,17 @@ public sealed class DriverInstanceActor : ReceiveActor, IWithTimers
private ISubscriptionHandle? _subscriptionHandle;
private EventHandler<DataChangeEventArgs>? _dataChangeHandler;
/// <summary>
/// Gets or sets the timer scheduler for scheduling reconnection attempts.
/// </summary>
public ITimerScheduler Timers { get; set; } = null!;
/// <summary>
/// Creates a Props object for instantiating a <see cref="DriverInstanceActor"/>.
/// </summary>
/// <param name="driver">The driver instance to wrap.</param>
/// <param name="reconnectInterval">Optional interval for reconnection attempts; defaults to 10 seconds.</param>
/// <param name="startStubbed">If true, the actor starts in stub mode for testing or unavailable platforms.</param>
public static Props Props(IDriver driver, TimeSpan? reconnectInterval = null, bool startStubbed = false) =>
Akka.Actor.Props.Create(() => new DriverInstanceActor(driver, reconnectInterval ?? DefaultReconnectInterval, startStubbed));
@@ -76,6 +85,8 @@ public sealed class DriverInstanceActor : ReceiveActor, IWithTimers
/// The v2 <c>"GalaxyMxGateway"</c> driver talks gRPC to an external mxaccessgw process,
/// so it runs on any platform .NET 10 supports — Linux containers included. Not stubbed.
/// </summary>
/// <param name="driverType">The type identifier of the driver.</param>
/// <param name="roles">Operational roles configured for this instance.</param>
public static bool ShouldStub(string driverType, IEnumerable<string> roles)
{
var isWindowsOnly = driverType is "Galaxy" or "Historian.Wonderware";
@@ -84,6 +95,12 @@ public sealed class DriverInstanceActor : ReceiveActor, IWithTimers
return false;
}
/// <summary>
/// Initializes a new instance of the <see cref="DriverInstanceActor"/> class.
/// </summary>
/// <param name="driver">The driver instance to wrap and manage.</param>
/// <param name="reconnectInterval">Interval between reconnection attempts.</param>
/// <param name="startStubbed">If true, start in stub mode for testing or unavailable platforms.</param>
public DriverInstanceActor(IDriver driver, TimeSpan reconnectInterval, bool startStubbed = false)
{
_driver = driver;
@@ -319,6 +336,7 @@ public sealed class DriverInstanceActor : ReceiveActor, IWithTimers
private static bool IsGoodStatus(uint statusCode) => (statusCode >> 30) == 0;
/// <inheritdoc />
protected override void PostStop()
{
DetachSubscription();
@@ -21,6 +21,8 @@ public static class DriverSpawnPlanner
/// treated as "not desired here": if a child exists for the id it goes into ToStop,
/// otherwise the row is dropped entirely (no spawn for a disabled driver).
/// </summary>
/// <param name="current">The currently running driver children keyed by ID.</param>
/// <param name="target">The target driver instances from the deployment artifact.</param>
public static DriverSpawnPlan Compute(
IReadOnlyDictionary<string, DriverChildSnapshot> current,
IReadOnlyList<DriverInstanceSpec> target)
@@ -23,11 +23,16 @@ public sealed class DbHealthProbeActor : ReceiveActor, IWithTimers
private readonly ILoggingAdapter _log = Context.GetLogger();
private DbHealthStatus _last = new(false, DateTime.MinValue, "not probed yet");
/// <summary>Gets or sets the timer scheduler for periodic health probes.</summary>
public ITimerScheduler Timers { get; set; } = null!;
/// <summary>Creates a Props instance for the DbHealthProbeActor.</summary>
/// <param name="dbFactory">The factory for creating ConfigDb contexts.</param>
public static Props Props(IDbContextFactory<OtOpcUaConfigDbContext> dbFactory) =>
Akka.Actor.Props.Create(() => new DbHealthProbeActor(dbFactory));
/// <summary>Initializes a new instance of the <see cref="DbHealthProbeActor"/> class.</summary>
/// <param name="dbFactory">The factory for creating ConfigDb contexts.</param>
public DbHealthProbeActor(IDbContextFactory<OtOpcUaConfigDbContext> dbFactory)
{
_dbFactory = dbFactory;
@@ -35,6 +40,7 @@ public sealed class DbHealthProbeActor : ReceiveActor, IWithTimers
Receive<Tick>(_ => RunProbe());
}
/// <inheritdoc />
protected override void PreStart()
{
RunProbe();
@@ -37,8 +37,16 @@ public sealed class PeerOpcUaProbeActor : ReceiveActor, IWithTimers
private readonly Action<object>? _broadcastOverride;
private readonly ILoggingAdapter _log = Context.GetLogger();
/// <summary>Gets or sets the timer scheduler for this actor.</summary>
public ITimerScheduler Timers { get; set; } = null!;
/// <summary>Creates actor props for the peer OPC UA probe.</summary>
/// <param name="peer">The node identifier of the peer to probe.</param>
/// <param name="interval">Optional probe interval; defaults to DefaultProbeInterval.</param>
/// <param name="connectTimeout">Optional connection timeout; defaults to DefaultConnectTimeout.</param>
/// <param name="opcUaPort">The OPC UA port to connect to; defaults to DefaultOpcUaPort.</param>
/// <param name="broadcast">Optional custom broadcast delegate for probe results.</param>
/// <returns>Props configured to create a PeerOpcUaProbeActor.</returns>
public static Props Props(
NodeId peer,
TimeSpan? interval = null,
@@ -52,6 +60,12 @@ public sealed class PeerOpcUaProbeActor : ReceiveActor, IWithTimers
opcUaPort,
broadcast));
/// <summary>Initializes a new instance of the PeerOpcUaProbeActor.</summary>
/// <param name="peer">The node identifier of the peer to probe.</param>
/// <param name="interval">The probe interval.</param>
/// <param name="connectTimeout">The connection timeout.</param>
/// <param name="opcUaPort">The OPC UA port to connect to.</param>
/// <param name="broadcastOverride">Optional custom broadcast delegate for probe results.</param>
public PeerOpcUaProbeActor(
NodeId peer,
TimeSpan interval,
@@ -68,6 +82,7 @@ public sealed class PeerOpcUaProbeActor : ReceiveActor, IWithTimers
ReceiveAsync<Tick>(_ => RunProbeAsync());
}
/// <inheritdoc />
protected override void PreStart() =>
Timers.StartPeriodicTimer("probe", Tick.Instance, _interval);
@@ -25,9 +25,14 @@ public sealed class HistorianAdapterActor : ReceiveActor
private readonly IAlarmHistorianSink _sink;
private readonly ILoggingAdapter _log = Context.GetLogger();
/// <summary>Creates the props for a HistorianAdapterActor instance.</summary>
/// <param name="sink">The alarm historian sink implementation, or null to use a null sink.</param>
/// <returns>Props configured for creating a HistorianAdapterActor.</returns>
public static Props Props(IAlarmHistorianSink? sink = null) =>
Akka.Actor.Props.Create(() => new HistorianAdapterActor(sink ?? NullAlarmHistorianSink.Instance));
/// <summary>Initializes a new instance of the HistorianAdapterActor class.</summary>
/// <param name="sink">The alarm historian sink to forward enqueued events to.</param>
public HistorianAdapterActor(IAlarmHistorianSink sink)
{
_sink = sink;
@@ -52,7 +52,9 @@ public sealed class OpcUaPublishActor : ReceiveActor
Array.Empty<ScriptedAlarmPlan>(),
Array.Empty<GalaxyTagPlan>());
/// <summary>Gets the number of writes performed.</summary>
public int WriteCount => _writes;
/// <summary>Gets the last published service level.</summary>
public byte LastServiceLevel => _lastServiceLevel;
/// <summary>Production Props — pins the OPC UA dispatcher + subscribes to the
@@ -60,6 +62,11 @@ public sealed class OpcUaPublishActor : ReceiveActor
/// publish path. When <paramref name="dbFactory"/> + <paramref name="applier"/> are supplied,
/// <see cref="RebuildAddressSpace"/> reads the latest deployment artifact + drives the
/// applier through the sink.</summary>
/// <param name="sink">The OPC UA address space sink.</param>
/// <param name="serviceLevel">The service level publisher.</param>
/// <param name="localNode">The local cluster node ID.</param>
/// <param name="dbFactory">The optional database context factory.</param>
/// <param name="applier">The optional Phase 7 applier.</param>
public static Props Props(
IOpcUaAddressSpaceSink? sink = null,
IServiceLevelPublisher? serviceLevel = null,
@@ -76,6 +83,12 @@ public sealed class OpcUaPublishActor : ReceiveActor
/// <summary>Test-only Props that omits the pinned-dispatcher requirement and skips the
/// DPS subscribe so unit tests can spin up the actor on a vanilla TestKit cluster.</summary>
/// <param name="sink">The OPC UA address space sink.</param>
/// <param name="serviceLevel">The service level publisher.</param>
/// <param name="subscribeRedundancyTopic">Whether to subscribe to the redundancy topic.</param>
/// <param name="localNode">The local cluster node ID.</param>
/// <param name="dbFactory">The optional database context factory.</param>
/// <param name="applier">The optional Phase 7 applier.</param>
public static Props PropsForTests(
IOpcUaAddressSpaceSink? sink = null,
IServiceLevelPublisher? serviceLevel = null,
@@ -91,6 +104,13 @@ public sealed class OpcUaPublishActor : ReceiveActor
dbFactory,
applier));
/// <summary>Initializes a new instance of the <see cref="OpcUaPublishActor"/> class.</summary>
/// <param name="sink">The OPC UA address space sink.</param>
/// <param name="serviceLevel">The service level publisher.</param>
/// <param name="subscribeRedundancyTopic">Whether to subscribe to the redundancy topic.</param>
/// <param name="localNode">The local cluster node ID.</param>
/// <param name="dbFactory">The optional database context factory.</param>
/// <param name="applier">The optional Phase 7 applier.</param>
public OpcUaPublishActor(
IOpcUaAddressSpaceSink sink,
IServiceLevelPublisher serviceLevel,
@@ -114,6 +134,7 @@ public sealed class OpcUaPublishActor : ReceiveActor
Receive<SubscribeAck>(_ => { /* PubSub ack */ });
}
/// <inheritdoc />
protected override void PreStart()
{
if (_subscribeRedundancyTopic)
@@ -19,6 +19,9 @@ public sealed class EfAlarmActorStateStore : IAlarmActorStateStore
private readonly IDbContextFactory<OtOpcUaConfigDbContext> _dbFactory;
private readonly ILogger<EfAlarmActorStateStore> _logger;
/// <summary>Initializes a new instance of the EfAlarmActorStateStore.</summary>
/// <param name="dbFactory">The factory for creating database contexts.</param>
/// <param name="logger">The logger instance.</param>
public EfAlarmActorStateStore(
IDbContextFactory<OtOpcUaConfigDbContext> dbFactory,
ILogger<EfAlarmActorStateStore> logger)
@@ -27,6 +30,10 @@ public sealed class EfAlarmActorStateStore : IAlarmActorStateStore
_logger = logger;
}
/// <summary>Loads the alarm state snapshot from the database.</summary>
/// <param name="alarmId">The identifier of the alarm.</param>
/// <param name="ct">The cancellation token.</param>
/// <returns>The alarm state snapshot, or null if not found.</returns>
public async Task<AlarmActorStateSnapshot?> LoadAsync(string alarmId, CancellationToken ct)
{
using var db = await _dbFactory.CreateDbContextAsync(ct).ConfigureAwait(false);
@@ -43,6 +50,9 @@ public sealed class EfAlarmActorStateStore : IAlarmActorStateStore
LastAckUser: row.LastAckUser);
}
/// <summary>Saves the alarm state snapshot to the database.</summary>
/// <param name="snapshot">The alarm state snapshot to save.</param>
/// <param name="ct">The cancellation token.</param>
public async Task SaveAsync(AlarmActorStateSnapshot snapshot, CancellationToken ct)
{
using var db = await _dbFactory.CreateDbContextAsync(ct).ConfigureAwait(false);
@@ -49,6 +49,12 @@ public sealed class ScriptedAlarmActor : ReceiveActor
public sealed record StateRestored(ScriptedAlarmActorState State, string? LastAckUser);
/// <summary>Creates a new Props for a ScriptedAlarmActor with the given configuration and optional dependencies.</summary>
/// <param name="config">The alarm configuration.</param>
/// <param name="evaluator">The alarm evaluator; defaults to null evaluator if not provided.</param>
/// <param name="publisherFactory">Optional factory for creating DPS publishers.</param>
/// <param name="stateStore">Optional state store for persistence; defaults to null store if not provided.</param>
/// <returns>Akka Props for creating the actor.</returns>
public static Props Props(
AlarmConfig config,
IScriptedAlarmEvaluator? evaluator = null,
@@ -62,9 +68,16 @@ public sealed class ScriptedAlarmActor : ReceiveActor
/// <summary>Legacy single-arg ctor kept for callers that only care about the state machine
/// (no engine evaluation, no DPS fan-out, no persistence). Equivalent to <c>Props(new AlarmConfig(...))</c>.</summary>
/// <param name="alarmId">The alarm identifier, used as both alarm ID and name.</param>
/// <returns>Akka Props for creating the actor with minimal configuration.</returns>
public static Props Props(string alarmId) =>
Props(new AlarmConfig(alarmId, alarmId, EquipmentPath: "", Severity: 500, Predicate: null));
/// <summary>Initializes a new ScriptedAlarmActor with the given configuration and dependencies.</summary>
/// <param name="config">The alarm configuration.</param>
/// <param name="evaluator">The alarm predicate evaluator.</param>
/// <param name="publisherFactory">Optional factory for creating DPS publishers.</param>
/// <param name="stateStore">The state store for loading and saving alarm state.</param>
public ScriptedAlarmActor(
AlarmConfig config,
IScriptedAlarmEvaluator evaluator,
@@ -83,6 +96,7 @@ public sealed class ScriptedAlarmActor : ReceiveActor
Receive<StateRestored>(OnStateRestored);
}
/// <inheritdoc />
protected override void PreStart()
{
// Load persisted state — when the store has a row, restore in-memory state before the
@@ -35,6 +35,7 @@ public static class ServiceCollectionExtensions
/// override this with <c>SqliteStoreAndForwardSink</c> wrapping <c>WonderwareHistorianClient</c>.
/// Call this BEFORE <c>AddAkka</c>.
/// </summary>
/// <param name="services">The service collection to register with.</param>
public static IServiceCollection AddOtOpcUaRuntime(this IServiceCollection services)
{
services.TryAddSingleton<IAlarmHistorianSink>(NullAlarmHistorianSink.Instance);
@@ -60,6 +61,7 @@ public static class ServiceCollectionExtensions
/// services.AddAkka("otopcua", (ab, sp) => { ab.WithOtOpcUaClusterBootstrap(sp); if (hasDriver) ab.WithOtOpcUaRuntimeActors(); });
/// </code>
/// </summary>
/// <param name="builder">The Akka configuration builder.</param>
public static AkkaConfigurationBuilder WithOtOpcUaRuntimeActors(this AkkaConfigurationBuilder builder)
{
// Production cluster HOCON (akka.conf) carries this dispatcher block, but consumers that
@@ -33,8 +33,11 @@ public sealed class DependencyMuxActor : ReceiveActor
private readonly Dictionary<IActorRef, HashSet<string>> _bySubscriber = new();
private readonly ILoggingAdapter _log = Context.GetLogger();
/// <summary>Creates props for the DependencyMuxActor.</summary>
/// <returns>The props for creating this actor.</returns>
public static Props Props() => Akka.Actor.Props.Create<DependencyMuxActor>();
/// <summary>Initializes a new instance of the <see cref="DependencyMuxActor"/> class.</summary>
public DependencyMuxActor()
{
Receive<RegisterInterest>(OnRegister);
@@ -35,6 +35,14 @@ public sealed class VirtualTagActor : ReceiveActor
private bool _hasLastValue;
private object? _lastValue;
/// <summary>Factory method to create Props for a VirtualTagActor.</summary>
/// <param name="virtualTagId">Unique identifier for the virtual tag.</param>
/// <param name="expression">The expression to evaluate.</param>
/// <param name="evaluator">Optional evaluator; defaults to a null-object instance.</param>
/// <param name="scriptId">Optional script identifier; defaults to <paramref name="virtualTagId"/>.</param>
/// <param name="publisherFactory">Optional factory for creating DPS publishers.</param>
/// <param name="dependencyRefs">Optional list of dependency tag references; defaults to empty.</param>
/// <param name="mux">Optional reference to a dependency multiplexer actor.</param>
public static Props Props(
string virtualTagId,
string expression,
@@ -51,6 +59,14 @@ public sealed class VirtualTagActor : ReceiveActor
dependencyRefs ?? Array.Empty<string>(),
mux));
/// <summary>Initializes a virtual tag actor with the given configuration and dependencies.</summary>
/// <param name="virtualTagId">Unique identifier for the virtual tag.</param>
/// <param name="expression">The expression to evaluate.</param>
/// <param name="evaluator">The evaluator responsible for computing the expression result.</param>
/// <param name="scriptId">The script identifier associated with this tag.</param>
/// <param name="publisherFactory">Optional factory for creating DPS publishers.</param>
/// <param name="dependencyRefs">List of dependency tag references that this tag depends on.</param>
/// <param name="mux">Optional reference to a dependency multiplexer actor.</param>
public VirtualTagActor(
string virtualTagId,
string expression,
@@ -71,6 +87,7 @@ public sealed class VirtualTagActor : ReceiveActor
Receive<DependencyValueChanged>(OnDependencyChanged);
}
/// <inheritdoc />
protected override void PreStart()
{
if (_mux is not null && _dependencyRefs.Count > 0)
@@ -79,6 +96,7 @@ public sealed class VirtualTagActor : ReceiveActor
}
}
/// <inheritdoc />
protected override void PostStop()
{
_mux?.Tell(new DependencyMuxActor.UnregisterInterest(Self));