using Microsoft.Extensions.Options; namespace ScadaLink.HealthMonitoring; /// /// HealthMonitoring-014: validates at /// startup. The interval values are fed straight into new PeriodicTimer(...) /// (and into a division for the offline-check cadence); a zero or negative value /// makes 's constructor throw /// , crashing the /// / / /// hosted service with an opaque exception /// that does not name the offending config key. Registered with /// ValidateOnStart() so a bad ScadaLink:HealthMonitoring section /// fails fast at boot with a clear, key-naming message. /// public sealed class HealthMonitoringOptionsValidator : IValidateOptions { public ValidateOptionsResult Validate(string? name, HealthMonitoringOptions options) { var failures = new List(); if (options.ReportInterval <= TimeSpan.Zero) { failures.Add( $"ScadaLink:HealthMonitoring:ReportInterval must be a positive duration " + $"(was {options.ReportInterval}); it is used directly as a PeriodicTimer period."); } if (options.OfflineTimeout <= TimeSpan.Zero) { failures.Add( $"ScadaLink:HealthMonitoring:OfflineTimeout must be a positive duration " + $"(was {options.OfflineTimeout}); it drives the offline-check PeriodicTimer cadence."); } if (options.CentralOfflineTimeout <= TimeSpan.Zero) { failures.Add( $"ScadaLink:HealthMonitoring:CentralOfflineTimeout must be a positive duration " + $"(was {options.CentralOfflineTimeout})."); } if (options.OfflineTimeout > TimeSpan.Zero && options.CentralOfflineTimeout > TimeSpan.Zero && options.CentralOfflineTimeout < options.OfflineTimeout) { failures.Add( $"ScadaLink:HealthMonitoring:CentralOfflineTimeout ({options.CentralOfflineTimeout}) " + $"must be >= OfflineTimeout ({options.OfflineTimeout}): the synthetic 'central' site has " + "no heartbeat source and is fed only by the slower self-report loop, so it needs at " + "least as much offline grace as a real site."); } return failures.Count > 0 ? ValidateOptionsResult.Fail(failures) : ValidateOptionsResult.Success; } }