基于open62541的opc ua 服务器开发实现(1)
关于opcua的介绍这里就不多说了,相信大家大都有了一些了解,open62541是一个开源C(C99)的opc-ua实现,开源代码可在官网或github上下载。
话不多说,首先搭建一个opcua服务器实例
#include <signal.h>
#include "open62541.h"
UA_Boolean running = true;
static void stopHandler(int sig)
{
UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "received ctrl-c");
running = false;
}
int main(void) {
signal(SIGINT, stopHandler);
signal(SIGTERM, stopHandler);
UA_ServerConfig *config = UA_ServerConfig_new_default();
UA_Server *server = UA_Server_new(config);
UA_StatusCode retval = UA_Server_run(server, &running);
UA_Server_delete(server);
UA_ServerConfig_delete(config);
return (int)retval;
}
以下代码演示如何使用数据类型以及如何向服务器添加变量节点。首先,我们向服务器添加一个新变量。查看UA变量属性结构的定义,以查看为变量节点定义的所有属性的列表。请注意,默认设置将变量值的访问级别设置为只读。有关使变量可写的信息,请参见下文。
#include <signal.h>
#include <stdio.h>
#include "open62541.h"
static void
addVariable(UA_Server *server) {
/* Define the attribute of the myInteger variable node */
UA_VariableAttributes attr = UA_VariableAttributes_default;
UA_Int32 myInteger = ;
UA_Variant_setScalar(&attr.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
attr.description = UA_LOCALIZEDTEXT("en-US","the answer");
attr.displayName = UA_LOCALIZEDTEXT("en-US","the answer");
attr.dataType = UA_TYPES[UA_TYPES_INT32].typeId;
attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
/* Add the variable node to the information model */
UA_NodeId myIntegerNodeId = UA_NODEID_STRING(, "the.answer");
UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(, "the answer");
UA_NodeId parentNodeId = UA_NODEID_NUMERIC(, UA_NS0ID_OBJECTSFOLDER);
UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(, UA_NS0ID_ORGANIZES);
UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId,
parentReferenceNodeId, myIntegerName,
UA_NODEID_NUMERIC(, UA_NS0ID_BASEDATAVARIABLETYPE),attr, NULL, NULL);
}
现在我们用 写服务 更改节点值。也可以通过网络由OPC UA的客户端访问来修改值 。
static void
writeVariable(UA_Server *server) {
UA_NodeId myIntegerNodeId =
UA_NODEID_STRING(,"the.answer"); /* Write a different integer value */
UA_Int32 myInteger = ;
UA_Variant myVar;
UA_Variant_init(&myVar);
UA_Variant_setScalar(&myVar, &myInteger,
&UA_TYPES[UA_TYPES_INT32]);
UA_Server_writeValue(server, myIntegerNodeId, myVar); /* Set the status code of the value to an error code. The
function
* UA_Server_write provides access to the raw service. The
above
* UA_Server_writeValue is syntactic sugar for writing a specific
node
* attribute with the write service. */
UA_WriteValue wv;
UA_WriteValue_init(&wv);
wv.nodeId = myIntegerNodeId;
wv.attributeId = UA_ATTRIBUTEID_VALUE;
wv.value.status = UA_STATUSCODE_BADNOTCONNECTED;
wv.value.hasStatus = true;
UA_Server_write(server, &wv); /* Reset the variable to a good statuscode with a value */
wv.value.hasStatus = false;
wv.value.value = myVar;
wv.value.hasValue = true;
UA_Server_write(server, &wv);
}
注意我们最初如何将变量节点的data type属性设置为int32数据类型的nodeid。这禁止写入非Int32的值。下面的代码显示了如何对每次写入执行一致性检查。
static void
writeWrongVariable(UA_Server *server) {
UA_NodeId myIntegerNodeId = UA_NODEID_STRING(, "the.answer"); /* Write a string */
UA_String myString = UA_STRING("test");
UA_Variant myVar;
UA_Variant_init(&myVar);
UA_Variant_setScalar(&myVar, &myString,
&UA_TYPES[UA_TYPES_STRING]);
UA_StatusCode retval = UA_Server_writeValue(server,
myIntegerNodeId, myVar);
printf("Writing a string returned statuscode %s\n",
UA_StatusCode_name(retval));
}
它遵循主服务器代码,使用上述定义。
UA_Boolean running = true;
static void stopHandler(int sign) {
UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "received ctrl-c");
running = false;
} int main(void) {
signal(SIGINT, stopHandler);
signal(SIGTERM, stopHandler); UA_ServerConfig *config = UA_ServerConfig_new_default();
UA_Server *server = UA_Server_new(config); addVariable(server);
writeVariable(server);
writeWrongVariable(server); UA_StatusCode retval = UA_Server_run(server, &running);
UA_Server_delete(server);
UA_ServerConfig_delete(config);
return (int)retval;
}
在基于OPC UA的体系结构中,服务器通常位于信息源附近。在工业环境中,这意味着服务器接近物理过程,客户机在运行时使用数据。在前面的教程中,我们看到了如何向OPC UA信息模型添加变量。本实例演示如何将变量连接到运行时信息,例如从物理过程的测量中连接。为简单起见,我们以系统时钟为基础的“过程”。以下代码段分别与运行时更新变量值的不同方式有关。总之,代码片段定义了一个可编译的源文件。
作为起点,假设已在服务器中为datetime类型的值创建了一个标识符为“ns=1,s=current time”的变量。假设当一个新的值从底层进程到达时,我们的应用程序被触发,我们可以直接写入变量。
#include <signal.h>
#include "open62541.h"
#pragma comment(lib, "WS2_32")
static void
updateCurrentTime(UA_Server *server) {
UA_DateTime now = UA_DateTime_now();
UA_Variant value;
UA_Variant_setScalar(&value, &now, &UA_TYPES[UA_TYPES_DATETIME]);
UA_NodeId currentNodeId = UA_NODEID_STRING(, "current-time");
UA_Server_writeValue(server, currentNodeId, value);
}
static void
addCurrentTimeVariable(UA_Server *server) {
UA_DateTime now = ;
UA_VariableAttributes attr = UA_VariableAttributes_default;
attr.displayName = UA_LOCALIZEDTEXT("en-US", "Current time");
attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
UA_Variant_setScalar(&attr.value, &now, &UA_TYPES[UA_TYPES_DATETIME]);
UA_NodeId currentNodeId = UA_NODEID_STRING(, "current-time");
UA_QualifiedName currentName = UA_QUALIFIEDNAME(, "current-time");
UA_NodeId parentNodeId = UA_NODEID_NUMERIC(, UA_NS0ID_OBJECTSFOLDER);
UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(, UA_NS0ID_ORGANIZES);
UA_NodeId variableTypeNodeId = UA_NODEID_NUMERIC(, UA_NS0ID_BASEDATAVARIABLETYPE);
UA_Server_addVariableNode(server, currentNodeId, parentNodeId,
parentReferenceNodeId, currentName,
variableTypeNodeId, attr, NULL, NULL);
updateCurrentTime(server);
}
2.值回调
当一个值不断变化时,例如系统时间,在一个紧密的循环中更新该值将占用大量的资源。值回调允许将变量值与外部表示同步。它们将回调附加到每次读操作之前和每次写操作之后执行的变量。
static void
beforeReadTime(UA_Server *server,
const UA_NodeId *sessionId, void *sessionContext,
const UA_NodeId *nodeid, void *nodeContext,
const UA_NumericRange *range, const UA_DataValue *data) {
UA_DateTime now = UA_DateTime_now();
UA_Variant value;
UA_Variant_setScalar(&value, &now, &UA_TYPES[UA_TYPES_DATETIME]);
UA_NodeId currentNodeId = UA_NODEID_STRING(, "current-time");
UA_Server_writeValue(server, currentNodeId, value);
}
static void
afterWriteTime(UA_Server *server,
const UA_NodeId *sessionId, void *sessionContext,
const UA_NodeId *nodeId, void *nodeContext,
const UA_NumericRange *range, const UA_DataValue *data) {
UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
"The variable was updated");
}
static void
addValueCallbackToCurrentTimeVariable(UA_Server *server) {
UA_NodeId currentNodeId = UA_NODEID_STRING(, "current-time");
UA_ValueCallback callback;
callback.onRead = beforeReadTime;
callback.onWrite = afterWriteTime;
UA_Server_setVariableNode_valueCallback(server, currentNodeId, callback);
}
3.变量数据源
对于值回调,该值仍存储在变量节点中。所谓的数据源更进一步。服务器将每个读写请求重定向到回调函数。在读取时,回调提供当前值的副本。在内部,数据源需要实现自己的内存管理。
static UA_StatusCode
readCurrentTime(UA_Server *server,
const UA_NodeId *sessionId, void *sessionContext,
const UA_NodeId *nodeId, void *nodeContext,
UA_Boolean sourceTimeStamp, const UA_NumericRange *range,
UA_DataValue *dataValue) {
UA_DateTime now = UA_DateTime_now();
UA_Variant_setScalarCopy(&dataValue->value, &now,
&UA_TYPES[UA_TYPES_DATETIME]);
dataValue->hasValue = true;
return UA_STATUSCODE_GOOD;
} static UA_StatusCode
writeCurrentTime(UA_Server *server,
const UA_NodeId *sessionId, void *sessionContext,
const UA_NodeId *nodeId, void *nodeContext,
const UA_NumericRange *range, const UA_DataValue *data) {
UA_LOG_INFO(UA_Log_out, UA_LOGCATEGORY_USERLAND,
"Changing the system time is not implemented");
return UA_STATUSCODE_GOOD;
} static void
addCurrentTimeDataSourceVariable(UA_Server *server) {
UA_VariableAttributes attr = UA_VariableAttributes_default;
attr.displayName = UA_LOCALIZEDTEXT("en-US", "Current time - data source");
attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
UA_NodeId currentNodeId = UA_NODEID_STRING(, "current-time-datasource");
UA_QualifiedName currentName = UA_QUALIFIEDNAME(, "current-time-datasource");
UA_NodeId parentNodeId = UA_NODEID_NUMERIC(, UA_NS0ID_OBJECTSFOLDER);
UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(, UA_NS0ID_ORGANIZES);
UA_NodeId variableTypeNodeId = UA_NODEID_NUMERIC(, UA_NS0ID_BASEDATAVARIABLETYPE);
UA_DataSource timeDataSource;
timeDataSource.read = readCurrentTime;
timeDataSource.write = writeCurrentTime;
UA_Server_addDataSourceVariableNode(server, currentNodeId, parentNodeId,
parentReferenceNodeId, currentName,
variableTypeNodeId, attr,
timeDataSource, NULL, NULL);
}
以下代码遵循主服务器代码,使用上述定义。
UA_Boolean running = true;
static void stopHandler(int sign) {
UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "received ctrl-c");
running = false;
}
int main(void) {
signal(SIGINT, stopHandler);
signal(SIGTERM, stopHandler);
UA_ServerConfig *config = UA_ServerConfig_new_default();
UA_Server *server = UA_Server_new(config);
addCurrentTimeVariable(server);
addValueCallbackToCurrentTimeVariable(server);
addCurrentTimeDataSourceVariable(server);
UA_StatusCode retval = UA_Server_run(server, &running);
UA_Server_delete(server);
UA_ServerConfig_delete(config);
return (int)retval;
}
基于open62541的opc ua 服务器开发实现(1)的更多相关文章
- SharpNodeSettings项目,可配置的数据采集,统一的工业数据网关,OPC UA服务器开发,PLC数据发布到自身内存,redis,opc ua,以及数据可视化开发
本项目隶属于 HslCommunication 项目的SDK套件,如果不清楚HslCommunication组件的话,可以先了解那个项目,源代码地址:https://github.com/dathli ...
- C# 读写opc ua服务器,浏览所有节点,读写节点,读历史数据,调用方法,订阅,批量订阅操作
OPC UA简介 OPC是应用于工业通信的,在windows环境的下一种通讯技术,原有的通信技术难以满足日益复杂的环境,在可扩展性,安全性,跨平台性方面的不足日益明显,所以OPC基金会在几年前提出了面 ...
- C# 实现opc ua服务器的远程连接(转)
原文转自:https://www.cnblogs.com/dathlin/p/7724834.html OPC UA简介 OPC是应用于工业通信的,在windows环境的下一种通讯技术,原有的通信技术 ...
- 分享一款免费OPC UA服务器
OPC UA基于OPC基金会提供的新一代技术,提供安全,可靠和独立于厂商的,实现原始数据和预处理的信息从制造层级到生产计划或ERP层级的传输.通过OPC UA,所有需要的信息在任何时间,任何地点对每个 ...
- C#实现访问OPC UA服务器
OPC UA服务器支持三种认证方式,分别是匿名认证.用户认证和证书认证.其中匿名认证安全等级最低,访问不做任何校验.用户认证访问时,OPC UA客户端需要提供用户名及密码认证,只有用户名和密码正确才允 ...
- C# OPC UA服务器 OPC UA网关 三菱 西门子 欧姆龙 Modbus转OPC UA 服务器 可配置的OPC UA服务器网关 HslSharp软件文档
前言 本文将使用一个基于开源项目HslCommunication创建的OPC UA网关,方便通过配置创建一个OPC UA的网关中心.具体的操作及支持的设备信息项目参照下面: 开源项目HslCommun ...
- OPC协议解析-OPC UA OPC统一架构
1 什么是OPC UA 为了应对标准化和跨平台的趋势,为了更好的推广OPC,OPC基金会近些年在之前OPC成功应用的基础上推出了一个新的OPC标准-OPC UA.OPC UA接口协议包含了之前的 ...
- OPC UA (统一架构)的优势
OPC UA OPC统一架构(OPC Unified Architecture)是OPC基金会(OPC Foundation)创建的新技术,更加安全.可靠.中性(与供应商无关),为制造现场到生产计划或 ...
- 转:OPC协议解析-OPC UA OPC统一架构
1 什么是OPC UA 为了应对标准化和跨平台的趋势,为了更好的推广OPC,OPC基金会近些年在之前OPC成功应用的基础上推出了一个新的OPC标准-OPC UA.OPC UA接口协议包含了之前的 ...
随机推荐
- rhel 7安装Mysql
rhel7安装mysql服务 环境: 1)rhel 7虚拟机 2)配置完163网络yum源,并且保证网络通畅 安装过程: 1) 安装Mysql和Mysql-devel 命令:yum install m ...
- MYSQL必知必会学习笔记
8.1.1 百分号( %)通配符最常使用的通配符是百分号( %).在搜索串中, %表示任何字符出现任意次数.例如,为了找出所有以词jet起头的产品,可使用以下SELECT语句:SELECT prod_ ...
- Hadoop namenode节点无法启动的问题解决
namenode是Hadoop集群HDFS的管理节点,管理着整个分布式文件系统的命名空间,以及文件与块的映射关系等,在Hadoop集群中扮演着至关重要的作用. 我之前安装的Hadoop集群中namen ...
- React事件绑定几种方法测试
前提 es6写法的类方法默认没有绑定this,不手动绑定this值为undefined. 因此讨论以下几种绑定方式. 一.构造函数constructor中用bind绑定 class App exten ...
- Selenium 实现 Web 自动化的原理 (软件测试52讲学习笔记)
Selenium 1.0 的工作原理 Selenium 1.0,又称Selenium RC ,RC是Remote Control的缩写.Selenium RC利用的原理:JavaScript代码可以方 ...
- Nginx的虚拟主机配置
虚拟主机技术能够让同一台服务器.同一组Nginx进程上运行多个网站,降低了资金和服务器资源的损耗.Nginx可以配置三种类型的虚拟主机,本文就是主要介绍这三种虚拟主机配置方式. 配置基于IP的虚拟主机 ...
- linux df -i 100%处理
发现空间是足够的,然后df -i 查看了下inodes,发现根目录下的inodes值使用率为100%了 解决方法:通过以下脚本进行检查,查看到底哪个目录下面的文件最多: find / -xdev -p ...
- SQL语句基本
基础 创建数据库 CREATE DATABASE database-name 1 删除数据库 drop database dbname 1 备份sql server 创建 备份数据的 device U ...
- 多选ui实现单选效果
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- java中级或者高级面试题分享
一. 基础知识: 1)集合类:List和Set比较,各自的子类比较(ArrayList,Vector,LinkedList:HashSet,TreeSet): 2)HashMap的底层实现,之后会问C ...