Explorar el Código

Merge branch '0.2'

Julius Pfrommer hace 8 años
padre
commit
a8e0bd092a

+ 7 - 3
.travis.yml

@@ -130,6 +130,10 @@ deploy:
     condition: $CC = gcc && $ANALYZE = false
 
 notifications:
-  irc: "chat.freenode.net#open62541"
-  on_success: ${IRC_NOTIFY_SUCCESS} # default: change, variable defined in ./tools/travis/travis_notification.sh
-  on_failure: ${IRC_NOTIFY_FAILURE} # default: always, variable defined in ./tools/travis/travis_notification.sh
+  irc:
+    channels:
+        - "chat.freenode.net#open62541"
+    on_success: ${IRC_NOTIFY_SUCCESS} # default: change, variable defined in ./tools/travis/travis_notification.sh
+    on_failure: ${IRC_NOTIFY_FAILURE} # default: always, variable defined in ./tools/travis/travis_notification.sh
+    use_notice: true
+    skip_join: true

+ 14 - 17
CMakeLists.txt

@@ -37,7 +37,7 @@ endif()
 # Options
 set(UA_LOGLEVEL 300 CACHE STRING "Level at which logs shall be reported")
 option(UA_ENABLE_METHODCALLS "Enable the Method service set" ON)
-option(UA_ENABLE_NODEMANAGEMENT "Enable dynamic addition and removal of nodes" ON)
+option(UA_ENABLE_NODEMANAGEMENT "Enable dynamic addition and removal of nodes at runtime" ON)
 option(UA_ENABLE_SUBSCRIPTIONS "Enable subscriptions support." ON)
 option(UA_ENABLE_MULTITHREADING "Enable multithreading" OFF)
 option(UA_ENABLE_DISCOVERY "Enable Discovery Service (LDS)" ON)
@@ -54,11 +54,12 @@ endif()
 option(UA_ENABLE_TYPENAMES "Add the type and member names to the UA_DataType structure" OFF)
 mark_as_advanced(UA_ENABLE_TYPENAMES)
 
-option(UA_ENABLE_GENERATE_NAMESPACE0 "Generate and load UA XML Namespace 0 definition" OFF)
-
-option(UA_ENABLE_EMBEDDED_LIBC "Target has no libc, use internal definitions" OFF)
+option(UA_ENABLE_EMBEDDED_LIBC "Use a custom implementation of some libc functions that might be missing on embedded targets (e.g. string handling)." OFF)
 mark_as_advanced(UA_ENABLE_EMBEDDED_LIBC)
 
+option(UA_ENABLE_GENERATE_NAMESPACE0 "Generate and load UA XML Namespace 0 definition (experimental)" OFF)
+mark_as_advanced(UA_ENABLE_GENERATE_NAMESPACE0)
+
 option(UA_ENABLE_EXTERNAL_NAMESPACES "Enable namespace handling by an external component (experimental)" OFF)
 mark_as_advanced(UA_ENABLE_EXTERNAL_NAMESPACES)
 
@@ -73,14 +74,20 @@ endif()
 
 # Build Targets
 option(UA_BUILD_EXAMPLES "Build example servers and clients" OFF)
-option(UA_BUILD_EXAMPLES_NODESET_COMPILER "Generate an OPC UA information model from a nodeset XML (experimental)" OFF)
 option(UA_BUILD_UNIT_TESTS "Build the unit tests" OFF)
 option(UA_BUILD_DOCUMENTATION "Generate doxygen/sphinx documentation" OFF)
+option(UA_BUILD_EXAMPLES_NODESET_COMPILER "Generate an OPC UA information model from a nodeset XML (experimental)" OFF)
 
 # Advanced Build Targets
 option(UA_BUILD_SELFSIGNED_CERTIFICATE "Generate self-signed certificate" OFF)
 mark_as_advanced(UA_BUILD_SELFSIGNED_CERTIFICATE)
 
+# Building shared libs (dll, so). This option is written into ua_config.h.
+set(UA_DYNAMIC_LINKING OFF)
+if(BUILD_SHARED_LIBS)
+  set(UA_DYNAMIC_LINKING ON)
+endif()
+
 #####################
 # Compiler Settings #
 #####################
@@ -337,9 +344,8 @@ endif()
 add_library(open62541 $<TARGET_OBJECTS:open62541-object>)
 target_link_libraries(open62541 ${open62541_LIBRARIES})
 
-target_compile_definitions(open62541-object PRIVATE -DUA_DYNAMIC_LINKING)
-target_compile_definitions(open62541 PRIVATE -DUA_DYNAMIC_LINKING)
-# target_compile_definitions(open62541-static PRIVATE -DUA_DYNAMIC_LINKING)
+target_compile_definitions(open62541-object PRIVATE -DUA_DYNAMIC_LINKING_EXPORT)
+target_compile_definitions(open62541 PRIVATE -DUA_DYNAMIC_LINKING_EXPORT)
 
 if(WIN32)
     target_link_libraries(open62541 ws2_32)
@@ -363,15 +369,6 @@ if(UA_BUILD_DOCUMENTATION)
     add_subdirectory(doc)
 endif()
 
-if(UA_BUILD_SELFSIGNED_CERTIFICATE)
-    find_package(OpenSSL REQUIRED)
-    add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/server_cert.der ${PROJECT_BINARY_DIR}/ca.crt
-                       COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/certs/create_self-signed.py ${PROJECT_BINARY_DIR}
-                       DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tools/certs/create_self-signed.py
-                               ${CMAKE_CURRENT_SOURCE_DIR}/tools/certs/localhost.cnf)
-    add_custom_target(selfsigned ALL DEPENDS ${PROJECT_BINARY_DIR}/server_cert.der ${PROJECT_BINARY_DIR}/ca.crt)
-endif()
-
 if(UA_BUILD_EXAMPLES_NODESET_COMPILER)
   add_custom_target(generate_informationmodel ALL
                     DEPENDS ${PROJECT_BINARY_DIR}/src_generated/nodeset.h ${PROJECT_BINARY_DIR}/src_generated/nodeset.c)

+ 7 - 5
appveyor.yml

@@ -56,7 +56,7 @@ build_script:
   - md build
   - cd build
   - echo "Testing MinGW32"
-  - cmake -DUA_BUILD_EXAMPLES:BOOL=ON -G"MinGW Makefiles" ..
+  - cmake -DUA_BUILD_EXAMPLES:BOOL=ON -DUA_BUILD_SELFSIGNED_CERTIFICATE:BOOL=ON -G"MinGW Makefiles" ..
   - mingw32-make
   - cd ..
   - rd /s /q build
@@ -69,22 +69,24 @@ build_script:
   - rd /s /q build
   - md build
   - cd build
-  - cmake -DUA_BUILD_EXAMPLES:BOOL=ON -DUA_ENABLE_AMALGAMATION:BOOL=ON -G"Visual Studio 12 2013" ..
+  - cmake -DUA_BUILD_EXAMPLES:BOOL=ON -DUA_BUILD_SELFSIGNED_CERTIFICATE:BOOL=ON -DUA_ENABLE_AMALGAMATION:BOOL=ON -G"Visual Studio 12 2013" ..
   - msbuild open62541.sln /m
   - copy C:\projects\open62541\build\open62541.c C:\projects\open62541\build\Debug\open62541.c
   - copy C:\projects\open62541\build\open62541.h C:\projects\open62541\build\Debug\open62541.h
+  - copy C:\projects\open62541\build\examples\server_cert.der C:\projects\open62541\build\Debug\server_cert.der
   - cd ..
   - echo "Win 64 build"
   - md build64
   - cd build64
-  - cmake -DUA_BUILD_EXAMPLES:BOOL=ON -DUA_ENABLE_AMALGAMATION:BOOL=ON -G"Visual Studio 12 2013 Win64" ..
+  - cmake -DUA_BUILD_EXAMPLES:BOOL=ON -DUA_BUILD_SELFSIGNED_CERTIFICATE:BOOL=ON -DUA_ENABLE_AMALGAMATION:BOOL=ON -G"Visual Studio 12 2013 Win64" ..
   - msbuild open62541.sln /m
   - copy C:\projects\open62541\build64\open62541.c C:\projects\open62541\build64\Debug\open62541.c
   - copy C:\projects\open62541\build64\open62541.h C:\projects\open62541\build64\Debug\open62541.h
+  - copy C:\projects\open62541\build64\examples\server_cert.der C:\projects\open62541\build64\Debug\server_cert.der
   - cd ..
 
 after_build:
-  - 7z a open62541-win32.zip %APPVEYOR_BUILD_FOLDER%\build\Debug\*
-  - 7z a open62541-win64.zip %APPVEYOR_BUILD_FOLDER%\build64\Debug\*
+  - 7z a open62541-win32.zip %APPVEYOR_BUILD_FOLDER%\build\Debug\* %APPVEYOR_BUILD_FOLDER%\build\examples\Debug\*
+  - 7z a open62541-win64.zip %APPVEYOR_BUILD_FOLDER%\build64\Debug\* %APPVEYOR_BUILD_FOLDER%\build64\examples\Debug\*
   - appveyor PushArtifact open62541-win32.zip
   - appveyor PushArtifact open62541-win64.zip

+ 67 - 46
doc/building.rst

@@ -1,17 +1,21 @@
 .. _building:
 
-Building the Library
-====================
+Building open62541
+^^^^^^^^^^^^^^^^^^
 
-Building the Single-File Release
---------------------------------
+Building the Examples
+=====================
 
-Using the GCC compiler, the following calls build the library on Linux.
+Using the GCC compiler, the following calls build the examples on Linux.
 
 .. code-block:: bash
 
-   gcc -std=c99 -fPIC -c open62541.c
-   gcc -shared open62541.o -o libopen62541.so
+   cp /path-to/open62541.* . # copy single-file distribution to the local directory
+   cp /path-to/examples/server_variable.c . # copy the example server to the local directory
+   gcc -std=c99 open62541.c server_variable.c -o server
+
+Building the Library
+====================
 
 Building with CMake on Ubuntu or Debian
 ---------------------------------------
@@ -24,6 +28,7 @@ Building with CMake on Ubuntu or Debian
    sudo apt-get install liburcu-dev # for multithreading
    sudo apt-get install check # for unit tests
    sudo apt-get install sphinx # for documentation generation
+   sudo apt-get install python-sphinx-rtd-theme # documentation style
 
    cd open62541
    mkdir build
@@ -58,7 +63,7 @@ with MinGW, just replace the compiler selection in the call to CMake.
    <path-to>\cmake.exe .. -G "Visual Studio 14 2015"
    :: You can use use cmake-gui for a graphical user-interface to select single features
 
-- Then open "build\open62541.sln" in Visual Studio 2015 and build as usual
+- Then open :file:`build\open62541.sln` in Visual Studio 2015 and build as usual
 
 Building on OS X
 ----------------
@@ -74,85 +79,101 @@ Building on OS X
 .. code-block:: bash
 
    brew install cmake
-   brew install libxml2
+   pip install sphinx # for documentation generation
+   pip install sphinx_rtd_theme # documentation style
    brew install check # for unit tests
    brew install userspace-rcu # for multi-threading support
-   brew install graphviz doxygen # for documentation generation
-   pip install sphinx # for documentation generation
 
 Follow Ubuntu instructions without the ``apt-get`` commands as these are taken care of by the above packages.
 
 Build Options
--------------
+=============
+
+Build Type and Logging
+----------------------
 
 **CMAKE_BUILD_TYPE**
-  - RelWithDebInfo: -O2 optimization with debug symbols
-  - Release: -O2 optimization without debug symbols
-  - Debug: -O0 optimization with debug symbols
-  - MinSizeRel: -Os optimization without debug symbols
+  - ``RelWithDebInfo`` -O2 optimization with debug symbols
+  - ``Release`` -O2 optimization without debug symbols
+  - ``Debug`` -O0 optimization with debug symbols
+  - ``MinSizeRel`` -Os optimization without debug symbols
 
 **UA_LOGLEVEL**
    The level of logging events that are reported
-   - 600: Fatal and all below
-   - 500: Error and all below
-   - 400: Error and all below
-   - 300: Info and all below
-   - 200: Debug and all below
-   - 100: Trace and all below
+
+     - 600: Fatal and all below
+     - 500: Error and all below
+     - 400: Error and all below
+     - 300: Info and all below
+     - 200: Debug and all below
+     - 100: Trace and all below
 
 Further options that are not inherited from the CMake configuration are defined
-in ua_config.h. Usually there is no need to adjust them.
+in :file:`ua_config.h`. Usually there is no need to adjust them.
 
 UA_BUILD_* group
-~~~~~~~~~~~~~~~~
+----------------
 
 By default only the shared object libopen62541.so or the library open62541.dll
 and open62541.dll.a resp. open62541.lib are build. Additional artifacts can be
 specified by the following options:
 
 **UA_BUILD_DOCUMENTATION**
-   Generate documentation with doxygen
-**UA_BUILD_EXAMPLECLIENT**
-   Compile example clients from client.c. There are a static and a dynamic binary client and client_static, respectively
-**UA_BUILD_EXAMPLESERVER**
-   Compile example server from server.c There are a static and a dynamic binary server and server_static, respectively
-**UA_BUILD_UNIT_TESTS**
-   Compile unit tests with Check framework. The tests can be executed with make test
+  Generate Make targets for documentation
+
+   * HTML documentation: ``make doc``
+   * Latex Files: ``latex``
+   * PDF documentation: ``make pdf``
+
 **UA_BUILD_EXAMPLES**
-   Compile specific examples from https://github.com/acplt/open62541/blob/master/examples/
+   Compile example servers and clients from :file:`examples/{xyz}.c`. A static and a dynamic binary is linked, respectively.
+
+**UA_BUILD_UNIT_TESTS**
+   Compile unit tests with Check framework. The tests can be executed with ``make test``
+
+**UA_BUILD_EXAMPLES_NODESET_COMPILER**
+   Generate an OPC UA information model from a nodeset XML (experimental)
+
 **UA_BUILD_SELFIGNED_CERTIFICATE**
    Generate a self-signed certificate for the server (openSSL required)
 
 UA_ENABLE_* group
-~~~~~~~~~~~~~~~~~
+-----------------
 
 This group contains build options related to the supported OPC UA features.
 
 **UA_ENABLE_SUBSCRIPTIONS**
    Enable subscriptions
 **UA_ENABLE_METHODCALLS**
-   Enable method calls in server and client
+   Enable the Method service set
 **UA_ENABLE_NODEMANAGEMENT**
-   Node management services (adding and removing nodes and references) at runtime in server and client
+   Enable dynamic addition and removal of nodes at runtime
 **UA_ENABLE_AMALGAMATION**
-   Compile a single-file release files open62541.c and open62541.h
+   Compile a single-file release files :file:`open62541.c` and :file:`open62541.h`
 **UA_ENABLE_MULTITHREADING**
-   Enable multi-threading support (experimental)
+   Enable multi-threading support
 **UA_ENABLE_COVERAGE**
    Measure the coverage of unit tests
-**UA_ENABLE_EMBEDDED_LIBC**
-   Use a custom implementation of some libc functions that might be missing on embedded targets (e.g. string handling).
 
 Some options are marked as advanced. The advanced options need to be toggled to
 be visible in the cmake GUIs.
 
-**UA_ENABLE_EXTERNAL_NAMESPACES**
-   Enable external namespaces in server
+**UA_ENABLE_TYPENAMES**
+   Add the type and member names to the UA_DataType structure
 **UA_ENABLE_GENERATE_NAMESPACE0**
-   Enable automatic generation of NS0
-**UA_ENABLE_GENERATE_NAMESPACE0_FILE**
-   File for NS0 generation from namespace0 folder. Default value is Opc.Ua.NodeSet2.xml
+   Generate and load UA XML Namespace 0 definition
+   ``UA_GENERATE_NAMESPACE0_FILE`` is used to specify the file for NS0 generation from namespace0 folder. Default value is ``Opc.Ua.NodeSet2.xml``
+**UA_ENABLE_EMBEDDED_LIBC**
+   Use a custom implementation of some libc functions that might be missing on embedded targets (e.g. string handling).
+**UA_ENABLE_EXTERNAL_NAMESPACES**
+  Enable namespace handling by an external component (experimental)
 **UA_ENABLE_NONSTANDARD_STATELESS**
-   Stateless service calls
+   Enable stateless extension
 **UA_ENABLE_NONSTANDARD_UDP**
-   UDP network layer
+   Enable udp extension
+
+Building a shared library
+-------------------------
+
+open62541 is small enough that most users will want to statically link the library into their programs. If a shared library (.dll, .so) is required, this can be enabled in CMake with the `BUILD_SHARED_LIBS` option.
+Note that this option modifies the :file:`ua_config.h` file that is also included in :file:`open62541.h` for the single-file distribution.

+ 4 - 40
doc/tutorial_client_firstSteps.rst

@@ -34,47 +34,11 @@ Reading a node attibute
 In this example we are going to connect to the server from the second tutorial
 and read the value-attribute of the added variable node.
 
-.. code-block:: c
-
-    #include <stdio.h>
-    #include "open62541.h"
-
-    int main(int argc, char *argv[])
-    {
-        /* create a client and connect */
-        UA_Client *client = UA_Client_new(UA_ClientConfig_standard);
-        UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:16664");
-        if(retval != UA_STATUSCODE_GOOD) {
-            UA_Client_delete(client);
-            return retval;
-        }
+.. literalinclude:: ../../examples/client_firstSteps.c
+   :language: c
+   :linenos:
+   :lines: 4,5,12,14-
 
-        /* create a readrequest with one entry */
-        UA_ReadRequest req;
-        UA_ReadRequest_init(&req);
-        req.nodesToRead = UA_Array_new(1, &UA_TYPES[UA_TYPES_READVALUEID]);
-        req.nodesToReadSize = 1;
-
-        /* define the node and attribute to be read */
-        req.nodesToRead[0].nodeId = UA_NODEID_STRING_ALLOC(1, "the.answer");
-        req.nodesToRead[0].attributeId = UA_ATTRIBUTEID_VALUE;
-
-        /* call the service and print the result */
-        UA_ReadResponse resp = UA_Client_Service_read(client, req);
-        if(resp.responseHeader.serviceResult == UA_STATUSCODE_GOOD &&
-           resp.resultsSize > 0 && resp.results[0].hasValue &&
-           UA_Variant_isScalar(&resp.results[0].value) &&
-            resp.results[0].value.type == &UA_TYPES[UA_TYPES_INT32]) {
-            UA_Int32 *value = (UA_Int32*)resp.results[0].value.data;
-            printf("the value is: %i\n", *value);
-        }
-
-        UA_ReadRequest_deleteMembers(&req);
-        UA_ReadResponse_deleteMembers(&resp);
-        UA_Client_disconnect(client);
-        UA_Client_delete(client);
-        return UA_STATUSCODE_GOOD;
-    }
 
 Further tasks
 -------------

+ 4 - 25
doc/tutorial_server_firstSteps.rst

@@ -17,31 +17,10 @@ have the ``open62541.c/.h`` files in the current folder.
 
 Now create a new C source-file called ``myServer.c`` with the following content:
 
-.. code-block:: c
-
-   #include <signal.h>
-   #include "open62541.h"
-
-   UA_Boolean running = true;
-   void signalHandler(int sig) {
-       running = false;
-   }
-
-   int main(void) {
-       signal(SIGINT, signalHandler); /* catch ctrl-c */
-
-       UA_ServerConfig config = UA_ServerConfig_standard;
-       UA_ServerNetworkLayer nl = UA_ServerNetworkLayerTCP(UA_ConnectionConfig_standard, 16664);
-       config.networkLayers = &nl;
-       config.networkLayersSize = 1;
-
-       UA_Server *server = UA_Server_new(config);
-       UA_Server_run(server, &running);
-       UA_Server_delete(server);
-       nl.deleteMembers(&nl);
-
-       return 0;
-   }
+.. literalinclude:: ../../examples/server_firstSteps.c
+   :language: c
+   :linenos:
+   :lines: 4,12,14-34
 
 This is all that is needed for a simple OPC UA server. Compile the the server
 with GCC using the following command:

+ 11 - 75
doc/tutorial_server_method.rst

@@ -5,82 +5,18 @@ This tutorial demonstrates how to add method nodes to the server. Use an UA
 client, e.g., UaExpert to call the method (right-click on the method node ->
 call).
 
-.. code-block:: c
+The first example shows how to define input and output arguments (lines 72 - 88),
+make the method executable (lines 94,95), add the method node (line 96-101)
+with a specified method callback (lines 10 - 24).
 
-    #include <signal.h>
-    #include <stdlib.h>
-    #include "open62541.h"
+The second example shows that a method can also be applied on an array
+as input argument and output argument.
 
-    UA_Boolean running = true;
-    static void stopHandler(int sign) {
-        UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "received ctrl-c");
-        running = 0;
-    }
+The last example presents a way to bind a new method callback to an already
+instantiated method node.
 
-    UA_Logger logger = UA_Log_Stdout;
 
-    static UA_StatusCode
-    helloWorldMethod(void *handle, const UA_NodeId objectId, size_t inputSize,
-                     const UA_Variant *input, size_t outputSize, UA_Variant *output) {
-            UA_String *inputStr = (UA_String*)input->data;
-            UA_String tmp = UA_STRING_ALLOC("Hello ");
-            if(inputStr->length > 0) {
-                tmp.data = realloc(tmp.data, tmp.length + inputStr->length);
-                memcpy(&tmp.data[tmp.length], inputStr->data, inputStr->length);
-                tmp.length += inputStr->length;
-            }
-            UA_Variant_setScalarCopy(output, &tmp, &UA_TYPES[UA_TYPES_STRING]);
-            UA_String_deleteMembers(&tmp);
-            UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "Hello World was called");
-            return UA_STATUSCODE_GOOD;
-    }
-
-    int main(int argc, char** argv) {
-        signal(SIGINT, stopHandler); /* catches ctrl-c */
-
-        /* initialize the server */
-        UA_ServerConfig config = UA_ServerConfig_standard;
-        UA_ServerNetworkLayer nl = UA_ServerNetworkLayerTCP(UA_ConnectionConfig_standard, 16664);
-        config.networkLayers = &nl;
-        config.networkLayersSize = 1;
-        UA_Server *server = UA_Server_new(config);
-
-        /* add the method node with the callback */
-        UA_Argument inputArguments;
-        UA_Argument_init(&inputArguments);
-        inputArguments.arrayDimensionsSize = 0;
-        inputArguments.arrayDimensions = NULL;
-        inputArguments.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
-        inputArguments.description = UA_LOCALIZEDTEXT("en_US", "A String");
-        inputArguments.name = UA_STRING("MyInput");
-        inputArguments.valueRank = -1;
-
-        UA_Argument outputArguments;
-        UA_Argument_init(&outputArguments);
-        outputArguments.arrayDimensionsSize = 0;
-        outputArguments.arrayDimensions = NULL;
-        outputArguments.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
-        outputArguments.description = UA_LOCALIZEDTEXT("en_US", "A String");
-        outputArguments.name = UA_STRING("MyOutput");
-        outputArguments.valueRank = -1;
-
-        UA_MethodAttributes helloAttr;
-        UA_MethodAttributes_init(&helloAttr);
-        helloAttr.description = UA_LOCALIZEDTEXT("en_US","Say `Hello World`");
-        helloAttr.displayName = UA_LOCALIZEDTEXT("en_US","Hello World");
-        helloAttr.executable = true;
-        helloAttr.userExecutable = true;
-        UA_Server_addMethodNode(server, UA_NODEID_NUMERIC(1,62541),
-                                UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
-                                UA_NODEID_NUMERIC(0, UA_NS0ID_HASORDEREDCOMPONENT),
-                                UA_QUALIFIEDNAME(1, "hello world"), 
-                                helloAttr, &helloWorldMethod, NULL,
-                                1, &inputArguments, 1, &outputArguments, NULL);
-
-        /* run the server */
-        UA_Server_run(server, &running);
-        UA_Server_delete(server);
-        nl.deleteMembers(&nl);
-
-        return 0;
-    }
+.. literalinclude:: ../../examples/server_method.c
+   :language: c
+   :linenos:
+   :lines: 4,5,14,16-

+ 15 - 44
doc/tutorial_server_variables.rst

@@ -11,57 +11,18 @@ connected to a physical process in the background. Make sure to read the
 This is the code for a server with a single variable node holding an integer. We
 will take this example to explain some of the fundamental concepts of open62541.
 
-.. code-block:: c
+.. literalinclude:: ../../examples/server_variable.c
+   :language: c
+   :linenos:
+   :lines: 4,13,15-
 
-    #include <signal.h>
-    #include "open62541.h"
-
-    UA_Boolean running = true;
-    static void stopHandler(int sign) {
-        running = false;
-    }
-
-    int main(int argc, char** argv) {
-        signal(SIGINT, stopHandler); /* catch ctrl-c */
-
-        UA_ServerConfig config = UA_ServerConfig_standard;
-        UA_ServerNetworkLayer nl = UA_ServerNetworkLayerTCP(UA_ConnectionConfig_standard, 16664);
-        config.networkLayers = &nl;
-        config.networkLayersSize = 1;
-        UA_Server *server = UA_Server_new(config);
-
-        /* 1) Define the attribute of the myInteger variable node */
-        UA_VariableAttributes attr;
-        UA_VariableAttributes_init(&attr);
-        UA_Int32 myInteger = 42;
-        UA_Variant_setScalar(&attr.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
-        attr.description = UA_LOCALIZEDTEXT("en_US","the answer");
-        attr.displayName = UA_LOCALIZEDTEXT("en_US","the answer");
-
-        /* 2) Add the variable node to the information model */
-        UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
-        UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer");
-        UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
-        UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
-        UA_StatusCode retval = UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId,
-                                                         parentReferenceNodeId, myIntegerName,
-                                                         UA_NODEID_NULL, attr, NULL, NULL);
-
-        if(retval == UA_STATUSCODE_GOOD)
-            UA_Server_run(server, &running);
-
-        UA_Server_delete(server);
-        nl.deleteMembers(&nl);
-
-        return retval;
-    }
 
 Variants and Datatypes
 ----------------------
 
 The datatype *variant* belongs to the built-in datatypes of OPC UA and is used
 as a container type. A variant can hold any other datatype as a scalar (except
-Variant) or as an array. Array variants can additionally denote the
+variant) or as an array. Array variants can additionally denote the
 dimensionality of the data (e.g. a 2x3 matrix) in an additional integer array.
 You can find the code that defines the variant datatype :ref:`here <variant>`.
 
@@ -133,6 +94,10 @@ some examples for their usage.
    UA_NodeId id3 = UA_NODEID_STRING_ALLOC(1, "testid");
    UA_NodeId_deleteMembers(&id3); /* free the allocated string */
 
+
+What is UA_NODEID_STRING_ALLOC for?
+
+
 Adding a variable node to the server that contains a user-defined callback
 --------------------------------------------------------------------------
 
@@ -143,6 +108,12 @@ callback pointer is intserted into the node.
 Consider ``examples/server_datasource.c`` in the repository. The examples are
 compiled if the Cmake option UA_BUILD_EXAMPLE is turned on.
 
+
+UA_Server_addVariableNode vs. UA_Server_addDataSourceVariableNode
+UA_ValueCallback
+UA_DataSource
+
+
 Asserting success/failure
 -------------------------
 

+ 13 - 0
examples/CMakeLists.txt

@@ -67,6 +67,19 @@ if(UA_ENABLE_METHODCALLS)
   target_link_libraries(server_method ${LIBS})
 endif()
 
+if(UA_BUILD_SELFSIGNED_CERTIFICATE)
+  find_package(OpenSSL REQUIRED)
+  add_custom_command(OUTPUT server_cert.der ca.crt
+                     COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/certs/create_self-signed.py ${CMAKE_CURRENT_BINARY_DIR}
+                     DEPENDS ${PROJECT_SOURCE_DIR}/tools/certs/create_self-signed.py
+                             ${PROJECT_SOURCE_DIR}/tools/certs/localhost.cnf)
+
+  add_custom_target(selfsigned ALL DEPENDS server_cert.der ca.crt)
+
+  add_executable(server_certificate server_certificate.c $<TARGET_OBJECTS:open62541-object> server_cert.der ca.crt)
+  target_link_libraries(server_certificate ${LIBS})
+endif()
+
 if(UA_ENABLE_DISCOVERY)
 
     add_executable(discovery_server_discovery discovery/server_discovery.c)

+ 10 - 5
examples/client_firstSteps.c

@@ -5,11 +5,11 @@
 #include <inttypes.h>
 
 #ifdef UA_NO_AMALGAMATION
-# include "ua_client.h"
-# include "ua_config_standard.h"
-# include "ua_network_tcp.h"
+#include "ua_client.h"
+#include "ua_config_standard.h"
+#include "ua_network_tcp.h"
 #else
-# include "open62541.h"
+#include "open62541.h"
 #endif
 
 int main(void) {
@@ -24,13 +24,18 @@ int main(void) {
     UA_DateTime raw_date = 0;
     UA_String string_date;
 
+    /* create a readrequest with one entry */
     UA_ReadRequest rReq;
     UA_ReadRequest_init(&rReq);
     rReq.nodesToRead = UA_Array_new(1, &UA_TYPES[UA_TYPES_READVALUEID]);
     rReq.nodesToReadSize = 1;
-    rReq.nodesToRead[0].nodeId = UA_NODEID_NUMERIC(0, 2258);
+
+    /* define the node and attribute to be read */
+    #define NS0_CURRENT_TIME 2258
+    rReq.nodesToRead[0].nodeId = UA_NODEID_NUMERIC(0, NS0_CURRENT_TIME);
     rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_VALUE;
 
+    /* call the service and print the result */
     UA_ReadResponse rResp = UA_Client_Service_read(client, rReq);
     if(rResp.responseHeader.serviceResult == UA_STATUSCODE_GOOD && rResp.resultsSize > 0 &&
        rResp.results[0].hasValue && UA_Variant_isScalar(&rResp.results[0].value) &&

+ 38 - 0
examples/server.c

@@ -5,6 +5,10 @@
  * - single-threaded: gcc -std=c99 server.c open62541.c -o server
  * - multi-threaded: gcc -std=c99 server.c open62541.c -o server -lurcu-cds -lurcu -lurcu-common -lpthread */
 
+#ifdef _MSC_VER
+#define _CRT_SECURE_NO_WARNINGS //disable fopen deprication warning in msvs
+#endif
+
 #ifdef UA_NO_AMALGAMATION
 # include <time.h>
 # include "ua_types.h"
@@ -39,6 +43,32 @@
 
 UA_Boolean running = 1;
 UA_Logger logger = UA_Log_Stdout;
+
+static UA_ByteString loadCertificate(void) {
+    UA_ByteString certificate = UA_STRING_NULL;
+    FILE *fp = NULL;
+    //FIXME: a potiential bug of locating the certificate, we need to get the path from the server's config
+    fp=fopen("server_cert.der", "rb");
+
+    if(!fp) {
+        errno = 0; // we read errno also from the tcp layer...
+        return certificate;
+    }
+
+    fseek(fp, 0, SEEK_END);
+    certificate.length = (size_t)ftell(fp);
+    certificate.data = malloc(certificate.length*sizeof(UA_Byte));
+    if(!certificate.data)
+        return certificate;
+
+    fseek(fp, 0, SEEK_SET);
+    if(fread(certificate.data, sizeof(UA_Byte), certificate.length, fp) < (size_t)certificate.length)
+        UA_ByteString_deleteMembers(&certificate); // error reading the cert
+    fclose(fp);
+
+    return certificate;
+}
+
 static void stopHandler(int sign) {
     UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "Received Ctrl-C");
     running = 0;
@@ -106,6 +136,10 @@ int main(int argc, char** argv) {
     UA_ServerConfig config = UA_ServerConfig_standard;
     config.networkLayers = &nl;
     config.networkLayersSize = 1;
+
+    /* load certificate */
+    config.serverCertificate = loadCertificate();
+
     UA_Server *server = UA_Server_new(config);
 
     /* add a static variable node to the server */
@@ -368,6 +402,10 @@ int main(int argc, char** argv) {
 
     /* run server */
     UA_StatusCode retval = UA_Server_run(server, &running); /* run until ctrl-c is received */
+
+    /* deallocate certificate's memory */
+    UA_ByteString_deleteMembers(&config.serverCertificate);
+
     UA_Server_delete(server);
     nl.deleteMembers(&nl);
     return (int)retval;

+ 82 - 0
examples/server_certificate.c

@@ -0,0 +1,82 @@
+/* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
+ * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
+
+#ifdef _MSC_VER
+#define _CRT_SECURE_NO_WARNINGS //disable fopen deprication warning in msvs
+#endif
+
+#ifdef UA_NO_AMALGAMATION
+#include "ua_types.h"
+#include "ua_server.h"
+#include "ua_config_standard.h"
+#include "ua_network_tcp.h"
+#include "ua_log_stdout.h"
+#else
+#include "open62541.h"
+#endif
+
+#include <errno.h> // errno, EINTR
+#include <stdio.h>
+#include <signal.h>
+#include <stdlib.h>
+
+UA_Boolean running = true;
+UA_Logger logger = UA_Log_Stdout;
+
+static UA_ByteString loadCertificate(void) {
+    UA_ByteString certificate = UA_STRING_NULL;
+    FILE *fp = NULL;
+    //FIXME: a potiential bug of locating the certificate, we need to get the path from the server's config
+    fp=fopen("server_cert.der", "rb");
+
+    if(!fp) {
+        errno = 0; // we read errno also from the tcp layer...
+        UA_LOG_ERROR(logger, UA_LOGCATEGORY_SERVER, "Could not open certificate file");
+        return certificate;
+    }
+
+    fseek(fp, 0, SEEK_END);
+    certificate.length = (size_t)ftell(fp);
+    certificate.data = malloc(certificate.length*sizeof(UA_Byte));
+    if(!certificate.data)
+        return certificate;
+
+    fseek(fp, 0, SEEK_SET);
+    if(fread(certificate.data, sizeof(UA_Byte), certificate.length, fp) < (size_t)certificate.length)
+        UA_ByteString_deleteMembers(&certificate); // error reading the cert
+    fclose(fp);
+
+    return certificate;
+}
+
+static void stopHandler(int sign) {
+    UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "received ctrl-c");
+    running = false;
+}
+
+int main(int argc, char** argv) {
+    signal(SIGINT, stopHandler); /* catches ctrl-c */
+
+    UA_ServerConfig config = UA_ServerConfig_standard;
+    UA_ServerNetworkLayer nl = UA_ServerNetworkLayerTCP(UA_ConnectionConfig_standard, 16664);
+    config.networkLayers = &nl;
+    config.networkLayersSize = 1;
+
+    /* load certificate */
+    config.serverCertificate = loadCertificate();
+    if(config.serverCertificate.length > 0)
+        UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "Certificate loaded");
+
+
+    UA_Server *server = UA_Server_new(config);
+
+    UA_StatusCode retval = UA_Server_run(server, &running);
+
+    /* deallocate certificate's memory */
+    UA_ByteString_deleteMembers(&config.serverCertificate);
+
+    UA_Server_delete(server);
+    nl.deleteMembers(&nl);
+
+    return (int)retval;
+}

+ 5 - 6
examples/server_firstSteps.c

@@ -1,16 +1,15 @@
 /* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
  * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
 
-#include <stdio.h>
 #include <signal.h>
 
 #ifdef UA_NO_AMALGAMATION
-# include "ua_types.h"
-# include "ua_server.h"
-# include "ua_config_standard.h"
-# include "ua_network_tcp.h"
+#include "ua_types.h"
+#include "ua_server.h"
+#include "ua_config_standard.h"
+#include "ua_network_tcp.h"
 #else
-# include "open62541.h"
+#include "open62541.h"
 #endif
 
 UA_Boolean running = true;

+ 75 - 57
examples/server_method.c

@@ -5,18 +5,20 @@
 #include <stdlib.h>
 
 #ifdef UA_NO_AMALGAMATION
-# include "ua_types.h"
-# include "ua_server.h"
-# include "ua_config_standard.h"
-# include "ua_network_tcp.h"
-# include "ua_log_stdout.h"
+#include "ua_types.h"
+#include "ua_server.h"
+#include "ua_config_standard.h"
+#include "ua_network_tcp.h"
+#include "ua_log_stdout.h"
 #else
-# include "open62541.h"
+#include "open62541.h"
 #endif
 
 UA_Boolean running = true;
 UA_Logger logger = UA_Log_Stdout;
 
+
+// EXAMPLE 1 method
 static UA_StatusCode
 helloWorldMethod(void *handle, const UA_NodeId objectId, size_t inputSize, const UA_Variant *input,
                  size_t outputSize, UA_Variant *output) {
@@ -33,6 +35,18 @@ helloWorldMethod(void *handle, const UA_NodeId objectId, size_t inputSize, const
         return UA_STATUSCODE_GOOD;
 }
 
+// EXAMPLE 2 method
+static UA_StatusCode
+IncInt32ArrayValuesMethod(void *handle, const UA_NodeId objectId, size_t inputSize,
+                          const UA_Variant *input, size_t outputSize, UA_Variant *output) {
+    UA_Variant_setArrayCopy(output, input->data, 5, &UA_TYPES[UA_TYPES_INT32]);
+    for(size_t i = 0; i< input->arrayLength; i++)
+        ((UA_Int32*)output->data)[i] = ((UA_Int32*)input->data)[i] + 1;
+    return UA_STATUSCODE_GOOD;
+}
+
+
+// EXAMPLE 3 method
 static UA_StatusCode
 fooBarMethod(void *handle, const UA_NodeId objectId, size_t inputSize, const UA_Variant *input,
                  size_t outputSize, UA_Variant *output) {
@@ -46,19 +60,10 @@ fooBarMethod(void *handle, const UA_NodeId objectId, size_t inputSize, const UA_
         }
         UA_Variant_setScalarCopy(output, &tmp, &UA_TYPES[UA_TYPES_STRING]);
         UA_String_deleteMembers(&tmp);
-        UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "Hello World was called");
+        UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "FooBar was called");
         return UA_STATUSCODE_GOOD;
 }
 
-static UA_StatusCode
-IncInt32ArrayValuesMethod(void *handle, const UA_NodeId objectId, size_t inputSize,
-                          const UA_Variant *input, size_t outputSize, UA_Variant *output) {
-    UA_Variant_setArrayCopy(output, input->data, 5, &UA_TYPES[UA_TYPES_INT32]);
-    for(size_t i = 0; i< input->arrayLength; i++)
-        ((UA_Int32*)output->data)[i] = ((UA_Int32*)input->data)[i] + 1;
-    return UA_STATUSCODE_GOOD;
-}
-
 static void stopHandler(int sign) {
     UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "received ctrl-c");
     running = 0;
@@ -76,23 +81,23 @@ int main(int argc, char** argv) {
 
     //EXAMPLE 1
     /* add the method node with the callback */
-    UA_Argument inputArguments;
-    UA_Argument_init(&inputArguments);
-    inputArguments.arrayDimensionsSize = 0;
-    inputArguments.arrayDimensions = NULL;
-    inputArguments.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
-    inputArguments.description = UA_LOCALIZEDTEXT("en_US", "A String");
-    inputArguments.name = UA_STRING("MyInput");
-    inputArguments.valueRank = -1;
-
-    UA_Argument outputArguments;
-    UA_Argument_init(&outputArguments);
-    outputArguments.arrayDimensionsSize = 0;
-    outputArguments.arrayDimensions = NULL;
-    outputArguments.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
-    outputArguments.description = UA_LOCALIZEDTEXT("en_US", "A String");
-    outputArguments.name = UA_STRING("MyOutput");
-    outputArguments.valueRank = -1;
+    UA_Argument inputArguments1;
+    UA_Argument_init(&inputArguments1);
+    inputArguments1.arrayDimensionsSize = 0;
+    inputArguments1.arrayDimensions = NULL;
+    inputArguments1.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
+    inputArguments1.description = UA_LOCALIZEDTEXT("en_US", "A String");
+    inputArguments1.name = UA_STRING("MyInput");
+    inputArguments1.valueRank = -1;
+
+    UA_Argument outputArguments1;
+    UA_Argument_init(&outputArguments1);
+    outputArguments1.arrayDimensionsSize = 0;
+    outputArguments1.arrayDimensions = NULL;
+    outputArguments1.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
+    outputArguments1.description = UA_LOCALIZEDTEXT("en_US", "A String");
+    outputArguments1.name = UA_STRING("MyOutput");
+    outputArguments1.valueRank = -1;
 
     UA_MethodAttributes helloAttr;
     UA_MethodAttributes_init(&helloAttr);
@@ -103,36 +108,37 @@ int main(int argc, char** argv) {
     UA_Server_addMethodNode(server, UA_NODEID_NUMERIC(1,62541),
                             UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
                             UA_NODEID_NUMERIC(0, UA_NS0ID_HASORDEREDCOMPONENT),
-                            UA_QUALIFIEDNAME(1, "hello world"), 
+                            UA_QUALIFIEDNAME(1, "hello world"),
                             helloAttr, &helloWorldMethod, NULL,
-                            1, &inputArguments, 1, &outputArguments, NULL);
-
+                            1, &inputArguments1, 1, &outputArguments1, NULL);
     //END OF EXAMPLE 1
 
     //EXAMPLE 2
     /* add another method node: output argument as 1d Int32 array*/
     // define input arguments
-    UA_Argument_init(&inputArguments);
-    inputArguments.arrayDimensionsSize = 1;
+    UA_Argument inputArguments2;
+    UA_Argument_init(&inputArguments2);
+    inputArguments2.arrayDimensionsSize = 1;
     UA_UInt32 * pInputDimensions = UA_UInt32_new();
     pInputDimensions[0] = 5;
-    inputArguments.arrayDimensions = pInputDimensions;
-    inputArguments.dataType = UA_TYPES[UA_TYPES_INT32].typeId;
-    inputArguments.description = UA_LOCALIZEDTEXT("en_US",
+    inputArguments2.arrayDimensions = pInputDimensions;
+    inputArguments2.dataType = UA_TYPES[UA_TYPES_INT32].typeId;
+    inputArguments2.description = UA_LOCALIZEDTEXT("en_US",
                     "input an array with 5 elements, type int32");
-    inputArguments.name = UA_STRING("int32 value");
-    inputArguments.valueRank = 1;
+    inputArguments2.name = UA_STRING("int32 value");
+    inputArguments2.valueRank = 1;
 
     // define output arguments
-    UA_Argument_init(&outputArguments);
-    outputArguments.arrayDimensionsSize = 1;
+    UA_Argument outputArguments2;
+    UA_Argument_init(&outputArguments2);
+    outputArguments2.arrayDimensionsSize = 1;
     UA_UInt32 * pOutputDimensions = UA_UInt32_new();
     pOutputDimensions[0] = 5;
-    outputArguments.arrayDimensions = pOutputDimensions;
-    outputArguments.dataType = UA_TYPES[UA_TYPES_INT32].typeId;
-    outputArguments.description = UA_LOCALIZEDTEXT("en_US", "increment each array index");
-    outputArguments.name = UA_STRING("output is the array, each index is incremented by one");
-    outputArguments.valueRank = 1;
+    outputArguments2.arrayDimensions = pOutputDimensions;
+    outputArguments2.dataType = UA_TYPES[UA_TYPES_INT32].typeId;
+    outputArguments2.description = UA_LOCALIZEDTEXT("en_US", "increment each array index");
+    outputArguments2.name = UA_STRING("output is the array, each index is incremented by one");
+    outputArguments2.valueRank = 1;
 
     UA_MethodAttributes incAttr;
     UA_MethodAttributes_init(&incAttr);
@@ -142,18 +148,30 @@ int main(int argc, char** argv) {
     incAttr.userExecutable = true;
     UA_Server_addMethodNode(server, UA_NODEID_STRING(1, "IncInt32ArrayValues"),
                             UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
-                            UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), 
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                             UA_QUALIFIEDNAME(1, "IncInt32ArrayValues"),
                             incAttr, &IncInt32ArrayValuesMethod, NULL,
-                            1, &inputArguments, 1, &outputArguments, NULL);
+                            1, &inputArguments2, 1, &outputArguments2, NULL);
     //END OF EXAMPLE 2
 
-    /* If out methodnode is part of an instantiated object, we never had
-       the opportunity to define the callback... we could do that now
-    */
-    UA_Server_setMethodNode_callback(server, UA_NODEID_NUMERIC(1,62541), &fooBarMethod, NULL);
-
+    //EXAMPLE 3
+    UA_MethodAttributes method3Attr;
+    UA_MethodAttributes_init(&method3Attr);
+    method3Attr.description = UA_LOCALIZEDTEXT("en_US","FooBar");
+    method3Attr.displayName = UA_LOCALIZEDTEXT("en_US","FooBar");
+    method3Attr.executable = true;
+    method3Attr.userExecutable = true;
+    UA_Server_addMethodNode(server, UA_NODEID_STRING(1,"FooBar"),
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
+                            UA_QUALIFIEDNAME(1, "FooBar"),
+                            method3Attr, NULL, NULL,
+                            1, &inputArguments1, 1, &outputArguments1, NULL);
+    /*  If the method node has no callback (because it was instantiated without one) or
+        if we just want to change it, this can be done with UA_Server_setMethodNode_callback() */
+    UA_Server_setMethodNode_callback(server,  UA_NODEID_NUMERIC(1,62542), &fooBarMethod, NULL);
     //END OF EXAMPLE 3
+
     /* start server */
     UA_StatusCode retval = UA_Server_run(server, &running);
 

+ 13 - 11
examples/server_variable.c

@@ -4,13 +4,13 @@
 #include <signal.h>
 
 #ifdef UA_NO_AMALGAMATION
-# include "ua_types.h"
-# include "ua_server.h"
-# include "ua_config_standard.h"
-# include "ua_network_tcp.h"
-# include "ua_log_stdout.h"
+#include "ua_types.h"
+#include "ua_server.h"
+#include "ua_config_standard.h"
+#include "ua_network_tcp.h"
+#include "ua_log_stdout.h"
 #else
-# include "open62541.h"
+#include "open62541.h"
 #endif
 
 UA_Boolean running = true;
@@ -38,13 +38,15 @@ int main(int argc, char** argv) {
     config.networkLayersSize = 1;
     UA_Server *server = UA_Server_new(config);
 
-    /* add a variable node to the address space */
+    /* 1) Define the attribute of the myInteger variable node */
     UA_VariableAttributes attr;
     UA_VariableAttributes_init(&attr);
-    /* UA_Int32 myInteger = 42; */
-    /* UA_Variant_setScalar(&attr.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]); */
-    /* attr.description = UA_LOCALIZEDTEXT("en_US","the answer"); */
-    /* attr.displayName = UA_LOCALIZEDTEXT("en_US","the answer"); */
+    UA_Int32 myInteger = 42;
+    UA_Variant_setScalar(&attr.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
+    attr.description = UA_LOCALIZEDTEXT("en_US","the answer");
+    attr.displayName = UA_LOCALIZEDTEXT("en_US","the answer");
+
+    /* 2) Add the variable node to the information model */
     UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
     UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer");
     UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);

+ 12 - 7
include/ua_config.h.in

@@ -46,27 +46,32 @@ extern "C" {
 /**
  * Function Export
  * --------------- */
-#ifdef _WIN32
-# ifdef UA_DYNAMIC_LINKING
+/* On Win32: Define UA_DYNAMIC_LINKING and UA_DYNAMIC_LINKING_EXPORT in order to
+   export symbols for a DLL. Define UA_DYNAMIC_LINKING only to import symbols
+   from a DLL.*/
+#cmakedefine UA_DYNAMIC_LINKING
+#if defined(_WIN32) && defined(UA_DYNAMIC_LINKING)
+# ifdef UA_DYNAMIC_LINKING_EXPORT /* export dll */
 #  ifdef __GNUC__
 #   define UA_EXPORT __attribute__ ((dllexport))
 #  else
 #   define UA_EXPORT __declspec(dllexport)
 #  endif
-# else
+# else /* import dll */
 #  ifdef __GNUC__
-#   define UA_EXPORT __attribute__ ((dllexport))
+#   define UA_EXPORT __attribute__ ((dllimport))
 #  else
 #   define UA_EXPORT __declspec(dllimport)
 #  endif
 # endif
-#else
+#else /* non win32 */
 # if __GNUC__ || __clang__
 #  define UA_EXPORT __attribute__ ((visibility ("default")))
-# else
-#  define UA_EXPORT
 # endif
 #endif
+#ifndef UA_EXPORT
+# define UA_EXPORT /* fallback to default */
+#endif
 
 /**
  * Inline Functions

+ 4 - 5
include/ua_types.h

@@ -439,8 +439,7 @@ typedef struct {
 
 static UA_INLINE UA_Boolean
 UA_QualifiedName_isNull(const UA_QualifiedName *q) {
-    return (q->namespaceIndex == 0 &&
-            q->name.length == 0);
+    return (q->namespaceIndex == 0 && q->name.length == 0);
 }
 
 static UA_INLINE UA_QualifiedName
@@ -615,9 +614,9 @@ typedef struct {
 UA_StatusCode UA_EXPORT
 UA_Variant_copyRange(const UA_Variant *src, UA_Variant *dst, const UA_NumericRange range);
 
-/* Insert a range of data into an existing variant. The data array can't be reused afterwards if it
- * contains types without a fixed size (e.g. strings) since the members are moved into the variant
- * and take on its lifecycle.
+/* Insert a range of data into an existing variant. The data array can't be
+ * reused afterwards if it contains types without a fixed size (e.g. strings)
+ * since the members are moved into the variant and take on its lifecycle.
  *
  * @param v The variant
  * @param dataArray The data array. The type must match the variant

+ 21 - 15
src/server/ua_server.c

@@ -452,18 +452,10 @@ createVariableTypeNode(UA_Server *server, char* name, UA_UInt32 variabletypeid,
     return variabletype;
 }
 
-static void
-addVariableTypeNode_organized(UA_Server *server, char* name, UA_UInt32 variabletypeid,
-                              UA_UInt32 parent, UA_Boolean abstract) {
-    UA_VariableTypeNode *variabletype = createVariableTypeNode(server, name, variabletypeid, parent, abstract);
-    addNodeInternal(server, (UA_Node*)variabletype, UA_NODEID_NUMERIC(0, parent), nodeIdOrganizes);
-}
-
 static void
 addVariableTypeNode_subtype(UA_Server *server, char* name, UA_UInt32 variabletypeid,
                             UA_UInt32 parent, UA_Boolean abstract) {
-    UA_VariableTypeNode *variabletype =
-        createVariableTypeNode(server, name, variabletypeid, parent, abstract);
+    UA_VariableTypeNode *variabletype = createVariableTypeNode(server, name, variabletypeid, parent, abstract);
     addNodeInternal(server, (UA_Node*)variabletype, UA_NODEID_NUMERIC(0, parent), nodeIdHasSubType);
 }
 
@@ -728,7 +720,8 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     hasnotifier->nodeId.identifier.numeric = UA_NS0ID_HASNOTIFIER;
     hasnotifier->isAbstract = false;
     hasnotifier->symmetric  = false;
-    addNodeInternal(server, (UA_Node*)hasnotifier, UA_NODEID_NUMERIC(0, UA_NS0ID_HASEVENTSOURCE), nodeIdHasSubType);
+    addNodeInternal(server, (UA_Node*)hasnotifier, UA_NODEID_NUMERIC(0, UA_NS0ID_HASEVENTSOURCE),
+                    nodeIdHasSubType);
 
     UA_ReferenceTypeNode *hasorderedcomponent = UA_NodeStore_newReferenceTypeNode();
     copyNames((UA_Node*)hasorderedcomponent, "HasOrderedComponent");
@@ -736,7 +729,8 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     hasorderedcomponent->nodeId.identifier.numeric = UA_NS0ID_HASORDEREDCOMPONENT;
     hasorderedcomponent->isAbstract = false;
     hasorderedcomponent->symmetric  = false;
-    addNodeInternal(server, (UA_Node*)hasorderedcomponent, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), nodeIdHasSubType);
+    addNodeInternal(server, (UA_Node*)hasorderedcomponent, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
+                    nodeIdHasSubType);
 
     UA_ReferenceTypeNode *hasmodelparent = UA_NodeStore_newReferenceTypeNode();
     copyNames((UA_Node*)hasmodelparent, "HasModelParent");
@@ -769,7 +763,7 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     hascause->isAbstract = false;
     hascause->symmetric  = false;
     addNodeInternal(server, (UA_Node*)hascause, nodeIdNonHierarchicalReferences, nodeIdHasSubType);
-    
+
     UA_ReferenceTypeNode *haseffect = UA_NodeStore_newReferenceTypeNode();
     copyNames((UA_Node*)haseffect, "HasEffect");
     haseffect->inverseName = UA_LOCALIZEDTEXT_ALLOC("en_US", "MayBeEffectedBy");
@@ -848,7 +842,6 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     /* Link in the bootstrapped baseobjecttype */
     addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTTYPESFOLDER), nodeIdOrganizes,
                          UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE), true);
-
     addObjectTypeNode(server, "ServerType", UA_NS0ID_SERVERTYPE, UA_NS0ID_BASEOBJECTTYPE, UA_NS0ID_HASSUBTYPE);
     addObjectTypeNode(server, "ServerDiagnosticsType", UA_NS0ID_SERVERDIAGNOSTICSTYPE,
                       UA_NS0ID_BASEOBJECTTYPE, UA_NS0ID_HASSUBTYPE);
@@ -902,13 +895,26 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     addDataTypeNode(server, "Enumeration", UA_NS0ID_ENUMERATION, UA_NS0ID_BASEDATATYPE);
         addDataTypeNode(server, "ServerState", UA_NS0ID_SERVERSTATE, UA_NS0ID_ENUMERATION);
 
+    /******************/
+    /* Variable Types */
+    /******************/
+
     UA_ObjectNode *variabletypes = UA_NodeStore_newObjectNode();
     copyNames((UA_Node*)variabletypes, "VariableTypes");
     variabletypes->nodeId.identifier.numeric = UA_NS0ID_VARIABLETYPESFOLDER;
     addNodeInternalWithType(server, (UA_Node*)variabletypes, UA_NODEID_NUMERIC(0, UA_NS0ID_TYPESFOLDER),
                             nodeIdOrganizes, nodeIdFolderType);
-    addVariableTypeNode_organized(server, "BaseVariableType", UA_NS0ID_BASEVARIABLETYPE,
-                                  UA_NS0ID_VARIABLETYPESFOLDER, true);
+
+    /* Bootstrap the BaseVariableType */
+    UA_VariableTypeNode *basevariabletype = UA_NodeStore_newVariableTypeNode();
+    copyNames((UA_Node*)basevariabletype, "BaseVariableType");
+    basevariabletype->nodeId.identifier.numeric = UA_NS0ID_BASEVARIABLETYPE;
+    UA_RCU_LOCK();
+    UA_NodeStore_insert(server->nodestore, (UA_Node*)basevariabletype);
+    UA_RCU_UNLOCK();
+    addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_VARIABLETYPESFOLDER), nodeIdOrganizes,
+                         UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BASEVARIABLETYPE), true);
+
     addVariableTypeNode_subtype(server, "BaseDataVariableType", UA_NS0ID_BASEDATAVARIABLETYPE,
                                 UA_NS0ID_BASEVARIABLETYPE, false);
     addVariableTypeNode_subtype(server, "PropertyType", UA_NS0ID_PROPERTYTYPE,

+ 31 - 3
src/server/ua_services_nodemanagement.c

@@ -148,6 +148,32 @@ Service_AddNodes_existing(UA_Server *server, UA_Session *session, UA_Node *node,
         return;
     }
 
+    /* Test if the reference to the parent is valid */
+    const UA_NodeId nodeIdHasSubtype = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE);
+    switch(node->nodeClass) {
+    case UA_NODECLASS_OBJECTTYPE:
+    case UA_NODECLASS_VARIABLETYPE:
+    case UA_NODECLASS_REFERENCETYPE:
+        /* DataTypeNodes have no subtype relations */
+        /* -- CTT throw errors with this check as they test all combinations of node and reference type -- */
+        /* if(!UA_NodeId_equal(referenceTypeId, &nodeIdHasSubtype)) { */
+        /*     UA_LOG_DEBUG_SESSION(server->config.logger, session, */
+        /*                          "AddNodes: Trying to add a type node without a hassubtype relation"); */
+        /*     result->statusCode = UA_STATUSCODE_BADREFERENCENOTALLOWED; */
+        /*     UA_NodeStore_deleteNode(node); */
+        /*     return; */
+        /* } */
+        break;
+    default:
+        if(UA_NodeId_equal(referenceTypeId, &nodeIdHasSubtype)) {
+            UA_LOG_DEBUG_SESSION(server->config.logger, session,
+                                 "AddNodes: Trying to add a non-type node with a hassubtype relation");
+            result->statusCode = UA_STATUSCODE_BADREFERENCENOTALLOWED;
+            UA_NodeStore_deleteNode(node);
+            return;
+        }
+    }
+
     const UA_ReferenceTypeNode *referenceType =
         (const UA_ReferenceTypeNode *)UA_NodeStore_get(server->nodestore, referenceTypeId);
     if(!referenceType) {
@@ -207,7 +233,7 @@ Service_AddNodes_existing(UA_Server *server, UA_Session *session, UA_Node *node,
             if(!found)
                 result->statusCode = UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
             if(result->statusCode != UA_STATUSCODE_GOOD) {
-                UA_LOG_DEBUG_SESSION(server->config.logger, session, "AddNodes: The object if not derived from BaseObjectType");
+                UA_LOG_DEBUG_SESSION(server->config.logger, session, "AddNodes: The object is not derived from BaseObjectType");
                 goto remove_node;
             }
         }
@@ -229,7 +255,7 @@ Service_AddNodes_existing(UA_Server *server, UA_Session *session, UA_Node *node,
             if(!found)
                 result->statusCode = UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
             if(result->statusCode != UA_STATUSCODE_GOOD) {
-                UA_LOG_DEBUG_SESSION(server->config.logger, session, "AddNodes: The variable if not derived from BaseVariableType");
+                UA_LOG_DEBUG_SESSION(server->config.logger, session, "AddNodes: The variable is not derived from BaseVariableType");
                 goto remove_node;
             }
         }
@@ -652,7 +678,9 @@ void Service_AddNodes_single(UA_Server *server, UA_Session *session, const UA_Ad
         node = variableNodeFromAttributes(item, item->nodeAttributes.content.decoded.data);
         break;
     case UA_NODECLASS_OBJECTTYPE:
-        if(item->nodeAttributes.content.decoded.type != &UA_TYPES[UA_TYPES_OBJECTTYPEATTRIBUTES]) {
+        /* The structures of UA_ObjectTypeAttributes and UA_ObjectType are identical. CTT uses this. */
+        if(item->nodeAttributes.content.decoded.type != &UA_TYPES[UA_TYPES_OBJECTTYPEATTRIBUTES] &&
+           item->nodeAttributes.content.decoded.type != &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES]) {
             result->statusCode = UA_STATUSCODE_BADNODEATTRIBUTESINVALID;
             return;
         }

+ 67 - 68
src/ua_connection.c

@@ -32,103 +32,102 @@ void UA_Connection_deleteMembers(UA_Connection *connection) {
 UA_StatusCode
 UA_Connection_completeMessages(UA_Connection *connection, UA_ByteString * UA_RESTRICT message,
                               UA_Boolean * UA_RESTRICT realloced) {
-    UA_ByteString *current = message;
-    *realloced = false;
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+
+    /* We have a stored an incomplete chunk. Concat the received message to the end.
+     * After this block, connection->incompleteMessage is always empty. */
     if(connection->incompleteMessage.length > 0) {
-        /* concat the existing incomplete message with the new message */
-        UA_Byte *data = UA_realloc(connection->incompleteMessage.data,
-                                   connection->incompleteMessage.length + message->length);
+        size_t length = connection->incompleteMessage.length + message->length;
+        UA_Byte *data = UA_realloc(connection->incompleteMessage.data, length);
         if(!data) {
-            /* not enough memory */
-            UA_ByteString_deleteMembers(&connection->incompleteMessage);
-            connection->releaseRecvBuffer(connection, message);
-            return UA_STATUSCODE_BADOUTOFMEMORY;
+            retval = UA_STATUSCODE_BADOUTOFMEMORY;
+            goto cleanup;
         }
-        memcpy(&data[connection->incompleteMessage.length], message->data, message->length);
-        connection->incompleteMessage.data = data;
-        connection->incompleteMessage.length += message->length;
+        memcpy(&data[connection->incompleteMessage.length], message->data, length);
         connection->releaseRecvBuffer(connection, message);
-        current = &connection->incompleteMessage;
+        message->data = data;
+        message->length += length;
         *realloced = true;
+        connection->incompleteMessage = UA_BYTESTRING_NULL;
     }
 
-    /* the while loop sets offset to the first element after the last complete message. if a message
-       contains garbage, the buffer length is set to contain only the "good" messages before. */
-    size_t offset = 0;
-    size_t delete_at = current->length-1; // garbled message after this point
-    while(current->length - offset >= 16) {
-        UA_UInt32 msgtype = (UA_UInt32)current->data[offset] +
-            ((UA_UInt32)current->data[offset+1] << 8) +
-            ((UA_UInt32)current->data[offset+2] << 16);
+    /* Loop over the chunks in the received buffer */
+    size_t complete_until = 0; /* the received complete chunks end at this point */
+    UA_Boolean garbage_end = false; /* garbage after the last complete message */
+    while(message->length - complete_until >= 8) {
+        /* Check the message type */
+        UA_UInt32 msgtype = (UA_UInt32)message->data[complete_until] +
+            ((UA_UInt32)message->data[complete_until+1] << 8) +
+            ((UA_UInt32)message->data[complete_until+2] << 16);
         if(msgtype != ('M' + ('S' << 8) + ('G' << 16)) &&
            msgtype != ('O' + ('P' << 8) + ('N' << 16)) &&
            msgtype != ('H' + ('E' << 8) + ('L' << 16)) &&
            msgtype != ('A' + ('C' << 8) + ('K' << 16)) &&
            msgtype != ('C' + ('L' << 8) + ('O' << 16))) {
-            /* the message type is not recognized */
-            delete_at = offset; // throw the remaining message away
+            garbage_end = true; /* the message type is not recognized */
             break;
         }
-        UA_UInt32 length = 0;
-        size_t length_pos = offset + 4;
-        UA_StatusCode retval = UA_UInt32_decodeBinary(current, &length_pos, &length);
-        if(retval != UA_STATUSCODE_GOOD || length < 16 || length > connection->localConf.recvBufferSize) {
-            /* the message size is not allowed. throw the remaining bytestring away */
-            delete_at = offset;
+
+        /* Decode the length of the chunk */
+        UA_UInt32 chunk_length = 0;
+        size_t length_pos = complete_until + 4;
+        UA_StatusCode decode_retval = UA_UInt32_decodeBinary(message, &length_pos, &chunk_length);
+
+        /* The message size is not allowed. Throw the remaining bytestring away */
+        if(decode_retval != UA_STATUSCODE_GOOD || chunk_length < 16 || chunk_length > connection->localConf.recvBufferSize) {
+            garbage_end = true;
             break;
         }
-        if(length + offset > current->length)
-            break; /* the message is incomplete. keep the beginning */
-        offset += length;
-    }
 
-    /* throw the message away */
-    if(delete_at == 0) {
-        if(!*realloced) {
-            connection->releaseRecvBuffer(connection, message);
-            *realloced = true;
-        } else
-            UA_ByteString_deleteMembers(current);
-        return UA_STATUSCODE_GOOD;
+        /* The chunk is okay but incomplete. Store the end. */
+        if(chunk_length + complete_until > message->length)
+            break;
+
+        complete_until += chunk_length; /* Go to the next chunk */
     }
 
-    /* no complete message at all */
-    if(offset == 0) {
-        if(!*realloced) {
-            /* store the buffer in the connection */
-            UA_ByteString_copy(current, &connection->incompleteMessage);
-            connection->releaseRecvBuffer(connection, message);
-            *realloced = true;
+    /* Separate incomplete chunks */
+    if(complete_until != message->length) {
+        /* Garbage after the last good chunk. No need to keep a buffer */
+        if(garbage_end) {
+            if(complete_until == 0)
+                goto cleanup; /* All garbage, only happens on messages from the network layer */
+            message->length = complete_until;
+            return UA_STATUSCODE_GOOD;
         }
-        return UA_STATUSCODE_GOOD;
-    }
 
-    /* there remains an incomplete message at the end */
-    if(current->length != offset) {
-        UA_Byte *data = UA_malloc(current->length - offset);
-        if(!data) {
-            UA_ByteString_deleteMembers(&connection->incompleteMessage);
+        /* No good chunk, only an incomplete one */
+        if(complete_until == 0) {
             if(!*realloced) {
+                retval = UA_ByteString_allocBuffer(&connection->incompleteMessage, message->length);
+                if(retval != UA_STATUSCODE_GOOD)
+                    goto cleanup;
+                memcpy(connection->incompleteMessage.data, message->data, message->length);
                 connection->releaseRecvBuffer(connection, message);
                 *realloced = true;
+            } else {
+                connection->incompleteMessage = *message;
+                *message = UA_BYTESTRING_NULL;
             }
-            return UA_STATUSCODE_BADOUTOFMEMORY;
+            return UA_STATUSCODE_GOOD;
         }
-        size_t newlength = current->length - offset;
-        memcpy(data, &current->data[offset], newlength);
-        current->length = offset;
-        if(*realloced)
-            *message = *current;
-        connection->incompleteMessage.data = data;
-        connection->incompleteMessage.length = newlength;
-        return UA_STATUSCODE_GOOD;
-    }
 
-    if(current == &connection->incompleteMessage) {
-        *message = *current;
-        connection->incompleteMessage = UA_BYTESTRING_NULL;
+        /* At least one good chunk and an incomplete one */
+        size_t incomplete_length = message->length - complete_until;
+        retval = UA_ByteString_allocBuffer(&connection->incompleteMessage, incomplete_length);
+        if(retval != UA_STATUSCODE_GOOD)
+            goto cleanup;
+        memcpy(&connection->incompleteMessage.data, &message->data[complete_until], incomplete_length);
+        message->length = complete_until;
     }
+
     return UA_STATUSCODE_GOOD;
+
+ cleanup:
+    if(!*realloced)
+        connection->releaseRecvBuffer(connection, message);
+    UA_ByteString_deleteMembers(&connection->incompleteMessage);
+    return retval;
 }
 
 #if (__GNUC__ >= 4 && __GNUC_MINOR__ >= 6)

+ 2 - 0
tools/certs/create_self-signed.py

@@ -54,3 +54,5 @@ shutil.move("server_cert.der", sys.argv[1])
 if os.path.isfile(os.path.join(sys.argv[1], "ca.crt")):
 	os.remove(os.path.join(sys.argv[1], "ca.crt"))
 shutil.move("ca.crt", sys.argv[1])
+
+print("Certificates generated in " + sys.argv[1])

+ 1 - 1
tools/certs/localhost.cnf

@@ -229,7 +229,7 @@ DNS.1 = localhost
 DNS.2 = ${hostname}
 IP.1 = 127.0.0.1
 IP.2 = 0.0.0.0
-URI.1 = urn:unconfigured:open62541:open62541Server
+URI.1 = urn:unconfigured:application
 
 [ v3_ca ]
 

+ 112 - 121
tools/pyUANamespace/generate_open62541CCode.py

@@ -62,124 +62,115 @@ parser.add_argument('-s','--suppress',
 
 parser.add_argument('-v','--verbose', action='count', help='Make the script more verbose. Can be applied up to 4 times')
 
-
-
-if __name__ == '__main__':
-  args = parser.parse_args()
-
-  level = logging.CRITICAL
-  verbosity = 0
-  if args.verbose:
-    verbosity = int(args.verbose)
-  if (verbosity==1):
-    level = logging.ERROR
-  elif (verbosity==2):
-    level = logging.WARNING
-  elif (verbosity==3):
-    level = logging.INFO
-  elif (verbosity>=4):
-    level = logging.DEBUG
-  logging.basicConfig(level=level)
-  logger.setLevel(logging.INFO)
-
-  # Creating the header is tendious. We can skip the entire process if
-  # the header exists.
-  #if path.exists(argv[-1]+".c") or path.exists(argv[-1]+".h"):
-  #  log(None, "File " + str(argv[-1]) + " does already exists.", LOG_LEVEL_INFO)
-  #  log(None, "Header generation will be skipped. Delete the header and rerun this script if necessary.", LOG_LEVEL_INFO)
-  #  exit(0)
-
-  # Open the output file
-  outfileh = open(args.outputFile+".h", r"w+")
-  outfilec = open(args.outputFile+".c", r"w+")
-
-  # Create a new namespace
-  # Note that the name is actually completely symbolic, it has no other
-  # function but to distinguish this specific class.
-  # A namespace class acts as a container for nodes. The nodes may belong
-  # to any number of different OPC-UA namespaces.
-  ns = opcua_namespace("open62541")
-
-  # Clean up the XML files by removing duplicate namespaces and unwanted prefixes
-  preProc = open62541_XMLPreprocessor()
-  for xmlfile in args.infiles:
-    logger.info("Preprocessing " + str(xmlfile.name))
-    preProc.addDocument(xmlfile.name)
-  preProc.preprocessAll()
-
-  for xmlfile in preProc.getPreProcessedFiles():
-    logger.info("Parsing " + str(xmlfile))
-    ns.parseXML(xmlfile)
-
-  # We need to notify the open62541 server of the namespaces used to be able to use i.e. ns=3
-  namespaceArrayNames = preProc.getUsedNamespaceArrayNames()
-  for key in namespaceArrayNames:
-    ns.addNamespace(key, namespaceArrayNames[key])
-
-  # Remove any temp files - they are not needed after the AST is created
-  # Removed for debugging
-  preProc.removePreprocessedFiles()
-
-  # Remove blacklisted nodes from the namespace
-  # Doing this now ensures that unlinkable pointers will be cleanly removed
-  # during sanitation.
-  for blacklist in args.blacklistFiles:
-    for line in blacklist.readlines():
-      line = line.replace(" ","")
-      id = line.replace("\n","")
-      if ns.getNodeByIDString(id) == None:
-        logger.info("Can't blacklist node, namespace does currently not contain a node with id " + str(id))
-      else:
-        ns.removeNodeById(line)
-    blacklist.close()
-
-  # Link the references in the namespace
-  logger.info("Linking namespace nodes and references")
-  ns.linkOpenPointers()
-
-  # Remove nodes that are not printable or contain parsing errors, such as
-  # unresolvable or no references or invalid NodeIDs
-  ns.sanitize()
-
-  # Parse Datatypes in order to find out what the XML keyed values actually
-  # represent.
-  # Ex. <rpm>123</rpm> is not encodable
-  #     only after parsing the datatypes, it is known that
-  #     rpm is encoded as a double
-  logger.info("Building datatype encoding rules")
-  ns.buildEncodingRules()
-
-  # Allocate/Parse the data values. In order to do this, we must have run
-  # buidEncodingRules.
-  logger.info("Allocating variables")
-  ns.allocateVariables()
-
-  # Users may have manually defined some nodes in their code already (such as serverStatus).
-  # To prevent those nodes from being reprinted, we will simply mark them as already
-  # converted to C-Code. That way, they will still be reffered to by other nodes, but
-  # they will not be created themselves.
-  ignoreNodes = []
-  for ignore in args.ignoreFiles:
-    for line in ignore.readlines():
-      line = line.replace(" ","")
-      id = line.replace("\n","")
-      if ns.getNodeByIDString(id) == None:
-        logger.warn("Can't ignore node, Namespace does currently not contain a node with id " + str(id))
-      else:
-        ignoreNodes.append(ns.getNodeByIDString(id))
-    ignore.close()
-
-  # Create the C Code
-  logger.info("Generating Header")
-  # Returns a tuple of (["Header","lines"],["Code","lines","generated"])
-  from os.path import basename
-  generatedCode=ns.printOpen62541Header(ignoreNodes, args.suppressedAttributes, outfilename=basename(args.outputFile))
-  for line in generatedCode[0]:
-    outfileh.write(line+"\n")
-  for line in generatedCode[1]:
-    outfilec.write(line+"\n")
-
-  outfilec.close()
-  outfileh.close()
-
-  exit(0)
+args = parser.parse_args()
+
+level = logging.CRITICAL
+verbosity = 0
+if args.verbose:
+  verbosity = int(args.verbose)
+if (verbosity==1):
+  level = logging.ERROR
+elif (verbosity==2):
+  level = logging.WARNING
+elif (verbosity==3):
+  level = logging.INFO
+elif (verbosity>=4):
+  level = logging.DEBUG
+logging.basicConfig(level=level)
+logger.setLevel(logging.INFO)
+
+# Creating the header is tendious. We can skip the entire process if
+# the header exists.
+#if path.exists(argv[-1]+".c") or path.exists(argv[-1]+".h"):
+#  log(None, "File " + str(argv[-1]) + " does already exists.", LOG_LEVEL_INFO)
+#  log(None, "Header generation will be skipped. Delete the header and rerun this script if necessary.", LOG_LEVEL_INFO)
+#  exit(0)
+
+# Open the output file
+outfileh = open(args.outputFile+".h", r"w+")
+outfilec = open(args.outputFile+".c", r"w+")
+
+# Create a new namespace. Note that the namespace name is not significant.
+ns = opcua_namespace("open62541")
+
+# Clean up the XML files by removing duplicate namespaces and unwanted prefixes
+preProc = open62541_XMLPreprocessor()
+for xmlfile in args.infiles:
+  logger.info("Preprocessing " + str(xmlfile.name))
+  preProc.addDocument(xmlfile.name)
+preProc.preprocessAll()
+
+# Parse the XML files
+for xmlfile in preProc.getPreProcessedFiles():
+  logger.info("Parsing " + str(xmlfile))
+  ns.parseXML(xmlfile)
+
+# We need to notify the open62541 server of the namespaces used to be able to use i.e. ns=3
+namespaceArrayNames = preProc.getUsedNamespaceArrayNames()
+for key in namespaceArrayNames:
+  ns.addNamespace(key, namespaceArrayNames[key])
+
+# Remove any temp files - they are not needed after the AST is created
+preProc.removePreprocessedFiles()
+
+# Remove blacklisted nodes from the namespace
+# Doing this now ensures that unlinkable pointers will be cleanly removed
+# during sanitation.
+for blacklist in args.blacklistFiles:
+  for line in blacklist.readlines():
+    line = line.replace(" ","")
+    id = line.replace("\n","")
+    if ns.getNodeByIDString(id) == None:
+      logger.info("Can't blacklist node, namespace does currently not contain a node with id " + str(id))
+    else:
+      ns.removeNodeById(line)
+  blacklist.close()
+
+# Link the references in the namespace
+logger.info("Linking namespace nodes and references")
+ns.linkOpenPointers()
+
+# Remove nodes that are not printable or contain parsing errors, such as
+# unresolvable or no references or invalid NodeIDs
+ns.sanitize()
+
+# Parse Datatypes in order to find out what the XML keyed values actually
+# represent.
+# Ex. <rpm>123</rpm> is not encodable
+#     only after parsing the datatypes, it is known that
+#     rpm is encoded as a double
+logger.info("Building datatype encoding rules")
+ns.buildEncodingRules()
+
+# Allocate/Parse the data values. In order to do this, we must have run
+# buidEncodingRules.
+logger.info("Allocating variables")
+ns.allocateVariables()
+
+# Users may have manually defined some nodes in their code already (such as serverStatus).
+# To prevent those nodes from being reprinted, we will simply mark them as already
+# converted to C-Code. That way, they will still be reffered to by other nodes, but
+# they will not be created themselves.
+ignoreNodes = []
+for ignore in args.ignoreFiles:
+  for line in ignore.readlines():
+    line = line.replace(" ","")
+    id = line.replace("\n","")
+    if ns.getNodeByIDString(id) == None:
+      logger.warn("Can't ignore node, Namespace does currently not contain a node with id " + str(id))
+    else:
+      ignoreNodes.append(ns.getNodeByIDString(id))
+  ignore.close()
+
+# Create the C Code
+logger.info("Generating Header")
+# Returns a tuple of (["Header","lines"],["Code","lines","generated"])
+from os.path import basename
+generatedCode = ns.printOpen62541Header(ignoreNodes, args.suppressedAttributes, outfilename=basename(args.outputFile))
+for line in generatedCode[0]:
+  outfileh.write(line+"\n")
+for line in generatedCode[1]:
+  outfilec.write(line+"\n")
+
+outfilec.close()
+outfileh.close()

+ 18 - 17
tools/pyUANamespace/ua_node_types.py

@@ -608,22 +608,23 @@ class opcua_node_t:
 
     for x in thisxml.childNodes:
       if x.nodeType == x.ELEMENT_NODE:
-        if   x.tagName == "BrowseName":
-          self.browseName(unicode(x.firstChild.data))
-          xmlelement.removeChild(x)
-        elif x.tagName == "DisplayName":
-          self.displayName(unicode(x.firstChild.data))
-          xmlelement.removeChild(x)
-        elif x.tagName == "Description":
-          self.description(unicode(x.firstChild.data))
-          xmlelement.removeChild(x)
-        elif x.tagName == "WriteMask":
-          self.writeMask(int(unicode(x.firstChild.data)))
-          xmlelement.removeChild(x)
-        elif x.tagName == "UserWriteMask":
-          self.userWriteMask(int(unicode(x.firstChild.data)))
-          xmlelement.removeChild(x)
-        elif x.tagName == "References":
+        if x.firstChild:
+          if   x.tagName == "BrowseName":
+            self.browseName(unicode(x.firstChild.data))
+            xmlelement.removeChild(x)
+          elif x.tagName == "DisplayName":
+            self.displayName(unicode(x.firstChild.data))
+            xmlelement.removeChild(x)
+          elif x.tagName == "Description":
+            self.description(unicode(x.firstChild.data))
+            xmlelement.removeChild(x)
+          elif x.tagName == "WriteMask":
+            self.writeMask(int(unicode(x.firstChild.data)))
+            xmlelement.removeChild(x)
+          elif x.tagName == "UserWriteMask":
+            self.userWriteMask(int(unicode(x.firstChild.data)))
+            xmlelement.removeChild(x)
+        if x.tagName == "References":
           self.initiateDummyXMLReferences(x)
           xmlelement.removeChild(x)
     self.parseXMLSubType(xmlelement)
@@ -801,7 +802,7 @@ class opcua_node_referenceType_t(opcua_node_t):
 
     for x in xmlelement.childNodes:
       if x.nodeType == x.ELEMENT_NODE:
-        if x.tagName == "InverseName":
+        if x.tagName == "InverseName" and x.firstChild:
           self.inverseName(str(unicode(x.firstChild.data)))
         else:
           logger.warn( "Unprocessable XML Element: " + x.tagName)

+ 1 - 1
tools/travis/travis_linux_script.sh

@@ -43,7 +43,7 @@ else
     make doc
     make selfsigned
     cp -r doc ../../
-    cp server_cert.der ../../
+    cp ./examples/server_cert.der ../../
     cd .. && rm build -rf
 
     # cross compilation only with gcc