docs: complete XML doc coverage (returns, summaries, inheritdoc)

Resolve all 622 issues flagged by the enhanced CommentChecker: add missing
<returns> tags (incl. the standard phrasing on non-generic Task methods),
add missing <summary> tags, and replace misused/redundant <inheritdoc/> on
members that override or implement nothing with real documentation.
Documentation-only — no behavior change; solution builds clean.
This commit is contained in:
Joseph Doherty
2026-06-03 11:39:32 -04:00
parent a050170414
commit eabf270d71
208 changed files with 867 additions and 114 deletions
@@ -85,6 +85,7 @@ public class CommunicationService
/// <summary>
/// Gets the central communication actor reference. Throws if not yet initialized.
/// </summary>
/// <returns>The <see cref="IActorRef"/> for the central communication actor.</returns>
public IActorRef GetCommunicationActor()
{
return _centralCommunicationActor
@@ -11,9 +11,13 @@ namespace ZB.MOM.WW.ScadaBridge.Communication.Grpc;
public static class AlarmShelveStateCodec
{
/// <summary>Returns the wire string for a shelve state (the enum member name).</summary>
/// <param name="state">The shelve state to encode as a wire string.</param>
/// <returns>The enum member name as a string (e.g., "Unshelved", "OneShotShelved").</returns>
public static string ToWire(AlarmShelveState state) => state.ToString();
/// <summary>Parses a wire string back to a shelve state; defaults to <see cref="AlarmShelveState.Unshelved"/>.</summary>
/// <param name="wire">The wire string to parse; empty or unrecognized values return <see cref="AlarmShelveState.Unshelved"/>.</param>
/// <returns>The matching <see cref="AlarmShelveState"/>, or <see cref="AlarmShelveState.Unshelved"/> for unrecognized input.</returns>
public static AlarmShelveState Parse(string? wire) =>
Enum.TryParse<AlarmShelveState>(wire, ignoreCase: true, out var state)
? state
@@ -38,6 +38,7 @@ public static class AuditEventDtoMapper
/// fields collapse to empty strings; null integer fields leave the wrapper unset.
/// </summary>
/// <param name="evt">The audit event to project to wire format.</param>
/// <returns>A populated <see cref="AuditEventDto"/> ready for transmission; null strings collapse to empty.</returns>
public static AuditEventDto ToDto(AuditEvent evt)
{
ArgumentNullException.ThrowIfNull(evt);
@@ -92,6 +93,7 @@ public static class AuditEventDtoMapper
/// are intentionally left null — the central ingest actor sets the latter.
/// </summary>
/// <param name="dto">The wire-format DTO to reconstruct into an <see cref="AuditEvent"/>.</param>
/// <returns>A canonical <see cref="AuditEvent"/> with domain fields recomposed into <c>DetailsJson</c>; <c>ForwardState</c> and <c>IngestedAtUtc</c> are left null.</returns>
public static AuditEvent FromDto(AuditEventDto dto)
{
ArgumentNullException.ThrowIfNull(dto);
@@ -46,6 +46,7 @@ public static class SiteCallDtoMapper
/// share one instant. The value sent on the wire is informational only.
/// </remarks>
/// <param name="dto">The wire-format site call DTO to map.</param>
/// <returns>A <see cref="SiteCall"/> entity populated from the DTO fields.</returns>
public static SiteCall FromDto(SiteCallOperationalDto dto)
{
ArgumentNullException.ThrowIfNull(dto);
@@ -100,6 +100,7 @@ public class SiteStreamGrpcClient : IAsyncDisposable, IDisposable
/// Creates a test-only instance that has no gRPC channel. Used to test
/// Unsubscribe and Dispose behavior without needing a real endpoint.
/// </summary>
/// <returns>A <see cref="SiteStreamGrpcClient"/> with no channel or client, for testing only.</returns>
internal static SiteStreamGrpcClient CreateForTesting() => new();
/// <summary>
@@ -153,6 +154,7 @@ public class SiteStreamGrpcClient : IAsyncDisposable, IDisposable
/// <param name="onEvent">Callback invoked for each domain event received from the stream.</param>
/// <param name="onError">Callback invoked when the subscription encounters an error.</param>
/// <param name="ct">Cancellation token to stop the subscription.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
public virtual async Task SubscribeAsync(
string correlationId,
string instanceUniqueName,
@@ -257,6 +259,8 @@ public class SiteStreamGrpcClient : IAsyncDisposable, IDisposable
};
/// <summary>Parses the wire "kind" string back to <see cref="AlarmKind"/>; defaults to Computed.</summary>
/// <param name="kind">The wire "kind" string from the gRPC payload; null or unrecognised defaults to <see cref="AlarmKind.Computed"/>.</param>
/// <returns>The parsed <see cref="AlarmKind"/>, or <see cref="AlarmKind.Computed"/> when the value is null or unrecognised.</returns>
internal static AlarmKind ParseAlarmKind(string? kind) =>
System.Enum.TryParse<AlarmKind>(kind, ignoreCase: true, out var k) ? k : AlarmKind.Computed;
@@ -320,6 +324,7 @@ public class SiteStreamGrpcClient : IAsyncDisposable, IDisposable
/// <summary>
/// Asynchronously disposes of the gRPC client and all subscriptions.
/// </summary>
/// <returns>A completed <see cref="ValueTask"/> after all subscriptions and the gRPC channel have been released.</returns>
public virtual ValueTask DisposeAsync()
{
ReleaseResources();
@@ -48,6 +48,7 @@ public class SiteStreamGrpcClientFactory : IAsyncDisposable, IDisposable
/// </summary>
/// <param name="siteIdentifier">Unique site identifier used as the cache key.</param>
/// <param name="grpcEndpoint">gRPC endpoint the returned client must be bound to.</param>
/// <returns>The cached or newly-created client bound to <paramref name="grpcEndpoint"/>.</returns>
public virtual SiteStreamGrpcClient GetOrCreate(string siteIdentifier, string grpcEndpoint)
{
// Fast path: a client is cached and already bound to the requested endpoint.
@@ -82,6 +83,7 @@ public class SiteStreamGrpcClientFactory : IAsyncDisposable, IDisposable
/// caching and disposal machinery.
/// </summary>
/// <param name="grpcEndpoint">gRPC endpoint the new client will connect to.</param>
/// <returns>A new <see cref="SiteStreamGrpcClient"/> connected to <paramref name="grpcEndpoint"/>.</returns>
protected virtual SiteStreamGrpcClient CreateClient(string grpcEndpoint)
{
var logger = _loggerFactory.CreateLogger<SiteStreamGrpcClient>();
@@ -96,6 +98,7 @@ public class SiteStreamGrpcClientFactory : IAsyncDisposable, IDisposable
/// cached gRPC client does not linger for the life of the process.
/// </summary>
/// <param name="siteIdentifier">Unique site identifier whose client should be removed.</param>
/// <returns>A task that completes when the cached client has been removed and disposed.</returns>
public async Task RemoveSiteAsync(string siteIdentifier)
{
if (_clients.TryRemove(siteIdentifier, out var client))
@@ -107,6 +110,7 @@ public class SiteStreamGrpcClientFactory : IAsyncDisposable, IDisposable
/// <summary>
/// Asynchronously disposes all cached clients and clears the cache.
/// </summary>
/// <returns>A value task that completes when all clients have been disposed.</returns>
public async ValueTask DisposeAsync()
{
foreach (var client in _clients.Values)
@@ -301,30 +301,7 @@ public class SiteStreamGrpcServer : SiteStreamService.SiteStreamServiceBase
}
}
/// <summary>
/// Audit Log (#23) M2 site→central push RPC. Decodes a site batch into
/// <see cref="AuditEvent"/> rows, Asks the central <c>AuditLogIngestActor</c>
/// proxy to persist them, and echoes the accepted EventIds back so the site
/// can flip its local rows to <c>Forwarded</c>.
/// </summary>
/// <remarks>
/// <para>
/// The DTO→entity conversion uses the shared <see cref="AuditEventDtoMapper"/>
/// (hosted in <c>ZB.MOM.WW.ScadaBridge.Communication</c> so both this server and
/// <c>ZB.MOM.WW.ScadaBridge.AuditLog</c> share one implementation without a
/// project-reference cycle).
/// </para>
/// <para>
/// When <see cref="_auditIngestActor"/> is not yet wired (host startup
/// race window), the RPC returns an empty <see cref="IngestAck"/> rather
/// than failing — the site treats the missing ack as a transient outcome
/// and retries on the next drain, which is the desired idempotent
/// behaviour.
/// </para>
/// </remarks>
/// <inheritdoc />
/// <param name="request">The audit event batch to ingest.</param>
/// <param name="context">The server call context.</param>
public override async Task<IngestAck> IngestAuditEvents(
AuditEventBatch request,
ServerCallContext context)
@@ -380,22 +357,7 @@ public class SiteStreamGrpcServer : SiteStreamService.SiteStreamServiceBase
return ack;
}
/// <summary>
/// Audit Log (#23) M3 site→central combined-telemetry push RPC. Decodes a
/// batch of <see cref="CachedTelemetryPacket"/> entries into matched
/// (AuditEvent, SiteCall) pairs, Asks the central <c>AuditLogIngestActor</c>
/// proxy to persist them in dual-write transactions, and echoes the
/// AuditEvent EventIds that committed back so the site can flip its local
/// rows to <c>Forwarded</c>.
/// </summary>
/// <remarks>
/// Same wiring-incomplete fallback as <see cref="IngestAuditEvents"/>: when
/// the actor proxy has not been set the RPC replies with an empty ack so
/// sites treat the outcome as transient and retry, never a hard fault.
/// </remarks>
/// <inheritdoc />
/// <param name="request">The cached telemetry batch to ingest.</param>
/// <param name="context">The server call context.</param>
public override async Task<IngestAck> IngestCachedTelemetry(
CachedTelemetryBatch request,
ServerCallContext context)
@@ -445,27 +407,7 @@ public class SiteStreamGrpcServer : SiteStreamService.SiteStreamServiceBase
return ack;
}
/// <summary>
/// Audit Log (#23) M6 reconciliation pull RPC. Central asks the site for any
/// AuditLog rows whose <c>OccurredAtUtc &gt;= since_utc</c> and whose
/// <c>ForwardState</c> is still <c>Pending</c> or <c>Forwarded</c> (i.e. not
/// yet confirmed reconciled), bounded by <c>batch_size</c>. The site responds
/// with the rows AND flips them to
/// <see cref="ZB.MOM.WW.ScadaBridge.Commons.Types.Enums.AuditForwardState.Reconciled"/>
/// AFTER serializing the response. The flip is best-effort — if it fails
/// (e.g. SQLite disposed mid-call), rows stay Pending/Forwarded and central
/// pulls them again on the next reconciliation cycle. Idempotent.
/// </summary>
/// <remarks>
/// When <see cref="_siteAuditQueue"/> is not wired (central-only host or a
/// composition-root test exercising the server in isolation) the RPC returns
/// an empty response — central treats that as "nothing to ship" and retries
/// on its next cycle, which is the same self-healing semantics as the
/// SetAuditIngestActor wiring race window.
/// </remarks>
/// <inheritdoc />
/// <param name="request">The pull request with time bounds and batch size.</param>
/// <param name="context">The server call context.</param>
public override async Task<PullAuditEventsResponse> PullAuditEvents(
PullAuditEventsRequest request,
ServerCallContext context)
@@ -7,6 +7,7 @@ public static class ServiceCollectionExtensions
{
/// <summary>Registers communication services including options, <see cref="CommunicationService"/>, gRPC client factory, and debug stream.</summary>
/// <param name="services">The DI service collection to register services into.</param>
/// <returns>The same <see cref="IServiceCollection"/> to allow chaining.</returns>
public static IServiceCollection AddCommunication(this IServiceCollection services)
{
services.AddOptions<CommunicationOptions>()
@@ -21,6 +22,7 @@ public static class ServiceCollectionExtensions
/// <summary>Hook for registering additional DI services needed by communication actors; actor creation itself happens inside <c>AkkaHostedService</c>.</summary>
/// <param name="services">The DI service collection to register services into.</param>
/// <returns>The same <see cref="IServiceCollection"/> to allow chaining.</returns>
public static IServiceCollection AddCommunicationActors(this IServiceCollection services)
{
// Actor registration happens in AkkaHostedService.RegisterCentralActors/RegisterSiteActors.