Controller base classes¶
This section describes the base classes designed for the implementation of controllers in BLISS.
Info
“If you read this lines for the first time, please have a look to the General introduction first”
ConfigItemContainer
base class¶
This class is designed for controllers with subitems and works with the generic plugin.
-
A subitem is an object created and managed by its controller (aka top-controller).
-
A subitem can be declared with a name inside the YAML configuration of its top-controller.
YAML configuration of a controller with named subitems
- class: FooController # object class (inheriting from ConfigItemContainer)
plugin: generic # ConfigItemContainer works with generic plugin
module: foo_module # module where the FooController class can be found
name: foo # a name for the controller (optional)
axes: # a section to declare subitems of type axis
- name: ax_hv # name of a subitem
tag: hv # something to identify the role of this subitem
- name: ax_cur # name of another subitem
tag: cur # something to identify the role of this subitem
baritems: # another section to declare another kind of subitems
- name: bar1
channel: A
- name: bar2
channel: B
Important note about the configuration key name
In BLISS configuration files, the key name:
means that the corresponding object can be imported in a session
(via the session configuration file or via the config.get('obj_name')
command).
Remember that the name can be chosen by users and must be unique among all configuration files hosted by one
Beacon server.
About the “subitem configuration” and “parent key”
The subitem configuration is the part of the YAML configuration concerning a subitem.
It corresponds to all keys found below the name of a subitem.
In the example above, the configuration of subitem ax_hv
is {'name':'ax_hv', 'tag':'hv'}
.
The parent_key
is the key under which the subitem configuration has been found.
In the example above, the parent key of ax_hv
is axes
.
Importing subitems¶
A subitem with a name in its controller YAML configuration, can be imported in a BLISS session via the
session YAML configuration file
or from a running session with the command config.get(<subitem_name>)
.
In that case, the generic
plugin will ensure that the top-controller is automatically created first and only once.
Then, the plugin obtains the subitem from the controller.
Off course, the controller itself can be directly imported in a session using its name.
About controller name
In some cases, it is foreseen to only expose subitems to users and keep the top-controller hidden. In that case, the controller name can be omitted in the configuration file. Still, the generic plugin will generate a unique default controller name for its registration inside BLISS internals.
Class signature¶
The signature of a ConfigItemContainer
takes a single argument config
.
It could be a ConfigNode
object (from YAML configuration file) or a standard dictionary.
Signature
class ConfigItemContainer:
def __init__(self, config):
Inherit from
from bliss.config.plugins.generic import ConfigItemContainer
class FooController(ConfigItemContainer):
def __init__(self, config):
super().__init__(config)
Class properties¶
This class implements two properties, one to retrieve the configuration object (config
) and
another one returning the name of the controller (name
). The name is found in the configuration, else
a generic name is built automatically.
Available properties
class ConfigItemContainer:
def __init__(self, config):
@property
def config(self):
"""Return the YAML configuration as a dict-like object"""
@property
def name(self):
"""Return the object/controller name"""
Instantiation by plugin¶
While importing the top-controller or one of its subitems in a BLISS session, the generic
plugin
will take care of the top-controller instantiation.
During this phase, the generic
plugin will call the following methods, in that order:
Initialization methods
def _load_config(self):
"""
Place holder to read and apply the configuration
"""
pass
def _init(self):
"""
Place holder for any action to perform after
the configuration has been loaded.
"""
pass
Instantiation by hand¶
A ConfigItemContainer
can be instantiated by hand providing a configuration as a dictionary.
In that case, users must call the method self._initialize_config()
just after the controller
instantiation to ensure that the controller is initialized in the same way as the plugin does.
The config dictionary should be structured like a YAML file (i.e. nested dictionaries and list) and objects references replaced by their corresponding object instances.
Instantiate and initialize by hand
config = {'name':'foo', 'fooparam':69, 'fooitems':[{'name':'subitem1',},]}
foo = ConfigItemContainer(config)
foo._initialize_config()
Subitem creation methods¶
When the top-controller needs to instantiate a subitem, it will call the following methods that developers must override:
Subitem default class name (to be overridden)
def _get_subitem_default_class_name(self, cfg, parent_key):
"""
Return the appropriate subitem default class name.
Arguments:
cfg: subitem configuration
parent_key: the key under which subitem configuration has been found
Return: class name (string)
"""
# === Example ===
# if parent_key == 'axes':
# return 'Axis'
raise NotImplementedError
Called when the class
key cannot be found in the subitem configuration.
Then a default class name must be returned. The choice of the subitem class is usually made
from the parent_key
value. Elements of the subitem configuration (cfg
) may also by used to make the
choice of the subitem class.
Returning "__pass__"
will tell the generic plugin to ignore the class import validation step
Subitem default class module (to be overridden)
def _get_subitem_default_module(self, class_name, cfg, parent_key):
"""
Return the path of the default module where class_name should be found.
Arguments:
class_name: subitem class name
cfg: subitem configuration
parent_key: the key under which subitem configuration has been found
Return: module path (string)
"""
# === Example ===
# if parent_key == 'axes':
# return 'bliss.common.axis'
raise NotImplementedError
Called when the given class_name
cannot be found at the controller module level.
Then a default module path must be returned. The choice of the subitem module is usually made from the parent_key
value.
Elements of the subitem configuration (cfg
) may also by used to make the choice of the subitem module.
If the default class can be found in the controller module, this method can remains not implemented
Subitem creation (to be overridden)
def _create_subitem_from_config(self, name, cfg, parent_key, item_class, item_obj=None):
"""
Create and return a subitem.
Arguments:
name: subitem name
cfg: subitem configuration
parent_key: the key under which subitem configuration has been found
item_class: class of the subitem
item_obj: subitem instance if passed as a reference, else None
Return: subitem instance
"""
# === Example ===
# if parent_key == "axes":
# if item_class is None: # subitem is a reference in config
# axis = item_obj # item_obj contains referenced object
# else:
# axis = item_class(name, self, cfg) # instantiate subitem
#
# return axis # return the subitem instance
raise NotImplementedError
Called when a subitem must be created.
The item_class
comes either from the class
key found in the subitem configuration or from
the default class retrieve by the _get_subitem_default_class_name
and _get_subitem_default_module
methods described above.
subitem passed as a configuration reference
If item_class
is None
it means that the subitem was given as a reference.
In that case the subitem object is already instantiated and is contained in item_obj
.
Overriding default subitem class¶
The class
key can be directly specified in the YAML configuration of a subitem. In that case this class will be used
instead of the default one specified by _get_subitem_default_class_name
.
Overriding subitem class from YAML configuration
- class: FooController
plugin: generic
module: foo_module
name: foo
baritems:
- name: bar1 # this one will use its default class
channel: A
- name: bar2
channel: B
class: MyBarClass # override default class
- name: bar3
channel: C
class: idxx.custom.IdxxBarClass # override default class and default module
The class
key value can be:
-
a full path containing the module path and the class name
example:
idxx.custom.IdxxBarClass
-
a class name that will be imported using the same rules as the default class
example:
MyBarClass
BlissController
base class¶
This class is designed for controllers with named counters. It inherits from the ConfigItemContainer class and from the CounterContainer protocol for compatibility with scans and counters.
Example of the YAML configuration of a BlissController
- class: FooController # object class (inheriting from BlissController)
plugin: generic # BlissController works with generic plugin
module: foo_module # module where the FooController class can be found
name: foo # a name for the controller (optional)
counters: # a section to declare counters
- name: cnt_hv # name for a counter
tag: hv # a tag to identify this counter within the controller
- name: cnt_cur # a name for another counter
tag: cur # a tag to identify this counter within the controller
The tag
key could be replaced by any other key, it only has a meaning in the controller context
Class signature¶
The signature of a BlissController
takes a single argument config
.
It could be a ConfigNode
object (from YAML configuration file) or a standard dictionary.
Signature
class BlissController(CounterContainer, ConfigItemContainer):
def __init__(self, config):
Inherit from
from bliss.controllers.bliss_controller import BlissController
class FooController(BlissController):
def __init__(self, config):
super().__init__(config)
Class methods¶
counters property (to be overridden)
@property
def counters(self):
raise NotImplementedError
default info method (can be overridden)
def __info__(self):
"""Return controller info as a string"""
info_str = f"Controller: {self.name} ({self.__class__.__name__})\n"
return info_str
see also properties and methods inherited from ConfigItemContainer
Default chain customization¶
The DEFAULT_CHAIN
can be customized with DEFAULT_CHAIN.set_settings
(see Default chain).
The devices introduced in the chain must be of the type Counter
, CounterController
or BlissController
.
While introducing a BlissController
in the default chain, the _get_default_chain_counter_controller
method
is called to obtain the CounterController
object that should be used.
default counter controller for the default chain (to be overridden)
def _get_default_chain_counter_controller(self):
"""Return the default counter controller that should be used
when this controller is used to customize the DEFAULT_CHAIN
"""
raise NotImplementedError