M3 R3.1: durable capture plan — drive native 2023 R2 gRPC client + IL-rewrite byte[] payloads
Records the feasibility-verified plan to capture the two remaining buffers (regular-tag RegisterTags btTagInfos + AddNonStreamValues btInput): - 2023 R2 aahClientManaged.dll is self-contained mixed-mode C++/CLI (only Windows + VC++ runtime native imports) — loadable in a net481 x64 process, no AVEVA install needed. - gRPC routes through the managed Archestra.Historian.GrpcClient.dll, so the byte[] payloads are capturable by IL-rewriting GrpcHistoryClient.RegisterTags / AddNonStreamValues (dnlib, the instrument-wcf-writemessage pattern; rewrite a copy, never the originals). - Connection is reflection-drivable: HistorianAccess.OpenConnection(HistorianConnectionArgs) with ConnectionMode=HistorianConnectionMode.Historian (the gRPC mode), TcpPort=32565, cert. - gRPC runtime deps (Grpc.Net.Client / Grpc.Core.Api / Google.Protobuf / ...) are present in msi-extract/ArchestrA/Toolkits/Bin/x64. - Risk: the C++ AddNonStreamedValue TagNotFoundInCache(129) gate (the 2020 D2 blocker) may block btInput; mitigation = read the tag first. RegisterTags is emitted before that gate. Build order documented (read-only connect -> IL-rewrite -> write capture -> serializer -> commit+read-back -> AddHistoricalValuesAsync), each live step gated on per-action auth. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B6mcaT2PjRFKcogzp9UkfC
This commit is contained in:
@@ -173,6 +173,46 @@ is now correctly identified as front-door `RegisterTags` (NOT `OpenStorageConnec
|
||||
`AddHistoricalValuesAsync` is blocked on capturing the regular-tag `RegisterTags` `btTagInfos` +
|
||||
the `AddNonStreamValues` `btInput`.**
|
||||
|
||||
### R3.1 capture plan (2026-06-21): drive the native 2023 R2 gRPC client + IL-rewrite the byte[] payloads
|
||||
|
||||
Feasibility verified end-to-end against `histsdk-2023r2-analysis/bin`:
|
||||
|
||||
- **Self-contained, loadable.** 2023 R2 `aahClientManaged.dll` is a 20 MB **mixed-mode C++/CLI**
|
||||
assembly whose native imports are only Windows + VC++ runtime (`MSVCP140`/`VCRUNTIME140_1`) — **no
|
||||
external AVEVA native dependency / no Historian install required** to load it in a `net481` x64
|
||||
process. The native C++ `HistorianClient` (the `<Module>.HistorianClient.*` globals,
|
||||
e.g. `AddNonStreamedValueAsync(client, &HISTORIAN_VALUE2, &SError)`) is compiled *into* it and is
|
||||
what builds `btInput`; it then hands the `byte[]` to the **managed** gRPC client.
|
||||
- **gRPC routes through managed code → IL-rewrite-able.** `Archestra.Historian.GrpcClient.dll`
|
||||
(`Grpc.Net`-based) is pure managed; `GrpcHistoryClient` holds both `m_historyClient` and
|
||||
`m_transactionClient`. Capture targets:
|
||||
- `GrpcHistoryClient.RegisterTags(string handle, byte[] tagInfos, …)` → dump `tagInfos`
|
||||
- `GrpcHistoryClient.AddNonStreamValues(string handle, string transactionId, byte[] inBuff, …)` → dump `inBuff`
|
||||
Use the existing dnlib IL-rewrite tooling (`tools/AVEVA.Historian.ReverseInstrumentation` +
|
||||
`instrument-wcf-writemessage` pattern), writing rewrites to a copy under
|
||||
`docs/reverse-engineering/dnlib-write-copy/` — never touch `histsdk-2023r2-analysis/bin` originals.
|
||||
- **gRPC runtime deps are available.** `Archestra.Historian.GrpcClient.dll` references `Grpc.Net.Client`,
|
||||
`Grpc.Core.Api`, `Grpc.Net.Client.Web`, `Google.Protobuf`, etc. — the full set is present in
|
||||
`histsdk-2023r2-analysis/msi-extract/ArchestrA/Toolkits/Bin/x64/` (alongside the 5 core DLLs in
|
||||
`…/bin/`). Assemble all of them into the harness runtime dir so `Assembly.LoadFrom` + the sibling
|
||||
resolver can satisfy the gRPC stack.
|
||||
- **Driving the write (reflection, like `NativeTraceHarness`).** `ArchestrA.HistorianAccess.OpenConnection(HistorianConnectionArgs, out err)`
|
||||
with `HistorianConnectionArgs { ServerName, TcpPort=32565, ConnectionMode=HistorianConnectionMode.Historian
|
||||
(the 2023 R2 gRPC mode; `ClassicHistorian`=legacy), ConnectionType=Process, ReadOnly=false,
|
||||
IntegratedSecurity/UserName/Password, AllowUnTrustedConnection=true, SecurityInfo=cert }`, then
|
||||
`AddNonStreamedValue(ConnectionIndex.Process, HistorianDataValue, bVersioned:false, out err)`.
|
||||
- **Cache-gate risk (the D2 blocker).** The C++ `AddNonStreamedValueAsync` has a per-connection
|
||||
`TagNotFoundInCache (129)` gate that, in the 2020 D2 probe, rejected the value **before any bytes
|
||||
left the client**. Mitigation to try: **read the target tag first** (populate the per-connection
|
||||
cache) before `AddNonStreamedValue`. `RegisterTags` is emitted during registration *before* this
|
||||
gate, so its `tagInfos` is capturable **even if** the gate still blocks `btInput`.
|
||||
|
||||
Build order (each live step = prod write, per-action auth): (1) `net481` x64 harness loads the 2023 R2
|
||||
DLL + opens a **read-only** gRPC connection + reads the tag (proves load+connect, no write); (2)
|
||||
IL-rewrite `Archestra.Historian.GrpcClient.dll`; (3) write-enabled run → capture `RegisterTags`
|
||||
`tagInfos` (+ `btInput` if the gate passes); (4) build golden serializer(s) in `src/`; (5) real
|
||||
`bCommit=true` write + SQL read-back on a sandbox tag → ship `AddHistoricalValuesAsync`.
|
||||
|
||||
---
|
||||
|
||||
## Legacy WCF analysis (preserved — still accurate for the 2020 WCF transport)
|
||||
|
||||
Reference in New Issue
Block a user