当前位置:首页 > C++ > IEC104 > 正文内容

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

tiandi2025-09-10IEC10428


基于C++和Qt开发IEC104规约主站调试软件需要综合协议规范、多线程编程和GUI框架知识以及IEC104通讯规约相关知识,以下是分步骤实现方案:
开发成果展示: 最终开发成果
IEC104通讯规约知识学习:https://download.csdn.net/download/liangwwe0/90616493?spm=1011.2124.3001.6210
上一章内容回顾: IEC104主站软件界面设计和连接104从站
当从站发送遥信、遥测数据时,程序会调用回调函数(asduReceivedHandler)。


1.解析单点遥信数据(类型标识0x01)

代码示例:

bool MainWindow::asduReceivedHandler(void *parameter, int address, CS101_ASDU asdu){
    if(parameter==nullptr)
        return true;
    MainWindow *that = (MainWindow*)parameter;
    //类型标识
    IEC60870_5_TypeID  typeId = CS101_ASDU_getTypeID(asdu);
    //数据数量
    int nums =  CS101_ASDU_getNumberOfElements(asdu);
    //传输原因
    CS101_CauseOfTransmission cause = CS101_ASDU_getCOT(asdu);
    QString time="无";
    QualityDescriptor qu;
    QString quStr;
    //传输原因转字符串
    QString causeStr = that->m_causeHash.value(cause);
    if(causeStr.isEmpty())
        causeStr=QString("未知(%1)").arg(cause);
    QString typeStr;
    if (typeId== C_IC_NA_1) {//响应总召唤
        if (cause== CS101_COT_ACTIVATION_CON) //激活确认
            emit that->showMessageSignal("总召激活确认",1);
        else if (cause == CS101_COT_ACTIVATION_TERMINATION) //激活停止
            emit that->showMessageSignal("总召结束,激活停止",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){
                emit that->showMessageSignal(QString("解析%1数据错误,序号=%2").arg(typeStr).arg(i),-1);
                continue;
            }
            //数据品质
            qu = SinglePointInformation_getQuality(io);
            quStr = that->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);
            
            QVariantHash vh;
            vh["address"] = address;
            vh["value"] = value ? 1:0;
            vh["type"] = typeStr;
            vh["cause"] = causeStr;
            vh["qu"] = quStr;
            vh["time"] = time;
            //将解析后的数据打印
            that->loadTable(vh);
        }
    }}
2.解析短浮点遥测数据(类型标识0x0E,0x24)

代码示例:

else if(typeId ==M_ME_TF_1 || typeId==M_ME_TC_1){ //短浮点遥测带时标
        CP56Time2a newTime;
        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){
                emit that->showMessageSignal(QString("解析%1数据错误,序号=%2").arg(typeStr).arg(i),-1);
                continue;
            }
            //品质
            qu = MeasuredValueShort_getQuality(io);
            quStr = that->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);
            QVariantHash vh;
            vh["address"] = address;
            vh["value"] = QString::number(value,'f',2);
            vh["type"] = typeStr;
            vh["cause"] = causeStr;
            vh["qu"] = quStr;
            vh["time"] = time;
            //将解析后的数据打印到列表
            that->loadTable(vh);
        }
    }
3.打印报文和解析后的数据

代码示例:

void MainWindow::loadTable(QVariantHash vh){
    int address = vh.value("address").toInt();
    QString value = vh.value("value").toString();
    QString quStr = vh.value("qu").toString();
    QString causeStr = vh.value("cause").toString();
    QString typeStr = vh.value("type").toString();
    QString time = vh.value("time").toString();
    emit showMessageSignal(QString("类型:%1,原因:%2,地址:%3,值:%4,品质:%5,时间:%6").arg(typeStr).arg(causeStr).arg(address).arg(value).arg(quStr).arg(time),1);
    if(m_addrHash.contains(address)){
        int row = m_addrHash.value(address)->row();
        QTableWidgetItem *item1 = ui->tableWidget->item(row,1);
        QTableWidgetItem *item2 = ui->tableWidget->item(row,2);
        QTableWidgetItem *item3 = ui->tableWidget->item(row,3);
        QTableWidgetItem *item4 = ui->tableWidget->item(row,4);
        QTableWidgetItem *item5 = ui->tableWidget->item(row,5);
        if(item1!=nullptr && item2!=nullptr && item3!=nullptr && item4!=nullptr  && item5!=nullptr){
            item1 ->setData(Qt::DisplayRole,value);
            item2 ->setData(Qt::DisplayRole,typeStr);
            item3 ->setData(Qt::DisplayRole,causeStr);
            item4 ->setData(Qt::DisplayRole,quStr);
            item5 ->setData(Qt::DisplayRole,time);
        }
    }
    else{
        int rows = ui->tableWidget->rowCount();
        ui->tableWidget->insertRow(rows);
        QTableWidgetItem *item = new QTableWidgetItem;
        item ->setData(Qt::DisplayRole,address);
        ui->tableWidget->setItem(rows,0,item);
        int row = item->row();
        m_addrHash.insert(address,item);
        ui->tableWidget->setItem(row,1,new QTableWidgetItem(value));
        ui->tableWidget->setItem(row,2,new QTableWidgetItem(typeStr));
        ui->tableWidget->setItem(row,3,new QTableWidgetItem(causeStr));
        ui->tableWidget->setItem(row,4,new QTableWidgetItem(quStr));
        ui->tableWidget->setItem(row,5,new QTableWidgetItem(time));
    }
    ui->tableWidget->update();}
4.运行演示

在这里插入图片描述

5.总结

本章主要描述IEC104主站软件遥信、遥测数据的解析和部分代码,其它类型标识数据解析方法类似,其中传输原因和数据品质需在软件启动时进行初始化。
下期分享IEC104主站调试软件遥控、遥调功能。


发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。