Files
lmxopcua/scripts/validation/aveva-history-801/README.md
T
Joseph Doherty f607ee7502 test(historian): #801 real-AVEVA history gate over the SDK wire (env-gated)
AvevaGatewayHistoryLiveGateTests boots the server in-process, wires the production
Gateway backend (HistorianBackendRegistry -> GatewayHistorianDataSource) to a real
HistorianGateway and drives HistoryRead over a real SDK session: raw (all, paged,
reverse, bounds), modified, the ten served aggregates and seven refused ones, AtTime,
events (all, 1-event pages, reverse), a missing tag, HistoryServerCapabilities and
node metadata, and a HistoryUpdate that must be refused (sandbox-bound node only).
Every wire answer is compared with the backend's direct answer; the run is dumped as
JSON for cross-SDK and SQL comparison. Skips unless HISTGW_GATEWAY_ENDPOINT and
HISTGW_GATEWAY_APIKEY are set; read-only by construction.

scripts/validation/aveva-history-801: run/compare instructions, the SDK 1.5.378
baseline port patch (API spelling only), and the diff / SQL / aggregate / histgw-cli
comparison scripts.

Claude-Session: https://claude.ai/code/session_01GT67ACmpMR7b9LGRPLijMi
2026-11-12 10:02:21 -05:00

2.7 KiB

#801 real-AVEVA history gate (SDK 2.0.1 vs 1.5.378.182)

Harness: tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests/AvevaGatewayHistoryLiveGateTests.cs. It boots the OtOpcUa server in-process (ServerEventRig), wires the production Gateway backend (HistorianBackendRegistry → GatewayHistorianDataSource) to a real HistorianGateway, and drives HistoryRead / HistoryUpdate over a real SDK client session. It skips unless HISTGW_GATEWAY_ENDPOINT and HISTGW_GATEWAY_APIKEY are set. It is read-only: the Gateway backend has no updater, and the only HistoryUpdate goes to a node bound to HISTGW_WRITE_SANDBOX_TAG and is refused by the server before any backend call.

Record of the 2026-11-12 run: docs/plans/evidence/2026-11-10-opcua2/aveva-history-801.md.

Run (SDK 2, this tree)

export HISTGW_GATEWAY_ENDPOINT=http://wonder-app-vd03.zmr.zimmer.com:5221
export HISTGW_GATEWAY_APIKEY="$(cat /path/to/key-file)"     # never on the command line
export HISTGW_WRITE_SANDBOX_TAG=HistGW.LiveTest.Sandbox
export OTOPCUA_AVEVA_GATE_OUT=/tmp/aveva-gate OTOPCUA_AVEVA_GATE_LABEL=sdk2-$(git rev-parse --short HEAD)
dotnet test tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests -c Release \
  --filter "FullyQualifiedName~AvevaGatewayHistoryLiveGate"

Baseline (SDK 1.5.378.182)

git worktree add --detach ../otopcua-sdk1 efb9197a      # master's first parent before the SDK 2 merge b36e688f
cp tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests/AvevaGatewayHistoryLiveGateTests.cs \
   ../otopcua-sdk1/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests/
(cd ../otopcua-sdk1 && patch -p1 < "$OLDPWD/scripts/validation/aveva-history-801/sdk1-baseline-port.patch")
# then the same dotnet test command in ../otopcua-sdk1 with OTOPCUA_AVEVA_GATE_LABEL=sdk1-efb9197a

sdk1-baseline-port.patch changes only SDK API spelling: collections in place of ArrayOf, byte[] in place of ByteString, DataValue.Value in place of ClrValue(). The queries, windows and comparisons are unchanged.

Compare

python3 -I diff_runs.py  $OUT/aveva-gate-sdk2-*.json $OUT/aveva-gate-sdk1-efb9197a.json   # every case, row and event
python3 -I sql_raw_check.py $OUT/aveva-gate-sdk2-*.json      # raw rows vs Runtime.dbo.History (Full, UTC); needs
                                                            # HISTORIAN_SQL_USER / HISTORIAN_SQL_PASSWORD, read-only
python3 -I aggregate_recompute.py $OUT/aveva-gate-sdk2-*.json  # simple aggregates recomputed from the raw rows
python3 -I hgcli_raw_check.py $OUT/aveva-gate-sdk2-*.json $OUT  # vs histgw-cli read-raw output (hgcli-<tag>-<date>.jsonl)