feat: 重构数据库schema分离空间与业务配置 - 新增algorithm_registry和roi_algo_bind表 - roi_configs简化为纯空间配置 - 新增AlgorithmInfo/ROIAlgoBind数据模型
This commit is contained in:
@@ -21,7 +21,7 @@ from redis.client import PubSub
|
||||
|
||||
from config.settings import get_settings, RedisConfig
|
||||
from config.database import get_sqlite_manager, SQLiteManager
|
||||
from config.config_models import CameraInfo as CameraInfoModel, ROIInfo, ConfigVersion
|
||||
from config.config_models import CameraInfo as CameraInfoModel, ROIInfo, ConfigVersion, ROIInfoNew, ROIAlgoBind
|
||||
from utils.version_control import get_version_control
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -282,7 +282,7 @@ class ConfigSyncManager:
|
||||
|
||||
def get_roi_configs(self, camera_id: Optional[str] = None,
|
||||
force_refresh: bool = False) -> List[ROIInfo]:
|
||||
"""获取ROI配置列表"""
|
||||
"""获取ROI配置列表(兼容旧版本)"""
|
||||
cache_key = f"rois_{camera_id}" if camera_id else "rois_all"
|
||||
|
||||
if not force_refresh:
|
||||
@@ -312,6 +312,59 @@ class ConfigSyncManager:
|
||||
cached = self._cache.get(cache_key)
|
||||
return cached or []
|
||||
|
||||
def get_roi_configs_with_bindings(self, camera_id: Optional[str] = None,
|
||||
force_refresh: bool = False) -> List[ROIInfoNew]:
|
||||
"""获取ROI配置列表(包含算法绑定信息)"""
|
||||
cache_key = f"rois_bindings_{camera_id}" if camera_id else "rois_bindings_all"
|
||||
|
||||
if not force_refresh:
|
||||
cached = self._cache.get(cache_key)
|
||||
if cached is not None:
|
||||
return cached
|
||||
|
||||
self._init_database()
|
||||
|
||||
if self._db_manager is None:
|
||||
logger.warning("数据库管理器不可用,返回空ROI配置列表")
|
||||
return []
|
||||
|
||||
try:
|
||||
if camera_id:
|
||||
roi_configs = self._db_manager.get_rois_by_camera(camera_id)
|
||||
bindings_list = self._db_manager.get_bindings_by_camera(camera_id)
|
||||
else:
|
||||
roi_configs = self._db_manager.get_all_roi_configs()
|
||||
bindings_list = []
|
||||
for roi in roi_configs:
|
||||
bindings = self._db_manager.get_bindings_by_roi(roi['roi_id'])
|
||||
bindings_list.extend(bindings)
|
||||
|
||||
roi_dict = {r['roi_id']: r for r in roi_configs}
|
||||
bindings_dict = {}
|
||||
for b in bindings_list:
|
||||
roi_id = b['roi_id']
|
||||
if roi_id not in bindings_dict:
|
||||
bindings_dict[roi_id] = []
|
||||
bindings_dict[roi_id].append(b)
|
||||
|
||||
result = []
|
||||
for roi_id, roi_data in roi_dict.items():
|
||||
roi_info = ROIInfoNew.from_dict(roi_data)
|
||||
if roi_id in bindings_dict:
|
||||
roi_info.bindings = [ROIAlgoBind.from_dict(b) for b in bindings_dict[roi_id]]
|
||||
result.append(roi_info)
|
||||
|
||||
result.sort(key=lambda x: x.priority, reverse=True)
|
||||
|
||||
self._cache.set(cache_key, result)
|
||||
logger.info(f"已加载ROI配置(含绑定): {len(result)} 个")
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"获取ROI配置(含绑定)失败: {e}")
|
||||
cached = self._cache.get(cache_key)
|
||||
return cached or []
|
||||
|
||||
def get_camera_rois(self, camera_id: str) -> List[ROIInfo]:
|
||||
"""获取指定摄像头的ROI配置"""
|
||||
return self.get_roi_configs(camera_id=camera_id)
|
||||
@@ -397,13 +450,8 @@ class ConfigSyncManager:
|
||||
"camera_id": roi_config.get("camera_id", ""),
|
||||
"roi_type": roi_config.get("roi_type", ""),
|
||||
"coordinates": str(roi_config.get("coordinates", [])),
|
||||
"algorithm_type": roi_config.get("algorithm_type", ""),
|
||||
"working_hours": str(roi_config.get("working_hours", [])),
|
||||
"confirm_on_duty_sec": str(roi_config.get("confirm_on_duty_sec", 10)),
|
||||
"confirm_leave_sec": str(roi_config.get("confirm_leave_sec", 10)),
|
||||
"cooldown_sec": str(roi_config.get("cooldown_sec", 300)),
|
||||
"target_class": roi_config.get("target_class", "person"),
|
||||
"enabled": str(roi_config.get("enabled", True)),
|
||||
"priority": str(roi_config.get("priority", 0)),
|
||||
})
|
||||
|
||||
self._redis_client.expire(key, 3600)
|
||||
@@ -413,6 +461,33 @@ class ConfigSyncManager:
|
||||
logger.error(f"缓存ROI配置到Redis失败: {e}")
|
||||
return False
|
||||
|
||||
def _cache_algo_bind_to_redis(self, bind_config: Dict[str, Any]) -> bool:
|
||||
"""将ROI算法绑定配置缓存到Redis"""
|
||||
if not self._redis_client:
|
||||
return False
|
||||
|
||||
try:
|
||||
bind_id = bind_config.get("bind_id")
|
||||
key = f"config:bind:{bind_id}"
|
||||
|
||||
self._redis_client.hset(key, mapping={
|
||||
"bind_id": bind_id,
|
||||
"roi_id": bind_config.get("roi_id", ""),
|
||||
"algo_code": bind_config.get("algo_code", ""),
|
||||
"params": str(bind_config.get("params", {})),
|
||||
"priority": str(bind_config.get("priority", 0)),
|
||||
"enabled": str(bind_config.get("enabled", True)),
|
||||
"algo_name": bind_config.get("algo_name", ""),
|
||||
"target_class": bind_config.get("target_class", "person"),
|
||||
})
|
||||
|
||||
self._redis_client.expire(key, 3600)
|
||||
logger.debug(f"ROI算法绑定配置已缓存到Redis: {key}")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"缓存ROI算法绑定配置到Redis失败: {e}")
|
||||
return False
|
||||
|
||||
def _cache_camera_to_redis(self, camera_config: Dict[str, Any]) -> bool:
|
||||
"""将摄像头配置缓存到Redis"""
|
||||
if not self._redis_client:
|
||||
@@ -454,6 +529,12 @@ class ConfigSyncManager:
|
||||
if self._cache_roi_to_redis(roi):
|
||||
count += 1
|
||||
|
||||
self.clear_redis_cache("bind")
|
||||
bindings = self._db_manager.get_bindings_by_camera("")
|
||||
for bind in bindings:
|
||||
if self._cache_algo_bind_to_redis(bind):
|
||||
count += 1
|
||||
|
||||
logger.info(f"已同步 {count} 条配置到Redis缓存")
|
||||
return count
|
||||
except Exception as e:
|
||||
@@ -469,13 +550,8 @@ class ConfigSyncManager:
|
||||
key = f"config:roi:{roi_id}"
|
||||
data = self._redis_client.hgetall(key)
|
||||
if data:
|
||||
if data.get('coordinates'):
|
||||
data['coordinates'] = eval(data['coordinates'])
|
||||
if data.get('working_hours'):
|
||||
data['working_hours'] = eval(data['working_hours'])
|
||||
data['confirm_on_duty_sec'] = int(data.get('confirm_on_duty_sec', 10))
|
||||
data['confirm_leave_sec'] = int(data.get('confirm_leave_sec', 10))
|
||||
data['cooldown_sec'] = int(data.get('cooldown_sec', 300))
|
||||
data['coordinates'] = eval(data['coordinates']) if data.get('coordinates') else []
|
||||
data['priority'] = int(data.get('priority', 0))
|
||||
data['enabled'] = data.get('enabled', 'True') == 'True'
|
||||
return data
|
||||
return None
|
||||
@@ -483,6 +559,47 @@ class ConfigSyncManager:
|
||||
logger.error(f"从Redis获取ROI配置失败: {e}")
|
||||
return None
|
||||
|
||||
def get_algo_bind_from_redis(self, bind_id: str) -> Optional[Dict[str, Any]]:
|
||||
"""从Redis获取ROI算法绑定配置"""
|
||||
if not self._redis_client:
|
||||
return None
|
||||
|
||||
try:
|
||||
key = f"config:bind:{bind_id}"
|
||||
data = self._redis_client.hgetall(key)
|
||||
if data:
|
||||
data['params'] = eval(data['params']) if data.get('params') else {}
|
||||
data['priority'] = int(data.get('priority', 0))
|
||||
data['enabled'] = data.get('enabled', 'True') == 'True'
|
||||
data['target_class'] = data.get('target_class', 'person')
|
||||
return data
|
||||
return None
|
||||
except Exception as e:
|
||||
logger.error(f"从Redis获取ROI算法绑定配置失败: {e}")
|
||||
return None
|
||||
|
||||
def get_bindings_from_redis(self, roi_id: str) -> List[Dict[str, Any]]:
|
||||
"""从Redis获取ROI的所有算法绑定"""
|
||||
if not self._redis_client:
|
||||
return []
|
||||
|
||||
try:
|
||||
pattern = "config:bind:*"
|
||||
keys = self._redis_client.keys(pattern)
|
||||
results = []
|
||||
for key in keys:
|
||||
data = self._redis_client.hgetall(key)
|
||||
if data and data.get('roi_id') == roi_id:
|
||||
data['params'] = eval(data['params']) if data.get('params') else {}
|
||||
data['priority'] = int(data.get('priority', 0))
|
||||
data['enabled'] = data.get('enabled', 'True') == 'True'
|
||||
data['target_class'] = data.get('target_class', 'person')
|
||||
results.append(data)
|
||||
return sorted(results, key=lambda x: x['priority'], reverse=True)
|
||||
except Exception as e:
|
||||
logger.error(f"从Redis获取ROI算法绑定列表失败: {e}")
|
||||
return []
|
||||
|
||||
def get_camera_from_redis(self, camera_id: str) -> Optional[Dict[str, Any]]:
|
||||
"""从Redis获取摄像头配置"""
|
||||
if not self._redis_client:
|
||||
@@ -529,6 +646,10 @@ class ConfigSyncManager:
|
||||
keys = self._redis_client.keys("config:camera:*")
|
||||
if keys:
|
||||
self._redis_client.delete(*keys)
|
||||
elif config_type == "bind":
|
||||
keys = self._redis_client.keys("config:bind:*")
|
||||
if keys:
|
||||
self._redis_client.delete(*keys)
|
||||
else:
|
||||
keys = self._redis_client.keys("config:*")
|
||||
if keys:
|
||||
@@ -543,6 +664,7 @@ class ConfigSyncManager:
|
||||
self.clear_redis_cache()
|
||||
count = self.sync_all_to_redis()
|
||||
self.notify_config_change("roi", [])
|
||||
self.notify_config_change("bind", [])
|
||||
logger.info(f"算法配置已重新加载,更新了 {count} 条缓存")
|
||||
|
||||
def close(self):
|
||||
|
||||
Reference in New Issue
Block a user