Skip to main content

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.xml next 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

OptionMeaning
-i, --inputfile(s) to check. Default: every model file in the folder (*.opcua.yaml, *.model.yaml, *.nodeset.yaml, model.yaml)
-C, --folderfolder to check, and the folder paths are reported relative to (default: the current folder)
--strictexit non-zero on warnings too, not only on errors
--jsonprint the report as JSON and nothing else
-q, --quietfindings 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.

CodeWhat it means
DSL-W-FORM-KIND-IN-HEADa 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-SEALEDnamed *.opcua.yaml, but carries no kind: symbols document. Run pack, or rename it to *.model.yaml
DSL-I-FORM-MISNAMEDa 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-CSVa .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?

CodeWhat it means
DSL-W-DOCTOR-LEDGER-STALEthe 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-IDtwo nodes hold the same NodeId. This ships a NodeSet whose references point at the wrong node
DSL-E-DOCTOR-RECYCLED-IDan id is both retired and live. A client that cached the old node now resolves it to a different one
DSL-E-DOCTOR-NEXTIDnextId is at or below an id already in use: the next node added would collide
DSL-I-DOCTOR-NO-LEDGERa 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.

In CI

opcua-modeler doctor --strict before opcua-modeler generate --frozen: the first catches a folder that has drifted, the second catches a model that has.