zl程序教程

您现在的位置是:首页 >  工具

当前栏目

qt之接收数据处理方式(补全断包数据)

Qt数据 方式 数据处理 接收 补全
2023-09-27 14:29:08 时间

一、前言

为什么写这个博客呢,因为我发现在qt中不论用到什么通信方式,最后处理数据的方法都是一样的,蓝牙数据接收,串口数据接收,网口数据接收,can数据接收,wifi通信(即网口)数据接收,基本都是一个套路完成的接口函数,然后去处理数据,这次写个模板出来不用每次都去查找以前的程序了,也怪费劲的。

二、环境

qt5.7 mingw windows8

三、正文

各种模板如下,如后期有新型,也会更新出来

第一步 都是通过各种接口函数,把识别到的一包或者连续几包接收打包起来,到一个QByteArray中去,然后在开启一个函数,

void MainWindow::comBatAnalyze(QByteArray &allData)

去传递这个QByteArray(单次接收全部数据)

不带CRC16 MODBUS校验方式:

1.将全部数据转换为16进制字节类型:

优点:数据内容清晰明确,类似于stm32接收数据处理判断。

缺点:数据定长、也可以自行处理申请动态数组,事先判断数据长度

方法:

void MainWindow::comBatAnalyze(QByteArray &allData)
{
    unsigned char rxdata[100];
    int len;
    bool ok;
    memset(rxdata,0,100);//清空数组
    QByteArray dataTemp=allData;
    len=dataTemp.count();
    for(int i=0;i<len/2;i++)
      rxdata[i]=(unsigned char)dataTemp.mid(i*2,2).toInt(&ok,16);
    if(rxdata[0]==0x00&&rxdata[1]==0x00&&rxdata[2]==0xff&&rxdata[3]){
        //...
    }
    //...
}

2.直接判断字符串形式:

优点:数据长度飘忽不定时,没有稳定数据格式,可采用此方式让数据一直累加判断

缺点:写起来麻烦一些,只能判断固定长度数据(因通讯原因而断开的)

方式一:

void MainWindow::comBatAnalyze(QByteArray &allData)
{
    QByteArray dataTemp;
    int len;
    int start;
    int move;
    bool ok;
    dataTemp=allData;
    while((len=dataTemp.count())>=37*2)
    {
      QCoreApplication::processEvents(QEventLoop::AllEvents, 200);
      move=2;
      start=dataTemp.indexOf("42",0);//搜索42字符,返回搜索到的起始位置
      if(start>=0)
      {
         len=len-start;//将42做为开头,长度随之变化
         dataTemp=dataTemp.right(len);//从右侧裁剪len长度数据保存
         if(len>=37*2)
         {          
         if(dataTemp.mid(0,2)=="42"&&dataTemp.mid(10,2)=="43"&&dataTemp.mid(22,2))//判断开头是否为effeef
           {
               if(dataTemp.mid(36,2)=="58"&&dataTemp.mid(46,2)=="2e"&&dataTemp.mid(52,2)=="5a"&&dataTemp.mid(60,2)=="2e"&&dataTemp.mid(72,2)=="25")//dian
               {
                    //...
                    move=37*2;
                    dataTemp=dataTemp.right(len-move);
                    if((len-move)>37*2){
                        dataTemp.clear();
                        allData.clear();
                    }
               }
               else{
                    dataTemp.clear();
                    break;
               }
             }
             else{
                 dataTemp.clear();
                 break;
             }
         }
         else{
            break;
         }
      }
      else{
         dataTemp.clear();
         break;
      }
    }
    allData=dataTemp;
}

方式二:

 

//处理串口得到的数据
void Sysin::comBatAnalyze(QByteArray &allData)
{
    QByteArray dataTemp;
    int len;
    int start;
    int move;
    dataTemp=allData.toUpper();
//    qDebug()<<dataTemp;
    while((len=dataTemp.count())>=12*2){
      QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
      move=2;
      start=dataTemp.indexOf("508B",0);//搜索508B字符,返回搜索到的起始位置
      if(start>=0){
         len=len-start;//将508B做为开头,长度随之变化
         dataTemp=dataTemp.right(len);//从右侧裁剪len长度数据保存
         if(len>=12*2){//再次判断数据长度
            if(dataTemp.mid(0,4)=="508B"){//判断开头是否为508B
                QString str;
                for(int i=0;i<12;i++){
                    str.append(dataTemp.mid(i*2,2));
                    str.append(" ");
                }
                ui->lineEdit->setText(str);
                //。。。继续处理数据

                move=12*2;
             }
             dataTemp=dataTemp.right(len-move);
         }
         else{
             break;
         }
      }
      else{
         dataTemp.clear();
         break;
      }
    }
    allData=dataTemp;
}

带CRC校验方式:

带crc校验就不能用以上的方式了,因为需要数据的每个字节去分析,方式如下:

方式一:

void MainWindow::comBatAnalyze(QByteArray &allData)
{
    QByteArray dataTemp;
    bool ok;
    int len;
    int start;
    dataTemp=allData;
    while((len=dataTemp.count())>=25*2)
    {
      QCoreApplication::processEvents(QEventLoop::AllEvents);
      start=dataTemp.indexOf("fe0414",0);//返回该字节数组中字节数组fe第一次出现的索引位置,然后从索引位置向前搜索。
      if(start>=0){
         len=len-start;
         dataTemp=dataTemp.right(len);
         if(len>=25*2){
           if(dataTemp.mid(0,6)=="fe0414"){
               dataTemp=dataTemp.left(50);//只处理一组即可,取前25字节
               QCoreApplication::processEvents(QEventLoop::AllEvents);
               QByteArray ccc;//crc用
               quint16 ddd;//crc用
               for(int i=0;i<25;i++){
                   ccc[i]=dataTemp.mid(i*2,2).toInt(&ok,16);
                   if(i==22)ddd=crc16ForModbus(ccc);//获取到前23位crc校验码
               }
               if((ccc[23]==ddd)&&(ccc[24]==(ddd>>8))){//校验crc
                    //...校验通过,处理数据
               }
           }
           dataTemp.clear();
         }
         else{
            break;
         }
      }
      else{
         dataTemp.clear();
         break;
      }
    }
    allData=dataTemp;
}

方式二:

//处理串口得到的数据
void MainWindow::comBatAnalyze(QByteArray &allData)
{
    QByteArray dataTemp;
    bool ok;
    int len=0;
    int start;
    int move;
    static unsigned char ccc[8];
    unsigned char ddd=0;//crc用
    dataTemp=allData;
    while((len=dataTemp.count())>=8*2)
    {
      move=2;
      start=dataTemp.indexOf("ea",0);//搜索ef字符,返回搜索到的起始位置
      if(start>=0)
      {
         len=len-start;//将ef做为开头,长度随之变化
         dataTemp=dataTemp.right(len);//从右侧裁剪len长度数据保存
         if(len>=4*2)
         {
            if(dataTemp.mid(0,4)=="eaea")//判断开头是否为
            {
                //qDebug()<<dataTemp;
                memset(ccc,0,8);
                for(int i=0;i<8;i++){
                   ccc[i]=(unsigned char)dataTemp.mid(i*2,2).toInt(&ok,16);
                   if(i>1&&i<7)ddd+=ccc[i];

                }
                if((ccc[0]==0xEA)&&(ccc[1]==0xEA)&&(ccc[7]==ddd))//校验
                {
                   AIvalue[0]=dataTemp.mid(4,4).toInt(&ok,16);
                   if((ccc[2]>>7)==1)//若最高位符号位为1,则做负数处理
                       AIvalue[0]=AIvalue[0]-0xffff-1;
                    //...继续处理数据
                    //刷新表格和曲线
                   if(start_get){
                    QCustomPlot_Updata(ui->customplot);
                    QTableWidget_Updata(ui->tableWidget);
                   }
                   move=8*2;
                }
             }
             dataTemp=dataTemp.right(len-move);
         }
         else{
            break;
         }
      }
      else{
         dataTemp.clear();
         break;
      }
    }
    allData=dataTemp;
}

CRC方式参考我这个帖子中的代码


20221128增加串口接收数据断包补全

优点:可以补全因通讯原因造成的数据断包

缺点:大量数据时无法使用此方法

原理:在通讯接口接收中断有数据时,建立的定时器开启10ms中断,这时候10ms串口过来的数据会在全局QByteArray数组中补全,10ms之后将数组传入到处理函数中,基本就是全的了,如果不全就适当调增延时时间,大量数据时可能不适用,未测试大数据

方法:

头文件:

    QSerialPort *serial1;//串口句柄
    QByteArray allData1;
    QTimer *timeserial1;

函数文件:

  //初始化部分
  timeserial1=new QTimer(this);
    connect(timeserial1,&QTimer::timeout,[=](){
        timeserial1->stop();
        comBatAnalyze1(allData1);//处理串口得到的数据
    });

//串口中断槽函数
void menu::serialRead1()
{
//    static QByteArray allData;
    timeserial1->start(10);//设置判断10ms内接收完毕数据,在处理
    while (!serial1->atEnd()){
        allData1 += serial1->readAll().toHex();
    }
}
//串口处理函数
void menu::comBatAnalyze1(QByteArray &allData)
{
    QByteArray dataTemp;
    bool ok;
    int len=0;
    int start;
    int move;
    dataTemp=allData.toUpper();
    len=dataTemp.size()/2;
    qDebug()<<dataTemp;
    QByteArray hexdata=QByteArray::fromHex(dataTemp);
    if(hexdata[0]==0x52&&hexdata[1]==0x45&&hexdata[2]==0x53&&hexdata[3]==0x54&&hexdata[4]==0x41&&hexdata[5]==0x52&&hexdata[6]==0x54){
        
    }
    dataTemp.clear();//若没有需求的数据,则清除整个数据内容,重新接收判断
    allData=dataTemp;
}

可以发现,接收全了,只要不是发送特别快连包,就能单次处理,如果连包,可以按照上面第二种方式将数据组合判断帧头处理

//后续更新会继续更新本篇博客

四、结语

记录,分享,查询,让我们成为 搬运工而不只是获取者。