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

updated WebSocket mockup for new API and smaller improvements

This commit is contained in:
Steffen Vogel 2017-04-07 17:24:51 +02:00
parent 0a270c7aad
commit 7ae878ca86
5 changed files with 246 additions and 133 deletions

54
web/socket/api.js Normal file
View file

@ -0,0 +1,54 @@
function Api(v, connected, error)
{
this.version = v
this.ids = new Object();
var url = wsUrl('v1');
var self = this;
this.connection = new WebSocket(url, 'api');
this.connection.onopen = function() {
console.log('API connected', this.url);
connected();
};
this.connection.onerror = function(e) {
console.log('API request failed:', e);
error(e);
};
this.connection.onmessage = function(e) {
var resp = JSON.parse(e.data);
var handler;
console.log('API response received', resp);
handler = self.ids[resp.id];
if (handler !== undefined) {
handler(resp.response);
delete self.ids[resp.id];
}
};
}
Api.prototype.request = function(action, request, handler)
{
var req = {
action : action,
request: request,
id : guid()
};
this.ids[req.id] = handler;
console.log('API request sent', req);
this.connection.send(JSON.stringify(req))
}
Api.prototype.close = function()
{
this.connection.close();
}

View file

@ -1,11 +1,13 @@
// global variables
var api;
var connection;
var timer;
var seq = 0;
var nodes = [];
var currentNode;
var nodes = [ ];
var paused = false;
var sequence = 0;
var plotData = [];
var plotOptions = {
@ -17,96 +19,145 @@ var plotOptions = {
}
};
var xDelta = 0.5*1000;
var xPast = xDelta*0.9;
var xFuture = xDelta*0.1;
var updateRate = 25;
var redrawPlot = true;
$(document).on('ready', function() {
$.getJSON('/nodes.json', function(data) {
nodes = data;
for (var i = 0; i < nodes.length; i++)
if (nodes[i].name == getParameterByName("node"))
currentNode = nodes[i];
if (currentNode === undefined)
currentNode = nodes[0];
nodes.forEach(function(node, index) {
$(".node-selector").append(
$("<li>").append(
$("<a>", {
text: node.description ? node.description : node.name,
title: node.name,
href: "?node=" + node.name
})
).addClass(node.name == currentNode.name ? 'active' : '')
);
});
wsConnect(wsUrl(currentNode.name), ["live"]);
});
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) {
wsConnect(wsUrl(currentNode.name), ["live"]);
connection = wsConnect(currentNode);
paused = false;
});
$('#pause').click(function(e, ui) {
connection.close();
});
$('#slider').slider({
min : 0,
max : 100,
slide : function(e, ui) {
var msg = new Msg({
timestamp : Date.now(),
sequence : seq++
}, [ ui.value ]);
var blob = msg.toArrayBuffer()
connection.send(blob);
}
paused = true;
});
$('#timespan').slider({
min : 200,
min : 1000,
max : 10000,
value : xDelta,
slide : function(e, ui) {
plotUpdateWindow(ui.value);
updatePlotWindow(ui.value);
}
});
$('#controls .buttons button').each(function(button) {
$(button).addClass('on');
$(button).onClick(function(value) {
var msg = new Msg({
timestamp : Date.now(),
sequence : seq++
}, [ value ]);
connection.send(msg.toArrayBuffer());
});
$('#updaterate').slider({
min : 1,
max : 50,
value : updateRate,
slide : function(e, ui) {
clearInterval(timer);
timer = setInterval(updatePlot, 1000.0 / updateRate);
updateRate = ui.value;
}
});
$('.inputs #slider').slider({
min : 0,
max : 100,
slide : sendData
});
plotUpdateWindow(10*1000); /* start plot refresh timer for 10sec window */
$('.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 plotUpdateWindow(delta) {
xDelta = delta
xPast = xDelta*0.9;
xFuture = xDelta*0.1;
function sendData()
{
var slider = $('.inputs #slider');
var checkboxes = $('.inputs-checkboxes input');
var data = [ $(slider).slider('value'), 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++,
id : currentNode.id,
data : data
});
console.log('Sending message', msg);
connection.send(msg.toArrayBuffer());
}
function plotUpdate() {
function apiConnected()
{
api.request('nodes', {},
function(response) {
nodes = response;
console.log("Found " + nodes.length + " nodes:", nodes);
for (var i = 0; i < nodes.length; i++)
if (nodes[i].name == getParameterByName('node'))
currentNode = nodes[i];
if (currentNode === undefined)
currentNode = nodes[0];
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++) {
@ -115,7 +166,7 @@ function plotUpdate() {
data[i] = {
data : plotData[i],
shadowSize : 0,
label : "Index " + String(i),
label : 'Index ' + String(i),
lines : {
lineWidth: 2
}
@ -132,46 +183,54 @@ function plotUpdate() {
},
grid: {
markings: [
{ xaxis: { from: Date.now(), to: Date.now() }, color: "#ff0000" }
{ xaxis: { from: Date.now(), to: Date.now() }, color: '#ff0000' }
]
}
}
/* update plot */
$.plot('.plot-container div', data, $.extend(true, options, plotOptions));
redrawPlot = false;
}
function wsConnect(url, protocol) {
connection = new WebSocket(url, protocol);
connection.binaryType = 'arraybuffer';
function wsConnect(node)
{
var url = wsUrl('');
var conn = new WebSocket(url, 'live');
conn.binaryType = 'arraybuffer';
connection.onopen = function() {
$('#connectionStatus')
conn.onopen = function() {
$('#status')
.text('Connected')
.css('color', 'green');
timer = setInterval(plotUpdate, 1000.0 / 25);
console.log('WebSocket connection established');
};
connection.onclose = function() {
$('#connectionStatus')
.text('Disconnected')
conn.onclose = function(error) {
console.log('WebSocket connection closed', error);
$('#status')
.text('Disconnected (' + error.reason + ')')
.css('color', 'red');
clearInterval(timer);
/* Try connect if close reason was CLOSE_NORMAL */
if (error.code == 1000 || error.code == 1001) {
setTimeout(function() {
wsConnect(currentNode);
}, 1000);
}
};
conn.onerror = function(error) {
console.log('WebSocket connection error', error);
setTimeout(function() {
wsConnect(wsUrl(currentNode.name), ["live"]);
}, 1000); // retry
$('#status').text('Status: Error: ' + error.message);
};
connection.onerror = function(error) {
$('#connectionStatus').text(function() {
return 'Status: Error: ' + error.message;
});
};
connection.onmessage = function(e) {
conn.onmessage = function(e) {
var msgs = Msg.fromArrayBufferVector(e.data);
console.log('Received ' + msgs.length + ' messages with ' + msgs[0].data.length + ' values from id ' + msgs[0].id + ' with timestamp ' + msgs[0].timestamp);
@ -184,6 +243,9 @@ function wsConnect(url, protocol) {
for (var j = 0; j < msgs.length; j++) {
var msg = msgs[j];
if (msg.id != currentNode.id)
continue;
// add empty arrays for data series
while (plotData.length < msg.length)
@ -193,25 +255,30 @@ function wsConnect(url, protocol) {
for (var i = 0; i < msg.length; i++)
plotData[i].push([msg.timestamp, msg.data[i]]);
}
};
redrawPlot = true;
};
return conn;
};
/* Helpers */
function wsUrl(endpoint) {
function wsUrl(endpoint)
{
var l = window.location;
var url = "";
var url = '';
if (l.protocol === "https:")
url += "wss://";
if (l.protocol === 'https:')
url += 'wss://';
else
url += "ws://";
url += 'ws://';
url += l.hostname;
if ((l.port) && (l.port != 80) && (l.port != 443))
url += ":" + l.port;
url += ':'+ l.port;
url += "/" + endpoint;
url += '/' + endpoint;
return url;
}

View file

@ -14,6 +14,7 @@
<script src="msg.js"></script>
<script src="app.js"></script>
<script src="utils.js"></script>
<script src="api.js"></script>
</head>
<body>
<div id="wrapper">
@ -22,7 +23,7 @@
<h1>VILLASweb Mockup</h1>
</div>
<div id="container">
<ul class="node-selector"></ul>
<div class="node-selector"></div>
<div class="plot-container">
<div id="placeholder" class="plot-placeholder"></div>
@ -32,21 +33,21 @@
<div class="left">
<div class="inputs">
<dl>
<dt><label>Buttons</label></dt>
<dd>
<button>1</button>
<button>2</button>
<button>3</button>
<button>4</button>
<button>5</button>
<dt><label>Switches</label></dt>
<dd class="inputs-checkboxes">
<label for="checkbox-1">1</label>
<input type="checkbox" name="checkbox-1" id="checkbox-1">
<label for="checkbox-2">2</label>
<input type="checkbox" name="checkbox-2" id="checkbox-2">
<label for="checkbox-3">3</label>
<input type="checkbox" name="checkbox-3" id="checkbox-3">
<label for="checkbox-4">4</label>
<input type="checkbox" name="checkbox-4" id="checkbox-4">
</dd>
<dt><label for="slider">Slider</label></dt>
<dd><div id="slider"></div></dd>
<dt><label for="text">Text</label></dt>
<dd><input type="text" id="text" /></dd>
<dt><label for="file">File</label></dt>
<dd><input type="file" onchange="fileStart(event)" /></dd>
</dl>
@ -57,6 +58,9 @@
<dl>
<dt><label for="timespan">Plot timespan</label></dt>
<dd><div id="timespan"></div></dd>
<dt><label for="updaterate">Plot update rate</label></dt>
<dd><div id="updaterate"></div></dd>
</dl>
</div>
@ -66,7 +70,7 @@
<dd>
<button id="pause">Pause</button>
<button id="play">Play</button>
<span id="connectionStatus"></span>
<span id="status"></span>
</dd>
</dl>
</div>
@ -75,13 +79,11 @@
</div>
</div>
<div id="footer">
<div class="left">
<p>Copyright 2016: Institute for Automation of Complex Power Systems, </br>EON Energy Research Center, RWTH Aachen University, Germany</p>
<p>Authors: <a href="mailto:stvogel@eonerc.rwth-aachen.de">Steffen Vogel</a>, <a href="mailto:mgrigul@eonerc.rwth-aachen.de">Markus Grigul</a></p>
</div>
<div class="right">
<p><a style="font-size: 1.5em" href="/doc/index.html" style="float: right">Documentation</a></p>
</div>
<p>&copy; 2017 Institute for Automation of Complex Power Systems<br>
EON Energy Research Center<br>
RWTH Aachen University<br>
</p>
<p>Authors: <a href="mailto:stvogel@eonerc.rwth-aachen.de">Steffen Vogel</a>, <a href="mailto:mgrigul@eonerc.rwth-aachen.de">Markus Grigul</a></p>
</div>
</div>
</body>

View file

@ -11,7 +11,7 @@
*********************************************************************************/
/* Class for parsing and printing a message */
function Msg(c, d)
function Msg(c)
{
this.sequence = typeof c.sequence === 'undefined' ? 0 : c.sequence;
this.length = typeof c.length === 'undefined' ? 0 : c.length;
@ -21,9 +21,9 @@ function Msg(c, d)
this.id = typeof c.id === 'undefined' ? -1 : c.id;
this.timestamp = typeof c.timestamp === 'undefined' ? Date.now() : c.timestamp;
if (Array.isArray(d)) {
this.length = d.length;
this.data = d
if (Array.isArray(c.data)) {
this.length = c.data.length;
this.data = c.data;
}
}

View file

@ -54,18 +54,8 @@ a {
height: 100%;
}
ul.node-selector {
padding: 0;
}
.node-selector li {
display: inline;
padding: 0.5em;
}
.node-selector li.active {
font-weight: bold;
background-color: #FFC7C7;
.node-selector {
margin-bottom: 1em;
}
.off {