using AVEVA.Historian.Client.Grpc; using AVEVA.Historian.Client.Models; using AVEVA.Historian.Client.Wcf; namespace AVEVA.Historian.Client.Protocol; internal sealed class Historian2020ProtocolDialect { private readonly HistorianClientOptions _options; public Historian2020ProtocolDialect(HistorianClientOptions options) { _options = options ?? throw new ArgumentNullException(nameof(options)); } private bool UseGrpc => _options.Transport == HistorianTransport.RemoteGrpc; public IAsyncEnumerable ReadRawAsync(string tag, DateTime startUtc, DateTime endUtc, int maxValues, CancellationToken cancellationToken) { return UseGrpc ? new HistorianGrpcReadOrchestrator(_options).ReadRawAsync(tag, startUtc, endUtc, maxValues, cancellationToken) : new HistorianWcfReadOrchestrator(_options).ReadRawAsync(tag, startUtc, endUtc, maxValues, cancellationToken); } public IAsyncEnumerable ReadAggregateAsync(string tag, DateTime startUtc, DateTime endUtc, RetrievalMode mode, TimeSpan interval, CancellationToken cancellationToken) { return UseGrpc ? new HistorianGrpcReadOrchestrator(_options).ReadAggregateAsync(tag, startUtc, endUtc, mode, interval, cancellationToken) : new HistorianWcfReadOrchestrator(_options).ReadAggregateAsync(tag, startUtc, endUtc, mode, interval, cancellationToken); } public Task> ReadAtTimeAsync(string tag, IReadOnlyList timestampsUtc, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); return UseGrpc ? new HistorianGrpcReadOrchestrator(_options).ReadAtTimeAsync(tag, timestampsUtc, cancellationToken) : new HistorianWcfReadOrchestrator(_options).ReadAtTimeAsync(tag, timestampsUtc, cancellationToken); } public IAsyncEnumerable ReadBlocksAsync(string tag, DateTime startUtc, DateTime endUtc, CancellationToken cancellationToken) { return Missing("StartBlockRetrievalQuery", cancellationToken); } public IAsyncEnumerable ReadEventsAsync(DateTime startUtc, DateTime endUtc, HistorianEventFilter? filter, CancellationToken cancellationToken) { return UseGrpc ? new HistorianGrpcEventOrchestrator(_options).ReadEventsAsync(startUtc, endUtc, filter, cancellationToken) : new HistorianWcfEventOrchestrator(_options).ReadEventsAsync(startUtc, endUtc, filter, cancellationToken); } public Task GetConnectionStatusAsync(CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); // Over gRPC (2023 R2) the status is measured from the gRPC handshake (the WCF synthesize-from- // probe path uses the MDAS binding, which can't reach the gRPC port). Non-gRPC stays on WCF. return UseGrpc ? HistorianGrpcStatusClient.GetConnectionStatusAsync(_options, cancellationToken) : Wcf.HistorianWcfStatusClient.GetConnectionStatusAsync(_options, cancellationToken); } public Task GetStoreForwardStatusAsync(CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); // Over gRPC (2023 R2) we return a MEASURED idle-state: the client actually contacts the server // (GetHistorianConsoleStatus) and reports ErrorOccurred when unreachable. The active-SF buffer // magnitude lives behind the D2 storage-engine console wall and stays false. Non-gRPC transports // keep the synthesized all-false (no SF sidecar to probe). See R4.3 §9.7. return UseGrpc ? HistorianGrpcStatusClient.GetStoreForwardStatusAsync(_options, cancellationToken) : Wcf.HistorianWcfStatusClient.GetStoreForwardStatusAsync(_options, cancellationToken); } public Task GetSystemParameterAsync(string name, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); ArgumentException.ThrowIfNullOrWhiteSpace(name); return UseGrpc ? HistorianGrpcStatusClient.GetSystemParameterAsync(_options, name, cancellationToken) : Wcf.HistorianWcfStatusClient.GetSystemParameterAsync(_options, name, cancellationToken); } public Task GetServerTimeZoneAsync(CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); // 2023 R2 gRPC returns the real server time-zone name; the 2020 WCF // GetSystemTimeZoneName is a client-side stub (empty value), so there is no evidence-backed // value to return on that transport — fail closed rather than hand back an empty string. if (!UseGrpc) { throw new ProtocolEvidenceMissingException("GetSystemTimeZoneName (2020 WCF stub — gRPC/2023R2 only)"); } return HistorianGrpcStatusClient.GetSystemTimeZoneNameAsync(_options, cancellationToken); } public Task GetRuntimeParameterAsync(string name, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); ArgumentException.ThrowIfNullOrWhiteSpace(name); return UseGrpc ? HistorianGrpcStatusClient.GetRuntimeParameterAsync(_options, name, cancellationToken) : Wcf.HistorianWcfStatusClient.GetRuntimeParameterAsync(_options, name, cancellationToken); } public Task> GetTagExtendedPropertiesAsync(string tag, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); ArgumentException.ThrowIfNullOrWhiteSpace(tag); return UseGrpc ? Grpc.HistorianGrpcTagClient.GetTagExtendedPropertiesAsync(_options, tag, cancellationToken) : Wcf.HistorianWcfTagExtendedPropertyClient.GetTagExtendedPropertiesAsync(_options, tag, cancellationToken); } public Task ExecuteSqlCommandAsync(string command, HistorianSqlExecuteOption option, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); ArgumentException.ThrowIfNullOrWhiteSpace(command); return UseGrpc ? Grpc.HistorianGrpcSqlClient.ExecuteSqlCommandAsync(_options, command, option, cancellationToken) : Wcf.HistorianWcfSqlClient.ExecuteSqlCommandAsync(_options, command, option, cancellationToken); } private static async IAsyncEnumerable Missing( string operation, [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken) { await Task.Yield(); cancellationToken.ThrowIfCancellationRequested(); throw new ProtocolEvidenceMissingException(operation); #pragma warning disable CS0162 yield break; #pragma warning restore CS0162 } }