From 86a6bac55eb15a2c2de4e40099875e7e827ce4ee Mon Sep 17 00:00:00 2001 From: Markus Grigull Date: Tue, 27 Jun 2017 09:58:10 +0200 Subject: [PATCH 01/16] Add node component --- package.json | 9 +-- src/components/node-tree.js | 75 +++++++++++++++++++++++++ src/containers/simulators.js | 18 ++++-- src/data-managers/nodes-data-manager.js | 24 ++++++++ src/data-managers/rest-data-manager.js | 2 +- src/stores/node-store.js | 25 +++++++++ 6 files changed, 142 insertions(+), 11 deletions(-) create mode 100644 src/components/node-tree.js create mode 100644 src/data-managers/nodes-data-manager.js create mode 100644 src/stores/node-store.js diff --git a/package.json b/package.json index a8cc8a2..3d0b18e 100644 --- a/package.json +++ b/package.json @@ -5,9 +5,12 @@ "dependencies": { "bootstrap": "^3.3.7", "classnames": "^2.2.5", + "d3-scale": "^1.0.5", "es6-promise": "^4.0.5", "flux": "^3.1.2", + "gaugeJS": "^1.3.2", "immutable": "^3.8.1", + "rc-slider": "^7.0.1", "rd3": "^0.7.4", "react": "^15.4.2", "react-bootstrap": "^0.30.7", @@ -19,10 +22,8 @@ "react-notification-system": "^0.2.13", "react-rnd": "^4.2.2", "react-router": "^3.0.2", - "superagent": "^3.5.0", - "gaugeJS": "^1.3.2", - "d3-scale": "^1.0.5", - "rc-slider": "^7.0.1" + "react-sortable-tree": "^0.1.19", + "superagent": "^3.5.0" }, "devDependencies": { "chai": "^3.5.0", diff --git a/src/components/node-tree.js b/src/components/node-tree.js new file mode 100644 index 0000000..6ef02b7 --- /dev/null +++ b/src/components/node-tree.js @@ -0,0 +1,75 @@ +/** + * File: node-tree.js + * Author: Markus Grigull + * Date: 26.06.2017 + * + * This file is part of VILLASweb. + * + * VILLASweb is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VILLASweb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with VILLASweb. If not, see . + ******************************************************************************/ + +import React from 'react'; +import SortableTree from 'react-sortable-tree'; +import { Button, Glyphicon } from 'react-bootstrap'; + +class NodeTree extends React.Component { + constructor(props) { + super(props); + + this.state = { + treeData: [ + { title: 'Chicken', subtitle: 'localhost:5000', children: [ { title: 'Egg' } ], expanded: true }, + { title: 'Cow', subtitle: 'localhost:5001', children: [ { title: 'Milk' }, { title: 'Cheese' }], expanded: true }, + ] + }; + } + + canNodeDrag(node, path) { + return (node.parentNode != null); + } + + canNodeDrop(node, prevPath) { + return (node.nextParent != null); + } + + generateNodeProps(rowInfo) { + var buttons = []; + + if (rowInfo.parentNode == null) { + buttons.push() + } + + buttons.push(); + + return { + buttons: buttons + }; + } + + render() { + return ( + this.setState({ treeData }) } + style={{ height: 400 }} + maxDepth='2' + canDrag={ (node, path) => this.canNodeDrag(node, path) } + canDrop={ (node, prevPath) => this.canNodeDrop(node, prevPath) } + generateNodeProps={(rowInfo) => this.generateNodeProps(rowInfo) } + /> + ); + } +} + +export default NodeTree; diff --git a/src/containers/simulators.js b/src/containers/simulators.js index e9a2af8..8a9c01e 100644 --- a/src/containers/simulators.js +++ b/src/containers/simulators.js @@ -24,21 +24,23 @@ import { Container } from 'flux/utils'; import { Button, Modal, Glyphicon } from 'react-bootstrap'; import AppDispatcher from '../app-dispatcher'; -import SimulatorStore from '../stores/simulator-store'; +//import SimulatorStore from '../stores/simulator-store'; +import NodeStore from '../stores/node-store'; import Table from '../components/table'; import TableColumn from '../components/table-column'; import NewSimulatorDialog from '../components/dialog/new-simulator'; import EditSimulatorDialog from '../components/dialog/edit-simulator'; +import NodeTree from '../components/node-tree'; class Simulators extends Component { static getStores() { - return [ SimulatorStore ]; + return [ NodeStore ]; } static calculateState() { return { - simulators: SimulatorStore.getState(), + nodes: NodeStore.getState(), newModal: false, deleteModal: false, @@ -49,7 +51,7 @@ class Simulators extends Component { componentWillMount() { AppDispatcher.dispatch({ - type: 'simulators/start-load' + type: 'nodes/start-load' }); } @@ -99,7 +101,11 @@ class Simulators extends Component {

Simulators

- + + + + + {/*
this.labelStyle(value)} labelModifier={(value) => this.labelModifier(value)} /> this.setState({ editModal: true, modalSimulator: this.state.simulators[index] })} onDelete={(index) => this.setState({ deleteModal: true, modalSimulator: this.state.simulators[index] })} /> @@ -124,7 +130,7 @@ class Simulators extends Component { - + */} ); } diff --git a/src/data-managers/nodes-data-manager.js b/src/data-managers/nodes-data-manager.js new file mode 100644 index 0000000..a253ac9 --- /dev/null +++ b/src/data-managers/nodes-data-manager.js @@ -0,0 +1,24 @@ +/** + * File: nodes-data-manager.js + * Author: Markus Grigull + * Date: 26.06.2017 + * + * This file is part of VILLASweb. + * + * VILLASweb is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VILLASweb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with VILLASweb. If not, see . + ******************************************************************************/ + +import RestDataManager from './rest-data-manager'; + +export default new RestDataManager('node', '/nodes'); diff --git a/src/data-managers/rest-data-manager.js b/src/data-managers/rest-data-manager.js index 51560a2..749cdcd 100644 --- a/src/data-managers/rest-data-manager.js +++ b/src/data-managers/rest-data-manager.js @@ -22,7 +22,7 @@ import RestAPI from '../api/rest-api'; import AppDispatcher from '../app-dispatcher'; -const HOST = process.env.REACT_APP_HTTP_PROXY || ""; +const HOST = process.env.REACT_APP_HTTP_PROXY || "http://localhost:4000"; const API_URL = HOST + '/api/v1'; class RestDataManager { diff --git a/src/stores/node-store.js b/src/stores/node-store.js new file mode 100644 index 0000000..8945885 --- /dev/null +++ b/src/stores/node-store.js @@ -0,0 +1,25 @@ +/** + * File: node-store.js + * Author: Markus Grigull + * Date: 26.06.2017 + * + * This file is part of VILLASweb. + * + * VILLASweb is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VILLASweb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with VILLASweb. If not, see . + ******************************************************************************/ + +import ArrayStore from './array-store'; +import NodesDataManager from '../data-managers/nodes-data-manager'; + +export default new ArrayStore('nodes', NodesDataManager); From cf83d4d969d9690cf7a8e088286e30bf990a4372 Mon Sep 17 00:00:00 2001 From: Markus Grigull Date: Thu, 6 Jul 2017 11:41:26 +0200 Subject: [PATCH 02/16] Add node dialogs and backend --- src/components/dialog/edit-node.js | 98 ++++++++++++++++++++++++++++++ src/components/dialog/new-node.js | 97 +++++++++++++++++++++++++++++ src/components/node-tree.js | 24 ++++++-- src/containers/simulators.js | 89 +++++++++++++-------------- 4 files changed, 256 insertions(+), 52 deletions(-) create mode 100644 src/components/dialog/edit-node.js create mode 100644 src/components/dialog/new-node.js diff --git a/src/components/dialog/edit-node.js b/src/components/dialog/edit-node.js new file mode 100644 index 0000000..da72a4a --- /dev/null +++ b/src/components/dialog/edit-node.js @@ -0,0 +1,98 @@ +/** + * File: edit-node.js + * Author: Markus Grigull + * Date: 06.07.2017 + * + * This file is part of VILLASweb. + * + * VILLASweb is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VILLASweb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with VILLASweb. If not, see . + ******************************************************************************/ + +import React from 'react'; +import { FormGroup, FormControl, ControlLabel } from 'react-bootstrap'; + +import Dialog from './dialog'; + +class NewNodeDialog extends React.Component { + valid: false; + + constructor(props) { + super(props); + + this.state = { + name: '', + endpoint: '', + config: {}, + simulators: [], + _id: '' + }; + } + + onClose(canceled) { + if (canceled === false) { + this.props.onClose(this.state); + } else { + this.props.onClose(); + } + } + + handleChange(e) { + this.setState({ [e.target.id]: e.target.value }); + } + + resetState() { + this.setState({ name: this.props.node.name, endpoint: this.props.node.endpoint, config: this.props.node.config, simulators: this.props.node.simulators, _id: this.props.node._id }); + } + + validateForm(target) { + // check all controls + var endpoint = true; + var name = true; + + if (this.state.name === '') { + name = false; + } + + if (this.state.endpoint === '') { + endpoint = false; + } + + this.valid = endpoint && name; + + // return state to control + if (target === 'name') return name ? "success" : "error"; + else return endpoint ? "success" : "error"; + } + + render() { + return ( + this.onClose(c)} onReset={() => this.resetState()} valid={this.valid}> +
+ + Name + this.handleChange(e)} /> + + + + Endpoint + this.handleChange(e)} /> + + + +
+ ); + } +} + +export default NewNodeDialog; diff --git a/src/components/dialog/new-node.js b/src/components/dialog/new-node.js new file mode 100644 index 0000000..ae89a35 --- /dev/null +++ b/src/components/dialog/new-node.js @@ -0,0 +1,97 @@ +/** + * File: new-node.js + * Author: Markus Grigull + * Date: 06.07.2017 + * + * This file is part of VILLASweb. + * + * VILLASweb is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VILLASweb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with VILLASweb. If not, see . + ******************************************************************************/ + +import React from 'react'; +import { FormGroup, FormControl, ControlLabel } from 'react-bootstrap'; + +import Dialog from './dialog'; + +class NewNodeDialog extends React.Component { + valid: false; + + constructor(props) { + super(props); + + this.state = { + name: '', + endpoint: '', + config: {}, + simulators: [] + }; + } + + onClose(canceled) { + if (canceled === false) { + this.props.onClose(this.state); + } else { + this.props.onClose(); + } + } + + handleChange(e) { + this.setState({ [e.target.id]: e.target.value }); + } + + resetState() { + this.setState({ name: '', endpoint: '', config: {}, simulators: [] }); + } + + validateForm(target) { + // check all controls + var endpoint = true; + var name = true; + + if (this.state.name === '') { + name = false; + } + + if (this.state.endpoint === '') { + endpoint = false; + } + + this.valid = endpoint && name; + + // return state to control + if (target === 'name') return name ? "success" : "error"; + else return endpoint ? "success" : "error"; + } + + render() { + return ( + this.onClose(c)} onReset={() => this.resetState()} valid={this.valid}> +
+ + Name + this.handleChange(e)} /> + + + + Endpoint + this.handleChange(e)} /> + + + +
+ ); + } +} + +export default NewNodeDialog; diff --git a/src/components/node-tree.js b/src/components/node-tree.js index 6ef02b7..ea79d6c 100644 --- a/src/components/node-tree.js +++ b/src/components/node-tree.js @@ -28,10 +28,7 @@ class NodeTree extends React.Component { super(props); this.state = { - treeData: [ - { title: 'Chicken', subtitle: 'localhost:5000', children: [ { title: 'Egg' } ], expanded: true }, - { title: 'Cow', subtitle: 'localhost:5001', children: [ { title: 'Milk' }, { title: 'Cheese' }], expanded: true }, - ] + treeData: [] }; } @@ -50,20 +47,35 @@ class NodeTree extends React.Component { buttons.push() } - buttons.push(); + buttons.push(); + buttons.push(); return { buttons: buttons }; } + componentWillReceiveProps(nextProps) { + // compare if data changed + if (this.props.data == null || this.props.data !== nextProps.data) { + // generate new state + var treeData = []; + + nextProps.data.forEach((node) => { + treeData.push({ title: node.name, subtitle: node.endpoint, id: node._id }); + }); + + this.setState({ treeData }); + } + } + render() { return ( this.setState({ treeData }) } style={{ height: 400 }} - maxDepth='2' + maxDepth={ 2 } canDrag={ (node, path) => this.canNodeDrag(node, path) } canDrop={ (node, prevPath) => this.canNodeDrop(node, prevPath) } generateNodeProps={(rowInfo) => this.generateNodeProps(rowInfo) } diff --git a/src/containers/simulators.js b/src/containers/simulators.js index 8a9c01e..a6024c5 100644 --- a/src/containers/simulators.js +++ b/src/containers/simulators.js @@ -27,10 +27,8 @@ import AppDispatcher from '../app-dispatcher'; //import SimulatorStore from '../stores/simulator-store'; import NodeStore from '../stores/node-store'; -import Table from '../components/table'; -import TableColumn from '../components/table-column'; -import NewSimulatorDialog from '../components/dialog/new-simulator'; -import EditSimulatorDialog from '../components/dialog/edit-simulator'; +import NewNodeDialog from '../components/dialog/new-node'; +import EditNodeDialog from '../components/dialog/edit-node'; import NodeTree from '../components/node-tree'; class Simulators extends Component { @@ -45,7 +43,7 @@ class Simulators extends Component { newModal: false, deleteModal: false, editModal: false, - modalSimulator: {} + modalData: {} }; } @@ -56,81 +54,80 @@ class Simulators extends Component { } closeNewModal(data) { - this.setState({ newModal : false }); + this.setState({ newModal: false }); if (data) { AppDispatcher.dispatch({ - type: 'simulators/start-add', + type: 'nodes/start-add', data: data }); } } + showEditModal(data) { + // find node with id + var node = this.state.nodes.find((element) => { + return element._id === data.id; + }); + + this.setState({ editModal: true, modalData: node }); + } + + closeEditModal(data) { + this.setState({ editModal: false }); + + if (data) { + AppDispatcher.dispatch({ + type: 'nodes/start-edit', + data: data + }); + } + } + + showDeleteModal(data) { + // find node with id + var node = this.state.nodes.find((element) => { + return element._id === data.id; + }); + + this.setState({ deleteModal: true, modalData: node }); + } + confirmDeleteModal() { this.setState({ deleteModal: false }); AppDispatcher.dispatch({ - type: 'simulators/start-remove', - data: this.state.modalSimulator + type: 'nodes/start-remove', + data: this.state.modalData }); } - closeEditModal(data) { - this.setState({ editModal : false }); - - if (data) { - AppDispatcher.dispatch({ - type: 'simulators/start-edit', - data: data - }); - } - } - - labelStyle(value) { - if (value === true) return 'success'; - else return 'warning'; - } - - labelModifier(value) { - if (value === true) return 'Running'; - else return 'Not running'; - } - render() { return (

Simulators

- + - + this.showDeleteModal(node)} onEdit={(node) => this.showEditModal(node)} /> - {/*
- this.labelStyle(value)} labelModifier={(value) => this.labelModifier(value)} /> - - this.setState({ editModal: true, modalSimulator: this.state.simulators[index] })} onDelete={(index) => this.setState({ deleteModal: true, modalSimulator: this.state.simulators[index] })} /> -
- - - - this.closeNewModal(data)} /> - - this.closeEditModal(data)} simulator={this.state.modalSimulator} /> + this.closeNewModal(data)} /> + this.closeEditModal(data)} /> - Delete Simulator + Delete Node - Are you sure you want to delete the simulator '{this.state.modalSimulator.name}'? + Are you sure you want to delete the node '{this.state.modalData.name}'? - */} +
); } From c5e4642797d30460bb9156b8242e329de31c118d Mon Sep 17 00:00:00 2001 From: Markus Grigull Date: Fri, 7 Jul 2017 10:25:07 +0200 Subject: [PATCH 03/16] Add simulators to nodes --- src/components/dialog/new-simulator.js | 18 +----- src/components/node-tree.js | 18 ++++-- src/containers/simulators.js | 89 ++++++++++++++++++++------ 3 files changed, 85 insertions(+), 40 deletions(-) diff --git a/src/components/dialog/new-simulator.js b/src/components/dialog/new-simulator.js index 395cf9e..8e22e3d 100644 --- a/src/components/dialog/new-simulator.js +++ b/src/components/dialog/new-simulator.js @@ -36,8 +36,7 @@ class NewSimulatorDialog extends Component { super(props); this.state = { - name: '', - endpoint: '' + name: '' }; } @@ -54,27 +53,21 @@ class NewSimulatorDialog extends Component { } resetState() { - this.setState({ name: '', endpoint: '' }); + this.setState({ name: '' }); } validateForm(target) { // check all controls - var endpoint = true; var name = true; if (this.state.name === '') { name = false; } - if (this.state.endpoint === '') { - endpoint = false; - } - - this.valid = endpoint && name; + this.valid = name; // return state to control if (target === 'name') return name ? "success" : "error"; - else return endpoint ? "success" : "error"; } render() { @@ -86,11 +79,6 @@ class NewSimulatorDialog extends Component { this.handleChange(e)} /> - - Endpoint - this.handleChange(e)} /> - - ); diff --git a/src/components/node-tree.js b/src/components/node-tree.js index ea79d6c..1cf8226 100644 --- a/src/components/node-tree.js +++ b/src/components/node-tree.js @@ -44,11 +44,15 @@ class NodeTree extends React.Component { var buttons = []; if (rowInfo.parentNode == null) { - buttons.push() + buttons.push(); + buttons.push(); + buttons.push(); + } else { + buttons.push(); + buttons.push(); } - buttons.push(); - buttons.push(); + console.log(rowInfo); return { buttons: buttons @@ -62,7 +66,13 @@ class NodeTree extends React.Component { var treeData = []; nextProps.data.forEach((node) => { - treeData.push({ title: node.name, subtitle: node.endpoint, id: node._id }); + var parent = { title: node.name, subtitle: node.endpoint, id: node._id, children: [], expanded: true }; + + node.simulators.forEach((simulator) => { + parent.children.push({ title: simulator.name }); + }); + + treeData.push(parent); }); this.setState({ treeData }); diff --git a/src/containers/simulators.js b/src/containers/simulators.js index a6024c5..8fd4a53 100644 --- a/src/containers/simulators.js +++ b/src/containers/simulators.js @@ -24,11 +24,11 @@ import { Container } from 'flux/utils'; import { Button, Modal, Glyphicon } from 'react-bootstrap'; import AppDispatcher from '../app-dispatcher'; -//import SimulatorStore from '../stores/simulator-store'; import NodeStore from '../stores/node-store'; import NewNodeDialog from '../components/dialog/new-node'; import EditNodeDialog from '../components/dialog/edit-node'; +import NewSimulatorDialog from '../components/dialog/new-simulator'; import NodeTree from '../components/node-tree'; class Simulators extends Component { @@ -40,10 +40,16 @@ class Simulators extends Component { return { nodes: NodeStore.getState(), - newModal: false, - deleteModal: false, - editModal: false, - modalData: {} + newNodeModal: false, + deleteNodeModal: false, + editNodeModal: false, + + addSimulatorModal: false, + editSimulatorModal: false, + deleteSimulatorModal: false, + + modalData: {}, + modalIndex: 0 }; } @@ -53,8 +59,8 @@ class Simulators extends Component { }); } - closeNewModal(data) { - this.setState({ newModal: false }); + closeNewNodeModal(data) { + this.setState({ newNodeModal: false }); if (data) { AppDispatcher.dispatch({ @@ -64,17 +70,17 @@ class Simulators extends Component { } } - showEditModal(data) { + showEditNodeModal(data) { // find node with id var node = this.state.nodes.find((element) => { return element._id === data.id; }); - this.setState({ editModal: true, modalData: node }); + this.setState({ editNodeModal: true, modalData: node }); } - closeEditModal(data) { - this.setState({ editModal: false }); + closeEditNodeModal(data) { + this.setState({ editNodeModal: false }); if (data) { AppDispatcher.dispatch({ @@ -84,16 +90,16 @@ class Simulators extends Component { } } - showDeleteModal(data) { + showDeleteNodeModal(data) { // find node with id var node = this.state.nodes.find((element) => { return element._id === data.id; }); - this.setState({ deleteModal: true, modalData: node }); + this.setState({ deleteNodeModal: true, modalData: node }); } - confirmDeleteModal() { + confirmDeleteNodeModal() { this.setState({ deleteModal: false }); AppDispatcher.dispatch({ @@ -102,30 +108,71 @@ class Simulators extends Component { }); } + showAddSimulatorModal(data) { + // find node with id + var node = this.state.nodes.find((element) => { + return element._id === data.id; + }); + + this.setState({ addSimulatorModal: true, modalData: node }); + } + + closeAddSimulatorModal(data) { + this.setState({ addSimulatorModal: false }); + + if (data) { + var node = this.state.modalData; + node.simulators.push(data); + + AppDispatcher.dispatch({ + type: 'nodes/start-edit', + data: node + }); + } + } + render() { return (

Simulators

- + - this.showDeleteModal(node)} onEdit={(node) => this.showEditModal(node)} /> + this.showDeleteNodeModal(node)} onNodeEdit={(node) => this.showEditNodeModal(node)} onNodeAdd={(node) => this.showAddSimulatorModal(node)} onSimulatorEdit={(index) => this.onSimulatorEdit(index)} onSimulatorDelete={(index) => this.onSimulatorDelete(index)} /> - this.closeNewModal(data)} /> - this.closeEditModal(data)} /> + this.closeNewNodeModal(data)} /> + this.closeEditNodeModal(data)} /> + this.closeAddSimulatorModal(data)} /> - + Delete Node Are you sure you want to delete the node '{this.state.modalData.name}'? +
+ This will delete all simulators assigned to this node.
- - + + + +
+ + + + Delete Simulator + + + + {/*Are you sure you want to delete the simulator '{this.state.modalData.simulators[this.state.modalIndex].name}'?*/} + + + + +
From 3e55659854d7af112245b755c472c960c441e0a2 Mon Sep 17 00:00:00 2001 From: Markus Grigull Date: Sat, 8 Jul 2017 00:23:01 +0200 Subject: [PATCH 04/16] Add simulator editing and moving --- src/components/dialog/edit-simulator.js | 21 ++------ src/components/node-tree.js | 70 +++++++++++++++++-------- src/containers/simulators.js | 67 +++++++++++++++++++++-- 3 files changed, 116 insertions(+), 42 deletions(-) diff --git a/src/components/dialog/edit-simulator.js b/src/components/dialog/edit-simulator.js index 1fdcc09..ef8082a 100644 --- a/src/components/dialog/edit-simulator.js +++ b/src/components/dialog/edit-simulator.js @@ -37,9 +37,7 @@ class EditSimulatorDialog extends Component { super(props); this.state = { - name: '', - endpoint: '', - _id: '' + name: '' }; } @@ -57,30 +55,22 @@ class EditSimulatorDialog extends Component { resetState() { this.setState({ - name: this.props.simulator.name, - endpoint: this.props.simulator.endpoint, - _id: this.props.simulator._id + name: this.props.simulator.name }); } validateForm(target) { // check all controls - var endpoint = true; var name = true; if (this.state.name === '') { name = false; } - if (this.state.endpoint === '') { - endpoint = false; - } - - this.valid = endpoint && name; + this.valid = name; // return state to control if (target === 'name') return name ? "success" : "error"; - else return endpoint ? "success" : "error"; } render() { @@ -92,11 +82,6 @@ class EditSimulatorDialog extends Component { this.handleChange(e)} /> - - Endpoint - this.handleChange(e)} /> - - ); diff --git a/src/components/node-tree.js b/src/components/node-tree.js index 1cf8226..1e60038 100644 --- a/src/components/node-tree.js +++ b/src/components/node-tree.js @@ -48,47 +48,75 @@ class NodeTree extends React.Component { buttons.push(); buttons.push(); } else { - buttons.push(); - buttons.push(); - } + // get child index + var index = rowInfo.path[1] - rowInfo.path[0] - 1; - console.log(rowInfo); + buttons.push(); + buttons.push(); + } return { buttons: buttons }; } + nodesToTreeData(nodes) { + var treeData = []; + + nodes.forEach((node) => { + var parent = { title: node.name, subtitle: node.endpoint, id: node._id, config: node.config, children: [], expanded: true }; + + node.simulators.forEach((simulator) => { + parent.children.push({ title: simulator.name }); + }); + + treeData.push(parent); + }); + + return treeData; + } + + treeDataToNodes(treeData) { + var nodes = []; + + treeData.forEach((data) => { + var node = { name: data.title, endpoint: data.subtitle, _id: data.id, config: data.config, simulators: [] }; + + data.children.forEach((child) => { + node.simulators.push({ name: child.title }); + }); + + nodes.push(node); + }); + + return nodes; + } + componentWillReceiveProps(nextProps) { // compare if data changed if (this.props.data == null || this.props.data !== nextProps.data) { // generate new state - var treeData = []; - - nextProps.data.forEach((node) => { - var parent = { title: node.name, subtitle: node.endpoint, id: node._id, children: [], expanded: true }; - - node.simulators.forEach((simulator) => { - parent.children.push({ title: simulator.name }); - }); - - treeData.push(parent); - }); - + var treeData = this.nodesToTreeData(nextProps.data); this.setState({ treeData }); } } + onDataChange(treeData) { + this.setState({ treeData }); + + this.props.onDataChange(this.treeDataToNodes(treeData)) + } + render() { return ( this.setState({ treeData }) } + treeData={this.state.treeData} + onChange={(treeData) => this.onDataChange(treeData)} style={{ height: 400 }} maxDepth={ 2 } - canDrag={ (node, path) => this.canNodeDrag(node, path) } - canDrop={ (node, prevPath) => this.canNodeDrop(node, prevPath) } - generateNodeProps={(rowInfo) => this.generateNodeProps(rowInfo) } + canDrag={(node, path) => this.canNodeDrag(node, path)} + canDrop={(node, prevPath) => this.canNodeDrop(node, prevPath)} + generateNodeProps={(rowInfo) => this.generateNodeProps(rowInfo)} /> ); } diff --git a/src/containers/simulators.js b/src/containers/simulators.js index 8fd4a53..fd6c450 100644 --- a/src/containers/simulators.js +++ b/src/containers/simulators.js @@ -29,6 +29,7 @@ import NodeStore from '../stores/node-store'; import NewNodeDialog from '../components/dialog/new-node'; import EditNodeDialog from '../components/dialog/edit-node'; import NewSimulatorDialog from '../components/dialog/new-simulator'; +import EditSimulatorDialog from '../components/dialog/edit-simulator'; import NodeTree from '../components/node-tree'; class Simulators extends Component { @@ -49,7 +50,8 @@ class Simulators extends Component { deleteSimulatorModal: false, modalData: {}, - modalIndex: 0 + modalIndex: 0, + modalName: '' }; } @@ -131,6 +133,61 @@ class Simulators extends Component { } } + showEditSimulatorModal(data, index) { + // find node with id + var node = this.state.nodes.find((element) => { + return element._id === data.id; + }); + + this.setState({ editSimulatorModal: true, modalData: node, modalIndex: index }); + } + + closeEditSimulatorModal(data) { + this.setState({ editSimulatorModal: false }); + + if (data) { + var node = this.state.modalData; + node.simulators[this.state.modalIndex] = data; + + AppDispatcher.dispatch({ + type: 'nodes/start-edit', + data: node + }); + } + } + + showDeleteSimulatorModal(data, index) { + // find node with id + var node = this.state.nodes.find((element) => { + return element._id === data.id; + }); + + this.setState({ deleteSimulatorModal: true, modalData: node, modalIndex: index, modalName: data.children[index].title }); + } + + confirmDeleteSimulatorModal() { + this.setState({ deleteSimulatorModal: false }); + + // remove simulator + var node = this.state.modalData; + node.simulators.splice(this.state.modalIndex); + + AppDispatcher.dispatch({ + type: 'nodes/start-edit', + data: node + }); + } + + onTreeDataChange(nodes) { + // update all at once + nodes.forEach((node) => { + AppDispatcher.dispatch({ + type: 'nodes/start-edit', + data: node + }); + }); + } + render() { return (
@@ -138,12 +195,16 @@ class Simulators extends Component { - this.showDeleteNodeModal(node)} onNodeEdit={(node) => this.showEditNodeModal(node)} onNodeAdd={(node) => this.showAddSimulatorModal(node)} onSimulatorEdit={(index) => this.onSimulatorEdit(index)} onSimulatorDelete={(index) => this.onSimulatorDelete(index)} /> + this.onTreeDataChange(treeData)} onNodeDelete={(node) => this.showDeleteNodeModal(node)} onNodeEdit={(node) => this.showEditNodeModal(node)} onNodeAdd={(node) => this.showAddSimulatorModal(node)} onSimulatorEdit={(node, index) => this.showEditSimulatorModal(node, index)} onSimulatorDelete={(node, index) => this.showDeleteSimulatorModal(node, index)} /> this.closeNewNodeModal(data)} /> this.closeEditNodeModal(data)} /> this.closeAddSimulatorModal(data)} /> + {this.state.editSimulatorModal && + this.closeEditSimulatorModal(data)} /> + } + Delete Node @@ -167,7 +228,7 @@ class Simulators extends Component { - {/*Are you sure you want to delete the simulator '{this.state.modalData.simulators[this.state.modalIndex].name}'?*/} + Are you sure you want to delete the simulator '{this.state.modalName}'? From 46bcb2fc21355929f9cc1907a10c4136816b4cfb Mon Sep 17 00:00:00 2001 From: Markus Grigull Date: Sat, 8 Jul 2017 12:37:13 +0200 Subject: [PATCH 05/16] Start simulator ID fetching --- src/containers/app.js | 7 ++--- src/data-managers/nodes-data-manager.js | 34 ++++++++++++++++++++++++- src/stores/node-store.js | 26 ++++++++++++++++++- 3 files changed, 62 insertions(+), 5 deletions(-) diff --git a/src/containers/app.js b/src/containers/app.js index 2af248e..14d02cc 100644 --- a/src/containers/app.js +++ b/src/containers/app.js @@ -28,6 +28,7 @@ import NotificationSystem from 'react-notification-system'; import AppDispatcher from '../app-dispatcher'; import SimulationStore from '../stores/simulation-store'; import SimulatorStore from '../stores/simulator-store'; +import NodeStore from '../stores/node-store'; import UserStore from '../stores/user-store'; import NotificationsDataManager from '../data-managers/notifications-data-manager'; @@ -40,7 +41,7 @@ import '../styles/app.css'; class App extends Component { static getStores() { - return [ SimulationStore, SimulatorStore, UserStore ]; + return [ SimulationStore, NodeStore, UserStore ]; } static calculateState(prevState) { @@ -164,12 +165,12 @@ class App extends Component { }); if (simulator != null) { - AppDispatcher.dispatch({ + /*AppDispatcher.dispatch({ type: 'simulatorData/open', identifier: simulator._id, endpoint: simulator.endpoint, signals: data.signals - }); + });*/ } } diff --git a/src/data-managers/nodes-data-manager.js b/src/data-managers/nodes-data-manager.js index a253ac9..ab04da9 100644 --- a/src/data-managers/nodes-data-manager.js +++ b/src/data-managers/nodes-data-manager.js @@ -20,5 +20,37 @@ ******************************************************************************/ import RestDataManager from './rest-data-manager'; +import RestAPI from '../api/rest-api'; -export default new RestDataManager('node', '/nodes'); +class NodesDataManager extends RestDataManager { + constructor() { + super('node', '/nodes'); + } + + getSimulators(node) { + RestAPI.post('http://' + node.endpoint + '/api/v1', { + action: 'nodes', + id: node._id + }).then(response => { + // assign IDs to simulators + response.response.forEach(element => { + if (element.type === "websocket") { + // add the (villas-node) node ID to the simulator + node.simulators = node.simulators.map(simulator => { + if (simulator.name === element.name) { + simulator.id = element.id; + } + + return simulator; + }); + } + }); + + console.log(node); + }).catch(error => { + console.warn(error); + }); + } +} + +export default new NodesDataManager(); diff --git a/src/stores/node-store.js b/src/stores/node-store.js index 8945885..d7d6112 100644 --- a/src/stores/node-store.js +++ b/src/stores/node-store.js @@ -22,4 +22,28 @@ import ArrayStore from './array-store'; import NodesDataManager from '../data-managers/nodes-data-manager'; -export default new ArrayStore('nodes', NodesDataManager); +class NodeStore extends ArrayStore { + constructor() { + super('nodes', NodesDataManager); + } + + reduce(state, action) { + switch(action.type) { + case 'nodes/loaded': + if (Array.isArray(action.data)) { + action.data.forEach(node => { + NodesDataManager.getSimulators(node); + }); + } else { + NodesDataManager.getSimulators(action.data); + } + + return super.reduce(state, action); + + default: + return super.reduce(state, action); + } + } +} + +export default new NodeStore(); From 89f70f027a86e0b939740893a92c859eed958f2e Mon Sep 17 00:00:00 2001 From: Markus Grigull Date: Sun, 9 Jul 2017 18:14:53 +0200 Subject: [PATCH 06/16] Change simulator selection in simulator model Disable simulator detection for testing --- .../dialog/edit-simulation-model.js | 22 +++++++++----- src/components/dialog/new-simulation-model.js | 21 ++++++++----- src/containers/app.js | 30 ++++++++++--------- src/containers/simulation.js | 24 ++++++--------- src/containers/simulators.js | 5 +++- src/data-managers/nodes-data-manager.js | 15 +++++++--- 6 files changed, 69 insertions(+), 48 deletions(-) diff --git a/src/components/dialog/edit-simulation-model.js b/src/components/dialog/edit-simulation-model.js index 3de81dd..de5855d 100644 --- a/src/components/dialog/edit-simulation-model.js +++ b/src/components/dialog/edit-simulation-model.js @@ -31,7 +31,7 @@ class EditSimulationModelDialog extends Component { show: PropTypes.bool.isRequired, onClose: PropTypes.func.isRequired, data: PropTypes.object.isRequired, - simulators: PropTypes.array.isRequired + nodes: PropTypes.array.isRequired }; valid: false; @@ -41,8 +41,9 @@ class EditSimulationModelDialog extends Component { this.state = { name: '', - simulator: '', - length: 1 + simulator: { node: '', simulator: '' }, + length: 1, + mapping: [{ name: 'Signal', type: 'Type' }] } } @@ -68,7 +69,12 @@ class EditSimulationModelDialog extends Component { } } - this.setState({ [e.target.id]: e.target.value }); + if (e.target.id === 'simulator') { + var value = e.target.value.split("/"); + this.setState({ simulator: { node: value[0], simulator: value[1] } }); + } else { + this.setState({ [e.target.id]: e.target.value }); + } } handleMappingChange(event, row, column) { @@ -124,9 +130,11 @@ class EditSimulationModelDialog extends Component { Simulator - this.handleChange(e)}> - {this.props.simulators.map(simulator => ( - + this.handleChange(e)}> + {this.props.nodes.map(node => ( + node.simulators.map((simulator, index) => ( + + )) ))} diff --git a/src/components/dialog/new-simulation-model.js b/src/components/dialog/new-simulation-model.js index 5789e51..0acc38d 100644 --- a/src/components/dialog/new-simulation-model.js +++ b/src/components/dialog/new-simulation-model.js @@ -30,7 +30,7 @@ class NewSimulationModelDialog extends Component { static propTypes = { show: PropTypes.bool.isRequired, onClose: PropTypes.func.isRequired, - simulators: PropTypes.array.isRequired + nodes: PropTypes.array.isRequired }; valid: false; @@ -40,7 +40,7 @@ class NewSimulationModelDialog extends Component { this.state = { name: '', - simulator: '', + simulator: { node: '', simulator: '' }, length: '1', mapping: [ { name: 'Signal', type: 'Type' } ] }; @@ -68,7 +68,12 @@ class NewSimulationModelDialog extends Component { } } - this.setState({ [e.target.id]: e.target.value }); + if (e.target.id === 'simulator') { + var value = e.target.value.split("/"); + this.setState({ simulator: { node: value[0], simulator: value[1] } }); + } else { + this.setState({ [e.target.id]: e.target.value }); + } } handleMappingChange(event, row, column) { @@ -86,7 +91,7 @@ class NewSimulationModelDialog extends Component { resetState() { this.setState({ name: '', - simulator: this.props.simulators[0] != null ? this.props.simulators[0]._id : '', + simulator: { node: this.props.nodes[0] ? this.props.nodes[0].name : '', simulator: this.props.nodes[0].simulators[0] ? this.props.nodes[0].simulators[0].name : '' }, length: '1', mapping: [ { name: 'Signal', type: 'Type' } ] }); @@ -130,9 +135,11 @@ class NewSimulationModelDialog extends Component { Simulator - this.handleChange(e)}> - {this.props.simulators.map(simulator => ( - + this.handleChange(e)}> + {this.props.nodes.map(node => ( + node.simulators.map((simulator, index) => ( + + )) ))} diff --git a/src/containers/app.js b/src/containers/app.js index 14d02cc..adc5f05 100644 --- a/src/containers/app.js +++ b/src/containers/app.js @@ -27,7 +27,7 @@ import NotificationSystem from 'react-notification-system'; import AppDispatcher from '../app-dispatcher'; import SimulationStore from '../stores/simulation-store'; -import SimulatorStore from '../stores/simulator-store'; +//import SimulatorStore from '../stores/simulator-store'; import NodeStore from '../stores/node-store'; import UserStore from '../stores/user-store'; import NotificationsDataManager from '../data-managers/notifications-data-manager'; @@ -41,12 +41,12 @@ import '../styles/app.css'; class App extends Component { static getStores() { - return [ SimulationStore, NodeStore, UserStore ]; + return [ NodeStore, UserStore, SimulationStore ]; } static calculateState(prevState) { // get list of running simulators - var simulators = SimulatorStore.getState().filter(simulator => { + /*var simulators = SimulatorStore.getState().filter(simulator => { return simulator.running === true; }); @@ -75,16 +75,16 @@ class App extends Component { if (equal) { simulators = prevState.runningSimulators; } - } + }*/ let currentUser = UserStore.getState().currentUser; return { - simulations: SimulationStore.getState(), + nodes: NodeStore.getState(), currentRole: currentUser? currentUser.role : '', - token: UserStore.getState().token, + token: UserStore.getState().token/*, - runningSimulators: simulators + runningSimulators: simulators*/ }; } @@ -104,7 +104,7 @@ class App extends Component { // load all simulators and simulations to fetch simulation data AppDispatcher.dispatch({ - type: 'simulators/start-load' + type: 'nodes/start-load' }); AppDispatcher.dispatch({ @@ -136,7 +136,9 @@ class App extends Component { } requiredSimulatorsBySimulations() { - var simulators = []; + return []; + + /*var simulators = []; this.state.simulations.forEach((simulation) => { simulation.models.forEach((simulationModel) => { @@ -155,24 +157,24 @@ class App extends Component { }); }); - return simulators; + return simulators;*/ } - connectSimulator(state, data) { + /*connectSimulator(state, data) { // get simulator object const simulator = state.runningSimulators.find(element => { return element._id === data.simulator; }); if (simulator != null) { - /*AppDispatcher.dispatch({ + AppDispatcher.dispatch({ type: 'simulatorData/open', identifier: simulator._id, endpoint: simulator.endpoint, signals: data.signals - });*/ + }); } - } + }*/ render() { // get children diff --git a/src/containers/simulation.js b/src/containers/simulation.js index 59e37c5..52f5649 100644 --- a/src/containers/simulation.js +++ b/src/containers/simulation.js @@ -24,7 +24,7 @@ import { Container } from 'flux/utils'; import { Button, Modal, Glyphicon } from 'react-bootstrap'; import SimulationStore from '../stores/simulation-store'; -import SimulatorStore from '../stores/simulator-store'; +import NodeStore from '../stores/node-store'; import AppDispatcher from '../app-dispatcher'; import Table from '../components/table'; @@ -34,13 +34,13 @@ import EditSimulationModelDialog from '../components/dialog/edit-simulation-mode class Simulation extends Component { static getStores() { - return [ SimulationStore, SimulatorStore ]; + return [ SimulationStore, NodeStore ]; } static calculateState() { return { simulations: SimulationStore.getState(), - simulators: SimulatorStore.getState(), + nodes: NodeStore.getState(), newModal: false, deleteModal: false, @@ -58,7 +58,7 @@ class Simulation extends Component { }); AppDispatcher.dispatch({ - type: 'simulators/start-load' + type: 'nodes/start-load' }); } @@ -119,14 +119,8 @@ class Simulation extends Component { } } - getSimulatorName(id) { - for (var i = 0; i < this.state.simulators.length; i++) { - if (this.state.simulators[i]._id === id) { - return this.state.simulators[i].name; - } - } - - return id; + getSimulatorName(simulator) { + return simulator.node + '/' + simulator.simulator; } render() { @@ -136,16 +130,16 @@ class Simulation extends Component { - this.getSimulatorName(id)} /> + this.getSimulatorName(simulator)} /> this.setState({ editModal: true, modalData: this.state.simulation.models[index], modalIndex: index })} onDelete={(index) => this.setState({ deleteModal: true, modalData: this.state.simulation.models[index], modalIndex: index })} />
- this.closeNewModal(data)} simulators={this.state.simulators} /> + this.closeNewModal(data)} nodes={this.state.nodes} /> - this.closeEditModal(data)} data={this.state.modalData} simulators={this.state.simulators} /> + this.closeEditModal(data)} data={this.state.modalData} nodes={this.state.nodes} /> diff --git a/src/containers/simulators.js b/src/containers/simulators.js index fd6c450..acaf5ad 100644 --- a/src/containers/simulators.js +++ b/src/containers/simulators.js @@ -193,7 +193,10 @@ class Simulators extends Component {

Simulators

- + + +
+ Hint: Node names must be unique. Simulator names must be unique on a node. this.onTreeDataChange(treeData)} onNodeDelete={(node) => this.showDeleteNodeModal(node)} onNodeEdit={(node) => this.showEditNodeModal(node)} onNodeAdd={(node) => this.showAddSimulatorModal(node)} onSimulatorEdit={(node, index) => this.showEditSimulatorModal(node, index)} onSimulatorDelete={(node, index) => this.showDeleteSimulatorModal(node, index)} /> diff --git a/src/data-managers/nodes-data-manager.js b/src/data-managers/nodes-data-manager.js index ab04da9..af68fc1 100644 --- a/src/data-managers/nodes-data-manager.js +++ b/src/data-managers/nodes-data-manager.js @@ -21,6 +21,7 @@ import RestDataManager from './rest-data-manager'; import RestAPI from '../api/rest-api'; +import AppDispatcher from '../app-dispatcher'; class NodesDataManager extends RestDataManager { constructor() { @@ -28,7 +29,7 @@ class NodesDataManager extends RestDataManager { } getSimulators(node) { - RestAPI.post('http://' + node.endpoint + '/api/v1', { + /*RestAPI.post('http://' + node.endpoint + '/api/v1', { action: 'nodes', id: node._id }).then(response => { @@ -46,10 +47,16 @@ class NodesDataManager extends RestDataManager { } }); - console.log(node); + AppDispatcher.dispatch({ + type: 'nodes/edited', + data: node + }); }).catch(error => { - console.warn(error); - }); + AppDispatcher.dispatch({ + type: 'nodes/edit-error', + error: error + }); + });*/ } } From a3d35ab5fc1a6ae40f77fae060ea95d44ac9d1b8 Mon Sep 17 00:00:00 2001 From: Markus Grigull Date: Tue, 11 Jul 2017 15:17:28 +0200 Subject: [PATCH 07/16] Change simulator data to node based --- src/containers/app.js | 67 +++++++++---------- src/data-managers/nodes-data-manager.js | 10 ++- .../simulator-data-data-manager.js | 33 ++++----- src/stores/node-store.js | 1 + src/stores/simulator-data-store.js | 36 ++++++---- 5 files changed, 79 insertions(+), 68 deletions(-) diff --git a/src/containers/app.js b/src/containers/app.js index adc5f05..70bbe67 100644 --- a/src/containers/app.js +++ b/src/containers/app.js @@ -27,7 +27,6 @@ import NotificationSystem from 'react-notification-system'; import AppDispatcher from '../app-dispatcher'; import SimulationStore from '../stores/simulation-store'; -//import SimulatorStore from '../stores/simulator-store'; import NodeStore from '../stores/node-store'; import UserStore from '../stores/user-store'; import NotificationsDataManager from '../data-managers/notifications-data-manager'; @@ -81,6 +80,7 @@ class App extends Component { return { nodes: NodeStore.getState(), + simulations: SimulationStore.getState(), currentRole: currentUser? currentUser.role : '', token: UserStore.getState().token/*, @@ -127,53 +127,46 @@ class App extends Component { return; } - // open connection to each required simulator - const requiredSimulators = this.requiredSimulatorsBySimulations(); + // open connection to each node + /*const requiredNodes = this.requiredNodesBySimulations(); - requiredSimulators.forEach(simulator => { - this.connectSimulator(nextState, simulator); - }); + requiredNodes.forEach(node => { + AppDispatcher.dispatch({ + type: 'simulatorData/open', + identifier: simulator._id, + endpoint: node.endpoint, + signals: data.signals + }); + });*/ } - requiredSimulatorsBySimulations() { - return []; + /*requiredNodesBySimulations() { + var nodes = {}; - /*var simulators = []; - - this.state.simulations.forEach((simulation) => { - simulation.models.forEach((simulationModel) => { - // add simulator to list if not already part of - const index = simulators.findIndex((element) => { - return element.simulator === simulationModel.simulator; + this.state.simulations.forEach(simulation => { + simulation.models.forEach(model => { + // get ID for node + var node = this.state.nodes.find(element => { + return element.name === model.simulator.node; }); - if (index === -1) { - simulators.push({ simulator: simulationModel.simulator, signals: simulationModel.length }); - } else { - if (simulators[index].length < simulationModel.length) { - simulators[index].length = simulationModel.length; + // add empty node if not existing + if (node !== undefined) { + if (nodes[node._id] == null) { + nodes[node._id] = { simulators: [] } } + + // get simulator id + var simulator = node.simulators.find(simulator => { + return simulator.name === model.simulator.simulator; + }); + + nodes[node._id].simulators.push({ id: simulator.id, signals: model.length }); } }); }); - return simulators;*/ - } - - /*connectSimulator(state, data) { - // get simulator object - const simulator = state.runningSimulators.find(element => { - return element._id === data.simulator; - }); - - if (simulator != null) { - AppDispatcher.dispatch({ - type: 'simulatorData/open', - identifier: simulator._id, - endpoint: simulator.endpoint, - signals: data.signals - }); - } + return nodes; }*/ render() { diff --git a/src/data-managers/nodes-data-manager.js b/src/data-managers/nodes-data-manager.js index af68fc1..12e4014 100644 --- a/src/data-managers/nodes-data-manager.js +++ b/src/data-managers/nodes-data-manager.js @@ -29,7 +29,7 @@ class NodesDataManager extends RestDataManager { } getSimulators(node) { - /*RestAPI.post('http://' + node.endpoint + '/api/v1', { + RestAPI.post('http://' + node.endpoint + '/api/v1', { action: 'nodes', id: node._id }).then(response => { @@ -51,12 +51,18 @@ class NodesDataManager extends RestDataManager { type: 'nodes/edited', data: node }); + + AppDispatcher.dispatch({ + type: 'simulatorData/open', + node: node, + endpoint: node.endpoint, + }); }).catch(error => { AppDispatcher.dispatch({ type: 'nodes/edit-error', error: error }); - });*/ + }); } } diff --git a/src/data-managers/simulator-data-data-manager.js b/src/data-managers/simulator-data-data-manager.js index 22f443b..666ea5e 100644 --- a/src/data-managers/simulator-data-data-manager.js +++ b/src/data-managers/simulator-data-data-manager.js @@ -27,21 +27,21 @@ class SimulatorDataDataManager { this._sockets = {}; } - open(endpoint, identifier, signals) { + open(endpoint, node) { // pass signals to onOpen callback - if (this._sockets[identifier] != null) { - if (this._sockets[identifier].url !== WebsocketAPI.getURL(endpoint)) { + if (this._sockets[node._id] != null) { + if (this._sockets[node._id].url !== WebsocketAPI.getURL(endpoint)) { // replace connection, since endpoint changed this._sockets.close(); - this._sockets[identifier] = WebsocketAPI.addSocket(endpoint, { onOpen: (event) => this.onOpen(event, identifier, signals), onClose: (event) => this.onClose(event, identifier), onMessage: (event) => this.onMessage(event, identifier) }); + this._sockets[node._id] = WebsocketAPI.addSocket(endpoint, { onOpen: (event) => this.onOpen(event, node), onClose: (event) => this.onClose(event, node), onMessage: (event) => this.onMessage(event, node) }); } } else { // set flag if a socket to this simulator was already create before - if (this._sockets[identifier] === null) { - this._sockets[identifier] = WebsocketAPI.addSocket(endpoint, { onOpen: (event) => this.onOpen(event, identifier, signals, false), onClose: (event) => this.onClose(event, identifier), onMessage: (event) => this.onMessage(event, identifier) }); + if (this._sockets[node._id] === null) { + this._sockets[node._id] = WebsocketAPI.addSocket(endpoint, { onOpen: (event) => this.onOpen(event, node, false), onClose: (event) => this.onClose(event, node), onMessage: (event) => this.onMessage(event, node) }); } else { - this._sockets[identifier] = WebsocketAPI.addSocket(endpoint, { onOpen: (event) => this.onOpen(event, identifier, signals, true), onClose: (event) => this.onClose(event, identifier), onMessage: (event) => this.onMessage(event, identifier) }); + this._sockets[node._id] = WebsocketAPI.addSocket(endpoint, { onOpen: (event) => this.onOpen(event, node, true), onClose: (event) => this.onClose(event, node), onMessage: (event) => this.onMessage(event, node) }); } } } @@ -56,33 +56,32 @@ class SimulatorDataDataManager { } } - onOpen(event, identifier, signals, firstOpen) { + onOpen(event, node, firstOpen) { AppDispatcher.dispatch({ type: 'simulatorData/opened', - identifier: identifier, - signals: signals, + node: node, firstOpen: firstOpen }); } - onClose(event, identifier) { + onClose(event, node) { AppDispatcher.dispatch({ type: 'simulatorData/closed', - identifier: identifier, + node: node, notification: (event.code !== 4000) }); // remove from list, keep null reference for flag detection - delete this._sockets[identifier]; + delete this._sockets[node._id]; } - onMessage(event, identifier) { + onMessage(event, node) { var message = this.bufferToMessage(event.data); AppDispatcher.dispatch({ type: 'simulatorData/data-changed', data: message, - identifier: identifier + node: node }); } @@ -95,6 +94,7 @@ class SimulatorDataDataManager { var bits = data.getUint8(0); var length = data.getUint16(0x02, 1); + var id = data.getUint8(1); var values = new Float32Array(data.buffer, data.byteOffset + 0x10, length); @@ -104,7 +104,8 @@ class SimulatorDataDataManager { length: length, sequence: data.getUint32(0x04, 1), timestamp: data.getUint32(0x08, 1) * 1e3 + data.getUint32(0x0C, 1) * 1e-6, - values: values + values: values, + id: id }; } } diff --git a/src/stores/node-store.js b/src/stores/node-store.js index d7d6112..aad1df6 100644 --- a/src/stores/node-store.js +++ b/src/stores/node-store.js @@ -30,6 +30,7 @@ class NodeStore extends ArrayStore { reduce(state, action) { switch(action.type) { case 'nodes/loaded': + // get simulator IDs if (Array.isArray(action.data)) { action.data.forEach(node => { NodesDataManager.getSimulators(node); diff --git a/src/stores/simulator-data-store.js b/src/stores/simulator-data-store.js index d52ac1d..457768c 100644 --- a/src/stores/simulator-data-store.js +++ b/src/stores/simulator-data-store.js @@ -40,48 +40,58 @@ class SimulationDataStore extends ReduceStore { switch (action.type) { case 'simulatorData/open': - SimulatorDataDataManager.open(action.endpoint, action.identifier, action.signals); + SimulatorDataDataManager.open(action.endpoint, action.node); return state; case 'simulatorData/opened': // create entry for simulator - state[action.identifier] = { signals: action.signals, values: [], sequence: null, timestamp: null }; + /*state[action.identifier] = { signals: action.signals, values: [], sequence: null, timestamp: null }; for (i = 0; i < action.signals; i++) { state[action.identifier].values.push([]); - } + }*/ + + state[action.node._id] = {}; + + action.node.simulators.forEach(simulator => { + state[action.node._id][simulator.id] = { sequence: -1, values: [] }; + }); return state; case 'simulatorData/data-changed': // only add data, if newer than current - if (state[action.identifier].sequence < action.data.sequence) { + if (state[action.node._id][action.data.id].sequence < action.data.sequence) { // add data to simulator - for (i = 0; i < state[action.identifier].signals; i++) { - state[action.identifier].values[i].push({ x: action.data.timestamp, y: action.data.values[i] }); + for (i = 0; i < action.data.length; i++) { + while (state[action.node._id][action.data.id].values.length < i + 1) { + state[action.node._id][action.data.id].values.push([]); + } + + state[action.node._id][action.data.id].values[i].push({ x: action.data.timestamp, y: action.data.values[i] }); // erase old values - if (state[action.identifier].values[i].length > MAX_VALUES) { - const pos = state[action.identifier].values[i].length - MAX_VALUES; - state[action.identifier].values[i].splice(0, pos); + if (state[action.node._id][action.data.id].values[i].length > MAX_VALUES) { + const pos = state[action.node._id][action.data.id].values[i].length - MAX_VALUES; + state[action.node._id][action.data.id].values[i].splice(0, pos); } } // update metadata - state[action.identifier].timestamp = action.data.timestamp; - state[action.identifier].sequence = action.data.sequence; + state[action.node._id][action.data.id].timestamp = action.data.timestamp; + state[action.node._id][action.data.id].sequence = action.data.sequence; // explicit call to prevent array copy this.__emitChange(); } else { - console.log('same sequence ' + state[action.identifier].sequence + ' ' + action.data.sequence); + console.log('same sequence ' + state[action.node._id][action.data.id].sequence + ' ' + action.data.sequence); } return state; case 'simulatorData/closed': // close and delete socket - if (state[action.identifier] != null) { + if (state[action.node] != null) { // delete data //delete state[action.identifier]; //state[action.identifier] = null; From b7156f5325ba0d0ec1b8fe7619c79ee29affb172 Mon Sep 17 00:00:00 2001 From: Markus Grigull Date: Tue, 11 Jul 2017 19:46:45 +0200 Subject: [PATCH 08/16] Change widget value to new simulator data --- .../dialog/edit-widget-signal-control.js | 8 ++++---- .../dialog/edit-widget-simulator-control.js | 13 ++++++------- src/components/dialog/edit-widget.js | 15 ++++++++++++--- src/components/dialog/new-simulation-model.js | 9 +++++---- src/components/widget-value.js | 9 ++++++--- src/containers/simulation.js | 10 +++++++++- src/containers/visualization.js | 19 ++++++++++--------- src/stores/simulator-data-store.js | 6 ------ 8 files changed, 52 insertions(+), 37 deletions(-) diff --git a/src/components/dialog/edit-widget-signal-control.js b/src/components/dialog/edit-widget-signal-control.js index 623b416..98d9b06 100644 --- a/src/components/dialog/edit-widget-signal-control.js +++ b/src/components/dialog/edit-widget-signal-control.js @@ -28,7 +28,7 @@ class EditWidgetSignalControl extends Component { this.state = { widget: { - simulator: '' + simulator: {} } }; } @@ -43,10 +43,10 @@ class EditWidgetSignalControl extends Component { if (this.props.simulation) { // get selected simulation model - const simulationModel = this.props.simulation.models.find( model => model.simulator === this.state.widget.simulator ); + const simulationModel = this.props.simulation.models.find( model => model.simulator.node === this.state.widget.simulator.node && model.simulator.simulator === this.state.widget.simulator.simulator ); // If simulation model update the signals to render - signalsToRender = simulationModel? simulationModel.mapping : []; + signalsToRender = simulationModel ? simulationModel.mapping : []; } return ( @@ -68,4 +68,4 @@ class EditWidgetSignalControl extends Component { } } -export default EditWidgetSignalControl; \ No newline at end of file +export default EditWidgetSignalControl; diff --git a/src/components/dialog/edit-widget-simulator-control.js b/src/components/dialog/edit-widget-simulator-control.js index 48186ef..ec4cec5 100644 --- a/src/components/dialog/edit-widget-simulator-control.js +++ b/src/components/dialog/edit-widget-simulator-control.js @@ -28,7 +28,7 @@ class EditWidgetSimulatorControl extends Component { this.state = { widget: { - simulator: '' + simulator: {} } }; } @@ -39,17 +39,16 @@ class EditWidgetSimulatorControl extends Component { } render() { - return ( - Simulator - this.props.handleChange(e)}> + Simulation Model + this.props.handleChange(e)}> { this.props.simulation.models.length === 0? ( - + ) : ( this.props.simulation.models.map((model, index) => ( - + ))) } @@ -58,4 +57,4 @@ class EditWidgetSimulatorControl extends Component { } } -export default EditWidgetSimulatorControl; \ No newline at end of file +export default EditWidgetSimulatorControl; diff --git a/src/components/dialog/edit-widget.js b/src/components/dialog/edit-widget.js index 8c2277f..e3d56ed 100644 --- a/src/components/dialog/edit-widget.js +++ b/src/components/dialog/edit-widget.js @@ -41,7 +41,7 @@ class EditWidgetDialog extends Component { this.state = { temporal: { name: '', - simulator: '', + simulator: {}, signal: 0 } }; @@ -58,7 +58,16 @@ class EditWidgetDialog extends Component { handleChange(e) { if (e.constructor === Array) { // Every property in the array will be updated - let changes = e.reduce( (changesObject, event) => { changesObject[event.target.id] = event.target.value; return changesObject }, {}); + let changes = e.reduce( (changesObject, event) => { + if (event.target.id === 'simulator') { + changesObject[event.target.id] = JSON.parse(event.target.value); + } else { + changesObject[event.target.id] = event.target.value; + } + + return changesObject; + }, {}); + this.setState({ temporal: Object.assign({}, this.state.temporal, changes ) }); } else { let changeObject = {}; @@ -87,7 +96,7 @@ class EditWidgetDialog extends Component { } render() { - + let controls = null; if (this.props.widget) { controls = createControls( diff --git a/src/components/dialog/new-simulation-model.js b/src/components/dialog/new-simulation-model.js index 0acc38d..8ae219c 100644 --- a/src/components/dialog/new-simulation-model.js +++ b/src/components/dialog/new-simulation-model.js @@ -48,6 +48,7 @@ class NewSimulationModelDialog extends Component { onClose(canceled) { if (canceled === false) { + console.log(this.state); this.props.onClose(this.state); } else { this.props.onClose(); @@ -69,8 +70,8 @@ class NewSimulationModelDialog extends Component { } if (e.target.id === 'simulator') { - var value = e.target.value.split("/"); - this.setState({ simulator: { node: value[0], simulator: value[1] } }); + console.log(e.target.value); + this.setState({ simulator: JSON.parse(e.target.value) }); } else { this.setState({ [e.target.id]: e.target.value }); } @@ -91,7 +92,7 @@ class NewSimulationModelDialog extends Component { resetState() { this.setState({ name: '', - simulator: { node: this.props.nodes[0] ? this.props.nodes[0].name : '', simulator: this.props.nodes[0].simulators[0] ? this.props.nodes[0].simulators[0].name : '' }, + simulator: { node: this.props.nodes[0] ? this.props.nodes[0]._id : '', simulator: this.props.nodes[0].simulators[0] ? 0 : '' }, length: '1', mapping: [ { name: 'Signal', type: 'Type' } ] }); @@ -138,7 +139,7 @@ class NewSimulationModelDialog extends Component { this.handleChange(e)}> {this.props.nodes.map(node => ( node.simulators.map((simulator, index) => ( - + )) ))} diff --git a/src/components/widget-value.js b/src/components/widget-value.js index eeceeed..f3451aa 100644 --- a/src/components/widget-value.js +++ b/src/components/widget-value.js @@ -32,15 +32,18 @@ class WidgetValue extends Component { componentWillReceiveProps(nextProps) { // update value - const simulator = nextProps.widget.simulator; + const simulator = nextProps.widget.simulator.simulator; + const node = nextProps.widget.simulator.node; - if (nextProps.data == null || nextProps.data[simulator] == null || nextProps.data[simulator].values == null) { + //console.log(nextProps.widget.simulator); + + if (nextProps.data == null || nextProps.data[node] == null || nextProps.data[node][simulator] == null || nextProps.data[node][simulator].values == null) { this.setState({ value: '' }); return; } // check if value has changed - const signal = nextProps.data[simulator].values[nextProps.widget.signal]; + const signal = nextProps.data[node][simulator].values[nextProps.widget.signal]; if (this.state.value !== signal[signal.length - 1].y) { this.setState({ value: signal[signal.length - 1].y }); } diff --git a/src/containers/simulation.js b/src/containers/simulation.js index 52f5649..0aff4bf 100644 --- a/src/containers/simulation.js +++ b/src/containers/simulation.js @@ -120,7 +120,15 @@ class Simulation extends Component { } getSimulatorName(simulator) { - return simulator.node + '/' + simulator.simulator; + var name = "undefined"; + + this.state.nodes.forEach(node => { + if (node._id === simulator.node) { + name = node.name + '/' + node.simulators[simulator.simulator].name; + } + }); + + return name; } render() { diff --git a/src/containers/visualization.js b/src/containers/visualization.js index a526df3..66c5403 100644 --- a/src/containers/visualization.js +++ b/src/containers/visualization.js @@ -65,13 +65,13 @@ class Visualization extends Component { editModal: prevState.editModal || false, modalData: prevState.modalData || null, modalIndex: prevState.modalIndex || null, - + maxWidgetHeight: prevState.maxWidgetHeight || 0, dropZoneHeight: prevState.dropZoneHeight || 0, last_widget_key: prevState.last_widget_key || 0 }; } - + componentWillMount() { AppDispatcher.dispatch({ type: 'visualizations/start-load' @@ -154,6 +154,7 @@ class Visualization extends Component { NotificationsDataManager.addNotification(NotificationsFactory.NO_SIM_MODEL_AVAILABLE); } else { defaultSimulator = this.state.simulation.models[0].simulator; + console.log(defaultSimulator); } // create new widget @@ -167,7 +168,7 @@ class Visualization extends Component { var visualization = Object.assign({}, this.state.visualization, { widgets: new_widgets }); - + this.increaseHeightWithWidget(widget); this.setState({ visualization: visualization }); } @@ -185,7 +186,7 @@ class Visualization extends Component { var visualization = Object.assign({}, this.state.visualization, { widgets: new_widgets }); - + // Check if the height needs to be increased, the section may have shrunk if not if (!this.increaseHeightWithWidget(updated_widget)) { this.computeHeightWithWidgets(visualization.widgets); @@ -201,12 +202,12 @@ class Visualization extends Component { let maxHeight = Object.keys(widgets).reduce( (maxHeightSoFar, widgetKey) => { let thisWidget = widgets[widgetKey]; let thisWidgetHeight = thisWidget.y + thisWidget.height; - + return thisWidgetHeight > maxHeightSoFar? thisWidgetHeight : maxHeightSoFar; }, 0); - this.setState({ - maxWidgetHeight: maxHeight, + this.setState({ + maxWidgetHeight: maxHeight, dropZoneHeight: maxHeight + 40 }); } @@ -219,8 +220,8 @@ class Visualization extends Component { let thisWidgetHeight = widget.y + widget.height; if (thisWidgetHeight > this.state.maxWidgetHeight) { increased = true; - this.setState({ - maxWidgetHeight: thisWidgetHeight, + this.setState({ + maxWidgetHeight: thisWidgetHeight, dropZoneHeight: thisWidgetHeight + 40 }); } diff --git a/src/stores/simulator-data-store.js b/src/stores/simulator-data-store.js index 457768c..a6bf05c 100644 --- a/src/stores/simulator-data-store.js +++ b/src/stores/simulator-data-store.js @@ -45,12 +45,6 @@ class SimulationDataStore extends ReduceStore { case 'simulatorData/opened': // create entry for simulator - /*state[action.identifier] = { signals: action.signals, values: [], sequence: null, timestamp: null }; - - for (i = 0; i < action.signals; i++) { - state[action.identifier].values.push([]); - }*/ - state[action.node._id] = {}; action.node.simulators.forEach(simulator => { From f66d75aede30715203d44214c6fa7fcf0bc49728 Mon Sep 17 00:00:00 2001 From: Markus Grigull Date: Tue, 11 Jul 2017 21:09:19 +0200 Subject: [PATCH 09/16] Change widget table data --- src/components/dialog/edit-widget.js | 7 ++++++- src/components/widget-table.js | 12 ++++++------ src/components/widget-value.js | 2 +- src/containers/visualization.js | 3 +-- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/components/dialog/edit-widget.js b/src/components/dialog/edit-widget.js index e3d56ed..a795fc2 100644 --- a/src/components/dialog/edit-widget.js +++ b/src/components/dialog/edit-widget.js @@ -71,7 +71,12 @@ class EditWidgetDialog extends Component { this.setState({ temporal: Object.assign({}, this.state.temporal, changes ) }); } else { let changeObject = {}; - changeObject[e.target.id] = e.target.value; + if (e.target.id === 'simulator') { + changeObject[e.target.id] = JSON.parse(e.target.value); + } else { + changeObject[e.target.id] = e.target.value; + } + this.setState({ temporal: Object.assign({}, this.state.temporal, changeObject ) }); } } diff --git a/src/components/widget-table.js b/src/components/widget-table.js index 9c48a00..44d3966 100644 --- a/src/components/widget-table.js +++ b/src/components/widget-table.js @@ -38,33 +38,33 @@ class WidgetTable extends Component { // check data const simulator = nextProps.widget.simulator; - if (nextProps.simulation == null || nextProps.data == null || nextProps.data[simulator] == null || nextProps.data[simulator].length === 0 || nextProps.data[simulator].values[0].length === 0) { + if (nextProps.simulation == null || nextProps.data == null || nextProps.data[simulator.node][simulator.simulator] == null || nextProps.data[simulator.node][simulator.simulator].length === 0 || nextProps.data[simulator.node][simulator.simulator].values.length === 0 || nextProps.data[simulator.node][simulator.simulator].values[0].length === 0) { // clear values this.setState({ rows: [], sequence: null }); return; } // check if new data, otherwise skip - if (this.state.sequence >= nextProps.data[simulator].sequence) { + /*if (this.state.sequence >= nextProps.data[simulator.node][simulator.simulator].sequence) { return; - } + }*/ // get simulation model const simulationModel = nextProps.simulation.models.find((model) => { - return (model.simulator === simulator); + return (model.simulator.node === simulator.node && model.simulator.simulator === simulator.simulator); }); // get rows var rows = []; - nextProps.data[simulator].values.forEach((signal, index) => { + nextProps.data[simulator.node][simulator.simulator].values.forEach((signal, index) => { rows.push({ name: simulationModel.mapping[index].name, value: signal[signal.length - 1].y.toFixed(3) }) }); - this.setState({ rows: rows, sequence: nextProps.data[simulator].sequence }); + this.setState({ rows: rows, sequence: nextProps.data[simulator.node][simulator.simulator].sequence }); } render() { diff --git a/src/components/widget-value.js b/src/components/widget-value.js index f3451aa..fdbb04b 100644 --- a/src/components/widget-value.js +++ b/src/components/widget-value.js @@ -44,7 +44,7 @@ class WidgetValue extends Component { // check if value has changed const signal = nextProps.data[node][simulator].values[nextProps.widget.signal]; - if (this.state.value !== signal[signal.length - 1].y) { + if (signal != null && this.state.value !== signal[signal.length - 1].y) { this.setState({ value: signal[signal.length - 1].y }); } } diff --git a/src/containers/visualization.js b/src/containers/visualization.js index 66c5403..425f394 100644 --- a/src/containers/visualization.js +++ b/src/containers/visualization.js @@ -154,7 +154,6 @@ class Visualization extends Component { NotificationsDataManager.addNotification(NotificationsFactory.NO_SIM_MODEL_AVAILABLE); } else { defaultSimulator = this.state.simulation.models[0].simulator; - console.log(defaultSimulator); } // create new widget @@ -236,7 +235,7 @@ class Visualization extends Component { if (data) { // save changes temporarily var widgets_update = {}; - widgets_update[this.state.modalIndex] = data; + widgets_update[this.state.modalIndex] = data; var new_widgets = Object.assign({}, this.state.visualization.widgets, widgets_update); From 9f3145aedf535eaa232098ee74d66f7874749000 Mon Sep 17 00:00:00 2001 From: Markus Grigull Date: Wed, 12 Jul 2017 11:22:52 +0200 Subject: [PATCH 10/16] Update plot widget to new simulator connection --- src/components/dialog/edit-widget-signals-control.js | 8 ++++---- src/components/widget-plot.js | 7 +++---- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/components/dialog/edit-widget-signals-control.js b/src/components/dialog/edit-widget-signals-control.js index 76b832b..c52b928 100644 --- a/src/components/dialog/edit-widget-signals-control.js +++ b/src/components/dialog/edit-widget-signals-control.js @@ -28,7 +28,7 @@ class EditWidgetSignalsControl extends Component { this.state = { widget: { - simulator: '' + simulator: {} } }; } @@ -58,12 +58,12 @@ class EditWidgetSignalsControl extends Component { if (this.props.simulation) { // get selected simulation model - const simulationModel = this.props.simulation.models.find( model => model.simulator === this.state.widget.simulator ); + const simulationModel = this.props.simulation.models.find( model => model.simulator.node === this.state.widget.simulator.node && model.simulator.simulator === this.state.widget.simulator.simulator ); // If simulation model update the signals to render signalsToRender = simulationModel? simulationModel.mapping : []; } - + return ( Signals @@ -81,4 +81,4 @@ class EditWidgetSignalsControl extends Component { } } -export default EditWidgetSignalsControl; \ No newline at end of file +export default EditWidgetSignalsControl; diff --git a/src/components/widget-plot.js b/src/components/widget-plot.js index ff0b1b1..c02aa97 100644 --- a/src/components/widget-plot.js +++ b/src/components/widget-plot.js @@ -27,7 +27,6 @@ import PlotLegend from './widget-plot/plot-legend'; class WidgetPlot extends Component { render() { - const simulator = this.props.widget.simulator; const simulation = this.props.simulation; let legendSignals = []; @@ -36,10 +35,10 @@ class WidgetPlot extends Component { // Proceed if a simulation with models and a simulator are available if (simulator && simulation && simulation.models.length > 0) { - const model = simulation.models.find( (model) => model.simulator === simulator ); + const model = simulation.models.find( model => model.simulator.node === simulator.node && model.simulator.simulator === simulator.simulator ); const chosenSignals = this.props.widget.signals; - simulatorData = this.props.data[simulator]; + simulatorData = this.props.data[simulator.node][simulator.simulator]; // Query the signals that will be displayed in the legend legendSignals = model.mapping.reduce( (accum, model_signal, signal_index) => { @@ -53,7 +52,7 @@ class WidgetPlot extends Component { return (

{this.props.widget.name}

- +
From 98eda9ced9cd2e7c2a24b65d7ee0569d390c7782 Mon Sep 17 00:00:00 2001 From: Markus Grigull Date: Wed, 12 Jul 2017 12:20:44 +0200 Subject: [PATCH 11/16] Update table plot to new simulator connection --- src/components/widget-plot-table.js | 51 ++++++++++++++++------------- src/components/widget-plot/plot.js | 22 ++++++------- 2 files changed, 39 insertions(+), 34 deletions(-) diff --git a/src/components/widget-plot-table.js b/src/components/widget-plot-table.js index 0507b3f..7862a05 100644 --- a/src/components/widget-plot-table.js +++ b/src/components/widget-plot-table.js @@ -45,7 +45,7 @@ class WidgetPlotTable extends Component { // Identify if there was a change in the preselected signals if (nextProps.simulation && (JSON.stringify(nextProps.widget.preselectedSignals) !== JSON.stringify(this.props.widget.preselectedSignals) || this.state.preselectedSignals.length === 0)) { - + // Update the currently selected signals by intersecting with the preselected signals // Do the same with the plot values var intersection = this.computeIntersection(nextProps.widget.preselectedSignals, nextProps.widget.signals); @@ -54,20 +54,19 @@ class WidgetPlotTable extends Component { this.updatePreselectedSignalsState(nextProps); return; } - } // Perform the intersection of the lists, alternatively could be done with Sets ensuring unique values computeIntersection(preselectedSignals, selectedSignals) { return preselectedSignals.filter( s => selectedSignals.includes(s)); } - + updatePreselectedSignalsState(nextProps) { const simulator = nextProps.widget.simulator; // get simulation model const simulationModel = nextProps.simulation.models.find((model) => { - return (model.simulator === simulator); + return (model.simulator.node === simulator.node && model.simulator.simulator === simulator.simulator); }); let preselectedSignals = []; @@ -89,13 +88,13 @@ class WidgetPlotTable extends Component { return accum; }, []); } - + this.setState({ preselectedSignals: preselectedSignals }); } updateSignalSelection(signal_index, checked) { // Update the selected signals and propagate to parent component - var new_widget = Object.assign({}, this.props.widget, { + var new_widget = Object.assign({}, this.props.widget, { signals: checked? this.state.signals.concat(signal_index) : this.state.signals.filter( (idx) => idx !== signal_index ) }); this.props.onWidgetChange(new_widget); @@ -106,31 +105,37 @@ class WidgetPlotTable extends Component { // Data passed to plot let simulator = this.props.widget.simulator; - let simulatorData = this.props.data[simulator]; + let simulatorData = []; + + if (this.props.data[simulator.node] != null && this.props.data[simulator.node][simulator.simulator] != null) { + simulatorData = this.props.data[simulator.node][simulator.simulator]; + } if (this.state.preselectedSignals && this.state.preselectedSignals.length > 0) { // Create checkboxes using the signal indices from simulation model checkBoxes = this.state.preselectedSignals.map( (signal) => { - var checked = this.state.signals.indexOf(signal.index) > -1; - var chkBxClasses = classNames({ - 'btn': true, - 'btn-default': true, - 'active': checked - }); - return this.updateSignalSelection(signal.index, e.target.checked) } > { signal.name } - }); + var checked = this.state.signals.indexOf(signal.index) > -1; + var chkBxClasses = classNames({ + 'btn': true, + 'btn-default': true, + 'active': checked + }); + return this.updateSignalSelection(signal.index, e.target.checked) } > { signal.name } + }); } // Prepare an array with the signals to show in the legend var legendSignals = this.state.preselectedSignals.reduce( (accum, signal, i) => { - if (this.state.signals.includes(signal.index)) { - accum.push({ - index: signal.index, - name: signal.name - }) - } - return accum; - }, []); + if (this.state.signals.includes(signal.index)) { + accum.push({ + index: signal.index, + name: signal.name + }); + } + return accum; + }, []); + + return (
diff --git a/src/components/widget-plot/plot.js b/src/components/widget-plot/plot.js index 6bae9d0..7b0c3aa 100644 --- a/src/components/widget-plot/plot.js +++ b/src/components/widget-plot/plot.js @@ -14,7 +14,7 @@ import { scaleOrdinal, schemeCategory10 } from 'd3-scale'; class Plot extends Component { constructor(props) { super(props); - + this.chartWrapper = null; // Initialize plot size and data @@ -24,7 +24,7 @@ class Plot extends Component { ); } - // Get an object with 'invisible' init data for the last minute. + // Get an object with 'invisible' init data for the last minute. // Include start/end timestamps if required. getPlotInitData(withRangeTimestamps = false) { @@ -32,8 +32,8 @@ class Plot extends Component { const initFirstTime = initSecondTime - 1000 * 60; // Decrease 1 min const values = [{ values: [{x: initFirstTime, y: 0}], strokeWidth: 0 }]; - let output = withRangeTimestamps? - { sequence: 0, values: values, firstTimestamp: initFirstTime, latestTimestamp: initSecondTime, } : + let output = withRangeTimestamps? + { sequence: 0, values: values, firstTimestamp: initFirstTime, latestTimestamp: initSecondTime, } : { sequence: 0, values: values }; return output; @@ -58,19 +58,19 @@ class Plot extends Component { // Identify simulation reset if (nextData == null || nextData.length === 0 || nextData.values[0].length === 0) { this.clearPlot(); return; } - + // check if new data, otherwise skip if (this.state.sequence >= nextData.sequence) { return; } - + this.updatePlotData(nextProps); } signalsWereJustCleared(nextProps) { - return this.props.signals && - nextProps.signals && - this.props.signals.length > 0 && + return this.props.signals && + nextProps.signals && + this.props.signals.length > 0 && nextProps.signals.length === 0; } @@ -102,7 +102,7 @@ class Plot extends Component { nextProps.signals.forEach((signal_index, i, arr) => ( // Include signal index, useful to relate them to the signal selection values.push( - { + { index: signal_index, values: nextData.values[signal_index].slice(firstIndex, nextData.values[signal_index].length - 1)}) )); @@ -138,4 +138,4 @@ class Plot extends Component { } -export default Plot; \ No newline at end of file +export default Plot; From fdc95601b1c01145465f022e1516311a844e8f68 Mon Sep 17 00:00:00 2001 From: Markus Grigull Date: Wed, 12 Jul 2017 12:25:09 +0200 Subject: [PATCH 12/16] Update gauge to new simulator connection --- src/components/widget-gauge.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/widget-gauge.js b/src/components/widget-gauge.js index b4365fe..6f5ac06 100644 --- a/src/components/widget-gauge.js +++ b/src/components/widget-gauge.js @@ -69,23 +69,23 @@ class WidgetGauge extends Component { return this.state.value !== nextState.value; } - componentWillReceiveProps(nextProps) { + componentWillReceiveProps(nextProps) { // update value const simulator = nextProps.widget.simulator; - if (nextProps.data == null || nextProps.data[simulator] == null || nextProps.data[simulator].values == null) { + if (nextProps.data == null || nextProps.data[simulator.node][simulator.simulator] == null || nextProps.data[simulator.node][simulator.simulator].values == null) { this.setState({ value: 0 }); return; } // check if value has changed - const signal = nextProps.data[simulator].values[nextProps.widget.signal]; + const signal = nextProps.data[simulator.node][simulator.simulator].values[nextProps.widget.signal]; // Take just 3 decimal positions // Note: Favor this method over Number.toFixed(n) in order to avoid a type conversion, since it returns a String - const new_value = Math.round( signal[signal.length - 1].y * 1e3 ) / 1e3; + const new_value = Math.round( signal[signal.length - 1].y * 1e3 ) / 1e3; if (this.state.value !== new_value) { this.setState({ value: new_value }); - + // update gauge's value this.gauge.set(new_value); } @@ -101,7 +101,7 @@ class WidgetGauge extends Component { var signalType = null; if (this.props.simulation) { - var simulationModel = this.props.simulation.models.filter((model) => model.simulator === this.props.widget.simulator)[0]; + var simulationModel = this.props.simulation.models.filter((model) => model.simulator.node === this.props.widget.simulator.node && model.simulator.simulator === this.props.widget.simulator.simulator)[0]; signalType = simulationModel && simulationModel.length > 0? simulationModel.mapping[this.props.widget.signal].type : ''; } From e390776480228419a9abae6a68148760ce5e1e09 Mon Sep 17 00:00:00 2001 From: Markus Grigull Date: Wed, 12 Jul 2017 12:50:38 +0200 Subject: [PATCH 13/16] Fix gauge signal data --- src/components/widget-gauge.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/components/widget-gauge.js b/src/components/widget-gauge.js index 6f5ac06..800f043 100644 --- a/src/components/widget-gauge.js +++ b/src/components/widget-gauge.js @@ -82,12 +82,14 @@ class WidgetGauge extends Component { const signal = nextProps.data[simulator.node][simulator.simulator].values[nextProps.widget.signal]; // Take just 3 decimal positions // Note: Favor this method over Number.toFixed(n) in order to avoid a type conversion, since it returns a String - const new_value = Math.round( signal[signal.length - 1].y * 1e3 ) / 1e3; - if (this.state.value !== new_value) { - this.setState({ value: new_value }); + if (signal != null) { + const new_value = Math.round( signal[signal.length - 1].y * 1e3 ) / 1e3; + if (this.state.value !== new_value) { + this.setState({ value: new_value }); - // update gauge's value - this.gauge.set(new_value); + // update gauge's value + this.gauge.set(new_value); + } } } @@ -102,7 +104,7 @@ class WidgetGauge extends Component { if (this.props.simulation) { var simulationModel = this.props.simulation.models.filter((model) => model.simulator.node === this.props.widget.simulator.node && model.simulator.simulator === this.props.widget.simulator.simulator)[0]; - signalType = simulationModel && simulationModel.length > 0? simulationModel.mapping[this.props.widget.signal].type : ''; + signalType = (simulationModel != null && simulationModel.length > 0) ? simulationModel.mapping[this.props.widget.signal].type : ''; } return ( From 47570d74d0a764a7802bd67658feb3a5d64829c2 Mon Sep 17 00:00:00 2001 From: Markus Grigull Date: Thu, 13 Jul 2017 11:39:16 +0200 Subject: [PATCH 14/16] Ensure unique node and simulator names in dialogs Node names are checked to be unique in dialogs Simulator names are checked to be unique in dialogs on one node --- src/components/dialog/edit-node.js | 4 ++-- src/components/dialog/edit-simulator.js | 2 +- src/components/dialog/new-node.js | 4 ++-- src/components/dialog/new-simulator.js | 2 +- src/containers/simulators.js | 8 ++++---- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/components/dialog/edit-node.js b/src/components/dialog/edit-node.js index da72a4a..b6475c3 100644 --- a/src/components/dialog/edit-node.js +++ b/src/components/dialog/edit-node.js @@ -60,11 +60,11 @@ class NewNodeDialog extends React.Component { var endpoint = true; var name = true; - if (this.state.name === '') { + if (this.state.name === '' || this.props.nodes.find(node => node._id !== this.state._id && node.name === this.state.name) !== undefined) { name = false; } - if (this.state.endpoint === '') { + if (this.state.endpoint === '' || this.props.nodes.find(node => node._id !== this.state._id && node.endpoint === this.state.endpoint) !== undefined) { endpoint = false; } diff --git a/src/components/dialog/edit-simulator.js b/src/components/dialog/edit-simulator.js index ef8082a..159b45c 100644 --- a/src/components/dialog/edit-simulator.js +++ b/src/components/dialog/edit-simulator.js @@ -63,7 +63,7 @@ class EditSimulatorDialog extends Component { // check all controls var name = true; - if (this.state.name === '') { + if (this.state.name === '' || this.props.node.simulators.find(simulator => this.props.simulator.name !== this.state.name && simulator.name === this.state.name) !== undefined) { name = false; } diff --git a/src/components/dialog/new-node.js b/src/components/dialog/new-node.js index ae89a35..44afa9d 100644 --- a/src/components/dialog/new-node.js +++ b/src/components/dialog/new-node.js @@ -59,11 +59,11 @@ class NewNodeDialog extends React.Component { var endpoint = true; var name = true; - if (this.state.name === '') { + if (this.state.name === '' || this.props.nodes.find(node => node.name === this.state.name) !== undefined) { name = false; } - if (this.state.endpoint === '') { + if (this.state.endpoint === '' || this.props.nodes.find(node => node.endpoint === this.state.endpoint) !== undefined) { endpoint = false; } diff --git a/src/components/dialog/new-simulator.js b/src/components/dialog/new-simulator.js index 8e22e3d..f4c293d 100644 --- a/src/components/dialog/new-simulator.js +++ b/src/components/dialog/new-simulator.js @@ -60,7 +60,7 @@ class NewSimulatorDialog extends Component { // check all controls var name = true; - if (this.state.name === '') { + if (this.state.name === '' || this.props.node.simulators.find(simulator => simulator.name === this.state.name) !== undefined) { name = false; } diff --git a/src/containers/simulators.js b/src/containers/simulators.js index acaf5ad..3e258a6 100644 --- a/src/containers/simulators.js +++ b/src/containers/simulators.js @@ -200,12 +200,12 @@ class Simulators extends Component { this.onTreeDataChange(treeData)} onNodeDelete={(node) => this.showDeleteNodeModal(node)} onNodeEdit={(node) => this.showEditNodeModal(node)} onNodeAdd={(node) => this.showAddSimulatorModal(node)} onSimulatorEdit={(node, index) => this.showEditSimulatorModal(node, index)} onSimulatorDelete={(node, index) => this.showDeleteSimulatorModal(node, index)} /> - this.closeNewNodeModal(data)} /> - this.closeEditNodeModal(data)} /> - this.closeAddSimulatorModal(data)} /> + this.closeNewNodeModal(data)} nodes={this.state.nodes} /> + this.closeEditNodeModal(data)} nodes={this.state.nodes} /> + this.closeAddSimulatorModal(data)} node={this.state.modalData}/> {this.state.editSimulatorModal && - this.closeEditSimulatorModal(data)} /> + this.closeEditSimulatorModal(data)} node={this.state.modalData} /> } From 5f6417d695654cf63ec7c5024173001452d0b08a Mon Sep 17 00:00:00 2001 From: Markus Grigull Date: Thu, 13 Jul 2017 11:46:48 +0200 Subject: [PATCH 15/16] Discard unrequested data --- src/stores/simulator-data-store.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/stores/simulator-data-store.js b/src/stores/simulator-data-store.js index a6bf05c..5f5a829 100644 --- a/src/stores/simulator-data-store.js +++ b/src/stores/simulator-data-store.js @@ -54,6 +54,11 @@ class SimulationDataStore extends ReduceStore { return state; case 'simulatorData/data-changed': + // check if data is required, otherwise discard + if (state[action.node._id] == null || state[action.data.id] == null) { + return state; + } + // only add data, if newer than current if (state[action.node._id][action.data.id].sequence < action.data.sequence) { // add data to simulator From cfb760fac8429ccbb0ec4cd9e43b776f47ce24d9 Mon Sep 17 00:00:00 2001 From: Markus Grigull Date: Thu, 13 Jul 2017 12:04:59 +0200 Subject: [PATCH 16/16] Display simulator status in tree-view Remove unneeded files anymore --- src/components/node-tree.js | 2 +- src/data-managers/simulators-data-manager.js | 107 ---------------- src/stores/simulator-store.js | 128 ------------------- 3 files changed, 1 insertion(+), 236 deletions(-) delete mode 100644 src/data-managers/simulators-data-manager.js delete mode 100644 src/stores/simulator-store.js diff --git a/src/components/node-tree.js b/src/components/node-tree.js index 1e60038..24c1be3 100644 --- a/src/components/node-tree.js +++ b/src/components/node-tree.js @@ -67,7 +67,7 @@ class NodeTree extends React.Component { var parent = { title: node.name, subtitle: node.endpoint, id: node._id, config: node.config, children: [], expanded: true }; node.simulators.forEach((simulator) => { - parent.children.push({ title: simulator.name }); + parent.children.push({ title: simulator.name, subtitle: simulator.id != null ? 'Online' : 'Offline' }); }); treeData.push(parent); diff --git a/src/data-managers/simulators-data-manager.js b/src/data-managers/simulators-data-manager.js deleted file mode 100644 index 535f1e2..0000000 --- a/src/data-managers/simulators-data-manager.js +++ /dev/null @@ -1,107 +0,0 @@ -/** - * File: simulators-data-manager.js - * Author: Markus Grigull - * Date: 02.03.2017 - * - * This file is part of VILLASweb. - * - * VILLASweb is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * VILLASweb is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with VILLASweb. If not, see . - ******************************************************************************/ - -import RestDataManager from './rest-data-manager'; -import RestAPI from '../api/rest-api'; -import AppDispatcher from '../app-dispatcher'; - -function isRunning(simulator) { - // get path to nodes.json and simulator name - var path = simulator.endpoint.substring(0, simulator.endpoint.lastIndexOf('/')); - var name = simulator.endpoint.substring(simulator.endpoint.lastIndexOf('/') + 1); - - var url = 'http://' + path + '/api/v1'; - var body = { - action: 'nodes', - id: '1234' /// @todo use random generated id - }; - - // send request - RestAPI.post(url, body).then(response => { - // check if simulator is running - simulator.running = false; - - if (response.id === body.id) { - response.response.forEach(sim => { - if (sim.name === name) { - simulator.running = true; - } - }); - } - - AppDispatcher.dispatch({ - type: 'simulators/running', - simulator: simulator, - running: simulator.running - }); - }).catch(error => { - simulator.running = false; - - AppDispatcher.dispatch({ - type: 'simulators/running', - simulator: simulator, - running: simulator.running - }); - }); -} - -class SimulatorsDataManager extends RestDataManager { - constructor() { - super('simulator', '/simulators', [ '_id', 'name', 'endpoint' ]); - - this._timers = []; - } - - startRunningDetection(obj) { - const simulator = JSON.parse(JSON.stringify(obj)); - - // check if timer is already running - const index = this._timers.findIndex(timer => { - return timer.simulator === simulator._id; - }); - - if (index !== -1) { - return; - } - - // do first request for fast response time - isRunning(simulator); - - // start new timer - const timerID = setInterval(isRunning, 5000, simulator); - this._timers.push({ id: timerID, simulator: simulator._id }); - } - - stopRunningDetection(simulator) { - // remove timer - const index = this._timers.findIndex(timer => { - return timer.simulator === simulator._id; - }); - - if (index !== -1) { - // stop timer and delete from list - clearInterval(this._timers[index].id); - this._timers.splice(index, 1); - } - } -} - -export default new SimulatorsDataManager(); diff --git a/src/stores/simulator-store.js b/src/stores/simulator-store.js deleted file mode 100644 index 4f8d6bd..0000000 --- a/src/stores/simulator-store.js +++ /dev/null @@ -1,128 +0,0 @@ -/** - * File: villas-store.js - * Author: Markus Grigull - * Date: 02.03.2017 - * - * This file is part of VILLASweb. - * - * VILLASweb is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * VILLASweb is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with VILLASweb. If not, see . - ******************************************************************************/ - -import ArrayStore from './array-store'; -import SimulatorsDataManager from '../data-managers/simulators-data-manager'; -import NotificationsDataManager from '../data-managers/notifications-data-manager'; - -class SimulatorStore extends ArrayStore { - constructor() { - super('simulators', SimulatorsDataManager); - } - - reduce(state, action) { - var simulator; - - switch (action.type) { - - case 'simulators/added': - SimulatorsDataManager.startRunningDetection(action.data); - - return super.reduce(state, action); - - case 'simulators/removed': - SimulatorsDataManager.stopRunningDetection(action.original); - - return super.reduce(state, action); - - case 'simulators/start-edit': - // An update will be requested, stop the 'runningDetection' already - SimulatorsDataManager.stopRunningDetection(action.data); - - return super.reduce(state, action); - - case 'simulators/edited': - // The update was done, resume the 'runningDetection' - SimulatorsDataManager.startRunningDetection(action.data); - - return super.reduce(state, action); - - case 'simulators/loaded': - // get simulator running state - if (Array.isArray(action.data)) { - action.data.forEach((simulator) => { - SimulatorsDataManager.startRunningDetection(simulator); - }); - } else { - SimulatorsDataManager.startRunningDetection(action.data); - } - - return super.reduce(state, action); - - case 'simulators/running': - // check if simulator running state changed - simulator = state.find(element => element._id === action.simulator._id ); - - // is this simulator still in the state? update it only if state changed - if (simulator && simulator.running !== action.simulator.running) { - state = this.updateElements(state, [ action.simulator ]); - } - - return state; - - case 'simulatorData/opened': - // get simulator - simulator = state.find(element => { - return element._id === action.identifier; - }); - - if (action.firstOpen === false) { - NotificationsDataManager.addNotification({ - title: 'Simulator online', - message: 'Simulator \'' + simulator.name + '\' went online.', - level: 'info' - }); - } - - // restart requesting again - SimulatorsDataManager.stopRunningDetection(simulator); - - return state; - - case 'simulatorData/closed': - // get simulator - simulator = state.find(element => { - return element._id === action.identifier; - }); - - // update running state - simulator.running = false; - - if (action.notification) { - NotificationsDataManager.addNotification({ - title: 'Simulator offline', - message: 'Simulator \'' + simulator.name + '\' went offline.', - level: 'info' - }); - - // restart requesting again - SimulatorsDataManager.startRunningDetection(simulator); - } - - return this.updateElements(state, [ simulator ]); - - default: - return super.reduce(state, action); - } - } -} - -export default new SimulatorStore();