diff --git a/src/AVEVA.Historian.Client/HistorianClient.cs b/src/AVEVA.Historian.Client/HistorianClient.cs index b2d3092..05e68b3 100644 --- a/src/AVEVA.Historian.Client/HistorianClient.cs +++ b/src/AVEVA.Historian.Client/HistorianClient.cs @@ -132,10 +132,6 @@ public sealed class HistorianClient : IAsyncDisposable public Task 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 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); } diff --git a/src/AVEVA.Historian.Client/Protocol/Historian2020ProtocolDialect.cs b/src/AVEVA.Historian.Client/Protocol/Historian2020ProtocolDialect.cs index 8c5bb04..68bd587 100644 --- a/src/AVEVA.Historian.Client/Protocol/Historian2020ProtocolDialect.cs +++ b/src/AVEVA.Historian.Client/Protocol/Historian2020ProtocolDialect.cs @@ -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 ReadRawAsync(string tag, DateTime startUtc, DateTime endUtc, int maxValues, CancellationToken cancellationToken) { - if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - return Missing("StartDataRetrievalQuery/Full requires Windows for the SSPI path", cancellationToken); - } - - return ReadRawWindowsAsync(tag, startUtc, endUtc, maxValues, cancellationToken); - } - - private IAsyncEnumerable 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 ReadAggregateAsync(string tag, DateTime startUtc, DateTime endUtc, RetrievalMode mode, TimeSpan interval, CancellationToken cancellationToken) { - if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - return Missing($"StartDataRetrievalQuery/{mode} requires Windows for the SSPI path", cancellationToken); - } - - return ReadAggregateWindowsAsync(tag, startUtc, endUtc, mode, interval, cancellationToken); - } - - private IAsyncEnumerable 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> ReadAtTimeAsync(string tag, IReadOnlyList 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 ReadBlocksAsync(string tag, DateTime startUtc, DateTime endUtc, CancellationToken cancellationToken) @@ -71,39 +38,19 @@ internal sealed class Historian2020ProtocolDialect public IAsyncEnumerable ReadEventsAsync(DateTime startUtc, DateTime endUtc, CancellationToken cancellationToken) { - if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - return Missing("StartEventDataRetrievalQuery requires Windows for the SSPI path", cancellationToken); - } - - return ReadEventsWindowsAsync(startUtc, endUtc, cancellationToken); - } - - private IAsyncEnumerable 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 GetConnectionStatusAsync(CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - if (!OperatingSystem.IsWindows()) - { - throw new ProtocolEvidenceMissingException("GetConnectionStatus on non-Windows"); - } return Wcf.HistorianWcfStatusClient.GetConnectionStatusAsync(_options, cancellationToken); } public Task 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); } diff --git a/src/AVEVA.Historian.Client/Wcf/HistorianWcfAuthChainHelper.cs b/src/AVEVA.Historian.Client/Wcf/HistorianWcfAuthChainHelper.cs index 8c32855..02a0e59 100644 --- a/src/AVEVA.Historian.Client/Wcf/HistorianWcfAuthChainHelper.cs +++ b/src/AVEVA.Historian.Client/Wcf/HistorianWcfAuthChainHelper.cs @@ -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; diff --git a/src/AVEVA.Historian.Client/Wcf/HistorianWcfBindingFactory.cs b/src/AVEVA.Historian.Client/Wcf/HistorianWcfBindingFactory.cs index dba9f31..9838178 100644 --- a/src/AVEVA.Historian.Client/Wcf/HistorianWcfBindingFactory.cs +++ b/src/AVEVA.Historian.Client/Wcf/HistorianWcfBindingFactory.cs @@ -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. /// - [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); diff --git a/src/AVEVA.Historian.Client/Wcf/HistorianWcfEventOrchestrator.cs b/src/AVEVA.Historian.Client/Wcf/HistorianWcfEventOrchestrator.cs index c4d34f6..e37d036 100644 --- a/src/AVEVA.Historian.Client/Wcf/HistorianWcfEventOrchestrator.cs +++ b/src/AVEVA.Historian.Client/Wcf/HistorianWcfEventOrchestrator.cs @@ -14,7 +14,6 @@ namespace AVEVA.Historian.Client.Wcf; /// orchestrator returns an empty enumeration but logs the row-buffer length via the /// diagnostic so a follow-up capture can decode the wire shape. /// -[SupportedOSPlatform("windows")] internal sealed class HistorianWcfEventOrchestrator { private const int OpenConnection3MinResponseLength = 5; diff --git a/src/AVEVA.Historian.Client/Wcf/HistorianWcfHistAddressingBehavior.cs b/src/AVEVA.Historian.Client/Wcf/HistorianWcfHistAddressingBehavior.cs index 70b4c0a..8293419 100644 --- a/src/AVEVA.Historian.Client/Wcf/HistorianWcfHistAddressingBehavior.cs +++ b/src/AVEVA.Historian.Client/Wcf/HistorianWcfHistAddressingBehavior.cs @@ -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. /// -[SupportedOSPlatform("windows")] internal sealed class HistorianWcfHistAddressingBehavior : IEndpointBehavior { public void Validate(ServiceEndpoint endpoint) { } diff --git a/src/AVEVA.Historian.Client/Wcf/HistorianWcfMessageCaptureBehavior.cs b/src/AVEVA.Historian.Client/Wcf/HistorianWcfMessageCaptureBehavior.cs index 30e9ffe..714e1d1 100644 --- a/src/AVEVA.Historian.Client/Wcf/HistorianWcfMessageCaptureBehavior.cs +++ b/src/AVEVA.Historian.Client/Wcf/HistorianWcfMessageCaptureBehavior.cs @@ -14,7 +14,6 @@ namespace AVEVA.Historian.Client.Wcf; /// instrument-wcf-{write,read}message native captures and diff offset-by-offset to /// isolate SDK-vs-native differences. NEVER enable in production. /// -[SupportedOSPlatform("windows")] internal sealed class HistorianWcfMessageCaptureBehavior : IEndpointBehavior { public const string CapturePathEnvVar = "AVEVA_HISTORIAN_SDK_WIRE_CAPTURE"; diff --git a/src/AVEVA.Historian.Client/Wcf/HistorianWcfReadOrchestrator.cs b/src/AVEVA.Historian.Client/Wcf/HistorianWcfReadOrchestrator.cs index 721a7a9..aa147ed 100644 --- a/src/AVEVA.Historian.Client/Wcf/HistorianWcfReadOrchestrator.cs +++ b/src/AVEVA.Historian.Client/Wcf/HistorianWcfReadOrchestrator.cs @@ -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; diff --git a/src/AVEVA.Historian.Client/Wcf/HistorianWcfStatusClient.cs b/src/AVEVA.Historian.Client/Wcf/HistorianWcfStatusClient.cs index 591fa12..44af582 100644 --- a/src/AVEVA.Historian.Client/Wcf/HistorianWcfStatusClient.cs +++ b/src/AVEVA.Historian.Client/Wcf/HistorianWcfStatusClient.cs @@ -6,7 +6,6 @@ using AVEVA.Historian.Client.Wcf.Contracts; namespace AVEVA.Historian.Client.Wcf; -[SupportedOSPlatform("windows")] internal static class HistorianWcfStatusClient { public static Task GetSystemParameterAsync( diff --git a/src/AVEVA.Historian.Client/Wcf/HistorianWcfTagWriteOrchestrator.cs b/src/AVEVA.Historian.Client/Wcf/HistorianWcfTagWriteOrchestrator.cs index 0f17a45..ba62a9b 100644 --- a/src/AVEVA.Historian.Client/Wcf/HistorianWcfTagWriteOrchestrator.cs +++ b/src/AVEVA.Historian.Client/Wcf/HistorianWcfTagWriteOrchestrator.cs @@ -16,7 +16,6 @@ namespace AVEVA.Historian.Client.Wcf; /// AddS2 is intentionally NOT here — it is blocked architecturally per /// docs/plans/write-commands-reverse-engineering.md Phase 2 findings. /// -[SupportedOSPlatform("windows")] internal sealed class HistorianWcfTagWriteOrchestrator { private readonly HistorianClientOptions _options;