feat(historian): AddServerHistorian DI + Host wiring of IHistorianDataSource

This commit is contained in:
Joseph Doherty
2026-06-14 20:17:10 -04:00
parent e6ec0ad8be
commit a6f1f4ef15
7 changed files with 346 additions and 0 deletions
@@ -42,6 +42,7 @@ public static class ServiceCollectionExtensions
public static IServiceCollection AddOtOpcUaRuntime(this IServiceCollection services)
{
services.TryAddSingleton<IAlarmHistorianSink>(NullAlarmHistorianSink.Instance);
services.TryAddSingleton<IHistorianDataSource>(NullHistorianDataSource.Instance);
services.TryAddSingleton<IDriverFactory>(NullDriverFactory.Instance);
services.TryAddSingleton<IOpcUaAddressSpaceSink>(NullOpcUaAddressSpaceSink.Instance);
services.TryAddSingleton<IServiceLevelPublisher>(NullServiceLevelPublisher.Instance);
@@ -93,6 +94,38 @@ public static class ServiceCollectionExtensions
return services;
}
/// <summary>
/// Config-gated server-side HistoryRead backend. When the <c>ServerHistorian</c> section has
/// <c>Enabled=true</c>, registers the <paramref name="dataSourceFactory"/>-supplied
/// <see cref="IHistorianDataSource"/> (the read-only Wonderware client) overriding the
/// <see cref="NullHistorianDataSource"/> default from <see cref="AddOtOpcUaRuntime"/>. Otherwise
/// a no-op (the Null default stays and the node manager's HistoryRead returns
/// <c>GoodNoData</c>-empty). The data source is injected so the Wonderware client can be supplied
/// by the Host, which is the only project that references it.
/// </summary>
/// <param name="services">The service collection to register with.</param>
/// <param name="configuration">The configuration carrying the <c>ServerHistorian</c> section.</param>
/// <param name="dataSourceFactory">
/// Factory the Host supplies to build the concrete read <see cref="IHistorianDataSource"/>
/// (the Wonderware client) from the bound options + the resolving provider.
/// </param>
/// <returns>The same <paramref name="services"/> instance for chaining.</returns>
public static IServiceCollection AddServerHistorian(
this IServiceCollection services,
IConfiguration configuration,
Func<ServerHistorianOptions, IServiceProvider, IHistorianDataSource> dataSourceFactory)
{
var opts = configuration.GetSection(ServerHistorianOptions.SectionName).Get<ServerHistorianOptions>();
if (opts is not { Enabled: true }) return services; // leave the Null default from AddOtOpcUaRuntime
foreach (var warning in opts.Validate())
Serilog.Log.Logger.ForContext<ServerHistorianOptions>().Warning("ServerHistorian config: {ServerHistorianConfigWarning}", warning);
// Last-registration-wins over the TryAddSingleton Null default seeded by AddOtOpcUaRuntime.
services.AddSingleton<IHistorianDataSource>(sp => dataSourceFactory(opts, sp));
return services;
}
/// <summary>
/// Spawns the per-node driver-role actors on the host's <see cref="ActorSystem"/>:
/// <see cref="DriverHostActor"/> (one per node), <see cref="DbHealthProbeActor"/>