【QT+HJ212】02:数据解析

前言

HJ/T212是由国家环保行业制定的数据传输标准协议。
目前广泛使用的是HJ/T212-2005和HJ/T212-2017通信协议。
通信方式包括RS232、RS485、GPRS、TCP/IP等。

学习资料

资料下载地址:https://download.csdn.net/download/qq_37373742/85747016
QT+HJ212专题学习地址

程序预览

程序支持HJ212-2017协议/HJ212-2005协议
程序一:通过串口通讯方式实现上位机和现场机的数据交互,下图左边上位机右边是现场机
【QT+HJ212】02:数据解析_第1张图片
程序二:通过TCP通讯方式实现上位机和现场机的数据交互,下图左边上位机右边是现场机
【QT+HJ212】02:数据解析_第2张图片

学习笔记

上位机程序逻辑【QT+HJ212】02:数据解析_第3张图片现场机程序逻辑
【QT+HJ212】02:数据解析_第4张图片
函数类含义【QT+HJ212】02:数据解析_第5张图片

关键程序代码

数据解析

数据帧解包最外层接口
1.判断是否数据帧完整
2.判断CRC校验是否通过、是否包含QN命令号字段
3.判断本次命令请求是否有错误 返回QnRtn
4.判断本次命令执行是否有错误 返回ExeRtn

//HJ/T212标准解包
int UserProtocol::protocolUnpack(const QString &buffer)
{
    bool ok;
    Frame.clear();
    Frame = frameCheck(buffer,ok);	//判断是否数据帧完整 
    if(ok) {
        if(frameUnpack(Frame)) {	//判断CRC校验是否通过、是否包含QN命令号字段
            if(packetUnpack(framePacket())) {           //判断请求结果
                if(contentUnpack(packetCP(),packetCN())) {  //判断执行结果
                    ok = true;
                }
            }
        }
    }
    if(ok) {
        return packetCN();
    } else {
        return -1;
    }
}

1.数据帧检测 判断是否为完整数据

QString UserFrame::frameCheck(const QString &buffer,bool &ok)
{
    QString frame;
    ok = false;
    frame.clear();
    if(!buffer.isEmpty()) {
        int head = buffer.indexOf("##",0,Qt::CaseSensitive);
        int tail = buffer.indexOf("\r\n",0,Qt::CaseSensitive);
        if((head != -1)&&(tail != -1)) {
            frame = buffer.mid(head,tail+2-head);
            ok = true;
        }
    }
    return frame;
}

1.1字段解析 从数据帧中找出对应字段的内容

QString UserPacket::fieldExtract(const QString &packet,const QString &field,bool *ok)
{
    QString value;
    *ok = false;
    int length = field.length() + 1;
    int ps = packet.indexOf(field+"=",0);
    int pe = packet.indexOf(";",ps+length);
    if(field == "QN" && packet.contains("CN=9013"))
        pe = packet.indexOf("&&",ps+length);
    if((ps != -1) && (pe != -1)) {
        ps += length;
        value = packet.mid(ps,pe-ps);
        *ok = true;
    }
    return value;
}

1.1.1使用方法:如查找SystemTime字段的内容

数据帧:
QString frame = "##0095ST=31;CN=1011;PW=123456;MN=399435XDLXG028;CP=&&QN=20220414010426069;SystemTime=20220414010003&&0780";
bool ok;
QString systemTime = fieldExtract(frame,"SystemTime",&ok);

2.数据帧检测 判断CRC校验是否通过、是否包含QN命令号字段
下面的req即9011命令号的QnRtn应答错误码 可以参考文章
【QT+HJ212】01:协议分析

bool UserFrame::frameUnpack(const QString &frame)
{
    bool ok = false;
    int req = -1;
    if(!frame.isEmpty()) {
        bool tmp;
        bool QNOK;
        Length = frame.mid(2,4).toInt(&tmp,10);
        setPacketQN(fieldExtract(frame,"QN",&QNOK));
        if(tmp && (Length+12 == frame.length())) {
            Parity = frame.mid(Length+6,4).toInt(&tmp,16);  //CRC16
            Packet = frame.mid(6,Length);                   //数据帧
            if(tmp && (Parity == packetVerifyCalculate(Packet))) {
                ok = true;
            } else {
                req = 9;
            }
        }
        else {
            req = 9;
        }
    }
    if(device()==NULL) {setRequest(req);} else {setRequest(req); device()->setRequest(req);}
    return ok;
}

3.判断本次命令请求是否有错误 返回QnRtn
HJ212-2017的9011指令通过QnRtn返回应答结果对应的错误码
【QT+HJ212】02:数据解析_第6张图片

bool UserPacket::packetUnpack(QString packet)
{
    QString mn,pw,st;
    int ver,request;
    bool dir;
    //获取报文解包所需基本信息
    if(device()==NULL) {
        mn = deviceMN();
        pw = devicePW();
        st = deviceST();
        ver = version();
        CN = -1;
        request = -1;
        if(direction()==Upper) {dir = true;} else {dir = false;}
    } else {
        mn = device()->deviceMN();
        pw = device()->devicePW();
        st = device()->deviceST();
        ver = device()->version();
        device()->setCommand(-1);
        request = -1;
        if(device()->direction()==UserDevice::Upper) {dir = true;} else {dir = false;}
    }
    Flag = 0;
    bool state = false;
    //报文解包
    if(!packet.isEmpty()) {
        bool ok;
        if(ver==0) { //HJ/T212-2005标准
            if(dir) { //服务器端
                //ST提取
                if(request == -1) {
                    ST = fieldExtract(packet,"ST",&ok);
                    if(!ok) {request = 5;}
                }
                //CN提取
                if(request == -1) {
                    CN = fieldExtract(packet,"CN",&ok).toInt();
                    if(!ok) {request = 9;}
                }
                if(request == -1) {
                    if(CN < 9000) {
                        if(ST != st) {request = 5;}
                    } else {
                        if(ST != "91") {request = 5;}
                    }
                }
                if(CN<2000 || CN>=3000) {
                    //QN提取
                    if(request == -1) {
                        QN = fieldExtract(packet,"QN",&ok);
                        if(!ok || (ok && QN.length()!=17)) {request = 8;}
                    }
                }
                //PW提取
                if(request == -1) {
                    PW = fieldExtract(packet,"PW",&ok);
                    if(!ok || (ok && PW.length()!=6) || (ok && PW.length()==6 && PW!=pw)) {request = 3;}
                }
                //MN提取
                if(request == -1) {
                    MN = fieldExtract(packet,"MN",&ok);
                    //if(!ok || (ok && MN.length()!=14) || (ok && MN.length()==14 && MN!=mn)) {request = 4;}
                    if(!ok || MN!=mn) {request = 4;}
                }
                if(CN==2072 || CN==9011) {
                    //Flag提取
                    if(request == -1) {
                        Flag = fieldExtract(packet,"Flag",&ok).toInt();
                        if(!ok || (ok && (Flag>>2)!=ver)) {request = 6;}
                    }
                }
                //CP提取
                if(request == -1) {
                    int ps = packet.indexOf("CP=&&",0);
                    int pe = packet.indexOf("&&",ps+5);
                    if((ps != -1) && (pe != -1)) {
                        pe += 2;
                        CP = packet.mid(ps,pe-ps);
                    } else {request = 100;}
                }
                if(request == -1 && (Flag&0x01)==0x01) {request =1;}
                //qDebug() << "request" << request << Flag << ((Flag&0x01)==0x01);
                if(device()!=NULL) {
                    setRequest(request);
                    device()->setRequest(request);
                }
                if(request==-1 || request==1) {state = true;}else {state =  false;}
            } else { //数采仪端
                //QN提取
                if(request == -1) {
                    QN = fieldExtract(packet,"QN",&ok);
                    if(!ok || (ok && QN.length()!=17)) {request = 8;}
                }
                //ST提取
                if(request == -1) {
                    ST = fieldExtract(packet,"ST",&ok);
                    if(!ok) {request = 5;}
                }
                //CN提取
                if(request == -1) {
                    CN = fieldExtract(packet,"CN",&ok).toInt();
                    if(!ok) {request = 9;}
                }
                if(request == -1) {
                    if(CN < 9000) {
                        if(ST != st) {request = 5;}
                    } else {
                        if(ST != "91") {request = 5;}
                    }
                }
                if(CN!=9014) {
                    //PW提取
                    if(request == -1) {
                        PW = fieldExtract(packet,"PW",&ok);
                        if(!ok || (ok && PW.length()!=6) || (ok && PW.length()==6 && PW!=pw)) {request = 3;}
                    }
                    //MN提取
                    if(request == -1) {
                        MN = fieldExtract(packet,"MN",&ok);
                        //if(!ok || (ok && MN.length()!=14) || (ok && MN.length()==14 && MN!=mn)) {request = 4;}
                        if(!ok || MN!=mn) {request = 4;}
                    }
                }
                if(CN!=9014 && CN!=2012 && CN!=2022) {
                    //Flag提取
                    if(request == -1) {
                        Flag = fieldExtract(packet,"Flag",&ok).toInt();
                        if(!ok || ((Flag>>2)!=ver)) {request = 6;}
                    }
                }
                //CP提取
                if(request == -1) {
                    int ps = packet.indexOf("CP=&&",0);
                    int pe = packet.indexOf("&&",ps+5);
                    if((ps != -1) && (pe != -1)) {
                        pe += 2;
                        CP = packet.mid(ps,pe-ps);
                    } else {request = 100;}
                }
                if(request == -1 && (Flag&0x01)==0x01) {request =1;}
                //qDebug() << "request" << request << Flag << ((Flag&0x01)==0x01);
                if(device()!=NULL) {
                    setRequest(request);
                    device()->setRequest(request);
                }
                if(request==-1 || request==1) {state = true;}else {state =  false;}
            }
        } else if(ver==1) { //HJ/T212-2016标准
            //QN提取
            if(request == -1) {
                QN = fieldExtract(packet,"QN",&ok);
                if(!ok || (ok && QN.length()!=17)) {request = 8;}
            }
            //ST提取
            if(request == -1) {
                ST = fieldExtract(packet,"ST",&ok);
                if(!ok) {request = 5;}
            }
            //CN提取
            if(request == -1) {
                CN = fieldExtract(packet,"CN",&ok).toInt();
                if(!ok) {request = 8;}
            }
            if(request == -1) {
                if(CN < 9000) {
                    if(ST != deviceST()) {request = 5;}
                } else {
                    if(ST != "91") {request = 5;}
                }
            }
            //PW提取
            if(request == -1) {
                PW = fieldExtract(packet,"PW",&ok);
                if(!ok || (ok && PW.length()!=6) || (ok && PW.length()==6 && PW!=pw)) {request = 3;}
            }
            //MN提取
            if(request == -1) {
                MN = fieldExtract(packet,"MN",&ok);
                //if(!ok || (ok && MN.length()!=24) || (ok && MN.length()==24 && MN!=mn)) {request = 4;}
                if(!ok || MN!=mn) {request = 4;}
            }
            //Flag提取
            if(request == -1) {
                Flag = fieldExtract(packet,"Flag",&ok).toInt();
                if(!ok || (ok && (Flag>>2)!=ver)) {request = 6;}
            }
            //PNUM提取
            if((request == -1)&&((Flag&0x02)==0x02)) {
                PNUM = fieldExtract(packet,"PNUM",&ok).toInt();
                if(!ok) {request = 100;}
            }
            //PNO提取
            if((request == -1)&&((Flag&0x02)==0x02)) {
                PNO = fieldExtract(packet,"PNO",&ok).toInt();
                if(!ok) {request = 100;}
            }
            if((request == -1) && (PNUM < PNO)) {request = 100;}
            //CP提取
            if(request == -1) {
                int ps = packet.indexOf("CP=&&",0);
                int pe = packet.indexOf("&&",ps+5);
                if((ps != -1) && (pe != -1)) {
                    pe += 2;
                    CP = packet.mid(ps,pe-ps);
                } else {request = 100;}
            }
            if(request == -1 && (Flag&0x01)==0x01) {request =1;}
            //qDebug() << "request" << request << Flag << ((Flag&0x01)==0x01);
            if(device()!=NULL) {
                setRequest(request);
                device()->setRequest(request);
            }
            if(request==-1 || request==1) {state = true;}else {state =  false;}
        }
    }
    return state;
}

4.判断本次命令执行是否有错误 返回ExeRtn
上位机和现场机有不同的解包方式
HJ212-2017的9012指令通过ExeRtn返回执行结果对应的错误码
【QT+HJ212】02:数据解析_第7张图片

报文内容解包
bool UserContent::contentUnpack(const QString &content,int cn)
{
    //qDebug() << "contentUnpack " << content;
    bool ok = false;
    if(DeviceDir==Upper) {      //上位机解包
        switch(cn) {
        case 1011 : ok = timeSetAndQueryUnpack(content); break;
        case 1013 : ok = emptyContentUnpack(content); break;
        case 1061 : ok = realCycleSetAndQueryUnpack(content); break;
        case 1063 : ok = minCycleSetAndQueryUnpack(content); break;
        case 2011 : ok = realDataUploadUnpack(content); break;
        case 2021 : ok = manageFacilityStatusUploadUnpack(content); break;
        case 2031 : ok = dayDataUploadUnpack(content); break;
        case 2041 : ok = manageFacilityTimeUploadUnpack(content); break;
        case 2051 : ok = minuteDataUploadUnpack(content); break;
        case 2061 : ok = hourDataUploadUnpack(content); break;
        case 2081 : ok = restartTimeUploadUnpack(content); break;
        case 3019 : ok = analyzerSamplingCycleUnpack(content); break;
        case 3020 : ok = analyzerSamplingStopTimeRespondUnpack(content); break;
        case 3021 : ok = analyzerIdentifierUploadUnpack(content); break;
        case 3022 : ok = informationUploadUnpack(content); break;
        case 9011 : ok = requestRespondUnpack(content); break;
        case 9012 : ok = resultRespondUnpack(content); break;
        }
    } else if(DeviceDir==Lower) {  //现场机解包
        switch(cn) {
        case 1000 : ok = overTimeAndReCountSetUnpack(content); break;
        case 1011 : ok = emptyContentUnpack(content); break;
        case 1012 : ok = timeSetAndQueryUnpack(content); break;
        case 1013 : ok = timeCalibrationRespondUnpack(content); break;
        case 1061 : ok = emptyContentUnpack(content); break;
        case 1062 : ok = realCycleSetAndQueryUnpack(content); break;
        case 1063 : ok = emptyContentUnpack(content); break;
        case 1064 : ok = minCycleSetAndQueryUnpack(content); break;
        case 1072 : ok = passwordSetUnpack(content); break;
        case 2011 : ok = emptyContentUnpack(content); if(ok) Device->setRealUpload(true); break;
        case 2012 : ok = emptyContentUnpack(content); if(ok) Device->setRealUpload(false); break;
        case 2021 : ok = emptyContentUnpack(content); if(ok) Device->setPollutionControlUpload(true);break;
        case 2022 : ok = emptyContentUnpack(content); if(ok) Device->setPollutionControlUpload(false);break;
        case 2031 : ok = historyRequestUnpack(content); break;
        case 2041 : ok = historyRequestUnpack(content); break;
        case 2051 : ok = historyRequestUnpack(content); break;
        case 2061 : ok = historyRequestUnpack(content); break;
        case 3011 : ok = analyzerRequestUnpack(content); break;
        case 3012 : ok = analyzerRequestUnpack(content); break;
        case 3013 : ok = analyzerRequestUnpack(content); break;
        case 3014 : ok = analyzerRequestUnpack(content); break;
        case 3015 : ok = analyzerRequestUnpack(content); break;
        case 3016 : ok = analyzerRequestUnpack(content); break;
        case 3017 : ok = analyzerTimeCalibrationRequestUnpack(content); break;
        case 3018 : ok = analyzerSamplingCycleUnpack(content); break;
        case 3019 : ok = analyzerRequestUnpack(content); break;
        case 3020 : ok = analyzerRequestUnpack(content); break;
        case 3021 : ok = analyzerRequestUnpack(content); break;
        case 3022 : ok = informationRequestUnpack(content); break;
        case 9013 : ok = true; Result = 0; break;
        case 9014 : ok = emptyContentUnpack(content); break;
        }
    }
    return ok;
}

4.1举例说明:
【QT+HJ212】02:数据解析_第8张图片
上位机接受到现场机1011命令回复时,会进入函数timeSetAndQueryUnpack
执行成功返回1,执行失败返回3

//数采仪时间设置/查询解包(1012/1011)
bool UserContent::timeSetAndQueryUnpack(const QString &content)
{
    int version,result;
    QDateTime systemTime;
    if(Device==NULL) {
        version = Version;
    } else {
        version = Device->version();
    }
    bool state = false;
    if(!content.isEmpty()) {
        if(version==0 || version==1) { //HJ/212-2005或HJ/212-2016标准
            result = 1;
            bool ok;
            QString time = fieldExtract(content,"SystemTime",&ok);
            if(ok) {
                systemTime = datetimeExtract(time);
            } else {
                result = 3;
            }
            state = true;
        }
    }
    Result = result;
    SystemTime = systemTime;
    if(Device!=NULL) {
        Device->setResult(result);
        Device->setSystemTime(systemTime);
    }
    return state;
}

数据回复

当接受数据帧通过校验并得到请求结果和执行结果后,程序需要对命令进行相应回复
【QT+HJ212】02:数据解析_第9张图片

上位机响应现场机指令

根据Request和Result的值来执行不同的响应
当Result为1,表示成功执行现场机请求
当Request为1,表示需要响应现场机

void UserDevice::receiveMessage(const QString &msg)
{
    if(DeviceDir==Upper) {
        emit protocolUnpack(msg);       //命令解包 获取result
        qDebug() << "服务器解包: Request" << Request << "Result" << Result << "Command" << Command;

        //请求指令执行
        //qDebug() << __FUNCTION__ << Command << Result;
        if(Result==1) {
            if(Request == 1) {    //判断是否需要回应请求
                bool PNUMOk;
                bool PNOOk;
                bool FlagOk;
                QString PNUM = fieldExtract(msg,"PNUM",&PNUMOk);
                QString PNO = fieldExtract(msg,"PNO",&PNOOk);
                int    Flag = getbit(fieldExtract(msg,"Flag",&FlagOk).toInt(),0);
                if((!PNUMOk && !PNOOk) || (PNUM == PNO  && PNUMOk && PNOOk  ))
                {
                    //if(FlagOk && Flag == 1)
                    //{
                        QString frame;
                        if(Command == 1013 || Command == 2012 || Command == 2022)
                            DevicePackAndSend("",&frame,9013,false,false);     //回复应答ST=91 CN=9014
                        else
                            DevicePackAndSend("",&frame,9014,false,false);     //回复应答ST=91 CN=9014
                    //}
                }
            }
            if(ReceiveCommandHandle()) {    //判断是否需要回应请求
                QString frame;
                DevicePackAndSend("",&frame,Command,needReplyCN(Command),false); //回复不同命令
            }
        } else {
            emit runningLog(QString("接收帧错误,ErrorCode=%1").arg(Result));
        }
    }
}

现场机响应上位机指令
根据Request和Result的值来执行不同的响应
当Result为1,表示成功执行上位机请求
当Request大于0,表示需要响应上位机

//数采仪接收消息
void UserDevice::receiveMessage(const QString &id,const QString &msg)
{
    if(DeviceDir==Lower) {
        emit protocolUnpack(msg);   //解析数据帧
        //不需要回复服务器的应答信号
        if(Command == 9014)
            return;
        qDebug() << "客户端解包: Request" << Request << "Result" << Result << "Command" << Command;

        //发送请求响应
        if(Request>0 || (Version == 0 && (Command == 2012 || Command == 2022))) {
            QString frame;
            if(Command == 1013 || Command == 2012 || Command == 2022)
                emit DevicePackAndSend(id,&frame,9013,false,false);     //回复应答ST=91 CN=9014
            else
                emit DevicePackAndSend(id,&frame,9011,false,false);
        }
        //请求指令执行
        if(Result==0) {
            if(ReceiveCommandHandle()) {
                QString frame;
                emit DevicePackAndSend(id,&frame,Command,false,false);      //目前默认回复的数据 需要应答
            }
        }
        //发送执行结果
        if(Result == 1) {
            QString frame;
            emit DevicePackAndSend(id,&frame,9012,false,false);
        }
    }
}

你可能感兴趣的:(QT+HJ212,qt,c++)