基于QtPy (PySide6) 的PLC-HMI工程项目(十五)后续完善和改进:PLC端数据解析的防粘包、残包、废包、拆包

  • "解析下行数据"FC:
python 复制代码
#pos := 2;
/////////////////////////解析区域变量的参数////////////////////
#tmp_areaVar.Area := PEEK(area := #buff_area_var.Area, dbNumber := #buff_area_var.DBNum, byteOffset := #pos);  //帧区域
#pos := #pos + 2;
#tmp_areaVar.DBNum := DWORD_TO_DINT(IN := PEEK_DWORD(area := #buff_area_var.Area, dbNumber := #buff_area_var.DBNum, byteOffset := #pos));  //帧DB号
#pos := #pos + 4;
#tmp_areaVar.Offset := DWORD_TO_DINT(PEEK_DWORD(area := #buff_area_var.Area, dbNumber := #buff_area_var.DBNum, byteOffset := #pos));  //帧字节偏移
#pos := #pos + 4;
#tmp_areaVar.byteCount := DWORD_TO_DINT(PEEK_DWORD(area := #buff_area_var.Area, dbNumber := #buff_area_var.DBNum, byteOffset := #pos));  //帧字节长度
#pos := #pos + 4;
#tmp_areaVar.bit := DWORD_TO_DINT(PEEK_DWORD(area := #buff_area_var.Area, dbNumber := #buff_area_var.DBNum, byteOffset := #pos));  //帧位(bit)
#pos := #pos + 4; //pos目前位于数据区之前
////////////////////////////解析数据内容//////////////////////////
//如果是bool
IF #tmp_areaVar.bit <> 16#FFFFFFFF THEN
    POKE_BOOL(area := #tmp_areaVar.Area,  ////写一个bool位
              dbNumber := #tmp_areaVar.DBNum,
              byteOffset := #tmp_areaVar.Offset,
              bitOffset := DINT_TO_INT(IN := #tmp_areaVar.bit),
              value := PEEK_BOOL(area := #buff_area_var.Area, dbNumber := #buff_area_var.DBNum, byteOffset := #pos, bitOffset := #tmp_areaVar.bit));
    /////////////////////////////////如果不是bool//////////////////////////////    
ELSE    //写块数据
    POKE_BLK(area_src := #buff_area_var.Area,
             dbNumber_src := #buff_area_var.DBNum,
             byteOffset_src := #pos,
             area_dest := #tmp_areaVar.Area,
             dbNumber_dest := #tmp_areaVar.DBNum,
             byteOffset_dest := #tmp_areaVar.Offset,
             count := #tmp_areaVar.byteCount);
END_IF;

"处理下行数据"FC:

python 复制代码
/////////////// 追加数据到buff区的尾部 ///////////

//缓冲区溢出检查
IF #pos_tail + #this_len > 2000 THEN  //缓冲区最大长度
    #pos_tail := 0;   // 溢出则清空, 防止越界写
    RETURN;
END_IF;

//把最新数据内容加到缓冲区的最后
POKE_BLK(area_src := #data_areaVar.Area,
         dbNumber_src := #data_areaVar.DBNum,
         byteOffset_src := 0,
         area_dest := #buf_areaVar.Area,
         dbNumber_dest := #buf_areaVar.DBNum,
         byteOffset_dest := #pos_tail,
         count := #this_len);
#pos_tail := #pos_tail + #this_len;
#pos_head := 0;


/////////// 循环处理数据直至最后一行/////////
WHILE #pos_tail > 0 DO
    //IF #pos_tail < 4 THEN
    //RETURN;
    //END_IF;
    //#pos_tail := 0;
    //RETURN;
    //"b_tmp" := PEEK(area := #buf_areaVar.Area, dbNumber := #buf_areaVar.DBNum, byteOffset := #pos_head);
    
    
    ///////////// 搜索帧头////////////
    #pos_head := 0;
    WHILE #pos_head + 1 < #pos_tail DO
        IF (PEEK(area := #buf_areaVar.Area, dbNumber := #buf_areaVar.DBNum, byteOffset := #pos_head) = #frameHeader1) AND
            (PEEK(area := #buf_areaVar.Area, dbNumber := #buf_areaVar.DBNum, byteOffset := #pos_head + 1) = #frameHeader2) THEN
            #tmp_status := 1;  //找到了帧头,可能的最后一个是#pos_tail-2
            //"char_TMP" := 'a';
            EXIT;   // 找到帧头立即退出
        ELSE
            //"char_TMP" := 'b';
            #pos_head := #pos_head + 1;  //没找到,可能的最后一个是#pos_tail-1
        END_IF;
    END_WHILE;
    
    /////////没找到帧头, 丢弃前面的垃圾///////////////////
    IF #pos_head = #pos_tail - 1 THEN  //如果找到了帧头,可能的最后一个是#pos_tail-2,所以走到了这里一定是没找到
        //如果最后一行是帧头,保留,等待下一帧凑齐第二个帧头
        IF PEEK(area := #buf_areaVar.Area, dbNumber := #buf_areaVar.DBNum, byteOffset := #pos_head) = #frameHeader1 THEN
          //  POKE_BLK(area_src := #buf_areaVar.Area,
            //         dbNumber_src := #buf_areaVar.DBNum,
              //       byteOffset_src := #pos_head,
                //     area_dest := #buf_areaVar.Area,
                  //   dbNumber_dest := #buf_areaVar.DBNum,
                    // byteOffset_dest := 0,
                     //count := 1);
            #pos_tail := 1;
            //RETURN;
        ELSE
            #pos_tail := 0;  //丢弃所有数据
        END_IF;
        RETURN;
    END_IF;
    
    // 查找到的帧头不在首位: 移到buf[0], 丢弃前面的无效数据
    IF #pos_head > 0 THEN
        POKE_BLK(area_src := #buf_areaVar.Area,
                 dbNumber_src := #buf_areaVar.DBNum,
                 byteOffset_src := #pos_head,
                 area_dest := #buf_areaVar.Area,
                 dbNumber_dest := #buf_areaVar.DBNum,
                 byteOffset_dest := 0,
                 count := #pos_tail - #pos_head);
        #pos_tail := #pos_tail - #pos_head;
        #pos_head := 0;
    END_IF;
    
    
    ///////////// 心跳帧////////////
    IF #pos_tail >= #pos_head + 3 AND PEEK(area := #buf_areaVar.Area, dbNumber := #buf_areaVar.DBNum, byteOffset := #pos_head + 2) = #frameTail1 AND
        PEEK(area := #buf_areaVar.Area, dbNumber := #buf_areaVar.DBNum, byteOffset := #pos_head + 3) = #frameTail2 THEN
        POKE_BLK(area_src := #buf_areaVar.Area,
                 dbNumber_src := #buf_areaVar.DBNum,
                 byteOffset_src := #pos_head + 4,
                 area_dest := #buf_areaVar.Area,
                 dbNumber_dest := #buf_areaVar.DBNum,
                 byteOffset_dest := 0,
                 count := #pos_tail - 4);
        #pos_tail := #pos_tail - 4;
        #pos_head := 0;
        CONTINUE;
    END_IF;
    
    IF #pos_tail < #pos_head + 16 THEN   // 半包, 等待补齐,当超过16个字节就可以读出本帧携带的数据长度
        RETURN;
    END_IF;
    
    //"int_tmp" := BYTE_TO_INT(PEEK(area := #buf_areaVar.Area, dbNumber := #buf_areaVar.DBNum,
                                                          // byteOffset := #pos_head + 2));
    "byte_tmp" := PEEK(area := #buf_areaVar.Area, dbNumber := #buf_areaVar.DBNum,
                       byteOffset := #pos_head+2);
    
    //////////////// 本帧携带的有效数据长度/////////////////////////
    IF (BYTE_TO_INT(PEEK(area := #buf_areaVar.Area, dbNumber := #buf_areaVar.DBNum,
                                             byteOffset := #pos_head + 2)) >= 129) AND
      (BYTE_TO_INT(PEEK(area := #buf_areaVar.Area, dbNumber := #buf_areaVar.DBNum,
                                     byteOffset := #pos_head + 2)) <= 132) THEN  //监测Array参数是否16#81--16#84
        #byte_count := DWORD_TO_DINT(PEEK_DWORD(area := #buf_areaVar.Area, dbNumber := #buf_areaVar.DBNum,
                                                                             byteOffset := #pos_head + 12));
        //"int_tmp" := #byte_count;
        //#byte_count := DWORD_TO_DINT(PEEK_DWORD(area := #buf_areaVar.Area, dbNumber := #buf_areaVar.DBNum,
                                                                             //byteOffset := #pos_head + 10));
       // "char_TMP" := 'c';
        
    ELSE
        POKE_BLK(area_src := #buf_areaVar.Area,
                 dbNumber_src := #buf_areaVar.DBNum,
                 byteOffset_src := 2,
                 area_dest := #buf_areaVar.Area,
                 dbNumber_dest := #buf_areaVar.DBNum,
                 byteOffset_dest := 0,
                 count := #pos_tail - 2);      //废帧,把帧头去掉后重新检测
        #pos_tail := #pos_tail - 2;
        CONTINUE;
    END_IF;
    
    // 帧总长=24+N
    IF #pos_tail < 24 + #byte_count THEN
        RETURN; // 数据不齐, 等待补齐
    END_IF;
    //////////////////////////// CRC计算 [修复6: 范围到23+N, 含CRC前一字节] //////////////////////////
    #wCRC := 16#FFFF;
    FOR #i := 0 TO #pos_head + 21 + #byte_count DO  // 对除了最后2位之外的所有字节做CEC
        #bTemp := PEEK(area := #buf_areaVar.Area,
                       byteOffset := #i,
                       dbNumber := #buf_areaVar.DBNum);
        #wTemp := #wCRC XOR BYTE_TO_WORD(IN := #bTemp);
        #wTemp := #wTemp AND 16#00FF;
        #nIndex := WORD_TO_INT(IN := #wTemp);
        #wCRC := SHR(IN := #wCRC, N := 8) XOR #DB_CRC_TABLE[#nIndex];
    END_FOR;
    
    ///////////// CRC校验 [失败则丢弃帧头以及area] ////////////
    IF #wCRC <> PEEK_WORD(area := #buf_areaVar.Area,
                              byteOffset := #pos_head + 22 + #byte_count,
                              dbNumber := #buf_areaVar.DBNum) THEN
        POKE_BLK(area_src := #buf_areaVar.Area,
                 dbNumber_src := #buf_areaVar.DBNum,
                 byteOffset_src := 4,
                 area_dest := #buf_areaVar.Area,
                 dbNumber_dest := #buf_areaVar.DBNum,
                 byteOffset_dest := 0,
                 count := #pos_tail - 4);
        #pos_tail := #pos_tail - 4;
        CONTINUE;
    END_IF;
    
    #done := TRUE;
    
    //////////////////////////// 解析数据内容 //////////////////////////
    "_build_AreaVal"(Area := #buf_areaVar.Area,
                     DBNum := #buf_areaVar.DBNum,
                     Offset := 0,
                     byteCount := 0,
                     destAreaVar := #tmp_areavar);
    
    "解析下行数据"(#tmp_areavar);
    
    //////////////////////////// 移除已处理的帧//////////////////////////
    #pos_head := 24 + #byte_count;
    IF #pos_head < #pos_tail THEN
        POKE_BLK(area_src := #buf_areaVar.Area,
                 dbNumber_src := #buf_areaVar.DBNum,
                 byteOffset_src := #pos_head,
                 area_dest := #buf_areaVar.Area,
                 dbNumber_dest := #buf_areaVar.DBNum,
                 byteOffset_dest := 0,
                 count := #pos_tail - #pos_head);
    END_IF;
    #pos_tail := #pos_tail - #pos_head;
    #pos_head := 0;
END_WHILE;





// 查找帧尾
//FOR #i:=#pos_head TO #pos_tail-2 DO
//WHILE #pos_head < #pos_tail-2 DO
//IF PEEK(area := #buf_areaVar.Area, dbNumber := #buf_areaVar.DBNum, byteOffset := #i) = #frameTail1 AND
// PEEK(area := #buf_areaVar.Area, dbNumber := #buf_areaVar.DBNum, byteOffset := #i +1) = #frameTail2 THEN
#tmp_status := 2;  //找到了帧尾
//EXIT;   // 找到帧尾立即退出
//END_IF;
//#pos_head := #pos_head + 1;
//END_WHILE;
//END_FOR;
//IF #i=#pos_tail-2 THEN
//RETURN;
相关推荐
小黄蚁1 小时前
LVGL学习笔记(六)
笔记·学习
zyf1044162 小时前
暑期实践日志 Day33:整体复盘核查视频,完善剪辑成果
学习·计算机网络·剪辑·暑期实践·课题任务
01_ice2 小时前
前端学习css
前端·css·学习
Wang's Blog2 小时前
PostgreSQL笔记49:向量检索核心算法、索引调优与过滤策略深度解析
笔记·算法·postgresql
疯狂打码的少年2 小时前
【数据结构】交换类排序:冒泡与快速排序
数据结构·笔记·算法·排序算法
chnyi6_ya2 小时前
VideoHallu 论文阅读笔记
论文阅读·笔记
Wang's Blog3 小时前
PostgreSQL笔记51: 基于 pgvector 构建企业级智能问答系统
数据库·笔记·postgresql
for_ever_love__3 小时前
PySpark学习: 数据输出
开发语言·python·学习·spark
自小吃多3 小时前
Capture软件原理图添加差分属性笔记
笔记·嵌入式硬件