SFA Framework

SFA Framework is a component of SCADA Final Aggregator that allows you to quickly create a web application with EVA interface for a specific configuration.

ui folder contains js/eva_sfa.js file, the framework itself and lib/jquery*.js - jQuery, necessary for correct operation. Lib folder also contains Bootstrap files often used for web application development.

Framework connection

Open the file ui/index.html in the editor, connect jQuery and SFA Framework:

<script src="lib/jquery.min.js"></script>
<script src="js/eva_sfa.min.js"></script>

Framework variables

eva_sfa_login, eva_sfa_password

The following variables contain the user login/password, and are used for the initial authentication:

eva_sfa_login = '';
eva_sfa_password = '';


Another way is to use the variable

eva_sfa_apikey = null;

in case its value is not NULL, the authentication is done with API key

eva_sfa_cb_login_success, eva_sfa_cb_login_error

The following two variables contain functions called when the authentication either succeeded or failed (data parameter is equal to jQuery post):

eva_sfa_cb_login_success = null;
eva_sfa_cb_login_error = null;


This function called after framework loads initial item states

eva_sfa_cb_states_loaded = null;


The interval for a server ping test (heartbeat)

eva_sfa_heartbeat_interval = 5;


The following function is automatically called in case of a server heartbeat error:

eva_sfa_heartbeat_error = eva_sfa_restart;

The function is called with data parameter containing HTTP error data, or without parameter if such data is not available (e. g. the error occurred when attempting to send data via WebSocket).


Interval (seconds) for updating data when framework is in AJAX mode:

eva_sfa_ajax_reload_interval = 2;


The next variable forces ajax updates if the framework is running in WebSocket mode. 0 value disables updating via AJAX completely, but it’s recommended to keep some value to be sure the interface has the actual data even if some websocket events are lost.

eva_sfa_force_reload_interval = 5;


Interval (seconds) for updating settings of the decision-making matrix rules. Rule settings are updated via AJAX only.

eva_sfa_rule_monitor_interval = 60;


The next variable is updated by heartbeat and contains API test call results. This variable may be used by the application to check whether the framework has established connection to the server - if not, the variable is null.

eva_sfa_server_info = null;


This variable contains the time difference (in seconds) between server and connected client. The value is updated every time client gets new server info.

eva_sfa_tsdiff = null;


This variable sets the framework working mode. If its value is true, SFA framework operates via WebSocket, if false - via AJAX. This value is changed by eva_sfa_init() which tries to detect if web browser is compatible with web socket. To change the mode manually, change the variable after the initial framework initialization.

eva_sfa_ws_mode = true;


The next variable contains function processing WebSocket data. If the user declares this function, it should return true (in case the data processing is possible hereafter) or false (if the data has already been processed). The function is called via data parameter with the event data set herein.

eva_sfa_ws_event_handler = null;


This variable contains function which’s called when SCADA Final Aggregator asks connected clients to reload the interface. If you want the interface to handle the reload event, you must define this function.


reload event can be processed only when the framework is in a websocket mode

eva_sfa_reload_handler = null;


This variable contains function which’s called when SCADA Final Aggregator notifies connected clients about server restart. Client application can prepare user for the server restart (e.g. display warning message) and forcibly reload data when the server is back online.

SFA cvars

All user-defined SFA variables are directly available in SFA Framework after login with any valid user or API key.

Initialization, authentication


To initialize the framework run



To start the framework, run


that will authorize the user and run the data update and event handling threads.


After the initialization succeeds, you may additionally start reloading the decision rules. The following function is not called by init/start and you should call it separately:



To stop the framework, call:


Event Handling


To manually get item state, use the function



  • oid item id in the following format: type:group/item_id, i.e. sensor:env/temperature/temp1

The function returns state object or undefined if the item state is unknown.

You can use a simple mask for oid (like *id, id*, *id*, i*d), in this case the function returns the array of all item with oids matching the specified mask.


Returns state history for the chosen item(s)

eva_sfa_state_history(oid, params, cb_success, cb_error);


  • oid item id in the following format: type:group/item_id, i.e. sensor:env/temperature/temp1, or multiple items comma separated
  • params dict with history formatting params equal to SFA API function state_history.


Returns a list of item groups.

eva_sfa_groups(p, g, cb_success, cb_error);


  • p item type (U for unit, S for sensor, LV for lvar)
  • g optional group filter (MQTT-style wildcards)
  • cb_success, cb_error - functions called when the access to API has either succeeded or failed.


When the new data is obtained from the server, the framework may run a specified function to handle events. To register such function in the framework, use

eva_sfa_register_update_state(oid, cb);


  • oid item id in the following format: type:group/item_id, i.e. sensor:env/temperature/temp1
  • cb function which’s called with state param containing the new item state data (state.status, state.value etc. equal to the regular state notification event.)

You can use a simple mask for oid (like *id, id*, *id*, i*d), in this case the specified state update function will be called always when item oid matches the specified mask.


Similarly, you can process the decision rules settings. When rule params are changed, the framework runs the function registered by

eva_sfa_register_rule(rule_id, cb);


  • rule_id rule id to monitor
  • cb function which’s called with props param containing all the rule props (similar to LM API list_rule_props<lm_list_rule_props>)

Macro execution and unit management


To execute macro, call the function:

eva_sfa_run(macro_id, args, wait, priority, uuid, cb_success, cb_error);

where macro_id - macro id (in a full format, group/macro_id) to execute, other params are equal to LM API run function, and cb_success, cb_error - functions called when the access to API has either succeeded or failed. The functions are called with data param which contains the API response.


To run the unit action, call the function:

eva_sfa_action(unit_id, nstatus, nvalue, wait, priority, uuid, cb_success,

Where unit_id - full unit id (group/id), other parameters are equal to UC API action, and cb_success, cb_error - functions called when the access to API has either succeeded or failed. The functions are called with data param which contains the API response.


In case you want to switch unit status between 0 and 1, call:

eva_sfa_action_toggle(unit_id, wait, priority, uuid, cb_success, cb_error);

eva_sfa_result, eva_sfa_result_by_uuid

To obtain a result of the executed actions, use the functions:

eva_sfa_result(unit_id, g, s, cb_success, cb_error);
eva_sfa_result_by_uuid(uuid, cb_success, cb_error);


Terminate unit action and clean up queued commands:

eva_sfa_kill(unit_id, cb_success, cb_error);


Clean unit action queue but keep the current action running:

eva_sfa_q_clean(unit_id, cb_success, cb_error);

eva_sfa_terminate, eva_sfa_terminate_by_uuid

Terminate the current unit action either by unit id, or by action uuid:

eva_sfa_terminate(unit_id, cb_success, cb_error);
eva_sfa_terminate_by_uuid(uuid, cb_success, cb_error);

Working with logic variables


To set the logic variable status, use the function:

eva_sfa_set(lvar_id, value, cb_success, cb_error);


To switch lvar value between 0 and 1 use

eva_sfa_toggle(lvar_id, cb_success, cb_error);


To reset lvar when used as timer or flag:

eva_sfa_reset(lvar_id, cb_success, cb_error);


To clear lvar flag or stop the timer:

eva_sfa_clear(lvar_id, cb_success, cb_error);


Get timer expiration (in seconds). Allows to display timers and interactive progress bars of the production cycles.


Returns float number of seconds to timer expiration, or:

  • undefined if lvar is not found, or eva_sfa_tsdiff is not set yet.
  • null if lvar has no expiration set
  • -1 if the timer is expired
  • -2 if the timer is disabled (stopped) and has status 0

Modifying decision rules


To change decision rules properties, call:

eva_sfa_set_rule_prop(rule_id, prop, value, save, cb_success, cb_error);

Processing logs

SFA Framework has built-in functions to display SFA logs. In case SFA is a log aggregator, this allows to view logs from the whole EVA installation.


For log processing the client API key should have sysfunc=yes permission.


This variable sets log reload interval if the framework works in AJAX mode.

eva_sfa_log_reload_interval = 2;


Maximum number of log records to get initially

eva_sfa_log_records_max = 200;


Function called with log record param, when the new log event arrives

eva_sfa_process_log_record = null;


Function called when all new log records are processed, i.e. to autoscroll the log viewer

eva_sfa_log_postprocess = null;


This function starts log processing engine


log_level - optional param, log level records with level >= 20 (INFO) are processed by default, if not specified.


This function allows to change log level processing


Here log_level param is required. The function reloads all log records with the specified level, so it’s a good idea to clean log viewer before.


This function returns log level name matching the given log level code:


Returns DEBUG for 10, INFO for 20, WARNING for 30, ERROR for 40, CRITICAL for 50.

UI functions


Draws load animation inside specified <div />



Calls eva_sfa_load_animation, then eva_sfa_state_history and builds a chart inside specified <div />

eva_sfa_chart(ctx, cfg, oid, timeframe, fill, update, prop);


  • ctx HTML element (<div />) ID to draw a chart in.
  • cfg chart configuration. SFA Framework uses Chart.js library. At this moment, line and bar charts are supported
  • oid item OID (or multiple, comma separated): type:group/id
  • timeframe timeframe to display, e.g. 5T - last 5 min, 2H - last 2 hours, 2D last 2 days etc.
  • fill precision, 10T-60T recommended. The more accurate precision is, the more data points are displayed (but chart is slower)
  • update chart update interval, in seconds. Set 0 or null to disable updates
  • prop item state property to use (default: ‘value’)


To work with charts you should include Chart.js library, which is located in file lib/chart.min.js (ui folder).

See Chart example.


Opens HTML5 popups



  • ctx html element id to use as popup (any empty <div /> is fine)
  • pclass popup class: info, warning or error. opens big popup window if ‘!’ is put before the class (i.e. !info)
  • title popup window title
  • msg popup window message
  • ct popup auto close time (sec), equal to pressing escape
  • btn1 button 1 name (default: ‘OK’)
  • btn2 button 2 name
  • btn1a function to run if button 1 (or enter) is pressed
  • btn2a function(arg) to run if button 2 (or escape) is pressed. arg is true if the button was pressed, false if escape key or auto close.
  • va validate function which runs before btn1a. If the function returns true, the popup is closed and btn1a function is executed. Otherwise the popup is kept and the function btn1a is not executed. va function is used to validate input, e.g. if popup contains any input fields.

Example (consider <div id=”popup” style=”display: none”></div> is placed somewhere in HTML):

// after successful login
eva_sfa_popup('popup', 'info', 'Logged in', 'You are logged in', 2);
// .......
// reload handler
function reload_me() {
eva_sfa_reload_handler = function() {
      'Reloading interface',


Examples of the SFA framework usage are provided in “Building an interface with SFA Framework” part of the EVA tutorial.

Timer example

The following example shows how to display the timer countdown. The countdown is updated every 500 ms.

function show_countdown() {
    var t = eva_sfa_expires_in('timers/timer1');
    if (t === undefined || t == null) {
    } else {
        if (t == -2) {
        } else if (t == -1 ) {
        } else {
            t = Number(Math.round(t * 10) / 10).toFixed(1);

setInterval(show_countdown, 500);

Chart example

We have 2 sensors, for internal and external air temperature and want their data to be placed in one chart.

Chart options:

var chart_opts = {
        responsive: false,
        //animation: false,
        legend: {
            display: true
        scales: {
            xAxes: [{
                type: "time",
                time: {
                    unit: 'hour',
                    unitStepSize: 1,
                    round: 'minute',
                    tooltipFormat: "H:mm:ss",
                    displayFormats: {
                      hour: 'MMM D, H:mm'
                ticks: {
                    minRotation: 90,
                    maxTicksLimit: 12,
                    autoSkip: true
                display: true,
            yAxes: [{
                display: true,
                ticks: {
                scaleLabel: {
                    display: true,
                    labelString: 'Degrees'

Chart configuration:

var chart_cfg = {
    type: 'line',
    data: {
        labels: [],
        datasets: [
            label: 'Temperature inside',
            data: [],
            fill: false,
            backgroundColor: 'red',
            borderColor: 'red'
            label: 'Temperature outside',
            data: [],
            fill: false,
            backgroundColor: 'blue',
            borderColor: 'blue'
    options: chart_opts

Chart code (consider <div id=”chart1” style=”display: none”></div> is placed somewhere in HTML), data for last 8 hours, 15 min precision, update every 10 seconds:


Log viewer example

The following example shows how to build a log viewer, similar to included in UC EI and LM EI.

  <script src="lib/jquery.min.js"></script>
  <script src="js/eva_sfa.js"></script>
  <style type="text/css">
    #logr {
      outline: none;
      width: 100%;
      height: 60% !important;
      font-size: 11px;
      overflow: scroll;
      overflow-x: hidden;
      margin-bottom: 10px;
      border-style : solid;
      border-color : #3ab0ea;
      border-color : rgba(58, 176, 234, 1);
      border-width : 2px;
      border-radius : 5px;
      -moz-border-radius : 5px;
      -webkit-border-radius : 5px;
    .logentry.logentry_color_10 { color: grey }
    .logentry.logentry_color_20 { color: black }
    .logentry.logentry_color_30 {
      color: orange;
      font-weight: bold;
      font-size: 14px
    .logentry.logentry_color_40 {
      color: red;
      font-weight: bold;
      font-size: 16px
    .logentry.logentry_color_50 {
      color: red;
      font-weight: bold;
      font-size: 20px;
      animation: blinker 0.5s linear infinite;
    @keyframes blinker {
      50% { opacity: 0; }
  <div id="logr"></div>
  <script type="text/javascript">
      function time_converter(UNIX_timestamp) {
        var a = new Date(UNIX_timestamp * 1000);
        var year = a.getFullYear();
        var month = a.getMonth() + 1;
        var date = a.getDate();
        var hour = a.getHours();
        var min = a.getMinutes();
        var sec = a.getSeconds();
        var time =
          year +
          '-' +
          pad(month, 2) +
          '-' +
          pad(date, 2) +
          ' ' +
          pad(hour, 2) +
          ':' +
          pad(min, 2) +
          ':' +
          pad(sec, 2);
        return time;

      function pad(num, size) {
        var s = num + '';
        while (s.length < size) s = '0' + s;
        return s;

      function format_log_record(l) {
        return (
          '<div class="logentry logentry_color_' +
          l.l +
          '">' +
          time_converter(l.t) +
          ' ' +
          l.h +
          ' ' +
          l.p +
          ' ' +
          eva_sfa_log_level_name(l.l) +
          ' ' +
          l.mod +
          ' ' +
          l.th +
          ': ' +
          l.msg +
      eva_sfa_process_log_record = function(l) {
        while ($('.logentry').length > eva_sfa_log_records_max) {
      eva_sfa_log_postprocess = function() {

      eva_sfa_cb_login_success = function(data) {
          eva_sfa_log_records_max = 100;

Updating multiple values

The following example will show how to update displayed values of 3 sensors with one function. Define HTML elements:

<div>Sensor 1 value: <span id="sensor:group1/sensor1"></span></div>
<div>Sensor 2 value: <span id="sensor:group1/sensor2"></span></div>
<div>Sensor 3 value: <span id="sensor:group1/sensor3"></span></div>

Then register update event function:

eva_sfa_register_update_state('sensor:group1/*', function(state) {
    $('#' + $.escapeSelector(state.oid)).html('S: ' + state.value);

Controlling reliability of the connection

An important moment of the web interface chosen for automation systems is reliability of the connection.

Common problems which may arise:

  • SFA server reboot and loss of session data.
  • Breaking the WebSocket connection due to frontend reboot or another reason.

To control the session, SFA Framework requests SFA API test every eva_sfa_heartbeat_interval (5 seconds by default). WebSocket is additionally controlled by the framework using { ‘s’: ‘ping’ } packet, whereto the server should send a response { ‘s’: ‘pong’ }. If there is no response within the time exceeding heartbeat interval, the connection is considered broken.

In case of short-term problems with the server, it will be enough to set the default value

eva_sfa_heartbeat_error = eva_sfa_restart;

and keep login/password in eva_sfa_login and eva_sfa_password variables, or API key in eva_sfa_apikey. If an error occurs, heartbeat will attempt to restart the framework once. If it fails or the variable data has been deleted after the initial authorization, the function specified in eva_sfa_cb_login_error will be called.

If your interface cleans up the authorization data, eva_sfa_heartbeat_error should do the following:

eva_sfa_heartbeat_error = function() {
    // stop framework, make another attempt to log out
    // if the login/password were used
        // your function that displays the authorization form

In case reconnection is automatic, heartbeat error calls eva_sfa_restart() that, in turn, calls eva_sfa_cb_login_error in case of failure.

And for automatic reconnection it should look like:

eva_sfa_cb_login_error = function(data) {
    if (data.status == 403) {
        // if the server returned error 403 (authentication failed
        // due to invalid auth data), the user should get a login form
        } else {
        // in case of other errors - try to restart framework in 3 seconds
        // and attempt to connect again
        setTimeout(eva_sfa_start, 3 * 1000);