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

Add websocket api with simulator data

Simulator data is passed to each widget to process the data
This commit is contained in:
Markus Grigull 2017-03-04 10:31:13 +01:00
parent d9a2ae55a9
commit 5441bd46f6
6 changed files with 218 additions and 17 deletions

36
src/api/websocket-api.js Normal file
View file

@ -0,0 +1,36 @@
/**
* File: websocket-api.js
* Author: Markus Grigull <mgrigull@eonerc.rwth-aachen.de>
* Date: 03.03.2017
* Copyright: 2017, Institute for Automation of Complex Power Systems, EONERC
* This file is part of VILLASweb. All Rights Reserved. Proprietary and confidential.
* Unauthorized copying of this file, via any medium is strictly prohibited.
**********************************************************************************/
class WebsocketAPI {
constructor() {
this.sockets = {};
}
addSocket(endpoint, identifier, callbacks) {
// create web socket client
var socket = new WebSocket('ws://' + endpoint, 'live');
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));
// save socket
this.sockets[identifier] = socket;
}
removeSocket(identifier) {
this.sockets[identifier].close();
delete this.sockets[identifier];
}
}
export default new WebsocketAPI();

View file

@ -14,7 +14,7 @@ import { ContextMenu, MenuItem } from 'react-contextmenu';
import ToolboxItem from '../components/toolbox-item';
import Dropzone from '../components/dropzone';
import Widget from '../components/widget';
import Widget from './widget';
import VisualizationStore from '../stores/visualization-store';
import AppDispatcher from '../app-dispatcher';
@ -27,8 +27,7 @@ class Visualization extends Component {
return {
visualizations: VisualizationStore.getState(),
visualization: {},
editing: false
visualization: {}
}
}
@ -92,6 +91,14 @@ class Visualization extends Component {
AppDispatcher.dispatch({
type: 'visualizations/start-load'
});
AppDispatcher.dispatch({
type: 'simulatorData/open',
endpoint: 'localhost:5000',
identifier: 'RTDS'
});
this.setState({ editing: false });
}
componentDidUpdate() {
@ -111,8 +118,6 @@ class Visualization extends Component {
}
render() {
console.log(this.state.visualization.widgets);
return (
<div>
<div>

View file

@ -1,26 +1,32 @@
/**
* File: widget.js
* Author: Markus Grigull <mgrigull@eonerc.rwth-aachen.de>
* Date: 02.03.2017
* Date: 04.03.2017
* Copyright: 2017, Institute for Automation of Complex Power Systems, EONERC
* This file is part of VILLASweb. All Rights Reserved. Proprietary and confidential.
* Unauthorized copying of this file, via any medium is strictly prohibited.
**********************************************************************************/
import React, { Component } from 'react';
import Rnd from 'react-rnd';
import { Container } from 'flux/utils';
import { ContextMenuTrigger } from 'react-contextmenu';
import Rnd from 'react-rnd';
import SimulatorDataStore from '../stores/simulator-data-store';
import '../styles/widgets.css';
class Widget extends Component {
resizeStop(direction, styleSize, clientSize, delta) {
// update widget
var widget = this.props.data;
widget.width = styleSize.width;
widget.height = styleSize.height;
static getStores() {
return [ SimulatorDataStore ];
}
this.props.onWidgetChange(widget, this.props.index);
static calculateState() {
return {
simulatorData: SimulatorDataStore.getState(),
widget: {}
};
}
dragStop(event, ui) {
@ -32,9 +38,25 @@ class Widget extends Component {
this.props.onWidgetChange(widget, this.props.index);
}
resizeStop(direction, styleSize, clientSize, delta) {
// update widget
var widget = this.props.data;
widget.width = styleSize.width;
widget.height = styleSize.height;
this.props.onWidgetChange(widget, this.props.index);
}
render() {
const widget = this.props.data;
var value = '';
if (this.state.simulatorData.RTDS && this.state.simulatorData.RTDS.values) {
const arr = this.state.simulatorData.RTDS.values[this.props.index];
value = arr[arr.length - 1].y;
}
if (this.props.editing) {
return (
<Rnd
@ -46,18 +68,18 @@ class Widget extends Component {
onDragStop={(event, ui) => this.dragStop(event, ui)}
>
<ContextMenuTrigger id={'widgetMenu' + this.props.index} attributes={{ style: { width: '100%', height: '100%' } }}>
<div>{widget.name}</div>
<div>{value}</div>
</ContextMenuTrigger>
</Rnd>
);
} else {
return (
<div className="widget" style={{ width: Number(widget.width), height: Number(widget.height), left: Number(widget.x), top: Number(widget.y), position: 'absolute' }}>
{widget.name}
{value}
</div>
);
}
}
}
export default Widget;
export default Container.create(Widget);

View file

@ -1,5 +1,5 @@
/**
* File: data-manager.js
* File: rest-data-manager.js
* Author: Markus Grigull <mgrigull@eonerc.rwth-aachen.de>
* Date: 03.03.2017
* Copyright: 2017, Institute for Automation of Complex Power Systems, EONERC

View file

@ -0,0 +1,72 @@
/**
* File: simulator-data-manager.js
* Author: Markus Grigull <mgrigull@eonerc.rwth-aachen.de>
* Date: 03.03.2017
* Copyright: 2017, Institute for Automation of Complex Power Systems, EONERC
* This file is part of VILLASweb. All Rights Reserved. Proprietary and confidential.
* Unauthorized copying of this file, via any medium is strictly prohibited.
**********************************************************************************/
import WebsocketAPI from '../api/websocket-api';
import AppDispatcher from '../app-dispatcher';
class SimulationDataManager {
open(endpoint, identifier) {
WebsocketAPI.addSocket(endpoint, identifier, { onOpen: event => this.onOpen(event), onClose: event => this.onClose(event), onMessage: event => this.onMessage(event) });
}
onOpen(event) {
// TODO: Add identifiers to callbacks
AppDispatcher.dispatch({
type: 'simulatorData/opened',
identifier: 'RTDS',
signals: 8
});
}
onClose(event) {
AppDispatcher.dispatch({
type: 'simulatorData/closed'
});
}
onMessage(event) {
var message = this.bufferToMessage(event.data);
AppDispatcher.dispatch({
type: 'simulatorData/data-changed',
data: message,
identifier: 'RTDS'
});
}
bufferToMessage(blob) {
// parse incoming message into usable data
var data = new DataView(blob);
let OFFSET_ENDIAN = 1;
let OFFSET_TYPE = 2;
let OFFSET_VERSION = 4;
var bits = data.getUint8(0);
var simulator = data.getUint8(0x01);
var endian = (bits >> OFFSET_ENDIAN) & 0x1 ? 0 : 1;
var length = data.getUint16(0x02, endian);
var values = new Float32Array(data.buffer, data.byteOffset + 0x10, length);
return {
endian: endian,
version: (bits >> OFFSET_VERSION) & 0xF,
type: (bits >> OFFSET_TYPE) & 0x3,
length: length,
sequence: data.getUint32(0x04, endian),
timestamp: data.getUint32(0x08, endian) * 1e3 + data.getUint32(0x0C, endian) * 1e-6,
values: values,
simulator: simulator
};
}
}
export default new SimulationDataManager();

View file

@ -0,0 +1,66 @@
/**
* File: simulator-data-store.js
* Author: Markus Grigull <mgrigull@eonerc.rwth-aachen.de>
* Date: 03.03.2017
* Copyright: 2017, Institute for Automation of Complex Power Systems, EONERC
* This file is part of VILLASweb. All Rights Reserved. Proprietary and confidential.
* Unauthorized copying of this file, via any medium is strictly prohibited.
**********************************************************************************/
import { ReduceStore } from 'flux/utils';
import AppDispatcher from '../app-dispatcher';
import SimulatorDataManager from '../data-managers/simulator-data-manager';
class SimulationDataStore extends ReduceStore {
constructor() {
super(AppDispatcher);
}
getInitialState() {
return {};
}
reduce(state, action) {
var i;
switch (action.type) {
case 'simulatorData/open':
SimulatorDataManager.open(action.endpoint, action.identifier);
return state;
case 'simulatorData/opened':
// create entry for simulator
state[action.identifier] = { signals: action.signals, values: [], sequence: null, timestamp: null };
for (i = 0; i < action.signals; i++) {
state[action.identifier].values.push([]);
}
return state;
case 'simulatorData/data-changed':
// add data to simulator
for (i = 0; i < state[action.identifier].signals; i++) {
state[action.identifier].values[i].push({ x: action.data.timestamp, y: action.data.values[i] });
}
// update metadata
state[action.identifier].timestamp = action.data.timestamp;
state[action.identifier].sequence = action.data.sequence;
// explicit call to prevent array copy
this.__emitChange();
return state;
case 'simulatorData/closed':
return state;
default:
return state;
}
}
}
export default new SimulationDataStore();