Commit f56fbaee authored by Charl's avatar Charl
Browse files

added awit flot toolkit lib

parent 10d44ce0
Project Leader
--------------
Nigel Kukard <nkukard@lbsd.net>
Charl Mert <cmert@lbsd.net>
This diff is collapsed.
/*
* Functions used for FLOT charts
* Copyright (c) 2013-2014, AllWorldIT
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
*
*/
// Function to format thousands with , and add Kbps
function awit_flot_format_bandwidth(value, axis) {
return awit_flot_format_thousands(value,axis) + ' Kbps';
}
// Function to format thousands
function awit_flot_format_thousands(value, axis) {
// Convert number to string
value = value.toString();
// Match on 3 digits
var R = new RegExp('(-?[0-9]+)([0-9]{3})');
while(R.test(value)) {
// Replace market with ,
value = value.replace(R, '$1,$2');
}
return value;
}
// Function draw a graph
function awit_flot_draw_graph(options) {
// Setting up the graph here
var baseOptions = {
series: {
lines: {
show: true,
lineWidth: 1,
fill: true,
fillColor: {
colors: [
{ opacity: 0.1 },
{ opacity: 0.13 }
]
}
},
points: {
show: false,
lineWidth: 2,
radius: 3
},
shadowSize: 0,
stack: true
},
grid: {
hoverable: true,
clickable: false,
tickColor: "#f9f9f9",
borderWidth: 1
},
legend: {
labelBoxBorderColor: "#aaa"
},
xaxes: [
{
mode: "time",
tickSize: [2, "second"],
tickLength: 10,
tickFormatter: function (v, axis) {
var date = new Date(v);
if (date.getSeconds() % 2 == 0) {
var hours = date.getHours() < 10 ? "0" + date.getHours() : date.getHours();
var minutes = date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes();
var seconds = date.getSeconds() < 10 ? "0" + date.getSeconds() : date.getSeconds();
return hours + ":" + minutes + ":" + seconds;
} else {
return "";
}
}
}
],
yaxes: [
{
min: 0,
tickFormatter: awit_flot_format_bandwidth
}
]
}
// Add additional yaxes if needed
if (options && options.yaxes && options.yaxes.length) {
for (k = 0; k < options.yaxes.length; k++) {
if (options.yaxes[k]) {
baseOptions.yaxes.push(options.yaxes[k]);
}
}
}
function processStatsData(statsData) {
for (i = 0; (i < statsData.length); i++) {
// Format time to match javascript's epoch in milliseconds
for (j = 0; j < statsData[i].data.length; j++) {
d = new Date(statsData[i].data[j][0] * 1000);
statsData[i].data[j][0] = statsData[i].data[j][0] * 1000;
}
// Loop with yaxes
for (k = 0; k < baseOptions.yaxes.length; k++) {
// Check if there are labels
if (baseOptions.yaxes[k].labels && baseOptions.yaxes[k].labels.length) {
// Loop through labels
for (l = 0; l < baseOptions.yaxes[k].labels.length; l++) {
// Check for match
if (statsData[i].label == baseOptions.yaxes[k].labels[l]) {
statsData[i].yaxis = k + 1;
}
}
}
}
}
return statsData;
}
if (typeof(baseOptions) != 'undefined' && typeof(baseOptions.websocket) != 'undefined' && typeof(baseOptions.websocket.uri) != 'undefined') {
plot = jQuery.plot(jQuery("#" + options.id), [], baseOptions);
} else {
baseOptions.websocket = options.websocket;
baseOptions.websocket.preProcessData = function(statsData) {
for (i = 0; (i < statsData.length); i++) {
// Format time to match javascript's epoch in milliseconds
statsData[i].data[0] = statsData[i].data[0] * 1000;
// Loop with yaxes
for (k = 0; k < baseOptions.yaxes.length; k++) {
// Check if there are labels
if (baseOptions.yaxes[k].labels && baseOptions.yaxes[k].labels.length) {
// Loop through labels
for (l = 0; l < baseOptions.yaxes[k].labels.length; l++) {
// Check for match
if (statsData[i].label == baseOptions.yaxes[k].labels[l]) {
statsData[i].yaxis = k + 1;
}
}
}
}
}
return statsData;
}
if (typeof(options) != 'undefined' && typeof(options.url) != 'undefined') {
// Load data from ajax
jQuery.ajax({
url: options.url,
dataType: 'json',
success: function(statsData) {
plot = null;
statsData = processStatsData(statsData);
if (statsData.length > 0) {
plot = jQuery.plot(jQuery("#" + options.id), statsData, baseOptions);
}
}
});
} else {
statsData = {}
plot = jQuery.plot(jQuery("#" + options.id), statsData, baseOptions);
}
}
}
// vim: ts=4
/*
* Copyright (c) 2013-2014, AllWorldIT
* Description: This is a flot plugin which allows
* any flot graph to easily create a connection
* to a websocket server and consume the data
* by specifying the websocket configuration
* as an option.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
*/
(function ($) {
var options = {
debug :true
};
var awitFlotWebsocket = null;
var buffer = [];
var chartData = [];
var gcount = 0;
if (typeof(plots) == 'undefined') {
plots = {};
}
if (typeof(plotOptions) == 'undefined') {
plotOptions = {};
}
if (typeof(chartData) == 'undefined') {
chartData = {};
}
function log_error(message)
{
if (typeof(console.error) != 'undefined' && options.debug) {
console.error(message);
}
}
function log_info(message)
{
if (typeof(console.info) != 'undefined' && options.debug) {
console.info(message);
}
}
function log(message)
{
if (typeof(console.log) != 'undefined' && options.debug) {
console.log(message);
}
}
// Expecting column data format: [yyyy-mm-dd hh-mm-ss, value]
// Converting timestamps
function addColumn(column, options, id)
{
// Creating dataset
var changed = [];
var statsData = {};
for (i = 0; (i < column.length); i++) {
if (typeof(column[i].data[0]) != 'undefined' && typeof(column[i].data[1]) != 'undefined') {
key = column[i].label;
timestamp = column[i].data[0];
value = column[i].data[1];
if (typeof(statsData[key]) == 'undefined') {
// Creating new field
statsData[key] = {
label: key,
data: [[timestamp, value]]
};
// adding rest of properties
for (var prop in column[i]) {
if (column[i].hasOwnProperty(prop)) {
eval('statsData[key].' + prop + ' = ' + 'column[i].' + prop);
}
}
} else {
// Adding existing data to end of array
statsData[key] = {
label: key,
data: [[timestamp, value]]
};
// adding rest of properties
for (var prop in column[i]) {
if (column[i].hasOwnProperty(prop)) {
eval('statsData[key].' + prop + ' = ' + 'column[i].' + prop);
}
}
// Appending timestamp, value to existing data
columnData = statsData[key];
columnData.data.push([timestamp, value]);
statsData[key] = columnData;
}
changed.push(statsData[key]);
} else {
log_error('Bad data structure');
}
}
if (typeof(chartData[id]) == 'undefined') {
chartData[id] = [];
}
if (chartData[id].length > 0) {
// charData array elements are in the same order as column elements so only 1 loop
for (i = 0; (i < column.length); i++) {
found = false;
for (j = 0; (j < chartData[id].length); j++) {
if (chartData[id][j].label == column[i].label) {
// Trimming extra data off the end of the main chartData
if (chartData[id][i].data.length >= options.websocket.maxTicksX) {
chartData[id][i].data.shift();
}
dataArr = [column[i].data[0], column[i].data[1]];
chartData[id][i].data.push(dataArr);
found = true;
}
}
if (!found) {
// Adding element at index
if (typeof(column[i].data[0]) != 'undefined' && typeof(column[i].data[1]) != 'undefined') {
key = column[i].label;
timestamp = column[i].data[0][0];
value = column[i].data[0][1];
chartData[id][i] = {
label: key,
data: [[timestamp, value]]
};
} else {
log_error('Bad data structure');
}
}
}
} else {
chartData[id] = changed;
}
return chartData[id];
}
// update the chart to reflect the new column
function updateChart(plot, options, column) {
id = jQuery(plot.getPlaceholder()).attr('id');
// add column to chartData
if (typeof(options.websocket) != 'undefined' && typeof(options.websocket.history) != 'undefined' && options.websocket.history) {
addColumn(column, options, id);
} else {
chartData[id] = column;
}
//TODO: find out why multiple shared series only works with the following code:
// i.e. 'Total Limits' enabled in the label array for more than one graph
//-----------------------------------------------
testCol = chartData[id];
// debugging weird time issue
for (i = 0; (i < testCol.length); i++) {
if (testCol[i].label == 'Total Limits') {
testCol[i].data[testCol[i].data.length - 3][0];
}
if (testCol[i].label == 'lended') {
testCol[i].data[testCol[i].data.length - 3][0];
}
}
//-----------------------------------------------
if (plot) {
jQuery.plot(plot.getPlaceholder(), chartData[id], options);
} else {
log_error('Plot object not initialized');
}
}
// connect websocket
function connectWebsocket(uri) {
if (awitFlotWebsocket != null) {
return awitFlotWebsocket;
}
try {
log('Connecting...: ' + uri);
awitFlotWebsocket = window['MozWebSocket'] ? new MozWebSocket(uri) : new WebSocket(uri);
return awitFlotWebsocket;
} catch (e) {
log_error('Sorry, the web socket at ' + uri + ' is un-available (' + e + ')', uri, e);
}
}
function preProcessData(statsData) {
return statsData;
}
function filterData(column, options) {
// console.log(options.websocket.labels);
retColumn = [];
// Loop with columns
for (k = 0; k < column.length; k++) {
// Check if there are labels
if (options.websocket.labels && options.websocket.labels.length) {
// Loop through labels
for (l = 0; l < options.websocket.labels.length; l++) {
// Check for match
if (column[k].label == options.websocket.labels[l]) {
retColumn[retColumn.length] = column[k];
}
}
}
}
return retColumn;
}
function addPlotToController(plot, options) {
plots[jQuery(plot.getPlaceholder()).attr('id')] = plot;
plotOptions[jQuery(plot.getPlaceholder()).attr('id')] = options;
if (typeof(options.websocket) != 'undefined' && typeof(options.websocket.preProcessData) == 'function') {
preProcessData = options.websocket.preProcessData;
}
if (typeof(options.websocket) != 'undefined') {
if (options.websocket.enabled == false) {
return false;
}
if (typeof(options.websocket.maxTicksX) == 'undefined') {
options.websocket.maxTicksX = 20;
}
uri = options.websocket.uri;
if (awitFlotWebsocket == null) {
awitFlotWebsocket = connectWebsocket(uri);
}
// When the connection is open, send some data to the server
awitFlotWebsocket.onopen = function () {
log('Sending Hello ... ');
log('Socket State: ['+awitFlotWebsocket.readyState+']');
if (awitFlotWebsocket && awitFlotWebsocket.readyState == 1) {
awitFlotWebsocket.send('Ping'); // Send the message to the server
} else {
log_error("Couldn't send Ping!");
}
};
// Log errors
awitFlotWebsocket.onerror = function (error) {
log('WebSocket Error:');
log_error(error);
};
// log messages from the server
awitFlotWebsocket.onmessage = function (e) {
try {
column = JSON.parse(e.data);
column = preProcessData(column);
// looping through all subscribed charts
$.each(plots, function(key, plot) {
if (typeof(plotOptions[key].websocket.labels) != 'undefined' && plotOptions[key].websocket.labels.length > 0) {
filteredColumn = filterData(column, plotOptions[key]);
} else {
filteredColumn = column;
}
updateChart(plots[key], plotOptions[key], filteredColumn);
})
gcount++;
// debug
//if (gcount > 4) {
// socket.close();
//}
//*/
} catch (e) {
log('Exception: ' + e);
log(e);
}
};
} else {
log('Websocket options not specified');
}
}
function init(plot) {
plot.hooks.processOptions.push(function (plot, options) {
addPlotToController(plot, options);
});
}
$.plot.plugins.push({
init: init,
options: options,
name: 'websockets',
version: '1.0'
});
})(jQuery);
// vim: ts=4
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment