diff --git a/backend/devices/base_device.py b/backend/devices/base_device.py index daaacefc..84fbe81a 100644 --- a/backend/devices/base_device.py +++ b/backend/devices/base_device.py @@ -41,6 +41,9 @@ class BaseDevice(ABC): self._socketio = None self._last_heartbeat = time.time() + # 状态变化回调 + self._status_change_callbacks = [] + # 设备状态信息 self._device_info = { 'name': device_name, @@ -133,6 +136,53 @@ class BaseDevice(ABC): 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): """ diff --git a/backend/devices/imu_manager.py b/backend/devices/imu_manager.py index 38d7b266..d925bc5e 100644 --- a/backend/devices/imu_manager.py +++ b/backend/devices/imu_manager.py @@ -316,6 +316,13 @@ class IMUManager(BaseDevice): if use_real_device: self.logger.info(f"使用真实IMU设备 - 端口: {self.port}, 波特率: {self.baudrate}") self.imu_device = RealIMUDevice(self.port, self.baudrate) + + # 检查真实设备是否连接成功 + if self.imu_device.ser is None: + self.logger.error(f"IMU设备连接失败: 无法打开串口 {self.port}") + self.is_connected = False + self.imu_device = None + return False else: self.logger.info("使用模拟IMU设备") self.imu_device = MockIMUDevice() diff --git a/backend/devices/utils/config.ini b/backend/devices/utils/config.ini index 9d34dfdd..07e1f81e 100644 --- a/backend/devices/utils/config.ini +++ b/backend/devices/utils/config.ini @@ -15,7 +15,7 @@ backup_interval = 24 max_backups = 7 [CAMERA] -device_index = 3 +device_index = 0 width = 1280 height = 720 fps = 30 diff --git a/backend/main.py b/backend/main.py index f8707bb0..7c4d17bb 100644 --- a/backend/main.py +++ b/backend/main.py @@ -198,6 +198,12 @@ class AppServer: 'imu': IMUManager(self.socketio, self.config_manager), 'pressure': PressureManager(self.socketio, self.config_manager) } + + # 为每个设备添加状态变化回调 + for device_name, manager in self.device_managers.items(): + if manager and hasattr(manager, 'add_status_change_callback'): + manager.add_status_change_callback(self._on_device_status_change) + self.logger.info('设备管理器初始化完成') # 初始化设备协调器 @@ -933,6 +939,9 @@ class AppServer: self.logger.info('设备命名空间客户端连接') emit('status', {'message': '设备命名空间连接成功'}, namespace='/devices') + # 连接时发送当前所有设备的状态 + self.broadcast_all_device_status() + @self.socketio.on('disconnect', namespace='/devices') def handle_devices_disconnect(): self.logger.info('设备命名空间客户端断开连接') @@ -1047,6 +1056,11 @@ class AppServer: # 输出启动结果摘要 successful_devices = [name for name, success in device_results.items() if success] + + # 广播设备状态更新 + for device_name, success in device_results.items(): + self.broadcast_device_status(device_name, success) + if successful_devices: self.logger.info(f'成功启动的设备: {", ".join(successful_devices)}') if failed_devices: @@ -1076,13 +1090,54 @@ class AppServer: manager.stop_streaming() manager.disconnect() self.logger.info(f'{device_name}设备已停止') + # 广播设备状态为未连接 + self.broadcast_device_status(device_name, False) except Exception as e: self.logger.error(f'停止{device_name}设备失败: {e}') + # 即使停止失败也广播为未连接状态 + self.broadcast_device_status(device_name, False) self.logger.info('设备数据推送已停止') except Exception as e: self.logger.error(f'停止设备数据推送失败: {e}') + + def broadcast_device_status(self, device_name: str, is_connected: bool): + """广播单个设备状态""" + if self.socketio: + try: + status_data = { + 'device_type': device_name, + 'status': is_connected, + 'timestamp': datetime.now().isoformat() + } + self.socketio.emit('device_status', status_data, namespace='/devices') + self.logger.info(f'广播设备状态: {device_name} -> {"已连接" if is_connected else "未连接"}') + except Exception as e: + self.logger.error(f'广播设备状态失败: {e}') + + def broadcast_all_device_status(self): + """广播所有设备状态""" + for device_name, manager in self.device_managers.items(): + if manager is not None: + try: + # 检查设备是否连接(使用is_connected属性) + is_connected = hasattr(manager, 'is_connected') and getattr(manager, 'is_connected', False) + self.broadcast_device_status(device_name, is_connected) + except Exception as e: + self.logger.error(f'检查{device_name}设备状态失败: {e}') + self.broadcast_device_status(device_name, False) + + def _on_device_status_change(self, device_name: str, is_connected: bool): + """ + 设备状态变化回调函数 + + Args: + device_name: 设备名称 + is_connected: 连接状态 + """ + self.logger.info(f'设备状态变化: {device_name} -> {"已连接" if is_connected else "未连接"}') + self.broadcast_device_status(device_name, is_connected) def _detection_worker(self, detection_id, duration): diff --git a/frontend/src/renderer/src/assets/no-image.svg b/frontend/src/renderer/src/assets/no-image.svg new file mode 100644 index 00000000..defc545b --- /dev/null +++ b/frontend/src/renderer/src/assets/no-image.svg @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/frontend/src/renderer/src/views/Detection.vue b/frontend/src/renderer/src/views/Detection.vue index 59ac852a..7684f384 100644 --- a/frontend/src/renderer/src/views/Detection.vue +++ b/frontend/src/renderer/src/views/Detection.vue @@ -58,12 +58,12 @@ 身体姿态 -