244 lines
7.8 KiB
Python
244 lines
7.8 KiB
Python
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
"""
|
||
Socket连接管理工具
|
||
提供设备间Socket.IO通信的统一管理
|
||
"""
|
||
|
||
import threading
|
||
import time
|
||
import logging
|
||
from typing import Dict, Any, Optional, Callable
|
||
from datetime import datetime
|
||
|
||
|
||
class SocketManager:
|
||
"""Socket连接管理器"""
|
||
|
||
def __init__(self, socketio=None):
|
||
"""
|
||
初始化Socket管理器
|
||
|
||
Args:
|
||
socketio: SocketIO实例
|
||
"""
|
||
self.socketio = socketio
|
||
self.logger = logging.getLogger(f"{__name__}.SocketManager")
|
||
self._namespaces = {}
|
||
self._event_handlers = {}
|
||
self._connection_stats = {}
|
||
self._lock = threading.RLock()
|
||
|
||
def set_socketio(self, socketio):
|
||
"""
|
||
设置SocketIO实例
|
||
|
||
Args:
|
||
socketio: SocketIO实例
|
||
"""
|
||
self.socketio = socketio
|
||
|
||
def register_namespace(self, namespace: str, device_name: str):
|
||
"""
|
||
注册设备命名空间
|
||
|
||
Args:
|
||
namespace: 命名空间路径(如 '/camera')
|
||
device_name: 设备名称
|
||
"""
|
||
with self._lock:
|
||
self._namespaces[namespace] = {
|
||
'device_name': device_name,
|
||
'registered_at': datetime.now().isoformat(),
|
||
'active': True
|
||
}
|
||
self._connection_stats[namespace] = {
|
||
'messages_sent': 0,
|
||
'messages_failed': 0,
|
||
'last_message_time': None,
|
||
'connected_clients': 0
|
||
}
|
||
|
||
self.logger.info(f"注册设备命名空间: {namespace} -> {device_name}")
|
||
|
||
def unregister_namespace(self, namespace: str):
|
||
"""
|
||
注销设备命名空间
|
||
|
||
Args:
|
||
namespace: 命名空间路径
|
||
"""
|
||
with self._lock:
|
||
if namespace in self._namespaces:
|
||
self._namespaces[namespace]['active'] = False
|
||
self.logger.info(f"注销设备命名空间: {namespace}")
|
||
|
||
def emit_to_namespace(self, namespace: str, event: str, data: Any,
|
||
callback: Optional[Callable] = None) -> bool:
|
||
"""
|
||
向指定命名空间发送数据
|
||
|
||
Args:
|
||
namespace: 命名空间路径
|
||
event: 事件名称
|
||
data: 数据
|
||
callback: 回调函数
|
||
|
||
Returns:
|
||
bool: 发送是否成功
|
||
"""
|
||
if not self.socketio:
|
||
self.logger.warning("SocketIO未初始化")
|
||
return False
|
||
|
||
try:
|
||
with self._lock:
|
||
if namespace not in self._namespaces:
|
||
self.logger.warning(f"命名空间未注册: {namespace}")
|
||
return False
|
||
|
||
if not self._namespaces[namespace]['active']:
|
||
self.logger.warning(f"命名空间已停用: {namespace}")
|
||
return False
|
||
|
||
# 发送数据
|
||
if callback:
|
||
self.socketio.emit(event, data, namespace=namespace, callback=callback)
|
||
else:
|
||
self.socketio.emit(event, data, namespace=namespace)
|
||
|
||
# 更新统计
|
||
with self._lock:
|
||
stats = self._connection_stats[namespace]
|
||
stats['messages_sent'] += 1
|
||
stats['last_message_time'] = time.time()
|
||
|
||
return True
|
||
|
||
except Exception as e:
|
||
self.logger.error(f"发送数据到 {namespace} 失败: {e}")
|
||
with self._lock:
|
||
if namespace in self._connection_stats:
|
||
self._connection_stats[namespace]['messages_failed'] += 1
|
||
return False
|
||
|
||
def broadcast_to_all(self, event: str, data: Any,
|
||
exclude_namespaces: Optional[list] = None) -> Dict[str, bool]:
|
||
"""
|
||
向所有活跃命名空间广播数据
|
||
|
||
Args:
|
||
event: 事件名称
|
||
data: 数据
|
||
exclude_namespaces: 排除的命名空间列表
|
||
|
||
Returns:
|
||
Dict[str, bool]: 各命名空间的发送结果
|
||
"""
|
||
exclude_namespaces = exclude_namespaces or []
|
||
results = {}
|
||
|
||
with self._lock:
|
||
active_namespaces = [
|
||
ns for ns, info in self._namespaces.items()
|
||
if info['active'] and ns not in exclude_namespaces
|
||
]
|
||
|
||
for namespace in active_namespaces:
|
||
results[namespace] = self.emit_to_namespace(namespace, event, data)
|
||
|
||
return results
|
||
|
||
def register_event_handler(self, namespace: str, event: str, handler: Callable):
|
||
"""
|
||
注册事件处理器
|
||
|
||
Args:
|
||
namespace: 命名空间
|
||
event: 事件名称
|
||
handler: 处理函数
|
||
"""
|
||
if not self.socketio:
|
||
self.logger.warning("SocketIO未初始化,无法注册事件处理器")
|
||
return
|
||
|
||
handler_key = f"{namespace}:{event}"
|
||
self._event_handlers[handler_key] = handler
|
||
|
||
# 注册到SocketIO
|
||
@self.socketio.on(event, namespace=namespace)
|
||
def wrapper(*args, **kwargs):
|
||
try:
|
||
return handler(*args, **kwargs)
|
||
except Exception as e:
|
||
self.logger.error(f"事件处理器 {handler_key} 执行失败: {e}")
|
||
|
||
self.logger.info(f"注册事件处理器: {handler_key}")
|
||
|
||
def get_namespace_stats(self, namespace: str) -> Optional[Dict[str, Any]]:
|
||
"""
|
||
获取命名空间统计信息
|
||
|
||
Args:
|
||
namespace: 命名空间
|
||
|
||
Returns:
|
||
Optional[Dict[str, Any]]: 统计信息
|
||
"""
|
||
with self._lock:
|
||
if namespace in self._connection_stats:
|
||
stats = self._connection_stats[namespace].copy()
|
||
if stats['last_message_time']:
|
||
stats['last_message_ago'] = time.time() - stats['last_message_time']
|
||
return stats
|
||
return None
|
||
|
||
def get_all_stats(self) -> Dict[str, Any]:
|
||
"""
|
||
获取所有统计信息
|
||
|
||
Returns:
|
||
Dict[str, Any]: 所有统计信息
|
||
"""
|
||
with self._lock:
|
||
return {
|
||
'namespaces': self._namespaces.copy(),
|
||
'stats': {ns: self.get_namespace_stats(ns) for ns in self._namespaces},
|
||
'total_namespaces': len(self._namespaces),
|
||
'active_namespaces': len([ns for ns, info in self._namespaces.items() if info['active']])
|
||
}
|
||
|
||
def cleanup_namespace(self, namespace: str):
|
||
"""
|
||
清理命名空间资源
|
||
|
||
Args:
|
||
namespace: 命名空间
|
||
"""
|
||
with self._lock:
|
||
if namespace in self._namespaces:
|
||
del self._namespaces[namespace]
|
||
if namespace in self._connection_stats:
|
||
del self._connection_stats[namespace]
|
||
|
||
# 清理事件处理器
|
||
handlers_to_remove = [
|
||
key for key in self._event_handlers.keys()
|
||
if key.startswith(f"{namespace}:")
|
||
]
|
||
for key in handlers_to_remove:
|
||
del self._event_handlers[key]
|
||
|
||
self.logger.info(f"清理命名空间: {namespace}")
|
||
|
||
def cleanup_all(self):
|
||
"""
|
||
清理所有资源
|
||
"""
|
||
with self._lock:
|
||
namespaces = list(self._namespaces.keys())
|
||
|
||
for namespace in namespaces:
|
||
self.cleanup_namespace(namespace)
|
||
|
||
self.logger.info("清理所有Socket资源") |