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

move IC pages into separate files

- default page
- gateway villas-node page #303
- manager villas-relay page #305
- gateway villas-relay to be done #304
This commit is contained in:
Sonja Happ 2021-05-07 11:53:33 +02:00
parent 9f1555ba73
commit 77ad58f892
4 changed files with 358 additions and 236 deletions

View file

@ -0,0 +1,42 @@
/**
* 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 {Col, Container, Row} from "react-bootstrap";
class DefaultICPage extends React.Component {
constructor() {
super();
}
render() {
return (<div className='section'>
<h1>{this.props.ic.name} </h1>
<Container>
<Row>
<Col>
{this.props.ICParamsTable(this.props.ic)}
</Col>
</Row>
</Container>
</div>
)
}
}
export default DefaultICPage;

View file

@ -0,0 +1,169 @@
/**
* 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 {Button, Col, Container, Row} from "react-bootstrap";
import IconButton from "../../common/icon-button";
import ConfirmCommand from "../confirm-command";
import FileSaver from 'file-saver';
import AppDispatcher from "../../common/app-dispatcher";
class GatewayVillasNode extends React.Component {
constructor() {
super();
this.state = {
confirmCommand: false,
command: '',
}
}
async downloadGraph(url) {
let blob = await fetch(url).then(r => r.blob())
FileSaver.saveAs(blob, this.props.ic.name + ".svg");
}
sendControlCommand() {
if (this.state.command === "restart") {
AppDispatcher.dispatch({
type: 'ics/restart',
url: this.props.ic.apiurl + "/restart",
token: this.props.sessionToken,
});
} else if (this.state.command === "shutdown") {
AppDispatcher.dispatch({
type: 'ics/shutdown',
url: this.props.ic.apiurl + "/shutdown",
token: this.props.sessionToken,
});
}
}
confirmCommand(canceled){
if(!canceled){
this.sendControlCommand();
}
this.setState({confirmCommand: false, command: ''});
}
render() {
let graphURL = ""
if (this.props.ic.apiurl !== "") {
graphURL = this.props.ic.apiurl + "/graph.svg"
}
console.log("Villasnode Gateway: ", this.props.ic)
return (<div className='section'>
<h1>{this.props.ic.name}
<span className='icon-button'>
<IconButton
childKey={2}
tooltip='Refresh'
onClick={() => this.props.refresh(this.props.ic, this.props.sessionToken)}
icon='sync-alt'
buttonStyle={this.props.buttonStyle}
iconStyle={this.props.iconStyle}
/>
</span>
</h1>
<Container>
<Row>
<Col>
{this.props.ICParamsTable(this.props.ic)}
</Col>
<Col>
<div className='section-buttons-group-right'>
<IconButton
childKey={0}
tooltip='Download Graph'
onClick={() => this.downloadGraph(graphURL)}
icon='download'
buttonStyle={this.props.buttonStyle}
iconStyle={this.props.iconStyle}
/>
</div>
<hr/>
<b>Graph:</b>
<div>
<img alt={"Graph image download failed and/or incorrect image API URL"} src={graphURL} />
</div>
{this.props.currentUser.role === "Admin" ?
<div>
<hr/>
<b>Controls:</b>
<div className='solid-button'>
<Button
variant='secondary'
style={{ margin: '5px' }}
size='lg'
onClick={() => this.setState({ confirmCommand: true, command: 'restart' })}>
Restart
</Button>
<Button
variant='secondary'
style={{ margin: '5px' }}
size='lg'
onClick={() => this.setState({ confirmCommand: true, command: 'shutdown' })}>
Shutdown
</Button>
</div>
</div>
: <div />
}
<ConfirmCommand
show={this.state.confirmCommand}
command={this.state.command}
name={this.props.ic.name}
onClose={c => this.confirmCommand(c)}
/>
</Col>
</Row>
<Row>
<Col>
<b>Raw Status</b>
{this.props.rawDataTable(this.props.ic.statusupdateraw)}
</Col>
<Col>
<b>Raw Config</b>
{this.props.rawDataTable(this.props.ic.statusupdateraw != null ? this.props.ic.statusupdateraw.config : null )}
</Col>
<Col>
<b>Raw Statistics</b>
{this.props.rawDataTable(this.props.ic.statusupdateraw != null ? this.props.ic.statusupdateraw.statistics : null)}
</Col>
<Col>
</Col>
</Row>
</Container>
</div>
)
}
}
export default GatewayVillasNode;

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 IconButton from "../../common/icon-button";
import {Col, Container, Row} from "react-bootstrap";
class ManagerVillasRelay extends React.Component {
constructor() {
super();
}
render() {
return (<div className='section'>
<h1>{this.props.ic.name}
<span className='icon-button'>
<IconButton
childKey={2}
tooltip='Refresh'
onClick={() => this.props.refresh(this.props.ic, this.props.sessionToken)}
icon='sync-alt'
buttonStyle={this.props.buttonStyle}
iconStyle={this.props.iconStyle}
/>
</span>
</h1>
<Container>
<Row>
<Col>
{this.props.ICParamsTable(this.props.ic)}
</Col>
<Col>
<b>Raw Status</b>
{this.props.rawDataTable(this.props.ic.statusupdateraw)}
</Col>
</Row>
</Container>
</div>
)
}
}
export default ManagerVillasRelay;

View file

@ -20,22 +20,18 @@ import ICstore from './ic-store';
import ICdataStore from './ic-data-store'
import { Container as FluxContainer } from 'flux/utils';
import AppDispatcher from '../common/app-dispatcher';
import { Container, Col, Row, Table, Button } from 'react-bootstrap';
import { Table, } from 'react-bootstrap';
import moment from 'moment';
import ReactJson from 'react-json-view';
import ConfirmCommand from './confirm-command';
import IconButton from '../common/icon-button';
import FileSaver from 'file-saver';
import GatewayVillasNode from './ic-pages/gateway-villas-node'
import ManagerVillasRelay from './ic-pages/manager-villas-relay'
import DefaultICPage from './ic-pages/default-page'
class InfrastructureComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
confirmCommand: false,
command: '',
sessionToken: localStorage.getItem("token"),
currentUser: JSON.parse(localStorage.getItem("currentUser")),
};
@ -61,21 +57,19 @@ class InfrastructureComponent extends React.Component {
});
}
refresh() {
static refresh(ic, token) {
// get status of VILLASnode and VILLASrelay ICs
if (this.state.ic.category === "gateway" && (this.state.ic.type === "villas-node" || this.state.ic.type === "villas-relay")
&& this.state.ic.apiurl !== '' && this.state.ic.apiurl !== undefined && this.state.ic.apiurl !== null && !this.state.ic.managedexternally) {
if (ic.apiurl !== '' && ic.apiurl !== undefined && ic.apiurl !== null && !ic.managedexternally) {
AppDispatcher.dispatch({
type: 'ics/get-status',
url: this.state.ic.apiurl,
token: this.state.sessionToken,
ic: this.state.ic
url: ic.apiurl,
token: token,
ic: ic
});
}
}
isJSON(data) {
static isJSON(data) {
if (data === undefined || data === null) {
return false;
}
@ -89,46 +83,61 @@ class InfrastructureComponent extends React.Component {
return true
}
async downloadGraph(url) {
let blob = await fetch(url).then(r => r.blob())
FileSaver.saveAs(blob, this.state.ic.name + ".svg");
}
sendControlCommand() {
if (this.state.command === "restart") {
AppDispatcher.dispatch({
type: 'ics/restart',
url: this.state.ic.apiurl + "/restart",
token: this.state.sessionToken,
});
} else if (this.state.command === "shutdown") {
AppDispatcher.dispatch({
type: 'ics/shutdown',
url: this.state.ic.apiurl + "/shutdown",
token: this.state.sessionToken,
});
static rawDataTable(rawData){
if(rawData !== null && InfrastructureComponent.isJSON(rawData)){
return (
<ReactJson
src={rawData}
name={false}
displayDataTypes={false}
displayObjectSize={false}
enableClipboard={false}
collapsed={1}
/>
)
} else {
return (
<div>No valid JSON raw data available.</div>
)
}
}
confirmCommand(canceled){
if(!canceled){
this.sendControlCommand();
}
this.setState({confirmCommand: false, command: ''});
static ICParamsTable(ic) {
return (
<Table striped size="sm">
<tbody>
<tr><td>Name</td><td>{ic.name}</td></tr>
<tr><td>Description</td><td>{ic.description}</td></tr>
<tr><td>UUID</td><td>{ic.uuid}</td></tr>
<tr><td>State</td><td>{ic.state}</td></tr>
<tr><td>Category</td><td>{ic.category}</td></tr>
<tr><td>Type</td><td>{ic.type}</td></tr>
<tr><td>Uptime</td><td>{moment.duration(ic.uptime, "seconds").humanize()}</td></tr>
<tr><td>Location</td><td>{ic.location}</td></tr>
<tr><td>Websocket URL</td><td>{ic.websocketurl}</td></tr>
<tr><td>API URL</td><td>{ic.apiurl}</td></tr>
<tr><td>Start parameter schema</td><td>
{InfrastructureComponent.isJSON(ic.startparameterschema) ?
<ReactJson
src={ic.startparameterschema}
name={false}
displayDataTypes={false}
displayObjectSize={false}
enableClipboard={false}
collapsed={0}
/> : <div>No Start parameter schema JSON available.</div>}
</td>
</tr>
</tbody>
</Table>
)
}
render() {
if (this.state.ic === undefined) {
return <h1>Loading Infrastructure Component...</h1>;
}
let graphURL = ""
if (this.state.ic.apiurl !== "") {
graphURL = this.state.ic.apiurl + "/graph.svg"
}
const buttonStyle = {
marginLeft: '5px',
}
@ -138,196 +147,36 @@ class InfrastructureComponent extends React.Component {
width: '25px'
}
return <div className='section'>
<h1>{this.state.ic.name}</h1>
<Container>
<Row>
<Col>
<Table striped size="sm">
<tbody>
<tr>
<td>Name</td>
<td>{this.state.ic.name}</td>
</tr>
<tr>
<td>Description</td>
<td>{this.state.ic.description}</td>
</tr>
<tr>
<td>UUID</td>
<td>{this.state.ic.uuid}</td>
</tr>
<tr>
<td>State</td>
<td>{this.state.ic.state}</td>
</tr>
<tr>
<td>Category</td>
<td>{this.state.ic.category}</td>
</tr>
<tr>
<td>Type</td>
<td>{this.state.ic.type}</td>
</tr>
<tr>
<td>Uptime</td>
<td>{moment.duration(this.state.ic.uptime, "seconds").humanize()}</td>
</tr>
<tr>
<td>Location</td>
<td>{this.state.ic.location}</td>
</tr>
<tr>
<td>Websocket URL</td>
<td>{this.state.ic.websocketurl}</td>
</tr>
<tr>
<td>API URL</td>
<td>{this.state.ic.apiurl}</td>
</tr>
<tr>
<td>Start parameter schema</td>
<td>
{this.isJSON(this.state.ic.startparameterschema) ?
<ReactJson
src={this.state.ic.startparameterschema}
name={false}
displayDataTypes={false}
displayObjectSize={false}
enableClipboard={false}
collapsed={0}
/> : <div>No Start parameter schema JSON available.</div>}
</td>
</tr>
</tbody>
</Table>
</Col>
<Col>
{this.state.category ==="gateway" && this.state.ic.type === "villas-node" ?
<>
<div className='section-buttons-group-right'>
<IconButton
childKey={0}
tooltip='Download Graph'
onClick={() => this.downloadGraph(graphURL)}
icon='download'
buttonStyle={buttonStyle}
iconStyle={iconStyle}
/>
</div>
<hr/>
<b>Graph:</b>
<div>
<img alt={"Graph image download failed and/or incorrect image API URL"} src={graphURL} />
</div>
{this.state.currentUser.role === "Admin" ?
<div>
<hr/>
<b>Controls:</b>
<div className='solid-button'>
<Button variant='secondary' style={{ margin: '5px' }} size='lg'
onClick={() => this.setState({ confirmCommand: true, command: 'restart' })}>Restart</Button>
<Button variant='secondary' style={{ margin: '5px' }} size='lg' onClick={() => this.setState({
confirmCommand: true,
command: 'shutdown'
})}>Shutdown</Button>
</div>
</div>
: <div />
}
<ConfirmCommand
show={this.state.confirmCommand}
command={this.state.command}
name={this.state.ic.name}
onClose={c => this.confirmCommand(c)}
/>
</>
: <div />}
{this.state.category ==="gateway" && this.state.ic.type === "villas-relay" ?
<>
<div className='section-buttons-group-right'>
<IconButton
childKey={1}
tooltip='Refresh'
onClick={() => this.refresh()}
icon='sync-alt'
buttonStyle={buttonStyle}
iconStyle={iconStyle}
/>
</div>
<hr/>
<b>Raw Status</b>
{this.state.ic.statusupdateraw !== null && this.isJSON(this.state.ic.statusupdateraw) ?
<ReactJson
src={this.state.ic.statusupdateraw}
name={false}
displayDataTypes={false}
displayObjectSize={false}
enableClipboard={false}
collapsed={1}
/> : <div>No valid JSON raw data available.</div>}
</>
:
<div />}
</Col>
</Row>
{this.state.category ==="gateway" && this.state.ic.type === "villas-node" ?
<>
<div className='section-buttons-group-right'>
<IconButton
childKey={2}
tooltip='Refresh'
onClick={() => this.refresh()}
icon='sync-alt'
buttonStyle={buttonStyle}
iconStyle={iconStyle}
/>
</div>
<Row>
<Col>
<b>Raw Status</b>
{this.state.ic.statusupdateraw !== null && this.isJSON(this.state.ic.statusupdateraw) ?
<ReactJson
src={this.state.ic.statusupdateraw}
name={false}
displayDataTypes={false}
displayObjectSize={false}
enableClipboard={false}
collapsed={1}
/> : <div>No valid JSON raw data available.</div>}
</Col>
<Col>
<b>Raw Config</b>
{this.state.ic.statusupdateraw && this.isJSON(this.state.ic.statusupdateraw["config"]) ?
<ReactJson
src={this.state.ic.statusupdateraw["config"]}
name={false}
displayDataTypes={false}
displayObjectSize={false}
enableClipboard={false}
collapsed={1}
/> : <div>No valid config JSON raw data available.</div>}
</Col>
<Col>
<b>Raw Statistics</b>
{this.state.ic.statusupdateraw && this.isJSON(this.state.ic.statusupdateraw["statistics"]) ?
<ReactJson
src={this.state.ic.statusupdateraw["statistics"]}
name={false}
displayDataTypes={false}
displayObjectSize={false}
enableClipboard={false}
collapsed={1}
/> : <div>No valid statistics JSON raw data available.</div>}
</Col>
</Row>
</>: <div />}
</Container>
</div>;
let page = <>IC page not defined</>
if (this.state.ic.category ==="gateway" && this.state.ic.type === "villas-node") {
page = <GatewayVillasNode
ic = {this.state.ic}
currentUser = {this.state.currentUser}
sessionToken = {this.state.sessionToken}
ICParamsTable = {(ic) => InfrastructureComponent.ICParamsTable(ic)}
rawDataTable = {(rawData) => InfrastructureComponent.rawDataTable(rawData)}
refresh = {(ic, token) => InfrastructureComponent.refresh(ic, token)}
buttonStyle = {buttonStyle}
iconStyle = {iconStyle}
/>
} else if (this.state.ic.category ==="manager" && this.state.ic.type === "villas-relay") {
page = <ManagerVillasRelay
ic = {this.state.ic}
currentUser = {this.state.currentUser}
sessionToken = {this.state.sessionToken}
ICParamsTable = {(ic) => InfrastructureComponent.ICParamsTable(ic)}
rawDataTable = {(rawData) => InfrastructureComponent.rawDataTable(rawData)}
refresh = {(ic, token) => InfrastructureComponent.refresh(ic, token)}
buttonStyle = {buttonStyle}
iconStyle = {iconStyle}
/>
} else {
page = <DefaultICPage
ic = {this.state.ic}
ICParamsTable = {(ic) => InfrastructureComponent.ICParamsTable(ic)}
/>
}
return page
}
}