Data Analytics NodeJS Guide

Overview

The Data Analytics functionality can be implemented as a Node.js script using the Node.js ZIOTC (Zebra IOT Connector) module resident on the reader.

The Node.js ZIOTC module is meant to abstract the underlying connections between the IoT Connector components (i.e. Radio Control and Reader Gateway) and allow the script filter, analyze, and act on the data itself.

The minimal flow of the script would be:

  1. Define callback functions

    1. New Message Callback: Operates on the incoming data. This function will be called whenever there is a new message to be filtered.

      const onNewMessageCallback = (msg) => {
          ...
      };
      

      For more details see New Message Callback

    2. Passthru: Callback when the a new passthru message has been received from the reader gateway.

      const onPassthruCallback = (msg) => {
          ...
      };
      

      For more details see DA Passthru

    3. OK: Callback when the module ends

      const onOKCallback = (msg) => {
          ...
      };
      
    4. error: Callback when the module encounters and error

      const onNewErrorCallback = (msg) => {
          ...
      };
      
  2. Open the ZIOTC module.

    This will establish connections between the script and the other IoT Connector components.

    const ziotc = require('./ziotc.node');
    
  3. Set Data Source.

    This will set the data source for incoming messages. The data source is set to CONNECT_FOR_RADIO_DATA by default. Refer to NodeJS API reference Guide for further details.

    ziotc.SetDataSource("CONNECT_FOR_RADIO_DATA");
    
  4. Register callbacks and Run ZIOTC

    This will cause register the callback previously defined callback functions and run the module.

    ziotc.Run(onErrorCallback, onOkCallback, onNewMessageCallback, onPassthruCallback);
    

New Message Callback

The user-defined new message callback function is the core of the script. The callback definition contains a single parameter: the message as a json object.

The function can then process the message. For example, the following callback function, sends out a data message if the beginning of the tag’s ID begins with a prefix.

var prefix = "00";
const onNewMessageCallback = (msg) => {
    if ( msg["type"] == 0 )
    {
        tag_id_hex = JSON.parse(msg["msg"])["data"]["idHex"]
        if (tag_id_hex.startsWith(prefix))
        {
        msg["type"]=3;
        ziotc.SendNextMsg(msg);
        }
    }
};

Input Message types

The new message callback will be called each time a new message arrives from the Radio Control. The message will be a json object. It contains a “type” field and a “msg” field. When type is 0, msg field contains a string that can be parsed into a json object. The format is identical to the format of tag messages coming directly from the Radio Control.

Output Message types

Messages can be sent to the Reader Gateway and off the reader by calling:

ziotc.SendNextMsg(msg);

The format for sending message matches the incoming messages. The currently available value for outgoing messages are:

ZIOTC_MSG_TYPE_DATA = 3
ZIOTC_MSG_TYPE_CTRL = 4
ZIOTC_MSG_TYPE_GPO  = 5

The ZIOTC_MSG_TYPE_DATA type will send the message to the Reader Gateway to output on the Data Interface. The ZIOTC_MSG_TYPE_CTRL type will send the message to the Reader Gateway to output on the Management Event Interface. The ZIOTC_MSG_TYPE_GPO type will send the message to the GPIO-LED module.

Data Analytics Passthru

In addition to processing data from the Radio Control and passing new, or filtered, data through to the Reader Gateway, messages can be passed through the Reader Gateway directly to the Data Analytics script. These messages can be sent to the Reader Gateway via the Management Command/Response interface.

In order to process the passed through messages, a pass through callback must be defined and registered with the ZIOTC object. The pass through callback takes in a single parameter - the passed through message. The function MUST issue a sendResponse with a message. This functionality enables a mechanism to configure the Data Analytics Script. The following code snippet continues the example above and allows for a single command “prefix” to be sent to the script. If only “prefix” is sent, the script returns the current value the script is using to filter tags. If prefix is followed by another string, it will update the “prefix” to the new value.

const onPassthruCallback = (msg) =>{
    try
    {
        if (msg.startsWith("prefix"))
        {
            parts = msg.split(" ")
            if (parts.length >= 2)
            {
                prefix = parts[1]
            }
            ziotc.SendResponse("prefix set to " + prefix);
        }
        else
        {
            ziotc.SendResponse("unrecognized command");
        }
    catch(err)
    {
        console.log(err.message);
    }
};

Management and Control of Reader

DA apps can manage and control the reader using the REST interface described here. Reader bundles the nodejs http module which can be used to call local rest apis.

const http = require('http');
const options = {
    hostname: '127.0.0.1',
    port: 80,
    path: '/cloud/status',
    method: 'GET',
};
const req = http.request(options, (res) => {
    let data = '';
    res.on('data', (chunk) => {
        data += chunk;
    });
    res.on('end', () => {
        console.log('Request successful!');
        console.log('Response data:', data);
    });
});
req.on('error', (error) => {
    console.error('Request failed. Error:', error.message);
});
req.end();

Full NodeJS script

Below is the full script based on the code snippets above. It contains a command line parser. The only accepts a single command, “exit”.

const ziotc = require('ziotc.node');

const onErrorCallback = (msg) => {
    console.log(msg);
};

const onOkCallback = () => {
    console.log("done");
};

var prefix = "00";
const onNewMessageCallback = (msg) => {
    if ( msg["type"] == 0 )
    {
        tag_id_hex = JSON.parse(msg["msg"])["data"]["idHex"]
        if (tag_id_hex.startsWith(prefix))
        {
        msg["type"]=3;
        ziotc.SendNextMsg(msg);
        }
    }
};

const onPassthruCallback = (msg) =>{
    try
    {
    if (msg.startsWith("prefix"))
    {
        parts = msg.split(" ")
        if (parts.length >= 2)
        {
        prefix = parts[1]
        }
        ziotc.SendResponse("prefix set to " + prefix);
    }
    else
        ziotc.SendResponse("unrecognized command");
    }
    catch(err)
    {
    console.log(err.message);
    }
};

ziotc.SetDataSource("CONNECT_FOR_RADIO_DATA");

ziotc.Run(onErrorCallback, onOkCallback, onNewMessageCallback, onPassthruCallback);
console.log("started DA simple application")

function stopZIOTC() {
    console.log(`stopping...`);
    ziotc.Stop();
}

setTimeout(stopZIOTC, 60000); // run for 1 minute