Qt+OPC开发笔记(二):OPC客户端介绍与读取和写入bool类型Demo
若该文为原创文章,转载请注明原文出处
本文章博客地址:https://hpzwl.blog.csdn.net/article/details/148528041
长沙红胖子Qt(长沙创微智科)博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬结合等等)持续更新中…
Qt开发专栏:三方库开发技术
上一篇:《Qt+OPC开发笔记(一):OPCUA介绍、open62541介绍、编译与基础环境Demo》
下一篇:敬请期待…
前言
本篇介绍opc,opc客户端,实现一个opc客户端读取bool和写入bool的demo,这是基本得数据读取写入的访问。
Demo
OPC客户端
OPC 客户端是一种利用OPC(OLE for Process Control)协议与 OPC 服务器进行通信的软件应用程序。
功能特点
- 数据访问:提供一套简单易用的 API,使开发人员能轻松地创建、读取、更新和删除OPC服务器上的数据项,可从传感器、PLC、DCS 系统、过程分析仪等各种数据源获取实时数据。
- 事件订阅:支持实时数据变化订阅,当服务器端的数据发生变化时,客户端能够立即获取到更新,以便及时响应和处理数据变化。
- 连接管理:负责建立和管理与 OPC 服务器的连接,包括连接的建立、监控连接状态以及在发生异常时进行重连或断开。
- 数据展示与处理:允许用户创建和管理数据视图,通常以表格或图形的方式展示实时数据流,还能对采集到的数据进行分析、存储、归档等处理,为决策提供支持。
OPC协议
系统架构
OPC 协议采用客户端 - 服务器(C/S)架构,该架构由以下部分组成:
- OPC 客户端:这是发起数据请求的应用程序,它通过 OPC 接口向 OPC 服务器请求数据或者订阅事件。
- OPC 服务器:作为数据提供者,OPC 服务器负责与硬件设备(像 PLC、DCS 等)或者其他数据源进行通信,对客户端的请求作出响应,并将数据返回给客户端。
- OPC标准接口:这是客户端和服务器进行通信的桥梁,它定义了一套标准的方法和属性,使得不同厂商开发的客户端和服务器能够实现互操作。
通信机制
OPC 协议基于 COM/DCOM(Component Object Model/Distributed Component Object Model)技术来实现分布式通信,也有基于 OPC UA(Unified Architecture)标准的实现,下面是这两种实现的具体介绍:
COM/DCOM通信
- 注册机制:OPC 服务器需要在 Windows 注册表中注册自己的接口和类信息,这样客户端才能发现并连接到它。
- 接口调用:客户端通过 COM 接口调用服务器提供的方法,比如读取数据、写入数据或者订阅事件等。
- 数据传输:服务器将数据以 COM 对象的形式返回给客户端,客户端再对这些数据进行解析和处理。
OPC UA 通信(注意:这是当前Demo选择的方式):
- 服务导向架构:OPC UA 采用服务导向的架构,使用 SOAP、XML 或者二进制协议进行通信,不再依赖于 COM/DCOM 技术,因此可以在跨平台环境下使用。
- 安全机制:OPC UA内置了身份验证、加密和访问控制等安全机制,能够有效保障数据传输的安全性。
- 信息建模:它使用统一的信息模型来描述工业数据,增强了不同系统间的互操作性。
数据访问方式
OPC 协议支持多种数据访问方式,以满足不同的应用场景需求:
- 同步访问:客户端发送请求后会一直等待,直到服务器返回响应。这种方式适用于对实时性要求较高的场景,但如果服务器响应时间较长,可能会导致客户端程序阻塞。
- 异步访问:客户端发送请求后不会等待服务器响应,而是继续执行后续操作。当服务器处理完请求后,会通过回调函数通知客户端。这种方式可以提高客户端程序的效率,避免阻塞。
- 订阅访问:客户端可以订阅特定的数据项,当这些数据项的值发生变化时,服务器会主动将更新后的数据推送给客户端。这种方式适用于需要实时监控数据变化的场景。
核心组件
OPC 协议的实现涉及以下核心组件:
- OPC DA(Data Access):这是最基础的OPC规范,用于实现实时数据的读写操作。它定义了客户端如何从服务器读取数据以及如何向服务器写入数据的标准接口。
- OPC AE(Alarms and Events):该规范用于处理报警和事件。当系统中出现报警或事件时,服务器会按照该规范的要求将相关信息通知给客户端。
- OPC HDA(Historical Data Access):此规范用于历史数据的访问。客户端可以通过该规范从服务器获取历史数据,以便进行数据分析和趋势预测等操作。
- OPC UA(Unified Architecture)(注意:这是当前Demo选择的方式):作为OPC技术的最新发展,OPC UA整合了上述所有规范,并提供了更安全、更可靠、更易用的跨平台解决方案。它解决了传统OPC规范依赖于COM/DCOM技术、只能在 Windows 平台上运行的局限性。
工作流程
OPC 客户端与服务器之间的通信一般遵循以下流程:
- 发现与连接:客户端通过注册表或者发现服务找到 OPC 服务器,并建立连接。
- 浏览地址空间:客户端可以浏览服务器的地址空间,了解服务器所提供的数据项及其组织结构。
- 创建组和项:客户端创建组(Group)和项(Item)来组织需要访问的数据。组是项的集合,客户端可以对组进行整体操作,提高数据访问效率。
- 数据交互:客户端通过读取、写入或者订阅等操作与服务器进行数据交互。读取操作可以获取数据项的当前值;写入操作可以修改数据项的值;订阅操作可以实时获取数据项值的变化。
- 断开连接:通信结束后,客户端断开与服务器的连接,释放相关资源。
同步读取数据和写入数据演示
步骤一:连接服务器
步骤二:读取数据bool
步骤三:写入数据
步骤四:断开连接服务器
Demo关键源码
读取bool
void OpcClientManager::slot_readBool(int ns, int i)
{// 读取服务器数据UA_Variant value;UA_Variant_init(&value);
/*方式1:数字ID(ns=2;i=1234)UA_NodeId nodeId = UA_NODEID_NUMERIC(2, 1234);方式2:字符串ID(ns=1;s=MyVariable)UA_NodeId nodeId = UA_NODEID_STRING(1, (char*)"MyVariable");方式3:从字符串解析(用于调试)UA_NodeId nodeId;UA_NodeId_fromString(UA_STRING("ns=1;i=1001"), &nodeId);*/UA_StatusCode statusCode = UA_Client_readValueAttribute(_pUAClient, UA_NODEID_NUMERIC(ns, i), &value);if( statusCode == UA_STATUSCODE_GOOD&& UA_Variant_hasScalarType(&value, &UA_TYPES[UA_TYPES_BOOLEAN])){LOG << value.data;emit signal_readBool(true, ns, i, *(UA_Boolean *)value.data);}else{LOG << QString("Failed to UA_Client_readValueAttribute ns=%1,i=%2, statusCode: 0x%3").arg(ns).arg(i).arg(statusCode, 8, 16, QChar('0'));emit signal_readBool(false, ns, i, false);}
}
写入bool
void OpcClientManager::slot_writeBool(int ns, int i, bool b)
{// 读取服务器数据UA_Variant value;UA_Variant_init(&value);UA_Boolean booleanValue = b; // 要写入的布尔值UA_Variant_setScalar(&value, &booleanValue, &UA_TYPES[UA_TYPES_BOOLEAN]);
/*方式1:数字ID(ns=2;i=1234)UA_NodeId nodeId = UA_NODEID_NUMERIC(2, 1234);方式2:字符串ID(ns=1;s=MyVariable)UA_NodeId nodeId = UA_NODEID_STRING(1, (char*)"MyVariable");方式3:从字符串解析(用于调试)UA_NodeId nodeId;UA_NodeId_fromString(UA_STRING("ns=1;i=1001"), &nodeId);*/UA_StatusCode statusCode = UA_Client_writeValueAttribute(_pUAClient, UA_NODEID_NUMERIC(ns, i), &value);if(statusCode == UA_STATUSCODE_GOOD){emit signal_writeBool(true, ns, i, b);}else{LOG << QString("Failed to UA_Client_readValueAttribute ns=%1,i=%2, statusCode: 0x%3").arg(ns).arg(i).arg(statusCode, 8, 16, QChar('0'));emit signal_writeBool(false, ns, i, b);}
}
工程模板v1.1.0
入坑
入坑一:读取bool失败
问题
读取bool总是false,这是使用代码读取值。
原因
打印调试输出:
检查opc服务器:
确认上面2个是一致的。
十有八九是nodeId错了,继续:
修改调试代码:
测试结果:
所以是方式错了,总结就是有三种表达方式:
- 有ns(命名空间,数字)+i(标识符,数字)
- 有ns(命名空间,名称)+i(标识符,名称)
- 有直接输入字符串解析
解决
nodeId输入错误,需要构成:
入坑二:读取bool值一直为true
问题
读取值一直为true
原因
指针指向的数据,而不是直接数据
修改为:
解决
上一篇:《Qt+OPC开发笔记(一):OPCUA介绍、open62541介绍、编译与基础环境Demo》
下一篇:敬请期待…
本文章博客地址:https://hpzwl.blog.csdn.net/article/details/148528041