Last updated on April 10, 2022
Compile open62541 with a custom UANodeSet using CMake. This step will use nodeset_compiler.py to convert the UANodeSet2.xml to C99 header and source files and then compile the complete open62541 C99 application into a single executable.
This Post is part of the OPC UA Information Model Tutorial.
You can use the open62541 Nodeset Compiler directly, or create a CMake project which is using the provided macros.
I strongly recommend to use the CMake approach.
Especially for more complex setups where you have multiple NodeSets based on each other, manually passing the parameters to the Nodeset Compiler is very cumbersome. CMake will do this for you.
Also, the CMake macro also calls the datatype generator (custom datatypes) and nodeid generator (header file with node ID defines) for you.
A more complete example with heavy use of heavy usage of ModelDesign files and dependencies between them is shown in this project:
Contents
Building an OPC UA Server with a custom UANodeSet using CMake
The open62541 OPC UA Stack uses a Python Script (Nodeset Compiler) to generate compilable C99 Code out of NodeSet2.xml files.
The complete documentation on the Nodeset Compiler can be found here:
https://open62541.org/doc/current/nodeset_compiler.html
Over the last years the interface and parameters to this Python Script became somewhat complext and not as intuitive as they should be (shame on me (Stefan) as I’m the one who extended it).
Therefore I created a simpler to use CMake Macro which handles all the complex stuff for you, as shown in the following example:
# Generate types and namespace for DI ua_generate_nodeset_and_datatypes( NAME "di" FILE_CSV "${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/OpcUaDiModel.csv" FILE_BSD "${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/Opc.Ua.Di.Types.bsd" NAMESPACE_IDX 2 FILE_NS "${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/Opc.Ua.Di.NodeSet2.xml" ) # 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 "${PROJECT_SOURCE_DIR}/deps/ua-nodeset/PLCopen/Opc.Ua.Plc.NodeSet2.xml" # PLCopen depends on the di nodeset, which must be generated before DEPENDS "di" )
As you can see, you need the following files of a NodeSet:
- .csv: Used to generate the NodeID header to provide defines for specific node IDs instead of numbers
- .bsd: Used to generate custom data types and struct definitions if you define your own Data Types in the nodeset
- NodeSet2.xml: Contains the whole nodeset definition and is used to generate the initialization code for the open62541 server.
Create a new CMake Project
The first step is to create a new CMake project in a new folder, let’s name it FooServer
. In this folder create a CMakeLists.txt
file with the following content:
(see also: https://github.com/Pro/opcua-modeling-tutorial-server/blob/master/CMakeLists.txt)
cmake_minimum_required(VERSION 3.2) project(opcua-modeling-tutorial-server) # open62541 must be installed. # If in custom path, then use -DCMAKE_PREFIX_PATH=/home/user/install find_package(open62541 1.1 REQUIRED COMPONENTS FullNamespace) # Output directory for Nodeset Compiler set(GENERATE_OUTPUT_DIR "${CMAKE_BINARY_DIR}/src_generated/") file(MAKE_DIRECTORY "${GENERATE_OUTPUT_DIR}") include_directories("${GENERATE_OUTPUT_DIR}") ua_generate_nodeset_and_datatypes( NAME "foo_flt" TARGET_PREFIX "${PROJECT_NAME}" FILE_CSV "${PROJECT_SOURCE_DIR}/model/Published/FooFlt/FooFltModel.csv" FILE_BSD "${PROJECT_SOURCE_DIR}/model/Published/FooFlt/FooFlt.Types.bsd" OUTPUT_DIR "${GENERATE_OUTPUT_DIR}" # This namespace index must match the order in which you are adding the nodeset in the source code NAMESPACE_IDX 2 FILE_NS "${PROJECT_SOURCE_DIR}/model/Published/FooFlt/FooFlt.NodeSet2.xml" INTERNAL ) # Previous macro automatically sets some variables which hold the generated source code files using the provided NAME add_executable(opcua-modeling-tutorial-server ${UA_NODESET_FOO_FLT_SOURCES} ${UA_TYPES_FOO_FLT_SOURCES} main.c ) # Make sure the nodeset compiler is execute before compiling the main file add_dependencies(opcua-modeling-tutorial-server ${PROJECT_NAME}-ns-foo_flt ) target_link_libraries(opcua-modeling-tutorial-server open62541::open62541)
And copy the previously generated NodeSet2 files into the model/Published
subfolder:
tree ~/FooServer . └── CMakeLists.txt └── model ├── FooFltModel.csv ├── FooFltModel.xml ├── Published │ └── FooFlt │ ├── FooFlt.Classes.cs │ ├── FooFlt.Constants.cs │ ├── FooFlt.DataTypes.cs │ ├── FooFltModel.csv │ ├── FooFltModel.xml │ ├── FooFlt.NodeSet2.xml │ ├── FooFlt.NodeSet.xml │ ├── FooFlt.PredefinedNodes.uanodes │ ├── FooFlt.PredefinedNodes.xml │ ├── FooFlt.Types.bsd │ └── FooFlt.Types.xsd
Create a custom Server Code which uses your Model
After you created a new CMake project in previous section, you now need to define your Source Code which is initializing an OPC UA Server with your model.
Therefore, create a new file called main.c
in the FooServer
directory with the following content:
(see also https://github.com/Pro/opcua-modeling-tutorial-server/blob/master/main.c)
#include <open62541/plugin/log_stdout.h> #include <open62541/server.h> #include <open62541/server_config_default.h> /* Files namespace_foo_flt_generated.h and namespace_foo_flt_generated.c are created from FooFlt.NodeSet2.xml in the * /src_generated directory by CMake */ #include "foo_flt_nodeids.h" #include "namespace_foo_flt_generated.h" #include <signal.h> #include <stdlib.h> 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(int argc, char** argv) { signal(SIGINT, stopHandler); signal(SIGTERM, stopHandler); UA_Server *server = UA_Server_new(); UA_ServerConfig_setDefault(UA_Server_getConfig(server)); UA_StatusCode retval; /* create nodes from nodeset */ if(namespace_foo_flt_generated(server) != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Could not add the Foo FLT nodeset. " "Check previous output for any error."); retval = UA_STATUSCODE_BADUNEXPECTEDERROR; } else { // Do some additional stuff with the nodes // this will just get the namespace index, since it is already added to the server UA_UInt16 nsIdx = UA_Server_addNamespace(server, "https://new.foo.com/zebra-compression/flattening-and-subspacefolding/UA/"); UA_NodeId testInstanceId = UA_NODEID_NUMERIC(nsIdx, UA_FOO_FLTID_APE); UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "The Ape has ns=%d;id=%d", testInstanceId.namespaceIndex, testInstanceId.identifier.numeric); retval = UA_Server_run(server, &running); } UA_Server_delete(server); return retval == UA_STATUSCODE_GOOD ? EXIT_SUCCESS : EXIT_FAILURE; }
Especially look at line 29 which is is calling the gererated Nodeset initialization. This call will create all the nodes defined in your NodeSet2.xml file inside the open62541 OPC UA server.
To build and run this server just use CMake and make. Note, if you installed open62541 in a custom location, you may need to use the CMAKE_PREFIX_PATH
option:
cd ~/FooServer mkdir build && cd build cmake -DCMAKE_PREFIX_PATH=/home/user/install .. make -j # Then run the server: ./opcua-modeling-tutorial-server
Now you can connect any OPC UA Client to your server and browse it.
Here is an example output:
$ make -j Scanning dependencies of target opcua-modeling-tutorial-server-ids-foo_flt Scanning dependencies of target opcua-modeling-tutorial-server-types-foo_flt [ 22%] Generating src_generated/types_foo_flt_generated.c, src_generated/types_foo_flt_generated.h, src_generated/types_foo_flt_generated_handling.h, src_generated/types_foo_flt_generated_encoding_binary.h [ 22%] Generating src_generated/foo_flt_nodeids.h [ 22%] Built target opcua-modeling-tutorial-server-ids-foo_flt [ 22%] Built target opcua-modeling-tutorial-server-types-foo_flt Scanning dependencies of target opcua-modeling-tutorial-server-ns-foo_flt [ 33%] Generating src_generated/namespace_foo_flt_generated.c, src_generated/namespace_foo_flt_generated.h INFO:__main__:Preprocessing (existing) /home/user/install/open62541/share/open62541/tools/ua-nodeset/Schema/Opc.Ua.NodeSet2.xml INFO:__main__:Preprocessing /home/user/FooServer/model/Published/FooFlt/FooFlt.NodeSet2.xml INFO:__main__:Generating Code for Backend: open62541 INFO:__main__:NodeSet generation code successfully printed [ 33%] Built target opcua-modeling-tutorial-server-ns-foo_flt Scanning dependencies of target opcua-modeling-tutorial-server [ 44%] Building C object CMakeFiles/opcua-modeling-tutorial-server.dir/src_generated/namespace_foo_flt_generated.c.o [ 55%] Building C object CMakeFiles/opcua-modeling-tutorial-server.dir/src_generated/types_foo_flt_generated.c.o [ 66%] Building C object CMakeFiles/opcua-modeling-tutorial-server.dir/main.c.o [ 77%] Linking C executable opcua-modeling-tutorial-server [100%] Built target opcua-modeling-tutorial-server $ ls CMakeFiles CMakeCache.txt Makefile src_generated cmake_install.cmake opcua-modeling-tutorial-server $ ./opcua-modeling-tutorial-server [2020-08-31 14:26:27.897 (UTC+0200)] warn/server AccessControl: Unconfigured AccessControl. Users have all permissions. [2020-08-31 14:26:27.897 (UTC+0200)] info/server AccessControl: Anonymous login is enabled [2020-08-31 14:26:27.897 (UTC+0200)] warn/server Username/Password configured, but no encrypting SecurityPolicy. This can leak credentials on the network. [2020-08-31 14:26:27.897 (UTC+0200)] warn/userland AcceptAll Certificate Verification. Any remote certificate will be accepted. [2020-08-31 14:26:27.898 (UTC+0200)] info/server The Ape has ns=2;id=15111 [2020-08-31 14:26:27.898 (UTC+0200)] info/network TCP network layer listening on opc.tcp://localhost:4840/ ^C[2020-08-31 14:26:32.597 (UTC+0200)] info/server received ctrl-c [2020-08-31 14:26:32.597 (UTC+0200)] info/network Shutting down the TCP network layer
And the view in UaExpert:

Building an example server with a custom UANodeset and gcc (the manual, hard way)
Instead of blindly relying on the pre-existing examples and make files of open62541, a manual build of the custom UANodeSet and open62541 OPC UA server will yield additional insights.
Manual validation and compilation of an UANodeSet
The main reference is: https://open62541.org/doc/current/nodeset_compiler.html
Before compiling an open62541 OPC UA server, the target UANodeSet file must be validated and compiled to C99 source and header files.
A valid FOO UANodeSet file FooFltModel.xml is provided in the Github Repo of this tutorial, including the generated NodeSet2.xml files in the Published folder:
https://github.com/Pro/opcua-modeling-tutorial/tree/master-published/Published/FooFlt
Compile the UANodeSet according to the documentation in the folder created at the beginning of this post:
cd ~/FooServer python ~/open62541/tools/nodeset_compiler/nodeset_compiler.py --types-array=UA_TYPES --existing ~/open62541/deps/ua-nodeset/Schema/Opc.Ua.NodeSet2.xml --xml model/Published/FooFlt/FooFlt.NodeSet2.xml FooFlt # INFO:__main__:Preprocessing (existing) /home/user/open62541/deps/ua-# nodeset/Schema/Opc.Ua.NodeSet2.xml # INFO:__main__:Preprocessing model/Published/FooFlt/FooFlt.NodeSet2.xml # INFO:__main__:Generating Code for Backend: open62541 # INFO:__main__:NodeSet generation code successfully printed
This will generate two source files, namely FooFlt.c
and FooFlt.h
.
At this point the UANodeSet is ready to be compiled into a open62541.
Building an open62541 OPC UA server with a custom UANodeset by hand
After manually creating the .c and .h file from the UANodeset, you need a main file which uses the generated source code.
Use this slightly adapted main.c file. Especially note the changed include files and that the Node IDs are not availalbe:
#include <open62541/plugin/log_stdout.h> #include <open62541/server.h> #include <open62541/server_config_default.h> /* Files namespace_foo_flt_generated.h and namespace_foo_flt_generated.c are created from FooFlt.NodeSet2.xml in the * /src_generated directory by CMake */ #include "FooFlt.h" #include <signal.h> #include <stdlib.h> 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(int argc, char** argv) { signal(SIGINT, stopHandler); signal(SIGTERM, stopHandler); UA_Server *server = UA_Server_new(); UA_ServerConfig_setDefault(UA_Server_getConfig(server)); UA_StatusCode retval; /* create nodes from nodeset */ if(FooFlt(server) != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Could not add the Foo FLT nodeset. " "Check previous output for any error."); retval = UA_STATUSCODE_BADUNEXPECTEDERROR; } else { // Do some additional stuff with the nodes // this will just get the namespace index, since it is already added to the server UA_UInt16 nsIdx = UA_Server_addNamespace(server, "https://new.foo.com/zebra-compression/flattening-and-subspacefolding/UA/"); // UA_FOO_FLTID_APE = 15111 UA_NodeId testInstanceId = UA_NODEID_NUMERIC(nsIdx, 15111); UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "The Ape has ns=%d;id=%d", testInstanceId.namespaceIndex, testInstanceId.identifier.numeric); retval = UA_Server_run(server, &running); } UA_Server_delete(server); return retval == UA_STATUSCODE_GOOD ? EXIT_SUCCESS : EXIT_FAILURE; }
Then compile the server:
gcc -std=c99 -I$HOME/install/include -L$HOME/install/lib main_manual.c FooFlt.c -lopen62541 -lmbedtls -lmbedx509 -lmbedcrypto -o myServer
To execute the generated server, make sure to add your custom installation path to the LD_LIBRARY_PATH, otherwise the open62541 library is not found:
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$HOME/install/lib ./myServer

Success!
Troubleshooting
Exception: Reference ns=1;i=15103–[ns=0;i=40]–>ns=0;i=2368 has an unknown target
This exception is thrown because the example server is compiled with Opc.Ua.NodeSet2.Reduced.xml
which does not support AnalogItemType
.
Compiling the example server with a custom nodeset may fail as follows
[ 78%] Built target tutorial_client_firststeps [ 78%] Generating ../../src_generated/open62541/namespace_example_generated.c, ../../src_generated/open62541/namespace_example_generated.h INFO:__main__:Preprocessing (existing) /home/user/open62541/tools/schema/Opc.Ua.NodeSet2.Reduced.xml INFO:__main__:Preprocessing /home/user/open62541/examples/nodeset/server_nodeset.xml ['http://opcfoundation.org/UA/', u'https://new.foo.com/zebra-compression/flattening-and-subspacefolding/UA/'] Traceback (most recent call last): File "/home/user/open62541/tools/nodeset_compiler/nodeset_compiler.py", line 154, in <module> ns.sanitize() File "/home/user/open62541/tools/nodeset_compiler/nodeset.py", line 131, in sanitize raise Exception("Reference " + str(ref) + " has an unknown target") Exception: Reference ns=1;i=15103--[ns=0;i=40]-->ns=0;i=2368 has an unknown target make[2]: *** [examples/nodeset/CMakeFiles/server_nodeset.dir/build.make:70: src_generated/open62541/namespace_example_generated.c] Error 1 make[1]: *** [CMakeFiles/Makefile2:2190: examples/nodeset/CMakeFiles/server_nodeset.dir/all] Error 2 make: *** [Makefile:152: all] Error 2 user@debian:~/open62541/build$
The output indicates:
- calling
nodeset_compiler.py
is part of the compilation process and breaks it- it is called on:
open62541/tools/schema/Opc.Ua.NodeSet2.Reduced.xml
open62541/examples/nodeset/server_nodeset.xml
the example nodeset that is replaced with the custom nodeset before compilation
- it is called on:
- and that
Reference ns=1;i=15103--[ns=0;i=40]-->ns=0;i=2368 has an unknown target
The missing 2368
can be located in https://github.com/OPCFoundation/UA-Nodeset (which is a git submodule of https://github.com/OPCFoundation/UA-ModelCompiler)
johndoe@winpc MINGW64 /c/FLT_SW/UA-ModelCompiler/Published/Schema ((5bbf784...)) $ grep -r 2368 NodeIds.csv:AnalogItemType,2368,VariableType Opc.Ua.Constants.cs: public const uint AnalogItemType = 2368; Opc.Ua.NodeSet.xml: <Identifier>i=2368</Identifier> Opc.Ua.NodeSet.xml: <Identifier>i=2368</Identifier> Opc.Ua.NodeSet.xml: <Identifier>i=2368</Identifier> Opc.Ua.NodeSet.xml: <Identifier>i=2368</Identifier> Opc.Ua.NodeSet.xml: <Identifier>i=2368</Identifier> Opc.Ua.NodeSet2.Part8.xml: <UAVariableType NodeId="i=2368" BrowseName="AnalogItemType" DataType="Number" ValueRank="-2"> Opc.Ua.NodeSet2.Part8.xml: <UAVariable NodeId="i=2370" BrowseName="InstrumentRange" ParentNodeId="i=2368" DataType="i=884"> Opc.Ua.NodeSet2.Part8.xml: <Reference ReferenceType="HasProperty" IsForward="false">i=2368</Reference> Opc.Ua.NodeSet2.Part8.xml: <UAVariable NodeId="i=2369" BrowseName="EURange" ParentNodeId="i=2368" DataType="i=884"> Opc.Ua.NodeSet2.Part8.xml: <Reference ReferenceType="HasProperty" IsForward="false">i=2368</Reference> Opc.Ua.NodeSet2.Part8.xml: <UAVariable NodeId="i=2371" BrowseName="EngineeringUnits" ParentNodeId="i=2368" DataType="i=887"> Opc.Ua.NodeSet2.Part8.xml: <Reference ReferenceType="HasProperty" IsForward="false">i=2368</Reference> Opc.Ua.NodeSet2.xml: <UAVariableType NodeId="i=2368" BrowseName="AnalogItemType" DataType="Number" ValueRank="-2"> Opc.Ua.NodeSet2.xml: <UAVariable NodeId="i=2370" BrowseName="InstrumentRange" ParentNodeId="i=2368" DataType="i=884"> Opc.Ua.NodeSet2.xml: <Reference ReferenceType="HasProperty" IsForward="false">i=2368</Reference> Opc.Ua.NodeSet2.xml: <UAVariable NodeId="i=2369" BrowseName="EURange" ParentNodeId="i=2368" DataType="i=884"> Opc.Ua.NodeSet2.xml: <Reference ReferenceType="HasProperty" IsForward="false">i=2368</Reference> Opc.Ua.NodeSet2.xml: <UAVariable NodeId="i=2371" BrowseName="EngineeringUnits" ParentNodeId="i=2368" DataType="i=887"> Opc.Ua.NodeSet2.xml: <Reference ReferenceType="HasProperty" IsForward="false">i=2368</Reference> Opc.Ua.PredefinedNodes.xml: <uax:Identifier>i=2368</uax:Identifier>
Pay special attention to
Opc.Ua.NodeSet2.Part8.xml: <UAVariableType NodeId="i=2368" BrowseName="AnalogItemType" DataType="Number" ValueRank="-2"> Opc.Ua.NodeSet2.xml: <UAVariableType NodeId="i=2368" BrowseName="AnalogItemType" DataType="Number" ValueRank="-2">
This leads to the suspicion, that Opc.Ua.NodeSet2.Reduced.xml
does not hold the required node. To verify that, call nodeset_compiler.py
with the full Opc.Ua.NodeSet2.xml
user@debian:~/open62541/tools/nodeset_compiler$ python ./nodeset_compiler.py --types-array=UA_TYPES --existing ../../deps/ua-nodeset/Schema/Opc.Ua.NodeSet2.xml --xml ../../examples/nodeset/server_nodeset.xml temporary_garbage_file INFO:__main__:Preprocessing (existing) ../../deps/ua-nodeset/Schema/Opc.Ua.NodeSet2.xml INFO:__main__:Preprocessing ../../examples/nodeset/server_nodeset.xml INFO:__main__:Generating Code for Backend: open62541 INFO:__main__:NodeSet generation code successfully printed
Consequently, one of the following solutions must be implemented:
- make the sample servers use a more complete nodeset than
Opc.Ua.NodeSet2.Reduced.xml
- write a custom CMake file as mentioned at https://open62541.org/doc/current/nodeset_compiler.html#getting-started
The first solution is implemented by replacing the reduced nodeset with the full nodeset as indicated by the following diff
:
user@debian:~/open62541$ git diff examples/nodeset/CMakeLists.txt diff --git a/examples/nodeset/CMakeLists.txt b/examples/nodeset/CMakeLists.txt index 073db5f0..49d23332 100644 --- a/examples/nodeset/CMakeLists.txt +++ b/examples/nodeset/CMakeLists.txt @@ -20,7 +20,7 @@ if(NOT CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) ua_generate_nodeset_and_datatypes( NAME "example" FILE_NS "${FILE_NS_DIRPREFIX}/server_nodeset.xml" - DEPENDS "${CMAKE_SOURCE_DIR}/tools/schema/Opc.Ua.NodeSet2.Reduced.xml" + DEPENDS "${CMAKE_SOURCE_DIR}/deps/ua-nodeset/Schema/Opc.Ua.NodeSet2.xml" ) else() # standalone examples build expects already installed Opc.Ua.NodeSet2.Reduced.xml
This change allows to build the open62541 example server with AnalogItemTypes and EUInformation.
Note: Alternative reduced standard nodesets are available from folder
open62541/tools/schema
KeyError: 65535 self.browseName.ns = nsMapping[self.browseName.ns]
Compilation of the UANodeSet may fail as follows:
user@debian:~/open62541/tools/nodeset_compiler$ python ./nodeset_compiler.py --types-array=UA_TYPES --existing ../../deps/ua-nodeset/Schema/Opc.Ua.NodeSet2.xml --xml ../../examples/nodeset/server_nodeset.xml server_nodeset INFO:__main__:Preprocessing (existing) ../../deps/ua-nodeset/Schema/Opc.Ua.NodeSet2.xml INFO:__main__:Preprocessing ../../examples/nodeset/server_nodeset.xml Traceback (most recent call last): File "./nodeset_compiler.py", line 131, in <module> ns.addNodeSet(xmlfile, typesArray=getTypesArray(nsCount)) File "/home/user/open62541/tools/nodeset_compiler/nodeset.py", line 296, in addNodeSet node.replaceNamespaces(namespaceMapping) File "/home/user/open62541/tools/nodeset_compiler/nodes.py", line 177, in replaceNamespaces self.browseName.ns = nsMapping[self.browseName.ns] KeyError: 65535
Note: Be aware that in this example
deps/ua-nodeset/Schema/Opc.Ua.NodeSet2.xml
is used, while the example servers of open62541 use reduced nodesets! This is relevant when using AnalogItemType or EUInformation types.
This is most likely caused by a missing namespace prefix like in the example below:

Insert the missing prefix to the ModelDesign, regenerate the UANodeSet with UA-ModelCompiler and rerun nodeset_compiler.py
Continue with Step 10
First of all thanks a lot for your work on these tutorials.
I have been looking for a long time instructions to build an OPC UA server from a nodeset2.xml file. In fact, I am trying to implement the Openfuncdation Robotics and automation Companion.
I followed the instructions from the Step 1 of the “OPC UA Information Model Tutorial”.I tried to implement the FooFlt example, but I got problems using the CMake for building the server:
CMake Error at /usr/local/lib/cmake/open62541/open62541Macros.cmake:488 (message):
ua_generate_nodeset_and_datatypes function requires NAMESPACE_MAP argument
if any of FILE_CSV or FILE_BSD are set
Call Stack (most recent call first):
CMakeLists.txt:10 (ua_generate_nodeset_and_datatypes)
— Configuring incomplete, errors occurred!
It looks like there was a problem to find the .bsd, and .csv files, but I verified the path and the files many times.
Then I decided to use the nodeset_compiler.py script:
python ~/open62541/tools/nodeset_compiler/nodeset_compiler.py –types-array=UA_TYPES –existing ~/open62541/deps/ua-nodeset/Schema/Opc.Ua.NodeSet2.xml –xml model/Published/FooFlt/FooFlt.NodeSet2.xml FooFlt
and
gcc -std=c99 -I$HOME/install/include -L$HOME/install/lib main.c FooFlt.c -lopen62541 -lmbedtls -lmbedx509 -lmbedcrypto -o myServer
(the manual, hard way)
Then I launched the executable an I got:
~/FooServer$ ./myServer
[2020-11-03 11:30:29.761 (UTC+0100)] warn/server AccessControl: Unconfigured AccessControl. Users have all permissions.
[2020-11-03 11:30:29.761 (UTC+0100)] info/server AccessControl: Anonymous login is enabled
[2020-11-03 11:30:29.761 (UTC+0100)] warn/server Username/Password configured, but no encrypting SecurityPolicy. This can leak credentials on the network.
[2020-11-03 11:30:29.761 (UTC+0100)] warn/userland AcceptAll Certificate Verification. Any remote certificate will be accepted.
[2020-11-03 11:30:29.762 (UTC+0100)] error/server Could not add the Foo FLT nodeset. Check previous output for any error.
if anyone can help me solve the bug or tell me where I can find information related to the error, I would be very grateful.
Hi Sebastian,
regarding your first error message:
CMake Error at /usr/local/lib/cmake/open62541/open62541Macros.cmake:488 (message):
ua_generate_nodeset_and_datatypes function requires NAMESPACE_MAP argument
if any of FILE_CSV or FILE_BSD are set
This is NOT about missing csv or bsd files. They are fine.
This is about the CMake function ua_generate_nodeset_and_datatypes wich requires NAMESPACE_MAP as argument BECAUSE you are using FILE_CSV and FILE_BSD.
1) If you google for “NAMESPACE_MAP open62541”, your first result will be the official documentation
https://open62541.org/doc/current/nodeset_compiler.html
Look at how they are using it. My first attempt would be to replace NAMESPACE_IDX with NAMESPACE_MAP (and respective values).
2) What is the console output of
python ~/open62541/tools/nodeset_compiler/nodeset_compiler.py –types-array=UA_TYPES –existing ~/open62541/deps/ua-nodeset/Schema/Opc.Ua.NodeSet2.xml –xml model/Published/FooFlt/FooFlt.NodeSet2.xml FooFlt
3) What is the console output of
gcc -std=c99 -I$HOME/install/include -L$HOME/install/lib main.c FooFlt.c -lopen62541 -lmbedtls -lmbedx509 -lmbedcrypto -o myServer
Hi!
Thanks for these tutorials. Helped me a lot in my journey on OPCUA.
I have developed an information model for a Robot and i am building the CMakeLists.txt file for my project. My model consists of multiple namespaces. For example my model built on (NS4) is dependent on OPCUA Robotics namespace (NS2) and another custom namespace (NS3).
Since there is no official bsd file for OPCUA Robotics, the issue arises when using the (ua_generate_nodeset_and_datatypes) function with NAMESPACE_MAP variable in CMake. ( The error requires both csv and bsd file if NAMESPACE_MAP is used)
To work around, it I created my own bsd & csv files for OPCUA Robotics, however it resulted in makefile execution issues.
I am working on this issue concurrently, any suggestions on how to include the Robotics NS as an intermediate NS with ua_generate_nodeset_and_datatypes) function & NAMESPACE_MAP would be helpful.
Thanks in advance.
Hi,
please have look at the notice at the very beginning of this article:
It shows how to manage dependencies between several namespaces. Have a look at the CMakeLists.txt files in the subdirectories as well.
I recommend you post your error messages when looking for help. Without those, I doubt anyone can or will help you in a meaningful way.
Kind regards
PS: The CSV and BSD files are optional. Here is their use case:
If you compile your model for the first time, the compiler will generate a CSV and BSD file. Those files will document the types and node IDs assigned to the nodes of your model. This ID assignment is somewhat arbitrary. Let us assume that node ‘foo’ received ID ‘123’.
If you change your model and recompile WITHOUT taking into account the CSV and BSD files, node ‘foo’ may now be assigned a different ID ‘234’.
If you change your model and recompile WITH taking into account the CSV and BSD files, they make sure that nodes will be assigned the SAME ID they have been assigned initially. That is important, because OPC UA depends on those IDs during runtime. If your node changes its ID with a new release of your model, OPC UA clients will not be able to successfully recognize that node as the same node they used before.
Hope that helps.
Thank you for your quick answer.
I applied your suggestion about using the NAMESPACE_MAP flag. It solved my bug.
I was able to successfully build the FOOFLT server example and running it.
Then I applied the same procedure with the Opc.Ua.Robotics.NodeSet2.xml, which I recovered from the official site:
https://opcfoundation.org/developer-tools/specifications-opc-ua-information-models/opc-unified-architecture-for-robotics/#:~:text=The%20OPC%20UA%20Robotics%20Companion,Industrial%20robots
(Link to the nodeset2.xml and nodels.cvs in the annexe A ).
For building the server I used the Cmake function:
ua_generate_nodeset_and_datatypes(
NAME “Robotics”
TARGET_PREFIX “${PROJECT_NAME}”
FILE_NS “${PROJECT_SOURCE_DIR}/Opc.Ua.Robotics.NodeSet2.xml”
# FILE_CSV “${PROJECT_SOURCE_DIR}/NodeIds.csv”
# NAMESPACE_MAP “1:http://opcfoundation.org/UA/Robotics/”
OUTPUT_DIR “${GENERATE_OUTPUT_DIR}”
INTERNAL
)
Called by:
cmake -DCMAKE_PREFIX_PATH=$USER/install ..
the console output:
— The C compiler identification is GNU 7.5.0
— The CXX compiler identification is GNU 7.5.0
— Check for working C compiler: /usr/bin/cc
— Check for working C compiler: /usr/bin/cc — works
— Detecting C compiler ABI info
— Detecting C compiler ABI info – done
— Detecting C compile features
— Detecting C compile features – done
— Check for working CXX compiler: /usr/bin/c++
— Check for working CXX compiler: /usr/bin/c++ — works
— Detecting CXX compiler ABI info
— Detecting CXX compiler ABI info – done
— Detecting CXX compile features
— Detecting CXX compile features – done
— Found PythonInterp: /usr/bin/python (found version “3.6.9”)
— Configuring done
— Generating done
— Build files have been written to: /home/ubuntu18ros/OPC-UA-Robotics-Model-Server/build
However, when I ran the Makefile I got:
Scanning dependencies of target opcua-Robotics-server-ns-Robotics
[ 20%] Generating src_generated/namespace_Robotics_generated.c, src_generated/namespace_Robotics_generated.h
INFO:__main__:Preprocessing (existing) /usr/local/share/open62541/tools/ua-nodeset/Schema/Opc.Ua.NodeSet2.xml
INFO:__main__:Preprocessing /home/ubuntu18ros/OPC-UA-Robotics-Model-Server/Opc.Ua.Robotics.NodeSet2.xml
[‘http://opcfoundation.org/UA/’, ‘http://opcfoundation.org/UA/Robotics/’, ‘http://opcfoundation.org/UA/DI/’]
Traceback (most recent call last):
File “/usr/local/share/open62541/tools/nodeset_compiler/nodeset_compiler.py”, line 154, in
ns.sanitize()
File “/usr/local/share/open62541/tools/nodeset_compiler/nodeset.py”, line 131, in sanitize
raise Exception(“Reference ” + str(ref) + ” has an unknown target”)
Exception: Reference ns=1;i=1002<–[ns=0;i=45]–ns=2;i=15063 has an unknown target
CMakeFiles/opcua-Robotics-server-ns-Robotics.dir/build.make:69: recipe for target 'src_generated/namespace_Robotics_generated.c' failed
make[2]: *** [src_generated/namespace_Robotics_generated.c] Error 1
CMakeFiles/Makefile2:67: recipe for target 'CMakeFiles/opcua-Robotics-server-ns-Robotics.dir/all' failed
make[1]: *** [CMakeFiles/opcua-Robotics-server-ns-Robotics.dir/all] Error 2
Makefile:83: recipe for target 'all' failed
make: *** [all] Error 2
I didn't use FILE_CSV , nether the NAMESPACE_MAP on the ua_generate_nodeset_and_datatypes() method, car the OPC UA Companion Specification doesn't provides a .BSD file.
I appreciate a lot the help to solve this problem. I'm beginner on OPC UA.
My complete project:
https://github.com/scepedaesp2018/OPC-UA-Robotics-Model-Server
Hi Sebastian,
The key information of your error is provided by the following line
Exception: Reference ns=1;i=1002<–[ns=0;i=45]–ns=2;i=15063 has an unknown target
I hope you notice, that this error message and its solution is discussed in this _very_ article.
https://opcua.rocks/step-9-using-a-custom-uanodeset-with-open62541/#Exception_Reference_ns1i151038211ns0i408211gtns0i2368_has_an_unknown_target
Thanks,
I also thought it was the error of this post the first time, so I used Opc.Ua.NodeSet2.xml as you can see in the Makefile’s results.
Hi Sebastian,
you have different namespaces and indices in your error message. Applying the specific solution from the article (use full nodeset instead of reduced nodeset) to your problem will most likely _not_ help. You have to follow the presented _method_
That is:
Research what nodes are represented by the indices in your error message and deduce the adequate solution for your problem.
Hi,
I am trying to compile my Nodeset consisting of 5 Namespaces using CMakeLists.txt. I have been successful in building the files using CMAKE command.
However, when I run the MAKE command, this is the encountering this error. The error is shown below.
[ 63%] Linking CXX executable opcua_server
c++: fatal error: Killed signal terminated program lto1
compilation terminated.
lto-wrapper: fatal error: /usr/bin/c++ returned 1 exit status
compilation terminated.
/usr/bin/ld: error: lto-wrapper failed
collect2: error: ld returned 1 exit status
make[2]: *** [CMakeFiles/opcua_server.dir/build.make:346: opcua_server] Error 1
make[1]: *** [CMakeFiles/Makefile2:419: CMakeFiles/opcua_server.dir/all] Error 2
make: *** [Makefile:84: all] Error 2
I am trying to understand this error. Any suggestions from your end would be helpful.
Thanks in advance.
Hi,
please see a related comment over at
https://opcua.rocks/step-1-setup-open62541-on-linux-build-first-server/
Search for “-fno-lto” but read the rest of the comment too.
Good luck!
Hi,
I am trying to compile the IO-Link nodeset to c on linux but it fails and maybe you can help me.
I changed the example like this:
—————————————————————————————–
# Generate types and namespace for DI
ua_generate_nodeset_and_datatypes(
NAME “di”
TARGET_PREFIX “${PROJECT_NAME}”
FILE_CSV “${PROJECT_SOURCE_DIR}/model/Published/DI/OpcUaDiModel.csv”
FILE_BSD “${PROJECT_SOURCE_DIR}/model/Published/DI/Opc.Ua.Di.Types.bsd”
OUTPUT_DIR “${GENERATE_OUTPUT_DIR}”
NAMESPACE_MAP “2:http://opcfoundation.org/UA/DI/”
FILE_NS “${PROJECT_SOURCE_DIR}/model/Published/DI/Opc.Ua.Di.NodeSet2.xml”
INTERNAL
)
ua_generate_nodeset_and_datatypes(
NAME “iolink”
TARGET_PREFIX “${PROJECT_NAME}”
OUTPUT_DIR “${GENERATE_OUTPUT_DIR}”
NAMESPACE_IDX 3
FILE_NS “${PROJECT_SOURCE_DIR}/model/Published/IOLink/Opc.Ua.IOLink.NodeSet2.xml”
DEPENDS “di”
INTERNAL
)
# Previous macro automatically sets some variables which hold the generated source code files using the provided NAME
add_executable(opcua-modeling-tutorial-server
${UA_NODESET_DI_SOURCES}
${UA_NODESET_IOLINK_SOURCES}
${UA_TYPES_DI_SOURCES}
${UA_TYPES_IOLINK_SOURCES}
main.c
)
# Make sure the nodeset compiler is execute before compiling the main file
add_dependencies(opcua-modeling-tutorial-server
${PROJECT_NAME}-ns-di
${PROJECT_NAME}-ns-iolink
)
——————–
However I am getting this error:
——————————–
INFO:__main__:Preprocessing /home/ioan/test/opcua-modeling-tutorial-server/model/Published/IOLink/Opc.Ua.IOLink.NodeSet2.xml
INFO:__main__:Generating Code for Backend: open62541
Traceback (most recent call last):
File “/usr/local/share/open62541/tools/nodeset_compiler/nodeset_compiler.py”, line 189, in
generateOpen62541Code(ns, args.outputFile, args.internal_headers, args.typesArray)
File “/usr/local/share/open62541/tools/nodeset_compiler/backend_open62541.py”, line 230, in generateOpen62541Code
code = generateNodeCode_begin(node, nodeset, code_global)
File “/usr/local/share/open62541/tools/nodeset_compiler/backend_open62541_nodes.py”, line 493, in generateNodeCode_begin
[code1, codeCleanup1, codeGlobal1] = generateVariableNodeCode(node, nodeset)
File “/usr/local/share/open62541/tools/nodeset_compiler/backend_open62541_nodes.py”, line 231, in generateVariableNodeCode
[code1, codeCleanup1, codeGlobal1] = generateCommonVariableCode(node, nodeset)
File “/usr/local/share/open62541/tools/nodeset_compiler/backend_open62541_nodes.py”, line 195, in generateCommonVariableCode
[code1, codeCleanup1, codeGlobal1] = generateValueCode(node.value, nodeset.nodes[node.id], nodeset)
File “/usr/local/share/open62541/tools/nodeset_compiler/backend_open62541_nodes.py”, line 406, in generateValueCode
valueName + “[” + str(idx) + “]” , v, instanceName, valueName, codeGlobal))
File “/usr/local/share/open62541/tools/nodeset_compiler/backend_open62541_datatypes.py”, line 121, in generateNodeValueCode
return prepend + ” = ” + generateLocalizedTextCode(node, alloc=asIndirect) + “;”
File “/usr/local/share/open62541/tools/nodeset_compiler/backend_open62541_datatypes.py”, line 72, in generateLocalizedTextCode
vt = makeCLiteral(value.text)
File “/usr/local/share/open62541/tools/nodeset_compiler/backend_open62541_datatypes.py”, line 24, in makeCLiteral
return re.sub(r'(?<!\\)"', r'\\"', value.replace('\\', r'\\').replace('"', r'\"').replace('\n', r'\\n').replace('\r', r''))
AttributeError: 'NoneType' object has no attribute 'replace'
make[2]: *** [CMakeFiles/opcua-modeling-tutorial-server-ns-iolink.dir/build.make:71: src_generated/namespace_iolink_generated.c] Error 1
make[1]: *** [CMakeFiles/Makefile2:163: CMakeFiles/opcua-modeling-tutorial-server-ns-iolink.dir/all] Error 2
make: *** [Makefile:84: all] Error 2
————–
I get the same error if I try to compile it directly with this command:
python ~/bdn/open62541/tools/nodeset_compiler/nodeset_compiler.py –types-array=UA_TYPES -e ../open62541/deps/ua-nodeset/Schema/Opc.Ua.NodeSet2.xml -e ~/bdn/open62541/deps/ua-nodeset/DI/Opc.Ua.Di.NodeSet2.xml -x ~/bdn/open62541/deps/ua-nodeset/IOLink/Opc.Ua.IOLink.NodeSet2.xml test
Thanks!
Fixed!
There seems to be some errors in the IO-Link NodeSet2 file.
Hello,
I am currently facing the same issue. Can you please explain how did you fix this problem? Thank you.
I don’t get how the macro should work. Where does it come from? It’s not even following the macro syntax by CMake. I get the following error.
CMake Error at CMakeLists.txt:13 (ua_generate_nodeset_and_datatypes):
Unknown CMake command “ua_generate_nodeset_and_datatypes”.
Even though I really appreciate your work you’ve put into these tutorials, I find them quite confusing at some points. I’m having a real hard time jumping on the OPC UA train, currently.
Okay, I figured it out myself.
The problem I had in understanding the CMake function was base on the following:
1. In step 1 of this series the server is build using the ccmake GUI, where $Home is not a valid variable. It gets wrapped into a string and so “make install” puts it into the build folder’s subdirectory called ‘$Home’/install. Because of this and the fact that I overlooked the CMAKE_PREFIX_PATH option, I could not find the package with find_package.
2. I moved the install folder by hand and linked directly to it via include_library and include_directory in CMake. This worked great for the minimal server but as soon as the generated files came into play it broke.
3. I am using CLion for the entire project, so I had to include CMAKE_PREFIX_PATH in CLion’s project settings following this guide: https://stackoverflow.com/questions/54444846/equivalent-of-cmake-prefix-path-in-clion
4. After this find_package worked and I could make the project compile.
However, I am still experiencing issues:
A. the target link stage takes forever. I am building open62541 on a Raspberry Pi 4 via remote build and have to wait nearly an hour to get the link stage to complete!
This was not an issue with the manually added libraries, however. Not sure why. But I can also remember that I tried it with the UA_ENABLE_AMALGAMATION option on and hat the .h an .c file copied into my project but linked against the compiles server.o. It was crazy fast!
B: In the custom UA model XML, when dealing with a prefix that has an upper case letter, everything except #include “types_prefixname_generated.h” in the namespace_prefixname_generated.h” follows this scheme. However, as the filenames are also following the scheme, this include breaks, as it points to an all-lower-case header file!
So I had to rename everything to all-lower-case again to not having to touch the auto-generated files manually.
Just one more thing: I don’t want to criticize you are blame you in any way. This tutorial gave me a lot of insights into open62541. Thank you for that.
However, I think between steps 1 and 9 there is a huge information gap. From compiling a simple server using just GCC to a “full-blown” CMake file is a steep learning curve. Moreover, the first custom UA Modell examples are completely different from the Zoo example here. Not only because it contains more stuff, but it changes how everything is called. So it is hard to build upon the first steps because so much has to be altered. However, if you are not familiar with all details, yet, it is hard to judge which changes are needed or even correct. Not to mention that tools like CLion / IntelliJ are mentioned but never shown. I thought that because you are recommending it, you will also show some details later on in the series, which you haven’t, unfortunately (for me).
Maybe this feedback can help somehow. Thanks again for your time and effort.
Hi Martin,
thank you for writing up your comments.
I am pleased to hear that this tutorial was of some aid to you.
Your critique is well received.
Regarding the information gap:
When writing this tutorial I made a deliberate choice in keeping scaffolding and build mechanisms simple and out of the way in favor of focusing on open62541 and modelling. CMake is a well-known, common and comprehensively documented tool, therefore I decided to invest my time and energy in the topics at hand instead of writing just an other cmake guide.
A good part of the cmake related content is based on the work of this blog’s owner (Stefan Profanter), who is using his own models and for his builds. That’s why there are inconsistencies in the models and the general way “how things are done”.
You’re right, the information about CLion / IntelliJ is still missing. When I wrote this guide I had to work with Visual Studio, so that’s what is documented. Also, documenting IDE-specific information is always a slippery slope because it’s harder to document than text/command line tools and because the IDE’s UI will add an other layer of abstraction that may change (and therefore become obsolete) in the future. CMake will most likely remain unchanged, no matter what IDE or surrounding toolchain you use.
If you documented the steps it took you to make things work with CLion I’d be open to incorporate them into this blog with your permission (and credit of course). Let me know via comment and I will contact you by mail.
Hi there again,
Sorry for the delay. My focus was taken from OPC UA, and now I have to revisit this topic, and I’m feeling like starting from zero.
So actually, last time I got CLion to work. I cannot remember how, however! I guess it was just a stupid dependency issue that made CMake not find the macros used here.
Anyhow… I have to use a GUI-based modeler now, and I’m using OPC-UA-modeler, which is Python-based. But this only produces the nodeset XML file.
Where do the CSV and BSD files come from? I cannot remember, nor do I have the energy to read through this entire series again. Sorry, no bad blood, I really appreciate your work, but it seems like an OPC UA issue: Each documentation or tutorial gets harder to follow than the last. It seems that OPC UA trainers can make good money with workshops, eh?! 😉
Using the nodeset-compiler directly, I could generate the h and c files, include them into my project and verify with a client that the nodes have been included.
However, how do I access the variables and methods in code now? Neither the official documentation nor this tutorial gives an easy-to-follow path. Or maybe I am just too stupid for this. But what does? I have to understand it one way or another. Maybe you could point me to the correct sources. Thanks a lot, and please excuse me if my words sound a little rough. I am currently in a hard HomeOffice blues, frustrated with this entire low-level implementation stuff.
Best wishes,
Martin
Hi Martin,
good to hear from you & welcome back!
@ CLion:
Happy to hear you got it to work! Maybe that hint will be helpful to someone else.
@ OPC UA modeler:
Open https://opcua.rocks/from-modelling-to-execution-opc-ua-information-model-tutorial/ and look at the first graph.
OPC UA modeler is an UANodeSet2 editor (dotted lines in the EDITING STAGE column). It edits and produces NodeSet2 XML files directly. This article series recommends to use a 2-step process instead, by editing ModelDesign XML (with CLion in your case) and then converting them with the UA-ModelCompiler to UANodeSet2 XML files.
@ CSV & BSD files:
They are auxiliar files of the UA-ModelCompiler. If you work with OPC-UA-Modeler, you don’t need them. The CSV & BSD files help UA-ModelCompiler, to keep generated UANodeSet2 files backwards compatible to previous revisions by ensuring, that NodeIDs that were used for a specific element in the model will stick with that element later on, even if you modify the model.
@ Sorry, no bad blood:
No offense taken at all. You are free to comment here and we are free to approve your comments and take our (free) time to reply to you. As long as you get feedback, we’re on good terms 🙂 That said, I am not surprised that each consecutive step of the tutorial appears to be increasingly difficult to follow if previous steps slip your memory for external reasons and you can’t be bothered to re-read the documentation at the same time.
@ good money:
I wish 😉 So far, all the content here is free and my advice here & right now is free as well, despite the fact that most of the information in this/my comment can be found in the articles or their respective comments.
@ generate the h and c files:
Happy to hear that!
@ However, how do I access the variables and methods in code now?
You’re questions are hitting the mark spot on! 🙂 So far, this article series does not cover this kind of questions. We are working on it though, but given our/my professional workload, I don’t see that content arriving here anytime soon.
Did you have a look at https://open62541.org/doc/current/tutorial_server_datasource.html ?
@ please excuse me if my words sound a little rough.:
Again, no trouble at all. Wish you all the best.
Cool! I want to thank you for your time spent answering my (panic-ish) comment and questions.
@Clion and CMakeList extensions:
I will set everything up from scratch as soon as my nightmare with the current project is over. It is my first OPC UA project, and I was thrown into it without much preparation. As it always is. But first, I have to get things done, and then I will provide my insights for others to learn from.
@Generating h and c files:
Maybe you could contact me by mail, as it could be a long correspondence, and I cluttered this section enough with little to no info for the others.
However, just a brief overview:
I tried two approaches to get my open62541 server populated with the model after giving up on writing the XML file manually.
1. I tried the open-source Python project opcua-modeler, which gives me the XML file but suffers from other show stoppers (I even cloned the project to fix some bugs… timekiller). And I tried UA-Modeler by Unified Automation inside a Linux VM (I’m on macOS, don’t judge me!) which is way better. Still, I have to move files between VM and Host System. I missed the right-click menu over the namespace where you can export the XML and CSV files. Now I have it at hand. Yay…
2. I tried to compile the XML files with the open62541 node-compiler, which gives me the c and h files. This is nice, but it is another manual step to do, as I have to run a bash script every time to invoke the compiler. I also tried “sailavid/ua-modelcompiler” as a Docker image, but it does not work with the opcua-modeler or UA-Modeler XML files. It produces
Error parsing file /model/myModel.xml (There is an error in XML document (2, 2).) (There is an error in XML document (2, 2).)
I tried to get my head around this issue, but I cannot modify the XML enough to get it working. With your Zoo-Model, it works, and it generates all the files!
So it seems that both modelers are missing something in the XML export – or I do during modeling.
Going the CMakeList extension route you’ve provided here, I can also not do. The extension asks for a BSD file that I do not have nor understand where this auxiliary file comes from. In another article of yours, I read that the modeler should produce these files, but I can only export the CSV file. The extension is using the UA-ModelCompiler in the background? If I point CMakeList to a non-existing BSD file, the project configuration works, but I get an error during the build. If I remove the BSD file path, I get:
usr/local/bin/cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_COMPILER=/usr/bin/gcc-8 -DCMAKE_CXX_COMPILER=/usr/bin/g++-8 -DCMAKE_PREFIX_PATH=/home/pi/install -G “CodeBlocks – Unix Makefiles” /home/pi/dev/projects/opc_ua
STATUS, Cmake Binary Dir: /home/pi/dev/projects/opc_ua/cmake-build-debug
CMake Error at /home/pi/install/lib/cmake/open62541/open62541Macros.cmake:482 (message):
ua_generate_nodeset_and_datatypes function requires FILE_BSD argument if
any of FILE_CSV or NAMESPACE_MAP are set
Call Stack (most recent call first):
CMakeLists.txt:23 (ua_generate_nodeset_and_datatypes)
Okay, so much about being “brief.”
Maybe you can drop me a mail, and we can elaborate on this topic a bit further.
Best wishes,
Martin
Hi Martin,
sorry I am late to reply to your comment.
Regarding “There is an error in XML document (2, 2)”
Seems there is a difference at line 2 between your XML file an the Zoo-file? Can help you much without knowing your file.
Regarding BSD file:
Please search for “BSD” on this very article to find the same question and my solution to this issue.
Kind regards and good luck,
although a little late 🙂
Hello,
I am starting to use open62541 with qtopcua 5.15.2. This uses open62541 v1.0. I was trying to put together an example with DI, Machinery, etc. but nodeset_compiler.py fails when trying to use Opc.Ua.Di.NodeSet2.xml
Could it be that v1.0 cannot be used for these cases and only from v1.1?
I run it as follows:
python ./nodeset_compiler.py –internal-headers –existing ../../deps/ua-nodeset/Schema/Opc.Ua.NodeSet2.xml –xml ./../deps/ua-nodeset/DI/Opc.Ua.Di.NodeSet2.xml namespace_di_generated
o
# Generate types and namespace for DI
ua_generate_nodeset_and_datatypes(
NAME “di”
FILE_NS “${open62541_NODESET_DIR}/DI/Opc.Ua.Di.NodeSet2.xml”
FILE_CSV “${open62541_NODESET_DIR}/DI/OpcUaDiModel.csv”
FILE_BSD “${open62541_NODESET_DIR}/DI/Opc.Ua.Di.Types.bsd”
NAMESPACE_IDX 2
INTERNAL
)
and the error it indicates is:
INFO:__main__:Preprocessing (existing) ../../deps/ua-nodeset/Schema/Opc.Ua.NodeSet2.xml
INFO:__main__:Preprocessing ../../deps/ua-nodeset/DI/Opc.Ua.Di.NodeSet2.xml
INFO:__main__:Generating Code for Backend: open62541
Traceback (most recent call last):
File “D:\Proyectos\Otros\open62541\tools\nodeset_compiler\nodeset_compiler.py”, line 202, in
generateOpen62541Code(ns, args.outputFile, args.internal_headers, args.typesArray)
File “D:\Proyectos\Otros\open62541\tools\nodeset_compiler\backend_open62541.py”, line 237, in generateOpen62541Code
code = generateNodeCode_begin(node, nodeset, code_global)
File “D:\Proyectos\Otros\open62541\tools\nodeset_compiler\backend_open62541_nodes.py”, line 495, in generateNodeCode_begin
[code1, codeCleanup1, codeGlobal1] = generateVariableNodeCode(node, nodeset)
File “D:\Proyectos\Otros\open62541\tools\nodeset_compiler\backend_open62541_nodes.py”, line 233, in generateVariableNodeCode
[code1, codeCleanup1, codeGlobal1] = generateCommonVariableCode(node, nodeset)
File “D:\Proyectos\Otros\open62541\tools\nodeset_compiler\backend_open62541_nodes.py”, line 194, in generateCommonVariableCode
[code1, codeCleanup1, codeGlobal1] = generateValueCode(node.value, nodeset.nodes[node.id], nodeset)
File “D:\Proyectos\Otros\open62541\tools\nodeset_compiler\backend_open62541_nodes.py”, line 434, in generateValueCode
code.append(generateNodeValueCode(“*” + valueName + ” = ” , node.value[0], instanceName, valueName, codeGlobal, asIndirect=True))
File “D:\Proyectos\Otros\open62541\tools\nodeset_compiler\backend_open62541_datatypes.py”, line 122, in generateNodeValueCode
return prepend + generateQualifiedNameCode(node.value, alloc=asIndirect) + “;”
File “D:\Proyectos\Otros\open62541\tools\nodeset_compiler\backend_open62541_datatypes.py”, line 73, in generateQualifiedNameCode
vn = makeCLiteral(value.name)
AttributeError: ‘NoneType’ object has no attribute ‘name’
Hi Alejandro,
your final error message
AttributeError: ‘NoneType’ object has no attribute ‘name’
tells me that there is something fundamentally off with your approach or files.
I tried to reproduce your command (using slightly different flags but the same files), but in my case it compiles successfully! See below:
python ./nodeset_compiler.py –existing ../../deps/ua-nodeset/Schema/Opc.Ua.NodeSet2.xml –xml ../../deps/ua-nodeset/DI/Opc.Ua.Di.NodeSet2.xml namesapce_di_generated
INFO:__main__:Preprocessing (existing) ../../deps/ua-nodeset/Schema/Opc.Ua.NodeSet2.xml
INFO:__main__:Preprocessing ../../deps/ua-nodeset/DI/Opc.Ua.Di.NodeSet2.xml
INFO:__main__:Generating Code for Backend: open62541
INFO:__main__:NodeSet generation code successfully printed
There are some mistakes in your example command (e.g. you’re using ./../ where I’d expect ../../ )but I guess they were introduced by manually copying the command?
Did you update the deps submodule in git? Try the following command within the git folder:
git submodule update
It should yield the following output:
Submodule path ‘../../deps/ua-nodeset’: checked out ‘7430c928aef795e408ebe5a69f2357737a0a3b36’
Let us know of any progress you make.
Cheers!
Hi there,
first of all thanks for the very good and complete tutorials.
I followed all the steps with success until step 9. I want to compile the server with the generated UANodeSet.
OS
~$ uname -a
Linux mik-industry 5.11.0-37-generic #41~20.04.2-Ubuntu SMP Fri Sep 24 09:06:38 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
GCC VERSION
gcc –version
gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
—————————————-
CMAKE VERSION
cmake –version
cmake version 3.16.3
——————–
GIT open62541 COMMIT HASH
~/open62541$ git rev-parse HEAD
4a12b64f32427042605dbeafa6fded3583316244
~/open62541$ git rev-parse –abbrev-ref HEAD
master
——
I successfully built the opcua library with cmake using the following custom options:
-buildType = RelWithDebInfo
-cmake install prefix = /home//install
-ua namespace zero full
tree -L 2 ~/install/ (I show only two depth levels)
/home/mik/install/
├── include
│ ├── aa_tree.h
│ ├── ms_stdint.h
│ ├── open62541
│ └── ziptree.h
├── lib
│ ├── cmake
│ ├── libopen62541.a
│ └── pkgconfig
└── share
└── open62541
I compiled the xml to obtain nodeset but, to be sure of the correctness of the NodeSets, I used the files in (https://github.com/Pro/opcua-modeling-tutorial/tree/master-published/Published/FooFlt)
1) Using CMAKE approach
mkdir ~/FooServer
I created the CMakeLists.txt in ~/FooServer folder. Pay attention, this file is different from https://github.com/Pro/opcua-modeling-tutorial-server/blob/master/CMakeLists.txt (NAMESPACE_MAP vs NAMESPACE_IDX). I used the one with NAMESPACE_MAP because of https://github.com/Pro/opcua-modeling-tutorial-server/commit/d936318a9d81047d7e053d7c5922162ea1e807c1 (fix CMakeList.txt because of breaking change in open62541(8103c27))
I created the server (main.c) inside the ~/FooServer folder
tree ~/FooServer/
/home/mik/FooServer/
├── CMakeLists.txt
├── main.c
└── model
├── FooFltModel.csv
├── FooFltModel.xml
└── Published
└── FooFlt
├── FooFlt.Classes.cs
├── FooFlt.Constants.cs
├── FooFlt.DataTypes.cs
├── FooFltModel.csv
├── FooFltModel.xml
├── FooFlt.NodeSet2.xml
├── FooFlt.NodeSet.xml
├── FooFlt.PredefinedNodes.uanodes
├── FooFlt.PredefinedNodes.xml
├── FooFlt.Types.bsd
└── FooFlt.Types.xsd
I tryed to build with my custom install location > cmake -DCMAKE_PREFIX_PATH=/home/mik/install .. SUCCESS
But make -j fails
ERROR
~/FooServer/build$ make -j
[ 22%] Built target opcua-modeling-tutorial-server-ids-foo_flt
[ 22%] Built target opcua-modeling-tutorial-server-types-foo_flt
[ 33%] Generating src_generated/namespace_foo_flt_generated.c, src_generated/namespace_foo_flt_generated.h
INFO:__main__:Preprocessing (existing) /home/mik/install/share/open62541/tools/ua-nodeset/Schema/Opc.Ua.NodeSet2.xml
INFO:__main__:Preprocessing /home/mik/FooServer/model/Published/FooFlt/FooFlt.NodeSet2.xml
Traceback (most recent call last):
File “/home/mik/install/share/open62541/tools/nodeset_compiler/nodeset_compiler.py”, line 163, in
ns.generateParser(args.existing, args.infiles, args.bsdFile)
File “/home/mik/install/share/open62541/tools/nodeset_compiler/nodeset.py”, line 431, in generateParser
if (nd.hasAttribute(“SymbolicName”) and (re.match(r”.*_BinarySchema”, nd.attributes[“SymbolicName”].nodeValue) or nd.attributes[“SymbolicName”].nodeValue == “TypeDictionary_BinarySchema”)) or (nd.hasAttribute(“ParentNodeId”) and not nd.hasAttribute(“SymbolicName”) and re.fullmatch(r”i=93″, nd.attributes[“ParentNodeId”].nodeValue)):
—-> AttributeError: ‘module’ object has no attribute ‘fullmatch’ <—-
make[2]: *** [CMakeFiles/opcua-modeling-tutorial-server-ns-foo_flt.dir/build.make:71: src_generated/namespace_foo_flt_generated.c] Error 1
make[1]: *** [CMakeFiles/Makefile2:107: CMakeFiles/opcua-modeling-tutorial-server-ns-foo_flt.dir/all] Error 2
make: *** [Makefile:84: all] Error 2
I am stuck here with the first approach. What am I doing wrong?
2) Using Python approach
I have always my Library in /home/mik/install, I used the https://github.com/Pro/opcua-modeling-tutorial/blob/master/FooFltModel.xml file and the generated NodeSet2 files from https://github.com/Pro/opcua-modeling-tutorial/tree/master-published/Published/FooFlt
SAME ERROR here
~/FooServer$ python ~/open62541/tools/nodeset_compiler/nodeset_compiler.py –types-array=UA_TYPES –existing ~/open62541/deps/ua-nodeset/Schema/Opc.Ua.NodeSet2.xml –xml model/Published/FooFlt/FooFlt.NodeSet2.xml FooFlt
INFO:__main__:Preprocessing (existing) /home/mik/open62541/deps/ua-nodeset/Schema/Opc.Ua.NodeSet2.xml
INFO:__main__:Preprocessing model/Published/FooFlt/FooFlt.NodeSet2.xml
Traceback (most recent call last):
File "/home/mik/open62541/tools/nodeset_compiler/nodeset_compiler.py", line 163, in
ns.generateParser(args.existing, args.infiles, args.bsdFile)
File “/home/mik/open62541/tools/nodeset_compiler/nodeset.py”, line 431, in generateParser
if (nd.hasAttribute(“SymbolicName”) and (re.match(r”.*_BinarySchema”, nd.attributes[“SymbolicName”].nodeValue) or nd.attributes[“SymbolicName”].nodeValue == “TypeDictionary_BinarySchema”)) or (nd.hasAttribute(“ParentNodeId”) and not nd.hasAttribute(“SymbolicName”) and re.fullmatch(r”i=93″, nd.attributes[“ParentNodeId”].nodeValue)):
—-> AttributeError: ‘module’ object has no attribute ‘fullmatch’ <—-
I am stuck here with the second approach. What am I doing wrong?
Sorry for the very long post, I wanted you to know all my configurations and previous steps.
Thank you so much for all the support you can give to me.
Bests,
Michele
Hi Michele,
I am happy to hear that this tutorial is helpful to you.
Long post such as yours, with lots of information, are very welcome and helpful to anyone looking for advice. No need to apologize.
The core of your issue is the following segment:
File “/home/mik/open62541/tools/nodeset_compiler/nodeset.py”, line 431, in generateParser
if (nd.hasAttribute(“SymbolicName”) and (re.match(r”.*_BinarySchema”, nd.attributes[“SymbolicName”].nodeValue) or nd.attributes[“SymbolicName”].nodeValue == “TypeDictionary_BinarySchema”)) or (nd.hasAttribute(“ParentNodeId”) and not nd.hasAttribute(“SymbolicName”) and re.fullmatch(r”i=93″, nd.attributes[“ParentNodeId”].nodeValue)):
—-> AttributeError: ‘module’ object has no attribute ‘fullmatch’ <—- It tells you, that nodeset.py wants to use re.fullmatch(...) in line 431, in generateParser, but 're' has no attribute 'fullmatch'. 're' is a python module [1] for regular expression [2] operations in pythons. To me, it looks like you're taking the right steps but at some point, you're using the wrong python version. You can test if re.fullmatch(...) is available in your python version. Below, I am testing that for python3 and python2 on my computer.
~ $ python
Python 3.9.6 (default, Sep 9 2021, 11:00:25)
[GCC 10.3.0] on linux
Type “help”, “copyright”, “credits” or “license” for more information.
>>> import re
>>> dir(re)
[‘A’, ‘ASCII’, ‘DEBUG’, ‘DOTALL’, ‘I’, ‘IGNORECASE’, ‘L’, ‘LOCALE’, ‘M’, ‘MULTILINE’, ‘Match’, ‘Pattern’, ‘RegexFlag’, ‘S’, ‘Scanner’, ‘T’, ‘TEMPLATE’, ‘U’, ‘UNICODE’, ‘VERBOSE’, ‘X’, ‘_MAXCACHE’, ‘__all__’, ‘__builtins__’, ‘__cached__’, ‘__doc__’, ‘__file__’, ‘__loader__’, ‘__name__’, ‘__package__’, ‘__spec__’, ‘__version__’, ‘_cache’, ‘_compile’, ‘_compile_repl’, ‘_expand’, ‘_locale’, ‘_pickle’, ‘_special_chars_map’, ‘_subx’, ‘compile’, ‘copyreg’, ‘enum’, ‘error’, ‘escape’, ‘findall’, ‘finditer’, ‘fullmatch’, ‘functools’, ‘match’, ‘purge’, ‘search’, ‘split’, ‘sre_compile’, ‘sre_parse’, ‘sub’, ‘subn’, ‘template’]
>>>
~ $ python2
Python 2.7.18 (default, Sep 9 2021, 11:04:49)
[GCC 10.3.0] on linux2
Type “help”, “copyright”, “credits” or “license” for more information.
>>> import re
>>> dir(re)
[‘DEBUG’, ‘DOTALL’, ‘I’, ‘IGNORECASE’, ‘L’, ‘LOCALE’, ‘M’, ‘MULTILINE’, ‘S’, ‘Scanner’, ‘T’, ‘TEMPLATE’, ‘U’, ‘UNICODE’, ‘VERBOSE’, ‘X’, ‘_MAXCACHE’, ‘__all__’, ‘__builtins__’, ‘__doc__’, ‘__file__’, ‘__name__’, ‘__package__’, ‘__version__’, ‘_alphanum’, ‘_cache’, ‘_cache_repl’, ‘_compile’, ‘_compile_repl’, ‘_expand’, ‘_locale’, ‘_pattern_type’, ‘_pickle’, ‘_subx’, ‘compile’, ‘copy_reg’, ‘error’, ‘escape’, ‘findall’, ‘finditer’, ‘match’, ‘purge’, ‘search’, ‘split’, ‘sre_compile’, ‘sre_parse’, ‘sub’, ‘subn’, ‘sys’, ‘template’]
>>>
Note, that ‘fullmatch’ is available for python3 but not python2!
‘fullmatch’ was introduced with python3.4 [3].
Your next step should be to figure out
1) what python version is expected by open62541/tools/nodeset_compiler/nodeset.py (answer: at least python version 3.4, or higher because it uses ‘re.fullmatch’)
2) why it is not using that python version and what python version it is using instead
3) make nodeset.py use the correct python version
From your uname command I’d suspect, that your ubuntu supports python 3.8 [4] as default python version.
Hope that helps!
[1] https://docs.python.org/3/library/re.html
[2] regular expressions are search patterns, most commonly applied on text strings to find certain text
[3] https://docs.python.org/3/library/re.html#re.fullmatch
[4]https://askubuntu.com/questions/1232812/whats-the-default-python-version-in-ubuntu-20-04
Dear Benedict,
Thank you so much for the answer! I forgot the proliferation of Python versions but anyway…I solved the problem! This step now runs fine.
I didn’t find the real problem but I solved it (for both the CMAKE and the PYTHON ways)
1) CMAKE way: I added the following line to the CMakeList.txt file:
…
# Force Python3
find_package(Python3 COMPONENTS Interpreter Development)
find_package(open62541 1.1 REQUIRED COMPONENTS FullNamespace)
…
I run cmake -> cmake -DCMAKE_PREFIX_PATH=/home/mik/install ..
— The C compiler identification is GNU 9.3.0
— The CXX compiler identification is GNU 9.3.0
— Check for working C compiler: /usr/bin/cc
— Check for working C compiler: /usr/bin/cc — works
— Detecting C compiler ABI info
— Detecting C compiler ABI info – done
— Detecting C compile features
— Detecting C compile features – done
— Check for working CXX compiler: /usr/bin/c++
— Check for working CXX compiler: /usr/bin/c++ — works
— Detecting CXX compiler ABI info
— Detecting CXX compiler ABI info – done
— Detecting CXX compile features
— Detecting CXX compile features – done
— Found Python3: /usr/bin/python3.8 (found version “3.8.10”) found components: Interpreter Development [1]
— Found PythonInterp: /usr/bin/python (found version “2.7.18”) –> [something weird HERE probably] <–
— Configuring done
— Generating done
— Build files have been written to: /home/mik/FooServer/build
It seems that someone is calling FindPythonInterp (deprecated [2]).
Now I did a very dirty solution…I suggest to not follow me 🙂 I modified these two lines of the generated CMakeCache.txt:
…
//Path to a program.
PYTHON_EXECUTABLE:FILEPATH=/usr/bin/python3
…
//Details about finding PythonInterp
FIND_PACKAGE_MESSAGE_DETAILS_PythonInterp:INTERNAL=[/usr/bin/python3][v3.8.10()]
…
Then, I deleted all other generated files (and folders) except for the CMakeCache.txt file and executed again cmake: cmake -DCMAKE_PREFIX_PATH=/home/mik/install ..
— The C compiler identification is GNU 9.3.0
— The CXX compiler identification is GNU 9.3.0
— Check for working C compiler: /usr/bin/cc
— Check for working C compiler: /usr/bin/cc — works
— Detecting C compiler ABI info
— Detecting C compiler ABI info – done
— Detecting C compile features
— Detecting C compile features – done
— Check for working CXX compiler: /usr/bin/c++
— Check for working CXX compiler: /usr/bin/c++ — works
— Detecting CXX compiler ABI info
— Detecting CXX compiler ABI info – done
— Detecting CXX compile features
— Detecting CXX compile features – done
— Configuring done
— Generating done
— Build files have been written to: /home/mik/FooServer/build
CMake prefers the cached values, so it did not overwrite my python interpreter again. Now how am I sure that py3 is used? grep inside the build folder:
~/FooServer/build$ grep -r -i "python" .
./CMakeFiles/opcua-modeling-tutorial-server-ns-foo_flt.dir/build.make: /usr/bin/python3 …
./CMakeFiles/opcua-modeling-tutorial-server-types-foo_flt.dir/build.make: /usr/bin/python3 …
…
2) the PYTHON way was trivial cause I am an idiot so I didn't try py3 version:
python3 ~/open62541/tools/nodeset_compiler/nodeset_compiler.py –types-array=UA_TYPES –existing ~/open62541/deps/ua-nodeset/Schema/Opc.Ua.NodeSet2.xml –xml model/Published/FooFlt/FooFlt.NodeSet2.xml FooFlt
INFO:__main__:Preprocessing (existing) /home/mik/open62541/deps/ua-nodeset/Schema/Opc.Ua.NodeSet2.xml
INFO:__main__:Preprocessing model/Published/FooFlt/FooFlt.NodeSet2.xml
INFO:__main__:Generating Code for Backend: open62541
INFO:__main__:NodeSet generation code successfully printed
Now both ways successfully generate the FooFlt.c FooFlt.h files and the gcc compilation of the server had success too. I know that the CMAKE cache edit is DIRTY and NOT PORTABLE but I could not find where and why the script found first all the components of Python3 (with find_package(Python3…) )and after it overwrote the interpreter with py2.
If I can help in some way, let me know!
Thank you so much again, and I reiterate that these are awesome tutorials to understand the complex OPC UA stack and its information model.
Bests,
Michele
[1] https://cmake.org/cmake/help/latest/module/FindPython3.html
[2] https://cmake.org/cmake/help/latest/module/FindPythonInterp.html
Hi Michele,
I am glad to see you coming up with a solution (actually two solutions) and reporting them here for everyone to read!
Even a dirty solution may give a valuable hint to someone else.
Looking at your CMAKE output I wonder if you manually set python2 as your default python, so that /usr/bin/python links to py2 ?
It seems on Ubuntu you can use the command update-alternatives to set the default python version.
Also, instead of messing with cmake cache, you may try to pass on the right values with -D to cmake, like they do here (adapt those solutions to your needs):
https://stackoverflow.com/a/36439709
https://stackoverflow.com/a/16045924
Thanks again for the praise. It’s well received. Hopefully the tutorials will serve you well moving forward.
Cheers!
Hi Benedict,
First of all thank you for your detailed and complete tutorial, it is very helpful to me, I tried to set the default value of the AnalogUnitRangeType variable in the information model, such as the value range and unit, but it always fails, can you give me some advice..
Jorman
Hi Jorman,
yes, I can give you advice but I need something to work with. Please share your error messages and the most simple, reduced version of your model to reproduce your issue.
Kind regards,
Benedict
Hi Benedict,
Thank you very much for your enthusiasm, I have found an example how to implement this initialization structure, like the following. Otherwise I really need your help.
Best regards,
Jorman
60
0110 Motor synchronization RPM
i=888
http://www.opcfoundation.org/UA/units/un/cefact
4534832
en
rpm/min
en
motor speed
i=14001
1200
2000
I implement this initialization structure for a variable
60
0110 Motor synchronization RPM
i=888
http://test.org/test/
1
en
rpm/min
en
motor speed
i=14001
1200
2000