Merge pull request #100 from stephb9959/main

[WIFI-10515] Crash fix when receiving corrupted statistics
This commit is contained in:
Charles Bourque
2022-08-08 16:59:27 +01:00
committed by GitHub
5 changed files with 230 additions and 231 deletions

4
package-lock.json generated
View File

@@ -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",

View File

@@ -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",

View File

@@ -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>
);

View File

@@ -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);

View File

@@ -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;
};