Skip to content

[#603] Adopt OPC UA quality codes - #628

Merged
wes-johnson merged 1 commit into
eclipse-sparkplug:developfrom
wes-johnson:develop
Sep 16, 2026
Merged

wes-johnson merged 1 commit into
eclipse-sparkplug:developfrom
wes-johnson:develop

Conversation

@wes-johnson

Copy link
Copy Markdown
Contributor

[#603] Adopt OPC UA quality codes

Closes #603. Per the spec team decision that this goes into v4.

The three defined qualities — 0 BAD, 192 GOOD, 500 STALE — are OPC DA legacy (192 is the
OPC DA Good byte), with no Uncertain tier and no machine-readable reason for a bad value. The only
assertion on the field, payloads-quality-value, required the value to be an int32, which the schema
already enforces.

Why now, and why this is a free change

quality was promoted to a first-class Metric field in d9a338d ([#145]), so the carrier question is
already settled and #603 is purely about vocabulary. Nothing implements that field yet: Tahu's
sparkplug_b.proto has no quality field at all, and v3 quality actually travels as a PropertySet
property named "Quality" (SparkplugUtil.getQualityCode(), defaulting to 192). So the vocabulary is
a free choice right now and stops being free when v4 ships.

Design

Adopts the OPC UA StatusCode numbering — normatively the bit layout and severity encoding,
informatively the wider code list. Ignition 8 already uses an OPC-UA-shaped QualityCode internally,
so this moves the wire format toward where the dominant Sparkplug source already is.

New Quality section, six statements:

Statement
quality-severity-valid — bits 30–31 ∈ {00 Good, 01 Uncertain, 10 Bad}; 11 reserved tck-testable
quality-severity-authoritative — consumers act on severity alone, MUST NOT require subcode recognition tck-not-testable
quality-absent-good — absent ≡ Good tck-not-testable
quality-recognized-codes — guaranteed named set; producers MAY use any OPC UA code tck-not-testable
quality-stale-code — STALE ≡ UncertainLastUsableValue (0x40900000) tck-not-testable
quality-context-not-required — diagnostic text only tck-not-testable

quality-severity-authoritative is what lets the spec name eleven codes rather than enumerate ~250:
an unrecognized subcode still yields correct Good/Uncertain/Bad handling. All eleven values were
checked against the OPC Foundation's published StatusCode.csv.

The 12 Chapter 5 STALE statements are unchanged — they say "mark as STALE", and STALE now resolves to
a code.

int32 → uint32

OPC UA StatusCode is a UInt32 and Bad severity is bit 31, so every Bad code is negative in an
int32
(BadNotConnected = 0x808A0000 = −2138439680). Three consequences:

  • protobuf sign-extends a negative int32 to 64 bits before varint encoding: 10 bytes on the wire
    versus 5 for the same bits as uint32.
  • spJv4.0 would serialize every Bad code as a large negative JSON number.
  • quality was the only int32 in the schema; uint32 is already used elsewhere.

Caveat: protobuf-java maps uint32 to int, so Tahu still needs Integer.toUnsignedLong() for
comparison and display. C#, Go, C++, Python and JavaScript get a genuinely unsigned type.

TCK

SendComplexDataTest.checkQualityCodeRequirement() validated the v3 property mechanism — key
"Quality", values 0/192/500 — which v4 no longer defines. Removed with a comment rather than
retargeted, since there is nothing equivalent to check; it becomes coverage for the v4 TCK rewrite.

Note for reviewers

The bare [tck-not-testable] form is unsafe. tck-audit.xsl:269 falls back to a position-derived
letter when the role carries no id-, so untestable statements get ids like a/b/c that are not
unique across the document, and requirements.py flattens them into one Java class — this broke
tck:compileJava with duplicate ID_A/ID_B until each statement was given an explicit id. The XSL
accepts [tck-not-testable tck-id-…], which is what this PR uses.

Follow-up: the two [tck-not-testable] statements added in #601 hold letters b and e by luck,
alongside the pre-existing Chapter 5 one at i. They compile today but will collide with any future
untestable statement, and should be given explicit ids.

Verified: ./gradlew build succeeds from clean; 605 → 610 assertions; zero dangling TCK constants;
all link:# anchors resolve; every tck-id appears exactly twice.

@wes-johnson
wes-johnson merged commit 1a6da12 into eclipse-sparkplug:develop Sep 16, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants