基于open62541的opc ua 服务器开发实现(1)

关于opcua的介绍这里就不多说了,相信大家大都有了一些了解,open62541是一个开源C(C99)的opc-ua实现,开源代码可在官网或github上下载。

话不多说,首先搭建一个opcua服务器实例

 1 #include <signal.h>
 2 #include "open62541.h"
 3    UA_Boolean running = true;
 4    static void stopHandler(int sig)      {
 5    UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "received ctrl-c");
 6    running = false;
 7    }
 8 int main(void) {
 9    signal(SIGINT, stopHandler);
10    signal(SIGTERM, stopHandler);
11    UA_ServerConfig *config = UA_ServerConfig_new_default();
12    UA_Server *server = UA_Server_new(config);
13    UA_StatusCode retval = UA_Server_run(server, &running);
14    UA_Server_delete(server);
15    UA_ServerConfig_delete(config);
16    return (int)retval;
17    }

以下代码演示如何使用数据类型以及如何向服务器添加变量节点。首先,我们向服务器添加一个新变量。查看UA变量属性结构的定义,以查看为变量节点定义的所有属性的列表。请注意,默认设置将变量值的访问级别设置为只读。有关使变量可写的信息,请参见下文。

 1 #include <signal.h>
 2 #include <stdio.h>
 3 #include "open62541.h"
 4 static void
 5 addVariable(UA_Server *server) {
 6    /* Define the attribute of the myInteger variable node */
 7    UA_VariableAttributes attr = UA_VariableAttributes_default;
 8    UA_Int32 myInteger = 42;
 9    UA_Variant_setScalar(&attr.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
10    attr.description = UA_LOCALIZEDTEXT("en-US","the answer");
11    attr.displayName = UA_LOCALIZEDTEXT("en-US","the answer");
12    attr.dataType = UA_TYPES[UA_TYPES_INT32].typeId;
13    attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
14    /* Add the variable node to the information model */
15    UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
16    UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer");
17    UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
18    UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
19    UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId,
20    parentReferenceNodeId, myIntegerName,
21    UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),attr, NULL, NULL);
22    }

现在我们用 写服务 更改节点值。也可以通过网络由OPC UA的客户端访问来修改值 。

 1 static void
 2 writeVariable(UA_Server *server) {
 3     UA_NodeId myIntegerNodeId =
 4     UA_NODEID_STRING(1,"the.answer");
 5
 6     /* Write a different integer value */
 7     UA_Int32 myInteger = 43;
 8     UA_Variant myVar;
 9     UA_Variant_init(&myVar);
10     UA_Variant_setScalar(&myVar, &myInteger,
11     &UA_TYPES[UA_TYPES_INT32]);
12     UA_Server_writeValue(server, myIntegerNodeId, myVar);
13
14     /* Set the status code of the value to an error code. The
15     function
16     * UA_Server_write provides access to the raw service. The
17     above
18     * UA_Server_writeValue is syntactic sugar for writing a specific
19     node
20     * attribute with the write service. */
21     UA_WriteValue wv;
22     UA_WriteValue_init(&wv);
23     wv.nodeId = myIntegerNodeId;
24     wv.attributeId = UA_ATTRIBUTEID_VALUE;
25     wv.value.status = UA_STATUSCODE_BADNOTCONNECTED;
26     wv.value.hasStatus = true;
27     UA_Server_write(server, &wv);
28
29     /* Reset the variable to a good statuscode with a value */
30     wv.value.hasStatus = false;
31     wv.value.value = myVar;
32     wv.value.hasValue = true;
33     UA_Server_write(server, &wv);
34     }

注意我们最初如何将变量节点的data type属性设置为int32数据类型的nodeid。这禁止写入非Int32的值。下面的代码显示了如何对每次写入执行一致性检查。

 1 static void
 2 writeWrongVariable(UA_Server *server) {
 3    UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
 4
 5    /* Write a string */
 6    UA_String myString = UA_STRING("test");
 7    UA_Variant myVar;
 8    UA_Variant_init(&myVar);
 9    UA_Variant_setScalar(&myVar, &myString,
10    &UA_TYPES[UA_TYPES_STRING]);
11    UA_StatusCode retval = UA_Server_writeValue(server,
12    myIntegerNodeId, myVar);
13    printf("Writing a string returned statuscode %s\n",
14    UA_StatusCode_name(retval));
15    }

它遵循主服务器代码,使用上述定义。

 1 UA_Boolean running = true;
 2 static void stopHandler(int sign) {
 3 UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "received ctrl-c");
 4 running = false;
 5 }
 6
 7 int main(void) {
 8    signal(SIGINT, stopHandler);
 9    signal(SIGTERM, stopHandler);
10
11    UA_ServerConfig *config = UA_ServerConfig_new_default();
12    UA_Server *server = UA_Server_new(config);
13
14    addVariable(server);
15    writeVariable(server);
16    writeWrongVariable(server);
17
18    UA_StatusCode retval = UA_Server_run(server, &running);
19    UA_Server_delete(server);
20    UA_ServerConfig_delete(config);
21    return (int)retval;
22    }

在基于OPC UA的体系结构中,服务器通常位于信息源附近。在工业环境中,这意味着服务器接近物理过程,客户机在运行时使用数据。在前面的教程中,我们看到了如何向OPC UA信息模型添加变量。本实例演示如何将变量连接到运行时信息,例如从物理过程的测量中连接。为简单起见,我们以系统时钟为基础的“过程”。以下代码段分别与运行时更新变量值的不同方式有关。总之,代码片段定义了一个可编译的源文件。

作为起点,假设已在服务器中为datetime类型的值创建了一个标识符为“ns=1,s=current time”的变量。假设当一个新的值从底层进程到达时,我们的应用程序被触发,我们可以直接写入变量。

 1 #include <signal.h>
 2 #include "open62541.h"
 3 #pragma comment(lib, "WS2_32")
 4 static void
 5 updateCurrentTime(UA_Server *server) {
 6     UA_DateTime now = UA_DateTime_now();
 7     UA_Variant value;
 8     UA_Variant_setScalar(&value, &now, &UA_TYPES[UA_TYPES_DATETIME]);
 9     UA_NodeId currentNodeId = UA_NODEID_STRING(1, "current-time");
10     UA_Server_writeValue(server, currentNodeId, value);
11 }
12 static void
13 addCurrentTimeVariable(UA_Server *server) {
14     UA_DateTime now = 0;
15     UA_VariableAttributes attr = UA_VariableAttributes_default;
16     attr.displayName = UA_LOCALIZEDTEXT("en-US", "Current time");
17     attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
18     UA_Variant_setScalar(&attr.value, &now, &UA_TYPES[UA_TYPES_DATETIME]);
19     UA_NodeId currentNodeId = UA_NODEID_STRING(1, "current-time");
20     UA_QualifiedName currentName = UA_QUALIFIEDNAME(1, "current-time");
21     UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
22     UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
23     UA_NodeId variableTypeNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE);
24     UA_Server_addVariableNode(server, currentNodeId, parentNodeId,
25         parentReferenceNodeId, currentName,
26         variableTypeNodeId, attr, NULL, NULL);
27     updateCurrentTime(server);
28 }

2.值回调

当一个值不断变化时,例如系统时间,在一个紧密的循环中更新该值将占用大量的资源。值回调允许将变量值与外部表示同步。它们将回调附加到每次读操作之前和每次写操作之后执行的变量。

 1 static void
 2 beforeReadTime(UA_Server *server,
 3     const UA_NodeId *sessionId, void *sessionContext,
 4     const UA_NodeId *nodeid, void *nodeContext,
 5     const UA_NumericRange *range, const UA_DataValue *data) {
 6     UA_DateTime now = UA_DateTime_now();
 7     UA_Variant value;
 8     UA_Variant_setScalar(&value, &now, &UA_TYPES[UA_TYPES_DATETIME]);
 9     UA_NodeId currentNodeId = UA_NODEID_STRING(1, "current-time");
10     UA_Server_writeValue(server, currentNodeId, value);
11 }
12 static void
13 afterWriteTime(UA_Server *server,
14     const UA_NodeId *sessionId, void *sessionContext,
15     const UA_NodeId *nodeId, void *nodeContext,
16     const UA_NumericRange *range, const UA_DataValue *data) {
17     UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
18         "The variable was updated");
19 }
20 static void
21 addValueCallbackToCurrentTimeVariable(UA_Server *server) {
22     UA_NodeId currentNodeId = UA_NODEID_STRING(1, "current-time");
23     UA_ValueCallback callback;
24     callback.onRead = beforeReadTime;
25     callback.onWrite = afterWriteTime;
26     UA_Server_setVariableNode_valueCallback(server, currentNodeId, callback);
27 }

3.变量数据源

对于值回调,该值仍存储在变量节点中。所谓的数据源更进一步。服务器将每个读写请求重定向到回调函数。在读取时,回调提供当前值的副本。在内部,数据源需要实现自己的内存管理。

 1 static UA_StatusCode
 2 readCurrentTime(UA_Server *server,
 3     const UA_NodeId *sessionId, void *sessionContext,
 4     const UA_NodeId *nodeId, void *nodeContext,
 5     UA_Boolean sourceTimeStamp, const UA_NumericRange *range,
 6     UA_DataValue *dataValue) {
 7     UA_DateTime now = UA_DateTime_now();
 8     UA_Variant_setScalarCopy(&dataValue->value, &now,
 9         &UA_TYPES[UA_TYPES_DATETIME]);
10     dataValue->hasValue = true;
11     return UA_STATUSCODE_GOOD;
12 }
13
14 static UA_StatusCode
15 writeCurrentTime(UA_Server *server,
16     const UA_NodeId *sessionId, void *sessionContext,
17     const UA_NodeId *nodeId, void *nodeContext,
18     const UA_NumericRange *range, const UA_DataValue *data) {
19     UA_LOG_INFO(UA_Log_out, UA_LOGCATEGORY_USERLAND,
20         "Changing the system time is not implemented");
21     return UA_STATUSCODE_GOOD;
22 }
23
24 static void
25 addCurrentTimeDataSourceVariable(UA_Server *server) {
26     UA_VariableAttributes attr = UA_VariableAttributes_default;
27     attr.displayName = UA_LOCALIZEDTEXT("en-US", "Current time - data source");
28     attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
29     UA_NodeId currentNodeId = UA_NODEID_STRING(1, "current-time-datasource");
30     UA_QualifiedName currentName = UA_QUALIFIEDNAME(1, "current-time-datasource");
31     UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
32     UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
33     UA_NodeId variableTypeNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE);
34     UA_DataSource timeDataSource;
35     timeDataSource.read = readCurrentTime;
36     timeDataSource.write = writeCurrentTime;
37     UA_Server_addDataSourceVariableNode(server, currentNodeId, parentNodeId,
38         parentReferenceNodeId, currentName,
39         variableTypeNodeId, attr,
40         timeDataSource, NULL, NULL);
41 }

以下代码遵循主服务器代码,使用上述定义。

 1 UA_Boolean running = true;
 2 static void stopHandler(int sign) {
 3     UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "received ctrl-c");
 4     running = false;
 5 }
 6 int main(void) {
 7     signal(SIGINT, stopHandler);
 8     signal(SIGTERM, stopHandler);
 9     UA_ServerConfig *config = UA_ServerConfig_new_default();
10     UA_Server *server = UA_Server_new(config);
11     addCurrentTimeVariable(server);
12     addValueCallbackToCurrentTimeVariable(server);
13     addCurrentTimeDataSourceVariable(server);
14     UA_StatusCode retval = UA_Server_run(server, &running);
15     UA_Server_delete(server);
16     UA_ServerConfig_delete(config);
17     return (int)retval;
18 }

原文地址:https://www.cnblogs.com/yuexiachacha/p/10742492.html

时间: 2024-10-05 01:51:16

基于open62541的opc ua 服务器开发实现(1)的相关文章

SharpNodeSettings项目,可配置的数据采集,统一的工业数据网关,OPC UA服务器开发,PLC数据发布到自身内存,redis,opc ua,以及数据可视化开发

本项目隶属于 HslCommunication 项目的SDK套件,如果不清楚HslCommunication组件的话,可以先了解那个项目,源代码地址:https://github.com/dathlin/HslCommunication 本项目源代码地址:https://github.com/dathlin/SharpNodeSettings 本项目的主要实现的功能主要有2个: 实现单个设备信息的可配置,可存储,采用一个相对标准的Xml存储机制实现,适用的场景是:如果你有20个西门子PLC(种类

C# 实现opc ua服务器的远程连接(转)

原文转自:https://www.cnblogs.com/dathlin/p/7724834.html OPC UA简介 OPC是应用于工业通信的,在windows环境的下一种通讯技术,原有的通信技术难以满足日益复杂的环境,在可扩展性,安全性,跨平台性方面的不足日益明显,所以OPC基金会在几年前提出了面向未来的架构设计的OPC 统一架构,简称OPC UA,截止目前为止,越来越多公司将OPC UA作为开放的数据标准,在未来工业4.0行业上也将大放异彩. 在OPC UA的服务器端.会公开一些数据节点

OPC协议解析-OPC UA OPC统一架构

1    什么是OPC UA 为了应对标准化和跨平台的趋势,为了更好的推广OPC,OPC基金会近些年在之前OPC成功应用的基础上推出了一个新的OPC标准-OPC UA.OPC UA接口协议包含了之前的 A&E, DA,OPC XML DA or HDA,只使用一个地址空间就能访问之前所有的对象,而且不受WINDOWS平台限制,因为它是从传输层Scoket以上来定义的,这点后面会提到,导致了灵活性和安全性比之前的OPC都提升了. 2    OPC UA的优势 1)一个通用接口集成了之前所有OPC的

OPC UA 统一架构学习4

今天华为发邮件咨询opc ua 的开发工作,也在进行嵌入式opc ua 的研究,在做通讯行业的预研,做万物互联的准备. 他的要求基本上可以如下实现: 1.用传统的开发方法开发出几个测试函数来模拟真实的业务操作.然后把这些测试函数写入到相应的板子中. 2.开发一个简单的opc ua server,将测试函数映射到地址空间中.地址空间里面其实就是类似于一个函数指针,指向测试函数的内存地址.而这个操作是sdk已经实现的. 3.开发或者使用一个ua client去调用相应的api.由服务器端的地址空间去

西门子PLC1500 OPC ua 通信测试(ua Expert)

一.资料: 1.西门子提供的OPC UA Client资料: 全球资源库搜索:109737901 此例不使用该测试软件 2.第三方测试软件 Ua Expert  下载地址: https://www.unified-automation.com/downloads/opc-ua-clients.html 3.视频资料地址: http://wap.siemens.com.cn/service/elearning/videos/1904.html 二.测试 1.设备组态.编译.下载完成. 2.导出客户

基于Java的高性能基金持仓分析服务器开发

基于Java的高性能基金持仓分析服务器开发(Java多线程\SOCKET编程\JAVA高并发) http://www.ibeifeng.com/goods-260.html 咨询QQ2110053820 课程讲师:hejing 课程分类:Java基础 适合人群:中级 课时数量:30课时 更新程度:完毕 用到技术:Java多线程.SOCKET编程.ant编译.poi组件 涉及项目:基金持仓分析服务器 本课程是一套采用JAVA开发大并发.高性能服务器系统的视频教程,此教程从头到 尾采用高性能基金持仓

Apache Solr采用Java开发、基于Lucene的全文搜索服务器

http://docs.spring.io/spring-data/solr/ 首先介绍一下solr: Apache Solr (读音: SOLer) 是一个开源.高性能.采用Java开发.基于Lucene的全文搜索服务器,文档通过Http利用XML加到一个搜索集合中,查询该集合也是通过 http收到一个XML/JSON响应来实现.Solr 中存储的资源是以 Document 为对象进行存储的.每个文档由一系列的 Field 构成,每个 Field 表示资源的一个属性.Solr 中的每个 Doc

从 OPC 到 OPC UA

[前言]OPC是一个工业标准,所属国际组织是OPC基金会,现有会员已超过220家,包括世界上所有主要的自动化控制系统.仪器仪表及过程控制系统的公司. [经典 OPC]经典OPC规范基于微软Windows系统提供的COM/DCOM技术,用于软件之间数据交换的规范.OPC规范定义了几种不同的,用于访问过程数据.报警信息以及历史数据的版本规范:OPC实时数据访问规范(OPC DA)定义了包括数据值,更新时间与数据品质信息的相关标准.OPC历史数据访问规范(OPC HDA)定义了查询.分析历史数据和含有

OPC UA

OPC UA将来自不同厂商不同设备的数据进行统一格式.统一显示. 以下转自:智能制造还有多远?--谈谈为什么要采用OPC UA? 9月5日,OPC UA正式成为国家推荐性标准.而对于OPC UA是什么,为什么选择OPC UA很多人却并不了解.以下推荐的这篇文章,将有助于大家全面立理解这一数据互联的基础标准与规范. 转自微信公众号:说东道西 ---------------以下是正文--------------- 前段时间与PLCopen主席严义老师探讨在PLCopen教育合作项目,据严老师前期的调