Boolean historization broken: EnsureTags(Int1) is server-degenerate — remap Boolean off Int1 #441

Closed
opened 2026-07-14 11:36:39 -04:00 by dohertj2 · 1 comment
Owner

Summary

The Gateway historian driver maps DriverDataType.Boolean → HistorianDataType.Int1
(HistorianTypeMapper, pinned by HistorianTypeMapperTests [InlineData(Boolean, Int1)] and the
0.2.0 proto3-presence tests). But EnsureTags(Int1) cannot provision a usable tag against a real
AVEVA Historian — Int1 is a Discrete/Boolean type and the gateway's analog tag-creation path stores
a degenerate tag, so the historian rejects/degrades it. Boolean historized tags therefore fail
auto-provisioning.

Surfaced by the live gate EnsureTags_boolean_sandbox_provisions_as_Int1
(tests/Drivers/.../Live/GatewayLiveIntegrationTests.cs) — the one red case in the R2-06
Category=LiveIntegration run (5/6).

Upstream root cause (now confirmed + closed on the gateway side)

HistorianGateway issue #11 investigated this with a live probe against the 2023 R2 historian
(2026-07-14) and proved Int1 is server-degenerate on the analog path, identical to UInt1:

Candidate code EnsureTags.BSuccess GetTagInfo Verdict
Int1 0x02 (Discrete) false 31-byte stub, no GUID degenerate
Int1 0x81 (Boolean) true 31-byte stub, no GUID degenerate
control Int2 0x29 true 115 bytes, real GUID valid

The 0x81 case even reports success=true but stores an unusable stub → a subsequent historical/live
write fails on the GetTagInfo GUID lookup. Creating an Int1/Boolean tag needs a discrete-tag path
that was never reverse-engineered.

HistorianGateway has (PR #16) documented Int1 as an explicit, evidence-based exclusion
(ProtocolEvidenceMissingException → gRPC Unimplemented) and reconciled its docs. The gateway
behaviour OtOpcUa sees is unchanged — EnsureTags(Int1) surfaces as per-tag success=false /
error="ProtocolEvidenceMissingException"; the batch is not aborted, deploy is not blocked, and a
pre-existing Float Boolean tag is neither retyped nor data-lost.

What OtOpcUa should decide

Boolean historization against this historian is not possible via Int1. Options:

  1. Map Boolean → a supported analog type (e.g. Int2) in HistorianTypeMapper and store 0/1.
    Boolean values round-trip as a small int; simplest path to a green live gate. Update
    HistorianTypeMapperTests + GatewayTagProvisionerTests + the live gate expectation.
  2. Leave Boolean unmapped / skip historization for Boolean tags and drop the live gate to an
    explicit "Boolean historization unsupported" assertion, matching the fire-and-forget failed=N
    reality.
  3. Wait for a discrete-tag creation path in the gateway/histsdk (no timeline; not scoped).

Recommendation: option 1 (Boolean → Int2) unless a caller specifically needs the historian tag
typed as discrete/boolean.

Refs

  • HistorianGateway issue #11 + PR #16 (root-cause + gateway-side resolution)
  • histsdk/docs/reverse-engineering/int1-write-degenerate-capture.md (live probe record)
  • OtOpcUa: src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway/ (HistorianTypeMapper,
    GatewayTagProvisioner); live gate GatewayLiveIntegrationTests.EnsureTags_boolean_sandbox_provisions_as_Int1
## Summary The Gateway historian driver maps `DriverDataType.Boolean → HistorianDataType.Int1` (`HistorianTypeMapper`, pinned by `HistorianTypeMapperTests` `[InlineData(Boolean, Int1)]` and the 0.2.0 proto3-presence tests). But **`EnsureTags(Int1)` cannot provision a usable tag** against a real AVEVA Historian — `Int1` is a Discrete/Boolean type and the gateway's analog tag-creation path stores a **degenerate** tag, so the historian rejects/degrades it. Boolean historized tags therefore fail auto-provisioning. Surfaced by the live gate `EnsureTags_boolean_sandbox_provisions_as_Int1` (`tests/Drivers/.../Live/GatewayLiveIntegrationTests.cs`) — the one red case in the R2-06 `Category=LiveIntegration` run (5/6). ## Upstream root cause (now confirmed + closed on the gateway side) HistorianGateway **issue #11** investigated this with a **live probe** against the 2023 R2 historian (2026-07-14) and proved `Int1` is server-degenerate on the analog path, identical to `UInt1`: | Candidate code | `EnsureTags.BSuccess` | GetTagInfo | Verdict | |---|---|---|---| | Int1 `0x02` (Discrete) | `false` | 31-byte stub, no GUID | degenerate | | Int1 `0x81` (Boolean) | `true` | 31-byte stub, no GUID | degenerate | | control Int2 `0x29` | `true` | 115 bytes, real GUID | valid | The `0x81` case even reports `success=true` but stores an unusable stub → a subsequent historical/live write fails on the `GetTagInfo` GUID lookup. Creating an Int1/Boolean tag needs a discrete-tag path that was never reverse-engineered. HistorianGateway has (PR #16) **documented Int1 as an explicit, evidence-based exclusion** (`ProtocolEvidenceMissingException` → gRPC `Unimplemented`) and reconciled its docs. The gateway behaviour OtOpcUa sees is unchanged — `EnsureTags(Int1)` surfaces as per-tag `success=false` / `error="ProtocolEvidenceMissingException"`; the batch is not aborted, deploy is not blocked, and a pre-existing Float Boolean tag is neither retyped nor data-lost. ## What OtOpcUa should decide Boolean historization against this historian is **not possible via Int1**. Options: 1. **Map Boolean → a supported analog type (e.g. `Int2`)** in `HistorianTypeMapper` and store 0/1. Boolean values round-trip as a small int; simplest path to a green live gate. Update `HistorianTypeMapperTests` + `GatewayTagProvisionerTests` + the live gate expectation. 2. **Leave Boolean unmapped / skip historization for Boolean tags** and drop the live gate to an explicit "Boolean historization unsupported" assertion, matching the fire-and-forget `failed=N` reality. 3. Wait for a discrete-tag creation path in the gateway/histsdk (no timeline; not scoped). Recommendation: **option 1 (Boolean → Int2)** unless a caller specifically needs the historian tag typed as discrete/boolean. ## Refs - HistorianGateway issue #11 + PR #16 (root-cause + gateway-side resolution) - `histsdk/docs/reverse-engineering/int1-write-degenerate-capture.md` (live probe record) - OtOpcUa: `src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway/` (`HistorianTypeMapper`, `GatewayTagProvisioner`); live gate `GatewayLiveIntegrationTests.EnsureTags_boolean_sandbox_provisions_as_Int1`
Author
Owner

Implemented option 1 (Boolean → Int2) in PR #442.

HistorianTypeMapper now maps DriverDataType.Boolean → HistorianDataType.Int2 and Boolean historizes as a small 0/1 integer. Int2 is a proven-creatable analog type (the #11 sweep: Int2/UInt2/Int4/UInt4/Float/Double all provision; only Int1 fails), and the value-write path already sends every value as a double on WriteLiveValues regardless of tag type, so a Boolean 0.0/1.0 round-trips cleanly. Both Float and Int2 are supported analog types, so a pre-existing Float Boolean tag retypes cleanly on its next deploy — no Int1 gap.

Unit pins updated (HistorianTypeMapperTests, GatewayTagProvisionerTests.Boolean_definition_carries_explicit_Int2_presence), the live gate renamed EnsureTags_boolean_sandbox_provisions_as_Int2 (retype probe reframed Float→Int2), docs updated (docs/Historian.md, CLAUDE.md, archreview STATUS.md → R2-06 live gate 6/6). Gateway driver unit 103/103, full build clean. Closes on merge of #442.

Implemented **option 1 (Boolean → Int2)** in PR #442. `HistorianTypeMapper` now maps `DriverDataType.Boolean → HistorianDataType.Int2` and Boolean historizes as a small 0/1 integer. Int2 is a proven-creatable analog type (the #11 sweep: Int2/UInt2/Int4/UInt4/Float/Double all provision; only Int1 fails), and the value-write path already sends every value as a `double` on `WriteLiveValues` regardless of tag type, so a Boolean `0.0/1.0` round-trips cleanly. Both Float and Int2 are supported analog types, so a pre-existing Float Boolean tag retypes cleanly on its next deploy — no Int1 gap. Unit pins updated (`HistorianTypeMapperTests`, `GatewayTagProvisionerTests.Boolean_definition_carries_explicit_Int2_presence`), the live gate renamed `EnsureTags_boolean_sandbox_provisions_as_Int2` (retype probe reframed Float→Int2), docs updated (`docs/Historian.md`, `CLAUDE.md`, archreview `STATUS.md` → R2-06 live gate 6/6). Gateway driver unit 103/103, full build clean. Closes on merge of #442.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dohertj2/lmxopcua#441