From b792b51bd0c08d68f9d9f3fa30a0cf757ab557d7 Mon Sep 17 00:00:00 2001 From: Charles Date: Mon, 8 Aug 2022 16:58:38 +0100 Subject: [PATCH] [WIFI-10515] Crash fix when receiving corrupted statistics Signed-off-by: Charles --- package-lock.json | 4 +- package.json | 2 +- .../LatestStatisticsModal.js | 21 +- .../StatisticsChartList.js | 412 +++++++++--------- src/contexts/WebSocketProvider/utils.js | 22 - 5 files changed, 230 insertions(+), 231 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7f3d249..d552c4d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index d978c8c..fdbd900 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/components/InterfaceStatistics/LatestStatisticsModal.js b/src/components/InterfaceStatistics/LatestStatisticsModal.js index 5b510f3..31743b4 100644 --- a/src/components/InterfaceStatistics/LatestStatisticsModal.js +++ b/src/components/InterfaceStatistics/LatestStatisticsModal.js @@ -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 }) => {
- +
-
{JSON.stringify(latestStats, null, 2)}
+
{latestStatsString}
); diff --git a/src/components/InterfaceStatistics/StatisticsChartList.js b/src/components/InterfaceStatistics/StatisticsChartList.js index 13f8189..a7a2533 100644 --- a/src/components/InterfaceStatistics/StatisticsChartList.js +++ b/src/components/InterfaceStatistics/StatisticsChartList.js @@ -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 ( + + Error while parsing statistics + + ); + } if (statOptions.interfaceList.length === 0) return

N/A

; const interfaceToShow = statOptions.interfaceList.find( @@ -273,8 +286,9 @@ const StatisticsChartList = ({ deviceSerialNumber, setOptions, section, time }) ); } + return

N/A

; - }, [statOptions, section]); + }, [statOptions, section, error]); const getStatistics = () => { setLoading(true); diff --git a/src/contexts/WebSocketProvider/utils.js b/src/contexts/WebSocketProvider/utils.js index 4204b41..9014cd2 100644 --- a/src/contexts/WebSocketProvider/utils.js +++ b/src/contexts/WebSocketProvider/utils.js @@ -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; -};