Includes: README explaining purpose / scope / temporary-location framing / format decision, CONTRIBUTING.md with proposed workflow + per-class semver versioning policy + validation commands, format/equipment-class.schema.json defining the shape of a class template (classId, version, displayName, applicability, signals, alarms, optional stateModel), format/tag-definition.schema.json defining the shape of a single canonical signal (name, dataType, category, unit, isArray, accessLevel, writeIdempotent, isHistorized, scaling), format/uns-subtree.schema.json defining the shape of a per-site UNS subtree (enterprise + site + areas + lines), classes/fanuc-cnc.json as the worked pilot class with 16 signals + 3 alarms + suggested state-derivation notes (per OtOpcUa corrections doc D1), uns/example-warsaw-west.json as a worked UNS subtree example, docs/overview.md (what / why / lifecycle / what's NOT in this repo), docs/format-decisions.md (8 numbered decisions covering JSON Schema choice per corrections D2, per-class semver, additive-only minor bumps, _default placeholder reservation, signal-name vs UNS-segment regex distinction, stateModel-as-informational, no per-equipment overrides at this layer, applicability.drivers as OtOpcUa driver enumeration), docs/consumer-integration.md (how OtOpcUa / Redpanda / dbt each integrate). $id URLs in the JSON schemas resolve at the actual current path so validators don't 404. Top-level README adds a row to the Component Detail Files table pointing to schemas/. Corrections doc B2 (schemas-repo dependencies) marked partially RESOLVED with the seed location and a list of what still needs the plan team or cross-team owner to decide (owner team naming, dedicated repo migration, format-decision ratification, FANUC CNC pilot confirmation, CI gate setup, Redpanda + dbt consumer integration plumbing). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6.6 KiB
schemas — Canonical OT Equipment Definitions
Status: DRAFT — initial seed contributed by the OtOpcUa team (
lmxopcua/docs/v2/acl-design.md,lmxopcua/docs/v2/plan.mddecisions #112, #115, corrections doc D1+D2). Ownership of this content is TBD — should be assigned to a cross-team authority since it's consumed by OT and IT systems alike. The future owner team should treat this seed as a starting point, not a finished spec.Temporary location: this seed lives at
3yearplan/schemas/as a sub-tree of the 3-year-plan repo because Gitea's push-to-create is disabled and creating the dedicatedschemasrepo requires a manual UI step. Once the dedicated repo is created (proposed:gitea.dohertylan.com/dohertj2/schemas) and an owner team is named, this content should migrate over and references update accordingly. Until then, treat this directory as the canonical location for the seed.
Purpose
Single source of truth for the org's canonical OT equipment definitions:
- UNS hierarchy — the per-site Unified Namespace subtree (Enterprise / Site / Area / Line / Equipment) per the 3-year-plan handoff §UNS Naming Hierarchy
- Equipment-class templates — per-class declarations of which raw signals each equipment type exposes (FANUC CNCs surface these; Modbus PLCs surface those; etc.)
- Type vocabulary — the canonical machine-state values (
Running/Idle/Faulted/Starved/Blocked) and any other shared enumerations consumers need
Who consumes this
Three surfaces per the 3-year-plan handoff §"Canonical Model Integration":
| Consumer | How |
|---|---|
| OtOpcUa equipment namespace | At deploy/config time, OtOpcUa nodes fetch the equipment-class template referenced by Equipment.EquipmentClassRef and use it to validate the operator-configured tag set. Drift = config validation error |
| Redpanda topics + Protobuf schemas | Equipment-class templates derive Protobuf message definitions for canonical events (equipment.state.transitioned, etc.) |
| dbt curated layer in Snowflake | Same templates derive column definitions and dimension tables for the curated analytics model |
OtOpcUa is one consumer of three. Decisions about format, structure, and naming live with the schemas-repo owner team (TBD), not with any one consumer.
Format
JSON Schema (Draft 2020-12) authored in this repo; Protobuf code-generated for wire serialization where needed (per OtOpcUa corrections doc D2 recommendation, blessed by the OtOpcUa team but pending schemas-repo team review).
Why JSON Schema as the authoring format:
- Idiomatic for .NET (System.Text.Json + JsonSchema.Net) — OtOpcUa reads templates with no extra dependencies
- Idiomatic for CI tooling (every CI runner can
jq/ validate JSON Schema without extra toolchain) - Supports validation at multiple layers: operator-visible Admin UI errors in OtOpcUa, schemas-repo CI gates, downstream consumer runtime validation
- Better authoring experience than Protobuf (binary format, .proto compiler, poor merge story)
- Where wire-format efficiency matters (Redpanda events), code-generate Protobuf from the JSON Schema source. One-way derivation is simpler than bidirectional sync.
Structure
schemas/
├── README.md This file
├── CONTRIBUTING.md How to add a new class, validate, PR process
├── format/ JSON Schemas defining the format of everything below
│ ├── equipment-class.schema.json Shape of an equipment-class template
│ ├── uns-subtree.schema.json Shape of a per-site UNS subtree definition
│ └── tag-definition.schema.json Shape of an individual tag declaration inside a template
├── classes/ Equipment-class templates
│ └── fanuc-cnc.json Pilot class per OtOpcUa corrections D1
├── uns/ Per-site UNS subtree definitions
│ └── example-warsaw-west.json Worked example
└── docs/
├── overview.md What this repo is, who uses it, lifecycle
├── format-decisions.md Why JSON Schema, structure rationale, Protobuf derivation
└── consumer-integration.md How each of the 3 consumers integrates
Lifecycle
- Author — humans edit JSON files in this repo, validated against the schemas in
format/by the CI gate - Publish — merging to
mainmakes the new content the authoritative version; semver tag on each release - Consume — each consumer pulls a specific tagged version (or
mainfor staging environments) and integrates perdocs/consumer-integration.md
The current state has no consumers actively reading from this repo — content is seed-only. First wiring happens in OtOpcUa Phase 1+ when Equipment.EquipmentClassRef validation lands (see lmxopcua/docs/v2/plan.md decision #112).
Open Questions
Owner-team-decisions (not for OtOpcUa or any single consumer to make alone):
- Repo ownership and review process: who owns this repo? PR review SLA? Format changes need who to sign off?
- Versioning policy: semver per release? Per-class versioning vs whole-repo versioning? How do consumers pin versions?
- Backward-compatibility policy: when a class adds a new required signal, do all existing equipment in OtOpcUa's central config DB need to be updated atomically? Or graceful degradation?
- Cross-class shared types: how does a
Positionmeasurement (used by both FANUC CNC and a Beckhoff axis) avoid duplicate definitions? - Pilot class scope: FANUC CNC chosen per OtOpcUa corrections D1 because FOCAS already has a fixed pre-defined hierarchy. Confirm with the schemas-repo team that this is the right starting class.
- Enterprise shortname (currently
entplaceholder) — the UNS subtree definitions reference an Enterprise segment; the canonical value comes from organizational naming authority.
References
- 3-year-plan handoff:
gitea.dohertylan.com/dohertj2/3yearplan→handoffs/otopcua-handoff.md§"UNS Naming Hierarchy" + §"Canonical Model Integration" + §"Digital Twin Touchpoints" - OtOpcUa corrections doc:
gitea.dohertylan.com/dohertj2/3yearplan→handoffs/otopcua-corrections-2026-04-17.md§B2 (schemas-repo dependencies) + §D1 (pilot class) + §D2 (format) - OtOpcUa v2 plan:
gitea.dohertylan.com/dohertj2/lmxopcua→docs/v2/plan.mddecisions #108–115 + §"UNS naming hierarchy"