diff --git a/src/app.js b/src/app.js
index 510a649..7940505 100644
--- a/src/app.js
+++ b/src/app.js
@@ -42,7 +42,6 @@ import { useSelector } from 'react-redux';
const App = () => {
const isTokenExpired = (token) => {
- console.log("decoded, ", jwt.decode(token))
let decodedToken = jwt.decode(token);
let timeNow = (new Date().getTime() + 1) / 1000;
return decodedToken.exp < timeNow;
diff --git a/src/common/api/websocket-api.js b/src/common/api/websocket-api.js
index e55290a..70e741f 100644
--- a/src/common/api/websocket-api.js
+++ b/src/common/api/websocket-api.js
@@ -14,101 +14,87 @@
* You should have received a copy of the GNU General Public License
* along with VILLASweb. If not, see .
******************************************************************************/
-import NotificationsDataManager from "../data-managers/notifications-data-manager";
-import NotificationsFactory from "../data-managers/notifications-factory";
-import AppDispatcher from '../app-dispatcher';
-class WebsocketAPI {
- constructor(websocketurl, callbacks) {
- this.websocketurl = websocketurl;
- this.callbacks = callbacks;
+const OFFSET_TYPE = 2;
+const OFFSET_VERSION = 4;
- this.wasConnected = false;
- this.isClosing = false;
-
- this.connect(websocketurl, callbacks);
+class WebSocketManager {
+ constructor() {
+ this.socket = null;
}
- connect(websocketurl, callbacks) {
- // create web socket client
- this.socket = new WebSocket(WebsocketAPI.getURL(websocketurl), 'live');
- this.socket.binaryType = 'arraybuffer';
- this.socket.onclose = this.onClose;
- this.socket.onopen = this.onOpen;
- this.socket.onerror = this.onError;
+ id = null;
- // register callbacks
- if (callbacks.onMessage)
- this.socket.onmessage = callbacks.onMessage;
- }
-
- reconnect() {
- //console.log("Reconnecting: " + this.websocketurl);
- this.connect(this.websocketurl, this.callbacks);
- }
-
- get url() {
- return WebsocketAPI.getURL(this.websocketurl);
- }
-
- send(data) {
- this.socket.send(data);
- }
-
- close(code, reason) {
- this.isClosing = true;
- this.socket.close(code, reason);
- }
-
- onError = e => {
- console.error('Error on WebSocket connection to: ' + this.websocketurl + ':', e);
-
- if ('onError' in this.callbacks)
- this.callbacks.onError(e);
- }
-
- onOpen = e => {
- AppDispatcher.dispatch({
- type: 'websocket/connected',
- data: this.websocketurl,
- });
- this.wasConnected = true;
-
- if ('onOpen' in this.callbacks)
- this.callbacks.onOpen(e);
- }
-
- onClose = e => {
- if (this.isClosing) {
- if ('onClose' in this.callbacks)
- this.callbacks.onClose(e);
+ connect(url, onMessage, onOpen, onClose) {
+ if (this.socket && this.socket.readyState === WebSocket.OPEN) {
+ console.log('Already connected to:', url);
+ return;
}
- else {
- if (this.wasConnected) {
- AppDispatcher.dispatch({
- type: 'websocket/connection-error',
- data: this.websocketurl,
- });
- NotificationsDataManager.addNotification(NotificationsFactory.WEBSOCKET_CONNECTION_WARN(this.websocketurl));
- console.log("Connection to " + this.websocketurl + " dropped. Attempt reconnect in 1 sec");
- window.setTimeout(() => { this.reconnect(); }, 1000);
+ if (this.socket) {
+ this.socket.close();
+ }
+
+ this.socket = new WebSocket(url, 'live');
+ this.socket.binaryType = 'arraybuffer';
+
+ this.socket.onopen = onOpen;
+ this.socket.onmessage = (event) => {
+ const msgs = this.bufferToMessageArray(event.data);
+ onMessage(msgs);
+ };
+ this.socket.onclose = onClose;
+ }
+
+ disconnect() {
+ if (this.socket) {
+ this.socket.close();
+ this.socket = null;
+ console.log('WebSocket connection closed');
+ }
+ }
+
+ bufferToMessageArray(blob) {
+ /* some local variables for parsing */
+ let offset = 0;
+ const msgs = [];
+
+ /* for every msg in vector */
+ while (offset < blob.byteLength) {
+ const msg = this.bufferToMessage(new DataView(blob, offset));
+
+ if (msg !== undefined) {
+ msgs.push(msg);
+ offset += msg.blob.byteLength;
}
}
- }
- static getURL(websocketurl) {
- // create an anchor element (note: no need to append this element to the document)
- var link = document.createElement('a');
- link.href = websocketurl;
-
- if (link.protocol === 'https:')
- link.protocol = 'wss:';
- else
- link.protocol = 'ws:';
-
- return link.href;
- }
+ return msgs;
}
-export default WebsocketAPI;
+ bufferToMessage(data) {
+ // parse incoming message into usable data
+ if (data.byteLength === 0) {
+ return null;
+ }
+
+ const source_index = data.getUint8(1);
+ const bits = data.getUint8(0);
+ const length = data.getUint16(0x02, 1);
+ const bytes = length * 4 + 16;
+
+ return {
+ version: (bits >> OFFSET_VERSION) & 0xF,
+ type: (bits >> OFFSET_TYPE) & 0x3,
+ source_index: source_index,
+ length: length,
+ sequence: data.getUint32(0x04, 1),
+ timestamp: data.getUint32(0x08, 1) * 1e3 + data.getUint32(0x0C, 1) * 1e-6,
+ values: new Float32Array(data.buffer, data.byteOffset + 0x10, length),
+ blob: new DataView(data.buffer, data.byteOffset + 0x00, bytes),
+ // id: id
+ };
+}
+}
+
+export const wsManager = new WebSocketManager();
diff --git a/src/pages/dashboards/dashboard-button-group.js b/src/pages/dashboards/dashboard-button-group.js
index 71db45e..04a1500 100644
--- a/src/pages/dashboards/dashboard-button-group.js
+++ b/src/pages/dashboards/dashboard-button-group.js
@@ -28,7 +28,7 @@ const buttonStyle = {
const iconStyle = {
height: '25px',
width: '25px'
-}
+};
let buttonkey = 0;
diff --git a/src/pages/dashboards/dashboard.js b/src/pages/dashboards/dashboard.js
index 971abb8..ce3fcb9 100644
--- a/src/pages/dashboards/dashboard.js
+++ b/src/pages/dashboards/dashboard.js
@@ -1,447 +1,521 @@
-// import React, { useState, useEffect, useCallback } from 'react';
-// import { useParams } from 'react-router-dom';
-// import Fullscreenable from 'react-fullscreenable';
-// import classNames from 'classnames';
-// import 'react-contexify/dist/ReactContexify.min.css';
-// import EditWidget from '../../widget/edit-widget/edit-widget';
-// import EditFilesDialog from '../../file/edit-files';
-// import EditSignalMappingDialog from '../scenarios/dialogs/edit-signal-mapping'
-// import WidgetToolbox from '../../widget/widget-toolbox';
-// import WidgetArea from './widget-area';
-// import DashboardButtonGroup from './dashboard-button-group';
-// import IconToggleButton from '../../common/buttons/icon-toggle-button';
-// import WidgetContainer from '../../widget/widget-container';
-// import Widget from "../../widget/widget";
-// import {
-// useGetDashboardQuery,
-// useLazyGetWidgetsQuery,
-// useLazyGetConfigsQuery,
-// useAddWidgetMutation,
-// useUpdateWidgetMutation,
-// useDeleteWidgetMutation
-// } from '../../store/apiSlice';
+import React, { useState, useEffect, useCallback, useRef } from 'react';
+import { useDispatch, useSelector } from 'react-redux';
+import { useParams } from 'react-router-dom';
+import Fullscreenable from 'react-fullscreenable';
+import classNames from 'classnames';
+import 'react-contexify/dist/ReactContexify.min.css';
+import EditWidget from './widget/edit-widget/edit-widget';
+import EditSignalMappingDialog from '../scenarios/dialogs/edit-signal-mapping'
+import WidgetToolbox from './widget/widget-toolbox';
+import WidgetArea from './widget-area';
+import DashboardButtonGroup from './dashboard-button-group';
+import IconToggleButton from '../../common/buttons/icon-toggle-button';
+import WidgetContainer from "./widget/widget-container";
+import Widget from "./widget/widget";
-// const startUpdaterWidgets = new Set(['Slider', 'Button', 'NumberInput']);
+import { connectWebSocket, disconnect } from '../../store/websocketSlice';
-// const Dashboard = ({ isFullscreen, toggleFullscreen }) => {
-// const params = useParams();
-// const { data: dashboardRes, error: dashboardError, isLoading: isDashboardLoading } = useGetDashboardQuery(params.dashboard);
-// const dashboard = dashboardRes ? dashboardRes.dashboard : {};
+import {
+ useGetDashboardQuery,
+ useLazyGetWidgetsQuery,
+ useLazyGetConfigsQuery,
+ useAddWidgetMutation,
+ useUpdateWidgetMutation,
+ useDeleteWidgetMutation,
+ useLazyGetFilesQuery,
+ useUpdateDashboardMutation,
+ useGetICSQuery,
+ useLazyGetSignalsQuery
+} from '../../store/apiSlice';
-// const [triggerGetWidgets] = useLazyGetWidgetsQuery();
-// const [triggerGetConfigs] = useLazyGetConfigsQuery();
-// const [addWidget] = useAddWidgetMutation();
-// const [updateWidget] = useUpdateWidgetMutation();
-// const [deleteWidgetMutation] = useDeleteWidgetMutation();
+const startUpdaterWidgets = new Set(['Slider', 'Button', 'NumberInput']);
-// const [widgets, setWidgets] = useState([]);
-// const [configs, setConfigs] = useState([]);
-// const [signals, setSignals] = useState([]);
-// const [sessionToken, setSessionToken] = useState(localStorage.getItem("token"));
-// const [files, setFiles] = useState([]);
-// const [ics, setIcs] = useState([]);
-// const [editing, setEditing] = useState(false);
-// const [paused, setPaused] = useState(false);
-// const [editModal, setEditModal] = useState(false);
-// const [editOutputSignalsModal, setEditOutputSignalsModal] = useState(false);
-// const [editInputSignalsModal, setEditInputSignalsModal] = useState(false);
-// const [filesEditModal, setFilesEditModal] = useState(false);
-// const [filesEditSaveState, setFilesEditSaveState] = useState([]);
-// const [modalData, setModalData] = useState(null);
-// const [modalIndex, setModalIndex] = useState(null);
-// const [widgetChangeData, setWidgetChangeData] = useState([]);
-// const [widgetOrigIDs, setWidgetOrigIDs] = useState([]);
-// const [maxWidgetHeight, setMaxWidgetHeight] = useState(null);
-// const [locked, setLocked] = useState(false);
+const Dashboard = ({ isFullscreen, toggleFullscreen }) => {
+ const dispatch = useDispatch();
+ const params = useParams();
+ const { data: dashboardRes, error: dashboardError, isLoading: isDashboardLoading } = useGetDashboardQuery(params.dashboard);
+ const dashboard = dashboardRes ? dashboardRes.dashboard : {};
+ const {data: icsRes} = useGetICSQuery();
+ const ics = icsRes ? icsRes.ics : [];
-// useEffect(() => {
-// if (dashboard.id) {
-// fetchWidgets(dashboard.id);
-// fetchConfigs(dashboard.scenarioID);
-// }
-// }, [dashboard]);
+ const [triggerGetWidgets] = useLazyGetWidgetsQuery();
+ const [triggerGetConfigs] = useLazyGetConfigsQuery();
+ const [triggerGetFiles] = useLazyGetFilesQuery();
+ const [triggerGetSignals] = useLazyGetSignalsQuery();
+ const [addWidget] = useAddWidgetMutation();
+ const [updateWidget] = useUpdateWidgetMutation();
+ const [deleteWidgetMutation] = useDeleteWidgetMutation();
+ const [updateDashboard] = useUpdateDashboardMutation();
-// const fetchWidgets = async (dashboardID) => {
-// try {
-// const res = await triggerGetWidgets(dashboardID).unwrap();
-// if (res.widgets) {
-// setWidgets(res.widgets);
-// }
-// } catch (err) {
-// console.log('error', err);
-// }
-// };
+ const [widgets, setWidgets] = useState([]);
+ const [widgetsToUpdate, setWidgetsToUpdate] = useState([]);
+ const [configs, setConfigs] = useState([]);
+ const [signals, setSignals] = useState([]);
+ const [sessionToken, setSessionToken] = useState(localStorage.getItem("token"));
+ const [files, setFiles] = useState([]);
+ const [editing, setEditing] = useState(false);
+ const [paused, setPaused] = useState(false);
+ const [editModal, setEditModal] = useState(false);
+ const [editOutputSignalsModal, setEditOutputSignalsModal] = useState(false);
+ const [editInputSignalsModal, setEditInputSignalsModal] = useState(false);
+ const [filesEditModal, setFilesEditModal] = useState(false);
+ const [filesEditSaveState, setFilesEditSaveState] = useState([]);
+ const [modalData, setModalData] = useState(null);
+ const [modalIndex, setModalIndex] = useState(null);
+ const [widgetChangeData, setWidgetChangeData] = useState([]);
+ const [widgetOrigIDs, setWidgetOrigIDs] = useState([]);
+ const [maxWidgetHeight, setMaxWidgetHeight] = useState(null);
+ const [locked, setLocked] = useState(false);
-// const fetchConfigs = async (scenarioID) => {
-// try {
-// const res = await triggerGetConfigs(scenarioID).unwrap();
-// if (res.configs) {
-// setConfigs(res.configs);
-// }
-// } catch (err) {
-// console.log('error', err);
-// }
-// };
+ const [height, setHeight] = useState(10);
+ const [grid, setGrid] = useState(50);
+ const [newHeightValue, setNewHeightValue] = useState(0);
-// const handleKeydown = useCallback((e) => {
-// switch (e.key) {
-// case ' ':
-// case 'p':
-// setPaused(prevPaused => !prevPaused);
-// break;
-// case 'e':
-// setEditing(prevEditing => !prevEditing);
-// break;
-// case 'f':
-// toggleFullscreen();
-// break;
-// default:
-// }
-// }, [toggleFullscreen]);
-// useEffect(() => {
-// window.addEventListener('keydown', handleKeydown);
-// return () => {
-// window.removeEventListener('keydown', handleKeydown);
-// };
-// }, [handleKeydown]);
+ useEffect(() => {
+ const wsUrl = 'wss://villas.k8s.eonerc.rwth-aachen.de/ws/ws_sig';
+ dispatch(connectWebSocket({ url: wsUrl, id: 547627 }));
-// const handleDrop = async (widget) => {
-// widget.dashboardID = dashboard.id;
+ return () => {
+ dispatch(disconnect());
+ };
+ }, [dispatch]);
-// if (widget.type === 'ICstatus') {
-// let allICids = ics.map(ic => ic.id);
-// widget.customProperties.checkedIDs = allICids;
-// }
-// try {
-// const res = await addWidget(widget).unwrap();
-// if (res) {
-// fetchWidgets(dashboard.id);
-// }
-// } catch (err) {
-// console.log('error', err);
-// }
-// };
+ useEffect(() => {
+ if (dashboard.id) {
+ //as soon as dashboard is loaded, load widgets, configs, signals and files for this dashboard
+ fetchWidgets(dashboard.id);
+ fetchWidgetData(dashboard.scenarioID);
+ setHeight(dashboard.height);
+ setGrid(dashboard.grid);
+ }
+ }, [dashboard]);
-// const widgetChange = async (widget) => {
-// setWidgetChangeData(prevWidgetChangeData => [...prevWidgetChangeData, widget]);
+ const fetchWidgets = async (dashboardID) => {
+ try {
+ const widgetsRes = await triggerGetWidgets(dashboardID).unwrap();
+ if (widgetsRes.widgets) {
+ setWidgets(widgetsRes.widgets);
+ }
+ } catch (err) {
+ console.log('error fetching data', err);
+ }
+ }
-// try {
-// await updateWidget({ widgetID: widget.id, updatedWidget: { widget } }).unwrap();
-// fetchWidgets(dashboard.id);
-// } catch (err) {
-// console.log('error', err);
-// }
-// };
+ const fetchWidgetData = async (scenarioID) => {
+ try {
+ const filesRes = await triggerGetFiles(scenarioID).unwrap();
+ if (filesRes.files) {
+ setFiles(filesRes.files);
+ }
+ const configsRes = await triggerGetConfigs(scenarioID).unwrap();
+ if (configsRes.configs) {
+ setConfigs(configsRes.configs);
+ //load signals if there are any configs
-// const onChange = async (widget) => {
-// try {
-// await updateWidget({ widgetID: widget.id, updatedWidget: { widget } }).unwrap();
-// fetchWidgets(dashboard.id);
-// } catch (err) {
-// console.log('error', err);
-// }
-// };
+ if(configsRes.configs.length > 0){
+ for(const config of configsRes.configs){
+ const signalsInRes = await triggerGetSignals({configID: config.id, direction: "in"}).unwrap();
+ const signalsOutRes = await triggerGetSignals({configID: config.id, direction: "out"}).unwrap();
+ setSignals(prevState => ([...signalsInRes.signals, ...signalsOutRes.signals, ...prevState]));
+ }
+ }
+
+ }
+ } catch (err) {
+ console.log('error fetching data', err);
+ }
+ }
-// const onSimulationStarted = () => {
-// widgets.forEach(async (widget) => {
-// if (startUpdaterWidgets.has(widget.type)) {
-// widget.customProperties.simStartedSendValue = true;
-// try {
-// await updateWidget({ widgetID: widget.id, updatedWidget: { widget } }).unwrap();
-// } catch (err) {
-// console.log('error', err);
-// }
-// }
-// });
-// };
+ const handleKeydown = useCallback((e) => {
+ switch (e.key) {
+ case ' ':
+ case 'p':
+ setPaused(prevPaused => !prevPaused);
+ break;
+ case 'e':
+ setEditing(prevEditing => !prevEditing);
+ break;
+ case 'f':
+ toggleFullscreen();
+ break;
+ default:
+ }
+ }, [toggleFullscreen]);
-// const editWidget = (widget, index) => {
-// setEditModal(true);
-// setModalData(widget);
-// setModalIndex(index);
-// };
+ useEffect(() => {
+ window.addEventListener('keydown', handleKeydown);
+ return () => {
+ window.removeEventListener('keydown', handleKeydown);
+ };
+ }, [handleKeydown]);
-// const duplicateWidget = async (widget) => {
-// let widgetCopy = { ...widget, id: undefined, x: widget.x + 50, y: widget.y + 50 };
-// try {
-// const res = await addWidget({ widget: widgetCopy }).unwrap();
-// if (res) {
-// fetchWidgets(dashboard.id);
-// }
-// } catch (err) {
-// console.log('error', err);
-// }
-// };
+ const handleDrop = async (widget) => {
+ widget.dashboardID = dashboard.id;
-// const startEditFiles = () => {
-// let tempFiles = files.map(file => ({ id: file.id, name: file.name }));
-// setFilesEditModal(true);
-// setFilesEditSaveState(tempFiles);
-// };
+ if (widget.type === 'ICstatus') {
+ let allICids = ics.map(ic => ic.id);
+ widget.customProperties.checkedIDs = allICids;
+ }
-// const closeEditFiles = () => {
-// widgets.forEach(widget => {
-// if (widget.type === "Image") {
-// widget.customProperties.update = true;
-// }
-// });
-// setFilesEditModal(false);
-// };
+ try {
+ const res = await addWidget(widget).unwrap();
+ if (res) {
+ fetchWidgets(dashboard.id);
+ }
+ } catch (err) {
+ console.log('error', err);
+ }
+ };
-// const closeEdit = async (data) => {
-// if (!data) {
-// setEditModal(false);
-// setModalData(null);
-// setModalIndex(null);
-// return;
-// }
+ const widgetChange = async (widget) => {
+ setWidgetsToUpdate(prevWidgetsToUpdate => [...prevWidgetsToUpdate, widget.id]);
+ setWidgets(prevWidgets => prevWidgets.map(w => w.id === widget.id ? {...widget} : w));
-// if (data.type === "Image") {
-// data.customProperties.update = true;
-// }
+ // try {
+ // await updateWidget({ widgetID: widget.id, updatedWidget: { widget } }).unwrap();
+ // fetchWidgets(dashboard.id);
+ // } catch (err) {
+ // console.log('error', err);
+ // }
+ };
-// try {
-// await updateWidget({ widgetID: data.id, updatedWidget: { widget: data } }).unwrap();
-// fetchWidgets(dashboard.id);
-// } catch (err) {
-// console.log('error', err);
-// }
+ const onChange = async (widget) => {
+ try {
+ await updateWidget({ widgetID: widget.id, updatedWidget: { widget: widget } }).unwrap();
+ fetchWidgets(dashboard.id);
+ } catch (err) {
+ console.log('error', err);
+ }
+ };
-// setEditModal(false);
-// setModalData(null);
-// setModalIndex(null);
-// };
+ const onSimulationStarted = () => {
+ widgets.forEach(async (widget) => {
+ if (startUpdaterWidgets.has(widget.type)) {
+ widget.customProperties.simStartedSendValue = true;
+ try {
+ await updateWidget({ widgetID: widget.id, updatedWidget: { widget } }).unwrap();
+ } catch (err) {
+ console.log('error', err);
+ }
+ }
+ });
+ };
-// const deleteWidget = async (widgetID) => {
-// try {
-// await deleteWidgetMutation(widgetID).unwrap();
-// fetchWidgets(dashboard.id);
-// } catch (err) {
-// console.log('error', err);
-// }
-// };
+ const editWidget = (widget, index) => {
+ setEditModal(true);
+ setModalData({...widget});
+ setModalIndex(index);
+ };
-// const startEditing = () => {
-// let originalIDs = widgets.map(widget => widget.id);
-// widgets.forEach(async (widget) => {
-// if (widget.type === 'Slider' || widget.type === 'NumberInput' || widget.type === 'Button') {
-// try {
-// await updateWidget({ widgetID: widget.id, updatedWidget: { widget } }).unwrap();
-// } catch (err) {
-// console.log('error', err);
-// }
-// } else if (widget.type === 'Image') {
-// widget.customProperties.update = true;
-// }
-// });
-// setEditing(true);
-// setWidgetOrigIDs(originalIDs);
-// };
+ const duplicateWidget = async (widget) => {
+ let widgetCopy = { ...widget, id: undefined, x: widget.x + 50, y: widget.y + 50 };
+ try {
+ const res = await addWidget({ widget: widgetCopy }).unwrap();
+ if (res) {
+ fetchWidgets(dashboard.id);
+ }
+ } catch (err) {
+ console.log('error', err);
+ }
+ };
-// const saveEditing = () => {
-// widgets.forEach(async (widget) => {
-// if (widget.type === 'Image') {
-// widget.customProperties.update = true;
-// }
-// try {
-// await updateWidget({ widgetID: widget.id, updatedWidget: { widget } }).unwrap();
-// } catch (err) {
-// console.log('error', err);
-// }
-// });
-// setEditing(false);
-// setWidgetChangeData([]);
-// };
+ const startEditFiles = () => {
+ let tempFiles = files.map(file => ({ id: file.id, name: file.name }));
+ setFilesEditModal(true);
+ setFilesEditSaveState(tempFiles);
+ };
-// const cancelEditing = () => {
-// widgets.forEach(async (widget) => {
-// if (widget.type === 'Image') {
-// widget.customProperties.update = true;
-// }
-// if (!widgetOrigIDs.includes(widget.id)) {
-// try {
-// await deleteWidget(widget.id).unwrap();
-// } catch (err) {
-// console.log('error', err);
-// }
-// }
-// });
-// setEditing(false);
-// setWidgetChangeData([]);
-// };
+ const closeEditFiles = () => {
+ widgets.forEach(widget => {
+ if (widget.type === "Image") {
+ //widget.customProperties.update = true;
+ }
+ });
+ setFilesEditModal(false);
+ };
-// const setGrid = (value) => {
-// setState(prevState => ({ ...prevState, dashboard: { ...dashboard, grid: value } }));
-// };
+ const closeEdit = async (data) => {
+ if (!data) {
+ setEditModal(false);
+ setModalData(null);
+ setModalIndex(null);
+ return;
+ }
-// const setDashboardSize = (value) => {
-// const maxHeight = Object.values(widgets).reduce((currentHeight, widget) => {
-// const absolutHeight = widget.y + widget.height;
-// return absolutHeight > currentHeight ? absolutHeight : currentHeight;
-// }, 0);
+ if (data.type === "Image") {
+ data.customProperties.update = true;
+ }
-// if (value === -1) {
-// if (dashboard.height >= 450 && dashboard.height >= (maxHeight + 80)) {
-// setState(prevState => ({ ...prevState, dashboard: { ...dashboard, height: dashboard.height - 50 } }));
-// }
-// } else {
-// setState(prevState => ({ ...prevState, dashboard: { ...dashboard, height: dashboard.height + 50 } }));
-// }
-// };
+ try {
+ await updateWidget({ widgetID: data.id, updatedWidget: { widget: data } }).unwrap();
+ fetchWidgets(dashboard.id);
+ } catch (err) {
+ console.log('error', err);
+ }
-// const pauseData = () => setPaused(true);
-// const unpauseData = () => setPaused(false);
-// const editInputSignals = () => setEditInputSignalsModal(true);
-// const editOutputSignals = () => setEditOutputSignalsModal(true);
+ setEditModal(false);
+ setModalData(null);
+ setModalIndex(null);
+ };
-// const closeEditSignalsModal = (direction) => {
-// if (direction === "in") {
-// setEditInputSignalsModal(false);
-// } else if (direction === "out") {
-// setEditOutputSignalsModal(false);
-// }
-// };
+ const deleteWidget = async (widgetID) => {
+ try {
+ await deleteWidgetMutation(widgetID).unwrap();
+ fetchWidgets(dashboard.id);
+ } catch (err) {
+ console.log('error', err);
+ }
+ };
-// const buttonStyle = { marginLeft: '10px' };
-// const iconStyle = { height: '25px', width: '25px' };
-// const grid = dashboard.grid;
-// const boxClasses = classNames('section', 'box', { 'fullscreen-padding': isFullscreen });
-// let dropZoneHeight = dashboard.height;
+ const startEditing = () => {
+ let originalIDs = widgets.map(widget => widget.id);
+ widgets.forEach(async (widget) => {
+ if (widget.type === 'Slider' || widget.type === 'NumberInput' || widget.type === 'Button') {
+ try {
+ await updateWidget({ widgetID: widget.id, updatedWidget: { widget } }).unwrap();
+ } catch (err) {
+ console.log('error', err);
+ }
+ } else if (widget.type === 'Image') {
+ //widget.customProperties.update = true;
+ }
+ });
+ setEditing(true);
+ setWidgetOrigIDs(originalIDs);
+ };
-// if (isDashboardLoading) {
-// return
Loading...
;
-// }
+ const saveEditing = async () => {
+ // widgets.forEach(async (widget) => {
+ // if (widget.type === 'Image') {
+ // widget.customProperties.update = true;
+ // }
+ // try {
+ // await updateWidget({ widgetID: widget.id, updatedWidget: { widget } }).unwrap();
+ // } catch (err) {
+ // console.log('error', err);
+ // }
+ // });
-// if (dashboardError) {
-// return Error. Dashboard not found
;
-// }
-// return (
-//
-//
-//
-//
-// {dashboard.name}
-//
-//
-//
-//
-//
+ if(height !== dashboard.height || grid !== dashboard.grid) {
+ try {
+ const {height: oldHeight, grid: oldGrid, ...rest} = dashboard;
+ await updateDashboard({dashboardID: dashboard.id, dashboard:{height: height, grid: grid, ...rest}}).unwrap();
+ } catch (err) {
+ console.log('error', err);
+ }
+ }
-//
-//
-//
e.preventDefault()}>
-// {editing &&
-//
-// }
+ if(widgetsToUpdate.length > 0){
+ try {
+ for(const index in widgetsToUpdate){
+ await updateWidget({ widgetID: widgetsToUpdate[index], updatedWidget: { widget: {...widgets.find(w => w.id == widgetsToUpdate[index])} } }).unwrap();
+ }
+ fetchWidgets(dashboard.id);
+ } catch (err) {
+ console.log('error', err);
+ }
+ }
-//
-// {widgets != null && Object.keys(widgets).map(widgetKey => (
-//
-// deleteWidget(widget.id)}
-// onChange={editing ? widgetChange : onChange}
-// >
-//
-//
-//
-// ))}
-//
+ setEditing(false);
+ setWidgetChangeData([]);
+ };
-//
+ const cancelEditing = () => {
+ // widgets.forEach(async (widget) => {
+ // if (widget.type === 'Image') {
+ // widget.customProperties.update = true;
+ // }
+ // if (!widgetOrigIDs.includes(widget.id)) {
+ // try {
+ // await deleteWidget(widget.id).unwrap();
+ // } catch (err) {
+ // console.log('error', err);
+ // }
+ // }
+ // });
+ fetchWidgets(dashboard.id);
+ setEditing(false);
+ setWidgetChangeData([]);
+ setHeight(dashboard.height);
+ setGrid(dashboard.grid);
+ };
-//
-//
-//
-// );
-// };
+ const updateGrid = (value) => {
+ setGrid(value);
+ };
-// export default Fullscreenable()(Dashboard);
+ const updateHeight = (value) => {
+ const maxHeight = Object.values(widgets).reduce((currentHeight, widget) => {
+ const absolutHeight = widget.y + widget.height;
+ return absolutHeight > currentHeight ? absolutHeight : currentHeight;
+ }, 0);
-const Dashboard = (props) => {
- return
-}
+ if (value === -1) {
+ if (dashboard.height >= 450 && dashboard.height >= (maxHeight + 80)) {
+ setHeight(prevState => (prevState - 50));
+ }
+ } else {
+ setHeight( prevState => ( prevState + 50));
+ }
+ };
-export default Dashboard;
+ const pauseData = () => setPaused(true);
+ const unpauseData = () => setPaused(false);
+ const editInputSignals = () => setEditInputSignalsModal(true);
+ const editOutputSignals = () => setEditOutputSignalsModal(true);
+
+ const closeEditSignalsModal = (direction) => {
+ if (direction === "in") {
+ setEditInputSignalsModal(false);
+ } else if (direction === "out") {
+ setEditOutputSignalsModal(false);
+ }
+ };
+
+ const buttonStyle = { marginLeft: '10px' };
+ const iconStyle = { height: '25px', width: '25px' };
+ const boxClasses = classNames('section', 'box', { 'fullscreen-padding': isFullscreen });
+
+ if (isDashboardLoading) {
+ return Loading...
;
+ }
+
+ if (dashboardError) {
+ return Error. Dashboard not found
;
+ }
+
+ return (
+
+
+
+
+ {dashboard.name}
+
+
+
+
+
+
+
+
+
+
e.preventDefault()}>
+ {editing &&
+
+ }
+
+
+ {widgets != null && Object.keys(widgets).map(widgetKey => (
+
+ deleteWidget(widget.id)}
+ onChange={editing ? widgetChange : onChange}
+ >
+
+
+
+ ))}
+
+
+
+
+ {/*
*/}
+
+
+
+
+ );
+};
+
+export default Fullscreenable()(Dashboard);
diff --git a/src/pages/dashboards/dialogs/edit-file-content.js b/src/pages/dashboards/dialogs/edit-file-content.js
new file mode 100644
index 0000000..c14570d
--- /dev/null
+++ b/src/pages/dashboards/dialogs/edit-file-content.js
@@ -0,0 +1,82 @@
+/**
+ * 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 { Form, Button, Col } from 'react-bootstrap';
+import Dialog from '../../../common/dialogs/dialog';
+
+class EditFileContent extends React.Component {
+ valid = true;
+
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ uploadFile: null,
+ };
+ }
+
+ selectUploadFile(event) {
+ this.setState({ uploadFile: event.target.files[0] });
+ };
+
+ startEditContent(){
+ const formData = new FormData();
+ formData.append("file", this.state.uploadFile);
+
+ AppDispatcher.dispatch({
+ type: 'files/start-edit',
+ data: formData,
+ token: this.props.sessionToken,
+ id: this.props.file.id
+ });
+
+ this.setState({ uploadFile: null });
+ };
+
+ onClose = () => {
+ this.props.onClose();
+ };
+
+ render() {
+ return ;
+ }
+}
+
+export default EditFileContent;
\ No newline at end of file
diff --git a/src/pages/dashboards/dialogs/edit-files-dialog.js b/src/pages/dashboards/dialogs/edit-files-dialog.js
new file mode 100644
index 0000000..3eacda4
--- /dev/null
+++ b/src/pages/dashboards/dialogs/edit-files-dialog.js
@@ -0,0 +1,192 @@
+/**
+ * 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 {Form, Button, Col, ProgressBar, Row} from 'react-bootstrap';
+import Dialog from '../../../common/dialogs/dialog';
+import { Table, ButtonColumn, DataColumn } from "../../../common/table";
+import EditFileContent from "./edit-file-content";
+
+class EditFilesDialog extends React.Component {
+ valid = true;
+
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ uploadFile: null,
+ uploadProgress: 0,
+ editModal: false,
+ modalFile: {}
+ };
+ }
+
+ onClose() {
+ this.props.onClose();
+ }
+
+ selectUploadFile(event) {
+ this.setState({ uploadFile: event.target.files[0] });
+ };
+
+ startFileUpload(){
+ // upload file
+ const formData = new FormData();
+ formData.append("file", this.state.uploadFile);
+
+ AppDispatcher.dispatch({
+ type: 'files/start-upload',
+ data: formData,
+ token: this.props.sessionToken,
+ progressCallback: this.updateUploadProgress,
+ finishedCallback: this.clearProgress,
+ scenarioID: this.props.scenarioID,
+ });
+
+ this.setState({ uploadFile: null });
+ };
+
+ updateUploadProgress = (event) => {
+ if (event.hasOwnProperty("percent")){
+ this.setState({ uploadProgress: parseInt(event.percent.toFixed(), 10) });
+ } else {
+ this.setState({ uploadProgress: 0 });
+ }
+ };
+
+ clearProgress = (newFileID) => {
+ this.setState({ uploadProgress: 0 });
+ };
+
+ closeEditModal() {
+ this.setState({editModal: false});
+ }
+
+ deleteFile(index){
+ let file = this.props.files[index]
+ AppDispatcher.dispatch({
+ type: 'files/start-remove',
+ data: file,
+ token: this.props.sessionToken
+ });
+ }
+
+ render() {
+ let fileOptions = [];
+ if (this.props.files.length > 0){
+ fileOptions.push(
+
+ )
+ fileOptions.push(this.props.files.map((file, index) => (
+
+ )))
+ } else {
+ fileOptions =
+ }
+
+ const progressBarStyle = {
+ marginLeft: '100px',
+ marginTop: '-40px'
+ };
+
+ let title = this.props.locked ? "View files of scenario" : "Edit Files of Scenario";
+
+ return (
+
+ );
+ }
+}
+
+export default EditFilesDialog;
\ No newline at end of file
diff --git a/src/pages/dashboards/widget-area.js b/src/pages/dashboards/widget-area.js
index 1e55641..a70fd9c 100644
--- a/src/pages/dashboards/widget-area.js
+++ b/src/pages/dashboards/widget-area.js
@@ -17,11 +17,9 @@
import React from 'react';
import PropTypes from 'prop-types';
-
import Dropzone from './dropzone';
import Grid from './grid';
-
-import WidgetFactory from '../../widget/widget-factory';
+import WidgetFactory from './widget/widget-factory';
class WidgetArea extends React.Component {
snapToGrid(value) {
diff --git a/src/widget/edit-widget/edit-widget-aspect-control.jsx b/src/pages/dashboards/widget/edit-widget/edit-widget-aspect-control.jsx
similarity index 100%
rename from src/widget/edit-widget/edit-widget-aspect-control.jsx
rename to src/pages/dashboards/widget/edit-widget/edit-widget-aspect-control.jsx
diff --git a/src/widget/edit-widget/edit-widget-checkbox-control.jsx b/src/pages/dashboards/widget/edit-widget/edit-widget-checkbox-control.jsx
similarity index 100%
rename from src/widget/edit-widget/edit-widget-checkbox-control.jsx
rename to src/pages/dashboards/widget/edit-widget/edit-widget-checkbox-control.jsx
diff --git a/src/widget/edit-widget/edit-widget-checkbox-list.jsx b/src/pages/dashboards/widget/edit-widget/edit-widget-checkbox-list.jsx
similarity index 100%
rename from src/widget/edit-widget/edit-widget-checkbox-list.jsx
rename to src/pages/dashboards/widget/edit-widget/edit-widget-checkbox-list.jsx
diff --git a/src/widget/edit-widget/edit-widget-color-control.jsx b/src/pages/dashboards/widget/edit-widget/edit-widget-color-control.jsx
similarity index 96%
rename from src/widget/edit-widget/edit-widget-color-control.jsx
rename to src/pages/dashboards/widget/edit-widget/edit-widget-color-control.jsx
index 7d3eecb..7cdde20 100644
--- a/src/widget/edit-widget/edit-widget-color-control.jsx
+++ b/src/pages/dashboards/widget/edit-widget/edit-widget-color-control.jsx
@@ -1,7 +1,7 @@
import React, { useState, useEffect } from 'react';
import { Form, Container, Row, Col, OverlayTrigger, Tooltip, Button} from 'react-bootstrap';
-import ColorPicker from '../../common/color-picker';
-import Icon from "../../common/icon";
+import ColorPicker from '../../../../common/color-picker';
+import Icon from "../../../../common/icon";
const EditWidgetColorControl = (props) => {
const [color, setColor] = useState(null);
diff --git a/src/widget/edit-widget/edit-widget-color-zones-control.js b/src/pages/dashboards/widget/edit-widget/edit-widget-color-zones-control.js
similarity index 98%
rename from src/widget/edit-widget/edit-widget-color-zones-control.js
rename to src/pages/dashboards/widget/edit-widget/edit-widget-color-zones-control.js
index 9f2ff77..da113ce 100644
--- a/src/widget/edit-widget/edit-widget-color-zones-control.js
+++ b/src/pages/dashboards/widget/edit-widget/edit-widget-color-zones-control.js
@@ -17,8 +17,8 @@
import React from 'react';
import { Form, Table, Button, Tooltip, OverlayTrigger } from 'react-bootstrap';
-import ColorPicker from '../../common/color-picker'
-import Icon from '../../common/icon';
+import ColorPicker from '../../../../common/color-picker'
+import Icon from '../../../../common/icon';
import { Collapse } from 'react-collapse';
class EditWidgetColorZonesControl extends React.Component {
diff --git a/src/widget/edit-widget/edit-widget-config-select.js b/src/pages/dashboards/widget/edit-widget/edit-widget-config-select.js
similarity index 100%
rename from src/widget/edit-widget/edit-widget-config-select.js
rename to src/pages/dashboards/widget/edit-widget/edit-widget-config-select.js
diff --git a/src/widget/edit-widget/edit-widget-control-creator.js b/src/pages/dashboards/widget/edit-widget/edit-widget-control-creator.js
similarity index 100%
rename from src/widget/edit-widget/edit-widget-control-creator.js
rename to src/pages/dashboards/widget/edit-widget/edit-widget-control-creator.js
diff --git a/src/widget/edit-widget/edit-widget-file-control.jsx b/src/pages/dashboards/widget/edit-widget/edit-widget-file-control.jsx
similarity index 100%
rename from src/widget/edit-widget/edit-widget-file-control.jsx
rename to src/pages/dashboards/widget/edit-widget/edit-widget-file-control.jsx
diff --git a/src/widget/edit-widget/edit-widget-html-content.js b/src/pages/dashboards/widget/edit-widget/edit-widget-html-content.js
similarity index 100%
rename from src/widget/edit-widget/edit-widget-html-content.js
rename to src/pages/dashboards/widget/edit-widget/edit-widget-html-content.js
diff --git a/src/widget/edit-widget/edit-widget-ic-select.js b/src/pages/dashboards/widget/edit-widget/edit-widget-ic-select.js
similarity index 100%
rename from src/widget/edit-widget/edit-widget-ic-select.js
rename to src/pages/dashboards/widget/edit-widget/edit-widget-ic-select.js
diff --git a/src/widget/edit-widget/edit-widget-min-max-control.js b/src/pages/dashboards/widget/edit-widget/edit-widget-min-max-control.js
similarity index 100%
rename from src/widget/edit-widget/edit-widget-min-max-control.js
rename to src/pages/dashboards/widget/edit-widget/edit-widget-min-max-control.js
diff --git a/src/widget/edit-widget/edit-widget-number-control.js b/src/pages/dashboards/widget/edit-widget/edit-widget-number-control.js
similarity index 100%
rename from src/widget/edit-widget/edit-widget-number-control.js
rename to src/pages/dashboards/widget/edit-widget/edit-widget-number-control.js
diff --git a/src/widget/edit-widget/edit-widget-orientation.js b/src/pages/dashboards/widget/edit-widget/edit-widget-orientation.js
similarity index 99%
rename from src/widget/edit-widget/edit-widget-orientation.js
rename to src/pages/dashboards/widget/edit-widget/edit-widget-orientation.js
index 6df5e1f..76e9d8b 100644
--- a/src/widget/edit-widget/edit-widget-orientation.js
+++ b/src/pages/dashboards/widget/edit-widget/edit-widget-orientation.js
@@ -17,7 +17,6 @@
import React, { Component } from 'react';
import { Col, Row, Form } from 'react-bootstrap';
-
import WidgetSlider from '../widgets/slider';
class EditWidgetOrientation extends Component {
diff --git a/src/widget/edit-widget/edit-widget-parameters-control.js b/src/pages/dashboards/widget/edit-widget/edit-widget-parameters-control.js
similarity index 95%
rename from src/widget/edit-widget/edit-widget-parameters-control.js
rename to src/pages/dashboards/widget/edit-widget/edit-widget-parameters-control.js
index d743017..8f60aa6 100644
--- a/src/widget/edit-widget/edit-widget-parameters-control.js
+++ b/src/pages/dashboards/widget/edit-widget/edit-widget-parameters-control.js
@@ -17,7 +17,7 @@
import React, { Component } from 'react';
import { Form } from 'react-bootstrap';
-import ParametersEditor from '../../common/parameters-editor';
+import ParametersEditor from '../../../../common/parameters-editor';
class EditWidgetParametersControl extends Component {
constructor(props) {
diff --git a/src/widget/edit-widget/edit-widget-plot-colors-control.js b/src/pages/dashboards/widget/edit-widget/edit-widget-plot-colors-control.js
similarity index 98%
rename from src/widget/edit-widget/edit-widget-plot-colors-control.js
rename to src/pages/dashboards/widget/edit-widget/edit-widget-plot-colors-control.js
index c058f12..5dd9a5e 100644
--- a/src/widget/edit-widget/edit-widget-plot-colors-control.js
+++ b/src/pages/dashboards/widget/edit-widget/edit-widget-plot-colors-control.js
@@ -17,8 +17,8 @@
import React, { Component } from 'react';
import { OverlayTrigger, Tooltip , Button, Form } from 'react-bootstrap';
-import ColorPicker from '../../common/color-picker'
-import Icon from "../../common/icon";
+import ColorPicker from '../../../../common/color-picker'
+import Icon from "../../../../common/icon";
import {schemeCategory10} from "d3-scale-chromatic";
class EditWidgetPlotColorsControl extends Component {
diff --git a/src/widget/edit-widget/edit-widget-plot-mode-control.js b/src/pages/dashboards/widget/edit-widget/edit-widget-plot-mode-control.js
similarity index 100%
rename from src/widget/edit-widget/edit-widget-plot-mode-control.js
rename to src/pages/dashboards/widget/edit-widget/edit-widget-plot-mode-control.js
diff --git a/src/widget/edit-widget/edit-widget-rotation-control.js b/src/pages/dashboards/widget/edit-widget/edit-widget-rotation-control.js
similarity index 100%
rename from src/widget/edit-widget/edit-widget-rotation-control.js
rename to src/pages/dashboards/widget/edit-widget/edit-widget-rotation-control.js
diff --git a/src/widget/edit-widget/edit-widget-samples-control.js b/src/pages/dashboards/widget/edit-widget/edit-widget-samples-control.js
similarity index 100%
rename from src/widget/edit-widget/edit-widget-samples-control.js
rename to src/pages/dashboards/widget/edit-widget/edit-widget-samples-control.js
diff --git a/src/widget/edit-widget/edit-widget-signal-control.js b/src/pages/dashboards/widget/edit-widget/edit-widget-signal-control.js
similarity index 100%
rename from src/widget/edit-widget/edit-widget-signal-control.js
rename to src/pages/dashboards/widget/edit-widget/edit-widget-signal-control.js
diff --git a/src/widget/edit-widget/edit-widget-signals-control.js b/src/pages/dashboards/widget/edit-widget/edit-widget-signals-control.js
similarity index 100%
rename from src/widget/edit-widget/edit-widget-signals-control.js
rename to src/pages/dashboards/widget/edit-widget/edit-widget-signals-control.js
diff --git a/src/widget/edit-widget/edit-widget-text-control.js b/src/pages/dashboards/widget/edit-widget/edit-widget-text-control.js
similarity index 100%
rename from src/widget/edit-widget/edit-widget-text-control.js
rename to src/pages/dashboards/widget/edit-widget/edit-widget-text-control.js
diff --git a/src/widget/edit-widget/edit-widget-text-size-control.js b/src/pages/dashboards/widget/edit-widget/edit-widget-text-size-control.js
similarity index 100%
rename from src/widget/edit-widget/edit-widget-text-size-control.js
rename to src/pages/dashboards/widget/edit-widget/edit-widget-text-size-control.js
diff --git a/src/widget/edit-widget/edit-widget.js b/src/pages/dashboards/widget/edit-widget/edit-widget.js
similarity index 96%
rename from src/widget/edit-widget/edit-widget.js
rename to src/pages/dashboards/widget/edit-widget/edit-widget.js
index 6b2b655..5d5a0ea 100644
--- a/src/widget/edit-widget/edit-widget.js
+++ b/src/pages/dashboards/widget/edit-widget/edit-widget.js
@@ -17,7 +17,7 @@
import React from 'react';
import { Form } from 'react-bootstrap';
-import Dialog from '../../common/dialogs/dialog';
+import Dialog from '../../../../common/dialogs/dialog';
import CreateControls from './edit-widget-control-creator';
class EditWidgetDialog extends React.Component {
@@ -92,6 +92,8 @@ class EditWidgetDialog extends React.Component {
handleChange(e) {
// TODO: check what we really need in this function. Can we reduce its complexity?
let parts = e.target.id.split('.');
+ // creating a deep copy of an object to be updated
+ //let changeObject = JSON.parse(JSON.stringify(this.state.temporal));;
let changeObject = this.state.temporal;
let customProperty = true;
if (parts.length === 1) {
@@ -146,9 +148,10 @@ class EditWidgetDialog extends React.Component {
} else {
customProperty ? changeObject[parts[0]][parts[1]] = e.target.value : changeObject[e.target.id] = e.target.value ;
}
+
+ console.log(changeObject)
this.setState({ temporal: changeObject});
-
}
resetState() {
diff --git a/src/widget/toolbox-item.jsx b/src/pages/dashboards/widget/toolbox-item.jsx
similarity index 97%
rename from src/widget/toolbox-item.jsx
rename to src/pages/dashboards/widget/toolbox-item.jsx
index ac5e834..5df4682 100644
--- a/src/widget/toolbox-item.jsx
+++ b/src/pages/dashboards/widget/toolbox-item.jsx
@@ -18,7 +18,7 @@
import React from "react";
import { DragSource } from "react-dnd";
import classNames from "classnames";
-import Icon from "../common/icon";
+import Icon from "../../../common/icon";
// Drag source specification
const toolboxItemSource = {
diff --git a/src/widget/websocket-store.js b/src/pages/dashboards/widget/websocket-store.js
similarity index 100%
rename from src/widget/websocket-store.js
rename to src/pages/dashboards/widget/websocket-store.js
diff --git a/src/widget/widget-container.js b/src/pages/dashboards/widget/widget-container.js
similarity index 99%
rename from src/widget/widget-container.js
rename to src/pages/dashboards/widget/widget-container.js
index 2b4bcfc..ff336e1 100644
--- a/src/widget/widget-container.js
+++ b/src/pages/dashboards/widget/widget-container.js
@@ -47,7 +47,6 @@ class WidgetContainer extends React.Component {
widget.x = this.snapToGrid(data.x);
widget.y = this.snapToGrid(data.y);
-
if (widget.x !== data.x || widget.y !== data.y) {
this.rnd.updatePosition({ x: widget.x, y: widget.y });
}
diff --git a/src/widget/widget-context-menu.js b/src/pages/dashboards/widget/widget-context-menu.js
similarity index 99%
rename from src/widget/widget-context-menu.js
rename to src/pages/dashboards/widget/widget-context-menu.js
index 3fed074..51d96e8 100644
--- a/src/widget/widget-context-menu.js
+++ b/src/pages/dashboards/widget/widget-context-menu.js
@@ -145,4 +145,4 @@ WidgetContextMenu.propTypes = {
onChange: PropTypes.func.isRequired
};
-export default WidgetContextMenu
+export default WidgetContextMenu;
diff --git a/src/widget/widget-factory.js b/src/pages/dashboards/widget/widget-factory.js
similarity index 90%
rename from src/widget/widget-factory.js
rename to src/pages/dashboards/widget/widget-factory.js
index 67e6116..213226c 100644
--- a/src/widget/widget-factory.js
+++ b/src/pages/dashboards/widget/widget-factory.js
@@ -15,7 +15,7 @@
* along with VILLASweb. If not, see .
******************************************************************************/
-import WidgetSlider from './widgets/slider';
+//import WidgetSlider from './widgets/slider';
class WidgetFactory {
@@ -148,23 +148,23 @@ class WidgetFactory {
widget.customProperties.value = '';
widget.customProperties.simStartedSendValue = false;
break;
- case 'Slider':
- widget.minWidth = 380;
- widget.minHeight = 30;
- widget.width = 400;
- widget.height = 50;
- widget.customProperties.orientation = WidgetSlider.OrientationTypes.HORIZONTAL.value; // Assign default orientation
- widget.customProperties.rangeMin = 0;
- widget.customProperties.rangeMax = 200;
- widget.customProperties.rangeUseMinMax = true;
- widget.customProperties.showUnit = false;
- widget.customProperties.continous_update = false;
- widget.customProperties.value = '';
- widget.customProperties.resizeLeftRightLock = false;
- widget.customProperties.resizeTopBottomLock = true;
- widget.customProperties.simStartedSendValue = false;
+ // case 'Slider':
+ // widget.minWidth = 380;
+ // widget.minHeight = 30;
+ // widget.width = 400;
+ // widget.height = 50;
+ // widget.customProperties.orientation = WidgetSlider.OrientationTypes.HORIZONTAL.value; // Assign default orientation
+ // widget.customProperties.rangeMin = 0;
+ // widget.customProperties.rangeMax = 200;
+ // widget.customProperties.rangeUseMinMax = true;
+ // widget.customProperties.showUnit = false;
+ // widget.customProperties.continous_update = false;
+ // widget.customProperties.value = '';
+ // widget.customProperties.resizeLeftRightLock = false;
+ // widget.customProperties.resizeTopBottomLock = true;
+ // widget.customProperties.simStartedSendValue = false;
- break;
+ // break;
case 'Gauge':
widget.minWidth = 100;
widget.minHeight = 150;
diff --git a/src/widget/widget-player/player-machine.js b/src/pages/dashboards/widget/widget-player/player-machine.js
similarity index 100%
rename from src/widget/widget-player/player-machine.js
rename to src/pages/dashboards/widget/widget-player/player-machine.js
diff --git a/src/widget/widget-plot/plot-legend.jsx b/src/pages/dashboards/widget/widget-plot/plot-legend.jsx
similarity index 100%
rename from src/widget/widget-plot/plot-legend.jsx
rename to src/pages/dashboards/widget/widget-plot/plot-legend.jsx
diff --git a/src/pages/dashboards/widget/widget-plot/plot.jsx b/src/pages/dashboards/widget/widget-plot/plot.jsx
new file mode 100644
index 0000000..268382b
--- /dev/null
+++ b/src/pages/dashboards/widget/widget-plot/plot.jsx
@@ -0,0 +1,283 @@
+/**
+ * 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 { scaleLinear, scaleTime, scaleOrdinal} from 'd3-scale';
+import { schemeCategory10 } from 'd3-scale-chromatic'
+import { extent } from 'd3-array';
+import { line } from 'd3-shape';
+import { axisBottom, axisLeft } from 'd3-axis';
+import { select } from 'd3-selection';
+import { timeFormat } from 'd3-time-format';
+import { format } from 'd3';
+
+const topMargin = 10;
+const bottomMargin = 25;
+const leftMargin = 40;
+const rightMargin = 10;
+
+let uniqueIdentifier = 0;
+
+class Plot extends React.Component {
+ constructor(props) {
+ super(props);
+ // create dummy axes
+ let labelMargin = 0;
+ if (props.yLabel !== '') {
+ labelMargin = 30;
+ }
+
+ const xScale = scaleTime().domain([Date.now() - props.time * 1000, Date.now()]).range([0, props.width - leftMargin - labelMargin - rightMargin]);
+ let yScale;
+
+ if (props.yUseMinMax) {
+ yScale = scaleLinear().domain([props.yMin, props.yMax]).range([props.height + topMargin - bottomMargin, topMargin]);
+ } else {
+ yScale = scaleLinear().domain([0, 10]).range([props.height + topMargin - bottomMargin, topMargin]);
+ }
+
+ const xAxis = axisBottom().scale(xScale).ticks(5).tickFormat(timeFormat("%M:%S"));
+ const yAxis = axisLeft().scale(yScale).ticks(5).tickFormat(format(".3s"));
+
+ this.state = {
+ data: null,
+ lines: null,
+ xAxis,
+ yAxis,
+ labelMargin,
+ identifier: uniqueIdentifier++,
+ stopTime: null,
+ firstTimestamp: null
+ };
+ }
+
+ componentDidMount() {
+ this.createInterval();
+ }
+
+ componentWillUnmount() {
+ this.removeInterval();
+ }
+
+ static getDerivedStateFromProps(props, state){
+
+ let labelMargin = 0;
+ if (props.yLabel !== '') {
+ labelMargin = 30;
+ }
+
+ // check if data is invalid
+ if (props.data == null || props.data.length === 0) {
+ // create empty plot axes
+ let xScale;
+ let yScale;
+ let stopTime;
+
+ if(!props.paused){
+ xScale = scaleTime().domain([Date.now() - props.time * 1000, Date.now()]).range([0, props.width - leftMargin - labelMargin - rightMargin]);
+ stopTime = Date.now();
+ }else{
+ stopTime = state.stopTime;
+ xScale = scaleLinear().domain([state.stopTime - props.time * 1000, state.stopTime]).range([0, props.width - leftMargin - labelMargin - rightMargin]);
+ }
+
+ if (props.yUseMinMax) {
+ yScale = scaleLinear().domain([props.yMin, props.yMax]).range([props.height + topMargin - bottomMargin, topMargin]);
+ } else {
+ yScale = scaleLinear().domain([0, 10]).range([props.height + topMargin - bottomMargin, topMargin]);
+ }
+
+ const xAxis = axisBottom().scale(xScale).ticks(5).tickFormat(timeFormat("%M:%S"));
+ const yAxis = axisLeft().scale(yScale).ticks(5).tickFormat(format(".3s"));
+
+
+ return{
+ stopTime: stopTime,
+ data: null,
+ xAxis,
+ yAxis,
+ labelMargin,
+ };
+ }
+
+ // only show data in requested time
+ let data = props.data;
+ let icDataset = data.find(function (element) {
+ return element !== undefined;
+ })
+
+ let firstTimestamp;
+ if (props.mode === "last samples") {
+ firstTimestamp = (data[0].length - 1 - props.samples) > 0 ? data[0][(data[0].length - 1) - props.samples].x : data[0][0].x;
+ let tempTimestamp;
+
+ for (let i = 1; i < props.signalIDs.length; i++) {
+ if (typeof props.data[i] !== "undefined") {
+ tempTimestamp = (data[i].length - 1 - props.samples) > 0 ? data[i][(data[i].length - 1) - props.samples].x : data[i][0].x;
+ firstTimestamp = tempTimestamp < firstTimestamp ? tempTimestamp : firstTimestamp;
+ }
+ }
+
+ }
+ else {
+ firstTimestamp = icDataset[icDataset.length - 1].x - (props.time + 1) * 1000;
+ if (icDataset[0].x < firstTimestamp) {
+ // only show data in range (+100 ms)
+ const index = icDataset.findIndex(value => value.x >= firstTimestamp - 100);
+ data = data.map(values => values.slice(index));
+ }
+ }
+
+
+ return {
+ data,
+ labelMargin,
+ firstTimestamp
+ };
+
+ }
+
+ componentDidUpdate(prevProps: Readonly, prevState: Readonly, snapshot: SS): void {
+ if (prevProps.time !== this.props.time) {
+ this.createInterval();
+ }
+ }
+
+ createInterval() {
+ this.removeInterval();
+
+ if (this.props.time < 30) {
+ this.interval = setInterval(this.tick, 50);
+ } else if (this.props.time < 120) {
+ this.interval = setInterval(this.tick, 100);
+ } else if (this.props.time < 300) {
+ this.interval = setInterval(this.tick, 200);
+ } else {
+ this.interval = setInterval(this.tick, 1000);
+ }
+ }
+
+ removeInterval() {
+ if (this.interval != null) {
+ clearInterval(this.interval);
+
+ this.interval = null;
+ }
+ }
+
+ tick = () => {
+
+ if (this.state.data == null) {
+ this.setState({ lines: null });
+ return;
+ }
+
+ if (this.props.paused === true) {
+ return;
+ }
+
+ // calculate yRange
+ let yRange = [0, 0];
+
+ if (this.props.yUseMinMax) {
+ yRange = [this.props.yMin, this.props.yMax];
+ } else if (this.props.data.length > 0) {
+ let icDataset = this.props.data.find(function(element) {
+ return element !== undefined;
+ })
+
+ yRange = [icDataset[0].y, icDataset[0].y];
+
+ this.props.data.forEach(values => {
+ const range = extent(values, p => p.y);
+
+ if (range[0] < yRange[0]) yRange[0] = range[0];
+ if (range[1] > yRange[1]) yRange[1] = range[1];
+ });
+ }
+
+ // create scale functions for both axes
+ let xScale;
+ let data = this.props.data;
+ if(this.props.mode === "last samples"){
+ let lastTimestamp = data[0][data[0].length - 1].x;
+
+ for (let i = 1; i < this.props.signalIDs.length; i++) {
+ if (typeof data[i] !== "undefined") {
+ lastTimestamp = data[i][data[i].length - 1].x > lastTimestamp ? data[i][data[i].length -1].x : lastTimestamp;
+ }
+ }
+
+
+ xScale = scaleTime().domain([this.state.firstTimestamp, lastTimestamp]).range([0, this.props.width - leftMargin - this.state.labelMargin - rightMargin]);
+ }
+ else{
+ xScale = scaleTime().domain([Date.now() - this.props.time * 1000, Date.now()]).range([0, this.props.width - leftMargin - this.state.labelMargin - rightMargin]);
+
+ }
+ const yScale = scaleLinear().domain(yRange).range([this.props.height + topMargin - bottomMargin, topMargin]);
+
+ const xAxis = axisBottom().scale(xScale).ticks(5).tickFormat(timeFormat("%M:%S"));
+ const yAxis = axisLeft().scale(yScale).ticks(5).tickFormat(format(".3s"));
+
+ // generate paths from data
+ const sparkLine = line().x(p => xScale(p.x)).y(p => yScale(p.y));
+
+ const lines = this.state.data.map((values, index) => {
+ let signalID = this.props.signalIDs[index];
+
+ if(this.props.lineColors === undefined || this.props.lineColors === null){
+ this.props.lineColors = [] // for backwards compatibility
+ }
+
+ if (typeof this.props.lineColors[index] === "undefined") {
+ this.props.lineColors[index] = schemeCategory10[index % 10];
+ }
+ return
+ });
+
+ this.setState({ lines, xAxis, yAxis });
+ }
+
+ render() {
+
+ const yLabelPos = {
+ x: 12,
+ y: this.props.height / 2
+ }
+
+ return ;
+ }
+}
+
+export default Plot;
diff --git a/src/widget/widget-store.js b/src/pages/dashboards/widget/widget-store.js
similarity index 100%
rename from src/widget/widget-store.js
rename to src/pages/dashboards/widget/widget-store.js
diff --git a/src/widget/widget-toolbox.js b/src/pages/dashboards/widget/widget-toolbox.js
similarity index 99%
rename from src/widget/widget-toolbox.js
rename to src/pages/dashboards/widget/widget-toolbox.js
index 3f8f7a6..0812987 100644
--- a/src/widget/widget-toolbox.js
+++ b/src/pages/dashboards/widget/widget-toolbox.js
@@ -20,7 +20,7 @@ import { Collapse } from 'react-collapse';
import PropTypes from 'prop-types';
import Slider from 'rc-slider';
import { Button, OverlayTrigger, Tooltip } from 'react-bootstrap';
-import Icon from "../common/icon";
+import Icon from '../../../common/icon';
import ToolboxItem from './toolbox-item';
let hasPintura = true;
diff --git a/src/pages/dashboards/widget/widget.js b/src/pages/dashboards/widget/widget.js
new file mode 100644
index 0000000..8dfbe8c
--- /dev/null
+++ b/src/pages/dashboards/widget/widget.js
@@ -0,0 +1,452 @@
+/**
+ * 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 { useState, useEffect } from 'react';
+import { useSelector } from 'react-redux';
+import WidgetLabel from './widgets/label';
+import WidgetLine from './widgets/line';
+import WidgetBox from './widgets/box';
+import WidgetImage from './widgets/image';
+import WidgetPlot from './widgets/plot';
+import WidgetTable from './widgets/table';
+import WidgetValue from './widgets/value';
+import WidgetLamp from './widgets/lamp';
+import WidgetGauge from './widgets/gauge';
+import WidgetTimeOffset from './widgets/time-offset';
+import WidgetICstatus from './widgets/icstatus';
+// import WidgetCustomAction from './widgets/custom-action';
+// import WidgetAction from './widgets/action';
+// import WidgetButton from './widgets/button';
+// import WidgetInput from './widgets/input';
+// import WidgetSlider from './widgets/slider';
+// import WidgetTopology from './widgets/topology';
+// import WidgetPlayer from './widgets/player';
+//import WidgetHTML from './widgets/html';
+import '../../../styles/widgets.css';
+import { useGetICSQuery, useGetSignalsQuery, useGetConfigsQuery } from '../../../store/apiSlice';
+
+const Widget = ({widget, editing, files, configs, signals, paused, ics, icData}) => {
+
+ const { token: sessionToken } = useSelector((state) => state.auth);
+
+ const [icIDs, setICIDs] = useState([]);
+
+ const icdata = useSelector((state) => state.websocket.icdata);
+
+ useEffect(() => {
+ if(signals.length > 0){
+ let ids = [];
+
+ for (let id of widget.signalIDs){
+ let signal = signals.find(s => s.id === id);
+ if (signal !== undefined) {
+ let config = configs.find(m => m.id === signal.configID);
+ if (config !== undefined){
+ ids[signal.id] = config.icID;
+ }
+ }
+ }
+
+ setICIDs(ids);
+ }
+ }, [signals])
+
+ // const {data: signals, isLoading: signalsLoading} = useGetSignalsQuery({})
+
+
+ switch(widget.type){
+ //Cosmetic widgets
+ case 'Line':
+ return
+ case 'Box':
+ return
+ case 'Label':
+ return ;
+ case 'Image':
+ return
+ //Displaying widgets
+ case 'Plot':
+ return
+ case 'Table':
+ return
+ case 'Value':
+ return
+ case 'Lamp':
+ return
+ case 'Gauge':
+ return
+ case 'TimeOffset':
+ return
+ case 'ICstatus':
+ return
+ //Manipulation widgets
+ default:
+ return Error: Widget not found!
+ }
+
+
+ // if (widget.type === 'CustomAction') {
+ // return
+ // } else if (widget.type === 'Action') {
+ // return
+ // } else if (widget.type === 'Lamp') {
+ // return
+ // } else if (widget.type === 'Value') {
+ // return
+ // } else if (widget.type === 'Plot') {
+ // return
+ // } else if (widget.type === 'Table') {
+ // return
+ // } else if (widget.type === 'Label') {
+ // return
+ // } else if (widget.type === 'Image') {
+ // return
+ // } else if (widget.type === 'Button') {
+ // return this.inputDataChanged(widget, value, controlID, controlValue, isFinalChange)}
+ // signals={this.state.signals}
+ // token={this.state.sessionToken}
+ // />
+ // } else if (widget.type === 'NumberInput') {
+ // return this.inputDataChanged(widget, value, controlID, controlValue, isFinalChange)}
+ // signals={this.state.signals}
+ // token={this.state.sessionToken}
+ // />
+ // } else if (widget.type === 'Slider') {
+ // return this.inputDataChanged(widget, value, controlID, controlValue, isFinalChange)}
+ // signals={this.state.signals}
+ // token={this.state.sessionToken}
+ // />
+ // } else if (widget.type === 'Gauge') {
+ // return
+ // //} else if (widget.type === 'HTML') {
+ // //return
+ // } else if (widget.type === 'Topology') {
+ // return
+ // } else if (widget.type === 'TimeOffset') {
+ // return
+ // } else if (widget.type === 'ICstatus') {
+ // return
+ // } else if (widget.type === 'Player') {
+ // return
+ // }
+
+ return null;
+}
+
+// class Widget extends React.Component {
+// static getStores() {
+// return [ ICDataStore, ConfigsStore, FileStore, SignalStore, WebsocketStore, ResultStore];
+// }
+
+// static calculateState(prevState, props) {
+
+// let websockets = WebsocketStore.getState();
+
+// let icData = {};
+
+// if (props.paused) {
+// if (prevState && prevState.icData) {
+// icData = JSON.parse(JSON.stringify(prevState.icData));
+// }
+// } else {
+// icData = ICDataStore.getState();
+// }
+
+// // Get the IC IDs and signal indexes for all signals of the widget
+// let configs = ConfigsStore.getState().filter(c => c.scenarioID === parseInt(props.scenarioID, 10));
+// // TODO make sure that the signals are only the signals that belong to the scenario at hand
+// let signals = SignalStore.getState();
+// let icIDs = [];
+
+// for (let id of props.data.signalIDs){
+// let signal = signals.find(s => s.id === id);
+// if (signal !== undefined) {
+// let config = configs.find(m => m.id === signal.configID);
+// if (config !== undefined){
+// icIDs[signal.id] = config.icID;
+// }
+// }
+// }
+
+// let results = ResultStore.getState().filter(r => r.scenarioID === parseInt(props.scenarioID, 10));
+// let files = FileStore.getState().filter(f => f.scenarioID === parseInt(props.scenarioID, 10));
+
+// return {
+// websockets: websockets,
+// icData: icData,
+// signals: signals,
+// icIDs: icIDs,
+// files: files,
+// sessionToken: localStorage.getItem("token"),
+// results: results,
+// };
+// }
+
+// inputDataChanged(widget, data, controlID, controlValue, isFinalChange) {
+// // controlID is the path to the widget customProperty that is changed (for example 'value')
+
+// // modify the widget customProperty
+// if (controlID !== '' && isFinalChange) {
+// let updatedWidget = JSON.parse(JSON.stringify(widget));
+// updatedWidget.customProperties[controlID] = controlValue;
+
+// AppDispatcher.dispatch({
+// type: 'widgets/start-edit',
+// token: this.state.sessionToken,
+// data: updatedWidget
+// });
+// }
+
+// // The following assumes that a widget modifies/ uses exactly one signal
+
+// // get the signal with the selected signal ID
+// let signalID = widget.signalIDs[0];
+// let signal = this.state.signals.filter(s => s.id === signalID)
+// if (signal.length === 0){
+// console.warn("Unable to send signal for signal ID", signalID, ". Signal not found.");
+// return;
+// }
+// // determine ID of infrastructure component related to signal[0]
+// // Remark: there is only one selected signal for an input type widget
+// let icID = this.state.icIDs[signal[0].id];
+// AppDispatcher.dispatch({
+// type: 'icData/inputChanged',
+// ic: icID,
+// signalID: signal[0].id,
+// signalIndex: signal[0].index,
+// data: signal[0].scalingFactor * data
+// });
+// }
+
+// createWidget(widget) {
+
+// if (widget.type === 'CustomAction') {
+// return
+// } else if (widget.type === 'Action') {
+// return
+// } else if (widget.type === 'Lamp') {
+// return
+// } else if (widget.type === 'Value') {
+// return
+// } else if (widget.type === 'Plot') {
+// return
+// } else if (widget.type === 'Table') {
+// return
+// } else if (widget.type === 'Label') {
+// return
+// } else if (widget.type === 'Image') {
+// return
+// } else if (widget.type === 'Button') {
+// return this.inputDataChanged(widget, value, controlID, controlValue, isFinalChange)}
+// signals={this.state.signals}
+// token={this.state.sessionToken}
+// />
+// } else if (widget.type === 'NumberInput') {
+// return this.inputDataChanged(widget, value, controlID, controlValue, isFinalChange)}
+// signals={this.state.signals}
+// token={this.state.sessionToken}
+// />
+// } else if (widget.type === 'Slider') {
+// return this.inputDataChanged(widget, value, controlID, controlValue, isFinalChange)}
+// signals={this.state.signals}
+// token={this.state.sessionToken}
+// />
+// } else if (widget.type === 'Gauge') {
+// return
+// } else if (widget.type === 'Box') {
+// return
+// //} else if (widget.type === 'HTML') {
+// //return
+// } else if (widget.type === 'Topology') {
+// return
+// } else if (widget.type === 'Line') {
+// return
+// } else if (widget.type === 'TimeOffset') {
+// return
+// } else if (widget.type === 'ICstatus') {
+// return
+// } else if (widget.type === 'Player') {
+// return
+// }
+
+// return null;
+// }
+
+// render() {
+// return this.createWidget(this.props.data);
+// }
+// }
+
+// let fluxContainerConverter = require('../common/FluxContainerConverter');
+// export default Container.create(fluxContainerConverter.convert(Widget), { withProps: true });
+
+export default Widget;
diff --git a/src/widget/widgets/action.jsx b/src/pages/dashboards/widget/widgets/action.jsx
similarity index 100%
rename from src/widget/widgets/action.jsx
rename to src/pages/dashboards/widget/widgets/action.jsx
diff --git a/src/widget/widgets/box.jsx b/src/pages/dashboards/widget/widgets/box.jsx
similarity index 100%
rename from src/widget/widgets/box.jsx
rename to src/pages/dashboards/widget/widgets/box.jsx
diff --git a/src/widget/widgets/button.jsx b/src/pages/dashboards/widget/widgets/button.jsx
similarity index 100%
rename from src/widget/widgets/button.jsx
rename to src/pages/dashboards/widget/widgets/button.jsx
diff --git a/src/widget/widgets/custom-action.jsx b/src/pages/dashboards/widget/widgets/custom-action.jsx
similarity index 100%
rename from src/widget/widgets/custom-action.jsx
rename to src/pages/dashboards/widget/widgets/custom-action.jsx
diff --git a/src/widget/widgets/gauge.jsx b/src/pages/dashboards/widget/widgets/gauge.jsx
similarity index 100%
rename from src/widget/widgets/gauge.jsx
rename to src/pages/dashboards/widget/widgets/gauge.jsx
diff --git a/src/widget/widgets/html.jsx b/src/pages/dashboards/widget/widgets/html.jsx
similarity index 100%
rename from src/widget/widgets/html.jsx
rename to src/pages/dashboards/widget/widgets/html.jsx
diff --git a/src/widget/widgets/icstatus.jsx b/src/pages/dashboards/widget/widgets/icstatus.jsx
similarity index 88%
rename from src/widget/widgets/icstatus.jsx
rename to src/pages/dashboards/widget/widgets/icstatus.jsx
index 19b7dba..943c418 100644
--- a/src/widget/widgets/icstatus.jsx
+++ b/src/pages/dashboards/widget/widgets/icstatus.jsx
@@ -17,8 +17,7 @@
import React, { useState, useEffect } from "react";
import { Badge } from "react-bootstrap";
-import { stateLabelStyle } from "../../ic/ics";
-import AppDispatcher from "../../common/app-dispatcher";
+import {stateLabelStyle} from "../../../infrastructure/styles";
const WidgetICstatus = (props) => {
const [sessionToken, setSessionToken] = useState(
@@ -31,11 +30,6 @@ const WidgetICstatus = (props) => {
if (props.ics) {
props.ics.forEach((ic) => {
let icID = parseInt(ic.id, 10);
- AppDispatcher.dispatch({
- type: "ics/start-load",
- data: icID,
- token: sessionToken,
- });
});
}
};
diff --git a/src/widget/widgets/image.jsx b/src/pages/dashboards/widget/widgets/image.jsx
similarity index 60%
rename from src/widget/widgets/image.jsx
rename to src/pages/dashboards/widget/widgets/image.jsx
index 8b658d8..b5964f6 100644
--- a/src/widget/widgets/image.jsx
+++ b/src/pages/dashboards/widget/widgets/image.jsx
@@ -16,41 +16,43 @@
******************************************************************************/
import React, { useState, useEffect } from "react";
-import AppDispatcher from "../../common/app-dispatcher";
const WidgetImage = (props) => {
- const [file, setFile] = useState(undefined);
+ const [file, setFile] = useState(null);
+
+ const widget = JSON.parse(JSON.stringify(props.widget));
useEffect(() => {
- let widgetFile = props.widget.customProperties.file;
- if (widgetFile !== -1 && file === undefined) {
- AppDispatcher.dispatch({
- type: "files/start-download",
- data: widgetFile,
- token: props.token,
- });
+ let widgetFile = widget.customProperties.file;
+ if (widgetFile !== -1 && file === null) {
+ // AppDispatcher.dispatch({
+ // type: "files/start-download",
+ // data: widgetFile,
+ // token: props.token,
+ // });
}
- }, [file, props.token, props.widget.customProperties.file]);
+ }, [file, props.token, widget.customProperties.file]);
useEffect(() => {
- if (props.widget.customProperties.file === -1) {
- props.widget.customProperties.update = false;
- if (file !== undefined) setFile(undefined);
+ if (widget.customProperties.file === -1) {
+ widget.customProperties.update = false;
+ if (file !== null) setFile(null);
} else {
+ console.log("looking in", props.files)
let foundFile = props.files.find(
- (f) => f.id === parseInt(props.widget.customProperties.file, 10)
+ (f) => f.id === parseInt(widget.customProperties.file, 10)
);
- if (foundFile && props.widget.customProperties.update) {
- props.widget.customProperties.update = false;
- AppDispatcher.dispatch({
- type: "files/start-download",
- data: foundFile.id,
- token: props.token,
- });
+ if (foundFile && widget.customProperties.update) {
+ widget.customProperties.update = false;
+ // AppDispatcher.dispatch({
+ // type: "files/start-download",
+ // data: foundFile.id,
+ // token: props.token,
+ // });
setFile(foundFile);
}
}
- }, [props.widget.customProperties, props.files, props.token, file]);
+ }, [widget.customProperties, props.files, props.token, file]);
const imageError = (e) => {
console.error("Image error:", e);
diff --git a/src/widget/widgets/input.jsx b/src/pages/dashboards/widget/widgets/input.jsx
similarity index 100%
rename from src/widget/widgets/input.jsx
rename to src/pages/dashboards/widget/widgets/input.jsx
diff --git a/src/widget/widgets/label.jsx b/src/pages/dashboards/widget/widgets/label.jsx
similarity index 100%
rename from src/widget/widgets/label.jsx
rename to src/pages/dashboards/widget/widgets/label.jsx
diff --git a/src/widget/widgets/lamp.jsx b/src/pages/dashboards/widget/widgets/lamp.jsx
similarity index 100%
rename from src/widget/widgets/lamp.jsx
rename to src/pages/dashboards/widget/widgets/lamp.jsx
diff --git a/src/widget/widgets/line.jsx b/src/pages/dashboards/widget/widgets/line.jsx
similarity index 100%
rename from src/widget/widgets/line.jsx
rename to src/pages/dashboards/widget/widgets/line.jsx
diff --git a/src/widget/widgets/player.js b/src/pages/dashboards/widget/widgets/player.js
similarity index 99%
rename from src/widget/widgets/player.js
rename to src/pages/dashboards/widget/widgets/player.js
index 398dd9d..1cee2b4 100644
--- a/src/widget/widgets/player.js
+++ b/src/pages/dashboards/widget/widgets/player.js
@@ -18,18 +18,15 @@
import React, { Component } from 'react';
import { Container, Row, Col } from 'react-bootstrap';
import JSZip from 'jszip';
-
import IconButton from '../../common/buttons/icon-button';
import IconTextButton from '../../common/buttons/icon-text-button';
import ParametersEditor from '../../common/parameters-editor';
import ICAction from '../../ic/ic-action';
import ResultPythonDialog from "../../pages/scenarios/dialogs/result-python-dialog";
import AppDispatcher from "../../common/app-dispatcher";
-
import { playerMachine } from '../widget-player/player-machine';
import { interpret } from 'xstate';
-
const playerService = interpret(playerMachine);
function transitionState(currentState, playerEvent) {
diff --git a/src/pages/dashboards/widget/widgets/plot.jsx b/src/pages/dashboards/widget/widgets/plot.jsx
new file mode 100644
index 0000000..27041b0
--- /dev/null
+++ b/src/pages/dashboards/widget/widgets/plot.jsx
@@ -0,0 +1,120 @@
+/**
+ * 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 Plot from '../widget-plot/plot';
+import PlotLegend from '../widget-plot/plot-legend';
+
+class WidgetPlot extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ data: [],
+ signals: []
+ };
+ }
+
+
+ static getDerivedStateFromProps(props, state){
+
+ let intersection = []
+ let data = [];
+ let signalID, sig;
+ for (signalID of props.widget.signalIDs) {
+ for (sig of props.signals) {
+ if (signalID === sig.id) {
+ intersection.push(sig);
+
+ // sig is a selected signal, get data
+ // determine ID of infrastructure component related to signal (via config)
+ let icID = props.icIDs[sig.id]
+
+ // distinguish between input and output signals
+ if (sig.direction === "out") {
+ if (props.data[icID] != null && props.data[icID].output != null && props.data[icID].output.values != null) {
+ if (props.data[icID].output.values[sig.index] !== undefined) {
+ let values = props.data[icID].output.values[sig.index];
+ if(sig.scalingFactor !== 1) {
+ let scaledValues = JSON.parse(JSON.stringify(values));
+ for (let i=0; i< scaledValues.length; i++){
+ scaledValues[i].y = scaledValues[i].y * sig.scalingFactor;
+ }
+ data.push(scaledValues);
+ } else {
+ data.push(values);
+ }
+ }
+ }
+ } else if (sig.direction === "in") {
+ if (props.data[icID] != null && props.data[icID].input != null && props.data[icID].input.values != null) {
+ if (props.data[icID].input.values[sig.index] !== undefined) {
+ let values = props.data[icID].output.values[sig.index];
+ if(sig.scalingFactor !== 1) {
+ let scaledValues = JSON.parse(JSON.stringify(values));
+ for (let i=0; i< scaledValues.length; i++){
+ scaledValues[i].y = scaledValues[i].y * sig.scalingFactor;
+ }
+ data.push(scaledValues);
+ } else {
+ data.push(values);
+ }
+ }
+ }
+ }
+ } // sig is selected signal
+ } // loop over props.signals
+ } // loop over selected signals
+
+ return {signals: intersection, data: data}
+
+ }
+
+ //do we need this function?
+ scaleData(data, scaleFactor){
+ // data is an array of value pairs x,y
+ }
+
+ render() {
+ return ;
+ }
+}
+
+export default WidgetPlot;
diff --git a/src/widget/widgets/slider.jsx b/src/pages/dashboards/widget/widgets/slider.jsx
similarity index 95%
rename from src/widget/widgets/slider.jsx
rename to src/pages/dashboards/widget/widgets/slider.jsx
index 89d33fd..7e2a40b 100644
--- a/src/widget/widgets/slider.jsx
+++ b/src/pages/dashboards/widget/widgets/slider.jsx
@@ -20,7 +20,6 @@ import { format } from "d3";
import classNames from "classnames";
import Slider from "rc-slider";
import "rc-slider/assets/index.css";
-import AppDispatcher from "../../common/app-dispatcher";
const WidgetSlider = (props) => {
const [value, setValue] = useState("");
@@ -29,11 +28,11 @@ const WidgetSlider = (props) => {
useEffect(() => {
let widget = { ...props.widget };
widget.customProperties.simStartedSendValue = false;
- AppDispatcher.dispatch({
- type: "widgets/start-edit",
- token: props.token,
- data: widget,
- });
+ // AppDispatcher.dispatch({
+ // type: "widgets/start-edit",
+ // token: props.token,
+ // data: widget,
+ // });
}, [props.token, props.widget]);
useEffect(() => {
diff --git a/src/widget/widgets/table.jsx b/src/pages/dashboards/widget/widgets/table.jsx
similarity index 98%
rename from src/widget/widgets/table.jsx
rename to src/pages/dashboards/widget/widgets/table.jsx
index e87fce7..88d546e 100644
--- a/src/widget/widgets/table.jsx
+++ b/src/pages/dashboards/widget/widgets/table.jsx
@@ -16,7 +16,7 @@
******************************************************************************/
import React, { useState, useEffect } from "react";
import { format } from "d3";
-import { Table, DataColumn } from "../../common/table";
+import { Table, DataColumn } from "../../../../common/table";
const WidgetTable = (props) => {
const [rows, setRows] = useState([]);
diff --git a/src/widget/widgets/time-offset.jsx b/src/pages/dashboards/widget/widgets/time-offset.jsx
similarity index 100%
rename from src/widget/widgets/time-offset.jsx
rename to src/pages/dashboards/widget/widgets/time-offset.jsx
diff --git a/src/widget/widgets/topology.js b/src/pages/dashboards/widget/widgets/topology.js
similarity index 100%
rename from src/widget/widgets/topology.js
rename to src/pages/dashboards/widget/widgets/topology.js
diff --git a/src/widget/widgets/value.jsx b/src/pages/dashboards/widget/widgets/value.jsx
similarity index 100%
rename from src/widget/widgets/value.jsx
rename to src/pages/dashboards/widget/widgets/value.jsx
diff --git a/src/pages/infrastructure/ic-action-board.js b/src/pages/infrastructure/ic-action-board.js
index f330777..6e90464 100644
--- a/src/pages/infrastructure/ic-action-board.js
+++ b/src/pages/infrastructure/ic-action-board.js
@@ -22,10 +22,12 @@ import classNames from 'classnames';
import { useState } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { sessionToken } from '../../localStorage';
-import { clearCheckedICs, deleteIC, loadAllICs, sendActionToIC } from '../../store/icSlice';
+import { clearCheckedICs, deleteIC, sendActionToIC } from '../../store/icSlice';
+import { useGetICSQuery } from '../../store/apiSlice';
const ICActionBoard = (props) => {
const dispatch = useDispatch();
+ const {refetch: refetchICs} = useGetICSQuery();
const checkedICsIds = useSelector(state => state.infrastructure.checkedICsIds);
let pickedTime = new Date();
@@ -60,7 +62,7 @@ const ICActionBoard = (props) => {
});
dispatch(clearCheckedICs());
- dispatch(loadAllICs({token: sessionToken}));
+ refetchICs();
}
const onShutdown = () => {
diff --git a/src/pages/infrastructure/ic-category-table.js b/src/pages/infrastructure/ic-category-table.js
index 59cd821..6b19698 100644
--- a/src/pages/infrastructure/ic-category-table.js
+++ b/src/pages/infrastructure/ic-category-table.js
@@ -26,12 +26,14 @@ import moment from 'moment'
import IconToggleButton from "../../common/buttons/icon-toggle-button";
import { updateCheckedICs, openDeleteModal, openEditModal } from "../../store/icSlice";
import { stateLabelStyle } from "./styles";
+import { useGetICSQuery } from "../../store/apiSlice";
//a Table of IC components of specific category from props.category
//titled with props.title
const ICCategoryTable = (props) => {
const dispatch = useDispatch();
- const ics = useSelector(state => state.infrastructure.ICsArray);
+ const {data: icsRes, isLoading, refetch: refetchICs} = useGetICSQuery();
+ const ics = icsRes ? icsRes.ics : [];
const [isGenericDisplayed, setIsGenericDisplayed] = useState(false);
const { user: currentUser } = useSelector((state) => state.auth);
diff --git a/src/pages/infrastructure/ic-pages/default-manager-page.js b/src/pages/infrastructure/ic-pages/default-manager-page.js
index 2210c53..46c306d 100644
--- a/src/pages/infrastructure/ic-pages/default-manager-page.js
+++ b/src/pages/infrastructure/ic-pages/default-manager-page.js
@@ -22,6 +22,7 @@ import IconButton from '../../../common/buttons/icon-button';
import ManagedICsTable from "./managed-ics-table";
import { useDispatch } from 'react-redux';
import { loadICbyId } from '../../../store/icSlice';
+import { useGetICSQuery } from '../../../store/apiSlice';
import ICParamsTable from '../ic-params-table';
import RawDataTable from '../../../common/rawDataTable';
@@ -31,7 +32,8 @@ import { iconStyle, buttonStyle } from "../styles";
const DefaultManagerPage = (props) => {
const ic = props.ic;
- const ics = useSelector((state) => state.infrastructure.ICsArray);
+ const {data: icsRes, isLoading, refetch: refetchICs} = useGetICSQuery();
+ const ics = icsRes ? icsRes.ics : [];
const { user: currentUser, token: sessionToken } = useSelector((state) => state.auth);
diff --git a/src/pages/infrastructure/ic-pages/kubernetes-ic-page.js b/src/pages/infrastructure/ic-pages/kubernetes-ic-page.js
index 80d8942..96e7b1e 100644
--- a/src/pages/infrastructure/ic-pages/kubernetes-ic-page.js
+++ b/src/pages/infrastructure/ic-pages/kubernetes-ic-page.js
@@ -20,6 +20,7 @@ import IconButton from "../../../common/buttons/icon-button";
import RawDataTable from "../../../common/rawDataTable";
import { useDispatch, useSelector } from "react-redux";
import { loadICbyId } from "../../../store/icSlice";
+import { useGetICSQuery } from "../../../store/apiSlice";
import ICParamsTable from "../ic-params-table";
@@ -33,7 +34,8 @@ const KubernetesICPage = (props) => {
const { user: currentUser, token: sessionToken } = useSelector((state) => state.auth);
const ic = props.ic;
- const ics = useSelector((state) => state.infrastructure.ICsArray);
+ const {data: icsRes, isLoading, refetch: refetchICs} = useGetICSQuery();
+ const ics = icsRes ? icsRes.ics : [];
const config = useSelector((state) => state.config.config);
//const managedICs = ics.filter(managedIC => managedIC.category !== "manager" && managedIC.manager === ic.uuid);
diff --git a/src/pages/infrastructure/ic-pages/manager-villas-node.js b/src/pages/infrastructure/ic-pages/manager-villas-node.js
index 0446e77..3735722 100644
--- a/src/pages/infrastructure/ic-pages/manager-villas-node.js
+++ b/src/pages/infrastructure/ic-pages/manager-villas-node.js
@@ -22,6 +22,7 @@ import ManagedICsTable from "./managed-ics-table";
import RawDataTable from "../../../common/rawDataTable";
import { downloadGraph } from "../../../utils/icUtils";
import { loadICbyId } from "../../../store/icSlice";
+import { useGetICSQuery } from "../../../store/apiSlice";
import ICParamsTable from "../ic-params-table";
@@ -35,7 +36,8 @@ const ManagerVillasNode = (props) => {
const ic = props.ic;
- const ics = useSelector((state) => state.infrastructure.ICsArray);
+ const {data: icsRes, isLoading, refetch: refetchICs} = useGetICSQuery();
+ const ics = icsRes ? icsRes.ics : [];
const managedICs = ics.filter(managedIC => managedIC.category !== "manager" && managedIC.manager === ic.uuid);
const graphURL = ic.apiurl !== "" ? ic.apiurl + "/graph.svg" : "";
diff --git a/src/pages/infrastructure/ic-pages/manager-villas-relay.js b/src/pages/infrastructure/ic-pages/manager-villas-relay.js
index f4f4c0e..0576d8e 100644
--- a/src/pages/infrastructure/ic-pages/manager-villas-relay.js
+++ b/src/pages/infrastructure/ic-pages/manager-villas-relay.js
@@ -20,7 +20,8 @@ import IconButton from "../../../common/buttons/icon-button";
import ManagedICsTable from "./managed-ics-table";
import RawDataTable from "../../../common/rawDataTable";
import { useDispatch, useSelector } from "react-redux";
-import { loadAllICs, loadICbyId } from "../../../store/icSlice";
+import { loadICbyId } from "../../../store/icSlice";
+import { useGetICSQuery } from "../../../store/apiSlice";
import ICParamsTable from "../ic-params-table";
@@ -34,7 +35,9 @@ const ManagerVillasRelay = (props) => {
const ic = props.ic;
- const ics = useSelector((state) => state.infrastructure.ICsArray);
+ const {data: icsRes, isLoading, refetch: refetchICs} = useGetICSQuery();
+ const ics = icsRes ? icsRes.ics : [];
+
const managedICs = ics.filter(managedIC => managedIC.category !== "manager" && managedIC.manager === ic.uuid);
const refresh = () => {
diff --git a/src/pages/infrastructure/ic.js b/src/pages/infrastructure/ic.js
index 51480d2..c43980b 100644
--- a/src/pages/infrastructure/ic.js
+++ b/src/pages/infrastructure/ic.js
@@ -18,7 +18,7 @@
import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { useParams } from 'react-router-dom';
-import { loadAllICs, loadICbyId } from "../../store/icSlice";
+import { loadICbyId } from "../../store/icSlice";
import { loadConfig } from "../../store/configSlice";
import DefaultManagerPage from "./ic-pages/default-manager-page";
@@ -36,10 +36,8 @@ const InfrastructureComponent = (props) => {
const { token: sessionToken } = useSelector((state) => state.auth);
const ic = useSelector(state => state.infrastructure.currentIC);
- const isICLoading = useSelector(state => state.infrastructure.isCurrentICLoading);
useEffect(() => {
- dispatch(loadAllICs({token: sessionToken}));
dispatch(loadICbyId({token: sessionToken, id: id}));
dispatch(loadConfig({token: sessionToken}));
}, []);
diff --git a/src/pages/infrastructure/infrastructure.js b/src/pages/infrastructure/infrastructure.js
index af2a1fe..0c22168 100644
--- a/src/pages/infrastructure/infrastructure.js
+++ b/src/pages/infrastructure/infrastructure.js
@@ -17,7 +17,7 @@
import { useEffect, useState } from "react"
import { useDispatch, useSelector } from "react-redux";
-import { loadAllICs, loadICbyId, addIC, sendActionToIC, closeDeleteModal, closeEditModal, editIC, deleteIC } from "../../store/icSlice";
+import { addIC, sendActionToIC, closeDeleteModal, closeEditModal, editIC, deleteIC } from "../../store/icSlice";
import IconButton from "../../common/buttons/icon-button";
import ICCategoryTable from "./ic-category-table";
import ICActionBoard from "./ic-action-board";
@@ -28,14 +28,16 @@ import EditICDialog from "./dialogs/edit-ic-dialog";
import DeleteDialog from "../../common/dialogs/delete-dialog";
import NotificationsDataManager from "../../common/data-managers/notifications-data-manager";
import NotificationsFactory from "../../common/data-managers/notifications-factory";
+import {useGetICSQuery} from '../../store/apiSlice';
-
-const Infrastructure = (props) => {
+const Infrastructure = () => {
const dispatch = useDispatch();
const { user: currentUser, token: sessionToken } = useSelector((state) => state.auth);
- const ics = useSelector(state => state.infrastructure.ICsArray);
+ const {data: icsRes, isLoading, refetch: refetchICs} = useGetICSQuery();
+ const ics = icsRes ? icsRes.ics : [];
+
const externalICs = ics.filter(ic => ic.managedexternally === true);
//track status of the modals
@@ -45,22 +47,14 @@ const Infrastructure = (props) => {
const [checkedICs, setCheckedICs] = useState([]);
useEffect(() => {
- //load array of ics and start a timer for periodic refresh
- dispatch(loadAllICs({token: sessionToken}));
- let timer = window.setInterval(() => refresh(), 10000);
+ //start a timer for periodic refresh
+ let timer = window.setInterval(() => refetchICs(), 10000);
return () => {
window.clearInterval(timer);
}
}, []);
- const refresh = () => {
- //if none of the modals are currently opened, we reload ics array
- if(!(isEditModalOpened || isDeleteModalOpened || isICModalOpened)){
- dispatch(loadAllICs({token: sessionToken}));
- }
- }
-
//modal actions and selectors
const isEditModalOpened = useSelector(state => state.infrastructure.isEditModalOpened);
@@ -75,7 +69,7 @@ const Infrastructure = (props) => {
if(data){
if(!data.managedexternally){
- dispatch(addIC({token: sessionToken, ic: data})).then(res => dispatch(loadAllICs({token: sessionToken})));
+ dispatch(addIC({token: sessionToken, ic: data}))
}else {
// externally managed IC: dispatch create action to selected manager
let newAction = {};
@@ -91,7 +85,7 @@ const Infrastructure = (props) => {
return;
}
- dispatch(sendActionToIC({token: sessionToken, id: managerIC.id, actions: newAction})).then(res => dispatch(loadAllICs({token: sessionToken})));
+ dispatch(sendActionToIC({token: sessionToken, id: managerIC.id, actions: newAction}))
}
}
}
@@ -99,20 +93,20 @@ const Infrastructure = (props) => {
const onImportModalClose = (data) => {
setIsImportModalOpened(false);
- dispatch(addIC({token: sessionToken, ic: data})).then(res => dispatch(loadAllICs({token: sessionToken})));
+ dispatch(addIC({token: sessionToken, ic: data}))
}
const onEditModalClose = (data) => {
if(data){
//some changes where done
- dispatch(editIC({token: sessionToken, ic: data})).then(res => dispatch(loadAllICs({token: sessionToken})));
+ dispatch(editIC({token: sessionToken, ic: data}))
}
dispatch(closeEditModal(data));
}
const onCloseDeleteModal = (isDeleteConfirmed) => {
if(isDeleteConfirmed){
- dispatch(deleteIC({token: sessionToken, id:deleteModalIC.id})).then(res => dispatch(loadAllICs({token: sessionToken})));
+ dispatch(deleteIC({token: sessionToken, id:deleteModalIC.id}))
}
dispatch(closeDeleteModal());
}
diff --git a/src/pages/scenarios/dialogs/edit-signal-mapping.js b/src/pages/scenarios/dialogs/edit-signal-mapping.js
index c416190..7ccaaee 100644
--- a/src/pages/scenarios/dialogs/edit-signal-mapping.js
+++ b/src/pages/scenarios/dialogs/edit-signal-mapping.js
@@ -1,10 +1,10 @@
-import { useState } from "react";
+import { useState, useEffect } from "react";
import { Button, Form, OverlayTrigger, Tooltip } from "react-bootstrap";
import { Table, ButtonColumn, CheckboxColumn, DataColumn } from '../../../common/table';
import { dialogWarningLabel, signalDialogCheckButton, buttonStyle } from "../styles";
import Dialog from "../../../common/dialogs/dialog";
import Icon from "../../../common/icon";
-import { useGetSignalsQuery, useAddSignalMutation, useDeleteSignalMutation } from "../../../store/apiSlice";
+import { useGetSignalsQuery, useAddSignalMutation, useDeleteSignalMutation, useUpdateSignalMutation } from "../../../store/apiSlice";
import { Collapse } from 'react-collapse';
const ExportSignalMappingDialog = ({isShown, direction, onClose, configID}) => {
@@ -14,10 +14,44 @@ const ExportSignalMappingDialog = ({isShown, direction, onClose, configID}) => {
const {data, refetch: refetchSignals } = useGetSignalsQuery({configID: configID, direction: direction});
const [addSignalToConfig] = useAddSignalMutation();
const [deleteSignal] = useDeleteSignalMutation();
+ const [updateSignal] = useUpdateSignalMutation();
const signals = data ? data.signals : [];
+ const [updatedSignals, setUpdatedSignals] = useState([]);
+ const [updatedSignalsIDs, setUpdatedSignalsIDs] = useState([]);
+
+ useEffect(() => {
+ if (signals.length > 0) {
+ setUpdatedSignals([...signals]);
+ }
+ }, [signals]);
+
const handleMappingChange = (e, row, column) => {
- console.log(e.target.value, row, column);
+ const signalToUpdate = {...updatedSignals[row]};
+ switch (column) {
+ case 1:
+ signalToUpdate.index = e.target.value;
+ break;
+ case 2:
+ signalToUpdate.name = e.target.value;
+ break;
+ case 3:
+ signalToUpdate.unit = e.target.value;
+ break;
+ case 4:
+ signalToUpdate.scalingFactor = e.target.value;
+ break;
+ default:
+ break;
+ }
+
+ setUpdatedSignals(prevState =>
+ prevState.map((signal, index) =>
+ index === row ? signalToUpdate : signal
+ )
+ );
+
+ setUpdatedSignalsIDs(prevState => ([signalToUpdate.id, ...prevState]));
}
const handleAdd = async () => {
@@ -58,6 +92,24 @@ const ExportSignalMappingDialog = ({isShown, direction, onClose, configID}) => {
refetchSignals();
}
+ const handleUpdate = async () => {
+ try {
+ for (let id of updatedSignalsIDs) {
+
+ const signalToUpdate = updatedSignals.find(signal => signal.id === id);
+
+ if (signalToUpdate) {
+ await updateSignal({ signalID: id, updatedSignal: signalToUpdate }).unwrap();
+ }
+ }
+
+ refetchSignals();
+ setUpdatedSignalsIDs([]);
+ } catch (error) {
+ console.error("Error updating signals:", error);
+ }
+ }
+
const onSignalChecked = (signal, event) => {
if(!checkedSignalsIDs.includes(signal.id)){
setCheckedSignalsIDs(prevState => ([...prevState, signal.id]));
@@ -102,14 +154,17 @@ const ExportSignalMappingDialog = ({isShown, direction, onClose, configID}) => {
title={"Edit Signal " + direction +" Mapping"}
buttonTitle="Close"
blendOutCancel = {true}
- onClose={(c) => onClose(c)}
+ onClose={(c) => {
+ handleUpdate();
+ onClose(c)
+ }}
onReset={() => {}}
valid={true}
>
IMPORTANT: Signal configurations that were created before January 2022 have to be fixed manually. Signal indices have to start at 0 and not 1.
Click in table cell to edit
- onSignalChecked(signal)} data={signals}>
+ onSignalChecked(signal)} data={updatedSignals}>
onSignalChecked(index, event)}
checked={(signal) => isSignalChecked(signal)}
diff --git a/src/pages/scenarios/tables/configs-table.js b/src/pages/scenarios/tables/configs-table.js
index bb171b0..afa7976 100644
--- a/src/pages/scenarios/tables/configs-table.js
+++ b/src/pages/scenarios/tables/configs-table.js
@@ -156,42 +156,40 @@ const ConfigsTable = ({scenario, ics}) => {
const copyConfig = async (configToCopy) => {
let copiedConfig = JSON.parse(JSON.stringify(configToCopy));
-
+
try {
- const signalsInRes = await triggerGetSignals({configID: configToCopy.id, direction: "in"}, ).unwrap();
- const signalsOutRes = await triggerGetSignals({configID: configToCopy.id, direction: "out"}, ).unwrap();
-
+ const signalsInRes = await triggerGetSignals({configID: configToCopy.id, direction: "in"}).unwrap();
+ const signalsOutRes = await triggerGetSignals({configID: configToCopy.id, direction: "out"}).unwrap();
+
let parsedInSignals = [];
let parsedOutSignals = [];
-
- if(signalsInRes.signals.length > 0){
- for(let signal of signalsInRes.signals){
- delete signal.configID;
- delete signal.id;
- parsedInSignals.push(signal);
+
+ if (signalsInRes.signals.length > 0) {
+ for (let signal of signalsInRes.signals) {
+ const { configID, id, ...rest } = signal;
+ parsedInSignals.push(rest);
}
}
-
- if(signalsOutRes.signals.length > 0){
- for(let signal of signalsOutRes.signals){
- delete signal.configID;
- delete signal.id;
- parsedOutSignals.push(signal);
+
+ if (signalsOutRes.signals.length > 0) {
+ for (let signal of signalsOutRes.signals) {
+ const { configID, id, ...rest } = signal;
+ parsedOutSignals.push(rest);
}
}
-
+
copiedConfig["inputMapping"] = parsedInSignals;
copiedConfig["outputMapping"] = parsedOutSignals;
-
- delete copiedConfig.id;
- delete copiedConfig.scenarioID;
-
- return copiedConfig;
+
+ const { id, scenarioID, ...finalConfig } = copiedConfig;
+
+ return finalConfig;
} catch (err) {
console.log(err);
return null;
}
}
+
const handleConfigExport = async (config) => {
try {
@@ -234,7 +232,11 @@ const ConfigsTable = ({scenario, ics}) => {
}
}
} catch (err) {
- notificationsDataManager.addNotification(NotificationsFactory.UPDATE_ERROR(err.data.message));
+ if(err.data){
+ notificationsDataManager.addNotification(NotificationsFactory.UPDATE_ERROR(err.data.message));
+ } else {
+ console.log(err)
+ }
}
refetchConfigs();
diff --git a/src/store/apiSlice.js b/src/store/apiSlice.js
index dac5b23..9d8608b 100644
--- a/src/store/apiSlice.js
+++ b/src/store/apiSlice.js
@@ -10,6 +10,7 @@ import { fileEndpoints } from './endpoints/file-endpoints';
import { signalEndpoints } from './endpoints/signal-endpoints';
import { resultEndpoints } from './endpoints/result-endpoints';
import { authEndpoints } from './endpoints/auth-endpoints';
+import { websocketEndpoints } from './endpoints/websocket-endpoints';
export const apiSlice = createApi({
reducerPath: 'api',
@@ -34,6 +35,7 @@ export const apiSlice = createApi({
...resultEndpoints(builder),
...signalEndpoints(builder),
...authEndpoints(builder),
+ ...websocketEndpoints(builder),
}),
});
@@ -80,4 +82,7 @@ export const {
useDeleteWidgetMutation,
useGetConfigQuery,
useAuthenticateUserMutation,
+ useLazyGetFilesQuery,
+ useUpdateSignalMutation,
+ useGetIcDataQuery
} = apiSlice;
diff --git a/src/store/configSlice.js b/src/store/configSlice.js
index 920fef4..744fcd6 100644
--- a/src/store/configSlice.js
+++ b/src/store/configSlice.js
@@ -51,4 +51,4 @@ export const loadConfig = createAsyncThunk(
}
);
-export default configSlice.reducer;
\ No newline at end of file
+export default configSlice.reducer;
diff --git a/src/store/endpoints/signal-endpoints.js b/src/store/endpoints/signal-endpoints.js
index 544d969..77ac615 100644
--- a/src/store/endpoints/signal-endpoints.js
+++ b/src/store/endpoints/signal-endpoints.js
@@ -29,6 +29,13 @@ export const signalEndpoints = (builder) => ({
body: { signal },
}),
}),
+ updateSignal: builder.mutation({
+ query: ({ signalID, updatedSignal }) => ({
+ url: `signals/${signalID}`,
+ method: 'PUT',
+ body: { signal: updatedSignal },
+ }),
+ }),
deleteSignal: builder.mutation({
query: (signalID) => ({
url: `signals/${signalID}`,
diff --git a/src/store/endpoints/websocket-endpoints.js b/src/store/endpoints/websocket-endpoints.js
new file mode 100644
index 0000000..cb191c5
--- /dev/null
+++ b/src/store/endpoints/websocket-endpoints.js
@@ -0,0 +1,103 @@
+/**
+ * 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 .
+ ******************************************************************************/
+
+
+function setupWebSocket(WS_URL, onMessage, protocol) {
+ const socket = new WebSocket(WS_URL, protocol); // Include the protocol here
+ socket.binaryType = 'arraybuffer'; // Set binary type
+ socket.onmessage = (event) => {
+ onMessage(event.data);
+ };
+ return socket;
+}
+
+const sendMessage = (message) => {
+ if (socket && socket.readyState === WebSocket.OPEN) {
+ socket.send(JSON.stringify(message));
+ }
+};
+
+export const websocketEndpoints = (builder) => ({
+ getIcData: builder.query({
+ query: () => ({data: []}),
+ async onCacheEntryAdded(
+ arg,
+ { updateCachedData, cacheDataLoaded, cacheEntryRemoved }
+ ) {
+ // create a websocket connection when the cache subscription starts
+ const socket = new WebSocket('wss://villas.k8s.eonerc.rwth-aachen.de/ws/ws_sig', 'live');
+ socket.binaryType = 'arraybuffer';
+ try {
+ // wait for the initial query to resolve before proceeding
+ await cacheDataLoaded;
+
+ // when data is received from the socket connection to the server,
+ // if it is a message and for the appropriate channel,
+ // update our query result with the received message
+ const listener = (event) => {
+ console.log(event.data)
+ }
+
+ socket.addEventListener('message', listener)
+ } catch {
+ // no-op in case `cacheEntryRemoved` resolves before `cacheDataLoaded`,
+ // in which case `cacheDataLoaded` will throw
+ }
+ // cacheEntryRemoved will resolve when the cache subscription is no longer active
+ await cacheEntryRemoved
+ // perform cleanup steps once the `cacheEntryRemoved` promise resolves
+ ws.close()
+ },
+ }),
+});
+
+function bufferToMessageArray(blob) {
+ let offset = 0;
+ const msgs = [];
+
+ while (offset < blob.byteLength) {
+ const msg = bufferToMessage(new DataView(blob, offset));
+ if (msg !== undefined) {
+ msgs.push(msg);
+ offset += msg.blob.byteLength;
+ }
+ }
+
+ return msgs;
+}
+
+function bufferToMessage(data) {
+ if (data.byteLength === 0) {
+ return null;
+ }
+
+ const source_index = data.getUint8(1);
+ const bits = data.getUint8(0);
+ const length = data.getUint16(0x02, 1);
+ const bytes = length * 4 + 16;
+
+ return {
+ version: (bits >> OFFSET_VERSION) & 0xF,
+ type: (bits >> OFFSET_TYPE) & 0x3,
+ source_index: source_index,
+ length: length,
+ sequence: data.getUint32(0x04, 1),
+ timestamp: data.getUint32(0x08, 1) * 1e3 + data.getUint32(0x0C, 1) * 1e-6,
+ values: new Float32Array(data.buffer, data.byteOffset + 0x10, length),
+ blob: new DataView(data.buffer, data.byteOffset + 0x00, bytes),
+ };
+}
\ No newline at end of file
diff --git a/src/store/icSlice.js b/src/store/icSlice.js
index aa4b95b..175b485 100644
--- a/src/store/icSlice.js
+++ b/src/store/icSlice.js
@@ -77,18 +77,10 @@ const icSlice = createSlice({
closeDeleteModal: (state, args) => {
state.deleteModalIC = null;
state.isDeleteModalOpened = false;
-
}
},
extraReducers: builder => {
builder
- .addCase(loadAllICs.pending, (state, action) => {
- state.isLoading = true
- })
- .addCase(loadAllICs.fulfilled, (state, action) => {
- state.ICsArray = action.payload;
- console.log("fetched ICs")
- })
.addCase(loadICbyId.pending, (state, action) => {
state.isCurrentICLoading = true
})
@@ -122,19 +114,6 @@ const icSlice = createSlice({
}
});
-//loads all ICs and saves them in the store
-export const loadAllICs = createAsyncThunk(
- 'infrastructure/loadAllICs',
- async (data) => {
- try {
- const res = await RestAPI.get('/api/v2/ic', data.token);
- return res.ics;
- } catch (error) {
- console.log("Error loading ICs data: ", error);
- }
- }
-);
-
//loads one IC by its id
export const loadICbyId = createAsyncThunk(
'infrastructure/loadICbyId',
diff --git a/src/store/index.js b/src/store/index.js
index c8acda2..71f8893 100644
--- a/src/store/index.js
+++ b/src/store/index.js
@@ -20,12 +20,14 @@ import icReducer from './icSlice';
import configReducer from './configSlice'
import { apiSlice } from "./apiSlice";
import authReducer from './authSlice';
+import websocketReducer from './websocketSlice';
export const store = configureStore({
reducer: {
auth: authReducer,
infrastructure: icReducer,
config: configReducer,
+ websocket: websocketReducer,
[apiSlice.reducerPath]: apiSlice.reducer,
},
middleware: (getDefaultMiddleware) =>
diff --git a/src/store/websocketSlice.js b/src/store/websocketSlice.js
new file mode 100644
index 0000000..d147902
--- /dev/null
+++ b/src/store/websocketSlice.js
@@ -0,0 +1,134 @@
+import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
+import { wsManager } from "../common/api/websocket-api";
+import { current } from "@reduxjs/toolkit";
+
+export const connectWebSocket = createAsyncThunk(
+ 'websocket/connect',
+ async ({ url, id, length }, { dispatch }) => {
+ return new Promise((resolve, reject) => {
+ wsManager.connect(
+ url,
+ (msgs) => {
+ const icdata = {
+ input: {
+ sequence: -1,
+ length: length,
+ version: 2,
+ type: 0,
+ timestamp: Date.now(),
+ values: new Array(length).fill(0),
+ },
+ output: {
+ values: [],
+ },
+ };
+
+ const MAX_VALUES = 10000;
+
+ if (msgs.length > 0) {
+ for (let j = 0; j < msgs.length; j++) {
+ let smp = msgs[j];
+
+ if (smp.source_index !== 0) {
+ for (let i = 0; i < smp.length; i++) {
+ while (icdata.input.values.length < i + 1) {
+ icdata.input.values.push([]);
+ }
+
+ icdata.input.values[i] = smp.values[i];
+
+ if (icdata.input.values[i].length > MAX_VALUES) {
+ const pos = icdata.input.values[i].length - MAX_VALUES;
+ icdata.input.values[i].splice(0, pos);
+ }
+ }
+
+ icdata.input.timestamp = smp.timestamp;
+ icdata.input.sequence = smp.sequence;
+ } else {
+ for (let i = 0; i < smp.length; i++) {
+ while (icdata.output.values.length < i + 1) {
+ icdata.output.values.push([]);
+ }
+
+ icdata.output.values[i].push({ x: smp.timestamp, y: smp.values[i] });
+
+ if (icdata.output.values[i].length > MAX_VALUES) {
+ const pos = icdata.output.values[i].length - MAX_VALUES;
+ icdata.output.values[i].splice(0, pos);
+ }
+ }
+
+ icdata.output.timestamp = smp.timestamp;
+ icdata.output.sequence = smp.sequence;
+ }
+ }
+
+ // Dispatch the action to update the Redux state
+ dispatch(updateIcData({ id, newIcData: icdata }));
+ }
+ },
+ () => {
+ console.log('WebSocket connected to:', url);
+ dispatch(setConnectedUrl({ url })); // Optional: Track the connected URL
+ resolve(); // Resolve the promise on successful connection
+ },
+ () => {
+ console.log('WebSocket disconnected from:', url);
+ dispatch(disconnect());
+ reject(); // Reject the promise if the connection is closed
+ }
+ );
+ });
+ }
+);
+
+const websocketSlice = createSlice({
+ name: 'websocket',
+ initialState: {
+ connectedUrl: null,
+ icdata: {},
+ },
+ reducers: {
+ setConnectedUrl: (state, action) => {
+ state.connectedUrl = action.payload.url;
+ },
+ disconnect: (state) => {
+ wsManager.disconnect(); // Ensure the WebSocket is disconnected
+ state.connectedUrl = null;
+ },
+ updateIcData: (state, action) => {
+ const { id, newIcData } = action.payload;
+ const currentICdata = current(state.icdata);
+ if(currentICdata[id]){
+ const {values, ...rest} = newIcData.output;
+ let oldValues = [...currentICdata[id].output.values];
+ for(let i = 0; i < newIcData.output.values.length; i++){
+ oldValues[i] = [...oldValues[i], ...values[i]]
+ }
+ state.icdata[id] = {
+ input: newIcData.input,
+ output: {
+ ...rest,
+ values: oldValues
+ }
+ }
+ } else {
+ state.icdata[id] = {
+ ...newIcData,
+ };
+ }
+ },
+ },
+ extraReducers: (builder) => {
+ builder.addCase(connectWebSocket.fulfilled, (state, action) => {
+ // Handle the fulfilled state if needed
+ });
+ builder.addCase(connectWebSocket.rejected, (state, action) => {
+ // Handle the rejected state if needed
+ });
+ },
+});
+
+export const { setConnectedUrl, disconnect, updateIcData } = websocketSlice.actions;
+export default websocketSlice.reducer;
diff --git a/src/widget/widget-plot/plot.jsx b/src/widget/widget-plot/plot.jsx
deleted file mode 100644
index 4f48a6b..0000000
--- a/src/widget/widget-plot/plot.jsx
+++ /dev/null
@@ -1,186 +0,0 @@
-/**
- * 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, { useState, useEffect, useRef } from "react";
-import { axisBottom, axisLeft } from "d3-axis";
-import { extent } from "d3-array";
-import { format } from "d3-format";
-import { line } from "d3-shape";
-import { scaleLinear, scaleTime } from "d3-scale";
-import { select } from "d3-selection";
-import { schemeCategory10 } from "d3-scale-chromatic";
-import { timeFormat } from "d3-time-format";
-
-const topMargin = 10;
-const bottomMargin = 25;
-const leftMargin = 40;
-const rightMargin = 10;
-
-let uniqueIdentifier = 0;
-
-function Plot(props) {
- const [data, setData] = useState(null);
- const [lines, setLines] = useState(null);
- const [labelMargin, setLabelMargin] = useState(0);
- const [identifier, setIdentifier] = useState(uniqueIdentifier++);
- const [stopTime, setStopTime] = useState(null);
- const [firstTimestamp, setFirstTimestamp] = useState(null);
- const [xAxis, setXAxis] = useState(null);
- const [yAxis, setYAxis] = useState(null);
- const intervalRef = useRef();
-
- useEffect(() => {
- const interval = createInterval(
- props,
- firstTimestamp,
- data,
- setData,
- setLines,
- setXAxis,
- setYAxis,
- labelMargin
- );
- intervalRef.current = interval;
-
- return () => {
- removeInterval(intervalRef.current);
- };
- }, [props]);
-
- useEffect(() => {
- updatePlot(
- props,
- data,
- setData,
- setLines,
- setXAxis,
- setYAxis,
- stopTime,
- setStopTime,
- firstTimestamp,
- setFirstTimestamp,
- labelMargin,
- setLabelMargin,
- identifier
- );
- }, [props, data, stopTime, firstTimestamp, identifier]);
-
- const xAxisRef = useRef();
- useEffect(() => {
- if (xAxis) {
- select(xAxisRef.current).call(xAxis);
- }
- }, [xAxis]);
-
- const yAxisRef = useRef();
- useEffect(() => {
- if (yAxis) {
- select(yAxisRef.current).call(yAxis);
- }
- }, [yAxis]);
-
- const yLabelPos = {
- x: 12,
- y: props.height / 2,
- };
-
- const plotWidth = props.width - rightMargin + 1;
- const plotHeight = props.height + topMargin + bottomMargin;
-
- return (
-
- );
-}
-
-function createInterval(
- props,
- firstTimestamp,
- data,
- setData,
- setLines,
- setXAxis,
- setYAxis,
- labelMargin
-) {
- // You would implement createInterval logic here to generate the interval based on props
- // Similarly to how it was calculated in the original class component's componentDidMount and createInterval methods.
-}
-
-function updatePlot(
- props,
- data,
- setData,
- setLines,
- setXAxis,
- setYAxis,
- stopTime,
- setStopTime,
- firstTimestamp,
- setFirstTimestamp,
- labelMargin,
- setLabelMargin,
- identifier
-) {
- // You would implement getDerivedStateFromProps logic here to update the plot.
- // Note: In functional components, derived state can be handled directly in the useEffect hook.
-}
-
-function removeInterval(interval) {
- if (interval != null) {
- clearInterval(interval);
- }
-}
-
-export default Plot;
diff --git a/src/widget/widget.js b/src/widget/widget.js
deleted file mode 100644
index d51f205..0000000
--- a/src/widget/widget.js
+++ /dev/null
@@ -1,277 +0,0 @@
-/**
- * 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 { Container } from 'flux/utils';
-
-import AppDispatcher from '../common/app-dispatcher';
-import ICDataStore from '../ic/ic-data-store';
-import ConfigsStore from '../componentconfig/config-store';
-import FileStore from '../file/file-store';
-import SignalStore from '../signal/signal-store'
-import WebsocketStore from './websocket-store'
-import ResultStore from '../result/result-store';
-
-import WidgetCustomAction from './widgets/custom-action';
-import WidgetAction from './widgets/action';
-import WidgetLamp from './widgets/lamp';
-import WidgetValue from './widgets/value';
-import WidgetPlot from './widgets/plot';
-import WidgetTable from './widgets/table';
-import WidgetLabel from './widgets/label';
-import WidgetImage from './widgets/image';
-import WidgetButton from './widgets/button';
-import WidgetInput from './widgets/input';
-import WidgetSlider from './widgets/slider';
-import WidgetGauge from './widgets/gauge';
-import WidgetBox from './widgets/box';
-import WidgetTopology from './widgets/topology';
-import WidgetLine from './widgets/line';
-import WidgetTimeOffset from './widgets/time-offset';
-import WidgetPlayer from './widgets/player';
-import WidgetICstatus from './widgets/icstatus';
-//import WidgetHTML from './widgets/html';
-
-
-import '../styles/widgets.css';
-
-class Widget extends React.Component {
- static getStores() {
- return [ ICDataStore, ConfigsStore, FileStore, SignalStore, WebsocketStore, ResultStore];
- }
-
- static calculateState(prevState, props) {
-
- let websockets = WebsocketStore.getState();
-
- let icData = {};
-
- if (props.paused) {
- if (prevState && prevState.icData) {
- icData = JSON.parse(JSON.stringify(prevState.icData));
- }
- } else {
- icData = ICDataStore.getState();
- }
-
- // Get the IC IDs and signal indexes for all signals of the widget
- let configs = ConfigsStore.getState().filter(c => c.scenarioID === parseInt(props.scenarioID, 10));
- // TODO make sure that the signals are only the signals that belong to the scenario at hand
- let signals = SignalStore.getState();
- let icIDs = [];
-
- for (let id of props.data.signalIDs){
- let signal = signals.find(s => s.id === id);
- if (signal !== undefined) {
- let config = configs.find(m => m.id === signal.configID);
- if (config !== undefined){
- icIDs[signal.id] = config.icID;
- }
- }
- }
-
- let results = ResultStore.getState().filter(r => r.scenarioID === parseInt(props.scenarioID, 10));
- let files = FileStore.getState().filter(f => f.scenarioID === parseInt(props.scenarioID, 10));
-
- return {
- websockets: websockets,
- icData: icData,
- signals: signals,
- icIDs: icIDs,
- files: files,
- sessionToken: localStorage.getItem("token"),
- results: results,
- };
- }
-
- inputDataChanged(widget, data, controlID, controlValue, isFinalChange) {
- // controlID is the path to the widget customProperty that is changed (for example 'value')
-
- // modify the widget customProperty
- if (controlID !== '' && isFinalChange) {
- let updatedWidget = JSON.parse(JSON.stringify(widget));
- updatedWidget.customProperties[controlID] = controlValue;
-
- AppDispatcher.dispatch({
- type: 'widgets/start-edit',
- token: this.state.sessionToken,
- data: updatedWidget
- });
- }
-
- // The following assumes that a widget modifies/ uses exactly one signal
-
- // get the signal with the selected signal ID
- let signalID = widget.signalIDs[0];
- let signal = this.state.signals.filter(s => s.id === signalID)
- if (signal.length === 0){
- console.warn("Unable to send signal for signal ID", signalID, ". Signal not found.");
- return;
- }
- // determine ID of infrastructure component related to signal[0]
- // Remark: there is only one selected signal for an input type widget
- let icID = this.state.icIDs[signal[0].id];
- AppDispatcher.dispatch({
- type: 'icData/inputChanged',
- ic: icID,
- signalID: signal[0].id,
- signalIndex: signal[0].index,
- data: signal[0].scalingFactor * data
- });
- }
-
- createWidget(widget) {
-
- if (widget.type === 'CustomAction') {
- return
- } else if (widget.type === 'Action') {
- return
- } else if (widget.type === 'Lamp') {
- return
- } else if (widget.type === 'Value') {
- return
- } else if (widget.type === 'Plot') {
- return
- } else if (widget.type === 'Table') {
- return
- } else if (widget.type === 'Label') {
- return
- } else if (widget.type === 'Image') {
- return
- } else if (widget.type === 'Button') {
- return this.inputDataChanged(widget, value, controlID, controlValue, isFinalChange)}
- signals={this.state.signals}
- token={this.state.sessionToken}
- />
- } else if (widget.type === 'NumberInput') {
- return this.inputDataChanged(widget, value, controlID, controlValue, isFinalChange)}
- signals={this.state.signals}
- token={this.state.sessionToken}
- />
- } else if (widget.type === 'Slider') {
- return this.inputDataChanged(widget, value, controlID, controlValue, isFinalChange)}
- signals={this.state.signals}
- token={this.state.sessionToken}
- />
- } else if (widget.type === 'Gauge') {
- return
- } else if (widget.type === 'Box') {
- return
- //} else if (widget.type === 'HTML') {
- //return
- } else if (widget.type === 'Topology') {
- return
- } else if (widget.type === 'Line') {
- return
- } else if (widget.type === 'TimeOffset') {
- return
- } else if (widget.type === 'ICstatus') {
- return
- } else if (widget.type === 'Player') {
- return
- }
-
- return null;
- }
-
- render() {
- return this.createWidget(this.props.data);
- }
-}
-
-let fluxContainerConverter = require('../common/FluxContainerConverter');
-export default Container.create(fluxContainerConverter.convert(Widget), { withProps: true });
diff --git a/src/widget/widgets-data-manager.js b/src/widget/widgets-data-manager.js
deleted file mode 100644
index 61d1cf0..0000000
--- a/src/widget/widgets-data-manager.js
+++ /dev/null
@@ -1,29 +0,0 @@
-/**
- * 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 '../common/data-managers/rest-data-manager';
-
-class WidgetsDataManager extends RestDataManager{
-
- constructor() {
- super('widget', '/widgets');
- }
-
-}
-
-export default new WidgetsDataManager()
diff --git a/src/widget/widgets/plot.jsx b/src/widget/widgets/plot.jsx
deleted file mode 100644
index 74d5a16..0000000
--- a/src/widget/widgets/plot.jsx
+++ /dev/null
@@ -1,96 +0,0 @@
-/**
- * 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, { useState, useEffect } from "react";
-import Plot from "../widget-plot/plot";
-import PlotLegend from "../widget-plot/plot-legend";
-
-const WidgetPlot = (props) => {
- const [data, setData] = useState([]);
- const [signals, setSignals] = useState([]);
-
- useEffect(() => {
- const intersection = [];
- const plotData = [];
- let signalID, sig;
- for (signalID of props.widget.signalIDs) {
- for (sig of props.signals) {
- if (signalID === sig.id) {
- intersection.push(sig);
-
- // Signal is a selected signal, get data
- let icID = props.icIDs[sig.id];
- let values = null;
-
- if (
- sig.direction === "out" &&
- props.data[icID]?.output?.values?.[sig.index] !== undefined
- ) {
- values = props.data[icID].output.values[sig.index];
- } else if (
- sig.direction === "in" &&
- props.data[icID]?.input?.values?.[sig.index] !== undefined
- ) {
- values = props.data[icID].input.values[sig.index];
- }
-
- if (values) {
- if (sig.scalingFactor !== 1) {
- values = values.map((v) => ({
- ...v,
- y: v.y * sig.scalingFactor,
- }));
- }
- plotData.push(values);
- }
- }
- }
- }
-
- setData(plotData);
- setSignals(intersection);
- }, [props.widget.signalIDs, props.signals, props.icIDs, props.data]);
-
- return (
-
- );
-};
-
-export default WidgetPlot;