Skip to content

Step 9: Using a custom UANodeSet with open62541

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.


Notice

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:

https://github.com/opcua-skills/plug-and-produce

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
  • 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:

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

Published inopen62541

31 Comments

  1. Sebastian Sebastian

    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.

    • Benedict Simlinger Benedict Simlinger

      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

  2. audupi audupi

    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.

    • Benedict Simlinger Benedict Simlinger

      Hi,

      please have look at the notice at the very beginning of this article:

      A more complete example with heavy use of heavy usage of ModelDesign files and dependencies between them is shown in this project:
      https://github.com/opcua-skills/plug-and-produce

      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.

  3. Sebastian Sebastian

    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

  4. Sebastian Sebastian

    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.

    • Benedict Simlinger Benedict Simlinger

      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.

  5. audupi audupi

    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.

  6. Ioan Ioan

    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!

    • Ioan Ioan

      Fixed!

      There seems to be some errors in the IO-Link NodeSet2 file.

  7. Deepak Yadav Deepak Yadav

    Hello,
    I am currently facing the same issue. Can you please explain how did you fix this problem? Thank you.

  8. Martin Martin

    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.

  9. Martin Martin

    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.

  10. Martin Martin

    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.

    • Benedict Simlinger Benedict Simlinger

      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.

      • Martin Martin

        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

        • Benedict Simlinger Benedict Simlinger

          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.

          • Martin Martin

            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

          • Benedict Simlinger Benedict Simlinger

            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 🙂

  11. Alejandro Alejandro

    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’

    • Benedict Simlinger Benedict Simlinger

      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!

  12. Michele Michele

    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

    • Benedict Simlinger Benedict Simlinger

      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

      • Michele Michele

        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

        • Benedict Simlinger Benedict Simlinger

          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!

  13. Jorman Jorman

    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

    • Benedict Simlinger Benedict Simlinger

      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

  14. Jorman Jorman

    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

  15. Jorman Jorman

    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

Leave a Reply

Your email address will not be published.

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.