1
0
Fork 0
mirror of https://git.rwth-aachen.de/acs/public/villas/node/ synced 2025-03-09 00:00:00 +01:00
VILLASnode/web/socket/app.js

331 lines
6.7 KiB
JavaScript

/** VILLASnode WebMockup Javascript application.
*
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2014-2020, Institute for Automation of Complex Power Systems, EONERC
* @license GNU General Public License (version 3)
*
* VILLASnode
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
// global variables
var api;
var connection;
var timer;
var plot;
var nodes = [];
var currentNode;
var paused = false;
var sequence = 0;
var plotData = [];
var plotOptions = {
xaxis: {
mode: 'time'
},
legend: {
show: true
},
selection: {
mode: "x"
}
};
var updateRate = 25;
var redrawPlot = true;
var xDelta = 5000;
var xPast = xDelta * 0.9;
var xFuture = xDelta * 0.1;
$(document).ready(function() {
api = new Api('v1', apiConnected);
$('#play').click(function(e, ui) {
connection = wsConnect(currentNode);
paused = false;
});
$('#pause').click(function(e, ui) {
connection.close();
paused = true;
});
$('#timespan').slider({
min : 100,
max : 10000,
value : xDelta,
slide : function(e, ui) {
updatePlotWindow(ui.value);
}
});
$('#updaterate').slider({
min : 1,
max : 100,
value : updateRate,
slide : function(e, ui) {
clearInterval(timer);
timer = setInterval(updatePlot, 1000.0 / updateRate);
updateRate = ui.value;
}
});
$('.inputs #slider').slider({
min : 0,
max : 10000,
slide : sendData
});
$('.inputs-checkboxes input').checkboxradio()
.each(function(idx, elm) {
$(elm).change(sendData);
});
timer = setInterval(updatePlot, 1000.0 / updateRate);
});
$(window).on('beforeunload', function() {
connection.close();
api.close();
});
function sendData()
{
var slider = $('.inputs #slider');
var checkboxes = $('.inputs-checkboxes input');
var data = [ $(slider).slider('value') / 100, 0 ];
for (var i = 0; i < checkboxes.length; i++)
data[1] += (checkboxes[i].checked ? 1 : 0) << i;
var msg = new Msg({
timestamp : Date.now(),
sequence : sequence++,
data : data
});
console.log('Sending message', msg);
connection.send(msg.toArrayBuffer());
}
function apiConnected()
{
api.request('nodes', {},
function(response) {
nodes = response;
console.log("Found " + nodes.length + " nodes:",);
nodes.forEach(function(node) {
console.log(node);
if (node.type == 'websocket' && node.name == getParameterByName('node'))
currentNode = node;
});
if (currentNode === undefined) {
nodes.forEach(function(node) {
if (node.type == 'websocket')
currentNode = node;
});
}
if (currentNode !== undefined) {
updateNodeList();
connection = wsConnect(currentNode);
}
}
);
}
function updateNodeList()
{
$('.node-selector').empty();
nodes.forEach(function(node, index) {
if (node.type == 'websocket') {
$('.node-selector').append(
$('<button>')
.addClass(node.name == currentNode.name ? 'ui-state-active' : '')
.text(node.description ? node.description : node.name)
.click(function() {
var url = node.name;
window.location = '?node=' + node.name;
})
);
}
});
$('.node-selector').buttonset();
}
function updatePlotWindow(delta)
{
xDelta = delta
xPast = xDelta * 0.9;
xFuture = xDelta * 0.1;
}
function updatePlot()
{
var data = [];
if (!redrawPlot)
return;
// add data to arrays
for (var i = 0; i < plotData.length; i++) {
var seriesOptions = nodes;
data[i] = {
data : plotData[i],
shadowSize : 0,
label : 'Index ' + String(i),
lines : {
lineWidth: 2
}
}
if (currentNode.out.signals !== undefined && currentNode.out.signals[i] !== undefined)
$.extend(true, data[i], {
label : currentNode.out.signals[i].name
});
}
var options = {
xaxis: {
min: Date.now() - xPast,
max: Date.now() + xFuture
},
grid: {
markings: [
{ xaxis: { from: Date.now(), to: Date.now() }, color: '#ff0000' }
]
}
}
var placeholder = $('.plot-container div');
/* update plot */
plot = $.plot(placeholder, data, $.extend(true, options, plotOptions));
placeholder.bind("plotselected", function (event, ranges) {
$.each(plot.getXAxes(), function(_, axis) {
var opts = axis.options;
opts.min = ranges.xaxis.from;
opts.max = ranges.xaxis.to;
});
plot.setupGrid();
plot.draw();
plot.clearSelection();
});
redrawPlot = false;
}
function wsConnect(node)
{
var url = wsUrl(node.name);
var conn = new WebSocket(url, 'live');
conn.binaryType = 'arraybuffer';
conn.onopen = function() {
$('#status')
.text('Connected')
.css('color', 'green');
console.log('WebSocket connection established');
};
conn.onclose = function(error) {
console.log('WebSocket connection closed', error);
$('#status')
.text('Disconnected' + (error.reason != '' ? ' (' + error.reason + ')' : ''))
.css('color', 'red');
setTimeout(function() {
wsConnect(currentNode);
}, 1000);
};
conn.onerror = function(error) {
console.log('WebSocket connection error', error);
$('#status').text('Status: Error: ' + error.message);
};
conn.onmessage = function(e) {
var msgs = Msg.fromArrayBufferVector(e.data);
for (var j = 0; j < plotData.length; j++) {
// remove old
while (plotData[j].length > 0 && plotData[j][0][0] < (Date.now() - xPast))
plotData[j].shift()
}
for (var j = 0; j < msgs.length; j++) {
var msg = msgs[j];
console.log('Received message with ' + msg.data.length + ' values from id ' + msg.id + ' with timestamp ' + new Date(msg.timestamp).toString() + ': '+ msg.data);
// add empty arrays for data series
while (plotData.length < msg.length)
plotData.push([]);
// add data to arrays
for (var i = 0; i < msg.length; i++)
plotData[i].push([msg.timestamp, msg.data[i]]);
}
redrawPlot = true;
};
return conn;
};
/* Helpers */
function wsUrl(endpoint)
{
var l = window.location;
var url = '';
if (l.protocol == 'file:') {
url += 'ws://localhost';
}
else {
if (l.protocol === 'https:')
url += 'wss://';
else
url += 'ws://';
url += l.hostname;
}
if ((l.port) && (l.port != 80) && (l.port != 443))
url += ':'+ l.port;
url += '/' + endpoint;
return url;
}
/* Some helpers */