Browse Source

Merge branch '0.2'

Julius Pfrommer 7 years ago
parent
commit
430b510c12

+ 3 - 5
CMakeLists.txt

@@ -105,7 +105,6 @@ endif()
 # Build Targets
 # Build Targets
 option(UA_BUILD_EXAMPLES "Build example servers and clients" OFF)
 option(UA_BUILD_EXAMPLES "Build example servers and clients" OFF)
 option(UA_BUILD_UNIT_TESTS "Build the unit tests" 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)
 option(UA_BUILD_EXAMPLES_NODESET_COMPILER "Generate an OPC UA information model from a nodeset XML (experimental)" OFF)
 
 
 # Advanced Build Targets
 # Advanced Build Targets
@@ -435,6 +434,9 @@ endif()
 # Build Selected Targets #
 # Build Selected Targets #
 ##########################
 ##########################
 
 
+# always include, builds with make doc
+add_subdirectory(doc)
+
 if(UA_BUILD_EXAMPLES)
 if(UA_BUILD_EXAMPLES)
     add_subdirectory(examples)
     add_subdirectory(examples)
 endif()
 endif()
@@ -445,10 +447,6 @@ if(UA_BUILD_UNIT_TESTS)
     add_subdirectory(tests)
     add_subdirectory(tests)
 endif()
 endif()
 
 
-if(UA_BUILD_DOCUMENTATION)
-    add_subdirectory(doc)
-endif()
-
 if(UA_BUILD_EXAMPLES_NODESET_COMPILER)
 if(UA_BUILD_EXAMPLES_NODESET_COMPILER)
   add_custom_target(generate_informationmodel ALL
   add_custom_target(generate_informationmodel ALL
                     DEPENDS ${PROJECT_BINARY_DIR}/src_generated/nodeset.h ${PROJECT_BINARY_DIR}/src_generated/nodeset.c)
                     DEPENDS ${PROJECT_BINARY_DIR}/src_generated/nodeset.h ${PROJECT_BINARY_DIR}/src_generated/nodeset.c)

+ 27 - 18
doc/CMakeLists.txt

@@ -1,4 +1,4 @@
-find_package(Sphinx REQUIRED)
+find_package(Sphinx)
 find_package(LATEX)
 find_package(LATEX)
 
 
 set(DOC_LATEX_DIR ${PROJECT_BINARY_DIR}/doc_latex)
 set(DOC_LATEX_DIR ${PROJECT_BINARY_DIR}/doc_latex)
@@ -8,25 +8,15 @@ set(DOC_SRC_DIR   ${PROJECT_BINARY_DIR}/doc_src)
 make_directory(${DOC_SRC_DIR})
 make_directory(${DOC_SRC_DIR})
 file(GLOB DOC_SRC "${PROJECT_SOURCE_DIR}/doc/*")
 file(GLOB DOC_SRC "${PROJECT_SOURCE_DIR}/doc/*")
 list(REMOVE_ITEM DOC_SRC "${PROJECT_SOURCE_DIR}/doc/conf.py")
 list(REMOVE_ITEM DOC_SRC "${PROJECT_SOURCE_DIR}/doc/conf.py")
-list(REMOVE_ITEM DOC_SRC "${PROJECT_SOURCE_DIR}/doc/tutorial_server_variables.rst")
-list(REMOVE_ITEM DOC_SRC "${PROJECT_SOURCE_DIR}/doc/tutorial_server_method.rst")
 file(COPY ${DOC_SRC} DESTINATION ${DOC_SRC_DIR})
 file(COPY ${DOC_SRC} DESTINATION ${DOC_SRC_DIR})
 configure_file("${PROJECT_SOURCE_DIR}/doc/conf.py" "${DOC_SRC_DIR}/conf.py")
 configure_file("${PROJECT_SOURCE_DIR}/doc/conf.py" "${DOC_SRC_DIR}/conf.py")
-configure_file("${PROJECT_SOURCE_DIR}/doc/tutorial_server_variables.rst"
-               "${DOC_SRC_DIR}/tutorial_server_variables.rst")
-configure_file("${PROJECT_SOURCE_DIR}/doc/tutorial_server_method.rst"
-               "${DOC_SRC_DIR}/tutorial_server_method.rst")
 
 
-# Copy example code
-set(EXAMPLES ${PROJECT_SOURCE_DIR}/examples/server_variable.c
-             ${PROJECT_SOURCE_DIR}/examples/server_method.c)
-file(COPY ${EXAMPLES} DESTINATION ${DOC_SRC_DIR})
-               
 function(generate_rst in out)
 function(generate_rst in out)
   add_custom_command(OUTPUT ${out} DEPENDS ${PROJECT_SOURCE_DIR}/tools/c2rst.py ${in}
   add_custom_command(OUTPUT ${out} DEPENDS ${PROJECT_SOURCE_DIR}/tools/c2rst.py ${in}
                      PRE_BUILD COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/c2rst.py ${in} ${out})
                      PRE_BUILD COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/c2rst.py ${in} ${out})
 endfunction()
 endfunction()
 
 
+# Doc from headers
 generate_rst(${PROJECT_SOURCE_DIR}/include/ua_types.h ${DOC_SRC_DIR}/types.rst)
 generate_rst(${PROJECT_SOURCE_DIR}/include/ua_types.h ${DOC_SRC_DIR}/types.rst)
 generate_rst(${PROJECT_SOURCE_DIR}/include/ua_constants.h ${DOC_SRC_DIR}/constants.rst)
 generate_rst(${PROJECT_SOURCE_DIR}/include/ua_constants.h ${DOC_SRC_DIR}/constants.rst)
 generate_rst(${PROJECT_BINARY_DIR}/src_generated/ua_types_generated.h ${DOC_SRC_DIR}/types_generated.rst)
 generate_rst(${PROJECT_BINARY_DIR}/src_generated/ua_types_generated.h ${DOC_SRC_DIR}/types_generated.rst)
@@ -39,10 +29,15 @@ generate_rst(${PROJECT_SOURCE_DIR}/src/server/ua_services.h ${DOC_SRC_DIR}/servi
 generate_rst(${PROJECT_SOURCE_DIR}/src/server/ua_nodestore.h ${DOC_SRC_DIR}/nodestore.rst)
 generate_rst(${PROJECT_SOURCE_DIR}/src/server/ua_nodestore.h ${DOC_SRC_DIR}/nodestore.rst)
 generate_rst(${PROJECT_SOURCE_DIR}/src/server/ua_nodes.h ${DOC_SRC_DIR}/information_modelling.rst)
 generate_rst(${PROJECT_SOURCE_DIR}/src/server/ua_nodes.h ${DOC_SRC_DIR}/information_modelling.rst)
 
 
-generate_rst(${PROJECT_SOURCE_DIR}/examples/tutorials/client_firststeps.c
-             ${DOC_SRC_DIR}/tutorial_client_firststeps.rst)
-generate_rst(${PROJECT_SOURCE_DIR}/examples/tutorials/server_firststeps.c
-             ${DOC_SRC_DIR}/tutorial_server_firststeps.rst)
+# Doc from tutorial code
+generate_rst(${PROJECT_SOURCE_DIR}/examples/tutorial_datatypes.c ${DOC_SRC_DIR}/tutorial_datatypes.rst)
+generate_rst(${PROJECT_SOURCE_DIR}/examples/tutorial_server_firststeps.c ${DOC_SRC_DIR}/tutorial_server_firststeps.rst)
+generate_rst(${PROJECT_SOURCE_DIR}/examples/tutorial_server_variable.c ${DOC_SRC_DIR}/tutorial_server_variable.rst)
+generate_rst(${PROJECT_SOURCE_DIR}/examples/tutorial_server_variabletype.c ${DOC_SRC_DIR}/tutorial_server_variabletype.rst)
+generate_rst(${PROJECT_SOURCE_DIR}/examples/tutorial_server_datasource.c ${DOC_SRC_DIR}/tutorial_server_datasource.rst)
+generate_rst(${PROJECT_SOURCE_DIR}/examples/tutorial_server_object.c ${DOC_SRC_DIR}/tutorial_server_object.rst)
+generate_rst(${PROJECT_SOURCE_DIR}/examples/tutorial_server_method.c ${DOC_SRC_DIR}/tutorial_server_method.rst)
+generate_rst(${PROJECT_SOURCE_DIR}/examples/tutorial_client_firststeps.c ${DOC_SRC_DIR}/tutorial_client_firststeps.rst)
 
 
 add_custom_target(doc_latex ${SPHINX_EXECUTABLE}
 add_custom_target(doc_latex ${SPHINX_EXECUTABLE}
   -b latex "${DOC_SRC_DIR}" "${DOC_LATEX_DIR}"
   -b latex "${DOC_SRC_DIR}" "${DOC_LATEX_DIR}"
@@ -51,8 +46,14 @@ add_custom_target(doc_latex ${SPHINX_EXECUTABLE}
           ${DOC_SRC_DIR}/log.rst ${DOC_SRC_DIR}/connection.rst ${DOC_SRC_DIR}/services.rst
           ${DOC_SRC_DIR}/log.rst ${DOC_SRC_DIR}/connection.rst ${DOC_SRC_DIR}/services.rst
           ${DOC_SRC_DIR}/nodestore.rst ${DOC_SRC_DIR}/information_modelling.rst
           ${DOC_SRC_DIR}/nodestore.rst ${DOC_SRC_DIR}/information_modelling.rst
           ${DOC_SRC_DIR}/protocol.rst
           ${DOC_SRC_DIR}/protocol.rst
-          ${DOC_SRC_DIR}/tutorial_server_firststeps.rst
+          ${DOC_SRC_DIR}/tutorial_datatypes.rst
           ${DOC_SRC_DIR}/tutorial_client_firststeps.rst
           ${DOC_SRC_DIR}/tutorial_client_firststeps.rst
+          ${DOC_SRC_DIR}/tutorial_server_firststeps.rst
+          ${DOC_SRC_DIR}/tutorial_server_variable.rst
+          ${DOC_SRC_DIR}/tutorial_server_variabletype.rst
+          ${DOC_SRC_DIR}/tutorial_server_datasource.rst
+          ${DOC_SRC_DIR}/tutorial_server_object.rst
+          ${DOC_SRC_DIR}/tutorial_server_method.rst
   COMMENT "Building LaTeX sources for documentation with Sphinx")
   COMMENT "Building LaTeX sources for documentation with Sphinx")
 add_dependencies(doc_latex open62541)
 add_dependencies(doc_latex open62541)
 
 
@@ -71,7 +72,15 @@ add_custom_target(doc ${SPHINX_EXECUTABLE}
           ${DOC_SRC_DIR}/log.rst ${DOC_SRC_DIR}/connection.rst ${DOC_SRC_DIR}/services.rst
           ${DOC_SRC_DIR}/log.rst ${DOC_SRC_DIR}/connection.rst ${DOC_SRC_DIR}/services.rst
           ${DOC_SRC_DIR}/nodestore.rst ${DOC_SRC_DIR}/information_modelling.rst
           ${DOC_SRC_DIR}/nodestore.rst ${DOC_SRC_DIR}/information_modelling.rst
           ${DOC_SRC_DIR}/protocol.rst
           ${DOC_SRC_DIR}/protocol.rst
-          ${DOC_SRC_DIR}/tutorial_server_firststeps.rst
+          ${DOC_SRC_DIR}/tutorial_datatypes.rst
           ${DOC_SRC_DIR}/tutorial_client_firststeps.rst
           ${DOC_SRC_DIR}/tutorial_client_firststeps.rst
+          ${DOC_SRC_DIR}/tutorial_server_firststeps.rst
+          ${DOC_SRC_DIR}/tutorial_server_variable.rst
+          ${DOC_SRC_DIR}/tutorial_server_variabletype.rst
+          ${DOC_SRC_DIR}/tutorial_server_datasource.rst
+          ${DOC_SRC_DIR}/tutorial_server_object.rst
+          ${DOC_SRC_DIR}/tutorial_server_method.rst
   COMMENT "Building HTML documentation with Sphinx")
   COMMENT "Building HTML documentation with Sphinx")
 add_dependencies(doc open62541)
 add_dependencies(doc open62541)
+
+set_target_properties(doc doc_latex doc_pdf PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD TRUE)

+ 4 - 7
doc/building.rst

@@ -40,6 +40,10 @@ Building with CMake on Ubuntu or Debian
    ccmake ..
    ccmake ..
    make
    make
 
 
+   # build documentation
+   make doc # html documentation
+   make doc_pdf # pdf documentation (requires LaTeX)
+
 Building with CMake on Windows
 Building with CMake on Windows
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 
@@ -145,13 +149,6 @@ 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
 and open62541.dll.a resp. open62541.lib are build. Additional artifacts can be
 specified by the following options:
 specified by the following options:
 
 
-**UA_BUILD_DOCUMENTATION**
-  Generate Make targets for documentation
-
-   * HTML documentation: ``make doc``
-   * Latex Files: ``latex``
-   * PDF documentation: ``make pdf``
-
 **UA_BUILD_EXAMPLES**
 **UA_BUILD_EXAMPLES**
    Compile example servers and clients from :file:`examples/{xyz}.c`. A static and a dynamic binary is linked, respectively.
    Compile example servers and clients from :file:`examples/{xyz}.c`. A static and a dynamic binary is linked, respectively.
 
 

File diff suppressed because it is too large
+ 12 - 171
doc/tutorial_noderelations.rst


+ 1 - 0
doc/toc.rst

@@ -13,4 +13,5 @@ open62541 Documentation
    server
    server
    client
    client
    constants
    constants
+   namespace_compiler
    internal
    internal

+ 0 - 22
doc/tutorial_server_method.rst

@@ -1,22 +0,0 @@
-Adding a server-side method
----------------------------
-
-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).
-
-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).
-
-The second example shows that a method can also be applied on an array
-as input argument and output argument.
-
-The last example presents a way to bind a new method callback to an already
-instantiated method node.
-
-
-.. literalinclude:: server_method.c
-   :language: c
-   :linenos:
-   :lines: 4,5,14,16-

+ 0 - 74
doc/tutorial_server_variables.rst

@@ -1,74 +0,0 @@
-.. role:: ccode(code)
-      :language: c
-
-Adding variables to a server
-----------------------------
-
-This tutorial shows how to add variable nodes to a server and how these can be
-connected to a physical process in the background.
-
-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.
-
-.. literalinclude:: server_variable.c
-   :language: c
-   :linenos:
-   :lines: 4,13,15-
-
-Variants and Datatypes
-^^^^^^^^^^^^^^^^^^^^^^
-
-The datatype :ref:`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
-dimensionality of the data (e.g. a 2x3 matrix) in an additional integer array.
-
-The `UA_VariableAttributes` type contains a variant member `value`. The command
-:ccode:`UA_Variant_setScalar(&attr.value, &myInteger,
-&UA_TYPES[UA_TYPES_INT32])` sets the variant to point to the integer. Note that
-this does not make a copy of the integer (for which `UA_Variant_setScalarCopy`
-can be used). The variant (and its content) is then copied into the newly
-created node.
-
-The above code could have used allocations by making copies of all entries of
-the attribute variant. Then, of course, the variant content needs to be deleted
-to prevent memleaks.
-
-.. code-block:: c
-
-    UA_VariableAttributes attr;
-    UA_VariableAttributes_init(&attr);
-    UA_Int32 myInteger = 42;
-    UA_Variant_setScalarCopy(&attr.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
-    attr.description = UA_LOCALIZEDTEXT_ALLOC("en_US","the answer");
-    attr.displayName = UA_LOCALIZEDTEXT_ALLOC("en_US","the answer");
-
-    /* add the variable node here */
-    UA_VariableAttributes_deleteMembers(&attr); /* free the allocated memory */
-
-Finally, one needs to tell the server where to add the new variable in the
-information model. For that, we state the NodeId of the parent node and the
-(hierarchical) reference to the parent node.
-
-NodeIds
-^^^^^^^
-
-A :ref:`nodeid` is a unique identifier in server's context. It contains the
-number of node's namespace and either a numeric, string or GUID (Globally
-Unique ID) identifier. The following are some examples for their usage.
-
-.. code-block:: c
-
-   UA_NodeId id1 = UA_NODEID_NUMERIC(1, 1234);
-
-   UA_NodeId id2 = UA_NODEID_STRING(1, "testid"); /* points to the static string */
-
-   UA_NodeId id3 = UA_NODEID_STRING_ALLOC(1, "testid"); /* copy to memory */
-   UA_NodeId_deleteMembers(&id3); /* free the allocated string */
-
-
-Adding a Variable with an external Data Source
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-In order to couple a running process to a variable, a :ref:`datasource` is used.
-Consider ``examples/server_datasource.c`` in the repository for an example.

+ 5 - 2
doc/tutorials.rst

@@ -3,8 +3,11 @@ Tutorials
 
 
 .. toctree::
 .. toctree::
 
 
+   tutorial_datatypes.rst
    tutorial_server_firststeps.rst
    tutorial_server_firststeps.rst
-   tutorial_server_variables.rst
+   tutorial_server_variable.rst
+   tutorial_server_datasource.rst
+   tutorial_server_variabletype.rst
+   tutorial_server_object.rst
    tutorial_server_method.rst
    tutorial_server_method.rst
-   tutorial_noderelations.rst
    tutorial_client_firststeps.rst
    tutorial_client_firststeps.rst

+ 22 - 12
examples/CMakeLists.txt

@@ -34,32 +34,46 @@ macro(add_example EXAMPLE_NAME EXAMPLE_SOURCE)
   endif()
   endif()
 endmacro()
 endmacro()
 
 
+#############
+# Tutorials #
+#############
+
+add_example(tutorial_datatypes tutorial_datatypes.c)
+
+add_example(tutorial_server_firststeps tutorial_server_firststeps.c)
+
+add_example(tutorial_server_variable tutorial_server_variable.c)
+
+add_example(tutorial_server_datasource tutorial_server_datasource.c)
+
+add_example(tutorial_server_variabletype tutorial_server_variabletype.c)
+
+add_example(tutorial_server_object tutorial_server_object.c)
+
+if(UA_ENABLE_METHODCALLS)
+  add_example(tutorial_server_method tutorial_server_method.c)
+endif()
+
+add_example(tutorial_client_firststeps tutorial_client_firststeps.c)
+
 ##################
 ##################
 # Example Server #
 # Example Server #
 ##################
 ##################
 
 
 add_example(server server.c)
 add_example(server server.c)
 
 
-add_example(server_firststeps tutorials/server_firststeps.c)
-
 ##################
 ##################
 # Example Client #
 # Example Client #
 ##################
 ##################
 
 
 add_example(client client.c)
 add_example(client client.c)
 
 
-add_example(client_firststeps tutorials/client_firststeps.c)
-
 ####################
 ####################
 # Feature Examples #
 # Feature Examples #
 ####################
 ####################
 
 
-add_example(server_variable server_variable.c)
-
 add_example(server_mainloop server_mainloop.c)
 add_example(server_mainloop server_mainloop.c)
 
 
-add_example(server_datasource server_datasource.c)
-
 add_example(server_instantiation server_instantiation.c)
 add_example(server_instantiation server_instantiation.c)
 
 
 add_example(server_repeated_job server_repeated_job.c)
 add_example(server_repeated_job server_repeated_job.c)
@@ -74,10 +88,6 @@ if(UA_BUILD_EXAMPLES_NODESET_COMPILER)
   target_include_directories(server_nodeset PRIVATE ${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/deps) # needs an internal header
   target_include_directories(server_nodeset PRIVATE ${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/deps) # needs an internal header
 endif()
 endif()
 
 
-if(UA_ENABLE_METHODCALLS)
-  add_example(server_method server_method.c)
-endif()
-
 if(UA_BUILD_SELFSIGNED_CERTIFICATE)
 if(UA_BUILD_SELFSIGNED_CERTIFICATE)
   find_package(OpenSSL REQUIRED)
   find_package(OpenSSL REQUIRED)
   add_custom_command(OUTPUT server_cert.der ca.crt
   add_custom_command(OUTPUT server_cert.der ca.crt

+ 0 - 74
examples/server_datasource.c

@@ -1,74 +0,0 @@
-/* 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 <signal.h>
-#include <stdio.h>
-#include "open62541.h"
-
-UA_Boolean running = true;
-UA_Logger logger = UA_Log_Stdout;
-
-static void stopHandler(int sign) {
-    UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "received ctrl-c");
-    running = false;
-}
-
-static UA_StatusCode
-readInteger(void *handle, const UA_NodeId nodeid, UA_Boolean sourceTimeStamp,
-            const UA_NumericRange *range, UA_DataValue *dataValue) {
-    dataValue->hasValue = true;
-    UA_Variant_setScalarCopy(&dataValue->value, (UA_UInt32*)handle, &UA_TYPES[UA_TYPES_INT32]);
-    // we know the nodeid is a string
-    UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "Node read %.*s",
-                nodeid.identifier.string.length, nodeid.identifier.string.data);
-    UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "read value %i", *(UA_UInt32*)handle);
-    return UA_STATUSCODE_GOOD;
-}
-
-static UA_StatusCode
-writeInteger(void *handle, const UA_NodeId nodeid,
-             const UA_Variant *data, const UA_NumericRange *range) {
-    if(UA_Variant_isScalar(data) && data->type == &UA_TYPES[UA_TYPES_INT32] && data->data){
-        *(UA_UInt32*)handle = *(UA_UInt32*)data->data;
-    }
-    // we know the nodeid is a string
-    UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "Node written %.*s",
-                nodeid.identifier.string.length, nodeid.identifier.string.data);
-    UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "written value %i", *(UA_UInt32*)handle);
-    return UA_STATUSCODE_GOOD;
-}
-
-
-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;
-    UA_Server *server = UA_Server_new(config);
-
-    /* add a variable node to the address space */
-    UA_Int32 myInteger = 42;
-    UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
-    UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer");
-    UA_DataSource dataSource;
-    dataSource.handle = &myInteger;
-    dataSource.read = readInteger;
-    dataSource.write = NULL;
-    UA_VariableAttributes attr;
-    UA_VariableAttributes_init(&attr);
-    attr.description = UA_LOCALIZEDTEXT("en_US","the answer");
-    attr.displayName = UA_LOCALIZEDTEXT("en_US","the answer");
-
-    UA_Server_addDataSourceVariableNode(server, myIntegerNodeId,
-                                        UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
-                                        UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
-                                        myIntegerName, UA_NODEID_NULL, attr, dataSource, NULL);
-
-    UA_StatusCode retval = UA_Server_run(server, &running);
-    UA_Server_delete(server);
-    nl.deleteMembers(&nl);
-
-    return (int)retval;
-}

+ 0 - 179
examples/server_method.c

@@ -1,179 +0,0 @@
-/* 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 <signal.h>
-#include <stdlib.h>
-#include "open62541.h"
-
-/* Example 1 */
-static UA_StatusCode
-helloWorldMethod(void *handle, const UA_NodeId *objectId,
-                 const UA_NodeId *sessionId, void *sessionHandle,
-                 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 = (UA_Byte*)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(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Hello World was called");
-    return UA_STATUSCODE_GOOD;
-}
-
-/* Example 2 */
-static UA_StatusCode
-IncInt32ArrayValuesMethod(void *handle, const UA_NodeId *objectId,
-                          const UA_NodeId *sessionId, void *sessionHandle,
-                          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 */
-static UA_StatusCode
-fooBarMethod(void *handle, const UA_NodeId *objectId,
-             const UA_NodeId *sessionId, void *sessionHandle,
-             size_t inputSize, const UA_Variant *input,
-             size_t outputSize, UA_Variant *output) {
-    /* the same as helloWorld, but returns foobar */
-    UA_String *inputStr = (UA_String*)input->data;
-    UA_String tmp = UA_STRING_ALLOC("FooBar! ");
-    if(inputStr->length > 0) {
-        tmp.data = (UA_Byte*)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(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "FooBar was called");
-    return UA_STATUSCODE_GOOD;
-}
-
-UA_Boolean running = true;
-static void stopHandler(int sign) {
-    UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "received ctrl-c");
-    running = 0;
-}
-
-int main(int argc, char** argv) {
-    signal(SIGINT, stopHandler); /* catches ctrl-c */
-
-    /* initialize the server */
-    UA_ServerConfig config = UA_ServerConfig_standard;
-    UA_ServerNetworkLayer nl;
-    nl = UA_ServerNetworkLayerTCP(UA_ConnectionConfig_standard, 16664);
-    config.networkLayers = &nl;
-    config.networkLayersSize = 1;
-    UA_Server *server = UA_Server_new(config);
-
-    /* Example 1 */
-    /* add the method node with the callback */
-    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);
-    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, &inputArguments1, 1, &outputArguments1, NULL);
-
-    /* Example 2 */
-    /* add another method node: output argument as 1d Int32 array*/
-    UA_Argument inputArguments2;
-    UA_Argument_init(&inputArguments2);
-    inputArguments2.arrayDimensionsSize = 1;
-    UA_UInt32 * pInputDimensions = UA_UInt32_new();
-    pInputDimensions[0] = 5;
-    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");
-    inputArguments2.name = UA_STRING("int32 value");
-    inputArguments2.valueRank = 1;
-
-    UA_Argument outputArguments2;
-    UA_Argument_init(&outputArguments2);
-    outputArguments2.arrayDimensionsSize = 1;
-    UA_UInt32 * pOutputDimensions = UA_UInt32_new();
-    pOutputDimensions[0] = 5;
-    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);
-    incAttr.description = UA_LOCALIZEDTEXT("en_US", "1dArrayExample");
-    incAttr.displayName = UA_LOCALIZEDTEXT("en_US", "1dArrayExample");
-    incAttr.executable = true;
-    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_QUALIFIEDNAME(1, "IncInt32ArrayValues"),
-                            incAttr, &IncInt32ArrayValuesMethod, NULL,
-                            1, &inputArguments2, 1, &outputArguments2, 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
-     * UA_Server_setMethodNode_callback() */
-    UA_Server_setMethodNode_callback(server,  UA_NODEID_NUMERIC(1,62542),
-                                     &fooBarMethod, NULL);
-
-    /* start server */
-    UA_StatusCode retval = UA_Server_run(server, &running);
-
-    /* ctrl-c received -> clean up */
-    UA_UInt32_delete(pInputDimensions);
-    UA_UInt32_delete(pOutputDimensions);
-    UA_Server_delete(server);
-    nl.deleteMembers(&nl);
-
-    return (int)retval;
-}

+ 14 - 13
examples/server_repeated_job.c

@@ -4,23 +4,24 @@
 #include <signal.h>
 #include <signal.h>
 #include "open62541.h"
 #include "open62541.h"
 
 
-UA_Boolean running = true;
-UA_Logger logger = UA_Log_Stdout;
+static void
+testCallback(UA_Server *server, void *data) {
+    UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "testcallback");
+}
 
 
+UA_Boolean running = true;
 static void stopHandler(int sign) {
 static void stopHandler(int sign) {
-    UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "received ctrl-c");
+    UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "received ctrl-c");
     running = false;
     running = false;
 }
 }
 
 
-static void testCallback(UA_Server *server, void *data) {
-    UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "testcallback");
-}
-
-int main(int argc, char** argv) {
-    signal(SIGINT, stopHandler); /* catches ctrl-c */
+int main(void) {
+    signal(SIGINT, stopHandler);
+    signal(SIGTERM, stopHandler);
 
 
     UA_ServerConfig config = UA_ServerConfig_standard;
     UA_ServerConfig config = UA_ServerConfig_standard;
-    UA_ServerNetworkLayer nl = UA_ServerNetworkLayerTCP(UA_ConnectionConfig_standard, 16664);
+    UA_ServerNetworkLayer nl =
+        UA_ServerNetworkLayerTCP(UA_ConnectionConfig_standard, 16664);
     config.networkLayers = &nl;
     config.networkLayers = &nl;
     config.networkLayersSize = 1;
     config.networkLayersSize = 1;
     UA_Server *server = UA_Server_new(config);
     UA_Server *server = UA_Server_new(config);
@@ -30,10 +31,10 @@ int main(int argc, char** argv) {
     job.type = UA_JOBTYPE_METHODCALL;
     job.type = UA_JOBTYPE_METHODCALL;
     job.job.methodCall.data = NULL;
     job.job.methodCall.data = NULL;
     job.job.methodCall.method = testCallback;
     job.job.methodCall.method = testCallback;
-    UA_Server_addRepeatedJob(server, job, 2000, NULL); // call every 2 sec
+    UA_Server_addRepeatedJob(server, job, 2000, NULL); /* call every 2 sec */
 
 
-    UA_StatusCode retval = UA_Server_run(server, &running);
+    UA_Server_run(server, &running);
     UA_Server_delete(server);
     UA_Server_delete(server);
     nl.deleteMembers(&nl);
     nl.deleteMembers(&nl);
-    return (int)retval;
+    return 0;
 }
 }

+ 0 - 84
examples/server_variable.c

@@ -1,84 +0,0 @@
-/* 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 <signal.h>
-#include "open62541.h"
-
-UA_Boolean running = true;
-UA_Logger logger = UA_Log_Stdout;
-
-static void stopHandler(int sign) {
-    UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "received ctrl-c");
-    running = false;
-}
-
-static void onRead(void *handle, const UA_NodeId nodeid, const UA_Variant *data,
-                   const UA_NumericRange *range) {
-    UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "onRead; handle is: %i",
-                (uintptr_t)handle);
-}
-
-static void onWrite(void *h, const UA_NodeId nodeid, const UA_Variant *data,
-                    const UA_NumericRange *range) {
-    UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "onWrite; handle: %i",
-                (uintptr_t)h);
-}
-
-int main(int argc, char** argv) {
-    signal(SIGINT, stopHandler); /* catches ctrl-c */
-
-    UA_ServerConfig config = UA_ServerConfig_standard;
-    UA_ServerNetworkLayer nl;
-    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_Server_addVariableNode(server, myIntegerNodeId, parentNodeId,
-                              parentReferenceNodeId, myIntegerName,
-                              UA_NODEID_NULL, attr, NULL, NULL);
-
-    UA_ValueCallback callback = {(void*)7, onRead, onWrite};
-    UA_Server_setVariableNode_valueCallback(server, myIntegerNodeId, callback);
-
-    /* 3) Write another value */
-    myInteger = 43;
-    UA_Variant myVar;
-    UA_Variant_init(&myVar);
-    UA_Variant_setScalar(&myVar, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
-    UA_Server_writeValue(server, myIntegerNodeId, myVar);
-
-    /* 4) Set the status code of the value */
-    UA_WriteValue wv;
-    UA_WriteValue_init(&wv);
-    wv.nodeId = myIntegerNodeId;
-    wv.attributeId = UA_ATTRIBUTEID_VALUE;
-    wv.value.status = UA_STATUSCODE_BADNOTCONNECTED;
-    wv.value.hasStatus = true;
-    UA_Server_write(server, &wv);
-
-    /* 5) Reset to a good statuscode with a value */
-    wv.value.hasStatus = false;
-    wv.value.value = myVar;
-    wv.value.hasValue = true;
-    UA_Server_write(server, &wv);
-
-    UA_StatusCode retval = UA_Server_run(server, &running);
-    UA_Server_delete(server);
-    nl.deleteMembers(&nl);
-
-    return (int)retval;
-}

+ 1 - 1
examples/tutorials/client_firststeps.c

@@ -2,7 +2,7 @@
  * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
  * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
 
 
 /**
 /**
- * Building a simple client
+ * Building a Simple Client
  * ------------------------
  * ------------------------
  * You should already have a basic server from the previous tutorials. open62541
  * You should already have a basic server from the previous tutorials. open62541
  * provides both a server- and clientside API, so creating a client is as easy as
  * provides both a server- and clientside API, so creating a client is as easy as

+ 137 - 0
examples/tutorial_datatypes.c

@@ -0,0 +1,137 @@
+/* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
+ * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
+
+/**
+ * .. _types-tutorial:
+ *
+ * Working with Data Types
+ * -----------------------
+ *
+ * OPC UA defines a type system for values that can be encoded in the protocol
+ * messages. This tutorial shows some examples for available data types and
+ * their use. See the section on :ref:`types` for the full definitions.
+ *
+ * Basic Data Handling
+ * ^^^^^^^^^^^^^^^^^^^
+ * This section shows the basic interaction patterns for data types. Make
+ * sure to compare with the type definitions in ``ua_types.h``. */
+
+#include <assert.h>
+#include "open62541.h"
+
+static void
+variables_basic(void) {
+    /* Int32 */
+    UA_Int32 i = 5;
+    UA_Int32 j;
+    UA_Int32_copy(&i, &j);
+
+    UA_Int32 *ip = UA_Int32_new();
+    UA_Int32_copy(&i, ip);
+    UA_Int32_delete(ip);
+
+    /* String */
+    UA_String s;
+    UA_String_init(&s); /* _init zeroes out the entire memory of the datatype */
+    char *test = "test";
+    s.length = strlen(test);
+    s.data = (UA_Byte*)test;
+
+    UA_String s2;
+    UA_String_copy(&s, &s2);
+    UA_String_deleteMembers(&s2); /* Copying heap-allocated the dynamic content */
+
+    UA_String s3 = UA_STRING("test2");
+    UA_String s4 = UA_STRING_ALLOC("test2"); /* Copies the content to the heap */
+    UA_Boolean eq = UA_String_equal(&s3, &s4);
+    UA_String_deleteMembers(&s4);
+    if(!eq)
+        return;
+    
+    /* Structured Type */
+    UA_CallRequest cr;
+    UA_init(&cr, &UA_TYPES[UA_TYPES_CALLREQUEST]); /* Generic method */
+    UA_CallRequest_init(&cr); /* Shorthand for the previous line */
+
+    cr.requestHeader.timestamp = UA_DateTime_now(); /* Members of a structure */
+
+    cr.methodsToCall = UA_Array_new(5, &UA_TYPES[UA_TYPES_CALLMETHODREQUEST]);
+    cr.methodsToCallSize = 5; /* Array size needs to be made known */
+
+    UA_CallRequest *cr2 = UA_CallRequest_new();
+    UA_copy(&cr, cr2, &UA_TYPES[UA_TYPES_CALLREQUEST]);
+    UA_CallRequest_deleteMembers(&cr);
+    UA_CallRequest_delete(cr2);
+}
+
+/**
+ * NodeIds
+ * ^^^^^^^
+ * An OPC UA information model is made up of nodes and references between nodes.
+ * Every node has a unique :ref:`nodeid`. NodeIds refer to a namespace with an
+ * additional identifier value that can be an integer, a string, a guid or a
+ * bytestring. */
+
+static void
+variables_nodeids(void) {
+    UA_NodeId id1 = UA_NODEID_NUMERIC(1, 1234);
+    id1.namespaceIndex = 3;
+
+    UA_NodeId id2 = UA_NODEID_STRING(1, "testid"); /* the string is static */
+    UA_Boolean eq = UA_NodeId_equal(&id1, &id2);
+    if(eq)
+        return;
+
+    UA_NodeId id3;
+    UA_NodeId_copy(&id2, &id3);
+    UA_NodeId_deleteMembers(&id3);
+
+    UA_NodeId id4 = UA_NODEID_STRING_ALLOC(1, "testid"); /* the string is copied
+                                                            to the heap */
+    UA_NodeId_deleteMembers(&id4);
+}
+
+/**
+ * Variants
+ * ^^^^^^^^
+ * The datatype :ref:`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 dimensionality of the data (e.g. a 2x3 matrix) in an additional
+ * integer array. */
+
+static void
+variables_variants(void) {
+    /* Set a scalar value */
+    UA_Variant v;
+    UA_Int32 i = 42;
+    UA_Variant_setScalar(&v, &i, &UA_TYPES[UA_TYPES_INT32]);
+
+    /* Make a copy */
+    UA_Variant v2;
+    UA_Variant_copy(&v, &v2);
+    UA_Variant_deleteMembers(&v2);
+
+    /* Set an array value */
+    UA_Variant v3;
+    UA_Double d[9] = {1.0, 2.0, 3.0,
+                      4.0, 5.0, 6.0,
+                      7.0, 8.0, 9.0};
+    UA_Variant_setArrayCopy(&v3, d, 9, &UA_TYPES[UA_TYPES_DOUBLE]);
+
+    /* Set array dimensions */
+    v3.arrayDimensions = UA_Array_new(2, &UA_TYPES[UA_TYPES_UINT32]);
+    v3.arrayDimensionsSize = 2;
+    v3.arrayDimensions[0] = 3;
+    v3.arrayDimensions[1] = 3;
+    UA_Variant_deleteMembers(&v3);
+}
+
+/** It follows the main function, making use of the above definitions. */
+
+int main(void) {
+    variables_basic();
+    variables_nodeids();
+    variables_variants();
+    return 0;
+}

+ 190 - 0
examples/tutorial_server_datasource.c

@@ -0,0 +1,190 @@
+/* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
+ * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
+
+/**
+ * Connecting a Variable with a Physical Process
+ * ---------------------------------------------
+ *
+ * In OPC UA-based architectures, servers are typically situated near the source
+ * of information. In an industrial context, this translates into servers being
+ * near the physical process and clients consuming the data at runtime. In the
+ * previous tutorial, we saw how to add variables to an OPC UA information
+ * model. This tutorial shows how to connect a variable to runtime information,
+ * for example from measurements of a physical process. For simplicty, we take
+ * the system clock as the underlying "process".
+ *
+ * The following code snippets are each concerned with a different way of
+ * updating variable values at runtime. Taken together, the code snippets define
+ * a compilable source file.
+ *
+ * Updating variables manually
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ * As a starting point, assume that a variable for a value of type
+ * :ref:`datetime` has been created in the server with the identifier
+ * "ns=1,s=current-time". Assuming that our applications gets triggered when a
+ * new value arrives from the underlying process, we can just write into the
+ * variable. */
+
+#include <signal.h>
+#include "open62541.h"
+
+static void
+updateCurrentTime(UA_Server *server) {
+    UA_DateTime now = UA_DateTime_now();
+    UA_Variant value;
+    UA_Variant_setScalar(&value, &now, &UA_TYPES[UA_TYPES_DATETIME]);
+    UA_NodeId currentNodeId = UA_NODEID_STRING(1, "current-time");
+    UA_Server_writeValue(server, currentNodeId, value);
+}
+
+static void
+addCurrentTimeVariable(UA_Server *server) {
+    UA_DateTime now = 0;
+    UA_VariableAttributes attr;
+    UA_VariableAttributes_init(&attr);
+    attr.displayName = UA_LOCALIZEDTEXT("en_US", "Current time");
+    UA_Variant_setScalar(&attr.value, &now, &UA_TYPES[UA_TYPES_DATETIME]);
+
+    UA_NodeId currentNodeId = UA_NODEID_STRING(1, "current-time");
+    UA_QualifiedName currentName = UA_QUALIFIEDNAME(1, "current-time");
+    UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
+    UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
+    UA_NodeId variableTypeNodeId = UA_NODEID_NULL;
+    UA_Server_addVariableNode(server, currentNodeId, parentNodeId,
+                              parentReferenceNodeId, currentName,
+                              variableTypeNodeId, attr, NULL, NULL);
+
+    updateCurrentTime(server);
+}
+
+/**
+ * Variable Value Callback
+ * ^^^^^^^^^^^^^^^^^^^^^^^
+ *
+ * When a value changes continuously, such as the system time, updating the
+ * value in a tight loop would take up a lot of resources. Value callbacks allow
+ * to synchronize a variable value with an external representation. They attach
+ * callbacks to the variable that are executed before every read and after every
+ * write operation. */
+
+static void
+beforeReadTime(void *handle, const UA_NodeId nodeid, const UA_Variant *data,
+               const UA_NumericRange *range) {
+    UA_Server *server = (UA_Server*)handle;
+    UA_DateTime now = UA_DateTime_now();
+    UA_Variant value;
+    UA_Variant_setScalar(&value, &now, &UA_TYPES[UA_TYPES_DATETIME]);
+    UA_NodeId currentNodeId = UA_NODEID_STRING(1, "current-time");
+    UA_Server_writeValue(server, currentNodeId, value);
+}
+
+static void
+afterWriteTime(void *handle, const UA_NodeId nodeid, const UA_Variant *data,
+               const UA_NumericRange *range) {
+    UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
+                "The variable was updated");
+}
+
+static void
+addValueCallbackToCurrentTimeVariable(UA_Server *server) {
+    UA_NodeId currentNodeId = UA_NODEID_STRING(1, "current-time");
+    UA_ValueCallback callback ;
+    callback.handle = server;
+    callback.onRead = beforeReadTime;
+    callback.onWrite = afterWriteTime;
+    UA_Server_setVariableNode_valueCallback(server, currentNodeId, callback);
+}
+
+/**
+ * Variable Data Sources
+ * ^^^^^^^^^^^^^^^^^^^^^
+ *
+ * With value callbacks, the value is still stored in the variable node.
+ * So-called data sources go one step further. The server redirects every read
+ * and write request to a callback function. Upon reading, the callback provides
+ * copy of the current value. Internally, the data source needs to implement its
+ * own memory management. */
+
+static UA_StatusCode
+readCurrentTime(void *handle, const UA_NodeId nodeid, UA_Boolean sourceTimeStamp,
+                const UA_NumericRange *range, UA_DataValue *dataValue) {
+    UA_DateTime now = UA_DateTime_now();
+    UA_Variant_setScalarCopy(&dataValue->value, &now,
+                             &UA_TYPES[UA_TYPES_DATETIME]);
+    dataValue->hasValue = true;
+    return UA_STATUSCODE_GOOD;
+}
+
+static UA_StatusCode
+writeCurrentTime(void *handle, const UA_NodeId nodeid, const UA_Variant *data,
+                 const UA_NumericRange *range) {
+    UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
+                "Changing the system time is not implemented");
+    return UA_STATUSCODE_BADINTERNALERROR;
+}
+
+static void
+addCurrentTimeDataSourceVariable(UA_Server *server) {
+    UA_VariableAttributes attr;
+    UA_VariableAttributes_init(&attr);
+    attr.displayName = UA_LOCALIZEDTEXT("en_US", "Current time - data source");
+
+    UA_NodeId currentNodeId = UA_NODEID_STRING(1, "current-time-datasource");
+    UA_QualifiedName currentName = UA_QUALIFIEDNAME(1, "current-time-datasource");
+    UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
+    UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
+    UA_NodeId variableTypeNodeId = UA_NODEID_NULL;
+
+    UA_DataSource timeDataSource;
+    timeDataSource.handle = NULL;
+    timeDataSource.read = readCurrentTime;
+    timeDataSource.write = writeCurrentTime;
+    UA_Server_addDataSourceVariableNode(server, currentNodeId, parentNodeId,
+                                        parentReferenceNodeId, currentName,
+                                        variableTypeNodeId, attr,
+                                        timeDataSource, NULL);
+}
+
+/** It follows the main server code, making use of the above definitions. */
+
+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(void) {
+    signal(SIGINT, stopHandler);
+    signal(SIGTERM, stopHandler);
+
+    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);
+
+    addCurrentTimeVariable(server);
+    addValueCallbackToCurrentTimeVariable(server);
+    addCurrentTimeDataSourceVariable(server);
+
+    UA_Server_run(server, &running);
+    UA_Server_delete(server);
+    nl.deleteMembers(&nl);
+    return 0;
+}
+
+/**
+ * DataChange Notifications
+ * ^^^^^^^^^^^^^^^^^^^^^^^^
+ * A client that is interested in the current value of a variable does not need
+ * to regularly poll the variable. Instead, he can use the Subscription
+ * mechanism to be notified about changes.
+ *
+ * Within a Subscription, the client adds so-called MonitoredItems. A DataChange
+ * MonitoredItem defines a node attribute (usually the value attribute) that is
+ * monitored for changes. The server internally reads the value in the defined
+ * interval and generates the appropriate notifications. The three ways of
+ * updating node values discussed above are all usable in combination with
+ * notifications. That is because notifications use the standard *Read* service
+ * to look for value changes. */

+ 111 - 0
examples/tutorial_server_firststeps.c

@@ -0,0 +1,111 @@
+/* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
+ * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
+
+/**
+ * Building a Simple Server
+ * ------------------------
+ *
+ * This series of tutorial guide you through your first steps with open62541.
+ * For compiling the examples, you need a compiler (MS Visual Studio 2015 or
+ * newer, GCC, Clang and MinGW32 are all known to be working). The compilation
+ * instructions are given for GCC but should be straightforward to adapt.
+ *
+ * It will also be very helpful to install an OPC UA Client with a graphical
+ * frontend, such as UAExpert by Unified Automation. That will enable you to
+ * examine the information model of any OPC UA server.
+ *
+ * To get started, downdload the open62541 single-file release from
+ * http://open62541.org or generate it according to the :ref:`build instructions
+ * <building>` with the "amalgamation" option enabled. From now on, we assume
+ * you have the ``open62541.c/.h`` files in the current folder. Now create a new
+ * C source-file called ``myServer.c`` with the following content: */
+
+#include <signal.h>
+#include "open62541.h"
+
+UA_Boolean running = true;
+static void stopHandler(int sig) {
+    UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "received ctrl-c");
+    running = false;
+}
+
+int main(void) {
+    signal(SIGINT, stopHandler);
+    signal(SIGTERM, stopHandler);
+
+    UA_ServerConfig config = UA_ServerConfig_standard;
+    UA_ServerNetworkLayer nl =
+        UA_ServerNetworkLayerTCP(UA_ConnectionConfig_standard, 4840);
+    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;
+}
+
+/**
+ * This is all that is needed for a simple OPC UA server. With the GCC compiler,
+ * the following command produces an executable:
+ *
+ * .. code-block:: bash
+ *
+ *    $ gcc -std=c99 open62541.c myServer.c -o myServer
+ *
+ * Now start the server (stop with ctrl-c):
+ *
+ * .. code-block:: bash
+ *
+ *    $ ./myServer
+ *
+ * You have now compiled and run your first OPC UA server. You can go ahead and
+ * browse the information model with client. The server is listening on
+ * ``opc.tcp://localhost:4840``. In the next two sections, we will continue to
+ * explain the different parts of the code in detail.
+ *
+ * Server Configuration and Plugins
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ *
+ * *open62541* provides a flexible framework for building OPC UA servers and
+ * clients. The goals is to have a core library that accomodates for all use
+ * cases and runs on all platforms. Users can then adjust the library to fit
+ * their use case via configuration and by developing (platform-specific)
+ * plugins. The core library is based on C99 only and does not even require
+ * basic POSIX support. For example, the lowlevel networking code is implemented
+ * as an exchangeable plugin. But don't worry. *open62541* provides plugin
+ * implementations for most platforms and sensible default configurations
+ * out-of-the-box.
+ * 
+ * In the above server code, we simply take the default server configuration and
+ * add a single TCP network layer that is listerning on port 4840.
+ *
+ * Server Lifecycle
+ * ^^^^^^^^^^^^^^^^
+ * The code in this example shows the three parts for server lifecycle
+ * management: Creating a server, running the server, and deleting the server.
+ * Creating and deleting a server is trivial once the configuration is set up.
+ * The server is started with ``UA_Server_run``. Internally, the server then
+ * uses timeouts to schedule regular tasks. Between the timeouts, the server
+ * listens on the network layer for incoming messages.
+ *
+ * You might ask how the server knows when to stop running. For this, we have
+ * created a global variable ``running``. Furthermore, we have registered the
+ * method ``stopHandler`` that catches the signal (interrupt) the program
+ * receives when the operating systems tries to close it. This happens for
+ * example when you press ctrl-c in a terminal program. The signal handler then
+ * sets the variable ``running`` to false and the server shuts down once it
+ * takes back control. [#f1]_
+ *
+ * In order to integrated OPC UA in a single-threaded application with its own
+ * mainloop (for example provided by a GUI toolkit), one can alternatively drive
+ * the server manually. See the section of the server documentation on
+ * :ref:`server-lifecycle` for details.
+ *
+ * The server configuration and lifecycle management is needed for all servers.
+ * We will use it in the following tutorials without further comment.
+ *
+ * .. [#f1] Be careful with global variables in multi-threaded applications. You
+ *          might want to allocate the ``running`` variable on the heap. */

+ 183 - 0
examples/tutorial_server_method.c

@@ -0,0 +1,183 @@
+/* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
+ * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
+
+/**
+ * Adding Methods to Objects
+ * -------------------------
+ *
+ * An object in an OPC UA information model may contain methods similar to
+ * objects in a programming language. Methods are represented by a MethodNode.
+ * Note that several objects may reference the same MethodNode. When an object
+ * type is instantiated, a reference to the method is added instead of copying
+ * the MethodNode. Therefore, the identifier of the context object is always
+ * explicitly stated when a method is called.
+ *
+ * The method callback takes as input a custom data pointer attached to the
+ * method node, the identifier of the object from which the method is called,
+ * and two arrays for the input and output arguments. The input and output
+ * arguments are all of type :ref:`variant`. Each variant may in turn contain a
+ * (multi-dimensional) array or scalar of any data type.
+ *
+ * Constraints for the method arguments are defined in terms of data type, value
+ * rank and array dimension (similar to variable definitions). The argument
+ * definitions are stored in child VariableNodes of the MethodNode with the
+ * respective BrowseNames ``(0, "InputArguments")`` and ``(0,
+ * "OutputArguments")``.
+ *
+ * Example: Hello World Method
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ * The method takes a string scalar and returns a string scalar with "Hello "
+ * prepended. The type and length of the input arguments is checked internally
+ * by the SDK, so that we don't have to verify the arguments in the callback. */
+
+#include <signal.h>
+#include "open62541.h"
+
+static UA_StatusCode
+helloWorldMethodCallback(void *handle, const UA_NodeId *objectId,
+                         const UA_NodeId *sessionId, void *sessionHandle,
+                         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(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Hello World was called");
+    return UA_STATUSCODE_GOOD;
+}
+
+static void
+addHellWorldMethod(UA_Server *server) {
+    UA_Argument inputArgument;
+    UA_Argument_init(&inputArgument);
+    inputArgument.description = UA_LOCALIZEDTEXT("en_US", "A String");
+    inputArgument.name = UA_STRING("MyInput");
+    inputArgument.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
+    inputArgument.valueRank = -1; /* scalar */
+
+    UA_Argument outputArgument;
+    UA_Argument_init(&outputArgument);
+    outputArgument.description = UA_LOCALIZEDTEXT("en_US", "A String");
+    outputArgument.name = UA_STRING("MyOutput");
+    outputArgument.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
+    outputArgument.valueRank = -1; /* scalar */
+
+    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, &helloWorldMethodCallback, NULL,
+                            1, &inputArgument, 1, &outputArgument, NULL);
+}
+
+/**
+ * Increase Array Values Method
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ * The method takes an array of 5 integers and a scalar as input. It returns a
+ * copy of the array with every entry increased by the scalar. */
+
+static UA_StatusCode
+IncInt32ArrayMethodCallback(void *handle, const UA_NodeId *objectId,
+                            const UA_NodeId *sessionId, void *sessionHandle,
+                            size_t inputSize, const UA_Variant *input,
+                            size_t outputSize, UA_Variant *output) {
+    UA_Int32 *inputArray = (UA_Int32*)input[0].data;
+    UA_Int32 delta = *(UA_Int32*)input[1].data;
+
+    /* Copy the input array */
+    UA_StatusCode retval = UA_Variant_setArrayCopy(output, inputArray, 5,
+                                                   &UA_TYPES[UA_TYPES_INT32]);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
+
+    /* Increate the elements */
+    UA_Int32 *outputArray = (UA_Int32*)output->data;
+    for(size_t i = 0; i < input->arrayLength; i++)
+        outputArray[i] = inputArray[i] + delta;
+
+    return UA_STATUSCODE_GOOD;
+}
+
+static void
+addIncInt32ArrayMethod(UA_Server *server) {
+    /* Two input arguments */
+    UA_Argument inputArguments[2];
+    UA_Argument_init(&inputArguments[0]);
+    inputArguments[0].description = UA_LOCALIZEDTEXT("en_US", "int32[5] array");
+    inputArguments[0].name = UA_STRING("int32 array");
+    inputArguments[0].dataType = UA_TYPES[UA_TYPES_INT32].typeId;
+    inputArguments[0].valueRank = 1;
+    UA_UInt32 pInputDimension = 5;
+    inputArguments[0].arrayDimensionsSize = 1;
+    inputArguments[0].arrayDimensions = &pInputDimension;
+
+    UA_Argument_init(&inputArguments[1]);
+    inputArguments[1].description = UA_LOCALIZEDTEXT("en_US", "int32 delta");
+    inputArguments[1].name = UA_STRING("int32 delta");
+    inputArguments[1].dataType = UA_TYPES[UA_TYPES_INT32].typeId;
+    inputArguments[1].valueRank = -1; /* scalar */
+
+    /* One output argument */
+    UA_Argument outputArgument;
+    UA_Argument_init(&outputArgument);
+    outputArgument.description = UA_LOCALIZEDTEXT("en_US", "int32[5] array");
+    outputArgument.name = UA_STRING("each entry is incremented by the delta");
+    outputArgument.dataType = UA_TYPES[UA_TYPES_INT32].typeId;
+    outputArgument.valueRank = 1;
+    UA_UInt32 pOutputDimension = 5;
+    outputArgument.arrayDimensionsSize = 1;
+    outputArgument.arrayDimensions = &pOutputDimension;
+
+    /* Add the method node */
+    UA_MethodAttributes incAttr;
+    UA_MethodAttributes_init(&incAttr);
+    incAttr.description = UA_LOCALIZEDTEXT("en_US", "IncInt32ArrayValues");
+    incAttr.displayName = UA_LOCALIZEDTEXT("en_US", "IncInt32ArrayValues");
+    incAttr.executable = true;
+    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_QUALIFIEDNAME(1, "IncInt32ArrayValues"),
+                            incAttr, &IncInt32ArrayMethodCallback, NULL,
+                            2, inputArguments, 1, &outputArgument, NULL);
+}
+
+/** It follows the main server code, making use of the above definitions. */
+
+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(void) {
+    signal(SIGINT, stopHandler);
+    signal(SIGTERM, stopHandler);
+
+    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);
+
+    addHellWorldMethod(server);
+    addIncInt32ArrayMethod(server);
+
+    UA_Server_run(server, &running);
+    UA_Server_delete(server);
+    nl.deleteMembers(&nl);
+    return 0;
+}

+ 336 - 0
examples/tutorial_server_object.c

@@ -0,0 +1,336 @@
+/* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
+ * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
+
+/**
+ * Working with Objects and Object Types
+ * -------------------------------------
+ *
+ * Using objects to structure information models
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ * Assume a situation where we want to model a set of pumps and their runtime
+ * state in an OPC UA information model. Of course, all pump representations
+ * should follow the same basic structure, For example, we might have graphical
+ * representation of pumps in a SCADA visualisation that shall be resuable for
+ * all pumps.
+ *
+ * Following the object-oriented programming paradigm, every pump is represented
+ * by an object with the following layout:
+ *
+ * .. graphviz::
+ *
+ *    digraph tree {
+ *
+ *    fixedsize=true;
+ *    node [width=2, height=0, shape=box, fillcolor="#E5E5E5", concentrate=true]
+ *
+ *    node_root [label=< <I>ObjectNode</I><BR/>Pump >]
+ *
+ *    { rank=same
+ *      point_1 [shape=point]
+ *      node_1 [label=< <I>VariableNode</I><BR/>ManufacturerName >] }
+ *    node_root -> point_1 [arrowhead=none]
+ *    point_1 -> node_1 [label="hasComponent"]
+ *
+ *    { rank=same
+ *      point_2 [shape=point]
+ *      node_2 [label=< <I>VariableNode</I><BR/>ModelName >] }
+ *    point_1 -> point_2 [arrowhead=none]
+ *    point_2 -> node_2 [label="hasComponent"]
+ *
+ *    {  rank=same
+ *       point_4 [shape=point]
+ *       node_4 [label=< <I>VariableNode</I><BR/>Status >] }
+ *    point_2 -> point_4 [arrowhead=none]
+ *    point_4 -> node_4 [label="hasComponent"]
+ *
+ *    {  rank=same
+ *       point_5 [shape=point]
+ *       node_5 [label=< <I>VariableNode</I><BR/>MotorRPM >] }
+ *    point_4 -> point_5 [arrowhead=none]
+ *    point_5 -> node_5 [label="hasComponent"]
+ *
+ *    }
+ *
+ * The following code manually defines a pump and its member variables. We omit
+ * setting constraints on the variable values as this is not the focus of this
+ * tutorial and was already covered. */
+
+#include <signal.h>
+#include "open62541.h"
+
+static void
+manuallyDefinePump(UA_Server *server) {
+    UA_NodeId pumpId; /* get the nodeid assigned by the server */
+    UA_ObjectAttributes oAttr;
+    UA_ObjectAttributes_init(&oAttr);
+    oAttr.displayName = UA_LOCALIZEDTEXT("en_US", "Pump (Manual)");
+    UA_Server_addObjectNode(server, UA_NODEID_NULL,
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
+                            UA_QUALIFIEDNAME(1, "Pump (Manual)"), UA_NODEID_NULL,
+                            oAttr, NULL, &pumpId);
+
+    UA_VariableAttributes mnAttr;
+    UA_VariableAttributes_init(&mnAttr);
+    UA_String manufacturerName = UA_STRING("Pump King Ltd.");
+    UA_Variant_setScalar(&mnAttr.value, &manufacturerName, &UA_TYPES[UA_TYPES_STRING]);
+    mnAttr.displayName = UA_LOCALIZEDTEXT("en_US", "ManufacturerName");
+    UA_Server_addVariableNode(server, UA_NODEID_NULL, pumpId,
+                              UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
+                              UA_QUALIFIEDNAME(1, "ManufacturerName"),
+                              UA_NODEID_NULL, mnAttr, NULL, NULL);
+
+    UA_VariableAttributes modelAttr;
+    UA_VariableAttributes_init(&modelAttr);
+    UA_String modelName = UA_STRING("Mega Pump 3000");
+    UA_Variant_setScalar(&modelAttr.value, &modelName, &UA_TYPES[UA_TYPES_STRING]);
+    modelAttr.displayName = UA_LOCALIZEDTEXT("en_US", "ModelName");
+    UA_Server_addVariableNode(server, UA_NODEID_NULL, pumpId,
+                              UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
+                              UA_QUALIFIEDNAME(1, "ModelName"),
+                              UA_NODEID_NULL, modelAttr, NULL, NULL);
+
+    UA_VariableAttributes statusAttr;
+    UA_VariableAttributes_init(&statusAttr);
+    UA_Boolean status = true;
+    UA_Variant_setScalar(&statusAttr.value, &status, &UA_TYPES[UA_TYPES_BOOLEAN]);
+    statusAttr.displayName = UA_LOCALIZEDTEXT("en_US", "Status");
+    UA_Server_addVariableNode(server, UA_NODEID_NULL, pumpId,
+                              UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
+                              UA_QUALIFIEDNAME(1, "Status"),
+                              UA_NODEID_NULL, statusAttr, NULL, NULL);
+
+    UA_VariableAttributes rpmAttr;
+    UA_VariableAttributes_init(&rpmAttr);
+    UA_Double rpm = 50.0;
+    UA_Variant_setScalar(&rpmAttr.value, &rpm, &UA_TYPES[UA_TYPES_DOUBLE]);
+    rpmAttr.displayName = UA_LOCALIZEDTEXT("en_US", "MotorRPM");
+    UA_Server_addVariableNode(server, UA_NODEID_NULL, pumpId,
+                              UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
+                              UA_QUALIFIEDNAME(1, "MotorRPMs"),
+                              UA_NODEID_NULL, rpmAttr, NULL, NULL);
+}
+
+/**
+ * Object types, type hierarchies and instantiation
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ * Building up each object manually requires us to write a lot of code.
+ * Furthermore, there is no way for clients to detect that an object represents
+ * a pump. (We might use naming conventions or similar to detect pumps. But
+ * that's not exactly a clean solution.) Furthermore, we might have more devices
+ * than just pumps. And we require all devices to share some common structure.
+ * The solution is to define ObjectTypes in a hierarchy with inheritance
+ * relations.
+ *
+ * .. graphviz::
+ *
+ *    digraph tree {
+ *
+ *    fixedsize=true;
+ *    node [width=2, height=0, shape=box, fillcolor="#E5E5E5", concentrate=true]
+ *
+ *    node_root [label=< <I>ObjectTypeNode</I><BR/>Device >]
+ *
+ *    { rank=same
+ *      point_1 [shape=point]
+ *      node_1 [label=< <I>VariableNode</I><BR/>ManufacturerName >] }
+ *    node_root -> point_1 [arrowhead=none]
+ *    point_1 -> node_1 [label="hasComponent"]
+ *
+ *    { rank=same
+ *      point_2 [shape=point]
+ *      node_2 [label=< <I>VariableNode</I><BR/>ModelName >] }
+ *    point_1 -> point_2 [arrowhead=none]
+ *    point_2 -> node_2 [label="hasComponent"]
+ *
+ *    {  rank=same
+ *       point_3 [shape=point]
+ *       node_3 [label=< <I>ObjectTypeNode</I><BR/>Pump >] }
+ *    point_2 -> point_3 [arrowhead=none]
+ *    point_3 -> node_3 [label="hasSubtype"]
+ *
+ *    {  rank=same
+ *       point_4 [shape=point]
+ *       node_4 [label=< <I>VariableNode</I><BR/>Status >] }
+ *    node_3 -> point_4 [arrowhead=none]
+ *    point_4 -> node_4 [label="hasComponent"]
+ *
+ *    {  rank=same
+ *       point_5 [shape=point]
+ *       node_5 [label=< <I>VariableNode</I><BR/>MotorRPM >] }
+ *    point_4 -> point_5 [arrowhead=none]
+ *    point_5 -> node_5 [label="hasComponent"]
+ *
+ *    }
+ *
+ */
+
+/* predefined identifier for later use */
+UA_NodeId pumpTypeId = {1, UA_NODEIDTYPE_NUMERIC, {1001}};
+
+static void
+defineObjectTypes(UA_Server *server) {
+    /* Define the object type for "Device" */
+    UA_NodeId deviceTypeId; /* get the nodeid assigned by the server */
+    UA_ObjectTypeAttributes dtAttr;
+    UA_ObjectTypeAttributes_init(&dtAttr);
+    dtAttr.displayName = UA_LOCALIZEDTEXT("en_US", "DeviceType");
+    UA_Server_addObjectTypeNode(server, UA_NODEID_NULL,
+                                UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE),
+                                UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
+                                UA_QUALIFIEDNAME(1, "DeviceType"), dtAttr,
+                                NULL, &deviceTypeId);
+
+    UA_VariableAttributes mnAttr;
+    UA_VariableAttributes_init(&mnAttr);
+    mnAttr.displayName = UA_LOCALIZEDTEXT("en_US", "ManufacturerName");
+    UA_Server_addVariableNode(server, UA_NODEID_NULL, deviceTypeId,
+                              UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
+                              UA_QUALIFIEDNAME(1, "ManufacturerName"),
+                              UA_NODEID_NULL, mnAttr, NULL, NULL);
+
+    UA_VariableAttributes modelAttr;
+    UA_VariableAttributes_init(&modelAttr);
+    modelAttr.displayName = UA_LOCALIZEDTEXT("en_US", "ModelName");
+    UA_Server_addVariableNode(server, UA_NODEID_NULL, deviceTypeId,
+                              UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
+                              UA_QUALIFIEDNAME(1, "ModelName"),
+                              UA_NODEID_NULL, modelAttr, NULL, NULL);
+
+    /* Define the object type for "Pump" */
+    UA_ObjectTypeAttributes ptAttr;
+    UA_ObjectTypeAttributes_init(&ptAttr);
+    ptAttr.displayName = UA_LOCALIZEDTEXT("en_US", "PumpType");
+    UA_Server_addObjectTypeNode(server, pumpTypeId,
+                                deviceTypeId, UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
+                                UA_QUALIFIEDNAME(1, "PumpType"), ptAttr,
+                                NULL, NULL);
+
+    UA_VariableAttributes statusAttr;
+    UA_VariableAttributes_init(&statusAttr);
+    statusAttr.displayName = UA_LOCALIZEDTEXT("en_US", "Status");
+    statusAttr.valueRank = -1;
+    UA_Server_addVariableNode(server, UA_NODEID_NULL, pumpTypeId,
+                              UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
+                              UA_QUALIFIEDNAME(1, "Status"),
+                              UA_NODEID_NULL, statusAttr, NULL, NULL);
+
+    UA_VariableAttributes rpmAttr;
+    UA_VariableAttributes_init(&rpmAttr);
+    rpmAttr.displayName = UA_LOCALIZEDTEXT("en_US", "MotorRPM");
+    rpmAttr.valueRank = -1;
+    UA_Server_addVariableNode(server, UA_NODEID_NULL, pumpTypeId,
+                              UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
+                              UA_QUALIFIEDNAME(1, "MotorRPMs"),
+                              UA_NODEID_NULL, rpmAttr, NULL, NULL);
+}
+
+/**
+ * Now we add the derived ObjectType for the pump that inherits from the device
+ * object type. The resulting object contains all four inherited child
+ * variables. The object has a reference of type ``hasTypeDefinition`` to the
+ * object type. Clients can browse this information at runtime and adjust
+ * accordingly.
+ */
+
+static void
+addPumpObjectInstance(UA_Server *server, char *name) {
+    UA_ObjectAttributes oAttr;
+    UA_ObjectAttributes_init(&oAttr);
+    oAttr.displayName = UA_LOCALIZEDTEXT("en_US", name);
+    UA_Server_addObjectNode(server, UA_NODEID_NULL,
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
+                            UA_QUALIFIEDNAME(1, name),
+                            pumpTypeId, /* this refers to the object type
+                                           identifier */
+                            oAttr, NULL, NULL);
+}
+
+/**
+ * Often times, we want to run a constructor function on a new object. This is
+ * especially useful when an object is instantiated at runtime (with the
+ * AddNodes service) and the integration with an underlying process canot be
+ * manually defined. In the following constructor example, we simply set the
+ * pump status to on.
+ */
+
+UA_Server *s = NULL; /* required to get the server pointer into the constructor
+                        function (will change for v0.3) */
+
+static void *
+pumpTypeConstructor(const UA_NodeId instance) {
+    UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "New pump created");
+
+    /* Find the NodeId of the status child variable */
+    UA_RelativePathElement rpe;
+    UA_RelativePathElement_init(&rpe);
+    rpe.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT);
+    rpe.isInverse = false;
+    rpe.includeSubtypes = false;
+    rpe.targetName = UA_QUALIFIEDNAME(1, "Status");
+    
+    UA_BrowsePath bp;
+    UA_BrowsePath_init(&bp);
+    bp.startingNode = instance;
+    bp.relativePath.elementsSize = 1;
+    bp.relativePath.elements = &rpe;
+    
+    UA_BrowsePathResult bpr =
+        UA_Server_translateBrowsePathToNodeIds(s, &bp);
+    if(bpr.statusCode != UA_STATUSCODE_GOOD ||
+       bpr.targetsSize < 1)
+        return NULL;
+
+    /* Set the status value */
+    UA_Boolean status = true;
+    UA_Variant value;
+    UA_Variant_setScalar(&value, &status, &UA_TYPES[UA_TYPES_BOOLEAN]);
+    UA_Server_writeValue(s, bpr.targets[0].targetId.nodeId, value);
+    UA_BrowsePathResult_deleteMembers(&bpr);
+
+    /* The return pointer of the constructor is attached to the ObjectNode */
+    return NULL;
+}
+
+static void
+addPumpTypeConstructor(UA_Server *server) {
+    UA_ObjectLifecycleManagement olm;
+    olm.constructor = pumpTypeConstructor;
+    olm.destructor = NULL;
+    UA_Server_setObjectTypeNode_lifecycleManagement(server, pumpTypeId, olm);
+}
+
+/** It follows the main server code, making use of the above definitions. */
+
+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(void) {
+    signal(SIGINT, stopHandler);
+    signal(SIGTERM, stopHandler);
+
+    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);
+    s = server; /* required for the constructor */
+
+    manuallyDefinePump(server);
+    defineObjectTypes(server);
+    addPumpObjectInstance(server, "pump2");
+    addPumpObjectInstance(server, "pump3");
+    addPumpTypeConstructor(server);
+    addPumpObjectInstance(server, "pump4");
+    addPumpObjectInstance(server, "pump5");
+
+    UA_Server_run(server, &running);
+    UA_Server_delete(server);
+    nl.deleteMembers(&nl);
+    return 0;
+}

+ 121 - 0
examples/tutorial_server_variable.c

@@ -0,0 +1,121 @@
+/* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
+ * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
+
+/**
+ * Adding Variables to a Server
+ * ----------------------------
+ *
+ * This tutorial shows how to work with data types and how to add variable nodes
+ * to a server. First, we add a new variable to the server. Take a look at the
+ * definition of the ``UA_VariableAttrbitues`` structure to see the list of all
+ * attributes defined for VariableNodes.
+ */
+
+#include <signal.h>
+#include <stdio.h>
+#include "open62541.h"
+
+static void
+addVariable(UA_Server *server) {
+    /* 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");
+    attr.dataType = UA_TYPES[UA_TYPES_INT32].typeId;
+
+    /* 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_Server_addVariableNode(server, myIntegerNodeId, parentNodeId,
+                              parentReferenceNodeId, myIntegerName,
+                              UA_NODEID_NULL, attr, NULL, NULL);
+}
+
+/**
+ * Now we change the value with the write service. This uses the same service
+ * implementation that can also be reached over the network by an OPC UA client.
+ */
+
+static void
+writeVariable(UA_Server *server) {
+    UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
+
+    /* Write a different integer value */
+    UA_Int32 myInteger = 43;
+    UA_Variant myVar;
+    UA_Variant_init(&myVar);
+    UA_Variant_setScalar(&myVar, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
+    UA_Server_writeValue(server, myIntegerNodeId, myVar);
+
+    /* Set the status code of the value to an error code. The function
+     * UA_Server_write provides access to the raw service. The above
+     * UA_Server_writeValue is syntactic sugar for writing a specific node
+     * attribute with the write service. */
+    UA_WriteValue wv;
+    UA_WriteValue_init(&wv);
+    wv.nodeId = myIntegerNodeId;
+    wv.attributeId = UA_ATTRIBUTEID_VALUE;
+    wv.value.status = UA_STATUSCODE_BADNOTCONNECTED;
+    wv.value.hasStatus = true;
+    UA_Server_write(server, &wv);
+
+    /* Reset the variable to a good statuscode with a value */
+    wv.value.hasStatus = false;
+    wv.value.value = myVar;
+    wv.value.hasValue = true;
+    UA_Server_write(server, &wv);
+}
+
+/**
+ * Note how we initially set the DataType attribute of the variable node to the
+ * NodeId of the Int32 data type. This forbids writing values that are not an
+ * Int32. The following code shows how this consistency check is performed for
+ * every write.
+ */
+
+static void
+writeWrongVariable(UA_Server *server) {
+    UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
+
+    /* Write a string */
+    UA_String myString = UA_STRING("test");
+    UA_Variant myVar;
+    UA_Variant_init(&myVar);
+    UA_Variant_setScalar(&myVar, &myString, &UA_TYPES[UA_TYPES_STRING]);
+    UA_StatusCode retval = UA_Server_writeValue(server, myIntegerNodeId, myVar);
+    printf("Writing a string returned statuscode %s\n", UA_StatusCode_name(retval));
+}
+
+/** It follows the main server code, making use of the above definitions. */
+
+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(void) {
+    signal(SIGINT, stopHandler);
+    signal(SIGTERM, stopHandler);
+
+    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);
+
+    addVariable(server);
+    writeVariable(server);
+    writeWrongVariable(server);
+
+    UA_Server_run(server, &running);
+    UA_Server_delete(server);
+    nl.deleteMembers(&nl);
+    return 0;
+}

+ 143 - 0
examples/tutorial_server_variabletype.c

@@ -0,0 +1,143 @@
+/* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
+ * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
+
+/**
+ * Working with Variable Types
+ * ---------------------------
+ *
+ * Variable types have three functions:
+ *
+ * - Constrain the possible data type, value rank and array dimensions of the
+ *   variables of that type. This allows interface code to be written against
+ *   the generic type definition, so it is applicable for all instances.
+ * - Provide a sensible default value
+ * - Enable a semantic interpretation of the variable based on its type
+ *
+ * In the example of this tutorial, we represent a point in 2D space by an array
+ * of double values. The following function adds the corresponding
+ * VariableTypeNode to the hierarchy of variable types.
+ */
+
+#include <signal.h>
+#include "open62541.h"
+
+static UA_NodeId pointTypeId;
+
+static void
+addVariableType2DPoint(UA_Server *server) {
+    UA_VariableTypeAttributes vtAttr;
+    UA_VariableTypeAttributes_init(&vtAttr);
+    vtAttr.dataType = UA_TYPES[UA_TYPES_DOUBLE].typeId;
+    vtAttr.valueRank = 1; /* array with one dimension */
+    UA_UInt32 arrayDims[1] = {2};
+    vtAttr.arrayDimensions = arrayDims;
+    vtAttr.arrayDimensionsSize = 1;
+    vtAttr.displayName = UA_LOCALIZEDTEXT("en_US", "2DPoint Type");
+
+    /* a matching default value is required */
+    UA_Double zero[2] = {0.0, 0.0};
+    UA_Variant_setArray(&vtAttr.value, zero, 2, &UA_TYPES[UA_TYPES_DOUBLE]);
+
+    UA_Server_addVariableTypeNode(server, UA_NODEID_NULL,
+                                  UA_NODEID_NUMERIC(0, UA_NS0ID_BASEVARIABLETYPE),
+                                  UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
+                                  UA_QUALIFIEDNAME(1, "2DPoint Type"), UA_NODEID_NULL,
+                                  vtAttr, NULL, &pointTypeId);
+}
+
+/**
+ * Now the new variable type for *2DPoint* can be referenced during the creation
+ * of a new variable. If no value is given, the default from the variable type
+ * is copied during instantiation.
+ */
+
+static UA_NodeId pointVariableId;
+
+static void
+addVariable(UA_Server *server) {
+    /* Prepare the node attributes */
+    UA_VariableAttributes vAttr;
+    UA_VariableAttributes_init(&vAttr);
+    vAttr.dataType = UA_TYPES[UA_TYPES_DOUBLE].typeId;
+    vAttr.valueRank = 1; /* array with one dimension */
+    UA_UInt32 arrayDims[1] = {2};
+    vAttr.arrayDimensions = arrayDims;
+    vAttr.arrayDimensionsSize = 1;
+    vAttr.displayName = UA_LOCALIZEDTEXT("en_US", "2DPoint Variable");
+    /* vAttr.value is left empty, the server instantiates with the default value */
+
+    /* Add the node */
+    UA_Server_addVariableNode(server, UA_NODEID_NULL,
+                              UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+                              UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
+                              UA_QUALIFIEDNAME(1, "2DPoint Type"), pointTypeId,
+                              vAttr, NULL, &pointVariableId);
+}
+
+/**
+ * The constraints of the variable type are enforced when creating new variable
+ * instances of the type. In the following function, adding a variable of
+ * *2DPoint* type with a string value fails because The value does not match the
+ * variable type constraints. */
+
+static void
+addVariableFail(UA_Server *server) {
+    /* Prepare the node attributes */
+    UA_VariableAttributes vAttr;
+    UA_VariableAttributes_init(&vAttr);
+    vAttr.dataType = UA_TYPES[UA_TYPES_DOUBLE].typeId;
+    vAttr.valueRank = -1; /* a scalar. this is not allowed per the variable type */
+    vAttr.displayName = UA_LOCALIZEDTEXT("en_US", "2DPoint Variable (fail)");
+    UA_String s = UA_STRING("2dpoint?");
+    UA_Variant_setScalar(&vAttr.value, &s, &UA_TYPES[UA_TYPES_STRING]);
+
+    /* Add the node */
+    UA_Server_addVariableNode(server, UA_NODEID_NULL,
+                              UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+                              UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
+                              UA_QUALIFIEDNAME(1, "2DPoint Type (fail)"), pointTypeId,
+                              vAttr, NULL, NULL);
+}
+
+/**
+ * The constraints of the variable type are enforced when writing the datatype,
+ * valuerank and arraydimensions attributes of the variable. This, in turn,
+ * constrains the value attribute of the variable. */
+
+static void
+writeVariable(UA_Server *server) {
+    UA_StatusCode retval = UA_Server_writeValueRank(server, pointVariableId, 0);
+    UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
+                "Setting the Value Rank failed with Status Code %s\n",
+                UA_StatusCode_name(retval));
+
+}
+
+/** It follows the main server code, making use of the above definitions. */
+
+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(void) {
+    signal(SIGINT, stopHandler);
+    signal(SIGTERM, stopHandler);
+
+    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);
+
+    addVariableType2DPoint(server);
+    addVariable(server);
+    addVariableFail(server);
+
+    UA_Server_run(server, &running);
+    UA_Server_delete(server);
+    nl.deleteMembers(&nl);
+    return 0;
+}

+ 0 - 69
examples/tutorials/server_firststeps.c

@@ -1,69 +0,0 @@
-/* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
- * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
-
-/**
- * Building a simple server
- * ------------------------
- *
- * This series of tutorial guide you through your first steps with open62541.
- * For compiling the examples, you need a compiler (MS Visual Studio 2015 or
- * newer, GCC, Clang and MinGW32 are all known to be working). The compilation
- * instructions are given for GCC but should be straightforward to adapt.
- *
- * It will also be very helpful to install an OPC UA Client with a graphical
- * frontend, such as UAExpert by Unified Automation. That will enable you to
- * examine the information model of any OPC UA server.
- *
- * To get started, downdload the open62541 single-file release from
- * http://open62541.org or generate it according to the :ref:`build instructions
- * <building>` with the "amalgamation" option enabled. From now on, we assume
- * you have the ``open62541.c/.h`` files in the current folder.
- *
- * Now create a new C source-file called ``myServer.c`` with the following content: */
-
-#include <signal.h>
-#include "open62541.h"
-
-UA_Boolean running = true;
-static void stopHandler(int sig) {
-    running = false;
-}
-
-int main(void) {
-    signal(SIGINT, stopHandler);
-    signal(SIGTERM, stopHandler);
-
-    UA_ServerConfig config = UA_ServerConfig_standard;
-    UA_ServerNetworkLayer nl;
-    nl = UA_ServerNetworkLayerTCP(UA_ConnectionConfig_standard, 4840);
-    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;
-}
-
-/**
- * This is all that is needed for a simple OPC UA server. Compile the the server
- * with GCC using the following command:
- *
- * .. code-block:: bash
- *
- *    $ gcc -std=c99 open62541.c myServer.c -o myServer
- *
- * Now start the server (and stop with ctrl-c):
- *
- * .. code-block:: bash
- *
- *    $ ./myServer
- *
- * You have now compiled and run your first OPC UA server. You can go ahead and
- * browse the information model with a generic client, such as UAExpert. The
- * server will be listening on ``opc.tcp://localhost:4840`` - go ahead and give
- * it a try.
- *
- * In the following tutorials, you will be shown how to populate the server's
- * information model and how to create a client application. */

+ 1 - 1
include/ua_client_highlevel.h

@@ -28,9 +28,9 @@ extern "C" {
  *
  *
  * Read Attributes
  * Read Attributes
  * ^^^^^^^^^^^^^^^
  * ^^^^^^^^^^^^^^^
- *
  * The following functions can be used to retrieve a single node attribute. Use
  * The following functions can be used to retrieve a single node attribute. Use
  * the regular service to read several attributes at once. */
  * the regular service to read several attributes at once. */
+
 /* Don't call this function, use the typed versions */
 /* Don't call this function, use the typed versions */
 UA_StatusCode UA_EXPORT
 UA_StatusCode UA_EXPORT
 __UA_Client_readAttribute(UA_Client *client, const UA_NodeId *nodeId,
 __UA_Client_readAttribute(UA_Client *client, const UA_NodeId *nodeId,

+ 2 - 0
include/ua_server.h

@@ -202,6 +202,8 @@ typedef struct {
 UA_UInt16 UA_EXPORT UA_Server_addNamespace(UA_Server *server, const char* name);
 UA_UInt16 UA_EXPORT UA_Server_addNamespace(UA_Server *server, const char* name);
 
 
 /**
 /**
+ * .. _server-lifecycle:
+ *
  * Server Lifecycle
  * Server Lifecycle
  * ---------------- */
  * ---------------- */
 UA_Server UA_EXPORT * UA_Server_new(const UA_ServerConfig config);
 UA_Server UA_EXPORT * UA_Server_new(const UA_ServerConfig config);

+ 46 - 42
include/ua_types.h

@@ -24,34 +24,16 @@ extern "C" {
  * generated from standard XML definitions. Their exact definitions can be
  * generated from standard XML definitions. Their exact definitions can be
  * looked up at https://opcfoundation.org/UA/schemas/Opc.Ua.Types.bsd.xml.
  * looked up at https://opcfoundation.org/UA/schemas/Opc.Ua.Types.bsd.xml.
  *
  *
- * Note that arrays can only be part of a scalar data type and never constitute
- * a data type on their own. Also, open62541 does not implement unions so far.
- * They are a recent addition to the protocol (since OPC UA v1.03). And so far,
- * no service definition makes of unions in the request / response message
- * definition. Instead, :ref:`Variants <variant>` are used when values of
- * different types are possible.
+ * For users that are new to open62541, take a look at the :ref:`tutorial for
+ * working with data types<types-tutorial>` before diving into the
+ * implementation details.
  *
  *
- * All data types ``T`` (builtin and generated) share the same basic API for
- * creation, copying and deletion:
- *
- * - ``void T_init(T *ptr)``: Initialize the data type. This is synonymous with
- *   zeroing out the memory, i.e. ``memset(ptr, 0, sizeof(T))``.
- * - ``T* T_new()``: Allocate and return the memory for the data type. The
- *   value is already initialized.
- * - ``UA_StatusCode T_copy(const T *src, T *dst)``: Copy the content of the
- *   data type. Returns ``UA_STATUSCODE_GOOD`` or
- *   ``UA_STATUSCODE_BADOUTOFMEMORY``.
- * - ``void T_deleteMembers(T *ptr)``: Delete the dynamically allocated content
- *   of the data type and perform a ``T_init`` to reset the type.
- * - ``void T_delete(T *ptr)``: Delete the content of the data type and the
- *   memory for the data type itself. */
+ * Builtin Types
+ * ------------- */
 
 
 #define UA_BUILTIN_TYPES_COUNT 25U
 #define UA_BUILTIN_TYPES_COUNT 25U
 
 
 /**
 /**
- * Builtin Types
- * -------------
- *
  * Boolean
  * Boolean
  * ^^^^^^^
  * ^^^^^^^
  * A two-state logical value (true or false). */
  * A two-state logical value (true or false). */
@@ -196,6 +178,8 @@ UA_STRING(char *chars) {
 #define UA_STRING_ALLOC(CHARS) UA_String_fromChars(CHARS)
 #define UA_STRING_ALLOC(CHARS) UA_String_fromChars(CHARS)
 
 
 /**
 /**
+ * .. _datetime:
+ *
  * DateTime
  * DateTime
  * ^^^^^^^^
  * ^^^^^^^^
  * An instance in time. A DateTime value is encoded as a 64-bit signed integer
  * An instance in time. A DateTime value is encoded as a 64-bit signed integer
@@ -256,8 +240,10 @@ UA_EXPORT extern const UA_Guid UA_GUID_NULL;
 typedef UA_String UA_ByteString;
 typedef UA_String UA_ByteString;
 
 
 static UA_INLINE UA_Boolean
 static UA_INLINE UA_Boolean
-UA_ByteString_equal(const UA_ByteString *string1, const UA_ByteString *string2) {
-    return UA_String_equal((const UA_String*)string1, (const UA_String*)string2);
+UA_ByteString_equal(const UA_ByteString *string1,
+                    const UA_ByteString *string2) {
+    return UA_String_equal((const UA_String*)string1,
+                           (const UA_String*)string2);
 }
 }
 
 
 /* Allocates memory of size length for the bytestring.
 /* Allocates memory of size length for the bytestring.
@@ -504,11 +490,11 @@ typedef struct UA_NumericRange UA_NumericRange;
 #define UA_EMPTY_ARRAY_SENTINEL ((void*)0x01)
 #define UA_EMPTY_ARRAY_SENTINEL ((void*)0x01)
 
 
 typedef enum {
 typedef enum {
-        UA_VARIANT_DATA,          /* The data has the same lifecycle as the
-                                     variant */
-        UA_VARIANT_DATA_NODELETE, /* The data is "borrowed" by the variant and
-                                     shall not be deleted at the end of the
-                                     variant's lifecycle. */
+    UA_VARIANT_DATA,          /* The data has the same lifecycle as the
+                                 variant */
+    UA_VARIANT_DATA_NODELETE, /* The data is "borrowed" by the variant and
+                                 shall not be deleted at the end of the
+                                 variant's lifecycle. */
 } UA_VariantStorageType;
 } UA_VariantStorageType;
 
 
 typedef struct {
 typedef struct {
@@ -650,13 +636,13 @@ UA_Variant_setRangeCopy(UA_Variant *v, const void *array,
  * are described. If the received data type is unkown, the encoded string and
  * are described. If the received data type is unkown, the encoded string and
  * target NodeId is stored instead of the decoded value. */
  * target NodeId is stored instead of the decoded value. */
 typedef enum {
 typedef enum {
-        UA_EXTENSIONOBJECT_ENCODED_NOBODY     = 0,
-        UA_EXTENSIONOBJECT_ENCODED_BYTESTRING = 1,
-        UA_EXTENSIONOBJECT_ENCODED_XML        = 2,
-        UA_EXTENSIONOBJECT_DECODED            = 3,
-        UA_EXTENSIONOBJECT_DECODED_NODELETE   = 4 /* Don't delete the content
-                                                     together with the
-                                                     ExtensionObject */
+    UA_EXTENSIONOBJECT_ENCODED_NOBODY     = 0,
+    UA_EXTENSIONOBJECT_ENCODED_BYTESTRING = 1,
+    UA_EXTENSIONOBJECT_ENCODED_XML        = 2,
+    UA_EXTENSIONOBJECT_DECODED            = 3,
+    UA_EXTENSIONOBJECT_DECODED_NODELETE   = 4 /* Don't delete the content
+                                                 together with the
+                                                 ExtensionObject */
 } UA_ExtensionObjectEncoding;
 } UA_ExtensionObjectEncoding;
 
 
 typedef struct {
 typedef struct {
@@ -721,10 +707,28 @@ typedef struct UA_DiagnosticInfo {
  *
  *
  * Generic Type Handling
  * Generic Type Handling
  * ---------------------
  * ---------------------
- * The builtin types can be combined to data structures. All information about a
- * (structured) data type is stored in a ``UA_DataType``. The array ``UA_TYPES``
- * contains the description of all standard-defined types and is used for
- * handling of generic types. */
+ *
+ * All information about a (builtin/structured) data type is stored in a
+ * ``UA_DataType``. The array ``UA_TYPES`` contains the description of all
+ * standard-defined types. This type description is used for the following
+ * generic operations that work on all types:
+ *
+ * - ``void T_init(T *ptr)``: Initialize the data type. This is synonymous with
+ *   zeroing out the memory, i.e. ``memset(ptr, 0, sizeof(T))``.
+ * - ``T* T_new()``: Allocate and return the memory for the data type. The
+ *   value is already initialized.
+ * - ``UA_StatusCode T_copy(const T *src, T *dst)``: Copy the content of the
+ *   data type. Returns ``UA_STATUSCODE_GOOD`` or
+ *   ``UA_STATUSCODE_BADOUTOFMEMORY``.
+ * - ``void T_deleteMembers(T *ptr)``: Delete the dynamically allocated content
+ *   of the data type and perform a ``T_init`` to reset the type.
+ * - ``void T_delete(T *ptr)``: Delete the content of the data type and the
+ *   memory for the data type itself.
+ *
+ * Specializations, such as ``UA_Int32_new()`` are derived from the generic
+ * type operations as static inline functions.
+ */
+
 typedef struct {
 typedef struct {
 #ifdef UA_ENABLE_TYPENAMES
 #ifdef UA_ENABLE_TYPENAMES
     const char *memberName;
     const char *memberName;
@@ -869,7 +873,7 @@ typedef struct {
     UA_UInt32 min;
     UA_UInt32 min;
     UA_UInt32 max;
     UA_UInt32 max;
 } UA_NumericRangeDimension;
 } UA_NumericRangeDimension;
-    
+
 struct UA_NumericRange {
 struct UA_NumericRange {
     size_t dimensionsSize;
     size_t dimensionsSize;
     UA_NumericRangeDimension *dimensions;
     UA_NumericRangeDimension *dimensions;

+ 8 - 1
src/server/ua_services_attribute.c

@@ -119,9 +119,16 @@ compatibleArrayDimensions(size_t constraintArrayDimensionsSize,
                           const UA_UInt32 *constraintArrayDimensions,
                           const UA_UInt32 *constraintArrayDimensions,
                           size_t testArrayDimensionsSize,
                           size_t testArrayDimensionsSize,
                           const UA_UInt32 *testArrayDimensions) {
                           const UA_UInt32 *testArrayDimensions) {
+    /* No array dimensions defined -> everything is permitted if the value rank fits */
+    if(constraintArrayDimensionsSize == 0) {
+        return UA_STATUSCODE_GOOD;
+    }
+
+    /* Dimension count must match */
     if(testArrayDimensionsSize != constraintArrayDimensionsSize)
     if(testArrayDimensionsSize != constraintArrayDimensionsSize)
         return UA_STATUSCODE_BADTYPEMISMATCH;
         return UA_STATUSCODE_BADTYPEMISMATCH;
-    /* dimension size zero in the constraint is a wildcard */
+
+    /* Dimension lengths must match; zero in the constraint is a wildcard */
     for(size_t i = 0; i < constraintArrayDimensionsSize; ++i) {
     for(size_t i = 0; i < constraintArrayDimensionsSize; ++i) {
         if(constraintArrayDimensions[i] != testArrayDimensions[i] &&
         if(constraintArrayDimensions[i] != testArrayDimensions[i] &&
            constraintArrayDimensions[i] != 0)
            constraintArrayDimensions[i] != 0)

+ 76 - 46
src/server/ua_services_nodemanagement.c

@@ -18,8 +18,8 @@ checkParentReference(UA_Server *server, UA_Session *session, UA_NodeClass nodeCl
     /* See if the parent exists */
     /* See if the parent exists */
     const UA_Node *parent = UA_NodeStore_get(server->nodestore, parentNodeId);
     const UA_Node *parent = UA_NodeStore_get(server->nodestore, parentNodeId);
     if(!parent) {
     if(!parent) {
-        UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                             "AddNodes: Parent node not found");
+        UA_LOG_INFO_SESSION(server->config.logger, session,
+                            "AddNodes: Parent node not found");
         return UA_STATUSCODE_BADPARENTNODEIDINVALID;
         return UA_STATUSCODE_BADPARENTNODEIDINVALID;
     }
     }
 
 
@@ -27,22 +27,22 @@ checkParentReference(UA_Server *server, UA_Session *session, UA_NodeClass nodeCl
     const UA_ReferenceTypeNode *referenceType =
     const UA_ReferenceTypeNode *referenceType =
         (const UA_ReferenceTypeNode*)UA_NodeStore_get(server->nodestore, referenceTypeId);
         (const UA_ReferenceTypeNode*)UA_NodeStore_get(server->nodestore, referenceTypeId);
     if(!referenceType) {
     if(!referenceType) {
-        UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                             "AddNodes: Reference type to the parent not found");
+        UA_LOG_INFO_SESSION(server->config.logger, session,
+                            "AddNodes: Reference type to the parent not found");
         return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
         return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
     }
     }
 
 
     /* Check if the referencetype is a reference type node */
     /* Check if the referencetype is a reference type node */
     if(referenceType->nodeClass != UA_NODECLASS_REFERENCETYPE) {
     if(referenceType->nodeClass != UA_NODECLASS_REFERENCETYPE) {
-        UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                             "AddNodes: Reference type to the parent invalid");
+        UA_LOG_INFO_SESSION(server->config.logger, session,
+                            "AddNodes: Reference type to the parent invalid");
         return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
         return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
     }
     }
 
 
     /* Check that the reference type is not abstract */
     /* Check that the reference type is not abstract */
     if(referenceType->isAbstract == true) {
     if(referenceType->isAbstract == true) {
-        UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                             "AddNodes: Abstract reference type to the parent invalid");
+        UA_LOG_INFO_SESSION(server->config.logger, session,
+                            "AddNodes: Abstract reference type to the parent not allowed");
         return UA_STATUSCODE_BADREFERENCENOTALLOWED;
         return UA_STATUSCODE_BADREFERENCENOTALLOWED;
     }
     }
 
 
@@ -54,16 +54,16 @@ checkParentReference(UA_Server *server, UA_Session *session, UA_NodeClass nodeCl
        nodeClass == UA_NODECLASS_REFERENCETYPE) {
        nodeClass == UA_NODECLASS_REFERENCETYPE) {
         /* type needs hassubtype reference to the supertype */
         /* type needs hassubtype reference to the supertype */
         if(!UA_NodeId_equal(referenceTypeId, &subtypeId)) {
         if(!UA_NodeId_equal(referenceTypeId, &subtypeId)) {
-            UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                                 "AddNodes: New type node need to have a "
-                                 "hassubtype reference");
+            UA_LOG_INFO_SESSION(server->config.logger, session,
+                                "AddNodes: New type node need to have a "
+                                "HasSubType reference");
             return UA_STATUSCODE_BADREFERENCENOTALLOWED;
             return UA_STATUSCODE_BADREFERENCENOTALLOWED;
         }
         }
         /* supertype needs to be of the same node type  */
         /* supertype needs to be of the same node type  */
         if(parent->nodeClass != nodeClass) {
         if(parent->nodeClass != nodeClass) {
-            UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                                 "AddNodes: New type node needs to be of the same "
-                                 "node type as the parent");
+            UA_LOG_INFO_SESSION(server->config.logger, session,
+                                "AddNodes: New type node needs to be of the same "
+                                "node type as the parent");
             return UA_STATUSCODE_BADPARENTNODEIDINVALID;
             return UA_STATUSCODE_BADPARENTNODEIDINVALID;
         }
         }
         return UA_STATUSCODE_GOOD;
         return UA_STATUSCODE_GOOD;
@@ -74,8 +74,8 @@ checkParentReference(UA_Server *server, UA_Session *session, UA_NodeClass nodeCl
         UA_NODEID_NUMERIC(0, UA_NS0ID_HIERARCHICALREFERENCES);
         UA_NODEID_NUMERIC(0, UA_NS0ID_HIERARCHICALREFERENCES);
     if(!isNodeInTree(server->nodestore, referenceTypeId,
     if(!isNodeInTree(server->nodestore, referenceTypeId,
                      &hierarchicalReference, &subtypeId, 1)) {
                      &hierarchicalReference, &subtypeId, 1)) {
-        UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                             "AddNodes: Reference type is not hierarchical");
+        UA_LOG_INFO_SESSION(server->config.logger, session,
+                            "AddNodes: Reference type is not hierarchical");
         return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
         return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
     }
     }
 
 
@@ -417,7 +417,7 @@ Service_AddNodes_existing(UA_Server *server, UA_Session *session, UA_Node *node,
 
 
     /* Check the namespaceindex */
     /* Check the namespaceindex */
     if(node->nodeId.namespaceIndex >= server->namespacesSize) {
     if(node->nodeId.namespaceIndex >= server->namespacesSize) {
-        UA_LOG_DEBUG_SESSION(server->config.logger, session, "AddNodes: Namespace invalid");
+        UA_LOG_INFO_SESSION(server->config.logger, session, "AddNodes: Namespace invalid");
         UA_NodeStore_deleteNode(node);
         UA_NodeStore_deleteNode(node);
         return UA_STATUSCODE_BADNODEIDINVALID;
         return UA_STATUSCODE_BADNODEIDINVALID;
     }
     }
@@ -426,9 +426,9 @@ Service_AddNodes_existing(UA_Server *server, UA_Session *session, UA_Node *node,
     UA_StatusCode retval = checkParentReference(server, session, node->nodeClass,
     UA_StatusCode retval = checkParentReference(server, session, node->nodeClass,
                                                 parentNodeId, referenceTypeId);
                                                 parentNodeId, referenceTypeId);
     if(retval != UA_STATUSCODE_GOOD) {
     if(retval != UA_STATUSCODE_GOOD) {
-        UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                             "AddNodes: Checking the reference to the parent returned"
-                             "error code %s", UA_StatusCode_name(retval));
+        UA_LOG_INFO_SESSION(server->config.logger, session,
+                            "AddNodes: Checking the reference to the parent returned"
+                            "error code %s", UA_StatusCode_name(retval));
         UA_NodeStore_deleteNode(node);
         UA_NodeStore_deleteNode(node);
         return retval;
         return retval;
     }
     }
@@ -436,9 +436,9 @@ Service_AddNodes_existing(UA_Server *server, UA_Session *session, UA_Node *node,
     /* Add the node to the nodestore */
     /* Add the node to the nodestore */
     retval = UA_NodeStore_insert(server->nodestore, node);
     retval = UA_NodeStore_insert(server->nodestore, node);
     if(retval != UA_STATUSCODE_GOOD) {
     if(retval != UA_STATUSCODE_GOOD) {
-        UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                             "AddNodes: Node could not be added to the nodestore "
-                             "with error code %s", UA_StatusCode_name(retval));
+        UA_LOG_INFO_SESSION(server->config.logger, session,
+                            "AddNodes: Node could not be added to the nodestore "
+                            "with error code %s", UA_StatusCode_name(retval));
         return retval;
         return retval;
     }
     }
 
 
@@ -446,8 +446,8 @@ Service_AddNodes_existing(UA_Server *server, UA_Session *session, UA_Node *node,
     if(addedNodeId) {
     if(addedNodeId) {
         retval = UA_NodeId_copy(&node->nodeId, addedNodeId);
         retval = UA_NodeId_copy(&node->nodeId, addedNodeId);
         if(retval != UA_STATUSCODE_GOOD) {
         if(retval != UA_STATUSCODE_GOOD) {
-            UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                                 "AddNodes: Could not copy the nodeid");
+            UA_LOG_INFO_SESSION(server->config.logger, session,
+                                "AddNodes: Could not copy the nodeid");
             goto remove_node;
             goto remove_node;
         }
         }
     }
     }
@@ -461,9 +461,9 @@ Service_AddNodes_existing(UA_Server *server, UA_Session *session, UA_Node *node,
     item.targetNodeId.nodeId = *parentNodeId;
     item.targetNodeId.nodeId = *parentNodeId;
     retval = Service_AddReferences_single(server, session, &item);
     retval = Service_AddReferences_single(server, session, &item);
     if(retval != UA_STATUSCODE_GOOD) {
     if(retval != UA_STATUSCODE_GOOD) {
-        UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                             "AddNodes: Could not add the reference to the parent"
-                             "with error code %s", UA_StatusCode_name(retval));
+        UA_LOG_INFO_SESSION(server->config.logger, session,
+                            "AddNodes: Could not add the reference to the parent"
+                            "with error code %s", UA_StatusCode_name(retval));
         goto remove_node;
         goto remove_node;
     }
     }
 
 
@@ -483,9 +483,9 @@ Service_AddNodes_existing(UA_Server *server, UA_Session *session, UA_Node *node,
         retval = instantiateNode(server, session, &node->nodeId, node->nodeClass,
         retval = instantiateNode(server, session, &node->nodeId, node->nodeClass,
                                  typeDefinition, instantiationCallback);
                                  typeDefinition, instantiationCallback);
         if(retval != UA_STATUSCODE_GOOD) {
         if(retval != UA_STATUSCODE_GOOD) {
-            UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                                 "AddNodes: Could not instantiate the node with"
-                                 "error code 0x%08x", retval);
+            UA_LOG_INFO_SESSION(server->config.logger, session,
+                                "AddNodes: Could not instantiate the node with"
+                                "error code %s", UA_StatusCode_name(retval));
             goto remove_node;
             goto remove_node;
         }
         }
     }
     }
@@ -531,9 +531,9 @@ copyCommonVariableAttributes(UA_Server *server, UA_VariableNode *node,
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     if(UA_NodeId_equal(&node->nodeId, &basevartype) || 
     if(UA_NodeId_equal(&node->nodeId, &basevartype) || 
        UA_NodeId_equal(&node->nodeId, &basedatavartype)) {
        UA_NodeId_equal(&node->nodeId, &basedatavartype)) {
-      node->dataType = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATATYPE);
-      node->valueRank = -2;
-      return retval;
+        node->dataType = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATATYPE);
+        node->valueRank = -2;
+        return retval;
     }
     }
     
     
     const UA_VariableTypeNode *vt =
     const UA_VariableTypeNode *vt =
@@ -552,14 +552,18 @@ copyCommonVariableAttributes(UA_Server *server, UA_VariableNode *node,
         return retval;
         return retval;
         
         
     /* Set the array dimensions. Check only against the vt. */
     /* Set the array dimensions. Check only against the vt. */
-    retval = compatibleArrayDimensions(attr->arrayDimensionsSize, attr->arrayDimensions,
-                                       vt->arrayDimensionsSize, vt->arrayDimensions);
-    if(retval != UA_STATUSCODE_GOOD)
-        return retval;
-    retval = UA_Array_copy(attr->arrayDimensions, attr->arrayDimensionsSize,
-                           (void**)&node->arrayDimensions, &UA_TYPES[UA_TYPES_UINT32]);
-    if(retval != UA_STATUSCODE_GOOD)
+    retval = compatibleArrayDimensions(vt->arrayDimensionsSize, vt->arrayDimensions,
+                                       attr->arrayDimensionsSize, attr->arrayDimensions);
+    if(retval == UA_STATUSCODE_GOOD) {
+        retval = UA_Array_copy(attr->arrayDimensions, attr->arrayDimensionsSize,
+                               (void**)&node->arrayDimensions, &UA_TYPES[UA_TYPES_UINT32]);
+    }
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_SERVER,
+                    "Array dimensions incompatible with the VariableType "
+                    "with error code %s", UA_StatusCode_name(retval));
         return retval;
         return retval;
+    }
     node->arrayDimensionsSize = attr->arrayDimensionsSize;
     node->arrayDimensionsSize = attr->arrayDimensionsSize;
 
 
     /* Set the valuerank */
     /* Set the valuerank */
@@ -567,15 +571,32 @@ copyCommonVariableAttributes(UA_Server *server, UA_VariableNode *node,
         retval = writeValueRankAttribute(server, node, attr->valueRank, vt->valueRank);
         retval = writeValueRankAttribute(server, node, attr->valueRank, vt->valueRank);
     else /* workaround common error where the valuerank is left as 0 */
     else /* workaround common error where the valuerank is left as 0 */
         node->valueRank = vt->valueRank;
         node->valueRank = vt->valueRank;
-    if(retval != UA_STATUSCODE_GOOD)
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_SERVER,
+                    "Value Rank incompatible with the VariableType "
+                    "with error code %s", UA_StatusCode_name(retval));
         return retval;
         return retval;
-    
+    }
+
     /* Set the value */
     /* Set the value */
     UA_DataValue value;
     UA_DataValue value;
     UA_DataValue_init(&value);
     UA_DataValue_init(&value);
-    value.value = attr->value;
     value.hasValue = true;
     value.hasValue = true;
-    retval |= writeValueAttribute(server, node, &value, NULL);
+    value.value = attr->value;
+    value.value.storageType = UA_VARIANT_DATA_NODELETE;
+
+    /* Use the default value from the vt if none is defined */
+    if(!value.value.type)
+        readValueAttribute(server, (const UA_VariableNode *)vt, &value);
+
+    /* Write the value to the node */
+    retval = writeValueAttribute(server, node, &value, NULL);
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_SERVER,
+                    "Could not set the value of the new node "
+                    "with error code %s", UA_StatusCode_name(retval));
+    }
+    UA_DataValue_deleteMembers(&value);
     return retval;
     return retval;
 }
 }
 
 
@@ -704,14 +725,23 @@ Service_AddNodes_single(UA_Server *server, UA_Session *session,
     /* Create the node from the attributes*/
     /* Create the node from the attributes*/
     UA_Node *node = NULL;
     UA_Node *node = NULL;
     result->statusCode = createNodeFromAttributes(server, item, &node);
     result->statusCode = createNodeFromAttributes(server, item, &node);
-    if(result->statusCode != UA_STATUSCODE_GOOD)
+    if(result->statusCode != UA_STATUSCODE_GOOD) {
+        UA_LOG_INFO_SESSION(server->config.logger, session,
+                            "Could not add node with error code %s",
+                            UA_StatusCode_name(result->statusCode));
         return;
         return;
+    }
 
 
     /* Run consistency checks and add the node */
     /* Run consistency checks and add the node */
     UA_assert(node != NULL);
     UA_assert(node != NULL);
     result->statusCode = Service_AddNodes_existing(server, session, node, &item->parentNodeId.nodeId,
     result->statusCode = Service_AddNodes_existing(server, session, node, &item->parentNodeId.nodeId,
                                                    &item->referenceTypeId, &item->typeDefinition.nodeId,
                                                    &item->referenceTypeId, &item->typeDefinition.nodeId,
                                                    instantiationCallback, &result->addedNodeId);
                                                    instantiationCallback, &result->addedNodeId);
+    if(result->statusCode != UA_STATUSCODE_GOOD) {
+        UA_LOG_INFO_SESSION(server->config.logger, session,
+                            "Could not add node with error code %s",
+                            UA_StatusCode_name(result->statusCode));
+    }
 }
 }
 
 
 void Service_AddNodes(UA_Server *server, UA_Session *session,
 void Service_AddNodes(UA_Server *server, UA_Session *session,

+ 7 - 11
tools/travis/travis_linux_after_success.sh

@@ -1,17 +1,13 @@
 #!/bin/bash
 #!/bin/bash
 set -ev
 set -ev
 
 
-if [ $ANALYZE = "true" ]; then
-    echo "=== Skipping after_success scripts in ANALYZE mode ==="
-else
+if [ "$ANALYZE" = "false" ] && [ "$CC" = "gcc" ] && [ "${TRAVIS_REPO_SLUG}" = "open62541/open62541" ]; then
     echo "=== Executing after_success scripts ==="
     echo "=== Executing after_success scripts ==="
-    if [ "$CC" = "gcc" ] && [ "${TRAVIS_REPO_SLUG}" = "open62541/open62541" ]; then
-        if [ ${TRAVIS_PULL_REQUEST} == "false" ] && [ ${TRAVIS_BRANCH} = "0.2" ]; then
-            sh ./tools/travis/travis_push_doc.sh
-            sh ./tools/travis/travis_push_coverity.sh
-        fi
-        sh ./tools/travis/travis_push_release.sh;
-    else
-        echo "  Skipping push scripts since not gcc and/or ${TRAVIS_REPO_SLUG} is not the main repo"
+    if [ "${TRAVIS_PULL_REQUEST}" = "false" ] && [ "${TRAVIS_BRANCH}" = "0.2" ]; then
+        sh ./tools/travis/travis_push_doc.sh
+        sh ./tools/travis/travis_push_coverity.sh
     fi
     fi
+    sh ./tools/travis/travis_push_release.sh;
+else
+    echo "=== Not in the main repository or not the main build; Skip release scripts ==="
 fi
 fi

+ 1 - 1
tools/travis/travis_push_release.sh

@@ -10,7 +10,6 @@ COMMENT="$(git log --pretty=format:"%s" --date=iso --abbrev=10 --all -1)"
 git clone --depth=5 -b gh-pages https://$GITAUTH@github.com/open62541/open62541-www
 git clone --depth=5 -b gh-pages https://$GITAUTH@github.com/open62541/open62541-www
 cd open62541-www
 cd open62541-www
 
 
-#hanndle releases
 cd releases
 cd releases
 if [ ! -e "$TAG.zip" ]; then
 if [ ! -e "$TAG.zip" ]; then
     #add the first line
     #add the first line
@@ -48,5 +47,6 @@ git config --global user.name "Open62541 travis-ci"
 git config --global push.default simple
 git config --global push.default simple
 git commit -am "added release files and updated releases webpage by travis-ci [ci skip]"
 git commit -am "added release files and updated releases webpage by travis-ci [ci skip]"
 git push https://$GITAUTH@github.com/open62541/open62541-www
 git push https://$GITAUTH@github.com/open62541/open62541-www
+
 cd ..
 cd ..
 rm -rf open62541-www
 rm -rf open62541-www