forked from zakaria/chicken-counting-sukawarna-det
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
This commit is contained in:
1 parent
b9f4ba2ef8
commit
6cddf2c9d3
4 files changed
+16
-9
No files matched your search
@@ -6,7 +6,7 @@ After=network.target
|
|||||||
Type=simple
|
Type=simple
|
||||||
User=dsutanto
|
User=dsutanto
|
||||||
WorkingDirectory=/media/jetson/DATA/.Codes/chicken-counting-sukawarna-det
|
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
|
Restart=always
|
||||||
RestartSec=5
|
RestartSec=5
|
||||||
Environment=PYTHONUNBUFFERED=1
|
Environment=PYTHONUNBUFFERED=1
|
||||||
|
|||||||
+7
-4
@@ -14,7 +14,8 @@ import sqlite3
|
|||||||
import threading
|
import threading
|
||||||
from pathlib import Path
|
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
|
# Suppress Flask request logs
|
||||||
log = logging.getLogger("werkzeug")
|
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"
|
frame_path = Path(app.config["shm_dir"]) / f"chicken_counter_{camera_id}" / "frame.jpg"
|
||||||
if not frame_path.exists():
|
if not frame_path.exists():
|
||||||
return jsonify({"error": "not found"}), 404
|
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():
|
def _get_db():
|
||||||
@@ -205,7 +209,6 @@ def main():
|
|||||||
parser.add_argument("--poll-ms", type=int, default=500)
|
parser.add_argument("--poll-ms", type=int, default=500)
|
||||||
parser.add_argument("--date", default="", help="Processing date")
|
parser.add_argument("--date", default="", help="Processing date")
|
||||||
parser.add_argument("--db", default="chicken_counts.db", help="SQLite database path")
|
parser.add_argument("--db", default="chicken_counts.db", help="SQLite database path")
|
||||||
parser.add_argument("--debug", action="store_true")
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
app.config["shm_dir"] = args.shm_dir
|
app.config["shm_dir"] = args.shm_dir
|
||||||
@@ -217,7 +220,7 @@ def main():
|
|||||||
_init_db(app.config["db_path"])
|
_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 ""))
|
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__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -121,11 +121,10 @@ function selectCam(id) {
|
|||||||
function poll() {
|
function poll() {
|
||||||
if (!activeCam) return;
|
if (!activeCam) return;
|
||||||
var t = Date.now();
|
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 => {
|
fetch("/shm/" + activeCam + "/stats.json?t=" + t).then(r => r.ok ? r.json() : null).then(s => {
|
||||||
if (!s) { setOffline(); return; }
|
if (!s) { setOffline(); return; }
|
||||||
lastUpdate = Date.now();
|
lastUpdate = Date.now();
|
||||||
|
var oldFrame = lastFramePerCam[activeCam] || 0;
|
||||||
lastFramePerCam[activeCam] = s.frame_index;
|
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 };
|
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;
|
document.getElementById("s-total").textContent = s.total_entered_count;
|
||||||
@@ -138,6 +137,10 @@ function poll() {
|
|||||||
tag.textContent = activeCam + " \u2022 " + status;
|
tag.textContent = activeCam + " \u2022 " + status;
|
||||||
tag.className = "tag " + (s.backward_active ? "off" : "live");
|
tag.className = "tag " + (s.backward_active ? "off" : "live");
|
||||||
document.getElementById("cam-count").textContent = s.total_entered_count;
|
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;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-2
@@ -1,7 +1,8 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -euo pipefail
|
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=(
|
dates=(
|
||||||
"2026-06-10" "2026-06-11" "2026-06-12" "2026-06-13" "2026-06-14"
|
"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
|
for date in "${dates[@]}"; do
|
||||||
echo "=== Processing $date ==="
|
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
|
done
|
||||||
Reference in new issue
Block a user