Files
ScadaBridge/src/ScadaLink.HealthMonitoring/ServiceCollectionExtensions.cs
T

63 lines
2.8 KiB
C#

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
namespace ScadaLink.HealthMonitoring;
public static class ServiceCollectionExtensions
{
/// <summary>
/// Register site-side health monitoring services (metric collection + periodic reporting).
/// Call this on site nodes only. For central, call AddCentralHealthAggregation() instead.
/// </summary>
public static IServiceCollection AddSiteHealthMonitoring(this IServiceCollection services)
{
AddOptionsValidation(services);
services.AddSingleton<ISiteHealthCollector, SiteHealthCollector>();
services.AddHostedService<HealthReportSender>();
return services;
}
/// <summary>
/// Register shared health monitoring services (safe for both central and site).
/// Does not start the HealthReportSender — call AddSiteHealthMonitoring() on site nodes for that.
/// </summary>
public static IServiceCollection AddHealthMonitoring(this IServiceCollection services)
{
AddOptionsValidation(services);
services.AddSingleton<ISiteHealthCollector, SiteHealthCollector>();
return services;
}
/// <summary>
/// Register central-side health aggregation services. Includes the
/// <see cref="CentralHealthReportLoop"/> that generates a self-report
/// for the central cluster so it appears on /monitoring/health.
/// </summary>
public static IServiceCollection AddCentralHealthAggregation(this IServiceCollection services)
{
AddOptionsValidation(services);
services.AddSingleton<CentralHealthAggregator>();
services.AddSingleton<ICentralHealthAggregator>(sp => sp.GetRequiredService<CentralHealthAggregator>());
services.AddHostedService(sp => sp.GetRequiredService<CentralHealthAggregator>());
services.AddHostedService<CentralHealthReportLoop>();
return services;
}
/// <summary>
/// HealthMonitoring-014: register the <see cref="HealthMonitoringOptionsValidator"/>
/// so a misconfigured <c>ScadaLink:HealthMonitoring</c> section (zero/negative
/// intervals, or a <c>CentralOfflineTimeout</c> shorter than
/// <c>OfflineTimeout</c>) is rejected with a clear, key-naming message when the
/// hosted services resolve their options at startup — rather than crashing
/// later inside a <see cref="PeriodicTimer"/> constructor with an opaque
/// <see cref="ArgumentOutOfRangeException"/>. Idempotent so it is safe when
/// more than one of the registration methods above is called.
/// </summary>
private static void AddOptionsValidation(IServiceCollection services)
{
services.TryAddEnumerable(
ServiceDescriptor.Singleton<IValidateOptions<HealthMonitoringOptions>, HealthMonitoringOptionsValidator>());
}
}