Skip to content

ADR 0022: Device discovery classifies on the type-definition namespace — a recognized PA-DIM / OPC UA-DI type is a data-plane signal

Status: Accepted Date: 2026-07-13 Issue: #934

Context

ADR 0020 decided that Device Discovery classifies each discovered OPC UA node onto a control plane — data-plane (field device ⇒ template-free ControlModule instance) or service-plane (module/skid ⇒ Unit + serviceBinding). The signal it shipped is structural: Methods present ⇒ service-plane; Variables only ⇒ data-plane. ADR 0020 explicitly forecast a stronger signal it did not yet build — "A PA-DIM (PADIMType) or MTP/PEA type-definition, when present, is a strong positive signal for the respective plane" — and filed it as follow-up B (#928).

The structural proxy has a real blind spot. A discovered field device that a server models as a full PA-DIM object still exposes only Variables, so the proxy lands it on the data-plane by accident of shape, not by recognition — and it renders identically to a node the wizard merely defaulted to ControlModule because it had nothing better to say. The operator cannot see that the wizard recognized a standards-modeled device versus fell back to a default. That missing provenance is what makes native PA-DIM ingestion (#935 — variable roles/units, NE 107 health) hard to build on top: there is no authoritative "this is a PA-DIM device" bit to key on.

#928 was decomposed (2026-07-13) into #934 (this decision), #935 (PA-DIM ingestion + NE 107 health — CRD surface, blocked on the OPC 30081 spec), and #936 (native MTP service model — blocked on VDI/VDE/NAMUR 2658-4:2022 and the unfinished ISA-88/MTP alignment). This ADR covers only the verifiable, non-blocked slice: recognizing a node's type-definition when it resolves into a public device-model companion namespace.

Two namespace URIs were verified 2026-07-13 against the OPC Foundation online reference (reference.opcfoundation.org) — not taken from memory, per the #928 caution that a prior companion-spec identifier (OPC 40080) proved unverifiable:

  • PA-DIMhttp://opcfoundation.org/UA/PADIM/ (OPC 30081, Annex A.1).
  • OPC UA for Devices (DI)http://opcfoundation.org/UA/DI/ (OPC 10000-100; DeviceType and subtypes).

Unlike the numeric namespace index (which is assigned per server and drifts), the namespace URI is stable across servers — so it is the correct thing to key classification on. The wizard already resolves node namespace URIs from the server's cached NamespaceArray (the #303 drift-resistant address work); this reuses that machinery for the type-definition's NodeID.

Decision

Device discovery resolves each candidate's type-definition namespace URI and, when it is a recognized device-model companion namespace (PA-DIM or OPC UA-DI), treats that as a strong positive data-plane signal — surfaced to the operator as recognition provenance. The structural Methods-vs-Variables proxy remains the fallback when no recognized type-definition is present.

  • pkg/opcuaclient resolves the type-definition NodeID's namespace URI (BrowseChild.TypeDefinitionNamespaceURI) via the cached NamespaceArray, and computes a control-plane hint (BrowseChild.ClassSignal) with a single exported pure function, PlaneSignalFor(uri), returning PlaneSignalData ("data") for the two recognized URIs and "" otherwise. Both fields ride through to SearchHit (the shape the wizard consumes) as additive, omitempty JSON.
  • The wizard (discover.js) labels a recognized candidate ("PA-DIM device" / "OPC UA device") in the scan list and on its review row. Because every leaf candidate already defaults to ControlModule (ADR 0020), the signal does not change the default — it changes the operator's information: a positive classification with visible provenance, distinguished from a bare default. The "Emit as" dropdown stays the manual override.
  • Recognition is exact-match on the namespace URI, not substring. A superset URI (a hypothetical .../PADIM/Extras/) does not match — new companion namespaces must be added deliberately, never absorbed by prefix.

Service-plane (MTP / PEA) type-definition recognition is deliberately out of scope. Unlike PA-DIM, MTP has no stable public PEAType companion namespace to key on — its OPC UA runtime binding (VDI/VDE/NAMUR 2658 Blatt 5.1) is not a distinct namespace — so service-plane classification stays structural until #936. This ADR does not claim PA-DIM conformance: it recognizes the type-definition namespace only; ingesting the PA-DIM information model (variable roles/units, NE 107 health) is #935.

Alternatives Considered

  • Match the numeric namespace index (e.g. "type-def in ns=5 ⇒ PA-DIM"): rejected — the index is server-assigned and unstable; only the URI is portable. This is the same reasoning as the #303 drift-resistant address.
  • Match by type-definition BrowseName (e.g. DeviceType, PADIMType): rejected — a BrowseName is not namespace-qualified in the wire form we hold and collides across vendors; the namespace URI is the authoritative, unambiguous key.
  • Reintroduce the #348/#857-deleted companion-spec registry to classify many device types: rejected — ADR 0020 already ruled this out. Only the two verified namespace URIs are recognized, not a general registry, and one of the old registry's identifiers (OPC 40080) was unverifiable.
  • Flip the default to Unit on a service-plane type-definition signal: not built here — folded into #936 because there is no reliable PEA type-definition signal to flip on.
  • Substring/prefix match on the URI (tolerate versioned suffixes): rejected — silent over-recognition of unintended namespaces is worse than requiring a deliberate addition; recognition is exact.

Consequences

  • Additive API surface, no CRD change. Two omitempty fields (typeDefinitionNamespaceURI, classSignal) appear on the /discovery/opcua/search (and /browse) response DTOs. Older clients ignore them; the field is empty for servers that expose no recognizable type-definition, so the structural path is unchanged for them.
  • A foundation for #935. The authoritative "this is a PA-DIM device" bit now exists at discovery time, which native PA-DIM ingestion keys on.
  • Bounded blast radius. PlaneSignalFor is a pure two-case function with exact-match semantics and direct unit tests; adding a future recognized namespace is a one-line, deliberate change.
  • No conformance claim. Recognition ≠ ingestion ≠ conformance. The wizard says "recognized as a PA-DIM device," not "PA-DIM conformant."
  • Reversibility: high — client-side labelling plus two additive backend fields, no schema or persisted-state change.