修正了页面重复关闭硬件资源,的问题。

This commit is contained in:
root 2025-09-27 12:14:19 +08:00
parent 26b6bc3e0a
commit 4ab2211cd8
12 changed files with 591955 additions and 199 deletions

591699
backend/Log/OrbbecSDK.log.1.txt Normal file

File diff suppressed because it is too large Load Diff

View File

@ -202,9 +202,8 @@ class BaseDevice(ABC):
# 只有状态真正改变时才触发回调 # 只有状态真正改变时才触发回调
if old_status != is_connected: if old_status != is_connected:
self._notify_status_change(is_connected) self._notify_status_change(is_connected)
def emit_data(self, event: str, data: Any, namespace: Optional[str] = None): def emit_data(self, event: str, data: Any, namespace: Optional[str] = None):
""" """
发送数据到前端 发送数据到前端

View File

@ -900,30 +900,118 @@ class CameraManager(BaseDevice):
try: try:
if not self.cap: if not self.cap:
# 如果相机实例不存在,尝试重新创建 # 如果相机实例不存在,尝试重新创建
self.logger.info("相机实例不存在,尝试重新创建-----------------")
return self._attempt_device_reconnection() return self._attempt_device_reconnection()
if not self.cap.isOpened(): if not self.cap.isOpened():
# 相机未打开,尝试重连 # 相机未打开,尝试重连
self.logger.info("相机未打开,尝试重新连接-----------------")
return self._attempt_device_reconnection() return self._attempt_device_reconnection()
# 尝试读取一帧来验证连接 # 多层次验证相机连接状态
try: try:
ret, frame = self.cap.read() # 第一步使用grab()方法快速清除所有缓存帧
if ret and frame is not None: self.logger.debug("快速清除相机缓存帧...")
# 立即释放帧内存 try:
del frame # grab()方法只获取帧但不解码,速度更快
# 连续grab多次以清空内部缓冲区
for _ in range(15): # 增加清除次数,确保缓存完全清空
if not self.cap.grab():
break # 如果grab失败说明没有更多缓存帧
except Exception as e:
self.logger.debug(f"清除缓存帧时出现异常: {e}")
# 第二步:严格的连续帧检测
failed_count = 0
total_frames = 15 # 增加检测帧数
consecutive_failures = 0 # 连续失败计数
for i in range(total_frames):
try:
ret, frame = self.cap.read()
if ret and frame is not None:
# 验证帧数据的有效性
if self._validate_frame_data(frame):
del frame
consecutive_failures = 0 # 重置连续失败计数
else:
failed_count += 1
consecutive_failures += 1
del frame
else:
failed_count += 1
consecutive_failures += 1
# 如果连续3帧失败立即判定为断开
if consecutive_failures >= 3:
self.logger.warning(f"相机连接检测失败:连续{consecutive_failures}帧失败")
return False
# 如果总失败帧数超过30%,判定为断开
if failed_count > total_frames * 0.3:
self.logger.warning(f"相机连接检测失败:{failed_count}/{i+1}帧读取失败超过30%阈值")
return False
except Exception as e:
failed_count += 1
consecutive_failures += 1
self.logger.debug(f"读取第{i+1}帧时异常: {e}")
# 连续异常也判定为断开
if consecutive_failures >= 3:
self.logger.warning(f"相机连接检测异常:连续{consecutive_failures}帧异常")
return False
# 短暂延时,避免过快读取
time.sleep(0.005) # 减少延时提高检测速度
# 第三步:最终判断
success_rate = (total_frames - failed_count) / total_frames
if success_rate >= 0.7: # 成功率需要达到70%
self.logger.info(f"相机连接检测成功:{total_frames-failed_count}/{total_frames}帧读取成功,成功率{success_rate:.1%}")
return True return True
else: else:
# 读取失败,可能设备已断开 self.logger.warning(f"相机连接检测失败:成功率{success_rate:.1%}低于70%阈值")
return self._attempt_device_reconnection() return False
except Exception:
# 读取异常,尝试重连 except Exception as e:
return self._attempt_device_reconnection() self.logger.warning(f"相机连接检测过程中发生异常: {e}")
return False
except Exception as e: except Exception as e:
self.logger.debug(f"检查相机硬件连接时发生异常: {e}") self.logger.debug(f"检查相机硬件连接时发生异常: {e}")
return False return False
def _validate_frame_data(self, frame) -> bool:
"""
验证帧数据的有效性
Args:
frame: 要验证的帧数据
Returns:
bool: 帧数据是否有效
"""
try:
if frame is None:
return False
# 检查帧尺寸
if frame.shape[0] < 10 or frame.shape[1] < 10:
return False
# 检查帧数据是否全为零(可能是无效帧)
if np.all(frame == 0):
return False
# 检查帧数据的方差(全黑或全白帧可能是无效的)
if np.var(frame) < 1.0:
return False
return True
except Exception:
return False
def _attempt_device_reconnection(self) -> bool: def _attempt_device_reconnection(self) -> bool:
""" """
尝试重新连接相机设备 尝试重新连接相机设备
@ -1011,65 +1099,6 @@ class CameraManager(BaseDevice):
except Exception as e: except Exception as e:
self.logger.error(f"清理相机资源时发生错误: {e}") self.logger.error(f"清理相机资源时发生错误: {e}")
def _save_frame_to_cache(self, frame, frame_type='camera'):
"""保存帧到全局缓存"""
try:
with self.frame_cache_lock:
current_time = time.time()
# 清理过期帧
self._cleanup_expired_frames()
# 如果缓存已满,移除最旧的帧
if frame_type in self.global_frame_cache and len(self.global_frame_cache[frame_type]) >= self.max_cache_size:
oldest_key = min(self.global_frame_cache[frame_type].keys())
del self.global_frame_cache[frame_type][oldest_key]
# 初始化帧类型缓存
if frame_type not in self.global_frame_cache:
self.global_frame_cache[frame_type] = {}
# 保存帧(深拷贝避免引用问题)
frame_data = {
'frame': frame.copy(),
'timestamp': current_time,
'frame_id': len(self.global_frame_cache[frame_type])
}
self.global_frame_cache[frame_type][current_time] = frame_data
except Exception as e:
self.logger.error(f'保存帧到缓存失败: {e}')
def _get_latest_frame_from_cache(self, frame_type='camera'):
"""从队列获取最新帧"""
try:
if self.frame_queue.empty():
self.logger.debug('帧队列为空')
return None, None
# 获取队列中的所有帧,保留最新的一个
frames = []
while not self.frame_queue.empty():
try:
frames.append(self.frame_queue.get_nowait())
except queue.Empty:
break
if not frames:
return None, None
# 获取最新帧(最后一个)
latest_frame = frames[-1]
# 将最新帧重新放回队列
try:
self.frame_queue.put_nowait(latest_frame)
except queue.Full:
pass # 队列满时忽略
return latest_frame['frame'].copy(), latest_frame['timestamp']
except Exception as e:
self.logger.error(f'从队列获取帧失败: {e}')
return None, None

View File

@ -141,6 +141,11 @@ class DeviceCoordinator:
# 并行初始化设备 # 并行初始化设备
futures = [] futures = []
# FemtoBolt深度相机
if self.device_configs.get('femtobolt', {}).get('enabled', False):
future = self.executor.submit(self._init_femtobolt)
futures.append(('femtobolt', future))
# 普通相机 # 普通相机
if self.device_configs.get('camera', {}).get('enabled', False): if self.device_configs.get('camera', {}).get('enabled', False):
future = self.executor.submit(self._init_camera) future = self.executor.submit(self._init_camera)
@ -156,10 +161,7 @@ class DeviceCoordinator:
future = self.executor.submit(self._init_pressure) future = self.executor.submit(self._init_pressure)
futures.append(('pressure', future)) futures.append(('pressure', future))
# FemtoBolt深度相机
if self.device_configs.get('femtobolt', {}).get('enabled', False):
future = self.executor.submit(self._init_femtobolt)
futures.append(('femtobolt', future))
# 等待所有设备初始化完成 # 等待所有设备初始化完成
success_count = 0 success_count = 0
@ -422,6 +424,10 @@ class DeviceCoordinator:
success_count = 0 success_count = 0
for device_name, device in self.devices.items(): for device_name, device in self.devices.items():
try: try:
# 对深度相机(femtobolt)和普通相机(camera)直接调用初始化和启动推流
if device_name in ['femtobolt', 'camera']:
continue
if hasattr(device, '_start_connection_monitor'): if hasattr(device, '_start_connection_monitor'):
device._start_connection_monitor() device._start_connection_monitor()
success_count += 1 success_count += 1
@ -429,7 +435,7 @@ class DeviceCoordinator:
else: else:
self.logger.warning(f"{device_name}设备不支持连接监控") self.logger.warning(f"{device_name}设备不支持连接监控")
except Exception as e: except Exception as e:
self.logger.error(f"启动{device_name}设备连接监控失败: {e}") self.logger.error(f"启动{device_name}设备失败: {e}")
self.logger.info(f"设备连接监控启动完成,成功: {success_count}/{len(self.devices)}") self.logger.info(f"设备连接监控启动完成,成功: {success_count}/{len(self.devices)}")
return success_count > 0 return success_count > 0
@ -452,6 +458,21 @@ class DeviceCoordinator:
success_count = 0 success_count = 0
for device_name, device in self.devices.items(): for device_name, device in self.devices.items():
try: try:
# 对深度相机(femtobolt)和普通相机(camera)直接调用停止推流
if device_name in ['femtobolt', 'camera']:
self.logger.info(f"停止{device_name}设备推流")
# # 调用设备的cleanup方法清理资源,停止推流
# if hasattr(device, 'cleanup'):
# if device.cleanup():
# success_count += 1
# self.logger.info(f"{device_name}设备推流已停止")
# else:
# self.logger.warning(f"{device_name}设备推流停止失败")
# else:
# self.logger.warning(f"{device_name}设备不支持推流停止")
continue
if hasattr(device, '_stop_connection_monitor'): if hasattr(device, '_stop_connection_monitor'):
device._stop_connection_monitor() device._stop_connection_monitor()
success_count += 1 success_count += 1
@ -459,7 +480,7 @@ class DeviceCoordinator:
else: else:
self.logger.warning(f"{device_name}设备不支持连接监控") self.logger.warning(f"{device_name}设备不支持连接监控")
except Exception as e: except Exception as e:
self.logger.error(f"停止{device_name}设备连接监控失败: {e}") self.logger.error(f"停止{device_name}设备失败: {e}")
self.logger.info(f"设备连接监控停止完成,成功: {success_count}/{len(self.devices)}") self.logger.info(f"设备连接监控停止完成,成功: {success_count}/{len(self.devices)}")
return True return True

View File

@ -588,10 +588,6 @@ class FemtoBoltManager(BaseDevice):
if self.is_streaming: if self.is_streaming:
self.logger.warning("FemtoBolt流已在运行") self.logger.warning("FemtoBolt流已在运行")
return True return True
if not self.is_connected:
if not self.initialize():
return False
try: try:
self.is_streaming = True self.is_streaming = True
@ -620,8 +616,14 @@ class FemtoBoltManager(BaseDevice):
try: try:
self.is_streaming = False self.is_streaming = False
# 等待流线程自然结束
if self.streaming_thread and self.streaming_thread.is_alive(): if self.streaming_thread and self.streaming_thread.is_alive():
self.streaming_thread.join(timeout=5.0) self.logger.info("等待FemtoBolt流线程结束...")
self.streaming_thread.join(timeout=3.0)
if self.streaming_thread.is_alive():
self.logger.warning("FemtoBolt流线程未能在超时时间内结束")
else:
self.logger.info("FemtoBolt流工作线程结束")
self.logger.info("FemtoBolt流已停止") self.logger.info("FemtoBolt流已停止")
return True return True
@ -791,11 +793,26 @@ class FemtoBoltManager(BaseDevice):
""" """
try: try:
if self.device_handle: if self.device_handle:
# 先停止Pipeline以释放设备资源
if hasattr(self, 'pipeline') and self.pipeline:
try:
self.logger.info("正在停止FemtoBolt Pipeline...")
self.pipeline.stop()
self.logger.info("FemtoBolt Pipeline已停止")
# 等待Pipeline完全释放资源
time.sleep(0.5)
except Exception as e:
self.logger.warning(f"停止Pipeline时出现警告: {e}")
finally:
self.pipeline = None
# 尝试停止设备如果有stop方法 # 尝试停止设备如果有stop方法
if hasattr(self.device_handle, 'stop'): if hasattr(self.device_handle, 'stop'):
try: try:
self.device_handle.stop() self.device_handle.stop()
self.logger.info("FemtoBolt设备已停止") self.logger.info("FemtoBolt设备已停止")
# 等待设备完全停止
time.sleep(0.3)
except Exception as e: except Exception as e:
self.logger.warning(f"停止FemtoBolt设备时出现警告: {e}") self.logger.warning(f"停止FemtoBolt设备时出现警告: {e}")
@ -804,6 +821,8 @@ class FemtoBoltManager(BaseDevice):
try: try:
self.device_handle.close() self.device_handle.close()
self.logger.info("FemtoBolt设备连接已关闭") self.logger.info("FemtoBolt设备连接已关闭")
# 等待设备连接完全关闭
time.sleep(0.2)
except Exception as e: except Exception as e:
self.logger.warning(f"关闭FemtoBolt设备时出现警告: {e}") self.logger.warning(f"关闭FemtoBolt设备时出现警告: {e}")
@ -811,6 +830,10 @@ class FemtoBoltManager(BaseDevice):
except Exception as e: except Exception as e:
self.logger.error(f"清理FemtoBolt设备失败: {e}") self.logger.error(f"清理FemtoBolt设备失败: {e}")
finally:
# 确保所有相关属性都被重置
self.pipeline = None
self.device_handle = None
def disconnect(self): def disconnect(self):
""" """
@ -870,31 +893,16 @@ class FemtoBoltManager(BaseDevice):
self.logger.error(f"重新加载FemtoBolt配置失败: {e}") self.logger.error(f"重新加载FemtoBolt配置失败: {e}")
return False return False
def check_hardware_connection(self) -> bool: def check_hardware_connection(self) -> bool:
""" """
检查FemtoBolt设备硬件连接状态 相机连接检测太复杂忽略连接检测
Returns: Returns:
bool: 设备是否物理连接 bool: 相机是否连接
""" """
try: return self.is_connected
if not self.sdk_initialized or not self.device_handle:
return False
# 尝试获取设备状态来验证连接
if hasattr(self.femtobolt, 'device_get_capture'):
try:
# 尝试获取一帧数据来验证设备连接
capture = self.femtobolt.device_get_capture(self.device_handle, timeout_in_ms=1000)
if capture:
self.femtobolt.capture_release(capture)
return True
except Exception:
pass
return False
except Exception as e:
self.logger.debug(f"检查FemtoBolt硬件连接时发生异常: {e}")
return False
def cleanup(self): def cleanup(self):
""" """
@ -902,7 +910,7 @@ class FemtoBoltManager(BaseDevice):
""" """
try: try:
# 清理监控线程 # 清理监控线程
self._cleanup_monitoring() # self._cleanup_monitoring()
self.stop_streaming() self.stop_streaming()
self._cleanup_device() self._cleanup_device()

View File

@ -62,6 +62,10 @@ class FPMS_DEVICE_INFO(ctypes.Structure):
class RealPressureDevice: class RealPressureDevice:
"""真实SMiTSense压力传感器设备""" """真实SMiTSense压力传感器设备"""
# 类级别的USB初始化状态跟踪
_usb_initialized = False
_usb_init_lock = threading.Lock()
def __init__(self, dll_path=None): def __init__(self, dll_path=None):
""" """
初始化SMiTSense压力传感器 初始化SMiTSense压力传感器
@ -139,9 +143,16 @@ class RealPressureDevice:
def _initialize_device(self): def _initialize_device(self):
"""初始化设备连接""" """初始化设备连接"""
try: try:
# 初始化USB连接 # 使用类级别锁确保USB子系统只初始化一次
if self.dll.fpms_usb_init_wrap(0) != 0: with RealPressureDevice._usb_init_lock:
raise RuntimeError("初始化失败") if not RealPressureDevice._usb_initialized:
# 初始化USB连接
if self.dll.fpms_usb_init_wrap(0) != 0:
raise RuntimeError("USB子系统初始化失败")
RealPressureDevice._usb_initialized = True
logger.info("USB子系统初始化成功")
else:
logger.info("USB子系统已初始化跳过重复初始化")
# 获取设备列表 # 获取设备列表
count = ctypes.c_int() count = ctypes.c_int()
@ -433,12 +444,21 @@ class RealPressureDevice:
try: try:
if self.is_connected and self.dll and self.device_handle: if self.is_connected and self.dll and self.device_handle:
self.dll.fpms_usb_close_wrap(self.device_handle.value) self.dll.fpms_usb_close_wrap(self.device_handle.value)
# 重置设备句柄
self.device_handle = None
# 设置连接状态为断开 # 设置连接状态为断开
self.is_connected = False self.is_connected = False
logger.info('SMiTSense压力传感器连接已关闭') logger.info('SMiTSense压力传感器连接已关闭')
except Exception as e: except Exception as e:
logger.error(f'关闭压力传感器连接异常: {e}') logger.error(f'关闭压力传感器连接异常: {e}')
@classmethod
def reset_usb_state(cls):
"""重置USB初始化状态用于设备完全断开后的重新初始化"""
with cls._usb_init_lock:
cls._usb_initialized = False
logger.info("USB子系统状态已重置")
def __del__(self): def __del__(self):
"""析构函数,确保资源清理""" """析构函数,确保资源清理"""
self.close() self.close()
@ -522,7 +542,8 @@ class PressureManager(BaseDevice):
except Exception as e: except Exception as e:
self.logger.error(f"压力板初始化失败: {e}") self.logger.error(f"压力板初始化失败: {e}")
self.is_connected = False # 使用set_connected方法停止连接监控线程
self.set_connected(False)
self.device = None self.device = None
return False return False
@ -590,9 +611,6 @@ class PressureManager(BaseDevice):
try: try:
while self.is_streaming: while self.is_streaming:
try: try:
# 读数成功,重置失败计数
self.is_connected = True
# 从设备读取数据 # 从设备读取数据
pressure_data = None pressure_data = None
if self.device: if self.device:
@ -601,7 +619,11 @@ class PressureManager(BaseDevice):
if hasattr(self.device, 'is_connected') and not self.device.is_connected: if hasattr(self.device, 'is_connected') and not self.device.is_connected:
self.is_connected = False self.is_connected = False
time.sleep(self.reconnect_delay) time.sleep(self.reconnect_delay)
continue continue
# 读数成功,立即更新心跳和连接状态
self.is_connected = True
self.update_heartbeat()
foot_pressure = pressure_data['foot_pressure'] foot_pressure = pressure_data['foot_pressure']
# 获取各区域压力值 # 获取各区域压力值
@ -647,9 +669,6 @@ class PressureManager(BaseDevice):
'timestamp': pressure_data['timestamp'] 'timestamp': pressure_data['timestamp']
} }
# 更新心跳时间,防止连接监控线程判定为超时
self.update_heartbeat()
# 更新统计信息 # 更新统计信息
self.packet_count += 1 self.packet_count += 1
self.last_data_time = time.time() self.last_data_time = time.time()
@ -784,16 +803,16 @@ class PressureManager(BaseDevice):
""" """
try: try:
if not self.device: if not self.device:
# 如果设备实例不存在,尝试重新创建 # 如果设备实例不存在,返回False表示硬件未连接
return self._attempt_device_reconnection() return self._attempt_device_reconnection()
# 对于真实设备检查DLL和设备句柄状态 # 对于真实设备检查DLL和设备句柄状态
if hasattr(self.device, 'dll') and hasattr(self.device, 'device_handle'): if hasattr(self.device, 'dll') and hasattr(self.device, 'device_handle'):
if not self.device.dll or not self.device.device_handle: if not self.device.dll or not self.device.device_handle:
# DLL或句柄无效尝试重连 # DLL或句柄无效返回False表示硬件未连接
return self._attempt_device_reconnection() return self._attempt_device_reconnection()
# 直接检查设备句柄的有效性不依赖is_connected状态避免循环依赖 # 直接检查设备句柄的有效性
try: try:
# 检查设备句柄是否有效 # 检查设备句柄是否有效
if not self.device.device_handle or not hasattr(self.device.device_handle, 'value'): if not self.device.device_handle or not hasattr(self.device.device_handle, 'value'):
@ -803,12 +822,25 @@ class PressureManager(BaseDevice):
if self.device.device_handle.value == 0: if self.device.device_handle.value == 0:
return self._attempt_device_reconnection() return self._attempt_device_reconnection()
# 尝试简单的设备状态检查,而不是完整的数据读取 # 尝试实际的设备通信来验证硬件连接
# 使用DLL函数检查设备列表验证设备是否真实存在
count = ctypes.c_int()
devs = (FPMS_DEVICE_INFO * 10)()
r = self.device.dll.fpms_usb_get_device_list_wrap(devs, 10, ctypes.byref(count))
# 如果获取设备列表失败或设备数量为0说明硬件已断开
if r != 0 or count.value == 0:
self.logger.debug(f"设备列表检查失败: r={r}, count={count.value}")
return self._attempt_device_reconnection()
# 设备列表正常,硬件连接正常
return True return True
except Exception:
except Exception as e:
self.logger.debug(f"硬件连接检查异常: {e}")
return self._attempt_device_reconnection() return self._attempt_device_reconnection()
# 对于模拟设备总是返回True # 对于Mock设备直接返回True
return True return True
except Exception as e: except Exception as e:
@ -834,19 +866,18 @@ class PressureManager(BaseDevice):
self.device = None self.device = None
# 重置USB状态为重新插入的设备做准备
RealPressureDevice.reset_usb_state()
# 根据设备类型重新创建设备实例 # 根据设备类型重新创建设备实例
if self.device_type == 'real': self.device = RealPressureDevice()
self.device = RealPressureDevice()
else:
self.device = MockPressureDevice()
# 检查新设备是否连接成功 # 检查新设备是否连接成功
if hasattr(self.device, 'is_connected') and self.device.is_connected: if hasattr(self.device, 'is_connected') and self.device.is_connected:
self._notify_status_change(True) self._notify_status_change(True)
# 重连成功后,确保数据流正在运行 # 重连成功后,确保数据流正在运行
if not self.is_streaming: self.logger.info("重连成功,启动压力数据流")
self.logger.info("重连成功,启动压力数据流") self.start_streaming()
self.start_streaming()
return True return True
else: else:

View File

@ -296,6 +296,7 @@ class RecordingManager:
# 设置录制参数 # 设置录制参数
self.current_session_id = session_id self.current_session_id = session_id
# self.logger.info(f'检测sessionID................: {self.current_session_id}')
self.current_patient_id = patient_id self.current_patient_id = patient_id
self.screen_region = tuple(screen_location) # [x, y, w, h] -> (x, y, w, h) self.screen_region = tuple(screen_location) # [x, y, w, h] -> (x, y, w, h)
self.camera_region = tuple(camera_location) # [x, y, w, h] -> (x, y, w, h) self.camera_region = tuple(camera_location) # [x, y, w, h] -> (x, y, w, h)

View File

@ -16,7 +16,7 @@ max_backups = 7
[CAMERA] [CAMERA]
enabled = True enabled = True
device_index = 0 device_index = 3
width = 1280 width = 1280
height = 720 height = 720
fps = 30 fps = 30
@ -41,7 +41,6 @@ imu_device_type = ble
imu_port = COM9 imu_port = COM9
imu_mac_address = ef:3c:1a:0a:fe:02 imu_mac_address = ef:3c:1a:0a:fe:02
imu_baudrate = 9600 imu_baudrate = 9600
pressure_enabled = True pressure_enabled = True
pressure_device_type = real pressure_device_type = real
pressure_use_mock = False pressure_use_mock = False

View File

@ -0,0 +1 @@
Glfw error [65544]: WGL: Failed to make context current

View File

@ -1029,13 +1029,13 @@ class AppServer:
except Exception as duration_error: except Exception as duration_error:
self.logger.error(f'更新会话持续时间失败: {duration_error}') self.logger.error(f'更新会话持续时间失败: {duration_error}')
# 停止设备连接监控 # # 停止设备连接监控
if self.device_coordinator: # if self.device_coordinator:
try: # try:
self.device_coordinator.stop_all_connection_monitor() # self.device_coordinator.stop_all_connection_monitor()
self.logger.info('检测停止,设备连接监控已停止') # self.logger.info('检测停止,设备连接监控已停止')
except Exception as monitor_error: # except Exception as monitor_error:
self.logger.error(f'停止设备连接监控失败: {monitor_error}') # self.logger.error(f'停止设备连接监控失败: {monitor_error}')
success = self.db_manager.update_session_status(session_id, 'completed') success = self.db_manager.update_session_status(session_id, 'completed')
if success: if success:

View File

@ -522,31 +522,7 @@
</el-form-item> </el-form-item>
<div class="cameraFormTitle">头部IMU</div>
<el-form-item label="IMU串口号">
<el-select v-model="cameraForm.imu.port" placeholder="请选择">
<el-option label="COM1" value="COM1" />
<el-option label="COM2" value="COM2" />
<el-option label="COM3" value="COM3" />
<el-option label="COM4" value="COM4" />
<el-option label="COM5" value="COM5" />
<el-option label="COM6" value="COM6" />
<el-option label="COM7" value="COM7" />
<el-option label="COM8" value="COM8" />
<el-option label="COM9" value="COM9" />
<el-option label="COM10" value="COM10" />
<el-option label="COM11" value="COM11" />
<el-option label="COM12" value="COM12" />
<el-option label="COM13" value="COM13" />
<el-option label="COM14" value="COM14" />
<el-option label="COM15" value="COM15" />
<el-option label="COM16" value="COM16" />
<el-option label="COM17" value="COM17" />
<el-option label="COM18" value="COM18" />
<el-option label="COM19" value="COM19" />
<el-option label="COM20" value="COM20" />
</el-select>
</el-form-item>
</el-form> </el-form>
</div> </div>
@ -786,6 +762,7 @@ const isRunning = ref(false);
const timerId = ref(null); const timerId = ref(null);
const blinkState = ref(false); const blinkState = ref(false);
const formattedTime = computed(() => { const formattedTime = computed(() => {
const mins = Math.floor(seconds.value / 60); const mins = Math.floor(seconds.value / 60);
const secs = seconds.value % 60; const secs = seconds.value % 60;
return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`; return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
@ -812,7 +789,7 @@ const startTimer = () => {
seconds.value = Math.round(elapsed / 1000); seconds.value = Math.round(elapsed / 1000);
// 10600 // 10600
if (seconds.value >= 60) { if (seconds.value >= 600) {
console.log('⏰ 检测时长超过10分钟自动停止检测'); console.log('⏰ 检测时长超过10分钟自动停止检测');
ElMessage.warning('检测时长已达到10分钟自动停止检测'); ElMessage.warning('检测时长已达到10分钟自动停止检测');
stopRecord() stopRecord()
@ -1004,8 +981,7 @@ function connectWebSocket() {
socket.on('disconnect', (reason) => { socket.on('disconnect', (reason) => {
console.log('⚠️ 主连接断开:', reason) console.log('⚠️ 主连接断开:', reason)
isConnected.value = false isConnected.value = false
stopDeviceDataPush()
//
if (isRecording.value) { if (isRecording.value) {
stopRecording() stopRecording()
isStart.value = false isStart.value = false
@ -1139,24 +1115,11 @@ function startDeviceDataPush() {
} }
} }
//
function stopDeviceDataPush() {
if (devicesSocket && devicesSocket.connected) {
console.log('🛑 发送停止设备数据推送请求...')
devicesSocket.emit('stop_push_data')
} else {
console.warn('⚠️ 设备Socket未连接无法停止设备数据推送')
}
}
// WebSocket // WebSocket
function disconnectWebSocket() { function disconnectWebSocket() {
if (socket && socket.connected) { if (socket && socket.connected) {
console.log('正在主动断开WebSocket连接...') console.log('正在主动断开WebSocket连接...')
//
stopDeviceDataPush()
// //
socket.disconnect() socket.disconnect()
socket = null socket = null
@ -1670,12 +1633,8 @@ async function handleStartStop() {
// //
async function startDetection() { async function startDetection() {
try { try {
console.log('🚀 正在开始检测...') console.log('🚀 正在开始检测...')
//
if (!patientInfo.value || !patientInfo.value.id) {
throw new Error('缺少患者信息,无法开始检测')
}
// API // API
const response = await fetch(`${BACKEND_URL}/api/detection/start`, { const response = await fetch(`${BACKEND_URL}/api/detection/start`, {
method: 'POST', method: 'POST',
@ -1683,7 +1642,7 @@ async function startDetection() {
'Content-Type': 'application/json' 'Content-Type': 'application/json'
}, },
body: JSON.stringify({ body: JSON.stringify({
patient_id: patientInfo.value.id, patient_id: route.params.id,
// //
creator_id: creatorId.value, creator_id: creatorId.value,
}) })
@ -1754,7 +1713,7 @@ const loadPatientInfo = async () => {
const result = await response.json() const result = await response.json()
if (result.success) { if (result.success) {
patientInfo.value = { ...result.data, sessionId: null } patientInfo.value = { ...result.data, sessionId: null }
startDetection()
console.log('患者信息加载成功:', patientInfo.value) console.log('患者信息加载成功:', patientInfo.value)
} else { } else {
throw new Error(result.message) throw new Error(result.message)
@ -1778,7 +1737,7 @@ const handleBeforeUnload = (event) => {
} }
// //
stopDetection() // stopDetection()
// WebSocket // WebSocket
disconnectWebSocket() disconnectWebSocket()
@ -1929,17 +1888,22 @@ const getDevicesInit = async () => {
} }
onMounted(() => { onMounted(() => {
//
loadPatientInfo()
// WebSocket
connectWebSocket()
//
window.addEventListener('beforeunload', handleBeforeUnload)
if (authStore.currentUser) { if (authStore.currentUser) {
console.log(authStore.currentUser) console.log(authStore.currentUser)
creatorId.value = authStore.currentUser.id creatorId.value = authStore.currentUser.id
} }
//
loadPatientInfo()
//
startDetection()
// WebSocket
connectWebSocket()
//
window.addEventListener('beforeunload', handleBeforeUnload)
}) })
onUnmounted(() => { onUnmounted(() => {
@ -1955,9 +1919,12 @@ onUnmounted(() => {
stopRecord() stopRecord()
} }
stopDetection() // stopDetection()
// WebSocket // WebSocket
disconnectWebSocket() disconnectWebSocket()
// //
if (tiltCharts) { if (tiltCharts) {
@ -1992,6 +1959,7 @@ onUnmounted(() => {
const startRecord = async () => { // const startRecord = async () => { //
try { try {
console.log('🚀 正在开始录屏...') console.log('🚀 正在开始录屏...')
startTimer()
// //
if (!patientInfo.value || !patientInfo.value.sessionId) { if (!patientInfo.value || !patientInfo.value.sessionId) {
throw new Error('缺少患者信息,无法开始录屏') throw new Error('缺少患者信息,无法开始录屏')