Support module YAML reference (*.ibek.support.yaml)#
A *.ibek.support.yaml file is authored by support-module developers. It
declares the set of Entity Models that a support module publishes — i.e. the
things an IOC instance is allowed to instantiate from that module (a motor
controller, an asyn port, a database, and so on). This is the developer-facing
half of ibek; the instance-facing half (the IOC’s ioc.yaml) is covered in
IOC instance YAML and schemas reference.
File header convention#
Every support file should begin with the language-server schema hint so editors can offer completion and validation:
# yaml-language-server: $schema=https://github.com/epics-containers/ibek/releases/download/<version>/ibek.support.schema.json
In the tested samples the hint points at a locally generated copy, e.g.
$schema=../schemas/ibek.support.schema.json. The global schema is produced by:
ibek support generate-schema
(See CLI reference for the full command surface.)
Model configuration that applies everywhere#
All models in this file are Pydantic models that inherit
globals.BaseSettings (src/ibek/globals.py:128-134), which sets:
extra="forbid"— unknown keys are rejected. A typo in a field name is an error, not a silently ignored value.use_enum_values=True— enum fields deserialize to their underlying value.
The one exception is SubEntity, which is declared with
extra="allow" (src/ibek/sub_entity.py:4) so it can carry arbitrary
pass-through parameters.
Top-level Support keys#
Source: src/ibek/support.py:17-30.
Key |
Type |
Required |
Default |
Meaning |
|---|---|---|---|---|
|
|
yes |
— |
Support module name, normally the repo name. |
|
|
yes |
— |
The Entity Models an IOC can create using this module. |
|
|
no |
|
Scratch space to hold YAML anchors for re-use; ibek does not read it. |
EntityModel keys#
Source: src/ibek/entity_model.py:124-174. Each entry under entity_models
describes one class of Entity, published to IOC instances as the type
<module>.<name>.
Key |
Type |
Required |
Default |
Meaning |
|---|---|---|---|---|
|
|
yes |
— |
Published as the Entity type |
|
|
yes |
— |
Human description of the Entity. |
|
|
no |
|
Calculated values rendered with Jinja before parameters. |
|
|
no |
|
The arguments an IOC instance supplies (discriminated by |
|
|
no |
|
Calculated values rendered with Jinja after parameters. |
|
|
no |
|
Startup-script snippets emitted before |
|
|
no |
|
Startup-script snippets emitted after |
|
|
no |
|
Database templates to load. |
|
|
no |
|
Environment variables to set in the boot script. |
|
|
no |
|
PVI device definition for this Entity. |
|
|
no |
|
Other Entities instantiated for each instance of this model. |
|
|
no |
|
YAML-anchor scratch space (as in |
Render order: pre_defines -> parameters -> post_defines#
These three groups are rendered in this strict order
(src/ibek/ioc.py:91-105). The practical consequences:
a
parameterdefault can refer to apre_define(the pre_define already has a value);a
post_definecan refer to aparameter(the parameter is already resolved);a
pre_definecannot read aparameterorpost_define, and aparametercannot read apost_define.
Deep Jinja semantics (the available context, filters, escaping) live in Jinja template context reference.
Parameter types#
parameters is a discriminated union keyed by type
(src/ibek/entity_model.py:118-121, classes in src/ibek/parameters.py:47-126).
Every parameter also takes a required description. The default field is
optional throughout; when omitted the IOC instance must supply the value.
|
|
Notes |
|---|---|---|
|
|
Plain string (may contain Jinja). |
|
|
A Jinja string is allowed so the default can be computed. |
|
|
As |
|
|
As |
|
|
YAML map, or a Jinja string rendering to one (e.g. |
|
|
YAML list, or a Jinja string rendering to one (e.g. |
|
|
A reference to another Entity by its id; resolved to the actual Entity during validation. |
|
|
Registers a unique global id for this Entity; other Entities’ |
|
any of the |
Requires a |
A JinjaString is just a str constrained to contain {{ ... }}
(src/ibek/parameters.py:14-16).
object and id semantics#
id— when the IOC sets this parameter, the value is added to a global id index. A duplicate id raisesValueError: Duplicate id ...(src/ibek/ioc.py:72-76). At most one parameter per model is the id field.object— the IOC supplies the id string of another Entity. During whole- model validation ibek looks that id up and replaces the string with the actual Entity instance (src/ibek/ioc.py:59-62), so Jinja can then reach into its fields (e.g.{{ controller.P }}).
enum values#
EnumParam requires a values mapping of label -> value
(src/ibek/parameters.py:101-109). The generated schema offers the labels to
the IOC author (src/ibek/entity_factory.py:139-143), while ibek substitutes
the corresponding value at render time
(IocFactory.fixup_enums, src/ibek/ioc_factory.py:57). Example:
DIR:
type: enum
description: The direction of the axis
default: 0
values:
Pos: 0
Neg: 1
Define model (values for pre_defines / post_defines)#
Source: src/ibek/parameters.py:35-44.
Key |
Type |
Required |
Default |
Meaning |
|---|---|---|---|---|
|
|
yes |
— |
What the calculated value is for. |
|
|
yes |
— |
The contents: a literal or a Jinja expression. |
|
|
no |
|
One of |
DefineType values are str, float, int, bool, list
(src/ibek/parameters.py:19-27).
Database#
Source: src/ibek/entity_model.py:34-53.
Key |
Type |
Required |
Default |
Meaning |
|---|---|---|---|---|
|
|
yes |
— |
Filename of the database template under |
|
|
no |
|
A Jinja string that renders to |
|
|
yes |
— |
Args passed through to the database. The key is required (it has no default), but you may pass |
In the sample, enabled: "{{not is_cs}}" selects one of two database templates
per axis, and the bare M: / ADDR: args take their values from the like-named
parameters.
EnvironmentVariable#
Source: src/ibek/entity_model.py:56-62.
Key |
Type |
Required |
Meaning |
|---|---|---|---|
|
|
yes |
Name of the environment variable (Jinja). |
|
|
yes |
Value to set (Jinja). |
Script (for pre_init / post_init)#
A Script is a sequence of Text or Comment snippets
(src/ibek/entity_model.py:65-88), discriminated by type.
Text (type: text, the default)#
Key |
Type |
Required |
Default |
Meaning |
|---|---|---|---|---|
|
|
no |
|
Discriminator. |
|
|
no |
|
Free string; |
|
|
no |
|
Raw text added to the startup script. |
EntityPVI#
Source: src/ibek/entity_model.py:91-115.
Key |
Type |
Required |
Default |
Meaning |
|---|---|---|---|---|
|
|
yes |
— |
Path to the |
|
|
no |
|
Whether to add the UI to the IOC index. |
|
|
no |
|
Macros to launch the UI; each key must be an arg of the owning Entity. |
|
|
no |
|
Whether to generate a PVI PV (a PVAccess PV describing the device structure). |
|
|
no |
|
PV prefix for the PVI PV, e.g. |
SubEntity#
Source: src/ibek/sub_entity.py. This is the only model with
extra="allow": beyond the two declared fields it accepts arbitrary keys, which
are passed through as parameters to the instantiated sub-Entity.
Key |
Type |
Required |
Default |
Meaning |
|---|---|---|---|---|
|
|
yes |
— |
The Entity type to instantiate ( |
|
|
no |
|
Enable or disable this sub-Entity instance. |
(any other key) |
— |
no |
— |
Pass-through parameter forwarded to the sub-Entity. |
Worked example#
The following file is part of the ibek test suite and exercises most of the
schema: id/object references, str/int/float/bool/enum parameters, enum
values, Jinja defaults, conditional databases, scripts and PVI. (It does not
demonstrate dict/list parameters, pre_defines/post_defines, env_vars,
sub_entities or Comment snippets.)
# yaml-language-server: $schema=../schemas/ibek.support.schema.json
# slightly extended version of motorMotorSim.ibek.support.yaml from ibek-support.
# the extra things are fictitious but are here to exercise all of the features
# of the ibek.support schema
module: motorSim
entity_models:
- name: simMotorController
description: |-
Creates a simulation motion controller
parameters:
controllerName:
type: id
description: |-
The name of the controller and its Asyn Port Name
P:
type: str
description: |-
Device PV Prefix
numAxes:
type: int
description: |-
The number of axes to create
port:
type: object
description: |-
a reference to the asyn port for communication with the controller
DESC:
type: str
description: |-
The description of the controller
default: "Simulated Motion Controller testing escaping: {% raw %} {{enclosed in escaped curly braces}} {% endraw %}"
pre_init:
- value: |
# motorSimCreateController(controller_asyn_port_name, axis_count)
# testing escaping: {% raw %} {{enclosed in escaped curly braces}} {% endraw %}
motorSimCreateController({{controllerName}}, {{numAxes}})
databases:
- file: sim_motor.db
args:
controllerName:
P:
DESC:
pvi:
yaml_path: simple.pvi.device.yaml
ui_macros:
P:
pv: true
pv_prefix: $(P)
- name: simMotorAxis
description: |-
Creates a simulation motor axis
parameters:
controller:
type: object
description: |-
a reference to the motion controller
M:
type: str
description: |-
PV suffix for the motor record
ADDR:
type: int
description: |-
The axis number (allowed to be from 0 to controller.numAxes-1)
DESC:
type: str
description: |-
The description of the axis
default: Motor {{ADDR}}
DLLM:
type: int
description: |-
The low limit of the axis (in counts)
default: -20000
DHLM:
type: int
description: |-
The high limit of the axis (in counts)
default: 20000
home:
type: int
description: |-
The home position of the axis (in counts)
default: 0
start:
type: str # int or jinja string
description: |-
The starting position of the axis (in counts)
default: "{{home}}"
DIR:
type: enum
description: |-
The direction of the axis
default: 0
values:
Pos: 0
Neg: 1
VELO:
type: float
description: |-
The velocity of the axis (in counts/sec)
default: 10.0
VMAX:
type: str # float or jinja string
description: |-
The maximum velocity of the axis (in counts/sec)
default: "{{VELO}}"
is_cs:
type: bool
description: |-
Set to True if this axis a coordinate system axis
default: false
CS_NUM:
type: int
description: |-
The coordinate system number for this axis
default: 0
post_init:
- when: once
value: |
# motorSimCreateAxis(controller_asyn_port_name, axis, axis_description)
- value: |
motorSimConfigAxis({{controller}}, {{ADDR}}, {{DHLM}}, {{DLLM}}, {{home}}, {{start}})
databases:
- file: basic_asyn_motor.db
enabled: "{{not is_cs}}"
args:
P: "{{controller.P}}"
M:
DTYP: asynMotor
PORT: "{{controller}}"
ADDR:
DESC:
EGU: degrees
DIR:
VELO:
VMAX:
MRES: ".01"
DHLM:
DLLM:
INIT: ""
- file: basic_cs_asyn_motor.db
enabled: "{{is_cs}}"
args:
P: "{{controller.P}}"
CS_NUM:
DTYP: asynMotor
PORT: "{{controller}}"
ADDR:
DESC:
EGU: degrees
DIR:
VELO:
VMAX:
MRES: ".01"
DHLM:
DLLM:
INIT: ""
Annotated skeleton#
# yaml-language-server: $schema=.../ibek.support.schema.json
module: myModule # REQUIRED: support module / repo name
entity_models: # REQUIRED: one or more Entity Models
- name: myThing # REQUIRED: published as myModule.myThing
description: What it makes # REQUIRED
pre_defines: # rendered first
base: { description: prefix, value: "{{ P }}", type: str }
parameters: # what the IOC supplies
name: { type: id, description: unique name }
P: { type: str, description: PV prefix }
port: { type: object, description: ref to another entity by id }
mode: { type: enum, description: mode, default: a, values: { a: 0, b: 1 } }
post_defines: # rendered last; may read parameters
full: { description: full pv, value: "{{ P }}:{{ name }}" }
pre_init:
- value: "someIocShellFunction({{ name }})" # Text, when: every
databases:
- file: my.db
enabled: "True"
args:
P: # None => pass through the same-named arg
env_vars:
- { name: MY_VAR, value: "{{ P }}" }
pvi:
yaml_path: my.pvi.device.yaml
sub_entities:
- type: myModule.other # extra keys below are pass-through params
someParam: "{{ P }}"
Comment(type: comment)#Key
Type
Required
Default
Meaning
type"comment"no
—
Discriminator (required to select this variant).
whenWhenno
everyThe
Whenenum:first/every/last.valuestrno
""Comment text; every line is prefixed with
#.Warning
Two foot-guns in the script model:
when: lastis not implemented. It raisesNotImplementedError("When.last not yet implemented")at render time (src/ibek/render.py:45-46). Onlyfirstandeverywork.Text.whenis a freestrwhileComment.whenis theWhenenum. This inconsistency meansTextwill silently accept an invalidwhenvalue whereCommentwould reject it. Stick tofirst/every.