更改了文件和视频存储方法
This commit is contained in:
parent
21c6611619
commit
566c199413
2404
.gitignore
vendored
2404
.gitignore
vendored
File diff suppressed because it is too large
Load Diff
@ -10,13 +10,16 @@ port = 5000
|
||||
cors_origins = *
|
||||
|
||||
[DATABASE]
|
||||
path = data/body_balance.db
|
||||
path = D:/BodyCheck/data/body_balance.db
|
||||
backup_interval = 24
|
||||
max_backups = 7
|
||||
|
||||
[FILEPATH]
|
||||
path = D:/BodyCheck/file/
|
||||
|
||||
[CAMERA]
|
||||
enabled = True
|
||||
device_index = 1
|
||||
device_index = 0
|
||||
width = 1280
|
||||
height = 720
|
||||
fps = 30
|
||||
|
@ -25,15 +25,18 @@ try:
|
||||
from .camera_manager import CameraManager
|
||||
from .femtobolt_manager import FemtoBoltManager
|
||||
from .pressure_manager import PressureManager
|
||||
from .utils.config_manager import ConfigManager
|
||||
except ImportError:
|
||||
from camera_manager import CameraManager
|
||||
from femtobolt_manager import FemtoBoltManager
|
||||
from pressure_manager import PressureManager
|
||||
from utils.config_manager import ConfigManager
|
||||
|
||||
class RecordingManager:
|
||||
def __init__(self, camera_manager: Optional[CameraManager] = None, db_manager=None,
|
||||
femtobolt_manager: Optional[FemtoBoltManager] = None,
|
||||
pressure_manager: Optional[PressureManager] = None):
|
||||
pressure_manager: Optional[PressureManager] = None,
|
||||
config_manager: Optional[ConfigManager] = None):
|
||||
"""
|
||||
初始化录制管理器
|
||||
|
||||
@ -42,12 +45,16 @@ class RecordingManager:
|
||||
db_manager: 数据库管理器实例
|
||||
femtobolt_manager: FemtoBolt深度相机管理器实例
|
||||
pressure_manager: 压力传感器管理器实例
|
||||
config_manager: 配置管理器实例
|
||||
"""
|
||||
self.camera_manager = camera_manager
|
||||
self.db_manager = db_manager
|
||||
self.femtobolt_manager = femtobolt_manager
|
||||
self.pressure_manager = pressure_manager
|
||||
|
||||
# 配置管理
|
||||
self.config_manager = config_manager or ConfigManager()
|
||||
|
||||
# 录制状态
|
||||
self.sync_recording = False
|
||||
self.is_recording = False
|
||||
@ -271,9 +278,6 @@ class RecordingManager:
|
||||
|
||||
# 设置默认录制类型
|
||||
recording_types = ['screen', 'feet']
|
||||
# recording_types = ['screen']
|
||||
|
||||
|
||||
|
||||
# 验证录制区域参数(仅对启用的录制类型进行验证)
|
||||
if 'screen' in recording_types:
|
||||
@ -317,15 +321,12 @@ class RecordingManager:
|
||||
self.global_recording_start_time = None
|
||||
|
||||
self.recording_start_time = datetime.now()
|
||||
db_base_path = os.path.join('data', 'patients', patient_id, session_id)
|
||||
# 创建主存储目录
|
||||
if getattr(sys, 'frozen', False):
|
||||
# 打包后的exe文件路径
|
||||
exe_dir = os.path.dirname(sys.executable)
|
||||
base_path = os.path.join(exe_dir, 'data', 'patients', patient_id, session_id)
|
||||
else:
|
||||
base_path = os.path.join('data', 'patients', patient_id, session_id)
|
||||
|
||||
# 创建主存储目录
|
||||
timestamp = datetime.now().strftime('%H%M%S%f')[:-3] # 精确到毫秒
|
||||
file_dir = self.config_manager.get_config_value('FILEPATH', 'path')
|
||||
base_path = os.path.join(file_dir, patient_id, session_id,f'video_{timestamp}')
|
||||
db_base_path = os.path.join(patient_id, session_id,f'video_{timestamp}')
|
||||
|
||||
try:
|
||||
# 设置目录权限
|
||||
@ -338,7 +339,7 @@ class RecordingManager:
|
||||
result['message'] = f'创建录制目录失败: {dir_error}'
|
||||
return result
|
||||
|
||||
# 定义视频文件路径
|
||||
|
||||
feet_video_path = os.path.join(base_path, 'feet.mp4')
|
||||
screen_video_path = os.path.join(base_path, 'screen.mp4')
|
||||
femtobolt_video_path = os.path.join(base_path, 'femtobolt.mp4')
|
||||
@ -801,13 +802,9 @@ class RecordingManager:
|
||||
Dict: 包含所有采集数据的字典,符合detection_data表结构
|
||||
"""
|
||||
# 生成采集时间戳
|
||||
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S_%f')[:-3] # 精确到毫秒
|
||||
if getattr(sys, 'frozen', False):
|
||||
# 打包后的exe文件路径
|
||||
exe_dir = os.path.dirname(sys.executable)
|
||||
data_dir = Path(os.path.join(exe_dir, 'data', 'patients', patient_id, session_id, timestamp))
|
||||
else:
|
||||
data_dir = Path(f'data/patients/{patient_id}/{session_id}/{timestamp}')
|
||||
timestamp = datetime.now().strftime('%H%M%S%f')[:-3] # 精确到毫秒
|
||||
file_path = self.config_manager.get_config_value('FILEPATH', 'path')
|
||||
data_dir = Path(os.path.join(file_path,patient_id, session_id, f"image_{timestamp}"))
|
||||
# 创建数据存储目录
|
||||
data_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
@ -861,7 +858,7 @@ class RecordingManager:
|
||||
|
||||
# 更新数据字典中的图片路径
|
||||
|
||||
data[field] = str(os.path.join('data', 'patients', patient_id, session_id, timestamp, filename))
|
||||
data[field] = str(os.path.join(patient_id, session_id, f"image_{timestamp}", filename))
|
||||
self.logger.debug(f'{field}保存成功: {filename}')
|
||||
|
||||
except Exception as e:
|
||||
@ -869,7 +866,7 @@ class RecordingManager:
|
||||
# 屏幕截图
|
||||
screen_image = self._capture_screen_image(data_dir,timestamp)
|
||||
if screen_image:
|
||||
data['screen_image'] = str(os.path.join('data', 'patients', patient_id, session_id, timestamp, screen_image))
|
||||
data['screen_image'] = str(os.path.join( patient_id, session_id, f"image_{timestamp}", screen_image))
|
||||
|
||||
self.logger.debug(f'数据保存完成: {session_id}, 时间戳: {timestamp}')
|
||||
|
||||
|
@ -1,71 +0,0 @@
|
||||
[APP]
|
||||
name = Body Balance Evaluation System
|
||||
version = 1.0.0
|
||||
debug = True
|
||||
log_level = INFO
|
||||
|
||||
[SERVER]
|
||||
host = 0.0.0.0
|
||||
port = 5000
|
||||
cors_origins = *
|
||||
|
||||
[DATABASE]
|
||||
path = data/body_balance.db
|
||||
backup_interval = 24
|
||||
max_backups = 7
|
||||
|
||||
[CAMERA]
|
||||
enabled = True
|
||||
device_index = 0
|
||||
width = 1280
|
||||
height = 720
|
||||
fps = 30
|
||||
buffer_size = 1
|
||||
fourcc = MJPG
|
||||
backend = directshow
|
||||
|
||||
[FEMTOBOLT]
|
||||
enabled = True
|
||||
algorithm_type = plt
|
||||
color_resolution = 1080P
|
||||
depth_mode = NFOV_2X2BINNED
|
||||
camera_fps = 20
|
||||
depth_range_min = 800
|
||||
depth_range_max = 1200
|
||||
fps = 15
|
||||
synchronized_images_only = False
|
||||
|
||||
[DEVICES]
|
||||
imu_enabled = True
|
||||
imu_device_type = ble
|
||||
imu_port = COM9
|
||||
imu_mac_address = ef:3c:1a:0a:fe:02
|
||||
imu_baudrate = 9600
|
||||
pressure_enabled = True
|
||||
pressure_device_type = real
|
||||
pressure_use_mock = False
|
||||
pressure_port = COM5
|
||||
pressure_baudrate = 115200
|
||||
|
||||
[SYSTEM]
|
||||
log_level = INFO
|
||||
max_cache_size = 10
|
||||
cache_timeout = 5.0
|
||||
|
||||
[DETECTION]
|
||||
default_duration = 60
|
||||
sampling_rate = 30
|
||||
balance_threshold = 0.2
|
||||
posture_threshold = 5.0
|
||||
|
||||
[DATA_PROCESSING]
|
||||
filter_window = 5
|
||||
outlier_threshold = 2.0
|
||||
chart_dpi = 300
|
||||
export_format = csv
|
||||
|
||||
[SECURITY]
|
||||
secret_key = 79fcc4983d478c2ee672f3305d5e12c7c84fd1b58a18acb650e9f8125bfa805f
|
||||
session_timeout = 3600
|
||||
max_login_attempts = 5
|
||||
|
@ -56,9 +56,7 @@ class ConfigManager:
|
||||
|
||||
# 开发环境下的路径
|
||||
possible_paths.extend([
|
||||
os.path.join(os.path.dirname(__file__), 'config.ini'),
|
||||
os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'config.ini'), # backend/config.ini
|
||||
os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))), 'config.ini') # 项目根目录/config.ini
|
||||
])
|
||||
|
||||
for path in possible_paths:
|
||||
@ -71,7 +69,7 @@ class ConfigManager:
|
||||
if getattr(sys, 'frozen', False):
|
||||
default_path = os.path.join(os.path.dirname(sys.executable), 'config.ini')
|
||||
else:
|
||||
default_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'config.ini')
|
||||
default_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'config.ini'), # backend/config.ini
|
||||
|
||||
self.logger.warning(f"未找到配置文件,使用默认路径: {default_path}")
|
||||
return default_path
|
||||
@ -244,6 +242,8 @@ class ConfigManager:
|
||||
'heartbeat_interval': self.config.getfloat('SYSTEM', 'heartbeat_interval', fallback=30.0)
|
||||
}
|
||||
|
||||
|
||||
|
||||
def get_config_value(self, section: str, key: str, fallback: Any = None) -> Any:
|
||||
"""
|
||||
获取配置值
|
||||
|
100
backend/main.py
100
backend/main.py
@ -164,26 +164,28 @@ class AppServer:
|
||||
def init_app(self):
|
||||
"""初始化应用组件"""
|
||||
try:
|
||||
# 初始化数据库管理器
|
||||
self.logger.info('正在初始化数据库管理器...')
|
||||
# 在打包环境中使用可执行文件的目录,在开发环境中使用脚本文件的目录
|
||||
if getattr(sys, 'frozen', False):
|
||||
# 打包环境
|
||||
base_dir = os.path.dirname(sys.executable)
|
||||
else:
|
||||
# 开发环境
|
||||
base_dir = os.path.dirname(__file__)
|
||||
db_path = os.path.join(base_dir, '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('正在初始化数据库管理器...')
|
||||
db_path = self.config_manager.get_config_value('DATABASE', 'path', fallback=None)
|
||||
self.logger.info(f'数据库文件路径: {db_path}')
|
||||
|
||||
# 确保数据库目录存在
|
||||
try:
|
||||
os.makedirs(os.path.dirname(db_path), exist_ok=True)
|
||||
except Exception as e:
|
||||
self.logger.error(f'创建数据库目录失败: {e}')
|
||||
raise
|
||||
self.db_manager = DatabaseManager(db_path)
|
||||
self.db_manager.init_database()
|
||||
self.logger.info('数据库管理器初始化完成')
|
||||
|
||||
|
||||
|
||||
# 初始化设备协调器(统一管理所有设备)
|
||||
self.logger.info('正在初始化设备协调器...')
|
||||
self.device_coordinator = DeviceCoordinator(self.socketio)
|
||||
@ -250,31 +252,35 @@ class AppServer:
|
||||
|
||||
# ==================== 静态文件服务 ====================
|
||||
|
||||
@self.app.route('/data/<path:filename>', methods=['GET'])
|
||||
@self.app.route('/<path:filename>', methods=['GET'])
|
||||
def serve_static_files(filename):
|
||||
"""提供静态文件服务,代理backend/data/目录"""
|
||||
try:
|
||||
# 获取data目录的绝对路径
|
||||
if getattr(sys, 'frozen', False):
|
||||
# 打包环境
|
||||
data_dir = os.path.join(os.path.dirname(sys.executable), 'data')
|
||||
else:
|
||||
# 开发环境
|
||||
data_dir = os.path.join(os.path.dirname(__file__), 'data')
|
||||
# 读取配置中的文件存储根目录(优先使用 FILEPATH.path)
|
||||
cfg_dir = self.config_manager.get_config_value('FILEPATH', 'path', fallback=None)
|
||||
if not cfg_dir:
|
||||
return jsonify({'error': '未配置文件存储路径'}), 500
|
||||
# 规范化允许目录路径,消除末尾斜杠、大小写和符号链接影响
|
||||
cfg_dir = os.path.abspath(os.path.realpath(os.path.normpath(cfg_dir)))
|
||||
|
||||
# 安全检查:防止路径遍历攻击
|
||||
safe_path = os.path.normpath(filename)
|
||||
if '..' in safe_path or safe_path.startswith('/'):
|
||||
if (
|
||||
'..' in safe_path or
|
||||
safe_path.startswith('/') or # POSIX绝对路径
|
||||
safe_path.startswith('\\\\') or # Windows UNC路径
|
||||
os.path.isabs(safe_path) # Windows盘符绝对路径
|
||||
):
|
||||
return jsonify({'error': '非法路径'}), 400
|
||||
|
||||
file_path = os.path.join(data_dir, safe_path)
|
||||
file_path = os.path.join(cfg_dir, safe_path)
|
||||
|
||||
|
||||
# 检查文件是否存在
|
||||
if not os.path.exists(file_path):
|
||||
return jsonify({'error': '文件不存在'}), 404
|
||||
|
||||
# 检查是否在允许的目录内
|
||||
if not os.path.commonpath([data_dir, file_path]) == data_dir:
|
||||
if os.path.commonpath([cfg_dir, file_path]) != cfg_dir:
|
||||
return jsonify({'error': '访问被拒绝'}), 403
|
||||
|
||||
# 返回文件
|
||||
@ -293,47 +299,7 @@ class AppServer:
|
||||
self.logger.error(f'静态文件服务错误: {e}')
|
||||
return jsonify({'error': '服务器内部错误'}), 500
|
||||
|
||||
@self.app.route('/data/', methods=['GET'])
|
||||
@self.app.route('/data', methods=['GET'])
|
||||
def list_data_directory():
|
||||
"""列出data目录下的文件和文件夹"""
|
||||
try:
|
||||
# 获取data目录的绝对路径
|
||||
if getattr(sys, 'frozen', False):
|
||||
# 打包环境
|
||||
data_dir = os.path.join(os.path.dirname(sys.executable), 'data')
|
||||
else:
|
||||
# 开发环境
|
||||
data_dir = os.path.join(os.path.dirname(__file__), 'data')
|
||||
|
||||
if not os.path.exists(data_dir):
|
||||
return jsonify({'error': 'data目录不存在'}), 404
|
||||
|
||||
# 获取目录内容
|
||||
items = []
|
||||
for item in os.listdir(data_dir):
|
||||
item_path = os.path.join(data_dir, item)
|
||||
is_dir = os.path.isdir(item_path)
|
||||
size = os.path.getsize(item_path) if not is_dir else None
|
||||
modified = datetime.fromtimestamp(os.path.getmtime(item_path)).isoformat()
|
||||
|
||||
items.append({
|
||||
'name': item,
|
||||
'type': 'directory' if is_dir else 'file',
|
||||
'size': size,
|
||||
'modified': modified,
|
||||
'url': f'/data/{item}' if not is_dir else None
|
||||
})
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'path': '/data/',
|
||||
'items': sorted(items, key=lambda x: (x['type'] == 'file', x['name']))
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f'目录列表错误: {e}')
|
||||
return jsonify({'error': '服务器内部错误'}), 500
|
||||
|
||||
@self.app.route('/test-socketio')
|
||||
def test_socketio():
|
||||
|
@ -100,7 +100,7 @@ function createWindow() {
|
||||
contextIsolation: true,
|
||||
preload: path.join(__dirname, 'preload.js')
|
||||
},
|
||||
icon: path.join(__dirname, '../../assets/icon.ico'),
|
||||
icon: path.join(__dirname, '../public/logo.png'),
|
||||
show: false,
|
||||
fullscreen: false,
|
||||
frame: true,
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "body-balance-renderer",
|
||||
"version": "1.0.0",
|
||||
"description": "平衡体态检测系统前端界面",
|
||||
"name": "body-balance-system",
|
||||
"version": "1.5.0",
|
||||
"description": "平衡体态检测系统",
|
||||
"main": "main/main.js",
|
||||
"scripts": {
|
||||
"dev": "concurrently \"npm run dev:renderer\" \"wait-on http://localhost:3000 && npm run dev:electron\"",
|
||||
@ -9,7 +9,7 @@
|
||||
"dev:electron": "electron .",
|
||||
"build": "npm run build:renderer && npm run build:electron",
|
||||
"build:renderer": "vite build",
|
||||
"build:electron": "electron-builder",
|
||||
"build:electron": "electron-builder --config ./build/electron-builder.install.json",
|
||||
"pack": "electron-packager . electron-browser --platform=win32 --arch=x64 --out=dist --overwrite",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
@ -34,40 +34,5 @@
|
||||
"electron-packager": "^17.1.2",
|
||||
"vite": "^4.4.9",
|
||||
"wait-on": "^7.0.1"
|
||||
},
|
||||
"build": {
|
||||
"appId": "com.bodybalance.evaluation",
|
||||
"productName": "平衡体态检测系统",
|
||||
"electronVersion": "27.3.11",
|
||||
"electronDownload": {
|
||||
"mirror": "https://npmmirror.com/mirrors/electron/"
|
||||
},
|
||||
"directories": {
|
||||
"output": "dist-electron"
|
||||
},
|
||||
"files": [
|
||||
"dist/**/*",
|
||||
"main/**/*",
|
||||
"package.json"
|
||||
],
|
||||
"extraFiles": [
|
||||
{
|
||||
"from": "../../../backend/dist",
|
||||
"to": "resources/backend",
|
||||
"filter": [
|
||||
"**/*"
|
||||
]
|
||||
}
|
||||
],
|
||||
"win": {
|
||||
"target": "nsis",
|
||||
"icon": "../../../install/assets/icon.ico"
|
||||
},
|
||||
"nsis": {
|
||||
"oneClick": false,
|
||||
"allowToChangeInstallationDirectory": true,
|
||||
"createDesktopShortcut": true,
|
||||
"createStartMenuShortcut": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 8.1 KiB |
@ -2063,7 +2063,7 @@ const startRecord = async () => { // 开始录屏
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
patient_id: patientInfo.value.sessionId,
|
||||
patient_id: patientId.value,
|
||||
// 可以添加其他录屏参数
|
||||
creator_id: creatorId.value,
|
||||
screen_location:[Math.round(screen_location.x), Math.round(screen_location.y) + titile_height, Math.round(screen_location.width), Math.round(screen_location.height)],
|
||||
|
Loading…
Reference in New Issue
Block a user