Compare commits
7 Commits
8979ad6db3
...
feature/pr
Author | SHA1 | Date | |
---|---|---|---|
9af0ba1bb8 | |||
04c27560ea | |||
73157520ab | |||
f862e4b8da | |||
f85346aea9 | |||
1d438bc349 | |||
86999cd646 |
@ -52,7 +52,7 @@
|
|||||||
<None Remove="ClientApp\src\components\auth\Auth.tsx" />
|
<None Remove="ClientApp\src\components\auth\Auth.tsx" />
|
||||||
<None Remove="ClientApp\src\components\auth\AuthClient.ts" />
|
<None Remove="ClientApp\src\components\auth\AuthClient.ts" />
|
||||||
<None Remove="ClientApp\src\components\auth\AuthService.ts" />
|
<None Remove="ClientApp\src\components\auth\AuthService.ts" />
|
||||||
<None Remove="ClientApp\src\components\dashboard\DashboardService.ts" />
|
<None Remove="ClientApp\src\components\dashboard\ServiceInfoService.ts" />
|
||||||
<None Remove="ClientApp\src\components\devices\DeviceService.ts" />
|
<None Remove="ClientApp\src\components\devices\DeviceService.ts" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
import { Box, Grid, IconButton, Paper, Typography } from '@material-ui/core';
|
import React, { Component } from 'react';
|
||||||
import { blueGrey } from '@material-ui/core/colors';
|
|
||||||
import { AddBox, Refresh } from '@material-ui/icons/';
|
|
||||||
import { withStyles } from '@material-ui/styles';
|
import { withStyles } from '@material-ui/styles';
|
||||||
import { HubConnectionBuilder } from '@microsoft/signalr';
|
import Services from './services/Services';
|
||||||
import React, { Component } from 'react';
|
import { blueGrey } from '@material-ui/core/colors';
|
||||||
import AddNewDialog from './AddNewDialog';
|
import { Box, Grid, IconButton, Paper, Typography } from '@material-ui/core';
|
||||||
import DashboardService, { ServiceRequest } from './DashboardService';
|
import DonutChart from './charts/DonutChart';
|
||||||
import ServiceInfoComponent from './ServiceInfoComponent';
|
import HeatmapChart from './charts/HeatmapChart';
|
||||||
import ServiceInfoSkeleton from './ServiceInfoSkeleton';
|
import BarChart from './charts/BarChart';
|
||||||
|
import LineChart from './charts/LineChart';
|
||||||
|
import DevicesContext from '../../contexts/DevicesContext';
|
||||||
|
import C from '../../common/Constants';
|
||||||
|
|
||||||
const styles = theme => ({
|
const styles = theme => ({
|
||||||
root: {
|
root: {
|
||||||
@ -15,142 +16,302 @@ const styles = theme => ({
|
|||||||
padding: '64px',
|
padding: '64px',
|
||||||
backgroundColor: theme.palette.primary.dark,
|
backgroundColor: theme.palette.primary.dark,
|
||||||
},
|
},
|
||||||
paper: {
|
|
||||||
backgroundColor: blueGrey[50],
|
|
||||||
height: '60px',
|
|
||||||
},
|
|
||||||
typo: {
|
typo: {
|
||||||
fontSize: theme.typography.pxToRem(20),
|
fontSize: theme.typography.pxToRem(20),
|
||||||
fontWeight: theme.typography.fontWeightRegular,
|
fontWeight: theme.typography.fontWeightRegular,
|
||||||
},
|
},
|
||||||
|
paper: {
|
||||||
|
backgroundColor: blueGrey[50],
|
||||||
|
padding: '16px',
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const hub_url = "/hubs/services";
|
|
||||||
const notify_method_name = "NotifyUpdatedAsync";
|
|
||||||
|
|
||||||
class Dashboard extends Component {
|
class Dashboard extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
hubConnection: null,
|
deviceSeries: [],
|
||||||
isDialogOpen: false,
|
sensorSeries: [],
|
||||||
isLoading: false,
|
heatmapSecondsSeries: [],
|
||||||
service: new DashboardService(),
|
heatmapMinutesSeries: [],
|
||||||
services: [],
|
barSeries: [],
|
||||||
serviceCount: [1, 2, 3],
|
barCategories: [],
|
||||||
|
lineSeries: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
this.updateSeries = this.updateSeries.bind(this);
|
||||||
|
this.updateDynamic = this.updateDynamic.bind(this);
|
||||||
|
this.performTask = this.performTask.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.handleDevicesUpdated = this.handleDevicesUpdated.bind(this);
|
static contextType = DevicesContext;
|
||||||
this.addDevice = this.addDevice.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleDevicesUpdated() {
|
|
||||||
this.setState({ isLoading: true });
|
|
||||||
|
|
||||||
this.state.service.getCount().then(result => {
|
|
||||||
const updatedCount = [];
|
|
||||||
for (var i = 0; i < result; i++) {
|
|
||||||
updatedCount.push(i);
|
|
||||||
}
|
|
||||||
this.setState({ serviceCount: updatedCount });
|
|
||||||
}).catch(ex => {
|
|
||||||
console.log(ex);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.state.service.get().then(result => {
|
|
||||||
const updatedServices = [];
|
|
||||||
for (var s of result) {
|
|
||||||
updatedServices.push(s);
|
|
||||||
}
|
|
||||||
this.setState({ services: updatedServices });
|
|
||||||
}).catch(ex => {
|
|
||||||
console.log(ex);
|
|
||||||
}).finally(() => this.setState({ isLoading: false }));
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.handleDevicesUpdated();
|
this.context.addHandler(C.update_all_method_name, this.updateSeries);
|
||||||
const newConnection = new HubConnectionBuilder()
|
this.context.addHandler(C.update_method_name, this.updateSeries);
|
||||||
.withUrl(hub_url)
|
this.updateSeries();
|
||||||
.withAutomaticReconnect()
|
this.updateDynamic();
|
||||||
.build();
|
|
||||||
|
|
||||||
this.setState({ hubConnection: newConnection });
|
|
||||||
|
|
||||||
newConnection.start()
|
|
||||||
.then(_ => {
|
|
||||||
console.log('Services hub Connected!');
|
|
||||||
newConnection.on(notify_method_name, () => this.handleDevicesUpdated());
|
|
||||||
}).catch(e => console.log('Services hub Connection failed: ', e));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
if (this.state.hubConnection != null) {
|
this.context.removeHandler(C.update_all_method_name, this.updateSeries);
|
||||||
this.state.hubConnection.off(notify_method_name);
|
this.context.removeHandler(C.update_method_name, this.updateSeries);
|
||||||
console.log('Services hub Disconnected!');
|
}
|
||||||
|
|
||||||
|
getItemsWithStatus(iterate, status) {
|
||||||
|
const items = [];
|
||||||
|
|
||||||
|
for (var d of iterate) {
|
||||||
|
if (d.status == status) {
|
||||||
|
items.push(d);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addDevice(name, url) {
|
return items;
|
||||||
this.setState({ isDialogOpen: false });
|
}
|
||||||
let request = new ServiceRequest();
|
|
||||||
request.id = 0;
|
|
||||||
request.name = name;
|
|
||||||
request.uri = url;
|
|
||||||
|
|
||||||
this.state.service.post(request).catch(ex => {
|
getDevicesWithStatus(status) {
|
||||||
console.log(ex);
|
return this.getItemsWithStatus(this.context.devices, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
getSensorsWithStatus(status) {
|
||||||
|
const sensors = [];
|
||||||
|
|
||||||
|
for (var d of this.context.devices) {
|
||||||
|
sensors.push(...d.sensors)
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.getItemsWithStatus(sensors, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
getDeviceSeries() {
|
||||||
|
var online = this.getDevicesWithStatus("Online").length;
|
||||||
|
var offline = this.getDevicesWithStatus("Offline").length;
|
||||||
|
var error = this.getDevicesWithStatus("Error").length;
|
||||||
|
|
||||||
|
return [online, offline, error]
|
||||||
|
}
|
||||||
|
|
||||||
|
getSensorSeries() {
|
||||||
|
var online = this.getSensorsWithStatus("Online").length;
|
||||||
|
var offline = this.getSensorsWithStatus("Offline").length;
|
||||||
|
var unknown = this.getSensorsWithStatus("Unknown").length;
|
||||||
|
|
||||||
|
return [online, offline, unknown]
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSeries() {
|
||||||
|
this.setState({
|
||||||
|
deviceSeries: this.getDeviceSeries(),
|
||||||
|
sensorSeries: this.getSensorSeries()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateDynamic = () => {
|
||||||
|
const secondAgo = new Date();
|
||||||
|
secondAgo.setMilliseconds(0);
|
||||||
|
const minuteAgo = new Date( Date.now() - 1000 * 60 );
|
||||||
|
const hourAgo = new Date( Date.now() - 1000 * 60 * 60 );
|
||||||
|
|
||||||
|
const minuteDevicePoints = {};
|
||||||
|
const hourDevicePoints = {};
|
||||||
|
const barDevicePoints = {};
|
||||||
|
const linePoints = {};
|
||||||
|
|
||||||
|
for (var d of this.context.devices) {
|
||||||
|
minuteDevicePoints[d.id] = Array(60).fill(0);
|
||||||
|
hourDevicePoints[d.id] = Array(60).fill(0);
|
||||||
|
barDevicePoints[d.id] = Array(3).fill(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const processMethod = (items, index) => {
|
||||||
|
const p = items[index];
|
||||||
|
if (p.date > minuteAgo) {
|
||||||
|
var seconds = Math.floor((p.date.getTime() - minuteAgo.getTime()) / 1000);
|
||||||
|
var oldProb = minuteDevicePoints[p.deviceId][seconds];
|
||||||
|
if (oldProb < p.prob) {
|
||||||
|
minuteDevicePoints[p.deviceId][seconds] = p.prob;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p.date > hourAgo) {
|
||||||
|
var minutes = Math.floor((p.date.getTime() - hourAgo.getTime()) / (1000 * 60));
|
||||||
|
var oldProb = hourDevicePoints[p.deviceId][minutes];
|
||||||
|
if (oldProb < p.prob) {
|
||||||
|
hourDevicePoints[p.deviceId][minutes] = p.prob;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p.prob > 0.5 && p.prob <= 0.7) {
|
||||||
|
barDevicePoints[p.deviceId][0] += 1;
|
||||||
|
}
|
||||||
|
if (p.prob > 0.7 && p.prob <= 0.9) {
|
||||||
|
barDevicePoints[p.deviceId][1] += 1;
|
||||||
|
}
|
||||||
|
if (p.prob > 0.9) {
|
||||||
|
barDevicePoints[p.deviceId][2] += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p.date < secondAgo) {
|
||||||
|
var shortDate = p.date.toUTCString();
|
||||||
|
var point = linePoints[shortDate];
|
||||||
|
if (point === undefined) {
|
||||||
|
linePoints[shortDate] = 1;
|
||||||
|
} else {
|
||||||
|
linePoints[shortDate] += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const finishMethod = () => {
|
||||||
|
const minuteHeatmapSeries = [];
|
||||||
|
|
||||||
|
var i = 0;
|
||||||
|
for (var p in minuteDevicePoints) {
|
||||||
|
minuteHeatmapSeries.push({
|
||||||
|
name: "Device " + i,
|
||||||
|
data: minuteDevicePoints[p].map((value, index) => ({
|
||||||
|
x: new Date( Date.now() - (60 - index) * 1000 ).toLocaleTimeString('hu-HU'),
|
||||||
|
y: value
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
i++;
|
||||||
|
};
|
||||||
|
|
||||||
|
const hourHeatmapSeries = [];
|
||||||
|
|
||||||
|
var i = 0;
|
||||||
|
for (var p in hourDevicePoints) {
|
||||||
|
hourHeatmapSeries.push({
|
||||||
|
name: "Device " + i,
|
||||||
|
data: hourDevicePoints[p].map((value, index) => ({
|
||||||
|
x: new Date( Date.now() - (60 - index) * 1000 * 60 ).toLocaleTimeString('hu-HU').substring(0, 5),
|
||||||
|
y: value
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
i++;
|
||||||
|
};
|
||||||
|
|
||||||
|
const barSeries = [];
|
||||||
|
|
||||||
|
const getCount = column => {
|
||||||
|
var counts = [];
|
||||||
|
|
||||||
|
for (var p in barDevicePoints) {
|
||||||
|
counts.unshift(barDevicePoints[p][column]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return counts;
|
||||||
|
};
|
||||||
|
|
||||||
|
barSeries.push({
|
||||||
|
name: "Prob > 0.5",
|
||||||
|
data: getCount(0),
|
||||||
|
});
|
||||||
|
barSeries.push({
|
||||||
|
name: "Prob > 0.7",
|
||||||
|
data: getCount(1),
|
||||||
|
});
|
||||||
|
barSeries.push({
|
||||||
|
name: "Prob > 0.9",
|
||||||
|
data: getCount(2),
|
||||||
|
});
|
||||||
|
|
||||||
|
const lineSeries = [{name: "message/sec", data: []}];
|
||||||
|
for (var m in linePoints) {
|
||||||
|
lineSeries[0].data.push({
|
||||||
|
x: new Date(m).getTime(),
|
||||||
|
y: linePoints[m],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const getBarCategories = () => {
|
||||||
|
const categories = [];
|
||||||
|
|
||||||
|
for (var i = this.context.devices.length - 1; i >= 0; i--) {
|
||||||
|
categories.push("Device " + i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return categories;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
heatmapSecondsSeries: minuteHeatmapSeries,
|
||||||
|
heatmapMinutesSeries: hourHeatmapSeries,
|
||||||
|
barSeries: barSeries,
|
||||||
|
barCategories: getBarCategories(),
|
||||||
|
lineSeries: lineSeries,
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(this.updateDynamic, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
const processHeatmapItem = processMethod.bind(this);
|
||||||
|
const onFinished = finishMethod.bind(this)
|
||||||
|
|
||||||
|
this.performTask(this.context.heatmapPoints, Math.ceil(this.context.heatmapPoints.length / 100), 10,
|
||||||
|
processHeatmapItem, onFinished);
|
||||||
|
}
|
||||||
|
|
||||||
|
performTask(items, numToProcess, wait, processItem, onFinished) {
|
||||||
|
var pos = 0;
|
||||||
|
// This is run once for every numToProcess items.
|
||||||
|
function iteration() {
|
||||||
|
// Calculate last position.
|
||||||
|
var j = Math.min(pos + numToProcess, items.length);
|
||||||
|
// Start at current position and loop to last position.
|
||||||
|
for (var i = pos; i < j; i++) {
|
||||||
|
processItem(items, i);
|
||||||
|
}
|
||||||
|
// Increment current position.
|
||||||
|
pos += numToProcess;
|
||||||
|
// Only continue if there are more items to process.
|
||||||
|
if (pos < items.length)
|
||||||
|
setTimeout(iteration, wait); // Wait 10 ms to let the UI update.
|
||||||
|
else
|
||||||
|
onFinished();
|
||||||
|
}
|
||||||
|
iteration();
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { classes } = this.props;
|
const { classes } = this.props;
|
||||||
|
|
||||||
const Services = this.state.services.map((info, index) => (
|
|
||||||
<ServiceInfoComponent key={index} isAdmin={this.props.isAdmin} info={info} service={this.state.service} />
|
|
||||||
));
|
|
||||||
|
|
||||||
const Skeletons = this.state.serviceCount.map((i, index) => (
|
|
||||||
<ServiceInfoSkeleton key={index} />
|
|
||||||
));
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box className={classes.root}>
|
<Box className={classes.root}>
|
||||||
<Grid container spacing={3}>
|
<Grid container spacing={3}>
|
||||||
<Grid item xs={8}>
|
<Grid item xs={12}>
|
||||||
<Paper className={classes.paper} square>
|
<Services isAdmin={this.props.isAdmin}/>
|
||||||
<Grid container
|
|
||||||
spacing={0}
|
|
||||||
direction="row"
|
|
||||||
justify="center"
|
|
||||||
alignItems="center">
|
|
||||||
<Grid item>
|
|
||||||
<Typography className={classes.typo}>Services</Typography>
|
|
||||||
</Grid>
|
|
||||||
<Grid item>
|
|
||||||
{this.props.isAdmin ?
|
|
||||||
<IconButton color="primary" onClick={() => this.setState({ isDialogOpen: true })}>
|
|
||||||
<AddBox fontSize="large" />
|
|
||||||
</IconButton>
|
|
||||||
: null
|
|
||||||
}
|
|
||||||
</Grid>
|
|
||||||
<Grid item>
|
|
||||||
<IconButton color="primary" onClick={this.handleDevicesUpdated}>
|
|
||||||
<Refresh fontSize="large" />
|
|
||||||
</IconButton>
|
|
||||||
</Grid>
|
|
||||||
<AddNewDialog open={this.state.isDialogOpen} handleClose={() => this.setState({ isDialogOpen: false })} handleAdd={this.addDevice}/>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
|
<Grid item xs={6}>
|
||||||
|
<Paper className={classes.paper}>
|
||||||
|
<DonutChart totalLabel="Devices" series={this.state.deviceSeries}/>
|
||||||
</Paper>
|
</Paper>
|
||||||
{this.state.isLoading ? Skeletons : Services}
|
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={4}>
|
<Grid item xs={6}>
|
||||||
<Paper className={classes.paper} />
|
<Paper className={classes.paper}>
|
||||||
|
<DonutChart totalLabel="Sensors" series={this.state.sensorSeries}/>
|
||||||
|
</Paper>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<Paper className={classes.paper} />
|
<Paper className={classes.paper}>
|
||||||
|
<HeatmapChart label="Highest probability per second by devices" series={this.state.heatmapSecondsSeries}/>
|
||||||
|
</Paper>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<Paper className={classes.paper}>
|
||||||
|
<HeatmapChart label="Highest probability per minute by devices" series={this.state.heatmapMinutesSeries}/>
|
||||||
|
</Paper>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={6}>
|
||||||
|
<Paper className={classes.paper}>
|
||||||
|
<BarChart label="# of messages by devices" series={this.state.barSeries} categories={this.state.barCategories}/>
|
||||||
|
</Paper>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={6}>
|
||||||
|
<Paper className={classes.paper}>
|
||||||
|
<LineChart label="# of messages per second" series={this.state.lineSeries}/>
|
||||||
|
</Paper>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Box>
|
</Box>
|
||||||
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,92 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import Chart from 'react-apexcharts';
|
||||||
|
import { blueGrey, green, red, orange, amber } from '@material-ui/core/colors';
|
||||||
|
|
||||||
|
export class BarChart extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
options: {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps) {
|
||||||
|
if (prevProps.categories !== this.props.categories) {
|
||||||
|
this.setState({options: {
|
||||||
|
chart: {
|
||||||
|
stacked: true,
|
||||||
|
animations: {
|
||||||
|
enabled: true,
|
||||||
|
easing: 'linear',
|
||||||
|
speed: 250,
|
||||||
|
animateGradually: {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
dynamicAnimation: {
|
||||||
|
enabled: true,
|
||||||
|
speed: 250
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plotOptions: {
|
||||||
|
bar: {
|
||||||
|
horizontal: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
colors: [blueGrey[500], blueGrey[700], blueGrey[900]],
|
||||||
|
stroke: {
|
||||||
|
width: 1,
|
||||||
|
colors: ['#fff']
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
text: this.props.label,
|
||||||
|
style: {
|
||||||
|
fontSize: '22px',
|
||||||
|
fontWeight: 600,
|
||||||
|
fontFamily: 'Helvetica, Arial, sans-serif',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
xaxis: {
|
||||||
|
categories: this.props.categories,
|
||||||
|
labels: {
|
||||||
|
formatter: function (val) {
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
yaxis: {
|
||||||
|
title: {
|
||||||
|
text: undefined
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
y: {
|
||||||
|
formatter: function (val) {
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fill: {
|
||||||
|
opacity: 1
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
position: 'top',
|
||||||
|
}
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<Chart
|
||||||
|
options={this.state.options}
|
||||||
|
series={this.props.series}
|
||||||
|
type="bar"
|
||||||
|
height={600}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BarChart;
|
@ -0,0 +1,67 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import Chart from 'react-apexcharts';
|
||||||
|
import { blueGrey, green, red } from '@material-ui/core/colors';
|
||||||
|
|
||||||
|
|
||||||
|
export class DonutChart extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
options: {
|
||||||
|
legend: {
|
||||||
|
fontSize: '18px',
|
||||||
|
},
|
||||||
|
plotOptions: {
|
||||||
|
pie: {
|
||||||
|
startAngle: 0,
|
||||||
|
expandOnClick: false,
|
||||||
|
offsetX: 0,
|
||||||
|
offsetY: 0,
|
||||||
|
customScale: 1,
|
||||||
|
dataLabels: {
|
||||||
|
offset: 0,
|
||||||
|
minAngleToShowLabel: 10
|
||||||
|
},
|
||||||
|
donut: {
|
||||||
|
size: '65%',
|
||||||
|
background: 'transparent',
|
||||||
|
labels: {
|
||||||
|
show: true,
|
||||||
|
total: {
|
||||||
|
show: true,
|
||||||
|
showAlways: true,
|
||||||
|
label: props.totalLabel,
|
||||||
|
fontSize: '22px',
|
||||||
|
fontFamily: 'Helvetica, Arial, sans-serif',
|
||||||
|
fontWeight: 600,
|
||||||
|
color: '#373d3f',
|
||||||
|
formatter: function (w) {
|
||||||
|
return w.globals.seriesTotals.reduce((a, b) => {
|
||||||
|
return a + b
|
||||||
|
}, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dataLabels: {
|
||||||
|
enabled: false
|
||||||
|
},
|
||||||
|
colors: [green[500], blueGrey[500], red[500]],
|
||||||
|
labels: ['Online', 'Offline', 'Error / Unknown']},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<Chart
|
||||||
|
options={this.state.options}
|
||||||
|
series={this.props.series}
|
||||||
|
type="donut"/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DonutChart;
|
@ -0,0 +1,40 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import Chart from 'react-apexcharts';
|
||||||
|
import { blueGrey, green, red } from '@material-ui/core/colors';
|
||||||
|
|
||||||
|
export class HeatmapChart extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
options: {
|
||||||
|
dataLabels: {
|
||||||
|
enabled: false
|
||||||
|
},
|
||||||
|
colors: [blueGrey[900]],
|
||||||
|
title: {
|
||||||
|
text: props.label,
|
||||||
|
style: {
|
||||||
|
fontSize: '22px',
|
||||||
|
fontWeight: 600,
|
||||||
|
fontFamily: 'Helvetica, Arial, sans-serif',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<Chart
|
||||||
|
options={this.state.options}
|
||||||
|
series={this.props.series}
|
||||||
|
type="heatmap"
|
||||||
|
height={600}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default HeatmapChart
|
@ -0,0 +1,74 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import Chart from 'react-apexcharts';
|
||||||
|
import { blueGrey, green, red } from '@material-ui/core/colors';
|
||||||
|
|
||||||
|
export class LineChart extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
options: {
|
||||||
|
chart: {
|
||||||
|
animations: {
|
||||||
|
enabled: true,
|
||||||
|
easing: 'linear',
|
||||||
|
speed: 250,
|
||||||
|
animateGradually: {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
dynamicAnimation: {
|
||||||
|
enabled: true,
|
||||||
|
speed: 250
|
||||||
|
}
|
||||||
|
},
|
||||||
|
zoom: {
|
||||||
|
enabled: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
colors: [blueGrey[900]],
|
||||||
|
dataLabels: {
|
||||||
|
enabled: false
|
||||||
|
},
|
||||||
|
stroke: {
|
||||||
|
curve: 'straight'
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
text: this.props.label,
|
||||||
|
align: 'left',
|
||||||
|
style: {
|
||||||
|
fontSize: '22px',
|
||||||
|
fontWeight: 600,
|
||||||
|
fontFamily: 'Helvetica, Arial, sans-serif',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
row: {
|
||||||
|
colors: ['#f3f3f3', 'transparent'], // takes an array which will be repeated on columns
|
||||||
|
opacity: 0.5
|
||||||
|
},
|
||||||
|
},
|
||||||
|
xaxis: {
|
||||||
|
type: 'datetime',
|
||||||
|
labels: {
|
||||||
|
formatter: function (val) {
|
||||||
|
return new Date(val).toLocaleTimeString('hu-HU');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<Chart
|
||||||
|
options={this.state.options}
|
||||||
|
series={this.props.series}
|
||||||
|
type="line"
|
||||||
|
height={600}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LineChart
|
@ -22,13 +22,13 @@ var __extends = (this && this.__extends) || (function () {
|
|||||||
})();
|
})();
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.ApiException = exports.HttpStatusCode = exports.ServiceRequest = exports.ServiceInfo = void 0;
|
exports.ApiException = exports.HttpStatusCode = exports.ServiceRequest = exports.ServiceInfo = void 0;
|
||||||
var DashboardService = /** @class */ (function () {
|
var ServiceInfoService = /** @class */ (function () {
|
||||||
function DashboardService(baseUrl, http) {
|
function ServiceInfoService(baseUrl, http) {
|
||||||
this.jsonParseReviver = undefined;
|
this.jsonParseReviver = undefined;
|
||||||
this.http = http ? http : window;
|
this.http = http ? http : window;
|
||||||
this.baseUrl = baseUrl !== undefined && baseUrl !== null ? baseUrl : "";
|
this.baseUrl = baseUrl !== undefined && baseUrl !== null ? baseUrl : "";
|
||||||
}
|
}
|
||||||
DashboardService.prototype.getCount = function () {
|
ServiceInfoService.prototype.getCount = function () {
|
||||||
var _this = this;
|
var _this = this;
|
||||||
var url_ = this.baseUrl + "/api/Services/count";
|
var url_ = this.baseUrl + "/api/Services/count";
|
||||||
url_ = url_.replace(/[?&]$/, "");
|
url_ = url_.replace(/[?&]$/, "");
|
||||||
@ -43,7 +43,7 @@ var DashboardService = /** @class */ (function () {
|
|||||||
return _this.processGetCount(_response);
|
return _this.processGetCount(_response);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
DashboardService.prototype.processGetCount = function (response) {
|
ServiceInfoService.prototype.processGetCount = function (response) {
|
||||||
var _this = this;
|
var _this = this;
|
||||||
var status = response.status;
|
var status = response.status;
|
||||||
var _headers = {};
|
var _headers = {};
|
||||||
@ -66,7 +66,7 @@ var DashboardService = /** @class */ (function () {
|
|||||||
}
|
}
|
||||||
return Promise.resolve(null);
|
return Promise.resolve(null);
|
||||||
};
|
};
|
||||||
DashboardService.prototype.get = function () {
|
ServiceInfoService.prototype.get = function () {
|
||||||
var _this = this;
|
var _this = this;
|
||||||
var url_ = this.baseUrl + "/api/Services";
|
var url_ = this.baseUrl + "/api/Services";
|
||||||
url_ = url_.replace(/[?&]$/, "");
|
url_ = url_.replace(/[?&]$/, "");
|
||||||
@ -81,7 +81,7 @@ var DashboardService = /** @class */ (function () {
|
|||||||
return _this.processGet(_response);
|
return _this.processGet(_response);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
DashboardService.prototype.processGet = function (response) {
|
ServiceInfoService.prototype.processGet = function (response) {
|
||||||
var _this = this;
|
var _this = this;
|
||||||
var status = response.status;
|
var status = response.status;
|
||||||
var _headers = {};
|
var _headers = {};
|
||||||
@ -110,7 +110,7 @@ var DashboardService = /** @class */ (function () {
|
|||||||
}
|
}
|
||||||
return Promise.resolve(null);
|
return Promise.resolve(null);
|
||||||
};
|
};
|
||||||
DashboardService.prototype.post = function (request) {
|
ServiceInfoService.prototype.post = function (request) {
|
||||||
var _this = this;
|
var _this = this;
|
||||||
var url_ = this.baseUrl + "/api/Services";
|
var url_ = this.baseUrl + "/api/Services";
|
||||||
url_ = url_.replace(/[?&]$/, "");
|
url_ = url_.replace(/[?&]$/, "");
|
||||||
@ -128,7 +128,7 @@ var DashboardService = /** @class */ (function () {
|
|||||||
return _this.processPost(_response);
|
return _this.processPost(_response);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
DashboardService.prototype.processPost = function (response) {
|
ServiceInfoService.prototype.processPost = function (response) {
|
||||||
var _this = this;
|
var _this = this;
|
||||||
var status = response.status;
|
var status = response.status;
|
||||||
var _headers = {};
|
var _headers = {};
|
||||||
@ -151,7 +151,7 @@ var DashboardService = /** @class */ (function () {
|
|||||||
}
|
}
|
||||||
return Promise.resolve(null);
|
return Promise.resolve(null);
|
||||||
};
|
};
|
||||||
DashboardService.prototype.put = function (request) {
|
ServiceInfoService.prototype.put = function (request) {
|
||||||
var _this = this;
|
var _this = this;
|
||||||
var url_ = this.baseUrl + "/api/Services";
|
var url_ = this.baseUrl + "/api/Services";
|
||||||
url_ = url_.replace(/[?&]$/, "");
|
url_ = url_.replace(/[?&]$/, "");
|
||||||
@ -168,7 +168,7 @@ var DashboardService = /** @class */ (function () {
|
|||||||
return _this.processPut(_response);
|
return _this.processPut(_response);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
DashboardService.prototype.processPut = function (response) {
|
ServiceInfoService.prototype.processPut = function (response) {
|
||||||
var status = response.status;
|
var status = response.status;
|
||||||
var _headers = {};
|
var _headers = {};
|
||||||
if (response.headers && response.headers.forEach) {
|
if (response.headers && response.headers.forEach) {
|
||||||
@ -187,7 +187,7 @@ var DashboardService = /** @class */ (function () {
|
|||||||
}
|
}
|
||||||
return Promise.resolve(null);
|
return Promise.resolve(null);
|
||||||
};
|
};
|
||||||
DashboardService.prototype.delete = function (id) {
|
ServiceInfoService.prototype.delete = function (id) {
|
||||||
var _this = this;
|
var _this = this;
|
||||||
var url_ = this.baseUrl + "/api/Services/{id}";
|
var url_ = this.baseUrl + "/api/Services/{id}";
|
||||||
if (id === undefined || id === null)
|
if (id === undefined || id === null)
|
||||||
@ -204,7 +204,7 @@ var DashboardService = /** @class */ (function () {
|
|||||||
return _this.processDelete(_response);
|
return _this.processDelete(_response);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
DashboardService.prototype.processDelete = function (response) {
|
ServiceInfoService.prototype.processDelete = function (response) {
|
||||||
var status = response.status;
|
var status = response.status;
|
||||||
var _headers = {};
|
var _headers = {};
|
||||||
if (response.headers && response.headers.forEach) {
|
if (response.headers && response.headers.forEach) {
|
||||||
@ -223,9 +223,9 @@ var DashboardService = /** @class */ (function () {
|
|||||||
}
|
}
|
||||||
return Promise.resolve(null);
|
return Promise.resolve(null);
|
||||||
};
|
};
|
||||||
return DashboardService;
|
return ServiceInfoService;
|
||||||
}());
|
}());
|
||||||
exports.default = DashboardService;
|
exports.default = ServiceInfoService;
|
||||||
var ServiceInfo = /** @class */ (function () {
|
var ServiceInfo = /** @class */ (function () {
|
||||||
function ServiceInfo(data) {
|
function ServiceInfo(data) {
|
||||||
if (data) {
|
if (data) {
|
||||||
@ -383,4 +383,4 @@ function throwException(message, status, response, headers, result) {
|
|||||||
else
|
else
|
||||||
throw new ApiException(message, status, response, headers, null);
|
throw new ApiException(message, status, response, headers, null);
|
||||||
}
|
}
|
||||||
//# sourceMappingURL=DashboardService.js.map
|
//# sourceMappingURL=SystemInfoService.js.map
|
File diff suppressed because one or more lines are too long
@ -7,7 +7,7 @@
|
|||||||
//----------------------
|
//----------------------
|
||||||
// ReSharper disable InconsistentNaming
|
// ReSharper disable InconsistentNaming
|
||||||
|
|
||||||
export default class DashboardService {
|
export default class ServiceInfoService {
|
||||||
private http: { fetch(url: RequestInfo, init?: RequestInit): Promise<Response> };
|
private http: { fetch(url: RequestInfo, init?: RequestInit): Promise<Response> };
|
||||||
private baseUrl: string;
|
private baseUrl: string;
|
||||||
protected jsonParseReviver: ((key: string, value: any) => any) | undefined = undefined;
|
protected jsonParseReviver: ((key: string, value: any) => any) | undefined = undefined;
|
@ -0,0 +1,146 @@
|
|||||||
|
import { Grid, IconButton, Paper, Typography } from '@material-ui/core';
|
||||||
|
import { blueGrey } from '@material-ui/core/colors';
|
||||||
|
import { AddBox, Refresh } from '@material-ui/icons/';
|
||||||
|
import { withStyles } from '@material-ui/styles';
|
||||||
|
import { HubConnectionBuilder } from '@microsoft/signalr';
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import AddNewDialog from './AddNewDialog';
|
||||||
|
import ServiceInfoService, { ServiceRequest } from './ServiceInfoService';
|
||||||
|
import ServiceInfoComponent from './ServiceInfoComponent';
|
||||||
|
import ServiceInfoSkeleton from './ServiceInfoSkeleton';
|
||||||
|
|
||||||
|
const styles = theme => ({
|
||||||
|
typo: {
|
||||||
|
fontSize: theme.typography.pxToRem(20),
|
||||||
|
fontWeight: theme.typography.fontWeightRegular,
|
||||||
|
},
|
||||||
|
paper: {
|
||||||
|
backgroundColor: blueGrey[50],
|
||||||
|
height: '60px',
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const hub_url = "/hubs/services";
|
||||||
|
const notify_method_name = "NotifyUpdatedAsync";
|
||||||
|
|
||||||
|
class Services extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
hubConnection: null,
|
||||||
|
isDialogOpen: false,
|
||||||
|
isLoading: false,
|
||||||
|
service: new ServiceInfoService(),
|
||||||
|
services: [],
|
||||||
|
serviceCount: [1, 2, 3],
|
||||||
|
}
|
||||||
|
|
||||||
|
this.handleDevicesUpdated = this.handleDevicesUpdated.bind(this);
|
||||||
|
this.addDevice = this.addDevice.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleDevicesUpdated() {
|
||||||
|
this.setState({ isLoading: true });
|
||||||
|
|
||||||
|
this.state.service.getCount().then(result => {
|
||||||
|
const updatedCount = [];
|
||||||
|
for (var i = 0; i < result; i++) {
|
||||||
|
updatedCount.push(i);
|
||||||
|
}
|
||||||
|
this.setState({ serviceCount: updatedCount });
|
||||||
|
}).catch(ex => {
|
||||||
|
console.log(ex);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.state.service.get().then(result => {
|
||||||
|
const updatedServices = [];
|
||||||
|
for (var s of result) {
|
||||||
|
updatedServices.push(s);
|
||||||
|
}
|
||||||
|
this.setState({ services: updatedServices });
|
||||||
|
}).catch(ex => {
|
||||||
|
console.log(ex);
|
||||||
|
}).finally(() => this.setState({ isLoading: false }));
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.handleDevicesUpdated();
|
||||||
|
const newConnection = new HubConnectionBuilder()
|
||||||
|
.withUrl(hub_url)
|
||||||
|
.withAutomaticReconnect()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
this.setState({ hubConnection: newConnection });
|
||||||
|
|
||||||
|
newConnection.start()
|
||||||
|
.then(_ => {
|
||||||
|
console.log('Services hub Connected!');
|
||||||
|
newConnection.on(notify_method_name, () => this.handleDevicesUpdated());
|
||||||
|
}).catch(e => console.log('Services hub Connection failed: ', e));
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
if (this.state.hubConnection != null) {
|
||||||
|
this.state.hubConnection.off(notify_method_name);
|
||||||
|
console.log('Services hub Disconnected!');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addDevice(name, url) {
|
||||||
|
this.setState({ isDialogOpen: false });
|
||||||
|
let request = new ServiceRequest();
|
||||||
|
request.id = 0;
|
||||||
|
request.name = name;
|
||||||
|
request.uri = url;
|
||||||
|
|
||||||
|
this.state.service.post(request).catch(ex => {
|
||||||
|
console.log(ex);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { classes } = this.props;
|
||||||
|
|
||||||
|
const ServiceComponents = this.state.services.map((info, index) => (
|
||||||
|
<ServiceInfoComponent key={index} isAdmin={this.props.isAdmin} info={info} service={this.state.service} />
|
||||||
|
));
|
||||||
|
|
||||||
|
const Skeletons = this.state.serviceCount.map((i, index) => (
|
||||||
|
<ServiceInfoSkeleton key={index} />
|
||||||
|
));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<Paper className={classes.paper} square>
|
||||||
|
<Grid container
|
||||||
|
spacing={0}
|
||||||
|
direction="row"
|
||||||
|
justify="center"
|
||||||
|
alignItems="center">
|
||||||
|
<Grid item>
|
||||||
|
<Typography className={classes.typo}>Services</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item>
|
||||||
|
{this.props.isAdmin ?
|
||||||
|
<IconButton color="primary" onClick={() => this.setState({ isDialogOpen: true })}>
|
||||||
|
<AddBox fontSize="large" />
|
||||||
|
</IconButton>
|
||||||
|
: null
|
||||||
|
}
|
||||||
|
</Grid>
|
||||||
|
<Grid item>
|
||||||
|
<IconButton color="primary" onClick={this.handleDevicesUpdated}>
|
||||||
|
<Refresh fontSize="large" />
|
||||||
|
</IconButton>
|
||||||
|
</Grid>
|
||||||
|
<AddNewDialog open={this.state.isDialogOpen} handleClose={() => this.setState({ isDialogOpen: false })} handleAdd={this.addDevice} />
|
||||||
|
</Grid>
|
||||||
|
</Paper>
|
||||||
|
{this.state.isLoading ? Skeletons : ServiceComponents}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default withStyles(styles)(Services);
|
@ -63,8 +63,8 @@ class DeviceComponent extends Component {
|
|||||||
if (status == "Online") {
|
if (status == "Online") {
|
||||||
return { color: green[600] };
|
return { color: green[600] };
|
||||||
} else if (status == "Offline") {
|
} else if (status == "Offline") {
|
||||||
return { color: orange[900] };
|
return { color: blueGrey[500] };
|
||||||
} else /* if (device.status == "unknown") */ {
|
} else /* if (device.status == "Unknown" || device.status == "Error") */ {
|
||||||
return { color: red[800] };
|
return { color: red[800] };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,9 +16,9 @@ class DeviceMarker extends Component {
|
|||||||
|
|
||||||
getColor() {
|
getColor() {
|
||||||
const { device } = this.props;
|
const { device } = this.props;
|
||||||
if (device.status == "Online") {
|
if (device.status === "Online") {
|
||||||
return { color: blue[800] };
|
return { color: blue[800] };
|
||||||
} else if (device.status == "Offline") {
|
} else if (device.status === "Offline") {
|
||||||
return { color: yellow[800] };
|
return { color: yellow[800] };
|
||||||
} else /* if (device.status == "unknown") */ {
|
} else /* if (device.status == "unknown") */ {
|
||||||
return { color: red[800] };
|
return { color: red[800] };
|
||||||
|
@ -60,6 +60,7 @@ export default class DevicesContextProvider extends Component {
|
|||||||
}
|
}
|
||||||
service.getall().then(result => {
|
service.getall().then(result => {
|
||||||
this.setState({ devices: result });
|
this.setState({ devices: result });
|
||||||
|
this.invokeHandlers(C.update_all_method_name, null);
|
||||||
}).catch(ex => {
|
}).catch(ex => {
|
||||||
console.log(ex);
|
console.log(ex);
|
||||||
});
|
});
|
||||||
@ -101,7 +102,7 @@ export default class DevicesContextProvider extends Component {
|
|||||||
newConnection.on(C.probability_method_name, (id, date, prob) => {
|
newConnection.on(C.probability_method_name, (id, date, prob) => {
|
||||||
//console.log(method_name + " recieved: [id: " + id + ", date: " + date + ", prob: " + prob + "]");
|
//console.log(method_name + " recieved: [id: " + id + ", date: " + date + ", prob: " + prob + "]");
|
||||||
var device = this.state.devices.filter(function (x) { return x.id === id })[0]
|
var device = this.state.devices.filter(function (x) { return x.id === id })[0]
|
||||||
var newPoint = { lat: device.coordinates.latitude, lng: device.coordinates.longitude, prob: prob, date: date };
|
var newPoint = { deviceId: device.id, lat: device.coordinates.latitude, lng: device.coordinates.longitude, prob: prob, date: new Date(date) };
|
||||||
this.setState({
|
this.setState({
|
||||||
heatmapPoints: [...this.state.heatmapPoints, newPoint]
|
heatmapPoints: [...this.state.heatmapPoints, newPoint]
|
||||||
});
|
});
|
||||||
@ -111,7 +112,6 @@ export default class DevicesContextProvider extends Component {
|
|||||||
|
|
||||||
newConnection.on(C.update_all_method_name, () => {
|
newConnection.on(C.update_all_method_name, () => {
|
||||||
this.updateAllDevicesInternal(service);
|
this.updateAllDevicesInternal(service);
|
||||||
this.invokeHandlers(C.update_all_method_name, null);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
newConnection.on(C.update_method_name, (id) => this.updateDeviceInternal(id, service));
|
newConnection.on(C.update_method_name, (id) => this.updateDeviceInternal(id, service));
|
||||||
|
Reference in New Issue
Block a user