Show statusbar with cpu and gpu stats (#9968)

* Show statusbar with cpu and gpu stats

* fix gif logic
This commit is contained in:
Nicolas Mowen 2024-02-21 19:27:02 -07:00 committed by GitHub
parent 33c77d03c7
commit 6626b8d758
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 170 additions and 6 deletions

View File

@ -616,8 +616,8 @@ def event_preview(id: str, max_cache_age=2592000):
) )
start_ts = event.start_time start_ts = event.start_time
end_ts = ( end_ts = start_ts + (
start_ts + min(event.end_time - event.start_time, 20) if event.end_time else 20 min(event.end_time - event.start_time, 20) if event.end_time else 20
) )
if datetime.fromtimestamp(event.start_time) < datetime.now().replace( if datetime.fromtimestamp(event.start_time) < datetime.now().replace(

View File

@ -103,8 +103,17 @@ def get_cpu_stats() -> dict[str, dict]:
docker_memlimit = get_docker_memlimit_bytes() / 1024 docker_memlimit = get_docker_memlimit_bytes() / 1024
total_mem = os.sysconf("SC_PAGE_SIZE") * os.sysconf("SC_PHYS_PAGES") / 1024 total_mem = os.sysconf("SC_PAGE_SIZE") * os.sysconf("SC_PHYS_PAGES") / 1024
system_cpu = psutil.cpu_percent(
interval=None
) # no interval as we don't want to be blocking
system_mem = psutil.virtual_memory()
usages["frigate.full_system"] = {
"cpu": str(system_cpu),
"mem": str(system_mem.percent),
}
for process in psutil.process_iter(["pid", "name", "cpu_percent", "cmdline"]): for process in psutil.process_iter(["pid", "name", "cpu_percent", "cmdline"]):
pid = process.info["pid"] pid = str(process.info["pid"])
try: try:
cpu_percent = process.info["cpu_percent"] cpu_percent = process.info["cpu_percent"]
cmdline = process.info["cmdline"] cmdline = process.info["cmdline"]

View File

@ -15,6 +15,8 @@ import NoMatch from "@/pages/NoMatch";
import Settings from "@/pages/Settings"; import Settings from "@/pages/Settings";
import UIPlayground from "./pages/UIPlayground"; import UIPlayground from "./pages/UIPlayground";
import Events from "./pages/Events"; import Events from "./pages/Events";
import { isDesktop } from "react-device-detect";
import Statusbar from "./components/Statusbar";
function App() { function App() {
const [sheetOpen, setSheetOpen] = useState(false); const [sheetOpen, setSheetOpen] = useState(false);
@ -30,9 +32,10 @@ function App() {
<Header onToggleNavbar={toggleNavbar} /> <Header onToggleNavbar={toggleNavbar} />
<div className="w-full h-full pt-2 overflow-hidden"> <div className="w-full h-full pt-2 overflow-hidden">
<Sidebar sheetOpen={sheetOpen} setSheetOpen={setSheetOpen} /> <Sidebar sheetOpen={sheetOpen} setSheetOpen={setSheetOpen} />
{isDesktop && <Statusbar />}
<div <div
id="pageRoot" id="pageRoot"
className="absolute left-0 md:left-16 top-16 md:top-2 right-0 bottom-0 overflow-hidden" className="absolute left-0 md:left-16 top-16 md:top-2 right-0 bottom-0 md:bottom-8 overflow-hidden"
> >
<Routes> <Routes>
<Route path="/" element={<Live />} /> <Route path="/" element={<Live />} />

View File

@ -11,6 +11,7 @@ import { produce, Draft } from "immer";
import useWebSocket, { ReadyState } from "react-use-websocket"; import useWebSocket, { ReadyState } from "react-use-websocket";
import { FrigateConfig } from "@/types/frigateConfig"; import { FrigateConfig } from "@/types/frigateConfig";
import { FrigateEvent, ToggleableSetting } from "@/types/ws"; import { FrigateEvent, ToggleableSetting } from "@/types/ws";
import { FrigateStats } from "@/types/stats";
type ReducerState = { type ReducerState = {
[topic: string]: { [topic: string]: {
@ -221,6 +222,13 @@ export function useFrigateEvents(): { payload: FrigateEvent } {
return { payload }; return { payload };
} }
export function useFrigateStats(): { payload: FrigateStats } {
const {
value: { payload },
} = useWs("stats", "");
return { payload };
}
export function useMotionActivity(camera: string): { payload: string } { export function useMotionActivity(camera: string): { payload: string } {
const { const {
value: { payload }, value: { payload },

View File

@ -34,7 +34,7 @@ function Sidebar({
/> />
))} ))}
</div> </div>
<SettingsNavItems className="hidden md:flex flex-col items-center" /> <SettingsNavItems className="hidden md:flex flex-col items-center mb-8" />
</aside> </aside>
); );

View File

@ -0,0 +1,84 @@
import { useFrigateStats } from "@/api/ws";
import { FrigateStats } from "@/types/stats";
import { useMemo } from "react";
import { MdCircle } from "react-icons/md";
import useSWR from "swr";
export default function Statusbar({}) {
const { data: initialStats } = useSWR<FrigateStats>("stats", {
revalidateOnFocus: false,
});
const { payload: latestStats } = useFrigateStats();
const stats = useMemo(() => {
if (latestStats) {
return latestStats;
}
return initialStats;
}, [initialStats, latestStats]);
const cpuPercent = useMemo(() => {
const systemCpu = stats?.cpu_usages["frigate.full_system"]?.cpu;
if (!systemCpu || systemCpu == "0.0") {
return null;
}
return parseInt(systemCpu);
}, [stats]);
return (
<div className="absolute left-0 bottom-0 right-0 w-full h-8 flex items-center px-4">
{cpuPercent && (
<div className="flex items-center text-sm mr-4">
<MdCircle
className={`w-2 h-2 mr-2 ${
cpuPercent < 50
? "text-green-500"
: cpuPercent < 80
? "text-orange-400"
: "text-danger"
}`}
/>
CPU {cpuPercent}%
</div>
)}
{Object.entries(stats?.gpu_usages || {}).map(([name, stats]) => {
if (name == "error-gpu") {
return;
}
let gpuTitle;
switch (name) {
case "amd-vaapi":
gpuTitle = "AMD GPU";
break;
case "intel-vaapi":
case "intel-qsv":
gpuTitle = "Intel GPU";
break;
default:
gpuTitle = name;
break;
}
const gpu = parseInt(stats.gpu);
return (
<div className="flex items-center text-sm">
<MdCircle
className={`w-2 h-2 mr-2 ${
gpu < 50
? "text-green-500"
: gpu < 80
? "text-orange-400"
: "text-danger"
}`}
/>
{gpuTitle} {gpu}%
</div>
);
})}
</div>
);
}

View File

@ -79,7 +79,7 @@ function Live() {
}, []); }, []);
return ( return (
<div className="w-full h-full overflow-scroll"> <div className="w-full h-full overflow-scroll px-2">
{events && events.length > 0 && ( {events && events.length > 0 && (
<ScrollArea> <ScrollArea>
<TooltipProvider> <TooltipProvider>

60
web/src/types/stats.ts Normal file
View File

@ -0,0 +1,60 @@
export interface FrigateStats {
cameras: { [camera_name: string]: CameraStats };
cpu_usages: { [pid: string]: CpuStats };
detectors: { [detectorKey: string]: DetectorStats };
gpu_usages?: { [gpuKey: string]: GpuStats };
processes: { [processKey: string]: ExtraProcessStats };
service: ServiceStats;
detection_fps: number;
}
export type CameraStats = {
audio_dBFPS: number;
audio_rms: number;
camera_fps: number;
capture_pid: number;
detection_enabled: number;
detection_fps: number;
ffmpeg_pid: number;
pid: number;
process_fps: number;
skipped_fps: number;
};
export type CpuStats = {
cmdline: string;
cpu: string;
cpu_average: string;
mem: string;
};
export type DetectorStats = {
detection_start: number;
inference_speed: number;
pid: number;
};
export type ExtraProcessStats = {
pid: number;
};
export type GpuStats = {
gpu: string;
mem: string;
};
export type ServiceStats = {
last_updated: number;
storage: { [path: string]: StorageStats };
temperatures: { [apex: string]: number };
update: number;
latest_version: string;
version: string;
};
export type StorageStats = {
free: number;
total: number;
used: number;
mount_type: string;
};