Equipment-class.schema.json gains an `extends` field for class inheritance — child classes inherit signals, alarms, and stateModel from the parent and can add new ones or override individual entries by name. Convention: `_` prefix on classId marks an abstract base class (e.g. `_base`) intended only to be extended, not assigned directly to equipment via Equipment.EquipmentClassRef. FANUC CNC class updated to extends: "_base"; redundant identity signals (Version, ActiveAlarmCount) removed since they're now in the base; remaining FANUC-specific signals updated with cross-references showing how they feed into the base signals at Layer 3 (RunState → canonical Running/Idle/Faulted derivation; AlarmActive → HasActiveAlarms / HighestActiveAlarmSeverity; PartsCount → TotalCycles; MainProgramNumber → CurrentRecipe). Format-decisions.md adds D9 (rationale for `_base` + `extends` inheritance, with references to OPC 40010 / Part 9 / ISO 22400 / handoff) and D10 (signal `category` drives OPC UA folder placement, per OPC 40010 Identification + Status pattern, with a category-to-folder mapping table). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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"