基于C++和Qt开发IEC104规约主站调试软件(第六章)终

基于C++和Qt开发IEC104规约主站调试软件需要综合协议规范、多线程编程和GUI框架知识以及IEC104通讯规约相关知识,以下是分步骤实现方案:
最终成果
IEC104通讯规约知识学习
上一章内容回顾:IEC104主站调试软件发送总召、时钟同步

第六章:IEC104主站调试软件功能按钮实现(报文解析、报文导出、报文滚动、清空报文)

1.报文解析:输入报文点击报文解析按钮
代码示例:

 QByteArray frame = ui->textEdit->toPlainText().trimmed().toUtf8();
    if(frame.isEmpty()){
        MessageReminder("请在报文列表中输入有效报文","确认","取消");
        return;
    }
    QByteArray hexFrame = QByteArray::fromHex(frame);
    int sz = hexFrame.length();
    if( sz<6 || (hexFrame[0]!= 0x68 && (hexFrame[1]< 0x04 || hexFrame[1]>0xFF))){
        showMessage("无效报文",-1);
        return;
    }
    if(sz>6){
        hexFrame = hexFrame.mid(6,sz-6);
        uint8_t* msg = (uint8_t*)hexFrame.data();
        CS104_Connection cn = CS104_Connection_create("127.0.0.1", 2404);
        CS101_AppLayerParameters alParams = CS104_Connection_getAppLayerParameters(cn);
        CS101_ASDU asdu = CS101_ASDU_createFromBuffer(alParams, msg, sz - 6);
        IEC60870_5_TypeID  typeId = CS101_ASDU_getTypeID(asdu);
        int nums =  CS101_ASDU_getNumberOfElements(asdu);
        CS101_CauseOfTransmission cause = CS101_ASDU_getCOT(asdu);
        CP56Time2a newTime;
        QString time;
        QualityDescriptor qu;
        QString quStr;
        QString causeStr = m_causeHash.value(cause);
        if(causeStr.isEmpty())
        causeStr=QString("未知(%1)").arg(cause);
        QString typeStr;
        int address;
        if (typeId == C_TS_TA_1)
        qInfo()<<"test command with timestamp";
        else if (typeId== C_IC_NA_1) {//总召唤
            if (cause== CS101_COT_ACTIVATION) //激活确认
                showMessage("主站总召激活确认请求报文",-1);
            if (cause== CS101_COT_ACTIVATION_CON) //激活确认
                showMessage("从站响应总召激活确认报文",-1);
            else if (cause == CS101_COT_ACTIVATION_TERMINATION) //激活停止
                showMessage("总召结束激活停止报文",-1);
        }
        else if(typeId ==M_SP_NA_1){ //单点遥信(不带时标)
            bool value;
            time = "无";
            typeStr ="无时标单点遥信";
            for (int i = 0; i < nums; i++) {
                SinglePointInformation io =(SinglePointInformation) CS101_ASDU_getElement(asdu, i);
                if(io==nullptr){
                showMessage(QString("解析%1数据错误,序号=%2").arg(typeStr).arg(i),-1);
                continue;
                }
                qu = SinglePointInformation_getQuality(io);
                quStr = m_quMap.value(qu);
                if(quStr.isEmpty())
                    quStr=QString("异常(0x%1)").arg(qu,2,16,QLatin1Char('0'));
                address = InformationObject_getObjectAddress((InformationObject) io);
                value = SinglePointInformation_getValue(io);
                SinglePointInformation_destroy(io);
                showMessage(QString("%1,原因:%2,地址:%3,值:%4,品质:%5,时间:%6").arg(typeStr).arg(causeStr).arg(address).arg(value).arg(quStr).arg(time),-1);
            }
        }
         else if(typeId ==M_ME_TF_1 || typeId==M_ME_TC_1){ //短浮点遥测带时标
            typeStr ="长时标短浮点遥测";
            float value;
            for (int i = 0; i < nums; i++) {
                MeasuredValueShortWithCP56Time2a iotm =(MeasuredValueShortWithCP56Time2a) CS101_ASDU_getElement(asdu, i);
                MeasuredValueShort io =(MeasuredValueShort) CS101_ASDU_getElement(asdu, i);
                if(io==nullptr){
                    showMessage(QString("解析%1数据错误,序号=%2").arg(typeStr).arg(i),-1);
                    continue;
                }
                qu = MeasuredValueShort_getQuality(io);
                quStr = m_quMap.value(qu);
                if(quStr.isEmpty())
                    quStr=QString("异常(0x%1)").arg(qu,2,16,QLatin1Char('0'));
                address = InformationObject_getObjectAddress((InformationObject) io);
                value = MeasuredValueShort_getValue(io);
                newTime = MeasuredValueShortWithCP56Time2a_getTimestamp(iotm);
                if( typeId==M_ME_TC_1){
                    typeStr ="短时标短浮点遥测";
                    time = QString("%5:%6.%7").arg(CP56Time2a_getMinute(newTime),2,10,QLatin1Char('0'))
                               .arg(CP56Time2a_getSecond(newTime),2,10,QLatin1Char('0'))
                               .arg(CP56Time2a_getMillisecond(newTime),3,10,QLatin1Char('0'));
                }
                else
                    time = QString("%1-%2-%3 %4:%5:%6.%7").arg(CP56Time2a_getYear(newTime) + 2000)
                               .arg(CP56Time2a_getMonth(newTime),2,10,QLatin1Char('0'))
                               .arg(CP56Time2a_getDayOfMonth(newTime),2,10,QLatin1Char('0'))
                               .arg(CP56Time2a_getHour(newTime),2,10,QLatin1Char('0'))
                               .arg(CP56Time2a_getMinute(newTime),2,10,QLatin1Char('0'))
                               .arg(CP56Time2a_getSecond(newTime),2,10,QLatin1Char('0'))
                               .arg(CP56Time2a_getMillisecond(newTime),3,10,QLatin1Char('0'));
                MeasuredValueShort_destroy(io);
                MeasuredValueShortWithCP56Time2a_destroy(iotm);
                showMessage(QString("%1,原因:%2,地址:%3,值:%4,品质:%5,时间:%6").arg(typeStr).arg(causeStr).arg(address).arg(value).arg(quStr).arg(time),-1);
            }
        }

运行演示:
基于C++和Qt开发IEC104规约主站调试软件(第六章)终_第1张图片
2.报文导出:点击报文导出按钮,导出报文到本地txt文件
代码示例:

 	QString frame = ui->textEdit->toPlainText();
    if(frame.isEmpty()){
        MessageReminder("报文为空,无法导出","确定","取消");
        return;
    }
    QString filename = QFileDialog::getSaveFileName(this, nullptr, nullptr, "*.txt").toUtf8();
    QFile file(filename);
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
        // 处理错误,例如可以抛出异常或者返回错误标志
        MessageReminder("导出到文件失败,导出文件不存在","确定","取消");
        return;
    }
    QTextStream out(&file);
    out<<frame;
    file.close(); // 关闭文件
    MessageReminder("导出报文完成","确定","取消");

3.报文开始停止滚动:点击开始滚动按钮即报文实时滚动,点击停止滚动按钮,即报文暂停滚动
代码示例:

    if(!m_roll)//停止滚动
        return;
    if(ui->textEdit->toPlainText().length()>50000)
        ui->textEdit->clear();
    ui->textEdit->setTextColor(QColor(Qt::black));
    QString typeStr = "主站:";
    if(type==0){
        typeStr = "主站:";
        ui->textEdit->setTextColor(QColor(Qt::black));
    }
    else if(type==1){
        typeStr ="从站:";
        ui->textEdit->setTextColor(QColor(Qt::blue));
    }
    else
        typeStr.clear();
    ui->textEdit->append(typeStr+msg);
    ui->textEdit->update();

4.清空报文:点击清空报文按钮,清空当前报文显示区
代码示例:

  connect(ui->clearButton,&QPushButton::clicked,[=](){
        ui->textEdit->clear();
    });

总结

本章节主要讲解功能按钮的功能实现和核心代码,结合前五个章节的内容,即可完成IEC104主站调试软件开发应用。关于IEC104主站调试软件开发到此章节正式结束,若有疑问欢迎各位博主咨询。

你可能感兴趣的:(IEC104,c++,qt,python,java,c#,网络协议)