基于C++和Qt开发IEC104规约主站调试软件(第三章)
第三章:IEC104主站解析遥信、遥测数据
基于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主站调试软件遥控、遥调功能。