ソースを参照

use compilable examples for the tutorials

Julius Pfrommer 7 年 前
コミット
68402c5ff6

+ 24 - 17
doc/CMakeLists.txt

@@ -8,25 +8,15 @@ set(DOC_SRC_DIR   ${PROJECT_BINARY_DIR}/doc_src)
 make_directory(${DOC_SRC_DIR})
 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/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})
 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)
   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})
 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_constants.h ${DOC_SRC_DIR}/constants.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_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}
   -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}/nodestore.rst ${DOC_SRC_DIR}/information_modelling.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_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")
 add_dependencies(doc_latex open62541)
 
@@ -71,7 +72,13 @@ add_custom_target(doc ${SPHINX_EXECUTABLE}
           ${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}/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_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")
 add_dependencies(doc open62541)

ファイルの差分が大きいため隠しています
+ 12 - 171
doc/tutorial_noderelations.rst


+ 1 - 0
doc/toc.rst

@@ -13,4 +13,5 @@ open62541 Documentation
    server
    client
    constants
+   namespace_compiler
    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::
 
+   tutorial_datatypes.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_noderelations.rst
    tutorial_client_firststeps.rst

+ 38 - 19
examples/CMakeLists.txt

@@ -21,6 +21,44 @@ if(UA_ENABLE_MULTITHREADING)
   list(APPEND LIBS urcu-cds urcu urcu-common)
 endif(UA_ENABLE_MULTITHREADING)
 
+#############
+# Tutorials #
+#############
+
+add_executable(tutorial_datatypes tutorial_datatypes.c $<TARGET_OBJECTS:open62541-object>)
+add_dependencies(tutorial_datatypes open625451_amalgamation)
+target_link_libraries(tutorial_datatypes ${LIBS})
+
+add_executable(tutorial_server_firststeps tutorial_server_firststeps.c $<TARGET_OBJECTS:open62541-object>)
+add_dependencies(tutorial_server_firststeps open625451_amalgamation)
+target_link_libraries(tutorial_server_firststeps ${LIBS})
+
+add_executable(tutorial_server_variable tutorial_server_variable.c $<TARGET_OBJECTS:open62541-object>)
+add_dependencies(tutorial_server_variable open625451_amalgamation)
+target_link_libraries(tutorial_server_variable ${LIBS})
+
+add_executable(tutorial_server_datasource tutorial_server_datasource.c $<TARGET_OBJECTS:open62541-object>)
+add_dependencies(tutorial_server_datasource open625451_amalgamation)
+target_link_libraries(tutorial_server_datasource ${LIBS})
+
+add_executable(tutorial_server_variabletype tutorial_server_variabletype.c $<TARGET_OBJECTS:open62541-object>)
+add_dependencies(tutorial_server_variabletype open625451_amalgamation)
+target_link_libraries(tutorial_server_variabletype ${LIBS})
+
+add_executable(tutorial_server_object tutorial_server_object.c $<TARGET_OBJECTS:open62541-object>)
+add_dependencies(tutorial_server_object open625451_amalgamation)
+target_link_libraries(tutorial_server_object ${LIBS})
+
+if(UA_ENABLE_METHODCALLS)
+  add_executable(tutorial_server_method tutorial_server_method.c $<TARGET_OBJECTS:open62541-object>)
+  add_dependencies(tutorial_server_method open625451_amalgamation)
+  target_link_libraries(tutorial_server_method ${LIBS})
+endif()
+
+add_executable(tutorial_client_firststeps tutorial_client_firststeps.c $<TARGET_OBJECTS:open62541-object>)
+add_dependencies(tutorial_client_firststeps open625451_amalgamation)
+target_link_libraries(tutorial_client_firststeps ${LIBS})
+
 ##################
 # Example Server #
 ##################
@@ -28,10 +66,6 @@ endif(UA_ENABLE_MULTITHREADING)
 add_executable(server server.c $<TARGET_OBJECTS:open62541-object>)
 target_link_libraries(server ${LIBS})
 
-add_executable(server_firststeps tutorials/server_firststeps.c $<TARGET_OBJECTS:open62541-object>)
-add_dependencies(server_firststeps open625451_amalgamation)
-target_link_libraries(server_firststeps ${LIBS})
-
 if(UA_ENABLE_NONSTANDARD_UDP)
   add_executable(server_udp server_udp.c $<TARGET_OBJECTS:open62541-object> ${PROJECT_SOURCE_DIR}/plugins/ua_network_udp.c)
   target_link_libraries(server_udp ${LIBS})
@@ -44,23 +78,13 @@ endif()
 add_executable(client client.c $<TARGET_OBJECTS:open62541-object>)
 target_link_libraries(client ${LIBS})
 
-add_executable(client_firststeps tutorials/client_firststeps.c $<TARGET_OBJECTS:open62541-object>)
-add_dependencies(client_firststeps open625451_amalgamation)
-target_link_libraries(client_firststeps ${LIBS})
-
 ####################
 # Feature Examples #
 ####################
 
-add_executable(server_variable server_variable.c $<TARGET_OBJECTS:open62541-object>)
-target_link_libraries(server_variable ${LIBS})
-
 add_executable(server_mainloop server_mainloop.c $<TARGET_OBJECTS:open62541-object>)
 target_link_libraries(server_mainloop ${LIBS})
 
-add_executable(server_datasource server_datasource.c $<TARGET_OBJECTS:open62541-object>)
-target_link_libraries(server_datasource ${LIBS})
-
 add_executable(server_instantiation server_instantiation.c $<TARGET_OBJECTS:open62541-object>)
 target_link_libraries(server_instantiation ${LIBS})
 
@@ -76,11 +100,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
 endif()
 
-if(UA_ENABLE_METHODCALLS)
-  add_executable(server_method server_method.c $<TARGET_OBJECTS:open62541-object>)
-  target_link_libraries(server_method ${LIBS})
-endif()
-
 if(UA_BUILD_SELFSIGNED_CERTIFICATE)
   find_package(OpenSSL REQUIRED)
   add_custom_command(OUTPUT server_cert.der ca.crt

+ 0 - 81
examples/server_datasource.c

@@ -1,81 +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>
-
-#ifdef UA_NO_AMALGAMATION
-# include "ua_types.h"
-# include "ua_server.h"
-# include "ua_config_standard.h"
-# include "ua_network_tcp.h"
-# include "ua_log_stdout.h"
-#else
-# include "open62541.h"
-#endif
-
-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 dateDataSource = (UA_DataSource) {
-        .handle = &myInteger, .read = readInteger, .write = writeInteger};
-    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, dateDataSource, NULL);
-
-    UA_StatusCode retval = UA_Server_run(server, &running);
-    UA_Server_delete(server);
-    nl.deleteMembers(&nl);
-
-    return (int)retval;
-}

+ 0 - 188
examples/server_method.c

@@ -1,188 +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>
-
-#ifdef UA_NO_AMALGAMATION
-#include "ua_types.h"
-#include "ua_server.h"
-#include "ua_config_standard.h"
-#include "ua_network_tcp.h"
-#include "ua_log_stdout.h"
-#else
-#include "open62541.h"
-#endif
-
-UA_Boolean running = true;
-UA_Logger logger = UA_Log_Stdout;
-
-
-/* Example 1 */
-static UA_StatusCode
-helloWorldMethod(void *handle, const UA_NodeId objectId,
-                 size_t inputSize, const UA_Variant *input,
-                 size_t outputSize, UA_Variant *output) {
-    UA_String *inputStr = (UA_String*)input->data;
-    UA_String tmp = UA_STRING_ALLOC("Hello ");
-    if(inputStr->length > 0) {
-        tmp.data = realloc(tmp.data, tmp.length + inputStr->length);
-        memcpy(&tmp.data[tmp.length], inputStr->data, inputStr->length);
-        tmp.length += inputStr->length;
-    }
-    UA_Variant_setScalarCopy(output, &tmp, &UA_TYPES[UA_TYPES_STRING]);
-    UA_String_deleteMembers(&tmp);
-    UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "Hello World was called");
-    return UA_STATUSCODE_GOOD;
-}
-
-/* Example 2 */
-static UA_StatusCode
-IncInt32ArrayValuesMethod(void *handle, const UA_NodeId objectId,
-                          size_t inputSize, const UA_Variant *input,
-                          size_t outputSize, UA_Variant *output) {
-    UA_Variant_setArrayCopy(output, input->data, 5, &UA_TYPES[UA_TYPES_INT32]);
-    for(size_t i = 0; i< input->arrayLength; i++)
-        ((UA_Int32*)output->data)[i] = ((UA_Int32*)input->data)[i] + 1;
-    return UA_STATUSCODE_GOOD;
-}
-
-
-/* Example 3 */
-static UA_StatusCode
-fooBarMethod(void *handle, const UA_NodeId objectId,
-             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 = realloc(tmp.data, tmp.length + inputStr->length);
-        memcpy(&tmp.data[tmp.length], inputStr->data, inputStr->length);
-        tmp.length += inputStr->length;
-    }
-    UA_Variant_setScalarCopy(output, &tmp, &UA_TYPES[UA_TYPES_STRING]);
-    UA_String_deleteMembers(&tmp);
-    UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "FooBar was called");
-    return UA_STATUSCODE_GOOD;
-}
-
-static void stopHandler(int sign) {
-    UA_LOG_INFO(logger, 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;
-}

+ 12 - 12
examples/server_repeated_job.c

@@ -13,20 +13,20 @@
 # include "open62541.h"
 #endif
 
-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) {
-    UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "received ctrl-c");
+    UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "received ctrl-c");
     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_ServerNetworkLayer nl = UA_ServerNetworkLayerTCP(UA_ConnectionConfig_standard, 16664);
@@ -37,10 +37,10 @@ int main(int argc, char** argv) {
     /* add a repeated job to the server */
     UA_Job job = {.type = UA_JOBTYPE_METHODCALL,
                   .job.methodCall = {.method = testCallback, .data = NULL} };
-    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);
     nl.deleteMembers(&nl);
-    return (int)retval;
+    return 0;
 }

+ 0 - 93
examples/server_variable.c

@@ -1,93 +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>
-
-#ifdef UA_NO_AMALGAMATION
-#include "ua_types.h"
-#include "ua_server.h"
-#include "ua_config_standard.h"
-#include "ua_network_tcp.h"
-#include "ua_log_stdout.h"
-#else
-#include "open62541.h"
-#endif
-
-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;
-}

+ 2 - 1
examples/tutorials/client_firststeps.c

@@ -2,12 +2,13 @@
  * 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
  * provides both a server- and clientside API, so creating a client is as easy as
  * creating a server. Copy the following into a file `myClient.c`: */
+
 #include <stdio.h>
 #include "open62541.h"
 

+ 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. */

+ 181 - 0
examples/tutorial_server_method.c

@@ -0,0 +1,181 @@
+/* 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,
+                         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,
+                            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;
+}

+ 144 - 0
examples/tutorial_server_variabletype.c

@@ -0,0 +1,144 @@
+/* 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. */