import { useFrigateStats } from "@/api/ws"; import { CameraLineGraph } from "@/components/graph/CameraGraph"; import { Skeleton } from "@/components/ui/skeleton"; import { FrigateConfig } from "@/types/frigateConfig"; import { FrigateStats } from "@/types/stats"; import { useEffect, useMemo, useState } from "react"; import useSWR from "swr"; type CameraMetricsProps = { lastUpdated: number; setLastUpdated: (last: number) => void; }; export default function CameraMetrics({ lastUpdated, setLastUpdated, }: CameraMetricsProps) { const { data: config } = useSWR("config"); // stats const { data: initialStats } = useSWR( ["stats/history", { keys: "cpu_usages,cameras,detection_fps,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.slice(1), updatedStats]); setLastUpdated(Date.now() / 1000); } }, [initialStats, updatedStats, statsHistory, lastUpdated, setLastUpdated]); // timestamps const updateTimes = useMemo( () => statsHistory.map((stats) => stats.service.last_updated), [statsHistory], ); // stats data const overallFpsSeries = useMemo(() => { if (!statsHistory) { return []; } const series: { [key: string]: { name: string; data: { x: number; y: number }[] }; } = {}; series["overall_fps"] = { name: "overall frames per second", data: [] }; series["overall_dps"] = { name: "overall detections per second", data: [] }; series["overall_skipped_dps"] = { name: "overall skipped detections per second", data: [], }; statsHistory.forEach((stats, statsIdx) => { if (!stats) { return; } let frames = 0; Object.values(stats.cameras).forEach( (camStat) => (frames += camStat.camera_fps), ); series["overall_fps"].data.push({ x: statsIdx, y: Math.round(frames), }); series["overall_dps"].data.push({ x: statsIdx, y: stats.detection_fps, }); let skipped = 0; Object.values(stats.cameras).forEach( (camStat) => (skipped += camStat.skipped_fps), ); series["overall_skipped_dps"].data.push({ x: statsIdx, y: skipped, }); }); return Object.values(series); }, [statsHistory]); const cameraCpuSeries = useMemo(() => { if (!statsHistory || statsHistory.length == 0) { return {}; } const series: { [cam: string]: { [key: string]: { name: string; data: { x: number; y: string }[] }; }; } = {}; statsHistory.forEach((stats, statsIdx) => { if (!stats) { return; } 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; }, [config, statsHistory]); const cameraFpsSeries = useMemo(() => { if (!statsHistory) { return {}; } const series: { [cam: string]: { [key: string]: { name: string; data: { x: number; y: number }[] }; }; } = {}; statsHistory.forEach((stats, statsIdx) => { if (!stats) { return; } Object.entries(stats.cameras).forEach(([key, camStats]) => { if (!(key in series)) { const camName = key.replaceAll("_", " "); series[key] = {}; series[key]["fps"] = { name: `${camName} frames per second`, data: [], }; series[key]["det"] = { name: `${camName} detections per second`, data: [], }; series[key]["skip"] = { name: `${camName} skipped detections per second`, data: [], }; } series[key]["fps"].data.push({ x: statsIdx, y: camStats.camera_fps, }); 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]); return (
Overview
{statsHistory.length != 0 ? (
Frames / Detections
) : ( )}
{config && Object.values(config.cameras).map((camera) => { if (camera.enabled) { return (
{camera.name.replaceAll("_", " ")}
{Object.keys(cameraCpuSeries).includes(camera.name) ? (
CPU
) : ( )} {Object.keys(cameraFpsSeries).includes(camera.name) ? (
Frames / Detections
) : ( )}
); } return null; })}
); }