Files
histsdk/docs/reverse-engineering/wcf-status-localhost.md
T
Joseph Doherty 04ea0b9a1f R1.3 GetServerTimeZoneAsync over gRPC (live-verified); R1.4 bounded out on gRPC
Live-probed both R1.3 and R1.4 against a real 2023 R2 server over the gRPC
StatusService; implemented the one that carries an evidence-backed value.

R1.3 GetServerTimeZoneAsync — SHIPPED:
- StatusService.GetSystemTimeZoneName(uiHandle) returns the real server zone
  over RemoteGrpc (the 2020 WCF op is a client-side stub returning empty).
- HistorianGrpcStatusClient.GetSystemTimeZoneNameAsync -> dialect routing ->
  public HistorianClient.GetServerTimeZoneAsync. Non-gRPC transports fail
  closed with ProtocolEvidenceMissingException (no empty-string lie).
- Golden message-shape unit test + non-gRPC guardrail unit test + gated live
  test. 271 unit tests pass.

R1.4 GetHistorianInfoAsync (EventStorageMode) — bounded out on gRPC too:
- gRPC GetHistorianInfo is the same named-value query as 2020 WCF (only
  HistorianVersion resolves); EventStorageMode + 7 variants fail on both
  GetHistorianInfo and GetSystemParameter. The 518-byte struct is filled by a
  native vtable+648 HCAL call, not the gRPC op (per the 2023 R2 decompile), so
  the field is never on the wire. Not shipped on any transport. Closes the
  roadmap's open "build against a live 2023 R2 server" caveat.

Also correct the stale M3 roadmap section: D2 already proved
Transaction.AddNonStreamValues* rides the storage-engine pipe (STransactPipeClient2
-> aaStorageEngine), not WCF — same wall as R4.2 — so M3-over-WCF is blocked, not
"the path that is NOT the gated cache push".

Docs: hcal-roadmap.md, wcf-historian-info.md, wcf-status-localhost.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B6mcaT2PjRFKcogzp9UkfC
2026-06-21 17:24:10 -04:00

7.0 KiB

WCF Status Evidence

Commands:

dotnet run --no-build --project tools\AVEVA.Historian.ReverseEngineering -- wcf-status localhost 32568
dotnet run --no-build --project tools\AVEVA.Historian.ReverseEngineering -- wcf-status localhost 32568 Version

Confirmed:

  • The local status endpoint is net.tcp://localhost:32568/Stat.
  • IStatusServiceContract2 is a static WCF contract named Stat in namespace aa. The managed definitions now include GetSystemParameter, GETHI, PNGS, and PNGP.
  • GetInterfaceVersion returns code 0, version 0 on the local 2020 install.
  • The decompiled CStatusConnectionWCF.GetServerTime implementation is a WCF-path stub that returns success without calling the Stat service. The managed direct call likewise returns code 0 with size 0 and no buffer.

Observed sanitized localhost results:

  • GetSystemTimeZoneName(handle: 0) returns code 4 and no value.
  • IsDBCaseSensitive(handle: 0) returns code 4.
  • GetSystemParameter(handle: 0, "Version") returns false with no error buffer.

Re-tested 2026-06-20 with a real authenticated client handle (full Open2 auth chain), not handle: 0:

  • GetSystemParameter(handle, "HistorianVersion") → real version string (works; shipped as GetSystemParameterAsync).
  • GetSystemTimeZoneName(handle) → return code 0x00000000 (success) but an empty value string. Same channel/handle that makes GetSystemParameter return real data, so this is the op's own behavior, not an auth/marshalling gap. GetSystemTimeZoneName is a member of the GetServerTime stub family: the 2020 WCF path returns success without producing a value (the native client computes the zone locally). It only becomes a real round-trip on the 2023 R2 gRPC front door (Status.GetSystemTimeZoneName), which is absent on this box.

Interpretation:

  • Stat endpoint routing is confirmed, but status operations that require a real client handle are not usable until managed session open is solved.
  • GetServerTime should not be promoted into the public SDK as a real server time call from this WCF path; native evidence shows it is a no-op stub here.
  • GetServerTimeZoneAsync (roadmap R1.3) is NOT a trivial WCF op on 2020 — it is a stub returning empty. Do not ship it over the 2020 WCF transport. Deliver it only against a live 2023 R2 gRPC server. Reclassified in docs/plans/hcal-roadmap.md.

GETRP / GetRuntimeParameter (roadmap R1.2) — DONE, live-verified 2026-06-20

Captured the native HistorianAccess.GetRuntimeParameter(List<string>, out List<object>) WCF traffic with scripts/Capture-RuntimeParam.ps1 (instrument-wcf-{write,read}message). Findings:

  • The WCF op is aa/Stat/GETRPbool GETRP(string handle, byte[] pRequestBuff, out byte[] pResponseBuff, out byte[] errorBuffer), i.e. the same string-handle + request/response-buffer shape as GETHI, not the simple GetSystemParameter(uint, string) shape the roadmap originally assumed.
  • The string handle is the Open2 storage-session GUID (the value ParseOpenConnectionResponse reads from outBuff[5..21]), sent UPPERCASE, dash-separated, no braces (ToString("D").ToUpperInvariant()).
  • Unlike GETHI (which the earlier probe found blocked), GETRP succeeds from the pure-managed client with that handle: GetRuntimeParameter("HistorianVersion")20,0,000,000.
  • pRequestBuff = 54 67 01 00 (sig+version) + uint nameCount + per name(uint charCount + UTF-16LE). pResponseBuff = version(1) + uint resultCount + CRetVariant(0x43 VT_BSTR + uint16 payloadLen + uint16 charCount + UTF-16LE).

Shipped as HistorianClient.GetRuntimeParameterAsync(name). See HistorianRuntimeParameterProtocol, golden WcfRuntimeParameterProtocolTests, and the handle-format lead in wcf-string-handle-wall.md §Update (retry GETHI/ExeC uppercased).

R1.3 timezone + R1.4 EventStorageMode — re-confirmed bounded out (2026-06-21)

Both were already classified 2023R2/gRPC-only; re-verified from two fresh angles that corroborate it more strongly than the original op-level probes:

  • Runtime DB schema (Runtime.dbo, the server's own source of truth): the SystemParameter table has no timezone parameter and no EventStorageMode (only EventStorageDuration / EventStorageLogPath). The server timezone exists only as per-block storage artifacts (HistoryBlock.TimeZoneOffset = e.g. 240 min, wwTimeZone = e.g. "Eastern Daylight Time") and a TimeZone reference/lookup table; StorageShard.TimeZoneId is NULL. So the timezone is a DST-specific, SQL-only, OS-derived value, not a clean server-config field exposed by any op.
  • Parameter-op probe (StringHandleProbeDiagnosticTests.TimezoneAndStorageMode_ParameterProbe): GetSystemParameter and GetRuntimeParameter (GETRP) were asked for every timezone candidate (TimeZone/ServerTimeZone/SystemTimeZone/TimeZoneName/SystemTimeZoneName/TimeStampRule/ ServerTime) and every storage-mode candidate (EventStorageMode/StorageMode/EventStorage/ EventStorageDuration). All returned null (GetSystemParameter) or threw ProtocolEvidenceMissingException (GETRP — non-string/empty response); only the HistorianVersion control returned a value (20,0,000,000). Note: TimeStampRule/EventStorageDuration do exist in the SystemParameter table yet GetSystemParameterAsync returns null for them — the shipped op only surfaces a whitelisted subset (a possible future widening, unrelated to R1.3/R1.4).

Conclusion: R1.3 GetServerTimeZoneAsync and R1.4 GetHistorianInfoAsync (EventStorageMode) are not deliverable as server ops on 2020. The only 2020 route to the timezone is a SQL read of HistoryBlock/TimeZone via ExecuteSqlCommand (R1.1) — a DST-specific value over a different mechanism than the roadmap's Status.GetSystemTimeZoneName. EventStorageMode has no 2020 representation at all (it is a 2023 R2 event-storage-architecture field). Deliver both only against a live 2023 R2 gRPC server.

Resolution against the live 2023 R2 gRPC server (2026-06-21) — the two diverged

Both ops were taken to the real 2023 R2 box (History iface 12) over the gRPC StatusService:

  • R1.3 GetServerTimeZoneAsync — SHIPPED. StatusService.GetSystemTimeZoneName(uiHandle) returns the real Windows zone name "Eastern Daylight Time" (the 2020 stub returned empty). HistorianClient.GetServerTimeZoneAsync routes over RemoteGrpc; the non-gRPC transports throw ProtocolEvidenceMissingException (fail-closed, no empty-string lie). Golden message-shape + non-gRPC guardrail unit tests + gated live test.
  • R1.4 GetHistorianInfoAsync (EventStorageMode) — bounded out on gRPC too. Over gRPC, GetHistorianInfo is the same named-value query as 2020 WCF (only HistorianVersion resolves); EventStorageMode + 7 variants fail on both GetHistorianInfo and GetSystemParameter. The 518-byte struct is C++-HCAL-internal (native vtable+648), not on the wire. Not shipped on any transport. See wcf-historian-info.md.