From dc447264b7f86eb92ac0bdfe9089db964e5f45b8 Mon Sep 17 00:00:00 2001 From: dsutanto Date: Wed, 22 Jul 2026 14:56:10 +0700 Subject: [PATCH] Reduce dashboard CPU: cache DB, suppress logs, add systemd service - Suppress Flask request logs (werkzeug ERROR only) - Cache SQLite connection with WAL + 8MB cache - Increase poll_ms 500->1000, history refresh 30s->60s - Show 'waiting for pipeline...' when /dev/shm empty - Add chicken-dashboard.service for systemd auto-start --- chicken-dashboard.service | 15 +++++++++++++++ dashboard.py | 30 ++++++++++++++++++++++++------ templates/index.html | 24 +++++++++++++++++++----- 3 files changed, 58 insertions(+), 11 deletions(-) create mode 100644 chicken-dashboard.service diff --git a/chicken-dashboard.service b/chicken-dashboard.service new file mode 100644 index 0000000..30d8ed1 --- /dev/null +++ b/chicken-dashboard.service @@ -0,0 +1,15 @@ +[Unit] +Description=Chicken Counter Dashboard +After=network.target + +[Service] +Type=simple +User=dsutanto +WorkingDirectory=/media/jetson/DATA/.Codes/chicken-counting-sukawarna-det +ExecStart=/usr/bin/python3 dashboard.py --port 8080 --db chicken_counts.db +Restart=always +RestartSec=5 +Environment=PYTHONUNBUFFERED=1 + +[Install] +WantedBy=multi-user.target diff --git a/dashboard.py b/dashboard.py index d49c0e6..a010ac3 100755 --- a/dashboard.py +++ b/dashboard.py @@ -9,20 +9,43 @@ from __future__ import annotations import argparse import json +import logging import sqlite3 +import threading from pathlib import Path from flask import Flask, Response, jsonify, render_template, send_file +# Suppress Flask request logs +log = logging.getLogger("werkzeug") +log.setLevel(logging.ERROR) + DEFAULT_SHM_DIR = "/dev/shm" DEFAULT_PORT = 8080 app = Flask(__name__) app.config["shm_dir"] = DEFAULT_SHM_DIR -app.config["poll_ms"] = 500 +app.config["poll_ms"] = 1000 app.config["run_date"] = "" app.config["db_path"] = "" +_db_conn = None +_db_lock = threading.Lock() + + +def _get_db(): + global _db_conn + db = app.config["db_path"] + if not db or not Path(db).exists(): + return None + with _db_lock: + if _db_conn is None: + _db_conn = sqlite3.connect(db, check_same_thread=False) + _db_conn.row_factory = sqlite3.Row + _db_conn.execute("PRAGMA journal_mode=WAL") + _db_conn.execute("PRAGMA cache_size=-8000") + return _db_conn + def _discover_cameras(): shm = Path(app.config["shm_dir"]) @@ -88,7 +111,6 @@ def db_summary(): ROUND(SUM(elapsed_seconds)/3600.0, 1) AS total_hours FROM batch_runs """).fetchone() - conn.close() return jsonify(dict(row)) @@ -107,7 +129,6 @@ def db_history(): ORDER BY date DESC, location LIMIT 50 """).fetchall() - conn.close() return jsonify([dict(r) for r in rows]) @@ -125,7 +146,6 @@ def db_date(date): total = conn.execute( "SELECT SUM(total_entered) AS total, ROUND(SUM(elapsed_seconds)/60.0,1) AS minutes " "FROM batch_runs WHERE date=?", (date,)).fetchone() - conn.close() return jsonify({"date": date, "total": dict(total), "cameras": [dict(r) for r in cameras]}) @@ -139,7 +159,6 @@ def db_camera(camera_id): ROUND(elapsed_seconds,1) AS elapsed_seconds, stopped_reason FROM batch_runs WHERE camera_id=? ORDER BY date DESC LIMIT 50 """, (camera_id,)).fetchall() - conn.close() return jsonify([dict(r) for r in rows]) @@ -159,7 +178,6 @@ def db_location(location): ROUND(SUM(elapsed_seconds)/3600.0,1) AS hours FROM batch_runs WHERE location=? """, (location,)).fetchone() - conn.close() return jsonify({"location": location, "summary": dict(summary), "history": [dict(r) for r in history]}) diff --git a/templates/index.html b/templates/index.html index 0245579..cc6b25f 100644 --- a/templates/index.html +++ b/templates/index.html @@ -45,10 +45,12 @@ body{font-family:system-ui,monospace;background:#0f0f14;color:#e0e0e0;overflow:h
-live stream -
-
CC1 • RUNNING
-
--
+ + +
waiting for pipeline...
+