Modules, Definitions and Entities#
This page is a top down explanation of the above key terms by referencing the example IOC instance bl45p-mo-ioc-02 used in the system tests for this project.
The explanations rely on an understanding of the difference between generic IOCs and IOC instances. See Generic IOCs and instances.
Modules#
Every generic IOC image will include a number of EPICS support modules.
The bl45p-mo-ioc-02 ioc instance example uses the generic IOC container image ioc-pmac . This image contains the support modules pmac and motor plus the common support modules from epics-modules
Each IOC instance will mount a generic IOC image and therefore be able to make use of the libraries and DB templates in any of those support modules.
The goal of ibek is to allow a developer to define an instance of an IOC and make use of functions of its support modules.
ibek uses a support module definition file to determine what features of a support module may be instantiated.
Hence there is a collection of support module definition files inside of each generic IOC.
In the code the class GenericIoc is used to represent a generic IOC and an instance of that class represent an IOC instance.
Definition#
Each support module has its own support module definition file . This
is a YAML file whose name is by convention <support_module>.ibek.support.yaml
These will all reside in a folder called /ibek in the container image.
The support module definition file contains Definitions which determine what Entities an IOC instance may instantiate.
For example the pmac support module declares the following Definitions
in pmac.ibek.support.yaml
(currently this is limited to 3 - the full implementation would have more):
Geobrick
DlsPmacAsynIPPort
DlsPmacAsynMotor
Each Definition describes a class of Entity by providing:
Entity class name
a list of arguments to supply when declaring an Entity
boot script entries to add for the Entity in the form of a jinja template that may refer to the above arguments
database templates to instantiate for the Entity with macro values from the above arguments
Expand below for the example support module definition file:
objects.ibek.support.yaml
# yaml-language-server: $schema=../schemas/ibek.support.schema.json ################################################################################ ### ibek support YAML to demonstrate the use of object references ################################################################################ module: object_module defs: - name: RefObject description: | Example object for reference from Consumer args: - type: id name: name description: Port name - type: str name: IP description: IP address of port default: 127.0.0.1 values: - name: test_value value: "{{ name }}.{{ IP }}" description: test jinja render of arg values env_vars: - name: REF_OBJECT_NAME value: "{{ name }}" pre_init: - when: first value: | # This line should appear once only in the pre_init section - value: | # TestValues testValue TestValues {{ test_value }} pvi: yaml_path: simple.pvi.device.yaml prefix: "{{ name }}" - name: Consumer description: | A class that uses RefObject args: - type: id name: name description: Consumer name - type: object name: PORT description: a reference to an RefObject - name: ConsumerTwo description: | Another class that uses RefObject args: - type: id name: name description: Consumer name - type: object name: PORT description: a reference to an RefObject pre_init: - value: | # ExampleTestFunction asynPortIP name port value ExampleTestFunction {{ PORT.IP }} {{ name }} {{ PORT }} {{ PORT.test_value }} # PORT defaults to the id of PORT, i.e. PORT.name databases: - file: test.db args: name: ip: "{{ PORT.IP }}" value: "{{ PORT.test_value }}"
Definition is implemented in the code using a class of the same name.
Entity#
ibek can generate an IOC instance using an
IOC instance entity file. This is
a YAML file with name of the form <ioc_name>.<container>.yaml
.
The IOC instance entity file declares the Entities that the IOC instance requires.
An Entity represents any piece of functionality of an IOC that is configured through EPICS database and/or startup script.
The classes of Entity that can be instantiated for a given generic IOC are declared in the Definitions files described above.
Declaring an Entity for an IOC instance will cause ibek to generate lines in the startup script. The generated startup script will also supply the EPICS database entries using dbLoadRecords and database templates.
The example motion IOC instance bl45p-mo-ioc-02 has the following entities:
DlsPmacAsynIPPort (one instance)
represents a connection to a motion controller
configured via
pmacAsynIPConfigure in the boot script
Geobrick (one instance)
represents the motion controller itself
configured via
pmacCreateController in boot script
pmacCreateAxes in the boot script
dbLoadRecords of pmacController.template and pmacStatus.template
DlsPmacAsynMotor (two instances)
represents a single motor connected to the controller
configured via:
dbLoadRecords of dls_pmac_asyn_motor.template
The example IOC instance entity file is shown below along with the st.cmd file that ibek will generate from it.
Click the arrows to reveal the files.
all.ibek.ioc.yaml
# yaml-language-server: $schema=../schemas/multiple.ibek.ioc.schema.json ioc_name: "{{ ioc_yaml_file_name }}" description: a basic example for testing multiple support definitions entities: - type: object_module.RefObject name: Ref1 - type: module.AllObject name: ioc_name={{ ioc_name }} my_object: Ref1 my_int_enum: full_speed my_inferred_enum: third clock_rate: dummy my_str: AllObject One String - type: module.AllObject name: AllObject Two my_object: Ref1 my_int_enum: all_ahead my_inferred_enum: first clock_rate: 2Hz my_str: AllObject Two String - type: object_module.ConsumerTwo name: Consumer Two With DB PORT: Ref1 - type: object_module.ConsumerTwo name: Disabled Consumer PORT: Ref1 entity_enabled: false - type: object_module.RefObject name: Disabled RefObject entity_enabled: falsest.cmd
# EPICS IOC Startup Script generated by https://github.com/epics-containers/ibek cd "/epics/ioc" epicsEnvSet REF_OBJECT_NAME Ref1 dbLoadDatabase dbd/ioc.dbd ioc_registerRecordDeviceDriver pdbbase # This line should appear once only in the pre_init section # TestValues testValue TestValues Ref1.127.0.0.1 # this is a comment # that spans multiple lines # testPreInit "ioc_name={{ ioc_name }}" "'AllObject One String'" my_str = AllObject One String my_inferred_enum = third clock_rate = dummy my_mixed_enum_no_default = . # this is a comment # that spans multiple lines # testPreInit "AllObject Two" "'AllObject Two String'" my_str = AllObject Two String my_inferred_enum = hello clock_rate = 1 my_mixed_enum_no_default = . # ExampleTestFunction asynPortIP name port value ExampleTestFunction 127.0.0.1 Consumer Two With DB Ref1 Ref1.127.0.0.1 # PORT defaults to the id of PORT, i.e. PORT.name dbLoadRecords $(RUNTIME_DIR)/ioc.db iocInit testPostInit "ioc_name={{ ioc_name }}" test_value: this should appear once only in the post_init section # post init comment testPostInit "AllObject Two" test_value: # post init comment
Entity is implemented in the code using a class of the same name.
Schemas#
The YAML files described above are constrained by schemas. These schemas are available to the developer and may be used to assist in generating the YAML.
Note that the author of an IOC instance needs access to <container>.schema.json in order to correctly craft a correct <ioc>.<container>.yaml. For this reason the container schema file is published as a github artifact along with the release of the container image. All other ibek operations happen within the container or during container development.
Thus, the sequence of files is as follows:
num |
Name |
Description |
---|---|---|
1 |
ibek.defs.schema.json |
Global Schema for 2 |
2 |
<support>.ibek.support.yaml |
Definition file for a support module. Generates part of 3 |
3 |
<container>.entities.schema.json |
Schema for 4. Generated by combining all of 2 from a container |
4 |
<ioc>.ibek.ioc.yaml |
Description of Entities for an IOC instance. |
5 |
IOC Startup Script st.cmd |
Startup script for booting the IOC |
The Global Schema and example IOC instance schema are below:
ibek.support.schema.json
{ "$defs": { "BoolArg": { "additionalProperties": false, "description": "An argument with an bool value", "properties": { "type": { "const": "bool", "default": "bool", "title": "Type" }, "name": { "description": "Name of the argument that the IOC instance should pass", "title": "Name", "type": "string" }, "description": { "description": "Description of what the argument will be used for", "title": "Description", "type": "string" }, "default": { "anyOf": [ { "type": "boolean" }, { "type": "null" } ], "default": null, "title": "Default" } }, "required": [ "name", "description" ], "title": "BoolArg", "type": "object" }, "Comment": { "additionalProperties": false, "description": "A script snippet that will have '# ' prepended to every line\nfor insertion into the startup script", "properties": { "type": { "const": "comment", "default": "comment", "title": "Type" }, "when": { "allOf": [ { "$ref": "#/$defs/When" } ], "default": "every", "description": "One of first / every / last" }, "value": { "default": "", "description": "A comment to add into the startup script", "title": "Value", "type": "string" } }, "title": "Comment", "type": "object" }, "Database": { "additionalProperties": false, "description": "A database file that should be loaded by the startup script and its args", "properties": { "file": { "description": "Filename of the database template in <module_root>/db", "title": "File", "type": "string" }, "args": { "additionalProperties": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "description": "Dictionary of args and values to pass through to database. A value of None is equivalent to ARG: '{{ ARG }}'", "title": "Args", "type": "object" } }, "required": [ "file", "args" ], "title": "Database", "type": "object" }, "Definition": { "additionalProperties": false, "description": "A single definition of a class of Entity that an IOC instance may instantiate", "properties": { "name": { "description": "Publish Definition as type <module>.<name> for IOC instances", "title": "Name", "type": "string" }, "description": { "description": "A description of the Support module defined here", "title": "Description", "type": "string" }, "args": { "default": [], "description": "The arguments IOC instance should supply", "items": { "anyOf": [ { "$ref": "#/$defs/FloatArg" }, { "$ref": "#/$defs/StrArg" }, { "$ref": "#/$defs/IntArg" }, { "$ref": "#/$defs/BoolArg" }, { "$ref": "#/$defs/ObjectArg" }, { "$ref": "#/$defs/IdArg" }, { "$ref": "#/$defs/EnumArg" } ] }, "title": "Args", "type": "array" }, "values": { "default": [], "description": "The values IOC instance should supply", "items": { "$ref": "#/$defs/Value" }, "title": "Values", "type": "array" }, "databases": { "default": [], "description": "Databases to instantiate", "items": { "$ref": "#/$defs/Database" }, "title": "Databases", "type": "array" }, "pre_init": { "default": [], "description": "Startup script snippets to add before iocInit()", "items": { "anyOf": [ { "$ref": "#/$defs/Text" }, { "$ref": "#/$defs/Comment" } ] }, "title": "Pre Init", "type": "array" }, "post_init": { "default": [], "description": "Startup script snippets to add post iocInit(), such as dbpf", "items": { "anyOf": [ { "$ref": "#/$defs/Text" }, { "$ref": "#/$defs/Comment" } ] }, "title": "Post Init", "type": "array" }, "env_vars": { "default": [], "description": "Environment variables to set in the boot script", "items": { "$ref": "#/$defs/EnvironmentVariable" }, "title": "Env Vars", "type": "array" }, "pvi": { "allOf": [ { "$ref": "#/$defs/EntityPVI" } ], "default": null, "description": "PVI definition for Entity" } }, "required": [ "name", "description" ], "title": "Definition", "type": "object" }, "EntityPVI": { "additionalProperties": false, "description": "Entity PVI definition", "properties": { "yaml_path": { "description": "Path to .pvi.device.yaml - absolute or relative to PVI_DEFS", "title": "Yaml Path", "type": "string" }, "index": { "default": true, "description": "Whether to add generated UI to index for Entity", "title": "Index", "type": "boolean" }, "prefix": { "description": "PV prefix to pass as $(prefix) on index button", "title": "Prefix", "type": "string" }, "pva_template": { "default": false, "description": "Whether to generate a database template with info tags that create a PVAccess structure defining the PV interface (PVI) for this entity", "title": "Pva Template", "type": "boolean" } }, "required": [ "yaml_path", "prefix" ], "title": "EntityPVI", "type": "object" }, "EnumArg": { "additionalProperties": false, "description": "An argument with an enum value", "properties": { "type": { "const": "enum", "default": "enum", "title": "Type" }, "name": { "description": "Name of the argument that the IOC instance should pass", "title": "Name", "type": "string" }, "description": { "description": "Description of what the argument will be used for", "title": "Description", "type": "string" }, "default": { "anyOf": [ {}, { "type": "null" } ], "default": null, "title": "Default" }, "values": { "description": "provides a list of values to make this argument an Enum", "title": "Values", "type": "object" } }, "required": [ "name", "description", "values" ], "title": "EnumArg", "type": "object" }, "EnvironmentVariable": { "additionalProperties": false, "description": "An environment variable that should be set in the startup script", "properties": { "name": { "description": "Name of environment variable", "title": "Name", "type": "string" }, "value": { "description": "Value to set", "title": "Value", "type": "string" } }, "required": [ "name", "value" ], "title": "EnvironmentVariable", "type": "object" }, "FloatArg": { "additionalProperties": false, "description": "An argument with a float value", "properties": { "type": { "const": "float", "default": "float", "title": "Type" }, "name": { "description": "Name of the argument that the IOC instance should pass", "title": "Name", "type": "string" }, "description": { "description": "Description of what the argument will be used for", "title": "Description", "type": "string" }, "default": { "anyOf": [ { "type": "number" }, { "type": "null" } ], "default": null, "title": "Default" } }, "required": [ "name", "description" ], "title": "FloatArg", "type": "object" }, "IdArg": { "additionalProperties": false, "description": "Explicit ID argument that an object can refer to", "properties": { "type": { "const": "id", "default": "id", "title": "Type" }, "name": { "description": "Name of the argument that the IOC instance should pass", "title": "Name", "type": "string" }, "description": { "description": "Description of what the argument will be used for", "title": "Description", "type": "string" }, "default": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Default" } }, "required": [ "name", "description" ], "title": "IdArg", "type": "object" }, "IntArg": { "additionalProperties": false, "description": "An argument with an int value", "properties": { "type": { "const": "int", "default": "int", "title": "Type" }, "name": { "description": "Name of the argument that the IOC instance should pass", "title": "Name", "type": "string" }, "description": { "description": "Description of what the argument will be used for", "title": "Description", "type": "string" }, "default": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "default": null, "title": "Default" } }, "required": [ "name", "description" ], "title": "IntArg", "type": "object" }, "ObjectArg": { "additionalProperties": false, "description": "A reference to another entity defined in this IOC", "properties": { "type": { "const": "object", "default": "object", "title": "Type" }, "name": { "description": "Name of the argument that the IOC instance should pass", "title": "Name", "type": "string" }, "description": { "description": "Description of what the argument will be used for", "title": "Description", "type": "string" }, "default": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Default" } }, "required": [ "name", "description" ], "title": "ObjectArg", "type": "object" }, "StrArg": { "additionalProperties": false, "description": "An argument with a str value", "properties": { "type": { "const": "str", "default": "str", "title": "Type" }, "name": { "description": "Name of the argument that the IOC instance should pass", "title": "Name", "type": "string" }, "description": { "description": "Description of what the argument will be used for", "title": "Description", "type": "string" }, "default": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Default" } }, "required": [ "name", "description" ], "title": "StrArg", "type": "object" }, "Text": { "additionalProperties": false, "description": "A script snippet to insert into the startup script", "properties": { "type": { "const": "text", "default": "text", "title": "Type" }, "when": { "default": "every", "description": "One of first / every / last", "title": "When", "type": "string" }, "value": { "default": "", "description": "raw text to add to the startup script", "title": "Value", "type": "string" } }, "title": "Text", "type": "object" }, "Value": { "additionalProperties": false, "description": "A calculated string value for a definition", "properties": { "name": { "description": "Name of the value that the IOC instance will expose", "title": "Name", "type": "string" }, "description": { "description": "Description of what the value will be used for", "title": "Description", "type": "string" }, "value": { "description": "The contents of the value", "title": "Value", "type": "string" } }, "required": [ "name", "description", "value" ], "title": "Value", "type": "object" }, "When": { "enum": [ "first", "every", "last" ], "title": "When", "type": "string" } }, "additionalProperties": false, "description": "Lists the definitions for a support module, this defines what Entities it supports\n\nProvides the deserialize entry point.", "properties": { "module": { "description": "Support module name, normally the repo name", "title": "Module", "type": "string" }, "defs": { "description": "The definitions an IOC can create using this module", "items": { "$ref": "#/$defs/Definition" }, "title": "Defs", "type": "array" } }, "required": [ "module", "defs" ], "title": "Support", "type": "object" }multiple.ibek.ioc.schema.json
{ "$defs": { "clock_rate": { "enum": [ "1Hz", "2Hz", "5Hz", "10Hz", "dummy" ], "title": "clock_rate", "type": "string" }, "module_AllObject": { "additionalProperties": false, "properties": { "type": { "const": "module.AllObject", "default": "module.AllObject", "description": "The type of this entity", "title": "Type" }, "entity_enabled": { "default": true, "description": "enable or disable this entity instance", "title": "Entity Enabled", "type": "boolean" }, "name": { "description": "identifier", "title": "Name", "type": "string" }, "my_int_enum": { "allOf": [ { "$ref": "#/$defs/my_int_enum" } ], "default": 2, "description": "integer enumerated type" }, "my_inferred_enum": { "allOf": [ { "$ref": "#/$defs/my_inferred_enum" } ], "description": "integer enumerated type with inferred value" }, "clock_rate": { "allOf": [ { "$ref": "#/$defs/clock_rate" } ], "description": "demonstrates use of VME clock rates as an enum\nthis is an example of an 'illegal' python enum\n" }, "my_str": { "description": "string arg", "title": "My Str", "type": "string" }, "my_int": { "default": 1, "description": "integer arg", "title": "My Int", "type": "integer" }, "my_object": { "description": "object arg", "title": "My Object" }, "my_float": { "default": 1.0, "description": "float arg", "title": "My Float", "type": "number" }, "my_bool": { "default": true, "description": "bool arg", "title": "My Bool", "type": "boolean" }, "calculated_one": { "default": "{{ name }}.{{ my_str }}.{{ my_int }}.{{ my_float }}.{{ my_bool }}", "description": "test jinja render of arg values", "title": "Calculated One", "type": "string" } }, "required": [ "name", "my_inferred_enum", "clock_rate", "my_str", "my_object" ], "title": "module_AllObject", "type": "object" }, "my_inferred_enum": { "enum": [ "first", "second", "third" ], "title": "my_inferred_enum", "type": "string" }, "my_int_enum": { "enum": [ "all_ahead", "full_speed", "ramming_speed", "stop" ], "title": "my_int_enum", "type": "string" }, "object_module_Consumer": { "additionalProperties": false, "properties": { "type": { "const": "object_module.Consumer", "default": "object_module.Consumer", "description": "The type of this entity", "title": "Type" }, "entity_enabled": { "default": true, "description": "enable or disable this entity instance", "title": "Entity Enabled", "type": "boolean" }, "name": { "description": "Consumer name", "title": "Name", "type": "string" }, "PORT": { "description": "a reference to an RefObject", "title": "Port" } }, "required": [ "name", "PORT" ], "title": "object_module_Consumer", "type": "object" }, "object_module_ConsumerTwo": { "additionalProperties": false, "properties": { "type": { "const": "object_module.ConsumerTwo", "default": "object_module.ConsumerTwo", "description": "The type of this entity", "title": "Type" }, "entity_enabled": { "default": true, "description": "enable or disable this entity instance", "title": "Entity Enabled", "type": "boolean" }, "name": { "description": "Consumer name", "title": "Name", "type": "string" }, "PORT": { "description": "a reference to an RefObject", "title": "Port" } }, "required": [ "name", "PORT" ], "title": "object_module_ConsumerTwo", "type": "object" }, "object_module_RefObject": { "additionalProperties": false, "properties": { "type": { "const": "object_module.RefObject", "default": "object_module.RefObject", "description": "The type of this entity", "title": "Type" }, "entity_enabled": { "default": true, "description": "enable or disable this entity instance", "title": "Entity Enabled", "type": "boolean" }, "name": { "description": "Port name", "title": "Name", "type": "string" }, "IP": { "default": "127.0.0.1", "description": "IP address of port", "title": "Ip", "type": "string" }, "test_value": { "default": "{{ name }}.{{ IP }}", "description": "test jinja render of arg values", "title": "Test Value", "type": "string" } }, "required": [ "name" ], "title": "object_module_RefObject", "type": "object" } }, "additionalProperties": false, "properties": { "ioc_name": { "description": "Name of IOC instance", "title": "Ioc Name", "type": "string" }, "description": { "description": "Description of what the IOC does", "title": "Description", "type": "string" }, "entities": { "description": "List of entities this IOC instantiates", "items": { "discriminator": { "mapping": { "module.AllObject": "#/$defs/module_AllObject", "object_module.Consumer": "#/$defs/object_module_Consumer", "object_module.ConsumerTwo": "#/$defs/object_module_ConsumerTwo", "object_module.RefObject": "#/$defs/object_module_RefObject" }, "propertyName": "type" }, "oneOf": [ { "$ref": "#/$defs/object_module_RefObject" }, { "$ref": "#/$defs/object_module_Consumer" }, { "$ref": "#/$defs/object_module_ConsumerTwo" }, { "$ref": "#/$defs/module_AllObject" } ] }, "title": "Entities", "type": "array" } }, "required": [ "ioc_name", "description", "entities" ], "title": "NewIOC", "type": "object" }
This results in the overall generated file structure:
Commands#
The ibek commands to progress through the file sequence above are as follows
num |
Name |
Command |
---|---|---|
1 |
|
|
2 |
|
Hand crafted by the container developer. Held in the container. |
3 |
|
|
4 |
|
Hand crafted at IOC instance design time |
5 |
IOC startup script |
|