mirror of
https://git.rwth-aachen.de/acs/public/villas/web/
synced 2025-03-09 00:00:01 +01:00
Merge branch '21-reverse-data-channel' into 'develop'
Resolve "Reverse data channel" Closes #21 See merge request acs/public/villas/VILLASweb!25
This commit is contained in:
commit
e76fdac8e3
22 changed files with 367 additions and 137 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -16,4 +16,4 @@ npm-debug.log*
|
|||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.vscode/
|
||||
|
||||
*.code-workspace
|
||||
|
|
|
@ -26,10 +26,10 @@ class WebsocketAPI {
|
|||
socket.binaryType = 'arraybuffer';
|
||||
|
||||
// register callbacks
|
||||
if (callbacks.onOpen) socket.addEventListener('open', event => callbacks.onOpen(event));
|
||||
if (callbacks.onClose) socket.addEventListener('close', event => callbacks.onClose(event));
|
||||
if (callbacks.onMessage) socket.addEventListener('message', event => callbacks.onMessage(event));
|
||||
if (callbacks.onError) socket.addEventListener('error', event => callbacks.onError(event));
|
||||
if (callbacks.onOpen) socket.onopen = callbacks.onOpen;
|
||||
if (callbacks.onClose) socket.onclose = callbacks.onClose;
|
||||
if (callbacks.onMessage) socket.onmessage = callbacks.onMessage;
|
||||
if (callbacks.onError) socket.onerror = callbacks.onError;
|
||||
|
||||
return socket;
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
******************************************************************************/
|
||||
|
||||
import React from 'react';
|
||||
import { FormGroup, FormControl, ControlLabel } from 'react-bootstrap';
|
||||
import { FormGroup, FormControl, ControlLabel, HelpBlock } from 'react-bootstrap';
|
||||
|
||||
import Table from '../table';
|
||||
import TableColumn from '../table-column';
|
||||
|
@ -35,8 +35,10 @@ class EditSimulationModelDialog extends React.Component {
|
|||
this.state = {
|
||||
name: '',
|
||||
simulator: { node: '', simulator: '' },
|
||||
length: 1,
|
||||
mapping: [{ name: 'Signal', type: 'Type' }]
|
||||
outputLength: 1,
|
||||
inputLength: 1,
|
||||
outputMapping: [{ name: 'Signal', type: 'Type' }],
|
||||
inputMapping: [{ name: 'Signal', type: 'Type' }]
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,16 +53,24 @@ class EditSimulationModelDialog extends React.Component {
|
|||
}
|
||||
|
||||
handleChange(e) {
|
||||
if (e.target.id === 'length') {
|
||||
let mapping = null;
|
||||
|
||||
if (e.target.id === 'outputLength') {
|
||||
mapping = this.state.outputMapping;
|
||||
} else if (e.target.id === 'inputLength') {
|
||||
mapping = this.state.inputMapping;
|
||||
}
|
||||
|
||||
if (mapping != null) {
|
||||
// change mapping size
|
||||
if (e.target.value > this.state.mapping.length) {
|
||||
if (e.target.value > mapping.length) {
|
||||
// add missing signals
|
||||
while (this.state.mapping.length < e.target.value) {
|
||||
this.state.mapping.push({ name: 'Signal', type: 'Type' });
|
||||
while (mapping.length < e.target.value) {
|
||||
mapping.push({ name: 'Signal', type: 'Type' });
|
||||
}
|
||||
} else {
|
||||
// remove signals
|
||||
this.state.mapping.splice(e.target.value, this.state.mapping.length - e.target.value);
|
||||
mapping.splice(e.target.value, mapping.length - e.target.value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -72,8 +82,8 @@ class EditSimulationModelDialog extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
handleMappingChange(event, row, column) {
|
||||
var mapping = this.state.mapping;
|
||||
handleMappingChange(key, event, row, column) {
|
||||
const mapping = this.state[key];
|
||||
|
||||
if (column === 1) {
|
||||
mapping[row].name = event.target.value;
|
||||
|
@ -81,37 +91,45 @@ class EditSimulationModelDialog extends React.Component {
|
|||
mapping[row].type = event.target.value;
|
||||
}
|
||||
|
||||
this.setState({ mapping: mapping });
|
||||
this.setState({ [key]: mapping });
|
||||
}
|
||||
|
||||
resetState() {
|
||||
this.setState({
|
||||
name: this.props.data.name,
|
||||
simulator: this.props.data.simulator,
|
||||
length: this.props.data.length,
|
||||
mapping: this.props.data.mapping
|
||||
outputLength: this.props.data.outputLength,
|
||||
inputLength: this.props.data.inputLength,
|
||||
outputMapping: this.props.data.outputMapping,
|
||||
inputMapping: this.props.data.inputMapping
|
||||
});
|
||||
}
|
||||
|
||||
validateForm(target) {
|
||||
// check all controls
|
||||
var name = true;
|
||||
var length = true;
|
||||
let inputLength = true;
|
||||
let outputLength = true;
|
||||
|
||||
if (this.state.name === '') {
|
||||
name = false;
|
||||
}
|
||||
|
||||
// test if simulatorid is a number (in a string, not type of number)
|
||||
if (!/^\d+$/.test(this.state.length)) {
|
||||
length = false;
|
||||
if (!/^\d+$/.test(this.state.outputLength)) {
|
||||
outputLength = false;
|
||||
}
|
||||
|
||||
this.valid = name && length;
|
||||
if (!/^\d+$/.test(this.state.inputLength)) {
|
||||
inputLength = false;
|
||||
}
|
||||
|
||||
this.valid = name && inputLength && outputLength;
|
||||
|
||||
// return state to control
|
||||
if (target === 'name') return name ? "success" : "error";
|
||||
else if (target === 'length') return length ? "success" : "error";
|
||||
else if (target === 'outputLength') return outputLength ? "success" : "error";
|
||||
else if (target === 'inputLength') return inputLength ? "success" : "error";
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -133,17 +151,32 @@ class EditSimulationModelDialog extends React.Component {
|
|||
))}
|
||||
</FormControl>
|
||||
</FormGroup>
|
||||
<FormGroup controlId="length" validationState={this.validateForm('length')}>
|
||||
<ControlLabel>Length</ControlLabel>
|
||||
<FormControl type="number" placeholder="Enter length" min="1" value={this.state.length} onChange={(e) => this.handleChange(e)} />
|
||||
<FormGroup controlId="outputLength" validationState={this.validateForm('outputLength')}>
|
||||
<ControlLabel>Output Length</ControlLabel>
|
||||
<FormControl type="number" placeholder="Enter length" min="1" value={this.state.outputLength} onChange={(e) => this.handleChange(e)} />
|
||||
<FormControl.Feedback />
|
||||
</FormGroup>
|
||||
<FormGroup controlId="mapping">
|
||||
<ControlLabel>Mapping</ControlLabel>
|
||||
<Table data={this.state.mapping}>
|
||||
<FormGroup controlId="outputMapping">
|
||||
<ControlLabel>Output Mapping</ControlLabel>
|
||||
<HelpBlock>Click Name or Type cell to edit</HelpBlock>
|
||||
<Table data={this.state.outputMapping}>
|
||||
<TableColumn title='ID' width='60' dataIndex />
|
||||
<TableColumn title='Name' dataKey='name' inlineEditable onInlineChange={(event, row, column) => this.handleMappingChange(event, row, column)} />
|
||||
<TableColumn title='Type' dataKey='type' inlineEditable onInlineChange={(event, row, column) => this.handleMappingChange(event, row, column)} />
|
||||
<TableColumn title='Name' dataKey='name' inlineEditable onInlineChange={(event, row, column) => this.handleMappingChange('outputMapping', event, row, column)} />
|
||||
<TableColumn title='Type' dataKey='type' inlineEditable onInlineChange={(event, row, column) => this.handleMappingChange('outputMapping', event, row, column)} />
|
||||
</Table>
|
||||
</FormGroup>
|
||||
<FormGroup controlId="inputLength" validationState={this.validateForm('inputLength')}>
|
||||
<ControlLabel>Input Length</ControlLabel>
|
||||
<FormControl type="number" placeholder="Enter length" min="1" value={this.state.inputLength} onChange={(e) => this.handleChange(e)} />
|
||||
<FormControl.Feedback />
|
||||
</FormGroup>
|
||||
<FormGroup controlId="inputMapping">
|
||||
<ControlLabel>Input Mapping</ControlLabel>
|
||||
<HelpBlock>Click Name or Type cell to edit</HelpBlock>
|
||||
<Table data={this.state.inputMapping}>
|
||||
<TableColumn title='ID' width='60' dataIndex />
|
||||
<TableColumn title='Name' dataKey='name' inlineEditable onInlineChange={(event, row, column) => this.handleMappingChange('inputMapping', event, row, column)} />
|
||||
<TableColumn title='Type' dataKey='type' inlineEditable onInlineChange={(event, row, column) => this.handleMappingChange('inputMapping', event, row, column)} />
|
||||
</Table>
|
||||
</FormGroup>
|
||||
</form>
|
||||
|
|
|
@ -117,13 +117,17 @@ export default function createControls(widgetType = null, widget = null, session
|
|||
break;
|
||||
case 'Slider':
|
||||
dialogControls.push(
|
||||
<EditWidgetOrientation key={0} widget={widget} validate={(id) => validateForm(id)} simulation={simulation} handleChange={(e) => handleChange(e)} />
|
||||
<EditWidgetOrientation key={0} widget={widget} validate={(id) => validateForm(id)} simulation={simulation} handleChange={(e) => handleChange(e)} />,
|
||||
<EditWidgetSimulatorControl key={1} widget={widget} validate={(id) => validateForm(id)} simulation={simulation} handleChange={(e) => valueBoundOnChange(e)} />,
|
||||
<EditWidgetSignalControl key={2} widget={widget} input validate={(id) => validateForm(id)} simulation={simulation} handleChange={(e) => handleChange(e)} />
|
||||
);
|
||||
break;
|
||||
case 'Button':
|
||||
dialogControls.push(
|
||||
<EditWidgetColorControl key={0} widget={widget} controlId={'background_color'} label={'Background'} validate={(id) => validateForm(id)} handleChange={(e) => handleChange(e)} />,
|
||||
<EditWidgetColorControl key={1} widget={widget} controlId={'font_color'} label={'Font color'} validate={(id) => validateForm(id)} handleChange={(e) => handleChange(e)} />
|
||||
<EditWidgetColorControl key={1} widget={widget} controlId={'font_color'} label={'Font color'} validate={(id) => validateForm(id)} handleChange={(e) => handleChange(e)} />,
|
||||
<EditWidgetSimulatorControl key={2} widget={widget} validate={(id) => validateForm(id)} simulation={simulation} handleChange={(e) => valueBoundOnChange(e)} />,
|
||||
<EditWidgetSignalControl key={3} widget={widget} input validate={(id) => validateForm(id)} simulation={simulation} handleChange={(e) => handleChange(e)} />
|
||||
);
|
||||
break;
|
||||
case 'Box':
|
||||
|
@ -151,6 +155,14 @@ export default function createControls(widgetType = null, widget = null, session
|
|||
);
|
||||
break;
|
||||
|
||||
case 'NumberInput':
|
||||
dialogControls.push(
|
||||
<EditWidgetTextControl key={0} widget={widget} controlId={'name'} label={'Text'} placeholder={'Enter text'} validate={id => validateForm(id)} handleChange={e => handleChange(e)} />,
|
||||
<EditWidgetSimulatorControl key={1} widget={widget} validate={(id) => validateForm(id)} simulation={simulation} handleChange={(e) => valueBoundOnChange(e)} />,
|
||||
<EditWidgetSignalControl key={2} widget={widget} input validate={(id) => validateForm(id)} simulation={simulation} handleChange={(e) => handleChange(e)} />
|
||||
);
|
||||
break;
|
||||
|
||||
default:
|
||||
console.log('Non-valid widget type: ' + widgetType);
|
||||
}
|
||||
|
|
|
@ -46,7 +46,12 @@ class EditWidgetSignalControl extends Component {
|
|||
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 : [];
|
||||
if (this.props.input) {
|
||||
signalsToRender = simulationModel ? simulationModel.inputMapping : [];
|
||||
} else {
|
||||
signalsToRender = simulationModel ? simulationModel.outputMapping : [];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -61,7 +61,7 @@ class EditWidgetSignalsControl extends Component {
|
|||
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.outputMapping : [];
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
******************************************************************************/
|
||||
|
||||
import React from 'react';
|
||||
import { FormGroup, FormControl, ControlLabel } from 'react-bootstrap';
|
||||
import { FormGroup, FormControl, ControlLabel, HelpBlock } from 'react-bootstrap';
|
||||
|
||||
import Table from '../table';
|
||||
import TableColumn from '../table-column';
|
||||
|
@ -36,8 +36,10 @@ class ImportSimulationModelDialog extends React.Component {
|
|||
this.state = {
|
||||
name: '',
|
||||
simulator: { node: '', simulator: '' },
|
||||
length: '1',
|
||||
mapping: [ { name: 'Signal', type: 'Type' } ]
|
||||
outputLength: '1',
|
||||
inputLength: '1',
|
||||
outputMapping: [ { name: 'Signal', type: 'Type' } ],
|
||||
inputMapping: [{ name: 'Signal', type: 'Type' }]
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -53,24 +55,34 @@ class ImportSimulationModelDialog extends React.Component {
|
|||
this.setState({
|
||||
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' } ]
|
||||
outputLength: '1',
|
||||
inputLength: '1',
|
||||
outputMapping: [{ name: 'Signal', type: 'Type' }],
|
||||
inputMapping: [{ name: 'Signal', type: 'Type' }]
|
||||
});
|
||||
|
||||
this.imported = false;
|
||||
}
|
||||
|
||||
handleChange(e) {
|
||||
if (e.target.id === 'length') {
|
||||
let mapping = null;
|
||||
|
||||
if (e.target.id === 'outputLength') {
|
||||
mapping = this.state.outputMapping;
|
||||
} else if (e.target.id === 'inputLength') {
|
||||
mapping = this.state.inputMapping;
|
||||
}
|
||||
|
||||
if (mapping != null) {
|
||||
// change mapping size
|
||||
if (e.target.value > this.state.mapping.length) {
|
||||
if (e.target.value > mapping.length) {
|
||||
// add missing signals
|
||||
while (this.state.mapping.length < e.target.value) {
|
||||
this.state.mapping.push({ name: 'Signal', type: 'Type' });
|
||||
while (mapping.length < e.target.value) {
|
||||
mapping.push({ name: 'Signal', type: 'Type' });
|
||||
}
|
||||
} else {
|
||||
// remove signals
|
||||
this.state.mapping.splice(e.target.value, this.state.mapping.length - e.target.value);
|
||||
mapping.splice(e.target.value, mapping.length - e.target.value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -81,8 +93,8 @@ class ImportSimulationModelDialog extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
handleMappingChange(event, row, column) {
|
||||
var mapping = this.state.mapping;
|
||||
handleMappingChange(key, event, row, column) {
|
||||
const mapping = this.state[key];
|
||||
|
||||
if (column === 1) {
|
||||
mapping[row].name = event.target.value;
|
||||
|
@ -90,7 +102,7 @@ class ImportSimulationModelDialog extends React.Component {
|
|||
mapping[row].type = event.target.value;
|
||||
}
|
||||
|
||||
this.setState({ mapping: mapping });
|
||||
this.setState({ [key]: mapping });
|
||||
}
|
||||
|
||||
loadFile(fileList) {
|
||||
|
@ -119,7 +131,8 @@ class ImportSimulationModelDialog extends React.Component {
|
|||
validateForm(target) {
|
||||
// check all controls
|
||||
var name = true;
|
||||
var length = true;
|
||||
let inputLength = true;
|
||||
let outputLength = true;
|
||||
var simulator = true;
|
||||
|
||||
if (this.state.name === '') {
|
||||
|
@ -131,15 +144,20 @@ class ImportSimulationModelDialog extends React.Component {
|
|||
}
|
||||
|
||||
// test if simulatorid is a number (in a string, not type of number)
|
||||
if (!/^\d+$/.test(this.state.length)) {
|
||||
length = false;
|
||||
if (!/^\d+$/.test(this.state.outputLength)) {
|
||||
outputLength = false;
|
||||
}
|
||||
|
||||
this.valid = name && length && simulator;
|
||||
if (!/^\d+$/.test(this.state.inputLength)) {
|
||||
inputLength = false;
|
||||
}
|
||||
|
||||
this.valid = name && inputLength && outputLength && simulator;
|
||||
|
||||
// return state to control
|
||||
if (target === 'name') return name ? "success" : "error";
|
||||
else if (target === 'length') return length ? "success" : "error";
|
||||
else if (target === 'outputLength') return outputLength ? "success" : "error";
|
||||
else if (target === 'inputLength') return inputLength ? "success" : "error";
|
||||
else if (target === 'simulator') return simulator ? "success" : "error";
|
||||
}
|
||||
|
||||
|
@ -167,17 +185,32 @@ class ImportSimulationModelDialog extends React.Component {
|
|||
))}
|
||||
</FormControl>
|
||||
</FormGroup>
|
||||
<FormGroup controlId="length" validationState={this.validateForm('length')}>
|
||||
<ControlLabel>Length</ControlLabel>
|
||||
<FormControl readOnly={!this.imported} type="number" placeholder="Enter length" min="1" value={this.state.length} onChange={(e) => this.handleChange(e)} />
|
||||
<FormGroup controlId="outputLength" validationState={this.validateForm('outputLength')}>
|
||||
<ControlLabel>Output Length</ControlLabel>
|
||||
<FormControl type="number" placeholder="Enter length" min="1" value={this.state.outputLength} onChange={(e) => this.handleChange(e)} />
|
||||
<FormControl.Feedback />
|
||||
</FormGroup>
|
||||
<FormGroup controlId="mapping">
|
||||
<ControlLabel>Mapping</ControlLabel>
|
||||
<Table data={this.state.mapping}>
|
||||
<FormGroup controlId="outputMapping">
|
||||
<ControlLabel>Output Mapping</ControlLabel>
|
||||
<HelpBlock>Click Name or Type cell to edit</HelpBlock>
|
||||
<Table data={this.state.outputMapping}>
|
||||
<TableColumn title='ID' width='60' dataIndex />
|
||||
<TableColumn title='Name' dataKey='name' inlineEditable onInlineChange={(event, row, column) => this.handleMappingChange(event, row, column)} />
|
||||
<TableColumn title='Type' dataKey='type' inlineEditable onInlineChange={(event, row, column) => this.handleMappingChange(event, row, column)} />
|
||||
<TableColumn title='Name' dataKey='name' inlineEditable onInlineChange={(event, row, column) => this.handleMappingChange('outputMapping', event, row, column)} />
|
||||
<TableColumn title='Type' dataKey='type' inlineEditable onInlineChange={(event, row, column) => this.handleMappingChange('outputMapping', event, row, column)} />
|
||||
</Table>
|
||||
</FormGroup>
|
||||
<FormGroup controlId="inputLength" validationState={this.validateForm('inputLength')}>
|
||||
<ControlLabel>Input Length</ControlLabel>
|
||||
<FormControl type="number" placeholder="Enter length" min="1" value={this.state.inputLength} onChange={(e) => this.handleChange(e)} />
|
||||
<FormControl.Feedback />
|
||||
</FormGroup>
|
||||
<FormGroup controlId="inputMapping">
|
||||
<ControlLabel>Input Mapping</ControlLabel>
|
||||
<HelpBlock>Click Name or Type cell to edit</HelpBlock>
|
||||
<Table data={this.state.inputMapping}>
|
||||
<TableColumn title='ID' width='60' dataIndex />
|
||||
<TableColumn title='Name' dataKey='name' inlineEditable onInlineChange={(event, row, column) => this.handleMappingChange('inputMapping', event, row, column)} />
|
||||
<TableColumn title='Type' dataKey='type' inlineEditable onInlineChange={(event, row, column) => this.handleMappingChange('inputMapping', event, row, column)} />
|
||||
</Table>
|
||||
</FormGroup>
|
||||
</form>
|
||||
|
|
|
@ -35,8 +35,10 @@ class NewSimulationModelDialog extends React.Component {
|
|||
this.state = {
|
||||
name: '',
|
||||
simulator: { node: '', simulator: '' },
|
||||
length: '1',
|
||||
mapping: [ { name: 'Signal', type: 'Type' } ]
|
||||
outputLength: '1',
|
||||
inputLength: '1',
|
||||
outputMapping: [ { name: 'Signal', type: 'Type' } ],
|
||||
inputMapping: [ { name: 'Signal', type: 'Type' } ]
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -51,16 +53,24 @@ class NewSimulationModelDialog extends React.Component {
|
|||
}
|
||||
|
||||
handleChange(e) {
|
||||
if (e.target.id === 'length') {
|
||||
let mapping = null;
|
||||
|
||||
if (e.target.id === 'outputLength') {
|
||||
mapping = this.state.outputMapping;
|
||||
} else if (e.target.id === 'inputLength') {
|
||||
mapping = this.state.inputMapping;
|
||||
}
|
||||
|
||||
if (mapping != null) {
|
||||
// change mapping size
|
||||
if (e.target.value > this.state.mapping.length) {
|
||||
if (e.target.value > mapping.length) {
|
||||
// add missing signals
|
||||
while (this.state.mapping.length < e.target.value) {
|
||||
this.state.mapping.push({ name: 'Signal', type: 'Type' });
|
||||
while (mapping.length < e.target.value) {
|
||||
mapping.push({ name: 'Signal', type: 'Type' });
|
||||
}
|
||||
} else {
|
||||
// remove signals
|
||||
this.state.mapping.splice(e.target.value, this.state.mapping.length - e.target.value);
|
||||
mapping.splice(e.target.value, mapping.length - e.target.value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -71,8 +81,8 @@ class NewSimulationModelDialog extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
handleMappingChange(event, row, column) {
|
||||
var mapping = this.state.mapping;
|
||||
handleMappingChange(key, event, row, column) {
|
||||
const mapping = this.state[key];
|
||||
|
||||
if (column === 1) {
|
||||
mapping[row].name = event.target.value;
|
||||
|
@ -80,23 +90,26 @@ class NewSimulationModelDialog extends React.Component {
|
|||
mapping[row].type = event.target.value;
|
||||
}
|
||||
|
||||
this.setState({ mapping: mapping });
|
||||
this.setState({ [key]: mapping });
|
||||
}
|
||||
|
||||
resetState() {
|
||||
this.setState({
|
||||
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' } ]
|
||||
outputLength: '1',
|
||||
inputLength: '1',
|
||||
outputMapping: [{ name: 'Signal', type: 'Type' }],
|
||||
inputMapping: [{ name: 'Signal', type: 'Type' }]
|
||||
});
|
||||
}
|
||||
|
||||
validateForm(target) {
|
||||
// check all controls
|
||||
var name = true;
|
||||
var length = true;
|
||||
var simulator = true;
|
||||
let name = true;
|
||||
let inputLength = true;
|
||||
let outputLength = true;
|
||||
let simulator = true;
|
||||
|
||||
if (this.state.name === '') {
|
||||
name = false;
|
||||
|
@ -107,15 +120,20 @@ class NewSimulationModelDialog extends React.Component {
|
|||
}
|
||||
|
||||
// test if simulatorid is a number (in a string, not type of number)
|
||||
if (!/^\d+$/.test(this.state.length)) {
|
||||
length = false;
|
||||
if (!/^\d+$/.test(this.state.outputLength)) {
|
||||
outputLength = false;
|
||||
}
|
||||
|
||||
this.valid = name && length && simulator;
|
||||
if (!/^\d+$/.test(this.state.inputLength)) {
|
||||
inputLength = false;
|
||||
}
|
||||
|
||||
this.valid = name && inputLength && outputLength && simulator;
|
||||
|
||||
// return state to control
|
||||
if (target === 'name') return name ? "success" : "error";
|
||||
else if (target === 'length') return length ? "success" : "error";
|
||||
else if (target === 'outputLength') return outputLength ? "success" : "error";
|
||||
else if (target === 'inputLength') return inputLength ? "success" : "error";
|
||||
else if (target === 'simulator') return simulator ? "success" : "error";
|
||||
}
|
||||
|
||||
|
@ -138,18 +156,32 @@ class NewSimulationModelDialog extends React.Component {
|
|||
))}
|
||||
</FormControl>
|
||||
</FormGroup>
|
||||
<FormGroup controlId="length" validationState={this.validateForm('length')}>
|
||||
<ControlLabel>Length</ControlLabel>
|
||||
<FormControl type="number" placeholder="Enter length" min="1" value={this.state.length} onChange={(e) => this.handleChange(e)} />
|
||||
<FormGroup controlId="outputLength" validationState={this.validateForm('outputLength')}>
|
||||
<ControlLabel>Output Length</ControlLabel>
|
||||
<FormControl type="number" placeholder="Enter length" min="1" value={this.state.outputLength} onChange={(e) => this.handleChange(e)} />
|
||||
<FormControl.Feedback />
|
||||
</FormGroup>
|
||||
<FormGroup controlId="mapping">
|
||||
<ControlLabel>Mapping</ControlLabel>
|
||||
<FormGroup controlId="outputMapping">
|
||||
<ControlLabel>Output Mapping</ControlLabel>
|
||||
<HelpBlock>Click Name or Type cell to edit</HelpBlock>
|
||||
<Table data={this.state.mapping}>
|
||||
<Table data={this.state.outputMapping}>
|
||||
<TableColumn title='ID' width='60' dataIndex />
|
||||
<TableColumn title='Name' dataKey='name' inlineEditable onInlineChange={(event, row, column) => this.handleMappingChange(event, row, column)} />
|
||||
<TableColumn title='Type' dataKey='type' inlineEditable onInlineChange={(event, row, column) => this.handleMappingChange(event, row, column)} />
|
||||
<TableColumn title='Name' dataKey='name' inlineEditable onInlineChange={(event, row, column) => this.handleMappingChange('outputMapping', event, row, column)} />
|
||||
<TableColumn title='Type' dataKey='type' inlineEditable onInlineChange={(event, row, column) => this.handleMappingChange('outputMapping', event, row, column)} />
|
||||
</Table>
|
||||
</FormGroup>
|
||||
<FormGroup controlId="inputLength" validationState={this.validateForm('inputLength')}>
|
||||
<ControlLabel>Input Length</ControlLabel>
|
||||
<FormControl type="number" placeholder="Enter length" min="1" value={this.state.inputLength} onChange={(e) => this.handleChange(e)} />
|
||||
<FormControl.Feedback />
|
||||
</FormGroup>
|
||||
<FormGroup controlId="inputMapping">
|
||||
<ControlLabel>Input Mapping</ControlLabel>
|
||||
<HelpBlock>Click Name or Type cell to edit</HelpBlock>
|
||||
<Table data={this.state.inputMapping}>
|
||||
<TableColumn title='ID' width='60' dataIndex />
|
||||
<TableColumn title='Name' dataKey='name' inlineEditable onInlineChange={(event, row, column) => this.handleMappingChange('inputMapping', event, row, column)} />
|
||||
<TableColumn title='Type' dataKey='type' inlineEditable onInlineChange={(event, row, column) => this.handleMappingChange('inputMapping', event, row, column)} />
|
||||
</Table>
|
||||
</FormGroup>
|
||||
</form>
|
||||
|
|
|
@ -105,12 +105,16 @@ class WidgetFactory {
|
|||
widget.height = 100;
|
||||
widget.background_color = 1;
|
||||
widget.font_color = 0;
|
||||
widget.simulator = defaultSimulator;
|
||||
widget.signal = 0;
|
||||
break;
|
||||
case 'NumberInput':
|
||||
widget.minWidth = 200;
|
||||
widget.minHeight = 50;
|
||||
widget.width = 200;
|
||||
widget.height = 50;
|
||||
widget.simulator = defaultSimulator;
|
||||
widget.signal = 0;
|
||||
break;
|
||||
case 'Slider':
|
||||
widget.minWidth = 380;
|
||||
|
@ -118,6 +122,8 @@ class WidgetFactory {
|
|||
widget.width = 400;
|
||||
widget.height = 50;
|
||||
widget.orientation = WidgetSlider.OrientationTypes.HORIZONTAL.value; // Assign default orientation
|
||||
widget.simulator = defaultSimulator;
|
||||
widget.signal = 0;
|
||||
break;
|
||||
case 'Gauge':
|
||||
widget.simulator = defaultSimulator;
|
||||
|
|
|
@ -40,15 +40,14 @@ class WidgetGauge extends Component {
|
|||
|
||||
if (nextProps.data == null || nextProps.data[simulator.node] == 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) {
|
||||
|| nextProps.data[simulator.node][simulator.simulator].output.values.length === 0
|
||||
|| nextProps.data[simulator.node][simulator.simulator].output.values[0].length === 0) {
|
||||
this.setState({ value: 0 });
|
||||
return;
|
||||
}
|
||||
|
||||
// check if value has changed
|
||||
const signal = nextProps.data[simulator.node][simulator.simulator].values[nextProps.widget.signal];
|
||||
const signal = nextProps.data[simulator.node][simulator.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) {
|
||||
|
@ -180,7 +179,7 @@ class WidgetGauge extends Component {
|
|||
|
||||
if (this.props.simulation) {
|
||||
const 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 != null && simulationModel.length > 0 && this.props.widget.signal < simulationModel.length) ? simulationModel.mapping[this.props.widget.signal].type : '';
|
||||
signalType = (simulationModel != null && simulationModel.length > 0 && this.props.widget.signal < simulationModel.length) ? simulationModel.outputMapping[this.props.widget.signal].type : '';
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -38,13 +38,13 @@ class WidgetLamp extends Component {
|
|||
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].values == null) {
|
||||
if (nextProps.data == null || nextProps.data[node] == null || nextProps.data[node][simulator] == null || nextProps.data[node][simulator].output.values == null) {
|
||||
this.setState({ value: '' });
|
||||
return;
|
||||
}
|
||||
|
||||
// check if value has changed
|
||||
const signal = nextProps.data[node][simulator].values[nextProps.widget.signal];
|
||||
const signal = nextProps.data[node][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 });
|
||||
}
|
||||
|
|
|
@ -71,7 +71,7 @@ class WidgetPlotTable extends Component {
|
|||
// Proceed if a simulation model is available
|
||||
if (simulationModel) {
|
||||
// Create checkboxes using the signal indices from simulation model
|
||||
preselectedSignals = simulationModel.mapping.reduce(
|
||||
preselectedSignals = simulationModel.outputMapping.reduce(
|
||||
// Loop through simulation model signals
|
||||
(accum, model_signal, signal_index) => {
|
||||
// Append them if they belong to the current selected type
|
||||
|
@ -107,7 +107,7 @@ class WidgetPlotTable extends Component {
|
|||
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].values.filter((values, index) => (
|
||||
simulatorData = this.props.data[simulator.node][simulator.simulator].output.values.filter((values, index) => (
|
||||
this.props.widget.signals.findIndex(value => value === index) !== -1
|
||||
));
|
||||
}
|
||||
|
|
|
@ -43,12 +43,12 @@ class WidgetPlot extends React.Component {
|
|||
const model = simulation.models.find(model => model.simulator.node === simulator.node && model.simulator.simulator === simulator.simulator);
|
||||
const chosenSignals = nextProps.widget.signals;
|
||||
|
||||
const data = nextProps.data[simulator.node][simulator.simulator].values.filter((values, index) => (
|
||||
const data = nextProps.data[simulator.node][simulator.simulator].output.values.filter((values, index) => (
|
||||
nextProps.widget.signals.findIndex(value => value === index) !== -1
|
||||
));
|
||||
|
||||
// Query the signals that will be displayed in the legend
|
||||
const legend = model.mapping.reduce( (accum, model_signal, signal_index) => {
|
||||
const legend = model.outputMapping.reduce( (accum, model_signal, signal_index) => {
|
||||
if (chosenSignals.includes(signal_index)) {
|
||||
accum.push({ index: signal_index, name: model_signal.name, type: model_signal.type });
|
||||
}
|
||||
|
|
|
@ -55,11 +55,9 @@ class WidgetSlider extends Component {
|
|||
}
|
||||
|
||||
valueChanged(newValue) {
|
||||
// Enable to propagate action
|
||||
// let newWidget = Object.assign({}, this.props.widget, {
|
||||
// value: newValue
|
||||
// });
|
||||
// this.props.onWidgetChange(newWidget);
|
||||
if (this.props.onInputChanged) {
|
||||
this.props.onInputChanged(newValue);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
|
|
|
@ -40,9 +40,9 @@ class WidgetTable extends Component {
|
|||
|
||||
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].length === 0
|
||||
|| nextProps.data[simulator.node][simulator.simulator].values.length === 0
|
||||
|| nextProps.data[simulator.node][simulator.simulator].values[0].length === 0) {
|
||||
|| 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) {
|
||||
// clear values
|
||||
this.setState({ rows: [], sequence: null });
|
||||
return;
|
||||
|
@ -61,16 +61,16 @@ class WidgetTable extends Component {
|
|||
// get rows
|
||||
var rows = [];
|
||||
|
||||
nextProps.data[simulator.node][simulator.simulator].values.forEach((signal, index) => {
|
||||
if (index < simulationModel.mapping.length) {
|
||||
nextProps.data[simulator.node][simulator.simulator].output.values.forEach((signal, index) => {
|
||||
if (index < simulationModel.outputMapping.length) {
|
||||
rows.push({
|
||||
name: simulationModel.mapping[index].name,
|
||||
name: simulationModel.outputMapping[index].name,
|
||||
value: signal[signal.length - 1].y.toFixed(3)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
this.setState({ rows: rows, sequence: nextProps.data[simulator.node][simulator.simulator].sequence });
|
||||
this.setState({ rows: rows, sequence: nextProps.data[simulator.node][simulator.simulator].output.sequence });
|
||||
}
|
||||
|
||||
render() {
|
||||
|
|
|
@ -36,7 +36,7 @@ class WidgetValue extends Component {
|
|||
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].values == null) {
|
||||
if (nextProps.data == null || nextProps.data[node] == null || nextProps.data[node][simulator] == null || nextProps.data[node][simulator].output.values == null) {
|
||||
this.setState({ value: '' });
|
||||
return;
|
||||
}
|
||||
|
@ -47,13 +47,13 @@ class WidgetValue extends Component {
|
|||
if (nextProps.simulation) {
|
||||
const simulationModel = nextProps.simulation.models.find(model => model.simulator.node === node && model.simulator.simulator === simulator);
|
||||
|
||||
if (nextProps.widget.signal < simulationModel.mapping.length) {
|
||||
unit = simulationModel.mapping[nextProps.widget.signal].type;
|
||||
if (nextProps.widget.signal < simulationModel.outputMapping.length) {
|
||||
unit = simulationModel.outputMapping[nextProps.widget.signal].type;
|
||||
}
|
||||
}
|
||||
|
||||
// check if value has changed
|
||||
const signal = nextProps.data[node][simulator].values[nextProps.widget.signal];
|
||||
const signal = nextProps.data[node][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 });
|
||||
}
|
||||
|
|
|
@ -480,9 +480,9 @@ class Visualization extends React.Component {
|
|||
<ToolboxItem name="Label" type="widget" />
|
||||
<ToolboxItem name="Image" type="widget" />
|
||||
<ToolboxItem name="PlotTable" type="widget" />
|
||||
<ToolboxItem name="Button" type="widget" disabled />
|
||||
<ToolboxItem name="NumberInput" type="widget" disabled />
|
||||
<ToolboxItem name="Slider" type="widget" disabled />
|
||||
<ToolboxItem name="Button" type="widget" />
|
||||
<ToolboxItem name="NumberInput" type="widget" />
|
||||
<ToolboxItem name="Slider" type="widget" />
|
||||
<ToolboxItem name="Gauge" type="widget" />
|
||||
<ToolboxItem name="Box" type="widget" />
|
||||
<ToolboxItem name="HTML" type="html" />
|
||||
|
|
|
@ -153,6 +153,15 @@ class Widget extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
inputDataChanged(widget, data) {
|
||||
AppDispatcher.dispatch({
|
||||
type: 'simulatorData/inputChanged',
|
||||
simulator: widget.simulator,
|
||||
signal: widget.signal,
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
// configure grid
|
||||
const grid = [this.props.grid, this.props.grid];
|
||||
|
@ -182,7 +191,7 @@ class Widget extends React.Component {
|
|||
} else if (widget.type === 'NumberInput') {
|
||||
element = <WidgetNumberInput widget={widget} editing={this.props.editing} />
|
||||
} else if (widget.type === 'Slider') {
|
||||
element = <WidgetSlider widget={widget} editing={this.props.editing} onWidgetChange={(w) => this.props.onWidgetStatusChange(w, this.props.index) } />
|
||||
element = <WidgetSlider widget={widget} editing={this.props.editing} onWidgetChange={(w) => this.props.onWidgetStatusChange(w, this.props.index) } onInputChanged={(value) => this.inputDataChanged(widget, value)} />
|
||||
} else if (widget.type === 'Gauge') {
|
||||
element = <WidgetGauge widget={widget} data={this.state.simulatorData} editing={this.props.editing} simulation={this.props.simulation} />
|
||||
} else if (widget.type === 'Box') {
|
||||
|
|
|
@ -29,6 +29,7 @@ class RestDataManager {
|
|||
this.url = url;
|
||||
this.type = type;
|
||||
this.keyFilter = keyFilter;
|
||||
this.onLoad = null;
|
||||
}
|
||||
|
||||
makeURL(part) {
|
||||
|
@ -61,6 +62,10 @@ class RestDataManager {
|
|||
type: this.type + 's/loaded',
|
||||
data: data
|
||||
});
|
||||
|
||||
if (this.onLoad != null) {
|
||||
this.onLoad(data);
|
||||
}
|
||||
}).catch(error => {
|
||||
AppDispatcher.dispatch({
|
||||
type: this.type + 's/load-error',
|
||||
|
@ -78,6 +83,10 @@ class RestDataManager {
|
|||
type: this.type + 's/loaded',
|
||||
data: data
|
||||
});
|
||||
|
||||
if (this.onLoad != null) {
|
||||
this.onLoad(data);
|
||||
}
|
||||
}).catch(error => {
|
||||
AppDispatcher.dispatch({
|
||||
type: this.type + 's/load-error',
|
||||
|
|
|
@ -20,5 +20,25 @@
|
|||
******************************************************************************/
|
||||
|
||||
import RestDataManager from './rest-data-manager';
|
||||
import AppDispatcher from '../app-dispatcher';
|
||||
|
||||
export default new RestDataManager('simulation', '/simulations', [ '_id', 'name', 'projects', 'models' ]);
|
||||
class SimulationsDataManager extends RestDataManager {
|
||||
constructor() {
|
||||
super('simulation', '/simulations', [ '_id', 'name', 'projects', 'models' ]);
|
||||
|
||||
this.onLoad = this.onSimulationsLoad;
|
||||
}
|
||||
|
||||
onSimulationsLoad(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
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default new SimulationsDataManager();
|
||||
|
|
|
@ -22,6 +22,9 @@
|
|||
import WebsocketAPI from '../api/websocket-api';
|
||||
import AppDispatcher from '../app-dispatcher';
|
||||
|
||||
const OFFSET_TYPE = 2;
|
||||
const OFFSET_VERSION = 4;
|
||||
|
||||
class SimulatorDataDataManager {
|
||||
constructor() {
|
||||
this._sockets = {};
|
||||
|
@ -34,14 +37,14 @@ class SimulatorDataDataManager {
|
|||
// 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) });
|
||||
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) });
|
||||
}
|
||||
} 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) });
|
||||
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) });
|
||||
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) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -56,6 +59,18 @@ class SimulatorDataDataManager {
|
|||
}
|
||||
}
|
||||
|
||||
send(message, nodeId) {
|
||||
const socket = this._sockets[nodeId];
|
||||
if (socket == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const data = this.messageToBuffer(message);
|
||||
socket.send(data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
onOpen(event, node, firstOpen) {
|
||||
AppDispatcher.dispatch({
|
||||
type: 'simulatorData/opened',
|
||||
|
@ -75,6 +90,10 @@ class SimulatorDataDataManager {
|
|||
delete this._sockets[node._id];
|
||||
}
|
||||
|
||||
onError(error, node) {
|
||||
console.error('Error on ' + node._id + ':' + error);
|
||||
}
|
||||
|
||||
onMessage(event, node) {
|
||||
var msgs = this.bufferToMessageArray(event.data);
|
||||
|
||||
|
@ -93,9 +112,6 @@ class SimulatorDataDataManager {
|
|||
return null;
|
||||
}
|
||||
|
||||
const OFFSET_TYPE = 2;
|
||||
const OFFSET_VERSION = 4;
|
||||
|
||||
const id = data.getUint8(1);
|
||||
const bits = data.getUint8(0);
|
||||
const length = data.getUint16(0x02, 1);
|
||||
|
@ -130,6 +146,30 @@ class SimulatorDataDataManager {
|
|||
|
||||
return msgs;
|
||||
}
|
||||
|
||||
messageToBuffer(message) {
|
||||
const buffer = new ArrayBuffer(16 + 4 * message.length);
|
||||
const view = new DataView(buffer);
|
||||
|
||||
let bits = 0;
|
||||
bits |= (message.version & 0xF) << OFFSET_VERSION;
|
||||
bits |= (message.type & 0x3) << OFFSET_TYPE;
|
||||
|
||||
const sec = Math.floor(message.timestamp / 1e3);
|
||||
const nsec = (message.timestamp - sec * 1e3) * 1e6;
|
||||
|
||||
view.setUint8(0x00, bits, true);
|
||||
view.setUint8(0x01, message.id, true);
|
||||
view.setUint16(0x02, message.length, true);
|
||||
view.setUint32(0x04, message.sequence, true);
|
||||
view.setUint32(0x08, sec, true);
|
||||
view.setUint32(0x0C, nsec, true);
|
||||
|
||||
const data = new Float32Array(buffer, 0x10, message.length);
|
||||
data.set(message.values);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
|
||||
export default new SimulatorDataDataManager();
|
||||
|
|
|
@ -46,11 +46,30 @@ class SimulationDataStore extends ReduceStore {
|
|||
case 'simulatorData/opened':
|
||||
// create entry for simulator
|
||||
state[action.node._id] = {};
|
||||
return state;
|
||||
|
||||
action.node.simulators.forEach((simulator, index) => {
|
||||
state[action.node._id][index] = { sequence: -1, values: [] };
|
||||
});
|
||||
case 'simulatorData/prepare':
|
||||
if (state[action.node.node] == null) {
|
||||
return state;
|
||||
}
|
||||
|
||||
state[action.node.node][action.node.simulator] = {
|
||||
output: {
|
||||
sequence: -1,
|
||||
length: action.outputLength,
|
||||
values: []
|
||||
},
|
||||
input: {
|
||||
sequence: -1,
|
||||
length: action.inputLength,
|
||||
version: 2,
|
||||
type: 0,
|
||||
id: action.node.simulator,
|
||||
timestamp: Date.now(),
|
||||
values: new Array(action.inputLength).fill(0)
|
||||
}
|
||||
};
|
||||
|
||||
return state;
|
||||
|
||||
case 'simulatorData/data-changed':
|
||||
|
@ -70,22 +89,22 @@ class SimulationDataStore extends ReduceStore {
|
|||
|
||||
// add data to simulator
|
||||
for (i = 0; i < smp.length; i++) {
|
||||
while (state[action.node._id][index].values.length < i + 1) {
|
||||
state[action.node._id][index].values.push([]);
|
||||
while (state[action.node._id][index].output.values.length < i + 1) {
|
||||
state[action.node._id][index].output.values.push([]);
|
||||
}
|
||||
|
||||
state[action.node._id][index].values[i].push({ x: smp.timestamp, y: smp.values[i] });
|
||||
state[action.node._id][index].output.values[i].push({ x: smp.timestamp, y: smp.values[i] });
|
||||
|
||||
// erase old values
|
||||
if (state[action.node._id][index].values[i].length > MAX_VALUES) {
|
||||
const pos = state[action.node._id][index].values[i].length - MAX_VALUES;
|
||||
state[action.node._id][index].values[i].splice(0, pos);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// update metadata
|
||||
state[action.node._id][index].timestamp = smp.timestamp;
|
||||
state[action.node._id][index].sequence = smp.sequence;
|
||||
state[action.node._id][index].output.timestamp = smp.timestamp;
|
||||
state[action.node._id][index].output.sequence = smp.sequence;
|
||||
}
|
||||
|
||||
// explicit call to prevent array copy
|
||||
|
@ -93,6 +112,21 @@ class SimulationDataStore extends ReduceStore {
|
|||
|
||||
return state;
|
||||
|
||||
case 'simulatorData/inputChanged':
|
||||
// find simulator in node array
|
||||
if (state[action.simulator.node] == null || state[action.simulator.node][action.simulator.simulator] == null) {
|
||||
return state;
|
||||
}
|
||||
|
||||
// update message properties
|
||||
state[action.simulator.node][action.simulator.simulator].input.timestamp = Date.now();
|
||||
state[action.simulator.node][action.simulator.simulator].input.sequence++;
|
||||
state[action.simulator.node][action.simulator.simulator].input.values[action.signal] = action.data;
|
||||
|
||||
SimulatorDataDataManager.send(state[action.simulator.node][action.simulator.simulator].input, action.simulator.node);
|
||||
|
||||
return state;
|
||||
|
||||
case 'simulatorData/closed':
|
||||
// close and delete socket
|
||||
if (state[action.node] != null) {
|
||||
|
|
Loading…
Add table
Reference in a new issue