0ffd4bdbea
Comment/XML-doc changes only, no code logic: - strip project-tracking references (WP*, P*-*, row-NN, #NN, Task N) from comments, keeping the explanatory rationale - add missing summaries, <param>, <returns>, <typeparam> and <inheritdoc/> - replace misused <inheritdoc/> with real docs; where <inheritdoc/> was combined with useful remarks, move the rationale into // comments - drop stale replicationActor <param> on DeploymentManagerActor ctor Remaining 5 are false positives (CWE-1236 x2, failures-1, year-0001, M365). Build 0 errors / 0 warnings.
87 lines
4.9 KiB
C#
87 lines
4.9 KiB
C#
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
|
using Microsoft.Extensions.Logging;
|
|
using Microsoft.Extensions.Options;
|
|
using ZB.MOM.WW.ScadaBridge.Communication.Grpc;
|
|
|
|
namespace ZB.MOM.WW.ScadaBridge.Communication;
|
|
|
|
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)
|
|
{
|
|
// Canonical section is ScadaBridge:Communication — the section every real
|
|
// appsettings actually writes (arch-review 08 round 2 NF8). The Host used to
|
|
// supply the real values via a duplicate Configure<> binding; that duplicate is
|
|
// gone, so AddCommunication must bind the canonical section itself.
|
|
services.AddOptions<CommunicationOptions>()
|
|
.BindConfiguration("ScadaBridge:Communication")
|
|
.ValidateOnStart();
|
|
services.TryAddEnumerable(
|
|
ServiceDescriptor.Singleton<IValidateOptions<CommunicationOptions>, CommunicationOptionsValidator>());
|
|
|
|
services.AddSingleton<CommunicationService>();
|
|
|
|
// Explicit factory rather than AddSingleton<T>(): the ISitePskProvider dependency is
|
|
// optional (central registers one, a site node does not), and constructor selection
|
|
// over a nullable interface parameter is exactly the case the container cannot decide
|
|
// for itself — GetService returns null cleanly where constructor injection would throw.
|
|
services.AddSingleton(sp => new SiteStreamGrpcClientFactory(
|
|
sp.GetRequiredService<ILoggerFactory>(),
|
|
sp.GetRequiredService<IOptions<CommunicationOptions>>(),
|
|
sp.GetService<ISitePskProvider>()));
|
|
services.AddSingleton<DebugStreamService>();
|
|
|
|
// Aggregated live alarm cache transient, in-memory, shared
|
|
// per-site aggregator backing the operator Alarm Summary page. Singleton so every
|
|
// Blazor circuit shares one aggregator per site; started/stopped by viewer
|
|
// reference-count. No persistence (locked [PERM]: no central alarm store).
|
|
services.AddSingleton<SiteAlarmLiveCacheService>();
|
|
services.AddSingleton<ISiteAlarmLiveCache>(sp => sp.GetRequiredService<SiteAlarmLiveCacheService>());
|
|
|
|
return services;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Registers the communication services that only a CENTRAL node can construct.
|
|
/// Call alongside <see cref="AddCommunication"/> from the Central composition root only.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// <see cref="ReconcileService"/> is scoped and takes <c>IDeploymentManagerRepository</c> —
|
|
/// an EF repository registered by <c>AddConfigurationDatabase</c>, which only central calls.
|
|
/// It used to live in the shared <see cref="AddCommunication"/> on the argument that it was
|
|
/// "harmless on site hosts: only the central actor ever resolves it". That is true of the
|
|
/// runtime behaviour but not of the container: the registration was UNCONSTRUCTIBLE on a
|
|
/// site node, so <c>ValidateOnBuild</c> failed the whole site graph and the site branch
|
|
/// could not have scope validation turned on at all (deferred register row 50). A
|
|
/// registration nothing can ever build is not harmless — it is the thing that stops the
|
|
/// validator from being usable, and it hides whatever real defect the validator would
|
|
/// otherwise have found.
|
|
/// </para>
|
|
/// </remarks>
|
|
/// <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 AddCentralCommunication(this IServiceCollection services)
|
|
{
|
|
// Startup reconciliation handler — scoped (holds scoped repositories), resolved
|
|
// per-request by CentralCommunicationActor inside a DI scope.
|
|
services.AddScoped<ReconcileService>();
|
|
|
|
return services;
|
|
}
|
|
|
|
/// <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.
|
|
// This method is a hook for any additional DI registrations needed by the communication actors.
|
|
return services;
|
|
}
|
|
}
|