#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ AppServer类 - 主应用服务器 实现Flask和SocketIO服务,替代app.py功能 """ import os import sys import json import time import threading from datetime import datetime from flask import Flask, jsonify from flask import request as flask_request from flask_cors import CORS import logging from flask_socketio import SocketIO, emit import configparser import argparse # 添加当前目录到路径 sys.path.append(os.path.dirname(os.path.abspath(__file__))) # 导入模块 from database import DatabaseManager from utils import config as app_config # 导入设备管理器 try: from devices.camera_manager import CameraManager from devices.imu_manager import IMUManager from devices.pressure_manager import PressureManager from devices.femtobolt_manager import FemtoBoltManager from devices.device_coordinator import DeviceCoordinator from devices.utils.config_manager import ConfigManager except ImportError: # 如果上面的导入失败,尝试直接导入 from camera_manager import CameraManager import imu_manager import pressure_manager import femtobolt_manager import device_coordinator from utils import config_manager IMUManager = imu_manager.IMUManager PressureManager = pressure_manager.PressureManager FemtoBoltManager = femtobolt_manager.FemtoBoltManager DeviceCoordinator = device_coordinator.DeviceCoordinator ConfigManager = config_manager.ConfigManager class AppServer: """主应用服务器类""" def __init__(self, host='localhost', port=5000, debug=False): """ 初始化应用服务器 Args: host: 服务器主机 port: 服务器端口 debug: 调试模式 """ self.host = host self.port = port self.debug = debug # 初始化日志 self._init_logging() # Flask应用 self.app = Flask(__name__) self.app.config['SECRET_KEY'] = 'body-balance-detection-system-2024' # SocketIO self._init_socketio() # CORS配置 CORS(self.app, origins='*', supports_credentials=True, allow_headers=['Content-Type', 'Authorization'], methods=['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS']) # 配置管理 self._init_config() # 全局变量 self.db_manager = None self.current_detection = None self.detection_thread = None # 数据推送状态 self.is_pushing_data = False # 设备管理器 self.config_manager = None self.device_coordinator = None self.device_managers = { 'camera': None, 'femtobolt': None, 'imu': None, 'pressure': None } # 注册路由和事件 self._register_routes() self._register_socketio_events() def _init_logging(self): """初始化日志配置""" # 日志目录 if getattr(sys, 'frozen', False): # 打包后的可执行文件 log_dir = os.path.join(os.path.dirname(sys.executable), 'logs') else: # 开发环境 log_dir = 'logs' # 创建日志目录 os.makedirs(log_dir, exist_ok=True) log_file = os.path.join(log_dir, 'backend.log') # 配置日志 logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', handlers=[ logging.FileHandler(log_file, encoding='utf-8'), logging.StreamHandler() ] ) self.logger = logging.getLogger(__name__) def _init_socketio(self): """初始化SocketIO""" try: self.socketio = SocketIO( self.app, cors_allowed_origins='*', async_mode='threading', logger=False, engineio_logger=False, ping_timeout=60, ping_interval=25, manage_session=False, always_connect=False, transports=['polling', 'websocket'], # 优先使用polling allow_upgrades=True, # 允许升级到websocket cookie=None # 禁用cookie ) self.logger.info('SocketIO初始化成功') except Exception as e: self.logger.error(f'SocketIO初始化失败: {e}') self.socketio = None # 设置SocketIO日志级别 logging.getLogger('socketio').setLevel(logging.WARNING) logging.getLogger('engineio').setLevel(logging.WARNING) def _init_config(self): """初始化配置""" self.config = configparser.ConfigParser() # 配置文件路径 if getattr(sys, 'frozen', False): # 打包后的可执行文件 config_path = os.path.join(os.path.dirname(sys.executable), 'config.ini') else: # 开发环境 config_path = os.path.join(os.path.dirname(__file__), 'config.ini') self.config.read(config_path, encoding='utf-8') device_index = self.config.get('CAMERA', 'device_index', fallback=None) print(f"设备号: {device_index}") def init_app(self): """初始化应用组件""" try: # 初始化数据库管理器 self.logger.info('正在初始化数据库管理器...') db_path = os.path.join(os.path.dirname(__file__), 'data', 'body_balance.db') os.makedirs(os.path.dirname(db_path), exist_ok=True) self.db_manager = DatabaseManager(db_path) self.db_manager.init_database() self.logger.info('数据库管理器初始化完成') # 初始化配置管理器 self.logger.info('正在初始化配置管理器...') self.config_manager = ConfigManager() self.logger.info('配置管理器初始化完成') # 初始化设备管理器 self.logger.info('正在初始化设备管理器...') self.device_managers = { 'camera': CameraManager(self.socketio, self.config_manager), 'femtobolt': FemtoBoltManager(self.socketio, self.config_manager), 'imu': IMUManager(self.socketio, self.config_manager), 'pressure': PressureManager(self.socketio, self.config_manager) } self.logger.info('设备管理器初始化完成') # 初始化设备协调器 self.logger.info('正在初始化设备协调器...') self.device_coordinator = DeviceCoordinator(self.socketio) self.logger.info('设备协调器初始化完成') # 启动Flask应用 host = app_config.get('host', self.host) port = app_config.get('port', self.port) debug = app_config.get('debug', self.debug) self.logger.info(f'启动Flask应用 - Host: {host}, Port: {port}, Debug: {debug}') if self.socketio: self.socketio.run(self.app, host=host, port=port, debug=debug, allow_unsafe_werkzeug=True) else: self.app.run(host=host, port=port, debug=debug) except Exception as e: self.logger.error(f'应用初始化失败: {e}') raise def _register_routes(self): """注册Flask路由""" # ==================== 基础API ==================== @self.app.route('/health', methods=['GET']) def health_check(): """健康检查""" return jsonify({ 'status': 'healthy', 'timestamp': datetime.now().isoformat(), 'version': '1.0.0' }) @self.app.route('/test-socketio') def test_socketio(): """测试SocketIO连接""" return '

SocketIO Test Page

' @self.app.route('/api/health', methods=['GET']) def api_health_check(): """API健康检查""" return jsonify({ 'success': True, 'message': '后端服务运行正常', 'timestamp': datetime.now().isoformat(), 'database': self.db_manager is not None, 'config_manager': self.config_manager is not None, 'device_coordinator': self.device_coordinator is not None, 'device_managers': {name: manager is not None for name, manager in self.device_managers.items()} }) # ==================== 认证API ==================== @self.app.route('/api/auth/login', methods=['POST']) def login(): """用户登录""" try: # 检查Content-Type if not flask_request.is_json: return jsonify({'success': False, 'message': '请求Content-Type必须为application/json'}), 415 data = flask_request.get_json(force=True) if not data: return jsonify({'success': False, 'message': '请求数据为空'}), 400 username = data.get('username') password = data.get('password') if not username or not password: return jsonify({'success': False, 'message': '用户名和密码不能为空'}), 400 # 验证用户 user = self.db_manager.authenticate_user(username, password) if user: # 检查用户是否已激活 if not user['is_active']: return jsonify({ 'success': False, 'message': '账户未激活,请联系管理员审核' }), 403 # 构建用户数据 user_data = { 'id': user['id'], 'username': user['username'], 'name': user['name'], 'role': 'admin' if user['user_type'] == 'admin' else 'user', 'user_type': user['user_type'], 'avatar': '' } # 生成token(实际项目中应使用JWT等安全token) token = f"token_{user['username']}_{int(time.time())}" self.logger.info(f'用户 {username} 登录成功') return jsonify({ 'success': True, 'data': { 'token': token, 'user': user_data }, 'message': '登录成功' }) else: self.logger.warning(f'用户 {username} 登录失败:用户名或密码错误') return jsonify({ 'success': False, 'message': '用户名或密码错误' }), 401 except Exception as e: self.logger.error(f'登录失败: {e}') return jsonify({'success': False, 'message': '登录失败'}), 500 @self.app.route('/api/auth/register', methods=['POST']) def register(): """用户注册""" try: # 检查Content-Type if not flask_request.is_json: return jsonify({'success': False, 'message': '请求Content-Type必须为application/json'}), 415 data = flask_request.get_json(force=True) username = data.get('username') password = data.get('password') name = data.get('name') or data.get('email', '') phone = data.get('phone') if not username or not password: return jsonify({ 'success': False, 'message': '用户名和密码不能为空' }), 400 if len(password) < 6: return jsonify({ 'success': False, 'message': '密码长度不能少于6位' }), 400 # 构建用户数据字典 user_data = { 'username': username, 'password': password, 'name': name, 'phone': phone } # 使用数据库注册用户 result = self.db_manager.register_user(user_data) if result['success']: self.logger.info(f'用户 {username} 注册成功,等待管理员审核') return jsonify({ 'success': True, 'message': '注册成功,请等待管理员审核后登录' }) else: return jsonify({ 'success': False, 'message': result['message'] }), 400 except Exception as e: self.logger.error(f'注册失败: {e}') return jsonify({'success': False, 'message': '注册失败'}), 500 @self.app.route('/api/auth/logout', methods=['POST']) def logout(): """用户登出""" try: # 这里可以添加token失效逻辑 return jsonify({'success': True, 'message': '登出成功'}) except Exception as e: self.logger.error(f'登出失败: {e}') return jsonify({'success': False, 'error': str(e)}), 500 @self.app.route('/api/auth/verify', methods=['GET']) def verify_token(): """验证token""" try: # 从请求头获取token auth_header = flask_request.headers.get('Authorization') if not auth_header or not auth_header.startswith('Bearer '): return jsonify({ 'success': False, 'message': '未提供有效的认证信息', 'data': {'valid': False} }), 401 token = auth_header.split(' ')[1] # 这里应该验证JWT token,简化处理 if token.startswith('token_'): return jsonify({ 'success': True, 'message': 'Token有效', 'data': {'valid': True} }) else: return jsonify({ 'success': False, 'message': 'Token无效', 'data': {'valid': False} }), 401 except Exception as e: self.logger.error(f'Token验证失败: {e}') return jsonify({ 'success': False, 'message': '验证失败', 'data': {'valid': False} }), 500 @self.app.route('/api/auth/forgot-password', methods=['POST']) def forgot_password(): """忘记密码""" try: data = flask_request.get_json() username = data.get('username') if not username: return jsonify({'success': False, 'error': '用户名不能为空'}), 400 # 这里应该发送重置密码邮件,简化处理 return jsonify({ 'success': True, 'message': '密码重置邮件已发送,请查收邮箱' }) except Exception as e: self.logger.error(f'忘记密码处理失败: {e}') return jsonify({'success': False, 'error': str(e)}), 500 # ==================== 用户管理API ==================== @self.app.route('/api/users', methods=['GET']) def get_users(): """获取用户列表""" try: page = int(flask_request.args.get('page', 1)) size = int(flask_request.args.get('size', 10)) users = self.db_manager.get_users(page, size) total = self.db_manager.get_users_count() return jsonify({ 'success': True, 'data': { 'users': users, 'total': total, 'page': page, 'size': size } }) except Exception as e: self.logger.error(f'获取用户列表失败: {e}') return jsonify({'success': False, 'error': str(e)}), 500 @self.app.route('/api/users//approve', methods=['POST']) def approve_user(user_id): """审核用户""" try: data = flask_request.get_json() status = data.get('status') # 'approved' 或 'rejected' if status not in ['approved', 'rejected']: return jsonify({'success': False, 'error': '无效的审核状态'}), 400 result = self.db_manager.update_user_status(user_id, status) if result: return jsonify({ 'success': True, 'message': f'用户已{"通过" if status == "approved" else "拒绝"}审核' }) else: return jsonify({'success': False, 'error': '用户不存在'}), 404 except Exception as e: self.logger.error(f'审核用户失败: {e}') return jsonify({'success': False, 'error': str(e)}), 500 @self.app.route('/api/users/', methods=['DELETE']) def delete_user(user_id): """删除用户""" try: result = self.db_manager.delete_user(user_id) if result: return jsonify({'success': True, 'message': '用户已删除'}) else: return jsonify({'success': False, 'error': '用户不存在'}), 404 except Exception as e: self.logger.error(f'删除用户失败: {e}') return jsonify({'success': False, 'error': str(e)}), 500 # ==================== 患者管理API ==================== @self.app.route('/api/patients', methods=['GET', 'POST']) def handle_patients(): """患者管理""" if flask_request.method == 'GET': # 获取患者列表 try: page = int(flask_request.args.get('page', 1)) size = int(flask_request.args.get('size', 10)) search = flask_request.args.get('search', '') patients = self.db_manager.get_patients(page, size, search) total = self.db_manager.get_patients_count(search) return jsonify({ 'success': True, 'data': { 'patients': patients, 'total': total, 'page': page, 'size': size } }) except Exception as e: self.logger.error(f'获取患者列表失败: {e}') return jsonify({'success': False, 'error': str(e)}), 500 elif flask_request.method == 'POST': # 创建患者 try: # 检查Content-Type if not flask_request.is_json: return jsonify({'success': False, 'message': '请求Content-Type必须为application/json'}), 415 data = flask_request.get_json(force=True) required_fields = ['name', 'gender', 'age'] for field in required_fields: if not data.get(field): return jsonify({'success': False, 'error': f'{field}不能为空'}), 400 patient_id = self.db_manager.create_patient( name=data['name'], gender=data['gender'], age=data['age'], height=data.get('height'), weight=data.get('weight'), medical_history=data.get('medical_history', ''), notes=data.get('notes', '') ) if patient_id: return jsonify({ 'success': True, 'message': '患者创建成功', 'data': {'patient_id': patient_id} }) else: return jsonify({'success': False, 'error': '患者创建失败'}), 500 except Exception as e: self.logger.error(f'创建患者失败: {e}') return jsonify({'success': False, 'error': str(e)}), 500 @self.app.route('/api/patients/', methods=['GET', 'PUT', 'DELETE']) def handle_patient(patient_id): """单个患者操作""" if flask_request.method == 'GET': # 获取患者详情 try: patient = self.db_manager.get_patient(patient_id) if patient: return jsonify({'success': True, 'data': patient}) else: return jsonify({'success': False, 'error': '患者不存在'}), 404 except Exception as e: self.logger.error(f'获取患者详情失败: {e}') return jsonify({'success': False, 'error': str(e)}), 500 elif flask_request.method == 'PUT': # 更新患者信息 try: data = flask_request.get_json() result = self.db_manager.update_patient( patient_id=patient_id, name=data.get('name'), gender=data.get('gender'), age=data.get('age'), height=data.get('height'), weight=data.get('weight'), medical_history=data.get('medical_history'), notes=data.get('notes') ) if result: return jsonify({'success': True, 'message': '患者信息更新成功'}) else: return jsonify({'success': False, 'error': '患者不存在'}), 404 except Exception as e: self.logger.error(f'更新患者信息失败: {e}') return jsonify({'success': False, 'error': str(e)}), 500 elif flask_request.method == 'DELETE': # 删除患者 try: result = self.db_manager.delete_patient(patient_id) if result: return jsonify({'success': True, 'message': '患者已删除'}) else: return jsonify({'success': False, 'error': '患者不存在'}), 404 except Exception as e: self.logger.error(f'删除患者失败: {e}') return jsonify({'success': False, 'error': str(e)}), 500 # ==================== 设备管理API ==================== @self.app.route('/api/devices/status', methods=['GET']) def get_device_status(): """获取设备状态""" try: if self.device_coordinator: status = self.device_coordinator.get_device_status() return jsonify({'success': True, 'data': status}) else: return jsonify({'success': False, 'error': '设备协调器未初始化'}), 500 except Exception as e: self.logger.error(f'获取设备状态失败: {e}') return jsonify({'success': False, 'error': str(e)}), 500 @self.app.route('/api/devices/refresh', methods=['POST']) def refresh_devices(): """刷新设备""" try: if self.device_coordinator: result = self.device_coordinator.refresh_all_devices() return jsonify({'success': True, 'data': result}) else: return jsonify({'success': False, 'error': '设备协调器未初始化'}), 500 except Exception as e: self.logger.error(f'刷新设备失败: {e}') return jsonify({'success': False, 'error': str(e)}), 500 @self.app.route('/api/devices/calibrate', methods=['POST']) def calibrate_device(): """校准设备""" try: data = flask_request.get_json() device_type = data.get('device_type') if not device_type: return jsonify({'success': False, 'error': '设备类型不能为空'}), 400 if device_type in self.device_managers and self.device_managers[device_type]: device_manager = self.device_managers[device_type] if hasattr(device_manager, 'calibrate'): result = device_manager.calibrate() return jsonify({'success': True, 'data': result}) else: return jsonify({'success': False, 'error': f'{device_type}设备不支持校准'}), 400 else: return jsonify({'success': False, 'error': f'{device_type}设备管理器未初始化'}), 500 except Exception as e: self.logger.error(f'校准设备失败: {e}') return jsonify({'success': False, 'error': str(e)}), 500 @self.app.route('/api/devices/imu/calibrate', methods=['POST']) def calibrate_imu(): """校准IMU""" try: if self.device_managers['imu']: result = self.device_managers['imu'].calibrate() if result: return jsonify({'success': True, 'message': 'IMU校准成功'}) else: return jsonify({'success': False, 'error': 'IMU校准失败'}), 500 else: return jsonify({'success': False, 'error': 'IMU设备管理器未初始化'}), 500 except Exception as e: self.logger.error(f'IMU校准失败: {e}') return jsonify({'success': False, 'error': str(e)}), 500 # ==================== 检测API ==================== @self.app.route('/api/detection/start', methods=['POST']) def start_detection(): """开始检测""" try: data = flask_request.get_json() patient_id = data.get('patient_id') detection_type = data.get('detection_type', 'balance') duration = data.get('duration', 30) if not patient_id: return jsonify({'success': False, 'error': '患者ID不能为空'}), 400 # 检查是否已有检测在进行 if self.current_detection: return jsonify({'success': False, 'error': '已有检测在进行中'}), 400 # 启动检测 detection_id = self.db_manager.create_detection_session( patient_id=patient_id, detection_type=detection_type, duration=duration ) if detection_id: self.current_detection = { 'id': detection_id, 'patient_id': patient_id, 'type': detection_type, 'duration': duration, 'start_time': time.time() } # 启动检测线程 self.detection_thread = threading.Thread( target=self._detection_worker, args=(detection_id, duration) ) self.detection_thread.start() return jsonify({ 'success': True, 'message': '检测已开始', 'data': {'detection_id': detection_id} }) else: return jsonify({'success': False, 'error': '创建检测会话失败'}), 500 except Exception as e: self.logger.error(f'开始检测失败: {e}') return jsonify({'success': False, 'error': str(e)}), 500 @self.app.route('/api/detection/stop', methods=['POST']) def stop_detection(): """停止检测""" try: if not self.current_detection: return jsonify({'success': False, 'error': '没有正在进行的检测'}), 400 # 停止检测 detection_id = self.current_detection['id'] self.db_manager.end_detection_session(detection_id) self.current_detection = None return jsonify({'success': True, 'message': '检测已停止'}) except Exception as e: self.logger.error(f'停止检测失败: {e}') return jsonify({'success': False, 'error': str(e)}), 500 @self.app.route('/api/detection/status', methods=['GET']) def get_detection_status(): """获取检测状态""" try: if self.current_detection: elapsed_time = time.time() - self.current_detection['start_time'] remaining_time = max(0, self.current_detection['duration'] - elapsed_time) return jsonify({ 'success': True, 'data': { 'is_detecting': True, 'detection_id': self.current_detection['id'], 'patient_id': self.current_detection['patient_id'], 'type': self.current_detection['type'], 'elapsed_time': elapsed_time, 'remaining_time': remaining_time, 'progress': min(100, (elapsed_time / self.current_detection['duration']) * 100) } }) else: return jsonify({ 'success': True, 'data': {'is_detecting': False} }) except Exception as e: self.logger.error(f'获取检测状态失败: {e}') return jsonify({'success': False, 'error': str(e)}), 500 @self.app.route('/api/detection/save-session', methods=['POST']) def save_session_info(): """保存会话信息""" try: data = flask_request.get_json() session_id = data.get('session_id') session_info = data.get('session_info', {}) if not session_id: return jsonify({'success': False, 'error': '会话ID不能为空'}), 400 result = self.db_manager.save_session_info(session_id, session_info) if result: return jsonify({'success': True, 'message': '会话信息保存成功'}) else: return jsonify({'success': False, 'error': '会话信息保存失败'}), 500 except Exception as e: self.logger.error(f'保存会话信息失败: {e}') return jsonify({'success': False, 'error': str(e)}), 500 @self.app.route('/api/detection/collect-data', methods=['POST']) def collect_detection_data(): """采集检测数据""" try: data = flask_request.get_json() session_id = data.get('session_id') data_type = data.get('data_type') data_content = data.get('data') if not all([session_id, data_type, data_content]): return jsonify({'success': False, 'error': '参数不完整'}), 400 result = self.db_manager.save_detection_data(session_id, data_type, data_content) if result: return jsonify({'success': True, 'message': '数据采集成功'}) else: return jsonify({'success': False, 'error': '数据采集失败'}), 500 except Exception as e: self.logger.error(f'采集检测数据失败: {e}') return jsonify({'success': False, 'error': str(e)}), 500 # ==================== 同步录制API ==================== @self.app.route('/api/sync-recording/start', methods=['POST']) def start_sync_recording(): """启动同步录制""" try: data = flask_request.get_json() session_id = data.get('session_id') if not session_id: return jsonify({'success': False, 'error': '会话ID不能为空'}), 400 if self.device_manager: result = self.device_manager.start_sync_recording(session_id) if result['success']: self.logger.info(f'同步录制已启动 - 会话ID: {session_id}') return jsonify(result) else: return jsonify(result), 500 else: return jsonify({'success': False, 'error': '设备管理器未初始化'}), 500 except Exception as e: self.logger.error(f'启动同步录制失败: {e}') return jsonify({'success': False, 'error': str(e)}), 500 @self.app.route('/api/sync-recording/stop', methods=['POST']) def stop_sync_recording(): """停止同步录制""" try: data = flask_request.get_json() session_id = data.get('session_id') if not session_id: return jsonify({'success': False, 'error': '会话ID不能为空'}), 400 if self.device_manager: result = self.device_manager.stop_sync_recording(session_id) if result['success']: self.logger.info(f'同步录制已停止 - 会话ID: {session_id}') return jsonify(result) else: return jsonify(result), 500 else: return jsonify({'success': False, 'error': '设备管理器未初始化'}), 500 except Exception as e: self.logger.error(f'停止同步录制失败: {e}') return jsonify({'success': False, 'error': str(e)}), 500 # ==================== 历史记录API ==================== @self.app.route('/api/history/sessions', methods=['GET']) def get_detection_sessions(): """获取检测会话历史""" try: page = int(flask_request.args.get('page', 1)) size = int(flask_request.args.get('size', 10)) patient_id = flask_request.args.get('patient_id') sessions = self.db_manager.get_detection_sessions(page, size, patient_id) total = self.db_manager.get_sessions_count(patient_id) return jsonify({ 'success': True, 'data': { 'sessions': sessions, 'total': total, 'page': page, 'size': size } }) except Exception as e: self.logger.error(f'获取检测历史失败: {e}') return jsonify({'success': False, 'error': str(e)}), 500 # ==================== 错误处理 ==================== @self.app.errorhandler(404) def not_found(error): return jsonify({'success': False, 'error': 'API接口不存在'}), 404 @self.app.errorhandler(500) def internal_error(error): return jsonify({'success': False, 'error': '服务器内部错误'}), 500 def _register_socketio_events(self): """注册SocketIO事件""" if self.socketio is None: return # 注册各设备命名空间的连接事件 @self.socketio.on('connect', namespace='/camera') def handle_camera_connect(): self.logger.info('相机命名空间客户端连接') emit('status', {'message': '相机命名空间连接成功'}, namespace='/camera') @self.socketio.on('connect', namespace='/femtobolt') def handle_femtobolt_connect(): self.logger.info('深度相机命名空间客户端连接') emit('status', {'message': '深度相机命名空间连接成功'}, namespace='/femtobolt') @self.socketio.on('connect', namespace='/imu') def handle_imu_connect(): self.logger.info('IMU命名空间客户端连接') emit('status', {'message': 'IMU命名空间连接成功'}, namespace='/imu') @self.socketio.on('connect', namespace='/pressure') def handle_pressure_connect(): self.logger.info('压力板命名空间客户端连接') emit('status', {'message': '压力板命名空间连接成功'}, namespace='/pressure') @self.socketio.on('disconnect', namespace='/camera') def handle_camera_disconnect(): self.logger.info('相机命名空间客户端断开连接') @self.socketio.on('disconnect', namespace='/femtobolt') def handle_femtobolt_disconnect(): self.logger.info('深度相机命名空间客户端断开连接') @self.socketio.on('disconnect', namespace='/imu') def handle_imu_disconnect(): self.logger.info('IMU命名空间客户端断开连接') @self.socketio.on('disconnect', namespace='/pressure') def handle_pressure_disconnect(): self.logger.info('压力板命名空间客户端断开连接') @self.socketio.on('start_push_data') def handle_start_push_data(): """启动数据推送""" try: self.start_device_push_data() emit('test_status', {'status': 'started', 'message': '数据推送已开始'}) except Exception as e: emit('test_status', {'status': 'error', 'message': str(e)}) @self.socketio.on('stop_push_data') def handle_stop_push_data(): """停止数据推送""" try: self.stop_device_push_data() emit('test_status', {'status': 'stopped', 'message': '数据推送已停止'}) except Exception as e: emit('test_status', {'status': 'error', 'message': str(e)}) def start_device_push_data(self): """开始设备数据推送""" if self.is_testing: self.logger.warning('设备数据推送已在运行') return try: self.logger.info('开始设备数据推送...') self.is_testing = True # 并行启动真实设备管理器 failed_devices = [] device_threads = {} device_results = {} def initialize_device(device_name, manager): """设备初始化工作函数""" try: print(f"[DEBUG] 尝试初始化设备: {device_name}") if manager.initialize(): print(f"[DEBUG] {device_name} 初始化成功,开始启动流") manager.start_streaming() device_results[device_name] = True self.logger.info(f'{device_name}设备启动成功') else: print(f"[DEBUG] {device_name} 初始化失败") device_results[device_name] = False self.logger.error(f'{device_name}设备启动失败') except Exception as e: print(f"[DEBUG] {device_name} 初始化异常: {e}") device_results[device_name] = False self.logger.error(f'{device_name}设备启动异常: {e}') # 为每个设备创建初始化线程 for device_name, manager in self.device_managers.items(): if manager is not None: # 确保管理器已初始化 thread = threading.Thread( target=initialize_device, args=(device_name, manager), name=f'Init-{device_name}', daemon=True ) device_threads[device_name] = thread thread.start() else: self.logger.warning(f'{device_name}管理器未初始化,跳过启动') device_results[device_name] = False # 等待所有设备初始化完成(最多等待30秒) for device_name, thread in device_threads.items(): thread.join(timeout=30.0) if thread.is_alive(): self.logger.warning(f'{device_name}设备初始化超时') device_results[device_name] = False # 收集失败的设备 for device_name, success in device_results.items(): if not success: failed_devices.append(device_name) # 输出启动结果摘要 successful_devices = [name for name, success in device_results.items() if success] if successful_devices: self.logger.info(f'成功启动的设备: {", ".join(successful_devices)}') if failed_devices: self.logger.warning(f'启动失败的设备: {", ".join(failed_devices)}') self.logger.info('设备数据推送已启动') except Exception as e: self.logger.error(f'启动设备数据推送失败: {e}') self.is_testing = False raise def stop_device_push_data(self): """停止设备数据推送""" if not self.is_testing: self.logger.warning('设备数据推送未运行') return try: self.logger.info('停止设备数据推送...') self.is_testing = False # 停止设备管理器 for device_name, manager in self.device_managers.items(): if manager is not None: try: manager.stop_streaming() manager.disconnect() self.logger.info(f'{device_name}设备已停止') except Exception as e: self.logger.error(f'停止{device_name}设备失败: {e}') self.logger.info('设备数据推送已停止') except Exception as e: self.logger.error(f'停止设备数据推送失败: {e}') def _detection_worker(self, detection_id, duration): """检测工作线程""" try: self.logger.info(f'检测线程启动 - ID: {detection_id}, 持续时间: {duration}秒') # 模拟检测过程 start_time = time.time() while time.time() - start_time < duration: if not self.current_detection: break # 发送检测进度 elapsed = time.time() - start_time progress = min(100, (elapsed / duration) * 100) if self.socketio: self.socketio.emit('detection_progress', { 'detection_id': detection_id, 'progress': progress, 'elapsed_time': elapsed, 'remaining_time': max(0, duration - elapsed) }) time.sleep(1) # 检测完成 if self.current_detection and self.current_detection['id'] == detection_id: self.db_manager.end_detection_session(detection_id) self.current_detection = None if self.socketio: self.socketio.emit('detection_complete', { 'detection_id': detection_id, 'message': '检测已完成' }) self.logger.info(f'检测完成 - ID: {detection_id}') except Exception as e: self.logger.error(f'检测线程异常: {e}') if self.current_detection: self.current_detection = None def run(self, debug=None): """运行服务器""" if debug is not None: self.debug = debug try: self.logger.info('正在启动AppServer...') self.init_app() except KeyboardInterrupt: self.logger.info('服务被用户中断') except Exception as e: self.logger.error(f'服务启动失败: {e}') sys.exit(1) finally: self.logger.info('AppServer已停止') def main(): """主函数""" # 解析命令行参数 parser = argparse.ArgumentParser(description='Body Balance Evaluation System Backend') parser.add_argument('--host', default='localhost', help='Host address to bind to') parser.add_argument('--port', type=int, default=5000, help='Port number to bind to') parser.add_argument('--debug', action='store_true', help='Enable debug mode') args = parser.parse_args() # 创建并运行服务器 server = AppServer(host=args.host, port=args.port, debug=args.debug) server.run() if __name__ == '__main__': main()