DelTep (extended-property delete) — wire format captured + serializer golden-proven, but live delete is server-blocked and NOT exposed publicly: - Captured the DelTep inBuff via a cross-session trick (harness add-tep gains --tep-skip-add + read-for-sync before --tep-delete; Capture-DeleteTagExtended Properties.ps1 / decode-del-tep-capture.py). Layout = same group framing as AddTEx but property-name-only (no 0x43 value) + 0x00 group trailer. - SerializeDeleteRequest + 4 golden tests pin the server-accepted buffer. - A decisive experiment shows SDK-added properties ARE deletable (the native client read-syncs and deletes one), so SDK-add is complete; the SDK's own DelTep is rejected by CHistStorage::DeleteTagExtendedProperties even with byte-identical inBuff, matching mode/handle, GetTgByNm+GetTepByNm prime, open channel, and 60s retries. Root cause: the native multiplexes services over one connection (per-connection working set); the SDK's per-service WCF channels don't reproduce it. Kept as documented-but-blocked internal orchestrator path; no public HistorianClient delete API. Bounded out with evidence (no code; docs + roadmap + probe): - R1.12 localized-property write — no op on 2020 (mirror of R1.6); no *LocalizedPropert*/TagLocalized* symbol in any current/*.dll. - R1.13 non-analog tag create — GATED; native AddTag rejects every non-analog type client-side (ValidationFailed, before any WCF op): SingleByteString, DoubleByteString, Int1 all fail, Float works. No Discrete type in the native enum, no TagType setter. No wire request to capture. - R1.3 timezone + R1.4 EventStorageMode — re-confirmed 2023R2/gRPC-only from the Runtime DB schema (no timezone param, no EventStorageMode anywhere) and a parameter-op probe (GetSystemParameter + GETRP return null/throw for every candidate; only HistorianVersion works). 238 unit tests pass; full solution builds with 0 warnings. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B6mcaT2PjRFKcogzp9UkfC
4.3 KiB
Non-analog tag create over 2020 WCF — GATED (HCAL R1.13)
Status: ⛔ bounded out (2026-06-21). No non-analog (string / discrete / wide-integer) tag-create
path is reachable on 2020 — the native managed client rejects every non-analog type client-side,
before any WCF op, so there is no wire format to capture and nothing to implement against.
EnsureTagAsync stays analog-only (Float, Double, Int2, UInt2, Int4, UInt4); unsupported types throw
ProtocolEvidenceMissingException from HistorianTagWriteProtocol.GetAnalogDataTypeCode.
What R1.13 asked for
Create string / discrete (non-analog) tags via History.EnsureTags, with a distinct CTagMetadata
variant. The roadmap flagged it "⚠ native AddTag rejected some types — confirm server path first;
may be GATED."
Findings (live-probed against the local 2020 Historian)
-
No discrete/boolean data type exists. The native
ArchestrA.HistorianDataTypeenum (current/aahClientManaged.dll, dumped viaenum-dump) has exactly 12 members:Int1, Int2, UInt2, Int4, UInt4, Float, Double, SingleByteString, DoubleByteString, Event, Structure. There is noDiscrete/Boolean, and noInt8/UInt8/UInt1/Guid/FileTime(those are SDK-only extensions inModels/HistorianDataType, recovered from the C++CDataTypepredicate IL — they are not settable on the managedHistorianTag). -
Tag type is data-type-derived, not separately settable.
ArchestrA.HistorianTag(--dump-type-members) has noTagTypeproperty — onlyTagDataType. It does carry the discrete/string-shaped fields (MessageOn/MessageOff,RolloverValue, dead-band/interpolation), and the type exposesValidateAnalog*,ValidateDiscreteGeneralProperties, andValidateDiscreteAndStringStorageProperties— but the analog-vs-discrete-vs-string decision is made internally from the data type, with no way to request "discrete." -
Native AddTag rejects every non-analog type client-side. Driving the native
HistorianAccess.AddTag(HistorianTag, …)(harnesswritescenario,--write-data-type) against the live server:Data type AddTag.Success ErrorCode ErrorType SingleByteString false ValidationFailed CustomError DoubleByteString false ValidationFailed CustomError Int1 false ValidationFailed CustomError Int8 / UInt8 n/a not in the native enum — Float (control) true Success — The error —
ErrorType=CustomError,ErrorCode=ValidationFailed,ErrorDescription="Transaction validation failed"— is raised by the client's ownValidate*chain before any WCF message is sent (the wrapper even auto-populates discrete defaultsMessageOn=ON/MessageOff=OFF, then fails validation). So the native client never emits a non-analogEnsT2/AddTagrequest.
Why it's not deliverable here
Because the native client refuses non-analog types client-side, no wire request exists to
reverse-engineer — there is no captured CTagMetadata variant for string or discrete tags, and the
SDK does not guess wire bytes. The rejection is not string-specific: Int1 (a non-string integer
outside the analog set {Float, Double, Int2, UInt2, Int4, UInt4}) fails identically, so the boundary
is "the analog set" rather than "strings only." Creating string/discrete tags on 2020 evidently goes
through a different subsystem (e.g. the configuration editor / SQL config path), not this client's
AddTag. R1.13 is closed as GATED, consistent with the mission note that these types "fail at native
AddTag — likely require a different path and are intentionally not supported."
Probe commands (read-only / sandbox-guarded)
dotnet run --project tools\AVEVA.Historian.ReverseEngineering -- enum-dump current\aahClientManaged.dll HistorianDataType
dotnet run --project tools\AVEVA.Historian.ReverseEngineering -- dnlib-method current\aahClientManaged.dll HistorianTag.ValidateDataType
# native AddTag probe (sandbox tag must start with RetestSdkWrite; --write-skip-add-value avoids the blocked value path):
dotnet run --project tools\AVEVA.Historian.NativeTraceHarness -- --scenario write --server-name localhost --tcp-port 32568 \
--write-sandbox-tag RetestSdkWriteNADoubleByteString --write-data-type DoubleByteString --write-skip-add-value