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

Rename plots to widgets

Add widgetData to prepare for future widgets
This commit is contained in:
Markus Grigull 2016-11-02 18:32:24 +01:00
parent c41c7a568e
commit 170f00c40a
25 changed files with 222 additions and 155 deletions

View file

@ -1,5 +1,5 @@
/**
* File: plot-abstract.js
* File: widget-abstract.js
* Author: Markus Grigull <mgrigull@eonerc.rwth-aachen.de>
* Date: 15.07.2016
* Copyright: 2016, Institute for Automation of Complex Power Systems, EONERC
@ -13,10 +13,10 @@ import Draggable from '../mixins/draggable';
export default Ember.Component.extend(Resizable, Draggable, {
tagName: 'div',
classNames: [ 'plotAbstract' ],
classNames: [ 'widgetAbstract' ],
attributeBindings: [ 'style' ],
plot: null,
widget: null,
editing: false,
grid: false,
data: null,
@ -30,20 +30,20 @@ export default Ember.Component.extend(Resizable, Draggable, {
grid_drag: [ 10, 10 ],
scroll_drag: true,
style: Ember.computed('plot', function() {
return Ember.String.htmlSafe('width: ' + this.get('plot.width') + 'px; height: ' + this.get('plot.height') + 'px; left: ' + this.get('plot.x') + 'px; top: ' + this.get('plot.y') + 'px;');
style: Ember.computed('widget', function() {
return Ember.String.htmlSafe('width: ' + this.get('widget.width') + 'px; height: ' + this.get('widget.height') + 'px; left: ' + this.get('widget.x') + 'px; top: ' + this.get('widget.y') + 'px;');
}),
name: Ember.computed('plot', function() {
return this.get('plot.name');
name: Ember.computed('widget', function() {
return this.get('widget.name');
}),
stop_resize(event, ui) {
var width = ui.size.width;
var height = ui.size.height;
this.set('plot.width', width);
this.set('plot.height', height);
this.set('widget.width', width);
this.set('widget.height', height);
},
resize_resize(/* event, ui */) {
@ -51,8 +51,8 @@ export default Ember.Component.extend(Resizable, Draggable, {
},
stop_drag(event, ui) {
this.set('plot.x', ui.position.left);
this.set('plot.y', ui.position.top);
this.set('widget.x', ui.position.left);
this.set('widget.y', ui.position.top);
},
drag_drag(/* event, ui */) {

View file

@ -1,5 +1,5 @@
/**
* File: plot-chart.js
* File: widget-chart.js
* Author: Markus Grigull <mgrigull@eonerc.rwth-aachen.de>
* Date: 28.06.2016
* Copyright: 2016, Institute for Automation of Complex Power Systems, EONERC

View file

@ -1,5 +1,5 @@
/**
* File: plot-container.js
* File: widget-container.js
* Author: Markus Grigull <mgrigull@eonerc.rwth-aachen.de>
* Date: 05.07.2016
* Copyright: 2016, Institute for Automation of Complex Power Systems, EONERC
@ -11,15 +11,15 @@ import Ember from 'ember';
export default Ember.Component.extend({
tagName: 'div',
classNames: [ 'plots' ],
classNames: [ 'widgets' ],
attributeBindings: [ 'style' ],
plots: null,
widgets: null,
editing: false,
grid: true,
data: null,
style: Ember.computed('plots.@each.height', 'plots.@each.y', function() {
style: Ember.computed('widgets.@each.height', 'widgets.@each.y', function() {
var height = this._calculateHeight();
if (this.get('editing') === true && height < 400) {
height = 400;
@ -28,18 +28,14 @@ export default Ember.Component.extend({
return Ember.String.htmlSafe('height: ' + height + 'px;');
}),
_value: Ember.computed('data.2.values.@each', function() {
console.log(this.get('data'));
}),
_calculateHeight() {
var maxHeight = 0;
var plots = this.get('plots');
var widgets = this.get('widgets');
plots.forEach(function(plot) {
var plotHeight = plot.get('y') + plot.get('height');
if (plotHeight > maxHeight) {
maxHeight = plotHeight;
widgets.forEach(function(widget) {
var widgetHeight = widget.get('y') + widget.get('height');
if (widgetHeight > maxHeight) {
maxHeight = widgetHeight;
}
});
@ -50,8 +46,8 @@ export default Ember.Component.extend({
},
actions: {
showPlotDialog(plot) {
this.sendAction('showPlotDialog', plot);
showWidgetDialog(widget) {
this.sendAction('showWidgetDialog', widget);
}
}
});

View file

@ -1,5 +1,5 @@
/**
* File: plot-table.js
* File: widget-table.js
* Author: Markus Grigull <mgrigull@eonerc.rwth-aachen.de>
* Date: 05.07.2016
* Copyright: 2016, Institute for Automation of Complex Power Systems, EONERC
@ -7,11 +7,18 @@
* Unauthorized copying of this file, via any medium is strictly prohibited.
**********************************************************************************/
import PlotAbstract from './plot-abstract';
import WidgetAbstract from './widget-abstract';
export default PlotAbstract.extend({
classNames: [ 'plotTable' ],
export default WidgetAbstract.extend({
classNames: [ 'widgetTable' ],
minWidth_resize: 200,
minHeight_resize: 60
minHeight_resize: 60,
_updateDataObserver: Ember.on('init', Ember.observer('widget.simulator', 'widget.signal', function() {
let query = 'data.' + this.get('widget.simulator') + '.sequence';
this.addObserver(query, function() {
// get values from array
});
}))
});

View file

@ -1,5 +1,5 @@
/**
* File: plot-value.js
* File: widget-value.js
* Author: Markus Grigull <mgrigull@eonerc.rwth-aachen.de>
* Date: 04.07.2016
* Copyright: 2016, Institute for Automation of Complex Power Systems, EONERC
@ -7,22 +7,22 @@
* Unauthorized copying of this file, via any medium is strictly prohibited.
**********************************************************************************/
import PlotAbstract from './plot-abstract';
import WidgetAbstract from './widget-abstract';
import Ember from 'ember';
export default PlotAbstract.extend({
classNames: [ 'plotValue' ],
export default WidgetAbstract.extend({
classNames: [ 'widgetValue' ],
minWidth_resize: 50,
minHeight_resize: 20,
_updateDataObserver: Ember.on('init', Ember.observer('plot.simulator', 'plot.signal', function() {
let query = 'data.' + this.get('plot.simulator') + '.sequence';
_updateDataObserver: Ember.on('init', Ember.observer('widget.widgetData.simulator', 'widget.widgetData.signal', function() {
let query = 'data.' + this.get('widget.widgetData.simulator') + '.sequence';
this.addObserver(query, function() {
// get value from array
let values = this.get('data.' + this.get('plot.simulator') + '.values');
let values = this.get('data.' + this.get('widget.widgetData.simulator') + '.values');
if (values) {
this.set('value', values[this.get('plot.signal')]);
this.set('value', values[this.get('widget.widgetData.signal')]);
} else {
this.set('value', null);
}
@ -32,13 +32,13 @@ export default PlotAbstract.extend({
doubleClick() {
if (this.get('editing') === true) {
// prepare modal
this.set('name', this.get('plot.name'));
this.set('name', this.get('widget.name'));
// get signal mapping for simulation model
let self = this;
let simulatorid = this.get('plot.simulator');
let simulatorid = this.get('widget.widgetData.simulator');
this.get('plot.visualization').then((visualization) => {
this.get('widget.visualization').then((visualization) => {
visualization.get('project').then((project) => {
project.get('simulation').then((simulation) => {
simulation.get('models').then((simulationModels) => {
@ -52,7 +52,7 @@ export default PlotAbstract.extend({
// set signal
let mapping = simulationModel.get('mapping');
self.set('signalName', mapping[self.get('plot.signal')]);
self.set('signalName', mapping[self.get('widget.widgetData.signal')]);
}
});
});
@ -70,8 +70,9 @@ export default PlotAbstract.extend({
submitModal() {
// verify properties
let properties = this.getProperties('name');
if (properties['name'] === null || properties['name'] === "") {
this.set('errorMessage', 'Plot name is missing');
this.set('errorMessage', 'Widget name is missing');
return;
}
@ -79,7 +80,7 @@ export default PlotAbstract.extend({
let simulationModelName = this.get('simulationModelName');
let self = this;
this.get('plot.visualization').then((visualization) => {
this.get('widget.visualization').then((visualization) => {
visualization.get('project').then((project) => {
project.get('simulation').then((simulation) => {
simulation.get('models').then((simulationModels) => {
@ -88,7 +89,8 @@ export default PlotAbstract.extend({
if (simulationModel.get('name') === simulationModelName) {
simulationModel.get('simulator').then((simulator) => {
// set simulator
properties['simulator'] = simulator.get('simulatorid');
let widgetData = {};
widgetData.simulator = simulator.get('simulatorid');
// set signal by name
let mapping = simulationModel.get('mapping');
@ -96,14 +98,16 @@ export default PlotAbstract.extend({
for (let i = 0; i < mapping.length; i++) {
if (mapping[i] === signalName) {
properties['signal'] = i;
widgetData.signal = i;
}
}
// save properties
self.get('plot').setProperties(properties);
properties['widgetData'] = widgetData;
self.get('plot').save().then(function() {
self.get('widget').setProperties(properties);
self.get('widget').save().then(function() {
self.set('isShowingModal', false);
});
});
@ -126,7 +130,7 @@ export default PlotAbstract.extend({
// get signal mapping for simulation model
let self = this;
this.get('plot.visualization').then((visualization) => {
this.get('widget.visualization').then((visualization) => {
visualization.get('project').then((project) => {
project.get('simulation').then((simulation) => {
simulation.get('models').then((simulationModels) => {

View file

@ -11,11 +11,11 @@ import Ember from 'ember';
import FetchLiveDataMixin from '../../mixins/fetch-live-data';
export default Ember.Controller.extend(FetchLiveDataMixin, {
isShowingPlotValueModal: false,
isShowingWidgetValueModal: false,
errorMessage: null,
plot: null,
widget: null,
name: null,
simulator: null,
simulatorName: null,
@ -27,46 +27,45 @@ export default Ember.Controller.extend(FetchLiveDataMixin, {
this.set('simulatorName', simulators.toArray()[0].get('name'));
}
}),
actions: {
addPlot(name) {
var plot = null;
addWidget(name) {
var widget = null;
if (name === 'chart') {
// create new chart plot
plot = this.store.createRecord('plot', { name: 'Chart 1', type: 'plot-chart' });
widget = this.store.createRecord('widget', { name: 'Chart 1', type: 'widget-chart' });
} else if (name === 'table') {
plot = this.store.createRecord('plot', { name: 'Table 1', type: 'plot-table', width: 500, height: 200, title: 'Table 1' });
widget = this.store.createRecord('widget', { name: 'Table 1', type: 'widget-table', width: 500, height: 200 });
} else if (name === 'value') {
plot = this.store.createRecord('plot', { name: 'Value 1', type: 'plot-value', simulator: 2 });
widget = this.store.createRecord('widget', { name: 'Value 1', type: 'widget-value', width: 250, height: 20, widgetData: { signal: 0, simulator: 2 } });
} else {
// DEBUG
console.log('Add plot: ' + name);
console.log('Add widget ' + name);
return;
}
if (plot != null) {
// add plot to visualization
this.get('model.plots').pushObject(plot);
if (widget != null) {
// add widget to visualization
this.get('model.widgets').pushObject(widget);
// save new plot
// save new widget
var visualization = this.get('model');
plot.save().then(function() {
// save the plot in the visualization
visualization.get('plots').pushObject(plot);
widget.save().then(function() {
// save the widget in the visualization
visualization.get('widgets').pushObject(widget);
visualization.save();
});
} else {
console.error('Unknown plot type: ' + name);
console.error('Unknown widget type: ' + name);
}
},
saveEdit() {
// save changes to store
var plots = this.get('model.plots');
plots.forEach(function(plot) {
plot.save();
var widgets = this.get('model.widgets');
widgets.forEach(function(widget) {
widget.save();
});
// go back to index
@ -81,50 +80,19 @@ export default Ember.Controller.extend(FetchLiveDataMixin, {
this.transitionToRoute('/visualization/' + id);
},
showPlotDialog(plot) {
// show dialog by plot type
let plotType = plot.get('type');
if (plotType === 'plot-value') {
showWidgetDialog(widget) {
// show dialog by widget type
let widgetType = widget.get('type');
if (widgetType === 'value') {
// set properties
this.set('plot', plot);
this.set('widget', widget);
/*this.set('name', plot.get('name'));
this.set('signal', plot.get('signal'));*/
//this.set('simulatorName', simulatorName);
this.set('isShowingPlotValueModal', true);
this.set('isShowingWidgetValueModal', true);
}
},
submitValuePlot() {
// verify properties
let properties = this.getProperties('name', 'simulator', 'signal');
if (properties['name'] === null || properties['name'] === "") {
this.set('errorMessage', 'Plot name is missing');
return;
}
properties['simulator'] = Number(properties['simulator']);
properties['signal'] = Number(properties['signal']);
// save properties
this.get('plot').setProperties(properties);
let self = this;
this.get('plot').save().then(function() {
self.set('isShowingPlotValueModal', false);
}, function() {
Ember.debug('Error saving value plot');
});
},
cancelValuePlot() {
this.set('isShowingPlotValueModal', false);
},
selectSimulator(simulator) {
this.set('simulatorName', simulator);
}
}
});

View file

@ -0,0 +1,5 @@
import DS from 'ember-data';
export default DS.Model.extend({
});

View file

@ -13,7 +13,7 @@ import { belongsTo, hasMany } from 'ember-data/relationships';
export default Model.extend({
name: attr('string'),
plots: hasMany('plot', { async: true }),
widgets: hasMany('widget', { async: true }),
project: belongsTo('project', { async: true }),
rows: attr('number', { defaultValue: 1 })
});

View file

@ -1,5 +1,5 @@
/**
* File: plot.js
* File: widget.js
* Author: Markus Grigull <mgrigull@eonerc.rwth-aachen.de>
* Date: 28.06.2016
* Copyright: 2016, Institute for Automation of Complex Power Systems, EONERC
@ -13,8 +13,7 @@ import { belongsTo } from 'ember-data/relationships';
export default Model.extend({
name: attr('string'),
signal: attr('number', { defaultValue: 1 }),
simulator: attr('number', { defaultValue: 1 }),
widgetData: attr(),
width: attr('number', { defaultValue: 100 }),
height: attr('number', { defaultValue: 100 }),
type: attr('string'),

View file

@ -49,12 +49,6 @@ Router.map(function() {
this.route('simulators');
this.route('simulator');
this.route('dialog', function() {
this.route('plot', function() {
this.route('value');
});
});
});
export default Router;

View file

@ -12,6 +12,6 @@ import ApplicationSerializer from './application';
export default ApplicationSerializer.extend({
attrs: {
project: { serialize: 'ids' },
plots: { serialize: 'ids' }
widgets: { serialize: 'ids' }
}
});

View file

@ -7,7 +7,7 @@
* Unauthorized copying of this file, via any medium is strictly prohibited.
**********************************************************************************/
@import 'plots';
@import 'widgets';
@import 'models';
@import 'simulations';
@import 'projects';

View file

@ -1,5 +1,5 @@
/**
* File: plots.css
* File: widgets.css
* Author: Markus Grigull <mgrigull@eonerc.rwth-aachen.de>
* Date: 19.07.2016
* Copyright: 2016, Institute for Automation of Complex Power Systems, EONERC
@ -8,25 +8,25 @@
**********************************************************************************/
/**
* Component: plot-container
* Component: widget-container
*/
.plots {
.widgets {
overflow: scroll;
position: relative;
}
/**
* Component: plot-toolbox
* Component: widget-toolbox
*/
.plot-toolbox {
.widget-toolbox {
margin: 20px 0;
}
/**
* Component: plot-abstract
* Component: widget-abstract
*/
.plotAbstract {
.widgetAbstract {
border: 1px solid lightgray;
margin: 10px;
@ -36,13 +36,13 @@
}
/**
* Component: plot-value
* Component: widget-value
*/
.plotValue {
.widgetValue {
}
.plot-edit-container {
.widget-edit-container {
width: 200px !important;
background-color: #ddd;
@ -50,18 +50,18 @@
border: 1px dotted black;
}
.plot-edit-form {
.widget-edit-form {
}
/**
* Component: plot-table
* Component: widget-table
*/
.plotTable {
.widgetTable {
}
.plotTable table {
.widgetTable table {
width: 100%;
/*height: 100%;*/
@ -69,7 +69,7 @@
border-collapse: collapse;
}
.plotTable td, th {
.widgetTable td, th {
border: 1px solid gray;
padding: 2px 5px;

View file

@ -1,3 +0,0 @@
{{#each plots as |plot|}}
{{component plot.type plot=plot editing=editing grid=grid data=data showPlotDialog=showPlotDialog}}
{{/each}}

View file

@ -0,0 +1,3 @@
{{#each widgets as |widget|}}
{{component widget.type widget=widget editing=editing grid=grid data=data showWidgetDialog=showWidgetDialog}}
{{/each}}

View file

@ -1,10 +1,10 @@
{{#if editing}}
{{input value=plot.title placeholder='Enter title'}}
{{input value=widget.title placeholder='Enter title'}}
{{else}}
<h4>{{plot.title}}</h4>
<h4>{{widget.title}}</h4>
{{/if}}
<table class="plotTable">
<table class="widgetTable">
<tr>
<th>Name</th>
<th>Value</th>

View file

@ -4,14 +4,14 @@
{{#modal-dialog attachment="middle center" translucentOverlay=true}}
<h1>Value</h1>
<form class="form-plot-value" {{action 'submitModal' on='submit'}} >
<form class="form-widget-value" {{action 'submitModal' on='submit'}} >
<table>
<tr>
<td>
<label for="name">Name</label>
</td>
<td>
{{input id='name' placeholder='Enter plot name' value=name}}
{{input id='name' placeholder='Enter widget name' value=name}}
</td>
</tr>
<tr>
@ -20,7 +20,7 @@
</td>
<td>
<select onchange={{action "selectSimulationModel" value="target.value"}}>
{{#each plot.visualization.project.simulation.models as |simulationModel|}}
{{#each widget.visualization.project.simulation.models as |simulationModel|}}
<option value={{simulationModel.name}} selected={{eq simulationModelName simulationModel.name}}>{{simulationModel.name}}</option>
{{/each}}
</select>

View file

@ -1,22 +1,22 @@
<h1>{{model.ame}}</h1>
<h1>{{model.name}}</h1>
<div class="plot-toolbox">
<div class="widget-toolbox">
<h3>Toolbox</h3>
<!-- {{#draggable-item content='chart'}}
<span>Chart</span>
{{/draggable-item}} -->
<!-- {{#draggable-item content='table'}}
{{#draggable-item content='table'}}
<span>Table</span>
{{/draggable-item}} -->
{{/draggable-item}}
{{#draggable-item content='value'}}
<span>Value</span>
{{/draggable-item}}
</div>
{{#draggable-dropzone dropped='addPlot'}}
{{plot-container plots=model.plots editing=true data=data showPlotDialog='showPlotDialog'}}
{{#draggable-dropzone dropped='addWidget'}}
{{widget-container widgets=model.widgets editing=true data=data showWidgetDialog='showWidgetDialog'}}
{{/draggable-dropzone}}
<p>

View file

@ -2,7 +2,7 @@
<h1>{{model.name}}</h1>
{{plot-container plots=model.plots data=data}}
{{widget-container widgets=model.widgets data=data}}
<p>
{{#link-to "visualization.edit" model.id}}Edit layout{{/link-to}}

View file

@ -0,0 +1,12 @@
import { moduleForModel, test } from 'ember-qunit';
moduleForModel('simulator-status', 'Unit | Model | simulator status', {
// Specify the other units that are required for this test.
needs: []
});
test('it exists', function(assert) {
let model = this.subject();
// let store = this.store();
assert.ok(!!model);
});

88
todo.md
View file

@ -1,12 +1,94 @@
# To-Do
- Change password
- 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
- 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
- Rebrand plots into widgets
- Change widget model (plot) to custom data to provide mechanism for all widgets
- Add widgets where dropped
websocketserverip/config.json
websocketserverip/nodes.json
{
"affinity": 1,
"debug": 5,
"stats": 3,
"name": "villas-acs",
"http": {
"htdocs": "/villas/web/socket",
"port": 80
},
"plugins": [
"simple_circuit.so",
"example_hook.so"
],
"nodes": {
"ws": {
"type": "websocket",
"unit": "MVa",
"units": [
"V",
"A",
"Var"
],
"description": "Demo Channel",
"source": {
"simulator": "OP5600",
"location": "ACS lab"
},
"series": [
{
"label": "Random walk"
},
{
"label": "Sine"
},
{
"label": "Rect"
}
]
}
},
"paths": [
{
"in": "ws",
"out": "ws"
}
]
}
websocketserverip/nodes.json:
[
{
"name": "ws",
"connections": 1,
"state": 3,
"vectorize": 1,
"affinity": 1,
"type": "websocket",
"unit": "MVa",
"units": [
"V",
"A",
"Var"
],
"description": "Demo Channel",
"source": {
"simulator": "OP5600",
"location": "ACS lab"
},
"series": [
{
"label": "Random walk"
},
{
"label": "Sine"
},
{
"label": "Rect"
}
]
}
]