diff --git a/batch_store.py b/batch_store.py index 94ee985..f2dfc2e 100644 --- a/batch_store.py +++ b/batch_store.py @@ -194,8 +194,20 @@ class BatchStore: self.ignore_batch_label = False self.log('Ignore batch label cooldown finished') + def _check_reset_signal(self): + reset_file = Path(str(self.state_file) + ".reset") + if reset_file.exists(): + reset_file.unlink(missing_ok=True) + if self.current_state is not None: + self.log("External reset signal — clearing in-memory state") + self.current_state = None + if self.batch_timer: + self.batch_timer.cancel() + self.batch_timer = None + def record_ayam_crossing(self, track_id): """Line-cross equivalent of production ayam-potong MQTT event.""" + self._check_reset_signal() with self.state_lock: counting_date = self.get_counting_date() started_new = False @@ -223,6 +235,7 @@ class BatchStore: def record_talenan_crossing(self, track_id): """Line-cross equivalent of production telenan MQTT batch close.""" + self._check_reset_signal() if self.ignore_batch_label: return False with self.state_lock: diff --git a/counter_dashboard.py b/counter_dashboard.py index 45e3ad4..a568a59 100644 --- a/counter_dashboard.py +++ b/counter_dashboard.py @@ -548,6 +548,16 @@ def export_day_xlsx(date): return _excel_response(wb, filename) +@app.route("/api/reset", methods=["POST"]) +def api_reset(): + try: + Path(CURRENT_BATCH_PATH).unlink(missing_ok=True) + Path(str(CURRENT_BATCH_PATH) + ".reset").touch() + except Exception: + pass + return jsonify({"success": True, "message": "State file cleared"}) + + if __name__ == "__main__": WSGIRequestHandler.protocol_version = "HTTP/1.1" print(f"Jetson counter dashboard at http://{DASHBOARD_HOST}:{DASHBOARD_PORT}") diff --git a/recounting_dashboard.py b/recounting_dashboard.py index 0d52856..c3dc477 100644 --- a/recounting_dashboard.py +++ b/recounting_dashboard.py @@ -4,6 +4,7 @@ Recounting dashboard — consumes live counter + recount APIs, lists OUTPUT_DIR MP4 files, streams MP4 via ffmpeg RTSP → go2rtc for preview. """ import json +import logging import os import re import subprocess @@ -18,6 +19,9 @@ from werkzeug.serving import WSGIRequestHandler from dotenv import load_dotenv load_dotenv() +log = logging.getLogger('werkzeug') +log.setLevel(logging.ERROR) + app = Flask(__name__, template_folder="templates") app.config["SECRET_KEY"] = os.getenv("SECRET_KEY", "change-me-in-production") @@ -126,6 +130,13 @@ def start_recount(): with _streaming_lock: _stop_ffmpeg_locked() + + try: + _http_session.post(f"{RECOUNT_API_URL}/api/reset", timeout=5) + except Exception: + pass + + with _streaming_lock: _current_file = mp4_path cmd = [ "ffmpeg", "-re", "-i", mp4_path,