Verify a converted IOC using db-compare#
After converting a builder XML IOC to ioc.yaml, the most rigorous way to
verify correctness is to run the Generic IOC inside the devcontainer, generate
the EPICS database, and compare it record-by-record against the original
builder-compiled _expanded.db.
builder2ibek db-compare performs this comparison, normalising floating-point
values and ignoring order, so it catches genuine record and field differences
rather than cosmetic ones.
Prerequisites#
A Generic IOC repo (e.g.
ioc-hidenrga) cloned locally with the devcontainer configuredThe converted
ioc.yaml(see Convert a builder XML IOC instance to ibek YAML)The original
_expanded.dbfrom the builder IOC build, accessible on the host (typically under/dls_sw/.../<IOC>/db/<IOC>_expanded.db)Docker or Podman, and VSCode with the Dev Containers extension
1. Prepare the IOC instance config#
The devcontainer expects the instance configuration in a folder containing
ioc.yaml. Make sure your services repository is cloned next to your
generic IOC folder e.g.
For example, to test BL11I-CS-IOC-09 which is an ioc-hidenrga based IOC we
would need:
my-work-folder/
├── ioc-hidenrga
└── i11-services
└── services
└── bl11i-cs-ioc-09
├── config/
│ └── ioc.yaml ← your converted ioc.yaml goes here
├── values.yaml
└── templates
2. Open the Generic IOC in the devcontainer#
First make sure the ibek-support* submodules are present
git submodule update --init
Open the Generic IOC repository in VSCode and choose “Reopen in Container”. This builds the developer image, compiles all support modules, and drops you into a shell with:
/epics/ioc/— the compiled IOC binary/epics/support/— all support libraries, DBs, and protocol files/epics/ibek-defs/— combined ibek schema for the container
The ibek-support and ibek-support-dls submodules from the repo are mounted
at /epics/generic-source/ibek-support*, so edits to support YAML are
immediately visible inside the container.
3. Point the devcontainer at your instance#
Inside the devcontainer terminal, use ibek dev instance to symlink your
instance config folder to /epics/ioc/config:
ibek dev instance /workspaces/i11-services/services/bl11i-cs-ioc-09
This creates:
/epics/ioc/config -> /workspaces/i11-services/services/bl11i-cs-ioc-09/config
Because it is a symlink any edits to ioc.yaml on the host are immediately
reflected inside the container without restarting.
4. Generate the EPICS runtime files#
Generate st.cmd and the compiled database without actually launching the IOC:
ibek runtime generate2 /epics/ioc/config
ibek runtime generate2 discovers all support YAML files automatically from
/epics/generic-source, so no manual list of files is required.
Alternatively, start.sh calls the same command and then launches the IOC:
/epics/ioc/start.sh
After generation, the runtime assets are in /epics/runtime/:
/epics/runtime/
├── st.cmd ← generated startup script
├── ioc.db ← fully expanded EPICS database
└── protocol/ ← StreamDevice protocol files
5. Compare with the original DB#
The original builder IOC’s expanded database lives in the builder tree at:
/dls_sw/work/R3.14.12.7/support/BL11I-BUILDER/iocs/BL11I-CS-IOC-09/db/BL11I-CS-IOC-09_expanded.db
(Host path — mounted at the same location in the devcontainer since dls_sw
is typically bind-mounted.)
Run builder2ibek db-compare:
uv run builder2ibek db-compare \
/dls_sw/work/R3.14.12.7/support/BL11I-BUILDER/iocs/BL11I-CS-IOC-09/db/BL11I-CS-IOC-09_expanded.db \
/epics/runtime/ioc.db
Example output when the databases match:
*******************************************************************
Records in original but not in new:
*******************************************************************
Records in new but not in original:
*******************************************************************
records in original: 37755
records in new: 37755
records missing in new: 0
records extra in new: 0
*******************************************************************
Save the output to a file for review:
uv run builder2ibek db-compare \
/dls_sw/.../BL11I-CS-IOC-09_expanded.db \
/epics/runtime/ioc.db \
--output compare.diff
6. Interpret the output#
Records in original but not in new — records present in the builder IOC but
missing from the ibek-generated DB. These represent features not yet covered
by the support YAML. Investigate whether they come from a support module that
needs an entity model, or from a dbLoadRecords call that needs adding to
pre_init or databases.
Records in new but not in original — extra records added by ibek. Often
benign additions from devIocStats or autosave, but worth checking.
Fields … are different — a record exists in both but has field-value differences. Usually caused by a macro not being passed correctly, a default value differing between builder and ibek, or a template version mismatch.
7. Iterating on discrepancies#
When you find differences:
Missing module records: add the module to
ibek-support-dlsfollowing the Creating ibek support YAML from a builder.py module and rebuild the devcontainer (or runibek dev support <module-path>to hot-load the module without a full rebuild).Wrong macro values: check
databases.argsin the support YAML and compare with the original substitution file.Wrong record type (e.g.
genSubvsaSub): this typically means the generic IOC is using a different version of the support module than the original builder IOC. Check the module version ininstall.yml.Expected differences: use
--ignoreto suppress known-good differences, e.g. IOC statistics records that always have different names:
uv run builder2ibek db-compare \
original.db new.db \
--ignore "BL11I-CS-IOC-09:VERSION" \
--ignore ":HEARTBEAT"
Quick reference#
Step |
Command |
|---|---|
Point devcontainer at instance |
|
Generate runtime files only |
|
Compare databases |
|
Save comparison to file |
|
Ignore specific records |
|
Remove duplicates in original |
|
Hot-load a support module |
|