using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Protocol; using ZB.MOM.WW.ScadaBridge.DataConnectionLayer.Adapters; using ZB.MOM.WW.Secrets.Abstractions; namespace ZB.MOM.WW.ScadaBridge.DataConnectionLayer; /// /// Default factory that resolves protocol type strings to IDataConnection adapters. /// Protocol extensibility: register new adapters via the constructor or AddAdapter method. /// public class DataConnectionFactory : IDataConnectionFactory { private readonly Dictionary, IDataConnection>> _factories = new(StringComparer.OrdinalIgnoreCase); private readonly ILoggerFactory _loggerFactory; private readonly ISecretResolver? _secretResolver; /// /// Initializes a new with default OPC UA global options. /// /// Logger factory used when creating protocol adapters. public DataConnectionFactory(ILoggerFactory loggerFactory) : this(loggerFactory, Options.Create(new OpcUaGlobalOptions())) { } /// /// Initializes a new with explicit OPC UA global options. /// /// Logger factory used when creating protocol adapters. /// Global OPC UA options applied to all OPC UA connections created by this factory. /// /// Optional secret resolver threaded into the MxGateway adapter so a per-endpoint /// secret:<name> ApiKey reference is resolved at connect time. DI supplies it on /// the Site container (where is registered); direct /// construction without it keeps literal-key behaviour. /// public DataConnectionFactory( ILoggerFactory loggerFactory, IOptions opcUaGlobalOptions, ISecretResolver? secretResolver = null, IOptions? dataConnectionOptions = null) { _loggerFactory = loggerFactory; _secretResolver = secretResolver; var globalOptions = opcUaGlobalOptions.Value; // Only the MxGateway supervisory-advise window is adapter-level today; the rest of // DataConnectionOptions is consumed by the actor, which is constructed elsewhere. var supervisoryAdviseParallelism = (dataConnectionOptions?.Value ?? new DataConnectionOptions()).MxSupervisoryAdviseParallelism; // Register built-in protocols. // Pass the ILoggerFactory into RealOpcUaClientFactory so // the RealOpcUaClient it builds gets a real logger — without it the // auto-accept-certificate security warning is silently discarded by NullLogger. RegisterAdapter("OpcUa", details => new OpcUaDataConnection( new RealOpcUaClientFactory(globalOptions, _loggerFactory), _loggerFactory.CreateLogger())); // MxGateway: gRPC to the MxAccess Gateway. The RealMxGatewayClient wraps the // ZB.MOM.WW.MxGateway.Client package; per-connection config arrives via the // flat details dict (see MxGatewayEndpointConfigSerializer). RegisterAdapter("MxGateway", details => new MxGatewayDataConnection( new RealMxGatewayClientFactory(_loggerFactory), _loggerFactory.CreateLogger(), _secretResolver, supervisoryAdviseParallelism)); } /// /// Registers a new protocol adapter factory. This is the extension point /// for adding new protocols without modifying existing code. /// /// The case-insensitive protocol type string (e.g. "OpcUa"). /// Factory delegate that creates an from connection details. public void RegisterAdapter(string protocolType, Func, IDataConnection> factory) { _factories[protocolType] = factory; } /// public IDataConnection Create(string protocolType, IDictionary connectionDetails) { if (!_factories.TryGetValue(protocolType, out var factory)) throw new ArgumentException($"Unknown protocol type: {protocolType}. Registered protocols: {string.Join(", ", _factories.Keys)}"); return factory(connectionDetails); } }