diff --git a/package-lock.json b/package-lock.json index e3c97f0..38683b8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -251,7 +251,7 @@ "version": "https://registry.npmjs.org/async/-/async-2.5.0.tgz", "integrity": "sha1-hDGQ/WtzV6C54clW7d3V7IRitU0=", "requires": { - "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz" + "lodash": "4.17.5" } }, "async-each": { @@ -315,7 +315,7 @@ "convert-source-map": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.0.tgz", "debug": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", "json5": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "lodash": "4.17.5", "minimatch": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "path-is-absolute": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "private": "https://registry.npmjs.org/private/-/private-0.1.7.tgz", @@ -342,7 +342,7 @@ "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz", "detect-indent": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", "jsesc": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", - "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "lodash": "4.17.5", "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", "trim-right": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz" } @@ -382,7 +382,7 @@ "babel-helper-function-name": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz", - "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz" + "lodash": "4.17.5" } }, "babel-helper-explode-assignable-expression": { @@ -435,7 +435,7 @@ "requires": { "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz", - "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz" + "lodash": "4.17.5" } }, "babel-helper-remap-async-to-generator": { @@ -596,7 +596,7 @@ "babel-template": "https://registry.npmjs.org/babel-template/-/babel-template-6.25.0.tgz", "babel-traverse": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.25.0.tgz", "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz", - "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz" + "lodash": "4.17.5" } }, "babel-plugin-transform-es2015-classes": { @@ -938,7 +938,7 @@ "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", "core-js": "https://registry.npmjs.org/core-js/-/core-js-2.4.1.tgz", "home-or-tmp": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", - "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "lodash": "4.17.5", "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "source-map-support": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.15.tgz" }, @@ -960,7 +960,7 @@ "convert-source-map": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.0.tgz", "debug": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", "json5": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "lodash": "4.17.5", "minimatch": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "path-is-absolute": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "private": "https://registry.npmjs.org/private/-/private-0.1.7.tgz", @@ -996,7 +996,7 @@ "babel-traverse": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.25.0.tgz", "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz", "babylon": "https://registry.npmjs.org/babylon/-/babylon-6.17.4.tgz", - "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz" + "lodash": "4.17.5" } }, "babel-traverse": { @@ -1011,7 +1011,7 @@ "debug": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", "globals": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", "invariant": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz", - "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz" + "lodash": "4.17.5" } }, "babel-types": { @@ -1020,7 +1020,7 @@ "requires": { "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", "esutils": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "lodash": "4.17.5", "to-fast-properties": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz" } }, @@ -2504,7 +2504,7 @@ "js-yaml": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.7.0.tgz", "json-stable-stringify": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", "levn": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "lodash": "4.17.5", "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "natural-compare": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "optionator": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", @@ -2563,7 +2563,7 @@ "version": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-2.34.0.tgz", "integrity": "sha1-uYdfMUZS5QgWI8nSsYo0a7t1nAk=", "requires": { - "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz" + "lodash": "4.17.5" } }, "eslint-plugin-import": { @@ -4229,7 +4229,7 @@ "bluebird": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz", "html-minifier": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.3.tgz", "loader-utils": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", - "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "lodash": "4.17.5", "pretty-error": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.1.tgz", "toposort": "https://registry.npmjs.org/toposort/-/toposort-1.0.3.tgz" }, @@ -4317,7 +4317,7 @@ "requires": { "http-proxy": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.16.2.tgz", "is-glob": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "lodash": "4.17.5", "micromatch": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz" }, "dependencies": { @@ -4420,7 +4420,7 @@ "cli-cursor": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", "cli-width": "https://registry.npmjs.org/cli-width/-/cli-width-2.1.0.tgz", "figures": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", - "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "lodash": "4.17.5", "readline2": "https://registry.npmjs.org/readline2/-/readline2-1.0.1.tgz", "run-async": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz", "rx-lite": "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz", @@ -5224,8 +5224,9 @@ } }, "lodash": { - "version": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", - "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=" + "version": "4.17.5", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz", + "integrity": "sha512-svL3uiZf1RwhH+cWrfZn3A4+U58wbP0tGVTLQPbjplZxZ8ROD9VLuNgsRniTlLe7OlSqR79RUehXgpBW/s0IQw==" }, "lodash._reinterpolate": { "version": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", @@ -7532,7 +7533,7 @@ "cli-width": "https://registry.npmjs.org/cli-width/-/cli-width-2.1.0.tgz", "external-editor": "https://registry.npmjs.org/external-editor/-/external-editor-2.0.4.tgz", "figures": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "lodash": "4.17.5", "mute-stream": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", "run-async": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", "rx-lite": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz", @@ -9217,7 +9218,7 @@ "ajv": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", "ajv-keywords": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-1.5.1.tgz", "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "lodash": "4.17.5", "slice-ansi": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", "string-width": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz" }, @@ -9864,7 +9865,7 @@ "integrity": "sha1-a2xxiq3oolN5lXhLRr0umDYFfKo=", "requires": { "fs-extra": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz", - "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz" + "lodash": "4.17.5" }, "dependencies": { "fs-extra": { diff --git a/package.json b/package.json index f1125c4..0d4d4f6 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "flux": "^3.1.2", "gaugeJS": "^1.3.2", "immutable": "^3.8.1", + "lodash": "^4.17.5", "rc-slider": "^8.3.0", "react": "^15.4.2", "react-bootstrap": "^0.31.1", diff --git a/src/api/websocket-api.js b/src/api/websocket-api.js index a5ceba1..79da2f7 100644 --- a/src/api/websocket-api.js +++ b/src/api/websocket-api.js @@ -20,9 +20,9 @@ ******************************************************************************/ class WebsocketAPI { - addSocket(node, callbacks) { + addSocket(endpoint, callbacks) { // create web socket client - const socket = new WebSocket(this.getURL(node), 'live'); + const socket = new WebSocket(this.getURL(endpoint), 'live'); socket.binaryType = 'arraybuffer'; // register callbacks @@ -34,10 +34,10 @@ class WebsocketAPI { return socket; } - getURL(node) { + getURL(endpoint) { // create an anchor element (note: no need to append this element to the document) var link = document.createElement('a'); - link.href = node.endpoint; + link.href = endpoint; if (link.protocol === 'https:') link.protocol = 'wss:'; diff --git a/src/components/dialog/edit-simulation-model.js b/src/components/dialog/edit-simulation-model.js index 9aacadd..3450a54 100644 --- a/src/components/dialog/edit-simulation-model.js +++ b/src/components/dialog/edit-simulation-model.js @@ -27,14 +27,14 @@ import TableColumn from '../table-column'; import Dialog from './dialog'; class EditSimulationModelDialog extends React.Component { - valid: false; + valid = false; constructor(props) { super(props); this.state = { name: '', - simulator: { node: '', simulator: '' }, + simulator: '', outputLength: 1, inputLength: 1, outputMapping: [{ name: 'Signal', type: 'Type' }], @@ -74,12 +74,7 @@ class EditSimulationModelDialog extends React.Component { } } - 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 }); - } + this.setState({ [e.target.id]: e.target.value }); } handleMappingChange(key, event, row, column) { @@ -143,11 +138,9 @@ class EditSimulationModelDialog extends React.Component { Simulator - this.handleChange(e)}> - {this.props.nodes.map(node => ( - node.simulators.map((simulator, index) => ( - - )) + this.handleChange(e)}> + {this.props.simulators.map(simulator => ( + ))} diff --git a/src/components/dialog/edit-simulator.js b/src/components/dialog/edit-simulator.js index c3db013..6002226 100644 --- a/src/components/dialog/edit-simulator.js +++ b/src/components/dialog/edit-simulator.js @@ -21,24 +21,36 @@ import React from 'react'; import { FormGroup, FormControl, ControlLabel } from 'react-bootstrap'; +import _ from 'lodash'; import Dialog from './dialog'; class EditSimulatorDialog extends React.Component { - valid: false; + valid = true; constructor(props) { super(props); this.state = { - name: '' + name: '', + endpoint: '' }; } onClose(canceled) { if (canceled === false) { if (this.valid) { - this.props.onClose(this.state); + let data = {}; + + if (this.state.name != null && this.state.name !== "" && this.state.name !== _.get(this.props.simulator, 'rawProperties.name')) { + data.name = this.state.name; + } + + if (this.state.endpoint != null && this.state.endpoint !== "" && this.state.endpoint !== "http://" && this.state.endpoint !== _.get(this.props.simulator, 'rawProperties.endpoint')) { + data.endpoint = this.state.endpoint; + } + + this.props.onClose(data); } } else { this.props.onClose(); @@ -51,31 +63,23 @@ class EditSimulatorDialog extends React.Component { resetState() { this.setState({ - name: this.props.simulator.name + name: _.get(this.props.simulator, 'properties.name') || _.get(this.props.simulator, 'rawProperties.name'), + endpoint: _.get(this.props.simulator, 'properties.endpoint') || _.get(this.props.simulator, 'rawProperties.endpoint') }); } - validateForm(target) { - // check all controls - var name = true; - - 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; - } - - this.valid = name; - - // return state to control - if (target === 'name') return name ? "success" : "error"; - } - render() { return ( this.onClose(c)} onReset={() => this.resetState()} valid={this.valid}>
- + Name - this.handleChange(e)} /> + this.handleChange(e)} /> + + + + Endpoint + this.handleChange(e)} /> diff --git a/src/components/dialog/import-node.js b/src/components/dialog/import-node.js index 5401745..c91f772 100644 --- a/src/components/dialog/import-node.js +++ b/src/components/dialog/import-node.js @@ -68,8 +68,8 @@ class ImportNodeDialog extends React.Component { } // create file reader - var reader = new FileReader(); - var self = this; + const reader = new FileReader(); + const self = this; reader.onload = function(event) { // read simulator diff --git a/src/components/dialog/import-simulation-model.js b/src/components/dialog/import-simulation-model.js index 196e885..b333bd3 100644 --- a/src/components/dialog/import-simulation-model.js +++ b/src/components/dialog/import-simulation-model.js @@ -35,7 +35,7 @@ class ImportSimulationModelDialog extends React.Component { this.state = { name: '', - simulator: { node: '', simulator: '' }, + simulator: '', outputLength: '1', inputLength: '1', outputMapping: [ { name: 'Signal', type: 'Type' } ], @@ -54,7 +54,7 @@ class ImportSimulationModelDialog extends React.Component { resetState() { this.setState({ name: '', - simulator: { node: this.props.nodes[0] ? this.props.nodes[0]._id : '', simulator: this.props.nodes[0].simulators[0] ? 0 : '' }, + simulator: '', outputLength: '1', inputLength: '1', outputMapping: [{ name: 'Signal', type: 'Type' }], @@ -86,11 +86,7 @@ class ImportSimulationModelDialog extends React.Component { } } - if (e.target.id === 'simulator') { - this.setState({ simulator: JSON.parse(e.target.value) }); - } else { - this.setState({ [e.target.id]: e.target.value }); - } + this.setState({ [e.target.id]: e.target.value }); } handleMappingChange(key, event, row, column) { @@ -177,11 +173,9 @@ class ImportSimulationModelDialog extends React.Component {
Simulator - this.handleChange(e)}> - {this.props.nodes.map(node => ( - node.simulators.map((simulator, index) => ( - - )) + this.handleChange(e)}> + {this.props.simulators.map(simulator => ( + ))} diff --git a/src/components/dialog/import-simulator.js b/src/components/dialog/import-simulator.js new file mode 100644 index 0000000..8320572 --- /dev/null +++ b/src/components/dialog/import-simulator.js @@ -0,0 +1,146 @@ +/** + * File: new-simulator.js + * Author: Markus Grigull + * Date: 27.03.2018 + * + * 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 _ from 'lodash'; + +import Dialog from './dialog'; + +class ImportSimulatorDialog extends React.Component { + valid = false; + imported = false; + + constructor(props) { + super(props); + + this.state = { + name: '', + endpoint: '', + uuid: '' + }; + } + + onClose(canceled) { + if (canceled === false) { + if (this.valid) { + const data = { + properties: { + name: this.state.name + }, + uuid: this.state.uuid + }; + + if (this.state.endpoint != null && this.state.endpoint !== "" && this.state.endpoint !== 'http://') { + data.properties.endpoint = this.state.endpoint; + } + + this.props.onClose(data); + } + } else { + this.props.onClose(); + } + } + + handleChange(e) { + this.setState({ [e.target.id]: e.target.value }); + } + + resetState() { + this.setState({ name: '', endpoint: 'http://', uuid: '' }); + } + + loadFile(fileList) { + // get file + const file = fileList[0]; + if (!file.type.match('application/json')) { + return; + } + + // create file reader + const reader = new FileReader(); + const self = this; + + reader.onload = function(event) { + // read simulator + const simulator = JSON.parse(event.target.result); + self.imported = true; + self.setState({ + name: _.get(simulator, 'properties.name') || _.get(simulator, 'rawProperties.name'), + endpoint: _.get(simulator, 'properties.endpoint') || _.get(simulator, 'rawProperties.endpoint'), + uuid: simulator.uuid + }); + }; + + reader.readAsText(file); + } + + validateForm(target) { + // check all controls + let name = true; + let uuid = true; + + if (this.state.name === '') { + name = false; + } + + if (this.state.uuid === '') { + uuid = false; + } + + this.valid = name || uuid; + + // return state to control + if (target === 'name') return name ? "success" : "error"; + if (target === 'uuid') return uuid ? "success" : "error"; + } + + render() { + return ( + this.onClose(c)} onReset={() => this.resetState()} valid={this.valid}> +
+ + Simulator File + this.loadFile(e.target.files)} /> + + + + Name + this.handleChange(e)} /> + + + + Endpoint + this.handleChange(e)} /> + + + + UUID + this.handleChange(e)} /> + + +
+
+ ); + } +} + +export default ImportSimulatorDialog; \ No newline at end of file diff --git a/src/components/dialog/new-simulation-model.js b/src/components/dialog/new-simulation-model.js index e94f562..5106355 100644 --- a/src/components/dialog/new-simulation-model.js +++ b/src/components/dialog/new-simulation-model.js @@ -34,7 +34,7 @@ class NewSimulationModelDialog extends React.Component { this.state = { name: '', - simulator: { node: '', simulator: '' }, + simulator: '', outputLength: '1', inputLength: '1', outputMapping: [ { name: 'Signal', type: 'Type' } ], @@ -74,11 +74,7 @@ class NewSimulationModelDialog extends React.Component { } } - if (e.target.id === 'simulator') { - this.setState({ simulator: JSON.parse(e.target.value) }); - } else { - this.setState({ [e.target.id]: e.target.value }); - } + this.setState({ [e.target.id]: e.target.value }); } handleMappingChange(key, event, row, column) { @@ -96,7 +92,7 @@ class NewSimulationModelDialog extends React.Component { resetState() { this.setState({ name: '', - simulator: { node: this.props.nodes[0] ? this.props.nodes[0]._id : '', simulator: this.props.nodes[0].simulators[0] ? 0 : '' }, + simulator: '', outputLength: '1', inputLength: '1', outputMapping: [{ name: 'Signal', type: 'Type' }], @@ -148,11 +144,9 @@ class NewSimulationModelDialog extends React.Component { Simulator - this.handleChange(e)}> - {this.props.nodes.map(node => ( - node.simulators.map((simulator, index) => ( - - )) + this.handleChange(e)}> + {this.props.simulators.map(simulator => ( + ))} diff --git a/src/components/dialog/new-simulator.js b/src/components/dialog/new-simulator.js index de8eb90..ff3ea08 100644 --- a/src/components/dialog/new-simulator.js +++ b/src/components/dialog/new-simulator.js @@ -25,20 +25,33 @@ import { FormGroup, FormControl, ControlLabel } from 'react-bootstrap'; import Dialog from './dialog'; class NewSimulatorDialog extends React.Component { - valid: false; + valid = false; constructor(props) { super(props); this.state = { - name: '' + name: '', + endpoint: '', + uuid: '' }; } onClose(canceled) { if (canceled === false) { if (this.valid) { - this.props.onClose(this.state); + const data = { + properties: { + name: this.state.name + }, + uuid: this.state.uuid + }; + + if (this.state.endpoint != null && this.state.endpoint !== "" && this.state.endpoint !== 'http://') { + data.properties.endpoint = this.state.endpoint; + } + + this.props.onClose(data); } } else { this.props.onClose(); @@ -50,21 +63,27 @@ class NewSimulatorDialog extends React.Component { } resetState() { - this.setState({ name: '' }); + this.setState({ name: '', endpoint: 'http://', uuid: '' }); } validateForm(target) { // check all controls - var name = true; + let name = true; + let uuid = true; - if (this.state.name === '' || this.props.node.simulators == null || this.props.node.simulators.find(simulator => simulator.name === this.state.name) !== undefined) { + if (this.state.name === '') { name = false; } - this.valid = name; + if (this.state.uuid === '') { + uuid = false; + } + + this.valid = name || uuid; // return state to control if (target === 'name') return name ? "success" : "error"; + if (target === 'uuid') return uuid ? "success" : "error"; } render() { @@ -76,6 +95,16 @@ class NewSimulatorDialog extends React.Component { this.handleChange(e)} /> + + Endpoint + this.handleChange(e)} /> + + + + UUID + this.handleChange(e)} /> + +
); diff --git a/src/components/table.js b/src/components/table.js index 3d5ac85..df2b7fe 100644 --- a/src/components/table.js +++ b/src/components/table.js @@ -20,6 +20,7 @@ ******************************************************************************/ import React, { Component } from 'react'; +import _ from 'lodash'; import { Table, Button, Glyphicon, FormControl, Label, Checkbox } from 'react-bootstrap'; import { Link } from 'react-router-dom'; @@ -46,19 +47,27 @@ class CustomTable extends Component { addCell(data, index, child) { // add data to cell - const dataKey = child.props.dataKey; - var cell = []; + let content = null; - if (dataKey && data[dataKey] != null) { - // get content - var content; - const modifier = child.props.modifier; - - if (modifier) { - content = modifier(data[dataKey]); - } else { - content = data[dataKey].toString(); + if ('dataKeys' in child.props) { + for (let key of child.props.dataKeys) { + if (_.get(data, key) != null) { + content = _.get(data, key); + break; + } } + } else if ('dataKey' in child.props) { + content = _.get(data, child.props.dataKey); + } + + const modifier = child.props.modifier; + if (modifier && content != null) { + content = modifier(content); + } + + let cell = []; + if (content != null) { + content = content.toString(); // check if cell should be a link const linkKey = child.props.linkKey; @@ -89,21 +98,21 @@ class CustomTable extends Component { // add buttons if (child.props.editButton) { - cell.push(); + cell.push(); } if (child.props.deleteButton) { - cell.push(); + cell.push(); } if (child.props.checkbox) { const checkboxKey = this.props.checkboxKey; - cell.push( child.props.onChecked(index, e)}>); + cell.push( child.props.onChecked(index, e)} />); } if (child.props.exportButton) { - cell.push(); + cell.push(); } return cell; @@ -155,7 +164,7 @@ class CustomTable extends Component { render() { // get children - var children = this.props.children; + let children = this.props.children; if (Array.isArray(this.props.children) === false) { children = [ children ]; } diff --git a/src/components/widget-gauge.js b/src/components/widget-gauge.js index cb23d31..0f2b7bd 100644 --- a/src/components/widget-gauge.js +++ b/src/components/widget-gauge.js @@ -38,16 +38,15 @@ class WidgetGauge extends Component { // update value const simulator = nextProps.widget.simulator; - if (nextProps.data == null || nextProps.data[simulator.node] == null - || nextProps.data[simulator.node][simulator.simulator] == null - || nextProps.data[simulator.node][simulator.simulator].output.values.length === 0 - || nextProps.data[simulator.node][simulator.simulator].output.values[0].length === 0) { + if (nextProps.data == null || nextProps.data[simulator] == null + || nextProps.data[simulator].output.values.length === 0 + || nextProps.data[simulator].output.values[0].length === 0) { this.setState({ value: 0 }); return; } // check if value has changed - const signal = nextProps.data[simulator.node][simulator.simulator].output.values[nextProps.widget.signal]; + const signal = nextProps.data[simulator].output.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 if (signal != null) { diff --git a/src/components/widget-lamp.js b/src/components/widget-lamp.js index db2fa06..60f3b73 100644 --- a/src/components/widget-lamp.js +++ b/src/components/widget-lamp.js @@ -35,16 +35,15 @@ class WidgetLamp extends Component { componentWillReceiveProps(nextProps) { // update value - const simulator = nextProps.widget.simulator.simulator; - const node = nextProps.widget.simulator.node; + const simulator = nextProps.widget.simulator; - if (nextProps.data == null || nextProps.data[node] == null || nextProps.data[node][simulator] == null || nextProps.data[node][simulator].output.values == null) { + if (nextProps.data == null || nextProps.data[simulator] == null || nextProps.data[simulator].output.values == null) { this.setState({ value: '' }); return; } // check if value has changed - const signal = nextProps.data[node][simulator].output.values[nextProps.widget.signal]; + const signal = nextProps.data[simulator].output.values[nextProps.widget.signal]; if (signal != null && this.state.value !== signal[signal.length - 1].y) { this.setState({ value: signal[signal.length - 1].y }); } diff --git a/src/components/widget-plot-table.js b/src/components/widget-plot-table.js index 3cba4c7..55992b1 100644 --- a/src/components/widget-plot-table.js +++ b/src/components/widget-plot-table.js @@ -64,7 +64,7 @@ class WidgetPlotTable extends Component { // get simulation model const simulationModel = nextProps.simulation.models.find((model) => { - return (model.simulator.node === simulator.node && model.simulator.simulator === simulator.simulator); + return (model.simulator === simulator); }); let preselectedSignals = []; @@ -106,8 +106,8 @@ class WidgetPlotTable extends Component { let simulator = this.props.widget.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].output.values.filter((values, index) => ( + if (this.props.data[simulator] != null) { + simulatorData = this.props.data[simulator].output.values.filter((values, index) => ( this.props.widget.signals.findIndex(value => value === index) !== -1 )); } diff --git a/src/components/widget-plot.js b/src/components/widget-plot.js index c492f3f..b6cf423 100644 --- a/src/components/widget-plot.js +++ b/src/components/widget-plot.js @@ -39,11 +39,11 @@ class WidgetPlot extends React.Component { const simulation = nextProps.simulation; // Proceed if a simulation with models and a simulator are available - if (simulator && nextProps.data[simulator.node] != null && nextProps.data[simulator.node][simulator.simulator] != null && simulation && simulation.models.length > 0) { - const model = simulation.models.find(model => model.simulator.node === simulator.node && model.simulator.simulator === simulator.simulator); + if (simulator && nextProps.data[simulator] != null && nextProps.data[simulator] != null && simulation && simulation.models.length > 0) { + const model = simulation.models.find(model => model.simulator === simulator); const chosenSignals = nextProps.widget.signals; - const data = nextProps.data[simulator.node][simulator.simulator].output.values.filter((values, index) => ( + const data = nextProps.data[simulator].output.values.filter((values, index) => ( nextProps.widget.signals.findIndex(value => value === index) !== -1 )); diff --git a/src/components/widget-table.js b/src/components/widget-table.js index 625b84b..a5ec93a 100644 --- a/src/components/widget-table.js +++ b/src/components/widget-table.js @@ -38,11 +38,10 @@ class WidgetTable extends Component { // check data const simulator = nextProps.widget.simulator; - if (nextProps.simulation == null || nextProps.data == null || nextProps.data[simulator.node] == null - || nextProps.data[simulator.node][simulator.simulator] == null - || nextProps.data[simulator.node][simulator.simulator].output.length === 0 - || nextProps.data[simulator.node][simulator.simulator].output.values.length === 0 - || nextProps.data[simulator.node][simulator.simulator].output.values[0].length === 0) { + if (nextProps.simulation == null || nextProps.data == null || nextProps.data[simulator] == null + || nextProps.data[simulator].output.length === 0 + || nextProps.data[simulator].output.values.length === 0 + || nextProps.data[simulator].output.values[0].length === 0) { // clear values this.setState({ rows: [], sequence: null }); return; @@ -61,7 +60,7 @@ class WidgetTable extends Component { // get rows var rows = []; - nextProps.data[simulator.node][simulator.simulator].output.values.forEach((signal, index) => { + nextProps.data[simulator].output.values.forEach((signal, index) => { if (index < simulationModel.outputMapping.length) { rows.push({ name: simulationModel.outputMapping[index].name, @@ -70,7 +69,7 @@ class WidgetTable extends Component { } }); - this.setState({ rows: rows, sequence: nextProps.data[simulator.node][simulator.simulator].output.sequence }); + this.setState({ rows: rows, sequence: nextProps.data[simulator].output.sequence }); } render() { diff --git a/src/components/widget-value.js b/src/components/widget-value.js index fc8b6d0..b0531c6 100644 --- a/src/components/widget-value.js +++ b/src/components/widget-value.js @@ -33,10 +33,7 @@ class WidgetValue extends Component { componentWillReceiveProps(nextProps) { // update value - const simulator = nextProps.widget.simulator.simulator; - const node = nextProps.widget.simulator.node; - - if (nextProps.data == null || nextProps.data[node] == null || nextProps.data[node][simulator] == null || nextProps.data[node][simulator].output.values == null) { + if (nextProps.data == null || nextProps.data[nextProps.widget.simulator] == null || nextProps.data[nextProps.widget.simulator].output == null || nextProps.data[nextProps.widget.simulator].output.values == null) { this.setState({ value: '' }); return; } @@ -45,7 +42,7 @@ class WidgetValue extends Component { let unit = ''; if (nextProps.simulation) { - const simulationModel = nextProps.simulation.models.find(model => model.simulator.node === node && model.simulator.simulator === simulator); + const simulationModel = nextProps.simulation.models.find(model => model.simulator === nextProps.widget.simulator); if (nextProps.widget.signal < simulationModel.outputMapping.length) { unit = simulationModel.outputMapping[nextProps.widget.signal].type; @@ -53,7 +50,7 @@ class WidgetValue extends Component { } // check if value has changed - const signal = nextProps.data[node][simulator].output.values[nextProps.widget.signal]; + const signal = nextProps.data[nextProps.widget.simulator].output.values[nextProps.widget.signal]; if (signal != null && this.state.value !== signal[signal.length - 1].y) { this.setState({ value: signal[signal.length - 1].y, unit }); } diff --git a/src/containers/app.js b/src/containers/app.js index 9df9f57..bb70935 100644 --- a/src/containers/app.js +++ b/src/containers/app.js @@ -29,7 +29,7 @@ import { Col } from 'react-bootstrap'; import AppDispatcher from '../app-dispatcher'; import SimulationStore from '../stores/simulation-store'; -import NodeStore from '../stores/node-store'; +import SimulatorStore from '../stores/simulator-store'; import UserStore from '../stores/user-store'; import NotificationsDataManager from '../data-managers/notifications-data-manager'; @@ -51,14 +51,14 @@ import '../styles/app.css'; class App extends React.Component { static getStores() { - return [ NodeStore, UserStore, SimulationStore ]; + return [ SimulatorStore, UserStore, SimulationStore ]; } static calculateState(prevState) { let currentUser = UserStore.getState().currentUser; return { - nodes: NodeStore.getState(), + simulators: SimulatorStore.getState(), simulations: SimulationStore.getState(), currentRole: currentUser ? currentUser.role : '', token: UserStore.getState().token, @@ -85,7 +85,7 @@ class App extends React.Component { componentDidMount() { // load all simulators and simulations to fetch simulation data AppDispatcher.dispatch({ - type: 'nodes/start-load', + type: 'simulators/start-load', token: this.state.token }); diff --git a/src/containers/simulation.js b/src/containers/simulation.js index 6d28515..2900857 100644 --- a/src/containers/simulation.js +++ b/src/containers/simulation.js @@ -25,7 +25,7 @@ import { Button, Modal, Glyphicon } from 'react-bootstrap'; import FileSaver from 'file-saver'; import SimulationStore from '../stores/simulation-store'; -import NodeStore from '../stores/node-store'; +import SimulatorStore from '../stores/simulator-store'; import UserStore from '../stores/user-store'; import AppDispatcher from '../app-dispatcher'; @@ -37,13 +37,13 @@ import ImportSimulationModelDialog from '../components/dialog/import-simulation- class Simulation extends React.Component { static getStores() { - return [ SimulationStore, NodeStore, UserStore ]; + return [ SimulationStore, SimulatorStore, UserStore ]; } static calculateState() { return { simulations: SimulationStore.getState(), - nodes: NodeStore.getState(), + simulators: SimulatorStore.getState(), sessionToken: UserStore.getState().token, newModal: false, @@ -64,7 +64,7 @@ class Simulation extends React.Component { }); AppDispatcher.dispatch({ - type: 'nodes/start-load', + type: 'simulators/start-load', token: this.state.sessionToken }); } @@ -143,16 +143,16 @@ class Simulation extends React.Component { } } - getSimulatorName(simulator) { - var name = "undefined"; - - this.state.nodes.forEach(node => { - if (node._id === simulator.node) { - name = node.name + '/' + node.simulators[simulator.simulator].name; + getSimulatorName(simulatorId) { + for (let simulator of this.state.simulators) { + if (simulator._id === simulatorId) { + if ('name' in simulator.rawProperties) { + return simulator.rawProperties.name; + } else { + return simulator.uuid; + } } - }); - - return name; + } } exportModel(index) { @@ -197,9 +197,9 @@ class Simulation extends React.Component { - this.closeNewModal(data)} nodes={this.state.nodes} /> - this.closeEditModal(data)} data={this.state.modalData} nodes={this.state.nodes} /> - this.closeImportModal(data)} nodes={this.state.nodes} /> + this.closeNewModal(data)} simulators={this.state.simulators} /> + this.closeEditModal(data)} data={this.state.modalData} simulators={this.state.simulators} /> + this.closeImportModal(data)} simulators={this.state.simulators} /> this.setState({ deleteModal: false })} onKeyPress={this.onModalKeyPress}> diff --git a/src/containers/simulators.js b/src/containers/simulators.js index 31ed312..00e1ce9 100644 --- a/src/containers/simulators.js +++ b/src/containers/simulators.js @@ -21,256 +21,170 @@ import React, { Component } from 'react'; import { Container } from 'flux/utils'; -import { Button, Modal, Glyphicon } from 'react-bootstrap'; +import { Button, Modal, Glyphicon, DropdownButton, MenuItem } from 'react-bootstrap'; import FileSaver from 'file-saver'; +import _ from 'lodash'; import AppDispatcher from '../app-dispatcher'; -import NodeStore from '../stores/node-store'; +import SimulatorStore from '../stores/simulator-store'; import UserStore from '../stores/user-store'; -import NewNodeDialog from '../components/dialog/new-node'; -import EditNodeDialog from '../components/dialog/edit-node'; +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'; -import ImportNodeDialog from '../components/dialog/import-node'; +import ImportSimulatorDialog from '../components/dialog/import-simulator'; class Simulators extends Component { static getStores() { - return [ NodeStore, UserStore ]; + return [ UserStore, SimulatorStore ]; } static calculateState() { return { - nodes: NodeStore.getState(), sessionToken: UserStore.getState().token, + simulators: SimulatorStore.getState(), - newNodeModal: false, - deleteNodeModal: false, - editNodeModal: false, - importModal: false, - exportModal: false, + modalSimulator: {}, + deleteModal: false, - addSimulatorModal: false, - editSimulatorModal: false, - deleteSimulatorModal: false, - - modalData: {}, - modalIndex: 0, - modalName: '' + runAction: 0, + runTitle: 'Reset', + selectedSimulators: [] }; } componentWillMount() { AppDispatcher.dispatch({ - type: 'nodes/start-load', + type: 'simulators/start-load', token: this.state.sessionToken }); } - closeNewNodeModal(data) { - this.setState({ newNodeModal: false }); + closeNewModal(data) { + this.setState({ newModal : false }); if (data) { AppDispatcher.dispatch({ - type: 'nodes/start-add', - data: data, - token: this.state.sessionToken - }); - } - } - - showEditNodeModal(data) { - // find node with id - var node = this.state.nodes.find((element) => { - return element._id === data.id; - }); - - this.setState({ editNodeModal: true, modalData: node }); - } - - closeEditNodeModal(data) { - this.setState({ editNodeModal: false }); - - if (data) { - AppDispatcher.dispatch({ - type: 'nodes/start-edit', - data: data, - token: this.state.sessionToken - }); - } - } - - showDeleteNodeModal(data) { - // find node with id - var node = this.state.nodes.find((element) => { - return element._id === data.id; - }); - - this.setState({ deleteNodeModal: true, modalData: node }); - } - - confirmDeleteNodeModal() { - this.setState({ deleteModal: false }); - - AppDispatcher.dispatch({ - type: 'nodes/start-remove', - data: this.state.modalData, - token: this.state.sessionToken - }); - } - - 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, - token: this.state.sessionToken - }); - } - } - - 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, - token: this.state.sessionToken - }); - } - } - - 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, - token: this.state.sessionToken - }); - } - - closeImportNodeModal(data) { - this.setState({ importNodeModal: false }); - - if (data) { - AppDispatcher.dispatch({ - type: 'nodes/start-add', + type: 'simulators/start-add', data, token: this.state.sessionToken }); } } - exportNode(data) { - const node = this.state.nodes.find((element) => { - return element._id === data.id; - }); + closeEditModal(data) { + this.setState({ editModal : false }); + if (data) { + let simulator = this.state.simulators[this.state.modalIndex]; + simulator.properties = data; + this.setState({ simulator }); + + AppDispatcher.dispatch({ + type: 'simulators/start-edit', + data: simulator, + token: this.state.sessionToken + }); + } + } + + confirmDeleteModal() { + this.setState({ deleteModal: false }); + + AppDispatcher.dispatch({ + type: 'simulators/start-remove', + data: this.state.modalSimulator, + token: this.state.sessionToken + }); + } + + exportSimulator(index) { // filter properties - let simulator = Object.assign({}, node); + let simulator = Object.assign({}, this.state.simulators[index]); delete simulator._id; - simulator.simulators.forEach(simulator => { - delete simulator.id; - }); - // show save dialog const blob = new Blob([JSON.stringify(simulator, null, 2)], { type: 'application/json' }); - FileSaver.saveAs(blob, 'node - ' + node.name + '.json'); + FileSaver.saveAs(blob, 'simulator - ' + (simulator.properties.name || simulator.rawProperties.name || 'undefined') + '.json'); } - labelStyle(value) { - if (value === true) return 'success'; - else return 'warning'; - } + closeImportModal(data) { + this.setState({ importModal: false }); - onTreeDataChange(nodes) { - // update all at once - nodes.forEach((node) => { + if (data) { AppDispatcher.dispatch({ - type: 'nodes/start-edit', - data: node, + type: 'simulators/start-add', + data, token: this.state.sessionToken }); - }); - } - - onNodeModalKeyPress = (event) => { - if (event.key === 'Enter') { - event.preventDefault(); - - this.confirmDeleteNodeModal(); } } - onSimulatorModalKeyPress = (event) => { - if (event.key === 'Enter') { - event.preventDefault(); - - this.confirmDeleteSimulatorModal(); - } - } + onSimulatorChecked(index, event) { + const selectedSimulators = this.state.selectedSimulators; + for (let key in selectedSimulators) { + if (selectedSimulators[key] === index) { + // update existing entry + if (event.target.checked) { + return; + } - loadFile(fileList) { - // get file - const file = fileList[0]; - if (!file.type.match('application/json')) { + selectedSimulators.splice(key, 1); + + this.setState({ selectedSimulators }); + return; + } + } + + // add new entry + if (event.target.checked === false) { return; } - // create file reader - var reader = new FileReader(); - var self = this; + selectedSimulators.push(index); + this.setState({ selectedSimulators }); + } - reader.onload = function(event) { - // read simulator - const simulator = JSON.parse(event.target.result); - self.setState({ importModal: true, modalSimulator: simulator }); - }; + setRunAction(index) { + let runTitle = ''; + switch (index) { + case '0': + runTitle = 'Reset'; + break; - reader.readAsText(file); + case '1': + runTitle = 'Shutdown'; + break; + + default: + console.log('Unknown index ' + index); + break; + } + + this.setState({ runAction: index, runTitle }); + } + + runAction() { + for (let index of this.state.selectedSimulators) { + let data; + switch (this.state.runAction) { + case '0': + data = { action: 'reset' }; + break; + + case '1': + data = { action: 'shutdown' }; + break; + } + + AppDispatcher.dispatch({ + type: 'simulators/start-action', + simulator: this.state.simulators[index], + data, + token: this.state.sessionToken + }); + } } render() { @@ -278,61 +192,55 @@ class Simulators extends Component {

Simulators

- - + + this.onSimulatorChecked(index, event)} width='30' /> + + + + + + + this.setState({ editModal: true, modalSimulator: this.state.simulators[index], modalIndex: index })} + onExport={index => this.exportSimulator(index)} + onDelete={index => this.setState({ deleteModal: true, modalSimulator: this.state.simulators[index], modalIndex: index })} + /> +
-
- Hint: Node names must be unique. Simulator names must be unique on a node. +
+ this.setRunAction(index)}> + Reset + Shutdown + - this.onTreeDataChange(treeData)} - onNodeDelete={(node) => this.showDeleteNodeModal(node)} - onNodeEdit={(node) => this.showEditNodeModal(node)} - onNodeAdd={(node) => this.showAddSimulatorModal(node)} - onNodeExport={node => this.exportNode(node)} - onSimulatorEdit={(node, index) => this.showEditSimulatorModal(node, index)} - onSimulatorDelete={(node, index) => this.showDeleteSimulatorModal(node, index)} - /> + +
- this.closeNewNodeModal(data)} nodes={this.state.nodes} /> - this.closeEditNodeModal(data)} nodes={this.state.nodes} /> - this.closeAddSimulatorModal(data)} node={this.state.modalData}/> - this.closeImportNodeModal(data)} nodes={this.state.nodes} /> +
+ + +
- {this.state.editSimulatorModal && - this.closeEditSimulatorModal(data)} node={this.state.modalData} /> - } + this.closeNewModal(data)} /> + this.closeEditModal(data)} simulator={this.state.modalSimulator} /> + this.closeImportModal(data)} /> - this.setState({ deleteNodeModal: false })} onKeyPress={this.onNodeModalKeyPress}> - - Delete Node - - - - Are you sure you want to delete the node '{this.state.modalData.name}'? -
- This will delete all simulators assigned to this node. -
- - - - - -
- - this.setState({ deleteSimulatorModal: false })} onKeyPress={this.onSimulatorModalKeyPress}> + this.setState({ deleteModal: false })} onKeyPress={this.onModalKeyPress}> Delete Simulator - Are you sure you want to delete the simulator '{this.state.modalName}'? + Are you sure you want to delete the simulator'{_.get(this.state.modalSimulator, 'properties.name') || _.get(this.state.modalSimulator, 'rawProperties.name') || 'Unknown'}'? - - + +
diff --git a/src/data-managers/simulations-data-manager.js b/src/data-managers/simulations-data-manager.js index bb9a6ba..4ded924 100644 --- a/src/data-managers/simulations-data-manager.js +++ b/src/data-managers/simulations-data-manager.js @@ -29,13 +29,23 @@ class SimulationsDataManager extends RestDataManager { this.onLoad = this.onSimulationsLoad; } - onSimulationsLoad(simulation) { + onSimulationsLoad(data) { + if (Array.isArray(data)) { + for (let simulation of data) { + this.loadSimulationData(simulation); + } + } else { + this.loadSimulationData(data); + } + } + + loadSimulationData(simulation) { for (let model of simulation.models) { AppDispatcher.dispatch({ type: 'simulatorData/prepare', inputLength: parseInt(model.inputLength, 10), outputLength: parseInt(model.outputLength, 10), - node: model.simulator + id: model.simulator }); } } diff --git a/src/data-managers/simulator-data-data-manager.js b/src/data-managers/simulator-data-data-manager.js index eee8e03..c697f36 100644 --- a/src/data-managers/simulator-data-data-manager.js +++ b/src/data-managers/simulator-data-data-manager.js @@ -30,22 +30,17 @@ class SimulatorDataDataManager { this._sockets = {}; } - open(endpoint, node) { + open(endpoint, identifier) { // pass signals to onOpen callback - if (this._sockets[node._id] != null) { - if (this._sockets[node._id].url !== WebsocketAPI.getURL(node)) { + if (this._sockets[identifier] != null) { + if (this._sockets[identifier].url !== WebsocketAPI.getURL(endpoint)) { // replace connection, since endpoint changed this._sockets.close(); - this._sockets[node._id] = WebsocketAPI.addSocket(node, { onOpen: (event) => this.onOpen(event, node), onClose: (event) => this.onClose(event, node), onMessage: (event) => this.onMessage(event, node), onError: (error) => this.onError(error, node) }); + this._sockets[identifier] = WebsocketAPI.addSocket(endpoint, { onOpen: (event) => this.onOpen(event, identifier), onClose: (event) => this.onClose(event, identifier), onMessage: (event) => this.onMessage(event, identifier), onError: (error) => this.onError(error, identifier) }); } } else { - // set flag if a socket to this simulator was already create before - if (this._sockets[node._id] === null) { - this._sockets[node._id] = WebsocketAPI.addSocket(node, { onOpen: (event) => this.onOpen(event, node, false), onClose: (event) => this.onClose(event, node), onMessage: (event) => this.onMessage(event, node), onError: (error) => this.onError(error, node) }); - } else { - this._sockets[node._id] = WebsocketAPI.addSocket(node, { onOpen: (event) => this.onOpen(event, node, true), onClose: (event) => this.onClose(event, node), onMessage: (event) => this.onMessage(event, node), onError: (error) => this.onError(error, node) }); - } + this._sockets[identifier] = WebsocketAPI.addSocket(endpoint, { onOpen: (event) => this.onOpen(event, identifier, false), onClose: (event) => this.onClose(event, identifier), onMessage: (event) => this.onMessage(event, identifier), onError: (error) => this.onError(error, identifier) }); } } @@ -59,8 +54,8 @@ class SimulatorDataDataManager { } } - send(message, nodeId) { - const socket = this._sockets[nodeId]; + send(message, identifier) { + const socket = this._sockets[identifier]; if (socket == null) { return false; } @@ -68,40 +63,42 @@ class SimulatorDataDataManager { const data = this.messageToBuffer(message); socket.send(data); + console.log(data); + return true; } - onOpen(event, node, firstOpen) { + onOpen(event, identifier, firstOpen) { AppDispatcher.dispatch({ type: 'simulatorData/opened', - node: node, + id: identifier, firstOpen: firstOpen }); } - onClose(event, node) { + onClose(event, identifier) { AppDispatcher.dispatch({ type: 'simulatorData/closed', - node: node, + id: identifier, notification: (event.code !== 4000) }); // remove from list, keep null reference for flag detection - delete this._sockets[node._id]; + delete this._sockets[identifier]; } - onError(error, node) { - console.error('Error on ' + node._id + ':' + error); + onError(error, identifier) { + console.error('Error on ' + identifier + ':' + error); } - onMessage(event, node) { + onMessage(event, identifier) { var msgs = this.bufferToMessageArray(event.data); if (msgs.length > 0) { AppDispatcher.dispatch({ type: 'simulatorData/data-changed', data: msgs, - node: node + id: identifier }); } } diff --git a/src/data-managers/simulators-data-manager.js b/src/data-managers/simulators-data-manager.js new file mode 100644 index 0000000..42ddec3 --- /dev/null +++ b/src/data-managers/simulators-data-manager.js @@ -0,0 +1,46 @@ +/** + * File: simulator-data-manager.js + * Author: Markus Grigull + * Date: 03.03.2018 + * + * 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'; + +class SimulatorsDataManager extends RestDataManager { + constructor() { + super('simulator', '/simulators'); + } + + doAction(simulator, action, token = null) { + RestAPI.post(this.makeURL(this.url + '/' + simulator._id), action, token).then(response => { + AppDispatcher.dispatch({ + type: 'simulators/action-started', + data: response + }); + }).catch(error => { + AppDispatcher.dispatch({ + type: 'simulators/action-error', + error + }); + }); + } +} + +export default new SimulatorsDataManager(); diff --git a/src/stores/simulator-data-store.js b/src/stores/simulator-data-store.js index 61864bf..1c303cc 100644 --- a/src/stores/simulator-data-store.js +++ b/src/stores/simulator-data-store.js @@ -36,24 +36,14 @@ class SimulationDataStore extends ReduceStore { } reduce(state, action) { - var i, j; - switch (action.type) { - case 'simulatorData/open': - SimulatorDataDataManager.open(action.endpoint, action.node); - return state; - case 'simulatorData/opened': // create entry for simulator - state[action.node._id] = {}; + state[action.id] = {}; return state; case 'simulatorData/prepare': - if (state[action.node.node] == null) { - return state; - } - - state[action.node.node][action.node.simulator] = { + state[action.id] = { output: { sequence: -1, length: action.outputLength, @@ -64,47 +54,49 @@ class SimulationDataStore extends ReduceStore { length: action.inputLength, version: 2, type: 0, - id: action.node.simulator, + id: 0, timestamp: Date.now(), values: new Array(action.inputLength).fill(0) } }; - + + this.__emitChange(); return state; case 'simulatorData/data-changed': // get index for simulator id - if (state[action.node._id] == null) { + if (state[action.id] == null) { return state; } + if (state[action.id].output == null) { + state[action.id].output = { + values: [] + }; + } + // loop over all samples in a vector - for (j = 0; j < action.data.length; j++) { + for (let j = 0; j < action.data.length; j++) { let smp = action.data[j]; - let index = action.node.simulators.findIndex(simulator => simulator.id === smp.id); - if (index === -1 || state[action.node._id][index] == null) { - return state; - } - // add data to simulator - for (i = 0; i < smp.length; i++) { - while (state[action.node._id][index].output.values.length < i + 1) { - state[action.node._id][index].output.values.push([]); + for (let i = 0; i < smp.length; i++) { + while (state[action.id].output.values.length < i + 1) { + state[action.id].output.values.push([]); } - state[action.node._id][index].output.values[i].push({ x: smp.timestamp, y: smp.values[i] }); + state[action.id].output.values[i].push({ x: smp.timestamp, y: smp.values[i] }); // erase old values - if (state[action.node._id][index].output.values[i].length > MAX_VALUES) { - const pos = state[action.node._id][index].output.values[i].length - MAX_VALUES; - state[action.node._id][index].output.values[i].splice(0, pos); + if (state[action.id].output.values[i].length > MAX_VALUES) { + const pos = state[action.id].output.values[i].length - MAX_VALUES; + state[action.id].output.values[i].splice(0, pos); } } // update metadata - state[action.node._id][index].output.timestamp = smp.timestamp; - state[action.node._id][index].output.sequence = smp.sequence; + state[action.id].output.timestamp = smp.timestamp; + state[action.id].output.sequence = smp.sequence; } // explicit call to prevent array copy diff --git a/src/stores/simulator-store.js b/src/stores/simulator-store.js new file mode 100644 index 0000000..75623c0 --- /dev/null +++ b/src/stores/simulator-store.js @@ -0,0 +1,71 @@ +/** + * File: simulator-store.js + * Author: Markus Grigull + * Date: 03.03.2018 + * + * 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 SimulatorDataDataManager from '../data-managers/simulator-data-data-manager'; + +class SimulatorStore extends ArrayStore { + constructor() { + super('simulators', SimulatorsDataManager); + } + + reduce(state, action) { + switch(action.type) { + case 'simulators/loaded': + // connect to each simulator + for (let simulator of action.data) { + if (simulator.endpoint != null && 'endpoint' in simulator.properties) { + SimulatorDataDataManager.open(simulator.properties.endpoint, simulator._id); + } else if (simulator.rawProperties != null && 'endpoint' in simulator.rawProperties) { + SimulatorDataDataManager.open(simulator.rawProperties.endpoint, simulator._id); + } else { + console.warn('Endpoint not found for simulator'); + console.log(simulator); + } + } + + return super.reduce(state, action); + + case 'simulators/edited': + return super.reduce(state, action); + + case 'simulators/fetched': + return this.updateElements(state, [action.data]); + + case 'simulators/fetch-error': + return state; + + case 'simulators/start-action': + SimulatorsDataManager.doAction(action.simulator, action.data, action.token); + return state; + + case 'simulators/action-error': + console.log(action.error); + return state; + + default: + return super.reduce(state, action); + } + } +} + +export default new SimulatorStore();