优化版本提交

This commit is contained in:
zhaozilong12 2025-08-17 16:42:05 +08:00
parent 7053427249
commit 50a0fe8641
14 changed files with 4856 additions and 226 deletions

View File

@ -29,7 +29,7 @@ depth_range_max = 1500
[DEVICES] [DEVICES]
imu_device_type = real imu_device_type = real
imu_port = COM6 imu_port = COM3
imu_baudrate = 9600 imu_baudrate = 9600
pressure_device_type = real pressure_device_type = real
pressure_use_mock = False pressure_use_mock = False

View File

@ -252,7 +252,7 @@ class DeviceManager:
# logger.info('准备启动FemtoBolt设备...') # logger.info('准备启动FemtoBolt设备...')
# 启动FemtoBolt设备 # 启动FemtoBolt设备
logger.info(f'尝试启动FemtoBolt设备...,参数详情是{self.femtobolt_config}') # logger.info(f'尝试启动FemtoBolt设备...,参数详情是{self.femtobolt_config}')
self.femtobolt_camera = pykinect.start_device(config=self.femtobolt_config) self.femtobolt_camera = pykinect.start_device(config=self.femtobolt_config)
if self.femtobolt_camera: if self.femtobolt_camera:
self.device_status['femtobolt'] = True self.device_status['femtobolt'] = True

View File

@ -370,7 +370,7 @@ class CameraManager(BaseDevice):
'device_id': self.device_id 'device_id': self.device_id
} }
self._socketio.emit('camera_frame', data, namespace='/camera') self._socketio.emit('camera_frame', data, namespace='/devices')
except Exception as e: except Exception as e:
self.logger.error(f"发送帧数据失败: {e}") self.logger.error(f"发送帧数据失败: {e}")

View File

@ -118,7 +118,7 @@ class DeviceCoordinator:
""" """
注册Socket.IO命名空间 注册Socket.IO命名空间
""" """
namespaces = ['/camera', '/imu', '/pressure', '/femtobolt', '/coordinator'] namespaces = ['/devices', '/coordinator']
for namespace in namespaces: for namespace in namespaces:
self.socket_manager.register_namespace(namespace) self.socket_manager.register_namespace(namespace)

View File

@ -537,7 +537,7 @@ class FemtoBoltManager(BaseDevice):
'min': self.depth_range_min, 'min': self.depth_range_min,
'max': self.depth_range_max 'max': self.depth_range_max
} }
}, namespace='/femtobolt') }, namespace='/devices')
frame_count += 1 frame_count += 1
# 更新统计 # 更新统计
@ -640,7 +640,7 @@ class FemtoBoltManager(BaseDevice):
send_data['color_image'] = color_data send_data['color_image'] = color_data
# 发送到SocketIO # 发送到SocketIO
self._socketio.emit('femtobolt_frame', send_data, namespace='/femtobolt') self._socketio.emit('femtobolt_frame', send_data, namespace='/devices')
except Exception as e: except Exception as e:
self.logger.error(f"发送深度数据失败: {e}") self.logger.error(f"发送深度数据失败: {e}")

View File

@ -502,7 +502,7 @@ class IMUManager(BaseDevice):
# 发送数据到前端 # 发送数据到前端
if self._socketio: if self._socketio:
self._socketio.emit('imu_data', data, namespace='/imu') self._socketio.emit('imu_data', data, namespace='/devices')
# 更新统计 # 更新统计
self.data_count += 1 self.data_count += 1

View File

@ -807,14 +807,64 @@ class PressureManager(BaseDevice):
if self.device: if self.device:
pressure_data = self.device.read_data() pressure_data = self.device.read_data()
if pressure_data: if pressure_data and 'foot_pressure' in pressure_data:
foot_pressure = pressure_data['foot_pressure']
# 获取各区域压力值
left_front = foot_pressure['left_front']
left_rear = foot_pressure['left_rear']
right_front = foot_pressure['right_front']
right_rear = foot_pressure['right_rear']
left_total = foot_pressure['left_total']
right_total = foot_pressure['right_total']
# 计算总压力
total_pressure = left_total + right_total
# 计算平衡比例(左脚压力占总压力的比例)
balance_ratio = left_total / total_pressure if total_pressure > 0 else 0.5
# 计算压力中心偏移
pressure_center_offset = (balance_ratio - 0.5) * 100 # 转换为百分比
# 计算前后足压力分布
left_front_ratio = left_front / left_total if left_total > 0 else 0.5
right_front_ratio = right_front / right_total if right_total > 0 else 0.5
# 构建完整的足部压力数据
complete_pressure_data = {
# 分区压力值
'pressure_zones': {
'left_front': left_front,
'left_rear': left_rear,
'right_front': right_front,
'right_rear': right_rear,
'left_total': left_total,
'right_total': right_total,
'total_pressure': total_pressure
},
# 平衡分析
'balance_analysis': {
'balance_ratio': round(balance_ratio, 3),
'pressure_center_offset': round(pressure_center_offset, 2),
'balance_status': 'balanced' if abs(pressure_center_offset) < 10 else 'unbalanced',
'left_front_ratio': round(left_front_ratio, 3),
'right_front_ratio': round(right_front_ratio, 3)
},
# 压力图片
'pressure_image': pressure_data.get('pressure_image', ''),
'timestamp': pressure_data['timestamp']
}
# 更新统计信息 # 更新统计信息
self.packet_count += 1 self.packet_count += 1
self.last_data_time = time.time() self.last_data_time = time.time()
# 发送数据到前端 # 发送数据到前端
if self._socketio: if self._socketio:
self._socketio.emit('pressure_data', pressure_data, namespace='/pressure') self._socketio.emit('pressure_data', {
'foot_pressure': complete_pressure_data,
'timestamp': datetime.now().isoformat()
}, namespace='/devices')
else: else:
self.logger.warning("SocketIO实例为空无法发送压力数据") self.logger.warning("SocketIO实例为空无法发送压力数据")

File diff suppressed because it is too large Load Diff

View File

@ -170,42 +170,35 @@ class DeviceTestServer:
except Exception as e: except Exception as e:
emit('test_status', {'status': 'error', 'message': str(e)}) emit('test_status', {'status': 'error', 'message': str(e)})
# 注册设备命名空间的连接事件 # 注册统一设备命名空间的连接事件
@self.socketio.on('connect', namespace='/camera') @self.socketio.on('connect', namespace='/devices')
def handle_camera_connect(): def handle_devices_connect():
self.logger.info('相机命名空间客户端连接') self.logger.info('设备命名空间客户端连接')
emit('status', {'message': '相机命名空间连接成功'}, namespace='/camera') emit('status', {'message': '设备命名空间连接成功'}, namespace='/devices')
@self.socketio.on('connect', namespace='/femtobolt') @self.socketio.on('disconnect', namespace='/devices')
def handle_femtobolt_connect(): def handle_devices_disconnect():
self.logger.info('深度相机命名空间客户端连接') self.logger.info('设备命名空间客户端断开连接')
emit('status', {'message': '深度相机命名空间连接成功'}, namespace='/femtobolt')
@self.socketio.on('connect', namespace='/imu') @self.socketio.on('subscribe_device', namespace='/devices')
def handle_imu_connect(): def handle_subscribe_device(data):
self.logger.info('IMU命名空间客户端连接') """处理设备订阅事件"""
emit('status', {'message': 'IMU命名空间连接成功'}, namespace='/imu') device_type = data.get('device_type')
self.logger.info(f'客户端订阅设备: {device_type}')
emit('subscription_status', {
'device_type': device_type,
'status': 'subscribed'
}, namespace='/devices')
@self.socketio.on('connect', namespace='/pressure') @self.socketio.on('unsubscribe_device', namespace='/devices')
def handle_pressure_connect(): def handle_unsubscribe_device(data):
self.logger.info('压力板命名空间客户端连接') """处理设备取消订阅事件"""
emit('status', {'message': '压力板命名空间连接成功'}, namespace='/pressure') device_type = data.get('device_type')
self.logger.info(f'客户端取消订阅设备: {device_type}')
@self.socketio.on('disconnect', namespace='/camera') emit('subscription_status', {
def handle_camera_disconnect(): 'device_type': device_type,
self.logger.info('相机命名空间客户端断开连接') 'status': 'unsubscribed'
}, namespace='/devices')
@self.socketio.on('disconnect', namespace='/femtobolt')
def handle_femtobolt_disconnect():
self.logger.info('深度相机命名空间客户端断开连接')
@self.socketio.on('disconnect', namespace='/imu')
def handle_imu_disconnect():
self.logger.info('IMU命名空间客户端断开连接')
@self.socketio.on('disconnect', namespace='/pressure')
def handle_pressure_disconnect():
self.logger.info('压力板命名空间客户端断开连接')
def start_device_test(self): def start_device_test(self):
"""开始设备测试""" """开始设备测试"""
@ -329,8 +322,8 @@ class DeviceTestServer:
# 生成模拟数据 # 生成模拟数据
data = generator.generate_data() data = generator.generate_data()
# 发送到对应的命名空间 # 发送到统一的设备命名空间
namespace = f'/{device_name}' namespace = '/devices'
event_name = self._get_event_name(device_name) event_name = self._get_event_name(device_name)
self.socketio.emit(event_name, data, namespace=namespace) self.socketio.emit(event_name, data, namespace=namespace)

View File

@ -840,7 +840,7 @@
} }
} }
// 处理压力板数据 // 处理压力板数据(兼容新老数据结构)
function handlePressureData(data) { function handlePressureData(data) {
if (!isTesting) return; if (!isTesting) return;
@ -848,20 +848,50 @@
document.getElementById('pressureDeviceStatus').textContent = '已连接'; document.getElementById('pressureDeviceStatus').textContent = '已连接';
document.getElementById('pressureDeviceStatus').classList.add('connected'); document.getElementById('pressureDeviceStatus').classList.add('connected');
if (data.pressure_image) { // 后端新结构:{ foot_pressure: { pressure_zones, balance_analysis, pressure_image }, timestamp }
// 旧结构:{ pressure_image, pressure_data: { left_total, right_total, total_pressure, balance_ratio } }
const footPressure = data && (data.foot_pressure || null);
const zones = footPressure && (footPressure.pressure_zones || footPressure.pressureZones || null);
const analysis = footPressure && (footPressure.balance_analysis || footPressure.balanceAnalysis || null);
// 压力图像(优先新结构,其次旧结构)
const pressureImage = (footPressure && footPressure.pressure_image) || data.pressure_image || null;
if (pressureImage) {
const img = document.getElementById('pressureImage'); const img = document.getElementById('pressureImage');
img.src = 'data:image/jpeg;base64,' + data.pressure_image; img.src = 'data:image/jpeg;base64,' + pressureImage;
img.style.display = 'block'; img.style.display = 'block';
document.getElementById('pressureNoSignal').style.display = 'none'; document.getElementById('pressureNoSignal').style.display = 'none';
} }
// 更新压力数据 // 数值数据:优先新结构的 pressure_zones/balance_analysis回退到旧结构的 pressure_data
if (data.pressure_data) { if (zones || analysis) {
const leftTotal = (zones && (zones.left_total ?? zones.leftTotal)) ?? 0;
const rightTotal = (zones && (zones.right_total ?? zones.rightTotal)) ?? 0;
const totalPressure = (zones && (zones.total_pressure ?? zones.totalPressure)) ?? (leftTotal + rightTotal);
let balanceRatioRaw = analysis && (analysis.balance_ratio ?? analysis.balanceRatio);
if (balanceRatioRaw == null && totalPressure) {
// 若缺失,按左右比例计算一个近似值
balanceRatioRaw = leftTotal / totalPressure; // 0~1
}
let balanceRatio = 0;
if (typeof balanceRatioRaw === 'number') {
balanceRatio = balanceRatioRaw <= 1 ? Math.round(balanceRatioRaw * 100) : Math.round(balanceRatioRaw);
}
document.getElementById('leftTotal').textContent = leftTotal;
document.getElementById('rightTotal').textContent = rightTotal;
document.getElementById('totalPressure').textContent = totalPressure;
document.getElementById('balanceRatio').textContent = `${balanceRatio}%`;
} else if (data && data.pressure_data) {
const pd = data.pressure_data; const pd = data.pressure_data;
document.getElementById('leftTotal').textContent = pd.left_total; const leftTotal = pd.left_total ?? pd.leftTotal ?? 0;
document.getElementById('rightTotal').textContent = pd.right_total; const rightTotal = pd.right_total ?? pd.rightTotal ?? 0;
document.getElementById('totalPressure').textContent = pd.total_pressure; const totalPressure = pd.total_pressure ?? pd.totalPressure ?? (leftTotal + rightTotal);
document.getElementById('balanceRatio').textContent = `${pd.balance_ratio}%`; let balanceRatioRaw = pd.balance_ratio ?? pd.balanceRatio ?? 0;
const balanceRatio = balanceRatioRaw <= 1 ? Math.round(balanceRatioRaw * 100) : Math.round(balanceRatioRaw);
document.getElementById('leftTotal').textContent = leftTotal;
document.getElementById('rightTotal').textContent = rightTotal;
document.getElementById('totalPressure').textContent = totalPressure;
document.getElementById('balanceRatio').textContent = `${balanceRatio}%`;
} }
} }

View File

@ -15,7 +15,7 @@ backup_interval = 24
max_backups = 7 max_backups = 7
[CAMERA] [CAMERA]
device_index = 0 device_index = 3
width = 1280 width = 1280
height = 720 height = 720
fps = 30 fps = 30
@ -29,7 +29,7 @@ depth_range_max = 1500
[DEVICES] [DEVICES]
imu_device_type = real imu_device_type = real
imu_port = COM6 imu_port = COM3
imu_baudrate = 9600 imu_baudrate = 9600
pressure_device_type = real pressure_device_type = real
pressure_use_mock = False pressure_use_mock = False

View File

@ -43,7 +43,7 @@ class SocketManager:
注册设备命名空间 注册设备命名空间
Args: Args:
namespace: 命名空间路径 '/camera' namespace: 命名空间路径 '/devices'
device_name: 设备名称 device_name: 设备名称
""" """
with self._lock: with self._lock:

View File

@ -138,6 +138,7 @@ class AppServer:
self.app, self.app,
cors_allowed_origins='*', cors_allowed_origins='*',
async_mode='threading', async_mode='threading',
#async_mode='eventlet',
logger=False, logger=False,
engineio_logger=False, engineio_logger=False,
ping_timeout=60, ping_timeout=60,
@ -926,70 +927,73 @@ class AppServer:
if self.socketio is None: if self.socketio is None:
return return
# 注册各设备命名空间的连接事件 # 注册统一设备命名空间的连接事件
@self.socketio.on('connect', namespace='/camera') @self.socketio.on('connect', namespace='/devices')
def handle_camera_connect(): def handle_devices_connect():
self.logger.info('相机命名空间客户端连接') self.logger.info('设备命名空间客户端连接')
emit('status', {'message': '相机命名空间连接成功'}, namespace='/camera') emit('status', {'message': '设备命名空间连接成功'}, namespace='/devices')
@self.socketio.on('connect', namespace='/femtobolt') @self.socketio.on('disconnect', namespace='/devices')
def handle_femtobolt_connect(): def handle_devices_disconnect():
self.logger.info('深度相机命名空间客户端连接') self.logger.info('设备命名空间客户端断开连接')
emit('status', {'message': '深度相机命名空间连接成功'}, namespace='/femtobolt')
@self.socketio.on('connect', namespace='/imu') # 注册设备订阅事件
def handle_imu_connect(): @self.socketio.on('subscribe_device', namespace='/devices')
self.logger.info('IMU命名空间客户端连接') def handle_subscribe_device(data):
emit('status', {'message': 'IMU命名空间连接成功'}, namespace='/imu') """订阅特定设备数据"""
device_type = data.get('device_type')
@self.socketio.on('connect', namespace='/pressure') if device_type in ['camera', 'femtobolt', 'imu', 'pressure']:
def handle_pressure_connect(): self.logger.info(f'客户端订阅{device_type}设备数据')
self.logger.info('压力板命名空间客户端连接') emit('subscription_status', {
emit('status', {'message': '压力板命名空间连接成功'}, namespace='/pressure') 'device_type': device_type,
'status': 'subscribed',
@self.socketio.on('disconnect', namespace='/camera') 'message': f'{device_type}设备数据订阅成功'
def handle_camera_disconnect(): }, namespace='/devices')
self.logger.info('相机命名空间客户端断开连接') else:
emit('subscription_status', {
@self.socketio.on('disconnect', namespace='/femtobolt') 'device_type': device_type,
def handle_femtobolt_disconnect(): 'status': 'error',
self.logger.info('深度相机命名空间客户端断开连接') 'message': '不支持的设备类型'
}, namespace='/devices')
@self.socketio.on('disconnect', namespace='/imu')
def handle_imu_disconnect(): @self.socketio.on('unsubscribe_device', namespace='/devices')
self.logger.info('IMU命名空间客户端断开连接') def handle_unsubscribe_device(data):
"""取消订阅特定设备数据"""
@self.socketio.on('disconnect', namespace='/pressure') device_type = data.get('device_type')
def handle_pressure_disconnect(): self.logger.info(f'客户端取消订阅{device_type}设备数据')
self.logger.info('压力板命名空间客户端断开连接') emit('subscription_status', {
'device_type': device_type,
'status': 'unsubscribed',
'message': f'{device_type}设备数据取消订阅成功'
}, namespace='/devices')
@self.socketio.on('start_push_data') @self.socketio.on('start_push_data', namespace='/devices')
def handle_start_push_data(): def handle_start_push_data():
"""启动数据推送""" """启动数据推送"""
try: try:
self.start_device_push_data() self.start_device_push_data()
emit('test_status', {'status': 'started', 'message': '数据推送已开始'}) emit('test_status', {'status': 'started', 'message': '数据推送已开始'}, namespace='/devices')
except Exception as e: except Exception as e:
emit('test_status', {'status': 'error', 'message': str(e)}) emit('test_status', {'status': 'error', 'message': str(e)}, namespace='/devices')
@self.socketio.on('stop_push_data') @self.socketio.on('stop_push_data', namespace='/devices')
def handle_stop_push_data(): def handle_stop_push_data():
"""停止数据推送""" """停止数据推送"""
try: try:
self.stop_device_push_data() self.stop_device_push_data()
emit('test_status', {'status': 'stopped', 'message': '数据推送已停止'}) emit('test_status', {'status': 'stopped', 'message': '数据推送已停止'}, namespace='/devices')
except Exception as e: except Exception as e:
emit('test_status', {'status': 'error', 'message': str(e)}) emit('test_status', {'status': 'error', 'message': str(e)}, namespace='/devices')
def start_device_push_data(self): def start_device_push_data(self):
"""开始设备数据推送""" """开始设备数据推送"""
if self.is_testing: if self.is_pushing_data:
self.logger.warning('设备数据推送已在运行') self.logger.warning('设备数据推送已在运行')
return return
try: try:
self.logger.info('开始设备数据推送...') self.logger.info('开始设备数据推送...')
self.is_testing = True self.is_pushing_data = True
# 并行启动真实设备管理器 # 并行启动真实设备管理器
failed_devices = [] failed_devices = []
@ -1052,18 +1056,18 @@ class AppServer:
except Exception as e: except Exception as e:
self.logger.error(f'启动设备数据推送失败: {e}') self.logger.error(f'启动设备数据推送失败: {e}')
self.is_testing = False self.is_pushing_data = False
raise raise
def stop_device_push_data(self): def stop_device_push_data(self):
"""停止设备数据推送""" """停止设备数据推送"""
if not self.is_testing: if not self.is_pushing_data:
self.logger.warning('设备数据推送未运行') self.logger.warning('设备数据推送未运行')
return return
try: try:
self.logger.info('停止设备数据推送...') self.logger.info('停止设备数据推送...')
self.is_testing = False self.is_pushing_data = False
# 停止设备管理器 # 停止设备管理器
for device_name, manager in self.device_managers.items(): for device_name, manager in self.device_managers.items():

View File

@ -532,6 +532,7 @@ const patientInfo = ref({
// WebSocket // WebSocket
let socket = null let socket = null
let devicesSocket = null
let cameraSocket = null let cameraSocket = null
let femtoboltSocket = null let femtoboltSocket = null
let imuSocket = null let imuSocket = null
@ -809,8 +810,24 @@ function connectWebSocket() {
socket.disconnect() socket.disconnect()
socket = null socket = null
} }
if (cameraSocket) {
cameraSocket.disconnect()
cameraSocket = null
}
if (femtoboltSocket) {
femtoboltSocket.disconnect()
femtoboltSocket = null
}
if (imuSocket) {
imuSocket.disconnect()
imuSocket = null
}
if (pressureSocket) {
pressureSocket.disconnect()
pressureSocket = null
}
// Socket.IO // Socket.IO
socket = io(BACKEND_URL, { socket = io(BACKEND_URL, {
transports: ['websocket', 'polling'], transports: ['websocket', 'polling'],
timeout: 10000, timeout: 10000,
@ -820,19 +837,29 @@ function connectWebSocket() {
reconnectionDelay: 1000 reconnectionDelay: 1000
}) })
// //
devicesSocket = io(BACKEND_URL + '/devices', {
transports: ['websocket', 'polling'],
timeout: 10000,
forceNew: true
})
// socket
cameraSocket = devicesSocket
femtoboltSocket = devicesSocket
imuSocket = devicesSocket
pressureSocket = devicesSocket
//
socket.on('connect', () => { socket.on('connect', () => {
console.log('✅ WebSocket连接成功Socket ID:', socket.id) console.log('✅ WebSocket连接成功Socket ID:', socket.id)
isConnected.value = true isConnected.value = true
//
startDeviceDataPush()
// //
initchart() initchart()
}) })
//
socket.on('connect_error', (error) => { socket.on('connect_error', (error) => {
console.error('❌ 连接失败:', error.message) console.error('❌ 连接失败:', error.message)
isConnected.value = false isConnected.value = false
// //
if (isRecording.value) { if (isRecording.value) {
@ -841,9 +868,8 @@ function connectWebSocket() {
} }
}) })
//
socket.on('disconnect', (reason) => { socket.on('disconnect', (reason) => {
console.log('⚠️ 连接断开:', reason) console.log('⚠️ 连接断开:', reason)
isConnected.value = false isConnected.value = false
stopDeviceDataPush() stopDeviceDataPush()
// //
@ -853,96 +879,90 @@ function connectWebSocket() {
} }
}) })
//
socket.on('reconnect', (attemptNumber) => { socket.on('reconnect', (attemptNumber) => {
console.log('🔄 WebSocket重连成功尝试次数:', attemptNumber) console.log('🔄 WebSocket重连成功尝试次数:', attemptNumber)
isConnected.value = true isConnected.value = true
}) })
//
socket.on('reconnect_attempt', (attemptNumber) => { socket.on('reconnect_attempt', (attemptNumber) => {
console.log('🔄 正在尝试重连...', attemptNumber) console.log('🔄 正在尝试重连主连接...', attemptNumber)
}) })
//
socket.on('reconnect_failed', () => { socket.on('reconnect_failed', () => {
console.error('❌ WebSocket重连失败') console.error('❌ WebSocket重连失败')
isConnected.value = false isConnected.value = false
}) })
//
socket.on('test_status', (data) => { socket.on('error', (error) => {
console.log('📊 测试状态:', data) console.error('❌ 主Socket错误:', error)
if (data.status === 'started') {
console.log('✅ 设备数据推送已开始')
} else if (data.status === 'stopped') {
console.log('⏹️ 设备数据推送已停止')
} else if (data.status === 'error') {
console.error('❌ 设备数据推送错误:', data.message)
}
}) })
// //
cameraSocket = io(BACKEND_URL + '/camera') devicesSocket.on('connect', () => {
cameraSocket.on('connect', () => { console.log('🔗 设备命名空间连接成功')
console.log('📹 相机命名空间连接成功')
videoStatus.value = '已连接' videoStatus.value = '已连接'
imuStatus.value = '已连接'
pressureStatus.value = '已连接'
//
devicesSocket.emit('subscribe_device', { device_type: 'camera' })
devicesSocket.emit('subscribe_device', { device_type: 'femtobolt' })
devicesSocket.emit('subscribe_device', { device_type: 'imu' })
devicesSocket.emit('subscribe_device', { device_type: 'pressure' })
//
startDeviceDataPush()
}) })
cameraSocket.on('disconnect', () => {
console.log('📹 相机命名空间断开连接') devicesSocket.on('disconnect', () => {
console.log('🔗 设备命名空间断开连接')
videoStatus.value = '未连接' videoStatus.value = '未连接'
imuStatus.value = '未连接'
pressureStatus.value = '未连接'
}) })
cameraSocket.on('video_frame', (data) => {
devicesSocket.on('connect_error', (error) => {
console.error('❌ 设备命名空间连接失败:', error.message)
})
//
devicesSocket.on('camera_frame', (data) => {
frameCount++ frameCount++
displayFrame(data.image) displayFrame(data.image)
}) })
devicesSocket.on('video_frame', (data) => {
// frameCount++
femtoboltSocket = io(BACKEND_URL + '/femtobolt') displayFrame(data.image)
femtoboltSocket.on('connect', () => {
console.log('🔍 深度相机命名空间连接成功')
}) })
femtoboltSocket.on('disconnect', () => {
console.log('🔍 深度相机命名空间断开连接') devicesSocket.on('femtobolt_frame', (data) => {
displayDepthCameraFrame(data.depth_image || data.image)
}) })
femtoboltSocket.on('depth_camera_frame', (data) => { devicesSocket.on('depth_camera_frame', (data) => {
displayDepthCameraFrame(data.image) displayDepthCameraFrame(data.depth_image || data.image)
}) })
// IMU devicesSocket.on('imu_data', (data) => {
imuSocket = io(BACKEND_URL + '/imu')
imuSocket.on('connect', () => {
console.log('🧭 IMU命名空间连接成功')
imuStatus.value = '已连接'
})
imuSocket.on('disconnect', () => {
console.log('🧭 IMU命名空间断开连接')
imuStatus.value = '未连接'
})
imuSocket.on('imu_data', (data) => {
handleIMUData(data) handleIMUData(data)
}) })
// devicesSocket.on('pressure_data', (data) => {
pressureSocket = io(BACKEND_URL + '/pressure') handlePressureData(data)
pressureSocket.on('connect', () => { })
console.log('⚖️ 压力板命名空间连接成功')
pressureStatus.value = '已连接' //
}) devicesSocket.on('test_status', (data) => {
pressureSocket.on('disconnect', () => { console.log('📊 测试状态:', data)
console.log('⚖️ 压力板命名空间断开连接') if (data.status === 'started') {
pressureStatus.value = '未连接' console.log('✅ 设备数据推送已开始')
}) } else if (data.status === 'stopped') {
pressureSocket.on('pressure_data', (data) => { console.log('⏹️ 设备数据推送已停止')
handlePressureData(data) } else if (data.status === 'error') {
}) console.error('❌ 设备数据推送错误:', data.message)
}
// })
socket.on('error', (error) => {
console.error('❌ Socket错误:', error)
})
} catch (error) { } catch (error) {
console.error('💥 连接异常:', error.message) console.error('💥 连接异常:', error.message)
@ -952,21 +972,21 @@ function connectWebSocket() {
// //
function startDeviceDataPush() { function startDeviceDataPush() {
if (socket && socket.connected) { if (devicesSocket && devicesSocket.connected) {
console.log('🚀 发送启动设备数据推送请求...') console.log('🚀 发送启动设备数据推送请求...')
socket.emit('start_push_data') devicesSocket.emit('start_push_data')
} else { } else {
console.warn('⚠️ Socket未连接无法启动设备数据推送') console.warn('⚠️ 设备Socket未连接无法启动设备数据推送')
} }
} }
// //
function stopDeviceDataPush() { function stopDeviceDataPush() {
if (socket && socket.connected) { if (devicesSocket && devicesSocket.connected) {
console.log('🛑 发送停止设备数据推送请求...') console.log('🛑 发送停止设备数据推送请求...')
socket.emit('stop_push_data') devicesSocket.emit('stop_push_data')
} else { } else {
console.warn('⚠️ Socket未连接无法停止设备数据推送') console.warn('⚠️ 设备Socket未连接无法停止设备数据推送')
} }
} }
@ -986,29 +1006,21 @@ function disconnectWebSocket() {
console.log('✅ 主WebSocket连接已断开') console.log('✅ 主WebSocket连接已断开')
} }
// //
if (cameraSocket && cameraSocket.connected) { if (devicesSocket && devicesSocket.connected) {
cameraSocket.disconnect() //
devicesSocket.emit('unsubscribe_device', { device_type: 'camera' })
devicesSocket.emit('unsubscribe_device', { device_type: 'femtobolt' })
devicesSocket.emit('unsubscribe_device', { device_type: 'imu' })
devicesSocket.emit('unsubscribe_device', { device_type: 'pressure' })
devicesSocket.disconnect()
devicesSocket = null
cameraSocket = null cameraSocket = null
console.log('📹 相机命名空间连接已断开')
}
if (femtoboltSocket && femtoboltSocket.connected) {
femtoboltSocket.disconnect()
femtoboltSocket = null femtoboltSocket = null
console.log('🔍 深度相机命名空间连接已断开')
}
if (imuSocket && imuSocket.connected) {
imuSocket.disconnect()
imuSocket = null imuSocket = null
console.log('🧭 IMU命名空间连接已断开')
}
if (pressureSocket && pressureSocket.connected) {
pressureSocket.disconnect()
pressureSocket = null pressureSocket = null
console.log('⚖️ 压力板命名空间连接已断开') console.log('🔗 统一设备命名空间连接已断开')
} }
// //
@ -1098,20 +1110,32 @@ function handleIMUData(data) {
const pVal = Math.round(pitch * 10) / 10 const pVal = Math.round(pitch * 10) / 10
const tVal = Math.round(tilt * 10) / 10 const tVal = Math.round(tilt * 10) / 10
if (rotationCharts) { if (rotationCharts && !rotationCharts.isDisposed()) {
rotationCharts.setOption({ try {
series: [{ data: [{ value: rVal }] }] rotationCharts.setOption({
}) series: [{ data: [{ value: rVal }] }]
})
} catch (e) {
console.warn('rotationCharts setOption error:', e);
}
} }
if (pitchCharts) { if (pitchCharts && !pitchCharts.isDisposed()) {
pitchCharts.setOption({ try {
series: [{ data: [{ value: pVal }] }] pitchCharts.setOption({
}) series: [{ data: [{ value: pVal }] }]
})
} catch (e) {
console.warn('pitchCharts setOption error:', e);
}
} }
if (tiltCharts) { if (tiltCharts && !tiltCharts.isDisposed()) {
tiltCharts.setOption({ try {
series: [{ data: [{ value: tVal }] }] tiltCharts.setOption({
}) series: [{ data: [{ value: tVal }] }]
})
} catch (e) {
console.warn('tiltCharts setOption error:', e);
}
} }
// 使 // 使
@ -2007,45 +2031,72 @@ const initchart = () => {
if (chartDom) { if (chartDom) {
// //
if (rotationCharts) { if (rotationCharts) {
rotationCharts.dispose(); try {
rotationCharts.dispose();
} catch (e) {
console.warn('rotationCharts dispose error:', e);
}
rotationCharts = null;
} }
rotationCharts = echarts.init(chartDom); rotationCharts = echarts.init(chartDom);
rotationCharts.setOption(chartoption.value); rotationCharts.setOption(chartoption.value);
} else { } else {
console.warn('找不到 ID 为 的 DOM 元素'); console.warn('找不到 ID 为 rotationChartId 的 DOM 元素');
} }
const chartDom2 = document.getElementById('pitchChartId'); const chartDom2 = document.getElementById('pitchChartId');
if (chartDom2) { if (chartDom2) {
// //
if (pitchCharts) { if (pitchCharts) {
pitchCharts.dispose(); try {
pitchCharts.dispose();
} catch (e) {
console.warn('pitchCharts dispose error:', e);
}
pitchCharts = null;
} }
pitchCharts = echarts.init(chartDom2); pitchCharts = echarts.init(chartDom2);
pitchCharts.setOption(chartoption.value); pitchCharts.setOption(chartoption.value);
} else { } else {
console.warn('找不到 ID 为 的 DOM 元素'); console.warn('找不到 ID 为 pitchChartId 的 DOM 元素');
} }
const chartDom3 = document.getElementById('tiltChartId'); const chartDom3 = document.getElementById('tiltChartId');
if (chartDom3) { if (chartDom3) {
// //
if (tiltCharts) { if (tiltCharts) {
tiltCharts.dispose(); try {
tiltCharts.dispose();
} catch (e) {
console.warn('tiltCharts dispose error:', e);
}
tiltCharts = null;
} }
tiltCharts = echarts.init(chartDom3); tiltCharts = echarts.init(chartDom3);
tiltCharts.setOption(chartoption.value); tiltCharts.setOption(chartoption.value);
} else { } else {
console.warn('找不到 ID 为 的 DOM 元素'); console.warn('找不到 ID 为 tiltChartId 的 DOM 元素');
} }
// //
window.addEventListener('resize', () => { window.addEventListener('resize', () => {
if (rotationCharts) { if (rotationCharts && !rotationCharts.isDisposed()) {
rotationCharts.resize(); try {
rotationCharts.resize();
} catch (e) {
console.warn('rotationCharts resize error:', e);
}
} }
if (pitchCharts) { if (pitchCharts && !pitchCharts.isDisposed()) {
pitchCharts.resize(); try {
pitchCharts.resize();
} catch (e) {
console.warn('pitchCharts resize error:', e);
}
} }
if (tiltCharts) { if (tiltCharts && !tiltCharts.isDisposed()) {
tiltCharts.resize(); try {
tiltCharts.resize();
} catch (e) {
console.warn('tiltCharts resize error:', e);
}
} }
}); });
}); });
@ -2101,13 +2152,28 @@ onUnmounted(() => {
// //
if (tiltCharts) { if (tiltCharts) {
tiltCharts.dispose(); try {
tiltCharts.dispose();
} catch (e) {
console.warn('tiltCharts dispose error in onUnmounted:', e);
}
tiltCharts = null;
} }
if (rotationCharts) { if (rotationCharts) {
rotationCharts.dispose(); try {
rotationCharts.dispose();
} catch (e) {
console.warn('rotationCharts dispose error in onUnmounted:', e);
}
rotationCharts = null;
} }
if (pitchCharts) { if (pitchCharts) {
pitchCharts.dispose(); try {
pitchCharts.dispose();
} catch (e) {
console.warn('pitchCharts dispose error in onUnmounted:', e);
}
pitchCharts = null;
} }
// //