James Devine

Dynamic Line Graph with RGraph, and PI Web API

Blog Post created by James Devine on Nov 13, 2019

Over the past several years I have enjoyed a little known high quality open source JavaScript charting library
found at RGraph.net. This particular feature rich, well documented, JavaScript charting library includes functions for AJAX, JSON parsing, lots of design features, and in my experience very good online support. It has lots and lots of demonstration pages to use as templates to get started. There is a full set of tools for <svg>, and another full set of tools for <canvas> HTML elements. There is a lot more to brag about this charting library, but I will leave it up to you to explore it for yourself.

 

This JavaScript charting example utilizes some of the best features of the RGraph.net line graph
tool combined with PI Web API as the data source. It updates every 5 seconds and resizes the number of x-axis values. Although I have purposefully kept this example simple, developers can easily integrate RGraph.net elements with responsive design configurations that will allow the charts to resize for use on various types of mobile devices.

 

When we are done you will have a page that looks something like this:

RGraph.net Beautiful JavaScript Charts for Websites

In this case I downloaded the RGraph.net charting library and stored it in the same directory as the HTML page I will show you how to build. The <script> references in the <head> element of the page will show what I mean.

 

Step 1: Set up the basic <html> page like this:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>PI Web API Dynamic Line Chart PI SQUARE - Filled Color</title>
<script src="charts/libraries/RGraph.svg.common.core.js"></script>
<script src="charts/libraries/RGraph.svg.common.ajax.js"></script>
<script src="charts/libraries/RGraph.svg.common.tooltips.js"></script>
<script src="charts/libraries/RGraph.svg.line.js"></script>
<style>
#mainContent {
margin-left: 2%;
margin-right: 2%
}
</style>

</head>

<body>

<div id="mainContent">
<divstyle="width: 1450px; height: 500px" id="chart-container"></div>
</div>

</body>
</html>

Here I am utilizing the <svg> set of tools. Any of your web pages utilizing RGraph.net will load the '/RGraph.svg.common.core.js'. The '/RGraph.svg.common.ajax.js' library is what gives us the ability to retrieve the data including the JSON parsing capability. The '/RGraph.svg.common.tooltips.js' is the piece that lets us hover over a chart point and get its value in the tool tip popup. Finally the '/RGraph.svg.common.line.js' is the library that gives us functionality to display line graphs (aka trends).

 

In my example I am using "RecordedData" for my "Processor_Total" pi tag. This is a quick look at how the raw data is assembled:

PI Web API JSON recorded data sample

 

The timestamps and values are child key:value pairs of the "Items:[]" array. When we parse this data we need to keep that in mind. The <div> tag with "id=chart-container" is where the chart will be assembled and displayed.

 

Step 2: Capture the URL for your pi tag's recorded data. So inside my <script> tag I am capturing 10 minutes worth of timestamps and values and so the JavaScript variable 'url' looks like this:

<script>
var url = "https://[YOUR_SERVER_NAME_HERE]/piwebapi/streams/[PI_TAG_WEB_ID_HERE]/recorded?starttime=*-10m&endtime=*";

 

Step 3: Utilize the AJAX method to retrieve and parse the JSON data. Inside this anonymous function is where we assemble all the parts the first time the page loads. So after your 'var url = ...' line of code add the following:

RGraph.SVG.AJAX.getJSON(url, function (json)
{
//# PI Values:
var values = [];
for (var i=0,values=[]; i<json.Items.length; i++) {
values.push(json.Items[i].Value);
}

//# PI Timestamps as Labels:
var labels = [];
for (var i=0,labels=[]; i<json.Items.length; i++) {
var currTime = json.Items[i].Timestamp.replace(/Z$/,'');
var d = new Date(currTime);
var tzOffset = d.getTimezoneOffset();
tzOffset = tzOffset/60;
var theHour = d.getHours() - tzOffset;
if(theHour < 10) { theHour = "0" + theHour.toString(); }
var theMinute = d.getMinutes();
if(theMinute < 10) { theMinute = "0" + theMinute.toString(); }
var theSecond = d.getSeconds();
if(theSecond < 10) { theSecond = "0" + theSecond.toString(); }

//# Assemble the newly converted string values of the timestamp:
var newTimestamp = theHour + ":" + theMinute + ":" + theSecond;
labels.push(newTimestamp);
}

In this step we are capturing the group of Items[i].Value and pushing them into the 'values[]' array. We are also capturing the group of Items[i].Timestamp and first converting the timestamp to the local time zone, converting the parts to strings, and pushing those to the 'labels[]' array.

 

Step 4: Assemble the Tool Tips. In this portion of code I went ahead and added the 'UnitsAbbreviation' in case the pi tag you have chosen has that. In my example I don't have that. It utilizes the JavaScript method of 'ToFixed(3)' to cut the number of decimal places to three for the tool tip pop up value.

//# PI Values as Tool Tips:
var toolTips = [];
for (var i=0,toolTips=[]; i<json.Items.length; i++) {
toolTips.push(json.Items[i].Value.toFixed(3).toString() + " " + json.Items[i].UnitsAbbreviation);
}

Again we are pushing these string values into the 'toolTips[]' array.

 

Step 5: Create 'line = new RGraph.SVG.Line()' and draw it in the 'chart-container' <div> element. In the line chart there are three primary parts of id, data, and options. The 'id' is the HTML element with the corresponding id that will host the new line graph. The 'data'  is the values array that will be represented by the tips or tick marks of the line graph. The 'options' are the key:value pairs that define the color, labels, tick marks, title and more of the line chart. You can explore a whole lot more about this at SVG Line charts documentation | RGraph. After you have configured those values you call the 'draw()' method, which is the built in function to render your line graph according to the specifications you have set. Here is the code that is again still inside the RGraph.SVG.AJAX.getJSON(url, function (json) anonymous function:

//# Build the Line Graph:
line = new RGraph.SVG.Line({
id: 'chart-container',
data: values,
options: {
marginLeft: 50,
title: 'Processor_Total PI Point 10 Minute Line Chart',
textSize: 14,
xaxisLabels: labels,
xaxisLabelsAngle: 80,
xaxisLabelsSize: 9,
marginBottom: 160,
yaxis: false,
backgroundGridBorder: false,
backgroundGridVlines: false,
spline: false,
tickmarksStyle: 'filledcircle',
tickmarksSize: 2.5,
tooltips: toolTips,
shadow: true,
filled: true,
filledColors: ['#E6F2FF']
}
}).draw();

 

Step 6: Configure the mouse-out action so the tool tips disappear (I learned this the hard way). The RGraph.net library has a .hideTooltip() method, which allows the tool tip to pop up when you mouse-over a tick mark, and then disappear when you mouse-out from that tick mark. Then after this piece you close out the RGraph.SVG.AJAX.getJSON(url, function (json) anonymous function:

//# Configure the Mouse Out Action so Tool Tips disappear:
line.svg.onmouseout = function(e)
{
RGraph.SVG.hideTooltip();
RGraph.SVG.redraw();
}
});

 

Step 7: Create the 'update()' function.  This function serves to update the 'values[]', 'labels[]', and 'toolTips[]' arrays, and then uses these new values to update the existing line graph properties. Simply stated it clears the existing values, labels, and tool tips and assigns the new array of values to those line graph properties. It then calls the 'RGraph.SVG.redraw()' method to re-draw the line graph. the final piece is it sets up a JavaScript setTimeout() function to call itself every 5 seconds (5000 ms).

function update ()
{
var url = "https://[YOUR_SERVER_NAME_HERE]/piwebapi/streams/[PI_TAG_WEB_ID_HERE]/recorded?starttime=*-10m&endtime=*";
RGraph.SVG.AJAX.getJSON(url, function (json)
{
//# PI Values:
var values = [];
for (var i=0,values=[]; i<json.Items.length; i++) {
values.push(json.Items[i].Value);
}

//# PI Timestamps as Labels:
var labels = [];
for (var i=0,labels=[]; i<json.Items.length; i++) {
var currTime = json.Items[i].Timestamp.replace(/Z$/,'');
var d = new Date(currTime);
var tzOffset = d.getTimezoneOffset();
tzOffset = tzOffset/60;
var theHour = d.getHours() - tzOffset;
if(theHour < 10) { theHour = "0" + theHour.toString(); }
var theMinute = d.getMinutes();
if(theMinute < 10) { theMinute = "0" + theMinute.toString(); }
var theSecond = d.getSeconds();
if(theSecond < 10) { theSecond = "0" + theSecond.toString(); }
var newTimestamp = theHour + ":" + theMinute + ":" + theSecond;
labels.push(newTimestamp);
}

//# PI Values as Tool Tips:
var toolTips = [];
for (var i=0,toolTips=[]; i<json.Items.length; i++) {
toolTips.push(json.Items[i].Value.toFixed(3).toString() + " " + json.Items[i].UnitsAbbreviation);
}

//# Clear existing properties and assign new values:
line.originalData[0] = [];
line.originalData[0] = values;
line.properties.xaxisLabels = [];
line.properties.xaxisLabels = labels;
line.properties.tooltips = [];
line.properties.tooltips = toolTips;

RGraph.SVG.redraw();
});

setTimeout(function ()
{
update()
}, 5000);
}

 

Step 8: call the 'update()' function at the end. When you open the page and the script tag loads for the very first time the first RGraph.SVG.AJAX.getJSON(url, function (json) anonymous function runs. The very next thing is the 'update()' function gets called. Embedded in the 'update()' function is a JavaScript setTimeout() function to call that same 'update()' function every five seconds. And then we close out the original </script> tag:

//# Call the update() function after the first rendering of the line graph:
update();
</script>

 

Step 9: Load it in your browser. I use Chrome or Edge. I must admit I have observed some odd behaviors in some of the other browsers such as FireFox, or Internet Explorer. 

 

For your convenience attached is a copy of the entire HTML file. It does not include the RGraph.net libraries. You need download those separately.

Outcomes