namespace ScadaLink.DataConnectionLayer.Adapters; /// /// WP-7: Abstraction over OPC UA client library for testability. /// The real implementation would wrap an OPC UA SDK (e.g., OPC Foundation .NET Standard Library). /// public interface IOpcUaClient : IAsyncDisposable { Task ConnectAsync(string endpointUrl, CancellationToken cancellationToken = default); Task DisconnectAsync(CancellationToken cancellationToken = default); bool IsConnected { get; } /// /// Creates a monitored item subscription for a node. Returns a subscription handle. /// Task CreateSubscriptionAsync( string nodeId, Action onValueChanged, CancellationToken cancellationToken = default); Task RemoveSubscriptionAsync(string subscriptionHandle, CancellationToken cancellationToken = default); Task<(object? Value, DateTime SourceTimestamp, uint StatusCode)> ReadValueAsync( string nodeId, CancellationToken cancellationToken = default); Task WriteValueAsync(string nodeId, object? value, CancellationToken cancellationToken = default); } /// /// Factory for creating IOpcUaClient instances. /// public interface IOpcUaClientFactory { IOpcUaClient Create(); } /// /// Default factory that creates stub OPC UA clients. /// In production, this would create real OPC UA SDK client instances. /// public class DefaultOpcUaClientFactory : IOpcUaClientFactory { public IOpcUaClient Create() => new StubOpcUaClient(); } /// /// Stub OPC UA client for development/testing. A real implementation would /// wrap the OPC Foundation .NET Standard Library. /// internal class StubOpcUaClient : IOpcUaClient { public bool IsConnected { get; private set; } public Task ConnectAsync(string endpointUrl, CancellationToken cancellationToken = default) { IsConnected = true; return Task.CompletedTask; } public Task DisconnectAsync(CancellationToken cancellationToken = default) { IsConnected = false; return Task.CompletedTask; } public Task CreateSubscriptionAsync( string nodeId, Action onValueChanged, CancellationToken cancellationToken = default) { return Task.FromResult(Guid.NewGuid().ToString()); } public Task RemoveSubscriptionAsync(string subscriptionHandle, CancellationToken cancellationToken = default) { return Task.CompletedTask; } public Task<(object? Value, DateTime SourceTimestamp, uint StatusCode)> ReadValueAsync( string nodeId, CancellationToken cancellationToken = default) { return Task.FromResult<(object?, DateTime, uint)>((null, DateTime.UtcNow, 0)); } public Task WriteValueAsync(string nodeId, object? value, CancellationToken cancellationToken = default) { return Task.FromResult(0); // Good status } public ValueTask DisposeAsync() { IsConnected = false; return ValueTask.CompletedTask; } }