BodyBalanceEvaluation/backend/devices/base_device.py

262 lines
6.7 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
设备抽象基类
定义所有设备管理器的通用接口和基础功能
"""
from abc import ABC, abstractmethod
from typing import Dict, Any, Optional
import threading
import logging
import time
from datetime import datetime
try:
from .utils.socket_manager import SocketManager
except ImportError:
from utils.socket_manager import SocketManager
class BaseDevice(ABC):
"""设备抽象基类"""
def __init__(self, device_name: str, config: Dict[str, Any]):
"""
初始化设备基类
Args:
device_name: 设备名称
config: 设备配置字典
"""
self.device_name = device_name
self.config = config
self.is_connected = False
self.is_streaming = False
self.socket_namespace = f"/{device_name}"
self.logger = logging.getLogger(f"device.{device_name}")
self._lock = threading.RLock() # 可重入锁
self._streaming_thread = None
self._stop_event = threading.Event()
self._socketio = None
self._last_heartbeat = time.time()
# 设备状态信息
self._device_info = {
'name': device_name,
'type': self.__class__.__name__,
'version': '1.0.0',
'initialized_at': None,
'last_error': None
}
# 性能统计
self._stats = {
'frames_processed': 0,
'errors_count': 0,
'start_time': None,
'last_frame_time': None
}
@abstractmethod
def initialize(self) -> bool:
"""
初始化设备
Returns:
bool: 初始化是否成功
"""
pass
@abstractmethod
def calibrate(self) -> Dict[str, Any]:
"""
校准设备
Returns:
Dict[str, Any]: 校准结果
"""
pass
@abstractmethod
def start_streaming(self, socketio) -> bool:
"""
启动数据推流
Args:
socketio: SocketIO实例
Returns:
bool: 启动是否成功
"""
pass
@abstractmethod
def stop_streaming(self) -> bool:
"""
停止数据推流
Returns:
bool: 停止是否成功
"""
pass
@abstractmethod
def get_status(self) -> Dict[str, Any]:
"""
获取设备状态
Returns:
Dict[str, Any]: 设备状态信息
"""
pass
@abstractmethod
def disconnect(self) -> None:
"""
断开设备连接
"""
pass
@abstractmethod
def cleanup(self) -> None:
"""
清理资源
"""
pass
def set_socketio(self, socketio):
"""
设置SocketIO实例
Args:
socketio: SocketIO实例
"""
self._socketio = socketio
def emit_data(self, event: str, data: Any, namespace: Optional[str] = None):
"""
发送数据到前端
Args:
event: 事件名称
data: 数据
namespace: 命名空间默认使用设备命名空间
"""
if self._socketio:
ns = namespace or self.socket_namespace
try:
self._socketio.emit(event, data, namespace=ns)
except Exception as e:
self.logger.error(f"发送数据失败: {e}")
def update_heartbeat(self):
"""
更新心跳时间
"""
self._last_heartbeat = time.time()
def is_alive(self, timeout: float = 30.0) -> bool:
"""
检查设备是否存活
Args:
timeout: 超时时间
Returns:
bool: 设备是否存活
"""
return (time.time() - self._last_heartbeat) < timeout
def get_device_info(self) -> Dict[str, Any]:
"""
获取设备信息
Returns:
Dict[str, Any]: 设备信息
"""
with self._lock:
return self._device_info.copy()
def get_stats(self) -> Dict[str, Any]:
"""
获取性能统计
Returns:
Dict[str, Any]: 性能统计信息
"""
with self._lock:
stats = self._stats.copy()
if stats['start_time']:
stats['uptime'] = time.time() - stats['start_time']
if stats['frames_processed'] > 0 and stats['uptime'] > 0:
stats['fps'] = stats['frames_processed'] / stats['uptime']
else:
stats['fps'] = 0
return stats
def _update_stats(self, frame_processed: bool = True, error: bool = False):
"""
更新统计信息
Args:
frame_processed: 是否处理了一帧
error: 是否发生错误
"""
with self._lock:
if frame_processed:
self._stats['frames_processed'] += 1
self._stats['last_frame_time'] = time.time()
if error:
self._stats['errors_count'] += 1
def _set_error(self, error_msg: str):
"""
设置错误信息
Args:
error_msg: 错误消息
"""
with self._lock:
self._device_info['last_error'] = {
'message': error_msg,
'timestamp': datetime.now().isoformat()
}
def _clear_error(self):
"""
清除错误信息
"""
with self._lock:
self._device_info['last_error'] = None
def _start_stats_tracking(self):
"""
开始统计跟踪
"""
with self._lock:
self._stats['start_time'] = time.time()
self._stats['frames_processed'] = 0
self._stats['errors_count'] = 0
def _stop_stats_tracking(self):
"""
停止统计跟踪
"""
with self._lock:
self._stats['start_time'] = None
def __enter__(self):
"""
上下文管理器入口
"""
return self
def __exit__(self, exc_type, exc_val, exc_tb):
"""
上下文管理器出口
"""
self.cleanup()
def __repr__(self):
return f"<{self.__class__.__name__}(name='{self.device_name}', connected={self.is_connected}, streaming={self.is_streaming})>"