Skip to content

Controller base classes

This section describes the base classes designed for the implementation of controllers in BLISS.

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