import useSWR from "swr"; import { FrigateStats } from "@/types/stats"; import { useEffect, useMemo, useState } from "react"; import SystemGraph from "@/components/graph/SystemGraph"; import { useFrigateStats } from "@/api/ws"; 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"; const metrics = ["general", "storage", "cameras"] as const; type SystemMetric = (typeof metrics)[number]; function System() { // stats page const [page, setPage] = useState("general"); const [lastUpdated, setLastUpdated] = useState(Date.now() / 1000); // stats collection const { data: statsSnapshot } = useSWR("stats", { revalidateOnFocus: false, }); return (
{ if (value) { setPage(value); } }} // don't allow the severity to be unselected > {Object.values(metrics).map((item) => (
{item}
))}
{lastUpdated && (
Last refreshed:
)}
System
{statsSnapshot && (
{statsSnapshot.service.version}
)}
{page == "general" && ( )}
); } export default System; /** * 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, statsIdx) => { if (!stats) { return; } const statTime = new Date(stats.service.last_updated * 1000); Object.entries(stats.cameras).forEach(([key, camStats]) => { if (!config?.cameras[key].enabled) { return; } 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: statsIdx, y: stats.cpu_usages[camStats.ffmpeg_pid.toString()]?.cpu ?? 0.0, }); series[key]["capture"].data.push({ x: statsIdx, y: stats.cpu_usages[camStats.capture_pid?.toString()]?.cpu ?? 0, }); series[key]["detect"].data.push({ x: statsIdx, 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, statsIdx) => { 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: statsIdx, y: camStats.detection_fps, }); series[key]["skip"].data.push({ x: statsIdx, y: camStats.skipped_fps, }); }); }); return series; }, [statsHistory]); * *
Cameras
{config && Object.values(config.cameras).map((camera) => { if (camera.enabled) { return (
); } return null; })}
*/ 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) => ( ))}
); }