IEC61850规约客户端软件开发实战(第四章)

最终开发成果展示:最终成果
上一章内容回顾:IEC61850数据模型加载
软件及完整源代码获取:欢迎咨询

一、IEC61850读写功能介绍

1.数据模型与分层结构​

IEC61850采用面向对象的数据建模方法,将设备功能抽象为逻辑设备(LD)、逻辑节点(LN)、数据对象(DO)和数据属性(DA)的层次结构。例如,一个断路器的状态可能表示为LD1/XCBR1.Pos.stVal,其中Pos.stVal是开关位置的数据属性值。这种分层模型支持灵活的数据访问,用户可通过标准化路径精准读写目标数据。

2.数据读写服务类型
  • ​​读数据值(GetDataValues)​​:获取指定逻辑节点中数据对象的当前值(如遥测值、遥信状态)。
  • ​​设置数据值(SetDataValues)​​:修改数据属性值(如定值区切换、保护参数配置)。
  • 批量读取(GetAllDataValues)​​:一次性获取逻辑节点下的全部数据属性,常用于初始化或全数据同步。
  • ​​模型发现服务(GetLogicalDeviceDirectory等)​​:动态获取设备的数据模型结构,实现即插即用。
3.实现机制
  • 基于MMS协议​​:数据读写通过制造报文规范(MMS)实现,映射到ISO/OSI应用层,支持客户端-服务器模式的请求与响应。
  • 功能约束(FC)​​:通过功能约束(如CO控制、CF配置)限定数据访问权限,确保操作的安全性。例如,仅允许通过Select Before Operate(SBO)机制执行遥控操作,防止误操作。
​​4.应用场景
  • 遥测遥信​​:实时采集电流、电压、开关状态等数据。
  • 定值管理​​:读写保护装置的定值参数(如过流保护阈值)。
  • ​​动态配置​​:通过在线修改数据集(DataSet)或控制块(Report Control Block)调整数据上报规则。

二、IEC61850读数据实现

1.读单个值
  • 选择需要读取的数据Item,点击读单个值按钮
    展示
    在这里插入图片描述
    核心代码:
	IedClientError error;
	//获取DA属性
	LinkedList dataAttributes = IedConnection_getDataDirectoryFC(m_iedConnectHandle,&error,Node->Ref.toUtf8().constData());
        if(error!=IED_ERROR_OK)
            return;
        if (dataAttributes != nullptr) {
            LinkedList dataAttribute = LinkedList_getNext(dataAttributes);
            while (dataAttribute != nullptr) {
                QString df = (char*) dataAttribute->data;//获取DA引用名
                int start =df.indexOf("[");
                int end = df.indexOf("]");
                QString daName = df.mid(0,start);
                QString fcstr=df.mid(start+1,end-1-daName.length());
                QString dref = QString("%1.%2").arg(Node->Ref,daName);
                QString reffc = QString("%1[%2]").arg(dref,fcstr);
                m_mutex.lock();
                Node_Data *nodeD = m_dataHash.value(reffc);
                m_mutex.unlock();
                if(nodeD==nullptr){
                    dataAttribute = LinkedList_getNext(dataAttribute);
                    continue;
                }
                //根据DA应用名称和功能约束读取DA值
                MmsValue* tmms_das = IedConnection_readObject(m_iedConnectHandle,&error, dref.toUtf8().constData(),FunctionalConstraint_fromString(fcstr.toUtf8().constData()));
                if(tmms_das!=nullptr){
                	//获取所读取数据的类型
                    MmsType type = MmsValue_getType(tmms_das);
                    nodeD->type = type;
                    if(type == MMS_STRUCTURE){//如果是结构体类型则递归读取
                            readSingalData(nodeD,citem);
                    }
                    else{
                        nodeD->Data = getValue(tmms_das);
                    }
                    MmsValue_delete(tmms_das);
                }
                dataAttribute = LinkedList_getNext(dataAttribute);
            }
            LinkedList_destroy(dataAttributes);
            //以下需实现所读数据与界面UI结合进行数据展示
			//TODO....

核心接口:IedConnection_readObject

2.读所有值
  • 选择对应逻辑节点,点击读所有值按钮读所有值
    展示:
    在这里插入图片描述
  • 说明:读所有值是可以一次读取整个模型数据,当模型数据量较大时,读取速度会变慢(5秒以上)。这里读所有值实现的功能是只读当前选择的逻辑节点下所有值,且通过多线程并发读取,提高读取效率。

核心代码:

void MainWindow::click_readAllData()
{
	//判断连接是否在线
    if(m_online!=IED_ERROR_ALREADY_CONNECTED)
        return;
     //将需要读取的DO、DA全部放入队列中
    for(QString reffc:m_datasetRefList)
        m_readQueue.enqueue(reffc);
    if(m_threadDoDaPool==nullptr)
        m_threadDoDaPool = new QThreadPool;
    m_threadDoDaPool->setMaxThreadCount(4);
    //启动4个子线程同时读取
    for (int i = 0; i < 4; ++i) {
        QRunnable* task = QRunnable::create([&](){
            showMessage("正在读取所有数据...");
            while(true){
                m_mutex.lock();
                if(m_readQueue.isEmpty()){
                    m_mutex.unlock();
                    break;
                }
                Node_Data *node = m_dataHash.value(m_readQueue.dequeue());
                m_mutex.unlock();
                if(node)
                    readAllData(node);
            }
        });
        m_threadDoDaPool->start(task);
    }
    m_threadPool->start(QRunnable::QRunnable::create([&](){
        m_threadDoDaPool->waitForDone();
        emit ui->treeWidget->itemPressed(ui->treeWidget->currentItem(),0);
        showMessage("读取所有数据完成");
    }));
}

void MainWindow::readAllData(Node_Data *NodeFD)
{
    FunctionalConstraint fc = FunctionalConstraint_fromString(NodeFD->FC.toUtf8().constData());
    IedClientError error;
    QString ref = NodeFD->Ref;
    LinkedList daAtts = IedConnection_getDataDirectoryByFC(m_iedConnectHandle, &error, ref.toUtf8().constData(), fc);
    if(daAtts==nullptr)
        return;
    LinkedList dataObject = LinkedList_getNext(daAtts);
    if(dataObject==nullptr){
        MmsValue *val = IedConnection_readObject(m_iedConnectHandle, &error,ref.toUtf8().constData(), fc);
        if(val){
            NodeFD->Data = getValue(val);
            MmsValue_delete(val);
        }
        return;
    }
    QString name;
    QString strFC = FunctionalConstraint_toString(fc);
    while(dataObject) {
        name  = (char*) dataObject->data;
        QString dref = QString("%1.%2").arg(ref,name);
        m_mutex.lock();
        Node_Data *Node_Child = m_dataHash.value(QString("%1[%2]").arg(dref,strFC));
        m_mutex.unlock();
        if(Node_Child==nullptr){
            dataObject = LinkedList_getNext(dataObject);
            continue;
        }
        MmsValue *val = IedConnection_readObject(m_iedConnectHandle, &error,dref.toUtf8().constData(), fc);
        if(val!=nullptr){
            MmsType type = MmsValue_getType(val);
            if(type==MMS_STRUCTURE)
                readAllData(Node_Child);
            else
                Node_Child->Data = getValue(val);
            MmsValue_delete(val);
        }
        dataObject = LinkedList_getNext(dataObject);
    }
    LinkedList_destroy(daAtts);
}

三、IEC61850写数据实现

写数据时,只有一些特殊数据才可以进行写,如遥调数据、配置数据、描述类数据,即功能约束为CF、SP、DC等数类型数据。
展示:
在这里插入图片描述
核心代码:

connect(&writeBtn,&QPushButton::clicked,[&](){
        IedClientError error;
        //获取功能约束
        FunctionalConstraint fc = FunctionalConstraint_fromString(Node->FC.toUtf8().constData());
        QHashIterator<QString,Node_Data*> it(wHashData);
        while (it.hasNext()) {
            it.next();
            Node_Data* node = it.value();
            MmsValue *value=nullptr;
            //创建不同类型的数据
            if(MMS_FLOAT==node->type)
                value = MmsValue_newFloat(node->Data.toFloat());
            else if(MMS_BOOLEAN==node->type)
                value = MmsValue_newBoolean(node->Data.toUInt());
            else if(MMS_INTEGER==node->type)
                value = MmsValue_newIntegerFromInt32(node->Data.toInt());
            else if(MMS_UNSIGNED==node->type)
                value = MmsValue_newUnsignedFromUint32(node->Data.toUInt());
            else if(MMS_STRING==node->type)
                value = MmsValue_newMmsString(node->Data.toUtf8().constData());
            else if(MMS_UTC_TIME==node->type)
                value = MmsValue_newUtcTimeByMsTime(QDateTime::fromString(node->Data,"yyyy-MM-dd hh:mm:ss.zzz").toMSecsSinceEpoch());
            else{
                if(error!=IED_ERROR_OK){
                    MessageReminder(QString("写数据失败,未找到对应的数据类型(%1)").arg(node->type).toUtf8(),"确定","取消");
                    return;
                }
            }
            //写数据
            IedConnection_writeObject(m_iedConnectHandle, &error,node->Ref.toUtf8().constData(),fc,value);
            if(error!=IED_ERROR_OK){
                MessageReminder(QString("写数据失败,error:%1").arg(node->type).toUtf8(),"确定","取消");
                return;
            }
        }
        showMessage("写数据成功");

核心接口:IedConnection_writeObject

四、IEC61850遥控功能介绍

1.遥控服务类型​​
  • ​​直接控制(Direct Control)​​:客户端直接发送控制命令,无需预选操作,适用于低风险场景。
  • 预选确认控制(SBO Control)​​:采用“选择-执行”两步操作,先验证设备可操作性(Select),再执行命令(Operate),确保安全性。
  • 增强型控制​​:支持检同期(Synchrocheck)、检无压等条件判断,仅当电网状态满足条件时执行操作。
2. 实现机制​​
  • MMS控制模型​​:通过Control类数据对象传递控制命令,例如LD1/CSWI1.Pos表示开关控制位置。
  • ​​GOOSE快速控制​​:对于需要毫秒级响应的操作(如保护跳闸),通过GOOSE报文实现无连接、实时广播控制指令。
  • ​​安全机制​​:结合身份认证、操作权限分级和操作日志记录,防止未授权访问
3. 应用场景​​
  • ​​断路器分合闸​​:远程控制开关状态,支持单命令或序列操作。
  • 保护压板投退​​:通过软压板远程切换保护功能。
  • ​​紧急联动​​:如火灾报警触发通风系统启动,通过GOOSE实现跨设备快速响应

五、IEC61850遥控功能实现

  • 点击控制按钮,进行遥控命令下发,遥控功能只有遥控类数据才可以控制,即功能约束为CO的数据。
    展示:
    在这里插入图片描述
    核心代码:
 connect(&operBtn,&QPushButton::clicked,[&](){
 		//创建控制命令类型
        if(cltYype==MMS_BOOLEAN)
            ctlVal = MmsValue_newBoolean(valComBox.currentIndex()?true:false);
        else if(cltYype==MMS_INTEGER)
            ctlVal = MmsValue_newInteger(valComBox.currentText().toInt());
        else if(cltYype==MMS_UNSIGNED)
            ctlVal = MmsValue_newUnsigned(valComBox.currentText().toInt());
        else if(cltYype==MMS_BIT_STRING)
            ctlVal = MmsValue_newBitString(valComBox.currentText().toInt());
        bool test = false;
        for(Node_Data *node:cNode->ListNodeData){
            if(node->DA=="Test"){
                test = node->Data.toUInt()?true:false;
                ControlObjectClient_setTestMode(ctlClient,test);
            }
            else if(node->DA=="origin"){
                QString orIdent;
                int orCat=0;
                for (Node_Data *nodeOri:node->ListNodeData) {
                    if(nodeOri->DA=="orCat")
                        orCat = nodeOri->Data.toInt();
                    else if(nodeOri->DA=="orIdent")
                        orIdent = nodeOri->Data;
                }
                ControlObjectClient_setOrigin(ctlClient,orIdent.toUtf8().constData(),orCat);
            }
        }
        if(!ctlVal){
            MessageReminder("控制指令执行失败,数据类型错误","确定","取消");
            return;
        }
        ControlObjectClient_setInterlockCheck(ctlClient,true);
        ControlObjectClient_setSynchroCheck(ctlClient,true);
        if(select){
            if(!ControlObjectClient_selectWithValue(ctlClient,ctlVal)){
                IedClientError error = ControlObjectClient_getLastError(ctlClient);
                MessageReminder(QString("选择执行失败,错误码:%1").arg(error).toUtf8(),"确定","取消");
                return;
            }
            showMessage("选择执行成功");
            if(MessageReminder("选择执行成功,是否立即执行","是","否")){
            	//下发控制命令
                bool operate = ControlObjectClient_operate(ctlClient, ctlVal, 0);
                if(!operate){
                    IedClientError error = ControlObjectClient_getLastError(ctlClient);
                    MessageReminder(QString("控制指令执行失败,错误码:%1").arg(error).toUtf8(),"确定","取消");
                    return;
                }
                readSingalData(Node,parentitem);
                showMessage("控制执行成功");
            }
            else{
                operBtn.setText("立即执行");
                select= false;
            }
        }

核心接口:ControlObjectClient_operate

六、总结

本章节讲解IEC61850标准中数据读取、写入和遥控功能的实现方法。在数据读取方面,详细展示了单个值和所有值的读取流程:通过IedConnection_readObject接口读取指定数据项,利用多线程并发提高批量读取效率;在数据写入部分,演示了如何修改设备参数并写入;遥控功能则实现了对设备的远程控制操作。且提供了核心代码示例,包括数据目录获取、递归读取结构体数据、多线程任务分配等关键技术实现,并配有操作界面截图说明。这些功能为智能变电站自动化系统提供了标准化的通信基础。
下期分享IEC61850规约报告功能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

半青年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值