Input Modules

In general, there are three possible types of input modules:

  • Input module: The base module for some input variables and input tags. For example an OPC UA client.
  • Input variable: A module generating data objects, if the configured variable changes. For example a subscription to an OPC UA variable.
  • Input tag: A module providing data of the configured variable, if it is requested. For example reading specific OPC UA variable.

Each of these types is represented as a separate building block during configuration, although they are all described in a single Python file. They are all optional. However, the existence of an input module is only necessary if an input variable or input tag module exists.


Input Module

Input modules are mainly used to establish connections for variable and tag modules. This allows you to reuse the same connection for multiple variable and tag module instances.

Example
class InputModule(AbstractInputModule):
    ...

    @dataclass
    class Configuration(models.InputModule):
        anonymous: bool = field(
            metadata=dict(description="Is authentication required.",
                          required=False),
            default=True)
        username: str = field(
            metadata=dict(description="The username for the basic authentication.",
                          required=False),
            default='admin')
        password: str = field(
            metadata=dict(description="The password for the basic authentication.",
                          required=False),
            default='admin')

    def __init__(self, configuration: Configuration):
        super().__init__(configuration=configuration)
        self.session = None

    def start(self):
        self.session = requests.Session()
        if not self.configuration.anonymous:
            self.session.auth = (self.configuration.username,
                                 self.configuration.password)

    def stop(self):
        self.session.close()

When an input module is configured, the module instance is passed to variable and tag modules as input_module_instance. There the input module instance can be used. In order to pass to correct input module, the variable and tag modules need the configuration parameter input_module.


Input Variable

An input variable is a module that most often executes cyclic logic to initially generate or request data. There are also event-driven subscriptions that receive data objects from third-party systems. Variable modules therefore have no input port - only an output port for forwarding data.

Example
from modules.base.inputs.base import AbstractInputModule, AbstractVariableModule, AbstractTagModule, models

class VariableModule(AbstractVariableModule):
    ...

    @dataclass
    class Configuration(models.VariableModule):
        input_module: str = field(
            metadata=dict(description="The id of the input module.",
                          required=True),
            default=None)
        url: str = field(
            metadata=dict(description="The url of the server.",
                          required=False,
                          dynamic=True),
            default="http://127.0.0.1:80/api/v1/test")
        interval: float = field(
            metadata=dict(description="Interval in seconds to poll the endpoint.",
                          required=False,
                          validate=Range(min=0, max=1000)),
            default=10)

    def __init__(self, configuration: Configuration, input_module_instance=None):
        super().__init__(configuration=configuration,
                         input_module_instance=input_module_instance)

    def start(self):
        Thread(target=self._subscribe,
               daemon=False,
               name="REST_Get_Variable_Module_Loop_{0}".format(self.configuration.id)).start()

    def _subscribe(self):
        """
        This method creates a new loop requesting the endpoint in the given interval.
        """
        while self.active and self.input_module_instance.session:
            try:
                response = self.input_module_instance.session.get(url=self._dyn(url))
                response.raise_for_status()
                fields = response.json()

                data = models.Data(measurement=self.configuration.measurement, fields=fields)
                self._call_links(data)

                time.sleep(self.configuration.interval)
            except Exception as e:
                self.logger.error("Something unexpected went wrong while requesting: {0}"
                                  .format(str(e)), exc_info=config.EXC_INFO)

Once you have created the general data object (models.Data), you can easily pass it to the linked modules by calling self._call_links(data).


Input Tag

Input tags are similar to input variables. However, they do not generate data themselves, but are queried for data. If so, they have to return a dictionary with the corresponding data.

Example
from modules.base.inputs.base import AbstractInputModule, AbstractVariableModule, AbstractTagModule, models

class TagModule(AbstractTagModule):
    ...

    @dataclass
    class Configuration(models.TagModule):
        key: str = field(
            metadata=dict(description="The key of the constant.",
                          required=True),
            default=None)
        value: str | float | int | bool | list = field(
            metadata=dict(description="The value of the constant.",
                          required=True),
            default=None)

    def __init__(self, configuration: Configuration, input_module_instance=None):
        super().__init__(configuration=configuration,
                         input_module_instance=input_module_instance)

    def _run(self) -> dict[str, Any]:
        """
        This methods returns the queried data.
        :returns: A dict containing the generated key-value pairs.
        """
        return {self.configuration.key: self.configuration.value}

Whether the returned data is attached to the existing data object as fields or tags is user-defined and handled by the underlying framework.