diff --git a/frigate/api/app.py b/frigate/api/app.py index a267ce595..7810c0b21 100644 --- a/frigate/api/app.py +++ b/frigate/api/app.py @@ -127,8 +127,11 @@ def stats(): @bp.route("/stats/history") def stats_history(): - json: dict[str, any] = request.get_json(silent=True) or {} - keys = json.get("keys") + keys = request.args.get("keys", default=None) + + if keys: + keys = keys.split(",") + return jsonify(current_app.stats_emitter.get_stats_history(keys)) @@ -154,9 +157,9 @@ def config(): config["plus"] = {"enabled": current_app.plus_api.is_active()} for detector, detector_config in config["detectors"].items(): - detector_config["model"]["labelmap"] = ( - current_app.frigate_config.model.merged_labelmap - ) + detector_config["model"][ + "labelmap" + ] = current_app.frigate_config.model.merged_labelmap return jsonify(config) diff --git a/frigate/stats/emitter.py b/frigate/stats/emitter.py index 8491891fe..5ae8181d9 100644 --- a/frigate/stats/emitter.py +++ b/frigate/stats/emitter.py @@ -59,7 +59,7 @@ class StatsEmitter(threading.Thread): selected = {} for k in keys: - selected[k] = selected_stats[k] + selected[k] = s[k] selected_stats.append(selected) diff --git a/web/src/pages/System.tsx b/web/src/pages/System.tsx index ee52ee61e..30ec629b4 100644 --- a/web/src/pages/System.tsx +++ b/web/src/pages/System.tsx @@ -10,11 +10,18 @@ import { DetectorMemThreshold, InferenceThreshold, } from "@/types/graph"; +import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"; +import { Button } from "@/components/ui/button"; + +const metrics = ["general", "storage", "cameras"] as const; +type SystemMetric = (typeof metrics)[number]; function System() { const { data: config } = useSWR("config"); - // stats chunks + // stats page + + const [page, setPage] = useState("general"); // stats collection @@ -53,90 +60,6 @@ function System() { // stats data pieces - const detInferenceTimeSeries = useMemo(() => { - if (!statsHistory) { - return []; - } - - const series: { - [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]) => { - if (!(key in series)) { - series[key] = { name: `${key} (${stats.pid})`, data: [] }; - } - - series[key].data.push({ x: statTime, y: stats.inference_speed }); - }); - }); - return Object.values(series); - }, [statsHistory]); - const detCpuSeries = useMemo(() => { - if (!statsHistory) { - return []; - } - - const series: { - [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]) => { - if (!(key in series)) { - series[key] = { name: key, data: [] }; - } - - series[key].data.push({ - x: statTime, - 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: 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]) => { - if (!(key in series)) { - series[key] = { name: key, data: [] }; - } - - series[key].data.push({ - x: statTime, - y: stats.cpu_usages[detStats.pid.toString()].mem, - }); - }); - }); - return Object.values(series); - }, [statsHistory]); const gpuSeries = useMemo(() => { if (!statsHistory) { return []; @@ -343,14 +266,29 @@ function System() { return (
-
-
System
- {initialStats && ( -
- {initialStats[0].service.version} -
- )} -
+ { + if (value) { + setPage(value); + } + }} // don't allow the severity to be unselected + > + {Object.values(metrics).map((item) => ( + +
{item}
+
+ ))} +
+
{lastUpdated && (
@@ -359,48 +297,15 @@ function System() { )}
- -
-
-
Detector Inference Speed
- {detInferenceTimeSeries.map((series) => ( - - ))} -
-
-
Detector CPU Usage
- {detCpuSeries.map((series) => ( - - ))} -
-
-
Detector Memory Usage
- {detMemSeries.map((series) => ( - - ))} -
+
+
System
+ {initialStats && ( +
+ {initialStats[0].service.version} +
+ )}
+ {page == "general" && }
); } @@ -503,3 +408,222 @@ export default System; />
*/ + +function GeneralMetrics() { + // stats + + const { data: initialStats } = useSWR( + ["stats/history", { keys: "cpu_usages,detectors,service" }], + { + revalidateOnFocus: false, + }, + ); + + const [statsHistory, setStatsHistory] = useState([]); + const { payload: updatedStats } = useFrigateStats(); + + useEffect(() => { + if (initialStats == undefined) { + return; + } + + if (statsHistory.length < initialStats.length) { + setStatsHistory(initialStats); + return; + } + + setStatsHistory([...statsHistory, updatedStats]); + }, [initialStats, updatedStats]); + + // stats data pieces + + const detInferenceTimeSeries = useMemo(() => { + if (!statsHistory) { + return []; + } + + const series: { + [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]) => { + if (!(key in series)) { + series[key] = { name: `${key} (${stats.pid})`, data: [] }; + } + + series[key].data.push({ x: statTime, y: stats.inference_speed }); + }); + }); + return Object.values(series); + }, [statsHistory]); + const detCpuSeries = useMemo(() => { + if (!statsHistory) { + return []; + } + + const series: { + [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]) => { + if (!(key in series)) { + series[key] = { name: key, data: [] }; + } + + series[key].data.push({ + x: statTime, + 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: 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]) => { + if (!(key in series)) { + series[key] = { name: key, data: [] }; + } + + series[key].data.push({ + x: statTime, + y: stats.cpu_usages[detStats.pid.toString()].mem, + }); + }); + }); + return Object.values(series); + }, [statsHistory]); + + 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 && ( + <> +
+
+ Detectors +
+ {Object.keys(statsHistory[0].gpu_usages).filter( + (key) => + key == "amd-vaapi" || + key == "intel-vaapi" || + key == "intel-qsv", + ).length > 0 && } +
+
+
+
Detector Inference Speed
+ {detInferenceTimeSeries.map((series) => ( + + ))} +
+ +
+
Detector CPU Usage
+ {detCpuSeries.map((series) => ( + + ))} +
+
+
Detector Memory Usage
+ {detMemSeries.map((series) => ( + + ))} +
+
+ + )} +
+ ); +}