Show telemetry in NodeJS using SocketIO and HighCharts

In my previous blog, I showed you how to host NodeJS in a Docker Image.

Today we will learn how we show telemetry in NodeJS. The message will arrive as a string on an HTML page using SocketIO and we will put it on a chart from HighCharts.

This is a great example of how we can represent raw data in something useful, something end user will understand.

We will extend our previous example. In that example, we were leaning on NodeJS and we have the Express web framework running to show an HTML page. We added SocketIO so users of the index.html can exchange messages.

But what if the incoming message is

{
  "machine":{
    "temperature":21.810507137356563
  },
  "timeCreated":"2018-01-06T23:55:56.1058921Z"
}

a JSON message on a single line? And it is shown as a string?

Nice, but this is only good for nerds like me.

What if we could represent it as:

a chart?

This a much better solution, isn’t it?

This is the last part of a series of blogs:

  1. Deploying a NodeJS server with SocketIO to Docker using Azure IoT Edge
  2. Show telemetry in NodeJS using SocketIO and HighCharts
  3. Visualizing Azure IoT Edge using local dashboard

Let see how this can be done.

To do this we need to alter a few things:

  1. We need to add a Chart library
  2. We have to reference the Chart library in our javascript
  3. We need to show a Chart in our HTML
  4. We have to put the incoming messages on the chart

Adding HighCharts to our Website

If we look at this small tutorial, we learn we can use a NodeJS package for our HighCharts library reference. How convenient as we are running a website in Node JS using NPM.

At the Dos-prompt, we only have to execute

npm install --save highcharts

And if we look in the node_modules, we see that the HighCharts library is added there:

Reference the Chart library in our javascript

To reference the libraries in the JavasScript, it is tempting to reference the node_modules location. This is wrong! You do not want to expose that location.

You can hide that location easily using some transformations in index.js:

var express = require('express'); // Get the module
var app = express(); // Create express by calling the prototype in var express

var http = require('http').Server(app);
var io = require('socket.io')(http);

app.get('/', function(req, res){
  res.sendFile(__dirname + '/index.html');
});

io.on('connection', function(socket){
  console.log('a user connected');

  socket.on('disconnect', function(){
    console.log('user disconnected');
  });

  socket.on('chat message', function(msg){
    io.emit('chat message', msg);
    console.log('message: ' + msg);
  });
});

// Do not expose internal modules path to your websites. Transform!
app.use('/scripts', express.static(__dirname + '/node_modules/highcharts/'));
app.use('/scripts1', express.static(__dirname + '/node_modules/highcharts/themes/'));

http.listen(80, function(){
  console.log('listening on *:80');
});

We want to expose both the charting library and a HightCharts theme (the black theme with the gradient looks nice, doesn’t it?).

The trick is in the transformation of internal paths to the logical “/scripts” and “/scripts1”. And for that, we need that reference to “express” itself.

And if we look at the “index.html”, we add the usage of these folders:

...




...

Yes, we can now safely reference “/scripts/” and “/scripts1/” as we normally do in generic HTML code.

Note: You will have to restart the NodeJS app to make these changes in NodeJS active…

The index.html does not show any changes yet. Let’s change that.

Show a Chart in our HTML

A chart is not a basic HTML element. We need a placeholder element which will be decorated as a chart using JavaScript.

Add a DIV as the first element of your body HTML and name it “charts-1”:

...</pre>
<div id="charts-1"></div>
<pre>...

And add the following piece of JavaScript to decorate the chart:

...
$(function () {
  var chartLine = Highcharts.chart('charts-1', {
                    chart: {
                      type: 'spline',
                      animation: Highcharts.svg, // don't animate in old IE
                      marginRight: 10,
                      events: { }
                    },
                    title: { text: 'Temperature emulation (utc)' },
                    xAxis: {
                      type: 'datetime', tickPixelInterval: 200
                    },
                    yAxis: {
                      title: { text: 'Combined scale' },
                      plotLines: [{
                        value: 0.5, width: 1, color: '#808080'
                      },
                      {
                        value: 0, width: 1, color: '#808080'
                      }]
                    },
                    tooltip: {
                      formatter: function () {
                                   return '<b>' + this.series.name + '</b>
' +
                                     Highcharts.dateFormat('%Y-%m-%d %H:%M:%S', this.x) + '
' +
                                     Highcharts.numberFormat(this.y, 2);
                      }
                    },
                    legend: { enabled: true },
                    exporting: { enabled: false },
                    series: [
                    {
                      name: 'Temperature',
                      data: (function () {
                               // generate an array of random data
                               var data = [], time = (new Date()).getTime(), i;
                               for (i = -12; i <= 0; i += 1) {
                                 data.push({
                                   x: time + i * 300000,
                                   y: 30
                                 });
                               }
                               return data;
                             }())
                    }]
                  });

Did you restart the app already? If you save this code in index.html and refresh the current page, you will see something like this:

Note: the HighChart chart is shown in UTC time.

Put the incoming messages on the chart

We arrive at the last part of the update.

Change the call-back function which prints the message received.

...
socket.on('chat message', function(msg){
  $('#messages').append($('</pre>
<ul>
 	<li>').text(msg));
  var json = $.parseJSON(msg);
  var timestamp = (new Date(json.timeCreated)).getTime();

  var seriesModule1 = chartLine.series[0];
  var x = timestamp, // UTC time
      y = json.machine.temperature;
  seriesModule1.addPoint([x, y], true, true);
})
...

This method now parses the incoming message and tries to read the temperature. And the (UTC) time of the message is retrieved too.

These values are then added to the chart as a new point on the line.

Testing incoming messages

Save the index.html and reload the page.

Now send some messages, line for line, like:

{"machine":{"temperature":34.974917383620014},"timeCreated":"2018-04-15T20:26:01.1283746Z"}
{"machine":{"temperature":24.974917383620014},"timeCreated":"2018-04-15T20:27:01.1283746Z"}
{"machine":{"temperature":31.974917383620014},"timeCreated":"2018-04-15T20:28:01.1283746Z"}
...

Note: Change the date time value to your current date and time. Remember to save the time in UTC format!

Then you will see something like:

Conclusion

In four steps we have changed a SocketIO chat application into a live dashboard for telemetry.

Please check out other features of HighCharts or combine this example with your own Charting libraries.

In my next blog, we will check out how we can use this in the Azure IoT Edge.

Advertenties

Reacties zijn gesloten.