From 6cddf2c9d34945e574c88f1bb163cb8d7f03bdac Mon Sep 17 00:00:00 2001 From: dsutanto Date: Wed, 22 Jul 2026 16:14:03 +0700 Subject: [PATCH] Fix dashboard freeze: waitress, reduce frame refresh, atomic reads - Switch Flask dev server -> waitress (8 threads, production WSGI) - Only refresh frame.jpg when frame_index changes, not every second - Safety try/except on send_file for edge-case file conflicts - Fix test_run.sh: export PYTHONPATH, use venv python - Update service file to use venv python --- chicken-dashboard.service | 2 +- dashboard.py | 11 +++++++---- templates/index.html | 7 +++++-- test_run.sh | 5 +++-- 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/chicken-dashboard.service b/chicken-dashboard.service index 30d8ed1..bf40484 100644 --- a/chicken-dashboard.service +++ b/chicken-dashboard.service @@ -6,7 +6,7 @@ After=network.target 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 +ExecStart=/media/jetson/DATA/karung-sukawarna/venv/bin/python dashboard.py --port 8080 --db chicken_counts.db Restart=always RestartSec=5 Environment=PYTHONUNBUFFERED=1 diff --git a/dashboard.py b/dashboard.py index 94e8c02..a131c9a 100755 --- a/dashboard.py +++ b/dashboard.py @@ -14,7 +14,8 @@ import sqlite3 import threading from pathlib import Path -from flask import Flask, Response, jsonify, render_template, send_file +from flask import Flask, jsonify, render_template, send_file +from waitress import serve # Suppress Flask request logs log = logging.getLogger("werkzeug") @@ -103,7 +104,10 @@ def shm_frame(camera_id): frame_path = Path(app.config["shm_dir"]) / f"chicken_counter_{camera_id}" / "frame.jpg" if not frame_path.exists(): return jsonify({"error": "not found"}), 404 - return send_file(frame_path, mimetype="image/jpeg", max_age=0, download_name=None) + try: + return send_file(frame_path, mimetype="image/jpeg", max_age=2, download_name=None) + except (FileNotFoundError, PermissionError): + return jsonify({"error": "not ready"}), 503 def _get_db(): @@ -205,7 +209,6 @@ def main(): parser.add_argument("--poll-ms", type=int, default=500) parser.add_argument("--date", default="", help="Processing date") parser.add_argument("--db", default="chicken_counts.db", help="SQLite database path") - parser.add_argument("--debug", action="store_true") args = parser.parse_args() app.config["shm_dir"] = args.shm_dir @@ -217,7 +220,7 @@ def main(): _init_db(app.config["db_path"]) print(f"[dashboard] http://0.0.0.0:{args.port} shm={args.shm_dir} db={args.db}" + (f" date={args.date}" if args.date else "")) - app.run(host="0.0.0.0", port=args.port, debug=args.debug, threaded=True) + serve(app, host="0.0.0.0", port=args.port, threads=8) if __name__ == "__main__": diff --git a/templates/index.html b/templates/index.html index cc6b25f..fb56837 100644 --- a/templates/index.html +++ b/templates/index.html @@ -121,11 +121,10 @@ function selectCam(id) { function poll() { if (!activeCam) return; var t = Date.now(); - var img = document.getElementById("frame-img"); - img.src = "/shm/" + activeCam + "/frame.jpg?t=" + t; fetch("/shm/" + activeCam + "/stats.json?t=" + t).then(r => r.ok ? r.json() : null).then(s => { if (!s) { setOffline(); return; } lastUpdate = Date.now(); + var oldFrame = lastFramePerCam[activeCam] || 0; lastFramePerCam[activeCam] = s.frame_index; perCam[activeCam] = { total: s.total_entered_count, inside: s.inside_box_count, frame: s.frame_index, tracks: s.track_count, speed: s.smoothed_speed }; document.getElementById("s-total").textContent = s.total_entered_count; @@ -138,6 +137,10 @@ function poll() { tag.textContent = activeCam + " \u2022 " + status; tag.className = "tag " + (s.backward_active ? "off" : "live"); document.getElementById("cam-count").textContent = s.total_entered_count; + // only refresh frame when index changes (every 5 frames with interval_frames=5) + if (s.frame_index != oldFrame) { + document.getElementById("frame-img").src = "/shm/" + activeCam + "/frame.jpg?t=" + t; + } }); } diff --git a/test_run.sh b/test_run.sh index 10d7b0e..40f953d 100755 --- a/test_run.sh +++ b/test_run.sh @@ -1,7 +1,8 @@ #!/bin/bash set -euo pipefail -CMD="PYTHONPATH=/media/jetson/DATA/.Codes/chicken-counting-sukawarna-det/src /media/jetson/DATA/karung-sukawarna/venv/bin/python -m chicken_counter.cli" +export PYTHONPATH=/media/jetson/DATA/.Codes/chicken-counting-sukawarna-det/src +PYTHON=/media/jetson/DATA/karung-sukawarna/venv/bin/python dates=( "2026-06-10" "2026-06-11" "2026-06-12" "2026-06-13" "2026-06-14" @@ -10,5 +11,5 @@ dates=( for date in "${dates[@]}"; do echo "=== Processing $date ===" - $CMD batch --config configs/cycle7_batch.yaml --date "$date" --no-video + $PYTHON -m chicken_counter.cli batch --config configs/cycle7_batch.yaml --date "$date" --no-video done