Decoded the GetTagInfoFromName response shape across multiple tag types via
captured raw bytes (sanitized decoder script in scripts/decode-taginfo-bytes.py):
- Compact-ASCII string slot count varies by tag origin: 2 strings for
MDAS-routed external tags (TagName + MetadataProvider), 4 strings for local
Sys tags (TagName + Description + ItemName + CreatedBy). Parser now walks
strings dynamically until the next byte isn't the 0x09 marker.
- Trailing region after the 4-byte fixed block holds (for analog tags) two
doubles for MinEU/MaxEU plus an optional EngineeringUnit compact ASCII
string and other fields whose exact positions vary. Parser uses a tolerant
scan: tries each 8-byte alignment 0..7, picks the first sane (Min ≤ Max,
finite, not all-zeros, |x| ≤ 1e15) double pair as MinEU/MaxEU, and finds
the first plausible compact ASCII string (1..32 ASCII bytes, not numeric)
as EngineeringUnit.
HistorianTagMetadata.Description / EngineeringUnit / MinRaw / MaxRaw nullable
slots already existed; they're now populated. Live verification: SysTimeSec
returns Description="System Time : Seconds", MaxRaw=59.0, EngineeringUnit
="Seconds".
Tests: 109 → 114 (+4 synthetic-fixture parser tests + 1 live integration
test for the populated analog metadata path). Bulk descriptor probe helper
(GetTagInfoRawBytesForProbe) added for future layout work; raw bytes never
committed because they contain CreatedBy DOMAIN\user identity.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Full read-only SDK (src/AVEVA.Historian.Client) implementing the CLAUDE.md required
surface against AVEVA Historian's binary WCF protocol — no native AVEVA runtime
dependency. All operations live-verified against a local Historian:
- ProbeAsync, ReadRawAsync, ReadAggregateAsync, ReadAtTimeAsync, ReadEventsAsync
- BrowseTagNamesAsync, GetTagMetadataAsync (17 native data-type codes mapped)
- GetConnectionStatusAsync, GetStoreForwardStatusAsync, GetSystemParameterAsync
- 108/108 unit + integration tests pass
Includes the reverse-engineering toolkit (tools/AVEVA.Historian.ReverseEngineering)
used to decode the protocol: WCF probes, IL inspection via dnlib, and IL-rewrite
instrumentation (instrument-wcf-{write,read}message etc.) plus the .NET Framework
trace harness (tools/AVEVA.Historian.NativeTraceHarness) for parity testing.
Sanitized handoff evidence under docs/reverse-engineering/. Native AVEVA binaries
(current/, aveva-install-x64/, aveva-install-x86/) are gitignored — fetch separately
from the AVEVA installer.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>