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:
parent
0a270c7aad
commit
7ae878ca86
5 changed files with 246 additions and 133 deletions
54
web/socket/api.js
Normal file
54
web/socket/api.js
Normal 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();
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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>© 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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Add table
Reference in a new issue