Quellcode durchsuchen

Merge pull request #796 from open62541/variable_datavalue_docs

DataValue and independent datatype, valuerank, array dimensions in variable node
Julius Pfrommer vor 8 Jahren
Ursprung
Commit
524536563f

+ 9 - 6
CMakeLists.txt

@@ -204,26 +204,29 @@ set(lib_sources ${PROJECT_SOURCE_DIR}/src/ua_types.c
                 ${PROJECT_SOURCE_DIR}/src/ua_session.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_server.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_server_binary.c
-                ${PROJECT_SOURCE_DIR}/src/server/ua_nodes.c
+                ${PROJECT_SOURCE_DIR}/src/server/ua_server_utils.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_server_worker.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_securechannel_manager.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_session_manager.c
+                ${PROJECT_SOURCE_DIR}/src/server/ua_nodes.c
+                # nodestores
+                ${PROJECT_SOURCE_DIR}/src/server/ua_nodestore.c
+                ${PROJECT_SOURCE_DIR}/src/server/ua_nodestore_concurrent.c
+                # services
                 ${PROJECT_SOURCE_DIR}/src/server/ua_services_discovery.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_services_securechannel.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_services_session.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_services_attribute.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_services_nodemanagement.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_services_view.c
-                ${PROJECT_SOURCE_DIR}/src/client/ua_client.c
-                ${PROJECT_SOURCE_DIR}/src/client/ua_client_highlevel.c
-                # nodestores
-                ${PROJECT_SOURCE_DIR}/src/server/ua_nodestore.c
-                ${PROJECT_SOURCE_DIR}/src/server/ua_nodestore_concurrent.c
                 # method call
                 ${PROJECT_SOURCE_DIR}/src/server/ua_services_call.c
                 # subscriptions
                 ${PROJECT_SOURCE_DIR}/src/server/ua_subscription.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_services_subscription.c
+                # client
+                ${PROJECT_SOURCE_DIR}/src/client/ua_client.c
+                ${PROJECT_SOURCE_DIR}/src/client/ua_client_highlevel.c
                 ${PROJECT_SOURCE_DIR}/src/client/ua_client_highlevel_subscriptions.c
                 # plugins and dependencies
                 ${PROJECT_SOURCE_DIR}/plugins/ua_network_tcp.c

+ 5 - 2
doc/CMakeLists.txt

@@ -26,12 +26,14 @@ generate_rst(${PROJECT_SOURCE_DIR}/include/ua_log.h ${DOC_SRC_DIR}/log.rst)
 generate_rst(${PROJECT_SOURCE_DIR}/include/ua_connection.h ${DOC_SRC_DIR}/connection.rst)
 generate_rst(${PROJECT_SOURCE_DIR}/src/server/ua_services.h ${DOC_SRC_DIR}/services.rst)
 generate_rst(${PROJECT_SOURCE_DIR}/src/server/ua_nodestore.h ${DOC_SRC_DIR}/nodestore.rst)
+generate_rst(${PROJECT_SOURCE_DIR}/src/server/ua_nodes.h ${DOC_SRC_DIR}/information_modelling.rst)
 
 add_custom_target(doc_latex ${SPHINX_EXECUTABLE}
   -b latex "${DOC_SRC_DIR}" "${DOC_LATEX_DIR}"
   DEPENDS ${DOC_SRC_DIR}/types.rst ${DOC_SRC_DIR}/constants.rst ${DOC_SRC_DIR}/types_generated.rst
           ${DOC_SRC_DIR}/server.rst ${DOC_SRC_DIR}/client.rst ${DOC_SRC_DIR}/client_highlevel.rst
-          ${DOC_SRC_DIR}/log.rst ${DOC_SRC_DIR}/connection.rst ${DOC_SRC_DIR}/services.rst ${DOC_SRC_DIR}/nodestore.rst
+          ${DOC_SRC_DIR}/log.rst ${DOC_SRC_DIR}/connection.rst ${DOC_SRC_DIR}/services.rst
+          ${DOC_SRC_DIR}/nodestore.rst ${DOC_SRC_DIR}/information_modelling.rst
   COMMENT "Building LaTeX sources for documentation with Sphinx")
 add_dependencies(doc_latex open62541)
 
@@ -48,6 +50,7 @@ add_custom_target(doc ${SPHINX_EXECUTABLE}
   -b html "${DOC_SRC_DIR}" "${DOC_HTML_DIR}"
   DEPENDS ${DOC_SRC_DIR}/types.rst ${DOC_SRC_DIR}/constants.rst ${DOC_SRC_DIR}/types_generated.rst
           ${DOC_SRC_DIR}/server.rst ${DOC_SRC_DIR}/client.rst ${DOC_SRC_DIR}/client_highlevel.rst
-          ${DOC_SRC_DIR}/log.rst ${DOC_SRC_DIR}/connection.rst ${DOC_SRC_DIR}/services.rst ${DOC_SRC_DIR}/nodestore.rst
+          ${DOC_SRC_DIR}/log.rst ${DOC_SRC_DIR}/connection.rst ${DOC_SRC_DIR}/services.rst
+          ${DOC_SRC_DIR}/nodestore.rst ${DOC_SRC_DIR}/information_modelling.rst
   COMMENT "Building HTML documentation with Sphinx")
 add_dependencies(doc open62541)

Datei-Diff unterdrückt, da er zu groß ist
+ 164 - 21
doc/index.rst


+ 0 - 1
doc/internal.rst

@@ -3,7 +3,6 @@ Internals
 
 .. toctree::
 
-   services
    nodestore
    connection
    log

+ 1 - 0
examples/server.c

@@ -248,6 +248,7 @@ int main(int argc, char** argv) {
 
         UA_VariableAttributes attr;
         UA_VariableAttributes_init(&attr);
+        attr.valueRank = -2;
         char name[15];
 #if defined(_WIN32) && !defined(__MINGW32__)
         sprintf_s(name, 15, "%02d", type);

+ 2 - 0
include/ua_client.h

@@ -172,6 +172,7 @@ UA_Client_Service_write(UA_Client *client, const UA_WriteRequest request) {
 /**
  * Method Service Set
  * ^^^^^^^^^^^^^^^^^^ */
+#ifdef UA_ENABLE_METHODCALLS
 static UA_INLINE UA_CallResponse
 UA_Client_Service_call(UA_Client *client, const UA_CallRequest request) {
     UA_CallResponse response;
@@ -179,6 +180,7 @@ UA_Client_Service_call(UA_Client *client, const UA_CallRequest request) {
                         &response, &UA_TYPES[UA_TYPES_CALLRESPONSE]);
     return response;
 }
+#endif
 
 /**
  * NodeManagement Service Set

+ 3 - 2
include/ua_constants.h

@@ -1,5 +1,4 @@
-/*
- * Copyright (C) 2013-2015 the contributors as stated in the AUTHORS file
+/* Copyright (C) 2013-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
@@ -26,6 +25,8 @@ extern "C" {
  * This section contains numerical and string constants that are defined in the
  * OPC UA standard.
  *
+ * .. _attribute-id:
+ *
  * Attribute Id
  * ------------
  * Every node in an OPC UA information model contains attributes depending on

+ 23 - 8
include/ua_server.h

@@ -30,6 +30,8 @@ extern "C" {
 #include "ua_connection.h"
 
 /**
+ * .. _server:
+ *
  * Server
  * ======
  *
@@ -382,13 +384,6 @@ UA_Server_readExecutable(UA_Server *server, const UA_NodeId nodeId,
  * - 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 */
 /* Overwrite an attribute of a node. The specialized functions below provide a
  * more concise syntax.
@@ -466,6 +461,27 @@ UA_Server_writeValue(UA_Server *server, const UA_NodeId nodeId,
                              &UA_TYPES[UA_TYPES_VARIANT], &value);
 }
 
+static UA_INLINE UA_StatusCode
+UA_Server_writeDataType(UA_Server *server, const UA_NodeId nodeId,
+                        const UA_NodeId dataType) {
+    return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_DATATYPE,
+                             &UA_TYPES[UA_TYPES_NODEID], &dataType);
+}
+
+static UA_INLINE UA_StatusCode
+UA_Server_writeValueRank(UA_Server *server, const UA_NodeId nodeId,
+                         const UA_Int32 valueRank) {
+    return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_VALUERANK,
+                             &UA_TYPES[UA_TYPES_INT32], &valueRank);
+}
+
+static UA_INLINE UA_StatusCode
+UA_Server_writeArrayDimensions(UA_Server *server, const UA_NodeId nodeId,
+                               const UA_Variant arrayDimensions) {
+    return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_VALUE,
+                             &UA_TYPES[UA_TYPES_VARIANT], &arrayDimensions);
+}
+
 static UA_INLINE UA_StatusCode
 UA_Server_writeAccessLevel(UA_Server *server, const UA_NodeId nodeId,
                            const UA_UInt32 accessLevel) {
@@ -655,7 +671,6 @@ UA_Server_setMethodNode_callback(UA_Server *server, const UA_NodeId methodNodeId
  * 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. */

+ 200 - 168
include/ua_types.h

@@ -1,5 +1,4 @@
-/*
- * Copyright (C) 2013-2015 the contributors as stated in the AUTHORS file
+/* Copyright (C) 2013-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_TYPES_H_
 #define UA_TYPES_H_
@@ -29,35 +27,42 @@ extern "C" {
  * Data Types
  * ==========
  *
- * In open62541, all data types share the same basic API for creation, copying
- * and deletion. The header ua_types.h defines the builtin types. In addition,
- * we auto-generate ua_types_generated.h with additional types as well as the
- * following function definitions for all (builtin and generated) data types
- * ``T``.
- *
- * ``void T_init(T *ptr)``
- *   Initialize the data type. This is synonymous with zeroing out the memory,
- *   i.e. ``memset(dataptr, 0, sizeof(T))``.
- * ``T* T_new()``
- *   Allocate and return the memory for the data type. The memory is already
- *   initialized.
- * ``UA_StatusCode T_copy(const T *src, T *dst)``
- *   Copy the content of the data type. Returns ``UA_STATUSCODE_GOOD`` or
- *   ``UA_STATUSCODE_BADOUTOFMEMORY``.
- * ``void T_deleteMembers(T *ptr)``
- *   Delete the dynamically allocated content of the data type and perform a
- *   ``T_init`` to reset the type.
- * ``void T_delete(T *ptr)``
- *   Delete the content of the data type and the memory for the data type itself.
+ * The OPC UA protocol defines 25 builtin data types and three ways of combining
+ * them into higher-order types: arrays, structures and unions. When the type of
+ * a value can be decided at runtime, it is be wrapped into a :ref:`variant`
+ * together with a description of the content. This metadata is used to decode
+ * the value on the receiving end. In open62541, the builtin data types are
+ * defined manually. Higher-order data types are generated from standard XML
+ * definitions. Their exact definitions can be looked up at
+ * https://opcfoundation.org/UA/schemas/Opc.Ua.Types.bsd.xml.
+ *
+ * Note that arrays can only be part of a scalar data type and never constitute
+ * a data type on their own. Also, open62541 does not implement unions so far.
+ * They are a recent addition to the protocol (since OPC UA v1.03). And so far,
+ * no service definition makes of unions in the request / response message
+ * definition. Instead, :ref:`Variants <variant>` are used where several member
+ * value types are possible.
  *
- * OPC UA defines 25 builtin data types. All other data types are combinations
- * of the 25 builtin data types. */
+ * All data types ``T`` (builtin and generated) share the same basic API for
+ * creation, copying and deletion:
+ *
+ * - ``void T_init(T *ptr)``: Initialize the data type. This is synonymous with
+ *   zeroing out the memory, i.e. ``memset(ptr, 0, sizeof(T))``.
+ * - ``T* T_new()``: Allocate and return the memory for the data type. The
+ *   memory is already initialized.
+ * - ``UA_StatusCode T_copy(const T *src, T *dst)``: Copy the content of the
+ *   data type. Returns ``UA_STATUSCODE_GOOD`` or
+ *   ``UA_STATUSCODE_BADOUTOFMEMORY``.
+ * - ``void T_deleteMembers(T *ptr)``: Delete the dynamically allocated content
+ *   of the data type and perform a ``T_init`` to reset the type.
+ * - ``void T_delete(T *ptr)``: Delete the content of the data type and the
+ *   memory for the data type itself. */
 
 #define UA_BUILTIN_TYPES_COUNT 25U
 
 /**
- * Builtin Types Part 1
- * --------------------
+ * Builtin Types
+ * -------------
  *
  * Boolean
  * ^^^^^^^
@@ -71,65 +76,65 @@ typedef bool UA_Boolean;
  * ^^^^^
  * An integer value between -128 and 127. */
 typedef int8_t UA_SByte;
-#define UA_SBYTE_MAX 127
 #define UA_SBYTE_MIN (-128)
+#define UA_SBYTE_MAX 127
 
 /**
  * Byte
  * ^^^^
  * An integer value between 0 and 256. */
 typedef uint8_t UA_Byte;
-#define UA_BYTE_MAX 256
 #define UA_BYTE_MIN 0
+#define UA_BYTE_MAX 256
 
 /**
  * Int16
  * ^^^^^
  * An integer value between -32 768 and 32 767. */
 typedef int16_t UA_Int16;
-#define UA_INT16_MAX 32767
 #define UA_INT16_MIN (-32768)
+#define UA_INT16_MAX 32767
 
 /**
  * UInt16
  * ^^^^^^
  * An integer value between 0 and 65 535. */
 typedef uint16_t UA_UInt16;
-#define UA_UINT16_MAX 65535
 #define UA_UINT16_MIN 0
+#define UA_UINT16_MAX 65535
 
 /**
  * Int32
  * ^^^^^
  * An integer value between -2 147 483 648 and 2 147 483 647. */
 typedef int32_t UA_Int32;
-#define UA_INT32_MAX 2147483647
 #define UA_INT32_MIN (-2147483648)
+#define UA_INT32_MAX 2147483647
 
 /**
  * UInt32
  * ^^^^^^
  * An integer value between 0 and 4 294 967 295. */
 typedef uint32_t UA_UInt32;
-#define UA_UINT32_MAX 4294967295
 #define UA_UINT32_MIN 0
+#define UA_UINT32_MAX 4294967295
 
 /**
  * Int64
  * ^^^^^
- * An integer value between -10 223 372 036 854 775 808 and
+ * An integer value between -9 223 372 036 854 775 808 and
  * 9 223 372 036 854 775 807. */
 typedef int64_t UA_Int64;
-#define UA_INT64_MAX (int64_t)9223372036854775807
 #define UA_INT64_MIN ((int64_t)-9223372036854775808)
+#define UA_INT64_MAX (int64_t)9223372036854775807
 
 /**
  * UInt64
  * ^^^^^^
  * An integer value between 0 and 18 446 744 073 709 551 615. */
 typedef uint64_t UA_UInt64;
-#define UA_UINT64_MAX (int64_t)18446744073709551615
 #define UA_UINT64_MIN (int64_t)0
+#define UA_UINT64_MAX (int64_t)18446744073709551615
 
 /**
  * Float
@@ -154,76 +159,6 @@ typedef double UA_Double;
 typedef uint32_t UA_StatusCode;
 
 /**
- * Array handling
- * --------------
- * In OPC UA, arrays can have a length of zero or more with the usual meaning.
- * In addition, arrays can be undefined. Then, they don't even have a length. In
- * the binary encoding, this is indicated by an array of length -1.
- *
- * In open62541 however, we use ``size_t`` for array lengths. An undefined array
- * has length 0 and the data pointer is NULL. An array of length 0 also has
- * length 0 but points to a sentinel memory address. */
-#define UA_EMPTY_ARRAY_SENTINEL ((void*)0x01)
-
-/** Forward Declaration of UA_DataType. See Section `Generic Type Handling`_
-    for details. */
-struct UA_DataType;
-typedef struct UA_DataType UA_DataType;
-
-/** The following functions are used for handling arrays of any data type. */
-
-/* Allocates and initializes an array of variables of a specific type
- *
- * @param size The requested array length
- * @param type The datatype description
- * @return Returns the memory location of the variable or (void*)0 if no memory
-           could be allocated */
-void UA_EXPORT * UA_Array_new(size_t size, const UA_DataType *type) UA_FUNC_ATTR_MALLOC;
-
-/* Allocates and copies an array
- *
- * @param src The memory location of the source array
- * @param size The size of the array
- * @param dst The location of the pointer to the new array
- * @param type The datatype of the array members
- * @return Returns UA_STATUSCODE_GOOD or UA_STATUSCODE_BADOUTOFMEMORY */
-UA_StatusCode UA_EXPORT
-UA_Array_copy(const void *src, size_t size, void **dst,
-              const UA_DataType *type) UA_FUNC_ATTR_WARN_UNUSED_RESULT;
-
-/* Deletes an array.
- *
- * @param p The memory location of the array
- * @param size The size of the array
- * @param type The datatype of the array members */
-void UA_EXPORT UA_Array_delete(void *p, size_t size, const UA_DataType *type);
-
-/**
- * .. _numericrange:
- *
- * NumericRange
- * ^^^^^^^^^^^^
- *
- * NumericRanges are used to indicate subsets of a (multidimensional) variant
- * array. NumericRange has no official type structure in the standard. On the
- * wire, it only exists as an encoded string, such as "1:2,0:3,5". The colon
- * separates min/max index and the comma separates dimensions. A single value
- * indicates a range with a single element (min==max). */
-
-typedef struct {
-    UA_UInt32 min;
-    UA_UInt32 max;
-} UA_NumericRangeDimension;
-    
-typedef struct {
-    size_t dimensionsSize;
-    UA_NumericRangeDimension *dimensions;
-} UA_NumericRange;
-
-/**
- * Builtin Types, Part 2
- * ---------------------
- *
  * String
  * ^^^^^^
  * A sequence of Unicode characters. Strings are just an array of UA_Byte. */
@@ -342,6 +277,8 @@ UA_BYTESTRING_ALLOC(const char *chars) {
 typedef UA_String UA_XmlElement;
 
 /**
+ * .. _nodeid:
+ *
  * NodeId
  * ^^^^^^
  * An identifier for a node in the address space of an OPC UA Server. */
@@ -462,6 +399,8 @@ UA_EXPANDEDNODEID_BYTESTRING_ALLOC(UA_UInt16 nsIndex, const char *chars) {
 }
 
 /**
+ * .. _qualifiedname:
+ *
  * QualifiedName
  * ^^^^^^^^^^^^^
  * A name qualified by a namespace. */
@@ -508,63 +447,52 @@ UA_LOCALIZEDTEXT_ALLOC(const char *locale, const char *text) {
     lt.text = UA_STRING_ALLOC(text); return lt;
 }
 
-/**
- * ExtensionObject
- * ^^^^^^^^^^^^^^^
- * ExtensionObjects may contain scalars of any data type. Even those that are
- * unknown to the receiver. See the Section `Generic Type Handling`_ on how
- * types are described. An ExtensionObject always contains the NodeId of the
- * Data Type. If the data cannot be decoded, we keep the encoded string and the
- * NodeId. */
-typedef struct {
-    enum {
-        UA_EXTENSIONOBJECT_ENCODED_NOBODY     = 0,
-        UA_EXTENSIONOBJECT_ENCODED_BYTESTRING = 1,
-        UA_EXTENSIONOBJECT_ENCODED_XML        = 2,
-        UA_EXTENSIONOBJECT_DECODED            = 3,
-        UA_EXTENSIONOBJECT_DECODED_NODELETE   = 4 /* Don't delete the content
-                                                     together with the
-                                                     ExtensionObject */
-    } encoding;
-    union {
-        struct {
-            UA_NodeId typeId;   /* The nodeid of the datatype */
-            UA_ByteString body; /* The bytestring of the encoded data */
-        } encoded;
-        struct {
-            const UA_DataType *type;
-            void *data;
-        } decoded;
-    } content;
-} UA_ExtensionObject;
-
 /**
  * .. _variant:
  *
  * Variant
  * ^^^^^^^
- * Variants may contain data of any type. See the Section `Generic Type
- * Handling`_ on how types are described. If the data is not of one of the 25
- * builtin types, it will be encoded as an `ExtensionObject`_ on the wire. (The
- * standard says that a variant is a union of the built-in types. open62541
- * generalizes this to any data type by transparently de- and encoding
- * ExtensionObjects in the background. If the decoding fails, the variant
- * contains the original ExtensionObject.)
- *
- * Variants can contain a single scalar or an array. For details on the handling
- * of arrays, see the Section `Array Handling`_. Array variants can have an
- * additional dimensionality (matrix, 3-tensor, ...) defined in an array of
- * dimension sizes. Higher rank dimensions are serialized first.
- *
- * The differentiation between variants containing a scalar, an array or no data
- * is as follows:
- *
- * - arrayLength == 0 && data == NULL: no existing data
- * - arrayLength == 0 && data == UA_EMPTY_ARRAY_SENTINEL: array of length 0
- * - arrayLength == 0 && data > UA_EMPTY_ARRAY_SENTINEL: scalar value
- * - arrayLength > 0: array of the given length */
+ *
+ * Variants may contain values of any type together with a description of the
+ * content. See the section on :ref:`generic-types` on how types are described.
+ * The standard mandates that variants contain built-in data types only. If the
+ * value is not of a builtin type, it is wrapped into an :ref:`extensionobject`.
+ * open62541 hides this wrapping transparently in the encoding layer. If the
+ * data type is unknown to the receiver, the variant contains the original
+ * ExtensionObject in binary or XML encoding.
+ *
+ * Variants may contain a scalar value or an array. For details on the handling
+ * of arrays, see the section on :ref:`array-handling`. Array variants can have
+ * an additional dimensionality (matrix, 3-tensor, ...) defined in an array of
+ * dimension lengths. The actual values are kept in an array of dimensions one.
+ * For users who work with higher-dimensions arrays directly, keep in mind that
+ * dimensions of higher rank are serialized first (the highest rank dimension
+ * has stride 1 and elements follow each other directly). Usually it is simplest
+ * to interact with higher-dimensional arrays via ``UA_NumericRange``
+ * descriptions (see :ref:`array-handling`).
+ *
+ * To differentiate between scalar / array variants, the following definition is
+ * used. ``UA_Variant_isScalar`` provides simplified access to these checks.
+ *
+ * - ``arrayLength == 0 && data == NULL``: undefined array of length -1
+ * - ``arrayLength == 0 && data == UA_EMPTY_ARRAY_SENTINEL``: array of length 0
+ * - ``arrayLength == 0 && data > UA_EMPTY_ARRAY_SENTINEL``: scalar value
+ * - ``arrayLength > 0``: array of the given length
+ *
+ * Variants can also be *empty*. Then, the pointer to the type description is
+ * ``NULL``. */
+/* Forward declaration. See the section on Generic Type Handling */
+struct UA_DataType;
+typedef struct UA_DataType UA_DataType;
+
+/* Forward declaration. See the section on Array Handling */
+struct UA_NumericRange;
+typedef struct UA_NumericRange UA_NumericRange;
+
+#define UA_EMPTY_ARRAY_SENTINEL ((void*)0x01)
+
 typedef struct {
-    const UA_DataType *type; /* The data type description */
+    const UA_DataType *type;      /* The data type description */
     enum {
         UA_VARIANT_DATA,          /* The data has the same lifecycle as the
                                      variant */
@@ -572,10 +500,10 @@ typedef struct {
                                      shall not be deleted at the end of the
                                      variant's lifecycle. */
     } storageType;
-    size_t arrayLength;         /* The number of elements in the data array */
-    void *data;                 /* Points to the scalar or array data */
-    size_t arrayDimensionsSize; /* The number of dimensions the data-array has */
-    UA_Int32 *arrayDimensions;  /* The length of each dimension */
+    size_t arrayLength;           /* The number of elements in the data array */
+    void *data;                   /* Points to the scalar or array data */
+    size_t arrayDimensionsSize;   /* The number of dimensions */
+    UA_UInt32 *arrayDimensions;   /* The length of each dimension */
 } UA_Variant;
 
 /* Returns true if the variant contains a scalar value. Note that empty variants
@@ -668,6 +596,40 @@ UA_Variant_setRangeCopy(UA_Variant *v, const void *array,
                         size_t arraySize, const UA_NumericRange range);
 
 /**
+ * .. _extensionobject:
+ *
+ * ExtensionObject
+ * ^^^^^^^^^^^^^^^
+ *
+ * ExtensionObjects may contain scalars of any data type. Even those that are
+ * unknown to the receiver. See the section on :ref:`generic-types` on how types
+ * are described. If the received data type is unkown, the encoded string and
+ * target NodeId is stored instead of the decoded value. */
+typedef struct {
+    enum {
+        UA_EXTENSIONOBJECT_ENCODED_NOBODY     = 0,
+        UA_EXTENSIONOBJECT_ENCODED_BYTESTRING = 1,
+        UA_EXTENSIONOBJECT_ENCODED_XML        = 2,
+        UA_EXTENSIONOBJECT_DECODED            = 3,
+        UA_EXTENSIONOBJECT_DECODED_NODELETE   = 4 /* Don't delete the content
+                                                     together with the
+                                                     ExtensionObject */
+    } encoding;
+    union {
+        struct {
+            UA_NodeId typeId;   /* The nodeid of the datatype */
+            UA_ByteString body; /* The bytestring of the encoded data */
+        } encoded;
+        struct {
+            const UA_DataType *type;
+            void *data;
+        } decoded;
+    } content;
+} UA_ExtensionObject;
+
+/**
+ * .. _datavalue:
+ *
  * DataValue
  * ^^^^^^^^^
  * A data value with an associated status code and timestamps. */
@@ -709,6 +671,8 @@ typedef struct UA_DiagnosticInfo {
 } UA_DiagnosticInfo;
 
 /**
+ * .. _generic-types:
+ *
  * Generic Type Handling
  * ---------------------
  * The builtin types can be combined to data structures. All information about a
@@ -729,8 +693,8 @@ typedef struct {
     UA_Boolean namespaceZero : 1; /* The type of the member is defined in
                                      namespace zero. In this implementation,
                                      types from custom namespace may contain
-                                     members from the same namespace or ns0
-                                     only.*/
+                                     members from the same namespace or
+                                     namespace zero only.*/
     UA_Boolean isArray       : 1; /* The member is an array */
 } UA_DataTypeMember;
 
@@ -748,8 +712,8 @@ struct UA_DataType {
                                     pointers */
     UA_Boolean overlayable  : 1; /* The type has the identical memory layout in
                                     memory and on the binary stream. */
-    //UA_UInt16  xmlEncodingId;    /* NodeId of datatype when encoded as XML */
-    UA_UInt16  binaryEncodingId;    /* NodeId of datatype when encoded as binary */
+    UA_UInt16  binaryEncodingId; /* NodeId of datatype when encoded as binary */
+    //UA_UInt16  xmlEncodingId;  /* NodeId of datatype when encoded as XML */
     UA_DataTypeMember *members;
 };
 
@@ -758,8 +722,8 @@ struct UA_DataType {
 /* Allocates and initializes a variable of type dataType
  *
  * @param type The datatype description
- * @return Returns the memory location of the variable or (void*)0 if no
- *         memory is available */
+ * @return Returns the memory location of the variable or NULL if no
+ *         memory could be allocated */
 void UA_EXPORT * UA_new(const UA_DataType *type) UA_FUNC_ATTR_MALLOC;
 
 /* Initializes a variable to default values
@@ -797,6 +761,65 @@ void UA_EXPORT UA_deleteMembers(void *p, const UA_DataType *type);
  * @param type The datatype description of the variable */
 void UA_EXPORT UA_delete(void *p, const UA_DataType *type);
 
+/**
+ * .. _array-handling:
+ *
+ * Array handling
+ * --------------
+ * In OPC UA, arrays can have a length of zero or more with the usual meaning.
+ * In addition, arrays can be undefined. Then, they don't even have a length. In
+ * the binary encoding, this is indicated by an array of length -1.
+ *
+ * In open62541 however, we use ``size_t`` for array lengths. An undefined array
+ * has length 0 and the data pointer is ``NULL``. An array of length 0 also has
+ * length 0 but a data pointer ``UA_EMPTY_ARRAY_SENTINEL``. */
+/* Allocates and initializes an array of variables of a specific type
+ *
+ * @param size The requested array length
+ * @param type The datatype description
+ * @return Returns the memory location of the variable or NULL if no memory
+           could be allocated */
+void UA_EXPORT * UA_Array_new(size_t size, const UA_DataType *type) UA_FUNC_ATTR_MALLOC;
+
+/* Allocates and copies an array
+ *
+ * @param src The memory location of the source array
+ * @param size The size of the array
+ * @param dst The location of the pointer to the new array
+ * @param type The datatype of the array members
+ * @return Returns UA_STATUSCODE_GOOD or UA_STATUSCODE_BADOUTOFMEMORY */
+UA_StatusCode UA_EXPORT
+UA_Array_copy(const void *src, size_t size, void **dst,
+              const UA_DataType *type) UA_FUNC_ATTR_WARN_UNUSED_RESULT;
+
+/* Deletes an array.
+ *
+ * @param p The memory location of the array
+ * @param size The size of the array
+ * @param type The datatype of the array members */
+void UA_EXPORT UA_Array_delete(void *p, size_t size, const UA_DataType *type);
+
+/**
+ * .. _numericrange:
+ *
+ * NumericRange
+ * ^^^^^^^^^^^^
+ *
+ * NumericRanges are used to indicate subsets of a (multidimensional) variant
+ * array. NumericRange has no official type structure in the standard. On the
+ * wire, it only exists as an encoded string, such as "1:2,0:3,5". The colon
+ * separates min/max index and the comma separates dimensions. A single value
+ * indicates a range with a single element (min==max). */
+typedef struct {
+    UA_UInt32 min;
+    UA_UInt32 max;
+} UA_NumericRangeDimension;
+    
+struct UA_NumericRange {
+    size_t dimensionsSize;
+    UA_NumericRangeDimension *dimensions;
+};
+
 /**
  * Random Number Generator
  * -----------------------
@@ -807,6 +830,15 @@ void UA_EXPORT UA_random_seed(UA_UInt64 seed);
 UA_UInt32 UA_EXPORT UA_UInt32_random(void); /* no cryptographic entropy */
 UA_Guid UA_EXPORT UA_Guid_random(void);     /* no cryptographic entropy */
 
+/**
+ * .. _generated-types:
+ *
+ * Generated Data Type Definitions
+ * -------------------------------
+ *
+ * The following data types were auto-generated from a definition in XML format.
+ *
+ * .. include:: types_generated.rst */
 #ifdef __cplusplus
 } // extern "C"
 #endif

+ 4 - 0
src/client/ua_client_highlevel.c

@@ -211,6 +211,8 @@ __UA_Client_addNode(UA_Client *client, const UA_NodeClass nodeClass, const UA_No
 /* Call */
 /********/
 
+#ifdef UA_ENABLE_METHODCALLS
+
 UA_StatusCode
 UA_Client_call(UA_Client *client, const UA_NodeId objectId, const UA_NodeId methodId, size_t inputSize,
                const UA_Variant *input, size_t *outputSize, UA_Variant **output) {
@@ -247,6 +249,8 @@ UA_Client_call(UA_Client *client, const UA_NodeId objectId, const UA_NodeId meth
     return retval;
 }
 
+#endif
+
 /********************/
 /* Write Attributes */
 /********************/

+ 63 - 36
src/server/ua_nodes.c

@@ -1,4 +1,6 @@
+#include "ua_server_internal.h"
 #include "ua_nodes.h"
+#include "ua_nodestore.h"
 #include "ua_util.h"
 
 void UA_Node_deleteMembersAnyNodeClass(UA_Node *node) {
@@ -7,7 +9,8 @@ void UA_Node_deleteMembersAnyNodeClass(UA_Node *node) {
     UA_QualifiedName_deleteMembers(&node->browseName);
     UA_LocalizedText_deleteMembers(&node->displayName);
     UA_LocalizedText_deleteMembers(&node->description);
-    UA_Array_delete(node->references, node->referencesSize, &UA_TYPES[UA_TYPES_REFERENCENODE]);
+    UA_Array_delete(node->references, node->referencesSize,
+                    &UA_TYPES[UA_TYPES_REFERENCENODE]);
     node->references = NULL;
     node->referencesSize = 0;
 
@@ -22,8 +25,13 @@ void UA_Node_deleteMembersAnyNodeClass(UA_Node *node) {
     case UA_NODECLASS_VARIABLE:
     case UA_NODECLASS_VARIABLETYPE: {
         UA_VariableNode *p = (UA_VariableNode*)node;
-        if(p->valueSource == UA_VALUESOURCE_VARIANT)
-            UA_Variant_deleteMembers(&p->value.variant.value);
+        UA_NodeId_deleteMembers(&p->dataType);
+        UA_Array_delete(p->arrayDimensions, p->arrayDimensionsSize,
+                        &UA_TYPES[UA_TYPES_INT32]);
+        p->arrayDimensions = NULL;
+        p->arrayDimensionsSize = 0;
+        if(p->valueSource == UA_VALUESOURCE_DATA)
+            UA_DataValue_deleteMembers(&p->value.data.value);
         break;
     }
     case UA_NODECLASS_REFERENCETYPE: {
@@ -48,21 +56,43 @@ UA_ObjectNode_copy(const UA_ObjectNode *src, UA_ObjectNode *dst) {
 }
 
 static UA_StatusCode
-UA_VariableNode_copy(const UA_VariableNode *src, UA_VariableNode *dst) {
+UA_CommonVariableNode_copy(const UA_VariableNode *src, UA_VariableNode *dst) {
+    UA_StatusCode retval = UA_Array_copy(src->arrayDimensions,
+                                         src->arrayDimensionsSize,
+                                         (void**)&dst->arrayDimensions,
+                                         &UA_TYPES[UA_TYPES_INT32]);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
+    dst->arrayDimensionsSize = src->arrayDimensionsSize;
+    retval = UA_NodeId_copy(&src->dataType, &dst->dataType);
     dst->valueRank = src->valueRank;
     dst->valueSource = src->valueSource;
-    if(src->valueSource == UA_VALUESOURCE_VARIANT) {
-        UA_StatusCode retval = UA_Variant_copy(&src->value.variant.value, &dst->value.variant.value);
-        if(retval != UA_STATUSCODE_GOOD)
-            return retval;
-        dst->value.variant.callback = src->value.variant.callback;
+    if(src->valueSource == UA_VALUESOURCE_DATA) {
+        retval |= UA_DataValue_copy(&src->value.data.value,
+                                    &dst->value.data.value);
+        dst->value.data.callback = src->value.data.callback;
     } else
         dst->value.dataSource = src->value.dataSource;
+    return retval;
+}
+
+static UA_StatusCode
+UA_VariableNode_copy(const UA_VariableNode *src, UA_VariableNode *dst) {
+    UA_StatusCode retval = UA_CommonVariableNode_copy(src, dst);
     dst->accessLevel = src->accessLevel;
-    dst->userAccessLevel = src->accessLevel;
+    dst->userAccessLevel = src->userAccessLevel;
     dst->minimumSamplingInterval = src->minimumSamplingInterval;
     dst->historizing = src->historizing;
-    return UA_STATUSCODE_GOOD;
+    return retval;
+}
+
+static UA_StatusCode
+UA_VariableTypeNode_copy(const UA_VariableTypeNode *src,
+                         UA_VariableTypeNode *dst) {
+    UA_StatusCode retval = UA_CommonVariableNode_copy((const UA_VariableNode*)src,
+                                                      (UA_VariableNode*)dst);
+    dst->isAbstract = src->isAbstract;
+    return retval;
 }
 
 static UA_StatusCode
@@ -82,23 +112,10 @@ UA_ObjectTypeNode_copy(const UA_ObjectTypeNode *src, UA_ObjectTypeNode *dst) {
 }
 
 static UA_StatusCode
-UA_VariableTypeNode_copy(const UA_VariableTypeNode *src, UA_VariableTypeNode *dst) {
-    dst->valueRank = src->valueRank;
-    dst->valueSource = src->valueSource;
-    if(src->valueSource == UA_VALUESOURCE_VARIANT){
-        UA_StatusCode retval = UA_Variant_copy(&src->value.variant.value, &dst->value.variant.value);
-        if(retval != UA_STATUSCODE_GOOD)
-            return retval;
-        dst->value.variant.callback = src->value.variant.callback;
-    } else
-        dst->value.dataSource = src->value.dataSource;
-    dst->isAbstract = src->isAbstract;
-    return UA_STATUSCODE_GOOD;
-}
-
-static UA_StatusCode
-UA_ReferenceTypeNode_copy(const UA_ReferenceTypeNode *src, UA_ReferenceTypeNode *dst) {
-    UA_StatusCode retval = UA_LocalizedText_copy(&src->inverseName, &dst->inverseName);
+UA_ReferenceTypeNode_copy(const UA_ReferenceTypeNode *src,
+                          UA_ReferenceTypeNode *dst) {
+    UA_StatusCode retval = UA_LocalizedText_copy(&src->inverseName,
+                                                 &dst->inverseName);
     dst->isAbstract = src->isAbstract;
     dst->symmetric = src->symmetric;
     return retval;
@@ -133,7 +150,8 @@ UA_StatusCode UA_Node_copyAnyNodeClass(const UA_Node *src, UA_Node *dst) {
         UA_Node_deleteMembersAnyNodeClass(dst);
         return retval;
     }
-    retval |= UA_Array_copy(src->references, src->referencesSize, (void**)&dst->references,
+    retval |= UA_Array_copy(src->references, src->referencesSize,
+                            (void**)&dst->references,
                             &UA_TYPES[UA_TYPES_REFERENCENODE]);
     if(retval != UA_STATUSCODE_GOOD) {
         UA_Node_deleteMembersAnyNodeClass(dst);
@@ -144,25 +162,32 @@ UA_StatusCode UA_Node_copyAnyNodeClass(const UA_Node *src, UA_Node *dst) {
     /* copy unique content of the nodeclass */
     switch(src->nodeClass) {
     case UA_NODECLASS_OBJECT:
-        retval = UA_ObjectNode_copy((const UA_ObjectNode*)src, (UA_ObjectNode*)dst);
+        retval = UA_ObjectNode_copy((const UA_ObjectNode*)src,
+                                    (UA_ObjectNode*)dst);
         break;
     case UA_NODECLASS_VARIABLE:
-        retval = UA_VariableNode_copy((const UA_VariableNode*)src, (UA_VariableNode*)dst);
+        retval = UA_VariableNode_copy((const UA_VariableNode*)src,
+                                      (UA_VariableNode*)dst);
         break;
     case UA_NODECLASS_METHOD:
-        retval = UA_MethodNode_copy((const UA_MethodNode*)src, (UA_MethodNode*)dst);
+        retval = UA_MethodNode_copy((const UA_MethodNode*)src,
+                                    (UA_MethodNode*)dst);
         break;
     case UA_NODECLASS_OBJECTTYPE:
-        retval = UA_ObjectTypeNode_copy((const UA_ObjectTypeNode*)src, (UA_ObjectTypeNode*)dst);
+        retval = UA_ObjectTypeNode_copy((const UA_ObjectTypeNode*)src,
+                                        (UA_ObjectTypeNode*)dst);
         break;
     case UA_NODECLASS_VARIABLETYPE:
-        retval = UA_VariableTypeNode_copy((const UA_VariableTypeNode*)src, (UA_VariableTypeNode*)dst);
+        retval = UA_VariableTypeNode_copy((const UA_VariableTypeNode*)src,
+                                          (UA_VariableTypeNode*)dst);
         break;
     case UA_NODECLASS_REFERENCETYPE:
-        retval = UA_ReferenceTypeNode_copy((const UA_ReferenceTypeNode*)src, (UA_ReferenceTypeNode*)dst);
+        retval = UA_ReferenceTypeNode_copy((const UA_ReferenceTypeNode*)src,
+                                           (UA_ReferenceTypeNode*)dst);
         break;
     case UA_NODECLASS_DATATYPE:
-        retval = UA_DataTypeNode_copy((const UA_DataTypeNode*)src, (UA_DataTypeNode*)dst);
+        retval = UA_DataTypeNode_copy((const UA_DataTypeNode*)src,
+                                      (UA_DataTypeNode*)dst);
         break;
     case UA_NODECLASS_VIEW:
         retval = UA_ViewNode_copy((const UA_ViewNode*)src, (UA_ViewNode*)dst);
@@ -170,7 +195,9 @@ UA_StatusCode UA_Node_copyAnyNodeClass(const UA_Node *src, UA_Node *dst) {
     default:
         break;
     }
+
     if(retval != UA_STATUSCODE_GOOD)
         UA_Node_deleteMembersAnyNodeClass(dst);
+
     return retval;
 }

+ 239 - 96
src/server/ua_nodes.h

@@ -1,17 +1,49 @@
 #ifndef UA_NODES_H_
 #define UA_NODES_H_
 
-#include "ua_server.h"
-#include "ua_types_generated.h"
-#include "ua_types_encoding_binary.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
 
-/*
- * Most APIs take and return UA_EditNode and UA_ConstNode. By looking up the
- * nodeclass, nodes can be cast to their "true" class, i.e. UA_VariableNode,
- * UA_ObjectNode, and so on.
- */
+#include "ua_server.h"
 
-#define UA_STANDARD_NODEMEMBERS                 \
+/**
+ * Information Modelling
+ * =====================
+ *
+ * Information modelling in OPC UA combines concepts from object-orientation and
+ * semantic modelling. At the core, an OPC UA information model is a graph made
+ * up of
+ *
+ * - Nodes: There are eight possible Node types (variable, object, method, ...)
+ * - References: Typed and directed relations between two nodes
+ *
+ * Every node is identified by a unique (within the server) :ref:`nodeid`.
+ * Reference are triples of the form ``(source-nodeid, referencetype-nodeid,
+ * target-nodeid)``. An example reference between nodes is a
+ * ``hasTypeDefinition`` reference between a Variable and its VariableType. Some
+ * ReferenceTypes are *hierarchic* and must not form *directed loops*. Every
+ * node (except ``Root``) must have at least one hierarchic reference to a
+ * parent. See the section on :ref:`ReferenceTypes <referencetypenode>` for more
+ * details on possible references and their semantics.
+ *
+ * The structures defined in this section are *not user-facing*. The interaction
+ * with the information model is possible only via the OPC UA :ref:`services`.
+ * Still, we reproduce how nodes are represented internally so that users may
+ * have a clear mental model.
+ *
+ * Base Node Attributes
+ * --------------------
+ *
+ * Nodes contain attributes according to their node type. The base node
+ * attributes are common to all node types. In the OPC UA :ref:`services`,
+ * attributes are referred to via the :ref:`nodeid` of the containing node and
+ * an integer :ref:`attribute-id`.
+ *
+ * Internally, open62541 uses ``UA_Node`` in places where the exact node type is
+ * not known or not important. The ``nodeClass`` attribute is used to ensure the
+ * correctness of casting from ``UA_Node`` to a specific node type. */
+#define UA_NODE_BASEATTRIBUTES                  \
     UA_NodeId nodeId;                           \
     UA_NodeClass nodeClass;                     \
     UA_QualifiedName browseName;                \
@@ -23,123 +55,234 @@
     UA_ReferenceNode *references;
 
 typedef struct {
-    UA_STANDARD_NODEMEMBERS
+    UA_NODE_BASEATTRIBUTES
 } UA_Node;
 
-void UA_Node_deleteMembersAnyNodeClass(UA_Node *node);
-UA_StatusCode UA_Node_copyAnyNodeClass(const UA_Node *src, UA_Node *dst);
-
-/**************/
-/* ObjectNode */
-/**************/
-
-typedef struct {
-    UA_STANDARD_NODEMEMBERS
-    UA_Byte eventNotifier;
-    void *instanceHandle;
-} UA_ObjectNode;
-
-/******************/
-/* ObjectTypeNode */
-/******************/
-
-typedef struct {
-    UA_STANDARD_NODEMEMBERS
-    UA_Boolean isAbstract;
-    UA_ObjectLifecycleManagement lifecycleManagement;
-} UA_ObjectTypeNode;
-
+/**
+ * VariableNode
+ * ------------
+ *
+ * Variables store values in a :ref:`datavalue` together with
+ * metadata for introspection. Most notably, the attributes data type, value
+ * rank and array dimensions constrain the possible values the variable can take
+ * on.
+ *
+ * Variables come in two flavours: properties and datavariables. Properties are
+ * related to a parent with a ``hasProperty`` reference and may not have child
+ * nodes themselves. Datavariables may contain properties (``hasProperty``) and
+ * also datavariables (``hasComponents``).
+ *
+ * All variables are instances of some :ref:`variabletypenode` in return
+ * constraining the possible data type, value rank and array dimensions
+ * attributes.
+ *
+ * Data Type
+ * ^^^^^^^^^
+ *
+ * The (scalar) data type of the variable is constrained to be of a specific
+ * type or one of its children in the type hierarchy. The data type is given as
+ * a NodeId pointing to a :ref:`datatypenode` in the type hierarchy. See the
+ * Section :ref:`datatypenode` for more details.
+ *
+ * If the data type attribute points to ``UInt32``, then the value attribute
+ * must be of that exact type since ``UInt32`` does not have children in the
+ * type hierarchy. If the data type attribute points ``Number``, then the type
+ * of the value attribute may still be ``UInt32``, but also ``Float`` or
+ * ``Byte``.
+ *
+ * Consistency between the data type attribute in the variable and its
+ * :ref:`VariableTypeNode` is ensured.
+ *
+ * Value Rank
+ * ^^^^^^^^^^
+ *
+ * This attribute indicates whether the value attribute of the variable is an
+ * array and how many dimensions the array has. It may have the following
+ * values:
+ *
+ * - ``n >= 1``: the value is an array with the specified number of dimensions
+ * - ``n =  0``: the value is an array with one or more dimensions
+ * - ``n = -1``: the value is a scalar
+ * - ``n = -2``: the value can be a scalar or an array with any number of dimensions
+ * - ``n = -3``: the value can be a scalar or a one dimensional array
+ *
+ * Consistency between the value rank attribute in the variable and its
+ * :ref:`variabletypenode` is ensured.
+ *
+ * Array Dimensions
+ * ^^^^^^^^^^^^^^^^
+ *
+ * If the value rank permits the value to be a (multi-dimensional) array, the
+ * exact length in each dimensions can be further constrained with this
+ * attribute.
+ *
+ * - For positive lengths, the variable value is guaranteed to be of the same
+ *   length in this dimension.
+ * - The dimension length zero is a wildcard and the actual value may have any
+ *   length in this dimension.
+ *
+ * Consistency between the array dimensions attribute in the variable and its
+ * :ref:`variabletypenode` is ensured. */
+/* Indicates whether a variable contains data inline or whether it points to an
+ * external data source */
 typedef enum {
-    UA_VALUESOURCE_VARIANT,
+    UA_VALUESOURCE_DATA,
     UA_VALUESOURCE_DATASOURCE
 } UA_ValueSource;
 
-/****************/
-/* VariableNode */
-/****************/
+#define UA_NODE_VARIABLEATTRIBUTES                                      \
+    /* Constraints on possible values */                                \
+    UA_NodeId dataType;                                                 \
+    UA_Int32 valueRank;                                                 \
+    size_t arrayDimensionsSize;                                         \
+    UA_UInt32 *arrayDimensions;                                         \
+                                                                        \
+    /* The current value */                                             \
+    UA_ValueSource valueSource;                                         \
+    union {                                                             \
+        struct {                                                        \
+            UA_DataValue value;                                         \
+            UA_ValueCallback callback;                                  \
+        } data;                                                         \
+        UA_DataSource dataSource;                                       \
+    } value;
 
 typedef struct {
-    UA_STANDARD_NODEMEMBERS
-    UA_Int32 valueRank; /**< n >= 1: the value is an array with the specified number of dimensions.
-                             n = 0: the value is an array with one or more dimensions.
-                             n = -1: the value is a scalar.
-                             n = -2: the value can be a scalar or an array with any number of dimensions.
-                             n = -3:  the value can be a scalar or a one dimensional array. */
-    UA_ValueSource valueSource;
-    union {
-        struct {
-        UA_Variant value;
-        UA_ValueCallback callback;
-        } variant;
-        UA_DataSource dataSource;
-    } value;
-    /* <--- similar to variabletypenodes up to there--->*/
+    UA_NODE_BASEATTRIBUTES
+    UA_NODE_VARIABLEATTRIBUTES
     UA_Byte accessLevel;
     UA_Byte userAccessLevel;
     UA_Double minimumSamplingInterval;
-    UA_Boolean historizing;
+    UA_Boolean historizing; /* currently unsupported */
 } UA_VariableNode;
 
-/********************/
-/* VariableTypeNode */
-/********************/
-
+/**
+ * .. _variabletypenode:
+ *
+ * VariableTypeNode
+ * ----------------
+ *
+ * VariableTypes are used to provide type definitions for variables.
+ * VariableTypes constrain the data type, value rank and array dimensions
+ * attributes of variable instances. Furthermore, instantiating from a specific
+ * variable type may provide semantic information. For example, an instance from
+ * ``MotorTemperatureVariableType`` is more meaningful than a float variable
+ * instantiated from ``BaseDataVariable``. */
 typedef struct {
-    UA_STANDARD_NODEMEMBERS
-    UA_Int32 valueRank;
-    UA_ValueSource valueSource;
-    union {
-        struct {
-            UA_Variant value;
-            UA_ValueCallback callback;
-        } variant;
-        UA_DataSource dataSource;
-    } value;
-    /* <--- similar to variablenodes up to there--->*/
+    UA_NODE_BASEATTRIBUTES
+    UA_NODE_VARIABLEATTRIBUTES
     UA_Boolean isAbstract;
 } UA_VariableTypeNode;
 
-/*********************/
-/* ReferenceTypeNode */
-/*********************/
-
-typedef struct {
-    UA_STANDARD_NODEMEMBERS
-    UA_Boolean isAbstract;
-    UA_Boolean symmetric;
-    UA_LocalizedText inverseName;
-} UA_ReferenceTypeNode;
-
-/**************/
-/* MethodNode */
-/**************/
-
+/**
+ * .. _methodnode:
+ *
+ * MethodNode
+ * ----------
+ *
+ * Methods define callable functions and are invoked using the :ref:`Call
+ * <method-services>` service. MethodNodes may have special properties (variable
+ * childen with a ``hasProperty`` reference) with the :ref:`qualifiedname` ``(0,
+ * "InputArguments")`` and ``(0, "OutputArguments")``. The input and output
+ * arguments are both described via an array of ``UA_Argument``. While the Call
+ * service uses a generic array of :ref:`variant` for input and output, the
+ * actual argument values are checked to match the signature of the MethodNode.
+ *
+ * Note that the same MethodNode may be referenced from several objects (and
+ * object types). For this, the NodeId of the method *and of the object
+ * providing context* is part of a Call request message.
+ */
 typedef struct {
-    UA_STANDARD_NODEMEMBERS
+    UA_NODE_BASEATTRIBUTES
     UA_Boolean executable;
     UA_Boolean userExecutable;
+
+    /* Members specific to open62541 */
     void *methodHandle;
     UA_MethodCallback attachedMethod;
 } UA_MethodNode;
 
-/************/
-/* ViewNode */
-/************/
-
+/**
+ * ObjectNode
+ * ----------
+ *
+ * Objects are used to represent systems, system components, real-world objects
+ * and software objects. Objects are instances of an :ref:`object
+ * type<objecttypenode>` and may contain variables, methods and further
+ * objects. */
 typedef struct {
-    UA_STANDARD_NODEMEMBERS
+    UA_NODE_BASEATTRIBUTES
     UA_Byte eventNotifier;
-    /* <-- the same as objectnode until here --> */
-    UA_Boolean containsNoLoops;
-} UA_ViewNode;
 
-/****************/
-/* DataTypeNode */
-/****************/
+    /* Members specific to open62541 */
+    void *instanceHandle;
+} UA_ObjectNode;
+
+/**
+ * .. _objecttypenode:
+ *
+ * ObjectTypeNode
+ * --------------
+ *
+ * ObjectTypes provide definitions for Objects. */
+typedef struct {
+    UA_NODE_BASEATTRIBUTES
+    UA_Boolean isAbstract;
+
+    /* Members specific to open62541 */
+    UA_ObjectLifecycleManagement lifecycleManagement;
+} UA_ObjectTypeNode;
+
+/**
+ * .. _referencetypenode:
+ *
+ * ReferenceTypeNode
+ * -----------------
+ *
+ * The OPC UA standard defines a set of ReferenceTypes as a mandatory part of
+ * OPC UA information models. */
+typedef struct {
+    UA_NODE_BASEATTRIBUTES
+    UA_Boolean isAbstract;
+    UA_Boolean symmetric;
+    UA_LocalizedText inverseName;
+} UA_ReferenceTypeNode;
 
+/**
+ * .. _datatypenode:
+ *
+ * DataTypeNode
+ * ------------
+ *
+ * DataTypes represent simple and structured data types (for scalar values). In
+ * open62541, DataTypeNodes in the hierarchy are matched to `UA_DataType` type
+ * descriptions for :ref:`generic-types` via their NodeId.
+ *
+ * Abstract DataTypes (e.g. ``Number``) cannot be the type of actual values.
+ * They are used to constrain values to possible child DataTypes (e.g.
+ * ``UInt32``). */
 typedef struct {
-    UA_STANDARD_NODEMEMBERS
+    UA_NODE_BASEATTRIBUTES
     UA_Boolean isAbstract;
 } UA_DataTypeNode;
 
+/**
+ * ViewNode
+ * --------
+ *
+ * Each View defines a subset of the Nodes in the AddressSpace. Views can be
+ * used when browsing an informatin model to focus on a subset of nodes and
+ * references only. ViewNodes can be created and be interacted with. But their
+ * use in the :ref:`Browse<view-services>` service is currently unsupported in
+ * open62541. */
+typedef struct {
+    UA_NODE_BASEATTRIBUTES
+    UA_Byte eventNotifier;
+    UA_Boolean containsNoLoops;
+} UA_ViewNode;
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
 #endif /* UA_NODES_H_ */

+ 5 - 2
src/server/ua_nodestore.c

@@ -1,4 +1,5 @@
 #include "ua_nodestore.h"
+#include "ua_server_internal.h"
 #include "ua_util.h"
 
 #ifndef UA_ENABLE_MULTITHREADING /* conditional compilation */
@@ -208,12 +209,14 @@ UA_NodeStore_newNode(UA_NodeClass class) {
     UA_NodeStoreEntry *entry = instantiateEntry(class);
     if(!entry)
         return NULL;
-    return (UA_Node*)&entry->node;
+    return &entry->node;
 }
 
 void
 UA_NodeStore_deleteNode(UA_Node *node) {
-    deleteEntry(container_of(node, UA_NodeStoreEntry, node));
+    UA_NodeStoreEntry *entry = container_of(node, UA_NodeStoreEntry, node);
+    UA_assert(&entry->node == node);
+    deleteEntry(entry);
 }
 
 UA_StatusCode

+ 1 - 0
src/server/ua_nodestore_concurrent.c

@@ -1,5 +1,6 @@
 #include "ua_util.h"
 #include "ua_nodestore.h"
+#include "ua_server_internal.h"
 
 #ifdef UA_ENABLE_MULTITHREADING /* conditional compilation */
 

+ 306 - 310
src/server/ua_server.c

@@ -41,11 +41,6 @@ static const UA_NodeId nodeIdFolderType = {
     .namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC,
     .identifier.numeric = UA_NS0ID_FOLDERTYPE};
 
-static const UA_ExpandedNodeId expandedNodeIdBaseDataVariabletype = {
-    .nodeId = {.namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC,
-               .identifier.numeric = UA_NS0ID_BASEDATAVARIABLETYPE},
-    .namespaceUri = {.length = 0, .data = NULL}, .serverIndex = 0};
-
 #ifndef UA_ENABLE_GENERATE_NAMESPACE0
 static const UA_NodeId nodeIdNonHierarchicalReferences = {
         .namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC,
@@ -163,8 +158,9 @@ addNodeInternal(UA_Server *server, UA_Node *node, const UA_NodeId parentNodeId,
     UA_AddNodesResult res;
     UA_AddNodesResult_init(&res);
     UA_RCU_LOCK();
-    Service_AddNodes_existing(server, &adminSession, node, &parentNodeId,
-                              &referenceTypeId, &UA_NODEID_NULL, NULL, &res);
+    res.statusCode = Service_AddNodes_existing(server, &adminSession, node, &parentNodeId,
+                                               &referenceTypeId, &UA_NODEID_NULL,
+                                               NULL, &res.addedNodeId);
     UA_RCU_UNLOCK();
     return res;
 }
@@ -175,8 +171,9 @@ addNodeInternalWithType(UA_Server *server, UA_Node *node, const UA_NodeId parent
     UA_AddNodesResult res;
     UA_AddNodesResult_init(&res);
     UA_RCU_LOCK();
-    Service_AddNodes_existing(server, &adminSession, node, &parentNodeId,
-                              &referenceTypeId, &typeIdentifier, NULL, &res);
+    res.statusCode = Service_AddNodes_existing(server, &adminSession, node, &parentNodeId,
+                                               &referenceTypeId, &typeIdentifier,
+                                               NULL, &res.addedNodeId);
     UA_RCU_UNLOCK();
     return res;
 }
@@ -327,7 +324,8 @@ readCurrentTime(void *handle, const UA_NodeId nodeid, UA_Boolean sourceTimeStamp
         return UA_STATUSCODE_GOOD;
     }
     UA_DateTime currentTime = UA_DateTime_now();
-    UA_StatusCode retval = UA_Variant_setScalarCopy(&value->value, &currentTime, &UA_TYPES[UA_TYPES_DATETIME]);
+    UA_StatusCode retval = UA_Variant_setScalarCopy(&value->value, &currentTime,
+                                                    &UA_TYPES[UA_TYPES_DATETIME]);
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
     value->hasValue = true;
@@ -345,11 +343,14 @@ static void copyNames(UA_Node *node, char *name) {
 }
 
 static void
-addDataTypeNode(UA_Server *server, char* name, UA_UInt32 datatypeid, UA_UInt32 parent) {
+addDataTypeNode(UA_Server *server, char* name, UA_UInt32 datatypeid,
+                UA_Boolean isAbstract, UA_UInt32 parent) {
     UA_DataTypeNode *datatype = UA_NodeStore_newDataTypeNode();
     copyNames((UA_Node*)datatype, name);
     datatype->nodeId.identifier.numeric = datatypeid;
-    addNodeInternal(server, (UA_Node*)datatype, UA_NODEID_NUMERIC(0, parent), nodeIdOrganizes);
+    datatype->isAbstract = isAbstract;
+    addNodeInternal(server, (UA_Node*)datatype,
+                    UA_NODEID_NUMERIC(0, parent), nodeIdHasSubType);
 }
 
 static void
@@ -364,34 +365,18 @@ addObjectTypeNode(UA_Server *server, char* name, UA_UInt32 objecttypeid,
 
 static UA_VariableTypeNode*
 createVariableTypeNode(UA_Server *server, char* name, UA_UInt32 variabletypeid,
-                       UA_UInt32 parent, UA_Boolean abstract) {
+                       UA_Boolean abstract) {
     UA_VariableTypeNode *variabletype = UA_NodeStore_newVariableTypeNode();
     copyNames((UA_Node*)variabletype, name);
     variabletype->nodeId.identifier.numeric = variabletypeid;
     variabletype->isAbstract = abstract;
-    variabletype->value.variant.value.type = &UA_TYPES[UA_TYPES_VARIANT];
     return variabletype;
 }
 
-static void
-addVariableTypeNode_organized(UA_Server *server, char* name, UA_UInt32 variabletypeid,
-                              UA_UInt32 parent, UA_Boolean abstract) {
-    UA_VariableTypeNode *variabletype = createVariableTypeNode(server, name, variabletypeid, parent, abstract);
-    addNodeInternal(server, (UA_Node*)variabletype, UA_NODEID_NUMERIC(0, parent), nodeIdOrganizes);
-}
-
-static void
-addVariableTypeNode_subtype(UA_Server *server, char* name, UA_UInt32 variabletypeid,
-                            UA_UInt32 parent, UA_Boolean abstract) {
-    UA_VariableTypeNode *variabletype =
-        createVariableTypeNode(server, name, variabletypeid, parent, abstract);
-    addNodeInternal(server, (UA_Node*)variabletype, UA_NODEID_NUMERIC(0, parent), nodeIdHasSubType);
-}
-
 #if defined(UA_ENABLE_METHODCALLS) && defined(UA_ENABLE_SUBSCRIPTIONS)
 static UA_StatusCode
 GetMonitoredItems(void *handle, const UA_NodeId objectId, size_t inputSize,
-                          const UA_Variant *input, size_t outputSize, UA_Variant *output) {
+                  const UA_Variant *input, size_t outputSize, UA_Variant *output) {
     UA_UInt32 subscriptionId = *((UA_UInt32*)(input[0].data));
     UA_Session* session = methodCallSession;
     UA_Subscription* subscription = UA_Session_getSubscriptionByID(session, subscriptionId);
@@ -496,12 +481,11 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
 
     server->startTime = UA_DateTime_now();
 
-    /**************/
-    /* References */
-    /**************/
-
 #ifndef UA_ENABLE_GENERATE_NAMESPACE0
-    /* Bootstrap reference hierarchy by manually inserting "references" and "hassubtype" */
+
+    /*********************************/
+    /* Bootstrap reference hierarchy */
+    /*********************************/
 
     UA_ReferenceTypeNode *references = UA_NodeStore_newReferenceTypeNode();
     copyNames((UA_Node*)references, "References");
@@ -509,10 +493,6 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     references->isAbstract = true;
     references->symmetric = true;
     references->inverseName = UA_LOCALIZEDTEXT_ALLOC("en_US", "References");
-    /* The reference to root is later inserted */
-    UA_RCU_LOCK();
-    UA_NodeStore_insert(server->nodestore, (UA_Node*)references);
-    UA_RCU_UNLOCK();
 
     UA_ReferenceTypeNode *hassubtype = UA_NodeStore_newReferenceTypeNode();
     copyNames((UA_Node*)hassubtype, "HasSubtype");
@@ -520,12 +500,12 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     hassubtype->nodeId.identifier.numeric = UA_NS0ID_HASSUBTYPE;
     hassubtype->isAbstract = false;
     hassubtype->symmetric = false;
-    /* The reference to root is later inserted */
+
     UA_RCU_LOCK();
+    UA_NodeStore_insert(server->nodestore, (UA_Node*)references);
     UA_NodeStore_insert(server->nodestore, (UA_Node*)hassubtype);
     UA_RCU_UNLOCK();
 
-    /* Continue adding reference types with normal "addnode" */
     UA_ReferenceTypeNode *hierarchicalreferences = UA_NodeStore_newReferenceTypeNode();
     copyNames((UA_Node*)hierarchicalreferences, "HierarchicalReferences");
     hierarchicalreferences->nodeId.identifier.numeric = UA_NS0ID_HIERARCHICALREFERENCES;
@@ -610,13 +590,13 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
 
     UA_ReferenceTypeNode *aggregates = UA_NodeStore_newReferenceTypeNode();
     copyNames((UA_Node*)aggregates, "Aggregates");
-    // Todo: Is there an inverse name?
+    aggregates->inverseName = UA_LOCALIZEDTEXT_ALLOC("en_US", "AggregatedBy");
     aggregates->nodeId.identifier.numeric = UA_NS0ID_AGGREGATES;
     aggregates->isAbstract = false;
     aggregates->symmetric  = false;
     addNodeInternal(server, (UA_Node*)aggregates, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCHILD), nodeIdHasSubType);
 
-    // complete bootstrap of hassubtype
+    /* complete bootstrap of hassubtype */
     addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCHILD), nodeIdHasSubType,
                          UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE), true);
 
@@ -701,29 +681,127 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     hashistoricalconfiguration->symmetric  = false;
     addNodeInternal(server, (UA_Node*)hashistoricalconfiguration, UA_NODEID_NUMERIC(0, UA_NS0ID_AGGREGATES), nodeIdHasSubType);
 
-    /********************/
-    /* Object Hierarchy */
-    /********************/
+    /**************/
+    /* Data Types */
+    /**************/
 
-    /* Bootstrap object hierarchy by manually inserting "root" and "baseobjecttype" */
-    UA_ObjectNode *root = UA_NodeStore_newObjectNode();
-    copyNames((UA_Node*)root, "Root");
-    root->nodeId.identifier.numeric = UA_NS0ID_ROOTFOLDER;
+    UA_DataTypeNode *basedatatype = UA_NodeStore_newDataTypeNode();
+    copyNames((UA_Node*)basedatatype, "BaseDataType");
+    basedatatype->nodeId.identifier.numeric = UA_NS0ID_BASEDATATYPE;
+    basedatatype->isAbstract = true;
+    UA_RCU_LOCK();
+    UA_NodeStore_insert(server->nodestore, (UA_Node*)basedatatype);
+    UA_RCU_UNLOCK();
+
+    addDataTypeNode(server, "Boolean", UA_NS0ID_BOOLEAN, false, UA_NS0ID_BASEDATATYPE);
+    addDataTypeNode(server, "Number", UA_NS0ID_NUMBER, true, UA_NS0ID_BASEDATATYPE);
+    addDataTypeNode(server, "Float", UA_NS0ID_FLOAT, false, UA_NS0ID_NUMBER);
+    addDataTypeNode(server, "Double", UA_NS0ID_DOUBLE, false, UA_NS0ID_NUMBER);
+    addDataTypeNode(server, "Integer", UA_NS0ID_INTEGER, true, UA_NS0ID_NUMBER);
+       addDataTypeNode(server, "SByte", UA_NS0ID_SBYTE, false, UA_NS0ID_INTEGER);
+       addDataTypeNode(server, "Int16", UA_NS0ID_INT16, false, UA_NS0ID_INTEGER);
+       addDataTypeNode(server, "Int32", UA_NS0ID_INT32, false, UA_NS0ID_INTEGER);
+       addDataTypeNode(server, "Int64", UA_NS0ID_INT64, false, UA_NS0ID_INTEGER);
+       addDataTypeNode(server, "UInteger", UA_NS0ID_UINTEGER, true, UA_NS0ID_INTEGER);
+          addDataTypeNode(server, "Byte", UA_NS0ID_BYTE, false, UA_NS0ID_UINTEGER);
+          addDataTypeNode(server, "UInt16", UA_NS0ID_UINT16, false, UA_NS0ID_UINTEGER);
+          addDataTypeNode(server, "UInt32", UA_NS0ID_UINT32, false, UA_NS0ID_UINTEGER);
+          addDataTypeNode(server, "UInt64", UA_NS0ID_UINT64, false, UA_NS0ID_UINTEGER);
+    addDataTypeNode(server, "String", UA_NS0ID_STRING, false, UA_NS0ID_BASEDATATYPE);
+    addDataTypeNode(server, "DateTime", UA_NS0ID_DATETIME, false, UA_NS0ID_BASEDATATYPE);
+    addDataTypeNode(server, "Guid", UA_NS0ID_GUID, false, UA_NS0ID_BASEDATATYPE);
+    addDataTypeNode(server, "ByteString", UA_NS0ID_BYTESTRING, false, UA_NS0ID_BASEDATATYPE);
+    addDataTypeNode(server, "XmlElement", UA_NS0ID_XMLELEMENT, false, UA_NS0ID_BASEDATATYPE);
+    addDataTypeNode(server, "NodeId", UA_NS0ID_NODEID, false, UA_NS0ID_BASEDATATYPE);
+    addDataTypeNode(server, "ExpandedNodeId", UA_NS0ID_EXPANDEDNODEID, false, UA_NS0ID_BASEDATATYPE);
+    addDataTypeNode(server, "StatusCode", UA_NS0ID_STATUSCODE, false, UA_NS0ID_BASEDATATYPE);
+    addDataTypeNode(server, "QualifiedName", UA_NS0ID_QUALIFIEDNAME, false, UA_NS0ID_BASEDATATYPE);
+    addDataTypeNode(server, "LocalizedText", UA_NS0ID_LOCALIZEDTEXT, false, UA_NS0ID_BASEDATATYPE);
+    addDataTypeNode(server, "Structure", UA_NS0ID_STRUCTURE, true, UA_NS0ID_BASEDATATYPE);
+       addDataTypeNode(server, "ServerStatusDataType", UA_NS0ID_SERVERSTATUSDATATYPE, false, UA_NS0ID_STRUCTURE);
+       addDataTypeNode(server, "BuildInfo", UA_NS0ID_BUILDINFO, false, UA_NS0ID_STRUCTURE);
+    addDataTypeNode(server, "DataValue", UA_NS0ID_DATAVALUE, false, UA_NS0ID_BASEDATATYPE);
+    addDataTypeNode(server, "DiagnosticInfo", UA_NS0ID_DIAGNOSTICINFO, false, UA_NS0ID_BASEDATATYPE);
+    addDataTypeNode(server, "Enumeration", UA_NS0ID_ENUMERATION, true, UA_NS0ID_BASEDATATYPE);
+       addDataTypeNode(server, "ServerState", UA_NS0ID_SERVERSTATE, false, UA_NS0ID_ENUMERATION);
+
+    /*****************/
+    /* VariableTypes */
+    /*****************/
+
+    UA_VariableTypeNode *basevartype =
+        createVariableTypeNode(server, "BaseVariableType", UA_NS0ID_BASEVARIABLETYPE, true);
+    basevartype->valueRank = -2;
+    basevartype->dataType = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATATYPE);
+    UA_RCU_LOCK();
+    UA_NodeStore_insert(server->nodestore, (UA_Node*)basevartype);
+    UA_RCU_UNLOCK();
+
+    UA_VariableTypeNode *basedatavartype =
+        createVariableTypeNode(server, "BaseDataVariableType", UA_NS0ID_BASEDATAVARIABLETYPE, false);
+    basedatavartype->valueRank = -2;
+    basedatavartype->dataType = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATATYPE);
+    addNodeInternalWithType(server, (UA_Node*)basedatavartype,
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_BASEVARIABLETYPE),
+                            nodeIdHasSubType, UA_NODEID_NUMERIC(0, UA_NS0ID_BASEVARIABLETYPE));
+
+    UA_VariableTypeNode *propertytype =
+        createVariableTypeNode(server, "PropertyType", UA_NS0ID_PROPERTYTYPE, false);
+    propertytype->valueRank = -2;
+    propertytype->dataType = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATATYPE);
+    addNodeInternalWithType(server, (UA_Node*)propertytype,
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_BASEVARIABLETYPE),
+                            nodeIdHasSubType, UA_NODEID_NUMERIC(0, UA_NS0ID_BASEVARIABLETYPE));
+
+    UA_VariableTypeNode *buildinfotype =
+        createVariableTypeNode(server, "BuildInfoType", UA_NS0ID_BUILDINFOTYPE, false);
+    buildinfotype->valueRank = -1;
+    buildinfotype->dataType = UA_NODEID_NUMERIC(0, UA_NS0ID_BUILDINFO);
+    addNodeInternalWithType(server, (UA_Node*)buildinfotype,
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
+                            nodeIdHasSubType, UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE));
+
+    UA_VariableTypeNode *serverstatustype =
+        createVariableTypeNode(server, "ServerStatusType", UA_NS0ID_SERVERSTATUSTYPE, false);
+    serverstatustype->valueRank = -1;
+    serverstatustype->dataType = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVERSTATUSDATATYPE);
+    addNodeInternalWithType(server, (UA_Node*)serverstatustype,
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
+                            nodeIdHasSubType, UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE));
+
+    /**********************/
+    /* Basic Object Types */
+    /**********************/
 
     UA_ObjectTypeNode *baseobjtype = UA_NodeStore_newObjectTypeNode();
     copyNames((UA_Node*)baseobjtype, "BaseObjectType");
     baseobjtype->nodeId.identifier.numeric = UA_NS0ID_BASEOBJECTTYPE;
-
     UA_RCU_LOCK();
-    UA_NodeStore_insert(server->nodestore, (UA_Node*)root);
     UA_NodeStore_insert(server->nodestore, (UA_Node*)baseobjtype);
     UA_RCU_UNLOCK();
 
+    addObjectTypeNode(server, "FolderType", UA_NS0ID_FOLDERTYPE,
+                      UA_NS0ID_BASEOBJECTTYPE, UA_NS0ID_HASSUBTYPE);
+    addObjectTypeNode(server, "ServerType", UA_NS0ID_SERVERTYPE,
+                      UA_NS0ID_BASEOBJECTTYPE, UA_NS0ID_HASSUBTYPE);
+    addObjectTypeNode(server, "ServerDiagnosticsType", UA_NS0ID_SERVERDIAGNOSTICSTYPE,
+                      UA_NS0ID_BASEOBJECTTYPE, UA_NS0ID_HASSUBTYPE);
+    addObjectTypeNode(server, "ServerCapatilitiesType", UA_NS0ID_SERVERCAPABILITIESTYPE,
+                      UA_NS0ID_BASEOBJECTTYPE, UA_NS0ID_HASSUBTYPE);
+
+    /******************/
+    /* Root and below */
+    /******************/
+
+    UA_ObjectNode *root = UA_NodeStore_newObjectNode();
+    copyNames((UA_Node*)root, "Root");
+    root->nodeId.identifier.numeric = UA_NS0ID_ROOTFOLDER;
+    UA_RCU_LOCK();
+    UA_NodeStore_insert(server->nodestore, (UA_Node*)root);
+    UA_RCU_UNLOCK();
     addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_ROOTFOLDER), nodeIdHasTypeDefinition,
                          UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), true);
 
-    addObjectTypeNode(server, "FolderType", UA_NS0ID_FOLDERTYPE, UA_NS0ID_BASEOBJECTTYPE, UA_NS0ID_HASSUBTYPE);
-
     UA_ObjectNode *objects = UA_NodeStore_newObjectNode();
     copyNames((UA_Node*)objects, "Objects");
     objects->nodeId.identifier.numeric = UA_NS0ID_OBJECTSFOLDER;
@@ -736,111 +814,51 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     addNodeInternalWithType(server, (UA_Node*)types, UA_NODEID_NUMERIC(0, UA_NS0ID_ROOTFOLDER),
                             nodeIdOrganizes, nodeIdFolderType);
 
-    UA_ObjectNode *views = UA_NodeStore_newObjectNode();
-    copyNames((UA_Node*)views, "Views");
-    views->nodeId.identifier.numeric = UA_NS0ID_VIEWSFOLDER;
-    addNodeInternalWithType(server, (UA_Node*)views, UA_NODEID_NUMERIC(0, UA_NS0ID_ROOTFOLDER),
-                            nodeIdOrganizes, nodeIdFolderType);
-
     UA_ObjectNode *referencetypes = UA_NodeStore_newObjectNode();
     copyNames((UA_Node*)referencetypes, "ReferenceTypes");
     referencetypes->nodeId.identifier.numeric = UA_NS0ID_REFERENCETYPESFOLDER;
     addNodeInternalWithType(server, (UA_Node*)referencetypes, UA_NODEID_NUMERIC(0, UA_NS0ID_TYPESFOLDER),
                             nodeIdOrganizes, nodeIdFolderType);
-
     addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_REFERENCETYPESFOLDER), nodeIdOrganizes,
                          UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_REFERENCES), true);
 
-    /**********************/
-    /* Basic Object Types */
-    /**********************/
-
-    UA_ObjectNode *objecttypes = UA_NodeStore_newObjectNode();
-    copyNames((UA_Node*)objecttypes, "ObjectTypes");
-    objecttypes->nodeId.identifier.numeric = UA_NS0ID_OBJECTTYPESFOLDER;
-    addNodeInternalWithType(server, (UA_Node*)objecttypes, UA_NODEID_NUMERIC(0, UA_NS0ID_TYPESFOLDER),
-                            nodeIdOrganizes, nodeIdFolderType);
-    /* Link in the bootstrapped baseobjecttype */
-    addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTTYPESFOLDER), nodeIdOrganizes,
-                         UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE), true);
-
-    addObjectTypeNode(server, "ServerType", UA_NS0ID_SERVERTYPE, UA_NS0ID_BASEOBJECTTYPE, UA_NS0ID_HASSUBTYPE);
-    addObjectTypeNode(server, "ServerDiagnosticsType", UA_NS0ID_SERVERDIAGNOSTICSTYPE,
-                      UA_NS0ID_BASEOBJECTTYPE, UA_NS0ID_HASSUBTYPE);
-    addObjectTypeNode(server, "ServerCapatilitiesType", UA_NS0ID_SERVERCAPABILITIESTYPE,
-                      UA_NS0ID_BASEOBJECTTYPE, UA_NS0ID_HASSUBTYPE);
-    addObjectTypeNode(server, "ServerStatusType", UA_NS0ID_SERVERSTATUSTYPE, UA_NS0ID_BASEOBJECTTYPE,
-                      UA_NS0ID_HASSUBTYPE);
-    addObjectTypeNode(server, "BuildInfoType", UA_NS0ID_BUILDINFOTYPE, UA_NS0ID_BASEOBJECTTYPE,
-                      UA_NS0ID_HASSUBTYPE);
-
-    /**************/
-    /* Data Types */
-    /**************/
-
     UA_ObjectNode *datatypes = UA_NodeStore_newObjectNode();
     copyNames((UA_Node*)datatypes, "DataTypes");
     datatypes->nodeId.identifier.numeric = UA_NS0ID_DATATYPESFOLDER;
     addNodeInternalWithType(server, (UA_Node*)datatypes, UA_NODEID_NUMERIC(0, UA_NS0ID_TYPESFOLDER),
                             nodeIdOrganizes, nodeIdFolderType);
-
-    addDataTypeNode(server, "BaseDataType", UA_NS0ID_BASEDATATYPE, UA_NS0ID_DATATYPESFOLDER);
-    addDataTypeNode(server, "Boolean", UA_NS0ID_BOOLEAN, UA_NS0ID_BASEDATATYPE);
-    addDataTypeNode(server, "Number", UA_NS0ID_NUMBER, UA_NS0ID_BASEDATATYPE);
-        addDataTypeNode(server, "Float", UA_NS0ID_FLOAT, UA_NS0ID_NUMBER);
-        addDataTypeNode(server, "Double", UA_NS0ID_DOUBLE, UA_NS0ID_NUMBER);
-        addDataTypeNode(server, "Integer", UA_NS0ID_INTEGER, UA_NS0ID_NUMBER);
-            addDataTypeNode(server, "SByte", UA_NS0ID_SBYTE, UA_NS0ID_INTEGER);
-            addDataTypeNode(server, "Int16", UA_NS0ID_INT16, UA_NS0ID_INTEGER);
-            addDataTypeNode(server, "Int32", UA_NS0ID_INT32, UA_NS0ID_INTEGER);
-            addDataTypeNode(server, "Int64", UA_NS0ID_INT64, UA_NS0ID_INTEGER);
-            addDataTypeNode(server, "UInteger", UA_NS0ID_UINTEGER, UA_NS0ID_INTEGER);
-                addDataTypeNode(server, "Byte", UA_NS0ID_BYTE, UA_NS0ID_UINTEGER);
-                addDataTypeNode(server, "UInt16", UA_NS0ID_UINT16, UA_NS0ID_UINTEGER);
-                addDataTypeNode(server, "UInt32", UA_NS0ID_UINT32, UA_NS0ID_UINTEGER);
-                addDataTypeNode(server, "UInt64", UA_NS0ID_UINT64, UA_NS0ID_UINTEGER);
-    addDataTypeNode(server, "String", UA_NS0ID_STRING, UA_NS0ID_BASEDATATYPE);
-    addDataTypeNode(server, "DateTime", UA_NS0ID_DATETIME, UA_NS0ID_BASEDATATYPE);
-    addDataTypeNode(server, "Guid", UA_NS0ID_GUID, UA_NS0ID_BASEDATATYPE);
-    addDataTypeNode(server, "ByteString", UA_NS0ID_BYTESTRING, UA_NS0ID_BASEDATATYPE);
-    addDataTypeNode(server, "XmlElement", UA_NS0ID_XMLELEMENT, UA_NS0ID_BASEDATATYPE);
-    addDataTypeNode(server, "NodeId", UA_NS0ID_NODEID, UA_NS0ID_BASEDATATYPE);
-    addDataTypeNode(server, "ExpandedNodeId", UA_NS0ID_EXPANDEDNODEID, UA_NS0ID_BASEDATATYPE);
-    addDataTypeNode(server, "StatusCode", UA_NS0ID_STATUSCODE, UA_NS0ID_BASEDATATYPE);
-    addDataTypeNode(server, "QualifiedName", UA_NS0ID_QUALIFIEDNAME, UA_NS0ID_BASEDATATYPE);
-    addDataTypeNode(server, "LocalizedText", UA_NS0ID_LOCALIZEDTEXT, UA_NS0ID_BASEDATATYPE);
-    addDataTypeNode(server, "Structure", UA_NS0ID_STRUCTURE, UA_NS0ID_BASEDATATYPE);
-        addDataTypeNode(server, "ServerStatusDataType", UA_NS0ID_SERVERSTATUSDATATYPE, UA_NS0ID_STRUCTURE);
-        addDataTypeNode(server, "BuildInfo", UA_NS0ID_BUILDINFO, UA_NS0ID_STRUCTURE);
-    addDataTypeNode(server, "DataValue", UA_NS0ID_DATAVALUE, UA_NS0ID_BASEDATATYPE);
-    addDataTypeNode(server, "DiagnosticInfo", UA_NS0ID_DIAGNOSTICINFO, UA_NS0ID_BASEDATATYPE);
-    addDataTypeNode(server, "Enumeration", UA_NS0ID_ENUMERATION, UA_NS0ID_BASEDATATYPE);
-        addDataTypeNode(server, "ServerState", UA_NS0ID_SERVERSTATE, UA_NS0ID_ENUMERATION);
+    addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_DATATYPESFOLDER), nodeIdOrganizes,
+                         UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BASEDATATYPE), true);
 
     UA_ObjectNode *variabletypes = UA_NodeStore_newObjectNode();
     copyNames((UA_Node*)variabletypes, "VariableTypes");
     variabletypes->nodeId.identifier.numeric = UA_NS0ID_VARIABLETYPESFOLDER;
     addNodeInternalWithType(server, (UA_Node*)variabletypes, UA_NODEID_NUMERIC(0, UA_NS0ID_TYPESFOLDER),
                             nodeIdOrganizes, nodeIdFolderType);
-    addVariableTypeNode_organized(server, "BaseVariableType", UA_NS0ID_BASEVARIABLETYPE,
-                                  UA_NS0ID_VARIABLETYPESFOLDER, true);
-    addVariableTypeNode_subtype(server, "BaseDataVariableType", UA_NS0ID_BASEDATAVARIABLETYPE,
-                                UA_NS0ID_BASEVARIABLETYPE, false);
-    addVariableTypeNode_subtype(server, "PropertyType", UA_NS0ID_PROPERTYTYPE,
-                                UA_NS0ID_BASEVARIABLETYPE, false);
+    addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_VARIABLETYPESFOLDER), nodeIdOrganizes,
+                         UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BASEVARIABLETYPE), true);
 
-    /***************/
-    /* Event Types */
-    /***************/
+    UA_ObjectNode *objecttypes = UA_NodeStore_newObjectNode();
+    copyNames((UA_Node*)objecttypes, "ObjectTypes");
+    objecttypes->nodeId.identifier.numeric = UA_NS0ID_OBJECTTYPESFOLDER;
+    addNodeInternalWithType(server, (UA_Node*)objecttypes, UA_NODEID_NUMERIC(0, UA_NS0ID_TYPESFOLDER),
+                            nodeIdOrganizes, nodeIdFolderType);
+    addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTTYPESFOLDER), nodeIdOrganizes,
+                         UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE), true);
 
     UA_ObjectNode *eventtypes = UA_NodeStore_newObjectNode();
     copyNames((UA_Node*)eventtypes, "EventTypes");
     eventtypes->nodeId.identifier.numeric = UA_NS0ID_EVENTTYPESFOLDER;
     addNodeInternalWithType(server, (UA_Node*)eventtypes, UA_NODEID_NUMERIC(0, UA_NS0ID_TYPESFOLDER),
                             nodeIdOrganizes, nodeIdFolderType);
-#endif
 
-#ifdef UA_ENABLE_GENERATE_NAMESPACE0
+    UA_ObjectNode *views = UA_NodeStore_newObjectNode();
+    copyNames((UA_Node*)views, "Views");
+    views->nodeId.identifier.numeric = UA_NS0ID_VIEWSFOLDER;
+    addNodeInternalWithType(server, (UA_Node*)views, UA_NODEID_NUMERIC(0, UA_NS0ID_ROOTFOLDER),
+                            nodeIdOrganizes, nodeIdFolderType);
+
+#else
     /* load the generated namespace externally */
     ua_namespaceinit_generated(server);
 #endif
@@ -863,55 +881,51 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
                                                         .write = NULL};
     namespaceArray->valueRank = 1;
     namespaceArray->minimumSamplingInterval = 1.0;
-    addNodeInternal(server, (UA_Node*)namespaceArray, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER), nodeIdHasProperty);
-    addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_NAMESPACEARRAY),
-                         nodeIdHasTypeDefinition, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE), true);
+    addNodeInternalWithType(server, (UA_Node*)namespaceArray, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER),
+                            nodeIdHasProperty, UA_NODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE));
 
     UA_VariableNode *serverArray = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)serverArray, "ServerArray");
     serverArray->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERARRAY;
-    UA_Variant_setArrayCopy(&serverArray->value.variant.value,
+    UA_Variant_setArrayCopy(&serverArray->value.data.value.value,
                             &server->config.applicationDescription.applicationUri, 1,
                             &UA_TYPES[UA_TYPES_STRING]);
+    serverArray->value.data.value.hasValue = true;
     serverArray->valueRank = 1;
     serverArray->minimumSamplingInterval = 1.0;
-    addNodeInternal(server, (UA_Node*)serverArray, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER), nodeIdHasProperty);
-    addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERARRAY), nodeIdHasTypeDefinition,
-                         UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE), true);
+    addNodeInternalWithType(server, (UA_Node*)serverArray, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER),
+                            nodeIdHasProperty, UA_NODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE));
 
     UA_ObjectNode *servercapablities = UA_NodeStore_newObjectNode();
     copyNames((UA_Node*)servercapablities, "ServerCapabilities");
     servercapablities->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERCAPABILITIES;
-    addNodeInternal(server, (UA_Node*)servercapablities, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER),
-                    nodeIdHasComponent);
-    addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES), nodeIdHasTypeDefinition,
-                         UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVERCAPABILITIESTYPE), true);
+    addNodeInternalWithType(server, (UA_Node*)servercapablities, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER),
+                            nodeIdHasComponent,
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_SERVERCAPABILITIESTYPE));
 
     UA_VariableNode *localeIdArray = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)localeIdArray, "LocaleIdArray");
     localeIdArray->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERCAPABILITIES_LOCALEIDARRAY;
-    localeIdArray->value.variant.value.data = UA_Array_new(1, &UA_TYPES[UA_TYPES_STRING]);
-    localeIdArray->value.variant.value.arrayLength = 1;
-    localeIdArray->value.variant.value.type = &UA_TYPES[UA_TYPES_STRING];
-    *(UA_String *)localeIdArray->value.variant.value.data = UA_STRING_ALLOC("en");
+    UA_String enLocale = UA_STRING("en");
+    UA_Variant_setArrayCopy(&localeIdArray->value.data.value.value,
+                            &enLocale, 1, &UA_TYPES[UA_TYPES_STRING]);
+    localeIdArray->value.data.value.hasValue = true;
     localeIdArray->valueRank = 1;
     localeIdArray->minimumSamplingInterval = 1.0;
-    addNodeInternal(server, (UA_Node*)localeIdArray,
-                    UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES), nodeIdHasProperty);
-    addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_LOCALEIDARRAY),
-                         nodeIdHasTypeDefinition, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE), true);
+    addNodeInternalWithType(server, (UA_Node*)localeIdArray,
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES),
+                            nodeIdHasProperty, UA_NODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE));
 
     UA_VariableNode *maxBrowseContinuationPoints = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)maxBrowseContinuationPoints, "MaxBrowseContinuationPoints");
     maxBrowseContinuationPoints->nodeId.identifier.numeric =
         UA_NS0ID_SERVER_SERVERCAPABILITIES_MAXBROWSECONTINUATIONPOINTS;
-    maxBrowseContinuationPoints->value.variant.value.data = UA_UInt16_new();
-    *((UA_UInt16*)maxBrowseContinuationPoints->value.variant.value.data) = MAXCONTINUATIONPOINTS;
-    maxBrowseContinuationPoints->value.variant.value.type = &UA_TYPES[UA_TYPES_UINT16];
-    addNodeInternal(server, (UA_Node*)maxBrowseContinuationPoints,
-                    UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES), nodeIdHasProperty);
-    addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_MAXBROWSECONTINUATIONPOINTS),
-                         nodeIdHasTypeDefinition, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE), true);
+    UA_Variant_setScalar(&maxBrowseContinuationPoints->value.data.value.value,
+                         UA_UInt16_new(), &UA_TYPES[UA_TYPES_UINT16]);
+    maxBrowseContinuationPoints->value.data.value.hasValue = true;
+    addNodeInternalWithType(server, (UA_Node*)maxBrowseContinuationPoints,
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES),
+                            nodeIdHasProperty, UA_NODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE));
 
     /** ServerProfileArray **/
 #define MAX_PROFILEARRAY 16 //a *magic* limit to the number of supported profiles
@@ -933,118 +947,107 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     UA_VariableNode *serverProfileArray = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)serverProfileArray, "ServerProfileArray");
     serverProfileArray->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERCAPABILITIES_SERVERPROFILEARRAY;
-    serverProfileArray->value.variant.value.arrayLength = profileArraySize;
-    serverProfileArray->value.variant.value.data = UA_Array_new(profileArraySize, &UA_TYPES[UA_TYPES_STRING]);
-    serverProfileArray->value.variant.value.type = &UA_TYPES[UA_TYPES_STRING];
+    UA_Variant_setArray(&serverProfileArray->value.data.value.value,
+                        UA_Array_new(profileArraySize, &UA_TYPES[UA_TYPES_STRING]),
+                        profileArraySize, &UA_TYPES[UA_TYPES_STRING]);
     for(UA_UInt16 i=0;i<profileArraySize;i++)
-        ((UA_String *)serverProfileArray->value.variant.value.data)[i] = profileArray[i];
+        ((UA_String *)serverProfileArray->value.data.value.value.data)[i] = profileArray[i];
+    serverProfileArray->value.data.value.hasValue = true;
     serverProfileArray->valueRank = 1;
     serverProfileArray->minimumSamplingInterval = 1.0;
-    addNodeInternal(server, (UA_Node*)serverProfileArray,
-                    UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES), nodeIdHasProperty);
-    addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_SERVERPROFILEARRAY),
-                         nodeIdHasTypeDefinition, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE), true);
+    addNodeInternalWithType(server, (UA_Node*)serverProfileArray,
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES),
+                            nodeIdHasProperty, UA_NODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE));
 
     UA_VariableNode *softwareCertificates = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)softwareCertificates, "SoftwareCertificates");
     softwareCertificates->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERCAPABILITIES_SOFTWARECERTIFICATES;
-    softwareCertificates->value.variant.value.type = &UA_TYPES[UA_TYPES_SIGNEDSOFTWARECERTIFICATE];
-    addNodeInternal(server, (UA_Node*)softwareCertificates,
-                    UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES), nodeIdHasProperty);
-    addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_SOFTWARECERTIFICATES),
-                         nodeIdHasTypeDefinition, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE), true);
+    softwareCertificates->dataType = UA_TYPES[UA_TYPES_SIGNEDSOFTWARECERTIFICATE].typeId;
+    addNodeInternalWithType(server, (UA_Node*)softwareCertificates,
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES),
+                            nodeIdHasProperty, UA_NODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE));
 
     UA_VariableNode *maxQueryContinuationPoints = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)maxQueryContinuationPoints, "MaxQueryContinuationPoints");
     maxQueryContinuationPoints->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERCAPABILITIES_MAXQUERYCONTINUATIONPOINTS;
-    maxQueryContinuationPoints->value.variant.value.type = &UA_TYPES[UA_TYPES_UINT16];
-    maxQueryContinuationPoints->value.variant.value.data = UA_UInt16_new();
-    //FIXME
-    *((UA_UInt16*)maxQueryContinuationPoints->value.variant.value.data) = 0;
-    addNodeInternal(server, (UA_Node*)maxQueryContinuationPoints,
-                    UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES), nodeIdHasProperty);
-    addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_MAXQUERYCONTINUATIONPOINTS),
-                         nodeIdHasTypeDefinition, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE), true);
+    UA_Variant_setScalar(&maxQueryContinuationPoints->value.data.value.value,
+                         UA_UInt16_new(), &UA_TYPES[UA_TYPES_UINT16]);
+    maxQueryContinuationPoints->value.data.value.hasValue = true;
+    addNodeInternalWithType(server, (UA_Node*)maxQueryContinuationPoints,
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES),
+                            nodeIdHasProperty, UA_NODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE));
 
     UA_VariableNode *maxHistoryContinuationPoints = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)maxHistoryContinuationPoints, "MaxHistoryContinuationPoints");
     maxHistoryContinuationPoints->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERCAPABILITIES_MAXHISTORYCONTINUATIONPOINTS;
-    maxHistoryContinuationPoints->value.variant.value.type = &UA_TYPES[UA_TYPES_UINT16];
-    maxHistoryContinuationPoints->value.variant.value.data = UA_UInt16_new();
-    //FIXME
-    *((UA_UInt16*)maxHistoryContinuationPoints->value.variant.value.data) = 0;
-    addNodeInternal(server, (UA_Node*)maxHistoryContinuationPoints,
-                    UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES), nodeIdHasProperty);
-    addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_MAXHISTORYCONTINUATIONPOINTS),
-                         nodeIdHasTypeDefinition, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE), true);
+    UA_Variant_setScalar(&maxHistoryContinuationPoints->value.data.value.value,
+                         UA_UInt16_new(), &UA_TYPES[UA_TYPES_UINT16]);
+    maxHistoryContinuationPoints->value.data.value.hasValue = true;
+    addNodeInternalWithType(server, (UA_Node*)maxHistoryContinuationPoints,
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES),
+                            nodeIdHasProperty, UA_NODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE));
 
     UA_VariableNode *minSupportedSampleRate = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)minSupportedSampleRate, "MinSupportedSampleRate");
     minSupportedSampleRate->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERCAPABILITIES_MINSUPPORTEDSAMPLERATE;
-    minSupportedSampleRate->value.variant.value.type = &UA_TYPES[UA_TYPES_DOUBLE];
-    minSupportedSampleRate->value.variant.value.data = UA_Double_new();
-    //FIXME
-    *((UA_Double*)minSupportedSampleRate->value.variant.value.data) = 0.0;
-    addNodeInternal(server, (UA_Node*)minSupportedSampleRate,
-                    UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES), nodeIdHasProperty);
-    addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_MINSUPPORTEDSAMPLERATE),
-                         nodeIdHasTypeDefinition, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE), true);
+    UA_Variant_setScalar(&minSupportedSampleRate->value.data.value.value,
+                         UA_Double_new(), &UA_TYPES[UA_TYPES_DOUBLE]);
+    minSupportedSampleRate->value.data.value.hasValue = true;
+    addNodeInternalWithType(server, (UA_Node*)minSupportedSampleRate,
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES),
+                            nodeIdHasProperty, UA_NODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE));
 
     UA_ObjectNode *modellingRules = UA_NodeStore_newObjectNode();
     copyNames((UA_Node*)modellingRules, "ModellingRules");
     modellingRules->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERCAPABILITIES_MODELLINGRULES;
-    addNodeInternal(server, (UA_Node*)modellingRules,
-                    UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES), nodeIdHasProperty);
-    addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_MODELLINGRULES),
-                         nodeIdHasTypeDefinition, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), true);
+    addNodeInternalWithType(server, (UA_Node*)modellingRules,
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES), nodeIdHasProperty,
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE));
 
     UA_ObjectNode *aggregateFunctions = UA_NodeStore_newObjectNode();
     copyNames((UA_Node*)aggregateFunctions, "AggregateFunctions");
     aggregateFunctions->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERCAPABILITIES_AGGREGATEFUNCTIONS;
-    addNodeInternal(server, (UA_Node*)aggregateFunctions,
-                    UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES), nodeIdHasProperty);
-    addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_AGGREGATEFUNCTIONS),
-                         nodeIdHasTypeDefinition, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), true);
+    addNodeInternalWithType(server, (UA_Node*)aggregateFunctions,
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES),
+                            nodeIdHasProperty, UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE));
 
     UA_ObjectNode *serverdiagnostics = UA_NodeStore_newObjectNode();
     copyNames((UA_Node*)serverdiagnostics, "ServerDiagnostics");
     serverdiagnostics->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERDIAGNOSTICS;
-    addNodeInternal(server, (UA_Node*)serverdiagnostics,
-                    UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER), nodeIdHasComponent);
-    addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS),
-                         nodeIdHasTypeDefinition, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVERDIAGNOSTICSTYPE), true);
+    addNodeInternalWithType(server, (UA_Node*)serverdiagnostics,
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER),
+                            nodeIdHasComponent, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVERDIAGNOSTICSTYPE));
 
     UA_VariableNode *enabledFlag = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)enabledFlag, "EnabledFlag");
     enabledFlag->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERDIAGNOSTICS_ENABLEDFLAG;
-    enabledFlag->value.variant.value.data = UA_Boolean_new(); //initialized as false
-    enabledFlag->value.variant.value.type = &UA_TYPES[UA_TYPES_BOOLEAN];
+    UA_Variant_setScalar(&enabledFlag->value.data.value.value, UA_Boolean_new(),
+                         &UA_TYPES[UA_TYPES_BOOLEAN]);
+    enabledFlag->value.data.value.hasValue = true;
     enabledFlag->valueRank = 1;
     enabledFlag->minimumSamplingInterval = 1.0;
-    addNodeInternal(server, (UA_Node*)enabledFlag,
-                    UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS), nodeIdHasProperty);
-    addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS_ENABLEDFLAG),
-                         nodeIdHasTypeDefinition, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE), true);
+    addNodeInternalWithType(server, (UA_Node*)enabledFlag,
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS),
+                            nodeIdHasProperty, UA_NODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE));
 
     UA_VariableNode *serverstatus = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)serverstatus, "ServerStatus");
     serverstatus->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS);
     serverstatus->valueSource = UA_VALUESOURCE_DATASOURCE;
-    serverstatus->value.dataSource = (UA_DataSource) {.handle = server, .read = readStatus, .write = NULL};
-    addNodeInternal(server, (UA_Node*)serverstatus, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER), nodeIdHasComponent);
-    addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS), nodeIdHasTypeDefinition,
-                         UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVERSTATUSTYPE), true);
+    serverstatus->value.dataSource = (UA_DataSource) {.handle = server, .read = readStatus,
+                                                      .write = NULL};
+    addNodeInternalWithType(server, (UA_Node*)serverstatus, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER),
+                            nodeIdHasComponent, UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE));
 
     UA_VariableNode *starttime = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)starttime, "StartTime");
     starttime->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_STARTTIME);
-    starttime->value.variant.value.storageType = UA_VARIANT_DATA_NODELETE;
-    starttime->value.variant.value.data = &server->startTime;
-    starttime->value.variant.value.type = &UA_TYPES[UA_TYPES_DATETIME];
-    addNodeInternal(server, (UA_Node*)starttime, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS),
-                    nodeIdHasComponent);
-    addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_STARTTIME),
-                         nodeIdHasTypeDefinition, expandedNodeIdBaseDataVariabletype, true);
+    UA_Variant_setScalarCopy(&starttime->value.data.value.value,
+                             &server->startTime, &UA_TYPES[UA_TYPES_DATETIME]);
+    starttime->value.data.value.hasValue = true;
+    addNodeInternalWithType(server, (UA_Node*)starttime,
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS),
+                            nodeIdHasComponent, UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE));
 
     UA_VariableNode *currenttime = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)currenttime, "CurrentTime");
@@ -1052,142 +1055,136 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     currenttime->valueSource = UA_VALUESOURCE_DATASOURCE;
     currenttime->value.dataSource = (UA_DataSource) {.handle = NULL, .read = readCurrentTime,
                                                      .write = NULL};
-    addNodeInternal(server, (UA_Node*)currenttime,
-                    UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS), nodeIdHasComponent);
-    addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME),
-                         nodeIdHasTypeDefinition, expandedNodeIdBaseDataVariabletype, true);
+    addNodeInternalWithType(server, (UA_Node*)currenttime,
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS),
+                            nodeIdHasComponent, UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE));
 
     UA_VariableNode *state = UA_NodeStore_newVariableNode();
-    UA_ServerState *stateEnum = UA_ServerState_new();
-    *stateEnum = UA_SERVERSTATE_RUNNING;
     copyNames((UA_Node*)state, "State");
     state->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERSTATUS_STATE;
-    state->value.variant.value.type = &UA_TYPES[UA_TYPES_SERVERSTATE];
-    state->value.variant.value.arrayLength = 0;
-    state->value.variant.value.data = stateEnum; // points into the other object.
-    addNodeInternal(server, (UA_Node*)state, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS),
-                    nodeIdHasComponent);
-    addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_STATE),
-                         nodeIdHasTypeDefinition, expandedNodeIdBaseDataVariabletype, true);
+    UA_Variant_setScalar(&state->value.data.value.value, UA_ServerState_new(),
+                         &UA_TYPES[UA_TYPES_SERVERSTATE]);
+    state->value.data.value.hasValue = true;
+    state->minimumSamplingInterval = 500.0f;
+    addNodeInternalWithType(server, (UA_Node*)state, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS),
+                            nodeIdHasComponent, UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE));
 
     UA_VariableNode *buildinfo = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)buildinfo, "BuildInfo");
     buildinfo->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO);
-    UA_Variant_setScalarCopy(&buildinfo->value.variant.value,
-                             &server->config.buildInfo,
-                             &UA_TYPES[UA_TYPES_BUILDINFO]);
-    addNodeInternal(server, (UA_Node*)buildinfo,
-                    UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS), nodeIdHasComponent);
-    addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO),
-                         nodeIdHasTypeDefinition, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BUILDINFOTYPE), true);
+    UA_Variant_setScalarCopy(&buildinfo->value.data.value.value,
+                             &server->config.buildInfo, &UA_TYPES[UA_TYPES_BUILDINFO]);
+    buildinfo->value.data.value.hasValue = true;
+    addNodeInternalWithType(server, (UA_Node*)buildinfo,
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS),
+                            nodeIdHasComponent, UA_NODEID_NUMERIC(0, UA_NS0ID_BUILDINFOTYPE));
 
     UA_VariableNode *producturi = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)producturi, "ProductUri");
     producturi->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_PRODUCTURI);
-    UA_Variant_setScalarCopy(&producturi->value.variant.value, &server->config.buildInfo.productUri,
+    UA_Variant_setScalarCopy(&producturi->value.data.value.value, &server->config.buildInfo.productUri,
                              &UA_TYPES[UA_TYPES_STRING]);
-    producturi->value.variant.value.type = &UA_TYPES[UA_TYPES_STRING];
-    addNodeInternal(server, (UA_Node*)producturi,
-                    UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO), nodeIdHasComponent);
-    addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_PRODUCTURI),
-                         nodeIdHasTypeDefinition, expandedNodeIdBaseDataVariabletype, true);
+    producturi->value.data.value.hasValue = true;
+    addNodeInternalWithType(server, (UA_Node*)producturi,
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO),
+                            nodeIdHasComponent, UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE));
 
     UA_VariableNode *manufacturername = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)manufacturername, "ManufacturerName");
     manufacturername->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_MANUFACTURERNAME);
-    UA_Variant_setScalarCopy(&manufacturername->value.variant.value,
+    UA_Variant_setScalarCopy(&manufacturername->value.data.value.value,
                              &server->config.buildInfo.manufacturerName,
                              &UA_TYPES[UA_TYPES_STRING]);
-    addNodeInternal(server, (UA_Node*)manufacturername,
-                    UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO), nodeIdHasComponent);
-    addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_MANUFACTURERNAME),
-                         nodeIdHasTypeDefinition, expandedNodeIdBaseDataVariabletype, true);
+    manufacturername->value.data.value.hasValue = true;
+    addNodeInternalWithType(server, (UA_Node*)manufacturername,
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO),
+                            nodeIdHasComponent, UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE));
 
     UA_VariableNode *productname = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)productname, "ProductName");
     productname->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_PRODUCTNAME);
-    UA_Variant_setScalarCopy(&productname->value.variant.value, &server->config.buildInfo.productName,
+    UA_Variant_setScalarCopy(&productname->value.data.value.value, &server->config.buildInfo.productName,
                              &UA_TYPES[UA_TYPES_STRING]);
-    addNodeInternal(server, (UA_Node*)productname,
-                    UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO), nodeIdHasComponent);
-    addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_PRODUCTNAME),
-                         nodeIdHasTypeDefinition, expandedNodeIdBaseDataVariabletype, true);
+    productname->value.data.value.hasValue = true;
+    addNodeInternalWithType(server, (UA_Node*)productname,
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO),
+                            nodeIdHasComponent, UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE));
 
     UA_VariableNode *softwareversion = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)softwareversion, "SoftwareVersion");
     softwareversion->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_SOFTWAREVERSION);
-    UA_Variant_setScalarCopy(&softwareversion->value.variant.value, &server->config.buildInfo.softwareVersion,
-                             &UA_TYPES[UA_TYPES_STRING]);
-    addNodeInternal(server, (UA_Node*)softwareversion,
-                    UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO), nodeIdHasComponent);
-    addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_SOFTWAREVERSION),
-                         nodeIdHasTypeDefinition, expandedNodeIdBaseDataVariabletype, true);
+    UA_Variant_setScalarCopy(&softwareversion->value.data.value.value,
+                             &server->config.buildInfo.softwareVersion, &UA_TYPES[UA_TYPES_STRING]);
+    softwareversion->value.data.value.hasValue = true;
+    addNodeInternalWithType(server, (UA_Node*)softwareversion,
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO),
+                            nodeIdHasComponent, UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE));
 
     UA_VariableNode *buildnumber = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)buildnumber, "BuildNumber");
     buildnumber->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_BUILDNUMBER);
-    UA_Variant_setScalarCopy(&buildnumber->value.variant.value, &server->config.buildInfo.buildNumber,
+    UA_Variant_setScalarCopy(&buildnumber->value.data.value.value, &server->config.buildInfo.buildNumber,
                              &UA_TYPES[UA_TYPES_STRING]);
-    addNodeInternal(server, (UA_Node*)buildnumber,
-                    UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO), nodeIdHasComponent);
-    addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_BUILDNUMBER),
-                         nodeIdHasTypeDefinition, expandedNodeIdBaseDataVariabletype, true);
+    buildnumber->value.data.value.hasValue = true;
+    addNodeInternalWithType(server, (UA_Node*)buildnumber,
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO),
+                            nodeIdHasComponent, UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE));
 
     UA_VariableNode *builddate = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)builddate, "BuildDate");
     builddate->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_BUILDDATE);
-    UA_Variant_setScalarCopy(&builddate->value.variant.value, &server->config.buildInfo.buildDate,
+    UA_Variant_setScalarCopy(&builddate->value.data.value.value, &server->config.buildInfo.buildDate,
                              &UA_TYPES[UA_TYPES_DATETIME]);
-    addNodeInternal(server, (UA_Node*)builddate,
-                    UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO), nodeIdHasComponent);
-    addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_BUILDNUMBER),
-                         nodeIdHasTypeDefinition, expandedNodeIdBaseDataVariabletype, true);
+    builddate->value.data.value.hasValue = true;
+    addNodeInternalWithType(server, (UA_Node*)builddate,
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO),
+                            nodeIdHasComponent, UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE));
 
     UA_VariableNode *secondstillshutdown = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)secondstillshutdown, "SecondsTillShutdown");
     secondstillshutdown->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_SECONDSTILLSHUTDOWN);
-    secondstillshutdown->value.variant.value.data = UA_UInt32_new();
-    secondstillshutdown->value.variant.value.type = &UA_TYPES[UA_TYPES_UINT32];
-    addNodeInternal(server, (UA_Node*)secondstillshutdown,
-                    UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS), nodeIdHasComponent);
-    addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_SECONDSTILLSHUTDOWN),
-                         nodeIdHasTypeDefinition, expandedNodeIdBaseDataVariabletype, true);
+    UA_Variant_setScalar(&secondstillshutdown->value.data.value.value, UA_UInt32_new(),
+                         &UA_TYPES[UA_TYPES_UINT32]);
+    secondstillshutdown->value.data.value.hasValue = true;
+    addNodeInternalWithType(server, (UA_Node*)secondstillshutdown,
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS),
+                            nodeIdHasComponent, UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE));
 
     UA_VariableNode *shutdownreason = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)shutdownreason, "ShutdownReason");
     shutdownreason->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_SHUTDOWNREASON);
-    shutdownreason->value.variant.value.data = UA_LocalizedText_new();
-    shutdownreason->value.variant.value.type = &UA_TYPES[UA_TYPES_LOCALIZEDTEXT];
-    addNodeInternal(server, (UA_Node*)shutdownreason,
-                    UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS), nodeIdHasComponent);
-    addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_SHUTDOWNREASON),
-                         nodeIdHasTypeDefinition, expandedNodeIdBaseDataVariabletype, true);
+    UA_Variant_setScalar(&shutdownreason->value.data.value.value, UA_LocalizedText_new(),
+                         &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
+    shutdownreason->value.data.value.hasValue = true;
+    addNodeInternalWithType(server, (UA_Node*)shutdownreason,
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS),
+                            nodeIdHasComponent, UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE));
 
     UA_VariableNode *servicelevel = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)servicelevel, "ServiceLevel");
     servicelevel->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVICELEVEL);
     servicelevel->valueSource = UA_VALUESOURCE_DATASOURCE;
-    servicelevel->value.dataSource = (UA_DataSource) {.handle = server, .read = readServiceLevel, .write = NULL};
-    addNodeInternal(server, (UA_Node*)servicelevel,
-                    UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER), nodeIdHasComponent);
-    addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVICELEVEL),
-                         nodeIdHasTypeDefinition, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE), true);
+    servicelevel->value.dataSource = (UA_DataSource) {.handle = server, .read = readServiceLevel,
+                                                      .write = NULL};
+    addNodeInternalWithType(server, (UA_Node*)servicelevel,
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER), nodeIdHasComponent,
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE));
 
     UA_VariableNode *auditing = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)auditing, "Auditing");
     auditing->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_AUDITING);
     auditing->valueSource = UA_VALUESOURCE_DATASOURCE;
     auditing->value.dataSource = (UA_DataSource) {.handle = server, .read = readAuditing, .write = NULL};
-    addNodeInternal(server, (UA_Node*)auditing,
-                    UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER), nodeIdHasComponent);
-    addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_AUDITING),
-                         nodeIdHasTypeDefinition, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE), true);
+    addNodeInternalWithType(server, (UA_Node*)auditing,
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER), nodeIdHasComponent,
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE));
 
     UA_ObjectNode *vendorServerInfo = UA_NodeStore_newObjectNode();
     copyNames((UA_Node*)vendorServerInfo, "VendorServerInfo");
     vendorServerInfo->nodeId.identifier.numeric = UA_NS0ID_SERVER_VENDORSERVERINFO;
-    addNodeInternal(server, (UA_Node*)vendorServerInfo,
-                    UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER), nodeIdHasProperty);
+    addNodeInternalWithType(server, (UA_Node*)vendorServerInfo,
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER), nodeIdHasProperty,
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE));
     /*
     addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_VENDORSERVERINFO),
                          nodeIdHasTypeDefinition, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_VENDORSERVERINFOTYPE), true);
@@ -1197,8 +1194,9 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     UA_ObjectNode *serverRedundancy = UA_NodeStore_newObjectNode();
     copyNames((UA_Node*)serverRedundancy, "ServerRedundancy");
     serverRedundancy->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERREDUNDANCY;
-    addNodeInternal(server, (UA_Node*)serverRedundancy,
-                    UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER), nodeIdHasProperty);
+    addNodeInternalWithType(server, (UA_Node*)serverRedundancy,
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER), nodeIdHasProperty,
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE));
     /*
     addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERREDUNDANCY),
                          nodeIdHasTypeDefinition, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVERREDUNDANCYTYPE), true);
@@ -1208,12 +1206,12 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     copyNames((UA_Node*)redundancySupport, "RedundancySupport");
     redundancySupport->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERREDUNDANCY_REDUNDANCYSUPPORT);
     //FIXME: enum is needed for type letting it uninitialized for now
-    redundancySupport->value.variant.value.data = UA_Int32_new();
-    redundancySupport->value.variant.value.type = &UA_TYPES[UA_TYPES_INT32];
-    addNodeInternal(server, (UA_Node*)redundancySupport, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERREDUNDANCY),
-            nodeIdHasComponent);
-    addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERREDUNDANCY_REDUNDANCYSUPPORT), nodeIdHasTypeDefinition,
-                         UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE), true);
+    UA_Variant_setScalar(&redundancySupport->value.data.value.value, UA_Int32_new(),
+                         &UA_TYPES[UA_TYPES_INT32]);
+    redundancySupport->value.data.value.hasValue = true;
+    addNodeInternalWithType(server, (UA_Node*)redundancySupport,
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERREDUNDANCY),
+                            nodeIdHasProperty, UA_NODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE));
 
 #if defined(UA_ENABLE_METHODCALLS) && defined(UA_ENABLE_SUBSCRIPTIONS)
     UA_Argument inputArguments;
@@ -1245,9 +1243,7 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
         UA_QUALIFIEDNAME(0, "GetMonitoredItems"), addmethodattributes,
         GetMonitoredItems, /* callback of the method node */
         NULL, /* handle passed with the callback */
-        1, &inputArguments,
-        2, outputArguments,
-        NULL);
+        1, &inputArguments, 2, outputArguments, NULL);
 #endif
 
     return server;

+ 7 - 6
src/server/ua_server_binary.c

@@ -346,16 +346,15 @@ processOPN(UA_Connection *connection, UA_Server *server,
     if(connection->channel && channelId != connection->channel->securityToken.channelId)
         retval |= UA_STATUSCODE_BADREQUESTTYPEINVALID;
 
+    /* Decode the request */
     UA_AsymmetricAlgorithmSecurityHeader asymHeader;
-    retval |= UA_AsymmetricAlgorithmSecurityHeader_decodeBinary(msg, offset, &asymHeader);
-
     UA_SequenceHeader seqHeader;
-    retval |= UA_SequenceHeader_decodeBinary(msg, offset, &seqHeader);
-
     UA_NodeId requestType;
-    retval |= UA_NodeId_decodeBinary(msg, offset, &requestType);
-
     UA_OpenSecureChannelRequest r;
+
+    retval |= UA_AsymmetricAlgorithmSecurityHeader_decodeBinary(msg, offset, &asymHeader);
+    retval |= UA_SequenceHeader_decodeBinary(msg, offset, &seqHeader);
+    retval |= UA_NodeId_decodeBinary(msg, offset, &requestType);
     retval |= UA_OpenSecureChannelRequest_decodeBinary(msg, offset, &r);
 
     /* Could not decode or wrong service type */
@@ -592,9 +591,11 @@ processRequest(UA_SecureChannel *channel, UA_Server *server,
         UA_LOG_INFO_CHANNEL(server->config.logger, channel, "Could not send the message over "
                              "the SecureChannel with error code 0x%08x", retval);
 
+#ifdef UA_ENABLE_SUBSCRIPTIONS
     /* See if we need to return publish requests without a subscription */
     if(session && requestType == &UA_TYPES[UA_TYPES_DELETESUBSCRIPTIONSREQUEST])
         UA_Session_answerPublishRequestsWithoutSubscription(session);
+#endif
 
     /* Clean up */
     UA_deleteMembers(request, requestType);

+ 112 - 7
src/server/ua_server_internal.h

@@ -78,6 +78,13 @@ struct UA_Server {
     UA_ServerConfig config;
 };
 
+/*****************/
+/* Node Handling */
+/*****************/
+
+void UA_Node_deleteMembersAnyNodeClass(UA_Node *node);
+UA_StatusCode UA_Node_copyAnyNodeClass(const UA_Node *src, UA_Node *dst);
+
 typedef UA_StatusCode (*UA_EditNodeCallback)(UA_Server*, UA_Session*, UA_Node*, const void*);
 
 /* Calls callback on the node. In the multithreaded case, the node is copied before and replaced in
@@ -91,16 +98,114 @@ UA_StatusCode UA_Server_delayedCallback(UA_Server *server, UA_ServerCallback cal
 UA_StatusCode UA_Server_delayedFree(UA_Server *server, void *data);
 void UA_Server_deleteAllRepeatedJobs(UA_Server *server);
 
-#ifdef UA_BUILD_UNIT_TESTS
-UA_StatusCode parse_numericrange(const UA_String *str, UA_NumericRange *range);
-#endif
+/* Add an existing node. The node is assumed to be "finished", i.e. no
+ * instantiation from inheritance is necessary. Instantiationcallback and
+ * addedNodeId may be NULL. */
+UA_StatusCode
+Service_AddNodes_existing(UA_Server *server, UA_Session *session, UA_Node *node,
+                          const UA_NodeId *parentNodeId,
+                          const UA_NodeId *referenceTypeId,
+                          const UA_NodeId *typeDefinition,
+                          UA_InstantiationCallback *instantiationCallback,
+                          UA_NodeId *addedNodeId);
+
+/*********************/
+/* Utility Functions */
+/*********************/
+
+UA_StatusCode
+parse_numericrange(const UA_String *str, UA_NumericRange *range);
+
+UA_Boolean
+UA_Node_hasSubTypeOrInstances(const UA_Node *node);
+
+const UA_VariableTypeNode *
+getVariableNodeType(UA_Server *server, const UA_VariableNode *node);
+
+const UA_ObjectTypeNode *
+getObjectNodeType(UA_Server *server, const UA_ObjectNode *node);
 
 UA_StatusCode
-getTypeHierarchy(UA_NodeStore *ns, const UA_NodeId *root, UA_NodeId **reftypes, size_t *reftypes_count);
+getTypeHierarchy(UA_NodeStore *ns, const UA_NodeId *root,
+                 UA_NodeId **reftypes, size_t *reftypes_count);
 
 UA_StatusCode
-isNodeInTree(UA_NodeStore *ns, const UA_NodeId *rootNode, const UA_NodeId *nodeToFind,
-             const UA_NodeId *referenceTypeIds, size_t referenceTypeIdsSize,
-             size_t maxDepth, UA_Boolean *found);
+isNodeInTree(UA_NodeStore *ns, const UA_NodeId *rootNode,
+             const UA_NodeId *nodeToFind, const UA_NodeId *referenceTypeIds,
+             size_t referenceTypeIdsSize, UA_Boolean *found);
+
+const UA_Node *
+getNodeType(UA_Server *server, const UA_Node *node);
+
+/***************************************/
+/* Check Information Model Consistency */
+/***************************************/
+
+UA_StatusCode
+UA_VariableNode_setArrayDimensions(UA_Server *server, UA_VariableNode *node,
+                                   const UA_VariableTypeNode *vt,
+                                   size_t arrayDimensionsSize, UA_UInt32 *arrayDimensions);
+
+UA_StatusCode
+UA_VariableNode_setValueRank(UA_Server *server, UA_VariableNode *node,
+                             const UA_VariableTypeNode *vt,
+                             const UA_Int32 valueRank);
+
+UA_StatusCode
+UA_VariableNode_setDataType(UA_Server *server, UA_VariableNode *node,
+                            const UA_VariableTypeNode *vt,
+                            const UA_NodeId *dataType);
+
+UA_StatusCode
+UA_VariableNode_setValue(UA_Server *server, UA_VariableNode *node,
+                         const UA_DataValue *value, const UA_String *indexRange);
+
+UA_StatusCode
+UA_Variant_matchVariableDefinition(UA_Server *server, const UA_NodeId *variableDataTypeId,
+                                   UA_Int32 variableValueRank, size_t variableArrayDimensionsSize,
+                                   const UA_UInt32 *variableArrayDimensions, const UA_Variant *value,
+                                   const UA_NumericRange *range, UA_Variant *equivalent);
+
+/*******************/
+/* Single-Services */
+/*******************/
+
+/* Some services take an array of "independent" requests. The single-services
+   are stored here to keep ua_services.h clean for documentation purposes. */
+
+UA_StatusCode
+Service_AddReferences_single(UA_Server *server, UA_Session *session,
+                             const UA_AddReferencesItem *item);
+
+UA_StatusCode
+Service_DeleteNodes_single(UA_Server *server, UA_Session *session,
+                           const UA_NodeId *nodeId, UA_Boolean deleteReferences);
+
+UA_StatusCode
+Service_DeleteReferences_single(UA_Server *server, UA_Session *session,
+                                const UA_DeleteReferencesItem *item);
+
+void Service_Browse_single(UA_Server *server, UA_Session *session,
+                           struct ContinuationPointEntry *cp,
+                           const UA_BrowseDescription *descr,
+                           UA_UInt32 maxrefs, UA_BrowseResult *result);
+
+void
+Service_TranslateBrowsePathsToNodeIds_single(UA_Server *server, UA_Session *session,
+                                             const UA_BrowsePath *path,
+                                             UA_BrowsePathResult *result);
+
+void Service_Read_single(UA_Server *server, UA_Session *session,
+                         UA_TimestampsToReturn timestamps,
+                         const UA_ReadValueId *id, UA_DataValue *v);
+
+UA_StatusCode Service_Write_single(UA_Server *server, UA_Session *session,
+                                   const UA_WriteValue *wvalue);
+
+void Service_Call_single(UA_Server *server, UA_Session *session,
+                         const UA_CallMethodRequest *request,
+                         UA_CallMethodResult *result);
+
+
 
 #endif /* UA_SERVER_INTERNAL_H_ */

+ 278 - 0
src/server/ua_server_utils.c

@@ -0,0 +1,278 @@
+#include "ua_server_internal.h"
+
+/**********************/
+/* Parse NumericRange */
+/**********************/
+
+static size_t
+readDimension(UA_Byte *buf, size_t buflen, UA_NumericRangeDimension *dim) {
+    size_t progress = UA_readNumber(buf, buflen, &dim->min);
+    if(progress == 0)
+        return 0;
+    if(buflen <= progress + 1 || buf[progress] != ':') {
+        dim->max = dim->min;
+        return progress;
+    }
+
+    progress++;
+    size_t progress2 = UA_readNumber(&buf[progress], buflen - progress, &dim->max);
+    if(progress2 == 0)
+        return 0;
+
+    /* invalid range */
+    if(dim->min >= dim->max)
+        return 0;
+    
+    return progress + progress2;
+}
+
+UA_StatusCode
+parse_numericrange(const UA_String *str, UA_NumericRange *range) {
+    size_t idx = 0;
+    size_t dimensionsMax = 0;
+    UA_NumericRangeDimension *dimensions = NULL;
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    size_t offset = 0;
+    while(true) {
+        /* alloc dimensions */
+        if(idx >= dimensionsMax) {
+            UA_NumericRangeDimension *newds;
+            size_t newdssize = sizeof(UA_NumericRangeDimension) * (dimensionsMax + 2);
+            newds = UA_realloc(dimensions, newdssize);
+            if(!newds) {
+                retval = UA_STATUSCODE_BADOUTOFMEMORY;
+                break;
+            }
+            dimensions = newds;
+            dimensionsMax = dimensionsMax + 2;
+        }
+
+        /* read the dimension */
+        size_t progress = readDimension(&str->data[offset], str->length - offset,
+                                        &dimensions[idx]);
+        if(progress == 0) {
+            retval = UA_STATUSCODE_BADINDEXRANGEINVALID;
+            break;
+        }
+        offset += progress;
+        idx++;
+
+        /* loop into the next dimension */
+        if(offset >= str->length)
+            break;
+
+        if(str->data[offset] != ',') {
+            retval = UA_STATUSCODE_BADINDEXRANGEINVALID;
+            break;
+        }
+        offset++;
+    }
+
+    if(retval == UA_STATUSCODE_GOOD && idx > 0) {
+        range->dimensions = dimensions;
+        range->dimensionsSize = idx;
+    } else
+        UA_free(dimensions);
+
+    return retval;
+}
+
+/********************************/
+/* Information Model Operations */
+/********************************/
+
+/* Returns the type and all subtypes. We start with an array with a single root
+ * nodeid. When a relevant reference is found, we add the nodeids to the back of
+ * the array and increase the size. Since the hierarchy is not cyclic, we can
+ * safely progress in the array to process the newly found referencetype
+ * nodeids. */
+UA_StatusCode
+getTypeHierarchy(UA_NodeStore *ns, const UA_NodeId *root,
+                 UA_NodeId **typeHierarchy, size_t *typeHierarchySize) {
+    const UA_Node *node = UA_NodeStore_get(ns, root);
+    if(!node)
+        return UA_STATUSCODE_BADNOMATCH;
+    if(node->nodeClass != UA_NODECLASS_REFERENCETYPE)
+        return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
+
+    size_t results_size = 20; // probably too big, but saves mallocs
+    UA_NodeId *results = UA_malloc(sizeof(UA_NodeId) * results_size);
+    if(!results)
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+
+    UA_StatusCode retval = UA_NodeId_copy(root, &results[0]);
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_free(results);
+        return retval;
+    }
+
+    size_t idx = 0; // where are we currently in the array?
+    size_t last = 0; // where is the last element in the array?
+    const UA_NodeId hasSubtypeNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE);
+    do {
+        node = UA_NodeStore_get(ns, &results[idx]);
+        if(!node || node->nodeClass != UA_NODECLASS_REFERENCETYPE)
+            continue;
+        for(size_t i = 0; i < node->referencesSize; i++) {
+            if(node->references[i].isInverse == true ||
+               !UA_NodeId_equal(&hasSubtypeNodeId, &node->references[i].referenceTypeId))
+                continue;
+
+            if(++last >= results_size) { // is the array big enough?
+                UA_NodeId *new_results = UA_realloc(results, sizeof(UA_NodeId) * results_size * 2);
+                if(!new_results) {
+                    retval = UA_STATUSCODE_BADOUTOFMEMORY;
+                    break;
+                }
+                results = new_results;
+                results_size *= 2;
+            }
+
+            retval = UA_NodeId_copy(&node->references[i].targetId.nodeId, &results[last]);
+            if(retval != UA_STATUSCODE_GOOD) {
+                last--; // for array_delete
+                break;
+            }
+        }
+    } while(++idx <= last && retval == UA_STATUSCODE_GOOD);
+
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_Array_delete(results, last, &UA_TYPES[UA_TYPES_NODEID]);
+        return retval;
+    }
+
+    *typeHierarchy = results;
+    *typeHierarchySize = last + 1;
+    return UA_STATUSCODE_GOOD;
+}
+
+/* Recursively searches "upwards" in the tree following specific reference types */
+UA_StatusCode
+isNodeInTree(UA_NodeStore *ns, const UA_NodeId *leafNode, const UA_NodeId *nodeToFind,
+             const UA_NodeId *referenceTypeIds, size_t referenceTypeIdsSize, UA_Boolean *found) {
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    if(UA_NodeId_equal(leafNode, nodeToFind)) {
+        *found = true;
+        return UA_STATUSCODE_GOOD;
+    }
+
+    const UA_Node *node = UA_NodeStore_get(ns,leafNode);
+    if(!node)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    /* Search upwards in the tree */
+    for(size_t i = 0; i < node->referencesSize; i++) {
+        if(!node->references[i].isInverse)
+            continue;
+
+        /* Recurse only for valid reference types */
+        for(size_t j = 0; j < referenceTypeIdsSize; j++) {
+            if(!UA_NodeId_equal(&node->references[i].referenceTypeId, &referenceTypeIds[j]))
+                continue;
+            retval = isNodeInTree(ns, &node->references[i].targetId.nodeId, nodeToFind,
+                                  referenceTypeIds, referenceTypeIdsSize, found);
+            if(*found || retval != UA_STATUSCODE_GOOD)
+                return retval;
+            break;
+        }
+    }
+
+    /* Dead end */
+    *found = false;
+    return UA_STATUSCODE_GOOD;
+}
+
+const UA_Node *
+getNodeType(UA_Server *server, const UA_Node *node) {
+    /* The reference to the parent is different for variable and variabletype */ 
+    UA_NodeId parentRef;
+    UA_Boolean inverse;
+    if(node->nodeClass == UA_NODECLASS_VARIABLE ||
+       node->nodeClass == UA_NODECLASS_OBJECT) {
+        parentRef = UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION);
+        inverse = false;
+    } else if(node->nodeClass == UA_NODECLASS_VARIABLETYPE ||
+              /* node->nodeClass == UA_NODECLASS_OBJECTTYPE || // objecttype may have multiple parents */
+              node->nodeClass == UA_NODECLASS_REFERENCETYPE ||
+              node->nodeClass == UA_NODECLASS_DATATYPE) {
+        parentRef = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE);
+        inverse = true;
+    } else {
+        return NULL;
+    }
+
+    /* stop at the first matching candidate */
+    UA_NodeId *parentId = NULL;
+    for(size_t i = 0; i < node->referencesSize; i++) {
+        if(node->references[i].isInverse == inverse &&
+           UA_NodeId_equal(&node->references[i].referenceTypeId, &parentRef)) {
+            parentId = &node->references[i].targetId.nodeId;
+            break;
+        }
+    }
+
+    if(!parentId)
+        return NULL;
+    return UA_NodeStore_get(server->nodestore, parentId);
+}
+
+const UA_VariableTypeNode *
+getVariableNodeType(UA_Server *server, const UA_VariableNode *node) {
+    const UA_Node *type = getNodeType(server, (const UA_Node*)node);
+    if(!type || type->nodeClass != UA_NODECLASS_VARIABLETYPE)
+        return NULL;
+    return (const UA_VariableTypeNode*)type;
+}
+
+const UA_ObjectTypeNode *
+getObjectNodeType(UA_Server *server, const UA_ObjectNode *node) {
+    const UA_Node *type = getNodeType(server, (const UA_Node*)node);
+    if(type->nodeClass != UA_NODECLASS_OBJECTTYPE)
+        return NULL;
+    return (const UA_ObjectTypeNode*)type;
+}
+
+UA_Boolean
+UA_Node_hasSubTypeOrInstances(const UA_Node *node) {
+    const UA_NodeId hasSubType = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE);
+    const UA_NodeId hasTypeDefinition = UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION);
+    for(size_t i = 0; i < node->referencesSize; i++) {
+        if(node->references[i].isInverse == false &&
+           UA_NodeId_equal(&node->references[i].referenceTypeId, &hasSubType))
+            return true;
+        if(node->references[i].isInverse == true &&
+           UA_NodeId_equal(&node->references[i].referenceTypeId, &hasTypeDefinition))
+            return true;
+    }
+    return false;
+}
+
+/* For mulithreading: make a copy of the node, edit and replace.
+ * For singletrheading: edit the original */
+UA_StatusCode
+UA_Server_editNode(UA_Server *server, UA_Session *session,
+                   const UA_NodeId *nodeId, UA_EditNodeCallback callback,
+                   const void *data) {
+    UA_StatusCode retval;
+    do {
+#ifndef UA_ENABLE_MULTITHREADING
+        const UA_Node *node = UA_NodeStore_get(server->nodestore, nodeId);
+        if(!node)
+            return UA_STATUSCODE_BADNODEIDUNKNOWN;
+        UA_Node *editNode = (UA_Node*)(uintptr_t)node; // dirty cast
+        retval = callback(server, session, editNode, data);
+        return retval;
+#else
+        UA_Node *copy = UA_NodeStore_getCopy(server->nodestore, nodeId);
+        if(!copy)
+            return UA_STATUSCODE_BADOUTOFMEMORY;
+        retval = callback(server, session, copy, data);
+        if(retval != UA_STATUSCODE_GOOD) {
+            UA_NodeStore_deleteNode(copy);
+            return retval;
+        }
+        retval = UA_NodeStore_replace(server->nodestore, copy);
+#endif
+    } while(retval != UA_STATUSCODE_GOOD);
+    return UA_STATUSCODE_GOOD;
+}

+ 24 - 58
src/server/ua_services.h

@@ -14,11 +14,24 @@ extern "C" {
  *
  * Services
  * ========
- * The services defined in the OPC UA standard. */
+ *
+ * In OPC UA, all communication is based on service calls, each consisting of a
+ * request and a response message. These messages are defined as data structures
+ * with a binary encoding and listed in :ref:`generated-types`. Since all
+ * Services are pre-defined in the standard, they cannot be modified by the
+ * user. But you can use the :ref:`Call <method-services>` service to invoke
+ * user-defined methods on the server.
+ *
+ * The following service signatures are internal and *not visible to users*.
+ * Still, we present them here for an overview of the capabilities of OPC UA.
+ * Please refer to the :ref:`client` and :ref:`server` API where the services
+ * are exposed to end users. Please see part 4 of the OPC UA standard for the
+ * authoritative definition of the service and their behaviour. */
 /* Most services take as input the server, the current session and pointers to
-   the request and response. The possible error codes are returned as part of
-   the response. */
-typedef void (*UA_Service)(UA_Server*, UA_Session*, const void*, void*);
+   the request and response structures. Possible error codes are returned as
+   part of the response. */
+typedef void (*UA_Service)(UA_Server*, UA_Session*,
+                           const void *request, void *response);
 
 /**
  * Discovery Service Set
@@ -50,7 +63,7 @@ void Service_OpenSecureChannel(UA_Server *server, UA_Connection *connection,
                                const UA_OpenSecureChannelRequest *request,
                                UA_OpenSecureChannelResponse *response);
 
-/** Used to terminate a SecureChannel. */
+/* Used to terminate a SecureChannel. */
 void Service_CloseSecureChannel(UA_Server *server, UA_SecureChannel *channel);
 
 /**
@@ -98,43 +111,24 @@ void Service_AddNodes(UA_Server *server, UA_Session *session,
                       const UA_AddNodesRequest *request,
                       UA_AddNodesResponse *response);
 
-/* Add an existing node. The node is assumed to be "finished", i.e. no
- * instantiation from inheritance is necessary */
-void
-Service_AddNodes_existing(UA_Server *server, UA_Session *session, UA_Node *node,
-                          const UA_NodeId *parentNodeId,
-                          const UA_NodeId *referenceTypeId,
-                          const UA_NodeId *typeDefinition,
-                          UA_InstantiationCallback *instantiationCallback,
-                          UA_AddNodesResult *result);
-
 /* Used to add one or more References to one or more Nodes. */
 void Service_AddReferences(UA_Server *server, UA_Session *session,
                            const UA_AddReferencesRequest *request,
                            UA_AddReferencesResponse *response);
 
-UA_StatusCode Service_AddReferences_single(UA_Server *server,
-                                           UA_Session *session,
-                                           const UA_AddReferencesItem *item);
-
 /* Used to delete one or more Nodes from the AddressSpace. */
 void Service_DeleteNodes(UA_Server *server, UA_Session *session,
                          const UA_DeleteNodesRequest *request,
                          UA_DeleteNodesResponse *response);
 
-UA_StatusCode Service_DeleteNodes_single(UA_Server *server, UA_Session *session,
-                                         const UA_NodeId *nodeId,
-                                         UA_Boolean deleteReferences);
-
 /* Used to delete one or more References of a Node. */
 void Service_DeleteReferences(UA_Server *server, UA_Session *session,
                               const UA_DeleteReferencesRequest *request,
                               UA_DeleteReferencesResponse *response);
 
-UA_StatusCode Service_DeleteReferences_single(UA_Server *server,
-                      UA_Session *session, const UA_DeleteReferencesItem *item);
-
 /**
+ * .. _view-services:
+ *
  * View Service Set
  * ----------------
  * Clients use the browse Services of the View Service Set to navigate through
@@ -147,11 +141,6 @@ void Service_Browse(UA_Server *server, UA_Session *session,
                     const UA_BrowseRequest *request,
                     UA_BrowseResponse *response);
 
-void Service_Browse_single(UA_Server *server, UA_Session *session,
-                           struct ContinuationPointEntry *cp,
-                           const UA_BrowseDescription *descr,
-                           UA_UInt32 maxrefs, UA_BrowseResult *result);
-
 /* Used to request the next set of Browse or BrowseNext response information
  * that is too large to be sent in a single response. "Too large" in this
  * context means that the Server is not able to return a larger response or that
@@ -166,11 +155,6 @@ void Service_TranslateBrowsePathsToNodeIds(UA_Server *server, UA_Session *sessio
              const UA_TranslateBrowsePathsToNodeIdsRequest *request,
              UA_TranslateBrowsePathsToNodeIdsResponse *response);
 
-void Service_TranslateBrowsePathsToNodeIds_single(UA_Server *server,
-                                                  UA_Session *session,
-                                                  const UA_BrowsePath *path,
-                                                  UA_BrowsePathResult *result);
-
 /* Used by Clients to register the Nodes that they know they will access
  * repeatedly (e.g. Write, Call). It allows Servers to set up anything needed so
  * that the access operations will be more efficient. */
@@ -211,10 +195,6 @@ void Service_Read(UA_Server *server, UA_Session *session,
                   const UA_ReadRequest *request,
                   UA_ReadResponse *response);
 
-void Service_Read_single(UA_Server *server, UA_Session *session,
-                         UA_TimestampsToReturn timestamps,
-                         const UA_ReadValueId *id, UA_DataValue *v);
-
 /* Used to write one or more Attributes of one or more Nodes. For constructed
  * Attribute values whose elements are indexed, such as an array, this Service
  * allows Clients to write the entire set of indexed values as a composite, to
@@ -223,18 +203,17 @@ void Service_Write(UA_Server *server, UA_Session *session,
                    const UA_WriteRequest *request,
                    UA_WriteResponse *response);
 
-UA_StatusCode Service_Write_single(UA_Server *server, UA_Session *session,
-                                   const UA_WriteValue *wvalue);
-
 /* Not Implemented: Service_HistoryRead */
 /* Not Implemented: Service_HistoryUpdate */
 
 /**
+ * .. _method-services:
+ *
  * Method Service Set
  * ------------------
  * The Method Service Set defines the means to invoke methods. A method shall be
- * a component of an Object. */
-#ifdef UA_ENABLE_METHODCALLS
+ * a component of an Object. See the section on :ref:`MethodNodes <methodnode>`
+ * for more information. */
 /* Used to call (invoke) a list of Methods. Each method call is invoked within
  * the context of an existing Session. If the Session is terminated, the results
  * of the method's execution cannot be returned to the Client and are
@@ -243,19 +222,12 @@ void Service_Call(UA_Server *server, UA_Session *session,
                   const UA_CallRequest *request,
                   UA_CallResponse *response);
 
-void Service_Call_single(UA_Server *server, UA_Session *session,
-                         const UA_CallMethodRequest *request,
-                         UA_CallMethodResult *result);
-#endif
-
 /**
  * MonitoredItem Service Set
  * -------------------------
  * Clients define MonitoredItems to subscribe to data and Events. Each
  * MonitoredItem identifies the item to be monitored and the Subscription to use
  * to send Notifications. The item to be monitored may be any Node Attribute. */
-#ifdef UA_ENABLE_SUBSCRIPTIONS
-
 /* Used to create and add one or more MonitoredItems to a Subscription. A
  * MonitoredItem is deleted automatically by the Server when the Subscription is
  * deleted. Deleting a MonitoredItem causes its entire set of triggered item
@@ -283,14 +255,10 @@ void Service_SetMonitoringMode(UA_Server *server, UA_Session *session,
 
 /* Not Implemented: Service_SetTriggering */
 
-#endif
-
 /**
  * Subscription Service Set
  * ------------------------
  * Subscriptions are used to report Notifications to the Client. */
-#ifdef UA_ENABLE_SUBSCRIPTIONS
-
 /* Used to create a Subscription. Subscriptions monitor a set of MonitoredItems
  * for Notifications and return them to the Client in response to Publish
  * requests. */
@@ -333,8 +301,6 @@ void Service_DeleteSubscriptions(UA_Server *server, UA_Session *session,
 
 /* Not Implemented: Service_TransferSubscription */
 
-#endif
-
 #ifdef __cplusplus
 } // extern "C"
 #endif

Datei-Diff unterdrückt, da er zu groß ist
+ 531 - 358
src/server/ua_services_attribute.c


+ 47 - 113
src/server/ua_services_call.c

@@ -25,90 +25,15 @@ getArgumentsVariableNode(UA_Server *server, const UA_MethodNode *ofMethod,
 }
 
 static UA_StatusCode
-satisfySignature(UA_Server *server, const UA_Variant *var, const UA_Argument *arg) {
-  if(var == NULL || var->type == NULL) 
-    return UA_STATUSCODE_BADINVALIDARGUMENT;
-  
-  if(!UA_NodeId_equal(&var->type->typeId, &arg->dataType)){
-        if(!UA_NodeId_equal(&var->type->typeId, &UA_TYPES[UA_TYPES_INT32].typeId))
-            return UA_STATUSCODE_BADINVALIDARGUMENT;
-
-        /* enumerations are encoded as int32 -> if provided var is integer, check if arg is an enumeration type */
-        UA_NodeId enumerationNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ENUMERATION);
-        UA_NodeId hasSubTypeNodeId = UA_NODEID_NUMERIC(0,UA_NS0ID_HASSUBTYPE);
-        UA_Boolean found = false;
-        UA_StatusCode retval = isNodeInTree(server->nodestore, &arg->dataType, &enumerationNodeId, &hasSubTypeNodeId, 1, 1, &found);
-        if(retval != UA_STATUSCODE_GOOD)
-            return UA_STATUSCODE_BADINTERNALERROR;
-        if(!found)
-            return UA_STATUSCODE_BADINVALIDARGUMENT;
-    }
-
-    // Note: The namespace compiler will compile nodes with their actual array dimensions
-    // Todo: Check if this is standard conform for scalars
-    if(arg->arrayDimensionsSize > 0 && var->arrayDimensionsSize > 0)
-        if(var->arrayDimensionsSize != arg->arrayDimensionsSize)
-            return UA_STATUSCODE_BADINVALIDARGUMENT;
-
-    UA_Int32 *varDims = var->arrayDimensions;
-    size_t varDimsSize = var->arrayDimensionsSize;
-    UA_Boolean scalar = UA_Variant_isScalar(var);
-
-    /* The dimension 1 is implicit in the array length */
-    UA_Int32 fakeDims;
-    if(!scalar && !varDims) {
-        fakeDims = (UA_Int32)var->arrayLength;
-        varDims = &fakeDims;
-        varDimsSize = 1;
-    }
-
-    /* ValueRank Semantics
-     *  n >= 1: the value is an array with the specified number of dimens*ions.
-     *  n = 0: the value is an array with one or more dimensions.
-     *  n = -1: the value is a scalar.
-     *  n = -2: the value can be a scalar or an array with any number of dimensions.
-     *  n = -3:  the value can be a scalar or a one dimensional array. */
-    switch(arg->valueRank) {
-    case -3:
-        if(varDimsSize > 1)
-            return UA_STATUSCODE_BADINVALIDARGUMENT;
-        break;
-    case -2:
-        break;
-    case -1:
-        if(!scalar)
-            return UA_STATUSCODE_BADINVALIDARGUMENT;
-        break;
-    case 0:
-        if(scalar || !varDims)
-            return UA_STATUSCODE_BADINVALIDARGUMENT;
-        break;
-    default:
-        break;
-    }
-
-    /* Do the variants dimensions match? Check only if defined in the argument. */
-    if(arg->arrayDimensionsSize > 0) {
-        if(arg->arrayDimensionsSize != varDimsSize)
-            return UA_STATUSCODE_BADINVALIDARGUMENT;
-        for(size_t i = 0; i < varDimsSize; i++) {
-            // A value of 0 for an individual dimension indicates that the dimension has a variable	length.
-            if((UA_Int32)arg->arrayDimensions[i]!=0 && (UA_Int32)arg->arrayDimensions[i] != varDims[i])
-                return UA_STATUSCODE_BADINVALIDARGUMENT;
-        }
-    }
-    return UA_STATUSCODE_GOOD;
-}
-
-static UA_StatusCode
-argConformsToDefinition(UA_Server *server, const UA_VariableNode *argRequirements, size_t argsSize, const UA_Variant *args) {
-    if(argRequirements->value.variant.value.type != &UA_TYPES[UA_TYPES_ARGUMENT])
+argumentsConformsToDefinition(UA_Server *server, const UA_VariableNode *argRequirements,
+                              size_t argsSize, const UA_Variant *args) {
+    if(argRequirements->value.data.value.value.type != &UA_TYPES[UA_TYPES_ARGUMENT])
         return UA_STATUSCODE_BADINTERNALERROR;
-    UA_Argument *argReqs = (UA_Argument*)argRequirements->value.variant.value.data;
-    size_t argReqsSize = argRequirements->value.variant.value.arrayLength;
-    if(argRequirements->valueSource != UA_VALUESOURCE_VARIANT)
+    UA_Argument *argReqs = (UA_Argument*)argRequirements->value.data.value.value.data;
+    size_t argReqsSize = argRequirements->value.data.value.value.arrayLength;
+    if(argRequirements->valueSource != UA_VALUESOURCE_DATA)
         return UA_STATUSCODE_BADINTERNALERROR;
-    if(UA_Variant_isScalar(&argRequirements->value.variant.value))
+    if(UA_Variant_isScalar(&argRequirements->value.data.value.value))
         argReqsSize = 1;
     if(argReqsSize > argsSize)
         return UA_STATUSCODE_BADARGUMENTSMISSING;
@@ -117,14 +42,18 @@ argConformsToDefinition(UA_Server *server, const UA_VariableNode *argRequirement
 
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     for(size_t i = 0; i < argReqsSize; i++)
-        retval |= satisfySignature(server, &args[i], &argReqs[i]);
+        retval |= UA_Variant_matchVariableDefinition(server, &argReqs[i].dataType,
+                                                     argReqs[i].valueRank,
+                                                     argReqs[i].arrayDimensionsSize,
+                                                     argReqs[i].arrayDimensions,
+                                                     &args[i], NULL, NULL);
     return retval;
 }
 
 void
-Service_Call_single(UA_Server *server, UA_Session *session, const UA_CallMethodRequest *request,
+Service_Call_single(UA_Server *server, UA_Session *session,
+                    const UA_CallMethodRequest *request,
                     UA_CallMethodResult *result) {
-
     /* Get/verify the method node */
     const UA_MethodNode *methodCalled =
         (const UA_MethodNode*)UA_NodeStore_get(server->nodestore, &request->methodId);
@@ -153,71 +82,76 @@ Service_Call_single(UA_Server *server, UA_Session *session, const UA_CallMethodR
         return;
     }
 
-    /* Verify method/object relations. Object must have a hasComponent or a subtype of hasComponent reference to the method node. */
-    /* Therefore, check every reference between the parent object and the method node if there is a hasComponent (or subtype) reference */
+    /* Verify method/object relations. Object must have a hasComponent or a
+     * subtype of hasComponent reference to the method node. Therefore, check
+     * every reference between the parent object and the method node if there is
+     * a hasComponent (or subtype) reference */
     UA_Boolean found = false;
     UA_NodeId hasComponentNodeId = UA_NODEID_NUMERIC(0,UA_NS0ID_HASCOMPONENT);
     UA_NodeId hasSubTypeNodeId = UA_NODEID_NUMERIC(0,UA_NS0ID_HASSUBTYPE);
-    for(size_t i=0;i<methodCalled->referencesSize;i++){
-        if (methodCalled->references[i].isInverse && UA_NodeId_equal(&methodCalled->references[i].targetId.nodeId,&withObject->nodeId)){
+    for(size_t i = 0; i < methodCalled->referencesSize; i++) {
+        if(methodCalled->references[i].isInverse &&
+           UA_NodeId_equal(&methodCalled->references[i].targetId.nodeId, &withObject->nodeId)) {
             //TODO adjust maxDepth to needed tree depth (define a variable in config?)
-            isNodeInTree(server->nodestore, &methodCalled->references[i].referenceTypeId, &hasComponentNodeId,
-                 &hasSubTypeNodeId, 1, 1, &found);
-            if(found){
+            isNodeInTree(server->nodestore, &methodCalled->references[i].referenceTypeId,
+                         &hasComponentNodeId, &hasSubTypeNodeId, 1, &found);
+            if(found)
                 break;
-            }
         }
     }
-    if(!found)
+    if(!found) {
         result->statusCode = UA_STATUSCODE_BADMETHODINVALID;
-    if(result->statusCode != UA_STATUSCODE_GOOD)
         return;
+    }
 
     /* Verify Input Argument count, types and sizes */
-    //check inputAgruments only if there are any
-    if(request->inputArgumentsSize > 0){
-        const UA_VariableNode *inputArguments = getArgumentsVariableNode(server, methodCalled, UA_STRING("InputArguments"));
+    const UA_VariableNode *inputArguments =
+        getArgumentsVariableNode(server, methodCalled, UA_STRING("InputArguments"));
 
-        if(!inputArguments) {
+    if(!inputArguments) {
+        if(request->inputArgumentsSize > 0) {
             result->statusCode = UA_STATUSCODE_BADINVALIDARGUMENT;
             return;
         }
-            result->statusCode = argConformsToDefinition(server, inputArguments, request->inputArgumentsSize,
-                                                     request->inputArguments);
+    } else {
+        result->statusCode = argumentsConformsToDefinition(server, inputArguments,
+                                                           request->inputArgumentsSize,
+                                                           request->inputArguments);
         if(result->statusCode != UA_STATUSCODE_GOOD)
             return;
     }
 
     /* Allocate the output arguments */
-    const UA_VariableNode *outputArguments = getArgumentsVariableNode(server, methodCalled, UA_STRING("OutputArguments"));
-    if(!outputArguments) {
-        result->outputArgumentsSize=0;
-    }else{
-        result->outputArguments = UA_Array_new(outputArguments->value.variant.value.arrayLength, &UA_TYPES[UA_TYPES_VARIANT]);
+    result->outputArgumentsSize = 0; /* the default */
+    const UA_VariableNode *outputArguments =
+        getArgumentsVariableNode(server, methodCalled, UA_STRING("OutputArguments"));
+    if(outputArguments) {
+        result->outputArguments = UA_Array_new(outputArguments->value.data.value.value.arrayLength,
+                                               &UA_TYPES[UA_TYPES_VARIANT]);
         if(!result->outputArguments) {
             result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
             return;
         }
-        result->outputArgumentsSize = outputArguments->value.variant.value.arrayLength;
+        result->outputArgumentsSize = outputArguments->value.data.value.value.arrayLength;
     }
 
+    /* Call the method */
 #if defined(UA_ENABLE_METHODCALLS) && defined(UA_ENABLE_SUBSCRIPTIONS)
     methodCallSession = session;
 #endif
-
-    /* Call the method */
     result->statusCode = methodCalled->attachedMethod(methodCalled->methodHandle, withObject->nodeId,
                                                       request->inputArgumentsSize, request->inputArguments,
                                                       result->outputArgumentsSize, result->outputArguments);
-
 #if defined(UA_ENABLE_METHODCALLS) && defined(UA_ENABLE_SUBSCRIPTIONS)
     methodCallSession = NULL;
 #endif
-    /* TODO: Verify Output Argument count, types and sizes */
+
+    /* TODO: Verify Output matches the argument definition */
 }
-void Service_Call(UA_Server *server, UA_Session *session, const UA_CallRequest *request,
-                  UA_CallResponse *response) {
 
+void Service_Call(UA_Server *server, UA_Session *session,
+                  const UA_CallRequest *request,
+                  UA_CallResponse *response) {
     UA_LOG_DEBUG_SESSION(server->config.logger, session, "Processing CallRequest");
     if(request->methodsToCallSize <= 0) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;

Datei-Diff unterdrückt, da er zu groß ist
+ 546 - 659
src/server/ua_services_nodemanagement.c


+ 104 - 59
src/server/ua_services_subscription.c

@@ -15,8 +15,7 @@ setSubscriptionSettings(UA_Server *server, UA_Subscription *subscription,
                         UA_Double requestedPublishingInterval,
                         UA_UInt32 requestedLifetimeCount,
                         UA_UInt32 requestedMaxKeepAliveCount,
-                        UA_UInt32 maxNotificationsPerPublish, UA_Byte priority)
-{
+                        UA_UInt32 maxNotificationsPerPublish, UA_Byte priority) {
     /* deregister the job if required */
     UA_StatusCode retval = Subscription_unregisterPublishJob(server, subscription);
     if(retval != UA_STATUSCODE_GOOD)
@@ -53,12 +52,12 @@ setSubscriptionSettings(UA_Server *server, UA_Subscription *subscription,
 void
 Service_CreateSubscription(UA_Server *server, UA_Session *session,
                            const UA_CreateSubscriptionRequest *request,
-                           UA_CreateSubscriptionResponse *response)
-{
+                           UA_CreateSubscriptionResponse *response) {
     /* Create the subscription */
     UA_Subscription *newSubscription = UA_Subscription_new(session, response->subscriptionId);
     if(!newSubscription) {
-        UA_LOG_DEBUG_SESSION(server->config.logger, session, "Processing CreateSubscriptionRequest failed");
+        UA_LOG_DEBUG_SESSION(server->config.logger, session,
+                             "Processing CreateSubscriptionRequest failed");
         response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
         return;
     }
@@ -78,15 +77,18 @@ Service_CreateSubscription(UA_Server *server, UA_Session *session,
     response->revisedLifetimeCount = newSubscription->lifeTimeCount;
     response->revisedMaxKeepAliveCount = newSubscription->maxKeepAliveCount;
 
-    UA_LOG_DEBUG_SESSION(server->config.logger, session, "CreateSubscriptionRequest: Created Subscription %u "
+    UA_LOG_DEBUG_SESSION(server->config.logger, session,
+                         "CreateSubscriptionRequest: Created Subscription %u "
                          "with a publishing interval of %f ms", response->subscriptionId,
                          newSubscription->publishingInterval);
 }
 
-void Service_ModifySubscription(UA_Server *server, UA_Session *session,
-                                const UA_ModifySubscriptionRequest *request,
-                                UA_ModifySubscriptionResponse *response) {
-    UA_LOG_DEBUG_SESSION(server->config.logger, session, "Processing ModifySubscriptionRequest");
+void
+Service_ModifySubscription(UA_Server *server, UA_Session *session,
+                           const UA_ModifySubscriptionRequest *request,
+                           UA_ModifySubscriptionResponse *response) {
+    UA_LOG_DEBUG_SESSION(server->config.logger, session,
+                         "Processing ModifySubscriptionRequest");
     UA_Subscription *sub = UA_Session_getSubscriptionByID(session, request->subscriptionId);
     if(!sub) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
@@ -103,10 +105,12 @@ void Service_ModifySubscription(UA_Server *server, UA_Session *session,
     return;
 }
 
-void Service_SetPublishingMode(UA_Server *server, UA_Session *session,
-                               const UA_SetPublishingModeRequest *request,
-                               UA_SetPublishingModeResponse *response) {
-    UA_LOG_DEBUG_SESSION(server->config.logger, session, "Processing SetPublishingModeRequest");
+void
+Service_SetPublishingMode(UA_Server *server, UA_Session *session,
+                          const UA_SetPublishingModeRequest *request,
+                          UA_SetPublishingModeResponse *response) {
+    UA_LOG_DEBUG_SESSION(server->config.logger, session,
+                         "Processing SetPublishingModeRequest");
     if(request->subscriptionIdsSize <= 0) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
         return;
@@ -121,7 +125,8 @@ void Service_SetPublishingMode(UA_Server *server, UA_Session *session,
 
     response->resultsSize = size;
     for(size_t i = 0; i < size; i++) {
-        UA_Subscription *sub = UA_Session_getSubscriptionByID(session, request->subscriptionIds[i]);
+        UA_Subscription *sub =
+            UA_Session_getSubscriptionByID(session, request->subscriptionIds[i]);
         if(!sub) {
             response->results[i] = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
             continue;
@@ -139,38 +144,68 @@ void Service_SetPublishingMode(UA_Server *server, UA_Session *session,
 
 static void
 setMonitoredItemSettings(UA_Server *server, UA_MonitoredItem *mon,
-                         UA_MonitoringMode monitoringMode, UA_UInt32 clientHandle,
-                         UA_Double samplingInterval, UA_UInt32 queueSize,
-                         UA_Boolean discardOldest) {
+                         UA_MonitoringMode monitoringMode,
+                         const UA_MonitoringParameters *params) {
     MonitoredItem_unregisterSampleJob(server, mon);
     mon->monitoringMode = monitoringMode;
-    mon->clientHandle = clientHandle;
+
+    /* ClientHandle */
+    mon->clientHandle = params->clientHandle;
+
+    /* SamplingInterval */
+    UA_Double samplingInterval = params->samplingInterval;
+    if(mon->attributeID == UA_ATTRIBUTEID_VALUE) {
+        const UA_VariableNode *vn = (const UA_VariableNode*)
+            UA_NodeStore_get(server->nodestore, &mon->monitoredNodeId);
+        if(vn && vn->nodeClass == UA_NODECLASS_VARIABLE &&
+           samplingInterval <  vn->minimumSamplingInterval)
+            samplingInterval = vn->minimumSamplingInterval;
+    } else if(mon->attributeID == UA_ATTRIBUTEID_EVENTNOTIFIER) {
+        /* TODO: events should not need a samplinginterval */
+        samplingInterval = 10000.0f; // 10 seconds to reduce the load
+    }
     mon->samplingInterval = samplingInterval;
     UA_BOUNDEDVALUE_SETWBOUNDS(server->config.samplingIntervalLimits,
         samplingInterval, mon->samplingInterval);
-    /* Check for nan */
-    if(samplingInterval != samplingInterval)
+    if(samplingInterval != samplingInterval) /* Check for nan */
         mon->samplingInterval = server->config.samplingIntervalLimits.min;
+
+    /* Filter */
+    if(params->filter.encoding != UA_EXTENSIONOBJECT_DECODED ||
+       params->filter.content.decoded.type != &UA_TYPES[UA_TYPES_DATACHANGEFILTER]) {
+        /* Default: Trigger only on the value and the statuscode */
+        mon->trigger = UA_DATACHANGETRIGGER_STATUSVALUE;
+    } else {
+        UA_DataChangeFilter *filter = params->filter.content.decoded.data;
+        mon->trigger = filter->trigger;
+    }
+
+    /* QueueSize */
     UA_BOUNDEDVALUE_SETWBOUNDS(server->config.queueSizeLimits,
-                               queueSize, mon->maxQueueSize);
-    mon->discardOldest = discardOldest;
+                               params->queueSize, mon->maxQueueSize);
+
+    /* DiscardOldest */
+    mon->discardOldest = params->discardOldest;
+
+    /* Register sample job if reporting is enabled */
     if(monitoringMode == UA_MONITORINGMODE_REPORTING)
         MonitoredItem_registerSampleJob(server, mon);
 }
 
 static const UA_String binaryEncoding = {sizeof("Default Binary")-1, (UA_Byte*)"Default Binary"};
+
 static void
-Service_CreateMonitoredItems_single(UA_Server *server, UA_Session *session, UA_Subscription *sub,
+Service_CreateMonitoredItems_single(UA_Server *server, UA_Session *session,
+                                    UA_Subscription *sub,
                                     const UA_TimestampsToReturn timestampsToReturn,
                                     const UA_MonitoredItemCreateRequest *request,
                                     UA_MonitoredItemCreateResult *result) {
-    /* Make an example read to get errors in the itemToMonitor */
+    /* Make an example read to get errors in the itemToMonitor. Allow return
+     * codes "good" and "uncertain", as well as a list of statuscodes that might
+     * be repaired inside the data source. */
     UA_DataValue v;
     UA_DataValue_init(&v);
     Service_Read_single(server, session, timestampsToReturn, &request->itemToMonitor, &v);
-
-    /* Allow return codes "good" and "uncertain", as well as a list of
-       statuscodes that might be repaired by the data source. */
     if(v.hasStatus && (v.status >> 30) > 1 &&
        v.status != UA_STATUSCODE_BADRESOURCEUNAVAILABLE &&
        v.status != UA_STATUSCODE_BADCOMMUNICATIONERROR &&
@@ -190,7 +225,8 @@ Service_CreateMonitoredItems_single(UA_Server *server, UA_Session *session, UA_S
     }
 
     /* Check if the encoding is set for a value */
-    if(request->itemToMonitor.attributeId != UA_ATTRIBUTEID_VALUE && request->itemToMonitor.dataEncoding.name.length > 0){
+    if(request->itemToMonitor.attributeId != UA_ATTRIBUTEID_VALUE &&
+       request->itemToMonitor.dataEncoding.name.length > 0) {
         result->statusCode = UA_STATUSCODE_BADDATAENCODINGINVALID;
         return;
     }
@@ -201,7 +237,8 @@ Service_CreateMonitoredItems_single(UA_Server *server, UA_Session *session, UA_S
         result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
         return;
     }
-    UA_StatusCode retval = UA_NodeId_copy(&request->itemToMonitor.nodeId, &newMon->monitoredNodeId);
+    UA_StatusCode retval = UA_NodeId_copy(&request->itemToMonitor.nodeId,
+                                          &newMon->monitoredNodeId);
     if(retval != UA_STATUSCODE_GOOD) {
         result->statusCode = retval;
         MonitoredItem_delete(server, newMon);
@@ -212,10 +249,7 @@ Service_CreateMonitoredItems_single(UA_Server *server, UA_Session *session, UA_S
     newMon->itemId = ++(sub->lastMonitoredItemId);
     newMon->timestampsToReturn = timestampsToReturn;
     setMonitoredItemSettings(server, newMon, request->monitoringMode,
-                             request->requestedParameters.clientHandle,
-                             request->requestedParameters.samplingInterval,
-                             request->requestedParameters.queueSize,
-                             request->requestedParameters.discardOldest);
+                             &request->requestedParameters);
     LIST_INSERT_HEAD(&sub->MonitoredItems, newMon, listEntry);
 
     /* Create the first sample */
@@ -233,9 +267,10 @@ void
 Service_CreateMonitoredItems(UA_Server *server, UA_Session *session,
                              const UA_CreateMonitoredItemsRequest *request,
                              UA_CreateMonitoredItemsResponse *response) {
-    UA_LOG_DEBUG_SESSION(server->config.logger, session, "Processing CreateMonitoredItemsRequest");
+    UA_LOG_DEBUG_SESSION(server->config.logger, session,
+                         "Processing CreateMonitoredItemsRequest");
 
-    /* check if the timestampstoreturn is valid */
+    /* Check if the timestampstoreturn is valid */
     if(request->timestampsToReturn > UA_TIMESTAMPSTORETURN_NEITHER) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADTIMESTAMPSTORETURNINVALID;
         return;
@@ -278,10 +313,7 @@ Service_ModifyMonitoredItems_single(UA_Server *server, UA_Session *session, UA_S
     }
 
     setMonitoredItemSettings(server, mon, mon->monitoringMode,
-                             request->requestedParameters.clientHandle,
-                             request->requestedParameters.samplingInterval,
-                             request->requestedParameters.queueSize,
-                             request->requestedParameters.discardOldest);
+                             &request->requestedParameters);
     result->revisedSamplingInterval = mon->samplingInterval;
     result->revisedQueueSize = mon->maxQueueSize;
 }
@@ -289,7 +321,8 @@ Service_ModifyMonitoredItems_single(UA_Server *server, UA_Session *session, UA_S
 void Service_ModifyMonitoredItems(UA_Server *server, UA_Session *session,
                                   const UA_ModifyMonitoredItemsRequest *request,
                                   UA_ModifyMonitoredItemsResponse *response) {
-    UA_LOG_DEBUG_SESSION(server->config.logger, session, "Processing ModifyMonitoredItemsRequest");
+    UA_LOG_DEBUG_SESSION(server->config.logger, session,
+                         "Processing ModifyMonitoredItemsRequest");
 
     /* check if the timestampstoreturn is valid */
     if(request->timestampsToReturn > UA_TIMESTAMPSTORETURN_NEITHER) {
@@ -297,6 +330,7 @@ void Service_ModifyMonitoredItems(UA_Server *server, UA_Session *session,
         return;
     }
 
+    /* Get the subscription */
     UA_Subscription *sub = UA_Session_getSubscriptionByID(session, request->subscriptionId);
     if(!sub) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
@@ -347,12 +381,19 @@ void Service_SetMonitoringMode(UA_Server *server, UA_Session *session,
     response->resultsSize = request->monitoredItemIdsSize;
 
     for(size_t i = 0; i < response->resultsSize; i++) {
-        UA_MonitoredItem *mon = UA_Subscription_getMonitoredItem(sub, request->monitoredItemIds[i]);
-        if(mon)
-            setMonitoredItemSettings(server, mon, request->monitoringMode, mon->clientHandle,
-                                     mon->samplingInterval, mon->maxQueueSize, mon->discardOldest);
-        else
+        UA_MonitoredItem *mon =
+            UA_Subscription_getMonitoredItem(sub, request->monitoredItemIds[i]);
+        if(!mon) {
             response->results[i] = UA_STATUSCODE_BADMONITOREDITEMIDINVALID;
+            continue;
+        }
+        if(request->monitoringMode == mon->monitoringMode)
+            continue;
+        mon->monitoringMode = request->monitoringMode;
+        if(mon->monitoringMode == UA_MONITORINGMODE_REPORTING)
+            MonitoredItem_registerSampleJob(server, mon);
+        else
+            MonitoredItem_unregisterSampleJob(server, mon);
     }
 }
 
@@ -390,18 +431,21 @@ Service_Publish(UA_Server *server, UA_Session *session,
     UA_PublishResponse *response = &entry->response;
     UA_PublishResponse_init(response);
     response->responseHeader.requestHandle = request->requestHeader.requestHandle;
-    response->results = UA_malloc(request->subscriptionAcknowledgementsSize * sizeof(UA_StatusCode));
-    if(!response->results) {
-        /* Respond immediately with the error code */
-        response->responseHeader.timestamp = UA_DateTime_now();
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-        UA_SecureChannel_sendBinaryMessage(session->channel, requestId, response,
-                                           &UA_TYPES[UA_TYPES_PUBLISHRESPONSE]);
-        UA_PublishResponse_deleteMembers(response);
-        UA_free(entry);
-        return;
+    if(request->subscriptionAcknowledgementsSize > 0) {
+        response->results = UA_Array_new(request->subscriptionAcknowledgementsSize,
+                                         &UA_TYPES[UA_TYPES_STATUSCODE]);
+        if(!response->results) {
+            /* Respond immediately with the error code */
+            response->responseHeader.timestamp = UA_DateTime_now();
+            response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
+            UA_SecureChannel_sendBinaryMessage(session->channel, requestId, response,
+                                               &UA_TYPES[UA_TYPES_PUBLISHRESPONSE]);
+            UA_PublishResponse_deleteMembers(response);
+            UA_free(entry);
+            return;
+        }
+        response->resultsSize = request->subscriptionAcknowledgementsSize;
     }
-    response->resultsSize = request->subscriptionAcknowledgementsSize;
 
     /* Delete Acknowledged Subscription Messages */
     for(size_t i = 0; i < request->subscriptionAcknowledgementsSize; i++) {
@@ -450,9 +494,10 @@ void
 Service_DeleteSubscriptions(UA_Server *server, UA_Session *session,
                             const UA_DeleteSubscriptionsRequest *request,
                             UA_DeleteSubscriptionsResponse *response) {
-    UA_LOG_DEBUG_SESSION(server->config.logger, session, "Processing DeleteSubscriptionsRequest");
+    UA_LOG_DEBUG_SESSION(server->config.logger, session,
+                         "Processing DeleteSubscriptionsRequest");
 
-    if(request->subscriptionIdsSize == 0){
+    if(request->subscriptionIdsSize == 0) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
         return;
     }

+ 101 - 46
src/server/ua_subscription.c

@@ -1,5 +1,6 @@
 #include "ua_subscription.h"
 #include "ua_server_internal.h"
+#include "ua_types_encoding_binary.h"
 #include "ua_services.h"
 #include "ua_nodestore.h"
 
@@ -46,33 +47,57 @@ void MonitoredItem_delete(UA_Server *server, UA_MonitoredItem *monitoredItem) {
 void UA_MoniteredItem_SampleCallback(UA_Server *server, UA_MonitoredItem *monitoredItem) {
     UA_Subscription *sub = monitoredItem->subscription;
     if(monitoredItem->monitoredItemType != UA_MONITOREDITEMTYPE_CHANGENOTIFY) {
-        UA_LOG_DEBUG_SESSION(server->config.logger, sub->session, "Subscription %u | MonitoredItem %i | "
-                             "Cannot process a monitoreditem that is not a data change notification",
+        UA_LOG_DEBUG_SESSION(server->config.logger, sub->session,
+                             "Subscription %u | MonitoredItem %i | "
+                             "Cannot process a monitoreditem that is not "
+                             "a data change notification",
                              sub->subscriptionID, monitoredItem->itemId);
         return;
     }
 
     MonitoredItem_queuedValue *newvalue = UA_malloc(sizeof(MonitoredItem_queuedValue));
     if(!newvalue) {
-        UA_LOG_WARNING_SESSION(server->config.logger, sub->session, "Subscription %u | MonitoredItem %i | "
-                               "Skipped a sample due to lack of memory", sub->subscriptionID,
-                               monitoredItem->itemId);
+        UA_LOG_WARNING_SESSION(server->config.logger, sub->session,
+                               "Subscription %u | MonitoredItem %i | "
+                               "Skipped a sample due to lack of memory",
+                               sub->subscriptionID, monitoredItem->itemId);
         return;
     }
     UA_DataValue_init(&newvalue->value);
     newvalue->clientHandle = monitoredItem->clientHandle;
 
+    /* Adjust timestampstoreturn to get source timestamp for triggering */
+    UA_TimestampsToReturn ts = monitoredItem->timestampsToReturn;
+    if(ts == UA_TIMESTAMPSTORETURN_SERVER)
+        ts = UA_TIMESTAMPSTORETURN_BOTH;
+    else if(ts == UA_TIMESTAMPSTORETURN_NEITHER)
+        ts = UA_TIMESTAMPSTORETURN_SOURCE;
+
     /* Read the value */
     UA_ReadValueId rvid;
     UA_ReadValueId_init(&rvid);
     rvid.nodeId = monitoredItem->monitoredNodeId;
     rvid.attributeId = monitoredItem->attributeID;
     rvid.indexRange = monitoredItem->indexRange;
-    Service_Read_single(server, sub->session, monitoredItem->timestampsToReturn,
-                        &rvid, &newvalue->value);
+    Service_Read_single(server, sub->session, ts, &rvid, &newvalue->value);
+
+    /* Apply Filter */
+    UA_Boolean hasValue = newvalue->value.hasValue;
+    UA_Boolean hasServerTimestamp = newvalue->value.hasServerTimestamp;
+    UA_Boolean hasServerPicoseconds = newvalue->value.hasServerPicoseconds;
+    UA_Boolean hasSourceTimestamp = newvalue->value.hasSourceTimestamp;
+    UA_Boolean hasSourcePicoseconds = newvalue->value.hasSourcePicoseconds;
+    newvalue->value.hasServerTimestamp = false;
+    newvalue->value.hasServerPicoseconds = false;
+    if(monitoredItem->trigger == UA_DATACHANGETRIGGER_STATUS)
+        newvalue->value.hasValue = false;
+    if(monitoredItem->trigger < UA_DATACHANGETRIGGER_STATUSVALUETIMESTAMP) {
+        newvalue->value.hasSourceTimestamp = false;
+        newvalue->value.hasSourcePicoseconds = false;
+    }
 
-    /* encode to see if the data has changed */
-    size_t binsize = UA_calcSizeBinary(&newvalue->value.value, &UA_TYPES[UA_TYPES_VARIANT]);
+    /* Encode the data for comparison */
+    size_t binsize = UA_calcSizeBinary(&newvalue->value, &UA_TYPES[UA_TYPES_DATAVALUE]);
     UA_ByteString newValueAsByteString;
     UA_StatusCode retval = UA_ByteString_allocBuffer(&newValueAsByteString, binsize);
     if(retval != UA_STATUSCODE_GOOD) {
@@ -81,26 +106,37 @@ void UA_MoniteredItem_SampleCallback(UA_Server *server, UA_MonitoredItem *monito
         return;
     }
     size_t encodingOffset = 0;
-    retval = UA_encodeBinary(&newvalue->value.value, &UA_TYPES[UA_TYPES_VARIANT],
+    retval = UA_encodeBinary(&newvalue->value, &UA_TYPES[UA_TYPES_DATAVALUE],
                              NULL, NULL, &newValueAsByteString, &encodingOffset);
 
-    /* error or the content has not changed */
+    /* Restore the settings changed for the filter */
+    newvalue->value.hasValue = hasValue;
+    newvalue->value.hasServerTimestamp = hasServerTimestamp;
+    newvalue->value.hasServerPicoseconds = hasServerPicoseconds;
+    if(monitoredItem->timestampsToReturn == UA_TIMESTAMPSTORETURN_SERVER ||
+       monitoredItem->timestampsToReturn == UA_TIMESTAMPSTORETURN_NEITHER) {
+        newvalue->value.hasSourceTimestamp = false;
+        newvalue->value.hasSourcePicoseconds = false;
+    } else {
+        newvalue->value.hasSourceTimestamp = hasSourceTimestamp;
+        newvalue->value.hasSourcePicoseconds = hasSourcePicoseconds;
+    }
+
+    /* Error or the value has not changed */
     if(retval != UA_STATUSCODE_GOOD ||
        (monitoredItem->lastSampledValue.data &&
         UA_String_equal(&newValueAsByteString, &monitoredItem->lastSampledValue))) {
-        UA_ByteString_deleteMembers(&newValueAsByteString);
-        UA_DataValue_deleteMembers(&newvalue->value);
-        UA_free(newvalue);
-        UA_LOG_DEBUG_SESSION(server->config.logger, sub->session, "Subscription %u | "
+        UA_LOG_TRACE_SESSION(server->config.logger, sub->session, "Subscription %u | "
                              "MonitoredItem %u | Do not sample an unchanged value",
                              sub->subscriptionID, monitoredItem->itemId);
-        return;
+        goto cleanup;
     }
 
-    UA_LOG_DEBUG_SESSION(server->config.logger, sub->session, "Subscription %u | MonitoredItem %u | "
+    UA_LOG_DEBUG_SESSION(server->config.logger, sub->session,
+                         "Subscription %u | MonitoredItem %u | "
                          "Sampling the value", sub->subscriptionID, monitoredItem->itemId);
 
-    /* Do we have space in the queue? */
+    /* Is enough space in the queue? */
     if(monitoredItem->currentQueueSize >= monitoredItem->maxQueueSize) {
         MonitoredItem_queuedValue *queueItem;
         if(monitoredItem->discardOldest)
@@ -109,13 +145,11 @@ void UA_MoniteredItem_SampleCallback(UA_Server *server, UA_MonitoredItem *monito
             queueItem = TAILQ_LAST(&monitoredItem->queue, QueueOfQueueDataValues);
 
         if(!queueItem) {
-            UA_LOG_WARNING_SESSION(server->config.logger, sub->session, "Subscription %u | MonitoredItem %u | "
-                                   "Cannot remove an element from the full queue. Internal error!",
-                                   sub->subscriptionID, monitoredItem->itemId);
-            UA_ByteString_deleteMembers(&newValueAsByteString);
-            UA_DataValue_deleteMembers(&newvalue->value);
-            UA_free(newvalue);
-            return;
+            UA_LOG_WARNING_SESSION(server->config.logger, sub->session, "Subscription %u | "
+                                   "MonitoredItem %u | Cannot remove an element from the full "
+                                   "queue. Internal error!", sub->subscriptionID,
+                                   monitoredItem->itemId);
+            goto cleanup;
         }
 
         TAILQ_REMOVE(&monitoredItem->queue, queueItem, listEntry);
@@ -125,23 +159,36 @@ void UA_MoniteredItem_SampleCallback(UA_Server *server, UA_MonitoredItem *monito
     }
 
     /* If the read request returned a datavalue pointing into the nodestore, we
-       must make a copy to keep the datavalue across mainloop iterations */
-    if(newvalue->value.hasValue && newvalue->value.value.storageType == UA_VARIANT_DATA_NODELETE) {
+     * must make a deep copy to keep the datavalue across mainloop iterations */
+    if(newvalue->value.hasValue &&
+       newvalue->value.value.storageType == UA_VARIANT_DATA_NODELETE) {
         UA_Variant tempv = newvalue->value.value;
         UA_Variant_copy(&tempv, &newvalue->value.value);
     }
 
-    /* add the sample */
+    /* Replace the comparison bytestring with the current sample */
     UA_ByteString_deleteMembers(&monitoredItem->lastSampledValue);
     monitoredItem->lastSampledValue = newValueAsByteString;
+
+    /* Add the sample to the queue for publication */
     TAILQ_INSERT_TAIL(&monitoredItem->queue, newvalue, listEntry);
     monitoredItem->currentQueueSize++;
+    return;
+
+ cleanup:
+    UA_ByteString_deleteMembers(&newValueAsByteString);
+    UA_DataValue_deleteMembers(&newvalue->value);
+    UA_free(newvalue);
 }
 
-UA_StatusCode MonitoredItem_registerSampleJob(UA_Server *server, UA_MonitoredItem *mon) {
-    UA_Job job = {.type = UA_JOBTYPE_METHODCALL,
-                  .job.methodCall = {.method = (UA_ServerCallback)UA_MoniteredItem_SampleCallback, .data = mon} };
-    UA_StatusCode retval = UA_Server_addRepeatedJob(server, job, (UA_UInt32)mon->samplingInterval,
+UA_StatusCode
+MonitoredItem_registerSampleJob(UA_Server *server, UA_MonitoredItem *mon) {
+    UA_Job job;
+    job.type = UA_JOBTYPE_METHODCALL;
+    job.job.methodCall.method = (UA_ServerCallback)UA_MoniteredItem_SampleCallback;
+    job.job.methodCall.data = mon;
+    UA_StatusCode retval = UA_Server_addRepeatedJob(server, job,
+                                                    (UA_UInt32)mon->samplingInterval,
                                                     &mon->sampleJobGuid);
     if(retval == UA_STATUSCODE_GOOD)
         mon->sampleJobIsRegistered = true;
@@ -301,7 +348,8 @@ void UA_Subscription_publishCallback(UA_Server *server, UA_Subscription *sub) {
         message->notificationDataSize = 1;
         UA_ExtensionObject *data = message->notificationData;
         UA_DataChangeNotification *dcn = UA_DataChangeNotification_new();
-        dcn->monitoredItems = UA_Array_new(notifications, &UA_TYPES[UA_TYPES_MONITOREDITEMNOTIFICATION]);
+        dcn->monitoredItems =
+            UA_Array_new(notifications, &UA_TYPES[UA_TYPES_MONITOREDITEMNOTIFICATION]);
         dcn->monitoredItemsSize = notifications;
         size_t l = 0;
         UA_MonitoredItem *mon;
@@ -319,10 +367,12 @@ void UA_Subscription_publishCallback(UA_Server *server, UA_Subscription *sub) {
                 mon->currentQueueSize--;
                 mon_l++;
             }
-            UA_LOG_DEBUG_SESSION(server->config.logger, sub->session, "Subscription %u | MonitoredItem %u | " \
-                                 "Adding %u notifications to the publish response. " \
+            UA_LOG_DEBUG_SESSION(server->config.logger, sub->session,
+                                 "Subscription %u | MonitoredItem %u | "
+                                 "Adding %u notifications to the publish response. "
                                  "%u notifications remain in the queue",
-                                 sub->subscriptionID, mon->itemId, mon_l, mon->currentQueueSize);
+                                 sub->subscriptionID, mon->itemId, mon_l,
+                                 mon->currentQueueSize);
             l += mon_l;
         }
         data->encoding = UA_EXTENSIONOBJECT_DECODED;
@@ -330,13 +380,16 @@ void UA_Subscription_publishCallback(UA_Server *server, UA_Subscription *sub) {
         data->content.decoded.type = &UA_TYPES[UA_TYPES_DATACHANGENOTIFICATION];
 
         /* Put the notification message into the retransmission queue */
-        UA_NotificationMessageEntry *retransmission = malloc(sizeof(UA_NotificationMessageEntry));
+        UA_NotificationMessageEntry *retransmission =
+            malloc(sizeof(UA_NotificationMessageEntry));
         if(retransmission) {
-            UA_NotificationMessage_copy(&response->notificationMessage, &retransmission->message);
+            UA_NotificationMessage_copy(&response->notificationMessage,
+                                        &retransmission->message);
             LIST_INSERT_HEAD(&sub->retransmissionQueue, retransmission, listEntry);
         } else {
-            UA_LOG_WARNING_SESSION(server->config.logger, sub->session, "Subscription %u | "
-                                   "Could not allocate memory for retransmission", sub->subscriptionID);
+            UA_LOG_WARNING_SESSION(server->config.logger, sub->session,
+                                   "Subscription %u | Could not allocate memory "
+                                   "for retransmission", sub->subscriptionID);
         }
     }
 
@@ -357,8 +410,8 @@ void UA_Subscription_publishCallback(UA_Server *server, UA_Subscription *sub) {
 
     /* Send the response */
     UA_LOG_DEBUG_SESSION(server->config.logger, sub->session,
-                         "Subscription %u | Sending out a publish response with %u notifications",
-                         sub->subscriptionID, (UA_UInt32)notifications);
+                         "Subscription %u | Sending out a publish response with %u "
+                         "notifications", sub->subscriptionID, (UA_UInt32)notifications);
     UA_SecureChannel_sendBinaryMessage(sub->session->channel, requestId, response,
                                        &UA_TYPES[UA_TYPES_PUBLISHRESPONSE]);
 
@@ -379,10 +432,12 @@ UA_StatusCode Subscription_registerPublishJob(UA_Server *server, UA_Subscription
     if(!sub->publishingEnabled)
         return UA_STATUSCODE_GOOD;
 
-    UA_Job job = (UA_Job) {.type = UA_JOBTYPE_METHODCALL,
-                           .job.methodCall = {.method = (UA_ServerCallback)UA_Subscription_publishCallback,
-                                              .data = sub} };
-    UA_StatusCode retval = UA_Server_addRepeatedJob(server, job, (UA_UInt32)sub->publishingInterval,
+    UA_Job job;
+    job.type = UA_JOBTYPE_METHODCALL;
+    job.job.methodCall.method = (UA_ServerCallback)UA_Subscription_publishCallback;
+    job.job.methodCall.data = sub;
+    UA_StatusCode retval = UA_Server_addRepeatedJob(server, job,
+                                                    (UA_UInt32)sub->publishingInterval,
                                                     &sub->publishJobGuid);
     if(retval == UA_STATUSCODE_GOOD)
         sub->publishJobIsRegistered = true;

+ 1 - 0
src/server/ua_subscription.h

@@ -41,6 +41,7 @@ typedef struct UA_MonitoredItem {
     UA_Boolean discardOldest;
     UA_String indexRange;
     // TODO: dataEncoding is hardcoded to UA binary
+    UA_DataChangeTrigger trigger;
 
     /* Sample Job */
     UA_Guid sampleJobGuid;

+ 3 - 2
src/ua_session.c

@@ -30,7 +30,7 @@ void UA_Session_init(UA_Session *session) {
     session->timeout = 0;
     UA_DateTime_init(&session->validTill);
     session->channel = NULL;
-    session->availableContinuationPoints = MAXCONTINUATIONPOINTS;
+    session->availableContinuationPoints = UA_MAXCONTINUATIONPOINTS;
     LIST_INIT(&session->continuationPoints);
 #ifdef UA_ENABLE_SUBSCRIPTIONS
     LIST_INIT(&session->serverSubscriptions);
@@ -107,7 +107,7 @@ UA_UInt32 UA_Session_getUniqueSubscriptionID(UA_Session *session) {
 }
 
 void UA_Session_answerPublishRequestsWithoutSubscription(UA_Session *session) {
-    /* Do we have publish requests but no subscriptions? */
+    /* Are there remaining subscriptions? */
     if(LIST_FIRST(&session->serverSubscriptions))
         return;
 
@@ -121,6 +121,7 @@ void UA_Session_answerPublishRequestsWithoutSubscription(UA_Session *session) {
         response->responseHeader.timestamp = UA_DateTime_now();
         UA_SecureChannel_sendBinaryMessage(session->channel, requestId, response,
                                            &UA_TYPES[UA_TYPES_PUBLISHRESPONSE]);
+        UA_PublishResponse_deleteMembers(response);
         UA_free(pre);
     }
 }

+ 3 - 1
src/ua_session.h

@@ -6,7 +6,7 @@
 #include "ua_securechannel.h"
 #include "ua_server.h"
 
-#define MAXCONTINUATIONPOINTS 5
+#define UA_MAXCONTINUATIONPOINTS 5
 
 struct ContinuationPointEntry {
     LIST_ENTRY(ContinuationPointEntry) pointers;
@@ -19,11 +19,13 @@ struct ContinuationPointEntry {
 struct UA_Subscription;
 typedef struct UA_Subscription UA_Subscription;
 
+#ifdef UA_ENABLE_SUBSCRIPTIONS
 typedef struct UA_PublishResponseEntry {
     SIMPLEQ_ENTRY(UA_PublishResponseEntry) listEntry;
     UA_UInt32 requestId;
     UA_PublishResponse response;
 } UA_PublishResponseEntry;
+#endif
 
 struct UA_Session {
     UA_ApplicationDescription clientDescription;

+ 6 - 8
src/ua_types.c

@@ -16,10 +16,12 @@
 /* Static definition of NULL type instances */
 UA_EXPORT const UA_String UA_STRING_NULL = {.length = 0, .data = NULL };
 UA_EXPORT const UA_ByteString UA_BYTESTRING_NULL = {.length = 0, .data = NULL };
-UA_EXPORT const UA_Guid UA_GUID_NULL = {.data1 = 0, .data2 = 0, .data3 = 0, .data4 = {0,0,0,0,0,0,0,0}};
+UA_EXPORT const UA_Guid UA_GUID_NULL = {.data1 = 0, .data2 = 0, .data3 = 0,
+                                        .data4 = {0,0,0,0,0,0,0,0}};
 UA_EXPORT const UA_NodeId UA_NODEID_NULL = {0, UA_NODEIDTYPE_NUMERIC, {0}};
 UA_EXPORT const UA_ExpandedNodeId UA_EXPANDEDNODEID_NULL = {
-    .nodeId = { .namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC, .identifier.numeric = 0 },
+    .nodeId = { .namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC,
+                .identifier.numeric = 0 },
     .namespaceUri = {.length = 0, .data = NULL}, .serverIndex = 0 };
 
 static void UA_deleteMembers_noInit(void *p, const UA_DataType *type);
@@ -419,12 +421,8 @@ computeStrides(const UA_Variant *v, const UA_NumericRange range,
     if(v->arrayDimensionsSize > 0) {
         dims_count = v->arrayDimensionsSize;
         dims = (UA_UInt32*)v->arrayDimensions;
-        for(size_t i = 0; i < dims_count; i++) {
-            /* dimensions can have negative size similar to array lengths */
-            if(v->arrayDimensions[i] < 0)
-                return UA_STATUSCODE_BADINDEXRANGEINVALID;
+        for(size_t i = 0; i < dims_count; i++)
             elements *= dims[i];
-        }
         if(elements != v->arrayLength)
             return UA_STATUSCODE_BADINTERNALERROR;
     }
@@ -611,7 +609,7 @@ UA_Variant_copyRange(const UA_Variant *orig_src, UA_Variant *dst,
         dst->arrayDimensionsSize = thisrange.dimensionsSize;
         for(size_t k = 0; k < thisrange.dimensionsSize; k++)
             dst->arrayDimensions[k] =
-                (UA_Int32)(thisrange.dimensions[k].max - thisrange.dimensions[k].min + 1);
+                thisrange.dimensions[k].max - thisrange.dimensions[k].min + 1;
     }
     return UA_STATUSCODE_GOOD;
 }

+ 64 - 76
tests/check_services_attributes.c

@@ -17,14 +17,18 @@
 #endif
 
 static UA_StatusCode
-readCPUTemperature_broken(void *handle, const UA_NodeId nodeid, UA_Boolean sourceTimeStamp,
-                          const UA_NumericRange *range, UA_DataValue *dataValue) {
-  dataValue->hasValue = true;
-  return UA_STATUSCODE_GOOD;
+readCPUTemperature(void *handle, const UA_NodeId nodeid, UA_Boolean sourceTimeStamp,
+                   const UA_NumericRange *range, UA_DataValue *dataValue) {
+    UA_Float temp = 20.5f;
+    UA_Variant_setScalarCopy(&dataValue->value, &temp, &UA_TYPES[UA_TYPES_FLOAT]);
+    dataValue->hasValue = true;
+    return UA_STATUSCODE_GOOD;
 }
 
-static UA_Server* makeTestSequence(void) {
-    UA_Server *server = UA_Server_new(UA_ServerConfig_standard);
+static UA_Server *
+makeTestSequence(void) {
+    UA_Server * server = UA_Server_new(UA_ServerConfig_standard);
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
 
     /* VariableNode */
     UA_VariableAttributes vattr;
@@ -38,37 +42,30 @@ static UA_Server* makeTestSequence(void) {
     UA_NodeId myIntegerNodeId = UA_NODEID_STRING(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, vattr, NULL, NULL);
+    retval = UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId,
+                                       parentReferenceNodeId, myIntegerName,
+                                       UA_NODEID_NULL, vattr, NULL, NULL);
+    ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
 
     /* DataSource VariableNode */
     UA_VariableAttributes_init(&vattr);
-    UA_DataSource temperatureDataSource = (UA_DataSource) {
-                                           .handle = NULL, .read = NULL, .write = NULL};
+    UA_DataSource temperatureDataSource =
+        (UA_DataSource) {.handle = NULL, .read = readCPUTemperature, .write = NULL};
     vattr.description = UA_LOCALIZEDTEXT("en_US","temperature");
     vattr.displayName = UA_LOCALIZEDTEXT("en_US","temperature");
-    UA_Server_addDataSourceVariableNode(server, UA_NODEID_STRING(1, "cpu.temperature"),
-                                        UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
-                                        UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
-                                        UA_QUALIFIEDNAME(1, "cpu temperature"),
-                                        UA_NODEID_NULL, vattr, temperatureDataSource, NULL);
-
-    /* DataSource Variable returning no value */
-    UA_DataSource temperatureDataSource1 = (UA_DataSource) {
-                                            .handle = NULL, .read = readCPUTemperature_broken, .write = NULL};
-    vattr.description = UA_LOCALIZEDTEXT("en_US","temperature1");
-    vattr.displayName = UA_LOCALIZEDTEXT("en_US","temperature1");
-    UA_Server_addDataSourceVariableNode(server, UA_NODEID_STRING(1, "cpu.temperature1"),
-                                        UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
-                                        UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
-                                        UA_QUALIFIEDNAME(1, "cpu temperature bogus"),
-                                        UA_NODEID_NULL, vattr, temperatureDataSource1, NULL);
+    retval = UA_Server_addDataSourceVariableNode(server, UA_NODEID_STRING(1, "cpu.temperature"),
+                                                 UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+                                                 UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
+                                                 UA_QUALIFIEDNAME(1, "cpu temperature"),
+                                                 UA_NODEID_NULL, vattr, temperatureDataSource, NULL);
+    ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
+
     /* VariableNode with array */
     UA_VariableAttributes_init(&vattr);
     UA_Int32 myIntegerArray[9] = {1,2,3,4,5,6,7,8,9};
     UA_Variant_setArray(&vattr.value, &myIntegerArray, 9, &UA_TYPES[UA_TYPES_INT32]);
-    UA_Int32 myIntegerDimensions[2] = {3,3};
+    vattr.valueRank = -2;
+    UA_UInt32 myIntegerDimensions[2] = {3,3};
     vattr.value.arrayDimensions = myIntegerDimensions;
     vattr.value.arrayDimensionsSize = 2;
     vattr.displayName = UA_LOCALIZEDTEXT("locale","myarray");
@@ -76,31 +73,34 @@ static UA_Server* makeTestSequence(void) {
     myIntegerNodeId = UA_NODEID_STRING(1, "myarray");
     parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
     parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
-    UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId,
-                              parentReferenceNodeId, myIntegerName,
-                              UA_NODEID_NULL, vattr, NULL, NULL);
+    retval = UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId,
+                                       parentReferenceNodeId, myIntegerName,
+                                       UA_NODEID_NULL, vattr, NULL, NULL);
+    ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
 
     /* ObjectNode */
     UA_ObjectAttributes obj_attr;
     UA_ObjectAttributes_init(&obj_attr);
     obj_attr.description = UA_LOCALIZEDTEXT("en_US","Demo");
     obj_attr.displayName = UA_LOCALIZEDTEXT("en_US","Demo");
-    UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, 50),
-                            UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
-                            UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
-                            UA_QUALIFIEDNAME(1, "Demo"),
-                            UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE),
-                            obj_attr, NULL, NULL);
+    retval = UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, 50),
+                                     UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+                                     UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
+                                     UA_QUALIFIEDNAME(1, "Demo"),
+                                     UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE),
+                                     obj_attr, NULL, NULL);
+    ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
 
     /* ViewNode */
     UA_ViewAttributes view_attr;
     UA_ViewAttributes_init(&view_attr);
     view_attr.description = UA_LOCALIZEDTEXT("en_US", "Viewtest");
     view_attr.displayName = UA_LOCALIZEDTEXT("en_US", "Viewtest");
-    UA_Server_addViewNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_VIEWNODE),
-                          UA_NODEID_NUMERIC(0, UA_NS0ID_VIEWSFOLDER),
-                          UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
-                          UA_QUALIFIEDNAME(0, "Viewtest"), view_attr, NULL, NULL);
+    retval = UA_Server_addViewNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_VIEWNODE),
+                                   UA_NODEID_NUMERIC(0, UA_NS0ID_VIEWSFOLDER),
+                                   UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
+                                   UA_QUALIFIEDNAME(0, "Viewtest"), view_attr, NULL, NULL);
+    ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
 
 #ifdef UA_ENABLE_METHODCALLS
     /* MethodNode */
@@ -108,11 +108,12 @@ static UA_Server* makeTestSequence(void) {
     UA_MethodAttributes_init(&ma);
     ma.description = UA_LOCALIZEDTEXT("en_US", "Methodtest");
     ma.displayName = UA_LOCALIZEDTEXT("en_US", "Methodtest");
-    UA_Server_addMethodNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_METHODNODE),
-                            UA_NODEID_NUMERIC(0, 3),
-                            UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
-                            UA_QUALIFIEDNAME_ALLOC(0, "Methodtest"), ma,
-                            NULL, NULL, 0, NULL, 0, NULL, NULL);
+    retval = UA_Server_addMethodNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_METHODNODE),
+                                     UA_NODEID_NUMERIC(0, 3),
+                                     UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
+                                     UA_QUALIFIEDNAME(0, "Methodtest"), ma,
+                                     NULL, NULL, 0, NULL, 0, NULL, NULL);
+    ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
 #endif
 
     return server;
@@ -122,7 +123,8 @@ static UA_VariableNode* makeCompareSequence(void) {
     UA_VariableNode *node = UA_NodeStore_newVariableNode();
 
     UA_Int32 myInteger = 42;
-    UA_Variant_setScalarCopy(&node->value.variant.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
+    UA_Variant_setScalarCopy(&node->value.data.value.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
+    node->value.data.value.hasValue = true;
 
     const UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer");
     UA_QualifiedName_copy(&myIntegerName,&node->browseName);
@@ -435,11 +437,13 @@ START_TEST(ReadSingleAttributeDataTypeWithoutTimestamp) {
     rReq.nodesToRead[0].nodeId = UA_NODEID_STRING_ALLOC(1, "the.answer");
     rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_DATATYPE;
     Service_Read_single(server, &adminSession, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0], &resp);
-    UA_NodeId* respval = (UA_NodeId*) resp.value.data;
     ck_assert_int_eq(0, resp.value.arrayLength);
+    ck_assert_int_eq(UA_STATUSCODE_GOOD, resp.status);
+    ck_assert_int_eq(true, resp.hasValue);
     ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_NODEID], resp.value.type);
+    UA_NodeId* respval = (UA_NodeId*)resp.value.data;
     ck_assert_int_eq(respval->namespaceIndex,0);
-    ck_assert_int_eq(respval->identifier.numeric,UA_NS0ID_INT32);
+    ck_assert_int_eq(respval->identifier.numeric, UA_NS0ID_BASEDATATYPE);
     UA_ReadRequest_deleteMembers(&rReq);
     UA_DataValue_deleteMembers(&resp);
     UA_Server_delete(server);
@@ -607,23 +611,6 @@ START_TEST(ReadSingleAttributeUserExecutableWithoutTimestamp) {
 #endif
 } END_TEST
 
-START_TEST(ReadSingleDataSourceAttributeDataTypeWithoutTimestampFromBrokenSource) {
-    UA_Server *server = makeTestSequence();
-    UA_DataValue resp;
-    UA_DataValue_init(&resp);
-    UA_ReadRequest rReq;
-    UA_ReadRequest_init(&rReq);
-    rReq.nodesToRead = UA_ReadValueId_new();
-    rReq.nodesToReadSize = 1;
-    rReq.nodesToRead[0].nodeId = UA_NODEID_STRING_ALLOC(1, "cpu.temperature1");
-    rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_DATATYPE;
-    Service_Read_single(server, &adminSession, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0], &resp);
-    ck_assert_int_eq(UA_STATUSCODE_GOOD, resp.status);
-    UA_Server_delete(server);
-    UA_ReadRequest_deleteMembers(&rReq);
-    UA_DataValue_deleteMembers(&resp);
-} END_TEST
-
 START_TEST(ReadSingleDataSourceAttributeValueWithoutTimestamp) {
     UA_Server *server = makeTestSequence();
     UA_DataValue resp;
@@ -635,7 +622,7 @@ START_TEST(ReadSingleDataSourceAttributeValueWithoutTimestamp) {
     rReq.nodesToRead[0].nodeId = UA_NODEID_STRING_ALLOC(1, "cpu.temperature");
     rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_VALUE;
     Service_Read_single(server, &adminSession, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0], &resp);
-    ck_assert_int_eq(UA_STATUSCODE_BADINTERNALERROR, resp.status);
+    ck_assert_int_eq(UA_STATUSCODE_GOOD, resp.status);
     UA_Server_delete(server);
     UA_ReadRequest_deleteMembers(&rReq);
     UA_DataValue_deleteMembers(&resp);
@@ -652,7 +639,8 @@ START_TEST(ReadSingleDataSourceAttributeDataTypeWithoutTimestamp) {
     rReq.nodesToRead[0].nodeId = UA_NODEID_STRING_ALLOC(1, "cpu.temperature");
     rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_DATATYPE;
     Service_Read_single(server, &adminSession, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0], &resp);
-    ck_assert_int_eq(UA_STATUSCODE_BADINTERNALERROR, resp.status);
+    ck_assert_int_eq(UA_STATUSCODE_GOOD, resp.status);
+    ck_assert_int_eq(resp.hasServerTimestamp, false);
     UA_Server_delete(server);
     UA_ReadRequest_deleteMembers(&rReq);
     UA_DataValue_deleteMembers(&resp);
@@ -669,7 +657,7 @@ START_TEST (ReadSingleDataSourceAttributeArrayDimensionsWithoutTimestamp) {
     rReq.nodesToRead[0].nodeId = UA_NODEID_STRING_ALLOC(1, "cpu.temperature");
     rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_ARRAYDIMENSIONS;
     Service_Read_single(server, &adminSession, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0], &resp);
-    ck_assert_int_eq(UA_STATUSCODE_BADINTERNALERROR, resp.status);
+    ck_assert_int_eq(UA_STATUSCODE_GOOD, resp.status);
     UA_Server_delete(server);
     UA_ReadRequest_deleteMembers(&rReq);
     UA_DataValue_deleteMembers(&resp);
@@ -860,6 +848,7 @@ START_TEST(WriteSingleAttributeValue) {
     wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
     wValue.attributeId = UA_ATTRIBUTEID_VALUE;
     UA_StatusCode retval = Service_Write_single(server, &adminSession, &wValue);
+    ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
 
     UA_DataValue resp;
     UA_DataValue_init(&resp);
@@ -869,7 +858,7 @@ START_TEST(WriteSingleAttributeValue) {
     id.attributeId = UA_ATTRIBUTEID_VALUE;
     Service_Read_single(server, &adminSession, UA_TIMESTAMPSTORETURN_NEITHER, &id, &resp);
     ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
-    ck_assert(wValue.value.hasValue);
+    ck_assert(resp.hasValue);
     ck_assert_int_eq(20, *(UA_Int32*)resp.value.data);
     UA_DataValue_deleteMembers(&resp);
     UA_Server_delete(server);
@@ -886,7 +875,7 @@ START_TEST(WriteSingleAttributeDataType) {
     wValue.value.hasValue = true;
     UA_Variant_setScalar(&wValue.value.value, &typeId, &UA_TYPES[UA_TYPES_NODEID]);
     UA_StatusCode retval = Service_Write_single(server, &adminSession, &wValue);
-    ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+    ck_assert_int_eq(retval, UA_STATUSCODE_BADTYPEMISMATCH);
     UA_Server_delete(server);
 } END_TEST
 
@@ -901,7 +890,7 @@ START_TEST(WriteSingleAttributeValueRank) {
     wValue.value.hasValue = true;
     UA_StatusCode retval = Service_Write_single(server, &adminSession, &wValue);
     // Returns attributeInvalid, since variant/value may be writable
-    ck_assert_int_eq(retval, UA_STATUSCODE_BADATTRIBUTEIDINVALID);
+    ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
     UA_Server_delete(server);
 } END_TEST
 
@@ -909,14 +898,14 @@ START_TEST(WriteSingleAttributeArrayDimensions) {
     UA_Server *server = makeTestSequence();
     UA_WriteValue wValue;
     UA_WriteValue_init(&wValue);
-    UA_Int32 testValue[] = {-1,-1,-1};
-    UA_Variant_setArray(&wValue.value.value, &testValue, 3, &UA_TYPES[UA_TYPES_INT32]);
+    UA_UInt32 testValue[] = {1,1,1};
+    UA_Variant_setArray(&wValue.value.value, &testValue, 3, &UA_TYPES[UA_TYPES_UINT32]);
     wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
     wValue.attributeId = UA_ATTRIBUTEID_ARRAYDIMENSIONS;
     wValue.value.hasValue = true;
     UA_StatusCode retval = Service_Write_single(server, &adminSession, &wValue);
     // Returns attributeInvalid, since variant/value may be writable
-    ck_assert_int_eq(retval, UA_STATUSCODE_BADATTRIBUTEIDINVALID);
+    ck_assert_int_eq(retval, UA_STATUSCODE_BADTYPEMISMATCH);
     UA_Server_delete(server);
 } END_TEST
 
@@ -1045,7 +1034,6 @@ static Suite * testSuite_services_attributes(void) {
     tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeHistorizingWithoutTimestamp);
     tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeExecutableWithoutTimestamp);
     tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeUserExecutableWithoutTimestamp);
-    tcase_add_test(tc_readSingleAttributes, ReadSingleDataSourceAttributeDataTypeWithoutTimestampFromBrokenSource);
     tcase_add_test(tc_readSingleAttributes, ReadSingleDataSourceAttributeValueWithoutTimestamp);
     tcase_add_test(tc_readSingleAttributes, ReadSingleDataSourceAttributeDataTypeWithoutTimestamp);
     tcase_add_test(tc_readSingleAttributes, ReadSingleDataSourceAttributeArrayDimensionsWithoutTimestamp);

+ 13 - 17
tests/check_session.c

@@ -5,8 +5,7 @@
 #include "server/ua_services.h"
 #include "check.h"
 
-START_TEST(Session_init_ShallWork)
-{
+START_TEST(Session_init_ShallWork) {
     UA_Session session;
     UA_Session_init(&session);
 
@@ -16,23 +15,22 @@ START_TEST(Session_init_ShallWork)
     UA_ApplicationDescription_init(&tmpAppDescription);
     UA_DateTime tmpDateTime;
     UA_DateTime_init(&tmpDateTime);
-    ck_assert_int_eq(session.activated,false);
-    ck_assert_int_eq(session.authenticationToken.identifier.numeric,tmpNodeId.identifier.numeric);
-    ck_assert_int_eq(session.availableContinuationPoints,MAXCONTINUATIONPOINTS);
-    ck_assert_ptr_eq(session.channel,NULL);
-    ck_assert_ptr_eq(session.clientDescription.applicationName.locale.data,NULL);
+    ck_assert_int_eq(session.activated, false);
+    ck_assert_int_eq(session.authenticationToken.identifier.numeric, tmpNodeId.identifier.numeric);
+    ck_assert_int_eq(session.availableContinuationPoints, UA_MAXCONTINUATIONPOINTS);
+    ck_assert_ptr_eq(session.channel, NULL);
+    ck_assert_ptr_eq(session.clientDescription.applicationName.locale.data, NULL);
     ck_assert_ptr_eq(session.continuationPoints.lh_first, NULL);
-    ck_assert_int_eq(session.maxRequestMessageSize,0);
-    ck_assert_int_eq(session.maxResponseMessageSize,0);
-    ck_assert_int_eq(session.sessionId.identifier.numeric,tmpNodeId.identifier.numeric);
-    ck_assert_ptr_eq(session.sessionName.data,NULL);
-    ck_assert_int_eq((int)session.timeout,0);
-    ck_assert_int_eq(session.validTill,tmpDateTime);
+    ck_assert_int_eq(session.maxRequestMessageSize, 0);
+    ck_assert_int_eq(session.maxResponseMessageSize, 0);
+    ck_assert_int_eq(session.sessionId.identifier.numeric, tmpNodeId.identifier.numeric);
+    ck_assert_ptr_eq(session.sessionName.data, NULL);
+    ck_assert_int_eq((int)session.timeout, 0);
+    ck_assert_int_eq(session.validTill, tmpDateTime);
 }
 END_TEST
 
-START_TEST(Session_updateLifetime_ShallWork)
-{
+START_TEST(Session_updateLifetime_ShallWork) {
     UA_Session session;
     UA_Session_init(&session);
     UA_DateTime tmpDateTime;
@@ -68,5 +66,3 @@ int main(void) {
 
     return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
 }
-
-

+ 5 - 5
tests/check_types_builtin.c

@@ -1322,8 +1322,8 @@ START_TEST(UA_Variant_copyShallWorkOn1DArrayExample) {
     srcArray[1] = UA_STRING_ALLOC("_62541");
     srcArray[2] = UA_STRING_ALLOC("opc ua");
 
-    UA_Int32 *dimensions;
-    dimensions = UA_malloc(sizeof(UA_Int32));
+    UA_UInt32 *dimensions;
+    dimensions = UA_malloc(sizeof(UA_UInt32));
     dimensions[0] = 3;
 
     UA_Variant value, copiedValue;
@@ -1373,9 +1373,9 @@ START_TEST(UA_Variant_copyShallWorkOn2DArrayExample) {
     srcArray[4] = 4;
     srcArray[5] = 5;
 
-    UA_Int32 *dimensions = UA_Array_new(2, &UA_TYPES[UA_TYPES_INT32]);
-    UA_Int32 dim1 = 3;
-    UA_Int32 dim2 = 2;
+    UA_UInt32 *dimensions = UA_Array_new(2, &UA_TYPES[UA_TYPES_INT32]);
+    UA_UInt32 dim1 = 3;
+    UA_UInt32 dim2 = 2;
     dimensions[0] = dim1;
     dimensions[1] = dim2;
 

+ 1 - 1
tests/check_types_range.c

@@ -23,7 +23,7 @@ START_TEST(parseRange) {
 
 START_TEST(parseRangeMinEqualMax) {
     UA_NumericRange range;
-    UA_String str = UA_STRING("1:2,1:1");
+    UA_String str = UA_STRING("1:2,1");
     UA_StatusCode retval = parse_numericrange(&str, &range);
     ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
     ck_assert_int_eq(range.dimensionsSize,2);

+ 1 - 1
tools/c2rst.py

@@ -13,7 +13,7 @@ import re
 # - Find the last line beginning with "#ifdef" -> end of the documentation
 
 remove_keyword = [" UA_EXPORT", " UA_FUNC_ATTR_WARN_UNUSED_RESULT",
-                  " UA_FUNC_ATTR_MALLOC"]
+                  " UA_FUNC_ATTR_MALLOC", " UA_RESTRICT "]
 
 def clean_comment(line):
     m = re.search("^( \* |/\*\* )(.*?)( \*/)?$", line)

+ 6 - 8
tools/generate_datatypes.py

@@ -414,21 +414,19 @@ extern "C" {
 #endif
 
 #include "ua_types.h"
-''' + ('\n#include "ua_types_generated.h"\n' if outname != "ua_types" else '') + '''
-
-/**
- * Additional Data Type Definitions
- * ================================
- */
-''')
+''' + ('\n#include "ua_types_generated.h"\n' if outname != "ua_types" else ''))
 
+printh('''/**
+ * Every type is assigned an index in an array containing the type descriptions.
+ * These descriptions are used during type handling (copying, deletion,
+ * binary encoding, ...). */''')
 printh("#define " + outname.upper() + "_COUNT %s" % (str(len(selected_types))))
 printh("extern UA_EXPORT const UA_DataType " + outname.upper() + "[" + outname.upper() + "_COUNT];")
 
 i = 0
 for t in iter_types(types):
     printh("\n/**\n * " +  t.name)
-    printh(" * " + "-" * len(t.name))
+    printh(" * " + "^" * len(t.name))
     if t.description == "":
         printh(" */")
     else:

+ 3 - 0
tools/schema/datatypes_minimal.txt

@@ -158,3 +158,6 @@ MonitoredItemModifyResult
 ModifyMonitoredItemsResponse
 SetMonitoringModeRequest
 SetMonitoringModeResponse
+DataChangeTrigger
+DeadbandType
+DataChangeFilter