f4709ff143
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>
135 lines
7.0 KiB
Markdown
135 lines
7.0 KiB
Markdown
# Plan: Speculative Items Sweep (2026-05-04)
|
|
|
|
The five items I previously called out as speculative / deferred. Goal:
|
|
exercise the cheap ones, investigate the medium ones to feasibility, and
|
|
write sub-plans for anything too big to ship in one push.
|
|
|
|
## Items
|
|
|
|
| ID | Item | Effort | Touches |
|
|
|---|---|---|---|
|
|
| C3 | Expose `IntegralDivisor` on `HistorianTagDefinition` | small | `HistorianTagDefinition.cs`, `HistorianTagWriteProtocol.cs`, orchestrator, tests |
|
|
| E | Unit tests for `AllowUntrustedServerCertificate` / `ServerDnsIdentity` | small | new test file under `tests/` |
|
|
| D3 | Root-cause Discrete/String/Int1/Int8/UInt8 EnsT2 failure | medium (investigation) | native harness, possibly serializer |
|
|
| D1 | Capture wire bytes for `AddTagExtendedProperties` | medium (capture + decode) | native harness, possibly new serializer + public API |
|
|
| D2 | Implement `AddRevisionValuesBegin/Value/End` (revision-write path) | large | new orchestrator + 3 new public APIs |
|
|
|
|
## Parallelism
|
|
|
|
Concurrency-safe groupings (each pair is independent at the file level):
|
|
|
|
- **C3 ↔ E** — C3 touches `HistorianTagDefinition.cs` + `HistorianTagWriteProtocol.cs` + orchestrator + integration tests; E adds a new test file + might add a small unit-test util. No file overlap.
|
|
- **D3 ↔ D1** — Both touch the native trace harness Program.cs, so they conflict if done concurrently. Sequence them.
|
|
- **C3/E ↔ D3/D1** — No file overlap; can run concurrently with the harness work.
|
|
- **D2** stands alone (different code paths entirely).
|
|
|
|
In a single-agent session, the order is:
|
|
|
|
1. C3 (small, predictable) — land first
|
|
2. E (small, predictable) — land second
|
|
3. D3 (investigation; documents findings whether or not implementation is possible)
|
|
4. D1 (investigation + capture; same pattern)
|
|
5. D2 — write a focused sub-plan; do NOT implement in this sweep
|
|
|
|
## Success Criteria
|
|
|
|
- C3: `HistorianTagDefinition.IntegralDivisor` (default 1.0) plumbed through serializer; unit test asserts non-default value flips the wire bytes; live test asserts the value persists in `Tag.IntegralDivisor` (or wherever it lands in SQL).
|
|
- E: 2-3 unit tests asserting `HistorianWcfClientCredentialsHelper.Configure` and `HistorianWcfBindingFactory.CreateEndpointAddress` honor the new options.
|
|
- D3: documented root cause + decision (workable path / not workable / requires further capture). If a workable path emerges quickly, also implement.
|
|
- D1: documented evidence summary + decision (worth implementing / defer / requires customer ask).
|
|
- D2: `docs/plans/revision-write-path.md` (or similar) with the 5-step capture sequence + open questions.
|
|
|
|
## Findings
|
|
|
|
### C3 — IntegralDivisor (executed 2026-05-05)
|
|
|
|
Plumbed through serializer + orchestrator; default 1.0. Unit test verifies
|
|
the 8-byte double immediately preceding the trailer flips with a non-default
|
|
value. Live probe: server accepts the wire bytes but `IntegralDivisor`
|
|
appears to be stored on `EngineeringUnit` (shared across all tags using that
|
|
EU) rather than per-tag, and the EU's stored value didn't change for the
|
|
test. Documented in the property's doc-comment. No live integration test
|
|
added (nothing to assert in SQL).
|
|
|
|
### E — Cert option unit tests (executed 2026-05-05)
|
|
|
|
Added `HistorianWcfCertOptionTests` (5 tests) covering:
|
|
|
|
- `Configure` is a no-op when `AllowUntrustedServerCertificate=false`
|
|
- `Configure` installs the accept-any validator + `RevocationMode.NoCheck`
|
|
when the option is true
|
|
- `CreateEndpointAddress` with no DNS identity returns an address with
|
|
`Identity == null`
|
|
- `CreateEndpointAddress` with a DNS identity attaches a `DnsEndpointIdentity`
|
|
- `CreateBindingPair(RemoteTcpCertificate)` propagates `ServerDnsIdentity`
|
|
to the History endpoint (and not to the Retrieval endpoint, which uses
|
|
plain MdasNetTcp without TLS)
|
|
|
|
### D3 — Discrete/String/Int1/Int8/UInt8 EnsT2 root cause (investigated 2026-05-05)
|
|
|
|
Probed each unsupported type via the native trace harness with
|
|
`--write-data-type {Type}`. Result for SingleByteString and Int1 (others
|
|
truncated in the same output):
|
|
|
|
- `HistorianAccess.AddTag` returns `Success=false`, `TagKey=0`
|
|
- Error: `ErrorCode=ValidationFailed`, `ErrorType=CustomError`,
|
|
`ErrorDescription="Transaction validation failed"`
|
|
|
|
**Conclusion:** The failure is **server-side**, not wire-format. The
|
|
`/Hist.EnsT2` server-side validator rejects non-analog types when invoked
|
|
through the `AddTag → EnsT2` code path. To unlock these types from the SDK
|
|
we'd need to:
|
|
|
|
1. Capture a successful native creation of a discrete/string tag via some
|
|
other mechanism (likely SMC's tag-import path or a different WCF op
|
|
like `AddTagExtendedProperties` carrying the discrete/string-specific
|
|
metadata)
|
|
2. Diff the working native flow against the failing one to see what
|
|
ancillary fields the validator expects (TagType vs CDataType, separate
|
|
StorageType, IO-server pre-registration, etc.)
|
|
|
|
**Decision:** defer until a customer asks. The native AVEVA wrapper itself
|
|
cannot create these tags via `AddTag` from a managed client — implementing
|
|
this would require RE work on a path the wrapper doesn't exercise, which
|
|
is much higher risk than the existing analog write surface.
|
|
|
|
### D1 — AddTagExtendedProperties feasibility (investigated 2026-05-05)
|
|
|
|
Managed surface confirmed. Native API:
|
|
|
|
- Public managed entry point: `ArchestrA.HistorianAccess.AddTagExtendedProperties`
|
|
(token `0x0600619B`, 140 IL instructions, 6 locals)
|
|
- WCF op: `CHistoryConnectionWCF.AddTagExtendedPropertyGroups`
|
|
(token `0x0600405C`)
|
|
- Underlying contract method: `IHistoryServiceContract2.AddTagExtendedProperties`
|
|
(already declared in our reproduced contracts)
|
|
- Managed input type: `HistorianTagExtendedPropertyGroup` wrapping the native
|
|
`CTagExtendedPropertyGroup` C++ class. Built from a `std::vector<CTagExtendedPropertyGroup>`
|
|
(visible in the IL locals). Property group structure not yet decoded.
|
|
|
|
**Decision:** defer implementation. Cost estimate:
|
|
|
|
1. Reflect-construct `HistorianTagExtendedPropertyGroup` via the native
|
|
harness (probably 2-4 hours — these C++/CLI types often have hidden
|
|
constructor requirements that surface only at runtime).
|
|
2. Call `AddTagExtendedProperties` with a sandbox group; capture wire bytes
|
|
via `instrument-wcf-writemessage` (1 hour).
|
|
3. Decode the `CTagExtendedPropertyGroup` payload — this is its own struct
|
|
that needs walking field-by-field against the native serializer IL
|
|
(token `0x06002038`, `CHistStorage.AddTagExtendedProperties`) (3-6 hours).
|
|
4. Implement managed `HistorianTagExtendedPropertyGroup` model + serializer
|
|
+ public `AddTagExtendedPropertiesAsync` API + tests (4-6 hours).
|
|
|
|
Total: 1-2 days of focused work. Defer until a customer asks for tag
|
|
extended properties or the analog write surface needs them as a
|
|
prerequisite.
|
|
|
|
### D2 — AddRevisionValuesBegin/Value/End
|
|
|
|
Sub-plan deferred to a dedicated session — see
|
|
`docs/plans/revision-write-path.md` (created in this sweep).
|
|
|
|
## Out of scope for this sweep
|
|
|
|
Refactoring `HistorianWcfTagClient` to respect `options.Transport` for browse / metadata (i.e., let it use cert binding from Windows). Worth doing but not part of the speculative-items list.
|