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:
41
frontend/views/robots.js
Normal file
41
frontend/views/robots.js
Normal file
@@ -0,0 +1,41 @@
|
||||
import { qs } from "../modules/dom.js";
|
||||
import { apiFetch } from "../modules/api.js";
|
||||
import { showToast, setServiceStatus } from "../modules/ui.js";
|
||||
import { renderRobots } from "./robotCards.js";
|
||||
import { store } from "../modules/store.js";
|
||||
import { loadDashboard } from "./dashboard.js";
|
||||
|
||||
export async function loadRobots() {
|
||||
try {
|
||||
const result = await apiFetch("/api/robots");
|
||||
renderRobots(result, store.dashboardData?.detection);
|
||||
setServiceStatus(true);
|
||||
} catch (error) {
|
||||
setServiceStatus(false);
|
||||
showToast(`无法读取机器人状态:${error.message}`, "error");
|
||||
}
|
||||
}
|
||||
|
||||
export async function testRobot(channel, button) {
|
||||
const originalText = button.textContent;
|
||||
button.disabled = true;
|
||||
button.textContent = "正在发送";
|
||||
try {
|
||||
const result = await apiFetch(`/api/robots/${encodeURIComponent(channel)}/test`, { method: "POST" });
|
||||
showToast(`${result.robot.name}测试消息发送成功`, "success");
|
||||
await Promise.all([loadRobots(), loadDashboard()]);
|
||||
} catch (error) {
|
||||
showToast(`测试消息发送失败:${error.message}`, "error");
|
||||
} finally {
|
||||
button.disabled = false;
|
||||
button.textContent = originalText;
|
||||
}
|
||||
}
|
||||
|
||||
export function initRobots() {
|
||||
qs("#refreshRobotsButton").addEventListener("click", loadRobots);
|
||||
qs("#robotCardGrid").addEventListener("click", (event) => {
|
||||
const button = event.target.closest("[data-test-robot]");
|
||||
if (button) testRobot(button.dataset.testRobot, button);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user