using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using ZB.MOM.WW.OtOpcUa.Configuration; using ZB.MOM.WW.OtOpcUa.Configuration.Entities; using ZB.MOM.WW.OtOpcUa.Core.AlarmHistorian; using ZB.MOM.WW.OtOpcUa.Core.Hosting; using ZB.MOM.WW.OtOpcUa.Core.OpcUa; using ZB.MOM.WW.OtOpcUa.Core.ScriptedAlarms; using ZB.MOM.WW.OtOpcUa.Server.OpcUa; namespace ZB.MOM.WW.OtOpcUa.Server.Phase7; /// /// Phase 7 follow-up (task #246) — orchestrates the runtime composition of virtual /// tags + scripted alarms + the historian sink + the driver-bridge that feeds the /// engines. Called by after the bootstrap generation /// loads + before . /// /// /// /// reads Script / VirtualTag / ScriptedAlarm rows from /// the central config DB at the bootstrapped generation, instantiates a /// , runs , /// starts a per registered driver feeding /// 's tag rows into the cache, and returns /// the engine-backed sources for /// . /// /// /// tears down the bridge first (so no more events /// arrive at the cache), then the engines (so cascades + timer ticks stop), then /// the SQLite sink (which flushes any in-flight drain). Lifetime is owned by the /// host; calls dispose during graceful /// shutdown. /// /// public sealed class Phase7Composer : IAsyncDisposable { private readonly IServiceScopeFactory _scopeFactory; private readonly DriverHost _driverHost; private readonly DriverEquipmentContentRegistry _equipmentRegistry; private readonly IAlarmHistorianSink _historianSink; private readonly ILoggerFactory _loggerFactory; private readonly Serilog.ILogger _scriptLogger; private readonly ILogger _logger; private DriverSubscriptionBridge? _bridge; private Phase7ComposedSources _sources = Phase7ComposedSources.Empty; // Sink we constructed in PrepareAsync (vs. the injected fallback). Held so // DisposeAsync can flush + tear down the SQLite drain timer. private SqliteStoreAndForwardSink? _ownedSink; private bool _disposed; public Phase7Composer( IServiceScopeFactory scopeFactory, DriverHost driverHost, DriverEquipmentContentRegistry equipmentRegistry, IAlarmHistorianSink historianSink, ILoggerFactory loggerFactory, Serilog.ILogger scriptLogger, ILogger logger) { _scopeFactory = scopeFactory ?? throw new ArgumentNullException(nameof(scopeFactory)); _driverHost = driverHost ?? throw new ArgumentNullException(nameof(driverHost)); _equipmentRegistry = equipmentRegistry ?? throw new ArgumentNullException(nameof(equipmentRegistry)); _historianSink = historianSink ?? throw new ArgumentNullException(nameof(historianSink)); _loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory)); _scriptLogger = scriptLogger ?? throw new ArgumentNullException(nameof(scriptLogger)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } public Phase7ComposedSources Sources => _sources; public async Task PrepareAsync(long generationId, CancellationToken ct) { if (_disposed) throw new ObjectDisposedException(nameof(Phase7Composer)); // Load the three Phase 7 row sets in one DB scope. List