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

few components

Signed-off-by: iripiri <ikoester@eonerc.rwth-aachen.de>
This commit is contained in:
Amir Nakhaei 2023-12-21 04:49:20 +01:00 committed by al3xa23
parent 5247019c56
commit 6ff1d4983f
26 changed files with 1066 additions and 1127 deletions

View file

@ -1,74 +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 <http://www.gnu.org/licenses/>.
******************************************************************************/
import React from 'react';
import { DragSource } from 'react-dnd';
import classNames from 'classnames';
import Icon from '../common/icon';
const toolboxItemSource = {
beginDrag(props) {
return {
name: props.name
};
}
};
function collect(connect, monitor) {
return {
connectDragSource: connect.dragSource(),
isDragging: monitor.isDragging()
}
}
class ToolboxItem extends React.Component {
static defaultProps = {
disabled: false
};
render() {
var itemClass = classNames({
'toolbox-item': true,
'toolbox-item-dragging': this.props.isDragging,
'toolbox-item-disabled': this.props.disabled
});
var dropEffect = 'copy';
if (this.props.disabled === false) {
return this.props.connectDragSource(
<div className={itemClass}>
<span className="btn " style={{marginTop: '5px'}}>
{this.props.icon && <Icon style={{marginRight: '5px'}} icon={this.props.icon} /> }
{this.props.name}
</span>
</div>
, {dropEffect});
}
else {
return (
<div className={itemClass}>
<span className="btn btn-info" style={{marginTop: '5px'}}>
{this.props.icon && <Icon style={{marginRight: '5px'}} icon={this.props.icon} /> }
{this.props.name}
</span>
</div>
);
}
}
}
export default DragSource('widget', toolboxItemSource, collect)(ToolboxItem);

View file

@ -0,0 +1,74 @@
/**
* 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 <http://www.gnu.org/licenses/>.
******************************************************************************/
import React from "react";
import { DragSource } from "react-dnd";
import classNames from "classnames";
import Icon from "../common/icon";
// Drag source specification
const toolboxItemSource = {
beginDrag(props) {
return {
name: props.name,
};
},
};
// The collect function gathers props for the component related to dragging
function collect(connect, monitor) {
return {
connectDragSource: connect.dragSource(),
isDragging: monitor.isDragging(),
};
}
// The functional component for ToolboxItem
const ToolboxItem = ({
connectDragSource,
isDragging,
disabled,
name,
icon,
}) => {
const itemClass = classNames({
"toolbox-item": true,
"toolbox-item-dragging": isDragging,
"toolbox-item-disabled": disabled,
});
const content = (
<span className="btn " style={{ marginTop: "5px" }}>
{icon && <Icon style={{ marginRight: "5px" }} icon={icon} />}
{name}
</span>
);
return disabled ? (
<div className={itemClass}>{content}</div>
) : (
connectDragSource(<div className={itemClass}>{content}</div>, {
dropEffect: "copy",
})
);
};
ToolboxItem.defaultProps = {
disabled: false,
};
export default DragSource("widget", toolboxItemSource, collect)(ToolboxItem);

View file

@ -1,56 +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 <http://www.gnu.org/licenses/>.
******************************************************************************/
import React from 'react';
import { scaleOrdinal} from 'd3-scale';
import { schemeCategory10 } from 'd3-scale-chromatic'
function Legend(props){
const signal = props.sig;
const hasScalingFactor = (signal.scalingFactor !== 1);
let color = typeof props.lineColor === "undefined" ? schemeCategory10[props.index % 10] : props.lineColor;
return (
<li key={signal.id} className="signal-legend" style={{ color: color }}>
<span className="signal-legend-name">{signal.name}</span>
{props.showUnit ? <span style={{marginLeft: '0.3em'}} className="signal-unit">{signal.unit}</span> : <></> }
{hasScalingFactor ? <span style={{ marginLeft: '0.3em' }} className="signal-scale">{signal.scalingFactor}</span> : <></>}
</li>
)
}
class PlotLegend extends React.Component {
render() {
return <div className="plot-legend">
<ul>
{ this.props.lineColors !== undefined && this.props.lineColors != null ? (
this.props.signals.map( (signal, idx) =>
<Legend key={signal.id} sig={signal} showUnit={this.props.showUnit} index={idx} lineColor={this.props.lineColors[idx]}/>
)) : (
this.props.signals.map( (signal, idx) =>
<Legend key={signal.id} sig={signal} showUnit={this.props.showUnit} index={idx} lineColor={"undefined"}/>
))
}
</ul>
</div>;
}
}
export default PlotLegend;

View file

@ -0,0 +1,62 @@
/**
* 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 <http://www.gnu.org/licenses/>.
******************************************************************************/
import React from "react";
import { schemeCategory10 } from "d3-scale-chromatic";
function Legend(props) {
const { sig: signal, lineColor, index, showUnit } = props;
const hasScalingFactor = signal.scalingFactor !== 1;
const color =
typeof lineColor === "undefined" ? schemeCategory10[index % 10] : lineColor;
return (
<li key={signal.id} className="signal-legend" style={{ color: color }}>
<span className="signal-legend-name">{signal.name}</span>
{showUnit && (
<span style={{ marginLeft: "0.3em" }} className="signal-unit">
{signal.unit}
</span>
)}
{hasScalingFactor && (
<span style={{ marginLeft: "0.3em" }} className="signal-scale">
{signal.scalingFactor}
</span>
)}
</li>
);
}
const PlotLegend = ({ signals, lineColors, showUnit }) => {
return (
<div className="plot-legend">
<ul>
{signals.map((signal, idx) => (
<Legend
key={signal.id}
sig={signal}
showUnit={showUnit}
index={idx}
lineColor={lineColors ? lineColors[idx] : undefined}
/>
))}
</ul>
</div>
);
};
export default PlotLegend;

View file

@ -15,29 +15,28 @@
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
import React, { Component } from 'react';
import { Button, ButtonGroup } from 'react-bootstrap';
import Icon from '../../common/icon';
import React from "react";
import { Button, ButtonGroup } from "react-bootstrap";
import Icon from "../../common/icon";
class WidgetAction extends Component {
constructor(props) {
super(props);
const WidgetAction = (props) => {
const onClick = (e) => {
// Put your onClick logic here
};
this.state = {
};
}
onClick(e) {
}
render() {
return <ButtonGroup>
<Button onClick={this.onClick} ><Icon icon="play" /> Start</Button>
<Button onClick={this.onClick} ><Icon icon="pause" /> Pause</Button>
<Button onClick={this.onClick} ><Icon icon="stop" /> Stop</Button>
</ButtonGroup>;
}
}
return (
<ButtonGroup>
<Button onClick={onClick}>
<Icon icon="play" /> Start
</Button>
<Button onClick={onClick}>
<Icon icon="pause" /> Pause
</Button>
<Button onClick={onClick}>
<Icon icon="stop" /> Stop
</Button>
</ButtonGroup>
);
};
export default WidgetAction;

View file

@ -15,25 +15,21 @@
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
import React, { Component } from 'react';
import React from "react";
const WidgetBox = (props) => {
let colorStyle = {
borderColor: props.widget.customProperties.border_color,
backgroundColor: props.widget.customProperties.background_color,
opacity: props.widget.customProperties.background_color_opacity,
borderWidth: props.widget.customProperties.border_width + "px",
};
class WidgetBox extends Component {
render() {
let colorStyle = {
borderColor: this.props.widget.customProperties.border_color,
backgroundColor: this.props.widget.customProperties.background_color,
opacity: this.props.widget.customProperties.background_color_opacity,
borderWidth: this.props.widget.customProperties.border_width + 'px'
}
return (
<div className="box-widget full" style={colorStyle}>
{ }
</div>
);
}
}
return (
<div className="box-widget full" style={colorStyle}>
{}
</div>
);
};
export default WidgetBox;

View file

@ -1,115 +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 <http://www.gnu.org/licenses/>.
******************************************************************************/
import React, { Component } from 'react';
import { Button } from 'react-bootstrap';
import AppDispatcher from '../../common/app-dispatcher';
class WidgetButton extends Component {
constructor(props) {
super(props);
this.state = {
pressed: props.widget.customProperties.pressed
}
}
componentDidMount() {
let widget = this.props.widget
widget.customProperties.simStartedSendValue = false
widget.customProperties.pressed = false
AppDispatcher.dispatch({
type: 'widgets/start-edit',
token: this.props.token,
data: widget
});
}
componentDidUpdate() {
// a simulaton was started, make an update
if (this.props.widget.customProperties.simStartedSendValue) {
// update widget, 'unpress' button at each simulation start
let widget = this.props.widget
widget.customProperties.simStartedSendValue = false
widget.customProperties.pressed = false
AppDispatcher.dispatch({
type: 'widgets/start-edit',
token: this.props.token,
data: widget
});
// send value without changing widget
this.props.onInputChanged(widget.customProperties.off_value, '', false, false);
}
}
static getDerivedStateFromProps(props, state){
return {
pressed: props.widget.customProperties.pressed
}
}
onPress(e) {
if (e.button === 0 && !this.props.widget.customProperties.toggle) {
this.valueChanged(this.props.widget.customProperties.on_value, true);
}
}
onRelease(e) {
if (e.button === 0) {
let nextState = false;
if (this.props.widget.customProperties.toggle) {
nextState = !this.state.pressed;
}
this.valueChanged(nextState ? this.props.widget.customProperties.on_value : this.props.widget.customProperties.off_value, nextState);
}
}
valueChanged(newValue, pressed) {
if (this.props.onInputChanged) {
this.props.onInputChanged(newValue, 'pressed', pressed, true);
}
}
render() {
let opacity = this.props.widget.customProperties.background_color_opacity
const buttonStyle = {
backgroundColor: this.props.widget.customProperties.background_color,
borderColor: this.props.widget.customProperties.border_color,
color: this.props.widget.customProperties.font_color,
opacity: this.state.pressed ? (opacity + 1)/4 : opacity,
};
return (
<div className="button-widget full">
<Button
className="full"
style={buttonStyle}
active={ this.state.pressed }
disabled={ this.props.editing }
onMouseDown={ (e) => this.onPress(e) }
onMouseUp={ (e) => this.onRelease(e) }>
{this.props.widget.name}
</Button>
</div>
);
}
}
export default WidgetButton;

View file

@ -0,0 +1,108 @@
/**
* 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 <http://www.gnu.org/licenses/>.
******************************************************************************/
import React, { useState, useEffect } from 'react';
import { Button } from 'react-bootstrap';
import AppDispatcher from '../../common/app-dispatcher';
const WidgetButton = (props) => {
const [pressed, setPressed] = useState(props.widget.customProperties.pressed);
useEffect(() => {
let widget = props.widget;
widget.customProperties.simStartedSendValue = false;
widget.customProperties.pressed = false;
AppDispatcher.dispatch({
type: 'widgets/start-edit',
token: props.token,
data: widget
});
// Effect cleanup
return () => {
// Clean up if needed
};
}, [props.token, props.widget]);
useEffect(() => {
if (props.widget.customProperties.simStartedSendValue) {
let widget = props.widget;
widget.customProperties.simStartedSendValue = false;
widget.customProperties.pressed = false;
AppDispatcher.dispatch({
type: 'widgets/start-edit',
token: props.token,
data: widget
});
props.onInputChanged(widget.customProperties.off_value, '', false, false);
}
}, [props, setPressed]);
useEffect(() => {
setPressed(props.widget.customProperties.pressed);
}, [props.widget.customProperties.pressed]);
const onPress = (e) => {
if (e.button === 0 && !props.widget.customProperties.toggle) {
valueChanged(props.widget.customProperties.on_value, true);
}
};
const onRelease = (e) => {
if (e.button === 0) {
let nextState = false;
if (props.widget.customProperties.toggle) {
nextState = !pressed;
}
valueChanged(nextState ? props.widget.customProperties.on_value : props.widget.customProperties.off_value, nextState);
}
};
const valueChanged = (newValue, newPressed) => {
if (props.onInputChanged) {
props.onInputChanged(newValue, 'pressed', newPressed, true);
}
setPressed(newPressed);
};
let opacity = props.widget.customProperties.background_color_opacity;
const buttonStyle = {
backgroundColor: props.widget.customProperties.background_color,
borderColor: props.widget.customProperties.border_color,
color: props.widget.customProperties.font_color,
opacity: pressed ? (opacity + 1) / 4 : opacity,
};
return (
<div className="button-widget full">
<Button
className="full"
style={buttonStyle}
active={pressed}
disabled={props.editing}
onMouseDown={(e) => onPress(e)}
onMouseUp={(e) => onRelease(e)}
>
{props.widget.name}
</Button>
</div>
);
};
export default WidgetButton;

View file

@ -1,66 +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 <http://www.gnu.org/licenses/>.
******************************************************************************/
import React, { Component } from 'react';
import { Button } from 'react-bootstrap';
import Icon from '../../common/icon';
import ICStore from '../../ic/ic-store';
import AppDispatcher from '../../common/app-dispatcher';
class WidgetCustomAction extends Component {
constructor(props) {
super(props);
this.state = {
ic: null
};
}
static getStores() {
return [ ICStore ];
}
static getDerivedStateFromProps(props, state){
if(props.widget.signalIDs.length === 0){
return null;
}
return{
ic: ICStore.getState().find(s => s.id === props.icIDs[0]),
sessionToken: localStorage.getItem("token")
};
}
onClick() {
AppDispatcher.dispatch({
type: 'ics/start-action',
ic: this.state.ic,
data: this.props.widget.customProperties.actions,
token: this.state.sessionToken
});
}
render() {
return <div className="widget-custom-action full">
<Button className="full" disabled={this.state.ic === null} onClick={(e) => this.onClick()}>
<Icon icon={this.props.widget.customProperties.icon} /> <span dangerouslySetInnerHTML={{ __html: this.props.widget.name }} />
</Button>
</div>;
}
}
export default WidgetCustomAction;

View file

@ -0,0 +1,118 @@
/**
* 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 <http://www.gnu.org/licenses/>.
******************************************************************************/
import React, { useState, useEffect } from "react";
import { Button } from "react-bootstrap";
import Icon from "../../common/icon";
import ICStore from "../../ic/ic-store";
import AppDispatcher from "../../common/app-dispatcher";
const WidgetCustomAction = (props) => {
const [ic, setIC] = useState(null);
const [sessionToken, setSessionToken] = useState(
localStorage.getItem("token")
);
useEffect(() => {
const handleStoresChanged = () => {
if (props.widget.signalIDs.length === 0) {
setIC(null);
return;
}
const newIC = ICStore.getState().find((s) => s.id === props.icIDs[0]);
setIC(newIC);
};
// Subscribe to store changes
const unsubscribe = ICStore.subscribe(handleStoresChanged);
handleStoresChanged(); // Also call it to set initial state
return () => {
unsubscribe(); // Clean up the subscription when the component is unmounted
};
}, [props.widget.signalIDs, props.icIDs]);
const onClick = () => {
AppDispatcher.dispatch({
type: "ics/start-action",
ic: ic,
data: props.widget.customProperties.actions,
token: sessionToken,
});
};
return (
<div className="widget-custom-action full">
<Button className="full" disabled={ic === null} onClick={onClick}>
<Icon icon={props.widget.customProperties.icon} />
<span dangerouslySetInnerHTML={{ __html: props.widget.name }} />
</Button>
</div>
);
};
export default WidgetCustomAction;
// import React, { Component } from 'react';
// import { Button } from 'react-bootstrap';
// import Icon from '../../common/icon';
// import ICStore from '../../ic/ic-store';
// import AppDispatcher from '../../common/app-dispatcher';
// class WidgetCustomAction extends Component {
// constructor(props) {
// super(props);
// this.state = {
// ic: null
// };
// }
// static getStores() {
// return [ ICStore ];
// }
// static getDerivedStateFromProps(props, state){
// if(props.widget.signalIDs.length === 0){
// return null;
// }
// return{
// ic: ICStore.getState().find(s => s.id === props.icIDs[0]),
// sessionToken: localStorage.getItem("token")
// };
// }
// onClick() {
// AppDispatcher.dispatch({
// type: 'ics/start-action',
// ic: this.state.ic,
// data: this.props.widget.customProperties.actions,
// token: this.state.sessionToken
// });
// }
// render() {
// return <div className="widget-custom-action full">
// <Button className="full" disabled={this.state.ic === null} onClick={(e) => this.onClick()}>
// <Icon icon={this.props.widget.customProperties.icon} /> <span dangerouslySetInnerHTML={{ __html: this.props.widget.name }} />
// </Button>
// </div>;
// }
// }
// export default WidgetCustomAction;

View file

@ -15,12 +15,16 @@
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
import React from 'react';
import React from "react";
class WidgetHTML extends React.Component {
render() {
return <div dangerouslySetInnerHTML={{__html: this.props.widget.customProperties.content }} />
}
}
const WidgetHTML = (props) => {
return (
<div
dangerouslySetInnerHTML={{
__html: props.widget.customProperties.content,
}}
/>
);
};
export default WidgetHTML;

View file

@ -1,79 +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 <http://www.gnu.org/licenses/>.
******************************************************************************/
import React from 'react';
import { Badge } from 'react-bootstrap';
import { stateLabelStyle } from "../../ic/ics";
import AppDispatcher from '../../common/app-dispatcher';
class WidgetICstatus extends React.Component {
constructor(props) {
super(props);
this.state = {
sessionToken: localStorage.getItem("token")
};
}
componentDidMount() {
// Start timer for periodic refresh
this.timer = window.setInterval(() => this.refresh(), 3000);
}
componentWillUnmount() {
window.clearInterval(this.timer);
}
refresh() {
if (this.props.ics) {
this.props.ics.forEach(ic => {
let icID = parseInt(ic.id, 10);
AppDispatcher.dispatch({
type: 'ics/start-load',
data: icID,
token: this.state.sessionToken,
});
})
}
}
render() {
let badges = []
let checkedICs = []
if (this.props.widget) {
checkedICs = this.props.widget.customProperties.checkedIDs
}
if (this.props.ics && checkedICs) {
this.props.ics.forEach(ic => {
if (!checkedICs.includes(ic.id)) {
return
}
let badgeStyle = stateLabelStyle(ic.state, ic)
badges.push(<Badge
key={ic.id}
bg={badgeStyle[0]}
className={badgeStyle[1]}>
{ic.name + ": " + ic.state}</Badge>)
})
}
return (<div>{badges}</div>);
}
}
export default WidgetICstatus;

View file

@ -0,0 +1,71 @@
/**
* 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 <http://www.gnu.org/licenses/>.
******************************************************************************/
import React, { useState, useEffect } from "react";
import { Badge } from "react-bootstrap";
import { stateLabelStyle } from "../../ic/ics";
import AppDispatcher from "../../common/app-dispatcher";
const WidgetICstatus = (props) => {
const [sessionToken, setSessionToken] = useState(
localStorage.getItem("token")
);
useEffect(() => {
// Function to refresh data
const refresh = () => {
if (props.ics) {
props.ics.forEach((ic) => {
let icID = parseInt(ic.id, 10);
AppDispatcher.dispatch({
type: "ics/start-load",
data: icID,
token: sessionToken,
});
});
}
};
// Start timer for periodic refresh
const timer = window.setInterval(() => refresh(), 3000);
// Cleanup function equivalent to componentWillUnmount
return () => {
window.clearInterval(timer);
};
}, [props.ics, sessionToken]);
let badges = [];
let checkedICs = props.widget ? props.widget.customProperties.checkedIDs : [];
if (props.ics && checkedICs) {
badges = props.ics
.filter((ic) => checkedICs.includes(ic.id))
.map((ic) => {
let badgeStyle = stateLabelStyle(ic.state, ic);
return (
<Badge key={ic.id} bg={badgeStyle[0]} className={badgeStyle[1]}>
{ic.name + ": " + ic.state}
</Badge>
);
});
}
return <div>{badges}</div>;
};
export default WidgetICstatus;

View file

@ -1,89 +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 <http://www.gnu.org/licenses/>.
******************************************************************************/
import React from 'react';
import AppDispatcher from '../../common/app-dispatcher';
class WidgetImage extends React.Component {
constructor(props) {
super(props);
this.state = {
file: undefined,
}
}
componentDidMount() {
// Query the image referenced by the widget
let widgetFile = this.props.widget.customProperties.file;
if (widgetFile !== -1 && this.state.file === undefined) {
AppDispatcher.dispatch({
type: 'files/start-download',
data: widgetFile,
token: this.props.token
});
}
}
componentDidUpdate(prevProps: Readonly<P>, prevState: Readonly<S>, snapshot: SS) {
if(this.props.widget.customProperties.file === -1){
this.props.widget.customProperties.update = false;
if(this.state.file !== undefined) this.setState({ file: undefined })
}
let file = this.props.files.find(file => file.id === parseInt(this.props.widget.customProperties.file, 10));
if (file !== undefined) {
if (this.props.widget.customProperties.update) {
this.props.widget.customProperties.update = false;
AppDispatcher.dispatch({
type: 'files/start-download',
data: file.id,
token: this.props.token
});
this.setState({ file: file })
}
}
}
imageError(e){
console.error("Image ", this.state.file.name, "cannot be displayed.");
}
render() {
let objectURL=''
if(this.state.file !== undefined && this.state.file.objectURL !== undefined) {
objectURL = this.state.file.objectURL
}
return (
<div className="full">
{objectURL !== '' ? (
<img onError={(e) => this.imageError(e)} className="full" alt={this.state.file.name} src={objectURL} onDragStart={e => e.preventDefault()} />
) : (
<img className="full" alt="No file selected." />
)}
</div>
);
}
}
export default WidgetImage;

View file

@ -0,0 +1,78 @@
/**
* 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 <http://www.gnu.org/licenses/>.
******************************************************************************/
import React, { useState, useEffect } from "react";
import AppDispatcher from "../../common/app-dispatcher";
const WidgetImage = (props) => {
const [file, setFile] = useState(undefined);
useEffect(() => {
let widgetFile = props.widget.customProperties.file;
if (widgetFile !== -1 && file === undefined) {
AppDispatcher.dispatch({
type: "files/start-download",
data: widgetFile,
token: props.token,
});
}
}, [file, props.token, props.widget.customProperties.file]);
useEffect(() => {
if (props.widget.customProperties.file === -1) {
props.widget.customProperties.update = false;
if (file !== undefined) setFile(undefined);
} else {
let foundFile = props.files.find(
(f) => f.id === parseInt(props.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,
});
setFile(foundFile);
}
}
}, [props.widget.customProperties, props.files, props.token, file]);
const imageError = (e) => {
console.error("Image error:", e);
};
let objectURL = file && file.objectURL ? file.objectURL : "";
return (
<div className="full">
{objectURL ? (
<img
onError={imageError}
className="full"
alt={file.name}
src={objectURL}
onDragStart={(e) => e.preventDefault()}
/>
) : (
<img className="full" alt="No file selected." />
)}
</div>
);
};
export default WidgetImage;

View file

@ -15,23 +15,20 @@
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
import React, { Component } from 'react';
import React from "react";
const WidgetLabel = (props) => {
const style = {
fontSize: props.widget.customProperties.textSize + "px",
color: props.widget.customProperties.fontColor,
opacity: props.widget.customProperties.fontColor_opacity,
};
class WidgetLabel extends Component {
render() {
const style = {
fontSize: this.props.widget.customProperties.textSize + 'px',
color: this.props.widget.customProperties.fontColor,
opacity: this.props.widget.customProperties.fontColor_opacity,
};
return (
<div className="label-widget">
<h4 style={style}>{this.props.widget.name}</h4>
</div>
);
}
}
return (
<div className="label-widget">
<h4 style={style}>{props.widget.name}</h4>
</div>
);
};
export default WidgetLabel;

View file

@ -1,86 +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 <http://www.gnu.org/licenses/>.
******************************************************************************/
import React, { Component } from 'react';
class WidgetLamp extends Component {
constructor(props) {
super(props);
this.state = {
value: '',
};
}
static getDerivedStateFromProps(props, state){
if(props.widget.signalIDs.length === 0){
return{ value: ''};
}
// get the signal with the selected signal ID
let signalID = props.widget.signalIDs[0];
let signal = props.signals.filter(s => s.id === signalID)
if(signal.length>0) {
// determine ID of infrastructure component related to signal[0] (there is only one signal for a lamp widget)
let icID = props.icIDs[signal[0].id];
// check if data available
if (props.data == null
|| props.data[icID] == null
|| props.data[icID].output == null
|| props.data[icID].output.values == null) {
return {value: ''};
}
// check if value has changed
const data = props.data[icID].output.values[signal[0].index];
if (data != null && Number(state.value) !== signal[0].scalingFactor * data[data.length - 1].y) {
return {value: signal[0].scalingFactor * data[data.length - 1].y};
}
}
return null;
}
render() {
let color;
let opacity;
if (Number(this.state.value) > Number(this.props.widget.customProperties.threshold)){
color = this.props.widget.customProperties.on_color;
opacity = this.props.widget.customProperties.on_color_opacity;
}
else{
color = this.props.widget.customProperties.off_color;
opacity = this.props.widget.customProperties.off_color_opacity;
}
let style = {
backgroundColor: color,
opacity: opacity
}
return (
<div className="lamp-widget" style={style} />
);
}
}
export default WidgetLamp;

View file

@ -0,0 +1,72 @@
/**
* 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 <http://www.gnu.org/licenses/>.
******************************************************************************/
import React, { useState, useEffect } from 'react';
const WidgetLamp = (props) => {
const [value, setValue] = useState('');
useEffect(() => {
if(props.widget.signalIDs.length === 0){
setValue('');
return;
}
let signalID = props.widget.signalIDs[0];
let signal = props.signals.find(s => s.id === signalID);
if(signal) {
let icID = props.icIDs[signal.id];
if (!(props.data &&
props.data[icID] &&
props.data[icID].output &&
props.data[icID].output.values)) {
setValue('');
return;
}
const data = props.data[icID].output.values[signal.index];
if(data) {
const newValue = String(signal.scalingFactor * data[data.length - 1].y);
if (value !== newValue) {
setValue(newValue);
}
}
}
}, [props.widget.signalIDs, props.signals, props.icIDs, props.data, value]);
let color, opacity;
if (Number(value) > Number(props.widget.customProperties.threshold)) {
color = props.widget.customProperties.on_color;
opacity = props.widget.customProperties.on_color_opacity;
} else {
color = props.widget.customProperties.off_color;
opacity = props.widget.customProperties.off_color_opacity;
}
let style = {
backgroundColor: color,
opacity: opacity
};
return (
<div className="lamp-widget" style={style} />
);
};
export default WidgetLamp;

View file

@ -1,83 +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 <http://www.gnu.org/licenses/>.
******************************************************************************/
import React, { Component } from 'react';
class WidgetLine extends Component {
// WidgetLine is newly created when widget props are changed and saved
constructor(props) {
super(props);
this.illustrateDuringEdit = this.illustrateDuringEdit.bind(this);
this.state = {
width: 0,
height: 0,
editing: false
}
}
// needed to update the looks of the line in edit mode
illustrateDuringEdit(newwidth, newheight) {
this.setState({width: newwidth, height: newheight, editing: true});
}
render() {
let rotation = this.props.widget.customProperties.rotation;
let rad = rotation * (Math.PI / 180);
// get the dimensions either from props (saved widget)
// or from the state (widget in edit mode)
let width = this.props.widget.width;
let height = this.props.widget.height;
if (this.state.editing) {
width = this.state.width;
height = this.state.height;
}
let length = width;
rotation = Math.abs(parseInt(rotation,10));
if(rotation % 90 === 0 && (rotation/90) % 2 === 1){
length = height;
}
// calculate line coordinates (in percent)
const x1 = width * 0.5 - 0.5 * Math.cos(rad) * length;
const x1p = '' + Math.round(100 * x1 / width) + '%';
const x2 = width * 0.5 + 0.5 * Math.cos(rad) * length;
const x2p = '' + Math.round(100 * x2/width) + '%';
const y1 = height * 0.5 + 0.5 * Math.sin(rad) * length;
const y1p = '' + Math.round(100 * y1/height) + '%';
const y2 = height * 0.5 - 0.5 * Math.sin(rad) * length;
const y2p = '' + Math.round(100 * y2/height) + '%';
const lineStyle = {
stroke: '' + this.props.widget.customProperties.border_color,
strokeWidth: '' + this.props.widget.customProperties.border_width + 'px'
};
return <svg height="100%" width="100%">
<line x1={x1p} x2={x2p} y1={y1p} y2={y2p} style={lineStyle}/>
</svg>;
}
}
export default WidgetLine;

View file

@ -0,0 +1,70 @@
/**
* 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 <http://www.gnu.org/licenses/>.
******************************************************************************/
import React, { useState } from "react";
const WidgetLine = (props) => {
const [dimensions, setDimensions] = useState({
width: props.widget.width || 0,
height: props.widget.height || 0,
editing: false,
});
const illustrateDuringEdit = (newWidth, newHeight) => {
setDimensions({ width: newWidth, height: newHeight, editing: true });
};
// Assuming illustrateDuringEdit may be called from outside to update the dimensions.
// If not, you can remove this line.
props.illustrateDuringEditRef &&
props.illustrateDuringEditRef(illustrateDuringEdit);
let { rotation } = props.widget.customProperties;
let rad = rotation * (Math.PI / 180);
let length = dimensions.editing ? dimensions.width : props.widget.width;
rotation = Math.abs(parseInt(rotation, 10));
if (rotation % 90 === 0 && (rotation / 90) % 2 === 1) {
length = dimensions.editing ? dimensions.height : props.widget.height;
}
// calculate line coordinates (in percent)
const x1 = length * 0.5 - 0.5 * Math.cos(rad) * length;
const x1p = `${Math.round((100 * x1) / length)}%`;
const x2 = length * 0.5 + 0.5 * Math.cos(rad) * length;
const x2p = `${Math.round((100 * x2) / length)}%`;
const y1 = length * 0.5 + 0.5 * Math.sin(rad) * length;
const y1p = `${Math.round((100 * y1) / length)}%`;
const y2 = length * 0.5 - 0.5 * Math.sin(rad) * length;
const y2p = `${Math.round((100 * y2) / length)}%`;
const lineStyle = {
stroke: props.widget.customProperties.border_color,
strokeWidth: `${props.widget.customProperties.border_width}px`,
};
return (
<svg height="100%" width="100%">
<line x1={x1p} x2={x2p} y1={y1p} y2={y2p} style={lineStyle} />
</svg>
);
};
export default WidgetLine;

View file

@ -1,120 +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 <http://www.gnu.org/licenses/>.
******************************************************************************/
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 <div className="plot-widget" ref="wrapper">
<div className="widget-plot">
<Plot
data={this.state.data}
mode={this.props.widget.customProperties.mode || "auto time-scrolling"}
height={this.props.widget.height - 55}
width={this.props.widget.width - 20}
time={this.props.widget.customProperties.time}
samples={this.props.widget.customProperties.nbrSamples || 100}
yMin={this.props.widget.customProperties.yMin}
yMax={this.props.widget.customProperties.yMax}
yUseMinMax={this.props.widget.customProperties.yUseMinMax}
paused={this.props.paused}
yLabel={this.props.widget.customProperties.ylabel}
lineColors={this.props.widget.customProperties.lineColors}
signalIDs={this.props.widget.signalIDs}
/>
</div>
<PlotLegend
signals={this.state.signals}
lineColors={this.props.widget.customProperties.lineColors}
showUnit={this.props.widget.customProperties.showUnit} />
</div>;
}
}
export default WidgetPlot;

View file

@ -0,0 +1,96 @@
/**
* 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 <http://www.gnu.org/licenses/>.
******************************************************************************/
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 (
<div className="plot-widget">
<div className="widget-plot">
<Plot
data={data}
mode={props.widget.customProperties.mode || "auto time-scrolling"}
height={props.widget.height - 55}
width={props.widget.width - 20}
time={props.widget.customProperties.time}
samples={props.widget.customProperties.nbrSamples || 100}
yMin={props.widget.customProperties.yMin}
yMax={props.widget.customProperties.yMax}
yUseMinMax={props.widget.customProperties.yUseMinMax}
paused={props.paused}
yLabel={props.widget.customProperties.ylabel}
lineColors={props.widget.customProperties.lineColors}
signalIDs={props.widget.signalIDs}
/>
</div>
<PlotLegend
signals={signals}
lineColors={props.widget.customProperties.lineColors}
showUnit={props.widget.customProperties.showUnit}
/>
</div>
);
};
export default WidgetPlot;

View file

@ -1,161 +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 <http://www.gnu.org/licenses/>.
******************************************************************************/
import React, { Component } from 'react';
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';
class WidgetSlider extends Component {
static get OrientationTypes() {
return ({
HORIZONTAL: {value: 0, name: 'Horizontal'},
VERTICAL: {value: 1, name: 'Vertical'}
})
}
constructor(props) {
super(props);
this.state = {
unit: '',
value: '',
};
}
componentDidMount() {
let widget = this.props.widget
widget.customProperties.simStartedSendValue = false
AppDispatcher.dispatch({
type: 'widgets/start-edit',
token: this.props.token,
data: widget
});
}
componentDidUpdate() {
// a simulaton was started, make an update
if (this.props.widget.customProperties.simStartedSendValue) {
let widget = this.props.widget
widget.customProperties.simStartedSendValue = false
AppDispatcher.dispatch({
type: 'widgets/start-edit',
token: this.props.token,
data: widget
});
// send value without changing widget
this.props.onInputChanged(widget.customProperties.value , '', '', false);
}
}
static getDerivedStateFromProps(props, state){
let value = ''
let unit = ''
if(props.widget.customProperties.hasOwnProperty('value') && props.widget.customProperties.value !== state.value){
// set value to customProperties.value if this property exists and the value is different from current state
value = Number(props.widget.customProperties.value);
} else if (state.value === '') {
value = 0.0;
}
// Update unit (assuming there is exactly one signal for this widget)
if (props.widget.signalIDs.length > 0) {
let signalID = props.widget.signalIDs[0];
let signal = props.signals.find(sig => sig.id === signalID);
if (signal !== undefined) {
unit = signal.unit;
}
}
if (unit !== '' && value !== ''){
// unit and value have changed
return {unit: unit, value: value};
} else if (unit !== ''){
// only unit has changed
return {unit: unit}
} else if (value !== ''){
// only value has changed
return {value: value}
} else {
// nothing has changed
return null
}
}
valueIsChanging(newValue) {
this.props.widget.customProperties.value = newValue;
if (this.props.widget.customProperties.continous_update)
this.valueChanged(newValue, false);
this.setState({ value: newValue });
}
valueChanged(newValue, isFinalChange) {
if (this.props.onInputChanged) {
this.props.onInputChanged(newValue, 'value', newValue, isFinalChange);
}
}
render() {
let isVertical = this.props.widget.customProperties.orientation === WidgetSlider.OrientationTypes.VERTICAL.value;
let fields = {
name: this.props.widget.name,
control: <Slider
min={ this.props.widget.customProperties.rangeMin }
max={ this.props.widget.customProperties.rangeMax }
step={ this.props.widget.customProperties.step }
value={ this.state.value }
disabled={ this.props.editing }
vertical={ isVertical }
onChange={ (v) => this.valueIsChanging(v) }
onAfterChange={ (v) => this.valueChanged(v, true)
}/>,
value: <span className="signal-value">{ format('.2f')(Number.parseFloat(this.state.value)) }</span>,
unit: <span className="signal-unit">{ this.state.unit }</span>
}
let widgetClasses = classNames({
'slider-widget': true,
'full': true,
'vertical': isVertical,
'horizontal': !isVertical
});
return (
<>
<div >
{ fields.name }
{ fields.value }
{this.props.widget.customProperties.showUnit && fields.unit}
</div>
<div className={widgetClasses}>
{ fields.control }
</div>
</>
);
}
}
export default WidgetSlider;

View file

@ -0,0 +1,148 @@
/**
* 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 <http://www.gnu.org/licenses/>.
******************************************************************************/
import React, { useState, useEffect } from "react";
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("");
const [unit, setUnit] = useState("");
useEffect(() => {
let widget = { ...props.widget };
widget.customProperties.simStartedSendValue = false;
AppDispatcher.dispatch({
type: "widgets/start-edit",
token: props.token,
data: widget,
});
}, [props.token, props.widget]);
useEffect(() => {
// A simulation was started, make an update
if (props.widget.customProperties.simStartedSendValue) {
let widget = { ...props.widget };
widget.customProperties.simStartedSendValue = false;
AppDispatcher.dispatch({
type: "widgets/start-edit",
token: props.token,
data: widget,
});
// Send value without changing widget
props.onInputChanged(widget.customProperties.value, "", "", false);
}
}, [props.token, props.widget, props.onInputChanged]);
useEffect(() => {
let newValue = "";
let newUnit = "";
if (
props.widget.customProperties.hasOwnProperty("value") &&
props.widget.customProperties.value !== value
) {
newValue = Number(props.widget.customProperties.value);
} else if (value === "") {
newValue = 0.0;
}
if (props.widget.signalIDs.length > 0) {
let signalID = props.widget.signalIDs[0];
let signal = props.signals.find((sig) => sig.id === signalID);
if (signal !== undefined) {
newUnit = signal.unit;
}
}
if (newUnit) {
setUnit(newUnit);
}
if (newValue !== "") {
setValue(newValue);
}
}, [props.signals, props.widget, value]);
const valueIsChanging = (newValue) => {
props.widget.customProperties.value = newValue;
if (props.widget.customProperties.continous_update) {
valueChanged(newValue, false);
}
setValue(newValue);
};
const valueChanged = (newValue, isFinalChange) => {
if (props.onInputChanged) {
props.onInputChanged(newValue, "value", newValue, isFinalChange);
}
};
let isVertical =
props.widget.customProperties.orientation ===
WidgetSlider.OrientationTypes.VERTICAL.value;
const fields = {
name: props.widget.name,
control: (
<Slider
min={props.widget.customProperties.rangeMin}
max={props.widget.customProperties.rangeMax}
step={props.widget.customProperties.step}
value={value}
disabled={props.editing}
vertical={isVertical}
onChange={valueIsChanging}
onAfterChange={(v) => valueChanged(v, true)}
/>
),
value: (
<span className="signal-value">
{format(".2f")(Number.parseFloat(value))}
</span>
),
unit: <span className="signal-unit">{unit}</span>,
};
const widgetClasses = classNames({
"slider-widget": true,
full: true,
vertical: isVertical,
horizontal: !isVertical,
});
return (
<>
<div>
{fields.name}
{fields.value}
{props.widget.customProperties.showUnit && fields.unit}
</div>
<div className={widgetClasses}>{fields.control}</div>
</>
);
};
WidgetSlider.OrientationTypes = {
HORIZONTAL: { value: 0, name: "Horizontal" },
VERTICAL: { value: 1, name: "Vertical" },
};
export default WidgetSlider;

View file

@ -1,136 +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 <http://www.gnu.org/licenses/>.
******************************************************************************/
import React, { Component } from 'react';
import { format } from 'd3';
import { Table, DataColumn } from '../../common/table';
class WidgetTable extends Component {
constructor(props) {
super(props);
this.state = {
rows: [],
};
}
static getDerivedStateFromProps(props, state){
let rows = [];
let signalID, sig;
for (signalID of props.widget.signalIDs) {
for (sig of props.signals) {
if (signalID === sig.id) {
// 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 data = props.data[icID].output.values[sig.index];
rows.push({
name: sig.name,
unit: sig.unit,
value: data[data.length - 1].y * sig.scalingFactor,
scalingFactor: sig.scalingFactor
});
} else {
// no data available
rows.push({
name: sig.name,
unit: sig.unit,
value: NaN,
scalingFactor: sig.scalingFactor
})
}
}
} 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 data = props.data[icID].input.values[sig.index];
rows.push({
name: sig.name,
unit: sig.unit,
value: data[data.length - 1].y * sig.scalingFactor,
scalingFactor: sig.scalingFactor
});
} else {
// no data available
rows.push({
name: sig.name,
unit: sig.unit,
value: NaN,
scalingFactor: sig.scalingFactor
})
}
}
}
} // sig is selected signal
} // loop over props.signals
} // loop over selected signals
return {rows: rows}
}
render() {
let showScalingFactor;
if (this.props.widget.customProperties.showScalingFactor !== undefined){ // this line ensures backwards compatibility with older versions of VILLASweb
showScalingFactor = this.props.widget.customProperties.showScalingFactor;
} else {
showScalingFactor = true;
}
let rows = this.state.rows;
if(rows.length === 0){
rows.push({
name: "no entries"
})
}
let columns = [
<DataColumn key={1} title="Signal" dataKey="name" width={120} />,
<DataColumn key={2} title="Value" dataKey="value" modifier={format('.4f')} />,
];
let nextKey = 3;
if (showScalingFactor) {
columns.push(<DataColumn key={nextKey} title="Scale" dataKey="scalingFactor" modifier={format('.2f')}/>);
nextKey++;
}
if (this.props.widget.customProperties.showUnit) {
columns.push(<DataColumn key={nextKey} title="Unit" dataKey="unit"/>);
}
return (
<div className="table-widget" style={{width: this.props.widget.width, height: this.props.widget.height, overflowY: 'auto'}}>
<Table data={rows}>
{ columns }
</Table>
</div>
);
}
}
export default WidgetTable;

View file

@ -0,0 +1,111 @@
/**
* 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 <http://www.gnu.org/licenses/>.
******************************************************************************/
import React, { useState, useEffect } from "react";
import { format } from "d3";
import { Table, DataColumn } from "../../common/table";
const WidgetTable = (props) => {
const [rows, setRows] = useState([]);
useEffect(() => {
let newRows = [];
let signalID, sig;
for (signalID of props.widget.signalIDs) {
for (sig of props.signals) {
if (signalID === sig.id) {
let icID = props.icIDs[sig.id];
let direction = sig.direction === "out" ? "output" : "input";
if (
props.data[icID] &&
props.data[icID][direction] &&
props.data[icID][direction].values
) {
if (props.data[icID][direction].values[sig.index] !== undefined) {
let data = props.data[icID][direction].values[sig.index];
newRows.push({
name: sig.name,
unit: sig.unit,
value: data[data.length - 1].y * sig.scalingFactor,
scalingFactor: sig.scalingFactor,
});
} else {
newRows.push({
name: sig.name,
unit: sig.unit,
value: NaN,
scalingFactor: sig.scalingFactor,
});
}
}
}
}
}
if (newRows.length === 0) {
newRows.push({ name: "no entries" });
}
setRows(newRows);
}, [props.widget.signalIDs, props.signals, props.icIDs, props.data]);
let showScalingFactor =
props.widget.customProperties.showScalingFactor !== undefined
? props.widget.customProperties.showScalingFactor
: true;
let columns = [
<DataColumn key={1} title="Signal" dataKey="name" width={120} />,
<DataColumn
key={2}
title="Value"
dataKey="value"
modifier={format(".4f")}
/>,
];
let nextKey = 3;
if (showScalingFactor) {
columns.push(
<DataColumn
key={nextKey}
title="Scale"
dataKey="scalingFactor"
modifier={format(".2f")}
/>
);
nextKey++;
}
if (props.widget.customProperties.showUnit) {
columns.push(<DataColumn key={nextKey} title="Unit" dataKey="unit" />);
}
return (
<div
className="table-widget"
style={{
width: props.widget.width,
height: props.widget.height,
overflowY: "auto",
}}
>
<Table data={rows}>{columns}</Table>
</div>
);
};
export default WidgetTable;