doctor
opcua-modeler doctor [-i <file> ...] [-C <folder>] [--strict] [--json]
generate answers one question: does this model compile? doctor answers the others,
the ones that are about the folder rather than the model:
- is this file the form its name promises?
- does its NodeId ledger still match the model it was sealed against?
- is the
.Nodeset2.xmlnext to it still the one this model produces?
It compiles nothing, reads only, and never writes. It works on a folder that does not build — which is exactly when you need it — and it runs in milliseconds, so it fits in a pre-commit hook or a CI gate.
$ opcua-modeler doctor
opcua-modeler doctor — .
1 file(s) checked
DSL-W-FORM-NOT-SEALED (titi.opcua.yaml)
titi.opcua.yaml carries the self-contained extension but holds no 'kind: symbols'
document. NodeIds will be read from and written to titi.opcua.symbols.csv instead
of the file itself.
fix: opcua-modeler pack -i titi.opcua.yaml (seal the symbol table into the file)
or rename it to titi.model.yaml to keep the pair form
0 error(s), 1 warning(s), 0 advisory(ies)
Options
| Option | Meaning |
|---|---|
-i, --input | file(s) to check. Default: every model file in the folder (*.opcua.yaml, *.model.yaml, *.nodeset.yaml, model.yaml) |
-C, --folder | folder to check, and the folder paths are reported relative to (default: the current folder) |
--strict | exit non-zero on warnings too, not only on errors |
--json | print the report as JSON and nothing else |
-q, --quiet | findings only: no header, no summary |
Exit code is 0 when there is no error (--strict: no error and no warning) and 1
otherwise, so opcua-modeler doctor --strict is usable as a gate.
doctor needs no licence: a diagnostic has to work on a machine where something is
wrong, and that includes the licence.
What it checks
Form — does the name match the file?
A model lives in one of two forms, and the NodeId ledger lives in a different place in
each: the pair (pump.model.yaml + pump.model.symbols.csv) or the
self-contained stream (pump.opcua.yaml, ledger embedded).
generate picks the form from the file's content, while .opcua.yaml is the reserved
extension of the sealed form — so a hand-written single document that happens to be named
titi.opcua.yaml silently takes the pair path.
| Code | What it means |
|---|---|
DSL-W-FORM-KIND-IN-HEAD | a kind: key in the model document. kind: opens a tail document; document 1 is the model and is marked profile: self-contained. A --- is missing above it |
DSL-W-FORM-NOT-SEALED | named *.opcua.yaml, but carries no kind: symbols document. Run pack, or rename it to *.model.yaml |
DSL-I-FORM-MISNAMED | a sealed stream that is not named *.opcua.yaml. Editors bind the single-document model schema to it and flag every line of its tail |
DSL-W-FORM-ORPHAN-CSV | a .symbols.csv beside a sealed stream. The embedded table is the only ledger that is read; the CSV is ignored and will drift |
Ledger — are the NodeIds sound?
| Code | What it means |
|---|---|
DSL-W-DOCTOR-LEDGER-STALE | the sealed digest no longer matches the model. Nodes added since have no id yet; nodes removed since are still listed. generate re-seals |
DSL-E-DOCTOR-DUPLICATE-ID | two nodes hold the same NodeId. This ships a NodeSet whose references point at the wrong node |
DSL-E-DOCTOR-RECYCLED-ID | an id is both retired and live. A client that cached the old node now resolves it to a different one |
DSL-E-DOCTOR-NEXTID | nextId is at or below an id already in use: the next node added would collide |
DSL-I-DOCTOR-NO-LEDGER | a pair-form model with no .symbols.csv. Every NodeId will be allocated afresh on the next generate — harmless the first time, a renumbering of a published NodeSet every time after |
Outputs — is the generated NodeSet still current?
DSL-W-DOCTOR-OUTPUT-STALE when the .Nodeset2.xml is older than the model it was
generated from. generate writes the ledger before the outputs precisely so that this
comparison is meaningful on a self-contained file, whose input is itself rewritten by the
build.
What it does not do
doctor never applies a fix. Most findings have two legitimate answers — seal it or
rename it, delete the CSV or unpack back to the pair — and only you know which one you
meant. Every finding names the exact command for each.
opcua-modeler doctor --strict before opcua-modeler generate --frozen: the first
catches a folder that has drifted, the second catches a model that has.