mirror of
https://github.com/optim-enterprises-bv/OptimCloud-gw-ui.git
synced 2025-10-29 17:32:20 +00:00
Merge pull request #100 from stephb9959/main
[WIFI-10515] Crash fix when receiving corrupted statistics
This commit is contained in:
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "ucentral-client",
|
||||
"version": "2.7.0(0)",
|
||||
"version": "2.7.0(1)",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "ucentral-client",
|
||||
"version": "2.7.0(0)",
|
||||
"version": "2.7.0(1)",
|
||||
"dependencies": {
|
||||
"@coreui/coreui": "^3.4.0",
|
||||
"@coreui/icons": "^2.0.1",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ucentral-client",
|
||||
"version": "2.7.0(0)",
|
||||
"version": "2.7.0(1)",
|
||||
"dependencies": {
|
||||
"@coreui/coreui": "^3.4.0",
|
||||
"@coreui/icons": "^2.0.1",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import React, { useState, useEffect, useMemo } from 'react';
|
||||
import { CButton, CModal, CModalHeader, CModalBody, CModalTitle, CPopover } from '@coreui/react';
|
||||
import CIcon from '@coreui/icons-react';
|
||||
import { cilX } from '@coreui/icons';
|
||||
@@ -32,6 +32,17 @@ const LatestStatisticsModal = ({ show, toggle }) => {
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
const latestStatsString = useMemo(() => {
|
||||
if (latestStats) {
|
||||
try {
|
||||
return JSON.stringify(latestStats, null, 2);
|
||||
} catch (e) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}, [latestStats]);
|
||||
|
||||
useEffect(() => {
|
||||
if (show) {
|
||||
getLatestStats();
|
||||
@@ -52,13 +63,9 @@ const LatestStatisticsModal = ({ show, toggle }) => {
|
||||
</CModalHeader>
|
||||
<CModalBody>
|
||||
<div style={{ textAlign: 'right' }}>
|
||||
<CopyToClipboardButton
|
||||
t={t}
|
||||
size="lg"
|
||||
content={JSON.stringify(latestStats ?? {}, null, 4)}
|
||||
/>
|
||||
<CopyToClipboardButton t={t} size="lg" content={latestStatsString} />
|
||||
</div>
|
||||
<pre className="ignore">{JSON.stringify(latestStats, null, 2)}</pre>
|
||||
<pre className="ignore">{latestStatsString}</pre>
|
||||
</CModalBody>
|
||||
</CModal>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { CSpinner } from '@coreui/react';
|
||||
import { CSpinner, CAlert } from '@coreui/react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { v4 as createUuid } from 'uuid';
|
||||
import axiosInstance from 'utils/axiosInstance';
|
||||
@@ -23,230 +23,243 @@ const StatisticsChartList = ({ deviceSerialNumber, setOptions, section, time })
|
||||
memory: [],
|
||||
settings: {},
|
||||
});
|
||||
const [error, setError] = useState(false);
|
||||
|
||||
const transformIntoDataset = (data) => {
|
||||
let sortedData = data.sort((a, b) => {
|
||||
if (a.recorded > b.recorded) return 1;
|
||||
if (b.recorded > a.recorded) return -1;
|
||||
return 0;
|
||||
});
|
||||
try {
|
||||
let sortedData = data.sort((a, b) => {
|
||||
if (a.recorded > b.recorded) return 1;
|
||||
if (b.recorded > a.recorded) return -1;
|
||||
return 0;
|
||||
});
|
||||
|
||||
const dataLength = sortedData.length;
|
||||
if (dataLength > 1000 && dataLength < 3000) {
|
||||
sortedData = sortedData.filter((dat, index) => index % 4 === 0);
|
||||
} else if (dataLength >= 3000 && dataLength < 5000) {
|
||||
sortedData = sortedData.filter((dat, index) => index % 8 === 0);
|
||||
} else if (dataLength >= 5000 && dataLength < 7000) {
|
||||
sortedData = sortedData.filter((dat, index) => index % 12 === 0);
|
||||
} else if (dataLength > 7000) {
|
||||
sortedData = sortedData.filter((dat, index) => index % 20 === 0);
|
||||
}
|
||||
const dataLength = sortedData.length;
|
||||
if (dataLength > 1000 && dataLength < 3000) {
|
||||
sortedData = sortedData.filter((dat, index) => index % 4 === 0);
|
||||
} else if (dataLength >= 3000 && dataLength < 5000) {
|
||||
sortedData = sortedData.filter((dat, index) => index % 8 === 0);
|
||||
} else if (dataLength >= 5000 && dataLength < 7000) {
|
||||
sortedData = sortedData.filter((dat, index) => index % 12 === 0);
|
||||
} else if (dataLength > 7000) {
|
||||
sortedData = sortedData.filter((dat, index) => index % 20 === 0);
|
||||
}
|
||||
|
||||
// Looping through data to build our memory graph data
|
||||
const memoryUsed = [
|
||||
{
|
||||
titleName: t('statistics.memory'),
|
||||
name: 'Used',
|
||||
backgroundColor: 'rgb(228,102,81,0.9)',
|
||||
data: [],
|
||||
fill: true,
|
||||
},
|
||||
{
|
||||
titleName: t('statistics.memory'),
|
||||
name: 'Buffered',
|
||||
backgroundColor: 'rgb(228,102,81,0.9)',
|
||||
data: [],
|
||||
fill: true,
|
||||
},
|
||||
{
|
||||
titleName: t('statistics.memory'),
|
||||
name: 'Cached',
|
||||
backgroundColor: 'rgb(228,102,81,0.9)',
|
||||
data: [],
|
||||
fill: true,
|
||||
},
|
||||
];
|
||||
// Looping through data to build our memory graph data
|
||||
const memoryUsed = [
|
||||
{
|
||||
titleName: t('statistics.memory'),
|
||||
name: 'Used',
|
||||
backgroundColor: 'rgb(228,102,81,0.9)',
|
||||
data: [],
|
||||
fill: true,
|
||||
},
|
||||
{
|
||||
titleName: t('statistics.memory'),
|
||||
name: 'Buffered',
|
||||
backgroundColor: 'rgb(228,102,81,0.9)',
|
||||
data: [],
|
||||
fill: true,
|
||||
},
|
||||
{
|
||||
titleName: t('statistics.memory'),
|
||||
name: 'Cached',
|
||||
backgroundColor: 'rgb(228,102,81,0.9)',
|
||||
data: [],
|
||||
fill: true,
|
||||
},
|
||||
];
|
||||
|
||||
for (const log of sortedData) {
|
||||
memoryUsed[0].data.push(
|
||||
Math.floor((log.data.unit.memory.total - log.data.unit.memory.free) / 1024 / 1024),
|
||||
for (const log of sortedData) {
|
||||
memoryUsed[0].data.push(
|
||||
Math.floor((log.data.unit.memory.total - log.data.unit.memory.free) / 1024 / 1024),
|
||||
);
|
||||
memoryUsed[1].data.push(Math.floor(log.data.unit.memory.buffered / 1024 / 1024));
|
||||
memoryUsed[2].data.push(Math.floor(log.data.unit.memory.cached / 1024 / 1024));
|
||||
}
|
||||
|
||||
const newUsed = memoryUsed[0].data;
|
||||
if (newUsed.length > 0) newUsed.shift();
|
||||
memoryUsed[0].data = newUsed;
|
||||
const newBuff = memoryUsed[1].data;
|
||||
if (newBuff.length > 0) newBuff.shift();
|
||||
memoryUsed[1].data = newBuff;
|
||||
const newCached = memoryUsed[2].data;
|
||||
if (newCached.length > 0) newCached.shift();
|
||||
memoryUsed[2].data = newCached;
|
||||
|
||||
// This dictionary will have a key that is the interface name and a value of it's index in the final array
|
||||
const interfaceTypes = {};
|
||||
const interfaceList = [];
|
||||
const categories = [];
|
||||
let i = 0;
|
||||
const areSameDay = datesSameDay(
|
||||
new Date(sortedData[0].recorded * 1000),
|
||||
new Date(sortedData[sortedData.length - 1].recorded * 1000),
|
||||
);
|
||||
memoryUsed[1].data.push(Math.floor(log.data.unit.memory.buffered / 1024 / 1024));
|
||||
memoryUsed[2].data.push(Math.floor(log.data.unit.memory.cached / 1024 / 1024));
|
||||
}
|
||||
|
||||
const newUsed = memoryUsed[0].data;
|
||||
if (newUsed.length > 0) newUsed.shift();
|
||||
memoryUsed[0].data = newUsed;
|
||||
const newBuff = memoryUsed[1].data;
|
||||
if (newBuff.length > 0) newBuff.shift();
|
||||
memoryUsed[1].data = newBuff;
|
||||
const newCached = memoryUsed[2].data;
|
||||
if (newCached.length > 0) newCached.shift();
|
||||
memoryUsed[2].data = newCached;
|
||||
|
||||
// This dictionary will have a key that is the interface name and a value of it's index in the final array
|
||||
const interfaceTypes = {};
|
||||
const interfaceList = [];
|
||||
const categories = [];
|
||||
let i = 0;
|
||||
const areSameDay = datesSameDay(
|
||||
new Date(sortedData[0].recorded * 1000),
|
||||
new Date(sortedData[sortedData.length - 1].recorded * 1000),
|
||||
);
|
||||
|
||||
// Just building the array for all the interfaces
|
||||
for (const log of sortedData) {
|
||||
categories.push(areSameDay ? unixToTime(log.recorded) : prettyDate(log.recorded));
|
||||
for (const logInterface of log.data.interfaces) {
|
||||
if (interfaceTypes[logInterface.name] === undefined) {
|
||||
interfaceTypes[logInterface.name] = i;
|
||||
interfaceList.push([
|
||||
{
|
||||
titleName: logInterface.name,
|
||||
name: 'Tx',
|
||||
backgroundColor: 'rgb(228,102,81,0.9)',
|
||||
data: [],
|
||||
fill: false,
|
||||
},
|
||||
{
|
||||
titleName: logInterface.name,
|
||||
name: 'Rx',
|
||||
backgroundColor: 'rgb(0,216,255,0.9)',
|
||||
data: [],
|
||||
fill: false,
|
||||
},
|
||||
]);
|
||||
i += 1;
|
||||
// Just building the array for all the interfaces
|
||||
for (const log of sortedData) {
|
||||
categories.push(areSameDay ? unixToTime(log.recorded) : prettyDate(log.recorded));
|
||||
for (const logInterface of log.data.interfaces) {
|
||||
if (interfaceTypes[logInterface.name] === undefined) {
|
||||
interfaceTypes[logInterface.name] = i;
|
||||
interfaceList.push([
|
||||
{
|
||||
titleName: logInterface.name,
|
||||
name: 'Tx',
|
||||
backgroundColor: 'rgb(228,102,81,0.9)',
|
||||
data: [],
|
||||
fill: false,
|
||||
},
|
||||
{
|
||||
titleName: logInterface.name,
|
||||
name: 'Rx',
|
||||
backgroundColor: 'rgb(0,216,255,0.9)',
|
||||
data: [],
|
||||
fill: false,
|
||||
},
|
||||
]);
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Looping through all the data
|
||||
const prevTxObj = {};
|
||||
const prevRxObj = {};
|
||||
for (const log of sortedData) {
|
||||
// Looping through the interfaces of the log
|
||||
const version = log.data.version ?? 0;
|
||||
for (const inter of log.data.interfaces) {
|
||||
if (version > 0) {
|
||||
const prevTx = prevTxObj[inter.name] !== undefined ? prevTxObj[inter.name] : 0;
|
||||
const prevRx = prevTxObj[inter.name] !== undefined ? prevRxObj[inter.name] : 0;
|
||||
const tx = inter.counters ? Math.floor(inter.counters.tx_bytes / 1024) : 0;
|
||||
const rx = inter.counters ? Math.floor(inter.counters.rx_bytes / 1024) : 0;
|
||||
interfaceList[interfaceTypes[inter.name]][0].data.push(Math.max(0, tx - prevTx));
|
||||
interfaceList[interfaceTypes[inter.name]][1].data.push(Math.max(0, rx - prevRx));
|
||||
prevTxObj[inter.name] = tx;
|
||||
prevRxObj[inter.name] = rx;
|
||||
} else {
|
||||
interfaceList[interfaceTypes[inter.name]][0].data.push(
|
||||
inter.counters ? Math.floor(inter.counters.tx_bytes / 1024) : 0,
|
||||
);
|
||||
interfaceList[interfaceTypes[inter.name]][1].data.push(
|
||||
inter.counters ? Math.floor(inter.counters.rx_bytes / 1024) : 0,
|
||||
);
|
||||
// Looping through all the data
|
||||
const prevTxObj = {};
|
||||
const prevRxObj = {};
|
||||
for (const log of sortedData) {
|
||||
// Looping through the interfaces of the log
|
||||
const version = log.data.version ?? 0;
|
||||
for (const inter of log.data.interfaces) {
|
||||
if (version > 0) {
|
||||
const prevTx = prevTxObj[inter.name] !== undefined ? prevTxObj[inter.name] : 0;
|
||||
const prevRx = prevTxObj[inter.name] !== undefined ? prevRxObj[inter.name] : 0;
|
||||
const tx = inter.counters ? Math.floor(inter.counters.tx_bytes / 1024) : 0;
|
||||
const rx = inter.counters ? Math.floor(inter.counters.rx_bytes / 1024) : 0;
|
||||
interfaceList[interfaceTypes[inter.name]][0].data.push(Math.max(0, tx - prevTx));
|
||||
interfaceList[interfaceTypes[inter.name]][1].data.push(Math.max(0, rx - prevRx));
|
||||
prevTxObj[inter.name] = tx;
|
||||
prevRxObj[inter.name] = rx;
|
||||
} else {
|
||||
interfaceList[interfaceTypes[inter.name]][0].data.push(
|
||||
inter.counters ? Math.floor(inter.counters.tx_bytes / 1024) : 0,
|
||||
);
|
||||
interfaceList[interfaceTypes[inter.name]][1].data.push(
|
||||
inter.counters ? Math.floor(inter.counters.rx_bytes / 1024) : 0,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let y = 0; y < interfaceList.length; y += 1) {
|
||||
for (let z = 0; z < interfaceList[y].length; z += 1) {
|
||||
const newArray = interfaceList[y][z].data;
|
||||
if (newArray.length > 0) newArray.shift();
|
||||
interfaceList[y][z].data = newArray;
|
||||
for (let y = 0; y < interfaceList.length; y += 1) {
|
||||
for (let z = 0; z < interfaceList[y].length; z += 1) {
|
||||
const newArray = interfaceList[y][z].data;
|
||||
if (newArray.length > 0) newArray.shift();
|
||||
interfaceList[y][z].data = newArray;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const newCategories = categories;
|
||||
if (newCategories.length > 0) newCategories.shift();
|
||||
const interfaceOptions = {
|
||||
chart: {
|
||||
id: 'chart',
|
||||
},
|
||||
stroke: {
|
||||
curve: 'smooth',
|
||||
},
|
||||
xaxis: {
|
||||
title: {
|
||||
text: 'Time',
|
||||
style: {
|
||||
fontSize: '15px',
|
||||
const newCategories = categories;
|
||||
if (newCategories.length > 0) newCategories.shift();
|
||||
const interfaceOptions = {
|
||||
chart: {
|
||||
id: 'chart',
|
||||
},
|
||||
stroke: {
|
||||
curve: 'smooth',
|
||||
},
|
||||
xaxis: {
|
||||
title: {
|
||||
text: 'Time',
|
||||
style: {
|
||||
fontSize: '15px',
|
||||
},
|
||||
},
|
||||
categories: newCategories,
|
||||
tickAmount: areSameDay ? 15 : 10,
|
||||
},
|
||||
yaxis: {
|
||||
labels: {
|
||||
minWidth: 40,
|
||||
},
|
||||
title: {
|
||||
text: t('statistics.data'),
|
||||
style: {
|
||||
fontSize: '15px',
|
||||
},
|
||||
},
|
||||
},
|
||||
categories: newCategories,
|
||||
tickAmount: areSameDay ? 15 : 10,
|
||||
},
|
||||
yaxis: {
|
||||
labels: {
|
||||
minWidth: 40,
|
||||
legend: {
|
||||
position: 'top',
|
||||
horizontalAlign: 'right',
|
||||
float: true,
|
||||
},
|
||||
title: {
|
||||
text: t('statistics.data'),
|
||||
style: {
|
||||
fontSize: '15px',
|
||||
};
|
||||
|
||||
const memoryOptions = {
|
||||
chart: {
|
||||
id: 'chart',
|
||||
},
|
||||
stroke: {
|
||||
curve: 'smooth',
|
||||
},
|
||||
xaxis: {
|
||||
tickAmount: areSameDay ? 15 : 10,
|
||||
title: {
|
||||
text: 'Time',
|
||||
style: {
|
||||
fontSize: '15px',
|
||||
},
|
||||
},
|
||||
categories,
|
||||
},
|
||||
yaxis: {
|
||||
tickAmount: 5,
|
||||
title: {
|
||||
text: t('statistics.data_mb'),
|
||||
style: {
|
||||
fontSize: '15px',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
position: 'top',
|
||||
horizontalAlign: 'right',
|
||||
float: true,
|
||||
},
|
||||
};
|
||||
|
||||
const memoryOptions = {
|
||||
chart: {
|
||||
id: 'chart',
|
||||
},
|
||||
stroke: {
|
||||
curve: 'smooth',
|
||||
},
|
||||
xaxis: {
|
||||
tickAmount: areSameDay ? 15 : 10,
|
||||
title: {
|
||||
text: 'Time',
|
||||
style: {
|
||||
fontSize: '15px',
|
||||
},
|
||||
legend: {
|
||||
position: 'top',
|
||||
horizontalAlign: 'right',
|
||||
float: true,
|
||||
},
|
||||
categories,
|
||||
},
|
||||
yaxis: {
|
||||
tickAmount: 5,
|
||||
title: {
|
||||
text: t('statistics.data_mb'),
|
||||
style: {
|
||||
fontSize: '15px',
|
||||
},
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
position: 'top',
|
||||
horizontalAlign: 'right',
|
||||
float: true,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const newOptions = {
|
||||
interfaceList,
|
||||
memory: [memoryUsed],
|
||||
interfaceOptions,
|
||||
memoryOptions,
|
||||
start: new Date(sortedData[0].recorded * 1000).toISOString(),
|
||||
end: new Date(sortedData[sortedData.length - 1].recorded * 1000).toISOString(),
|
||||
};
|
||||
const newOptions = {
|
||||
interfaceList,
|
||||
memory: [memoryUsed],
|
||||
interfaceOptions,
|
||||
memoryOptions,
|
||||
start: new Date(sortedData[0].recorded * 1000).toISOString(),
|
||||
end: new Date(sortedData[sortedData.length - 1].recorded * 1000).toISOString(),
|
||||
};
|
||||
|
||||
if (statOptions !== newOptions) {
|
||||
const sectionOptions = newOptions.interfaceList.map((opt) => ({
|
||||
value: opt[0].titleName,
|
||||
label: opt[0].titleName,
|
||||
}));
|
||||
setOptions([...sectionOptions, { value: 'memory', label: t('statistics.memory') }]);
|
||||
setStatOptions({ ...newOptions });
|
||||
if (statOptions !== newOptions) {
|
||||
const sectionOptions = newOptions.interfaceList.map((opt) => ({
|
||||
value: opt[0].titleName,
|
||||
label: opt[0].titleName,
|
||||
}));
|
||||
setOptions([...sectionOptions, { value: 'memory', label: t('statistics.memory') }]);
|
||||
setStatOptions({ ...newOptions });
|
||||
}
|
||||
setError(false);
|
||||
} catch (e) {
|
||||
setError(true);
|
||||
}
|
||||
};
|
||||
|
||||
const getInterface = useCallback(() => {
|
||||
if (error) {
|
||||
return (
|
||||
<CAlert color="danger" style={{ width: '240px' }}>
|
||||
Error while parsing statistics
|
||||
</CAlert>
|
||||
);
|
||||
}
|
||||
if (statOptions.interfaceList.length === 0) return <p>N/A</p>;
|
||||
|
||||
const interfaceToShow = statOptions.interfaceList.find(
|
||||
@@ -273,8 +286,9 @@ const StatisticsChartList = ({ deviceSerialNumber, setOptions, section, time })
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return <p>N/A</p>;
|
||||
}, [statOptions, section]);
|
||||
}, [statOptions, section, error]);
|
||||
|
||||
const getStatistics = () => {
|
||||
setLoading(true);
|
||||
|
||||
@@ -31,25 +31,3 @@ export const extractWebSocketResponse = (message) => {
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
export const getStatusFromNotification = (notification) => {
|
||||
let status = 'success';
|
||||
if (notification.content.warning?.length > 0) status = 'warning';
|
||||
if (notification.content.error?.length > 0) status = 'error';
|
||||
|
||||
return status;
|
||||
};
|
||||
|
||||
export const getNotificationDescription = (t, notification) => {
|
||||
if (
|
||||
notification.content.type === 'venue_configuration_update' ||
|
||||
notification.content.type === 'entity_configuration_update'
|
||||
) {
|
||||
return t('configurations.notification_details', {
|
||||
success: notification.content.success.length,
|
||||
warning: notification.content.warning.length,
|
||||
error: notification.content.error.length,
|
||||
});
|
||||
}
|
||||
return notification.content.details;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user