using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Kpi; using ZB.MOM.WW.ScadaBridge.SiteCallAudit.Kpi; namespace ZB.MOM.WW.ScadaBridge.SiteCallAudit; /// /// Composition root for the Site Call Audit component. /// /// /// /// Binds (stuck-call detection + KPI /// windowing for the read-side query/KPI handlers). The central→site /// Retry/Discard relay is wired in . /// /// /// The repository (ISiteCallAuditRepository) is registered by /// ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.ServiceCollectionExtensions.AddConfigurationDatabase, /// so callers (the Host on the central node) must also call that. The actor's /// Props are wired up in Host registration. /// /// public static class ServiceCollectionExtensions { /// Configuration section bound to . public const string OptionsSection = "ScadaBridge:SiteCallAudit"; /// /// Registers Site Call Audit services: the /// binding consumed by the actor's read-side KPI/query handlers. The actor's /// Props are still constructed inline in Host wiring. /// /// The service collection to register into. /// The same collection, for chaining. public static IServiceCollection AddSiteCallAudit(this IServiceCollection services) { ArgumentNullException.ThrowIfNull(services); // Eager startup validation (arch-review 08 §1.5): ValidateOnStart makes a // bad ScadaBridge:SiteCallAudit section — a zero/negative cadence that would // spin an Akka scheduler, or a non-positive KPI/retention knob — fail fast // at boot with a clear, key-naming message. services.AddOptions() .BindConfiguration(OptionsSection) .ValidateOnStart(); services.TryAddEnumerable( ServiceDescriptor.Singleton, SiteCallAuditOptionsValidator>()); // KPI History: the central recorder enumerates every registered // IKpiSampleSource each sampling pass; this one snapshots the SiteCalls // KPIs (global + per-site + per-node) into the KpiSample history store. // Scoped so each pass resolves a fresh ISiteCallAuditRepository scope, // mirroring the actor's scope-per-message repository access. // TryAddEnumerable is idempotent — no double-registration if AddSiteCallAudit // is ever called more than once, matching the AuditLog idiom. services.TryAddEnumerable(ServiceDescriptor.Scoped()); return services; } }