Get system graphs working for inference time

This commit is contained in:
Nicolas Mowen 2024-03-29 14:57:33 -06:00
parent b9d8cc14de
commit 8295a0d11a
4 changed files with 151 additions and 54 deletions

View File

@ -14,6 +14,9 @@ from frigate.types import StatsTrackingTypes
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
MAX_STATS_POINTS = 120
class StatsEmitter(threading.Thread): class StatsEmitter(threading.Thread):
def __init__( def __init__(
self, self,
@ -55,7 +58,7 @@ class StatsEmitter(threading.Thread):
self.config, self.stats_tracking, self.hwaccel_errors self.config, self.stats_tracking, self.hwaccel_errors
) )
self.stats_history.append(stats) self.stats_history.append(stats)
self.stats_history = self.stats_history[-10:] self.stats_history = self.stats_history[-MAX_STATS_POINTS:]
self.requestor.send_data("stats", json.dumps(stats)) self.requestor.send_data("stats", json.dumps(stats))
logger.debug("Finished stats collection") logger.debug("Finished stats collection")
logger.info("Exiting stats emitter...") logger.info("Exiting stats emitter...")

View File

@ -1,18 +1,36 @@
import { Threshold } from "@/types/graph";
import { useMemo } from "react";
import Chart from "react-apexcharts"; import Chart from "react-apexcharts";
type SystemGraphProps = { type SystemGraphProps = {
graphId: string; graphId: string;
title?: string; name: string;
unit: string; unit: string;
threshold: Threshold;
data: ApexAxisChartSeries; data: ApexAxisChartSeries;
}; };
export default function SystemGraph({ export default function SystemGraph({
graphId, graphId,
title, name,
unit, unit,
threshold,
data, data,
}: SystemGraphProps) { }: SystemGraphProps) {
const lastValue = useMemo<number>(
// @ts-expect-error y is valid
() => data[0].data[data[0].data.length - 1]?.y ?? 0,
[data],
);
return ( return (
<div className="w-full flex flex-col">
<div className="flex items-center gap-1">
<div className="text-xs text-muted-foreground">{name}</div>
<div className="text-xs text-primary-foreground">
{lastValue}
{unit}
</div>
</div>
<Chart <Chart
type="bar" type="bar"
options={{ options={{
@ -28,27 +46,47 @@ export default function SystemGraph({
enabled: false, enabled: false,
}, },
}, },
legend: { colors: [
show: true, ({ value }: { value: number }) => {
showForSingleSeries: true, if (value >= threshold.error) {
position: "top", return "#FA5252";
horizontalAlign: "left", } else if (value >= threshold.warning) {
onItemClick: { return "#aa00aa";
toggleDataSeries: true, } else {
return "#404040";
}
}, },
],
grid: {
show: false,
},
legend: {
show: false,
}, },
dataLabels: { dataLabels: {
enabled: false, enabled: false,
}, },
xaxis: { xaxis: {
type: "datetime", type: "datetime",
axisBorder: {
show: false,
},
axisTicks: {
show: false,
},
labels: {
format: "h:mm",
datetimeUTC: false,
},
}, },
yaxis: { yaxis: {
show: false, show: false,
max: lastValue * 2,
}, },
}} }}
series={data} series={data}
height="120" height="120"
/> />
</div>
); );
} }

View File

@ -1,4 +1,3 @@
import Heading from "@/components/ui/heading";
import useSWR from "swr"; import useSWR from "swr";
import { FrigateStats } from "@/types/stats"; import { FrigateStats } from "@/types/stats";
import { useEffect, useMemo, useState } from "react"; import { useEffect, useMemo, useState } from "react";
@ -6,11 +5,17 @@ import SystemGraph from "@/components/graph/SystemGraph";
import { useFrigateStats } from "@/api/ws"; import { useFrigateStats } from "@/api/ws";
import TimeAgo from "@/components/dynamic/TimeAgo"; import TimeAgo from "@/components/dynamic/TimeAgo";
import { FrigateConfig } from "@/types/frigateConfig"; import { FrigateConfig } from "@/types/frigateConfig";
import {
DetectorCpuThreshold,
DetectorMemThreshold,
InferenceThreshold,
} from "@/types/graph";
function System() { function System() {
const { data: config } = useSWR<FrigateConfig>("config"); const { data: config } = useSWR<FrigateConfig>("config");
// stats // stats
const { data: initialStats } = useSWR<FrigateStats[]>("stats/history", { const { data: initialStats } = useSWR<FrigateStats[]>("stats/history", {
revalidateOnFocus: false, revalidateOnFocus: false,
}); });
@ -42,9 +47,10 @@ function System() {
} }
setStatsHistory([...statsHistory, updatedStats]); setStatsHistory([...statsHistory, updatedStats]);
}, [initialStats, updatedStats, statsHistory]); }, [initialStats, updatedStats]);
// stats data pieces // stats data pieces
const detInferenceTimeSeries = useMemo(() => { const detInferenceTimeSeries = useMemo(() => {
if (!statsHistory) { if (!statsHistory) {
return []; return [];
@ -200,6 +206,10 @@ function System() {
const statTime = new Date(stats.service.last_updated * 1000); const statTime = new Date(stats.service.last_updated * 1000);
Object.entries(stats.cameras).forEach(([key, camStats]) => { Object.entries(stats.cameras).forEach(([key, camStats]) => {
if (!config?.cameras[key].enabled) {
return;
}
if (!(key in series)) { if (!(key in series)) {
const camName = key.replaceAll("_", " "); const camName = key.replaceAll("_", " ");
series[key] = {}; series[key] = {};
@ -210,11 +220,11 @@ function System() {
series[key]["ffmpeg"].data.push({ series[key]["ffmpeg"].data.push({
x: statTime, x: statTime,
y: stats.cpu_usages[camStats.ffmpeg_pid.toString()].cpu, y: stats.cpu_usages[camStats.ffmpeg_pid.toString()]?.cpu ?? 0.0,
}); });
series[key]["capture"].data.push({ series[key]["capture"].data.push({
x: statTime, x: statTime,
y: stats.cpu_usages[camStats.capture_pid.toString()].cpu, y: stats.cpu_usages[camStats.capture_pid?.toString()]?.cpu ?? 0,
}); });
series[key]["detect"].data.push({ series[key]["detect"].data.push({
x: statTime, x: statTime,
@ -348,32 +358,45 @@ function System() {
</div> </div>
</div> </div>
<div className="grid grid-cols-1 sm:grid-cols-3 gap-2"> <div className="mt-4 grid grid-cols-1 sm:grid-cols-3 gap-2">
<div className="p-2.5 bg-primary rounded-2xl flex-col"> <div className="p-2.5 bg-primary rounded-2xl flex-col">
<div>Inference Speed</div> <div className="mb-5">Detector Inference Speed</div>
{detInferenceTimeSeries.map((series) => ( {detInferenceTimeSeries.map((series) => (
<SystemGraph <SystemGraph
key={series.name}
graphId="detector-inference" graphId="detector-inference"
name={series.name}
unit="ms" unit="ms"
threshold={InferenceThreshold}
data={[series]} data={[series]}
/> />
))} ))}
</div> </div>
<div className="bg-primary rounded-2xl flex-col"> <div className="p-2.5 bg-primary rounded-2xl flex-col">
<div className="mb-5">Detector CPU Usage</div>
{detCpuSeries.map((series) => (
<SystemGraph <SystemGraph
graphId="detector-usages" key={series.name}
title="CPU" graphId="detector-cpu-usages"
unit="%" unit="%"
data={detCpuSeries} name={series.name}
threshold={DetectorCpuThreshold}
data={[series]}
/> />
))}
</div> </div>
<div className="bg-primary rounded-2xl flex-col"> <div className="p-2.5 bg-primary rounded-2xl flex-col">
<div className="mb-5">Detector Memory Usage</div>
{detMemSeries.map((series) => (
<SystemGraph <SystemGraph
graphId="detector-usages" key={series.name}
title="Memory" graphId="detector-mem-usages"
unit="%" unit="%"
data={detMemSeries} name={series.name}
threshold={DetectorMemThreshold}
data={[series]}
/> />
))}
</div> </div>
</div> </div>
</div> </div>
@ -383,6 +406,19 @@ function System() {
export default System; export default System;
/** /**
* <div className="bg-primary rounded-2xl flex-col">
</div>
<div className="bg-primary rounded-2xl flex-col">
<SystemGraph
graphId="detector-usages"
unit="%"
name={""}
threshold={InferenceThreshold}
data={detMemSeries}
/>
</div>
*
* <Heading as="h4">Detectors</Heading> * <Heading as="h4">Detectors</Heading>
<div className="grid grid-cols-1 sm:grid-cols-3"> <div className="grid grid-cols-1 sm:grid-cols-3">
<SystemGraph <SystemGraph

View File

@ -7,3 +7,23 @@ export type GraphData = {
name?: string; name?: string;
data: GraphDataPoint[]; data: GraphDataPoint[];
}; };
export type Threshold = {
warning: number;
error: number;
};
export const InferenceThreshold = {
warning: 50,
error: 100,
} as Threshold;
export const DetectorCpuThreshold = {
warning: 25,
error: 50,
} as Threshold;
export const DetectorMemThreshold = {
warning: 20,
error: 50,
} as Threshold;