修正了设备未连接的状态错误
This commit is contained in:
parent
50a0fe8641
commit
4e89ab6107
@ -41,6 +41,9 @@ class BaseDevice(ABC):
|
|||||||
self._socketio = None
|
self._socketio = None
|
||||||
self._last_heartbeat = time.time()
|
self._last_heartbeat = time.time()
|
||||||
|
|
||||||
|
# 状态变化回调
|
||||||
|
self._status_change_callbacks = []
|
||||||
|
|
||||||
# 设备状态信息
|
# 设备状态信息
|
||||||
self._device_info = {
|
self._device_info = {
|
||||||
'name': device_name,
|
'name': device_name,
|
||||||
@ -133,6 +136,53 @@ class BaseDevice(ABC):
|
|||||||
socketio: SocketIO实例
|
socketio: SocketIO实例
|
||||||
"""
|
"""
|
||||||
self._socketio = socketio
|
self._socketio = socketio
|
||||||
|
|
||||||
|
def add_status_change_callback(self, callback):
|
||||||
|
"""
|
||||||
|
添加状态变化回调函数
|
||||||
|
|
||||||
|
Args:
|
||||||
|
callback: 回调函数,接收参数 (device_name, is_connected)
|
||||||
|
"""
|
||||||
|
if callback not in self._status_change_callbacks:
|
||||||
|
self._status_change_callbacks.append(callback)
|
||||||
|
|
||||||
|
def remove_status_change_callback(self, callback):
|
||||||
|
"""
|
||||||
|
移除状态变化回调函数
|
||||||
|
|
||||||
|
Args:
|
||||||
|
callback: 要移除的回调函数
|
||||||
|
"""
|
||||||
|
if callback in self._status_change_callbacks:
|
||||||
|
self._status_change_callbacks.remove(callback)
|
||||||
|
|
||||||
|
def _notify_status_change(self, is_connected: bool):
|
||||||
|
"""
|
||||||
|
通知状态变化
|
||||||
|
|
||||||
|
Args:
|
||||||
|
is_connected: 连接状态
|
||||||
|
"""
|
||||||
|
for callback in self._status_change_callbacks:
|
||||||
|
try:
|
||||||
|
callback(self.device_name, is_connected)
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"状态变化回调执行失败: {e}")
|
||||||
|
|
||||||
|
def set_connected(self, is_connected: bool):
|
||||||
|
"""
|
||||||
|
设置连接状态并触发回调
|
||||||
|
|
||||||
|
Args:
|
||||||
|
is_connected: 连接状态
|
||||||
|
"""
|
||||||
|
old_status = self.is_connected
|
||||||
|
self.is_connected = is_connected
|
||||||
|
|
||||||
|
# 只有状态真正改变时才触发回调
|
||||||
|
if old_status != 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):
|
||||||
"""
|
"""
|
||||||
|
@ -316,6 +316,13 @@ class IMUManager(BaseDevice):
|
|||||||
if use_real_device:
|
if use_real_device:
|
||||||
self.logger.info(f"使用真实IMU设备 - 端口: {self.port}, 波特率: {self.baudrate}")
|
self.logger.info(f"使用真实IMU设备 - 端口: {self.port}, 波特率: {self.baudrate}")
|
||||||
self.imu_device = RealIMUDevice(self.port, self.baudrate)
|
self.imu_device = RealIMUDevice(self.port, self.baudrate)
|
||||||
|
|
||||||
|
# 检查真实设备是否连接成功
|
||||||
|
if self.imu_device.ser is None:
|
||||||
|
self.logger.error(f"IMU设备连接失败: 无法打开串口 {self.port}")
|
||||||
|
self.is_connected = False
|
||||||
|
self.imu_device = None
|
||||||
|
return False
|
||||||
else:
|
else:
|
||||||
self.logger.info("使用模拟IMU设备")
|
self.logger.info("使用模拟IMU设备")
|
||||||
self.imu_device = MockIMUDevice()
|
self.imu_device = MockIMUDevice()
|
||||||
|
@ -15,7 +15,7 @@ backup_interval = 24
|
|||||||
max_backups = 7
|
max_backups = 7
|
||||||
|
|
||||||
[CAMERA]
|
[CAMERA]
|
||||||
device_index = 3
|
device_index = 0
|
||||||
width = 1280
|
width = 1280
|
||||||
height = 720
|
height = 720
|
||||||
fps = 30
|
fps = 30
|
||||||
|
@ -198,6 +198,12 @@ class AppServer:
|
|||||||
'imu': IMUManager(self.socketio, self.config_manager),
|
'imu': IMUManager(self.socketio, self.config_manager),
|
||||||
'pressure': PressureManager(self.socketio, self.config_manager)
|
'pressure': PressureManager(self.socketio, self.config_manager)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# 为每个设备添加状态变化回调
|
||||||
|
for device_name, manager in self.device_managers.items():
|
||||||
|
if manager and hasattr(manager, 'add_status_change_callback'):
|
||||||
|
manager.add_status_change_callback(self._on_device_status_change)
|
||||||
|
|
||||||
self.logger.info('设备管理器初始化完成')
|
self.logger.info('设备管理器初始化完成')
|
||||||
|
|
||||||
# 初始化设备协调器
|
# 初始化设备协调器
|
||||||
@ -933,6 +939,9 @@ class AppServer:
|
|||||||
self.logger.info('设备命名空间客户端连接')
|
self.logger.info('设备命名空间客户端连接')
|
||||||
emit('status', {'message': '设备命名空间连接成功'}, namespace='/devices')
|
emit('status', {'message': '设备命名空间连接成功'}, namespace='/devices')
|
||||||
|
|
||||||
|
# 连接时发送当前所有设备的状态
|
||||||
|
self.broadcast_all_device_status()
|
||||||
|
|
||||||
@self.socketio.on('disconnect', namespace='/devices')
|
@self.socketio.on('disconnect', namespace='/devices')
|
||||||
def handle_devices_disconnect():
|
def handle_devices_disconnect():
|
||||||
self.logger.info('设备命名空间客户端断开连接')
|
self.logger.info('设备命名空间客户端断开连接')
|
||||||
@ -1047,6 +1056,11 @@ class AppServer:
|
|||||||
|
|
||||||
# 输出启动结果摘要
|
# 输出启动结果摘要
|
||||||
successful_devices = [name for name, success in device_results.items() if success]
|
successful_devices = [name for name, success in device_results.items() if success]
|
||||||
|
|
||||||
|
# 广播设备状态更新
|
||||||
|
for device_name, success in device_results.items():
|
||||||
|
self.broadcast_device_status(device_name, success)
|
||||||
|
|
||||||
if successful_devices:
|
if successful_devices:
|
||||||
self.logger.info(f'成功启动的设备: {", ".join(successful_devices)}')
|
self.logger.info(f'成功启动的设备: {", ".join(successful_devices)}')
|
||||||
if failed_devices:
|
if failed_devices:
|
||||||
@ -1076,13 +1090,54 @@ class AppServer:
|
|||||||
manager.stop_streaming()
|
manager.stop_streaming()
|
||||||
manager.disconnect()
|
manager.disconnect()
|
||||||
self.logger.info(f'{device_name}设备已停止')
|
self.logger.info(f'{device_name}设备已停止')
|
||||||
|
# 广播设备状态为未连接
|
||||||
|
self.broadcast_device_status(device_name, False)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f'停止{device_name}设备失败: {e}')
|
self.logger.error(f'停止{device_name}设备失败: {e}')
|
||||||
|
# 即使停止失败也广播为未连接状态
|
||||||
|
self.broadcast_device_status(device_name, False)
|
||||||
|
|
||||||
self.logger.info('设备数据推送已停止')
|
self.logger.info('设备数据推送已停止')
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f'停止设备数据推送失败: {e}')
|
self.logger.error(f'停止设备数据推送失败: {e}')
|
||||||
|
|
||||||
|
def broadcast_device_status(self, device_name: str, is_connected: bool):
|
||||||
|
"""广播单个设备状态"""
|
||||||
|
if self.socketio:
|
||||||
|
try:
|
||||||
|
status_data = {
|
||||||
|
'device_type': device_name,
|
||||||
|
'status': is_connected,
|
||||||
|
'timestamp': datetime.now().isoformat()
|
||||||
|
}
|
||||||
|
self.socketio.emit('device_status', status_data, namespace='/devices')
|
||||||
|
self.logger.info(f'广播设备状态: {device_name} -> {"已连接" if is_connected else "未连接"}')
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f'广播设备状态失败: {e}')
|
||||||
|
|
||||||
|
def broadcast_all_device_status(self):
|
||||||
|
"""广播所有设备状态"""
|
||||||
|
for device_name, manager in self.device_managers.items():
|
||||||
|
if manager is not None:
|
||||||
|
try:
|
||||||
|
# 检查设备是否连接(使用is_connected属性)
|
||||||
|
is_connected = hasattr(manager, 'is_connected') and getattr(manager, 'is_connected', False)
|
||||||
|
self.broadcast_device_status(device_name, is_connected)
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f'检查{device_name}设备状态失败: {e}')
|
||||||
|
self.broadcast_device_status(device_name, False)
|
||||||
|
|
||||||
|
def _on_device_status_change(self, device_name: str, is_connected: bool):
|
||||||
|
"""
|
||||||
|
设备状态变化回调函数
|
||||||
|
|
||||||
|
Args:
|
||||||
|
device_name: 设备名称
|
||||||
|
is_connected: 连接状态
|
||||||
|
"""
|
||||||
|
self.logger.info(f'设备状态变化: {device_name} -> {"已连接" if is_connected else "未连接"}')
|
||||||
|
self.broadcast_device_status(device_name, is_connected)
|
||||||
|
|
||||||
|
|
||||||
def _detection_worker(self, detection_id, duration):
|
def _detection_worker(self, detection_id, duration):
|
||||||
|
5
frontend/src/renderer/src/assets/no-image.svg
Normal file
5
frontend/src/renderer/src/assets/no-image.svg
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<svg width="300" height="200" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect width="100%" height="100%" fill="#323232"/>
|
||||||
|
<rect x="10" y="10" width="280" height="180" fill="none" stroke="#666" stroke-width="2" stroke-dasharray="5,5"/>
|
||||||
|
<text x="150" y="105" font-family="Arial, sans-serif" font-size="18" fill="#999" text-anchor="middle">无图片</text>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 360 B |
@ -58,12 +58,12 @@
|
|||||||
身体姿态
|
身体姿态
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div :style="{ color: videoStatus == '已连接' ? '#00CC33' : '#808080' }" style="font-size: 14px;">
|
<div :style="{ color: femtoboltStatus == '已连接' ? '#00CC33' : '#808080' }" style="font-size: 14px;">
|
||||||
{{ videoStatus }}</div>
|
{{ femtoboltStatus }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div style="display: flex;justify-content: center;height: 100%;padding-top: 0px;">
|
<div style="display: flex;justify-content: center;height: 100%;padding-top: 0px;">
|
||||||
<!-- 使用深度相机视频流替换静态图片 -->
|
<!-- 使用深度相机视频流替换静态图片 -->
|
||||||
<img :src="depthCameraImgSrc || '@/assets/posture.png'" alt="深度相机视频流"
|
<img :src="(femtoboltStatus === '已连接' && depthCameraImgSrc) ? depthCameraImgSrc : noImageSvg" alt="深度相机视频流"
|
||||||
style="width: 100%;height: calc(100% - 10px);object-fit:contain;background:#323232;">
|
style="width: 100%;height: calc(100% - 10px);object-fit:contain;background:#323232;">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -199,7 +199,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style="position: relative;width: 300px;height: 300px;">
|
<div style="position: relative;width: 300px;height: 300px;">
|
||||||
<img :src="footImgSrc" style="width: 300px;height: 300px;" alt="">
|
<img :src="(pressureStatus === '已连接' && footImgSrc) ? footImgSrc : noImageSvg" style="width: 300px;height: 300px;" alt="">
|
||||||
<div class="xline"></div>
|
<div class="xline"></div>
|
||||||
<div class="yline"></div>
|
<div class="yline"></div>
|
||||||
</div>
|
</div>
|
||||||
@ -328,12 +328,11 @@
|
|||||||
视频
|
视频
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div :style="{ color: videoStatus == '已连接' ? '#00CC33' : '#808080' }" style="font-size: 14px;">{{
|
<div :style="{ color: cameraStatus == '已连接' ? '#00CC33' : '#808080' }" style="font-size: 14px;">{{ cameraStatus }}</div>
|
||||||
videoStatus }}</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 使用img元素显示视频流(优化的Data URL方案) -->
|
<!-- 使用img元素显示视频流(优化的Data URL方案) -->
|
||||||
<img :src="rtspImgSrc" alt=""
|
<img :src="(cameraStatus === '已连接' && rtspImgSrc) ? rtspImgSrc : noImageSvg" alt=""
|
||||||
style="width: 100%;height: calc(100% - 80px);object-fit:contain;background:#323232;" />
|
style="width: 100%;height: calc(100% - 80px);object-fit:contain;background:#323232;" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -497,6 +496,7 @@ import Header from '@/views/Header.vue'
|
|||||||
import { useAuthStore } from '../stores/index.js'
|
import { useAuthStore } from '../stores/index.js'
|
||||||
import * as echarts from 'echarts'
|
import * as echarts from 'echarts'
|
||||||
import { getBackendUrl, patientAPI } from '../services/api.js'
|
import { getBackendUrl, patientAPI } from '../services/api.js'
|
||||||
|
import noImageSvg from '@/assets/no-image.svg'
|
||||||
const authStore = useAuthStore()
|
const authStore = useAuthStore()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@ -672,9 +672,14 @@ const chartoption = ref({
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
const videoStatus = ref('未连接')
|
// 四个设备的独立连接状态
|
||||||
const pressureStatus = ref('未连接')
|
const cameraStatus = ref('未连接') // 相机设备状态
|
||||||
const imuStatus = ref('未连接')
|
const femtoboltStatus = ref('未连接') // 深度相机(FemtoBolt)设备状态
|
||||||
|
const imuStatus = ref('未连接') // IMU设备状态
|
||||||
|
const pressureStatus = ref('未连接') // 压力传感器设备状态
|
||||||
|
|
||||||
|
// 为了向后兼容,保留videoStatus但映射到cameraStatus
|
||||||
|
const videoStatus = computed(() => cameraStatus.value)
|
||||||
// 计时器状态
|
// 计时器状态
|
||||||
const seconds = ref(0);
|
const seconds = ref(0);
|
||||||
const isRunning = ref(false);
|
const isRunning = ref(false);
|
||||||
@ -902,9 +907,6 @@ function connectWebSocket() {
|
|||||||
// 统一设备命名空间事件监听
|
// 统一设备命名空间事件监听
|
||||||
devicesSocket.on('connect', () => {
|
devicesSocket.on('connect', () => {
|
||||||
console.log('🔗 设备命名空间连接成功')
|
console.log('🔗 设备命名空间连接成功')
|
||||||
videoStatus.value = '已连接'
|
|
||||||
imuStatus.value = '已连接'
|
|
||||||
pressureStatus.value = '已连接'
|
|
||||||
|
|
||||||
// 连接成功后订阅所有设备数据
|
// 连接成功后订阅所有设备数据
|
||||||
devicesSocket.emit('subscribe_device', { device_type: 'camera' })
|
devicesSocket.emit('subscribe_device', { device_type: 'camera' })
|
||||||
@ -918,7 +920,9 @@ function connectWebSocket() {
|
|||||||
|
|
||||||
devicesSocket.on('disconnect', () => {
|
devicesSocket.on('disconnect', () => {
|
||||||
console.log('🔗 设备命名空间断开连接')
|
console.log('🔗 设备命名空间断开连接')
|
||||||
videoStatus.value = '未连接'
|
// 断开连接时重置所有设备状态
|
||||||
|
cameraStatus.value = '未连接'
|
||||||
|
femtoboltStatus.value = '未连接'
|
||||||
imuStatus.value = '未连接'
|
imuStatus.value = '未连接'
|
||||||
pressureStatus.value = '未连接'
|
pressureStatus.value = '未连接'
|
||||||
})
|
})
|
||||||
@ -963,6 +967,34 @@ function connectWebSocket() {
|
|||||||
console.error('❌ 设备数据推送错误:', data.message)
|
console.error('❌ 设备数据推送错误:', data.message)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 监听各设备独立连接状态事件
|
||||||
|
devicesSocket.on('device_status', (data) => {
|
||||||
|
console.log('📱 设备状态更新:', data)
|
||||||
|
const { device_type, status } = data
|
||||||
|
const statusText = status ? '已连接' : '未连接'
|
||||||
|
|
||||||
|
switch (device_type) {
|
||||||
|
case 'camera':
|
||||||
|
cameraStatus.value = statusText
|
||||||
|
console.log(`📷 相机状态: ${statusText}`)
|
||||||
|
break
|
||||||
|
case 'femtobolt':
|
||||||
|
femtoboltStatus.value = statusText
|
||||||
|
console.log(`🔍 深度相机状态: ${statusText}`)
|
||||||
|
break
|
||||||
|
case 'imu':
|
||||||
|
imuStatus.value = statusText
|
||||||
|
console.log(`🧭 IMU状态: ${statusText}`)
|
||||||
|
break
|
||||||
|
case 'pressure':
|
||||||
|
pressureStatus.value = statusText
|
||||||
|
console.log(`⚖️ 压力传感器状态: ${statusText}`)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
console.warn('⚠️ 未知设备类型:', device_type)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('💥 连接异常:', error.message)
|
console.error('💥 连接异常:', error.message)
|
||||||
@ -1023,8 +1055,9 @@ function disconnectWebSocket() {
|
|||||||
console.log('🔗 统一设备命名空间连接已断开')
|
console.log('🔗 统一设备命名空间连接已断开')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 重置状态
|
// 重置所有设备状态
|
||||||
videoStatus.value = '未连接'
|
cameraStatus.value = '未连接'
|
||||||
|
femtoboltStatus.value = '未连接'
|
||||||
imuStatus.value = '未连接'
|
imuStatus.value = '未连接'
|
||||||
pressureStatus.value = '未连接'
|
pressureStatus.value = '未连接'
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ export default defineConfig({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
port: 3002,
|
port: 3000,
|
||||||
host: '0.0.0.0',
|
host: '0.0.0.0',
|
||||||
// 开发服务器配置
|
// 开发服务器配置
|
||||||
cors: true,
|
cors: true,
|
||||||
|
Loading…
Reference in New Issue
Block a user