From 75c3380ff37dbf5a107c9d855c4fcef988ac6ee4 Mon Sep 17 00:00:00 2001 From: root <13910913995@163.com> Date: Sat, 7 Feb 2026 13:41:31 +0800 Subject: [PATCH] =?UTF-8?q?=E8=BF=9B=E5=85=A5=E6=A3=80=E6=B5=8B=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2=EF=BC=8C=E5=A2=9E=E5=8A=A0=E4=BA=86=E8=AE=BE=E5=A4=87?= =?UTF-8?q?=E6=98=AF=E5=90=A6=E5=B0=B1=E7=BB=AA=E7=9A=84=E6=A3=80=E6=B5=8B?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/config.ini | 4 +- backend/devices/device_coordinator.py | 113 ++++++- backend/devices/pressure_manager.py | 20 +- backend/main.py | 44 ++- frontend/src/renderer/src/views/Detection.vue | 293 +++++++++++++++++- 5 files changed, 440 insertions(+), 34 deletions(-) diff --git a/backend/config.ini b/backend/config.ini index 125af762..40992f17 100644 --- a/backend/config.ini +++ b/backend/config.ini @@ -30,7 +30,7 @@ backend = directshow [CAMERA2] enable = True -device_index = 1 +device_index = 2 width = 1280 height = 720 fps = 30 @@ -60,7 +60,7 @@ pressure_port = COM3 pressure_baudrate = 115200 [REMOTE] -enable = True +enable = False port = COM6 baudrate = 115200 timeout = 0.1 diff --git a/backend/devices/device_coordinator.py b/backend/devices/device_coordinator.py index 7c569fb4..cb720318 100644 --- a/backend/devices/device_coordinator.py +++ b/backend/devices/device_coordinator.py @@ -57,6 +57,10 @@ class DeviceCoordinator: self.is_initialized = False self.is_running = False self.coordinator_lock = threading.RLock() + self._init_summary = { + 'initialized_at': None, + 'device_results': {}, + } # 监控线程 self.monitor_thread = None @@ -116,18 +120,22 @@ class DeviceCoordinator: self._register_namespaces() # 初始化设备(失败则降级继续) - if not self._initialize_devices(): - self.logger.warning("设备初始化失败,将以降级模式继续运行") - + init_ok = bool(self._initialize_devices()) + self._init_summary['initialized_at'] = time.time() + if not init_ok: + self.logger.warning("设备初始化失败(没有任何设备初始化成功)") + self.is_initialized = False + return False + self.is_initialized = True self.stats['start_time'] = time.time() - + # 启动监控线程 self._start_monitor() - + self.logger.info("设备协调器初始化成功") self._emit_event('coordinator_initialized', {'devices': list(self.devices.keys())}) - + return True except Exception as e: @@ -195,12 +203,14 @@ class DeviceCoordinator: try: timeout_s = 45 if device_name == 'imu' else 30 result = future.result(timeout=timeout_s) + self._init_summary['device_results'][device_name] = bool(result) if result: success_count += 1 self.logger.info(f"{device_name}设备初始化成功") else: self.logger.error(f"{device_name}设备初始化失败") except Exception as e: + self._init_summary['device_results'][device_name] = False self.logger.error(f"{device_name}设备初始化异常: {e}") # 至少需要一个设备初始化成功 @@ -214,6 +224,97 @@ class DeviceCoordinator: except Exception as e: self.logger.error(f"设备初始化失败: {e}") return False + + def get_enabled_devices(self) -> List[str]: + enabled = [] + try: + for name, cfg in (self.device_configs or {}).items(): + if isinstance(cfg, dict) and cfg.get('enable', False): + enabled.append(name) + except Exception: + pass + return enabled + + def get_required_devices_for_detection(self) -> List[str]: + enabled = self.get_enabled_devices() + required = [d for d in enabled if d not in ('remote',)] + return required + + def get_device_readiness(self, device_name: str) -> Dict[str, Any]: + enabled = bool(self.device_configs.get(device_name, {}).get('enable', False)) + device = self.devices.get(device_name) + readiness = { + 'device_name': device_name, + 'enabled': enabled, + 'exists': device is not None, + 'initializing': False, + 'is_connected': False, + 'is_streaming': False, + 'ready': False, + } + + if not device: + return readiness + status = None + try: + if hasattr(device, 'get_status'): + status = device.get_status() + except Exception: + status = None + + try: + readiness['initializing'] = bool(getattr(device, '_initializing', False)) + except Exception: + readiness['initializing'] = False + try: + if isinstance(status, dict) and 'is_connected' in status: + readiness['is_connected'] = bool(status.get('is_connected')) + else: + readiness['is_connected'] = bool(getattr(device, 'is_connected', False)) + except Exception: + readiness['is_connected'] = False + try: + if isinstance(status, dict) and 'is_streaming' in status: + readiness['is_streaming'] = bool(status.get('is_streaming')) + else: + readiness['is_streaming'] = bool(getattr(device, 'is_streaming', False)) + except Exception: + readiness['is_streaming'] = False + + ok = readiness['is_connected'] and (not readiness['initializing']) + readiness['ready'] = bool(ok) + return readiness + + def get_readiness_snapshot(self, required_devices: Optional[List[str]] = None) -> Dict[str, Any]: + required = required_devices if required_devices is not None else self.get_required_devices_for_detection() + devices = {} + for name in required: + devices[name] = self.get_device_readiness(name) + + all_ready = bool(self.is_initialized) and all(devices[name].get('ready', False) for name in devices) + return { + 'coordinator': { + 'is_initialized': bool(self.is_initialized), + 'is_running': bool(self.is_running), + 'enabled_devices': self.get_enabled_devices(), + 'required_devices': required, + 'init_summary': self._init_summary, + }, + 'devices': devices, + 'all_ready': all_ready, + } + + def wait_until_ready_for_detection(self, timeout_s: float = 10.0, poll_interval_s: float = 0.2) -> Dict[str, Any]: + deadline = time.time() + max(0.0, float(timeout_s)) + last_snapshot = self.get_readiness_snapshot() + while time.time() < deadline: + last_snapshot = self.get_readiness_snapshot() + if last_snapshot.get('all_ready', False): + return last_snapshot + time.sleep(max(0.05, float(poll_interval_s))) + last_snapshot['timeout'] = True + last_snapshot['timeout_s'] = float(timeout_s) + return last_snapshot def _init_camera_by_name(self, device_name: str, section: str = 'CAMERA1') -> bool: """ diff --git a/backend/devices/pressure_manager.py b/backend/devices/pressure_manager.py index 78c8b3a8..1d81f4d8 100644 --- a/backend/devices/pressure_manager.py +++ b/backend/devices/pressure_manager.py @@ -704,6 +704,7 @@ class PressureManager(BaseDevice): Returns: bool: 初始化是否成功 """ + self._initializing = True try: self.logger.info(f"正在初始化压力板设备...") @@ -716,13 +717,28 @@ class PressureManager(BaseDevice): else: self.device = MockPressureDevice() + connected = False + try: + if self.use_mock: + connected = True + elif hasattr(self.device, 'is_connected'): + connected = bool(self.device.is_connected) + else: + connected = bool(self.check_hardware_connection()) + except Exception: + connected = False + # 使用set_connected方法启动连接监控线程 - self.set_connected(True) + self.set_connected(bool(connected)) self._device_info.update({ 'device_type': 'mock' if self.use_mock else 'real', 'matrix_size': '4x4' if hasattr(self.device, 'rows') else 'unknown' }) + if not connected: + self.logger.warning("压力板初始化完成但硬件未连接") + return False + self.logger.info(f"压力板初始化成功 - use_mock: {self.use_mock}") return True @@ -732,6 +748,8 @@ class PressureManager(BaseDevice): self.set_connected(False) self.device = None return False + finally: + self._initializing = False def start_streaming(self) -> bool: diff --git a/backend/main.py b/backend/main.py index cb1ba4ba..fc1a3e73 100644 --- a/backend/main.py +++ b/backend/main.py @@ -1293,22 +1293,38 @@ class AppServer: try: if not self.db_manager or not self.device_coordinator: return jsonify({'success': False, 'error': '数据库管理器或设备管理器未初始化'}), 500 - - # 检查设备是否已初始化 - if self.device_coordinator and not self.device_coordinator.is_initialized: - self.logger.info('设备尚未初始化,等待初始化完成...') - # 最多等待10秒 - start_wait = time.time() - while not self.device_coordinator.is_initialized: - if time.time() - start_wait > 10: - return jsonify({'success': False, 'error': '设备初始化超时,请稍后重试'}), 503 - time.sleep(0.5) - self.logger.info('设备初始化完成,继续开始检测') - data = flask_request.get_json() + data = flask_request.get_json() or {} patient_id = data.get('patient_id') - creator_id = data.get('creator_id') + creator_id = data.get('creator_id') + force_start = bool(data.get('force_start', False)) + if self.device_coordinator: + if not getattr(self.device_coordinator, 'is_initialized', False): + try: + threading.Thread(target=self._initialize_devices, daemon=True).start() + except Exception: + pass + + readiness = {} + if not force_start: + try: + readiness = self.device_coordinator.wait_until_ready_for_detection(timeout_s=10.0, poll_interval_s=0.2) + except Exception as e: + self.logger.error(f'等待设备就绪失败: {e}') + readiness = {} + + if not readiness or not readiness.get('all_ready', False): + return jsonify({ + 'success': False, + 'error': '设备未就绪,请稍后重试', + 'readiness': readiness + }), 503 + else: + try: + readiness = self.device_coordinator.get_readiness_snapshot() + except Exception: + readiness = {} if not patient_id or not creator_id: return jsonify({'success': False, 'error': '缺少患者ID或创建人ID'}), 400 @@ -1324,7 +1340,7 @@ class AppServer: except Exception as monitor_error: self.logger.error(f'启动设备连接监控失败: {monitor_error}') - return jsonify({'success': True, 'session_id': session_id}) + return jsonify({'success': True, 'session_id': session_id, 'forced': bool(force_start), 'readiness': readiness}) except Exception as e: self.logger.error(f'开始检测失败: {e}') return jsonify({'success': False, 'error': str(e)}), 500 diff --git a/frontend/src/renderer/src/views/Detection.vue b/frontend/src/renderer/src/views/Detection.vue index 48052cc8..d11c95af 100644 --- a/frontend/src/renderer/src/views/Detection.vue +++ b/frontend/src/renderer/src/views/Detection.vue @@ -778,6 +778,37 @@ +