Real-Time Gauge with ChartJS and Spike-Engine
Presents a real-time gauge implementation that can be used for building dashboards
Introduction
Everything in the web becomes increasingly real-time and HTML5 finally offers some facilities to build efficient, simple and robust real-time applications on the web. This article serves as a demonstration of such facilities in building a gauge, updated in real time 5 times per second which you can use for server monitoring or building dashboards:
- It uses websockets internally, but abstracted by Spike-Engine, will fallback to flash sockets for older browsers.
- It updates the gauges using a publish-subscribe model with JSON formatted packets.
- It uses ChartJS library for rendering the gauges, the gauges are rendered as SVG.
- It is cross-platform and with a minimized packet payload and message compression.
- The application server is a self-hosted executable and the client is just a plain HTML file.

[View a live demo]
Background
While browsing around the internet, I stumbled upon a very nice HTML5 charting library by DevExpress, called ChartJs. They provide a nice declarative API for creating various good looking charts that can be potentially used for visual analytics and dashboards. However, none of their examples seemed to show how to update their charts in a real-time client-server way.
Hence, I decided to create a simple gauge that would monitor packets per second rate on my server, using Spike-Engine as a backend.
Making the Server
Let's look at our server implementation, our server needs to perform several things in order to update the gauges:
- Listen on a particular endpoint (IP + port) and create a publish-subscribe channel for posting our messages to the client.
- Compute packets per second, and publish the value to all subscribed clients 5 times per second (every 200 milliseconds).
The first point is rather easy with Spike-Engine, we need to simply call Service.Listen
method in order to start listening on a specific IPAddress
and a port (called endpoint), or on several endpoints for that matter. This line in the Main
method accomplishes it:
Service.Listen(new TcpBinding(IPAddress.Any, 8002));
Next, we create a new PubHub
instance, provided us by Spike-Engine. This implements publish-subscribe model. In publish-subscribe model senders of messages, called publishers, do not program the messages to be sent directly to specific receivers, called subscribers. Instead, published messages are characterized into classes, without knowledge of what, if any, subscribers there may be. Similarly, subscribers express interest in one or more classes, and only receive messages that are of interest, without knowledge of what, if any, publishers there are [Wiki].
We first create our PubHub
instance and give it a name. The name is important as we will need to provide the same name in our clients when we want to subscribe.
var hub = Spike.Service.Hubs.GetOrCreatePubHub("PacketWatch");
Then we schedule a function to be called every 200 milliseconds, this function will publish messages to the PubHub
.
hub.Schedule(TimeSpan.FromMilliseconds(200), OnTick);
That's it for the point 1. By now, we have a publish subscribe channel (PubHub
) and scheduled execution of a function named OnTick
.
Now, let's look at point 2 as we need to compute packets per second rate. We do this by implementing a simple rolling window periodic sampling and we sample every 200 milliseconds.

This can be done fairly easily by maintaining a Queue
of elements and sampling the delta value (difference) to it. We need a queue as the window needs to be shifted, this is accomplished using a Dequeue()
call when there's more than 5 elements in the queue. It allows us to have a precise rate count, with a 200 millisecond resolution. Here's the implementation of our OnTick
method, called 5 times per second.
private static void OnTick(IHub hub)
{
// Cast is as PubHub
var pubHub = hub as PubHub;
// In this case, we're just taking those values from Spike-Engine itself, but
// you could replace it to get values from elsewhere.
var packetsIn = Monitoring.PacketsIncoming.Value;
var packetsOut = Monitoring.PacketsOutgoing.Value;
// Compute the delta
var packetsDelta = (packetsIn + packetsOut) - PacketCount;
PacketCount = packetsIn + packetsOut;
// Maintain a queue of 5 elements, to match for a second (200 ms * 5 = 1 second)
PacketSampler.Enqueue(packetsDelta);
if (PacketSampler.Count > 5)
PacketSampler.Dequeue();
// Publish the floating sum
pubHub.Publish(PacketSampler.Sum());
}
Making the Client
Now let's have a look at the actual HTML/JavaScript code that accomplishes the following:
- Renders the gauges using
ChartJS
library. - Subscribes to our server and updates the gauges when the server tells it to.
We first include various dependencies: Spike-Engine JavaScript SDK and ChartJS (JQuery, Knockout and Globalize are required by ChartJS).
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="http://ajax.aspnetcdn.com/ajax/knockout/knockout-2.2.1.js"></script>
<script src="http://ajax.aspnetcdn.com/ajax/globalize/0.1.1/globalize.min.js"></script>
<script src="http://cdn3.devexpress.com/jslib/13.1.6/js/dx.chartjs.js"></script>
<script src="js/spike-sdk.min.js"></script>
Once this is done, we can start putting up the layout of the page. Check out the full source of the HTML page to see the code. After that, we need to create the actual Gauges with ChartJS
library, this is fairly easy as you just need to define various ranges and visual features. We also set the animationDuration
to 200, this is needed so our gauge needle won't take too much time to interpolate between updates.
$("#packetGauge1").dxCircularGauge({
scale: {
startValue: 0,
endValue: 100,
majorTick: {
tickInterval: 25
},
label: {
indentFromTick: 8
}
},
margin: {
left: 10,
right: 10,
top: 10,
bottom: 10
},
rangeContainer: {
width: 4,
backgroundColor: 'none',
ranges: [
{
startValue: 0,
endValue: 24,
color: '#A6C567'
},
{
startValue: 26,
endValue: 49,
color: '#A6C567'
},
{
startValue: 51,
endValue: 74,
color: '#A6C567'
},
{
startValue: 76,
endValue: 100,
color: '#FCBB69'
}
]
},
animationDuration: 200,
animationEnabled: true,
needles: [{
offset: 5,
indentFromCenter: 7,
value: 0,
color: '#43474b'
}],
spindle: {
color: '#43474b'
},
});
After that, we need to actually perform the connection to our server, here we used the local server (127.0.0.1), one should use the public IP ideally. This code snippet does few things:
- It first creates a connection to the remote server.
- After that, when the client is connected to the server, it subscribes to
PacketWatch
hub as we named it. - Next, it hooks up
hubEventInform
event which is invoked by Spike-Engine every time a message arrives from ourPubHub
. - Finally, it deserializes the JSON message and updates the gauges.
// When the document is ready, we connect
$(document).ready(function () {
var server = new ServerChannel("127.0.0.1:8002");
// When the browser is connected to the server
server.on('connect', function () {
server.hubSubscribe('PacketWatch', null);
});
// When we got a notification from the server
server.on('hubEventInform', function (p) {
var value = JSON.parse(p.message);
var count = $('#packetCounter');
count.text(value);
var gauge1 = $('#packetGauge1').dxCircularGauge('instance');
gauge1.needleValue(0, value);
var gauge2 = $('#packetGauge2').dxCircularGauge('instance');
gauge2.needleValue(0, value);
});
});
That's it, I hope you like the article. I am looking forward to your remarks or suggestions on how to improve it!
Server Code
Below, for completeness is the full server implementation:
class Program
{
/// <summary>
/// Entry point to our console application.
/// </summary>
static void Main(string[] args)
{
// Start listening on the port 8002
Service.Listen(
new TcpBinding(IPAddress.Any, 8002)
);
}
/// <summary>
/// This function will be automatically invoked when the service starts
/// listening and accepting clients.
/// </summary>
[InvokeAt(InvokeAtType.Initialize)]
public static void Initialize()
{
// We create a PubHub which acts as publish-subscribe channel. This allows us to publish
// simple string messages and remote clients can subscribe to the publish notifications.
var hub = Spike.Service.Hubs.GetOrCreatePubHub("PacketWatch");
// We schedule the OnTick() function to be executed every 200 milliseconds.
hub.Schedule(TimeSpan.FromMilliseconds(200), OnTick);
}
/// <summary>
/// Last packet count.
/// </summary>
private static long PacketCount = 0;
/// <summary>
/// A queue to hold our packets. We need this to calculate a floating sum.
/// </summary>
private static Queue<long> PacketSampler
= new Queue<long>();
/// <summary>
/// Occurs when our timer ticks.
/// </summary>
private static void OnTick(IHub hub)
{
// Cast is as PubHub
var pubHub = hub as PubHub;
// In this case, we're just taking those values from Spike-Engine itself, but
// you could replace it to get values from elsewhere.
var packetsIn = Monitoring.PacketsIncoming.Value;
var packetsOut = Monitoring.PacketsOutgoing.Value;
// Compute the delta
var packetsDelta = (packetsIn + packetsOut) - PacketCount;
PacketCount = packetsIn + packetsOut;
// Maintain a queue of 5 elements, to match for a second (200 ms * 5 = 1 second)
PacketSampler.Enqueue(packetsDelta);
if (PacketSampler.Count > 5)
PacketSampler.Dequeue();
// Publish the floating sum
pubHub.Publish(PacketSampler.Sum());
}
}
Client Code
The full client code can be viewed in the attached archive. Alternatively, I've provided a live demo for this article.
History
- 23/6/2015 - Source code & article updated to Spike v3
- 9/7/2013 - Initial article
- 9/8/2013 - Updated the code & article formatting