You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于QSerialPort的Qt5 Modbus主站程序接收数据丢失原因排查

排查Qt Modbus主站接收消息丢失问题

看起来你的Modbus主站状态机逻辑有几个关键漏洞,这很可能就是导致20%丢包率的原因。结合你提供的代码和调试输出,我帮你梳理几个核心问题和修复方案:

1. WaitForHead状态的两个致命错误

在serial_read的WaitForHead分支里,有两个直接导致丢包的问题:

case CommMaster::WaitForHead:{
    int headIdx=read.indexOf(expectReceiveHead);
    if(headIdx>=0){//head found
        int length=read.length()-headIdx;
        if(length>=expectReceiveLength){
            handleReceive((read.data(),expectReceiveLength)); // 语法错误!
            receive.clear();
            comm.mainState=CommMaster::Idle;
        }else{
            receive=read.mid(headIdx);
            // 漏切换到WaitForComplete状态!
        }
    }
    break;
}
  • 语法错误:(read.data(),expectReceiveLength)是逗号表达式,实际只会把expectReceiveLength传给handleReceive,完全没传递正确的接收数据。应该改成handleReceive(read.mid(headIdx, expectReceiveLength).data(), expectReceiveLength)。
  • 状态切换遗漏:当找到头部但数据长度不足时,你把剩余数据存入了receive,但没有切换subState到WaitForComplete。这导致后续触发readyRead时,程序仍然会进入WaitForHead状态,从头查找头部,而不是追加数据补全剩余长度,直接丢失了之前保存的部分数据。

2. 超时处理的缓冲区读取顺序错误

在timer_timeout的超时分支里,你先读取了串口缓冲区的数据,再打印receive,这会导致receive里的部分数据和串口缓冲区的数据被拆分打印,甚至丢失:

QByteArray readAll=serial->readAll();
qDebug()<<"Receive timeout, receive="<<receive<<" serial->readAll()="<<readAll;

正确的做法应该是先把串口缓冲区的数据追加到receive,再统一打印,避免数据拆分:

receive.append(serial->readAll());
qDebug()<<"Receive timeout, receive buffer content:"<<receive;
receive.clear();
comm.mainState=CommMaster::Idle;
comm.subState=CommMaster::WaitForHead; // 重置子状态,避免下次等待出错

3. 非等待状态下的串口数据积压

当前serial_read只有在WaitForReceive状态下才会处理数据,如果在Idle状态下串口有残留数据(比如从站延迟响应的旧帧),这些数据会留在串口缓冲区里,下次进入WaitForReceive时可能干扰新的接收,甚至直接覆盖新数据。

建议在serial_read开头增加缓冲区清理逻辑:

void MainWindow::serial_read()
{
    QByteArray read = serial->readAll();
    if(comm.mainState != CommMaster::WaitForReceive){
        qDebug()<<"Discard stale data in non-wait state:"<<read;
        return;
    }
    // 后续状态机逻辑...
}

4. WaitForComplete状态的冗余计算

这个问题虽然不致命,但可以让代码更严谨:

int length=receive.length()+read.length();
receive.append(read);
if(length>=expectReceiveLength){

可以简化为直接判断追加后的长度:

receive.append(read);
if(receive.length()>=expectReceiveLength){

修复后的核心代码片段

修正后的serial_read

void MainWindow::serial_read()
{
    QByteArray read = serial->readAll();
    if(comm.mainState != CommMaster::WaitForReceive){
        qDebug()<<"Discard stale data in non-wait state:"<<read;
        return;
    }

    switch(comm.subState){
    case CommMaster::WaitForHead:{
        int headIdx = read.indexOf(expectReceiveHead);
        if(headIdx >= 0){
            QByteArray remaining = read.mid(headIdx);
            if(remaining.length() >= expectReceiveLength){
                handleReceive(remaining.left(expectReceiveLength).data(), expectReceiveLength);
                receive.clear();
                comm.mainState = CommMaster::Idle;
                comm.subState = CommMaster::WaitForHead;
            }else{
                receive = remaining;
                comm.subState = CommMaster::WaitForComplete; // 关键:切换子状态
            }
        }
        break;
    }
    case CommMaster::WaitForComplete:{
        receive.append(read);
        if(receive.length() >= expectReceiveLength){
            handleReceive(receive.left(expectReceiveLength).data(), expectReceiveLength);
            receive.clear();
            comm.mainState = CommMaster::Idle;
            comm.subState = CommMaster::WaitForHead;
        }
        break;
    }
    default:
        qDebug()<<"Wrong sub state:"<<comm.subState;
    }
}

修正后的超时处理

case CommMaster::WaitForReceive:
    if(passedTime >= kReceiveTimeout){
        receive.append(serial->readAll());
        qDebug()<<"Receive timeout, receive buffer content:"<<receive;
        comm.mainState = CommMaster::Idle;
        comm.subState = CommMaster::WaitForHead;
        receive.clear();
    }
    break;

这些修复应该能解决你遇到的接收丢包问题,尤其是状态切换遗漏这个核心错误,直接导致了部分帧无法被正确拼接处理。

内容的提问来源于stack exchange,提问作者Jackoo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 17:37:44