Author Your Own Runtime Pattern#
In Add the Standard Detector Plugins you vendored a published runtime pattern — the
standard AreaDetector plugin set — into an existing IOC instance. Here you
author your own pattern: a cut-down plugin set called basicPlugins,
published from a fork of the ibek-runtime-support library.
This is the runtime mirror of Create a Generic IOC, where you forked a
build-time support library and rebuilt an image. This time nothing is
rebuilt — a generic IOC picks up your new support at its next container start,
because ibek pattern vendors the file-set into the instance’s config/ and
ibek runtime generate2 discovers it at boot.
For this tutorial you are back in your t01-services compose project on the
workstation — the same repo you built up in Create an IOC Instance and the earlier
compose tutorials — not the generic-IOC devcontainer from Create a Generic IOC.
You will use the same bl01t worked example; substitute your own names
throughout.
Note
ibek pattern runs on your workstation and needs ibek ≥ 4.6.1. If
ibek is not installed, run uv tool install ibek --upgrade.
Fork the pattern library#
Patterns live in curated central libraries, so — exactly as with ibek-support
in Create a Generic IOC — you work from a fork and open a pull request later if
the pattern is generally useful.
Fork
ibek-runtime-support.Clone your fork next to your
t01-servicescheckout:
# run alongside your t01-services directory
git clone https://github.com/<your-org>/ibek-runtime-support
Author the pattern#
A pattern is just a top-level folder named after the pattern, holding one
or more files. The only required member is the *.ibek.support.yaml support
definition. Create the folder and file in your fork:
cd ibek-runtime-support
mkdir basicPlugins
code basicPlugins/basicPlugins.ibek.support.yaml
basicPlugins declares a single entity_model whose sub_entities wire up
just two AreaDetector plugins — a viewable Channel Access array
(NDStdArrays) and live statistics (NDStats). It mirrors the shape of the
real detectorPlugins model but trimmed to the essentials:
# yaml-language-server: $schema=https://github.com/epics-containers/ibek/releases/download/3.0.1/ibek.support.schema.json
module: basicPlugins
entity_models:
- name: basicPlugins
description: A minimal AreaDetector plugin set — a viewable array and stats
parameters:
P:
type: str
description: Prefix for the AreaDetector PVs
CAM:
type: str
description: AreaDetector NDArray port name of the camera
PORTPREFIX:
type: str
description: Prefix for all plugin ports
sub_entities:
- type: ADCore.NDStdArrays
P: "{{ P }}"
R: ":ARR:"
PORT: "{{ PORTPREFIX }}.arr"
NDARRAY_PORT: "{{ CAM }}"
NELEMENTS: 1048576
TYPE: Int8
FTVL: CHAR
- type: ADCore.NDStats
P: "{{ P }}"
R: ":STAT:"
PORT: "{{ PORTPREFIX }}.stat"
NDARRAY_PORT: "{{ CAM }}"
HIST_SIZE: 256
XSIZE: 1280
YSIZE: 1024
Only the three wiring parameters are exposed. Most of the plugins’ tuning knobs
(channel count, queue depth …) keep their ADCore defaults; the few that have
no default are set inline — NELEMENTS so the array is big enough to view,
and NDStats’s required HIST_SIZE / XSIZE / YSIZE (the histogram bin count
and the maximum image dimensions it computes statistics over). A production
pattern like the real detectorPlugins promotes more of these to parameters —
add them the same way when you need them.
The ADCore plugins it references are compiled into every AreaDetector image,
so this pattern needs no .db / .template of its own — it only adds the
ibek entities that instantiate and connect them at runtime.
Create a fresh instance to vendor into#
Make a new instance bl01t-ea-cam-02 exactly as you made bl01t-ea-cam-01 in
Create an IOC Instance: copy services/.ioc_template, set the name and image
in its compose.yml, and register it in the repo-root compose.yml. Use a
separate instance so its plugin ports do not clash with the detectorPlugins
already running in bl01t-ea-cam-01. Pin the SimDetector image:
image: ghcr.io/epics-containers/ioc-adsimdetector-runtime:2.11ec3
Give it a camera in services/bl01t-ea-cam-02/config/ioc.yaml:
# yaml-language-server: $schema=https://github.com/epics-containers/ioc-adsimdetector/releases/download/2.11ec3/ibek.ioc.schema.json
ioc_name: "{{ _global.get_env('IOC_NAME') }}"
description: An IOC that simulates an area detector
entities:
- type: epics.EpicsEnvSet
name: EPICS_TZ
value: GMT0BST
- type: devIocStats.iocAdminSoft
IOC: "{{ ioc_name | upper }}"
- type: ADSimDetector.simDetector
PORT: DET.DET
P: BL01T-EA-CAM-02
R: ":DET:"
Vendor your pattern#
Because you are still iterating, vendor straight from your local clone with
--source. A local-path source is copied as-is, so you do not have to commit,
push or tag the fork between edits:
cd t01-services
ibek pattern add --source ../ibek-runtime-support basicPlugins services/bl01t-ea-cam-02
This writes basicPlugins.ibek.support.yaml into
services/bl01t-ea-cam-02/config/ with a # Vendored from … — DO NOT EDIT
header and records its sha256 in services/bl01t-ea-cam-02/runtime-lock.yaml.
That is all the vendor step needs: at container start ibek runtime generate2 config discovers the vendored support file and loads your entity — no image
rebuild, and nothing else to wire up.
The lock now pins the pattern. A local source pins to HEAD:
basicPlugins:
version: HEAD
source: ../ibek-runtime-support
files:
basicPlugins.ibek.support.yaml: "sha256:…"
ibek pattern check services/bl01t-ea-cam-02 re-hashes the vendored file and
exits non-zero if it has drifted from the lock — run it in CI or a pre-commit
hook to guarantee the committed config/ matches what was pinned.
Use the new entity#
Add one basicPlugins.basicPlugins entity to the instance’s ioc.yaml, wired
to the camera’s Asyn port (CAM: DET.DET, the simDetector PORT above):
- type: basicPlugins.basicPlugins
P: BL01T-EA-CAM-02
CAM: DET.DET
PORTPREFIX: DET
This single entity expands — via its sub_entities — into both plugins.
Now set the compose project up to run it. First register the instance in the
repo-root compose.yml include: list:
include:
- services/bl01t-ea-cam-01/compose.yml
- services/bl01t-ea-cam-02/compose.yml
...
Then point Phoebus at the new instance. PVI auto-generates an index.bob for it
at opi/auto-generated/bl01t-ea-cam-02/index.bob; open it on launch by editing
the command: line of the phoebus service in services/phoebus/compose.yml:
command: phoebus-product/phoebus.sh -settings /config/settings.ini -resource /opi/auto-generated/bl01t-ea-cam-02/index.bob -server 7010
Bring the beamline up:
source ./environment.sh
docker compose up -d
At startup PVI generates an engineering screen per entity under
opi/auto-generated/bl01t-ea-cam-02/. Your new pattern adds two panels: an
NDStdArrays panel publishing the camera frames as a Channel Access waveform
(open it in Phoebus’s image widget after Acquire on the simDetector and
Enable on the plugin), and an NDStats panel showing live min/max/mean,
sigma and a histogram. A maintainer screenshot will be added here.
Promote the pattern#
Local-path vendoring is for iteration only. Once the pattern is settled, make it shareable:
Commit and push it to your fork, then cut a release tag (
vX.Y.Z) — a tag versions the whole library at once.Re-pin the instance from the published fork instead of the local path. Point the
ibek-runtime-supportlibrary name at your fork with theIBEK_PATTERN_LIBRARIESenvironment variable, thenaddthe qualified reference at your new tag:export IBEK_PATTERN_LIBRARIES="ibek-runtime-support=https://github.com/<your-org>/ibek-runtime-support" ibek pattern add ibek-runtime-support:basicPlugins@v0.1.0 services/bl01t-ea-cam-02
The lock now records
version: v0.1.0andsource: github.com/<your-org>/ibek-runtime-support— a fully reproducible pin. Once the pattern is merged upstream you can drop the override and pin the canonical library directly.If the pattern is generally useful, open a pull request against
ibek-runtime-supportso every beamline can vendor it.
Note
A pattern’s file-set is not fixed to a single file. The lock simply hashes
a file list, so a pattern may also ship .template / .db, autosave .req
files, or a .pvi.device.yaml screen descriptor alongside its support yaml —
whatever the support definition references. Add a Stream Device with ibek pattern vendors exactly
such a multi-file device-support pattern.