write-commands plan: execute Phase 1 discovery (no DB writes)
Phase 1 of docs/plans/write-commands-reverse-engineering.md is purely
discovery — static IL inspection, public-API enumeration, scope
elimination — and produces no DB writes. Phase 2 requires extending
NativeTraceHarness with a write scenario AND explicit operator approval
per the plan's safety rule §1, so it is deferred to a separate session.
Phase 1 findings recorded inline in the plan's status header:
1. §3.4 ModifyData/DeleteData eliminated — no managed wrapper exists.
`methods` returns zero hits for EditValue, ModifyValue, EditData,
DeleteData, ModifyData, OverwriteData. Per the plan's own §3.4
disposition rule, this op is REST/SMC-only.
2. §4.a native serializer tokens identified for Phase 2:
Public managed-wrapper write API in ArchestrA.HistorianAccess:
- AddTag (0x0600619A)
- AddStreamedValue × 3 overloads (0x0600618C/D/E)
- AddNonStreamedValue × 2 overloads (0x0600618F/90)
- DeleteTags (0x060061A4)
- AddRevisionValuesBegin / AddRevisionValue / AddRevisionValuesEnd
/ AddRevisionValues (0x06006175-77, 0x0600617F)
— covers the bulk-modify use case the eliminated ModifyData would
have served. Worth folding into Phase 2 scope.
Native serializers:
- CTagUtil.ConvertTagMetadataToHistorianTag (0x060055CE) — 412 IL
instructions; builds CTagMetadata for analog/discrete/string.
Calls every CTagMetadata accessor we'd need to surface
(Min/MaxRaw, Min/MaxEU, Unit, Message0/1, MaxLength,
IntegralDivisor, DefaultTagRate, RolloverValue) plus all the
CDataType predicates already decoded.
- CHistoryConnectionWCF.AddStreamValuesToHistorian (0x0600404C)
— confirms the on-wire shape matches our existing
IHistoryServiceContract2.AddStreamValues2 declaration.
3. Chicken-and-egg resolved: the first §3.1 EnsT2 test creates the
sandbox tag itself, so no SMC step is needed.
4. Open question §8.6 answered — the wrapper exposes both
AddStreamedValue and AddNonStreamedValue, so the SDK should
eventually surface both real-time and backfill write modes.
Phase 2 next steps recorded inline in the plan as a 5-item executable
checklist (extend harness with --scenario write, capture, decode
EnsT2(analog) + AddS2 bytes, implement public surface, gated live
tests).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,98 @@
|
||||
# Plan: Reverse-Engineering Write Commands
|
||||
|
||||
Status: PLAN ONLY (no implementation yet). Extends the read/event
|
||||
work in `docs/reverse-engineering/handoff.md` (2026-05-04).
|
||||
Status: **PHASE 1 EXECUTED on 2026-05-04 — discovery complete, awaiting
|
||||
operator decision on Phase 2.** No code changes; no DB writes.
|
||||
|
||||
## Phase 1 findings (recorded here, not implementing)
|
||||
|
||||
### §3.4 ModifyData/DeleteData — ELIMINATED FROM SCOPE
|
||||
|
||||
`methods aahClientManaged.dll` returns no managed wrapper for any of:
|
||||
`EditValue`, `ModifyValue`, `EditData`, `DeleteData`, `ModifyData`,
|
||||
`OverwriteData`. Per the plan's §3.4 disposition rule, this op is
|
||||
REST-only / SMC-only and remains out of scope for the SDK.
|
||||
|
||||
### §4.a Native serializers identified (token IDs for future Phase 2)
|
||||
|
||||
The wrapper does have managed-public write API:
|
||||
|
||||
| Public method | Token | Used for |
|
||||
|---|---|---|
|
||||
| `ArchestrA.HistorianAccess.AddTag` | `0x0600619A` | Creates a new tag (drives `EnsT2`) |
|
||||
| `ArchestrA.HistorianAccess.AddStreamedValue` | `0x0600618C/D/E` (3 overloads) | Pushes one timestamped value (drives `AddS2`) |
|
||||
| `ArchestrA.HistorianAccess.AddNonStreamedValue` | `0x0600618F/90` (2 overloads) | Pushes one timestamped value, non-stream mode |
|
||||
| `ArchestrA.HistorianAccess.DeleteTags` | `0x060061A4` | Removes tags (drives `DelT`) |
|
||||
| `ArchestrA.HistorianAccess.AddVersionedStreamedValue` | `0x0600616F` | Pushes versioned value (rev edit) |
|
||||
| `ArchestrA.HistorianAccess.AddRevisionValuesBegin/Value/End/AddRevisionValues` | `0x06006175-77, 0x0600617F` | Multi-row revision write (replaces `ModifyData` use case) |
|
||||
|
||||
So even though the engine doesn't expose `ModifyData` over WCF, the
|
||||
**revision-write path** (`AddRevisionValuesBegin → AddRevisionValue * N →
|
||||
AddRevisionValuesEnd`) covers the bulk-modify use case. This is a NEW
|
||||
discovery worth folding into the Phase 2 scope.
|
||||
|
||||
Native serializer for `EnsT2(analog/discrete/string)`:
|
||||
**`<Module>.CTagUtil.ConvertTagMetadataToHistorianTag`** at token
|
||||
`0x060055CE` (412 IL instructions, 10 locals). Calls `CTagMetadata.GetUnit`,
|
||||
`GetMessage0`, `GetMessage1`, `GetMaxLength`, `GetMinRaw`, `GetMaxRaw`,
|
||||
`GetMinEU`, `GetMaxEU`, `GetIntegralDivisor`, `GetDefaultTagRate`,
|
||||
`GetRolloverValue`, plus `CDataType.IsAnalog/IsWideString/GetRawType/
|
||||
GetTagType` — i.e. every field the analog `CTagMetadata` shape would
|
||||
need is wired through this method. Decoding it line-by-line **OR**
|
||||
capturing live wire bytes against a sandbox tag are the two ways
|
||||
forward.
|
||||
|
||||
WCF wrapper for `AddS2`: **`<Module>.CHistoryConnectionWCF.AddStreamValuesToHistorian`**
|
||||
at token `0x0600404C`. Confirms the on-wire shape is
|
||||
`IHistoryServiceContract2.AddStreamValues2(string handle, byte[] pBuf,
|
||||
out byte[] errorBuffer)` — matches our existing contract. Handle is
|
||||
the same Open2 v6 session GUID we already extract.
|
||||
|
||||
### Phase 2 chicken-and-egg resolved
|
||||
|
||||
Per §5 ordering: §3.1 (EnsT2) must come before §3.2 (AddS2) because
|
||||
AddS2 needs an existing tag. The sandbox tag itself is created BY
|
||||
the first §3.1 EnsT2 test. So the very first write-flow run creates
|
||||
`RetestSdkWriteSandbox`. No SMC required — the chain is closed.
|
||||
|
||||
### Open question (was §8.6) answered
|
||||
|
||||
The wrapper exposes `AddStreamedValue` AND `AddNonStreamedValue`.
|
||||
The latter is the documented path for backfilling values older than
|
||||
`RealTimeWindow`. So the SDK should expose both modes, not just
|
||||
`AddStreamedValue`. Update the success criteria for `AddS2`
|
||||
accordingly.
|
||||
|
||||
### Phase 2 next steps (NOT EXECUTED in this session)
|
||||
|
||||
1. Extend `tools/AVEVA.Historian.NativeTraceHarness/Program.cs` with a
|
||||
`--scenario write` that calls `HistorianAccess.AddTag` (creating
|
||||
`RetestSdkWriteSandbox` if absent) followed by
|
||||
`HistorianAccess.AddStreamedValue`. New args:
|
||||
`--write-sandbox-tag <name>` (default: `RetestSdkWriteSandbox`),
|
||||
`--write-value <numeric>`, `--write-data-type analog|discrete|string`.
|
||||
2. Run the harness with `instrument-wcf-writemessage` +
|
||||
`instrument-wcf-readmessage` instrumented copies of
|
||||
`aahClientManaged.dll` to capture the full write flow.
|
||||
3. Decode `EnsT2(analog)` `InBuff` bytes against the IL of
|
||||
`CTagUtil.ConvertTagMetadataToHistorianTag` (token `0x060055CE`).
|
||||
4. Decode `AddS2` `pBuf` bytes against the IL of
|
||||
`CHistoryConnectionWCF.AddStreamValuesToHistorian` (token
|
||||
`0x0600404C`).
|
||||
5. Implement `WriteValueAsync`, `EnsureTagAsync`, `DeleteTagAsync` per
|
||||
§4.e of the original plan; live tests gated by
|
||||
`HISTORIAN_WRITE_SANDBOX_TAG`.
|
||||
|
||||
Phase 2 was deferred because (a) it requires extending the harness
|
||||
(non-trivial scaffolding) and (b) per safety §1, even sandbox-tag
|
||||
writes warrant explicit operator approval before the first run. The
|
||||
operator decides whether to proceed; if yes, the instructions above
|
||||
are executable as-is.
|
||||
|
||||
---
|
||||
|
||||
Original plan content below.
|
||||
|
||||
|
||||
|
||||
## 1. Goal
|
||||
|
||||
|
||||
Reference in New Issue
Block a user