diff --git a/web/src/components/graph/SystemGraph.tsx b/web/src/components/graph/SystemGraph.tsx index 1593548ca..dbc45ebf5 100644 --- a/web/src/components/graph/SystemGraph.tsx +++ b/web/src/components/graph/SystemGraph.tsx @@ -2,7 +2,7 @@ import Chart from "react-apexcharts"; type SystemGraphProps = { graphId: string; - title: string; + title?: string; unit: string; data: ApexAxisChartSeries; }; @@ -14,7 +14,7 @@ export default function SystemGraph({ }: SystemGraphProps) { return ( { - return `${value.toFixed(2)} ${unit}`; - }, - }, + show: false, }, }} series={data} - height="100%" + height="120" /> ); } diff --git a/web/src/pages/System.tsx b/web/src/pages/System.tsx index 78cdec9bf..f20ee319e 100644 --- a/web/src/pages/System.tsx +++ b/web/src/pages/System.tsx @@ -5,15 +5,18 @@ import { useEffect, useMemo, useState } from "react"; import SystemGraph from "@/components/graph/SystemGraph"; import { useFrigateStats } from "@/api/ws"; import TimeAgo from "@/components/dynamic/TimeAgo"; +import { FrigateConfig } from "@/types/frigateConfig"; function System() { + const { data: config } = useSWR("config"); + // stats const { data: initialStats } = useSWR("stats/history", { revalidateOnFocus: false, }); const { payload: updatedStats } = useFrigateStats(); const [statsHistory, setStatsHistory] = useState( - initialStats || [] + initialStats || [], ); const lastUpdated = useMemo(() => { @@ -39,7 +42,7 @@ function System() { } setStatsHistory([...statsHistory, updatedStats]); - }, [initialStats, updatedStats]); + }, [initialStats, updatedStats, statsHistory]); // stats data pieces const detInferenceTimeSeries = useMemo(() => { @@ -48,10 +51,14 @@ function System() { } const series: { - [key: string]: { name: string; data: { x: any; y: any }[] }; + [key: string]: { name: string; data: { x: object; y: number }[] }; } = {}; statsHistory.forEach((stats) => { + if (!stats) { + return; + } + const statTime = new Date(stats.service.last_updated * 1000); Object.entries(stats.detectors).forEach(([key, stats]) => { @@ -70,10 +77,14 @@ function System() { } const series: { - [key: string]: { name: string; data: { x: any; y: any }[] }; + [key: string]: { name: string; data: { x: object; y: string }[] }; } = {}; statsHistory.forEach((stats) => { + if (!stats) { + return; + } + const statTime = new Date(stats.service.last_updated * 1000); Object.entries(stats.detectors).forEach(([key, detStats]) => { @@ -95,10 +106,14 @@ function System() { } const series: { - [key: string]: { name: string; data: { x: any; y: any }[] }; + [key: string]: { name: string; data: { x: object; y: string }[] }; } = {}; statsHistory.forEach((stats) => { + if (!stats) { + return; + } + const statTime = new Date(stats.service.last_updated * 1000); Object.entries(stats.detectors).forEach(([key, detStats]) => { @@ -120,10 +135,14 @@ function System() { } const series: { - [key: string]: { name: string; data: { x: any; y: any }[] }; + [key: string]: { name: string; data: { x: object; y: string }[] }; } = {}; statsHistory.forEach((stats) => { + if (!stats) { + return; + } + const statTime = new Date(stats.service.last_updated * 1000); Object.entries(stats.gpu_usages || []).forEach(([key, stats]) => { @@ -142,10 +161,14 @@ function System() { } const series: { - [key: string]: { name: string; data: { x: any; y: any }[] }; + [key: string]: { name: string; data: { x: object; y: string }[] }; } = {}; statsHistory.forEach((stats) => { + if (!stats) { + return; + } + const statTime = new Date(stats.service.last_updated * 1000); Object.entries(stats.gpu_usages || {}).forEach(([key, stats]) => { @@ -158,16 +181,104 @@ function System() { }); return Object.values(series); }, [statsHistory]); + const cameraCpuSeries = useMemo(() => { + if (!statsHistory || statsHistory.length == 0) { + return {}; + } + + const series: { + [cam: string]: { + [key: string]: { name: string; data: { x: object; y: string }[] }; + }; + } = {}; + + statsHistory.forEach((stats) => { + if (!stats) { + return; + } + + const statTime = new Date(stats.service.last_updated * 1000); + + Object.entries(stats.cameras).forEach(([key, camStats]) => { + if (!(key in series)) { + const camName = key.replaceAll("_", " "); + series[key] = {}; + series[key]["ffmpeg"] = { name: `${camName} ffmpeg`, data: [] }; + series[key]["capture"] = { name: `${camName} capture`, data: [] }; + series[key]["detect"] = { name: `${camName} detect`, data: [] }; + } + + series[key]["ffmpeg"].data.push({ + x: statTime, + y: stats.cpu_usages[camStats.ffmpeg_pid.toString()].cpu, + }); + series[key]["capture"].data.push({ + x: statTime, + y: stats.cpu_usages[camStats.capture_pid.toString()].cpu, + }); + series[key]["detect"].data.push({ + x: statTime, + y: stats.cpu_usages[camStats.pid.toString()].cpu, + }); + }); + }); + return series; + }, [statsHistory]); + const cameraFpsSeries = useMemo(() => { + if (!statsHistory) { + return {}; + } + + const series: { + [cam: string]: { + [key: string]: { name: string; data: { x: object; y: number }[] }; + }; + } = {}; + + statsHistory.forEach((stats) => { + if (!stats) { + return; + } + + const statTime = new Date(stats.service.last_updated * 1000); + + Object.entries(stats.cameras).forEach(([key, camStats]) => { + if (!(key in series)) { + const camName = key.replaceAll("_", " "); + series[key] = {}; + series[key]["det"] = { name: `${camName} detections`, data: [] }; + series[key]["skip"] = { + name: `${camName} skipped detections`, + data: [], + }; + } + + series[key]["det"].data.push({ + x: statTime, + y: camStats.detection_fps, + }); + series[key]["skip"].data.push({ + x: statTime, + y: camStats.skipped_fps, + }); + }); + }); + return series; + }, [statsHistory]); const otherProcessCpuSeries = useMemo(() => { if (!statsHistory) { return []; } const series: { - [key: string]: { name: string; data: { x: any; y: any }[] }; + [key: string]: { name: string; data: { x: object; y: string }[] }; } = {}; statsHistory.forEach((stats) => { + if (!stats) { + return; + } + const statTime = new Date(stats.service.last_updated * 1000); Object.entries(stats.processes).forEach(([key, procStats]) => { @@ -191,10 +302,14 @@ function System() { } const series: { - [key: string]: { name: string; data: { x: any; y: any }[] }; + [key: string]: { name: string; data: { x: object; y: string }[] }; } = {}; statsHistory.forEach((stats) => { + if (!stats) { + return; + } + const statTime = new Date(stats.service.last_updated * 1000); Object.entries(stats.processes).forEach(([key, procStats]) => { @@ -214,20 +329,61 @@ function System() { }, [statsHistory]); return ( - <> -
- System - {initialStats && ( -
{initialStats[0].service.version}
- )} -
- {lastUpdated && ( -
- Last refreshed: +
+
+
+
System
+ {initialStats && ( +
+ {initialStats[0].service.version} +
+ )}
- )} +
+ {lastUpdated && ( +
+ Last refreshed: +
+ )} +
+
- Detectors +
+
+
Inference Speed
+ {detInferenceTimeSeries.map((series) => ( + + ))} +
+
+ +
+
+ +
+
+
+ ); +} + +export default System; + +/** + * Detectors
0 && ( <> GPUs -
+
)} + Cameras +
+ {config && + Object.values(config.cameras).map((camera) => { + if (camera.enabled) { + return ( +
+ + +
+ ); + } + + return null; + })} +
Other Processes
- - ); -} - -export default System; + */