修改了配置及相关bug

This commit is contained in:
root 2026-01-10 22:28:56 +08:00
parent 93e45aec19
commit 9f2ecf02c4
11 changed files with 120 additions and 467 deletions

View File

@ -1,15 +0,0 @@
{
"product": "BodyBalanceEvaluation",
"version": "1.5.0",
"machine_id": "W10-F5AC32003C0A160B",
"platform": "Windows",
"request_time": "2026-01-09T02:40:10.695573+00:00",
"hardware_info": {
"system": "Windows",
"machine": "AMD64",
"processor": "Intel64 Family 6 Model 165 Stepping 3, GenuineIntel",
"node": "PS2020LTDYIFOO"
},
"company_name": "北京天宏博科技有限公司",
"contact_info": "thb@163.com"
}

View File

@ -19,7 +19,7 @@ max_backups = 7
path = D:/BodyCheck/file/
[CAMERA1]
enabled = True
enable = True
device_index = 0
width = 1280
height = 720
@ -29,8 +29,8 @@ fourcc = MJPG
backend = directshow
[CAMERA2]
enabled = False
device_index = 3
enable = True
device_index = 1
width = 1280
height = 720
fps = 30
@ -39,7 +39,7 @@ fourcc = MJPG
backend = directshow
[FEMTOBOLT]
enabled = False
enable = True
algorithm_type = plt
color_resolution = 1080P
depth_mode = NFOV_2X2BINNED
@ -50,19 +50,19 @@ fps = 15
synchronized_images_only = False
[DEVICES]
imu_enabled = False
imu_enable = False
imu_device_type = ble
imu_port = COM9
imu_mac_address = ef:3c:1a:0a:fe:02
imu_baudrate = 9600
pressure_enabled = False
pressure_enable = False
pressure_device_type = real
pressure_use_mock = False
pressure_port = COM5
pressure_baudrate = 115200
[REMOTE]
enable = False
enable = True
port = COM6
baudrate = 115200
timeout = 0.1
@ -97,20 +97,12 @@ grace_days = 7
dev_mode = False
[SCREEN_RECORDING]
# 录屏策略ffmpeg外部进程录制或 threaded内部线程录制
strategy = ffmpeg
# ffmpeg可执行文件绝对路径Windows示例D:/BodyCheck/ffmpeg/bin/ffmpeg.exe
ffmpeg_path = D:/BodyCheck/ffmpeg/bin/ffmpeg.exe
# 编码器libx264CPU或 h264_nvencGPUCPU占用更低需显卡支持
ffmpeg_codec = libx264
# 编码预设ultrafastCPU更低、文件更大NVENC用 p1/p2 更快
ffmpeg_preset = ultrafast
# 编码线程数限制CPU占用按机器性能调整
ffmpeg_threads = 2
# B帧数量0可降低编码复杂度与CPU占用
ffmpeg_bframes = 0
# 关键帧间隔GOP单位数值越大CPU越低seek精度下降
ffmpeg_gop = 50
# 是否录制鼠标0关闭1开启
ffmpeg_draw_mouse = 0

View File

@ -619,90 +619,7 @@ class DatabaseManager:
except Exception as e:
conn.rollback()
logger.error(f'更新会话状态失败: {e}')
return False
def update_session_endcheck(self, session_id: str, diagnosis_info: Optional[str] = None,
treatment_info: Optional[str] = None,
suggestion_info: Optional[str] = None) -> bool:
"""结束检测:根据 start_time 与当前时间计算持续时长并写入结束信息
行为
- 计算并保存 `duration`
- 保存 `end_time` 为当前中国时区时间
- 可选更新 `diagnosis_info``treatment_info``suggestion_info`
- 设置 `status = 'checked'`
- 同步更新患者 `updated_at`
"""
conn = self.get_connection()
cursor = conn.cursor()
try:
# 读取会话的开始时间与患者ID
cursor.execute('SELECT start_time, patient_id FROM detection_sessions WHERE id = ?', (session_id,))
row = cursor.fetchone()
if not row:
logger.error(f'会话不存在: {session_id}')
return False
start_time_str, patient_id = row
if not start_time_str:
logger.error(f'会话缺少开始时间: {session_id}')
return False
# 计算持续时间(秒)
now_str = self.get_china_time()
try:
start_dt = datetime.strptime(start_time_str, '%Y-%m-%d %H:%M:%S')
now_dt = datetime.strptime(now_str, '%Y-%m-%d %H:%M:%S')
duration_seconds = max(0, int((now_dt - start_dt).total_seconds()))
except Exception as e:
logger.error(f'解析时间失败: start_time={start_time_str}, now={now_str}, error={e}')
return False
# 构造更新语句
update_fields = [
'duration = ?',
'end_time = ?'
]
update_values = [duration_seconds, now_str]
if diagnosis_info is not None:
update_fields.append('diagnosis_info = ?')
update_values.append(diagnosis_info)
if suggestion_info is not None:
update_fields.append('suggestion_info = ?')
update_values.append(suggestion_info)
status='checked' #默认状态为checked已完成检查
if treatment_info is not None: #处理信息不为空时状态设为completed已诊断处理
update_fields.append('treatment_info = ?')
update_values.append(treatment_info)
status='completed'
update_fields.append('status = ?')
update_values.append(status)
# 添加会话ID到参数列表
update_values.append(session_id)
sql = f'''UPDATE detection_sessions SET {', '.join(update_fields)} WHERE id = ?'''
cursor.execute(sql, update_values)
# 同步更新患者表的updated_at时间
cursor.execute('''UPDATE patients SET updated_at = ? WHERE id = ?''', (now_str, patient_id))
self._sync_patient_medical_history_from_session(cursor, session_id)
conn.commit()
logger.info(f'结束检测并更新会话: {session_id}, duration={duration_seconds}s, status={status}')
return True
except Exception as e:
conn.rollback()
logger.error(f'结束检测更新失败: {e}')
return False
def update_session_all_info(self, session_id: str, diagnosis_info: str = None, treatment_info: str = None, suggestion_info: str = None):
"""同时更新会话的诊断信息、处理信息、建议信息和状态"""
@ -714,18 +631,21 @@ class DatabaseManager:
update_fields = []
update_values = []
if diagnosis_info is not None:
if diagnosis_info is not None and diagnosis_info.strip() != '':
update_fields.append('diagnosis_info = ?')
update_values.append(diagnosis_info)
status='checked' #默认状态为checked已完成检查
if treatment_info is not None:
if treatment_info is not None and treatment_info.strip() != '':
update_fields.append('treatment_info = ?')
update_values.append(treatment_info)
status='completed' #处理信息不为空时状态设为completed已诊断处理
if suggestion_info is not None:
if suggestion_info is not None and suggestion_info.strip() != '':
update_fields.append('suggestion_info = ?')
update_values.append(suggestion_info)
update_values.append(suggestion_info)
update_fields.append('status = ?')
update_values.append(status)
if not update_fields:
logger.warning(f'没有提供要更新的信息: {session_id}')
@ -754,11 +674,11 @@ class DatabaseManager:
updated_info.append(f'状态({status})')
logger.info(f'批量更新会话信息成功: {session_id}, 更新字段: {", ".join(updated_info)}')
return True
except Exception as e:
conn.rollback()
logger.error(f'批量更新会话信息失败: {e}')
raise
return False
def _sync_patient_medical_history_from_session(self, cursor, session_id: str) -> None:
"""根据会话信息同步患者 medical_history 的 doctor/status/lastcheck_time 字段"""
@ -806,7 +726,8 @@ class DatabaseManager:
try:
offset = (page - 1) * size
cursor.execute('''
SELECT s.id, s.status, s.start_time, u.name as creator_name,s.detection_report as detection_report,s.data_ids as data_ids
SELECT s.id, s.status, s.start_time, u.name as creator_name,s.detection_report as detection_report,s.data_ids as data_ids,
(SELECT COUNT(*) FROM detection_data WHERE session_id = s.id) as data_count
FROM detection_sessions s
LEFT JOIN users u ON s.creator_id = u.id
WHERE s.patient_id = ? and s.status in ('checked','completed','reported')
@ -827,7 +748,8 @@ class DatabaseManager:
'start_time': r[2],
'creator_name': r[3],
'detection_report': r[4],
'data_ids': r[5]
'data_ids': r[5],
'data_count': r[6]
}
sessions.append({
'id': item.get('id'),
@ -835,7 +757,8 @@ class DatabaseManager:
'start_time': item.get('start_time'),
'creator_name': item.get('creator_name'),
'detection_report': item.get('detection_report'),
'data_ids': item.get('data_ids')
'data_ids': item.get('data_ids'),
'data_count': item.get('data_count', 0)
})
return sessions
@ -1118,15 +1041,22 @@ class DatabaseManager:
return False
def has_session_detection_data(self, session_id: str) -> bool:
"""检查指定会话是否存在检测数据,用于判断单次检测是否有效"""
"""检查指定会话是否存在检测数据或视频数据,用于判断单次检测是否有效"""
conn = self.get_connection()
cursor = conn.cursor()
try:
# 检查检测数据
cursor.execute('SELECT COUNT(1) FROM detection_data WHERE session_id = ?', (session_id,))
row = cursor.fetchone()
count = row[0] if row else 0
exists = count > 0
logger.info(f'会话 {session_id} 检测数据存在: {exists} (count={count})')
data_count = row[0] if row else 0
# 检查视频数据
cursor.execute('SELECT COUNT(1) FROM detection_video WHERE session_id = ?', (session_id,))
video_row = cursor.fetchone()
video_count = video_row[0] if video_row else 0
exists = (data_count > 0) or (video_count > 0)
logger.info(f'会话 {session_id} 数据检查: data_count={data_count}, video_count={video_count}, exists={exists}')
return exists
except Exception as e:
logger.error(f'检查会话检测数据存在失败: {e}')

View File

@ -159,29 +159,29 @@ class DeviceCoordinator:
futures = []
# FemtoBolt深度相机
if self.device_configs.get('femtobolt', {}).get('enabled', False):
if self.device_configs.get('femtobolt', {}).get('enable', False):
future = self.executor.submit(self._init_femtobolt)
futures.append(('femtobolt', future))
# 普通相机初始化两个实例camera1 与 camera2
# camera1 使用 [CAMERA1] 配置camera2 使用 [CAMERA2](若不存在则回退为 device_index+1
if self.device_configs.get('camera1', {}).get('enabled', False):
if self.device_configs.get('camera1', {}).get('enable', False):
futures.append(('camera1', self.executor.submit(self._init_camera_by_name, 'camera1', 'CAMERA1')))
if self.device_configs.get('camera2', {}).get('enabled', False):
if self.device_configs.get('camera2', {}).get('enable', False):
futures.append(('camera2', self.executor.submit(self._init_camera_by_name, 'camera2', 'CAMERA2')))
# IMU传感器
if self.device_configs.get('imu', {}).get('enabled', False):
if self.device_configs.get('imu', {}).get('enable', False):
future = self.executor.submit(self._init_imu)
futures.append(('imu', future))
# 压力传感器
if self.device_configs.get('pressure', {}).get('enabled', False):
if self.device_configs.get('pressure', {}).get('enable', False):
future = self.executor.submit(self._init_pressure)
futures.append(('pressure', future))
# 遥控器
if self.device_configs.get('remote', {}).get('enabled', False):
if self.device_configs.get('remote', {}).get('enable', False):
future = self.executor.submit(self._init_remote)
futures.append(('remote', future))
@ -244,8 +244,8 @@ class DeviceCoordinator:
return parser.getboolean(sec, key)
except Exception:
return fallback
enabled = get_bool(section, 'enabled', True)
if not enabled:
enable = get_bool(section, 'enable', True)
if not enable:
self.logger.info(f"{device_name} 未启用,跳过初始化")
return False
# 填充覆盖项
@ -682,8 +682,8 @@ class DeviceCoordinator:
return parser.getboolean(sec, key)
except Exception:
return fallback
enabled = get_bool(section, 'enabled', True)
if not enabled:
enable = get_bool(section, 'enable', True)
if not enable:
raise Exception(f"{device_name} 未启用")
idx2 = get_int(section, 'device_index', None)
if idx2 is not None:

View File

@ -39,7 +39,7 @@ class RemoteControlManager(BaseDevice):
timeout = float(self.config_manager.get_config_value('REMOTE', 'timeout', fallback='0.1'))
instance_config: Dict[str, Any] = {
'enabled': True,
'enable': True,
'port': port,
'baudrate': baudrate,
'timeout': timeout,

View File

@ -1280,7 +1280,7 @@ class RecordingManager:
'body_pose': None,
'body_image': None,
'foot_data': detection_data.get('foot_data'),
'foot_data_image': None,
'foot_data_image': detection_data.get('foot_data_image'),
'foot1_image': None,
'foot2_image': None,
'screen_image': None,
@ -1292,8 +1292,7 @@ class RecordingManager:
image_fields = [
('body_image', 'body'),
('foot1_image', 'foot1'),
('foot2_image', 'foot2'),
('foot_data_image', 'foot_data')
('foot2_image', 'foot2')
]
for field, prefix in image_fields:
@ -1322,11 +1321,15 @@ class RecordingManager:
except Exception as e:
self.logger.error(f'保存{field}失败: {e}')
# 屏幕截图
screen_image = self._capture_screen_image(data_dir, data.get('screen_location'), timestamp=timestamp)
# 完整屏幕截图--根据screen_location 进行截图
screen_image = self._capture_screen_image(data_dir, data.get('screen_location'),'screen', timestamp=timestamp)
if screen_image:
data['screen_image'] = str(os.path.join( patient_id, session_id, f"image_{timestamp}", screen_image))
# 足部压力屏幕截图——根据foot_data_image 进行截图
foot_data_image = self._capture_screen_image(data_dir, data.get('foot_data_image'),'foot_data', timestamp=timestamp)
if foot_data_image:
data['foot_data_image'] = str(os.path.join( patient_id, session_id, f"image_{timestamp}", foot_data_image))
self.logger.debug(f'数据保存完成: {session_id}, 时间戳: {timestamp}')
except Exception as e:
@ -1336,7 +1339,7 @@ class RecordingManager:
def _capture_screen_image(self, data_dir, screen_location, timestamp) -> Optional[str]:
def _capture_screen_image(self, data_dir, screen_location,type, timestamp) -> Optional[str]:
"""
采集屏幕截图根据screen_region 进行截图
@ -1358,7 +1361,7 @@ class RecordingManager:
# 保存截图
from pathlib import Path
screen_filename = f'screen_{timestamp}.jpg'
screen_filename = f'{type}_{timestamp}.jpg'
image_path = Path(data_dir) / screen_filename
screenshot.save(str(image_path), quality=95, optimize=True)

View File

@ -143,7 +143,7 @@ class ConfigManager:
获取设备配置
Args:
device_name: 设备名称 (camera1, camera2, femtobolt, imu, pressure)
device_name: 设备名称 (camera1, camera2, femtobolt, imu, pressure, remote)
Returns:
Dict[str, Any]: 设备配置字典
@ -180,7 +180,7 @@ class ConfigManager:
Dict[str, Any]: 相机配置
"""
return {
'enabled': self.config.getboolean('CAMERA1', 'enabled', fallback=True),
'enable': self.config.getboolean('CAMERA1', 'enable', fallback=False),
'device_index': self.config.getint('CAMERA1', 'device_index', fallback=0),
'width': self.config.getint('CAMERA1', 'width', fallback=1280),
'height': self.config.getint('CAMERA1', 'height', fallback=720),
@ -197,7 +197,7 @@ class ConfigManager:
Dict[str, Any]:
"""
return {
'enabled': self.config.getboolean('CAMERA2', 'enabled', fallback=True),
'enable': self.config.getboolean('CAMERA2', 'enable', fallback=False),
'device_index': self.config.getint('CAMERA2', 'device_index', fallback=0),
'width': self.config.getint('CAMERA2', 'width', fallback=1280),
'height': self.config.getint('CAMERA2', 'height', fallback=720),
@ -214,7 +214,7 @@ class ConfigManager:
Dict[str, Any]: FemtoBolt配置
"""
return {
'enabled': self.config.getboolean('FEMTOBOLT', 'enabled', fallback=True),
'enable': self.config.getboolean('FEMTOBOLT', 'enable', fallback=False),
'algorithm_type': self.config.get('FEMTOBOLT', 'algorithm_type', fallback='opencv'),
'color_resolution': self.config.get('FEMTOBOLT', 'color_resolution', fallback='1080P'),
'depth_mode': self.config.get('FEMTOBOLT', 'depth_mode', fallback='NFOV_UNBINNED'),
@ -232,7 +232,7 @@ class ConfigManager:
Dict[str, Any]: IMU配置
"""
return {
'enabled': self.config.getboolean('DEVICES', 'imu_enabled', fallback=True),
'enable': self.config.getboolean('DEVICES', 'imu_enable', fallback=False),
'device_type': self.config.get('DEVICES', 'imu_device_type', fallback='mock'),
'port': self.config.get('DEVICES', 'imu_port', fallback='COM7'),
'baudrate': self.config.getint('DEVICES', 'imu_baudrate', fallback=9600),
@ -249,7 +249,7 @@ class ConfigManager:
Dict[str, Any]: 压力传感器配置
"""
return {
'enabled': self.config.getboolean('DEVICES', 'pressure_enabled', fallback=True),
'enable': self.config.getboolean('DEVICES', 'pressure_enable', fallback=False),
'device_type': self.config.get('DEVICES', 'pressure_device_type', fallback='mock'),
'port': self.config.get('DEVICES', 'pressure_port', fallback='COM8'),
'baudrate': self.config.getint('DEVICES', 'pressure_baudrate', fallback=115200),
@ -265,7 +265,7 @@ class ConfigManager:
Dict[str, Any]: 远程控制配置
"""
return {
'enabled': self.config.getboolean('REMOTE', 'enable', fallback=True),
'enable': self.config.getboolean('REMOTE', 'enable', fallback=False),
'port': self.config.get('REMOTE', 'port', fallback='COM6'),
'baudrate': self.config.getint('REMOTE', 'baudrate', fallback=115200),
'timeout': self.config.getfloat('REMOTE', 'timeout', fallback=0.1),
@ -376,7 +376,7 @@ class ConfigManager:
warnings = []
# 验证必需的配置段
required_sections = ['DEVICES', 'CAMERA1', 'CAMERA2', 'FEMTOBOLT', 'SYSTEM']
required_sections = ['DEVICES', 'CAMERA1', 'CAMERA2', 'FEMTOBOLT', 'REMOTE']
for section in required_sections:
if not self.config.has_section(section):
errors.append(f"缺少必需的配置段: {section}")
@ -395,228 +395,7 @@ class ConfigManager:
'valid': len(errors) == 0
}
# HTTP接口设备参数设置方法
def set_imu_config(self, config_data: Dict[str, Any]) -> Dict[str, Any]:
"""
设置IMU设备配置
Args:
config_data: IMU配置数据
{
'device_type': 'real' | 'mock' | 'ble',
'port': 'COM6',
'baudrate': 9600,
'mac_address': 'ef:3c:1a:0a:fe:02'
}
Returns:
Dict[str, Any]: 设置结果
"""
try:
# 验证必需参数
if 'device_type' in config_data:
self.set_config_value('DEVICES', 'imu_device_type', config_data['device_type'])
if 'port' in config_data:
self.set_config_value('DEVICES', 'imu_port', config_data['port'])
if 'baudrate' in config_data:
self.set_config_value('DEVICES', 'imu_baudrate', str(config_data['baudrate']))
if 'mac_address' in config_data:
self.set_config_value('DEVICES', 'imu_mac_address', config_data['mac_address'])
# 保存配置
self.save_config()
self.logger.info(f"IMU配置已更新: {config_data}")
return {
'success': True,
'message': 'IMU配置更新成功',
'config': self.get_device_config('imu')
}
except Exception as e:
self.logger.error(f"设置IMU配置失败: {e}")
return {
'success': False,
'message': f'设置IMU配置失败: {str(e)}'
}
def set_pressure_config(self, config_data: Dict[str, Any]) -> Dict[str, Any]:
"""
设置压力板设备配置
Args:
config_data: 压力板配置数据
{
'device_type': 'real' | 'mock',
'use_mock': False,
'port': 'COM5',
'baudrate': 115200
}
Returns:
Dict[str, Any]: 设置结果
"""
try:
# 验证必需参数
if 'device_type' in config_data:
self.set_config_value('DEVICES', 'pressure_device_type', config_data['device_type'])
if 'use_mock' in config_data:
self.set_config_value('DEVICES', 'pressure_use_mock', str(config_data['use_mock']))
if 'port' in config_data:
self.set_config_value('DEVICES', 'pressure_port', config_data['port'])
if 'baudrate' in config_data:
self.set_config_value('DEVICES', 'pressure_baudrate', str(config_data['baudrate']))
# 保存配置
self.save_config()
self.logger.info(f"压力板配置已更新: {config_data}")
return {
'success': True,
'message': '压力板配置更新成功',
'config': self.get_device_config('pressure')
}
except Exception as e:
self.logger.error(f"设置压力板配置失败: {e}")
return {
'success': False,
'message': f'设置压力板配置失败: {str(e)}'
}
def set_camera1_config(self, config_data: Dict[str, Any]) -> Dict[str, Any]:
"""
设置相机设备配置
Args:
config_data: 相机配置数据
{
'device_index': 1,
'width': 1280,
'height': 720,
'fps': 30
}
Returns:
Dict[str, Any]: 设置结果
"""
try:
# 验证必需参数
if 'device_index' in config_data:
self.set_config_value('CAMERA1', 'device_index', str(config_data['device_index']))
if 'width' in config_data:
self.set_config_value('CAMERA1', 'width', str(config_data['width']))
if 'height' in config_data:
self.set_config_value('CAMERA1', 'height', str(config_data['height']))
if 'fps' in config_data:
self.set_config_value('CAMERA1', 'fps', str(config_data['fps']))
if 'backend' in config_data:
self.set_config_value('CAMERA1', 'backend', str(config_data['backend']))
# 保存配置
self.save_config()
self.logger.info(f"相机配置已更新: {config_data}")
return {
'success': True,
'message': '相机配置更新成功',
'config': self.get_device_config('camera1')
}
except Exception as e:
self.logger.error(f"设置相机配置失败: {e}")
return {
'success': False,
'message': f'设置相机配置失败: {str(e)}'
}
def set_camera2_config(self, config_data: Dict[str, Any]) -> Dict[str, Any]:
"""
设置相机设备配置
Args:
config_data: 相机配置数据
{
'device_index': 1,
'width': 1280,
'height': 720,
'fps': 30
}
Returns:
Dict[str, Any]: 设置结果
"""
try:
# 验证必需参数
if 'device_index' in config_data:
self.set_config_value('CAMERA2', 'device_index', str(config_data['device_index']))
if 'width' in config_data:
self.set_config_value('CAMERA2', 'width', str(config_data['width']))
if 'height' in config_data:
self.set_config_value('CAMERA2', 'height', str(config_data['height']))
if 'fps' in config_data:
self.set_config_value('CAMERA2', 'fps', str(config_data['fps']))
if 'backend' in config_data:
self.set_config_value('CAMERA2', 'backend', str(config_data['backend']))
# 保存配置
self.save_config()
self.logger.info(f"相机配置已更新: {config_data}")
return {
'success': True,
'message': '相机配置更新成功',
'config': self.get_device_config('camera2')
}
except Exception as e:
self.logger.error(f"设置相机配置失败: {e}")
return {
'success': False,
'message': f'设置相机配置失败: {str(e)}'
}
def set_femtobolt_config(self, config_data: Dict[str, Any]) -> Dict[str, Any]:
"""
设置FemtoBolt设备配置
Args:
config_data: FemtoBolt配置数据
{
'color_resolution': '1080P',
'depth_mode': 'NFOV_UNBINNED',
'fps': 30,
'depth_range_min': 1200,
'depth_range_max': 1500
}
Returns:
Dict[str, Any]: 设置结果
"""
try:
# 验证必需参数
if 'algorithm_type' in config_data:
self.set_config_value('FEMTOBOLT', 'algorithm_type', config_data['algorithm_type'])
if 'color_resolution' in config_data:
self.set_config_value('FEMTOBOLT', 'color_resolution', config_data['color_resolution'])
if 'depth_mode' in config_data:
self.set_config_value('FEMTOBOLT', 'depth_mode', config_data['depth_mode'])
if 'camera_fps' in config_data:
self.set_config_value('FEMTOBOLT', 'camera_fps', str(config_data['camera_fps']))
if 'depth_range_min' in config_data:
self.set_config_value('FEMTOBOLT', 'depth_range_min', str(config_data['depth_range_min']))
if 'depth_range_max' in config_data:
self.set_config_value('FEMTOBOLT', 'depth_range_max', str(config_data['depth_range_max']))
# 保存配置
self.save_config()
self.logger.info(f"FemtoBolt配置已更新: {config_data}")
return {
'success': True,
'message': 'FemtoBolt配置更新成功',
'config': self.get_device_config('femtobolt')
}
except Exception as e:
self.logger.error(f"设置FemtoBolt配置失败: {e}")
return {
'success': False,
'message': f'设置FemtoBolt配置失败: {str(e)}'
}
def get_all_device_configs(self) -> Dict[str, Any]:
"""
@ -652,6 +431,8 @@ class ConfigManager:
if 'imu' in configs:
try:
config_data = configs['imu']
if 'enable' in config_data:
self.set_config_value('DEVICES', 'imu_enable', str(config_data['enable']))
if 'device_type' in config_data:
self.set_config_value('DEVICES', 'imu_device_type', config_data['device_type'])
if 'use_mock' in config_data:
@ -677,6 +458,8 @@ class ConfigManager:
if 'pressure' in configs:
try:
config_data = configs['pressure']
if 'enable' in config_data:
self.set_config_value('DEVICES', 'pressure_enable', str(config_data['enable']))
if 'device_type' in config_data:
self.set_config_value('DEVICES', 'pressure_device_type', config_data['device_type'])
if 'use_mock' in config_data:
@ -702,6 +485,8 @@ class ConfigManager:
if 'camera1' in configs:
try:
config_data = configs['camera1']
if 'enable' in config_data:
self.set_config_value('CAMERA1', 'enable', str(config_data['enable']))
if 'device_index' in config_data:
self.set_config_value('CAMERA1', 'device_index', str(config_data['device_index']))
if 'width' in config_data:
@ -734,6 +519,8 @@ class ConfigManager:
if 'camera2' in configs:
try:
config_data = configs['camera2']
if 'enable' in config_data:
self.set_config_value('CAMERA2', 'enable', str(config_data['enable']))
if 'device_index' in config_data:
self.set_config_value('CAMERA2', 'device_index', str(config_data['device_index']))
if 'width' in config_data:
@ -767,6 +554,8 @@ class ConfigManager:
if 'femtobolt' in configs:
try:
config_data = configs['femtobolt']
if 'enable' in config_data:
self.set_config_value('FEMTOBOLT', 'enable', str(config_data['enable']))
if 'algorithm_type' in config_data:
self.set_config_value('FEMTOBOLT', 'algorithm_type', config_data['algorithm_type'])
if 'color_resolution' in config_data:
@ -802,8 +591,8 @@ class ConfigManager:
if 'remote' in configs:
try:
config_data = configs['remote']
if 'enabled' in config_data:
self.set_config_value('REMOTE', 'enable', str(config_data['enabled']))
if 'enable' in config_data:
self.set_config_value('REMOTE', 'enable', str(config_data['enable']))
if 'port' in config_data:
self.set_config_value('REMOTE', 'port', config_data['port'])
if 'baudrate' in config_data:

View File

@ -1211,7 +1211,7 @@ class AppServer:
}), 400
# 验证数据格式
supported_devices = ['camera1','camera2', 'femtobolt','imu','pressure']
supported_devices = ['camera1','camera2', 'femtobolt','imu','pressure','remote']
for device_name in data.keys():
if device_name not in supported_devices:
return jsonify({
@ -1386,7 +1386,7 @@ class AppServer:
diagnosis_info = data.get('diagnosis_info')
treatment_info = data.get('treatment_info')
suggestion_info = data.get('suggestion_info')
success = self.db_manager.update_session_endcheck(
success = self.db_manager.update_session_all_info(
session_id,
diagnosis_info=diagnosis_info,
treatment_info=treatment_info,
@ -1534,13 +1534,6 @@ class AppServer:
suggestion_info = data.get('suggestion_info')
# 验证至少提供一个要更新的字段
if not any([diagnosis_info, treatment_info, suggestion_info]):
return jsonify({
'success': False,
'error': '至少需要提供一个要更新的字段diagnosis_info, treatment_info, suggestion_info'
}), 400
# 调用数据库管理器的批量更新方法
self.db_manager.update_session_all_info(
session_id=session_id,
@ -1556,9 +1549,7 @@ class AppServer:
if treatment_info is not None:
updated_fields.append('处理信息')
if suggestion_info is not None:
updated_fields.append('建议信息')
if status is not None:
updated_fields.append(f'状态({status})')
updated_fields.append('建议信息')
self.logger.info(f'会话信息保存成功: {session_id}, 更新字段: {", ".join(updated_fields)}')

View File

@ -10,7 +10,7 @@
- [REMOTE] port缺省 COM6
- [REMOTE] baudrate缺省 115200
- [REMOTE] timeout缺省 0.1 秒
- [DEVICES] remote_enabled(是否启用),缺省 true
- [DEVICES] remote_enable是否启用缺省 true
- 串口参数115200 bps8 数据位1 停止位无校验8N1
## 报文格式

View File

@ -17,15 +17,14 @@
<el-button type="primary" class="endbutton" @click="endClick">结束检测</el-button>
</div>
<div class="displayleft">
<div class="icon-box" @click="isPhotoAlbum = true" title="图片相册">
<img src="@/assets/detection/photoalbum.svg" alt="">
</div>
<div class="icon-box" title="截图" @click="saveDetectionData">
<img src="@/assets/detection/screenshot.png" alt="">
</div>
<div class="icon-box" @click="isPhotoAlbum = true" title="相册">
<img src="@/assets/detection/photoalbum.svg" alt="">
</div>
<div v-if="!isStartVideo" class="icon-box" @click="startVideoClick" title="开始录像">
<img src="@/assets/detection/startvideo.png" alt="">
</div>
@ -712,8 +711,7 @@
<div class="pop-up-tip-text" v-if="!isVideoOperation">本次检测未截图或录像操作不予存档记录</div>
<div class="pop-up-tip-text" v-if="isVideoOperation">本次检测未截图操作存档记录不可生成报告</div>
<div class="tipconfirmbutton-box">
<el-button v-if="!isVideoOperation" type="primary" class="tipconfirmbutton" @click="closeTipClick">确定</el-button>
<el-button v-if="isVideoOperation" type="primary" class="tipconfirmbutton" @click="isDiagnosticMessage = true">确定</el-button>
<el-button type="primary" class="tipconfirmbutton" @click="closeTipClick">确定</el-button>
</div>
</div>
</div>
@ -746,7 +744,7 @@
<div style="margin: 0 10px;"></div>
<el-input v-model="cameraForm.femtobolt.depth_range_max" placeholder="请输入最大值"
style="width: 200px;"/>
<el-checkbox v-model="cameraForm.femtobolt.enabled" label="有效" size="large" style="width: 60px;margin-left:10px ;" />
<el-checkbox v-model="cameraForm.femtobolt.enable" label="有效" size="large" style="width: 60px;margin-left:10px ;" />
</div>
<div class="pop-up-camera-display" style="padding-top: 30px;padding-bottom: 10px;">
@ -757,27 +755,27 @@
<div class="pop-up-camera-name">相机上</div>
<el-radio-group v-model="cameraForm.camera1.device_index">
<div style="display: flex;justify-content: space-between;width: 435px;">
<el-radio :value="0" border>0</el-radio>
<el-radio :value="1" border>1</el-radio>
<el-radio :value="2" border>2</el-radio>
<el-radio :value="3" border>3</el-radio>
<el-radio :value="4" border>4</el-radio>
<el-radio :value="5" border>5</el-radio>
<el-radio :value="4" border>4</el-radio>
</div>
</el-radio-group>
<el-checkbox v-model="cameraForm.camera1.enabled" label="有效" size="large" style="width: 60px;margin-left:10px ;" />
<el-checkbox v-model="cameraForm.camera1.enable" label="有效" size="large" style="width: 60px;margin-left:10px ;" />
</div>
<div class="pop-up-camera-display" style="padding-top: 20px;">
<div class="pop-up-camera-name">相机下</div>
<el-radio-group v-model="cameraForm.camera2.device_index">
<div style="display: flex;justify-content: space-between;width: 435px;">
<el-radio :value="0" border>0</el-radio>
<el-radio :value="1" border>1</el-radio>
<el-radio :value="2" border>2</el-radio>
<el-radio :value="3" border>3</el-radio>
<el-radio :value="4" border>4</el-radio>
<el-radio :value="5" border>5</el-radio>
<el-radio :value="4" border>4</el-radio>
</div>
</el-radio-group>
<el-checkbox v-model="cameraForm.camera2.enabled" label="有效" size="large" style="width: 60px;margin-left:10px ;" />
<el-checkbox v-model="cameraForm.camera2.enable" label="有效" size="large" style="width: 60px;margin-left:10px ;" />
</div>
<div class="pop-up-camera-display" style="padding-top: 30px;padding-bottom: 00px;">
<div class="pop-up-camera-line"></div>
@ -788,7 +786,7 @@
<el-select v-model="cameraForm.remote.port" placeholder="请选择" style="width: 434px;">
<el-option v-for="item in remotePortData" :label="item" :value="item" />
</el-select>
<el-checkbox v-model="cameraForm.remote.enabled" label="有效" size="large" style="width: 60px;margin-left:10px ;" />
<el-checkbox v-model="cameraForm.remote.enable" label="有效" size="large" style="width: 60px;margin-left:10px ;" />
</div>
<div class="form-actions-display">
@ -1017,7 +1015,7 @@ const cameraForm = ref({ // 相机参数
},
remote:{
port: '', // IMU
enabled: false
enable: false
}
})
const calculatedAge = ref(null)
@ -1922,51 +1920,7 @@ function handlePressureData(data) {
}
async function handleDiagnosticInfo(status) {
try {
// ID
if (!patientInfo.value.sessionId) {
throw new Error('缺少会话Id')
}
// API
const response = await fetch(`${BACKEND_URL}/api/detection/${patientInfo.value.sessionId}/save-info`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
diagnosis_info: diagnosticForm.diagnosis_info,
treatment_info: diagnosticForm.treatment_info,
suggestion_info: diagnosticForm.suggestion_info,
status: status,
session_id: patientInfo.value.sessionId,
})
})
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`)
}
const result = await response.json()
if (result.success) {
//
ElMessage.success({
message: status + '诊断信息成功',
duration: 5000
})
} else {
throw new Error(result.message || '诊断信息失败')
}
} catch (error) {
ElMessage.error({
message: errorMessage,
duration: 5000
})
} finally {
}
}
//
async function saveDetectionData() {
@ -2216,10 +2170,6 @@ const loadPatientInfo = async () => {
//
const handleBeforeUnload = (event) => {
console.log('页面即将关闭,正在清理资源...')
//
stopDetection()
// WebSocket
disconnectWebSocket()
@ -2404,17 +2354,8 @@ onUnmounted(() => {
clearInterval(timerId.value)
timerId.value = null
console.log('✅ 定时器已清理')
}
// //
// if (isRecording.value === true) {
// stopRecord()
// console.log(' ')
// }
// //
// stopDetection()
}
// WebSocket
disconnectWebSocket()
@ -2576,6 +2517,8 @@ function refreshClick(type) {
devicesSocket.emit('restart_device', { device_type: 'imu' })
}else if(type == 'pressure'){
devicesSocket.emit('restart_device', { device_type: 'pressure' })
}else if(type == 'remote'){
devicesSocket.emit('restart_device', { device_type: 'remote' })
}
} else {
console.warn('⚠️ Socket服务未连接无法重启设备')

View File

@ -103,7 +103,27 @@
<el-table-column prop="report" label="报告" width="60" align="center">
<template #default="scope">
<div
v-if="scope.row.detection_report == null"
v-if="scope.row.data_count == 0"
style="width:28px;
font-family: 'Noto Sans SC';
font-weight: 400;
font-style: normal;
font-size: 14px;
color: rgba(255, 255, 255, 0.5);
line-height: 18px;
cursor: not-allowed;"> 数据</div>
<div
v-if="scope.row.data_count > 0 && scope.row.status == 'checked'"
style="width:28px;
font-family: 'Noto Sans SC';
font-weight: 400;
font-style: normal;
font-size: 14px;
color: rgba(255, 255, 255, 0.5);
line-height: 18px;
cursor: not-allowed;"> 诊断</div>
<div
v-else-if="scope.row.status == 'completed'"
style="width:28px;
font-family: 'Noto Sans SC';
font-weight: 400;
@ -113,7 +133,7 @@
line-height: 18px;
cursor: pointer;"
@click.stop="generateReport(scope.row,scope.$index)">生成报告</div>
<div class="patientprofile-file-box" v-if="scope.row.detection_report != null">
<div class="patientprofile-file-box" v-else-if="scope.row.status == 'reported'">
<img src="@/assets/new/file.png" alt="" @click.stop="fileClick(scope.row)">
<img src="@/assets/new/del.png" class="patientprofile-del" alt=""
@click.stop="delPDFClick(scope.row)">