diff --git a/docs/reverse-engineering/handoff.md b/docs/reverse-engineering/handoff.md index f4561e8..4b25886 100644 --- a/docs/reverse-engineering/handoff.md +++ b/docs/reverse-engineering/handoff.md @@ -1,6 +1,69 @@ # AVEVA Historian Managed Driver Handoff -Last updated: 2026-05-04 (write surface live: EnsT2 + DelT + ApplyScaling) +Last updated: 2026-06-22 (roadmap exhausted — no actionable pure-code tasks remain) + +> **Current status supersedes the historical blocker narrative below.** The +> sections from "Active Blocker" onward are a preserved reverse-engineering +> record of how the 2020 WCF read/write/event paths were cracked (2026-05-04). +> They are kept for provenance; they are **not** the live state. Start with +> "Current Status" immediately below. + +## Current Status (2026-06-22) — roadmap exhausted + +`docs/plans/hcal-roadmap.md` reachable surface is **complete**, and every plan +under `docs/plans/` is either DONE or has only gated items left. There are +**zero actionable pure-code tasks remaining**. Memory anchor: +`project_roadmap_exhausted_2020wcf`. + +**Shipped + live-verified across both transports:** + +- **Reads** (WCF + gRPC): `ProbeAsync`, `ReadRawAsync`, `ReadAggregateAsync`, + `ReadAtTimeAsync`, `ReadEventsAsync`, `BrowseTagNamesAsync`, + `GetTagMetadataAsync`, status helpers (`GetConnectionStatusAsync`, + `GetStoreForwardStatusAsync`, `GetSystemParameterAsync`). +- **Writes**: `EnsureTagAsync` (analog Float/Double/Int2/Int4/UInt4, `ApplyScaling`), + `DeleteTagAsync`, `RenameTagsAsync`, `AddTagExtendedPropertiesAsync`, and the + M3 historical/backfill `AddHistoricalValuesAsync` (**gRPC-only**, all five + analog types golden-tested + live write/read-back). +- **Config reads** (mostly gRPC): `GetRuntimeParameterAsync`, + `GetTagExtendedPropertiesAsync`, `ExecuteSqlCommandAsync` (WCF; gRPC + server-walled), `GetServerTimeZoneAsync` (gRPC-only). +- **Client-side**: M4 R4.1 store-and-forward outbox, R4.4 redundancy, + R4.3 measured-idle SF status. + +The 2023 R2 **gRPC transport** (`HistorianTransport.RemoteGrpc`, port 32565) +reuses the proven 2020 WCF byte serializers/parsers unchanged inside protobuf +`bytes` fields, keyed by the Open2 session handle. Live-verified against a real +2023 R2 server (History interface v12) — see `reference_2023r2_live_server_access`. + +**Everything still open is gated — none is a pure-code task:** + +1. **gRPC event ROW retrieval** (`ReadEventsAsync` #2) — chain + StartEventQuery + work; `GetNextEventQueryResultBuffer` long-polls on no data. Needs an + **event-bearing 2023 R2 server**. Bounded ≤30s, throws on no-row. +2. **R4.3 active-SF magnitude** — needs an **SF-active server** (D2 storage-engine + console handle). +3. **SendEvent over gRPC** — **capture-gated**: no distinct RPC, framing uncaptured. +4. **ExecuteSqlCommand over gRPC** — **server-walled** (`CSrvDbConnection`; + RegisterTags prime doesn't help). Use WCF for SQL. +5. **R4.2 revision EDITS** — storage-engine-pipe-only on BOTH transports (the D2 wall). +6. **ReadBlocks** (`StartBlockRetrievalQuery`) — never captured on either transport. +7. **DeleteTagExtendedProperties** — server-blocked on BOTH transports. The gRPC + multiplexed-channel hypothesis was **PROBED + DISPROVEN 2026-06-22** (merge + `c88260c`): GetTgByNm + GetTepByNm primes succeed on one shared write-enabled + gRPC channel, yet DelTep is still rejected (native code=1) and the property + survives — the working set is native in-process registration state, not the + wire session. Pinned by gated negative test + `DeleteTagExtendedProperties_OverGrpc_ProbeMultiplexedChannel`. +8. **Deferred-by-design** items (`write-commands` D1–D3, non-analog tag create, + etc.) — bounded out until an explicit customer/user demand signal. + +To move any remaining item you need a **different server** (event-bearing or +SF-active), a **fresh native capture** (SendEvent gRPC framing), or a **demand +signal** to unlock a deferred item. Live-server gRPC probe recipe: set +`HISTORIAN_GRPC_HOST`/`_PORT 32565`/`_TLS true`/`_DNSID` + domain creds (strip +quotes — `reference_wonder_sql_vd03_credentials`) and run the gated +`HistorianGrpcIntegrationTests`. ## Project Direction @@ -73,10 +136,12 @@ dotnet build .\Histsdk.slnx --no-restore dotnet test .\Histsdk.slnx --no-build --logger "console;verbosity=minimal" ``` -Current known-good result: +Current known-good result (2026-06-22): -- Build succeeds. -- Unit tests pass: 55/55. +- Build succeeds (0 warnings / 0 errors). +- Offline tests pass: 321/321 (live gRPC/integration tests skip cleanly without + their env vars). Gated live tests add to this when `HISTORIAN_*` / + `HISTORIAN_GRPC_*` are set. The workspace is a Git working tree (origin: gitea.dohertylan.com). Use normal git workflow for change tracking; the prior "no working tree, use @@ -227,7 +292,10 @@ Negative evidence: - Running the same managed `ValCl` path through .NET Framework also fails, so this is not just a .NET 10 WCF behavior difference. -## Active Blocker +## Historical: Read-Path Blocker (resolved 2026-05-04) + +> Preserved RE record. This was the *original* active blocker; it is long +> resolved and is not the live state — see "Current Status" at the top. **Resolved on `2026-05-04`.** The previous blocker — managed `ValCl` rejected by the server — had two causes, both now fixed: