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

Add simulation-model to simulator relationship

Add simulation to projects
Add running dialog to simulations
Remove running dialog from simulators (simulator should be auto-detected)
Change running-simulation to simulation-models based
This commit is contained in:
Markus Grigull 2016-10-06 16:56:08 +02:00
parent 35b885f19b
commit f3d10d8340
17 changed files with 361 additions and 112 deletions

View file

@ -49,7 +49,7 @@ export default Ember.Controller.extend({
submitNew() {
// verify properties
var properties = this.getProperties('name');
if (properties['name'] == null || properties['name'] == "") {
if (properties['name'] == null || properties['name'] === "") {
this.set('errorMessage', 'Visualization name is missing');
return;
}
@ -79,7 +79,7 @@ export default Ember.Controller.extend({
submitEdit() {
// verify properties
var properties = this.getProperties('name');
if (properties['name'] == null || properties['name'] == "") {
if (properties['name'] == null || properties['name'] === "") {
this.set('errorMessage', 'Visualization name is missing');
return;
}

View file

@ -19,6 +19,14 @@ export default Ember.Controller.extend({
errorMessage: null,
project: null,
projectSimulation: null,
_updateSimulations: function() {
if (this.get('model.simulations') != null && this.get('model.simulations.length') > 0) {
var simulations = this.get('model.simulations');
this.set('projectSimulation', simulations.toArray()[0]);
}
}.observes('model'),
actions: {
showNewModal() {
@ -35,6 +43,7 @@ export default Ember.Controller.extend({
this.set('errorMessage', null);
this.set('project', project);
this.set('name', project.get('name'));
this.set('projectSimulation', project.get('simulation'));
// show the dialog
this.set('isShowingEditModal', true);
@ -51,17 +60,24 @@ export default Ember.Controller.extend({
submitNew() {
// verify properties
var properties = this.getProperties('name');
if (properties['name'] == null || properties['name'] == "") {
if (properties['name'] == null || properties['name'] === "") {
this.set('errorMessage', 'Project name is missing');
return;
}
// set owner properties
// set owner property
var user = this.get('sessionUser.user');
properties['owner'] = user;
// set simulation property
properties['simulation'] = this.get('projectSimulation.id');
// create new project
var project = this.store.createRecord('project', properties);
// this change will not be saved, but it is nessecary otherwise ember will omit the simulation's id in the post request
this.get('projectSimulation.projects').pushObject(project);
var controller = this;
project.save().then(function() {
@ -78,14 +94,22 @@ export default Ember.Controller.extend({
submitEdit() {
// verify properties
var properties = this.getProperties('name');
if (properties['name'] == null || properties['name'] == "") {
if (properties['name'] == null || properties['name'] === "") {
this.set('errorMessage', 'Project name is missing');
return;
}
// remove from old simulation
// save properties
properties['simulation'] = this.get('projectSimulation.id');
this.get('project').setProperties(properties);
// this change will not be saved, but it is nessecary otherwise ember will omit the simulation's id in the post request
this.get('projectSimulation.projects').pushObject(this.get('project'));
var controller = this;
this.get('project').save().then(function() {
@ -110,6 +134,18 @@ export default Ember.Controller.extend({
cancelDelete() {
this.set('isShowingDeleteModal', false);
},
selectSimulation(simulationName) {
// get simulation by name
var simulations = this.get('model.simulations');
var controller = this;
simulations.forEach(function(simulation) {
if (simulation.get('name') === simulationName) {
controller.set('projectSimulation', simulation);
}
});
}
}
});

View file

@ -43,11 +43,11 @@ export default Ember.Controller.extend({
this.set('name', simulationModel.get('name'));
var simulators = this.get('model.simulators');
var simulatorId = simulationModel.get('simulator');
var simulatorId = simulationModel.get('simulator.id');
var simulatorName = null;
simulators.forEach(function(simulator) {
if (simulator.get('simulatorid') == simulatorId) {
if (simulator.get('id') === simulatorId) {
simulatorName = simulator.get('name');
}
});
@ -69,33 +69,25 @@ export default Ember.Controller.extend({
submitNew() {
// verify properties
var properties = this.getProperties('name');
if (properties['name'] == null || properties['name'] == "") {
if (properties['name'] == null || properties['name'] === "") {
this.set('errorMessage', 'Simulation model name is missing');
return;
}
// set simuatlion properties
var simulation = this.get('model.simulation');
properties['simulation'] = simulation.get('id');;
properties['simulation'] = simulation;
// get the simulator id by simulator name
var simulators = this.get('model.simulators');
var simulatorId = null;
var simulatorName = this.get('simulatorName');
simulators.forEach(function(simulator) {
if (simulator.get('name') === simulatorName) {
simulatorId = simulator.get('simulatorid');
properties['simulator'] = simulator;
}
});
if (simulatorId == null) {
Ember.debug('Unable to find simulator by name');
return;
}
properties['simulator'] = simulatorId;
// create new model
var simulationModel = this.store.createRecord('simulation-model', properties);
@ -118,14 +110,14 @@ export default Ember.Controller.extend({
submitEdit() {
// verify properties
var properties = this.getProperties('name');
if (properties['name'] == null || properties['name'] == "") {
if (properties['name'] == null || properties['name'] === "") {
this.set('errorMessage', 'Simulation model name is missing');
return;
}
// set simuatlion properties
var simulation = this.get('model.simulation');
properties['simulation'] = simulation.get('id');;
properties['simulation'] = simulation.get('id');
// get the simulator id by simulator name
var simulators = this.get('model.simulators');

View file

@ -14,11 +14,12 @@ export default Ember.Controller.extend({
isShowingNewModal: false,
isShowingEditModal: false,
isShowingEditModal: false,
isShowingDeleteModal: false,
errorMessage: null,
simulation: null,
simulationRunning: true,
actions: {
showNewModal() {
@ -48,10 +49,19 @@ export default Ember.Controller.extend({
this.set('isShowingDeleteModal', true);
},
showRunningModal(simulation) {
// set properties
this.set('simulation', simulation);
this.set('simulationRunning', simulation.get('running'));
// show the dialog
this.set('isShowingRunningModal', true);
},
submitNew() {
// verify properties
var properties = this.getProperties('name');
if (properties['name'] == null || properties['name'] == "") {
if (properties['name'] == null || properties['name'] === "") {
this.set('errorMessage', 'Simulation name is missing');
return;
}
@ -78,7 +88,7 @@ export default Ember.Controller.extend({
submitEdit() {
// verify properties
var properties = this.getProperties('name');
if (properties['name'] == null || properties['name'] == "") {
if (properties['name'] == null || properties['name'] === "") {
this.set('errorMessage', 'Simulation name is missing');
return;
}
@ -110,6 +120,34 @@ export default Ember.Controller.extend({
cancelDelete() {
this.set('isShowingDeleteModal', false);
},
confirmRunningSimulation() {
// set the property
var simulation = this.get('simulation');
simulation.set('running', this.get('simulationRunning'));
// save property
var controller = this;
simulation.save().then(function() {
controller.set('isShowingRunningModal', false);
}, function() {
Ember.debug('Error saving running simulation');
});
},
cancelRunningSimulation() {
this.set('isShowingRunningModal', false);
},
selectRunning(running) {
// NOTE: running is a string and not a boolean value
if (running === 'true') {
this.set('simulationRunning', true);
} else {
this.set('simulationRunning', false);
}
}
}
});

View file

@ -13,12 +13,14 @@ export default Ember.Controller.extend({
isShowingNewModal: false,
isShowingDeleteModal: false,
isShowingEditModal: false,
isShowingRunningModal: false,
simulatorid: 1,
errorMessage: null,
simulator: null,
simulatorName: null,
simulatorEdit: null,
simulatorRunning: true,
actions: {
showNewModal() {
@ -49,6 +51,15 @@ export default Ember.Controller.extend({
this.set('isShowingEditModal', true);
},
showRunningModal(simulator) {
// set properties
this.set('simulator', simulator);
this.set('simulatorRunning', simulator.get('running'));
// show the dialog
this.set('isShowingRunningModal', true);
},
newSimulator() {
// verify properties
var properties = this.getProperties('name', 'simulatorid', 'endpoint');
@ -114,6 +125,34 @@ export default Ember.Controller.extend({
cancelEditSimulator() {
this.set('isShowingEditModal', false);
},
confirmRunningSimulation() {
// set the property
var simulator = this.get('simulator');
simulator.set('running', this.get('simulatorRunning'));
// save property
var controller = this;
simulator.save().then(function() {
controller.set('isShowingRunningModal', false);
}, function() {
Ember.debug('Error saving running simulator');
});
},
cancelRunningSimulation() {
this.set('isShowingRunningModal', false);
},
selectRunning(running) {
// NOTE: running is a string and not a boolean value
if (running === 'true') {
this.set('simulatorRunning', true);
} else {
this.set('simulatorRunning', false);
}
}
}
});

View file

@ -13,43 +13,95 @@ import ENV from '../config/environment';
const { service } = Ember.inject;
export default Ember.Mixin.create({
host: 'ws://' + ENV.APP.LIVE_HOST,
namespace: '',
runningSimulation: service('running-simulation'),
runningSimulations: service('running-simulations'),
store: service(),
socket: null,
sockets: [],
init() {
this._super(...arguments);
// start simulation service
this.get('runningSimulation').loadRunningSimulation();
// load simulators
var self = this;
this.store.findAll('simulator').then(function() {
// start simulators service
self.get('runningSimulations').loadRunningSimulations();
});
},
_runningSimulationChanged: function() {
// called each time running simulation did change
var simulation = this.get('runningSimulation.simulation');
if (simulation !== null) {
if (this.socket === null) {
// create new socket connection
this.socket = new WebSocket(this.host + this.namespace);
this.socket.binaryType = 'arraybuffer';
_runningSimulationsChanged: function() {
// called each time running simulations did change
var self = this;
// register callbacks
var self = this;
this.socket.onopen = function(event) { self.onopen.apply(self, [event]); };
this.socket.onclose = function(event) { self.onclose.apply(self, [event]); };
this.socket.onmessage = function(event) { self.onmessage.apply(self, [event]); };
this.socket.onerror = function(event) { self.onerror.apply(self, [event]); };
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');
}
}
} else {
// stop stream if still opened
if (this.socket !== null) {
this.socket.close();
this.socket = null;
});
}.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++;
}
}
}.observes('runningSimulation.simulation'),
},
onopen(/*event*/) {
Ember.debug('websocket opened');

View file

@ -13,7 +13,7 @@ import { belongsTo/*, hasMany*/ } from 'ember-data/relationships';
export default Model.extend({
name: attr('string'),
simulator: attr('number'),
simulator: belongsTo('simulator', { async: true }),
length: attr('number'),
mapping: attr('array'),
simulation: belongsTo('simulation', { async: true })

View file

@ -14,8 +14,12 @@ export default Ember.Route.extend(AuthenticatedRouteMixin, {
sessionUser: Ember.inject.service('session-user'),
model() {
// get projects for current user
// get projects for current user, simulations are needed for the simulation picker
var user = this.get('sessionUser.user');
return user.get('projects');
return Ember.RSVP.hash({
projects: user.get('projects'),
simulations: this.store.findAll('simulation')
});
}
});

View file

@ -12,6 +12,9 @@ import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-rout
export default Ember.Route.extend(AuthenticatedRouteMixin, {
model(params) {
return this.store.findRecord('visualization', params.visualizationid);
return Ember.RSVP.hash({
/*simulation: this.store.findRecord('simulation', params.simulationid),*/
visualization: this.store.findRecord('visualization', params.visualizationid)
});
}
});

View file

@ -1,42 +0,0 @@
/**
* File: running-simulation.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'),
store: service(),
loadRunningSimulation: function() {
var self = this;
// check every second for running simulation
/*
setInterval(function() {
// check if running simulation did changed
self.get('store').findAll('simulation').then(function(simulations) {
var newSimulation = null;
simulations.forEach(function(simulation) {
if (simulation.get('running') === true) {
newSimulation = simulation;
}
});
if (newSimulation !== self.get('simulation')) {
self.set('simulation', newSimulation);
}
});
}, 1000);*/
}
});

View file

@ -0,0 +1,64 @@
/**
* 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);
}
});

View file

@ -4,13 +4,17 @@
<table class="table-full-width">
<tr>
<th>Name</th>
<th>Simulation</th>
<th width="100px"></th>
</tr>
{{#each model as |project|}}
{{#each model.projects as |project|}}
<tr>
<td>
{{#link-to "project.index" project.id}}{{project.name}}{{/link-to}}
</td>
<td>
{{project.simulation.name}}
</td>
<td>
<div class="projects-row-controls">
<a href="" {{action 'showEditModal' project}}>Edit</a>
@ -40,6 +44,18 @@
{{input id='name' placeholder='Enter project name' value=name}}
</td>
</tr>
<tr>
<td>
<label for="simulation">Simulation</label>
</td>
<td>
<select onchange={{action "selectSimulation" value="target.value"}}>
{{#each model.simulations as |simulation|}}
<option value={{simulation.name}}>{{simulation.name}}</option>
{{/each}}
</select>
</td>
</tr>
<tr>
<td colspan="2">
<button {{action 'cancelNew'}}>Cancel</button>
@ -69,6 +85,18 @@
{{input id='name' placeholder='Enter project name' value=name}}
</td>
</tr>
<tr>
<td>
<label for="simulation">Simulation</label>
</td>
<td>
<select onchange={{action "selectSimulation" value="target.value"}}>
{{#each model.simulations as |simulation|}}
<option value={{simulation.name}} selected={{eq simulation.name projectSimulation.name}}>{{simulation.name}}</option>
{{/each}}
</select>
</td>
</tr>
<tr>
<td colspan="2">
<button {{action 'cancelEdit'}}>Cancel</button>

View file

@ -6,7 +6,7 @@
<table class="table-full-width">
<tr>
<th>Name</th>
<th width="80px" class="column-center">Simulator</th>
<th>Simulator</th>
<th width="100px"></th>
</tr>
{{#each model.simulation.models as |simulationModel|}}
@ -14,8 +14,8 @@
<td>
{{#link-to "simulation-model.index" simulationModel.id}}{{simulationModel.name}}{{/link-to}}
</td>
<td class="column-center">
{{simulationModel.simulator}}
<td>
{{simulationModel.simulator.name}}
</td>
<td>
<div class="simulation-index-row-controls">

View file

@ -5,7 +5,7 @@
<tr>
<th>Name</th>
<th width="80px" class="column-center">Running</th>
<th width="100px"></th>
<th width="140px"></th>
</tr>
{{#each model as |simulation|}}
<tr>
@ -17,8 +17,7 @@
</td>
<td>
<div class="simulations-row-controls">
<!-- {{#link-to "simulation.edit" simulation.id}}Edit{{/link-to}} -->
<!-- {{#link-to "simulation.delete" simulation.id}}Delete{{/link-to}} -->
<a href="" {{action 'showRunningModal' simulation}}>Running</a>
<a href="" {{action 'showEditModal' simulation}}>Edit</a>
<a href="" {{action 'showDeleteModal' simulation}}>Delete</a>
</div>
@ -100,3 +99,21 @@
<button {{action 'confirmDelete'}}>Delete</button>
{{/modal-dialog}}
{{/if}}
{{#if isShowingRunningModal}}
{{#modal-dialog attachment="middle center" translucentOverlay=true}}
<h1>Simulation running</h1>
{{simulation.name}}:
<select onchange={{action "selectRunning" value="target.value"}}>
<option value=true selected={{eq simulationRunning true}}>running</option>
<option value=false selected={{eq simulationRunning false}}>not running</option>
</select>
<br />
<button {{action 'cancelRunningSimulation'}}>Cancel</button>
<button {{action 'confirmRunningSimulation'}}>Save</button>
{{/modal-dialog}}
{{/if}}

View file

@ -7,12 +7,12 @@
<th width="80px" class="column-center">ID</th>
<th width="80px" class="column-center">Running</th>
<th width="120px" class="column-center">Endpoint</th>
<!-- <th width="140px"></th> -->
<th width="100px"></th>
</tr>
{{#each model as |simulator|}}
<tr>
<td>
<!-- {{#link-to "simulator.index" simulator.id}}{{simulator.name}}{{/link-to}} -->
{{simulator.name}}
</td>
<td>
@ -26,8 +26,7 @@
</td>
<td>
<div class="simulators-row-controls">
<!-- {{#link-to "simulator.edit" simulator.id}}Edit{{/link-to}} -->
<!-- {{#link-to "simulator.delete" simulator.id}}Delete{{/link-to}} -->
<!-- <a href="" {{action 'showRunningModal' simulator}}>Running</a> -->
<a href="" {{action 'showEditModal' simulator}}>Edit</a>
<a href="" {{action 'showDeleteModal' simulator}}>Delete</a>
</div>
@ -141,3 +140,21 @@
{{/if}}
{{/modal-dialog}}
{{/if}}
{{#if isShowingRunningModal}}
{{#modal-dialog attachment="middle center" translucentOverlay=true}}
<h1>Simulator running</h1>
{{simulator.name}}:
<select onchange={{action "selectRunning" value="target.value"}}>
<option value=true selected={{eq simulatorRunning true}}>running</option>
<option value=false selected={{eq simulatorRunning false}}>not running</option>
</select>
<br />
<button {{action 'cancelRunningSimulator'}}>Cancel</button>
<button {{action 'confirmRunningSimulator'}}>Save</button>
{{/modal-dialog}}
{{/if}}

View file

@ -1,8 +1,9 @@
<h1>{{model.name}}</h1>
{{#link-to 'project.index' project.id}}Back to {{model.project.name}}{{/link-to}}
{{plot-container plots=model.plots}}
<h1>{{model.visualization.name}}</h1>
{{plot-container plots=model.visualization.plots}}
<p>
{{#link-to "visualization.edit" model.id}}Edit visualization{{/link-to}}
{{#link-to "visualization.delete" model.id}}Delete visualization{{/link-to}}
{{#link-to "visualization.edit" model.visualization.id}}Edit layout{{/link-to}}
</p>

View file

@ -3,10 +3,10 @@
- Don't log out on unauthorized access (admin level lower than required)
- Move plot attributes/styling from plot-container into actual plots
- Move drag-n-drop to mixins
- Websocket node is working in develop branch
- Add API host to config/environment.js
- Empty visualization after delete
- Go into edit mode if visualization is empty
- Auto-detect if simulators are running
- Remove running socket if it's not in the updated list
- Real relationship between simulation-model and simulator
websocketserverip/config.json
websocketserverip/nodes.json