From db192823ce75cb64e8baff9b270222ca2f35a081 Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Thu, 12 Mar 2026 14:28:32 -0500 Subject: [PATCH] add shm frame lifetime calculation and update UI for shared memory metrics --- web/public/locales/en/views/system.json | 6 +- web/src/types/stats.ts | 1 + web/src/views/system/StorageMetrics.tsx | 126 +++++++++++++++++------- 3 files changed, 96 insertions(+), 37 deletions(-) diff --git a/web/public/locales/en/views/system.json b/web/public/locales/en/views/system.json index faaff31c9..460a1d337 100644 --- a/web/public/locales/en/views/system.json +++ b/web/public/locales/en/views/system.json @@ -135,7 +135,11 @@ }, "shm": { "title": "SHM (shared memory) allocation", - "warning": "The current SHM size of {{total}}MB is too small. Increase it to at least {{min_shm}}MB." + "warning": "The current SHM size of {{total}}MB is too small. Increase it to at least {{min_shm}}MB.", + "frameLifetime": { + "title": "Frame lifetime", + "description": "Each camera has {{frames}} frame slots in shared memory. At the fastest camera's frame rate, each frame is available for approximately {{lifetime}}s before being overwritten." + } }, "cameraStorage": { "title": "Camera Storage", diff --git a/web/src/types/stats.ts b/web/src/types/stats.ts index 8b22849be..1046e0b47 100644 --- a/web/src/types/stats.ts +++ b/web/src/types/stats.ts @@ -86,6 +86,7 @@ export type StorageStats = { used: number; mount_type: string; min_shm?: number; + shm_frame_count?: number; }; export type PotentialProblem = { diff --git a/web/src/views/system/StorageMetrics.tsx b/web/src/views/system/StorageMetrics.tsx index 2c222d6c3..d36200849 100644 --- a/web/src/views/system/StorageMetrics.tsx +++ b/web/src/views/system/StorageMetrics.tsx @@ -89,6 +89,35 @@ export default function StorageMetrics({ timezone, ); + const shmFrameLifetime = useMemo(() => { + if (!stats || !config) { + return undefined; + } + + const shmFrameCount = stats.service.storage["/dev/shm"]?.shm_frame_count; + + if (!shmFrameCount || shmFrameCount <= 0) { + return undefined; + } + + let maxCameraFps = 0; + + for (const [name, camStats] of Object.entries(stats.cameras)) { + if (config.cameras[name]?.enabled && camStats.camera_fps > 0) { + maxCameraFps = Math.max(maxCameraFps, camStats.camera_fps); + } + } + + if (maxCameraFps === 0) { + return undefined; + } + + return { + frames: shmFrameCount, + lifetime: Math.round((shmFrameCount / maxCameraFps) * 10) / 10, + }; + }, [stats, config]); + if (!cameraStorage || !stats || !totalStorage || !config) { return; } @@ -148,43 +177,68 @@ export default function StorageMetrics({
/dev/shm - {stats.service.storage["/dev/shm"]["total"] < - (stats.service.storage["/dev/shm"]["min_shm"] ?? 0) && ( - - - - - -
- {t("storage.shm.warning", { - total: stats.service.storage["/dev/shm"]["total"], - min_shm: stats.service.storage["/dev/shm"]["min_shm"], - })} -
- - {t("readTheDocumentation", { ns: "common" })} - - +
+ {shmFrameLifetime && ( + + + + + +
+ {t("storage.shm.frameLifetime.description", { + frames: shmFrameLifetime.frames, + lifetime: shmFrameLifetime.lifetime, + })}
-
- - - )} + + + )} + {stats.service.storage["/dev/shm"]["total"] < + (stats.service.storage["/dev/shm"]["min_shm"] ?? 0) && ( + + + + + +
+ {t("storage.shm.warning", { + total: stats.service.storage["/dev/shm"]["total"], + min_shm: stats.service.storage["/dev/shm"]["min_shm"], + })} +
+ + {t("readTheDocumentation", { ns: "common" })} + + +
+
+
+
+ )} +