open62541学习:文件传输

news2024/10/6 10:35:02

           作为一种通信协议,文件传输是非常重要的。例如传输执行程序,图片,配置文件等等。文件传输的机制和类型在 OPC UA 中已经存在很长时间了。FileType (作为ObjectType)和ImageType长期以来一直是内置模型的一部分,并且也用于许多配套规范(例如;用于设备的 OPC UA、用于机器视觉的 OPC UA、OPC UA 用于数控)。 

        自版本 1.5.2 起,文件传输已从第 5 部分中删除,并给出了自己的部分(UA 第 20 部分:文件传输)。

       在OPC UA DI中,该方法用于下载和上传软件。另一个成功的用例是Machinery 的ResultTransferType,它旨在为测量结果生成相应的报告并将其传输给客户端。

我计划使用Filetype 文件传输的方法来下载IEC61499 的功能块网络,功能块库。如此一来,将IEC61499 标准完全建立在opcua 的基础之上。而不采用其它自定义的协议。

文件类型(FileType)

FileType 是一个内置的对象类型,包含了一些特征变量和方法。

建立文件实例的方法:

  UA_NodeId fileID;
    UA_ObjectAttributes fileAttr = UA_ObjectAttributes_default;
    fileAttr.displayName = UA_LOCALIZEDTEXT((char *)"en-US", (char *)"File");

    UA_Server_addObjectNode(server, UA_NODEID_NULL,
                            UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
                            UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
                            UA_QUALIFIEDNAME(1, (char *)"File"), UA_NODEID_NUMERIC(0, UA_NS0ID_FILETYPE),
                            fileAttr, NULL, &fileID);

不过,需要对Writable ,Useritable 设置为true。另一个重点是为其中的方法编写callback

FileType 中的方法

 FileType 中的方法包括:

  • 打开文件 Open 
  • 写文件 Write
  • 读文件 Read
  • 获取位置(Getposition)
  • 设置位置(SetPosition)
  • 关闭 Close

这几个方法与C语言中的文件操作基本是一一对应的。 

Open方法

输入参数:mode

输出参数:handle

其中:

mode 是文件打开的模式,与C++ 的对应关系如下

OPC统一协议

C++

说明

文件已存在时的操作

文件不存在时的操作

附加

删除

0

0

0

1

“r”

打开文件进行读取

从头开始读

错误

0

1

1

0

“w”

创建一个用于写入的文件

销毁内容

创建新的

1

0

1

0

“A”

追加到文件以进行写入

写到最后

创建新的

0

0

1

1

“r+”

打开文件进行读/写

从头开始读

C/C++ 中的错误[2]

0

1

1

1

“w+”

创建一个文件用于读/写

销毁内容

创建新的

1

0

1

1

“一个+”

打开文件进行读/写

写到最后

创建新的

handle 是文件句柄,当打开文件后,返回一个文件句柄,后续的操作使用文件句柄。

写文件

输入参数:

1 文件句柄 handle

3 写入数据 ByteString 

关闭文件

输入参数:文件句柄(handle)

传输效率

国外有一些测试,结果如下:

Protocol

Max. transfer time

Min. transfer time

Average transfer time

HTTP

36.96 [s]

17.01 [s]

22.27 [s]

FTP

34.73 [s]

19.41 [s]

22.99 [s]

OPC UA

23.88 [s]

18.21 [s]

20.53 [s]

相比执行,opcua 文件传输的效率是不错的。

open62541 文件传输

open61541 是开源opcua 项目,该项目中并没有对FileType 操作的支持,但是FileType 是一个普通的对象类型,完全可以使用基本的程序实现FileType 对象的建立。而方法回调函数和文件操作需要程序需要额外的代码。可惜的是open61541 项目中并没有FileType 的Example,网络上也没有相对完整的例子。于是我写了一个:

代码(支持WriteFile)

#include <open62541/server.h>
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <unistd.h>
#include <fcntl.h>
/* Build Instructions (Linux)
 * - g++ server.cpp -lopen62541 -o server */

using namespace std;
char *UA_String2string(UA_String uaString)
{
  char *convert = (char *)UA_malloc(sizeof(char) * uaString.length + 1);
  memcpy(convert, uaString.data, uaString.length);
  convert[uaString.length] = '\0';
  return convert;
}
UA_StatusCode OpenFileCallback(UA_Server *server, const UA_NodeId *sessionId,
                           void *sessionContext, const UA_NodeId *methodId,
                           void *methodContext, const UA_NodeId *objectId,
                           void *objectContext, size_t inputSize,
                           const UA_Variant *input, size_t outputSize,
                           UA_Variant *output)
{
    cout << "Open File Callback" << endl;
    int FileMode=*(UA_Byte *)(input[0].data);
    cout<<"mode:"<<FileMode<<endl;
  uint32_t handle=open("File.txt",O_CREAT|O_RDWR,FileMode);
  cout<<"handel:"<<handle<<endl;
    UA_Variant_setScalarCopy(output, &handle, &UA_TYPES[UA_TYPES_UINT32]);
 //   output[0]=Handle;
    return UA_STATUSCODE_GOOD;
}
UA_StatusCode WriteFileCallback(UA_Server *server, const UA_NodeId *sessionId,
                           void *sessionContext, const UA_NodeId *methodId,
                           void *methodContext, const UA_NodeId *objectId,
                           void *objectContext, size_t inputSize,
                           const UA_Variant *input, size_t outputSize,
                           UA_Variant *output)
{
    cout << "Write File Callback" << endl;
    uint32_t handle=*(UA_UInt32 *)(input[0].data);
    cout<<"handle:"<<handle<<endl;
    UA_ByteString data=*(UA_ByteString *)(input[1].data);
  //  cout<<"Data:"<<UA_String2string(data)<<endl;
    write(handle,UA_String2string(data),data.length);
    return UA_STATUSCODE_GOOD;
}
UA_StatusCode CloseFileCallback(UA_Server *server, const UA_NodeId *sessionId,
                           void *sessionContext, const UA_NodeId *methodId,
                           void *methodContext, const UA_NodeId *objectId,
                           void *objectContext, size_t inputSize,
                           const UA_Variant *input, size_t outputSize,
                           UA_Variant *output)
{
    cout << "Close File Callback" << endl;
      uint32_t handle=*(UA_UInt32 *)(input[0].data);
      close(handle);
    return UA_STATUSCODE_GOOD;
}
int getNodeIdByPath(UA_Server *server,
                    UA_NodeId parentNode,
                    const int relativePathCnt,
                    const UA_QualifiedName targetNameArr[],
                    UA_NodeId *result)
{
    int ret = 0;

    UA_BrowsePathResult bpr = UA_Server_browseSimplifiedBrowsePath(server,
                                                                   parentNode, relativePathCnt, targetNameArr);

    if (bpr.statusCode != UA_STATUSCODE_GOOD || bpr.targetsSize < 1)
    {
        // printf("error: %s\n", UA_StatusCode_name(bpr.statusCode));
        ret = -1;
    }
    else
    {
        UA_NodeId_copy(&bpr.targets[0].targetId.nodeId, result);
    }

    UA_BrowsePathResult_clear(&bpr);
    return ret;
}
int main()
{
    UA_Server *server = UA_Server_new();

    // add a variable node to the adresspace
    UA_VariableAttributes attr = UA_VariableAttributes_default;
    UA_Int32 myInteger = 42;
    UA_Variant_setScalarCopy(&attr.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
    attr.description = UA_LOCALIZEDTEXT_ALLOC("en-US", "the answer");
    attr.displayName = UA_LOCALIZEDTEXT_ALLOC("en-US", "the answer");
    UA_NodeId myIntegerNodeId = UA_NODEID_STRING_ALLOC(1, "the.answer");
    UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME_ALLOC(1, "the answer");
    UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
    UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
    UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId,
                              parentReferenceNodeId, myIntegerName,
                              UA_NODEID_NULL, attr, NULL, NULL);
    // Add FileType
    UA_NodeId fileID;
    UA_ObjectAttributes fileAttr = UA_ObjectAttributes_default;
    fileAttr.displayName = UA_LOCALIZEDTEXT((char *)"en-US", (char *)"File");

    UA_Server_addObjectNode(server, UA_NODEID_NULL,
                            UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
                            UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
                            UA_QUALIFIEDNAME(1, (char *)"File"), UA_NODEID_NUMERIC(0, UA_NS0ID_FILETYPE),
                            fileAttr, NULL, &fileID);
    //set Writeable true
    UA_Boolean enabled = true;
    UA_Variant value;
    UA_Variant_setScalar(&value, &enabled, &UA_TYPES[UA_TYPES_BOOLEAN]);
     UA_QualifiedName targetNameArr[1] = {
        UA_QUALIFIEDNAME(0, (char *)"Writable")};
    UA_NodeId WritableNodeId;
    int ret = getNodeIdByPath(server, fileID,
                              1, targetNameArr, &WritableNodeId);
     UA_Server_writeValue(server, WritableNodeId, value);  
     //set UserWritable true
        UA_QualifiedName targetNameArrU[1] = {
        UA_QUALIFIEDNAME(0, (char *)"UserWritable")};
    UA_NodeId UserWritableNodeId;
    ret = getNodeIdByPath(server, fileID,
                              1, targetNameArrU, &UserWritableNodeId);
     UA_Server_writeValue(server, UserWritableNodeId, value);                          
    //Open
    UA_QualifiedName targetNameArrA[1] = {
        UA_QUALIFIEDNAME(0, (char *)"Open")};
    UA_NodeId OpenFileNodeId;
    ret = getNodeIdByPath(server, fileID,
                              1, targetNameArrA, &OpenFileNodeId);
    cout << "OpenFileNodeId:" << OpenFileNodeId.identifier.numeric << endl;
    UA_Server_setMethodNode_callback(server, OpenFileNodeId, OpenFileCallback);
    //Write
      UA_QualifiedName targetNameArrB[1] = {
        UA_QUALIFIEDNAME(0, (char *)"Write")};
    UA_NodeId WriteFileNodeId;
     ret = getNodeIdByPath(server, fileID,
                              1, targetNameArrB, &WriteFileNodeId);
    cout << "WriteFileNodeId:" << WriteFileNodeId.identifier.numeric << endl;
    UA_Server_setMethodNode_callback(server, WriteFileNodeId, WriteFileCallback);
    //Close File
     
      UA_QualifiedName targetNameArrC[1] = {
        UA_QUALIFIEDNAME(0, (char *)"Close")};
    UA_NodeId CloseFileNodeId;
     ret = getNodeIdByPath(server, fileID,
                              1, targetNameArrC, &CloseFileNodeId);
    cout << "CloseFileNodeId:" << CloseFileNodeId.identifier.numeric << endl;
    UA_Server_setMethodNode_callback(server, CloseFileNodeId, CloseFileCallback);
    //
    /* allocations on the heap need to be freed */
    UA_VariableAttributes_clear(&attr);
    UA_NodeId_clear(&myIntegerNodeId);
    UA_QualifiedName_clear(&myIntegerName);

    UA_StatusCode retval = UA_Server_runUntilInterrupt(server);

    UA_Server_delete(server);
    return retval == UA_STATUSCODE_GOOD ? EXIT_SUCCESS : EXIT_FAILURE;
}

 代码有一些调试的痕迹,但是在ubuntu 上是能够运行的。

使用uaExpert 测试

在FileType 实例击右键

点击 Write from local file

结束语

希望能够帮到你。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1062547.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux:TCP三握四挥简析

文章目录 1. 前言2. 背景3. TCP连接的建立和断开3.1 TCP协议状态机3.2 TCP的三握四挥3.2.1 TCP 连接建立的三次握手过程分析3.2.1.1 服务端和客户端套接字的创建3.2.1.2 服务端进入 LISTEN 状态3.2.1.3 服务端在 LISTEN 状态等待客户端的 SYN 请求3.2.1.4 客户端向服务端发送 S…

【云笔记篇】Microsoft OneNote笔记分区数据删除方法

【云笔记篇】Microsoft OneNote笔记分区数据删除方法 Microsoft OneNote删除分区数据需要在网页端操作才能彻底删除—【蘇小沐】 1、实验 系统版本Windows 11 专业工作站版22H2&#xff08;22621.1485&#xff09;&#xff1b;OneNoteOneNote 2016(版本 2303 Build 16.0.162…

香橙派Zero3安装miniconda3(问题多多,已全部解决)

文章目录 前言一、miniconda3版本二、使用步骤1.安装2.添加环境变量3.更新源4.创建新环境 总结另外 前言 你会遇到很多问题&#xff0c;按照我说的基本没问题。 香橙派是zero3。 一、miniconda3版本 Miniconda3-py37_4.9.2-Linux-aarch64.sh这个版本是测试没问题的&#xff0…

HDLbits : Module addsub

module top_module(input [31:0] a,input [31:0] b,input sub,output [31:0] sum );wire w1;add16 add16_1(a[15:0],b[15:0]^{16{sub}},sub,sum[15:0],w1);add16 add16_2(a[31:16],b[31:16]^{16{sub}},w1,sum[31:16],);endmodule 注意&#xff1a;sub位扩展

latex方程组编写,一种可以保证方程编号自适应的方法

问题描述&#xff1a; 在利用latex编写方程组时&#xff0c;可以有很多种方法&#xff0c;但不总是编辑好的公式能够显示出编号&#xff0c;故提出一种有效的方程组编写方法 方法&#xff1a; \begin{equation}X_{ t1}\left \{ \begin{matrix}\frac{x_{i}}{a} \quad\quad 0&l…

数学建模预测模型MATLAB代码大合集及皮尔逊相关性分析(无需调试、开源)

已知2010-2020数据&#xff0c;预测2021-2060数据 一、Logistic预测人口 %%logistic预测2021-2060年结果 clear;clc; X[7869.34, 8022.99, 8119.81, 8192.44, 8281.09, 8315.11, 8381.47, 8423.50, 8446.19, 8469.09, 8477.26]; nlength(X)-1; for t1:nZ(t)(X(t1)-X(t))/X(t1…

redis高可用(主从复制,哨兵,集群)

目录 一、主从复制&#xff1a; 1.主从复制介绍&#xff1a; 2.主从复制的作用&#xff1a; 3.主从复制流程&#xff1a; 4.搭建Redis 主从复制&#xff1a; 4.1 环境准备&#xff1a; 4.2 安装redis&#xff1a; 4.3 master节点修改 Redis 配置文件&#xff1a; 4.4 slave节点…

判断三条边是否构成三角形(Python实现)

组成三角形的三条边a,b,c需满足条件: ab>c ac>b bc>a 已知&#xff1a;三角形任意三条边的长度之和大于第三条边。 解题&#xff1a;定义3个变量a、b、c&#xff0c;让用户输入任意三个数字赋值给三个变量。判断三个变量中是否任意两个之和大于第三个数值。 判断条件之…

Docker快速搭建漏洞靶场指南

user: admin passwrod&#xff1a;password 查看容器是否已开启&#xff1a; 最常见漏洞&#xff1a; 防护级别提高后&#xff1a; 更改防护级别&#xff1a; 复现vulhub漏洞靶场&#xff1a; 开启&#xff1a; 一个是漏洞类型一个是真实的漏洞靶场。

VS的调式技巧你真的掌握了吗?

目录 什么是bug? 调式是什么&#xff1f;有多重要&#xff1f; 调试是什么&#xff1f; 调试的基本步骤 debug和release的介绍 windows环境调试介绍 1.调试环境的准备 2.学会快捷键 F11 VS F10 F9 & F5 3.调试时查看程序当前信息 查看临时变量的值 查看内存信…

LLMs: 近端策略优化PPO Proximal policy optimization

Dr. Ehsan Kamalinejad&#xff0c;通常简称为EK&#xff0c;是一位机器学习应用科学家。他目前是亚马逊NLP开发中的精英科学家。以前&#xff0c;他共同创办了Visual One&#xff0c;一家Y Combinator计算机视觉初创公司。在此之前&#xff0c;他曾担任苹果的首席机器学习工程…

WPS/word 表格跨行如何续表、和表的名称

1&#xff1a;具体操作&#xff1a; 将光标定位在跨页部分的第一行任意位置&#xff0c;按下快捷键ctrlshiftenter&#xff0c;就可以在跨页的表格上方插入空行&#xff08;在空行可以写&#xff0c;表1-3 xxxx&#xff08;续&#xff09;&#xff09; 在空行中输入…

NUWA论文阅读

论文链接&#xff1a;NUWA: Visual Synthesis Pre-training for Neural visUal World creAtion 文章目录 摘要引言相关工作视觉自回归模型视觉稀疏自注意 方法3D数据表征3D Nearby Self-Attention3D编码器-解码器训练目标 实验实现细节与SOTA比较T2I微调T2V微调V2V微调Sketch-t…

高效的ProtoBuf

一、背景 Google ProtoBuf介绍 这篇文章我们讲了怎么使用ProtoBuf进行序列化&#xff0c;但ProtoBuf怎么做到最高效的&#xff0c;它的数据又是如何压缩的&#xff0c;下面先看一个例子&#xff0c;然后再讲ProtoBuf压缩机制。 二、案例 网上有各种序列化方式性能对比&#…

elment以及elementPlus选中组件出现黑框问题解决!!

目录 问题&#xff1a; 图示&#xff1a; 解决方案&#xff1a; 问题&#xff1a; 使用elementPlus的按钮组件&#xff0c;点击按钮后会出现黑框&#xff0c;除非点击其他地方才能取消掉&#xff08;之前使用elment-ui其它组件时也出现过&#xff09; 图示&#xff1a; 解决方案…

2023网络安全工程师可以考哪些证书?只学习不考证怎么行呢?

想入网安行业的小可爱们&#xff0c;如果你在疯狂学习知识技能的同时&#xff0c;也计划着考取一些证书当做入门的金钥匙&#x1f511;。 先别急&#xff0c;如果还没有进入网络安全工作岗位&#xff0c;那么&#xff0c;首要任务是打好基础&#xff0c;学好网络安全技术。若是…

docker容器启动成功外部访问不到

1.重启docker服务 systemctl restart docker

9张图深入剖析ConcurrentHashMap

前言 在日常的开发中&#xff0c;我们经常使用key-value键值对的HashMap&#xff0c;其使用哈希表实现&#xff0c;用空间换取时间&#xff0c;提升查询性能 但在多线程的并发场景中&#xff0c;HashMap并不是线程安全的 如果想使用线程安全的&#xff0c;可以使用Concurren…

Swing程序设计(5)绝对布局,流布局

文章目录 前言一、布局管理器二、介绍 1.绝对布局2.流布局总结 前言 Swing窗体中&#xff0c;每一个组件都有大小和具体的位置。而在容器中摆放各种组件时&#xff0c;很难判断其组件的具体位置和大小。即一个完整的界面中&#xff0c;往往有多个组件&#xff0c;那么如何将这…

R函数optim()最小化或者最大化多参数函数

一、optimize()最小化或者最大化单参数函数 1.1函数介绍 函数功能描述&#xff1a;给定一个单参数函数f&#xff0c;需要找到使得f达到其最小值或者最大值的点。 使用optimize()函数最小化单参数函数时&#xff0c;需要指定最小化的函数f及其定义域&#xff08;x的上界和下界…