using ZB.MOM.WW.OtOpcUa.Core.Abstractions; namespace ZB.MOM.WW.OtOpcUa.Core.Hosting; /// /// Process-local registry + lifecycle manager for loaded instances /// (decision #65). Phase 1 scaffold — per-process isolation for Tier C drivers (Galaxy, FOCAS) /// is implemented in Phase 2 via named-pipe RPC; this class handles in-process drivers today /// and exposes the same registration interface so the Tier C wrapper can slot in later. /// public sealed class DriverHost : IAsyncDisposable { private readonly Dictionary _drivers = new(); private readonly object _lock = new(); public IReadOnlyCollection RegisteredDriverIds { get { lock (_lock) return [.. _drivers.Keys]; } } public DriverHealth? GetHealth(string driverInstanceId) { lock (_lock) return _drivers.TryGetValue(driverInstanceId, out var d) ? d.GetHealth() : null; } /// /// Look up a registered driver by instance id. Used by the OPC UA server runtime /// (OtOpcUaServer) to instantiate one DriverNodeManager per driver at /// startup. Returns null when the driver is not registered. /// public IDriver? GetDriver(string driverInstanceId) { lock (_lock) return _drivers.TryGetValue(driverInstanceId, out var d) ? d : null; } /// /// Registers the driver and calls . If initialization /// throws, the driver is kept in the registry so the operator can retry; quality on its /// nodes will reflect until Reinitialize succeeds. /// public async Task RegisterAsync(IDriver driver, string driverConfigJson, CancellationToken ct) { ArgumentNullException.ThrowIfNull(driver); var id = driver.DriverInstanceId; lock (_lock) { if (_drivers.ContainsKey(id)) throw new InvalidOperationException($"Driver '{id}' is already registered."); _drivers[id] = driver; } try { await driver.InitializeAsync(driverConfigJson, ct); } catch { // Keep the driver registered — operator will see Faulted state and can reinitialize. throw; } } public async Task UnregisterAsync(string driverInstanceId, CancellationToken ct) { IDriver? driver; lock (_lock) { if (!_drivers.TryGetValue(driverInstanceId, out driver)) return; _drivers.Remove(driverInstanceId); } try { await driver.ShutdownAsync(ct); } catch { /* shutdown is best-effort; logs elsewhere */ } } public async ValueTask DisposeAsync() { List snapshot; lock (_lock) { snapshot = [.. _drivers.Values]; _drivers.Clear(); } foreach (var driver in snapshot) { try { await driver.ShutdownAsync(CancellationToken.None); } catch { /* ignore */ } (driver as IDisposable)?.Dispose(); } } }