mirror of
https://git.rwth-aachen.de/acs/public/villas/web/
synced 2025-03-09 00:00:01 +01:00
Merge running-simulations and websocket mixin into live-data mixin
This commit is contained in:
parent
f3d10d8340
commit
f2d494a1b0
7 changed files with 180 additions and 250 deletions
166
app/mixins/live-data.js
Normal file
166
app/mixins/live-data.js
Normal file
|
@ -0,0 +1,166 @@
|
|||
/**
|
||||
* File: live-data.js
|
||||
* Author: Markus Grigull <mgrigull@eonerc.rwth-aachen.de>
|
||||
* Date: 06.10.2016
|
||||
* Copyright: 2016, Institute for Automation of Complex Power Systems, EONERC
|
||||
* This file is part of VILLASweb. All Rights Reserved. Proprietary and confidential.
|
||||
* Unauthorized copying of this file, via any medium is strictly prohibited.
|
||||
**********************************************************************************/
|
||||
|
||||
import Ember from 'ember';
|
||||
|
||||
const { service } = Ember.inject;
|
||||
|
||||
export default Ember.Mixin.create({
|
||||
store: service(),
|
||||
sessionUser: service('session-user'),
|
||||
|
||||
INTERVAL: 5000,
|
||||
|
||||
_sockets: [],
|
||||
|
||||
init: function() {
|
||||
this._super();
|
||||
|
||||
// fetch the simulations for the first time
|
||||
this._fetchRunningSimulations();
|
||||
|
||||
// start the polling loop
|
||||
setInterval((function(self) {
|
||||
return function() {
|
||||
self._fetchRunningSimulations();
|
||||
}
|
||||
})(this), this.INTERVAL);
|
||||
},
|
||||
|
||||
_fetchRunningSimulations: function() {
|
||||
// check if the user is logged in
|
||||
if (this.get('sessionUser.user') != null) {
|
||||
// load simulators
|
||||
var self = this;
|
||||
|
||||
this.get('store').findAll('simulator').then(function() {
|
||||
// get all simulations to find all running ones
|
||||
self.get('store').findAll('simulation').then(function(simulations) {
|
||||
simulations.forEach(function(simulation) {
|
||||
// check if the simulation is running
|
||||
if (simulation.get('running')) {
|
||||
// get all models
|
||||
simulation.get('models').forEach(function(model) {
|
||||
self.get('store').findRecord('simulation-model', model.get('id')).then(function(m) {
|
||||
self._addSocket(m);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_addSocket(simulationModel) {
|
||||
// search for existing socket
|
||||
var length = this.get('_sockets').length;
|
||||
|
||||
for (var i = 0; i < length; i++) {
|
||||
if (this.get('_sockets')[i].id === simulationModel.get('id')) {
|
||||
// dont do anything
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// add new socket
|
||||
var socket = new WebSocket('ws://' + simulationModel.get('simulator.endpoint'));
|
||||
socket.binaryType = 'arraybuffer';
|
||||
|
||||
// register callbacks
|
||||
var self = this;
|
||||
|
||||
socket.onopen = function(event) { self._onSocketOpen.apply(self, [event]); };
|
||||
socket.onclose = function(event) { self._onSocketClose.apply(self, [event]); };
|
||||
socket.onmessage = function(event) { self._onSocketMessage.apply(self, [event]); };
|
||||
socket.onerror = function(event) { self._onSocketError.apply(self, [event]); };
|
||||
|
||||
this.get('_sockets').pushObject({ id: simulationModel.get('id'), socket: socket });
|
||||
|
||||
console.log('Socket created for ' + simulationModel.get('name') + ': ws://' + simulationModel.get('simulator.endpoint'));
|
||||
},
|
||||
|
||||
_removeSocket(socket) {
|
||||
var length = this.get('_sockets').length;
|
||||
var i = 0;
|
||||
|
||||
while (i < length) {
|
||||
if (this.get('_sockets')[i].socket === socket) {
|
||||
// remove object from array
|
||||
this.get('_sockets').slice(i, 1);
|
||||
console.log('socket removed');
|
||||
} else {
|
||||
// increase index if no object was removed
|
||||
i++;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_onSocketOpen(/* event */) {
|
||||
Ember.debug('websocket opened');
|
||||
},
|
||||
|
||||
_onSocketClose(event) {
|
||||
if (event.wasClean) {
|
||||
|
||||
} else {
|
||||
Ember.debug('websocket closed: ' + event.code);
|
||||
}
|
||||
|
||||
// remove socket from array
|
||||
this._removeSocket(event.target);
|
||||
},
|
||||
|
||||
_onSocketMessage(event) {
|
||||
// read the message into JSON
|
||||
var message = this._messageToJSON(event.data);
|
||||
|
||||
var simulationData = this.store.peekRecord('simulation-data', message.simulator);
|
||||
if (simulationData != null) {
|
||||
simulationData.set('sequence', message.sequence);
|
||||
simulationData.set('values', message.values);
|
||||
} else {
|
||||
this.store.createRecord('simulation-data', {
|
||||
sequence: message.sequence,
|
||||
values: message.values,
|
||||
id: message.simulator
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_onSocketError(/* event */) {
|
||||
Ember.debug('websocket error');
|
||||
},
|
||||
|
||||
_messageToJSON(blob) {
|
||||
var data = new DataView(blob);
|
||||
|
||||
let OFFSET_ENDIAN = 1;
|
||||
let OFFSET_TYPE = 2;
|
||||
let OFFSET_VERSION = 4;
|
||||
|
||||
var bits = data.getUint8(0);
|
||||
var simulator = data.getUint8(0x01);
|
||||
var endian = (bits >> OFFSET_ENDIAN) & 0x1 ? 0 : 1;
|
||||
var length = data.getUint16(0x02, endian);
|
||||
|
||||
var values = new Float32Array(data.buffer, data.byteOffset + 0x10, length);
|
||||
|
||||
return {
|
||||
endian: endian,
|
||||
version: (bits >> OFFSET_VERSION) & 0xF,
|
||||
type: (bits >> OFFSET_TYPE) & 0x3,
|
||||
length: length,
|
||||
sequence: data.getUint32(0x04, endian),
|
||||
timestamp: data.getUint32(0x08, endian) * 1e3 + data.getUint32(0x0C, endian) * 1e-6,
|
||||
values: values,
|
||||
simulator: simulator
|
||||
};
|
||||
}
|
||||
});
|
|
@ -1,160 +0,0 @@
|
|||
/**
|
||||
* File: websocket-live-stream-mixin.js
|
||||
* Author: Markus Grigull <mgrigull@eonerc.rwth-aachen.de>
|
||||
* Date: 21.07.2016
|
||||
* Copyright: 2016, Institute for Automation of Complex Power Systems, EONERC
|
||||
* This file is part of VILLASweb. All Rights Reserved. Proprietary and confidential.
|
||||
* Unauthorized copying of this file, via any medium is strictly prohibited.
|
||||
**********************************************************************************/
|
||||
|
||||
import Ember from 'ember';
|
||||
import ENV from '../config/environment';
|
||||
|
||||
const { service } = Ember.inject;
|
||||
|
||||
export default Ember.Mixin.create({
|
||||
runningSimulations: service('running-simulations'),
|
||||
store: service(),
|
||||
|
||||
sockets: [],
|
||||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
|
||||
// load simulators
|
||||
var self = this;
|
||||
|
||||
this.store.findAll('simulator').then(function() {
|
||||
// start simulators service
|
||||
self.get('runningSimulations').loadRunningSimulations();
|
||||
});
|
||||
},
|
||||
|
||||
_runningSimulationsChanged: function() {
|
||||
// called each time running simulations did change
|
||||
var self = this;
|
||||
|
||||
this.get('runningSimulations.simulationModels').forEach(function(simulationModel) {
|
||||
//console.log('Model: ' + simulationModel.get('name') + ' (' + simulationModel.get('simulator.name') + ')');
|
||||
|
||||
// get socket for simulation model
|
||||
let modelid = simulationModel.get('id');
|
||||
var socket = self._socketForSimulationModel(modelid);
|
||||
|
||||
// create new socket for simulation model if not running yet
|
||||
if (socket == null) {
|
||||
// try to create new socket
|
||||
socket = new WebSocket('ws://' + simulationModel.get('simulator.endpoint'));
|
||||
console.log('opened ' + simulationModel.get('simulator.endpoint'));
|
||||
|
||||
if (socket != null) {
|
||||
socket.binaryType = 'arraybuffer';
|
||||
|
||||
// register callbacks
|
||||
socket.onopen = function(event) { self.onopen.apply(self, [event]); };
|
||||
socket.onclose = function(event) { self.onclose.apply(self, [event]); };
|
||||
socket.onmessage = function(event) { self.onmessage.apply(self, [event]); };
|
||||
socket.onerror = function(event) { self.onerror.apply(self, [event]); };
|
||||
|
||||
// save socket
|
||||
self._addSocketForSimulationModel(socket, modelid);
|
||||
|
||||
console.log('simulation model \'' + simulationModel.get('name') + '\' started');
|
||||
}
|
||||
}
|
||||
});
|
||||
}.observes('runningSimulations.simulationModels.@each.mod'),
|
||||
|
||||
_socketForSimulationModel(modelid) {
|
||||
this.get('sockets').forEach(function(s) {
|
||||
if (s.id === modelid) {
|
||||
return s.socket;
|
||||
}
|
||||
});
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
_addSocketForSimulationModel(socket, modelid) {
|
||||
// search for existing socket to replace
|
||||
this.get('sockets').forEach(function(s) {
|
||||
if (s.id === modelid) {
|
||||
s.socket = socket;
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
// add new socket
|
||||
this.get('sockets').pushObject({ id: modelid, socket: socket });
|
||||
},
|
||||
|
||||
_removeSocketForSimulationModel(modelid) {
|
||||
var sockets = this.get('sockets');
|
||||
var i = 0;
|
||||
|
||||
while (i < sockets.get('length')) {
|
||||
if (sockets[i].id === modelid) {
|
||||
// remove object from array
|
||||
sockets.slice(i, 1);
|
||||
} else {
|
||||
// only increase index if no object was removed
|
||||
i++;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onopen(/*event*/) {
|
||||
Ember.debug('websocket opened');
|
||||
},
|
||||
|
||||
onclose(event) {
|
||||
Ember.debug('websocket closed: ' + event.code);
|
||||
},
|
||||
|
||||
onmessage(event) {
|
||||
// read the message into JSON
|
||||
var message = this._messageToJSON(event.data);
|
||||
|
||||
var simulationData = this.store.peekRecord('simulation-data', message.simulator);
|
||||
if (simulationData != null) {
|
||||
simulationData.set('sequence', message.sequence);
|
||||
simulationData.set('values', message.values);
|
||||
} else {
|
||||
this.store.createRecord('simulation-data', {
|
||||
sequence: message.sequence,
|
||||
values: message.values,
|
||||
id: message.simulator
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
onerror(/*event*/) {
|
||||
Ember.debug('websocket error');
|
||||
},
|
||||
|
||||
_messageToJSON(blob) {
|
||||
var data = new DataView(blob);
|
||||
|
||||
let OFFSET_ENDIAN = 1;
|
||||
let OFFSET_TYPE = 2;
|
||||
let OFFSET_VERSION = 4;
|
||||
|
||||
var bits = data.getUint8(0);
|
||||
var simulator = data.getUint8(0x01);
|
||||
var endian = (bits >> OFFSET_ENDIAN) & 0x1 ? 0 : 1;
|
||||
var length = data.getUint16(0x02, endian);
|
||||
|
||||
var values = new Float32Array(data.buffer, data.byteOffset + 0x10, length);
|
||||
|
||||
return {
|
||||
endian: endian,
|
||||
version: (bits >> OFFSET_VERSION) & 0xF,
|
||||
type: (bits >> OFFSET_TYPE) & 0x3,
|
||||
length: length,
|
||||
sequence: data.getUint32(0x04, endian),
|
||||
timestamp: data.getUint32(0x08, endian) * 1e3 + data.getUint32(0x0C, endian) * 1e-6,
|
||||
values: values,
|
||||
simulator: simulator
|
||||
};
|
||||
}
|
||||
});
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
import Ember from 'ember';
|
||||
import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin';
|
||||
import WebsocketLiveStreamMixin from '../mixins/websocket-live-stream-mixin';
|
||||
import LiveDataMixin from '../mixins/live-data';
|
||||
|
||||
export default Ember.Route.extend(AuthenticatedRouteMixin, WebsocketLiveStreamMixin, {
|
||||
export default Ember.Route.extend(AuthenticatedRouteMixin, LiveDataMixin, {
|
||||
});
|
||||
|
|
|
@ -1,64 +0,0 @@
|
|||
/**
|
||||
* File: running-simulations.js
|
||||
* Author: Markus Grigull <mgrigull@eonerc.rwth-aachen.de>
|
||||
* Date: 26.07.2016
|
||||
* Copyright: 2016, Institute for Automation of Complex Power Systems, EONERC
|
||||
* This file is part of VILLASweb. All Rights Reserved. Proprietary and confidential.
|
||||
* Unauthorized copying of this file, via any medium is strictly prohibited.
|
||||
**********************************************************************************/
|
||||
|
||||
import Ember from 'ember';
|
||||
|
||||
const {
|
||||
inject: { service }
|
||||
} = Ember;
|
||||
|
||||
export default Ember.Service.extend({
|
||||
session: service('session'),
|
||||
sessionUser: Ember.inject.service('session-user'),
|
||||
store: service(),
|
||||
|
||||
simulationModels: [],
|
||||
|
||||
loadRunningSimulations: function() {
|
||||
var self = this;
|
||||
|
||||
// check for running simulations
|
||||
setInterval(function() {
|
||||
if (self.get('sessionUser.user') != null) {
|
||||
// check if running simulations did changed
|
||||
self.get('store').findAll('simulation').then(function(simulations) {
|
||||
// search for running simulations
|
||||
simulations.forEach(function(simulation) {
|
||||
if (simulation.get('running') === true) {
|
||||
// get all models of the simulation
|
||||
simulation.get('models').forEach(function(model) {
|
||||
self.get('store').findRecord('simulation-model', model.get('id')).then(function(m) {
|
||||
// add to array
|
||||
self._addSimulationModel(m);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// clear all models of the simulation
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}, 3000);
|
||||
},
|
||||
|
||||
_addSimulationModel(simulationModel) {
|
||||
// check if the model is already in the array
|
||||
var models = this.get('simulationModels');
|
||||
var length = models.get('length');
|
||||
|
||||
for (var i = 0; i < length; i++) {
|
||||
if (models[i].get('id') === simulationModel.get('id')) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// not found, so add to the array
|
||||
this.get('simulationModels').pushObject(simulationModel);
|
||||
}
|
||||
});
|
12
tests/unit/mixins/live-data-test.js
Normal file
12
tests/unit/mixins/live-data-test.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
import Ember from 'ember';
|
||||
import LiveDataMixin from 'villasweb-frontend/mixins/live-data';
|
||||
import { module, test } from 'qunit';
|
||||
|
||||
module('Unit | Mixin | live data');
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it works', function(assert) {
|
||||
let LiveDataObject = Ember.Object.extend(LiveDataMixin);
|
||||
let subject = LiveDataObject.create();
|
||||
assert.ok(subject);
|
||||
});
|
|
@ -1,12 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
import WebsocketLiveStreamMixinMixin from 'villasweb-frontend/mixins/websocket-live-stream-mixin';
|
||||
import { module, test } from 'qunit';
|
||||
|
||||
module('Unit | Mixin | websocket live stream mixin');
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it works', function(assert) {
|
||||
let WebsocketLiveStreamMixinObject = Ember.Object.extend(WebsocketLiveStreamMixinMixin);
|
||||
let subject = WebsocketLiveStreamMixinObject.create();
|
||||
assert.ok(subject);
|
||||
});
|
|
@ -1,12 +0,0 @@
|
|||
import { moduleFor, test } from 'ember-qunit';
|
||||
|
||||
moduleFor('service:running-simulation', 'Unit | Service | running simulation', {
|
||||
// Specify the other units that are required for this test.
|
||||
// needs: ['service:foo']
|
||||
});
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it exists', function(assert) {
|
||||
let service = this.subject();
|
||||
assert.ok(service);
|
||||
});
|
Loading…
Add table
Reference in a new issue