通讯遇到的问题

熔炉项目

问题现象

  • 炉子2能正常显示"倾炉中",但炉子1超时时,前端没有显示"连接断开"
  • 日志显示有错误信息,但前端状态没变

问题根源

错误消息中缺少从站号!

text

❌ 之前发射的信号:
emit error(name, "读取失败: " + errorStr);
→ 结果: "读取失败: Request timeout."
→ 没有从站号,FurnaceController不知道是哪个炉子出错

为什么炉子2能显示?

炉子2是通过 coilsRead 信号更新的,这个信号包含从站号:

cpp

emit coilsRead(name, slaveId, startAddress, values);  // ✓ 有slaveId

为什么炉子1不能显示?

错误信号 error 没有从站号:

cpp

emit error(name, "读取失败: " + errorStr);  // ✗ 没有slaveId

解决方案

在错误消息中加入从站号:

cpp

QString fullErrorMsg = QString("读取失败: %1 (从站地址:%2)")
                          .arg(errorStr).arg(slaveId);
emit error(name, fullErrorMsg);  // ✓ 现在有slaveId了

问题总结:Modbus通信与状态显示问题排查全过程

一、核心问题

炉子2能正常显示"倾炉中",但炉子1超时时,前端没有显示"连接断开"

二、排查过程

1️⃣ 第一阶段:发现日志问题

从日志看到:

text

IP[ "127.0.0.1" ] 读取失败: "Request timeout." 从站地址: 1 起始地址: 0

但前端没反应 → 说明C++收到错误,但没传到前端

2️⃣ 第二阶段:定位信号问题

发现错误信号发射有问题:

cpp

// ❌ 错误:信号中没有从站号
emit error(name, "读取失败: " + errorStr);
// 结果: "读取失败: Request timeout."

// ✅ 正确:信号中要包含从站号
QString fullErrorMsg = QString("读取失败: %1 (从站地址:%2)")
                       .arg(errorStr).arg(slaveId);
emit error(name, fullErrorMsg);

3️⃣ 第三阶段:发现重复调用问题

cpp

void startMonitoring() {
    // ❌ 每次调用都创建新定时器、新连接
    QTimer* timer = new QTimer(this);  // 多个定时器!
    connect(...);  // 多个信号连接!
}

4️⃣ 第四阶段:添加保护机制

cpp

// ✅ 防止重复启动
static bool signalsConnected = false;
if (!signalsConnected) {
    connect(...);  // 只连接一次
    signalsConnected = true;
}

// ✅ 保存定时器指针,避免重复创建
if (m_readTimer) {
    m_readTimer->stop();
    delete m_readTimer;
}

5️⃣ 第五阶段:解决误判问题

cpp

// ✅ 错误计数,避免网络抖动误判
if (f.errorCount >= 10) {  // 连续10次错误才判定断开
    f.status = "连接断开";
}

6️⃣ 第六阶段:添加心跳检测

cpp

// ✅ 监控定时器健康状态
connect(m_heartbeatTimer, &QTimer::timeout, [this]() {
    if (!m_readTimer->isActive()) {
        restartReadTimer();  // 定时器意外关闭时自动重启
    }
});

三、最终解决方案

问题 解决方案
错误消息缺少从站号 在错误信号中加入从站号和起始地址
重复调用startMonitoring 添加m_monitoringStarted标志位
多个定时器同时运行 用成员变量保存定时器指针
多个信号连接 用static标志位控制只连接一次
网络抖动误判 错误计数+时间窗口(10次/30秒)
定时器意外关闭 心跳检测机制,自动重启
状态显示不匹配 QML中添加disconnected状态处理

四、关键教训

  1. 信号要带足够信息:错误信号必须包含从站号
  2. 防止重复初始化:检查是否已启动
  3. 资源管理:用成员变量保存指针,避免内存泄漏
  4. 容错处理:错误计数、心跳检测
  5. 前后端一致:C++发的状态名要和QML匹配

五、最终效果

text

正常情况:
炉子2 → 收到数据 → 状态"倾炉中" → 绿色

异常情况:
炉子1 → 超时 → 错误计数+1 → 计数≥10 → 状态"连接断开" → 红色
        ↓
    定时器意外关闭 → 心跳检测 → 自动重启 → 恢复正常

系统现在更加稳定、可靠,能准确显示每个炉子的真实状态! 🚀

posted @ 2026-03-13 16:53  拿受用  阅读(2)  评论(0)    收藏  举报