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