Files
histsdk/src/AVEVA.Historian.Client/HistorianClientOptions.cs
T
Joseph Doherty de8d5e91ce feat(wcf): EventReadConnectionModeOverride + cross-platform/bounded C2 spike
Live investigation (direct from a VPN host to the 2023 R2 historian's real WCF
port) showed the certificate transport + NegotiateAuthentication auth work
cross-platform, and that the event-read chain needs the 0x501 event connection
mode for CM_EVENT RegisterTags to succeed (0x402/0x401 fail). Even with
registration succeeding over a window that has events, StartEventQuery returns a
0-row header and long-polls — the same server-side per-connection row gate proven
for gRPC. Adds: EventReadConnectionModeOverride (diagnostic), and spike knobs —
cross-platform cert gate, version-check bypass, per-call timeout, overall budget
with phase-diagnostic dump, connection-mode override.

Claude-Session: https://claude.ai/code/session_012SDSQ3AcaXqPcBtDESBRii
2026-06-26 04:38:58 -04:00

97 lines
4.9 KiB
C#

using AVEVA.Historian.Client.Models;
namespace AVEVA.Historian.Client;
public sealed class HistorianClientOptions
{
public const int DefaultPort = 32568;
/// <summary>Default TCP port of the 2023 R2 Historian Client Access Point gRPC endpoint.</summary>
public const int DefaultGrpcPort = 32565;
public required string Host { get; init; }
public int Port { get; init; } = DefaultPort;
public TimeSpan ConnectTimeout { get; init; } = TimeSpan.FromSeconds(5);
public TimeSpan RequestTimeout { get; init; } = TimeSpan.FromSeconds(30);
public string UserName { get; init; } = string.Empty;
public string Password { get; init; } = string.Empty;
public bool IntegratedSecurity { get; init; }
public bool Compression { get; init; }
public HistorianConnectionKind ConnectionKind { get; init; } = HistorianConnectionKind.Process;
public HistorianTransport Transport { get; init; } = HistorianTransport.LocalPipe;
public string TargetSpn { get; init; } = @"NT SERVICE\aahClientAccessPoint";
/// <summary>
/// When true, the WCF channel factories used by the SDK accept the server's
/// X.509 certificate without chain validation. Useful when connecting to a
/// development / on-prem Historian whose <c>/HistCert</c> endpoint presents an
/// installer-generated self-signed cert that isn't in the local trust store
/// (notably .NET WCF on Linux ignores the system CA bundle for its own
/// X509Chain checks). Default false; do not enable in production where the
/// server's identity matters.
/// </summary>
public bool AllowUntrustedServerCertificate { get; init; }
/// <summary>
/// Overrides the expected DNS identity in the endpoint address — set this to
/// whatever DNS name the server's certificate actually claims (often
/// <c>localhost</c> on installer-generated AVEVA Historian certificates) when
/// connecting via IP address or a hostname that doesn't match the cert SAN/CN.
/// Without this override WCF rejects the channel with
/// "Identity check failed for outgoing message". Has no effect on transports
/// that don't validate a server certificate.
/// </summary>
public string? ServerDnsIdentity { get; init; }
/// <summary>
/// Optional WCF "Via" address (e.g. <c>net.tcp://host:42568</c>). When set, the SDK's WCF
/// channel factories <b>connect</b> to this address while still addressing the SOAP message
/// <c>To</c> the logical endpoint built from <see cref="Host"/>/<see cref="Port"/>. Use this when
/// the Historian is reached through a port-forwarding tunnel or proxy whose local port differs
/// from the server's real service port: point <see cref="Host"/>/<see cref="Port"/> at the
/// server's real endpoint (so the server's WCF AddressFilter matches) and set this to the tunnel
/// endpoint. Has no effect on the gRPC transport. Default null (connect == address).
/// </summary>
public string? ConnectViaAddress { get; init; }
/// <summary>
/// Diagnostic override for the native OpenConnection mode the WCF event-read chain uses (default
/// <c>0x402</c>, read-only process). Set to e.g. <c>0x501</c> (event) or <c>0x401</c> (write-enabled)
/// to probe whether CM_EVENT registration / event-row retrieval needs a different connection type on a
/// 2023 R2 server. Null = the default read-only process mode. Intended for protocol investigation.
/// </summary>
public uint? EventReadConnectionModeOverride { get; init; }
/// <summary>
/// For <see cref="HistorianTransport.RemoteGrpc"/>: when true the channel uses TLS
/// (<c>https://</c>); when false it uses plaintext (<c>http://</c>). Matches the stock
/// 2023 R2 client's <c>securedConnection</c> flag. The TLS host is taken from
/// <see cref="ServerDnsIdentity"/> when set (to match the server certificate's name),
/// otherwise <see cref="Host"/>. When <see cref="AllowUntrustedServerCertificate"/> is
/// true the server certificate chain is not validated. Default false.
/// </summary>
public bool GrpcUseTls { get; init; }
/// <summary>
/// When true (default) the SDK verifies, at connect time, that the Historian server
/// reports the native interface versions its byte serializers were built against
/// (History=11, Retrieval=4, Transaction=2 — evidence from a live AVEVA Historian 2020
/// server). A mismatch throws <see cref="ProtocolEvidenceMissingException"/> rather than
/// risk misparsing version-framed native buffers. Set false only when you have
/// independently confirmed wire compatibility with a different server version — e.g.
/// when bringing up a 2023 R2 gRPC server whose reported interface integers have not yet
/// been captured. See <see cref="HistorianServerVersionGate"/>.
/// </summary>
public bool VerifyServerInterfaceVersion { get; init; } = true;
}