From b93031649f21799643a65d417bfb24724519a700 Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Thu, 4 Apr 2024 08:03:25 -0600 Subject: [PATCH] Use separate view for general metrics --- web/src/components/graph/SystemGraph.tsx | 4 + web/src/pages/System.tsx | 412 +--------------------- web/src/views/system/GeneralMetrics.tsx | 415 +++++++++++++++++++++++ 3 files changed, 421 insertions(+), 410 deletions(-) create mode 100644 web/src/views/system/GeneralMetrics.tsx diff --git a/web/src/components/graph/SystemGraph.tsx b/web/src/components/graph/SystemGraph.tsx index e999c36d4..5fafb6a7d 100644 --- a/web/src/components/graph/SystemGraph.tsx +++ b/web/src/components/graph/SystemGraph.tsx @@ -124,3 +124,7 @@ export function ThresholdBarGraph({ ); } + +export function StorageGraph() { + +} diff --git a/web/src/pages/System.tsx b/web/src/pages/System.tsx index 46516cd1d..5fe6a7e16 100644 --- a/web/src/pages/System.tsx +++ b/web/src/pages/System.tsx @@ -1,20 +1,10 @@ import useSWR from "swr"; import { FrigateStats } from "@/types/stats"; -import { useEffect, useMemo, useState } from "react"; -import { useFrigateStats } from "@/api/ws"; +import { useState } from "react"; import TimeAgo from "@/components/dynamic/TimeAgo"; -import { - DetectorCpuThreshold, - DetectorMemThreshold, - GPUMemThreshold, - GPUUsageThreshold, - InferenceThreshold, -} from "@/types/graph"; import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"; -import { Button } from "@/components/ui/button"; -import VainfoDialog from "@/components/overlay/VainfoDialog"; import { isDesktop } from "react-device-detect"; -import { ThresholdBarGraph } from "@/components/graph/SystemGraph"; +import GeneralMetrics from "@/views/system/GeneralMetrics"; const metrics = ["general", "storage", "cameras"] as const; type SystemMetric = (typeof metrics)[number]; @@ -204,401 +194,3 @@ export default System; })} */ - -type GeneralMetricsProps = { - lastUpdated: number; - setLastUpdated: (last: number) => void; -}; -function GeneralMetrics({ lastUpdated, setLastUpdated }: GeneralMetricsProps) { - // extra info - - const [showVainfo, setShowVainfo] = useState(false); - - // stats - - const { data: initialStats } = useSWR( - [ - "stats/history", - { keys: "cpu_usages,detectors,gpu_usages,processes,service" }, - ], - { - revalidateOnFocus: false, - }, - ); - - const [statsHistory, setStatsHistory] = useState([]); - const { payload: updatedStats } = useFrigateStats(); - - useEffect(() => { - if (initialStats == undefined || initialStats.length == 0) { - return; - } - - if (statsHistory.length == 0) { - setStatsHistory(initialStats); - return; - } - - if (!updatedStats) { - return; - } - - if (updatedStats.service.last_updated > lastUpdated) { - setStatsHistory([...statsHistory, updatedStats]); - setLastUpdated(Date.now() / 1000); - } - }, [initialStats, updatedStats, statsHistory, lastUpdated, setLastUpdated]); - - // timestamps - - const updateTimes = useMemo( - () => statsHistory.map((stats) => stats.service.last_updated), - [statsHistory], - ); - - // detectors stats - - const detInferenceTimeSeries = useMemo(() => { - if (!statsHistory) { - return []; - } - - const series: { - [key: string]: { name: string; data: { x: number; y: number }[] }; - } = {}; - - statsHistory.forEach((stats, statsIdx) => { - if (!stats) { - return; - } - - Object.entries(stats.detectors).forEach(([key, stats]) => { - if (!(key in series)) { - series[key] = { name: key, data: [] }; - } - - series[key].data.push({ x: statsIdx, y: stats.inference_speed }); - }); - }); - return Object.values(series); - }, [statsHistory]); - - const detCpuSeries = useMemo(() => { - if (!statsHistory) { - return []; - } - - const series: { - [key: string]: { name: string; data: { x: number; y: string }[] }; - } = {}; - - statsHistory.forEach((stats, statsIdx) => { - if (!stats) { - return; - } - - Object.entries(stats.detectors).forEach(([key, detStats]) => { - if (!(key in series)) { - series[key] = { name: key, data: [] }; - } - - series[key].data.push({ - x: statsIdx, - y: stats.cpu_usages[detStats.pid.toString()].cpu, - }); - }); - }); - return Object.values(series); - }, [statsHistory]); - - const detMemSeries = useMemo(() => { - if (!statsHistory) { - return []; - } - - const series: { - [key: string]: { name: string; data: { x: number; y: string }[] }; - } = {}; - - statsHistory.forEach((stats, statsIdx) => { - if (!stats) { - return; - } - - Object.entries(stats.detectors).forEach(([key, detStats]) => { - if (!(key in series)) { - series[key] = { name: key, data: [] }; - } - - series[key].data.push({ - x: statsIdx, - y: stats.cpu_usages[detStats.pid.toString()].mem, - }); - }); - }); - return Object.values(series); - }, [statsHistory]); - - // gpu stats - - const gpuSeries = useMemo(() => { - if (!statsHistory) { - return []; - } - - const series: { - [key: string]: { name: string; data: { x: number; y: string }[] }; - } = {}; - - statsHistory.forEach((stats, statsIdx) => { - if (!stats) { - return; - } - - Object.entries(stats.gpu_usages || []).forEach(([key, stats]) => { - if (!(key in series)) { - series[key] = { name: key, data: [] }; - } - - series[key].data.push({ x: statsIdx, y: stats.gpu }); - }); - }); - return Object.keys(series).length > 0 ? Object.values(series) : []; - }, [statsHistory]); - - const gpuMemSeries = useMemo(() => { - if (!statsHistory) { - return []; - } - - const series: { - [key: string]: { name: string; data: { x: number; y: string }[] }; - } = {}; - - statsHistory.forEach((stats, statsIdx) => { - if (!stats) { - return; - } - - Object.entries(stats.gpu_usages || {}).forEach(([key, stats]) => { - if (!(key in series)) { - series[key] = { name: key, data: [] }; - } - - series[key].data.push({ x: statsIdx, y: stats.mem }); - }); - }); - return Object.values(series); - }, [statsHistory]); - - // other processes stats - - const otherProcessCpuSeries = useMemo(() => { - if (!statsHistory) { - return []; - } - - const series: { - [key: string]: { name: string; data: { x: number; y: string }[] }; - } = {}; - - statsHistory.forEach((stats, statsIdx) => { - if (!stats) { - return; - } - - Object.entries(stats.processes).forEach(([key, procStats]) => { - if (procStats.pid.toString() in stats.cpu_usages) { - if (!(key in series)) { - series[key] = { name: key, data: [] }; - } - - series[key].data.push({ - x: statsIdx, - y: stats.cpu_usages[procStats.pid.toString()].cpu, - }); - } - }); - }); - return Object.keys(series).length > 0 ? Object.values(series) : []; - }, [statsHistory]); - - const otherProcessMemSeries = useMemo(() => { - if (!statsHistory) { - return []; - } - - const series: { - [key: string]: { name: string; data: { x: number; y: string }[] }; - } = {}; - - statsHistory.forEach((stats, statsIdx) => { - if (!stats) { - return; - } - - Object.entries(stats.processes).forEach(([key, procStats]) => { - if (procStats.pid.toString() in stats.cpu_usages) { - if (!(key in series)) { - series[key] = { name: key, data: [] }; - } - - series[key].data.push({ - x: statsIdx, - y: stats.cpu_usages[procStats.pid.toString()].mem, - }); - } - }); - }); - return Object.values(series); - }, [statsHistory]); - - if (statsHistory.length == 0) { - return; - } - - return ( - <> - - -
-
- Detectors -
-
-
-
Detector Inference Speed
- {detInferenceTimeSeries.map((series) => ( - - ))} -
-
-
Detector CPU Usage
- {detCpuSeries.map((series) => ( - - ))} -
-
-
Detector Memory Usage
- {detMemSeries.map((series) => ( - - ))} -
-
- - {statsHistory.length > 0 && statsHistory[0].gpu_usages && ( - <> -
-
- GPUs -
- {Object.keys(statsHistory[0].gpu_usages).filter( - (key) => - key == "amd-vaapi" || - key == "intel-vaapi" || - key == "intel-qsv", - ).length > 0 && ( - - )} -
-
-
-
GPU Usage
- {gpuSeries.map((series) => ( - - ))} -
-
-
GPU Memory
- {gpuMemSeries.map((series) => ( - - ))} -
-
- - )} - -
- Other Processes -
-
-
-
Process CPU Usage
- {otherProcessCpuSeries.map((series) => ( - - ))} -
-
-
Process Memory Usage
- {otherProcessMemSeries.map((series) => ( - - ))} -
-
-
- - ); -} diff --git a/web/src/views/system/GeneralMetrics.tsx b/web/src/views/system/GeneralMetrics.tsx new file mode 100644 index 000000000..ccee33190 --- /dev/null +++ b/web/src/views/system/GeneralMetrics.tsx @@ -0,0 +1,415 @@ +import useSWR from "swr"; +import { FrigateStats } from "@/types/stats"; +import { useEffect, useMemo, useState } from "react"; +import { useFrigateStats } from "@/api/ws"; +import { + DetectorCpuThreshold, + DetectorMemThreshold, + GPUMemThreshold, + GPUUsageThreshold, + InferenceThreshold, +} from "@/types/graph"; +import { Button } from "@/components/ui/button"; +import VainfoDialog from "@/components/overlay/VainfoDialog"; +import { ThresholdBarGraph } from "@/components/graph/SystemGraph"; + +type GeneralMetricsProps = { + lastUpdated: number; + setLastUpdated: (last: number) => void; +}; +export default function GeneralMetrics({ + lastUpdated, + setLastUpdated, +}: GeneralMetricsProps) { + // extra info + + const [showVainfo, setShowVainfo] = useState(false); + + // stats + + const { data: initialStats } = useSWR( + [ + "stats/history", + { keys: "cpu_usages,detectors,gpu_usages,processes,service" }, + ], + { + revalidateOnFocus: false, + }, + ); + + const [statsHistory, setStatsHistory] = useState([]); + const { payload: updatedStats } = useFrigateStats(); + + useEffect(() => { + if (initialStats == undefined || initialStats.length == 0) { + return; + } + + if (statsHistory.length == 0) { + setStatsHistory(initialStats); + return; + } + + if (!updatedStats) { + return; + } + + if (updatedStats.service.last_updated > lastUpdated) { + setStatsHistory([...statsHistory, updatedStats]); + setLastUpdated(Date.now() / 1000); + } + }, [initialStats, updatedStats, statsHistory, lastUpdated, setLastUpdated]); + + // timestamps + + const updateTimes = useMemo( + () => statsHistory.map((stats) => stats.service.last_updated), + [statsHistory], + ); + + // detectors stats + + const detInferenceTimeSeries = useMemo(() => { + if (!statsHistory) { + return []; + } + + const series: { + [key: string]: { name: string; data: { x: number; y: number }[] }; + } = {}; + + statsHistory.forEach((stats, statsIdx) => { + if (!stats) { + return; + } + + Object.entries(stats.detectors).forEach(([key, stats]) => { + if (!(key in series)) { + series[key] = { name: key, data: [] }; + } + + series[key].data.push({ x: statsIdx, y: stats.inference_speed }); + }); + }); + return Object.values(series); + }, [statsHistory]); + + const detCpuSeries = useMemo(() => { + if (!statsHistory) { + return []; + } + + const series: { + [key: string]: { name: string; data: { x: number; y: string }[] }; + } = {}; + + statsHistory.forEach((stats, statsIdx) => { + if (!stats) { + return; + } + + Object.entries(stats.detectors).forEach(([key, detStats]) => { + if (!(key in series)) { + series[key] = { name: key, data: [] }; + } + + series[key].data.push({ + x: statsIdx, + y: stats.cpu_usages[detStats.pid.toString()].cpu, + }); + }); + }); + return Object.values(series); + }, [statsHistory]); + + const detMemSeries = useMemo(() => { + if (!statsHistory) { + return []; + } + + const series: { + [key: string]: { name: string; data: { x: number; y: string }[] }; + } = {}; + + statsHistory.forEach((stats, statsIdx) => { + if (!stats) { + return; + } + + Object.entries(stats.detectors).forEach(([key, detStats]) => { + if (!(key in series)) { + series[key] = { name: key, data: [] }; + } + + series[key].data.push({ + x: statsIdx, + y: stats.cpu_usages[detStats.pid.toString()].mem, + }); + }); + }); + return Object.values(series); + }, [statsHistory]); + + // gpu stats + + const gpuSeries = useMemo(() => { + if (!statsHistory) { + return []; + } + + const series: { + [key: string]: { name: string; data: { x: number; y: string }[] }; + } = {}; + + statsHistory.forEach((stats, statsIdx) => { + if (!stats) { + return; + } + + Object.entries(stats.gpu_usages || []).forEach(([key, stats]) => { + if (!(key in series)) { + series[key] = { name: key, data: [] }; + } + + series[key].data.push({ x: statsIdx, y: stats.gpu }); + }); + }); + return Object.keys(series).length > 0 ? Object.values(series) : []; + }, [statsHistory]); + + const gpuMemSeries = useMemo(() => { + if (!statsHistory) { + return []; + } + + const series: { + [key: string]: { name: string; data: { x: number; y: string }[] }; + } = {}; + + statsHistory.forEach((stats, statsIdx) => { + if (!stats) { + return; + } + + Object.entries(stats.gpu_usages || {}).forEach(([key, stats]) => { + if (!(key in series)) { + series[key] = { name: key, data: [] }; + } + + series[key].data.push({ x: statsIdx, y: stats.mem }); + }); + }); + return Object.values(series); + }, [statsHistory]); + + // other processes stats + + const otherProcessCpuSeries = useMemo(() => { + if (!statsHistory) { + return []; + } + + const series: { + [key: string]: { name: string; data: { x: number; y: string }[] }; + } = {}; + + statsHistory.forEach((stats, statsIdx) => { + if (!stats) { + return; + } + + Object.entries(stats.processes).forEach(([key, procStats]) => { + if (procStats.pid.toString() in stats.cpu_usages) { + if (!(key in series)) { + series[key] = { name: key, data: [] }; + } + + series[key].data.push({ + x: statsIdx, + y: stats.cpu_usages[procStats.pid.toString()].cpu, + }); + } + }); + }); + return Object.keys(series).length > 0 ? Object.values(series) : []; + }, [statsHistory]); + + const otherProcessMemSeries = useMemo(() => { + if (!statsHistory) { + return []; + } + + const series: { + [key: string]: { name: string; data: { x: number; y: string }[] }; + } = {}; + + statsHistory.forEach((stats, statsIdx) => { + if (!stats) { + return; + } + + Object.entries(stats.processes).forEach(([key, procStats]) => { + if (procStats.pid.toString() in stats.cpu_usages) { + if (!(key in series)) { + series[key] = { name: key, data: [] }; + } + + series[key].data.push({ + x: statsIdx, + y: stats.cpu_usages[procStats.pid.toString()].mem, + }); + } + }); + }); + return Object.values(series); + }, [statsHistory]); + + if (statsHistory.length == 0) { + return; + } + + return ( + <> + + +
+
+ Detectors +
+
+
+
Detector Inference Speed
+ {detInferenceTimeSeries.map((series) => ( + + ))} +
+
+
Detector CPU Usage
+ {detCpuSeries.map((series) => ( + + ))} +
+
+
Detector Memory Usage
+ {detMemSeries.map((series) => ( + + ))} +
+
+ + {statsHistory.length > 0 && statsHistory[0].gpu_usages && ( + <> +
+
+ GPUs +
+ {Object.keys(statsHistory[0].gpu_usages).filter( + (key) => + key == "amd-vaapi" || + key == "intel-vaapi" || + key == "intel-qsv", + ).length > 0 && ( + + )} +
+
+
+
GPU Usage
+ {gpuSeries.map((series) => ( + + ))} +
+
+
GPU Memory
+ {gpuMemSeries.map((series) => ( + + ))} +
+
+ + )} + +
+ Other Processes +
+
+
+
Process CPU Usage
+ {otherProcessCpuSeries.map((series) => ( + + ))} +
+
+
Process Memory Usage
+ {otherProcessMemSeries.map((series) => ( + + ))} +
+
+
+ + ); +}