Exchanger

Description

ezvpn-fld-exchanger is a powerful container that manages value transfers among different PLCs and protocols, and containers.

ezvpn-fld-exchanger is, together with ezvpn-mqtt, the heart of IOhubTM. See some examples below to realize how powerful and simple it is.

The control flow is:

  • an incoming value, from mqtt, is detected
  • a program is executed

The program triggered by each incoming variable is written in EXCH language. EXCH is a simple, yet flexible, scripting language that allows data transformation and mqtt publish operations, integrating potentially every existing container, generating actions like send a Slack message, send a text message, turn on a light, stop a robot, create a production order on your ERP, save some data to a DB, write a value to a PLC, etc.

You can refer the EXCH to check how a program is created.

You acknowledge when a program should be triggered; ezvpn-fld-exchanger will be on the lookout for changed events and run the EXCH script.

For example, you can monitor a temperature value on a Siemens PLC with S7 protocol and run some logic by using the following snippet.

{
    "from": {
        "protocol": "step7",
        "name": "temp2"
    },
    "trigger": {
        "when": "onChange",
        "exch": [ "TEMP_CHANGED" ]
    }
}

TEMP_CHANGED must be supplied to ezvpn-fld-exchanger as an environment variable. The content of TEMP_CHANGED is the EXCH script to run.

TEMP_CHANGED might contain, for example, the following script

# convert from tenths of C to C
temp = step7/temp2 / 10

if (${temp} > 200) then
    # send a slack notification
    publishValue "slack/mychannel" "Alert: Temp2 reached " + ${temp} + " C degrees"

    # stop the system
    writeField "step7" "running" false
endif

How to use it

ezvpn-fld-exchanger is a Docker container image pre-configured for communication with ezvpn-mqtt. In case you need to use it with your MQTT broker, see the section regarding customization below.

If used with ezvpn-mqtt, only one environment variable must be provided: EXCHANGER_CFG.

EXCHANGER_CFG is a string representing a JSON object, describing the configuration to read and write measurement values.

[
    {
        "from": {
            "protocol": "<protocol>",
            "name": "<address>"
        },
        "trigger": {
            "when": "always | onChange",
            "exch": [ "<env var containing the program 1>",
                    "<env var containing the program 2>",
                    ...,
                    "<env var containing the program n>" ]
        }
    },
    {
        "topic": "<my custom topic>",
        "trigger": {
            "when": "always | onChange",
            "exch": [ "<env var containing the program 1>",
                    "<env var containing the program 2>",
                    ...,
                    "<env var containing the program n>" ]
        }
    },
    {
        "cron": "<cron expression>",
        "timezone": "<timezone>",
        "trigger": {
            "exch": [ "<env var containing the program 1>",
                    "<env var containing the program 2>",
                    ...,
                    "<env var containing the program n>" ]
        }
    }
]

from definition

To manage messages from/to the field containers, adehering to the IOhubTM standard format: { "value": <value>, "ts": <timestamp> }

  • from: where to read data from
    • protocol: the protocol of the field container
    • name: the address of the field container

topic definition

To manage messages in raw format.

  • topic: where to read data from; you can specify a generic topic name e.g.
    • myname
    • myprotocol/mysubprotocol
    • myprotocol/mysubprotocol/+/something/#

cron definition

To use the container as a time-based job scheduler.

  • cron: to schedule the programs execution. A string in cron time format

  • timezone: an optional timezone, see the list, to use for the cron expression. If not defined UTC is used.

When the program execution is triggered by a cron expression, the variables passed to the program have the following default values:

  • ${_p}, ${_m} contain an empty string
  • ${_v} contains the number 0
  • ${_t} contains the timestamp generated at the execution time.

trigger definition

  • trigger: which action must be triggered, upon an incoming value.
  • when can have value always or onChange. If always the program is executed every time a value is read from mqtt. If onChange the program is executed only if the new value is different from the previous one. Not needed when triggered by a cron expression,, required in the other definitions,
  • exch contains a list of names of environment variable containing the script to be executed. All the programs are treated as a single program resulting by joining, in the listed order, each program.

The scripts in the exch variable are not to be considered as independent programming modules. In EXCH the executed program is resulting by joining sequentially each part and therefore the listing order matters.

If you want to write different EXCH programs, implementing different functionalities, based on the same trigger, you can duplicate a trigger, using different scripts.

You can find additional examples in the EXCH dedicated section.

In case your configuration contains only one definition, you do not need to put it inside a one-element array; the following configuration is still valid:

{
    "cron": "<cron expression>",
    "trigger": { "exch": [ "<program>" ] }
}

Exchanger lifecycle

The image below describes the full lifecycle of the Exchanger container, from boot to programs execution.

Exchanger Lifecycle

Docker Container description

Image: us-central1-docker.pkg.dev/ez-shared/iohub/iohub-fld-exchanger

Supported architecture: amd64

Environment variables

When you start the ezvpn-fld-exchanger image, you can adjust the instance's configuration by passing one or more environment variables to the docker run command.

  • EXCHANGER_CFG(Required): mapping description, as documented above.
  • MQTT_HOST: ip address / host name of the MQTT broker. Defaults to 127.0.0.1 only if MQTT_GLOBAL_HOST is not defined (In IOhubTM the variable is set to ezpn-mqtt value)
  • MQTT_PORT: MQTT broker port. Defaults to 1883.
  • MQTT_GLOBAL_HOST: ip address / host name of the global MQTT broker.
  • MQTT_GLOBAL_PORT: MQTT global broker port. Defaults to 1883.
  • MQTT_GLOBAL_IS_DEFAULT: if true, by default the global broker is used to publish data. Defaults to false.
  • STORAGE_FOLDER: folder for permanent variable storage. Defaults to /data.
  • EZVPN_APPLICATION_NAME: name of the application. See the paragraph Application Synchronization below.
  • EXCHANGER_KEEPALIVE: keepalive frequency. See the paragraph Application Synchronization below.

All the incoming values both from MQTT_HOST and MQTT_GLOBAL_HOST (if defined) are managed as coming from a single source.

If MQTT_GLOBAL_IS_DEFAULT is set to true, every EXCH mqtt/http publish operation (writeField, httpGet, ...) are by default sent to the global broker.

Running locally with Docker

Example of running an ezvpn-fld-exchanger Docker container:

Save the JSON config in a temp variable named CFG

read -r -d '' CFG << EOM
[
    {
        "from": {
            "protocol": "opcua",
            "name": "plant1.pressure"
        },
        "trigger": {
            "when": "onChange",
            "exch": [ "PRESSURE_CHANGED" ]
        }
    }
]
EOM

Execute the container with an MQTT broker, in this example we assume you have installed an MQTT host on your PC.

docker run -it -e EXCHANGER_CFG=${CFG} \
    -e MQTT_HOST="127.0.0.1" \
    us-central1-docker.pkg.dev/ez-shared/iohub/iohub-fld-exchanger

Example with docker-compose:

docker-compose.yml

version: "3"

services:
    myapp-fld-exchanger:
        image: us-central1-docker.pkg.dev/ez-shared/iohub/iohub-fld-exchanger
        environment:
            MQTT_HOST: "<mqtt host>"
            EXCHANGER_CFG: |-
                [
                    {
                        "from": {
                            "protocol": "opcua",
                            "name": "plant1.pressure"
                        },
                        "trigger": {
                            "when": "onChange",
                            "exch": [ "PRESSURE_CHANGED" ]
                        }
                    }
                ]

Modify the EXCHANGER_CFG environment variable accordingly to your actual PLC configuration.

Application Synchronization

When designing a complex application, your application should be split into smaller applications, possibly managing their internal state using a local mqtt broker and exchanging data among other local applications through the global mqtt broker.

As application "producer" would like to wait for the application "consumer" being ready to consume mqtt messages, instead of immediately sending messages, that would get lost.

Each exchanger can automatically send its status to the global mqtt broker through a keepalive mechanism to allow the right program execution order. To enable the keepalive, you have to provide values to the following environment variables:

  • EZVPN_APPLICATION_NAME: name of the application (e.g. "publisher", "consumer", ...).
  • EXCHANGER_KEEPALIVE: keepalive frequency. Defaults to 0.
    • If < 0, keepalive is disabled.
    • If 0, only one message is sent at the startup.
    • If > 0, every EXCHANGER_KEEPALIVE milliseconds, a message is sent.

Using these two variables, a message is sent to the topic fld/exchanger/r/<value of EZVPN_APPLICATION_NAME>. The message is in the standard format, with value = 1, e.g. {"value":1,"ts":1626734682145}.

When the keepalive is enabled, the application "publisher" can wait until the application "consumer" is ready using the following snippet:

init
    check exchanger/consumer
endinit

# rest of the program

If you are using the exchanger container from the management website, the EZVPN_APPLICATION_NAME is automatically populated with the application name and not visible in the user interface.

Deprecation

Starting from version v1.0.1 the old form using from/to values is being deprecated. In a future version it will be removed.

Each existing configuration using from/to is now automatically translated into the new form, using the EXCH language as described below.

The following sample configuration

{
    "from": {
        "protocol": "step7",
        "name": "temp2"
    },
    "to": {
        "protocol": "opcua",
        "name": "plant1.temp2"
    }
}

is automatically translated into the new format

{
    "from": {
        "protocol": "step7",
        "name": "temp2"
    },
    "trigger": {
        "when": "always",
        "exch": [ "<some name>" ]
    }
}

and the content of the variable <some name> will be:

writeField "opcua" "plant1.temp2" step7/temp2

Changelog

v1.1.1
  • EXCH 1.1.6 version
v1.1.0
  • EXCH 1.1.5 version
v1.0.9
  • EXCH 1.1.4 version
v1.0.8
  • EXCH 1.1.3 version
v1.0.7
  • EXCH 1.1.2 version
v1.0.6
  • EXCH 1.1.1 version
v1.0.5
  • cron in configuration
v1.0.4
  • EXCH 1.1.0 version
  • topic trigger type in configuration
v1.0.3
  • Smaller image
v1.0.2
  • init block added
  • fail added
v1.0.1
  • from/to deprecated
  • EXCH added
v1.0.0
  • First Release