BodyBalanceEvaluation/backend/app.py
2025-07-31 17:53:09 +08:00

994 lines
33 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
平衡体态检测系统 - 后端服务
基于Flask的本地API服务
"""
import os
import sys
import json
import time
import threading
from datetime import datetime
from flask import Flask, request, jsonify, send_file
from flask_cors import CORS
import sqlite3
import logging
from pathlib import Path
from flask_socketio import SocketIO, emit
import cv2
import configparser
import os
# 添加当前目录到Python路径
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
# 导入自定义模块
from database import DatabaseManager
from device_manager import DeviceManager, VideoStreamManager
from detection_engine import DetectionEngine, detection_bp
from data_processor import DataProcessor
from utils import config as app_config
# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('logs/backend.log', encoding='utf-8'),
logging.StreamHandler()
]
)
logger = logging.getLogger(__name__)
# 创建Flask应用
app = Flask(__name__)
app.config['SECRET_KEY'] = 'body-balance-detection-system-2024'
socketio = SocketIO(app, cors_allowed_origins='*', async_mode='threading')
# 启用CORS支持
CORS(app, origins='*', supports_credentials=True, allow_headers=['Content-Type', 'Authorization'], methods=['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'])
# 注册Blueprint
app.register_blueprint(detection_bp)
# 读取RTSP配置
config = configparser.ConfigParser()
config.read(os.path.join(os.path.dirname(__file__), 'config.ini'), encoding='utf-8')
rtsp_url = config.get('CAMERA', 'rtsp_url', fallback=None)
# 全局变量
db_manager = None
device_manager = None
detection_engine = None
data_processor = None
current_detection = None
detection_thread = None
video_stream_manager = None
def init_app():
"""初始化应用"""
global db_manager, device_manager, detection_engine, data_processor, video_stream_manager
try:
# 创建必要的目录
os.makedirs('logs', exist_ok=True)
os.makedirs('data', exist_ok=True)
os.makedirs('exports', exist_ok=True)
os.makedirs('videos', exist_ok=True)
# 从配置文件读取数据库路径
db_path = app_config.get('DATABASE', 'path', 'backend/data/body_balance.db')
# 确保数据库目录存在
db_dir = os.path.dirname(db_path)
os.makedirs(db_dir, exist_ok=True)
# 初始化数据库
db_manager = DatabaseManager(db_path)
db_manager.init_database()
# 初始化设备管理器
device_manager = DeviceManager()
# 初始化检测引擎
detection_engine = DetectionEngine()
# 初始化数据处理器
data_processor = DataProcessor()
# 初始化视频流管理器
video_stream_manager = VideoStreamManager(socketio)
logger.info('应用初始化完成')
except Exception as e:
logger.error(f'应用初始化失败: {e}')
raise
# ==================== 基础API ====================
@app.route('/health', methods=['GET'])
def health_check():
"""健康检查"""
return jsonify({
'status': 'ok',
'timestamp': datetime.now().isoformat(),
'version': '1.0.0'
})
@app.route('/api/health', methods=['GET'])
def api_health_check():
"""API健康检查"""
return jsonify({
'status': 'ok',
'timestamp': datetime.now().isoformat(),
'version': '1.0.0'
})
# ==================== 认证API ====================
@app.route('/api/auth/login', methods=['POST'])
def login():
"""用户登录"""
try:
data = request.get_json()
username = data.get('username')
password = data.get('password')
remember = data.get('remember', False)
if not username or not password:
return jsonify({
'success': False,
'message': '用户名或密码不能为空'
}), 400
# 使用数据库验证用户
user = 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_{username}_{int(time.time())}"
logger.info(f'用户 {username} 登录成功')
return jsonify({
'success': True,
'data': {
'token': token,
'user': user_data
},
'message': '登录成功'
})
else:
logger.warning(f'用户 {username} 登录失败:用户名或密码错误')
return jsonify({
'success': False,
'message': '用户名或密码错误'
}), 401
except Exception as e:
logger.error(f'登录失败: {e}')
return jsonify({'success': False, 'message': '登录失败'}), 500
@app.route('/api/auth/register', methods=['POST'])
def register():
"""用户注册"""
try:
data = request.get_json()
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 = db_manager.register_user(user_data)
if result['success']:
logger.info(f'用户 {username} 注册成功,等待管理员审核')
return jsonify({
'success': True,
'message': '注册成功,请等待管理员审核后登录'
})
else:
return jsonify({
'success': False,
'message': result['message']
}), 400
except Exception as e:
logger.error(f'注册失败: {e}')
return jsonify({'success': False, 'message': '注册失败'}), 500
@app.route('/api/auth/logout', methods=['POST'])
def logout():
"""用户退出"""
try:
return jsonify({
'success': True,
'message': '退出成功'
})
except Exception as e:
logger.error(f'退出失败: {e}')
return jsonify({'success': False, 'message': '退出失败'}), 500
@app.route('/api/auth/verify', methods=['GET'])
def verify_token():
"""验证token"""
try:
# 简单的token验证实际项目中应验证JWT等
auth_header = request.headers.get('Authorization')
if auth_header and auth_header.startswith('Bearer '):
token = auth_header.split(' ')[1]
# 这里可以添加真实的token验证逻辑
return jsonify({
'success': True,
'data': {'valid': True}
})
else:
return jsonify({
'success': True,
'data': {'valid': True} # 暂时总是返回有效
})
except Exception as e:
logger.error(f'验证token失败: {e}')
return jsonify({'success': False, 'message': '验证失败'}), 500
@app.route('/api/auth/forgot-password', methods=['POST'])
def forgot_password():
"""忘记密码 - 根据用户名和手机号找回密码"""
try:
data = request.get_json()
username = data.get('username')
phone = data.get('phone')
if not username:
return jsonify({
'success': False,
'error': '请输入用户名'
}), 400
if not phone:
return jsonify({
'success': False,
'error': '请输入手机号码'
}), 400
# 验证手机号格式
import re
phone_pattern = r'^1[3-9]\d{9}$'
if not re.match(phone_pattern, phone):
return jsonify({
'success': False,
'error': '手机号格式不正确'
}), 400
# 查询用户信息
conn = db_manager.get_connection()
cursor = conn.cursor()
cursor.execute('''
SELECT username, password, phone FROM users
WHERE username = ? AND phone = ?
''', (username, phone))
user = cursor.fetchone()
if user:
# 用户存在且手机号匹配,返回密码
# 注意:这里返回的是加密后的密码,实际应用中需要解密或重置
# 为了演示,我们假设有一个简单的解密方法
encrypted_password = user['password']
# 这里简化处理,实际应该有更安全的密码找回机制
# 由于使用MD5加密无法直接解密所以返回提示信息
return jsonify({
'success': True,
'password': '1234567', # 演示用固定密码
'message': '密码找回成功'
})
else:
# 检查用户是否存在
cursor.execute('SELECT username FROM users WHERE username = ?', (username,))
user_exists = cursor.fetchone()
if not user_exists:
return jsonify({
'success': False,
'error': '用户不存在'
}), 400
else:
return jsonify({
'success': False,
'error': '手机号不匹配'
}), 400
except Exception as e:
logger.error(f'忘记密码处理失败: {e}')
return jsonify({'success': False, 'error': '处理失败'}), 500
# ==================== 用户管理API ====================
@app.route('/api/users', methods=['GET'])
def get_users():
"""获取用户列表(管理员功能)"""
try:
# 这里应该验证管理员权限
page = int(request.args.get('page', 1))
size = int(request.args.get('size', 10))
status = request.args.get('status') # active, inactive, all
users = db_manager.get_users(page, size, status)
total = db_manager.get_user_count(status)
return jsonify({
'success': True,
'data': {
'users': users,
'total': total,
'page': page,
'size': size
}
})
except Exception as e:
logger.error(f'获取用户列表失败: {e}')
return jsonify({'success': False, 'message': '获取用户列表失败'}), 500
@app.route('/api/users/<int:user_id>/approve', methods=['POST'])
def approve_user(user_id):
"""审核用户(管理员功能)"""
try:
# 这里应该验证管理员权限
data = request.get_json()
approve = data.get('approve', True)
result = db_manager.approve_user(user_id, approve)
if result['success']:
action = '审核通过' if approve else '审核拒绝'
logger.info(f'用户 {user_id} {action}')
return jsonify({
'success': True,
'message': f'用户{action}成功'
})
else:
return jsonify({
'success': False,
'message': result['message']
}), 400
except Exception as e:
logger.error(f'审核用户失败: {e}')
return jsonify({'success': False, 'message': '审核用户失败'}), 500
@app.route('/api/users/<int:user_id>', methods=['DELETE'])
def delete_user(user_id):
"""删除用户(管理员功能)"""
try:
# 这里应该验证管理员权限
result = db_manager.delete_user(user_id)
if result['success']:
logger.info(f'用户 {user_id} 删除成功')
return jsonify({
'success': True,
'message': '用户删除成功'
})
else:
return jsonify({
'success': False,
'message': result['message']
}), 400
except Exception as e:
logger.error(f'删除用户失败: {e}')
return jsonify({'success': False, 'message': '删除用户失败'}), 500
@app.route('/api/auth/reset-password', methods=['POST'])
def reset_password():
"""重置密码"""
try:
data = request.get_json()
token = data.get('token')
password = data.get('password')
if token and password:
return jsonify({
'success': True,
'message': '密码重置成功'
})
else:
return jsonify({
'success': False,
'message': '参数不完整'
}), 400
except Exception as e:
logger.error(f'重置密码失败: {e}')
return jsonify({'success': False, 'message': '重置失败'}), 500
@app.route('/api/system/info', methods=['GET'])
def get_system_info():
"""获取系统信息"""
try:
info = {
'version': '1.0.0',
'start_time': datetime.now().isoformat(),
'database_status': 'connected' if db_manager else 'disconnected',
'device_count': len(device_manager.get_connected_devices()) if device_manager else 0
}
return jsonify({'success': True, 'data': info})
except Exception as e:
logger.error(f'获取系统信息失败: {e}')
return jsonify({'success': False, 'error': str(e)}), 500
# ==================== 设备管理API ====================
@app.route('/api/devices/status', methods=['GET'])
def get_device_status():
"""获取设备状态"""
try:
if not device_manager:
return jsonify({'camera': False, 'imu': False, 'pressure': False})
status = device_manager.get_device_status()
return jsonify(status)
except Exception as e:
logger.error(f'获取设备状态失败: {e}')
return jsonify({'camera': False, 'imu': False, 'pressure': False})
@app.route('/api/devices/refresh', methods=['POST'])
def refresh_devices():
"""刷新设备连接"""
try:
if device_manager:
device_manager.refresh_devices()
return jsonify({'success': True, 'message': '设备已刷新'})
except Exception as e:
logger.error(f'刷新设备失败: {e}')
return jsonify({'success': False, 'error': str(e)}), 500
@app.route('/api/devices/calibrate', methods=['POST'])
def calibrate_devices():
"""校准设备"""
try:
if device_manager:
result = device_manager.calibrate_devices()
return jsonify({'success': True, 'data': result})
return jsonify({'success': False, 'error': '设备管理器未初始化'}), 500
except Exception as e:
logger.error(f'设备校准失败: {e}')
return jsonify({'success': False, 'error': str(e)}), 500
# ==================== 患者管理API ====================
@app.route('/api/patients', methods=['GET'])
def get_patients():
"""获取患者列表"""
try:
page = int(request.args.get('page', 1))
size = int(request.args.get('size', 10))
keyword = request.args.get('keyword', '')
patients = db_manager.get_patients(page, size, keyword)
total = db_manager.get_patients_count(keyword)
return jsonify({
'success': True,
'data': {
'patients': patients,
'total': total,
'page': page,
'size': size
}
})
except Exception as e:
logger.error(f'获取患者列表失败: {e}')
return jsonify({'success': False, 'error': str(e)}), 500
@app.route('/api/patients', methods=['POST'])
def create_patient():
"""创建患者"""
try:
data = request.get_json()
patient_id = db_manager.create_patient(data)
return jsonify({
'success': True,
'data': {'patient_id': patient_id},
'message': '患者创建成功'
})
except Exception as e:
logger.error(f'创建患者失败: {e}')
return jsonify({'success': False, 'error': str(e)}), 500
@app.route('/api/patients/<patient_id>', methods=['PUT'])
def update_patient(patient_id):
"""更新患者信息"""
try:
data = request.get_json()
db_manager.update_patient(patient_id, data)
return jsonify({'success': True, 'message': '患者信息更新成功'})
except Exception as e:
logger.error(f'更新患者信息失败: {e}')
return jsonify({'success': False, 'error': str(e)}), 500
@app.route('/api/patients/<patient_id>', methods=['GET'])
def get_patient(patient_id):
"""获取单个患者信息"""
try:
patient = 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:
logger.error(f'获取患者信息失败: {e}')
return jsonify({'success': False, 'error': str(e)}), 500
@app.route('/api/patients/<patient_id>', methods=['DELETE'])
def delete_patient(patient_id):
"""删除患者"""
try:
db_manager.delete_patient(patient_id)
return jsonify({'success': True, 'message': '患者删除成功'})
except Exception as e:
logger.error(f'删除患者失败: {e}')
return jsonify({'success': False, 'error': str(e)}), 500
# ==================== 检测管理API ====================
@app.route('/api/detection/start', methods=['POST'])
def start_detection():
"""开始检测"""
global current_detection, detection_thread
try:
data = request.get_json()
patient_id = data.get('patientId')
settings_str = data.get('settings', '{}')
if not patient_id:
return jsonify({'success': False, 'error': '缺少患者ID'}), 400
# 检查是否已有检测在进行
if current_detection and current_detection.get('status') == 'running':
return jsonify({'success': False, 'error': '已有检测在进行中'}), 400
# 解析settings参数
try:
if isinstance(settings_str, str):
settings = json.loads(settings_str)
else:
settings = settings_str or {}
except json.JSONDecodeError:
settings = {}
# 设置默认值
duration = settings.get('duration', 300) # 默认5分钟
frequency = settings.get('frequency', 30) # 默认30Hz
# 创建检测会话
session_id = db_manager.create_detection_session(
patient_id=patient_id,
settings=settings
)
logger.info(f'创建检测会话成功: {session_id}, 患者ID: {patient_id}')
# 初始化检测状态
current_detection = {
'session_id': session_id,
'patient_id': patient_id,
'status': 'running',
'start_time': datetime.now(),
'settings': settings,
'data_points': 0
}
# 启动检测线程
detection_thread = threading.Thread(
target=run_detection,
args=(session_id, settings)
)
detection_thread.start()
return jsonify({
'success': True,
'data': {'session_id': session_id},
'message': '检测已开始'
})
except Exception as e:
logger.error(f'开始检测失败: {e}')
return jsonify({'success': False, 'error': str(e)}), 500
@app.route('/api/detection/stop', methods=['POST'])
def stop_detection():
"""停止检测"""
global current_detection, detection_thread
try:
data = request.get_json() or {}
session_id = data.get('sessionId')
frontend_duration = data.get('duration', 0) # 前端计算的持续时间
if not current_detection or current_detection.get('status') != 'running':
return jsonify({'success': False, 'error': '没有正在进行的检测'}), 400
# 更新检测状态
current_detection['status'] = 'stopped'
end_time = datetime.now()
current_detection['end_time'] = end_time
# 计算持续时间(优先使用前端传递的时间,否则使用后端计算的时间)
duration = frontend_duration
if duration <= 0:
start_time = current_detection.get('start_time')
duration = int((end_time - start_time).total_seconds()) if start_time else 0
# 等待检测线程结束
if detection_thread and detection_thread.is_alive():
detection_thread.join(timeout=5)
# 如果提供了sessionId更新数据库中的会话状态
if session_id:
# 更新会话状态为completed并设置结束时间
db_manager.update_session_status(session_id, 'completed')
# 更新持续时间
if duration > 0:
db_manager.update_session_duration(session_id, duration)
logger.info(f'检测会话已停止: {session_id}, 持续时间: {duration}')
current_detection = None
return jsonify({
'success': True,
'message': '检测已停止',
'sessionId': session_id,
'duration': duration
})
current_detection = None
return jsonify({
'success': True,
'message': '检测已停止'
})
except Exception as e:
logger.error(f'停止检测失败: {e}')
return jsonify({
'success': False,
'message': f'停止检测失败: {str(e)}'
}), 500
@app.route('/api/sessions/<session_id>/video-path', methods=['PUT'])
def update_session_video_path(session_id):
"""更新会话视频路径"""
try:
data = request.get_json()
if not data or 'videoPath' not in data:
return jsonify({
'success': False,
'message': '缺少视频路径参数'
}), 400
video_path = data['videoPath']
# 更新数据库中的视频路径
db_manager.update_session_video_path(session_id, video_path)
return jsonify({
'success': True,
'message': '视频路径更新成功',
'sessionId': session_id,
'videoPath': video_path
})
except Exception as e:
logger.error(f'更新会话视频路径失败: {e}')
return jsonify({
'success': False,
'message': f'更新失败: {str(e)}'
}), 500
@app.route('/api/detection/status', methods=['GET'])
def get_detection_status():
"""获取检测状态"""
try:
if not current_detection:
return jsonify({
'success': True,
'data': {'status': 'idle'}
})
# 计算运行时间
if current_detection.get('status') == 'running':
elapsed = (datetime.now() - current_detection['start_time']).total_seconds()
current_detection['elapsed_time'] = int(elapsed)
return jsonify({
'success': True,
'data': current_detection
})
except Exception as e:
logger.error(f'获取检测状态失败: {e}')
return jsonify({'success': False, 'error': str(e)}), 500
@app.route('/api/detection/data', methods=['GET'])
def get_realtime_data():
"""获取实时检测数据"""
try:
if not current_detection or current_detection.get('status') != 'running':
return jsonify({'success': False, 'error': '没有正在进行的检测'})
# 获取最新的检测数据
session_id = current_detection['session_id']
data = detection_engine.get_latest_data(session_id)
return jsonify({
'success': True,
'data': data
})
except Exception as e:
logger.error(f'获取实时数据失败: {e}')
return jsonify({'success': False, 'error': str(e)}), 500
# ==================== 数据分析API ====================
@app.route('/api/analysis/session/<session_id>', methods=['GET'])
def analyze_session(session_id):
"""分析检测会话数据"""
try:
# 获取会话数据
session_data = db_manager.get_session_data(session_id)
if not session_data:
return jsonify({'success': False, 'error': '会话不存在'}), 404
# 进行数据分析
analysis_result = data_processor.analyze_session(session_data)
# 保存分析结果
db_manager.save_analysis_result(session_id, analysis_result)
return jsonify({
'success': True,
'data': analysis_result
})
except Exception as e:
logger.error(f'分析会话数据失败: {e}')
return jsonify({'success': False, 'error': str(e)}), 500
@app.route('/api/export/report/<session_id>', methods=['GET'])
def export_report(session_id):
"""导出检测报告"""
try:
# 生成报告
report_path = data_processor.generate_report(session_id)
if not os.path.exists(report_path):
return jsonify({'success': False, 'error': '报告生成失败'}), 500
return send_file(
report_path,
as_attachment=True,
download_name=f'detection_report_{session_id}.pdf'
)
except Exception as e:
logger.error(f'导出报告失败: {e}')
return jsonify({'success': False, 'error': str(e)}), 500
# ==================== 历史记录API ====================
@app.route('/api/history/sessions', methods=['GET'])
def get_detection_sessions():
"""获取检测会话历史"""
try:
page = int(request.args.get('page', 1))
size = int(request.args.get('size', 10))
patient_id = request.args.get('patient_id')
sessions = db_manager.get_detection_sessions(page, size, patient_id)
total = db_manager.get_sessions_count(patient_id)
return jsonify({
'success': True,
'data': {
'sessions': sessions,
'total': total,
'page': page,
'size': size
}
})
except Exception as e:
logger.error(f'获取检测历史失败: {e}')
return jsonify({'success': False, 'error': str(e)}), 500
def run_detection(session_id, settings):
"""运行检测的后台线程"""
global current_detection
try:
logger.info(f'开始检测会话: {session_id}')
# 检测循环
while (current_detection and
current_detection.get('status') == 'running'):
# 采集数据
if device_manager:
data = device_manager.collect_data()
if data:
# 保存数据到数据库
db_manager.save_detection_data(session_id, data)
current_detection['data_points'] += 1
# 根据采样频率控制循环间隔
frequency = settings.get('frequency', 60)
time.sleep(1.0 / frequency)
# 检查是否达到设定时长
duration = settings.get('duration', 0)
if duration > 0:
elapsed = (datetime.now() - current_detection['start_time']).total_seconds()
if elapsed >= duration:
current_detection['status'] = 'completed'
break
# 更新会话状态
if current_detection:
db_manager.update_session_status(
session_id,
current_detection['status'],
current_detection.get('data_points', 0)
)
logger.info(f'检测会话完成: {session_id}')
except Exception as e:
logger.error(f'检测线程异常: {e}')
if current_detection:
current_detection['status'] = 'error'
current_detection['error'] = str(e)
# ==================== 截图和录像API已移至detection_engine.py ====================
# 相关路由现在通过detection_bp Blueprint提供
# ==================== 错误处理 ====================
@app.errorhandler(404)
def not_found(error):
return jsonify({'success': False, 'error': 'API接口不存在'}), 404
@app.errorhandler(500)
def internal_error(error):
return jsonify({'success': False, 'error': '服务器内部错误'}), 500
if __name__ == '__main__':
import argparse
# 解析命令行参数
parser = argparse.ArgumentParser(description='Body Balance Evaluation System Backend')
parser.add_argument('--host', default=None, help='Host address to bind to')
parser.add_argument('--port', type=int, default=None, help='Port number to bind to')
parser.add_argument('--debug', action='store_true', help='Enable debug mode')
args = parser.parse_args()
try:
# 初始化应用
init_app()
# 确定主机和端口
host = args.host if args.host else config.get('SERVER', 'host', fallback='127.0.0.1')
port = args.port if args.port else config.getint('SERVER', 'port', fallback=5000)
debug = args.debug if args.debug else config.getboolean('APP', 'debug', fallback=False)
# 启动Flask+SocketIO服务
logger.info(f'启动后端服务... Host: {host}, Port: {port}, Debug: {debug}')
socketio.run(app,
host=host,
port=port,
debug=debug,
use_reloader=False, # 禁用热重载以避免进程问题
log_output=True, # 启用详细日志
allow_unsafe_werkzeug=True
)
except KeyboardInterrupt:
logger.info('服务被用户中断')
except Exception as e:
logger.error(f'服务启动失败: {e}')
sys.exit(1)
finally:
logger.info('后端服务已停止')
# ==================== WebSocket 事件处理 ====================
@socketio.on('start_rtsp')
def handle_start_rtsp(data=None):
logger.info(f'收到start_rtsp事件客户端ID: {request.sid}, 数据: {data}')
try:
if video_stream_manager:
result = video_stream_manager.start_rtsp_stream()
emit('rtsp_status', result)
else:
emit('rtsp_status', {'status': 'error', 'message': '视频流管理器未初始化'})
except Exception as e:
logger.error(f'启动RTSP失败: {e}')
emit('rtsp_status', {'status': 'error', 'message': f'启动失败: {str(e)}'})
@socketio.on('stop_rtsp')
def handle_stop_rtsp(data=None):
logger.info(f'收到stop_rtsp事件客户端ID: {request.sid}, 数据: {data}')
try:
if video_stream_manager:
result = video_stream_manager.stop_rtsp_stream()
emit('rtsp_status', result)
else:
emit('rtsp_status', {'status': 'error', 'message': '视频流管理器未初始化'})
except Exception as e:
logger.error(f'停止RTSP失败: {e}')
emit('rtsp_status', {'status': 'error', 'message': f'停止失败: {str(e)}'})
@socketio.on('connect')
def handle_connect():
logger.info(f'客户端连接: {request.sid}')
emit('connect_status', {'status': 'connected', 'sid': request.sid, 'message': '连接成功'})
@socketio.on('disconnect')
def handle_disconnect():
logger.info(f'客户端断开连接: {request.sid}')
@socketio.on('ping')
def handle_ping(data=None):
logger.info(f'收到ping事件客户端ID: {request.sid}, 数据: {data}')
emit('pong', {'timestamp': time.time(), 'message': 'pong'})