BodyBalanceEvaluation/backend/devices/base_device.py

367 lines
11 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._status_change_callbacks = []
2025-09-10 09:13:21 +08:00
# 设备连接监控
self._connection_monitor_thread = None
self._monitor_stop_event = threading.Event()
self._connection_check_interval = config.get('connection_check_interval', 5.0) # 默认5秒检查一次
self._connection_timeout = config.get('connection_timeout', 30.0) # 默认30秒超时
# 设备状态信息
self._device_info = {
'name': device_name,
'type': self.__class__.__name__,
'version': '1.0.0',
'initialized_at': None,
'last_error': 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
@abstractmethod
def reload_config(self) -> bool:
"""
重新加载设备配置
Returns:
bool: 重新加载是否成功
"""
pass
2025-09-10 09:13:21 +08:00
@abstractmethod
def check_hardware_connection(self) -> bool:
"""
检查设备硬件连接状态
Returns:
bool: 设备是否物理连接
"""
pass
def set_socketio(self, socketio):
"""
设置SocketIO实例
Args:
socketio: SocketIO实例
"""
self._socketio = socketio
def add_status_change_callback(self, callback):
"""
添加状态变化回调函数
Args:
callback: 回调函数接收参数 (device_name, is_connected)
"""
if callback not in self._status_change_callbacks:
self._status_change_callbacks.append(callback)
def remove_status_change_callback(self, callback):
"""
移除状态变化回调函数
Args:
callback: 要移除的回调函数
"""
if callback in self._status_change_callbacks:
self._status_change_callbacks.remove(callback)
def _notify_status_change(self, is_connected: bool):
"""
通知状态变化
Args:
is_connected: 连接状态
"""
for callback in self._status_change_callbacks:
try:
callback(self.device_name, is_connected)
except Exception as e:
self.logger.error(f"状态变化回调执行失败: {e}")
def set_connected(self, is_connected: bool):
"""
设置连接状态并触发回调
Args:
is_connected: 连接状态
"""
old_status = self.is_connected
self.is_connected = is_connected
# 只有状态真正改变时才触发回调
if old_status != is_connected:
self._notify_status_change(is_connected)
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 _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
2025-09-10 09:13:21 +08:00
def _start_connection_monitor(self):
"""
启动连接监控线程
"""
if self._connection_monitor_thread and self._connection_monitor_thread.is_alive():
return
self._monitor_stop_event.clear()
self._connection_monitor_thread = threading.Thread(
target=self._connection_monitor_worker,
name=f"{self.device_name}_connection_monitor",
daemon=True
)
self._connection_monitor_thread.start()
self.logger.info(f"设备 {self.device_name} 连接监控线程已启动")
def _stop_connection_monitor(self):
"""
停止连接监控线程
"""
if self._connection_monitor_thread:
self._monitor_stop_event.set()
if self._connection_monitor_thread.is_alive():
self._connection_monitor_thread.join(timeout=2.0)
self._connection_monitor_thread = None
self.logger.info(f"设备 {self.device_name} 连接监控线程已停止")
def _connection_monitor_worker(self):
"""
连接监控工作线程
"""
self.logger.info(f"设备 {self.device_name} 连接监控开始")
while not self._monitor_stop_event.is_set():
try:
# 检查硬件连接状态
hardware_connected = self.check_hardware_connection()
self.logger.info(f"检测到设备 {self.device_name} 硬件连接状态: {hardware_connected} is_connected{self.is_connected}")
2025-09-10 09:13:21 +08:00
# 如果硬件断开但软件状态仍为连接,则更新状态
if not hardware_connected and self.is_connected:
self.logger.warning(f"检测到设备 {self.device_name} 硬件连接断开")
# 直接更新状态避免在监控线程中调用set_connected导致死锁
self.is_connected = False
self._notify_status_change(False)
# 如果硬件重新连接但软件状态仍为断开,则更新状态
elif hardware_connected and not self.is_connected:
self.logger.info(f"检测到设备 {self.device_name} 硬件重新连接")
# 直接更新状态避免在监控线程中调用set_connected导致死锁
self.is_connected = True
# 重置心跳时间,避免立即触发心跳超时
self.update_heartbeat()
self._notify_status_change(True)
2025-09-10 09:13:21 +08:00
# 检查心跳超时(仅在当前状态为连接时检查)
2025-09-10 09:13:21 +08:00
if self.is_connected and time.time() - self._last_heartbeat > self._connection_timeout:
self.logger.warning(f"设备 {self.device_name} 心跳超时,判定为断开连接")
# 直接更新状态避免在监控线程中调用set_connected导致死锁
self.is_connected = False
self._notify_status_change(False)
2025-09-10 09:13:21 +08:00
except Exception as e:
self.logger.error(f"设备 {self.device_name} 连接监控异常: {e}")
# 等待下次检查
self._monitor_stop_event.wait(self._connection_check_interval)
self.logger.info(f"设备 {self.device_name} 连接监控结束")
# 清理线程引用
self._connection_monitor_thread = None
def __enter__(self):
"""
上下文管理器入口
"""
return self
def __exit__(self, exc_type, exc_val, exc_tb):
"""
上下文管理器出口
"""
self.cleanup()
2025-09-10 09:13:21 +08:00
def _cleanup_monitoring(self):
"""
清理监控线程
"""
self._stop_connection_monitor()
def __repr__(self):
return f"<{self.__class__.__name__}(name='{self.device_name}', connected={self.is_connected}, streaming={self.is_streaming})>"