Drop SupportedOSPlatform gates; SDK now runs on Linux
The dialect / orchestrators were defensively gated on Windows because
HistorianSspiClient previously P/Invoked InitializeSecurityContextW. With
that replaced by NegotiateAuthentication (cross-platform), the gates are
unnecessary. Removed them from:
- Historian2020ProtocolDialect (4 read paths + 3 status helpers)
- HistorianClient.EnsureTagAsync / DeleteTagAsync
- HistorianWcf{Auth,Read,Event,Status,TagWrite}Orchestrator/Helper
- HistorianWcf{HistAddressing,MessageCapture}Behavior
- HistorianWcfBindingFactory (with #pragma on the Named-Pipe builder
which still requires Windows at the BCL level)
Runtime constraint: LocalPipe and RemoteTcpIntegrated transports still
require Windows because NetNamedPipeBinding and the Windows transport
security binding are Windows-only at the BCL level. RemoteTcpCertificate
is now usable from Linux, and ProbeAsync is verified working from a
Debian client (10.100.0.35) against the Windows Historian (10.100.0.48).
171/171 tests still pass on Windows.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -132,10 +132,6 @@ public sealed class HistorianClient : IAsyncDisposable
|
||||
public Task<bool> EnsureTagAsync(HistorianTagDefinition definition, CancellationToken cancellationToken = default)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(definition);
|
||||
if (!OperatingSystem.IsWindows())
|
||||
{
|
||||
throw new ProtocolEvidenceMissingException("EnsureTagAsync requires Windows for the SSPI auth path");
|
||||
}
|
||||
return new HistorianWcfTagWriteOrchestrator(_options).EnsureTagAsync(definition, cancellationToken);
|
||||
}
|
||||
|
||||
@@ -150,10 +146,6 @@ public sealed class HistorianClient : IAsyncDisposable
|
||||
public Task<bool> DeleteTagAsync(string tagName, CancellationToken cancellationToken = default)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(tagName);
|
||||
if (!OperatingSystem.IsWindows())
|
||||
{
|
||||
throw new ProtocolEvidenceMissingException("DeleteTagAsync requires Windows for the SSPI auth path");
|
||||
}
|
||||
return new HistorianWcfTagWriteOrchestrator(_options).DeleteTagAsync(tagName, cancellationToken);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using AVEVA.Historian.Client.Models;
|
||||
using AVEVA.Historian.Client.Wcf;
|
||||
|
||||
@@ -15,53 +14,21 @@ internal sealed class Historian2020ProtocolDialect
|
||||
|
||||
public IAsyncEnumerable<HistorianSample> ReadRawAsync(string tag, DateTime startUtc, DateTime endUtc, int maxValues, CancellationToken cancellationToken)
|
||||
{
|
||||
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
return Missing<HistorianSample>("StartDataRetrievalQuery/Full requires Windows for the SSPI path", cancellationToken);
|
||||
}
|
||||
|
||||
return ReadRawWindowsAsync(tag, startUtc, endUtc, maxValues, cancellationToken);
|
||||
}
|
||||
|
||||
private IAsyncEnumerable<HistorianSample> ReadRawWindowsAsync(string tag, DateTime startUtc, DateTime endUtc, int maxValues, CancellationToken cancellationToken)
|
||||
{
|
||||
#pragma warning disable CA1416 // Validated by RuntimeInformation.IsOSPlatform check above.
|
||||
HistorianWcfReadOrchestrator orchestrator = new(_options);
|
||||
return orchestrator.ReadRawAsync(tag, startUtc, endUtc, maxValues, cancellationToken);
|
||||
#pragma warning restore CA1416
|
||||
}
|
||||
|
||||
public IAsyncEnumerable<HistorianAggregateSample> ReadAggregateAsync(string tag, DateTime startUtc, DateTime endUtc, RetrievalMode mode, TimeSpan interval, CancellationToken cancellationToken)
|
||||
{
|
||||
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
return Missing<HistorianAggregateSample>($"StartDataRetrievalQuery/{mode} requires Windows for the SSPI path", cancellationToken);
|
||||
}
|
||||
|
||||
return ReadAggregateWindowsAsync(tag, startUtc, endUtc, mode, interval, cancellationToken);
|
||||
}
|
||||
|
||||
private IAsyncEnumerable<HistorianAggregateSample> ReadAggregateWindowsAsync(string tag, DateTime startUtc, DateTime endUtc, RetrievalMode mode, TimeSpan interval, CancellationToken cancellationToken)
|
||||
{
|
||||
#pragma warning disable CA1416
|
||||
HistorianWcfReadOrchestrator orchestrator = new(_options);
|
||||
return orchestrator.ReadAggregateAsync(tag, startUtc, endUtc, mode, interval, cancellationToken);
|
||||
#pragma warning restore CA1416
|
||||
}
|
||||
|
||||
public Task<IReadOnlyList<HistorianSample>> ReadAtTimeAsync(string tag, IReadOnlyList<DateTime> timestampsUtc, CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
throw new ProtocolEvidenceMissingException("StartDataRetrievalQuery/Interpolated at-time requires Windows for the SSPI path");
|
||||
}
|
||||
|
||||
#pragma warning disable CA1416
|
||||
HistorianWcfReadOrchestrator orchestrator = new(_options);
|
||||
return orchestrator.ReadAtTimeAsync(tag, timestampsUtc, cancellationToken);
|
||||
#pragma warning restore CA1416
|
||||
}
|
||||
|
||||
public IAsyncEnumerable<HistorianBlock> ReadBlocksAsync(string tag, DateTime startUtc, DateTime endUtc, CancellationToken cancellationToken)
|
||||
@@ -71,39 +38,19 @@ internal sealed class Historian2020ProtocolDialect
|
||||
|
||||
public IAsyncEnumerable<HistorianEvent> ReadEventsAsync(DateTime startUtc, DateTime endUtc, CancellationToken cancellationToken)
|
||||
{
|
||||
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
return Missing<HistorianEvent>("StartEventDataRetrievalQuery requires Windows for the SSPI path", cancellationToken);
|
||||
}
|
||||
|
||||
return ReadEventsWindowsAsync(startUtc, endUtc, cancellationToken);
|
||||
}
|
||||
|
||||
private IAsyncEnumerable<HistorianEvent> ReadEventsWindowsAsync(DateTime startUtc, DateTime endUtc, CancellationToken cancellationToken)
|
||||
{
|
||||
#pragma warning disable CA1416
|
||||
HistorianWcfEventOrchestrator orchestrator = new(_options);
|
||||
return orchestrator.ReadEventsAsync(startUtc, endUtc, cancellationToken);
|
||||
#pragma warning restore CA1416
|
||||
}
|
||||
|
||||
public Task<HistorianConnectionStatus> GetConnectionStatusAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
if (!OperatingSystem.IsWindows())
|
||||
{
|
||||
throw new ProtocolEvidenceMissingException("GetConnectionStatus on non-Windows");
|
||||
}
|
||||
return Wcf.HistorianWcfStatusClient.GetConnectionStatusAsync(_options, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<HistorianStoreForwardStatus> GetStoreForwardStatusAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
if (!OperatingSystem.IsWindows())
|
||||
{
|
||||
throw new ProtocolEvidenceMissingException("GetStoreForwardStatus on non-Windows");
|
||||
}
|
||||
return Wcf.HistorianWcfStatusClient.GetStoreForwardStatusAsync(_options, cancellationToken);
|
||||
}
|
||||
|
||||
@@ -111,10 +58,6 @@ internal sealed class Historian2020ProtocolDialect
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(name);
|
||||
if (!OperatingSystem.IsWindows())
|
||||
{
|
||||
throw new ProtocolEvidenceMissingException("GetSystemParameter on non-Windows");
|
||||
}
|
||||
return Wcf.HistorianWcfStatusClient.GetSystemParameterAsync(_options, name, cancellationToken);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ using AVEVA.Historian.Client.Wcf.Contracts;
|
||||
|
||||
namespace AVEVA.Historian.Client.Wcf;
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
internal static class HistorianWcfAuthChainHelper
|
||||
{
|
||||
private const int OpenConnection3MinResponseLength = 5;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Net.Security;
|
||||
using System.Runtime.Versioning;
|
||||
using System.ServiceModel;
|
||||
using System.ServiceModel.Channels;
|
||||
|
||||
@@ -92,7 +91,10 @@ internal static class HistorianWcfBindingFactory
|
||||
};
|
||||
}
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
// NetNamedPipeBinding is Windows-only at the BCL level; calling this on Linux
|
||||
// throws PlatformNotSupportedException at runtime. Cross-platform callers should
|
||||
// choose Transport = RemoteTcpCertificate (or RemoteTcpIntegrated on Windows).
|
||||
#pragma warning disable CA1416 // Documented Windows-only entry point
|
||||
public static Binding CreateMdasNetNamedPipeBinding(TimeSpan timeout, int maxBufferSize = 64 * 1024 * 1024)
|
||||
{
|
||||
NetNamedPipeBinding nativeShape = new()
|
||||
@@ -121,8 +123,8 @@ internal static class HistorianWcfBindingFactory
|
||||
SendTimeout = timeout
|
||||
};
|
||||
}
|
||||
#pragma warning restore CA1416
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
public static (Binding HistoryBinding, EndpointAddress HistoryEndpoint, Binding RetrievalBinding, EndpointAddress RetrievalEndpoint) CreateBindingPair(
|
||||
HistorianClientOptions options)
|
||||
{
|
||||
@@ -188,7 +190,9 @@ internal static class HistorianWcfBindingFactory
|
||||
/// transport-security upgrade that the History service negotiates; the established session
|
||||
/// authenticates the client already.
|
||||
/// </summary>
|
||||
[SupportedOSPlatform("windows")]
|
||||
// NetNamedPipeBinding / WindowsStreamSecurityBindingElement are Windows-only at the
|
||||
// BCL level; calling this on Linux throws PlatformNotSupportedException at runtime.
|
||||
// Cross-platform callers should choose Transport = RemoteTcpCertificate.
|
||||
public static Binding CreateAuxiliaryBinding(HistorianClientOptions options)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(options);
|
||||
|
||||
@@ -14,7 +14,6 @@ namespace AVEVA.Historian.Client.Wcf;
|
||||
/// orchestrator returns an empty enumeration but logs the row-buffer length via the
|
||||
/// <see cref="LastResultBufferLength"/> diagnostic so a follow-up capture can decode the wire shape.
|
||||
/// </remarks>
|
||||
[SupportedOSPlatform("windows")]
|
||||
internal sealed class HistorianWcfEventOrchestrator
|
||||
{
|
||||
private const int OpenConnection3MinResponseLength = 5;
|
||||
|
||||
@@ -14,7 +14,6 @@ namespace AVEVA.Historian.Client.Wcf;
|
||||
/// endpoint address, but the captured SDK bytes show it absent — re-asserting it
|
||||
/// here closes the gap.
|
||||
/// </remarks>
|
||||
[SupportedOSPlatform("windows")]
|
||||
internal sealed class HistorianWcfHistAddressingBehavior : IEndpointBehavior
|
||||
{
|
||||
public void Validate(ServiceEndpoint endpoint) { }
|
||||
|
||||
@@ -14,7 +14,6 @@ namespace AVEVA.Historian.Client.Wcf;
|
||||
/// <c>instrument-wcf-{write,read}message</c> native captures and diff offset-by-offset to
|
||||
/// isolate SDK-vs-native differences. NEVER enable in production.
|
||||
/// </remarks>
|
||||
[SupportedOSPlatform("windows")]
|
||||
internal sealed class HistorianWcfMessageCaptureBehavior : IEndpointBehavior
|
||||
{
|
||||
public const string CapturePathEnvVar = "AVEVA_HISTORIAN_SDK_WIRE_CAPTURE";
|
||||
|
||||
@@ -7,7 +7,6 @@ using AVEVA.Historian.Client.Wcf.Contracts;
|
||||
|
||||
namespace AVEVA.Historian.Client.Wcf;
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
internal sealed class HistorianWcfReadOrchestrator
|
||||
{
|
||||
private const ushort StartQueryRequestType = HistorianDataQueryProtocol.QueryRequestTypeData;
|
||||
|
||||
@@ -6,7 +6,6 @@ using AVEVA.Historian.Client.Wcf.Contracts;
|
||||
|
||||
namespace AVEVA.Historian.Client.Wcf;
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
internal static class HistorianWcfStatusClient
|
||||
{
|
||||
public static Task<string?> GetSystemParameterAsync(
|
||||
|
||||
@@ -16,7 +16,6 @@ namespace AVEVA.Historian.Client.Wcf;
|
||||
/// AddS2 is intentionally NOT here — it is blocked architecturally per
|
||||
/// <c>docs/plans/write-commands-reverse-engineering.md</c> Phase 2 findings.
|
||||
/// </remarks>
|
||||
[SupportedOSPlatform("windows")]
|
||||
internal sealed class HistorianWcfTagWriteOrchestrator
|
||||
{
|
||||
private readonly HistorianClientOptions _options;
|
||||
|
||||
Reference in New Issue
Block a user