作为一种通信协议,文件传输是非常重要的。例如传输执行程序,图片,配置文件等等。文件传输的机制和类型在 OPC UA 中已经存在很长时间了。FileType (作为ObjectType)和ImageType长期以来一直是内置模型的一部分,并且也用于许多配套规范(例如;用于设备机器,视觉和数控的配套规范)。
自版本 1.5.2 起,文件传输已从第 5 部分中删除,并给出了自己的部分(UA 第 20 部分:文件传输)。
在OPC UA DI中,该方法用于下载和上传软件。另一个成功的用例是Machinery 的ResultTransferType,它旨在为测量结果生成相应的报告并将其传输给客户端。
我计划使用Filetype 文件传输的方法来下载IEC61499 的功能块网络,功能块库。如此一来,将IEC61499 标准完全建立在opcua 的基础之上。而不采用其它自定义的协议。
FileType 是一个内置的对象类型,包含了一些特征变量和方法。
文件大小(size)
定义文件的大小(以字节为单位)
允许写(Writable)
指示文件是否可写
用户允许写(UserWritable)
指示文件是否可写,同时考虑用户访问权限。
打开计数器(OpenCount)
指示文件上当前有效的文件句柄数。
最大长度(MaxBytestringlength)
上次打开时间(LastModifiedTime)
指示上次修改文件的时间。
- 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 中的方法包括:
这几个方法与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 是文件句柄,当打开文件后,返回一个文件句柄,后续的操作使用文件句柄。
写文件(Write)
输入参数:
1 文件句柄 handle
3 写入数据 ByteString
读文件(Read)
输入参数:
1 文件句柄(Handle)
2 长度(Length)
输出参数:
1 读出数据(ByteString)
关闭文件
输入参数:文件句柄(handle)
获取位置(GetPosition)
提供文件句柄的当前位置。
输入参数:文件句柄(Handle)
输出参数:位置(UInt64 )
设置位置(SetPosition)
设置文件句柄的当前位置。如果调用读取或写入,则从该位置开始。如果位置高于文件大小,则位置设置为文件的末尾。
输入参数:
1 文件句柄(UInt32 )
2 位置(UInt64 )
OPC UA 文件传输的效率,国外有一些测试,结果如下:
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 文件传输的效率是不错的。
open61541 是开源opcua 项目,该项目中并没有对FileType 操作的支持,但是FileType 是一个普通的对象类型,完全可以使用基本的程序实现FileType 对象的建立。而方法回调函数和文件操作需要程序需要额外的代码。可惜的是open61541 项目中并没有FileType 的Example,网络上也没有相对完整的例子。
代码(支持WriteFile)
- #include
- #include
- #include
- #include
- #include
- #include
- /* 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:"<
- uint32_t handle=open("File.txt",O_CREAT|O_RDWR,FileMode);
- cout<<"handel:"<
- 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:"<
- UA_ByteString data=*(UA_ByteString *)(input[1].data);
- // cout<<"Data:"<
- 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,选择本地文件。
结束语
文件传输对于自动化控制器十分重要,在传统的控制器中,普遍采用专有协议,或者FTP,TCP/IP ,HTTP等非语义通信协议实现文件下载。造成了IDE 与下载协议紧耦合,阻碍了系统的开放性。OPCUA 定义了文件类型信息模型。为文件传输定义了规范。任何一个OPCUA 的Client 都能够实现文件的下载和上传。笔者主张控制器尽可能采用单一协议。开发工具与下载文件协议解构。实现完全开放,同时,使用OPCUA协议能够保证通信的安全性。
-
相关阅读:
Mac反编译APK
Pytest之用例执行--并发执行、重复执行、输出报告
ArcGIS_修改文本样式
BAT027:删除当前目录指定文件夹以外的文件夹
【Pytorch】深度学习之优化器
交叉验证Cross Validation
Delta3D(9)教程:添加消息发送和可激活体
Win10安装-我们无法创建新的分区,也找不到现有的分区
Maven
Python实例☞组织结构案例
-
原文地址:https://blog.csdn.net/yaojiawan/article/details/133583524