MQTT转SignalR数据接收异常:跨网络数据丢失问题排查
问题分析与修复方案
问题根源
- 摄像头ID全局冲突:不同网络的摄像头ID可能重复,但DOM元素(表格、图片卡片)仅用
cameraId作为唯一标识,导致不同网络的数据共用同一DOM节点。切换网络时,旧网络的清除操作会清空新网络需要使用的DOM内容,而新数据因更新条件限制无法写入。 - 数据更新条件不合理:
updateCameraTable中仅当单元格内容为'-'时才更新数据,若同一cameraId的DOM节点已被其他网络填充过数据,新网络的数据无法正常写入。 - 清除操作未区分网络:
clearTableForCamera直接根据cameraId清除DOM内容,会误清除其他网络的同ID摄像头数据。
修复代码
1. 先调整DOM元素命名规则
确保每个网络的摄像头元素包含网络地址前缀,避免跨网络冲突:
- 表格ID改为
tableZone${receivedAddress}_${cameraId} - 图片卡片ID改为
ImageCard${receivedAddress}_${cameraId}
2. 修正JavaScript代码
// Object to store unique camera IDs for each network let uniqueCameraIds = {}; // Mapping for camera IDs to image paths let cameraImagePaths = {}; // Object to store network-specific data let networkData = {}; let connection = new signalR.HubConnectionBuilder() .withUrl("/hub/status") .build(); connection.start() .then(() => { console.log("SignalR connected"); connection.on("ReceiveMessage", (message, imgPaths) => { console.log("Received data from SignalR:", message); try { let data = typeof message === 'string' ? JSON.parse(message) : message; let msgTypes = data.msg_content.split(":"); if (msgTypes[0] === 'm') { handleObjectMessage(data, imgPaths); } else if (msgTypes[0] === 'h') { handleHeartbeatMessage(data); } } catch (error) { console.error("Error parsing JSON or extracting values:", error); } }); }) .catch((err) => { console.error("Error starting SignalR connection:", err.toString()); }); function handleObjectMessage(data, imgPaths) { let msgContent = data.msg_content.slice(2); let zoneDataArray = msgContent.split('|'); // Extract and log the address let receivedAddress = `${data.network_add}.${data.node_add}`; console.log("Received address:", receivedAddress); // Check if the networkData object exists for the received network if (!networkData[receivedAddress]) { console.log(`Initializing data for network: ${receivedAddress}`); networkData[receivedAddress] = { uniqueCameraIds: [], cameraImagePaths: {}, isFirstTime: true // 仅初始化时为true }; } // 仅清除当前网络自己的旧数据 if (!networkData[receivedAddress].isFirstTime) { console.log(`Clearing old data for network: ${receivedAddress}`); clearDataForNetwork(receivedAddress); } // 保留当前网络的摄像头ID zoneDataArray.forEach(zoneData => { let cameraId = zoneData.split(',')[0]; if (!networkData[receivedAddress].uniqueCameraIds.includes(cameraId)) { networkData[receivedAddress].uniqueCameraIds.push(cameraId); } }); // 存储当前网络的摄像头图片路径 imgPaths.forEach(imgPath => { Object.keys(imgPath).forEach(cameraId => { networkData[receivedAddress].cameraImagePaths[cameraId] = imgPath[cameraId]; }); }); // 更新状态 networkData[receivedAddress].isFirstTime = false; // 更新摄像头表格(仅当对应的DOM存在时) let dashboardElement = document.getElementById(`dashboard${receivedAddress}`); if (dashboardElement !== null) { console.log(`Received address ${receivedAddress} exists in the HTML model.`); updateCameraTable(zoneDataArray, data, receivedAddress); } else { console.warn(`Received address ${receivedAddress} has no matching dashboard element.`); } } function clearDataForNetwork(networkAddress) { networkData[networkAddress].uniqueCameraIds.forEach((cameraId) => { clearTableForCamera(networkAddress, cameraId); }); // 重置当前网络的数据存储 networkData[networkAddress].uniqueCameraIds = []; networkData[networkAddress].cameraImagePaths = {}; } function updateCameraTable(zoneDataArray, data, receivedAddress) { zoneDataArray.forEach((zoneData) => { let values = zoneData.split(','); let cameraId = values[0]; let zoneId = values[1]; let peopleCount = values[2]; let carCount = values[3]; let truckCount = values[4]; let motorcycleCount = values[5]; let miscCount = values[6]; console.log("Extracted values:", receivedAddress, data.msg_id, cameraId, zoneId, peopleCount, carCount, truckCount, motorcycleCount, miscCount); if (!cameraId) { console.error("Camera ID is missing."); return; } let formattedZoneId = `Zone${zoneId.replace('|', '_')}`; // 使用带网络地址的表格选择器 let tableCell = $(`#tableZone${receivedAddress}_${cameraId} [data-zone="${formattedZoneId}"]`); // 每次收到数据都更新,移除仅当内容为'-'才更新的限制 tableCell.find('.people').text(` ${peopleCount}`); tableCell.find('.car').text(` ${carCount}`); tableCell.find('.truck').text(` ${truckCount}`); tableCell.find('.bike').text(` ${motorcycleCount}`); tableCell.find('.misc').text(` ${miscCount}`); // 使用带网络地址的图片卡片选择器 if (networkData[receivedAddress].cameraImagePaths[cameraId]) { $(`#ImageCard${receivedAddress}_${cameraId} .image-card`).attr('src', networkData[receivedAddress].cameraImagePaths[cameraId]); console.log(`Image path for camera ${cameraId} (network ${receivedAddress}): ${networkData[receivedAddress].cameraImagePaths[cameraId]}`); } }); } function handleHeartbeatMessage(data) { // TODO: 接收心跳时更新ONLINE-OFFLINE状态 let spanElement = document.getElementById(`d-${data.network_add}.${data.node_add}`); console.log("Found heartbeat element:", spanElement); } function clearTableForCamera(networkAddress, cameraId) { // 仅清除当前网络下该摄像头的表格数据 $(`#tableZone${networkAddress}_${cameraId} [data-zone] td:not(:first-child)`).text('-'); }
关键修复点说明
- DOM元素隔离:给每个网络的摄像头元素添加网络地址前缀,彻底解决不同网络间的ID冲突,确保各网络数据独立存储和显示。
- 移除不合理更新限制:删除仅当单元格为
'-'才更新的逻辑,保证每次收到数据都能正确刷新对应网络的摄像头数据。 - 简化清除逻辑:去掉冗余的
currentNetworkAddress判断,直接根据当前接收的网络地址清除对应数据,避免逻辑混乱。
内容的提问来源于stack exchange,提问作者Surya Chandra Mohan
相关产品推荐
相关产品推荐

