feat: SQLite persistence, non-blocking inference, modular frontend
Backend:
- Persist alert events and robot delivery counters in SQLite (data/app.db,
YOLO_DB_PATH override); EventStore keeps its interface, DeliveryStatsStore
uses read/write-through UPSERT; in-memory fallback remains for standalone
AlertManager use
- Serve /api/detect from a sync endpoint and serialize YOLO inference with a
lock, so concurrent requests no longer block the event loop
- Unify single-image and session detection: requests without session_id
share the fixed single-image session and run full frame confirmation
- Store UTC Z-suffixed timestamps for sortable string comparison; close
executor and database on shutdown via FastAPI lifespan
Frontend:
- Split app.js into ES modules (main.js + modules/{dom,api,ui,charts,store}
+ views/{dashboard,inspection,events,robots,robotCards}), no build step
- Escape all server data interpolated into innerHTML; guard missing
event.classes; replace lazy element ID list with memoized qs()
- Remove hardcoded fake stats (trend badge, device donut segment)
Docs:
- Rewrite README: accurate weight policy (only trained best.pt committed,
no Git LFS), SQLite persistence, single-worker note, data asset inventory
- Align models/pretrained/README.md with actual files; add YOLO_DB_PATH to
.env.example; add persistence unit tests; ruff clean
This commit is contained in:
37
frontend/main.js
Normal file
37
frontend/main.js
Normal file
@@ -0,0 +1,37 @@
|
||||
import { qs, VIEW_LABELS } from "./modules/dom.js";
|
||||
import { updateClock } from "./modules/ui.js";
|
||||
import { loadDashboard } from "./views/dashboard.js";
|
||||
import { loadEvents, initEvents } from "./views/events.js";
|
||||
import { loadRobots, initRobots } from "./views/robots.js";
|
||||
import { initInspection, resetSession } from "./views/inspection.js";
|
||||
import { renderRobots } from "./views/robotCards.js";
|
||||
|
||||
function switchView(viewName) {
|
||||
if (!VIEW_LABELS[viewName]) return;
|
||||
document.querySelectorAll("[data-view-panel]").forEach((panel) => {
|
||||
panel.classList.toggle("is-active", panel.dataset.viewPanel === viewName);
|
||||
});
|
||||
document.querySelectorAll("[data-view]").forEach((button) => {
|
||||
button.classList.toggle("is-active", button.dataset.view === viewName);
|
||||
});
|
||||
qs("#viewTitle").textContent = VIEW_LABELS[viewName];
|
||||
qs("#viewBreadcrumb").textContent = VIEW_LABELS[viewName];
|
||||
qs("#sidebar").classList.remove("is-open");
|
||||
if (viewName === "events") loadEvents();
|
||||
if (viewName === "robots") loadRobots();
|
||||
if (["overview", "risks", "settings"].includes(viewName)) loadDashboard();
|
||||
}
|
||||
|
||||
document.querySelectorAll("[data-view], [data-view-jump]").forEach((control) => {
|
||||
control.addEventListener("click", () => switchView(control.dataset.view || control.dataset.viewJump));
|
||||
});
|
||||
qs("#menuButton").addEventListener("click", () => qs("#sidebar").classList.toggle("is-open"));
|
||||
|
||||
initInspection();
|
||||
initEvents();
|
||||
initRobots();
|
||||
updateClock();
|
||||
setInterval(updateClock, 1000);
|
||||
resetSession();
|
||||
renderRobots();
|
||||
Promise.all([loadDashboard(), loadEvents()]);
|
||||
Reference in New Issue
Block a user