diff --git a/cleanup_orphan_rois.py b/cleanup_orphan_rois.py new file mode 100644 index 0000000..c4e2ada --- /dev/null +++ b/cleanup_orphan_rois.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python3 +""" +清理孤儿ROI配置 +- 删除没有对应摄像头配置的ROI记录 +- 删除关联的算法绑定记录 +""" +import sys +from pathlib import Path +sys.path.insert(0, str(Path(__file__).parent)) + +from core.config_sync import get_config_sync_manager + +def main(): + print("=== Orphan ROI Cleanup Tool ===\n") + + config_manager = get_config_sync_manager() + + # 1. Get all cameras and ROIs + cameras = config_manager.get_cameras() + camera_ids = {c.camera_id for c in cameras} + + rois = config_manager.get_roi_configs(force_refresh=True) + binds = config_manager.get_bindings() + + print(f"Current Status:") + print(f" - Total cameras: {len(cameras)}") + print(f" - Total ROIs: {len(rois)}") + print(f" - Total bindings: {len(binds)}") + + # 2. Find orphan ROIs + orphan_rois = [roi for roi in rois if roi.camera_id not in camera_ids] + + if not orphan_rois: + print("\n[OK] No orphan ROIs found. Database is clean!") + return + + print(f"\n[PROBLEM] Found {len(orphan_rois)} orphan ROIs:") + for roi in orphan_rois: + print(f" - ROI ID: {roi.roi_id}") + print(f" Camera ID: {roi.camera_id} (MISSING)") + print(f" ROI Type: {roi.roi_type if hasattr(roi, 'roi_type') else 'N/A'}") + print() + + # 3. Find orphan bindings + orphan_roi_ids = {roi.roi_id for roi in orphan_rois} + orphan_binds = [b for b in binds if b.roi_id in orphan_roi_ids] + + if orphan_binds: + print(f"Related orphan bindings: {len(orphan_binds)}") + for bind in orphan_binds: + print(f" - Bind ID: {bind.bind_id}") + print(f" ROI ID: {bind.roi_id}") + print(f" Algorithm: {bind.algo_code}") + print() + + # 4. Ask for confirmation + print("=" * 50) + print("Cleanup Plan:") + print(f" 1. Delete {len(orphan_rois)} orphan ROI records") + print(f" 2. Delete {len(orphan_binds)} orphan binding records") + print("=" * 50) + + response = input("\nProceed with cleanup? (yes/no): ").strip().lower() + + if response != 'yes': + print("\n[CANCELLED] Cleanup aborted by user") + return + + # 5. Perform cleanup + print("\n[EXECUTING] Cleanup in progress...") + + # Initialize database manager + config_manager._init_database() + db_manager = config_manager._db_manager + + if not db_manager: + print("[ERROR] Database manager not available") + return + + deleted_rois = 0 + deleted_binds = 0 + + # Delete orphan bindings first (foreign key constraint) + for bind in orphan_binds: + try: + db_manager.delete_binding(bind.bind_id) + deleted_binds += 1 + print(f" [OK] Deleted binding: {bind.bind_id}") + except Exception as e: + print(f" [FAIL] Failed to delete binding {bind.bind_id}: {e}") + + # Delete orphan ROIs + for roi in orphan_rois: + try: + db_manager.delete_roi(roi.roi_id) + deleted_rois += 1 + print(f" [OK] Deleted ROI: {roi.roi_id}") + except Exception as e: + print(f" [FAIL] Failed to delete ROI {roi.roi_id}: {e}") + + # 6. Invalidate cache + config_manager.invalidate_all_cache() + + # 7. Summary + print("\n" + "=" * 50) + print("Cleanup Summary") + print("=" * 50) + print(f"Deleted ROIs: {deleted_rois}/{len(orphan_rois)}") + print(f"Deleted bindings: {deleted_binds}/{len(orphan_binds)}") + + if deleted_rois == len(orphan_rois) and deleted_binds == len(orphan_binds): + print("\n[SUCCESS] All orphan records cleaned up!") + print("\nNext steps:") + print(" 1. Restart the Edge service to apply changes") + print(" 2. Warnings should disappear from logs") + else: + print("\n[WARNING] Some records failed to delete") + print(" Please check error messages above") + +if __name__ == "__main__": + main() diff --git a/core/config_sync.py b/core/config_sync.py index d2e4f0c..df37cec 100644 --- a/core/config_sync.py +++ b/core/config_sync.py @@ -886,15 +886,17 @@ class ConfigSyncManager: sync_mode = config_data.get("sync_mode", "partial") if sync_mode == "full": self._init_database() - if self._db_manager and incoming_ids: + if self._db_manager: try: existing = self._db_manager.get_all_camera_configs() for cam in existing: old_id = cam.get("camera_id") + # 如果incoming_ids为空,清除所有旧配置 + # 如果incoming_ids不为空,清除不在列表中的旧配置 if old_id and old_id not in incoming_ids: self._clear_rois_for_camera_ids([old_id]) self._db_manager.delete_camera_config(old_id) - logger.info(f"[EDGE] 清除不在推送列表中的旧摄像头: {old_id}") + logger.info(f"[EDGE] 全量同步清除旧摄像头: {old_id}") except Exception as e: logger.warning(f"[EDGE] 清理旧摄像头失败: {e}") else: