在之前的文章中,生成namespace文件是使用open62541提供的nodeset_compiler.py,根据nodeset_compiler.rst(位于open62541/doc/)里的描述,有更好的方法:使用cmake命令ua_generate_nodeset_and_datatypes来生成。
nodeset_compiler.py只能生成nodeset文件;ua_generate_nodeset_and_datatypes不仅可以生成nodeset文件,还能生成datatype文件和nodeid文件,另外可以很简单的处理依赖问题。
PS:ua_generate_nodeset_and_datatypes也是open62541提供的。
open62541也提供了例子,即example/nodeset/,但是看着明白却不知道怎么用。本文从实践的角度来讲解具体用法,开发环境是Debian 10
一 修改python脚本权限
因为ua_generate_nodeset_and_datatypes会调用python脚本generate_nodeid_header.py,所以先要给脚本添加权限,否则无法生成,这个坑也是我调试了很久才发现的…
chmod 755 open62541/tools/generate_nodeid_header.py
二 命令解释
ua_generate_nodeset_and_datatypes是一个自定义的CMake命令,位于文件open62541/tools/cmake/macros_public.cmake里,
其用法介绍如下,
其产生的CMake目标为open62541-generator-ns-${NAME}
,这个NAME由用户指定。该命令的输入参数有以下这些,
- Options:
- INTERNAL:Include internal headers. Required if custom datatypes are added.
- Arguments taking one value:
- NAME:Short name of the nodeset. E.g. ‘di’
- FILE_NS:Path to the NodeSet2.xml file. Multiple values can be passed. These nodesets will be combined into one output.
- [FILE_CSV]:Optional path to the .csv file containing the node ids, e.g. ‘OpcUaDiModel.csv’
- [FILE_BSD]:Optional path to the .bsd file containing the type definitions, e.g. ‘Opc.Ua.Di.Types.bsd’. Multiple files can be passed which will all combined to one resulting code.
- [BLACKLIST]:Blacklist file passed as --blacklist to the nodeset compiler. All the given nodes will be removed from the generated nodeset, including all the references to and from that node. The format is a node id per line. Supported formats: “i=123” (for NS0), “ns=2;s=asdf” (matches NS2 in that specific file), or recommended “ns=http://opcfoundation.org/UA/DI/;i=123” namespace index independent node id
- [TARGET_PREFIX]:Optional prefix for the resulting targets. Default
open62541-generator
- Arguments taking multiple values:
- [NAMESPACE_MAP]:Array of Namespace index mappings to indicate the final namespace index of a namespace uri when the server is started. This parameter is mandatory if FILE_CSV or FILE_BSD is set.
- [IMPORT_BSD]:Optional combination of types array and path to the .bsd file containing additional type definitions referenced by the FILES_BSD files. The value is separated with a hash sign, i.e. ‘UA_TYPES#${PROJECT_SOURCE_DIR}/deps/ua-nodeset/Schema/Opc.Ua.Types.bsd’.
Multiple files can be passed which will all be imported. - [DEPENDS]:Optional list of nodeset names on which this nodeset depends. These names must match any name from a previous call to this funtion. E.g. ‘di’ if you are generating the ‘plcopen’ nodeset
这里只需要注意:xx.NodeSet2.xml提供nodeset,.csv文件提供nodeid的定义,.bsd文件提供datatype的定义。
三 使用
首先搭建一个基本工程,这里不再赘述,工程里的CMakeLists.txt内容如下,
cmake_minimum_required(VERSION 3.5)
project(demo)
set(OPEN62541_VERSION "v1.3.4") # 设置open62541的版本号
set(UA_NAMESPACE_ZERO FULL CACHE STRINGS "xxx" FORCE) # UA_NAMESPACE_ZERO必须设置为FULL
# 必须要有这个,因为ua_generate_nodeset_and_datatypes由open62541提供
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/open62541)
# 必须设置变量open62541_TOOLS_DIR,否则报错,用户根据实际位置定义
set(open62541_TOOLS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/open62541/tools CACHE PATH "Path to the directory that contains the tooling of the stack")
# 必须设置变量open62541_NODESET_DIR,否则报错,用户根据实际位置定义
set(open62541_NODESET_DIR ${CMAKE_CURRENT_SOURCE_DIR}/open62541/deps/ua-nodeset CACHE PATH "Path to the directory that contains the OPC UA schema repository")
# PLCopen and Robotics requires the full ns0 as basis
if(UA_NAMESPACE_ZERO STREQUAL "FULL")
# Generate types and namespace for DI
ua_generate_nodeset_and_datatypes(
NAME "di"
FILE_CSV "${open62541_NODESET_DIR}/DI/OpcUaDiModel.csv"
FILE_BSD "${open62541_NODESET_DIR}/DI/Opc.Ua.Di.Types.bsd"
NAMESPACE_MAP "2:http://opcfoundation.org/UA/DI/"
FILE_NS "${open62541_NODESET_DIR}/DI/Opc.Ua.Di.NodeSet2.xml"
INTERNAL
)
# generate PLCopen namespace which is using DI
ua_generate_nodeset_and_datatypes(
NAME "plc"
# PLCopen does not define custom types. Only generate the nodeset
FILE_NS "${open62541_NODESET_DIR}/PLCopen/Opc.Ua.PLCopen.NodeSet2_V1.02.xml"
# PLCopen depends on the di nodeset, which must be generated before
DEPENDS "di"
INTERNAL
)
# Generate types and namespace for Robotics
ua_generate_nodeset_and_datatypes(
NAME "robotics"
FILE_CSV "${open62541_NODESET_DIR}/Robotics/Opc.Ua.Robotics.NodeIds.csv"
FILE_BSD "${open62541_NODESET_DIR}/Robotics/Opc.Ua.Robotics.Types.bsd"
NAMESPACE_MAP "3:http://opcfoundation.org/UA/Robotics/"
FILE_NS "${open62541_NODESET_DIR}/Robotics/Opc.Ua.Robotics.NodeSet2.xml"
DEPENDS "di"
INTERNAL
)
endif()
本文例子生成3个nodeset:DI,PLCopen和Robotics,其中PLCopen和Robotics依赖DI,相关nodeset文件在open62541/deps/ua-nodeset里。注意要先生成DI的nodeset,因为它是被另外2个依赖。
操作步骤如下:
- 修改python脚本权限,第一节已经说过
- cd到build目录下执行
cmake ..
- 生成DI的nodeset:
make open62541-generator-ns-di
, "di"就是NAME对应的值 - 生成PLCopen的nodeset:
make open62541-generator-ns-plc
,"plc"就是NAME对应的值 - 生成Robotics的nodeset:
make open62541-generator-ns-robotics
,"robotics"就是NAME对应的值
执行完毕之后,在build/src_generated/open62541下可以看到生成的.c/h文件,
过去使用nodeset_compiler.py只能生成namespace_xx_generated.c/h,而使用这个cmake命令还能生成xx_nodeids.h和types_xx_generated.c/h。
当然也可以根据需要来设置,如果需要生成xx_nodeids.h和types_xx_generated.c/h,那么就使用FILE_CSV和FILE_BSD来指定对应的文件,前提是这些csv和bsd文件事先存在;如果不需要,只要namespace_xx_generated.c/h,那么就可以删除FILE_CSV和FILE_BSD,例如Robotics,可以改成如下,
# Generate namespace for Robotics
ua_generate_nodeset_and_datatypes(
NAME "robotics"
FILE_NS "${open62541_NODESET_DIR}/Robotics/Opc.Ua.Robotics.NodeSet2.xml"
DEPENDS "di"
INTERNAL
)