feat(dcl): Layer D OpcUaGlobalOptions for app-wide identity + cert paths
New deployment-wide options bound from the "OpcUa" section of appsettings.json: - ApplicationName (default "ScadaLink-DCL") - TrustedIssuerStorePath / TrustedPeerStorePath / RejectedCertificateStorePath Empty paths fall back to Path.GetTempPath()/ScadaLink/pki/* so dev runs work without explicit config — same defaults the hardcoded values previously used. Wiring: - ServiceCollectionExtensions binds OpcUaGlobalOptions to the OpcUa section. - DataConnectionFactory takes IOptions<OpcUaGlobalOptions> and constructs RealOpcUaClientFactory with the snapshot. - RealOpcUaClient(globalOptions) replaces the hardcoded ApplicationName and the three CertificateTrustList store paths in ApplicationConfiguration. - Parameterless ctors on factory and client preserved for the existing test suite (32/32 DCL tests still green).
This commit is contained in:
@@ -17,6 +17,12 @@ public class RealOpcUaClient : IOpcUaClient
|
||||
private readonly Dictionary<string, Action<string, object?, DateTime, uint>> _callbacks = new();
|
||||
private volatile bool _connectionLostFired;
|
||||
private OpcUaConnectionOptions _options = new();
|
||||
private readonly OpcUaGlobalOptions _globalOptions;
|
||||
|
||||
public RealOpcUaClient(OpcUaGlobalOptions? globalOptions = null)
|
||||
{
|
||||
_globalOptions = globalOptions ?? new OpcUaGlobalOptions();
|
||||
}
|
||||
|
||||
public bool IsConnected => _session?.Connected ?? false;
|
||||
public event Action? ConnectionLost;
|
||||
@@ -34,15 +40,17 @@ public class RealOpcUaClient : IOpcUaClient
|
||||
|
||||
var appConfig = new ApplicationConfiguration
|
||||
{
|
||||
ApplicationName = "ScadaLink-DCL",
|
||||
ApplicationName = string.IsNullOrWhiteSpace(_globalOptions.ApplicationName)
|
||||
? "ScadaLink-DCL"
|
||||
: _globalOptions.ApplicationName,
|
||||
ApplicationType = ApplicationType.Client,
|
||||
SecurityConfiguration = new SecurityConfiguration
|
||||
{
|
||||
AutoAcceptUntrustedCertificates = opts.AutoAcceptUntrustedCerts,
|
||||
ApplicationCertificate = new CertificateIdentifier(),
|
||||
TrustedIssuerCertificates = new CertificateTrustList { StorePath = Path.Combine(Path.GetTempPath(), "ScadaLink", "pki", "issuers") },
|
||||
TrustedPeerCertificates = new CertificateTrustList { StorePath = Path.Combine(Path.GetTempPath(), "ScadaLink", "pki", "trusted") },
|
||||
RejectedCertificateStore = new CertificateTrustList { StorePath = Path.Combine(Path.GetTempPath(), "ScadaLink", "pki", "rejected") }
|
||||
TrustedIssuerCertificates = new CertificateTrustList { StorePath = ResolveStorePath(_globalOptions.TrustedIssuerStorePath, "issuers") },
|
||||
TrustedPeerCertificates = new CertificateTrustList { StorePath = ResolveStorePath(_globalOptions.TrustedPeerStorePath, "trusted") },
|
||||
RejectedCertificateStore = new CertificateTrustList { StorePath = ResolveStorePath(_globalOptions.RejectedCertificateStorePath, "rejected") }
|
||||
},
|
||||
ClientConfiguration = new ClientConfiguration { DefaultSessionTimeout = opts.SessionTimeoutMs },
|
||||
TransportQuotas = new TransportQuotas { OperationTimeout = opts.OperationTimeoutMs }
|
||||
@@ -270,6 +278,11 @@ public class RealOpcUaClient : IOpcUaClient
|
||||
"BOTH" => TimestampsToReturn.Both,
|
||||
_ => TimestampsToReturn.Source
|
||||
};
|
||||
|
||||
private static string ResolveStorePath(string configured, string fallbackLeaf) =>
|
||||
string.IsNullOrWhiteSpace(configured)
|
||||
? Path.Combine(Path.GetTempPath(), "ScadaLink", "pki", fallbackLeaf)
|
||||
: configured;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -277,5 +290,13 @@ public class RealOpcUaClient : IOpcUaClient
|
||||
/// </summary>
|
||||
public class RealOpcUaClientFactory : IOpcUaClientFactory
|
||||
{
|
||||
public IOpcUaClient Create() => new RealOpcUaClient();
|
||||
private readonly OpcUaGlobalOptions _globalOptions;
|
||||
|
||||
public RealOpcUaClientFactory() : this(new OpcUaGlobalOptions()) { }
|
||||
public RealOpcUaClientFactory(OpcUaGlobalOptions globalOptions)
|
||||
{
|
||||
_globalOptions = globalOptions;
|
||||
}
|
||||
|
||||
public IOpcUaClient Create() => new RealOpcUaClient(_globalOptions);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using ScadaLink.Commons.Interfaces.Protocol;
|
||||
using ScadaLink.DataConnectionLayer.Adapters;
|
||||
|
||||
@@ -14,12 +15,17 @@ public class DataConnectionFactory : IDataConnectionFactory
|
||||
private readonly ILoggerFactory _loggerFactory;
|
||||
|
||||
public DataConnectionFactory(ILoggerFactory loggerFactory)
|
||||
: this(loggerFactory, Options.Create(new OpcUaGlobalOptions())) { }
|
||||
|
||||
public DataConnectionFactory(ILoggerFactory loggerFactory, IOptions<OpcUaGlobalOptions> opcUaGlobalOptions)
|
||||
{
|
||||
_loggerFactory = loggerFactory;
|
||||
var globalOptions = opcUaGlobalOptions.Value;
|
||||
|
||||
// Register built-in protocols
|
||||
RegisterAdapter("OpcUa", details => new OpcUaDataConnection(
|
||||
new RealOpcUaClientFactory(), _loggerFactory.CreateLogger<OpcUaDataConnection>()));
|
||||
new RealOpcUaClientFactory(globalOptions),
|
||||
_loggerFactory.CreateLogger<OpcUaDataConnection>()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
15
src/ScadaLink.DataConnectionLayer/OpcUaGlobalOptions.cs
Normal file
15
src/ScadaLink.DataConnectionLayer/OpcUaGlobalOptions.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
namespace ScadaLink.DataConnectionLayer;
|
||||
|
||||
/// <summary>
|
||||
/// Deployment-wide OPC UA application identity. Bound from the "OpcUa" section
|
||||
/// of appsettings.json. Per-endpoint behavior lives on OpcUaEndpointConfig.
|
||||
/// Empty paths fall back to a default under Path.GetTempPath() so dev runs
|
||||
/// work without explicit configuration.
|
||||
/// </summary>
|
||||
public class OpcUaGlobalOptions
|
||||
{
|
||||
public string ApplicationName { get; set; } = "ScadaLink-DCL";
|
||||
public string TrustedIssuerStorePath { get; set; } = "";
|
||||
public string TrustedPeerStorePath { get; set; } = "";
|
||||
public string RejectedCertificateStorePath { get; set; } = "";
|
||||
}
|
||||
@@ -9,6 +9,9 @@ public static class ServiceCollectionExtensions
|
||||
services.AddOptions<DataConnectionOptions>()
|
||||
.BindConfiguration("DataConnectionLayer");
|
||||
|
||||
services.AddOptions<OpcUaGlobalOptions>()
|
||||
.BindConfiguration("OpcUa");
|
||||
|
||||
// WP-34: Register the factory for protocol extensibility
|
||||
services.AddSingleton<IDataConnectionFactory, DataConnectionFactory>();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user