From f2d494a1b0fbe2e529758540bffb6972cea31345 Mon Sep 17 00:00:00 2001 From: Markus Grigull Date: Fri, 7 Oct 2016 10:21:32 +0200 Subject: [PATCH] Merge running-simulations and websocket mixin into live-data mixin --- app/mixins/live-data.js | 166 ++++++++++++++++++ app/mixins/websocket-live-stream-mixin.js | 160 ----------------- app/routes/index.js | 4 +- app/services/running-simulations.js | 64 ------- tests/unit/mixins/live-data-test.js | 12 ++ .../websocket-live-stream-mixin-test.js | 12 -- .../unit/services/running-simulation-test.js | 12 -- 7 files changed, 180 insertions(+), 250 deletions(-) create mode 100644 app/mixins/live-data.js delete mode 100644 app/mixins/websocket-live-stream-mixin.js delete mode 100644 app/services/running-simulations.js create mode 100644 tests/unit/mixins/live-data-test.js delete mode 100644 tests/unit/mixins/websocket-live-stream-mixin-test.js delete mode 100644 tests/unit/services/running-simulation-test.js diff --git a/app/mixins/live-data.js b/app/mixins/live-data.js new file mode 100644 index 0000000..4f28247 --- /dev/null +++ b/app/mixins/live-data.js @@ -0,0 +1,166 @@ +/** + * File: live-data.js + * Author: Markus Grigull + * 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 + }; + } +}); diff --git a/app/mixins/websocket-live-stream-mixin.js b/app/mixins/websocket-live-stream-mixin.js deleted file mode 100644 index 72d2200..0000000 --- a/app/mixins/websocket-live-stream-mixin.js +++ /dev/null @@ -1,160 +0,0 @@ -/** - * File: websocket-live-stream-mixin.js - * Author: Markus Grigull - * 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 - }; - } -}); diff --git a/app/routes/index.js b/app/routes/index.js index ab4ab0a..ca13931 100644 --- a/app/routes/index.js +++ b/app/routes/index.js @@ -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, { }); diff --git a/app/services/running-simulations.js b/app/services/running-simulations.js deleted file mode 100644 index 8aef65c..0000000 --- a/app/services/running-simulations.js +++ /dev/null @@ -1,64 +0,0 @@ -/** - * File: running-simulations.js - * Author: Markus Grigull - * 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); - } -}); diff --git a/tests/unit/mixins/live-data-test.js b/tests/unit/mixins/live-data-test.js new file mode 100644 index 0000000..804897f --- /dev/null +++ b/tests/unit/mixins/live-data-test.js @@ -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); +}); diff --git a/tests/unit/mixins/websocket-live-stream-mixin-test.js b/tests/unit/mixins/websocket-live-stream-mixin-test.js deleted file mode 100644 index 7cc9580..0000000 --- a/tests/unit/mixins/websocket-live-stream-mixin-test.js +++ /dev/null @@ -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); -}); diff --git a/tests/unit/services/running-simulation-test.js b/tests/unit/services/running-simulation-test.js deleted file mode 100644 index f74cde8..0000000 --- a/tests/unit/services/running-simulation-test.js +++ /dev/null @@ -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); -});