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);
}
}