How to use live data?

If you have set up historical data in the IXON Cloud, your IXrouter or IXagent will send PLC data to the IXON Cloud. A live data connection where your machine data will be sent to your PC every half second, can be established using a WebSocket. Data retrieved through this connection will not be stored in the IXON Cloud. Please follow the steps below to establish a WebSocket connection that receives live data using the APIv2.

🚧

Live data cannot be used as a basis for historical data


Live data is a feature used only to display real-time data to end users. It cannot be used as a basis for historical data. Mainly because there is no way to replay the data received if the WebSocket TCP connection is reset or lost, resulting in immediate data loss. Please use the historical data or the MQTT broker feature for this purpose.

Step 1: Retrieve the required identifiers

To get started, you first need to retrieve an application ID, bearer token, company ID and agent ID. These values are necessary for proper authorisation and for the APIv2 to find the correct IXrouter or IXagent. This article explains how to request these values.

Step 2: Create a JWT Token

You need a JWT authorization token to create a live data stream. You can request a JWT Token using the AuthTokenDataList endpoint. You have to include the values you requested in step 1 in the request. You also have to set the expiration time for the token. The expiration time has to be between 1 minute (60 seconds) and 1 hour (3600 seconds). The example below contains the format for the request for a JWT Token and its response:

curl --request POST \
     --url '<<url>>:443/api/auth-tokens/data' \
     --header 'Api-Version: 2' \
     --header "Api-Application: $application_id" \
     --header "Api-Company: $company_id" \
     --header 'Content-Type: application/json' \
     --header "Authorization: Bearer $bearer_token" \
     --data '{
         "expiresIn": 3600,
         "agents": [
             {
                 "publicId": "$agent_id"
             }
         ]
     }'
{
    "status": "success",
    "type": "AuthTokenCreateResponse",
    "data": {
        "publicId": "$token_id",
        "secretId": "$jwt-token",
        "expiresOn": "2021-02-02T01:00:00Z"
    }
}

Step 3: Create a WebSocket entry

The next step is to set up a WebSocket connection to read the live data. You can find the correct URL under the AgentDataRealTimeWebSocket endpoint in the discovery request to <<url>>:443/api/. The WebSocket URL may change in the future, it is therefore recommended to request the URL from the above endpoint, instead of hardcoding it.

Step 4: Authenticate your connection

When setting up live data in your software solution, you have to add a message with the JWT-token you requested in step 2. You have to put an open event listener to prevent that the WebSocket will be closed by the server. You can prevent this by sending the JWT-Token with the onopen event listener. An HTML example of how this live data stream should be handled is provided below.

<!DOCTYPE html>
<head>
 <meta charset="utf-8" />
 <title>Live Data WebSocket Test</title>
 <script type="text/javascript">

 // Fill in the correct URL from the AgentDataRealTimeWebSocket endpoint (see step 3). If you keep
 // using the hardcoded URL below, this can potentially result to breakage in the future.
 const wsUri = "wss://wse-mdr.dkn-core-nl-ams-02.dkn.ayayot.com/agents/{publicIdList}/data-realtime";
 let ws;

 function onOpen(evt) {
   writeToScreen('CONNECTED');
   // Fill in the JWT token retrieved in step 2
   doSend('Authorization: <the JWT-token from step 2>');
 }

 function onClose(evt) {
   writeToScreen('DISCONNECTED');
 }

 function onMessage(evt) {
   writeToScreen(`<span style="color: blue;">RESPONSE:</span> ${evt.data}`);
 }

 function onError(evt) {
   writeToScreen(`<span style="color: red;">ERROR:</span> ${evt.data}`);
 }

 function doSend(message) {
   writeToScreen(`SENT: ${message}`);
   ws.send(message);
 }

 function writeToScreen(message) {
   const pre = document.createElement('pre');
   pre.style.wordWrap = 'break-word';
   pre.innerHTML = message;
   document.getElementById('output').appendChild(pre);
 }

 function openWebSocket() {
   ws = new WebSocket(wsUri);
   ws.addEventListener('open', onOpen);
   ws.addEventListener('error', onError);
   ws.addEventListener('message', onMessage);
   ws.addEventListener('close', onClose);
 }

 window.addEventListener('load', openWebSocket, false);

 </script>
</head>
<body>
 <h2>Live Data WebSocket Test</h2>

 <div id="output"></div>
</body>
</html>

Step 5: Interpret your live data

Live data is being returned in the JSON format. As the output is compressed to ensure the fastest connection possible, the data is a little difficult to interpret. In the output you will find a row that is called tags. The values in this row are variable ID's, which are the key to interpret the data.

To understand the variable ID's, you send a GET request to the AgentDataVariableList endpoint with at least the fields "variableId", "name", "type", "signed" and "width". Your response looks like the example below.

{		
	"type": "AgentDataVariable",
	"data": [
 		{...},
		{
           	"publicId": "$public_id",
           	"variableId": 6,
           	"name": "Weight",
           	"type": "int",
         	"signed": true,
           	"width": "16"
       	},
  		{...}
	],
    "moreAfter": null,
    "status": "success"
}

The variable ID corresponds with the value you can find in the "tags" row of the live data output. The name is the name of the variable to which that ID belongs, so we can see here that variable 6 is the weight. The other 3 fields can be used to determine the data type. In this case the data type is a 16 bit signed integer (int16).

With this information we can search for the weight in the live data output, which is shown in the example below. The first value for a data type is the one with the lowest variable ID, the second value for a data type is the one with the second lowest variable ID et cetera. So if you obtained the Variable IDs, data types and names, you can match the output with the names.

In the output below, variable numbers 6, 7, 11 and 12 are from the data type int16 (as we know from the full output of the example above). This means that variable 6 has value 225 and variable 11 has value 3 in the example below. From the previous request, we know that variable 6 has the name "weight" and thus the measured weight at the given time was 225.

In the whole output with all variable IDs we could also see that variables 8 and 9 are uint16 data types with the names "upperweight" and "underweight". Which are the highest and lowest reported weights at the given moment. Thus we can see that the highest weight was 250 and the lowest 200 in the given time.

{
  "message": {
    "points": [
      {
        "bool": [false],
        "int16": [225,175,3,2],
        "str": [""],
        "tags": [1,2,3,4,5,6,7,8,9,10,11,12,13],
        "time": 1607079466500,
        "uint16": [250,200,86,83,61,86],
        "uint32": [1655898122]
      }
    ],
    "sentTime": 1607079466590
  },
  "agent": {
    "publicId": "$agent_id"
  },
  "device": {
    "publicId": "$source_id"
  }
}

When using two or more data sources

When the AgentDataVariable endpoint is used on a device with two or more data sources, the same variableId can be used by both data sources. In this case, you can add the field source to your query so it also returns the sourcepublicId that the variable belongs to:

curl --request GET \
     --url '<<url>>:443/api/agents/$agent_id/data-variables?fields=variableId,name,source' \
     --header 'Api-Version: 2' \
     --header "Api-Application: $application_id" \
     --header "Api-Company: $company_id" \
     --header 'content-type: application/json' \
     --header "Authorization: Bearer $bearer_token"