• open62541学习:文件传输


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

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

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

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

    文件类型(FileType)

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

    主要特征

    文件大小(size)

    定义文件的大小(以字节为单位)

    允许写(Writable)

    指示文件是否可写

    用户允许写(UserWritable)

    指示文件是否可写,同时考虑用户访问权限。

    打开计数器(OpenCount)

    指示文件上当前有效的文件句柄数。

    最大长度(MaxBytestringlength)

    上次打开时间(LastModifiedTime)

    指示上次修改文件的时间。

    建立文件实例的方法:

    1. UA_NodeId fileID;
    2. UA_ObjectAttributes fileAttr = UA_ObjectAttributes_default;
    3. fileAttr.displayName = UA_LOCALIZEDTEXT((char *)"en-US", (char *)"File");
    4. UA_Server_addObjectNode(server, UA_NODEID_NULL,
    5. UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
    6. UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
    7. UA_QUALIFIEDNAME(1, (char *)"File"), UA_NODEID_NUMERIC(0, UA_NS0ID_FILETYPE),
    8. 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 是文件句柄,当打开文件后,返回一个文件句柄,后续的操作使用文件句柄。

    写文件(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 文件传输的效率是不错的。

    open62541 文件传输

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

    代码(支持WriteFile)

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. /* Build Instructions (Linux)
    8. * - g++ server.cpp -lopen62541 -o server */
    9. using namespace std;
    10. char *UA_String2string(UA_String uaString)
    11. {
    12. char *convert = (char *)UA_malloc(sizeof(char) * uaString.length + 1);
    13. memcpy(convert, uaString.data, uaString.length);
    14. convert[uaString.length] = '\0';
    15. return convert;
    16. }
    17. UA_StatusCode OpenFileCallback(UA_Server *server, const UA_NodeId *sessionId,
    18. void *sessionContext, const UA_NodeId *methodId,
    19. void *methodContext, const UA_NodeId *objectId,
    20. void *objectContext, size_t inputSize,
    21. const UA_Variant *input, size_t outputSize,
    22. UA_Variant *output)
    23. {
    24. cout << "Open File Callback" << endl;
    25. int FileMode=*(UA_Byte *)(input[0].data);
    26. cout<<"mode:"<
    27. uint32_t handle=open("File.txt",O_CREAT|O_RDWR,FileMode);
    28. cout<<"handel:"<
    29. UA_Variant_setScalarCopy(output, &handle, &UA_TYPES[UA_TYPES_UINT32]);
    30. // output[0]=Handle;
    31. return UA_STATUSCODE_GOOD;
    32. }
    33. UA_StatusCode WriteFileCallback(UA_Server *server, const UA_NodeId *sessionId,
    34. void *sessionContext, const UA_NodeId *methodId,
    35. void *methodContext, const UA_NodeId *objectId,
    36. void *objectContext, size_t inputSize,
    37. const UA_Variant *input, size_t outputSize,
    38. UA_Variant *output)
    39. {
    40. cout << "Write File Callback" << endl;
    41. uint32_t handle=*(UA_UInt32 *)(input[0].data);
    42. cout<<"handle:"<
    43. UA_ByteString data=*(UA_ByteString *)(input[1].data);
    44. // cout<<"Data:"<
    45. write(handle,UA_String2string(data),data.length);
    46. return UA_STATUSCODE_GOOD;
    47. }
    48. UA_StatusCode CloseFileCallback(UA_Server *server, const UA_NodeId *sessionId,
    49. void *sessionContext, const UA_NodeId *methodId,
    50. void *methodContext, const UA_NodeId *objectId,
    51. void *objectContext, size_t inputSize,
    52. const UA_Variant *input, size_t outputSize,
    53. UA_Variant *output)
    54. {
    55. cout << "Close File Callback" << endl;
    56. uint32_t handle=*(UA_UInt32 *)(input[0].data);
    57. close(handle);
    58. return UA_STATUSCODE_GOOD;
    59. }
    60. int getNodeIdByPath(UA_Server *server,
    61. UA_NodeId parentNode,
    62. const int relativePathCnt,
    63. const UA_QualifiedName targetNameArr[],
    64. UA_NodeId *result)
    65. {
    66. int ret = 0;
    67. UA_BrowsePathResult bpr = UA_Server_browseSimplifiedBrowsePath(server,
    68. parentNode, relativePathCnt, targetNameArr);
    69. if (bpr.statusCode != UA_STATUSCODE_GOOD || bpr.targetsSize < 1)
    70. {
    71. // printf("error: %s\n", UA_StatusCode_name(bpr.statusCode));
    72. ret = -1;
    73. }
    74. else
    75. {
    76. UA_NodeId_copy(&bpr.targets[0].targetId.nodeId, result);
    77. }
    78. UA_BrowsePathResult_clear(&bpr);
    79. return ret;
    80. }
    81. int main()
    82. {
    83. UA_Server *server = UA_Server_new();
    84. // add a variable node to the adresspace
    85. UA_VariableAttributes attr = UA_VariableAttributes_default;
    86. UA_Int32 myInteger = 42;
    87. UA_Variant_setScalarCopy(&attr.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
    88. attr.description = UA_LOCALIZEDTEXT_ALLOC("en-US", "the answer");
    89. attr.displayName = UA_LOCALIZEDTEXT_ALLOC("en-US", "the answer");
    90. UA_NodeId myIntegerNodeId = UA_NODEID_STRING_ALLOC(1, "the.answer");
    91. UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME_ALLOC(1, "the answer");
    92. UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
    93. UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
    94. UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId,
    95. parentReferenceNodeId, myIntegerName,
    96. UA_NODEID_NULL, attr, NULL, NULL);
    97. // Add FileType
    98. UA_NodeId fileID;
    99. UA_ObjectAttributes fileAttr = UA_ObjectAttributes_default;
    100. fileAttr.displayName = UA_LOCALIZEDTEXT((char *)"en-US", (char *)"File");
    101. UA_Server_addObjectNode(server, UA_NODEID_NULL,
    102. UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
    103. UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
    104. UA_QUALIFIEDNAME(1, (char *)"File"), UA_NODEID_NUMERIC(0, UA_NS0ID_FILETYPE),
    105. fileAttr, NULL, &fileID);
    106. //set Writeable true
    107. UA_Boolean enabled = true;
    108. UA_Variant value;
    109. UA_Variant_setScalar(&value, &enabled, &UA_TYPES[UA_TYPES_BOOLEAN]);
    110. UA_QualifiedName targetNameArr[1] = {
    111. UA_QUALIFIEDNAME(0, (char *)"Writable")};
    112. UA_NodeId WritableNodeId;
    113. int ret = getNodeIdByPath(server, fileID,
    114. 1, targetNameArr, &WritableNodeId);
    115. UA_Server_writeValue(server, WritableNodeId, value);
    116. //set UserWritable true
    117. UA_QualifiedName targetNameArrU[1] = {
    118. UA_QUALIFIEDNAME(0, (char *)"UserWritable")};
    119. UA_NodeId UserWritableNodeId;
    120. ret = getNodeIdByPath(server, fileID,
    121. 1, targetNameArrU, &UserWritableNodeId);
    122. UA_Server_writeValue(server, UserWritableNodeId, value);
    123. //Open
    124. UA_QualifiedName targetNameArrA[1] = {
    125. UA_QUALIFIEDNAME(0, (char *)"Open")};
    126. UA_NodeId OpenFileNodeId;
    127. ret = getNodeIdByPath(server, fileID,
    128. 1, targetNameArrA, &OpenFileNodeId);
    129. cout << "OpenFileNodeId:" << OpenFileNodeId.identifier.numeric << endl;
    130. UA_Server_setMethodNode_callback(server, OpenFileNodeId, OpenFileCallback);
    131. //Write
    132. UA_QualifiedName targetNameArrB[1] = {
    133. UA_QUALIFIEDNAME(0, (char *)"Write")};
    134. UA_NodeId WriteFileNodeId;
    135. ret = getNodeIdByPath(server, fileID,
    136. 1, targetNameArrB, &WriteFileNodeId);
    137. cout << "WriteFileNodeId:" << WriteFileNodeId.identifier.numeric << endl;
    138. UA_Server_setMethodNode_callback(server, WriteFileNodeId, WriteFileCallback);
    139. //Close File
    140. UA_QualifiedName targetNameArrC[1] = {
    141. UA_QUALIFIEDNAME(0, (char *)"Close")};
    142. UA_NodeId CloseFileNodeId;
    143. ret = getNodeIdByPath(server, fileID,
    144. 1, targetNameArrC, &CloseFileNodeId);
    145. cout << "CloseFileNodeId:" << CloseFileNodeId.identifier.numeric << endl;
    146. UA_Server_setMethodNode_callback(server, CloseFileNodeId, CloseFileCallback);
    147. //
    148. /* allocations on the heap need to be freed */
    149. UA_VariableAttributes_clear(&attr);
    150. UA_NodeId_clear(&myIntegerNodeId);
    151. UA_QualifiedName_clear(&myIntegerName);
    152. UA_StatusCode retval = UA_Server_runUntilInterrupt(server);
    153. UA_Server_delete(server);
    154. return retval == UA_STATUSCODE_GOOD ? EXIT_SUCCESS : EXIT_FAILURE;
    155. }

     代码有一些调试的痕迹,但是在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