Logic Manager configuration

So, let us proceed with our configuration. As soon as Universal Controller is already configured, let us move on with Logic Manager.

Configuring notification system and API key for SFA

Note

If you’ve used “easy setup” for the EVA installation, you may skip this step because all controllers are already connected.

The first step is to connect the server to the local MQTT to allow Logic Manager to receive UC item states in real time:

eva ns lm create eva_1 mqtt:eva:secret@localhost -s plant1 -y

then subscribe the notification system to receive the states of the local logic variables which will be created later:

eva ns lm subscribe state eva_1 -v lvar -g '#'
eva ns lm config eva_1
{
    "enabled": true,
    "events": [
        {
            "groups": [
                "#"
            ],
            "subject": "state",
            "types": [
                "#"
            ]
        }
    ],
    "host": "localhost",
    "id": "eva_1",
    "password": "secret",
    "space": "plant1",
    "type": "mqtt",
    "username": "eva"
}

Add API key for SCADA Final Aggregator in etc/lm_apikeys.ini:

[sfa]
key = secret_for_sfa2
groups = #
hosts_allow = 127.0.0.1

Restart LM PLC:

eva lm server restart

Connecting UC controller

Note

If you’ve used “easy setup” for the EVA installation, you may skip this step because all controllers are already connected.

Connect the local UC to Logic Manager using the key we’ve created in etc/uc_apikeys.ini in the previous part of the tutorial:

eva lm controller append http://localhost:8812 -a secret_for_lm -m eva_1 -y
eva lm -J remote -p S
[
    {
        "controller_id": "uc/uc1",
        "group": "security",
        "id": "motion1",
        "oid": "sensor:security/motion1",
        "status": 1,
        "type": "sensor",
        "value": "0"
    },
    {
        "controller_id": "uc/uc1",
        "group": "env",
        "id": "temp1",
        "oid": "sensor:env/temp1",
        "status": 1,
        "type": "sensor",
        "value": "25.4"
    }
]

Looks correct, sensors are loaded, let’s check the units:

eva lm remote -p U

Let LM PLC reload the items from the connected controller every 60 seconds, if new ones are added in future:

eva lm controller set uc1 reload_interval 60 -y

Building logic

We have two tasks: to switch on the inside ventilation if the temperature is above 25 degrees, and handle the events received from the motion sensor. Do not forget that the inside ventilation should be off from 9pm till 7am. Though this will be later implemented via eva sfa (sfa-cmd) and system cron, we should get it prepared now.

Ventilation logic

We chose our example, as far as the boundary conditions of the sensor is a very common problem for such tasks.

If we solve this problem by creating the following two rules:

  • if the temperature is above 25 degrees, the fan is switched on
  • if below - switched off

the following problem may occur: if the temperature will hover around 25 degrees, the ventilation system will constantly switch on and off. Therefore, a breakdown is highly possible. We cannot simply set up chillout_time in the rule, it completely disables the rule, so it doesn’t match after the chillout is over, if both previous and current state are in the range.

Due to the flexibility of EVA there is a number of solutions of this problem:

Option 1:

Ventilation is switched on, if the temperature is above 25 degrees, and switched off if it is below e.g. 25.5. The logic will have half a degree gap for the equipment not to be overloaded. If the temperature changes not that quickly, this option would be the best one.

Option 2:

  • Create the rule without a condition activated whenever the env/temp1 sensor changes its value
  • The stop-start logic, as well as the temperature monitoring logic, is fully transferred to the macro executed by the above rule.
  • macro reads the value of lvar ventilation/start_temp (to avoid hardcoding 25 in a macro code and let it have an ability to be changed from outside)
  • To avoid the continuous running of macro, use the rule prop chillout_time. Or even
  • Use unit_status macro function to get the current ventilation status and use macro lock function to block its changing too often e.g. for 5 minutes

The macro code will look like:

if status('unit:ventilation/vi') and \
    value('sensor:env/temp1') < value('ventilation/start_temp'):
  try: lock('ventilation/vi/control', 5, 300)
  except: exit()
  stop('ventilation/vi')
elif not status('unit:ventilation/vi') and \
    value('sensor:env/temp1') >= value('ventilation/start_temp'):
  try: lock('ventilation/vi/control', 5, 300)
  except: exit()
  start('ventilation/vi')

Option 3

Increase update_interval prop of env/temp1 sensor e.g. to 300 seconds. This option will work, though it is not that good because the system will obtain the current temperature with a 5 min delay (or we need to duplicate the sensor in UC and create a quicker one).

Option 4

Add a 5 minutes delay at the end of xc/uc/vi action script, allow vi queues (set action_queue=1 unit prop), and start ventilation from the macro in the following way:

try: lock('ventilation-example3', 5, 10)
except: exit()
# clean up all queued action
q_clean('ventilation/vi')
# exec our action
start('ventilation/vi')
unlock('ventilation-example3')

Not bad, but we loose an ability to exec the actions with w param and get the correct completion status.

Option 5

Use system crontab to copy env/temp1 value to some logic variable every 5 minutes. Then work only with this logic variable. This option is too awkward, the logic of the system crontabs will sooner or later turn into the script hell. Therefore, we will use cron only for the time schedule-based logic, everything else will be done using EVA.

Option 6 - the best one

There are more than 10 options to solve our problem, but we will choose the best one: delayed start. Moreover, we have a condition to run ventilation only in 5 minutes after the temperature becomes >=25. So, we will use this option. The other ones have been reviewed just because this is a tutorial.

Create two logic variables:

eva lm create lvar:ventilation/vi_auto -y
eva lm create lvar:ventilation/vi_timer -y
eva lm set lvar:ventilation/vi_auto -s 1 -v 1

The first one will act as a flag for the ventilation control macro: if the flag is on 1, the control is possible, if off - the ventilation should not be touched. The second variable will act as a delayed start timer.

Create a control macro which satisfies for our current task and the scheduled ventilation switching. We will give it two parameters: the first - what to do with ventilation (0 - switch off, 1 - switch on), the second - who runs it: temperature event, our delayed start timer or a system cron:

eva lm macro create control/vi_control -y

Put a macro code in xc/lm/vi_control.py file

if _1: # if anyone asks to switch on the ventilation
    if _2 == 'event': # if it's an event
    reset('ventilation/vi_timer') # reset delayed start timer
    elif _2 == 'timer': # if it's a timer
        if value('ventilation/vi_auto'):
            start('ventilation/vi') # start ventilation if allowed
    elif _2 == 'cron': # if it's a system cron
        # disable the ventilation automation for everyone but cron
        clear('ventilation/vi_auto', 0):
        start('ventilation/vi') # start ventilation
else: # if it's a command to switch off
    clear('ventilation/vi_timer') # stop the delayed start timer
    if value('ventilation/vi_auto') or _2 == 'cron':
        # in case the command is send by cron or
        # if allowed to stop - stop it
        stop('ventilation/vi')
    if _2 == 'cron':
        # if the ventilation is switched off by cron
        # then enable automation back for everyone
        set('ventilation/vi_auto', 1)

The macro requires 3 rules:

The first one will match if the temperature is above or equal to 25 degrees and activates the delayed start timer:

eva lm -I
rule create
rule set <rule_uuid> oid sensor:env/temp1.value
rule set <rule_uuid> condition x >= 25
rule set <rule_uuid> macro vi_control
rule set <rule_uuid> macro_args "1 event"

The second one will match if the temperature is below 25 degrees and switches the ventilation off (if it’s allowed):

rule create
rule set <rule_uuid> oid sensor:env/temp1.value
rule set <rule_uuid> condition x < 25
rule set <rule_uuid> macro vi_control
rule set <rule_uuid> macro_args "0 event"

The third rule will run the macro to turn the ventilation on as soon as the delayed start timer finishes the countdown:

rule create
rule set <rule_uuid> oid lvar:ventilation/vi_timer.status
rule set <rule_uuid> condition x == -1
rule set <rule_uuid> macro vi_control
rule set <rule_uuid> macro_args "1 timer"

Motion sensor logic

We will need one variable identifying whether the alarm is switched on or not:

eva lm create lvar:security/alarm_enabled -y
eva lm set lvar:security/alarm_enabled -s 1 -v 0

Additionally, we will need two macros. The first one will send API call to alarm system:

eva lm macro create security/alarm_start -y

put its code to xc/lm/alarm_start.py:

# do not send API calls more than once in 30 minutes
try: lock('alarm-start', expires = 1800)
except: exit()
# call the alarm system API
requests.get('http://alarmserver/api/activate?apikey=blahblahblah')

The second one will check whether the alarm is switched, to either switch on the alarm system or just turn on the lighting:

eva lm macro create security/motion1_handler -y

put its code to xc/lm/motion1_handler.py:

if value('security/alarm_enabled'):
    run('security/alarm_start')
else:
    start('light/lamp1')

Plus the additional rule executing motion1_handler macro when the motion sensor detects an activity:

eva lm rule create
eva lm rule set <rule_uuid> oid sensor:security/motion1/value
eva lm rule set <rule_uuid> condition 'x=1'
eva lm rule set <rule_uuid> macro motion1_handler
eva lm rule enable <rule_uuid> -y

The logic is set up. We can review and test it in LM EI and configure SCADA Final Aggregator configuration to interact with the external applications (in our case - the system cron, for the scheduled ventilation control) and serve the system web interface.