Procházet zdrojové kódy

New stats module, refactor stats generation out of http module.

StatsEmitter thread to send stats to MQTT every 60 seconds by default, optional stats_interval config value.

New service stats attribute, containing uptime in seconds and version.
Nat Morris před 4 roky
rodič
revize
76403bba8e
4 změnil soubory, kde provedl 94 přidání a 30 odebrání
  1. 12 1
      frigate/app.py
  2. 8 1
      frigate/config.py
  3. 4 28
      frigate/http.py
  4. 70 0
      frigate/stats.py

+ 12 - 1
frigate/app.py

@@ -20,6 +20,7 @@ from frigate.models import Event
 from frigate.mqtt import create_mqtt_client
 from frigate.object_processing import TrackedObjectProcessor
 from frigate.record import RecordingMaintainer
+from frigate.stats import StatsEmitter, stats_init
 from frigate.video import capture_camera, track_camera
 from frigate.watchdog import FrigateWatchdog
 from frigate.zeroconf import broadcast_zeroconf
@@ -115,8 +116,11 @@ class FrigateApp():
         self.db.bind(models)
         self.db.create_tables(models, safe=True)
 
+    def init_stats(self):
+        self.stats_tracking = stats_init(self.camera_metrics, self.detectors)
+
     def init_web_server(self):
-        self.flask_app = create_app(self.config, self.db, self.camera_metrics, self.detectors, self.detected_frames_processor)
+        self.flask_app = create_app(self.config, self.db, self.stats_tracking, self.detected_frames_processor)
 
     def init_mqtt(self):
         self.mqtt_client = create_mqtt_client(self.config)
@@ -173,6 +177,10 @@ class FrigateApp():
         self.recording_maintainer = RecordingMaintainer(self.config, self.stop_event)
         self.recording_maintainer.start()
 
+    def start_stats_emitter(self):
+        self.stats_emitter = StatsEmitter(self.config, self.stats_tracking, self.mqtt_client, self.config.mqtt.topic_prefix, self.stop_event)
+        self.stats_emitter.start()
+
     def start_watchdog(self):
         self.frigate_watchdog = FrigateWatchdog(self.detectors, self.stop_event)
         self.frigate_watchdog.start()
@@ -200,10 +208,12 @@ class FrigateApp():
         self.start_detected_frames_processor()
         self.start_camera_processors()
         self.start_camera_capture_processes()
+        self.init_stats()
         self.init_web_server()
         self.start_event_processor()
         self.start_event_cleanup()
         self.start_recording_maintainer()
+        self.start_stats_emitter()
         self.start_watchdog()
         # self.zeroconf = broadcast_zeroconf(self.config.mqtt.client_id)
 
@@ -224,6 +234,7 @@ class FrigateApp():
         self.event_processor.join()
         self.event_cleanup.join()
         self.recording_maintainer.join()
+        self.stats_emitter.join()
         self.frigate_watchdog.join()
 
         for detector in self.detectors.values():

+ 8 - 1
frigate/config.py

@@ -37,6 +37,7 @@ MQTT_SCHEMA = vol.Schema(
         vol.Optional('port', default=1883): int,
         vol.Optional('topic_prefix', default='frigate'): str,
         vol.Optional('client_id', default='frigate'): str,
+        vol.Optional('stats_interval', default=60): int,
         'user': str,
         'password': str
     }
@@ -324,6 +325,7 @@ class MqttConfig():
         self._client_id = config['client_id']
         self._user = config.get('user')
         self._password = config.get('password')
+        self._stats_interval = config.get('stats_interval')
     
     @property
     def host(self):
@@ -349,13 +351,18 @@ class MqttConfig():
     def password(self):
         return self._password
 
+    @property
+    def stats_interval(self):
+        return self._stats_interval
+
     def to_dict(self):
         return {
             'host': self.host,
             'port': self.port,
             'topic_prefix': self.topic_prefix,
             'client_id': self.client_id,
-            'user': self.user
+            'user': self.user,
+            'stats_interval': self.stats_interval
         }
 
 class CameraInput():

+ 4 - 28
frigate/http.py

@@ -13,6 +13,7 @@ from peewee import SqliteDatabase, operator, fn, DoesNotExist
 from playhouse.shortcuts import model_to_dict
 
 from frigate.models import Event
+from frigate.stats import stats_snapshot
 from frigate.util import calculate_region
 from frigate.version import VERSION
 
@@ -20,7 +21,7 @@ logger = logging.getLogger(__name__)
 
 bp = Blueprint('frigate', __name__)
 
-def create_app(frigate_config, database: SqliteDatabase, camera_metrics, detectors, detected_frames_processor):
+def create_app(frigate_config, database: SqliteDatabase, stats_tracking, detected_frames_processor):
     app = Flask(__name__)
 
     @app.before_request
@@ -33,8 +34,7 @@ def create_app(frigate_config, database: SqliteDatabase, camera_metrics, detecto
             database.close()
 
     app.frigate_config = frigate_config
-    app.camera_metrics = camera_metrics
-    app.detectors = detectors
+    app.stats_tracking = stats_tracking
     app.detected_frames_processor = detected_frames_processor
     
     app.register_blueprint(bp)
@@ -152,31 +152,7 @@ def version():
 
 @bp.route('/stats')
 def stats():
-    camera_metrics = current_app.camera_metrics
-    stats = {}
-
-    total_detection_fps = 0
-
-    for name, camera_stats in camera_metrics.items():
-        total_detection_fps += camera_stats['detection_fps'].value
-        stats[name] = {
-            'camera_fps': round(camera_stats['camera_fps'].value, 2),
-            'process_fps': round(camera_stats['process_fps'].value, 2),
-            'skipped_fps': round(camera_stats['skipped_fps'].value, 2),
-            'detection_fps': round(camera_stats['detection_fps'].value, 2),
-            'pid': camera_stats['process'].pid,
-            'capture_pid': camera_stats['capture_process'].pid
-        }
-    
-    stats['detectors'] = {}
-    for name, detector in current_app.detectors.items():
-        stats['detectors'][name] = {
-            'inference_speed': round(detector.avg_inference_speed.value*1000, 2),
-            'detection_start': detector.detection_start.value,
-            'pid': detector.detect_process.pid
-        }
-    stats['detection_fps'] = round(total_detection_fps, 2)
-
+    stats = stats_snapshot(current_app.stats_tracking)
     return jsonify(stats)
 
 @bp.route('/<camera_name>/<label>/best.jpg')

+ 70 - 0
frigate/stats.py

@@ -0,0 +1,70 @@
+import json
+import logging
+import threading
+import time
+
+from frigate.config import FrigateConfig
+from frigate.version import VERSION
+
+logger = logging.getLogger(__name__)
+
+def stats_init(camera_metrics, detectors):
+    stats_tracking = {
+        'camera_metrics': camera_metrics,
+        'detectors': detectors,
+        'started': int(time.time())
+    }
+    return stats_tracking
+
+def stats_snapshot(stats_tracking):
+    camera_metrics = stats_tracking['camera_metrics']
+    stats = {}
+
+    total_detection_fps = 0
+
+    for name, camera_stats in camera_metrics.items():
+        total_detection_fps += camera_stats['detection_fps'].value
+        stats[name] = {
+            'camera_fps': round(camera_stats['camera_fps'].value, 2),
+            'process_fps': round(camera_stats['process_fps'].value, 2),
+            'skipped_fps': round(camera_stats['skipped_fps'].value, 2),
+            'detection_fps': round(camera_stats['detection_fps'].value, 2),
+            'pid': camera_stats['process'].pid,
+            'capture_pid': camera_stats['capture_process'].pid
+        }
+
+    stats['detectors'] = {}
+    for name, detector in stats_tracking["detectors"].items():
+        stats['detectors'][name] = {
+            'inference_speed': round(detector.avg_inference_speed.value * 1000, 2),
+            'detection_start': detector.detection_start.value,
+            'pid': detector.detect_process.pid
+        }
+    stats['detection_fps'] = round(total_detection_fps, 2)
+
+    stats['service'] = {
+        'uptime': (int(time.time()) - stats_tracking['started']),
+        'version': VERSION
+    }
+
+    return stats
+
+class StatsEmitter(threading.Thread):
+    def __init__(self, config: FrigateConfig, stats_tracking, mqtt_client, topic_prefix, stop_event):
+        threading.Thread.__init__(self)
+        self.name = 'frigate_stats_emitter'
+        self.config = config
+        self.stats_tracking = stats_tracking
+        self.mqtt_client = mqtt_client
+        self.topic_prefix = topic_prefix
+        self.stop_event = stop_event
+
+    def run(self):
+        time.sleep(10)
+        while True:
+            if self.stop_event.is_set():
+                logger.info(f"Exiting watchdog...")
+                break
+            stats = stats_snapshot(self.stats_tracking)
+            self.mqtt_client.publish(f"{self.topic_prefix}/stats", json.dumps(stats), retain=False)
+            time.sleep(self.config.mqtt.stats_interval)