修正了设备未连接的状态错误
This commit is contained in:
parent
50a0fe8641
commit
4e89ab6107
@ -41,6 +41,9 @@ class BaseDevice(ABC):
|
||||
self._socketio = None
|
||||
self._last_heartbeat = time.time()
|
||||
|
||||
# 状态变化回调
|
||||
self._status_change_callbacks = []
|
||||
|
||||
# 设备状态信息
|
||||
self._device_info = {
|
||||
'name': device_name,
|
||||
@ -134,6 +137,53 @@ class BaseDevice(ABC):
|
||||
"""
|
||||
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):
|
||||
"""
|
||||
发送数据到前端
|
||||
|
@ -316,6 +316,13 @@ class IMUManager(BaseDevice):
|
||||
if use_real_device:
|
||||
self.logger.info(f"使用真实IMU设备 - 端口: {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:
|
||||
self.logger.info("使用模拟IMU设备")
|
||||
self.imu_device = MockIMUDevice()
|
||||
|
@ -15,7 +15,7 @@ backup_interval = 24
|
||||
max_backups = 7
|
||||
|
||||
[CAMERA]
|
||||
device_index = 3
|
||||
device_index = 0
|
||||
width = 1280
|
||||
height = 720
|
||||
fps = 30
|
||||
|
@ -198,6 +198,12 @@ class AppServer:
|
||||
'imu': IMUManager(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('设备管理器初始化完成')
|
||||
|
||||
# 初始化设备协调器
|
||||
@ -933,6 +939,9 @@ class AppServer:
|
||||
self.logger.info('设备命名空间客户端连接')
|
||||
emit('status', {'message': '设备命名空间连接成功'}, namespace='/devices')
|
||||
|
||||
# 连接时发送当前所有设备的状态
|
||||
self.broadcast_all_device_status()
|
||||
|
||||
@self.socketio.on('disconnect', namespace='/devices')
|
||||
def handle_devices_disconnect():
|
||||
self.logger.info('设备命名空间客户端断开连接')
|
||||
@ -1047,6 +1056,11 @@ class AppServer:
|
||||
|
||||
# 输出启动结果摘要
|
||||
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:
|
||||
self.logger.info(f'成功启动的设备: {", ".join(successful_devices)}')
|
||||
if failed_devices:
|
||||
@ -1076,14 +1090,55 @@ class AppServer:
|
||||
manager.stop_streaming()
|
||||
manager.disconnect()
|
||||
self.logger.info(f'{device_name}设备已停止')
|
||||
# 广播设备状态为未连接
|
||||
self.broadcast_device_status(device_name, False)
|
||||
except Exception as e:
|
||||
self.logger.error(f'停止{device_name}设备失败: {e}')
|
||||
# 即使停止失败也广播为未连接状态
|
||||
self.broadcast_device_status(device_name, False)
|
||||
|
||||
self.logger.info('设备数据推送已停止')
|
||||
|
||||
except Exception as 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):
|
||||
"""检测工作线程"""
|
||||
|
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 :style="{ color: videoStatus == '已连接' ? '#00CC33' : '#808080' }" style="font-size: 14px;">
|
||||
{{ videoStatus }}</div>
|
||||
<div :style="{ color: femtoboltStatus == '已连接' ? '#00CC33' : '#808080' }" style="font-size: 14px;">
|
||||
{{ femtoboltStatus }}</div>
|
||||
</div>
|
||||
<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;">
|
||||
</div>
|
||||
</div>
|
||||
@ -199,7 +199,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<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="yline"></div>
|
||||
</div>
|
||||
@ -328,12 +328,11 @@
|
||||
视频
|
||||
</div>
|
||||
</div>
|
||||
<div :style="{ color: videoStatus == '已连接' ? '#00CC33' : '#808080' }" style="font-size: 14px;">{{
|
||||
videoStatus }}</div>
|
||||
<div :style="{ color: cameraStatus == '已连接' ? '#00CC33' : '#808080' }" style="font-size: 14px;">{{ cameraStatus }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 使用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;" />
|
||||
</div>
|
||||
</div>
|
||||
@ -497,6 +496,7 @@ import Header from '@/views/Header.vue'
|
||||
import { useAuthStore } from '../stores/index.js'
|
||||
import * as echarts from 'echarts'
|
||||
import { getBackendUrl, patientAPI } from '../services/api.js'
|
||||
import noImageSvg from '@/assets/no-image.svg'
|
||||
const authStore = useAuthStore()
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
@ -672,9 +672,14 @@ const chartoption = ref({
|
||||
}
|
||||
]
|
||||
})
|
||||
const videoStatus = ref('未连接')
|
||||
const pressureStatus = ref('未连接')
|
||||
const imuStatus = ref('未连接')
|
||||
// 四个设备的独立连接状态
|
||||
const cameraStatus = ref('未连接') // 相机设备状态
|
||||
const femtoboltStatus = ref('未连接') // 深度相机(FemtoBolt)设备状态
|
||||
const imuStatus = ref('未连接') // IMU设备状态
|
||||
const pressureStatus = ref('未连接') // 压力传感器设备状态
|
||||
|
||||
// 为了向后兼容,保留videoStatus但映射到cameraStatus
|
||||
const videoStatus = computed(() => cameraStatus.value)
|
||||
// 计时器状态
|
||||
const seconds = ref(0);
|
||||
const isRunning = ref(false);
|
||||
@ -902,9 +907,6 @@ function connectWebSocket() {
|
||||
// 统一设备命名空间事件监听
|
||||
devicesSocket.on('connect', () => {
|
||||
console.log('🔗 设备命名空间连接成功')
|
||||
videoStatus.value = '已连接'
|
||||
imuStatus.value = '已连接'
|
||||
pressureStatus.value = '已连接'
|
||||
|
||||
// 连接成功后订阅所有设备数据
|
||||
devicesSocket.emit('subscribe_device', { device_type: 'camera' })
|
||||
@ -918,7 +920,9 @@ function connectWebSocket() {
|
||||
|
||||
devicesSocket.on('disconnect', () => {
|
||||
console.log('🔗 设备命名空间断开连接')
|
||||
videoStatus.value = '未连接'
|
||||
// 断开连接时重置所有设备状态
|
||||
cameraStatus.value = '未连接'
|
||||
femtoboltStatus.value = '未连接'
|
||||
imuStatus.value = '未连接'
|
||||
pressureStatus.value = '未连接'
|
||||
})
|
||||
@ -964,6 +968,34 @@ function connectWebSocket() {
|
||||
}
|
||||
})
|
||||
|
||||
// 监听各设备独立连接状态事件
|
||||
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) {
|
||||
console.error('💥 连接异常:', error.message)
|
||||
isConnected.value = false
|
||||
@ -1023,8 +1055,9 @@ function disconnectWebSocket() {
|
||||
console.log('🔗 统一设备命名空间连接已断开')
|
||||
}
|
||||
|
||||
// 重置状态
|
||||
videoStatus.value = '未连接'
|
||||
// 重置所有设备状态
|
||||
cameraStatus.value = '未连接'
|
||||
femtoboltStatus.value = '未连接'
|
||||
imuStatus.value = '未连接'
|
||||
pressureStatus.value = '未连接'
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ export default defineConfig({
|
||||
}
|
||||
},
|
||||
server: {
|
||||
port: 3002,
|
||||
port: 3000,
|
||||
host: '0.0.0.0',
|
||||
// 开发服务器配置
|
||||
cors: true,
|
||||
|
Loading…
Reference in New Issue
Block a user