Parcourir la source

use single-file release in documentation; fold unused tutorial files into the documentation

Julius Pfrommer il y a 8 ans
Parent
commit
a895cf8030

+ 8 - 8
doc/building.rst

@@ -1,3 +1,5 @@
+.. _building:
+
 Building the Library
 ====================
 
@@ -10,20 +12,18 @@ Using the GCC compiler, the following calls build the library on Linux.
 
    gcc -std=c99 -fPIC -c open62541.c
    gcc -shared open62541.o -o libopen62541.so
-   
 
 Building with CMake on Ubuntu or Debian
 ---------------------------------------
 
 .. code-block:: bash
-   
+
    sudo apt-get install git build-essential gcc pkg-config cmake python
 
    # enable additional features
-   sudo apt-get install libexpat1-dev # for XML-encodingi
    sudo apt-get install liburcu-dev # for multithreading
    sudo apt-get install check # for unit tests
-   sudo apt-get install graphviz doxygen # for documentation generation
+   sudo apt-get install sphinx # for documentation generation
 
    cd open62541
    mkdir build
@@ -46,7 +46,7 @@ replace the compiler selection in the call to CMake.
   - Python 2.7.x (Python 3.x should work, too): https://python.org/downloads
   - CMake: http://www.cmake.org/cmake/resources/software.html
   - Microsoft Visual Studio 2015 Community Edition: https://www.visualstudio.com/products/visual-studio-community-vs
-    
+
 - Download the open62541 sources (using git or as a zipfile from github)
 - Open a command shell (cmd) and run
 
@@ -81,7 +81,7 @@ Building on OS X
    pip install sphinx # for documentation generation
 
 Follow Ubuntu instructions without the ``apt-get`` commands as these are taken care of by the above packages.
-   
+
 Build Options
 -------------
 
@@ -104,7 +104,7 @@ Further options that are not inherited from the CMake configuration are defined
 in ua_config.h. Usually there is no need to adjust them.
 
 UA_BUILD_* group
-~~~~~~~~~~~~~
+~~~~~~~~~~~~~~~~
 
 By default only the shared object libopen62541.so or the library open62541.dll
 and open62541.dll.a resp. open62541.lib are build. Additional artifacts can be
@@ -124,7 +124,7 @@ specified by the following options:
    Generate a self-signed certificate for the server (openSSL required)
 
 UA_ENABLE_* group
-~~~~~~~~~~~~~~
+~~~~~~~~~~~~~~~~~
 
 This group contains build options related to the supported OPC UA features.
 

+ 29 - 4
doc/in_a_nutshell.rst

@@ -1,3 +1,5 @@
+.. _introduction:
+
 Introduction to OPC UA
 ======================
 
@@ -67,9 +69,7 @@ OPC UA, a web of nodes
 ----------------------
 
 The information model in each OPC UA server is a web of interconnected nodes.
-There are eight different types of nodes. Depending on its type, every node
-contains different attributes. Some attributes, such as the *NodeId* (unique
-identifier) and the *BrowseName*, are contained in all node types.
+There are eight different types of nodes.
 
 +-----------------------+-------------------+
 | ReferenceTypeNode     | MethodNode        |
@@ -80,7 +80,32 @@ identifier) and the *BrowseName*, are contained in all node types.
 +-----------------------+-------------------+
 | VariableNode          | ViewNode          |
 +-----------------------+-------------------+
-                                                                                                            
+
+Depending on its type, every node contains different attributes. Some
+attributes, are contained in all node types:
+
++----------------+---------------+
+| Name           | Type          |
++================+===============+
+| nodeID         | NodeId        |
++----------------+---------------+
+| nodeClass      | NodeClass     |
++----------------+---------------+
+| browseName     | QualifiedName |
++----------------+---------------+
+| displayName    | LocalizedText |
++----------------+---------------+
+| description    | LocalizedText |
++----------------+---------------+
+| writeMask      | UInt32        |
++----------------+---------------+
+| userWriteMask  | UInt32        |
++----------------+---------------+
+| referencesSize | Int32         |
++----------------+---------------+
+| references     |ReferenceNode[]|
++----------------+---------------+
+
 Nodes are interconnected by directed reference-triples of the form ``(nodeid,
 referencetype, target-nodeid)``. Therefore an OPC UA information model is
 easiest imagined as a *web of nodes*. Reference types can be

+ 1 - 0
doc/internal.rst

@@ -2,6 +2,7 @@ Internals
 #########
 
 .. toctree::
+
    services
    nodestore
    types_generated

+ 62 - 220
doc/tutorial_client_firstSteps.rst

@@ -1,246 +1,88 @@
-First steps with open62541-client
-=================================
-
-In the previous :doc:`tutorial_server_firstSteps` tutorial, you should have gotten your build environment verified and created a first OPC UA server using the open62541 stack. The created server however doesn't do much yet and there is no client to interact with the server. We are going to remedy that in this tutorial by creating some nodes and variables.
-
-You should already have a basic server from the previous tutorial. open62541 provides both a server- and clientside API, so creating a client is equally as easy as creating a server. We are going to use dynamic linking (libopen62541.so) from now on, because our client and server will share a lot of code. Reusing the shared library will considerably reduce the overhead. To avoid confusion, remove the amalgated open62541.c/h files from your example directory.
-
-As a recap, your directory structure should now look like this::
- 
-  :myApp> rm *.o open62541.*
-  :myApp> cp ../open62541/build/open62541.h include/
-  :myApp> ln -s ../open62541/build/*so ./
-  :myApp> tree
-  .
-  ├── include
-  │   ├── logger_stdout.h
-  │   ├── networklayer_tcp.h
-  │   ├── networklayer_udp.h
-  │   ├── open62541.h
-  │   ├── ua_client.h
-  │   ├── ua_client_highlevel.h
-  │   ├── ua_config.h
-  │   ├── ua_config.h.in
-  │   ├── ua_connection.h
-  │   ├── ua_constants.h
-  │   ├── ua_job.h
-  │   ├── ua_log.h
-  │   ├── ua_nodeids.h
-  │   ├── ua_server_external_ns.h
-  │   ├── ua_server.h
-  │   ├── ua_transport_generated_encoding_binary.h
-  │   ├── ua_transport_generated.h
-  │   ├── ua_types_generated_encoding_binary.h
-  │   ├── ua_types_generated.h
-  │   └── ua_types.h
-  ├── libopen62541.so -> ../open62541/build/libopen62541.so
-  ├── myServer
-  └── myServer.c
-
-Note that I have linked the library into the folder to spare me the trouble of copying it every time I change/rebuild the stack.
-
-To create a really basic client, navigate back into the myApp folder from the previous tutorial and create a client:
+5. 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`:
 
 .. code-block:: c
 
-    #include <stdio.h>
-    
-    #include "ua_types.h"
-    #include "ua_server.h"
-    #include "networklayer_tcp.h"
-    #include "ua_config_standard.h"
-    
+    #include "open62541.h"
+
     int main(void) {
       UA_Client *client = UA_Client_new(UA_ClientConfig_standard);
       UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:16664");
       if(retval != UA_STATUSCODE_GOOD) {
-        UA_Client_delete(client);
-        return retval;
+          UA_Client_delete(client);
+          return retval;
       }
-    
+
       UA_Client_disconnect(client);
       UA_Client_delete(client);
       return 0;
     }
 
-Let's recompile both server and client - if you feel up to it, you can create a Makefile for this procedure. I will show a final command line compile example and ommit the compilation directives in future examples.::
-
-    :myApp> gcc -Wl,-rpath=./ -L./ -I ./include -o myClient myClient.c  -lopen62541
-
-
-Asserting success/failure
--------------------------
-
-Almost all functions of the open62541 API will return a ``UA_StatusCode``, which in the C world would be represented by a ``unsigned int``. OPC UA defines large number of good and bad return codes represented by this number. The constant UA_STATUSCODE_GOOD is defined as 0 in ``include/ua_constants.h`` along with many other return codes. It pays off to check the return code of your function calls, as we already did implicitly in the client.
-
-Minimalistic introduction to OPC UA nodes and node IDs
-------------------------------------------------------
-
-OPC UA nodespace model defines 9 standard attribute for every node:
+Compilation is very much similar to the server example.
 
-+---------------+----------------+
-| Type          | Name           |
-+===============+================+
-| NodeId        | nodeID         |
-+---------------+----------------+
-| NodeClass     | nodeClass      |
-+---------------+----------------+
-| QualifiedName | browseName     |
-+---------------+----------------+
-| LocalizedText | displayName    |
-+---------------+----------------+
-| LocalizedText | description    |
-+---------------+----------------+
-| UInt32        | writeMask      |
-+---------------+----------------+
-| UInt32        | userWriteMask  |
-+---------------+----------------+
-| Int32         | referencesSize |
-+---------------+----------------+
-|ReferenceNode[]| references     |
-+---------------+----------------+
+.. code-block:: bash
 
-Furthermore, there are different node types that are stored in NodeClass. 
-For different classes, nodes have additional properties.
+   $ gcc -std=c99 open6251.c myClient.c -o myClient
 
-In this tutorial we are interested in one of these types: "Variable". In this case a node will have an additional attribute called "value" which we are going to read.
+Reading a node attibute
+-----------------------
 
-Let us go on with node IDs. A node ID is a unique identifier in server's context. It is composed of two members:
-
-+-------------+-----------------+---------------------------+
-| Type        | Name            | Notes                     |
-+=============+=================+===========================+
-| UInt16      | namespaceIndex  |  Number of the namespace  |
-+-------------+-----------------+---------------------------+
-| Union       | identifier      |  One idenifier of the     |
-|             |  * String       |  listed types             |
-|             |  * Integer      |                           |
-|             |  * GUID         |                           |
-|             |  * ByteString   |                           |
-+-------------+-----------------+---------------------------+
-
-The first parameter is the number of node's namespace, the second one may be a numeric, a string or a GUID (Globally Unique ID) identifier. 
-
-Reading variable's node value
------------------------------
-
-In this example we are going to read node (n=0,i=2258), i.e. a node in namespace 0 with a numerical id 2258. This node is present in every server (since it is located in namespace 0) and contains server current time (encoded as UInt64).
-
-Let us extend the client with with an action reading node's value:
+In this example we are going to connect to the server from the second tutorial
+and read the value-attribute of the added variable node.
 
 .. code-block:: c
 
     #include <stdio.h>
-    
-    #include "ua_types.h"
-    #include "ua_server.h"
-    #include "networklayer_tcp.h"
-    #include "ua_config_standard.h"
-    #include <inttypes.h>
-    
-    int main(void) {
-      UA_Client *client = UA_Client_new(UA_ClientConfig_standard);
-      UA_StatusCode retval = UA_Client_connect(client,"opc.tcp://localhost:16664");
-      if(retval != UA_STATUSCODE_GOOD) {
+    #include "open62541.h"
+
+    int main(int argc, char *argv[])
+    {
+        /* create a client and connect */
+        UA_Client *client = UA_Client_new(UA_ClientConfig_standard);
+        UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:16664");
+        if(retval != UA_STATUSCODE_GOOD) {
+            UA_Client_delete(client);
+            return retval;
+        }
+
+        /* create a readrequest with one entry */
+        UA_ReadRequest req;
+        UA_ReadRequest_init(&req);
+        req.nodesToRead = UA_Array_new(1, &UA_TYPES[UA_TYPES_READVALUEID]);
+        req.nodesToReadSize = 1;
+
+        /* define the node and attribute to be read */
+        req.nodesToRead[0].nodeId = UA_NODEID_STRING_ALLOC(1, "the.answer");
+        req.nodesToRead[0].attributeId = UA_ATTRIBUTEID_VALUE;
+
+        /* call the service and print the result */
+        UA_ReadResponse resp = UA_Client_Service_read(client, req);
+        if(resp.responseHeader.serviceResult == UA_STATUSCODE_GOOD &&
+           resp.resultsSize > 0 && resp.results[0].hasValue &&
+           UA_Variant_isScalar(&resp.results[0].value) &&
+            resp.results[0].value.type == &UA_TYPES[UA_TYPES_INT32]) {
+            UA_Int32 *value = (UA_Int32*)resp.results[0].value.data;
+            printf("the value is: %i\n", *value);
+        }
+
+        UA_ReadRequest_deleteMembers(&req);
+        UA_ReadResponse_deleteMembers(&resp);
+        UA_Client_disconnect(client);
         UA_Client_delete(client);
-        return retval;
-      }
-      
-      //variable to store data
-      UA_DateTime raw_date = 0;
-      
-      UA_ReadRequest rReq;
-      UA_ReadRequest_init(&rReq);
-      rReq.nodesToRead = UA_Array_new(1, &UA_TYPES[UA_TYPES_READVALUEID]);
-      rReq.nodesToReadSize = 1;
-      rReq.nodesToRead[0].nodeId = UA_NODEID_NUMERIC(0, 2258);
-      rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_VALUE;
-      
-      UA_ReadResponse rResp = UA_Client_Service_read(client, rReq);
-      if(rResp.responseHeader.serviceResult == UA_STATUSCODE_GOOD &&
-        rResp.resultsSize > 0 && rResp.results[0].hasValue &&
-        UA_Variant_isScalar(&rResp.results[0].value) &&
-        rResp.results[0].value.type == &UA_TYPES[UA_TYPES_DATETIME]) {
-         raw_date = *(UA_DateTime*)rResp.results[0].value.data;
-         printf("raw date is: %" PRId64 "\n", raw_date);
-      }
-      
-      UA_ReadRequest_deleteMembers(&rReq);
-      UA_ReadResponse_deleteMembers(&rResp);
-      
-      UA_Client_disconnect(client);
-      UA_Client_delete(client);
-      return UA_STATUSCODE_GOOD;
+        return UA_STATUSCODE_GOOD;
     }
 
-You should see raw time in milliseconds since January 1, 1601 UTC midnight::
-
-    :myApp> ./myClient
-    :myApp> raw date is: 130856974061125520
-
-Firstly we constructed a read request "rReq", it contains 1 node's attribute we want to query for. The attribute is filled with the numeric id "UA_NODEID_NUMERIC(0, 2258)" and the attribute we are reading "UA_ATTRIBUTEID_VALUE". After the read request was sent, we can find the actual read value in the read response.
-
-As the last step for this tutorial, we are going to convert the raw date value into a well formatted string:
-
-.. code-block:: c
-
-    #include <stdio.h>
-    
-    #include "ua_types.h"
-    #include "ua_server.h"
-    #include "networklayer_tcp.h"
-    #include "ua_config_standard.h"
-    #include <inttypes.h>
-    
-    int main(void) {
-      UA_Client *client = UA_Client_new(UA_ClientConfig_standard);
-      UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:16664");
-      if(retval != UA_STATUSCODE_GOOD) {
-        UA_Client_delete(client);
-        return retval;
-      }
-      
-      //variables to store data
-      UA_DateTime raw_date = 0;
-      UA_String string_date;
-    
-      UA_ReadRequest rReq;
-      UA_ReadRequest_init(&rReq);
-      rReq.nodesToRead = UA_Array_new(1, &UA_TYPES[UA_TYPES_READVALUEID]);
-      rReq.nodesToReadSize = 1;
-      rReq.nodesToRead[0].nodeId = UA_NODEID_NUMERIC(0, 2258);
-      rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_VALUE;
-      
-      UA_ReadResponse rResp = UA_Client_Service_read(client, rReq);
-      if(rResp.responseHeader.serviceResult == UA_STATUSCODE_GOOD &&
-          rResp.resultsSize > 0 && rResp.results[0].hasValue &&
-          UA_Variant_isScalar(&rResp.results[0].value) &&
-          rResp.results[0].value.type == &UA_TYPES[UA_TYPES_DATETIME]) {
-        raw_date = *(UA_DateTime*)rResp.results[0].value.data;
-        printf("raw date is: %" PRId64 "\n", raw_date);
-        string_date = UA_DateTime_toString(raw_date);
-        printf("string date is: %.*s\n", (int)string_date.length, string_date.data);
-      }
-      
-      UA_ReadRequest_deleteMembers(&rReq);
-      UA_ReadResponse_deleteMembers(&rResp);
-      UA_String_deleteMembers(&string_date);
-      
-      UA_Client_disconnect(client);
-      UA_Client_delete(client);
-      return UA_STATUSCODE_GOOD;
-    }
-
-Note that this file can be found as "examples/client_firstSteps.c" in the repository.
-    
-Now you should see raw time and a formatted date::
-
-    :myApp> ./myClient
-    :myApp> raw date is: 130856981449041870
-            string date is: 09/02/2015 20:09:04.904.187.000
-
 Further tasks
 -------------
-* Try to connect to some other OPC UA server by changing "opc.tcp://localhost:16664" to an appropriate address (remember that the queried node is contained in any OPC UA server).
-* Display the value of the variable node (ns=1,i="the.answer") containing an "Int32" from the example server (which is built in :doc:`tutorial_server_firstSteps`). Note that the identifier of this node is a string type: use "UA_NODEID_STRING_ALLOC". The answer can be found in "examples/exampleClient.c".
-* Try to set the value of the variable node (ns=1,i="the.answer") containing an "Int32" from the example server (which is built in :doc:`tutorial_server_firstSteps`) using "UA_Client_write" function. The example server needs some more modifications, i.e., changing request types. The answer can be found in "examples/exampleClient.c".
+* Try to connect to some other OPC UA server by changing
+  "opc.tcp://localhost:16664" to an appropriate address (remember that the
+  queried node is contained in any OPC UA server).
+* Try to set the value of the variable node (ns=1,i="the.answer") containing an
+  "Int32" from the example server (which is built in
+  :doc:`tutorial_server_firstSteps`) using "UA_Client_write" function. The
+  example server needs some more modifications, i.e., changing request types.
+  The answer can be found in "examples/exampleClient.c".

+ 2 - 2
doc/tutorial_noderelations.rst

@@ -1,5 +1,5 @@
-Examining and interacting with node relations
-=============================================
+Generating an OPC UA Information Model from XML Descriptions
+============================================================
 
 In the past tutorials you have learned to compile the stack in various configurations, create/delete nodes and manage variables. The core of OPC UA is its data modelling capabilities, and you will invariably find yourself confronted to investigate these relations during runtime. This tutorial will show you how to interact with object and type hierarchies and how to create your own.
 

Fichier diff supprimé car celui-ci est trop grand
+ 0 - 202
doc/tutorial_nodes.rst


Fichier diff supprimé car celui-ci est trop grand
+ 0 - 266
doc/tutorial_nodescontents.rst


Fichier diff supprimé car celui-ci est trop grand
+ 42 - 345
doc/tutorial_server_firstSteps.rst


+ 83 - 5
doc/tutorial_server_method.rst

@@ -1,8 +1,86 @@
-Adding server-side methods
-======================
+3. Adding a server-side method
+==============================
 
-This tutorial demonstrates how to add method nodes to the server.
+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).
 
-Consider 'examples/server_method.c' in the repository. The examples are compiled if the Cmake option BUILD_EXAMPLE is turned on.
+.. code-block:: c
 
-Use an UA client, e.g., UaExpert to call the method (right-click on the method node -> call).
+    #include <signal.h>
+    #include <stdlib.h>
+    #include "open62541.h"
+
+    UA_Boolean running = true;
+    static void stopHandler(int sign) {
+        UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "received ctrl-c");
+        running = 0;
+    }
+
+    UA_Logger logger = UA_Log_Stdout;
+
+    static UA_StatusCode
+    helloWorldMethod(void *handle, const UA_NodeId objectId, size_t inputSize,
+                     const UA_Variant *input, size_t outputSize, UA_Variant *output) {
+            UA_String *inputStr = (UA_String*)input->data;
+            UA_String tmp = UA_STRING_ALLOC("Hello ");
+            if(inputStr->length > 0) {
+                tmp.data = realloc(tmp.data, tmp.length + inputStr->length);
+                memcpy(&tmp.data[tmp.length], inputStr->data, inputStr->length);
+                tmp.length += inputStr->length;
+            }
+            UA_Variant_setScalarCopy(output, &tmp, &UA_TYPES[UA_TYPES_STRING]);
+            UA_String_deleteMembers(&tmp);
+            UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "Hello World was called");
+            return UA_STATUSCODE_GOOD;
+    }
+
+    int main(int argc, char** argv) {
+        signal(SIGINT, stopHandler); /* catches ctrl-c */
+
+        /* initialize the server */
+        UA_ServerConfig config = UA_ServerConfig_standard;
+        UA_ServerNetworkLayer nl = UA_ServerNetworkLayerTCP(UA_ConnectionConfig_standard, 16664);
+        config.networkLayers = &nl;
+        config.networkLayersSize = 1;
+        UA_Server *server = UA_Server_new(config);
+
+        /* add the method node with the callback */
+        UA_Argument inputArguments;
+        UA_Argument_init(&inputArguments);
+        inputArguments.arrayDimensionsSize = 0;
+        inputArguments.arrayDimensions = NULL;
+        inputArguments.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
+        inputArguments.description = UA_LOCALIZEDTEXT("en_US", "A String");
+        inputArguments.name = UA_STRING("MyInput");
+        inputArguments.valueRank = -1;
+
+        UA_Argument outputArguments;
+        UA_Argument_init(&outputArguments);
+        outputArguments.arrayDimensionsSize = 0;
+        outputArguments.arrayDimensions = NULL;
+        outputArguments.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
+        outputArguments.description = UA_LOCALIZEDTEXT("en_US", "A String");
+        outputArguments.name = UA_STRING("MyOutput");
+        outputArguments.valueRank = -1;
+
+        UA_MethodAttributes helloAttr;
+        UA_MethodAttributes_init(&helloAttr);
+        helloAttr.description = UA_LOCALIZEDTEXT("en_US","Say `Hello World`");
+        helloAttr.displayName = UA_LOCALIZEDTEXT("en_US","Hello World");
+        helloAttr.executable = true;
+        helloAttr.userExecutable = true;
+        UA_Server_addMethodNode(server, UA_NODEID_NUMERIC(1,62541),
+                                UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+                                UA_NODEID_NUMERIC(0, UA_NS0ID_HASORDEREDCOMPONENT),
+                                UA_QUALIFIEDNAME(1, "hello world"), 
+                                helloAttr, &helloWorldMethod, NULL,
+                                1, &inputArguments, 1, &outputArguments, NULL);
+
+        /* run the server */
+        UA_Server_run(server, &running);
+        UA_Server_delete(server);
+        nl.deleteMembers(&nl);
+
+        return 0;
+    }

+ 106 - 43
doc/tutorial_server_variables.rst

@@ -1,59 +1,122 @@
-Adding nodes to a server and connecting nodes to user-defined values
-====================================================================
+.. role:: ccode(code)
+      :language: c
 
-This tutorial shows how to add variable nodes to a server and how these can be connected to user-defined values and callbacks.
+2. Adding variables to a server
+===============================
 
-Firstly, we need to introduce a concept of Variants. This is a data structure able to hold any datatype.
+This tutorial shows how to add variable nodes to a server and how these can be
+connected to a physical process in the background. Make sure to read the
+:ref:`introduction <introduction>` first.
 
-Variants
---------
-The datatype UA_Variant a belongs to the built-in datatypes of OPC UA and is used as a container type. A Variant can hold any other built-in scalar datatype (except Variants) or array built-in datatype (array of variants too). The variant is structured like this in open62541:
+This is the code for a server with a single variable node holding an integer. We
+will take this example to explain some of the fundamental concepts of open62541.
 
 .. code-block:: c
 
-	typedef struct {
-		const UA_DataType *type; ///< The nodeid of the datatype
-		enum {
-		    UA_VARIANT_DATA, ///< The data is "owned" by this variant (copied and deleted together)
-		    UA_VARIANT_DATA_NODELETE, /**< The data is "borrowed" by the variant and shall not be
-		                                   deleted at the end of this variant's lifecycle. It is not
-		                                   possible to overwrite borrowed data due to concurrent access.
-		                                   Use a custom datasource with a mutex. */
-		} storageType; ///< Shall the data be deleted together with the variant
-		UA_Int32  arrayLength;  ///< the number of elements in the data-pointer
-		void     *data; ///< points to the scalar or array data
-		UA_Int32  arrayDimensionsSize; ///< the number of dimensions the data-array has
-		UA_Int32 *arrayDimensions; ///< the length of each dimension of the data-array
-	} UA_Variant;
+    #include <signal.h>
+    #include "open62541.h"
+
+    UA_Boolean running = true;
+    static void stopHandler(int sign) {
+        running = false;
+    }
+
+    int main(int argc, char** argv) {
+        signal(SIGINT, stopHandler); /* catch ctrl-c */
+
+        UA_ServerConfig config = UA_ServerConfig_standard;
+        UA_ServerNetworkLayer nl = UA_ServerNetworkLayerTCP(UA_ConnectionConfig_standard, 16664);
+        config.networkLayers = &nl;
+        config.networkLayersSize = 1;
+        UA_Server *server = UA_Server_new(config);
+
+        /* 1) Define the attribute of the myInteger variable node */
+        UA_VariableAttributes attr;
+        UA_VariableAttributes_init(&attr);
+        UA_Int32 myInteger = 42;
+        UA_Variant_setScalar(&attr.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
+        attr.description = UA_LOCALIZEDTEXT("en_US","the answer");
+        attr.displayName = UA_LOCALIZEDTEXT("en_US","the answer");
+
+        /* 2) Add the variable node to the information model */
+        UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
+        UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer");
+        UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
+        UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
+        UA_StatusCode retval = UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId,
+                                                         parentReferenceNodeId, myIntegerName,
+                                                         UA_NODEID_NULL, attr, NULL, NULL);
+
+        if(retval == UA_STATUSCODE_GOOD)
+            UA_Server_run(server, &running);
+
+        UA_Server_delete(server);
+        nl.deleteMembers(&nl);
+
+        return retval;
+    }
+
+Variants and Datatypes
+----------------------
+
+The datatype *variant* belongs to the built-in datatypes of OPC UA and is used
+as a container type. A variant can hold any other datatype as a scalar (except
+Variant) or as an array. Array variants can additionally denote the
+dimensionality of the data (e.g. a 2x3 matrix) in an additional integer array.
+You can find the code that defines the variant datatype :ref:`here <variant>`.
+
+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.
+
+Since it is a bit involved to set variants by hand, there are four basic
+functions you should be aware of:
+
+  * **UA_Variant_setScalar** will set the contents of the variant to a pointer
+    to the object that you pass with the call. Make sure to never deallocate
+    that object while the variant exists!
+  * **UA_Variant_setScalarCopy** will copy the object pointed to into a new
+    object of the same type and attach that to the variant.
+  * **UA_Variant_setArray** will set the contents of the variant to be an array
+    and point to the exact pointer/object that you passed the call.
+  * **UA_Variant_setArrayCopy** will create a copy of the array passed with the
+    call.
+
+The equivalent code using allocations is as follows:
 
-The members of the struct are
-
-* type: a pointer to the vtable entry. It points onto the functions which handles the stored data i.e. encode/decode etc.
-
-* storageType:  used to declare who the owner of data is and to which lifecycle it belongs. Three different cases are possible:
-
- * UA_VARIANT_DATA: this is the simplest case. The data belongs to the variant, which means if the variant is deleted so does the data which is kept inside,
- 
- * UA_VARIANT_NODELETE: in this case user-defined data is inside of the variant, meaning that the data will not be automatically freed by the stack.
-
-* arrayLength: length of the array (-1 if a scalar is saved)
+.. code-block:: c
 
-* data: raw pointer to the saved vale or callback
+    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");
 
-* arrayDimensionsSize: size of arrayDimensions array
+    /* add the variable node here */
+    UA_VariableAttributes_deleteMembers(&attr); /* free the allocated memory */
 
-* arrayDimensions: dimensinos array in case the array is interpreted as a multi-dimensional construction, e.g., [5,5] for a 5x5 matrix
+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.
 
-Adding a variable node to the server that contains a user-defined variable
+Adding a variable node to the server that contains a user-defined callback
 --------------------------------------------------------------------------
 
-This simple case allows to 'inject' a pre-defined variable into a variable node. The variable is wrapped by a "UA_Variant" before being insterted into the node.
-
-Consider 'examples/server_variable.c' in the repository. The examples are compiled if the Cmake option UA_BUILD_EXAMPLE is turned on.
+The latter case allows to define callback functions that are executed on read or
+write of the node. In this case an "UA_DataSource" containing the respective
+callback pointer is intserted into the node.
 
-Adding a variable node to the server that contains a user-defined callback
---------------------------------------------------------------------------
+Consider ``examples/server_datasource.c`` in the repository. The examples are
+compiled if the Cmake option UA_BUILD_EXAMPLE is turned on.
 
-The latter case allows to define callback functions that are executed on read or write of the node. In this case an "UA_DataSource" containing the respective callback pointer is intserted into the node.
+Asserting success/failure
+-------------------------
 
-Consider 'examples/server_datasource.c' in the repository. The examples are compiled if the Cmake option UA_BUILD_EXAMPLE is turned on.
+Almost all functions of the open62541 API will return a `StatusCode` 32-bit
+integer. The actual statuscodes are defined :ref:`here <statuscodes>`. Normally,
+the functions should return `UA_STATUSCODE_GOOD`, which maps to the zero
+integer.

+ 1 - 61
doc/tutorials.rst

@@ -1,70 +1,10 @@
 Tutorials
 =========
 
-This section contains structured tutorials
-   
-**Tutorial 1: First steps with open62541-server**
-
-:doc:`tutorial_server_firstSteps`
-
-Contents:
-
-* Checking out the stack
-
-* Creating a minimal user-defined server
-
-* Working with amalgamated files
-
-* Compiling built-in server and client examples
-
-**Tutorial 2: First steps with open62541-client**
-
-:doc:`tutorial_client_firstSteps`
-
-Contents:
-
-* Checking out the stack
-
-* Creating a minimal client
-
-* Minimalistic introduction to OPC UA nodes and node IDs
-
-* Reading a variable
-
-* Introduction to strings
-
-**Tutorial 3: Adding nodes to a server and connecting nodes to user-defined values**
-
-:doc:`tutorial_server_variables`
-
-Contents:
-
-* Introduction to Variants
-
-* Adding user-defined nodes to a server
-
-* Connecting a node to a variable
-
-* Connecting a node to a callback function
-
-**Tutorial 4: Adding server-side methods**
-
-:doc:`tutorial_server_method`
-
-Contents:
-
-* Defining server-side method nodes
-
-**Tutorial 5: Nodeset handling**
-
-:doc:`tutorial_noderelations`
-
 .. toctree::
-   :maxdepth: 2
-   :hidden:
 
    tutorial_server_firstSteps
-   tutorial_client_firstSteps
    tutorial_server_variables
    tutorial_server_method
    tutorial_noderelations
+   tutorial_client_firstSteps

+ 4 - 3
examples/server.c

@@ -1,8 +1,9 @@
 /* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
  * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
-//to compile with single file releases:
-// * single-threaded: gcc -std=c99 server.c open62541.c -o server
-// * multi-threaded: gcc -std=c99 server.c open62541.c -o server -lurcu-cds -lurcu -lurcu-common -lpthread
+
+/* Compile with single file release:
+ * - single-threaded: gcc -std=c99 server.c open62541.c -o server
+ * - multi-threaded: gcc -std=c99 server.c open62541.c -o server -lurcu-cds -lurcu -lurcu-common -lpthread */
 
 #ifdef UA_NO_AMALGAMATION
 # include <time.h>

+ 18 - 6
include/ua_client.h

@@ -1,5 +1,4 @@
-/*
- * Copyright (C) 2014 the contributors as stated in the AUTHORS file
+/* Copyright (C) 2014-2016 the contributors as stated in the AUTHORS file
  *
  * This file is part of open62541. open62541 is free software: you can
  * redistribute it and/or modify it under the terms of the GNU Lesser General
@@ -10,8 +9,7 @@
  * open62541 is distributed in the hope that it will be useful, but WITHOUT ANY
  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
- * details.
- */
+ * details. */
 
 #ifndef UA_CLIENT_H_
 #define UA_CLIENT_H_
@@ -27,9 +25,22 @@ extern "C" {
 #include "ua_types_generated.h"
 
 /**
+ * .. _client:
+ *
  * Client
  * ======
  *
+ * The client implementation allows remote access to all OPC UA services. For
+ * convenience, some functionality has been wrapped in :ref:`high-level
+ * abstractions <client-highlevel>`.
+ *
+ * **However**: At this point, the client does not yet contain its own thread or
+ * event-driven main-loop. So the client will not perform any actions
+ * automatically in the background. This is especially relevant for
+ * subscriptions. The user will have to periodically call
+ * `UA_Client_Subscriptions_manuallySendPublishRequest`. See also :ref:`here
+ * <client-subscriptions>`.
+ *
  * Client Configuration
  * -------------------- */
 typedef UA_Connection (*UA_ConnectClientConnection)(UA_ConnectionConfig localConf,
@@ -122,11 +133,12 @@ UA_StatusCode UA_EXPORT UA_Client_disconnect(UA_Client *client);
 UA_StatusCode UA_EXPORT UA_Client_manuallyRenewSecureChannel(UA_Client *client);
 
 /**
+ * .. _client-services:
+ *
  * Raw Services
  * ------------
  * The raw OPC UA services are exposed to the client. But most of them time, it is better to use the
- * convenience functions from `ua_client_highlevel.h` that wrap the raw services. See the Section
- * :ref:`services` for a detailed description of each service. */
+ * convenience functions from ``ua_client_highlevel.h`` that wrap the raw services. */
 /* Don't use this function. Use the type versions below instead. */
 void UA_EXPORT
 __UA_Client_Service(UA_Client *client, const void *request, const UA_DataType *requestType,

+ 34 - 10
include/ua_client_highlevel.h

@@ -23,11 +23,24 @@ extern "C" {
 #include "ua_client.h"
 
 /**
+ * .. _client-highlevel:
+ *
  * Highlevel Client Functionality
  * ------------------------------
  * The following definitions are convenience functions making use of the
  * standard OPC UA services in the background.
  *
+ * The high level abstractions concetrate on getting the job done in a simple
+ * manner for the user. This is a less flexible way of handling the stack,
+ * because at many places sensible defaults are presumed; at the same time using
+ * these functions is the easiest way of implementing an OPC UA application, as
+ * you will not have to consider all the details that go into the OPC UA
+ * services. A concept of how nodes and datatypes are used are completely
+ * sufficient to use OPC UA with this layer.
+ *
+ * If more flexibility is needed, you can always achieve the same functionality
+ * using the raw :ref:`OPC UA services <client-services>`.
+ *
  * Read Attributes
  * ===============
  * The following functions can be used to retrieve a single node attribute. Use
@@ -147,7 +160,7 @@ UA_Client_writeNodeClassAttribute(UA_Client *client, const UA_NodeId nodeId, con
 static UA_INLINE UA_StatusCode
 UA_Client_writeBrowseNameAttribute(UA_Client *client, const UA_NodeId nodeId, const UA_QualifiedName *newBrowseName) {
     return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_BROWSENAME, newBrowseName, &UA_TYPES[UA_TYPES_QUALIFIEDNAME]); }
-    
+
 static UA_INLINE UA_StatusCode
 UA_Client_writeDisplayNameAttribute(UA_Client *client, const UA_NodeId nodeId, const UA_LocalizedText *newDisplayName) {
     return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_DISPLAYNAME, newDisplayName, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]); }
@@ -183,11 +196,11 @@ UA_Client_writeContainsNoLoopsAttribute(UA_Client *client, const UA_NodeId nodeI
 static UA_INLINE UA_StatusCode
 UA_Client_writeEventNotifierAttribute(UA_Client *client, const UA_NodeId nodeId, const UA_Byte *newEventNotifier) {
     return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_EVENTNOTIFIER, newEventNotifier, &UA_TYPES[UA_TYPES_BYTE]); }
-    
+
 static UA_INLINE UA_StatusCode
 UA_Client_writeValueAttribute(UA_Client *client, const UA_NodeId nodeId, const UA_Variant *newValue) {
     return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_VALUE, newValue, &UA_TYPES[UA_TYPES_VARIANT]); }
-                                     
+
 static UA_INLINE UA_StatusCode
 UA_Client_writeDataTypeAttribute(UA_Client *client, const UA_NodeId nodeId, const UA_NodeId *newDataType) {
     return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_DATATYPE, newDataType, &UA_TYPES[UA_TYPES_NODEID]); }
@@ -233,7 +246,10 @@ UA_Client_call(UA_Client *client, const UA_NodeId objectId, const UA_NodeId meth
 
 /**
  * Node Management
- * =============== */
+ * ===============
+ *
+ * See the section on :ref:`server-side node management <addnodes>`.
+ */
 UA_StatusCode UA_EXPORT
 UA_Client_addReference(UA_Client *client, const UA_NodeId sourceNodeId, const UA_NodeId referenceTypeId,
                        UA_Boolean isForward, const UA_String targetServerUri,
@@ -246,7 +262,7 @@ UA_Client_deleteReference(UA_Client *client, const UA_NodeId sourceNodeId, const
 
 UA_StatusCode UA_EXPORT
 UA_Client_deleteNode(UA_Client *client, const UA_NodeId nodeId, UA_Boolean deleteTargetReferences);
-    
+
 /* Don't call this function, use the typed versions */
 UA_StatusCode UA_EXPORT
 __UA_Client_addNode(UA_Client *client, const UA_NodeClass nodeClass,
@@ -336,8 +352,16 @@ UA_Client_addMethodNode(UA_Client *client, const UA_NodeId requestedNewNodeId,
                                outNewNodeId); }
 
 /**
+ * .. _client-subscriptions:
+ *
  * Subscriptions Handling
- * ====================== */
+ * ======================
+ *
+ * At this point, the client does not yet contain its own thread or event-driven
+ * main-loop. So the client will not perform any actions automatically in the
+ * background. This is especially relevant for subscriptions. The user will have
+ * to periodically call `UA_Client_Subscriptions_manuallySendPublishRequest`.
+ * See also :ref:`here <client-subscriptions>`. */
 #ifdef UA_ENABLE_SUBSCRIPTIONS
 
 typedef struct {
@@ -350,11 +374,11 @@ typedef struct {
 } UA_SubscriptionSettings;
 
 extern const UA_EXPORT UA_SubscriptionSettings UA_SubscriptionSettings_standard;
-    
+
 UA_StatusCode UA_EXPORT
 UA_Client_Subscriptions_new(UA_Client *client, UA_SubscriptionSettings settings,
                             UA_UInt32 *newSubscriptionId);
-    
+
 UA_StatusCode UA_EXPORT
 UA_Client_Subscriptions_remove(UA_Client *client, UA_UInt32 subscriptionId);
 
@@ -390,11 +414,11 @@ UA_Client_NamespaceGetIndex(UA_Client *client, UA_String *namespaceUri, UA_UInt1
 #ifndef HAVE_NODEITER_CALLBACK
 #define HAVE_NODEITER_CALLBACK
 /* Iterate over all nodes referenced by parentNodeId by calling the callback
-   function for each child node */                                                        
+   function for each child node */
 typedef UA_StatusCode (*UA_NodeIteratorCallback)(UA_NodeId childId, UA_Boolean isInverse,
                                                   UA_NodeId referenceTypeId, void *handle);
 #endif
- 
+
 UA_StatusCode UA_EXPORT
 UA_Client_forEachChildNodeCall(UA_Client *client, UA_NodeId parentNodeId,
                                UA_NodeIteratorCallback callback, void *handle) ;

+ 275 - 247
include/ua_server.h

@@ -188,6 +188,256 @@ UA_StatusCode UA_EXPORT UA_Server_removeRepeatedJob(UA_Server *server, UA_Guid j
 /* Add a new namespace to the server. Returns the index of the new namespace */
 UA_UInt16 UA_EXPORT UA_Server_addNamespace(UA_Server *server, const char* name);
 
+/**
+ * Reading / Writing Node Attributes
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ *
+ * The node attributes are read/written separately. The read/write functions do
+ * not require the construction of variants used in the service definition. This
+ * is handled internally.
+ *
+ * Reading Node Attributes
+ * ~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * The following attributes cannot be read, since the local "admin" user always has
+ * full rights.
+ *
+ * - UserWriteMask
+ * - UserAccessLevel
+ * - UserExecutable */
+/* Don't use this function. There are typed versions for every supported attribute. */
+UA_StatusCode UA_EXPORT
+__UA_Server_read(UA_Server *server, const UA_NodeId *nodeId,
+                 UA_AttributeId attributeId, void *v);
+
+static UA_INLINE UA_StatusCode
+UA_Server_readNodeId(UA_Server *server, const UA_NodeId nodeId,
+                     UA_NodeId *outNodeId) {
+    return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_NODEID, outNodeId); }
+
+static UA_INLINE UA_StatusCode
+UA_Server_readNodeClass(UA_Server *server, const UA_NodeId nodeId,
+                        UA_NodeClass *outNodeClass) {
+    return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_NODECLASS, outNodeClass); }
+
+static UA_INLINE UA_StatusCode
+UA_Server_readBrowseName(UA_Server *server, const UA_NodeId nodeId,
+                         UA_QualifiedName *outBrowseName) {
+    return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_BROWSENAME, outBrowseName); }
+
+static UA_INLINE UA_StatusCode
+UA_Server_readDisplayName(UA_Server *server, const UA_NodeId nodeId,
+                          UA_LocalizedText *outDisplayName) {
+    return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_DISPLAYNAME, outDisplayName); }
+
+static UA_INLINE UA_StatusCode
+UA_Server_readDescription(UA_Server *server, const UA_NodeId nodeId,
+                          UA_LocalizedText *outDescription) {
+    return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_DESCRIPTION, outDescription); }
+
+static UA_INLINE UA_StatusCode
+UA_Server_readWriteMask(UA_Server *server, const UA_NodeId nodeId,
+                        UA_UInt32 *outWriteMask) {
+    return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_WRITEMASK, outWriteMask); }
+
+static UA_INLINE UA_StatusCode
+UA_Server_readIsAbstract(UA_Server *server, const UA_NodeId nodeId,
+                         UA_Boolean *outIsAbstract) {
+    return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_ISABSTRACT, outIsAbstract); }
+
+static UA_INLINE UA_StatusCode
+UA_Server_readSymmetric(UA_Server *server, const UA_NodeId nodeId,
+                        UA_Boolean *outSymmetric) {
+    return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_SYMMETRIC, outSymmetric); }
+
+static UA_INLINE UA_StatusCode
+UA_Server_readInverseName(UA_Server *server, const UA_NodeId nodeId,
+                          UA_LocalizedText *outInverseName) {
+    return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_INVERSENAME, outInverseName); }
+
+static UA_INLINE UA_StatusCode
+UA_Server_readContainsNoLoop(UA_Server *server, const UA_NodeId nodeId,
+                             UA_Boolean *outContainsNoLoops) {
+    return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_CONTAINSNOLOOPS,
+                            outContainsNoLoops); }
+
+static UA_INLINE UA_StatusCode
+UA_Server_readEventNotifier(UA_Server *server, const UA_NodeId nodeId,
+                            UA_Byte *outEventNotifier) {
+    return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_EVENTNOTIFIER, outEventNotifier); }
+
+static UA_INLINE UA_StatusCode
+UA_Server_readValue(UA_Server *server, const UA_NodeId nodeId,
+                    UA_Variant *outValue) {
+    return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_VALUE, outValue); }
+
+static UA_INLINE UA_StatusCode
+UA_Server_readDataType(UA_Server *server, const UA_NodeId nodeId,
+                       UA_NodeId *outDataType) {
+    return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_DATATYPE, outDataType); }
+
+static UA_INLINE UA_StatusCode
+UA_Server_readValueRank(UA_Server *server, const UA_NodeId nodeId,
+                        UA_Int32 *outValueRank) {
+    return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_VALUERANK, outValueRank); }
+
+/* Returns a variant with an int32 array */
+static UA_INLINE UA_StatusCode
+UA_Server_readArrayDimensions(UA_Server *server, const UA_NodeId nodeId,
+                              UA_Variant *outArrayDimensions) {
+    return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_ARRAYDIMENSIONS,
+                            outArrayDimensions); }
+
+static UA_INLINE UA_StatusCode
+UA_Server_readAccessLevel(UA_Server *server, const UA_NodeId nodeId,
+                          UA_UInt32 *outAccessLevel) {
+    return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_ACCESSLEVEL, outAccessLevel); }
+
+static UA_INLINE UA_StatusCode
+UA_Server_readMinimumSamplingInterval(UA_Server *server, const UA_NodeId nodeId,
+                                      UA_Double *outMinimumSamplingInterval) {
+    return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL,
+                            outMinimumSamplingInterval); }
+
+static UA_INLINE UA_StatusCode
+UA_Server_readHistorizing(UA_Server *server, const UA_NodeId nodeId,
+                          UA_Boolean *outHistorizing) {
+    return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_HISTORIZING, outHistorizing); }
+
+static UA_INLINE UA_StatusCode
+UA_Server_readExecutable(UA_Server *server, const UA_NodeId nodeId,
+                         UA_Boolean *outExecutable) {
+    return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_EXECUTABLE, outExecutable); }
+
+/**
+ * Writing Node Attributes
+ * ~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * The following node attributes cannot be changed once a node has been created:
+ *
+ * - NodeClass
+ * - NodeId
+ * - Symmetric
+ * - ContainsNoLoop
+ *
+ * The following attributes cannot be written from the server, as they are
+ * specific to the different users:
+ *
+ * - UserWriteMask
+ * - UserAccessLevel
+ * - UserExecutable
+ *
+ * The following attributes are currently taken from the value variant and not
+ * stored separately in the nodes:
+ *
+ * - DataType
+ * - ValueRank
+ * - ArrayDimensions
+ *
+ * Historizing is currently unsupported */
+/* Don't use this function. There are typed versions with no additional overhead. */
+UA_StatusCode UA_EXPORT
+__UA_Server_write(UA_Server *server, const UA_NodeId *nodeId,
+                  const UA_AttributeId attributeId,
+                  const UA_DataType *type, const void *value);
+
+static UA_INLINE UA_StatusCode
+UA_Server_writeBrowseName(UA_Server *server, const UA_NodeId nodeId,
+                          const UA_QualifiedName browseName) {
+    return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_BROWSENAME,
+                             &UA_TYPES[UA_TYPES_QUALIFIEDNAME], &browseName); }
+
+static UA_INLINE UA_StatusCode
+UA_Server_writeDisplayName(UA_Server *server, const UA_NodeId nodeId,
+                           const UA_LocalizedText displayName) {
+    return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_DISPLAYNAME,
+                             &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], &displayName); }
+
+static UA_INLINE UA_StatusCode
+UA_Server_writeDescription(UA_Server *server, const UA_NodeId nodeId,
+                           const UA_LocalizedText description) {
+    return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_DESCRIPTION,
+                             &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], &description); }
+
+static UA_INLINE UA_StatusCode
+UA_Server_writeWriteMask(UA_Server *server, const UA_NodeId nodeId,
+                         const UA_UInt32 writeMask) {
+    return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_WRITEMASK,
+                             &UA_TYPES[UA_TYPES_UINT32], &writeMask); }
+
+static UA_INLINE UA_StatusCode
+UA_Server_writeIsAbstract(UA_Server *server, const UA_NodeId nodeId,
+                          const UA_Boolean isAbstract) {
+    return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_ISABSTRACT,
+                             &UA_TYPES[UA_TYPES_BOOLEAN], &isAbstract); }
+
+static UA_INLINE UA_StatusCode
+UA_Server_writeInverseName(UA_Server *server, const UA_NodeId nodeId,
+                           const UA_LocalizedText inverseName) {
+    return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_INVERSENAME,
+                             &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], &inverseName); }
+
+static UA_INLINE UA_StatusCode
+UA_Server_writeEventNotifier(UA_Server *server, const UA_NodeId nodeId,
+                             const UA_Byte eventNotifier) {
+    return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_EVENTNOTIFIER,
+                             &UA_TYPES[UA_TYPES_BYTE], &eventNotifier); }
+
+static UA_INLINE UA_StatusCode
+UA_Server_writeValue(UA_Server *server, const UA_NodeId nodeId,
+                     const UA_Variant value) {
+    return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_VALUE,
+                             &UA_TYPES[UA_TYPES_VARIANT], &value); }
+
+static UA_INLINE UA_StatusCode
+UA_Server_writeAccessLevel(UA_Server *server, const UA_NodeId nodeId,
+                           const UA_UInt32 accessLevel) {
+    return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_ACCESSLEVEL,
+                             &UA_TYPES[UA_TYPES_UINT32], &accessLevel); }
+
+static UA_INLINE UA_StatusCode
+UA_Server_writeMinimumSamplingInterval(UA_Server *server, const UA_NodeId nodeId,
+                                       const UA_Double miniumSamplingInterval) {
+    return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL,
+                             &UA_TYPES[UA_TYPES_DOUBLE], &miniumSamplingInterval); }
+
+static UA_INLINE UA_StatusCode
+UA_Server_writeExecutable(UA_Server *server, const UA_NodeId nodeId,
+                          const UA_Boolean executable) {
+    return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_EXECUTABLE,
+                             &UA_TYPES[UA_TYPES_BOOLEAN], &executable); }
+
+/**
+ * Browsing
+ * -------- */
+UA_BrowseResult UA_EXPORT
+UA_Server_browse(UA_Server *server, UA_UInt32 maxrefs, const UA_BrowseDescription *descr);
+
+UA_BrowseResult UA_EXPORT
+UA_Server_browseNext(UA_Server *server, UA_Boolean releaseContinuationPoint,
+                     const UA_ByteString *continuationPoint);
+
+#ifndef HAVE_NODEITER_CALLBACK
+#define HAVE_NODEITER_CALLBACK
+/* Iterate over all nodes referenced by parentNodeId by calling the callback
+ * function for each child node (in ifdef because GCC/CLANG handle include order
+ * differently) */
+typedef UA_StatusCode (*UA_NodeIteratorCallback)(UA_NodeId childId, UA_Boolean isInverse,
+                                                 UA_NodeId referenceTypeId, void *handle);
+#endif
+
+UA_StatusCode UA_EXPORT
+UA_Server_forEachChildNodeCall(UA_Server *server, UA_NodeId parentNodeId,
+                               UA_NodeIteratorCallback callback, void *handle);
+
+/**
+ * Method Call
+ * ----------- */
+#ifdef UA_ENABLE_METHODCALLS
+UA_CallMethodResult UA_EXPORT
+UA_Server_call(UA_Server *server, const UA_CallMethodRequest *request);
+#endif
+
 /**
  * Node Management
  * ---------------
@@ -208,9 +458,15 @@ UA_UInt16 UA_EXPORT UA_Server_addNamespace(UA_Server *server, const char* name);
  *
  * Data Source Callback
  * ~~~~~~~~~~~~~~~~~~~~
- * Datasources are the interface to local data providers. It is expected that
- * the read and release callbacks are implemented. The write callback can be set
- * to a null-pointer. */
+ *
+ * The server has a unique way of dealing with the content of variables. Instead
+ * of storing a variant attached to the variable node, the node can point to a
+ * function with a local data provider. Whenever the value attribute is read,
+ * the function will be called and asked to provide a UA_DataValue return value
+ * that contains the value content and additional timestamps.
+ *
+ * It is expected that the read callback is implemented. The write callback can
+ * be set to a null-pointer. */
 typedef struct {
     void *handle; /* A custom pointer to reuse the same datasource functions for
                      multiple sources */
@@ -298,13 +554,22 @@ UA_Server_setMethodNode_callback(UA_Server *server, const UA_NodeId methodNodeId
 #endif
 
 /**
+ * .. _addnodes:
+ *
  * Node Addition and Deletion
- * ^^^^^^^^^^^^^^^^^^^^^^^^^^ */
-UA_StatusCode UA_EXPORT
-UA_Server_deleteNode(UA_Server *server, const UA_NodeId nodeId, UA_Boolean deleteReferences);
-
-/**
- * The instantiation callback is used to track the addition of new nodes. It is
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ *
+ * When creating dynamic node instances at runtime, chances are that you will
+ * not care about the specific NodeId of the new node, as long as you can
+ * reference it later. When passing numeric NodeIds with a numeric identifier 0,
+ * the stack evaluates this as "select a randome free NodeId in that namespace".
+ * To find out which NodeId was actually assigned to the new node, you may pass
+ * a pointer `outNewNodeId`, which will (after a successfull node insertion)
+ * contain the nodeId of the new node. You may also pass NULL pointer if this
+ * result is not relevant. The namespace index for nodes you create should never
+ * be 0, as that index is reserved for OPC UA's self-description (namespace 0). */
+
+/* The instantiation callback is used to track the addition of new nodes. It is
  * also called for all sub-nodes contained in an object or variable type node
  * that is instantiated. */
 typedef struct {
@@ -430,214 +695,8 @@ UA_Server_addMethodNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
                         UA_NodeId *outNewNodeId);
 #endif
 
-/**
- * Write Node Attributes
- * ^^^^^^^^^^^^^^^^^^^^^
- * The following node attributes cannot be written
- *
- * - NodeClass
- * - NodeId
- * - Symmetric
- * - ContainsNoLoop
- *  
- * The following attributes cannot be written from the server, as there is no "user" in the server
- *
- * - UserWriteMask
- * - UserAccessLevel
- * - UserExecutable
- *
- * The following attributes are currently taken from the value variant:
- * TODO: Handle them independent from the variable, ensure that the implicit constraints hold
- *
- * - DataType
- * - ValueRank
- * - ArrayDimensions
- * 
- * - Historizing is currently unsupported */
-/* Don't use this function. There are typed versions with no additional overhead. */
-UA_StatusCode UA_EXPORT
-__UA_Server_write(UA_Server *server, const UA_NodeId *nodeId,
-                  const UA_AttributeId attributeId,
-                  const UA_DataType *type, const void *value);
-
-static UA_INLINE UA_StatusCode
-UA_Server_writeBrowseName(UA_Server *server, const UA_NodeId nodeId,
-                          const UA_QualifiedName browseName) {
-    return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_BROWSENAME,
-                             &UA_TYPES[UA_TYPES_QUALIFIEDNAME], &browseName); }
-
-static UA_INLINE UA_StatusCode
-UA_Server_writeDisplayName(UA_Server *server, const UA_NodeId nodeId,
-                           const UA_LocalizedText displayName) {
-    return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_DISPLAYNAME,
-                             &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], &displayName); }
-
-static UA_INLINE UA_StatusCode
-UA_Server_writeDescription(UA_Server *server, const UA_NodeId nodeId,
-                           const UA_LocalizedText description) {
-    return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_DESCRIPTION,
-                             &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], &description); }
-
-static UA_INLINE UA_StatusCode
-UA_Server_writeWriteMask(UA_Server *server, const UA_NodeId nodeId,
-                         const UA_UInt32 writeMask) {
-    return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_WRITEMASK,
-                             &UA_TYPES[UA_TYPES_UINT32], &writeMask); }
-
-static UA_INLINE UA_StatusCode
-UA_Server_writeIsAbstract(UA_Server *server, const UA_NodeId nodeId,
-                          const UA_Boolean isAbstract) {
-    return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_ISABSTRACT,
-                             &UA_TYPES[UA_TYPES_BOOLEAN], &isAbstract); }
-
-static UA_INLINE UA_StatusCode
-UA_Server_writeInverseName(UA_Server *server, const UA_NodeId nodeId,
-                           const UA_LocalizedText inverseName) {
-    return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_INVERSENAME,
-                             &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], &inverseName); }
-
-static UA_INLINE UA_StatusCode
-UA_Server_writeEventNotifier(UA_Server *server, const UA_NodeId nodeId,
-                             const UA_Byte eventNotifier) {
-    return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_EVENTNOTIFIER,
-                             &UA_TYPES[UA_TYPES_BYTE], &eventNotifier); }
-
-static UA_INLINE UA_StatusCode
-UA_Server_writeValue(UA_Server *server, const UA_NodeId nodeId,
-                     const UA_Variant value) {
-    return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_VALUE,
-                             &UA_TYPES[UA_TYPES_VARIANT], &value); }
-
-static UA_INLINE UA_StatusCode
-UA_Server_writeAccessLevel(UA_Server *server, const UA_NodeId nodeId,
-                           const UA_UInt32 accessLevel) {
-    return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_ACCESSLEVEL,
-                             &UA_TYPES[UA_TYPES_UINT32], &accessLevel); }
-
-static UA_INLINE UA_StatusCode
-UA_Server_writeMinimumSamplingInterval(UA_Server *server, const UA_NodeId nodeId,
-                                       const UA_Double miniumSamplingInterval) {
-    return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL,
-                             &UA_TYPES[UA_TYPES_DOUBLE], &miniumSamplingInterval); }
-
-static UA_INLINE UA_StatusCode
-UA_Server_writeExecutable(UA_Server *server, const UA_NodeId nodeId,
-                          const UA_Boolean executable) {
-    return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_EXECUTABLE,
-                             &UA_TYPES[UA_TYPES_BOOLEAN], &executable); }
-
-/**
- * Read Node Attributes
- * ^^^^^^^^^^^^^^^^^^^^
- * The following attributes cannot be read, since the local "admin" user always has
- * full rights.
- *
- * - UserWriteMask
- * - UserAccessLevel
- * - UserExecutable */
-/* Don't use this function. There are typed versions for every supported attribute. */
 UA_StatusCode UA_EXPORT
-__UA_Server_read(UA_Server *server, const UA_NodeId *nodeId,
-                 UA_AttributeId attributeId, void *v);
-  
-static UA_INLINE UA_StatusCode
-UA_Server_readNodeId(UA_Server *server, const UA_NodeId nodeId,
-                     UA_NodeId *outNodeId) {
-    return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_NODEID, outNodeId); }
-
-static UA_INLINE UA_StatusCode
-UA_Server_readNodeClass(UA_Server *server, const UA_NodeId nodeId,
-                        UA_NodeClass *outNodeClass) {
-    return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_NODECLASS, outNodeClass); }
-
-static UA_INLINE UA_StatusCode
-UA_Server_readBrowseName(UA_Server *server, const UA_NodeId nodeId,
-                         UA_QualifiedName *outBrowseName) {
-    return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_BROWSENAME, outBrowseName); }
-
-static UA_INLINE UA_StatusCode
-UA_Server_readDisplayName(UA_Server *server, const UA_NodeId nodeId,
-                          UA_LocalizedText *outDisplayName) {
-    return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_DISPLAYNAME, outDisplayName); }
-
-static UA_INLINE UA_StatusCode
-UA_Server_readDescription(UA_Server *server, const UA_NodeId nodeId,
-                          UA_LocalizedText *outDescription) {
-    return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_DESCRIPTION, outDescription); }
-
-static UA_INLINE UA_StatusCode
-UA_Server_readWriteMask(UA_Server *server, const UA_NodeId nodeId,
-                        UA_UInt32 *outWriteMask) {
-    return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_WRITEMASK, outWriteMask); }
-
-static UA_INLINE UA_StatusCode
-UA_Server_readIsAbstract(UA_Server *server, const UA_NodeId nodeId,
-                         UA_Boolean *outIsAbstract) {
-    return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_ISABSTRACT, outIsAbstract); }
-
-static UA_INLINE UA_StatusCode
-UA_Server_readSymmetric(UA_Server *server, const UA_NodeId nodeId,
-                        UA_Boolean *outSymmetric) {
-    return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_SYMMETRIC, outSymmetric); }
-
-static UA_INLINE UA_StatusCode
-UA_Server_readInverseName(UA_Server *server, const UA_NodeId nodeId,
-                          UA_LocalizedText *outInverseName) {
-    return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_INVERSENAME, outInverseName); }
-
-static UA_INLINE UA_StatusCode
-UA_Server_readContainsNoLoop(UA_Server *server, const UA_NodeId nodeId,
-                             UA_Boolean *outContainsNoLoops) {
-    return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_CONTAINSNOLOOPS,
-                            outContainsNoLoops); }
-
-static UA_INLINE UA_StatusCode
-UA_Server_readEventNotifier(UA_Server *server, const UA_NodeId nodeId,
-                            UA_Byte *outEventNotifier) {
-    return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_EVENTNOTIFIER, outEventNotifier); }
-
-static UA_INLINE UA_StatusCode
-UA_Server_readValue(UA_Server *server, const UA_NodeId nodeId,
-                    UA_Variant *outValue) {
-    return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_VALUE, outValue); }
-
-static UA_INLINE UA_StatusCode
-UA_Server_readDataType(UA_Server *server, const UA_NodeId nodeId,
-                       UA_NodeId *outDataType) {
-    return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_DATATYPE, outDataType); }
-
-static UA_INLINE UA_StatusCode
-UA_Server_readValueRank(UA_Server *server, const UA_NodeId nodeId,
-                        UA_Int32 *outValueRank) {
-    return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_VALUERANK, outValueRank); }
-
-/* Returns a variant with an int32 array */
-static UA_INLINE UA_StatusCode
-UA_Server_readArrayDimensions(UA_Server *server, const UA_NodeId nodeId,
-                              UA_Variant *outArrayDimensions) {
-    return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_ARRAYDIMENSIONS,
-                            outArrayDimensions); }
-
-static UA_INLINE UA_StatusCode
-UA_Server_readAccessLevel(UA_Server *server, const UA_NodeId nodeId,
-                          UA_UInt32 *outAccessLevel) {
-    return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_ACCESSLEVEL, outAccessLevel); }
-
-static UA_INLINE UA_StatusCode
-UA_Server_readMinimumSamplingInterval(UA_Server *server, const UA_NodeId nodeId,
-                                      UA_Double *outMinimumSamplingInterval) {
-    return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL,
-                            outMinimumSamplingInterval); }
-
-static UA_INLINE UA_StatusCode
-UA_Server_readHistorizing(UA_Server *server, const UA_NodeId nodeId,
-                          UA_Boolean *outHistorizing) {
-    return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_HISTORIZING, outHistorizing); }
-
-static UA_INLINE UA_StatusCode
-UA_Server_readExecutable(UA_Server *server, const UA_NodeId nodeId,
-                         UA_Boolean *outExecutable) {
-    return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_EXECUTABLE, outExecutable); }
+UA_Server_deleteNode(UA_Server *server, const UA_NodeId nodeId, UA_Boolean deleteReferences);
 
 /**
  * Reference Management
@@ -651,37 +710,6 @@ UA_Server_deleteReference(UA_Server *server, const UA_NodeId sourceNodeId,
                           const UA_NodeId referenceTypeId, UA_Boolean isForward,
                           const UA_ExpandedNodeId targetNodeId, UA_Boolean deleteBidirectional);
 
-/**
- * Browsing
- * -------- */
-UA_BrowseResult UA_EXPORT
-UA_Server_browse(UA_Server *server, UA_UInt32 maxrefs, const UA_BrowseDescription *descr);
-
-UA_BrowseResult UA_EXPORT
-UA_Server_browseNext(UA_Server *server, UA_Boolean releaseContinuationPoint,
-                     const UA_ByteString *continuationPoint);
-
-#ifndef HAVE_NODEITER_CALLBACK
-#define HAVE_NODEITER_CALLBACK
-/* Iterate over all nodes referenced by parentNodeId by calling the callback
- * function for each child node (in ifdef because GCC/CLANG handle include order
- * differently) */
-typedef UA_StatusCode (*UA_NodeIteratorCallback)(UA_NodeId childId, UA_Boolean isInverse,
-                                                 UA_NodeId referenceTypeId, void *handle);
-#endif
-
-UA_StatusCode UA_EXPORT
-UA_Server_forEachChildNodeCall(UA_Server *server, UA_NodeId parentNodeId,
-                               UA_NodeIteratorCallback callback, void *handle);
-
-/**
- * Method Call
- * ----------- */
-#ifdef UA_ENABLE_METHODCALLS
-UA_CallMethodResult UA_EXPORT
-UA_Server_call(UA_Server *server, const UA_CallMethodRequest *request);
-#endif
-
 #ifdef __cplusplus
 }
 #endif

+ 3 - 3
tools/travis/travis_linux_script.sh

@@ -79,8 +79,8 @@ else
     make -j8
     tar -pczf open62541-linux64.tar.gz ../../doc ../../server_cert.der ../LICENSE ../AUTHORS ../README.md server_static server client_static client libopen62541.so open62541.h open62541.c
     cp open62541-linux64.tar.gz ..
-    cp open62541.h .. #copy single file-release
-    cp open62541.c .. #copy single file-release
+    cp open62541.h .. # copy single file-release
+    cp open62541.c .. # copy single file-release
     cd .. && rm build -rf
 
     if [ "$CC" = "gcc" ]; then
@@ -104,7 +104,7 @@ else
     #this run inclides full examples and methodcalls
     echo "Debug build and unit tests (64 bit)"
     mkdir -p build && cd build
-    cmake -DCMAKE_BUILD_TYPE=Debug -DUA_BUILD_EXAMPLES=ON -DUA_ENABLE_METHODCALLS=ON -DUA_BUILD_DEMO_NODESET=ON -DUA_BUILD_UNIT_TESTS=ON -DUA_BUILD_EXAMPLESERVER=ON -DUA_ENABLE_COVERAGE=ON ..
+    cmake -DCMAKE_BUILD_TYPE=Debug -DUA_BUILD_EXAMPLES=ON -DUA_ENABLE_METHODCALLS=ON -DUA_BUILD_UNIT_TESTS=ON -DUA_BUILD_EXAMPLESERVER=ON -DUA_ENABLE_COVERAGE=ON ..
     make -j8 && make test ARGS="-V"
     echo "Run valgrind to see if the server leaks memory (just starting up and closing..)"
     (valgrind --leak-check=yes --error-exitcode=3 ./server & export pid=$!; sleep 2; kill -INT $pid; wait $pid);