fix(data-connection-layer): resolve DataConnectionLayer-014..017 — real logger for OPC UA client, initial-connect failover, accurate subscribe response, per-tag write-batch results
This commit is contained in:
@@ -228,10 +228,28 @@ public class OpcUaDataConnection : IDataConnection
|
||||
|
||||
public async Task<IReadOnlyDictionary<string, WriteResult>> WriteBatchAsync(IDictionary<string, object?> values, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// DataConnectionLayer-017: a mid-batch fault must not abort the whole batch.
|
||||
// WriteAsync calls EnsureConnected(), which throws InvalidOperationException when
|
||||
// the connection drops partway through; catch per-tag exceptions and record a
|
||||
// failed WriteResult so the caller (including WriteBatchAndWaitAsync) receives a
|
||||
// complete result map. OperationCanceledException is still propagated so a
|
||||
// cancelled batch aborts as a whole — mirrors the DCL-007 fix for ReadBatchAsync.
|
||||
var results = new Dictionary<string, WriteResult>();
|
||||
foreach (var (tagPath, value) in values)
|
||||
{
|
||||
results[tagPath] = await WriteAsync(tagPath, value, cancellationToken);
|
||||
try
|
||||
{
|
||||
results[tagPath] = await WriteAsync(tagPath, value, cancellationToken);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// Cancellation aborts the whole batch — propagate it.
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
results[tagPath] = new WriteResult(false, ex.Message);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
@@ -316,11 +316,24 @@ public class RealOpcUaClientFactory : IOpcUaClientFactory
|
||||
{
|
||||
private readonly OpcUaGlobalOptions _globalOptions;
|
||||
|
||||
// DataConnectionLayer-014: a real logger must be threaded through to every
|
||||
// RealOpcUaClient this factory builds, otherwise the DCL-012 auto-accept-certificate
|
||||
// warning emitted in RealOpcUaClient.ConnectAsync sinks into NullLogger and is never
|
||||
// seen in production. The factory is constructed by DataConnectionFactory, which has
|
||||
// an ILoggerFactory available.
|
||||
private readonly ILoggerFactory _loggerFactory;
|
||||
|
||||
public RealOpcUaClientFactory() : this(new OpcUaGlobalOptions()) { }
|
||||
|
||||
public RealOpcUaClientFactory(OpcUaGlobalOptions globalOptions)
|
||||
: this(globalOptions, NullLoggerFactory.Instance) { }
|
||||
|
||||
public RealOpcUaClientFactory(OpcUaGlobalOptions globalOptions, ILoggerFactory loggerFactory)
|
||||
{
|
||||
_globalOptions = globalOptions;
|
||||
_loggerFactory = loggerFactory;
|
||||
}
|
||||
|
||||
public IOpcUaClient Create() => new RealOpcUaClient(_globalOptions);
|
||||
public IOpcUaClient Create() =>
|
||||
new RealOpcUaClient(_globalOptions, _loggerFactory.CreateLogger<RealOpcUaClient>());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user