Files
histsdk/docs/plans/revision-write-path.md
T
Joseph Doherty f4709ff143 Speculative-items sweep: IntegralDivisor, cert tests, D3/D1/D2 findings
Plan: docs/plans/speculative-items-sweep.md (also covers parallelism +
findings).

Implemented:
- C3: HistorianTagDefinition.IntegralDivisor (default 1.0). Wire bytes
  flip per the captured native serializer; live probe shows the server
  stores IntegralDivisor on EngineeringUnit (shared) rather than per-tag,
  so the value is accepted on the wire but doesn't visibly persist for
  the test EU. Documented in the property's doc-comment.
- E: HistorianWcfCertOptionTests (5 tests) covering AllowUntrustedServer-
  Certificate validator installation + ServerDnsIdentity propagation
  through CreateEndpointAddress and CreateBindingPair.

Investigated + documented (deferred):
- D3: Discrete/String/Int1/Int8/UInt8 EnsT2 root cause — server-side
  ValidationFailed: "Transaction validation failed". Native AddTag's
  validator rejects non-analog types; not a wire-format issue. To unlock,
  need to capture a working native flow via a different code path
  (likely SMC's tag-import path or AddTagExtendedProperties carrying
  type-specific metadata). Defer until a customer asks.
- D1: AddTagExtendedProperties feasibility — managed surface confirmed
  (ArchestrA.HistorianAccess.AddTagExtendedProperties + WCF op
  AddTagExtendedPropertyGroups). Cost estimated at 1-2 days of focused
  RE work due to CTagExtendedPropertyGroup payload complexity. Defer.
- D2: AddRevisionValuesBegin/Value/End — sub-plan written at
  docs/plans/revision-write-path.md with 5-step capture sequence,
  workstream estimates, and risk register. Implementation deferred.

177/177 tests pass (was 172; +5 cert tests + 1 IntegralDivisor unit
test, harness probe results not committed).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 00:11:40 -04:00

6.1 KiB
Raw Blame History

Plan: Revision-Write Path (AddRevisionValuesBegin/Value/End)

Status: NOT STARTED. Sub-plan extracted from speculative-items-sweep.md item D2 because the work is too large for a one-push sweep.

Context

The Historian's "revision write" path is the documented mechanism for editing historized data after the fact (replaces the inferred ModifyData / DeleteData use cases that don't exist as WCF ops). Native managed surface (per Phase 1 findings of the write-commands plan):

Public method Token Purpose
ArchestrA.HistorianAccess.AddRevisionValuesBegin 0x06006175 Open a revision-edit transaction
ArchestrA.HistorianAccess.AddRevisionValue 0x06006176 Append a value to the open transaction
ArchestrA.HistorianAccess.AddRevisionValuesEnd 0x06006177 Commit the transaction
ArchestrA.HistorianAccess.AddRevisionValues 0x0600617F Single-shot variant
ArchestrA.HistorianAccess.AddVersionedStreamedValue 0x0600616F Push one versioned value (related path)

WCF surface is unknown — likely a new op group on IHistoryServiceContract2 or IRetrievalServiceContract4 or a new contract.

Goal

Public SDK API:

public Task<HistorianRevisionTransaction> BeginRevisionAsync(string tag, CancellationToken ct);
// On the returned transaction:
public Task AddRevisionValueAsync(HistorianSampleEdit sample, CancellationToken ct);
public Task<bool> CommitAsync(CancellationToken ct);
// IDisposable / IAsyncDisposable for cancellation rollback if such a thing exists

Or a single batch convenience:

public Task<bool> WriteRevisionsAsync(string tag, IReadOnlyList<HistorianSampleEdit> samples, CancellationToken ct);

The choice depends on the wire shape — if Begin/Value/End requires the caller to maintain a server handle between calls, the disposable transaction is necessary; if it's stateless, the batch convenience is fine.

Workstreams

A. Static analysis (1-2 hours)

  • Inspect IL for the four managed public methods to identify the underlying CHistoryConnectionWCF.* calls and their server-side WCF contract methods.
  • Add the contract methods to Wcf/Contracts/IHistoryServiceContract2.cs (or a new contract if appropriate) with [OperationContract(Name = "...")]
    • [MessageParameter] attributes once names are known.

B. Native harness extension (2-3 hours)

  • Add --scenario revision-write to the harness.
  • Refer to existing --scenario write plumbing for the AddTag wrapper pattern.
  • Sequence:
    1. Open connection (probably write-enabled mode 0x401)
    2. AddTag for sandbox tag (re-uses existing harness flow)
    3. AddStreamedValue for the initial sample (currently blocked architecturally per Phase 2 findings — but may not be required if the revision path operates directly on the historian engine state)
    4. AddRevisionValuesBegin / AddRevisionValue × N / AddRevisionValuesEnd
    5. Read back via existing read path; verify the samples reflect the edits

C. Wire capture (1 hour)

  • Same instrument-wcf-writemessage + instrument-wcf-readmessage IL-rewrite tooling already used for EnsT2 / DelT.
  • Capture both Begin/Value/End and the single-shot AddRevisionValues variant for byte-level diff.

D. Decode + managed serializer (4-6 hours)

  • Walk the captured InBuff bytes against the native serializer IL.
  • The Begin payload likely seeds a server-side transaction handle that Value calls reference. Look for an out-returned handle in the Begin response.
  • Value payload structure is likely similar to AddS2's pBuf (uint16 version + uint32 sampleCount + N × {tagId, FILETIME, quality, typed value bytes}) but may include a per-sample revision/version field.

E. Public API + tests (4-6 hours)

  • New types: HistorianSampleEdit (sample + reason/version metadata), HistorianRevisionTransaction (disposable handle).
  • Public methods on HistorianClient per the Goal section.
  • Unit tests: golden-byte fixtures for Begin/Value/End/Commit payloads.
  • Live integration tests: write a known sample, edit it via the revision path, read back and assert the new value appears.

Risks

  • Server-cache prerequisite. If the historian's revision path also requires the tag to be "live in the runtime cache" (the same blocker that killed AddS2), the entire path may be unimplementable for the same architectural reason.
  • State across calls. Begin/Value/End may store transaction state on the server keyed by the WCF session GUID. WCF's session model needs to be configured to keep the same channel alive across all three calls — which is a different lifecycle from the existing one-call-per-channel pattern in the SDK orchestrators.
  • Concurrent edits. Server may reject concurrent revision transactions on the same tag — needs probing.
  • Time bounds. Revision likely respects the same RealTimeWindow / FutureTimeThreshold system parameters as AddS2. Out-of-window edits silently drop or error — needs probing.

Success Criteria

  • Public BeginRevisionAsync (or batch variant) live-verified against a sandbox tag created by EnsureTagAsync.
  • Round-trip test: write initial value → revise it → read back → verify the revised value persists in History extension table via SQL.
  • Golden-byte fixtures for Begin / Value / End / Commit captured against the sandbox tag.
  • Decision documented for whether the AddRevisionValues single-shot variant is exposed in addition to the Begin/Value/End sequence.

Dependencies

  • Existing analog write surface (EnsureTagAsync) — done.
  • AddS2 is not a prerequisite; the revision path may be an independent code path that bypasses the runtime-cache gate. If it doesn't, this plan is blocked the same way AddS2 is.

Out of scope

  • Editing event tags. Events come from AVEVA AnE; the SDK only reads them.
  • Bulk schema changes. Forbidden over the wire per the Historian's architecture.

Trigger to start

A customer-driven request, or a real need to expose historical data correction in the SDK's API. Without one, this remains the most substantive remaining write-path workstream but isn't worth the 1-2 days of focused work speculatively.