Executes docs/plans/tcp-connection-validation.md. Full read-only SDK
surface now works against a remote AVEVA Historian over Net.TCP with
Windows transport authentication. 124/124 tests pass; the +10 new live
integration tests in RemoteTcpIntegrationTests.cs are gated by
HISTORIAN_REMOTE_TCP_HOST + HISTORIAN_REMOTE_TCP_TAG.
Two SDK bugs found while executing the plan:
1. Historian2020ProtocolDialect.ReadRawAsync / ReadAggregateAsync /
ReadAtTimeAsync / ReadEventsAsync had explicit
`if (_options.Transport != HistorianTransport.LocalPipe) return Missing<T>`
guards. These were a guardrail from before the orchestrators handled
TCP; the orchestrators have always used CreateBindingPair(options)
which dispatches on transport correctly. Gates removed.
2. HistorianWcfStatusClient and HistorianWcfEventOrchestrator hardcoded
HistorianWcfBindingFactory.CreatePipeEndpointAddress for the auxiliary
services (Stat, Trx, Retr). Worked for LocalPipe; for TCP it produced
an EndpointAddress with scheme net.pipe attached to a TCP binding
(channel factory rejected the URI). Worse, when only the endpoint was
transport-aware, the binding still requested a Windows-transport-
security upgrade that the Stat endpoint over TCP doesn't support
(auxiliaries don't repeat the auth — the Hist session is already
authenticated). Added two helpers:
- HistorianWcfBindingFactory.CreateAuxiliaryEndpointAddress(options, name)
-> net.pipe for LocalPipe, net.tcp for remote
- HistorianWcfBindingFactory.CreateAuxiliaryBinding(options)
-> NamedPipe for LocalPipe, plain MdasNetTcpBinding for remote
Both call sites updated.
Live verification against the remote (probed previously in prior
sessions; reachability re-confirmed today):
- ProbeAsync over RemoteTcpIntegrated and RemoteTcpCertificate
- ReadRawAsync (8 samples returned for SysTimeSec)
- ReadAggregateAsync (TimeWeightedAverage, 1-min cycle, 10-min window)
- ReadAtTimeAsync (3 timestamps)
- BrowseTagNamesAsync (finds the test tag)
- GetTagMetadataAsync (full metadata populated)
- ReadEventsAsync (chain runs without throwing)
- GetConnectionStatusAsync (ConnectedToServer=true)
- GetSystemParameterAsync (HistorianVersion="20,0,000,000")
The default 'NT SERVICE\aahClientAccessPoint' SPN turned out to work
for the remote too — discovery workstream A (SPN-finding) was not
needed in practice.
README and the TCP plan doc updated to reflect the executed status.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
docs/plans/tcp-connection-validation.md (308 lines):
Plan to live-verify the RemoteTcpIntegrated and RemoteTcpCertificate
transports against an actual remote AVEVA Historian. The SDK's
HistorianWcfBindingFactory already builds all three bindings
(CreateMdasNetTcpBinding, CreateMdasNetTcpWindowsBinding,
CreateMdasNetTcpCertificateBinding) but only LocalPipe has been
exercised end-to-end. Wire format is identical across transports;
only WCF binding shape and credential negotiation differ.
Discovery workstreams A/B/C run in parallel (SPN discovery via static
IL + WCF probe; cert binding requirements via wcf-cert-probe; operator
preconditions checklist). D blocks on A. Verification tracks V1-V5 also
parallelize once V1 (ProbeAsync) confirms the transport is reachable.
Includes risks (SPN mismatch, cert chain validation, idle disconnect,
Open2 response delta, compression negotiation, time skew, false-positive
empty reads), success criteria, eight open questions, and explicit
out-of-scope items filed under the existing write-commands and
store-forward plans.
No code changes; no preconditions assumed met. Implementer must satisfy
§2 preconditions (reachable remote Historian, port 32568 open, test
account, SPN registered, etc.) before §4 discovery starts.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>