Ver código fonte

Merge branch '0.2'

Julius Pfrommer 8 anos atrás
pai
commit
24604543cd
66 arquivos alterados com 5076 adições e 2648 exclusões
  1. 1 0
      .gitignore
  2. 1 0
      AUTHORS
  3. 46 6
      CMakeLists.txt
  4. 18 4
      doc/CMakeLists.txt
  5. 37 11
      doc/building.rst
  6. 0 132
      doc/in_a_nutshell.rst
  7. 28 30
      doc/index.rst
  8. 15 0
      doc/index_html.rst
  9. 5 7
      doc/internal.rst
  10. 67 0
      doc/introduction.rst
  11. 7 7
      doc/tutorial_client_firstSteps.rst
  12. 33 173
      doc/tutorial_noderelations.rst
  13. 4 4
      doc/tutorial_server_firstSteps.rst
  14. 3 3
      doc/tutorial_server_method.rst
  15. 21 67
      doc/tutorial_server_variables.rst
  16. 8 6
      doc/tutorials.rst
  17. 3 0
      examples/CMakeLists.txt
  18. 1 0
      examples/server.c
  19. 4 3
      examples/server_firstSteps.c
  20. 95 0
      examples/server_instantiation.c
  21. 3 1
      include/ua_client.h
  22. 11 13
      include/ua_client_highlevel.h
  23. 22 21
      include/ua_connection.h
  24. 3 2
      include/ua_constants.h
  25. 2 1
      include/ua_log.h
  26. 29 9
      include/ua_server.h
  27. 197 168
      include/ua_types.h
  28. 7 1
      plugins/ua_network_tcp.c
  29. 6 0
      plugins/ua_network_udp.c
  30. 5 1
      src/client/ua_client_highlevel.c
  31. 63 36
      src/server/ua_nodes.c
  32. 244 96
      src/server/ua_nodes.h
  33. 95 64
      src/server/ua_nodestore.c
  34. 5 5
      src/server/ua_nodestore.h
  35. 1 0
      src/server/ua_nodestore_concurrent.c
  36. 347 312
      src/server/ua_server.c
  37. 7 6
      src/server/ua_server_binary.c
  38. 112 7
      src/server/ua_server_internal.h
  39. 278 0
      src/server/ua_server_utils.c
  40. 24 58
      src/server/ua_services.h
  41. 589 358
      src/server/ua_services_attribute.c
  42. 47 113
      src/server/ua_services_call.c
  43. 687 677
      src/server/ua_services_nodemanagement.c
  44. 104 59
      src/server/ua_services_subscription.c
  45. 101 46
      src/server/ua_subscription.c
  46. 1 0
      src/server/ua_subscription.h
  47. 1 1
      src/ua_connection.c
  48. 3 2
      src/ua_session.c
  49. 3 1
      src/ua_session.h
  50. 6 8
      src/ua_types.c
  51. 2 2
      src/ua_types_encoding_binary.h
  52. 13 8
      src/ua_util.h
  53. 96 78
      tests/check_services_attributes.c
  54. 13 17
      tests/check_session.c
  55. 5 5
      tests/check_types_builtin.c
  56. 1 1
      tests/check_types_range.c
  57. 1 1
      tools/c2rst.py
  58. 6 8
      tools/generate_datatypes.py
  59. 0 0
      tools/pyUANamespace/NodeID_AssumeExternal.txt
  60. 0 0
      tools/pyUANamespace/NodeID_Blacklist.txt
  61. 0 0
      tools/pyUANamespace/NodeID_Blacklist_FullNS0.txt
  62. 1514 0
      tools/pyUANamespace/NodeID_NameSpace0_All.txt
  63. 15 5
      tools/pyUANamespace/open62541_MacroHelper.py
  64. 3 0
      tools/pyUANamespace/ua_namespace.py
  65. 4 4
      tools/pyUANamespace/ua_node_types.py
  66. 3 0
      tools/schema/datatypes_minimal.txt

+ 1 - 0
.gitignore

@@ -76,3 +76,4 @@ Makefile
 /exampleClient
 /exampleClient
 /exampleClient_legacy
 /exampleClient_legacy
 /src_generated/
 /src_generated/
+/build

+ 1 - 0
AUTHORS

@@ -3,6 +3,7 @@ The authors of open62541 are (in alphabetical order)
 Bauer, Maximilian
 Bauer, Maximilian
 Graube, Markus <markus.graube (at) tu-dresden.de>
 Graube, Markus <markus.graube (at) tu-dresden.de>
 Gruener, Sten <s.gruener (at) plt.rwth-aachen.de>
 Gruener, Sten <s.gruener (at) plt.rwth-aachen.de>
+Iatrou, Chris Paul <chris_paul.iatrou (at) tu-dresden.de>
 Jeromin, Holger
 Jeromin, Holger
 Palm, Florian <f.palm (at) plt.rwth-aachen.de>
 Palm, Florian <f.palm (at) plt.rwth-aachen.de>
 Pfrommer, Julius <julius.pfrommer (at) kit.edu>
 Pfrommer, Julius <julius.pfrommer (at) kit.edu>

+ 46 - 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/ua_session.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_server.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_server.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_server_binary.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_server_worker.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_securechannel_manager.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_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_discovery.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_services_securechannel.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_session.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_services_attribute.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_nodemanagement.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_services_view.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
                 # method call
                 ${PROJECT_SOURCE_DIR}/src/server/ua_services_call.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_services_call.c
                 # subscriptions
                 # subscriptions
                 ${PROJECT_SOURCE_DIR}/src/server/ua_subscription.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_subscription.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_services_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
                 ${PROJECT_SOURCE_DIR}/src/client/ua_client_highlevel_subscriptions.c
                 # plugins and dependencies
                 # plugins and dependencies
                 ${PROJECT_SOURCE_DIR}/plugins/ua_network_tcp.c
                 ${PROJECT_SOURCE_DIR}/plugins/ua_network_tcp.c
@@ -350,6 +353,7 @@ if(UA_ENABLE_AMALGAMATION)
     add_library(open62541-object OBJECT ${PROJECT_BINARY_DIR}/open62541.c ${PROJECT_BINARY_DIR}/open62541.h)
     add_library(open62541-object OBJECT ${PROJECT_BINARY_DIR}/open62541.c ${PROJECT_BINARY_DIR}/open62541.h)
     target_include_directories(open62541-object PRIVATE ${PROJECT_BINARY_DIR})
     target_include_directories(open62541-object PRIVATE ${PROJECT_BINARY_DIR})
 else()
 else()
+    add_definitions(-DUA_NO_AMALGAMATION)
     add_library(open62541-object OBJECT ${lib_sources} ${internal_headers} ${exported_headers})
     add_library(open62541-object OBJECT ${lib_sources} ${internal_headers} ${exported_headers})
     target_include_directories(open62541-object PRIVATE ${PROJECT_SOURCE_DIR}/include ${PROJECT_SOURCE_DIR}/src
     target_include_directories(open62541-object PRIVATE ${PROJECT_SOURCE_DIR}/include ${PROJECT_SOURCE_DIR}/src
                                                         ${PROJECT_SOURCE_DIR}/plugins ${PROJECT_SOURCE_DIR}/deps)
                                                         ${PROJECT_SOURCE_DIR}/plugins ${PROJECT_SOURCE_DIR}/deps)
@@ -386,3 +390,39 @@ if(UA_BUILD_EXAMPLES_NODESET_COMPILER)
   add_custom_target(generate_informationmodel ALL
   add_custom_target(generate_informationmodel ALL
                     DEPENDS ${PROJECT_BINARY_DIR}/src_generated/nodeset.h ${PROJECT_BINARY_DIR}/src_generated/nodeset.c)
                     DEPENDS ${PROJECT_BINARY_DIR}/src_generated/nodeset.h ${PROJECT_BINARY_DIR}/src_generated/nodeset.c)
 endif()
 endif()
+
+
+##########################
+# Installation           #
+##########################
+# invoke via `make install`
+# specify install location with `-DCMAKE_INSTALL_PREFIX=xyz`
+# Enable shared library with `-DBUILD_SHARED_LIBS=ON`
+
+# export library (either static or shared depending on BUILD_SHARED_LIBS)
+install(TARGETS open62541
+        LIBRARY DESTINATION lib
+        ARCHIVE DESTINATION lib
+    )
+# export amalgamated header open62541.h which is generated due to build of open62541-object
+install(FILES ${PROJECT_BINARY_DIR}/open62541.h DESTINATION include)
+
+
+
+##########################
+# Packaging (DEB/RPM)    #
+##########################
+# invoke via `make package`
+
+SET(CPACK_GENERATOR "TGZ;DEB;RPM")
+SET(CPACK_PACKAGE_VENDOR "open62541 team")
+SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "OPC UA implementation")
+SET(CPACK_PACKAGE_DESCRIPTION "open62541 is a C-based library (linking with C++ projects is possible) with all necessary tools to implement dedicated OPC UA clients and servers, or to integrate OPC UA-based communication into existing applications.")
+SET(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/README.md")
+SET(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
+SET(CPACK_PACKAGE_VERSION_MAJOR "0")
+SET(CPACK_PACKAGE_VERSION_MINOR "2")
+SET(CPACK_PACKAGE_VERSION_PATCH "0")
+SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "open62541 team") #required
+
+INCLUDE(CPack)

+ 18 - 4
doc/CMakeLists.txt

@@ -8,8 +8,20 @@ set(DOC_SRC_DIR   ${PROJECT_BINARY_DIR}/doc_src)
 make_directory(${DOC_SRC_DIR})
 make_directory(${DOC_SRC_DIR})
 file(GLOB DOC_SRC "${PROJECT_SOURCE_DIR}/doc/*")
 file(GLOB DOC_SRC "${PROJECT_SOURCE_DIR}/doc/*")
 list(REMOVE_ITEM DOC_SRC "${PROJECT_SOURCE_DIR}/doc/conf.py")
 list(REMOVE_ITEM DOC_SRC "${PROJECT_SOURCE_DIR}/doc/conf.py")
-configure_file("${PROJECT_SOURCE_DIR}/doc/conf.py" "${DOC_SRC_DIR}/conf.py")
+list(REMOVE_ITEM DOC_SRC "${PROJECT_SOURCE_DIR}/doc/tutorial_server_variables.rst")
+list(REMOVE_ITEM DOC_SRC "${PROJECT_SOURCE_DIR}/doc/tutorial_server_method.rst")
+list(REMOVE_ITEM DOC_SRC "${PROJECT_SOURCE_DIR}/doc/tutorial_server_firstSteps.rst")
+list(REMOVE_ITEM DOC_SRC "${PROJECT_SOURCE_DIR}/doc/tutorial_client_firstSteps.rst")
 file(COPY ${DOC_SRC} DESTINATION ${DOC_SRC_DIR})
 file(COPY ${DOC_SRC} DESTINATION ${DOC_SRC_DIR})
+configure_file("${PROJECT_SOURCE_DIR}/doc/conf.py" "${DOC_SRC_DIR}/conf.py")
+configure_file("${PROJECT_SOURCE_DIR}/doc/tutorial_server_variables.rst"
+               "${DOC_SRC_DIR}/tutorial_server_variables.rst")
+configure_file("${PROJECT_SOURCE_DIR}/doc/tutorial_server_method.rst"
+               "${DOC_SRC_DIR}/tutorial_server_method.rst")
+configure_file("${PROJECT_SOURCE_DIR}/doc/tutorial_server_firstSteps.rst"
+               "${DOC_SRC_DIR}/tutorial_server_firstSteps.rst")
+configure_file("${PROJECT_SOURCE_DIR}/doc/tutorial_client_firstSteps.rst"
+               "${DOC_SRC_DIR}/tutorial_client_firstSteps.rst")
 
 
 function(generate_rst in out)
 function(generate_rst in out)
   add_custom_command(OUTPUT ${out} DEPENDS ${PROJECT_SOURCE_DIR}/tools/c2rst.py ${in}
   add_custom_command(OUTPUT ${out} DEPENDS ${PROJECT_SOURCE_DIR}/tools/c2rst.py ${in}
@@ -26,16 +38,17 @@ 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}/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_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_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}
 add_custom_target(doc_latex ${SPHINX_EXECUTABLE}
   -b latex "${DOC_SRC_DIR}" "${DOC_LATEX_DIR}"
   -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
   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}/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")
   COMMENT "Building LaTeX sources for documentation with Sphinx")
 add_dependencies(doc_latex open62541)
 add_dependencies(doc_latex open62541)
 
 
-
 add_custom_target(doc_pdf ${PDFLATEX_COMPILER} -q "open62541.tex"
 add_custom_target(doc_pdf ${PDFLATEX_COMPILER} -q "open62541.tex"
   WORKING_DIRECTORY ${DOC_LATEX_DIR}
   WORKING_DIRECTORY ${DOC_LATEX_DIR}
   # compile it twice so that the contents pages are correct
   # compile it twice so that the contents pages are correct
@@ -48,6 +61,7 @@ add_custom_target(doc ${SPHINX_EXECUTABLE}
   -b html "${DOC_SRC_DIR}" "${DOC_HTML_DIR}"
   -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
   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}/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")
   COMMENT "Building HTML documentation with Sphinx")
 add_dependencies(doc open62541)
 add_dependencies(doc open62541)

+ 37 - 11
doc/building.rst

@@ -1,10 +1,10 @@
 .. _building:
 .. _building:
 
 
 Building open62541
 Building open62541
-^^^^^^^^^^^^^^^^^^
+==================
 
 
 Building the Examples
 Building the Examples
-=====================
+---------------------
 
 
 Using the GCC compiler, the following calls build the examples on Linux.
 Using the GCC compiler, the following calls build the examples on Linux.
 
 
@@ -15,10 +15,10 @@ Using the GCC compiler, the following calls build the examples on Linux.
    gcc -std=c99 open62541.c server_variable.c -o server
    gcc -std=c99 open62541.c server_variable.c -o server
 
 
 Building the Library
 Building the Library
-====================
+--------------------
 
 
 Building with CMake on Ubuntu or Debian
 Building with CMake on Ubuntu or Debian
----------------------------------------
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 
 .. code-block:: bash
 .. code-block:: bash
 
 
@@ -41,7 +41,7 @@ Building with CMake on Ubuntu or Debian
    make
    make
 
 
 Building with CMake on Windows
 Building with CMake on Windows
-------------------------------
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 
 Here we explain the build process for Visual Studio (2013 or newer). To build
 Here we explain the build process for Visual Studio (2013 or newer). To build
 with MinGW, just replace the compiler selection in the call to CMake.
 with MinGW, just replace the compiler selection in the call to CMake.
@@ -66,7 +66,7 @@ with MinGW, just replace the compiler selection in the call to CMake.
 - Then open :file:`build\open62541.sln` in Visual Studio 2015 and build as usual
 - Then open :file:`build\open62541.sln` in Visual Studio 2015 and build as usual
 
 
 Building on OS X
 Building on OS X
-----------------
+^^^^^^^^^^^^^^^^
 
 
 - Download and install
 - Download and install
 
 
@@ -87,11 +87,37 @@ Building on OS X
 
 
 Follow Ubuntu instructions without the ``apt-get`` commands as these are taken care of by the above packages.
 Follow Ubuntu instructions without the ``apt-get`` commands as these are taken care of by the above packages.
 
 
+Building on OpenBSD
+-------------------
+The procedure below works on OpenBSD 5.8 with gcc version 4.8.4, cmake version 3.2.3 and Python version 2.7.10.
+
+- Install a recent gcc, python and cmake:
+
+.. code-block:: bash
+   
+   pkg_add gcc python cmake
+
+- Tell the system to actually use the recent gcc (it gets installed as egcc on OpenBSD): 
+
+.. code-block:: bash
+   
+   export CC=egcc CXX=eg++
+
+- Now procede as described for Ubuntu/Debian:
+
+.. code-block:: bash
+
+   cd open62541
+   mkdir build
+   cd build
+   cmake ..
+   make
+
 Build Options
 Build Options
-=============
+-------------
 
 
 Build Type and Logging
 Build Type and Logging
-----------------------
+^^^^^^^^^^^^^^^^^^^^^^
 
 
 **CMAKE_BUILD_TYPE**
 **CMAKE_BUILD_TYPE**
   - ``RelWithDebInfo`` -O2 optimization with debug symbols
   - ``RelWithDebInfo`` -O2 optimization with debug symbols
@@ -113,7 +139,7 @@ Further options that are not inherited from the CMake configuration are defined
 in :file:`ua_config.h`. Usually there is no need to adjust them.
 in :file:`ua_config.h`. Usually there is no need to adjust them.
 
 
 UA_BUILD_* group
 UA_BUILD_* group
-----------------
+^^^^^^^^^^^^^^^^
 
 
 By default only the shared object libopen62541.so or the library open62541.dll
 By default only the shared object libopen62541.so or the library open62541.dll
 and open62541.dll.a resp. open62541.lib are build. Additional artifacts can be
 and open62541.dll.a resp. open62541.lib are build. Additional artifacts can be
@@ -139,7 +165,7 @@ specified by the following options:
    Generate a self-signed certificate for the server (openSSL required)
    Generate a self-signed certificate for the server (openSSL required)
 
 
 UA_ENABLE_* group
 UA_ENABLE_* group
------------------
+^^^^^^^^^^^^^^^^^
 
 
 This group contains build options related to the supported OPC UA features.
 This group contains build options related to the supported OPC UA features.
 
 
@@ -174,7 +200,7 @@ be visible in the cmake GUIs.
    Enable udp extension
    Enable udp extension
 
 
 Building a shared library
 Building a shared library
--------------------------
+^^^^^^^^^^^^^^^^^^^^^^^^^
 
 
 open62541 is small enough that most users will want to statically link the library into their programs. If a shared library (.dll, .so) is required, this can be enabled in CMake with the `BUILD_SHARED_LIBS` option.
 open62541 is small enough that most users will want to statically link the library into their programs. If a shared library (.dll, .so) is required, this can be enabled in CMake with the `BUILD_SHARED_LIBS` option.
 Note that this option modifies the :file:`ua_config.h` file that is also included in :file:`open62541.h` for the single-file distribution.
 Note that this option modifies the :file:`ua_config.h` file that is also included in :file:`open62541.h` for the single-file distribution.

+ 0 - 132
doc/in_a_nutshell.rst

@@ -1,132 +0,0 @@
-.. _introduction:
-
-Introduction to OPC UA
-======================
-
-OPC UA, a collection of services
---------------------------------
-
-In OPC UA, all communication is based on service calls, each consisting of a request and a response
-message. Be careful to note the difference between services and methods. Services are pre-defined in
-the standard and cannot be changed. But you can use the *Call* service to invoke user-defined
-methods on the server.
-
-For completeness, the following tables contain all services defined in the standard. Do not bother
-with their details yet. We will introduce the different services later in the text. In open62541,
-each service is implemented in a single function. See the :ref:`services` section for details.
-
-**Establishing communication**
-
-+-----------------------------+-----------------------------+------------------------------+
-| Discovery Service Set       | SecureChannel Service Set   | Session Service Set          |
-+=============================+=============================+==============================+
-| FindServers                 | OpenSecureChannel           | CreateSession                |
-+-----------------------------+-----------------------------+------------------------------+
-| GetEndpoints                | CloseSecureChannel          | ActivateSession              |
-+-----------------------------+-----------------------------+------------------------------+
-| RegisterServer              |                             | CloseSession                 |
-+-----------------------------+-----------------------------+------------------------------+
-|                             |                             | Cancel                       |
-+-----------------------------+-----------------------------+------------------------------+
-
-**Interaction with the information model**
-
-+-----------------------------+-------------------------------+------------------------------+------------------------------+----------------------+
-| Attribute Service Set       | View Service Set              | Method Service Set           | NodeManagement Service Set   | Query Service Set    |
-+=============================+===============================+==============================+==============================+======================+
-| Read                        | Browse                        | Call                         | AddNodes                     | QueryFirst           |
-+-----------------------------+-------------------------------+------------------------------+------------------------------+----------------------+
-| HistoryRead                 | BrowseNext                    |                              | AddReferences                | QueryNext            |
-+-----------------------------+-------------------------------+------------------------------+------------------------------+----------------------+
-| Write                       | TranslateBrowsePathsToNodeids |                              | DeleteNodes                  |                      |
-+-----------------------------+-------------------------------+------------------------------+------------------------------+----------------------+
-| HistoryUpdate               | RegisterNodes                 |                              | DeleteReferences             |                      |
-+-----------------------------+-------------------------------+------------------------------+------------------------------+----------------------+
-|                             | UnregisterNodes               |                              |                              |                      |
-+-----------------------------+-------------------------------+------------------------------+------------------------------+----------------------+
-
-**Notifications**
-
-+-----------------------------+-------------------------------+
-| MonitoredItem Service Set   | Subscription Service Set      |
-+=============================+===============================+
-| CreateMonitoredItems        | CreateSubscription            |
-+-----------------------------+-------------------------------+
-| ModifyMonitoreditems        | ModifySubscription            |
-+-----------------------------+-------------------------------+
-| SetMonitoringMode           | SetPublishingMode             |
-+-----------------------------+-------------------------------+
-| SetTriggering               | Publish                       |
-+-----------------------------+-------------------------------+
-| DeleteMonitoredItems        | Republish                     |
-+-----------------------------+-------------------------------+
-|                             | TransferSubscription          |
-+-----------------------------+-------------------------------+
-|                             | DeleteSubscription            |
-+-----------------------------+-------------------------------+
-
-OPC UA, a web of nodes
-----------------------
-
-The information model in each OPC UA server is a web of interconnected nodes.
-There are eight different types of nodes.
-
-+-----------------------+-------------------+
-| ReferenceTypeNode     | MethodNode        |
-+-----------------------+-------------------+
-| DataTypeNode          | ObjectTypeNode    |
-+-----------------------+-------------------+
-| VariableTypeNode      | ObjectNode        |
-+-----------------------+-------------------+
-| VariableNode          | ViewNode          |
-+-----------------------+-------------------+
-
-Depending on its type, every node contains different attributes. Some
-attributes, are contained in all node types:
-
-+----------------+---------------+
-| Name           | Type          |
-+================+===============+
-| nodeID         | NodeId        |
-+----------------+---------------+
-| nodeClass      | NodeClass     |
-+----------------+---------------+
-| browseName     | QualifiedName |
-+----------------+---------------+
-| displayName    | LocalizedText |
-+----------------+---------------+
-| description    | LocalizedText |
-+----------------+---------------+
-| writeMask      | UInt32        |
-+----------------+---------------+
-| userWriteMask  | UInt32        |
-+----------------+---------------+
-| referencesSize | Int32         |
-+----------------+---------------+
-| references     |ReferenceNode[]|
-+----------------+---------------+
-
-Nodes are interconnected by directed reference-triples of the form ``(nodeid,
-referencetype, target-nodeid)``. Therefore an OPC UA information model is
-easiest imagined as a *web of nodes*. Reference types can be
-
-- standard- or user-defined and
-- either non-hierarchical (e.g., indicating the type of a variable-node) or
-  hierarchical (e.g., indicating a parent-child relationship).
-
-OPC UA, a protocol
-------------------
-
-The OPC UA protocol (both binary and XML-based) is based on 25 *built-in*
-datatypes. In open62541, these are defined in ua_types.h.
-
-The builtin datatypes are combined to more complex structures. When the structure contains an array,
-then the size of the array is stored in an Int32 value just before the array itself. A size of -1
-indicates an undefined array. Positive sizes (and zero) have the usual semantics.
-
-Most importantly, every service has a request and a response message defined as such a data
-structure. The entire OPC UA protocol revolves around the exchange of these request and response
-messages. Their exact definitions can be looked up here:
-https://opcfoundation.org/UA/schemas/Opc.Ua.Types.bsd.xml. In open62541, we autogenerate the
-C-structs to handle the standard-defined structures automatically. See ua_types_generated.h for
-comparison.

+ 28 - 30
doc/index.rst

@@ -1,30 +1,28 @@
-Welcome to open62541's documentation!
-=====================================
-
-`OPC UA <http://en.wikipedia.org/wiki/OPC_Unified_Architecture>`_ (short for OPC
-Unified Architecture) is a protocol for industrial communication and has been
-standardized in the IEC62541. At its core, OPC UA defines a set of services to
-interact with a server-side object-oriented information model. Besides the
-service-calls initiated by the client, push-notification of remote events (such
-as data changes) can be negotiated with the server. The client/server
-interaction is mapped either to a binary encoding and TCP-based transmission or
-to SOAP-based webservices. As of late, OPC UA is marketed as the one standard
-for non-realtime industrial communication.
-
-We believe that it is best to understand OPC UA *from the inside out*, building
-upon conceptually simple first principles. After establishing a first
-understanding, we go on explaining how these principles are realized in detail.
-Examples are given based on the *open62541* implementation of the
-standard.
-
-.. toctree::
-   :maxdepth: 3
-
-   in_a_nutshell
-   building
-   tutorials
-   types
-   constants
-   server
-   client
-   internal
+.. only:: html
+
+   .. include:: index_html.rst
+
+.. only:: latex
+
+   open62541 Documentation
+   #######################
+
+   .. include:: introduction.rst
+
+   .. include:: building.rst
+
+   .. include:: tutorials.rst
+
+   .. include:: types.rst
+
+   .. include:: information_modelling.rst
+
+   .. include:: services.rst
+
+   .. include:: server.rst
+
+   .. include:: client.rst
+
+   .. include:: constants.rst
+
+   .. include:: internal.rst

+ 15 - 0
doc/index_html.rst

@@ -0,0 +1,15 @@
+.. include:: introduction.rst
+
+.. toctree::
+    :hidden:
+
+    self
+    building
+    tutorials
+    types
+    information_modelling
+    services
+    server
+    client
+    constants
+    internal

+ 5 - 7
doc/internal.rst

@@ -1,10 +1,8 @@
 Internals
 Internals
-#########
+=========
 
 
-.. toctree::
+.. include:: nodestore.rst
 
 
-   services
-   nodestore
-   connection
-   log
-   types_generated
+.. include:: connection.rst
+
+.. include:: log.rst

Diferenças do arquivo suprimidas por serem muito extensas
+ 67 - 0
doc/introduction.rst


+ 7 - 7
doc/tutorial_client_firstSteps.rst

@@ -1,5 +1,5 @@
-5. Building a simple client
-===========================
+Building a simple client
+------------------------
 
 
 You should already have a basic server from the previous tutorials. open62541
 You should already have a basic server from the previous tutorials. open62541
 provides both a server- and clientside API, so creating a client is as easy as
 provides both a server- and clientside API, so creating a client is as easy as
@@ -29,24 +29,24 @@ Compilation is very much similar to the server example.
    $ gcc -std=c99 open6251.c myClient.c -o myClient
    $ gcc -std=c99 open6251.c myClient.c -o myClient
 
 
 Reading a node attibute
 Reading a node attibute
------------------------
+^^^^^^^^^^^^^^^^^^^^^^^
 
 
 In this example we are going to connect to the server from the second tutorial
 In this example we are going to connect to the server from the second tutorial
 and read the value-attribute of the added variable node.
 and read the value-attribute of the added variable node.
 
 
-.. literalinclude:: ../../examples/client_firstSteps.c
+.. literalinclude:: ${PROJECT_SOURCE_DIR}/examples/client_firstSteps.c
    :language: c
    :language: c
    :linenos:
    :linenos:
    :lines: 4,5,12,14-
    :lines: 4,5,12,14-
 
 
 
 
 Further tasks
 Further tasks
--------------
+^^^^^^^^^^^^^
 * Try to connect to some other OPC UA server by changing
 * Try to connect to some other OPC UA server by changing
-  "opc.tcp://localhost:16664" to an appropriate address (remember that the
+  ``opc.tcp://localhost:16664`` to an appropriate address (remember that the
   queried node is contained in any OPC UA server).
   queried node is contained in any OPC UA server).
 * Try to set the value of the variable node (ns=1,i="the.answer") containing an
 * Try to set the value of the variable node (ns=1,i="the.answer") containing an
-  "Int32" from the example server (which is built in
+  ``Int32`` from the example server (which is built in
   :doc:`tutorial_server_firstSteps`) using "UA_Client_write" function. The
   :doc:`tutorial_server_firstSteps`) using "UA_Client_write" function. The
   example server needs some more modifications, i.e., changing request types.
   example server needs some more modifications, i.e., changing request types.
   The answer can be found in "examples/exampleClient.c".
   The answer can be found in "examples/exampleClient.c".

Diferenças do arquivo suprimidas por serem muito extensas
+ 33 - 173
doc/tutorial_noderelations.rst


+ 4 - 4
doc/tutorial_server_firstSteps.rst

@@ -1,12 +1,12 @@
-1. Building a simple server
-===========================
+Building a simple server
+------------------------
 
 
 This series of tutorial guide you through your first steps with open62541. For
 This series of tutorial guide you through your first steps with open62541. For
 compiling the examples, you need a compiler (MS Visual Studio 2015 or newer,
 compiling the examples, you need a compiler (MS Visual Studio 2015 or newer,
 GCC, Clang and MinGW32 are all known to be working). The compilation
 GCC, Clang and MinGW32 are all known to be working). The compilation
 instructions are given for GCC but should be straightforward to adapt.
 instructions are given for GCC but should be straightforward to adapt.
 
 
-It will also be very helpfull to install an OPC UA Client with a graphical
+It will also be very helpful to install an OPC UA Client with a graphical
 frontend, such as UAExpert by Unified Automation. That will enable you to
 frontend, such as UAExpert by Unified Automation. That will enable you to
 examine the information model of any OPC UA server.
 examine the information model of any OPC UA server.
 
 
@@ -17,7 +17,7 @@ have the ``open62541.c/.h`` files in the current folder.
 
 
 Now create a new C source-file called ``myServer.c`` with the following content:
 Now create a new C source-file called ``myServer.c`` with the following content:
 
 
-.. literalinclude:: ../../examples/server_firstSteps.c
+.. literalinclude:: ${PROJECT_SOURCE_DIR}/examples/server_firstSteps.c
    :language: c
    :language: c
    :linenos:
    :linenos:
    :lines: 4,12,14-34
    :lines: 4,12,14-34

+ 3 - 3
doc/tutorial_server_method.rst

@@ -1,5 +1,5 @@
-3. Adding a server-side method
-==============================
+Adding a server-side method
+---------------------------
 
 
 This tutorial demonstrates how to add method nodes to the server. Use an UA
 This tutorial demonstrates how to add method nodes to the server. Use an UA
 client, e.g., UaExpert to call the method (right-click on the method node ->
 client, e.g., UaExpert to call the method (right-click on the method node ->
@@ -16,7 +16,7 @@ The last example presents a way to bind a new method callback to an already
 instantiated method node.
 instantiated method node.
 
 
 
 
-.. literalinclude:: ../../examples/server_method.c
+.. literalinclude:: ${PROJECT_SOURCE_DIR}/examples/server_method.c
    :language: c
    :language: c
    :linenos:
    :linenos:
    :lines: 4,5,14,16-
    :lines: 4,5,14,16-

+ 21 - 67
doc/tutorial_server_variables.rst

@@ -1,30 +1,27 @@
 .. role:: ccode(code)
 .. role:: ccode(code)
       :language: c
       :language: c
 
 
-2. Adding variables to a server
-===============================
+Adding variables to a server
+----------------------------
 
 
 This tutorial shows how to add variable nodes to a server and how these can be
 This tutorial shows how to add variable nodes to a server and how these can be
-connected to a physical process in the background. Make sure to read the
-:ref:`introduction <introduction>` first.
+connected to a physical process in the background.
 
 
 This is the code for a server with a single variable node holding an integer. We
 This is the code for a server with a single variable node holding an integer. We
 will take this example to explain some of the fundamental concepts of open62541.
 will take this example to explain some of the fundamental concepts of open62541.
 
 
-.. literalinclude:: ../../examples/server_variable.c
+.. literalinclude:: ${PROJECT_SOURCE_DIR}/examples/server_variable.c
    :language: c
    :language: c
    :linenos:
    :linenos:
    :lines: 4,13,15-
    :lines: 4,13,15-
 
 
-
 Variants and Datatypes
 Variants and Datatypes
-----------------------
+^^^^^^^^^^^^^^^^^^^^^^
 
 
-The datatype *variant* belongs to the built-in datatypes of OPC UA and is used
-as a container type. A variant can hold any other datatype as a scalar (except
-variant) or as an array. Array variants can additionally denote the
+The datatype :ref:`variant` belongs to the built-in datatypes of OPC UA and is
+used as a container type. A variant can hold any other datatype as a scalar
+(except variant) or as an array. Array variants can additionally denote the
 dimensionality of the data (e.g. a 2x3 matrix) in an additional integer array.
 dimensionality of the data (e.g. a 2x3 matrix) in an additional integer array.
-You can find the code that defines the variant datatype :ref:`here <variant>`.
 
 
 The `UA_VariableAttributes` type contains a variant member `value`. The command
 The `UA_VariableAttributes` type contains a variant member `value`. The command
 :ccode:`UA_Variant_setScalar(&attr.value, &myInteger,
 :ccode:`UA_Variant_setScalar(&attr.value, &myInteger,
@@ -33,20 +30,9 @@ this does not make a copy of the integer (for which `UA_Variant_setScalarCopy`
 can be used). The variant (and its content) is then copied into the newly
 can be used). The variant (and its content) is then copied into the newly
 created node.
 created node.
 
 
-Since it is a bit involved to set variants by hand, there are four basic
-functions you should be aware of:
-
-  * **UA_Variant_setScalar** will set the contents of the variant to a pointer
-    to the object that you pass with the call. Make sure to never deallocate
-    that object while the variant exists!
-  * **UA_Variant_setScalarCopy** will copy the object pointed to into a new
-    object of the same type and attach that to the variant.
-  * **UA_Variant_setArray** will set the contents of the variant to be an array
-    and point to the exact pointer/object that you passed the call.
-  * **UA_Variant_setArrayCopy** will create a copy of the array passed with the
-    call.
-
-The equivalent code using allocations is as follows:
+The above code could have used allocations by making copies of all entries of
+the attribute variant. Then, of course, the variant content needs to be deleted
+to prevent memleaks.
 
 
 .. code-block:: c
 .. code-block:: c
 
 
@@ -65,25 +51,11 @@ information model. For that, we state the NodeId of the parent node and the
 (hierarchical) reference to the parent node.
 (hierarchical) reference to the parent node.
 
 
 NodeIds
 NodeIds
--------
-
-A node ID is a unique identifier in server's context. It is composed of two members:
-
-+-------------+-----------------+---------------------------+
-| Type        | Name            | Notes                     |
-+=============+=================+===========================+
-| UInt16      | namespaceIndex  |  Number of the namespace  |
-+-------------+-----------------+---------------------------+
-| Union       | identifier      |  One idenifier of the     |
-|             |  * String       |  listed types             |
-|             |  * Integer      |                           |
-|             |  * GUID         |                           |
-|             |  * ByteString   |                           |
-+-------------+-----------------+---------------------------+
-
-The first parameter is the number of node's namespace, the second one may be a
-numeric, a string or a GUID (Globally Unique ID) identifier. The following are
-some examples for their usage.
+^^^^^^^
+
+A :ref:`nodeid` is a unique identifier in server's context. It contains the
+number of node's namespace and either a numeric, string or GUID (Globally
+Unique ID) identifier. The following are some examples for their usage.
 
 
 .. code-block:: c
 .. code-block:: c
 
 
@@ -91,30 +63,12 @@ some examples for their usage.
 
 
    UA_NodeId id2 = UA_NODEID_STRING(1, "testid"); /* points to the static string */
    UA_NodeId id2 = UA_NODEID_STRING(1, "testid"); /* points to the static string */
 
 
-   UA_NodeId id3 = UA_NODEID_STRING_ALLOC(1, "testid");
+   UA_NodeId id3 = UA_NODEID_STRING_ALLOC(1, "testid"); /* copy to memory */
    UA_NodeId_deleteMembers(&id3); /* free the allocated string */
    UA_NodeId_deleteMembers(&id3); /* free the allocated string */
 
 
 
 
-Adding a variable node to the server that contains a user-defined callback
---------------------------------------------------------------------------
-
-The latter case allows to define callback functions that are executed on read or
-write of the node. In this case an "UA_DataSource" containing the respective
-callback pointer is intserted into the node.
-
-Consider ``examples/server_datasource.c`` in the repository. The examples are
-compiled if the Cmake option UA_BUILD_EXAMPLE is turned on.
-
-
-UA_Server_addVariableNode vs. UA_Server_addDataSourceVariableNode
-UA_ValueCallback
-UA_DataSource
-
-
-Asserting success/failure
--------------------------
+Adding a Variable with an external Data Source
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 
-Almost all functions of the open62541 API will return a `StatusCode` 32-bit
-integer. The actual statuscodes are defined :ref:`here <statuscodes>`. Normally,
-the functions should return `UA_STATUSCODE_GOOD`, which maps to the zero
-integer.
+In order to couple a running process to a variable, a :ref:`datasource` is used.
+Consider ``examples/server_datasource.c`` in the repository for an example.

+ 8 - 6
doc/tutorials.rst

@@ -1,10 +1,12 @@
 Tutorials
 Tutorials
 =========
 =========
 
 
-.. toctree::
+.. include:: tutorial_server_firstSteps.rst
 
 
-   tutorial_server_firstSteps
-   tutorial_server_variables
-   tutorial_server_method
-   tutorial_noderelations
-   tutorial_client_firstSteps
+.. include:: tutorial_server_variables.rst
+
+.. include:: tutorial_server_method.rst
+
+.. include:: tutorial_noderelations.rst
+
+.. include:: tutorial_client_firstSteps.rst

+ 3 - 0
examples/CMakeLists.txt

@@ -55,6 +55,9 @@ target_link_libraries(server_datasource ${LIBS})
 add_executable(server_firstSteps server_firstSteps.c $<TARGET_OBJECTS:open62541-object>)
 add_executable(server_firstSteps server_firstSteps.c $<TARGET_OBJECTS:open62541-object>)
 target_link_libraries(server_firstSteps ${LIBS})
 target_link_libraries(server_firstSteps ${LIBS})
 
 
+add_executable(server_instantiation server_instantiation.c $<TARGET_OBJECTS:open62541-object>)
+target_link_libraries(server_instantiation ${LIBS})
+
 add_executable(server_repeated_job server_repeated_job.c $<TARGET_OBJECTS:open62541-object>)
 add_executable(server_repeated_job server_repeated_job.c $<TARGET_OBJECTS:open62541-object>)
 target_link_libraries(server_repeated_job ${LIBS})
 target_link_libraries(server_repeated_job ${LIBS})
 
 

+ 1 - 0
examples/server.c

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

+ 4 - 3
examples/server_firstSteps.c

@@ -22,13 +22,14 @@ int main(void) {
     signal(SIGTERM, stopHandler);
     signal(SIGTERM, stopHandler);
 
 
     UA_ServerConfig config = UA_ServerConfig_standard;
     UA_ServerConfig config = UA_ServerConfig_standard;
-    UA_ServerNetworkLayer nl = UA_ServerNetworkLayerTCP(UA_ConnectionConfig_standard, 16664);
+    UA_ServerNetworkLayer nl;
+    nl = UA_ServerNetworkLayerTCP(UA_ConnectionConfig_standard, 16664);
     config.networkLayers = &nl;
     config.networkLayers = &nl;
     config.networkLayersSize = 1;
     config.networkLayersSize = 1;
     UA_Server *server = UA_Server_new(config);
     UA_Server *server = UA_Server_new(config);
 
 
-    UA_StatusCode retval = UA_Server_run(server, &running);
+    UA_Server_run(server, &running);
     UA_Server_delete(server);
     UA_Server_delete(server);
     nl.deleteMembers(&nl);
     nl.deleteMembers(&nl);
-    return (int)retval;
+    return 0;
 }
 }

+ 95 - 0
examples/server_instantiation.c

@@ -0,0 +1,95 @@
+/* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
+ * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
+
+#include <signal.h>
+
+#ifdef UA_NO_AMALGAMATION
+#include "ua_types.h"
+#include "ua_server.h"
+#include "ua_config_standard.h"
+#include "ua_network_tcp.h"
+#else
+#include "open62541.h"
+#endif
+
+UA_Boolean running = true;
+static void stopHandler(int sig) {
+    running = false;
+}
+
+int main(void) {
+    signal(SIGINT,  stopHandler);
+    signal(SIGTERM, stopHandler);
+
+    UA_ServerConfig config = UA_ServerConfig_standard;
+    UA_ServerNetworkLayer nl = UA_ServerNetworkLayerTCP(UA_ConnectionConfig_standard, 16664);
+    config.networkLayers = &nl;
+    config.networkLayersSize = 1;
+    UA_Server *server = UA_Server_new(config);
+    
+    /* Create a rudimentary objectType
+     * 
+     * BaseObjectType
+     * |
+     * +- (OT) MamalType
+     *    + (V) Age
+     *    + (OT) DogType
+     *      |
+     *      + (V) Name
+     */ 
+    UA_StatusCode retval;
+    UA_ObjectTypeAttributes otAttr;
+    UA_ObjectTypeAttributes_init(&otAttr);
+    
+    otAttr.description = UA_LOCALIZEDTEXT("en_US", "A mamal");
+    otAttr.displayName = UA_LOCALIZEDTEXT("en_US", "MamalType");
+    UA_Server_addObjectTypeNode(server, UA_NODEID_NUMERIC(1, 10000), 
+                                UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE), UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
+                                UA_QUALIFIEDNAME(1, "MamalType"), otAttr, NULL, NULL);
+  
+    UA_VariableAttributes   vAttr;
+    UA_VariableAttributes_init(&vAttr);
+    vAttr.description =  UA_LOCALIZEDTEXT("en_US", "This mamals Age in months");
+    vAttr.displayName =  UA_LOCALIZEDTEXT("en_US", "Age");
+    UA_UInt32 ageVar = 0;
+    UA_Variant_setScalarCopy(&vAttr.value, &ageVar, &UA_TYPES[UA_TYPES_UINT32]);
+    UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, 10001), 
+                              UA_NODEID_NUMERIC(1, 10000), UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY),
+                              UA_QUALIFIEDNAME(1, "Age"), UA_NODEID_NULL, vAttr, NULL, NULL);
+  
+    UA_ObjectTypeAttributes_init(&otAttr);
+    otAttr.description = UA_LOCALIZEDTEXT("en_US", "A dog, subtype of mamal");
+    otAttr.displayName = UA_LOCALIZEDTEXT("en_US", "DogType");
+    UA_Server_addObjectTypeNode(server, UA_NODEID_NUMERIC(1, 10002), 
+                                UA_NODEID_NUMERIC(1, 10000), UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
+                                UA_QUALIFIEDNAME(1, "DogType"), otAttr, NULL, NULL);
+    
+    UA_VariableAttributes_init(&vAttr);
+    vAttr.description =  UA_LOCALIZEDTEXT("en_US", "This mamals Age in months");
+    vAttr.displayName =  UA_LOCALIZEDTEXT("en_US", "Name");
+    UA_String defaultName = UA_STRING("unnamed dog");
+    UA_Variant_setScalarCopy(&vAttr.value, &defaultName, &UA_TYPES[UA_TYPES_STRING]);
+    UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, 10003), 
+                              UA_NODEID_NUMERIC(1, 10002), UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY),
+                              UA_QUALIFIEDNAME(1, "Name"), UA_NODEID_NULL, vAttr, NULL, NULL);
+    
+    /* Instatiate a dog named bello:
+     * (O) Objects
+     *   + O Bello <DogType>
+     *     + Age 
+     *     + Name
+     */
+    
+    UA_ObjectAttributes oAttr;
+    UA_ObjectAttributes_init(&oAttr);
+    oAttr.description = UA_LOCALIZEDTEXT("en_US", "A dog named Bello");
+    oAttr.displayName = UA_LOCALIZEDTEXT("en_US", "Bello");
+    UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, 0), 
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
+                            UA_QUALIFIEDNAME(1, "Bello"), UA_NODEID_NUMERIC(1, 10002), oAttr, NULL, NULL);
+    
+    retval = UA_Server_run(server, &running);
+    UA_Server_delete(server);
+    nl.deleteMembers(&nl);
+    return (int)retval;
+}

+ 3 - 1
include/ua_client.h

@@ -35,7 +35,7 @@ extern "C" {
  * convenience, some functionality has been wrapped in :ref:`high-level
  * convenience, some functionality has been wrapped in :ref:`high-level
  * abstractions <client-highlevel>`.
  * abstractions <client-highlevel>`.
  *
  *
- * **However**: At this point, the client does not yet contain its own thread or
+ * **However**: At this time, the client does not yet contain its own thread or
  * event-driven main-loop. So the client will not perform any actions
  * event-driven main-loop. So the client will not perform any actions
  * automatically in the background. This is especially relevant for
  * automatically in the background. This is especially relevant for
  * subscriptions. The user will have to periodically call
  * subscriptions. The user will have to periodically call
@@ -172,6 +172,7 @@ UA_Client_Service_write(UA_Client *client, const UA_WriteRequest request) {
 /**
 /**
  * Method Service Set
  * Method Service Set
  * ^^^^^^^^^^^^^^^^^^ */
  * ^^^^^^^^^^^^^^^^^^ */
+#ifdef UA_ENABLE_METHODCALLS
 static UA_INLINE UA_CallResponse
 static UA_INLINE UA_CallResponse
 UA_Client_Service_call(UA_Client *client, const UA_CallRequest request) {
 UA_Client_Service_call(UA_Client *client, const UA_CallRequest request) {
     UA_CallResponse response;
     UA_CallResponse response;
@@ -179,6 +180,7 @@ UA_Client_Service_call(UA_Client *client, const UA_CallRequest request) {
                         &response, &UA_TYPES[UA_TYPES_CALLRESPONSE]);
                         &response, &UA_TYPES[UA_TYPES_CALLRESPONSE]);
     return response;
     return response;
 }
 }
+#endif
 
 
 /**
 /**
  * NodeManagement Service Set
  * NodeManagement Service Set

+ 11 - 13
include/ua_client_highlevel.h

@@ -27,22 +27,19 @@ extern "C" {
  *
  *
  * Highlevel Client Functionality
  * Highlevel Client Functionality
  * ------------------------------
  * ------------------------------
- * The following definitions are convenience functions making use of the
- * standard OPC UA services in the background.
- *
- * The high level abstractions concetrate on getting the job done in a simple
- * manner for the user. This is a less flexible way of handling the stack,
- * because at many places sensible defaults are presumed; at the same time using
- * these functions is the easiest way of implementing an OPC UA application, as
- * you will not have to consider all the details that go into the OPC UA
- * services. A concept of how nodes and datatypes are used are completely
- * sufficient to use OPC UA with this layer.
  *
  *
- * If more flexibility is needed, you can always achieve the same functionality
- * using the raw :ref:`OPC UA services <client-services>`.
+ * The following definitions are convenience functions making use of the
+ * standard OPC UA services in the background. This is a less flexible way of
+ * handling the stack, because at many places sensible defaults are presumed; at
+ * the same time using these functions is the easiest way of implementing an OPC
+ * UA application, as you will not have to consider all the details that go into
+ * the OPC UA services. If more flexibility is needed, you can always achieve
+ * the same functionality using the raw :ref:`OPC UA services
+ * <client-services>`.
  *
  *
  * Read Attributes
  * Read Attributes
  * ^^^^^^^^^^^^^^^
  * ^^^^^^^^^^^^^^^
+ *
  * The following functions can be used to retrieve a single node attribute. Use
  * The following functions can be used to retrieve a single node attribute. Use
  * the regular service to read several attributes at once. */
  * the regular service to read several attributes at once. */
 /* Don't call this function, use the typed versions */
 /* Don't call this function, use the typed versions */
@@ -221,6 +218,7 @@ UA_Client_readUserExecutableAttribute(UA_Client *client, const UA_NodeId nodeId,
 /**
 /**
  * Write Attributes
  * Write Attributes
  * ^^^^^^^^^^^^^^^^
  * ^^^^^^^^^^^^^^^^
+ *
  * The following functions can be use to write a single node attribute at a
  * The following functions can be use to write a single node attribute at a
  * time. Use the regular write service to write several attributes at once. */
  * time. Use the regular write service to write several attributes at once. */
 /* Don't call this function, use the typed versions */
 /* Don't call this function, use the typed versions */
@@ -555,7 +553,7 @@ UA_Client_addMethodNode(UA_Client *client, const UA_NodeId requestedNewNodeId,
  *
  *
  * Subscriptions Handling
  * Subscriptions Handling
  * ^^^^^^^^^^^^^^^^^^^^^^
  * ^^^^^^^^^^^^^^^^^^^^^^
- * At this point, the client does not yet contain its own thread or event-driven
+ * At this time, the client does not yet contain its own thread or event-driven
  * main-loop. So the client will not perform any actions automatically in the
  * main-loop. So the client will not perform any actions automatically in the
  * background. This is especially relevant for subscriptions. The user will have
  * background. This is especially relevant for subscriptions. The user will have
  * to periodically call `UA_Client_Subscriptions_manuallySendPublishRequest`.
  * to periodically call `UA_Client_Subscriptions_manuallySendPublishRequest`.

+ 22 - 21
include/ua_connection.h

@@ -43,7 +43,7 @@ typedef struct UA_SecureChannel UA_SecureChannel;
  * without being aware of the underlying transport technology.
  * without being aware of the underlying transport technology.
  *
  *
  * Connection Config
  * Connection Config
- * ================= */
+ * ^^^^^^^^^^^^^^^^^ */
 typedef struct UA_ConnectionConfig {
 typedef struct UA_ConnectionConfig {
     UA_UInt32 protocolVersion;
     UA_UInt32 protocolVersion;
     UA_UInt32 sendBufferSize;
     UA_UInt32 sendBufferSize;
@@ -56,7 +56,7 @@ extern const UA_EXPORT UA_ConnectionConfig UA_ConnectionConfig_standard;
 
 
 /**
 /**
  * Connection Structure
  * Connection Structure
- * ==================== */
+ * ^^^^^^^^^^^^^^^^^^^^ */
 typedef enum UA_ConnectionState {
 typedef enum UA_ConnectionState {
     UA_CONNECTION_OPENING,     /* The socket is open, but the HEL/ACK handshake
     UA_CONNECTION_OPENING,     /* The socket is open, but the HEL/ACK handshake
                                   is not done */
                                   is not done */
@@ -119,28 +119,29 @@ void UA_EXPORT UA_Connection_deleteMembers(UA_Connection *connection);
 
 
 
 
 /**
 /**
- * EndpointURL helper
- */
-
-/**
- * Split the given endpoint url into hostname and port
+ * EndpointURL Helper
+ * ^^^^^^^^^^^^^^^^^^ */
+/* Split the given endpoint url into hostname and port
  * @param endpointUrl The endpoint URL to split up
  * @param endpointUrl The endpoint URL to split up
- * @param hostname the target array for hostname. Has to be at least 256 size. If an IPv6 address is given, hostname contains e.g. '[2001:0db8:85a3::8a2e:0370:7334]'
+ * @param hostname the target array for hostname. Has to be at least 256 size.
+ *        If an IPv6 address is given, hostname contains e.g.
+ *        '[2001:0db8:85a3::8a2e:0370:7334]'
  * @param port set to the port of the url or 0
  * @param port set to the port of the url or 0
- * @param path pointing to the end of given endpointUrl or to NULL if no path given. The starting '/' is NOT included in path
- * @return UA_STATUSCODE_BADOUTOFRANGE if url too long, UA_STATUSCODE_BADATTRIBUTEIDINVALID if url not starting with 'opc.tcp://', UA_STATUSCODE_GOOD on success
- */
-UA_StatusCode UA_EXPORT UA_EndpointUrl_split(const char *endpointUrl, char *hostname, UA_UInt16 * port, const char ** path);
-
-/**
- * Convert given byte string to a number. Returns the number of valid digits.
- * Stops if a non-digit char is found and returns the number of digits up to that point.
- * @param buf
- * @param buflen
- * @param number
- * @return
+ * @param path pointing to the end of given endpointUrl or to NULL if no
+ *        path given. The starting '/' is NOT included in path
+ * @return UA_STATUSCODE_BADOUTOFRANGE if url too long,
+ *         UA_STATUSCODE_BADATTRIBUTEIDINVALID if url not starting with
+ *         'opc.tcp://', UA_STATUSCODE_GOOD on success
  */
  */
-size_t UA_EXPORT UA_readNumber(UA_Byte *buf, size_t buflen, UA_UInt32 *number);
+UA_StatusCode UA_EXPORT
+UA_EndpointUrl_split(const char *endpointUrl, char *hostname,
+                     UA_UInt16 * port, const char ** path);
+
+/* Convert given byte string to a positive number. Returns the number of valid
+ * digits. Stops if a non-digit char is found and returns the number of digits
+ * up to that point. */
+size_t UA_EXPORT
+UA_readNumber(UA_Byte *buf, size_t buflen, UA_UInt32 *number);
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus
 } // extern "C"
 } // extern "C"

+ 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
  * 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
  * 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
  * This section contains numerical and string constants that are defined in the
  * OPC UA standard.
  * OPC UA standard.
  *
  *
+ * .. _attribute-id:
+ *
  * Attribute Id
  * Attribute Id
  * ------------
  * ------------
  * Every node in an OPC UA information model contains attributes depending on
  * Every node in an OPC UA information model contains attributes depending on

+ 2 - 1
include/ua_log.h

@@ -25,6 +25,7 @@ extern "C" {
 /**
 /**
  * Logging
  * Logging
  * -------
  * -------
+ *
  * Servers and clients may contain a logger. Every logger needs to implement the
  * Servers and clients may contain a logger. Every logger needs to implement the
  * `UA_Logger` signature. An example logger that writes to stdout is provided in
  * `UA_Logger` signature. An example logger that writes to stdout is provided in
  * the plugins folder.
  * the plugins folder.
@@ -105,7 +106,7 @@ typedef void (*UA_Logger)(UA_LogLevel level, UA_LogCategory category,
 
 
 /**
 /**
  * Convenience macros for complex types
  * Convenience macros for complex types
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */
 #define UA_PRINTF_GUID_FORMAT "{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}"
 #define UA_PRINTF_GUID_FORMAT "{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}"
 #define UA_PRINTF_GUID_DATA(GUID) (GUID).data1, (GUID).data2, (GUID).data3, \
 #define UA_PRINTF_GUID_DATA(GUID) (GUID).data1, (GUID).data2, (GUID).data3, \
         (GUID).data4[0], (GUID).data4[1], (GUID).data4[2], (GUID).data4[3], \
         (GUID).data4[0], (GUID).data4[1], (GUID).data4[2], (GUID).data4[3], \

+ 29 - 9
include/ua_server.h

@@ -30,6 +30,8 @@ extern "C" {
 #include "ua_connection.h"
 #include "ua_connection.h"
 
 
 /**
 /**
+ * .. _server:
+ *
  * Server
  * Server
  * ======
  * ======
  *
  *
@@ -393,13 +395,6 @@ UA_Server_readExecutable(UA_Server *server, const UA_NodeId nodeId,
  * - UserAccessLevel
  * - UserAccessLevel
  * - UserExecutable
  * - 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 */
  * Historizing is currently unsupported */
 /* Overwrite an attribute of a node. The specialized functions below provide a
 /* Overwrite an attribute of a node. The specialized functions below provide a
  * more concise syntax.
  * more concise syntax.
@@ -477,6 +472,27 @@ UA_Server_writeValue(UA_Server *server, const UA_NodeId nodeId,
                              &UA_TYPES[UA_TYPES_VARIANT], &value);
                              &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
 static UA_INLINE UA_StatusCode
 UA_Server_writeAccessLevel(UA_Server *server, const UA_NodeId nodeId,
 UA_Server_writeAccessLevel(UA_Server *server, const UA_NodeId nodeId,
                            const UA_UInt32 accessLevel) {
                            const UA_UInt32 accessLevel) {
@@ -575,6 +591,8 @@ UA_Server_call(UA_Server *server, const UA_CallMethodRequest *request);
  * - Method callbacks, where a user-defined method is exposed in the information
  * - Method callbacks, where a user-defined method is exposed in the information
  *   model
  *   model
  *
  *
+ * .. _datasource:
+ *
  * Data Source Callback
  * Data Source Callback
  * ~~~~~~~~~~~~~~~~~~~~
  * ~~~~~~~~~~~~~~~~~~~~
  *
  *
@@ -647,6 +665,8 @@ UA_Server_setVariableNode_valueCallback(UA_Server *server, const UA_NodeId nodeI
                                         const UA_ValueCallback callback);
                                         const UA_ValueCallback callback);
 
 
 /**
 /**
+ * .. _object-lifecycle:
+ *
  * Object Lifecycle Management Callbacks
  * Object Lifecycle Management Callbacks
  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  * Lifecycle management adds constructor and destructor callbacks to
  * Lifecycle management adds constructor and destructor callbacks to
@@ -691,7 +711,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
  * 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
  * 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). */
  * 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
 /* 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
  * also called for all sub-nodes contained in an object or variable type node
  * that is instantiated. */
  * that is instantiated. */
@@ -736,12 +755,13 @@ UA_Server_addVariableTypeNode(UA_Server *server,
                               const UA_NodeId parentNodeId,
                               const UA_NodeId parentNodeId,
                               const UA_NodeId referenceTypeId,
                               const UA_NodeId referenceTypeId,
                               const UA_QualifiedName browseName,
                               const UA_QualifiedName browseName,
+                              const UA_NodeId typeDefinition,
                               const UA_VariableTypeAttributes attr,
                               const UA_VariableTypeAttributes attr,
                               UA_InstantiationCallback *instantiationCallback,
                               UA_InstantiationCallback *instantiationCallback,
                               UA_NodeId *outNewNodeId) {
                               UA_NodeId *outNewNodeId) {
     return __UA_Server_addNode(server, UA_NODECLASS_VARIABLETYPE,
     return __UA_Server_addNode(server, UA_NODECLASS_VARIABLETYPE,
                                requestedNewNodeId, parentNodeId, referenceTypeId,
                                requestedNewNodeId, parentNodeId, referenceTypeId,
-                               browseName, UA_NODEID_NULL,
+                               browseName, typeDefinition,
                                (const UA_NodeAttributes*)&attr,
                                (const UA_NodeAttributes*)&attr,
                                &UA_TYPES[UA_TYPES_VARIABLETYPEATTRIBUTES],
                                &UA_TYPES[UA_TYPES_VARIABLETYPEATTRIBUTES],
                                instantiationCallback, outNewNodeId);
                                instantiationCallback, outNewNodeId);

+ 197 - 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
  * 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
  * 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
  * 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
  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
  * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
- * details.
- */
+ * details. */
 
 
 #ifndef UA_TYPES_H_
 #ifndef UA_TYPES_H_
 #define UA_TYPES_H_
 #define UA_TYPES_H_
@@ -29,35 +27,39 @@ extern "C" {
  * Data Types
  * 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. In open62541,
+ * the builtin data types are defined manually. All other 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 when values of
+ * different 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
+ *   value is already initialized.
+ * - ``UA_StatusCode T_copy(const T *src, T *dst)``: Copy the content of the
+ *   data type. Returns ``UA_STATUSCODE_GOOD`` or
+ *   ``UA_STATUSCODE_BADOUTOFMEMORY``.
+ * - ``void T_deleteMembers(T *ptr)``: Delete the dynamically allocated content
+ *   of the data type and perform a ``T_init`` to reset the type.
+ * - ``void T_delete(T *ptr)``: Delete the content of the data type and the
+ *   memory for the data type itself. */
 
 
 #define UA_BUILTIN_TYPES_COUNT 25U
 #define UA_BUILTIN_TYPES_COUNT 25U
 
 
 /**
 /**
- * Builtin Types Part 1
- * --------------------
+ * Builtin Types
+ * -------------
  *
  *
  * Boolean
  * Boolean
  * ^^^^^^^
  * ^^^^^^^
@@ -71,65 +73,65 @@ typedef bool UA_Boolean;
  * ^^^^^
  * ^^^^^
  * An integer value between -128 and 127. */
  * An integer value between -128 and 127. */
 typedef int8_t UA_SByte;
 typedef int8_t UA_SByte;
-#define UA_SBYTE_MAX 127
 #define UA_SBYTE_MIN (-128)
 #define UA_SBYTE_MIN (-128)
+#define UA_SBYTE_MAX 127
 
 
 /**
 /**
  * Byte
  * Byte
  * ^^^^
  * ^^^^
  * An integer value between 0 and 256. */
  * An integer value between 0 and 256. */
 typedef uint8_t UA_Byte;
 typedef uint8_t UA_Byte;
-#define UA_BYTE_MAX 256
 #define UA_BYTE_MIN 0
 #define UA_BYTE_MIN 0
+#define UA_BYTE_MAX 256
 
 
 /**
 /**
  * Int16
  * Int16
  * ^^^^^
  * ^^^^^
  * An integer value between -32 768 and 32 767. */
  * An integer value between -32 768 and 32 767. */
 typedef int16_t UA_Int16;
 typedef int16_t UA_Int16;
-#define UA_INT16_MAX 32767
 #define UA_INT16_MIN (-32768)
 #define UA_INT16_MIN (-32768)
+#define UA_INT16_MAX 32767
 
 
 /**
 /**
  * UInt16
  * UInt16
  * ^^^^^^
  * ^^^^^^
  * An integer value between 0 and 65 535. */
  * An integer value between 0 and 65 535. */
 typedef uint16_t UA_UInt16;
 typedef uint16_t UA_UInt16;
-#define UA_UINT16_MAX 65535
 #define UA_UINT16_MIN 0
 #define UA_UINT16_MIN 0
+#define UA_UINT16_MAX 65535
 
 
 /**
 /**
  * Int32
  * Int32
  * ^^^^^
  * ^^^^^
  * An integer value between -2 147 483 648 and 2 147 483 647. */
  * An integer value between -2 147 483 648 and 2 147 483 647. */
 typedef int32_t UA_Int32;
 typedef int32_t UA_Int32;
-#define UA_INT32_MAX 2147483647
 #define UA_INT32_MIN (-2147483648)
 #define UA_INT32_MIN (-2147483648)
+#define UA_INT32_MAX 2147483647
 
 
 /**
 /**
  * UInt32
  * UInt32
  * ^^^^^^
  * ^^^^^^
  * An integer value between 0 and 4 294 967 295. */
  * An integer value between 0 and 4 294 967 295. */
 typedef uint32_t UA_UInt32;
 typedef uint32_t UA_UInt32;
-#define UA_UINT32_MAX 4294967295
 #define UA_UINT32_MIN 0
 #define UA_UINT32_MIN 0
+#define UA_UINT32_MAX 4294967295
 
 
 /**
 /**
  * Int64
  * 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. */
  * 9 223 372 036 854 775 807. */
 typedef int64_t UA_Int64;
 typedef int64_t UA_Int64;
-#define UA_INT64_MAX (int64_t)9223372036854775807
 #define UA_INT64_MIN ((int64_t)-9223372036854775808)
 #define UA_INT64_MIN ((int64_t)-9223372036854775808)
+#define UA_INT64_MAX (int64_t)9223372036854775807
 
 
 /**
 /**
  * UInt64
  * UInt64
  * ^^^^^^
  * ^^^^^^
  * An integer value between 0 and 18 446 744 073 709 551 615. */
  * An integer value between 0 and 18 446 744 073 709 551 615. */
 typedef uint64_t UA_UInt64;
 typedef uint64_t UA_UInt64;
-#define UA_UINT64_MAX (int64_t)18446744073709551615
 #define UA_UINT64_MIN (int64_t)0
 #define UA_UINT64_MIN (int64_t)0
+#define UA_UINT64_MAX (int64_t)18446744073709551615
 
 
 /**
 /**
  * Float
  * Float
@@ -154,76 +156,6 @@ typedef double UA_Double;
 typedef uint32_t UA_StatusCode;
 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
  * String
  * ^^^^^^
  * ^^^^^^
  * A sequence of Unicode characters. Strings are just an array of UA_Byte. */
  * A sequence of Unicode characters. Strings are just an array of UA_Byte. */
@@ -342,6 +274,8 @@ UA_BYTESTRING_ALLOC(const char *chars) {
 typedef UA_String UA_XmlElement;
 typedef UA_String UA_XmlElement;
 
 
 /**
 /**
+ * .. _nodeid:
+ *
  * NodeId
  * NodeId
  * ^^^^^^
  * ^^^^^^
  * An identifier for a node in the address space of an OPC UA Server. */
  * An identifier for a node in the address space of an OPC UA Server. */
@@ -462,6 +396,8 @@ UA_EXPANDEDNODEID_BYTESTRING_ALLOC(UA_UInt16 nsIndex, const char *chars) {
 }
 }
 
 
 /**
 /**
+ * .. _qualifiedname:
+ *
  * QualifiedName
  * QualifiedName
  * ^^^^^^^^^^^^^
  * ^^^^^^^^^^^^^
  * A name qualified by a namespace. */
  * A name qualified by a namespace. */
@@ -508,63 +444,52 @@ UA_LOCALIZEDTEXT_ALLOC(const char *locale, const char *text) {
     lt.text = UA_STRING_ALLOC(text); return lt;
     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:
  *
  *
  * 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 {
 typedef struct {
-    const UA_DataType *type; /* The data type description */
+    const UA_DataType *type;      /* The data type description */
     enum {
     enum {
         UA_VARIANT_DATA,          /* The data has the same lifecycle as the
         UA_VARIANT_DATA,          /* The data has the same lifecycle as the
                                      variant */
                                      variant */
@@ -572,10 +497,10 @@ typedef struct {
                                      shall not be deleted at the end of the
                                      shall not be deleted at the end of the
                                      variant's lifecycle. */
                                      variant's lifecycle. */
     } storageType;
     } 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;
 } UA_Variant;
 
 
 /* Returns true if the variant contains a scalar value. Note that empty variants
 /* Returns true if the variant contains a scalar value. Note that empty variants
@@ -668,6 +593,40 @@ UA_Variant_setRangeCopy(UA_Variant *v, const void *array,
                         size_t arraySize, const UA_NumericRange range);
                         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
  * DataValue
  * ^^^^^^^^^
  * ^^^^^^^^^
  * A data value with an associated status code and timestamps. */
  * A data value with an associated status code and timestamps. */
@@ -709,6 +668,8 @@ typedef struct UA_DiagnosticInfo {
 } UA_DiagnosticInfo;
 } UA_DiagnosticInfo;
 
 
 /**
 /**
+ * .. _generic-types:
+ *
  * Generic Type Handling
  * Generic Type Handling
  * ---------------------
  * ---------------------
  * The builtin types can be combined to data structures. All information about a
  * The builtin types can be combined to data structures. All information about a
@@ -729,8 +690,8 @@ typedef struct {
     UA_Boolean namespaceZero : 1; /* The type of the member is defined in
     UA_Boolean namespaceZero : 1; /* The type of the member is defined in
                                      namespace zero. In this implementation,
                                      namespace zero. In this implementation,
                                      types from custom namespace may contain
                                      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_Boolean isArray       : 1; /* The member is an array */
 } UA_DataTypeMember;
 } UA_DataTypeMember;
 
 
@@ -748,8 +709,8 @@ struct UA_DataType {
                                     pointers */
                                     pointers */
     UA_Boolean overlayable  : 1; /* The type has the identical memory layout in
     UA_Boolean overlayable  : 1; /* The type has the identical memory layout in
                                     memory and on the binary stream. */
                                     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;
     UA_DataTypeMember *members;
 };
 };
 
 
@@ -758,8 +719,8 @@ struct UA_DataType {
 /* Allocates and initializes a variable of type dataType
 /* Allocates and initializes a variable of type dataType
  *
  *
  * @param type The datatype description
  * @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;
 void UA_EXPORT * UA_new(const UA_DataType *type) UA_FUNC_ATTR_MALLOC;
 
 
 /* Initializes a variable to default values
 /* Initializes a variable to default values
@@ -797,6 +758,65 @@ void UA_EXPORT UA_deleteMembers(void *p, const UA_DataType *type);
  * @param type The datatype description of the variable */
  * @param type The datatype description of the variable */
 void UA_EXPORT UA_delete(void *p, const UA_DataType *type);
 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
  * Random Number Generator
  * -----------------------
  * -----------------------
@@ -807,6 +827,15 @@ void UA_EXPORT UA_random_seed(UA_UInt64 seed);
 UA_UInt32 UA_EXPORT UA_UInt32_random(void); /* no cryptographic entropy */
 UA_UInt32 UA_EXPORT UA_UInt32_random(void); /* no cryptographic entropy */
 UA_Guid UA_EXPORT UA_Guid_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
 #ifdef __cplusplus
 } // extern "C"
 } // extern "C"
 #endif
 #endif

+ 7 - 1
plugins/ua_network_tcp.c

@@ -42,13 +42,19 @@
 # ifdef __QNX__
 # ifdef __QNX__
 #  include <sys/socket.h>
 #  include <sys/socket.h>
 # endif
 # endif
+#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
+# include <sys/param.h>
+# if defined(BSD)
+#  include<sys/socket.h>
+# endif
+#endif
 # ifndef __CYGWIN__
 # ifndef __CYGWIN__
 #  include <netinet/tcp.h>
 #  include <netinet/tcp.h>
 # endif
 # endif
 #endif
 #endif
 
 
 /* unsigned int for windows and workaround to a glibc bug */
 /* unsigned int for windows and workaround to a glibc bug */
-#if defined(_WIN32) || (defined(__GNU_LIBRARY__) && (__GNU_LIBRARY__ <= 6) && (__GLIBC__ <= 2) && (__GLIBC_MINOR__ < 16))
+#if defined(_WIN32) || defined(__OpenBSD__) || (defined(__GNU_LIBRARY__) && (__GNU_LIBRARY__ <= 6) && (__GLIBC__ <= 2) && (__GLIBC_MINOR__ < 16))
 # define UA_fd_set(fd, fds) FD_SET((unsigned int)fd, fds)
 # define UA_fd_set(fd, fds) FD_SET((unsigned int)fd, fds)
 # define UA_fd_isset(fd, fds) FD_ISSET((unsigned int)fd, fds)
 # define UA_fd_isset(fd, fds) FD_ISSET((unsigned int)fd, fds)
 #else
 #else

+ 6 - 0
plugins/ua_network_udp.c

@@ -24,6 +24,12 @@
 #ifdef __QNX__
 #ifdef __QNX__
 #include <sys/socket.h>
 #include <sys/socket.h>
 #endif
 #endif
+#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
+# include <sys/param.h>
+# if defined(BSD)
+#  include<sys/socket.h>
+# endif
+#endif
 # define CLOSESOCKET(S) close(S)
 # define CLOSESOCKET(S) close(S)
 
 
 #define MAXBACKLOG 100
 #define MAXBACKLOG 100

+ 5 - 1
src/client/ua_client_highlevel.c

@@ -211,6 +211,8 @@ __UA_Client_addNode(UA_Client *client, const UA_NodeClass nodeClass, const UA_No
 /* Call */
 /* Call */
 /********/
 /********/
 
 
+#ifdef UA_ENABLE_METHODCALLS
+
 UA_StatusCode
 UA_StatusCode
 UA_Client_call(UA_Client *client, const UA_NodeId objectId, const UA_NodeId methodId, size_t inputSize,
 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) {
                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;
     return retval;
 }
 }
 
 
+#endif
+
 /********************/
 /********************/
 /* Write Attributes */
 /* Write Attributes */
 /********************/
 /********************/
@@ -328,7 +332,7 @@ __UA_Client_readAttribute(UA_Client *client, const UA_NodeId *nodeId, UA_Attribu
     UA_DataValue *res = response.results;
     UA_DataValue *res = response.results;
     if(res->hasStatus != UA_STATUSCODE_GOOD)
     if(res->hasStatus != UA_STATUSCODE_GOOD)
         retval = res->hasStatus;
         retval = res->hasStatus;
-    else if(!res->hasValue || !UA_Variant_isScalar(&res->value))
+    else if(!res->hasValue)
         retval = UA_STATUSCODE_BADUNEXPECTEDERROR;
         retval = UA_STATUSCODE_BADUNEXPECTEDERROR;
     if(retval != UA_STATUSCODE_GOOD) {
     if(retval != UA_STATUSCODE_GOOD) {
         UA_ReadResponse_deleteMembers(&response);
         UA_ReadResponse_deleteMembers(&response);

+ 63 - 36
src/server/ua_nodes.c

@@ -1,4 +1,6 @@
+#include "ua_server_internal.h"
 #include "ua_nodes.h"
 #include "ua_nodes.h"
+#include "ua_nodestore.h"
 #include "ua_util.h"
 #include "ua_util.h"
 
 
 void UA_Node_deleteMembersAnyNodeClass(UA_Node *node) {
 void UA_Node_deleteMembersAnyNodeClass(UA_Node *node) {
@@ -7,7 +9,8 @@ void UA_Node_deleteMembersAnyNodeClass(UA_Node *node) {
     UA_QualifiedName_deleteMembers(&node->browseName);
     UA_QualifiedName_deleteMembers(&node->browseName);
     UA_LocalizedText_deleteMembers(&node->displayName);
     UA_LocalizedText_deleteMembers(&node->displayName);
     UA_LocalizedText_deleteMembers(&node->description);
     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->references = NULL;
     node->referencesSize = 0;
     node->referencesSize = 0;
 
 
@@ -22,8 +25,13 @@ void UA_Node_deleteMembersAnyNodeClass(UA_Node *node) {
     case UA_NODECLASS_VARIABLE:
     case UA_NODECLASS_VARIABLE:
     case UA_NODECLASS_VARIABLETYPE: {
     case UA_NODECLASS_VARIABLETYPE: {
         UA_VariableNode *p = (UA_VariableNode*)node;
         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;
         break;
     }
     }
     case UA_NODECLASS_REFERENCETYPE: {
     case UA_NODECLASS_REFERENCETYPE: {
@@ -48,21 +56,43 @@ UA_ObjectNode_copy(const UA_ObjectNode *src, UA_ObjectNode *dst) {
 }
 }
 
 
 static UA_StatusCode
 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->valueRank = src->valueRank;
     dst->valueSource = src->valueSource;
     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
     } else
         dst->value.dataSource = src->value.dataSource;
         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->accessLevel = src->accessLevel;
-    dst->userAccessLevel = src->accessLevel;
+    dst->userAccessLevel = src->userAccessLevel;
     dst->minimumSamplingInterval = src->minimumSamplingInterval;
     dst->minimumSamplingInterval = src->minimumSamplingInterval;
     dst->historizing = src->historizing;
     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
 static UA_StatusCode
@@ -82,23 +112,10 @@ UA_ObjectTypeNode_copy(const UA_ObjectTypeNode *src, UA_ObjectTypeNode *dst) {
 }
 }
 
 
 static UA_StatusCode
 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->isAbstract = src->isAbstract;
     dst->symmetric = src->symmetric;
     dst->symmetric = src->symmetric;
     return retval;
     return retval;
@@ -133,7 +150,8 @@ UA_StatusCode UA_Node_copyAnyNodeClass(const UA_Node *src, UA_Node *dst) {
         UA_Node_deleteMembersAnyNodeClass(dst);
         UA_Node_deleteMembersAnyNodeClass(dst);
         return retval;
         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]);
                             &UA_TYPES[UA_TYPES_REFERENCENODE]);
     if(retval != UA_STATUSCODE_GOOD) {
     if(retval != UA_STATUSCODE_GOOD) {
         UA_Node_deleteMembersAnyNodeClass(dst);
         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 */
     /* copy unique content of the nodeclass */
     switch(src->nodeClass) {
     switch(src->nodeClass) {
     case UA_NODECLASS_OBJECT:
     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;
         break;
     case UA_NODECLASS_VARIABLE:
     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;
         break;
     case UA_NODECLASS_METHOD:
     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;
         break;
     case UA_NODECLASS_OBJECTTYPE:
     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;
         break;
     case UA_NODECLASS_VARIABLETYPE:
     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;
         break;
     case UA_NODECLASS_REFERENCETYPE:
     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;
         break;
     case UA_NODECLASS_DATATYPE:
     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;
         break;
     case UA_NODECLASS_VIEW:
     case UA_NODECLASS_VIEW:
         retval = UA_ViewNode_copy((const UA_ViewNode*)src, (UA_ViewNode*)dst);
         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:
     default:
         break;
         break;
     }
     }
+
     if(retval != UA_STATUSCODE_GOOD)
     if(retval != UA_STATUSCODE_GOOD)
         UA_Node_deleteMembersAnyNodeClass(dst);
         UA_Node_deleteMembersAnyNodeClass(dst);
+
     return retval;
     return retval;
 }
 }

+ 244 - 96
src/server/ua_nodes.h

@@ -1,17 +1,51 @@
 #ifndef UA_NODES_H_
 #ifndef UA_NODES_H_
 #define 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
+ * =====================
+ *
+ * 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_NodeId nodeId;                           \
     UA_NodeClass nodeClass;                     \
     UA_NodeClass nodeClass;                     \
     UA_QualifiedName browseName;                \
     UA_QualifiedName browseName;                \
@@ -23,123 +57,237 @@
     UA_ReferenceNode *references;
     UA_ReferenceNode *references;
 
 
 typedef struct {
 typedef struct {
-    UA_STANDARD_NODEMEMBERS
+    UA_NODE_BASEATTRIBUTES
 } UA_Node;
 } 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 {
 typedef enum {
-    UA_VALUESOURCE_VARIANT,
+    UA_VALUESOURCE_DATA,
     UA_VALUESOURCE_DATASOURCE
     UA_VALUESOURCE_DATASOURCE
 } UA_ValueSource;
 } 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 {
 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 accessLevel;
     UA_Byte userAccessLevel;
     UA_Byte userAccessLevel;
     UA_Double minimumSamplingInterval;
     UA_Double minimumSamplingInterval;
-    UA_Boolean historizing;
+    UA_Boolean historizing; /* currently unsupported */
 } UA_VariableNode;
 } 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 {
 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_Boolean isAbstract;
 } UA_VariableTypeNode;
 } 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 {
 typedef struct {
-    UA_STANDARD_NODEMEMBERS
+    UA_NODE_BASEATTRIBUTES
     UA_Boolean executable;
     UA_Boolean executable;
     UA_Boolean userExecutable;
     UA_Boolean userExecutable;
+
+    /* Members specific to open62541 */
     void *methodHandle;
     void *methodHandle;
     UA_MethodCallback attachedMethod;
     UA_MethodCallback attachedMethod;
 } UA_MethodNode;
 } 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 {
 typedef struct {
-    UA_STANDARD_NODEMEMBERS
+    UA_NODE_BASEATTRIBUTES
     UA_Byte eventNotifier;
     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. Abstract objects cannot be
+ * instantiated. See :ref:`object-lifecycle` for the use of constructor and
+ * destructor callbacks. */
+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. DataTypes may contain
+ * arrays. But they always describe the structure of a single instance. In
+ * open62541, DataTypeNodes in the information model 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 {
 typedef struct {
-    UA_STANDARD_NODEMEMBERS
+    UA_NODE_BASEATTRIBUTES
     UA_Boolean isAbstract;
     UA_Boolean isAbstract;
 } UA_DataTypeNode;
 } 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_ */
 #endif /* UA_NODES_H_ */

+ 95 - 64
src/server/ua_nodestore.c

@@ -1,4 +1,5 @@
 #include "ua_nodestore.h"
 #include "ua_nodestore.h"
+#include "ua_server_internal.h"
 #include "ua_util.h"
 #include "ua_util.h"
 
 
 #ifndef UA_ENABLE_MULTITHREADING /* conditional compilation */
 #ifndef UA_ENABLE_MULTITHREADING /* conditional compilation */
@@ -10,6 +11,8 @@ typedef struct UA_NodeStoreEntry {
     UA_Node node;
     UA_Node node;
 } UA_NodeStoreEntry;
 } UA_NodeStoreEntry;
 
 
+#define UA_NODESTORE_TOMBSTONE ((UA_NodeStoreEntry*)0x01)
+
 struct UA_NodeStore {
 struct UA_NodeStore {
     UA_NodeStoreEntry **entries;
     UA_NodeStoreEntry **entries;
     UA_UInt32 size;
     UA_UInt32 size;
@@ -20,7 +23,7 @@ struct UA_NodeStore {
 #include "ua_nodestore_hash.inc"
 #include "ua_nodestore_hash.inc"
 
 
 /* The size of the hash-map is always a prime number. They are chosen to be
 /* The size of the hash-map is always a prime number. They are chosen to be
-   close to the next power of 2. So the size ca. doubles with each prime. */
+ * close to the next power of 2. So the size ca. doubles with each prime. */
 static hash_t const primes[] = {
 static hash_t const primes[] = {
     7,         13,         31,         61,         127,         251,
     7,         13,         31,         61,         127,         251,
     509,       1021,       2039,       4093,       8191,        16381,
     509,       1021,       2039,       4093,       8191,        16381,
@@ -29,7 +32,8 @@ static hash_t const primes[] = {
     134217689, 268435399,  536870909,  1073741789, 2147483647,  4294967291
     134217689, 268435399,  536870909,  1073741789, 2147483647,  4294967291
 };
 };
 
 
-static UA_UInt16 higher_prime_index(hash_t n) {
+static UA_UInt16
+higher_prime_index(hash_t n) {
     UA_UInt16 low  = 0;
     UA_UInt16 low  = 0;
     UA_UInt16 high = (UA_UInt16)(sizeof(primes) / sizeof(hash_t));
     UA_UInt16 high = (UA_UInt16)(sizeof(primes) / sizeof(hash_t));
     while(low != high) {
     while(low != high) {
@@ -42,7 +46,8 @@ static UA_UInt16 higher_prime_index(hash_t n) {
     return low;
     return low;
 }
 }
 
 
-static UA_NodeStoreEntry * instantiateEntry(UA_NodeClass nodeClass) {
+static UA_NodeStoreEntry *
+instantiateEntry(UA_NodeClass nodeClass) {
     size_t size = sizeof(UA_NodeStoreEntry) - sizeof(UA_Node);
     size_t size = sizeof(UA_NodeStoreEntry) - sizeof(UA_Node);
     switch(nodeClass) {
     switch(nodeClass) {
     case UA_NODECLASS_OBJECT:
     case UA_NODECLASS_OBJECT:
@@ -79,63 +84,75 @@ static UA_NodeStoreEntry * instantiateEntry(UA_NodeClass nodeClass) {
     return entry;
     return entry;
 }
 }
 
 
-static void deleteEntry(UA_NodeStoreEntry *entry) {
+static void
+deleteEntry(UA_NodeStoreEntry *entry) {
     UA_Node_deleteMembersAnyNodeClass(&entry->node);
     UA_Node_deleteMembersAnyNodeClass(&entry->node);
     UA_free(entry);
     UA_free(entry);
 }
 }
 
 
-/* Returns true if an entry was found under the nodeid. Otherwise, returns
-   false and sets slot to a pointer to the next free slot. */
-static UA_Boolean
-containsNodeId(const UA_NodeStore *ns, const UA_NodeId *nodeid, UA_NodeStoreEntry ***entry) {
+/* returns slot of a valid node or null */
+static UA_NodeStoreEntry **
+findNode(const UA_NodeStore *ns, const UA_NodeId *nodeid) {
     hash_t h = hash(nodeid);
     hash_t h = hash(nodeid);
     UA_UInt32 size = ns->size;
     UA_UInt32 size = ns->size;
     hash_t idx = mod(h, size);
     hash_t idx = mod(h, size);
-    UA_NodeStoreEntry *e = ns->entries[idx];
+    hash_t hash2 = mod2(h, size);
 
 
-    if(!e) {
-        *entry = &ns->entries[idx];
-        return false;
+    while(true) {
+        UA_NodeStoreEntry *e = ns->entries[idx];
+        if(!e)
+            return NULL;
+        if(e > UA_NODESTORE_TOMBSTONE &&
+           UA_NodeId_equal(&e->node.nodeId, nodeid))
+            return &ns->entries[idx];
+        idx += hash2;
+        if(idx >= size)
+            idx -= size;
     }
     }
 
 
-    if(UA_NodeId_equal(&e->node.nodeId, nodeid)) {
-        *entry = &ns->entries[idx];
-        return true;
-    }
+    /* NOTREACHED */
+    return NULL;
+}
 
 
+/* returns an empty slot or null if the nodeid exists */
+static UA_NodeStoreEntry **
+findSlot(const UA_NodeStore *ns, const UA_NodeId *nodeid) {
+    hash_t h = hash(nodeid);
+    UA_UInt32 size = ns->size;
+    hash_t idx = mod(h, size);
     hash_t hash2 = mod2(h, size);
     hash_t hash2 = mod2(h, size);
-    for(;;) {
+
+    while(true) {
+        UA_NodeStoreEntry *e = ns->entries[idx];
+        if(e > UA_NODESTORE_TOMBSTONE &&
+           UA_NodeId_equal(&e->node.nodeId, nodeid))
+            return NULL;
+        if(ns->entries[idx] <= UA_NODESTORE_TOMBSTONE)
+            return &ns->entries[idx];
         idx += hash2;
         idx += hash2;
         if(idx >= size)
         if(idx >= size)
             idx -= size;
             idx -= size;
-        e = ns->entries[idx];
-        if(!e) {
-            *entry = &ns->entries[idx];
-            return false;
-        }
-        if(UA_NodeId_equal(&e->node.nodeId, nodeid)) {
-            *entry = &ns->entries[idx];
-            return true;
-        }
     }
     }
 
 
     /* NOTREACHED */
     /* NOTREACHED */
-    return true;
+    return NULL;
 }
 }
 
 
 /* The occupancy of the table after the call will be about 50% */
 /* The occupancy of the table after the call will be about 50% */
-static UA_StatusCode expand(UA_NodeStore *ns) {
+static UA_StatusCode
+expand(UA_NodeStore *ns) {
     UA_UInt32 osize = ns->size;
     UA_UInt32 osize = ns->size;
     UA_UInt32 count = ns->count;
     UA_UInt32 count = ns->count;
-    /* Resize only when table after removal of unused elements is either too full or too empty  */
+    /* Resize only when table after removal of unused elements is either too
+       full or too empty */
     if(count * 2 < osize && (count * 8 > osize || osize <= UA_NODESTORE_MINSIZE))
     if(count * 2 < osize && (count * 8 > osize || osize <= UA_NODESTORE_MINSIZE))
         return UA_STATUSCODE_GOOD;
         return UA_STATUSCODE_GOOD;
 
 
     UA_NodeStoreEntry **oentries = ns->entries;
     UA_NodeStoreEntry **oentries = ns->entries;
     UA_UInt32 nindex = higher_prime_index(count * 2);
     UA_UInt32 nindex = higher_prime_index(count * 2);
     UA_UInt32 nsize = primes[nindex];
     UA_UInt32 nsize = primes[nindex];
-    UA_NodeStoreEntry **nentries;
-    if(!(nentries = UA_calloc(nsize, sizeof(UA_NodeStoreEntry*))))
+    UA_NodeStoreEntry **nentries = UA_calloc(nsize, sizeof(UA_NodeStoreEntry*));
+    if(!nentries)
         return UA_STATUSCODE_BADOUTOFMEMORY;
         return UA_STATUSCODE_BADOUTOFMEMORY;
 
 
     ns->entries = nentries;
     ns->entries = nentries;
@@ -144,10 +161,9 @@ static UA_StatusCode expand(UA_NodeStore *ns) {
 
 
     /* recompute the position of every entry and insert the pointer */
     /* recompute the position of every entry and insert the pointer */
     for(size_t i = 0, j = 0; i < osize && j < count; i++) {
     for(size_t i = 0, j = 0; i < osize && j < count; i++) {
-        if(!oentries[i])
+        if(oentries[i] <= UA_NODESTORE_TOMBSTONE)
             continue;
             continue;
-        UA_NodeStoreEntry **e;
-        containsNodeId(ns, &oentries[i]->node.nodeId, &e);  /* We know this returns an empty entry here */
+        UA_NodeStoreEntry **e = findSlot(ns, &oentries[i]->node.nodeId);
         *e = oentries[i];
         *e = oentries[i];
         j++;
         j++;
     }
     }
@@ -160,43 +176,51 @@ static UA_StatusCode expand(UA_NodeStore *ns) {
 /* Exported functions */
 /* Exported functions */
 /**********************/
 /**********************/
 
 
-UA_NodeStore * UA_NodeStore_new(void) {
-    UA_NodeStore *ns;
-    if(!(ns = UA_malloc(sizeof(UA_NodeStore))))
+UA_NodeStore *
+UA_NodeStore_new(void) {
+    UA_NodeStore *ns = UA_malloc(sizeof(UA_NodeStore));
+    if(!ns)
         return NULL;
         return NULL;
     ns->sizePrimeIndex = higher_prime_index(UA_NODESTORE_MINSIZE);
     ns->sizePrimeIndex = higher_prime_index(UA_NODESTORE_MINSIZE);
     ns->size = primes[ns->sizePrimeIndex];
     ns->size = primes[ns->sizePrimeIndex];
     ns->count = 0;
     ns->count = 0;
-    if(!(ns->entries = UA_calloc(ns->size, sizeof(UA_NodeStoreEntry*)))) {
+    ns->entries = UA_calloc(ns->size, sizeof(UA_NodeStoreEntry*));
+    if(!ns->entries) {
         UA_free(ns);
         UA_free(ns);
         return NULL;
         return NULL;
     }
     }
     return ns;
     return ns;
 }
 }
 
 
-void UA_NodeStore_delete(UA_NodeStore *ns) {
+void
+UA_NodeStore_delete(UA_NodeStore *ns) {
     UA_UInt32 size = ns->size;
     UA_UInt32 size = ns->size;
     UA_NodeStoreEntry **entries = ns->entries;
     UA_NodeStoreEntry **entries = ns->entries;
     for(UA_UInt32 i = 0; i < size; i++) {
     for(UA_UInt32 i = 0; i < size; i++) {
-        if(entries[i])
+        if(entries[i] > UA_NODESTORE_TOMBSTONE)
             deleteEntry(entries[i]);
             deleteEntry(entries[i]);
     }
     }
     UA_free(ns->entries);
     UA_free(ns->entries);
     UA_free(ns);
     UA_free(ns);
 }
 }
 
 
-UA_Node * UA_NodeStore_newNode(UA_NodeClass class) {
+UA_Node *
+UA_NodeStore_newNode(UA_NodeClass class) {
     UA_NodeStoreEntry *entry = instantiateEntry(class);
     UA_NodeStoreEntry *entry = instantiateEntry(class);
     if(!entry)
     if(!entry)
         return NULL;
         return NULL;
-    return (UA_Node*)&entry->node;
+    return &entry->node;
 }
 }
 
 
-void UA_NodeStore_deleteNode(UA_Node *node) {
-    deleteEntry(container_of(node, UA_NodeStoreEntry, node));
+void
+UA_NodeStore_deleteNode(UA_Node *node) {
+    UA_NodeStoreEntry *entry = container_of(node, UA_NodeStoreEntry, node);
+    UA_assert(&entry->node == node);
+    deleteEntry(entry);
 }
 }
 
 
-UA_StatusCode UA_NodeStore_insert(UA_NodeStore *ns, UA_Node *node) {
+UA_StatusCode
+UA_NodeStore_insert(UA_NodeStore *ns, UA_Node *node) {
     if(ns->size * 3 <= ns->count * 4) {
     if(ns->size * 3 <= ns->count * 4) {
         if(expand(ns) != UA_STATUSCODE_GOOD)
         if(expand(ns) != UA_STATUSCODE_GOOD)
             return UA_STATUSCODE_BADINTERNALERROR;
             return UA_STATUSCODE_BADINTERNALERROR;
@@ -207,22 +231,24 @@ UA_StatusCode UA_NodeStore_insert(UA_NodeStore *ns, UA_Node *node) {
     tempNodeid.namespaceIndex = 0;
     tempNodeid.namespaceIndex = 0;
     UA_NodeStoreEntry **entry;
     UA_NodeStoreEntry **entry;
     if(UA_NodeId_isNull(&tempNodeid)) {
     if(UA_NodeId_isNull(&tempNodeid)) {
+        /* create a random nodeid */
         if(node->nodeId.namespaceIndex == 0)
         if(node->nodeId.namespaceIndex == 0)
             node->nodeId.namespaceIndex = 1;
             node->nodeId.namespaceIndex = 1;
-        /* find a free nodeid */
         UA_UInt32 identifier = ns->count+1; // start value
         UA_UInt32 identifier = ns->count+1; // start value
         UA_UInt32 size = ns->size;
         UA_UInt32 size = ns->size;
         hash_t increase = mod2(identifier, size);
         hash_t increase = mod2(identifier, size);
         while(true) {
         while(true) {
             node->nodeId.identifier.numeric = identifier;
             node->nodeId.identifier.numeric = identifier;
-            if(!containsNodeId(ns, &node->nodeId, &entry))
+            entry = findSlot(ns, &node->nodeId);
+            if(entry)
                 break;
                 break;
             identifier += increase;
             identifier += increase;
             if(identifier >= size)
             if(identifier >= size)
                 identifier -= size;
                 identifier -= size;
         }
         }
     } else {
     } else {
-        if(containsNodeId(ns, &node->nodeId, &entry)) {
+        entry = findSlot(ns, &node->nodeId);
+        if(!entry) {
             deleteEntry(container_of(node, UA_NodeStoreEntry, node));
             deleteEntry(container_of(node, UA_NodeStoreEntry, node));
             return UA_STATUSCODE_BADNODEIDEXISTS;
             return UA_STATUSCODE_BADNODEIDEXISTS;
         }
         }
@@ -235,29 +261,32 @@ UA_StatusCode UA_NodeStore_insert(UA_NodeStore *ns, UA_Node *node) {
 
 
 UA_StatusCode
 UA_StatusCode
 UA_NodeStore_replace(UA_NodeStore *ns, UA_Node *node) {
 UA_NodeStore_replace(UA_NodeStore *ns, UA_Node *node) {
-    UA_NodeStoreEntry **entry;
-    if(!containsNodeId(ns, &node->nodeId, &entry))
+    UA_NodeStoreEntry **entry = findNode(ns, &node->nodeId);
+    if(!entry)
         return UA_STATUSCODE_BADNODEIDUNKNOWN;
         return UA_STATUSCODE_BADNODEIDUNKNOWN;
     UA_NodeStoreEntry *newEntry = container_of(node, UA_NodeStoreEntry, node);
     UA_NodeStoreEntry *newEntry = container_of(node, UA_NodeStoreEntry, node);
     if(*entry != newEntry->orig) {
     if(*entry != newEntry->orig) {
+        // the node was replaced since the copy was made
         deleteEntry(newEntry);
         deleteEntry(newEntry);
-        return UA_STATUSCODE_BADINTERNALERROR; // the node was replaced since the copy was made
+        return UA_STATUSCODE_BADINTERNALERROR;
     }
     }
     deleteEntry(*entry);
     deleteEntry(*entry);
     *entry = newEntry;
     *entry = newEntry;
     return UA_STATUSCODE_GOOD;
     return UA_STATUSCODE_GOOD;
 }
 }
 
 
-const UA_Node * UA_NodeStore_get(UA_NodeStore *ns, const UA_NodeId *nodeid) {
-    UA_NodeStoreEntry **entry;
-    if(!containsNodeId(ns, nodeid, &entry))
+const UA_Node *
+UA_NodeStore_get(UA_NodeStore *ns, const UA_NodeId *nodeid) {
+    UA_NodeStoreEntry **entry = findNode(ns, nodeid);
+    if(!entry)
         return NULL;
         return NULL;
     return (const UA_Node*)&(*entry)->node;
     return (const UA_Node*)&(*entry)->node;
 }
 }
 
 
-UA_Node * UA_NodeStore_getCopy(UA_NodeStore *ns, const UA_NodeId *nodeid) {
-    UA_NodeStoreEntry **slot;
-    if(!containsNodeId(ns, nodeid, &slot))
+UA_Node *
+UA_NodeStore_getCopy(UA_NodeStore *ns, const UA_NodeId *nodeid) {
+    UA_NodeStoreEntry **slot = findNode(ns, nodeid);
+    if(!slot)
         return NULL;
         return NULL;
     UA_NodeStoreEntry *entry = *slot;
     UA_NodeStoreEntry *entry = *slot;
     UA_NodeStoreEntry *new = instantiateEntry(entry->node.nodeClass);
     UA_NodeStoreEntry *new = instantiateEntry(entry->node.nodeClass);
@@ -267,16 +296,17 @@ UA_Node * UA_NodeStore_getCopy(UA_NodeStore *ns, const UA_NodeId *nodeid) {
         deleteEntry(new);
         deleteEntry(new);
         return NULL;
         return NULL;
     }
     }
-    new->orig = entry;
+    new->orig = entry; // store the pointer to the original
     return &new->node;
     return &new->node;
 }
 }
 
 
-UA_StatusCode UA_NodeStore_remove(UA_NodeStore *ns, const UA_NodeId *nodeid) {
-    UA_NodeStoreEntry **slot;
-    if(!containsNodeId(ns, nodeid, &slot))
+UA_StatusCode
+UA_NodeStore_remove(UA_NodeStore *ns, const UA_NodeId *nodeid) {
+    UA_NodeStoreEntry **slot = findNode(ns, nodeid);
+    if(!slot)
         return UA_STATUSCODE_BADNODEIDUNKNOWN;
         return UA_STATUSCODE_BADNODEIDUNKNOWN;
     deleteEntry(*slot);
     deleteEntry(*slot);
-    *slot = NULL;
+    *slot = UA_NODESTORE_TOMBSTONE;
     ns->count--;
     ns->count--;
     /* Downsize the hashmap if it is very empty */
     /* Downsize the hashmap if it is very empty */
     if(ns->count * 8 < ns->size && ns->size > 32)
     if(ns->count * 8 < ns->size && ns->size > 32)
@@ -284,9 +314,10 @@ UA_StatusCode UA_NodeStore_remove(UA_NodeStore *ns, const UA_NodeId *nodeid) {
     return UA_STATUSCODE_GOOD;
     return UA_STATUSCODE_GOOD;
 }
 }
 
 
-void UA_NodeStore_iterate(UA_NodeStore *ns, UA_NodeStore_nodeVisitor visitor) {
+void
+UA_NodeStore_iterate(UA_NodeStore *ns, UA_NodeStore_nodeVisitor visitor) {
     for(UA_UInt32 i = 0; i < ns->size; i++) {
     for(UA_UInt32 i = 0; i < ns->size; i++) {
-        if(ns->entries[i])
+        if(ns->entries[i] > UA_NODESTORE_TOMBSTONE)
             visitor((UA_Node*)&ns->entries[i]->node);
             visitor((UA_Node*)&ns->entries[i]->node);
     }
     }
 }
 }

+ 5 - 5
src/server/ua_nodestore.h

@@ -10,7 +10,7 @@ extern "C" {
 
 
 /**
 /**
  * Nodestore
  * Nodestore
- * =========
+ * ---------
  * Stores nodes that can be indexed by their NodeId. Internally, it is based on
  * Stores nodes that can be indexed by their NodeId. Internally, it is based on
  * a hash-map implementation. */
  * a hash-map implementation. */
 struct UA_NodeStore;
 struct UA_NodeStore;
@@ -18,7 +18,7 @@ typedef struct UA_NodeStore UA_NodeStore;
 
 
 /**
 /**
  * Nodestore Lifecycle
  * Nodestore Lifecycle
- * ------------------- */
+ * ^^^^^^^^^^^^^^^^^^^ */
 /* Create a new nodestore */
 /* Create a new nodestore */
 UA_NodeStore * UA_NodeStore_new(void);
 UA_NodeStore * UA_NodeStore_new(void);
 
 
@@ -28,7 +28,7 @@ void UA_NodeStore_delete(UA_NodeStore *ns);
 
 
 /**
 /**
  * Node Lifecycle
  * Node Lifecycle
- * ---------------
+ * ^^^^^^^^^^^^^^
  *
  *
  * The following definitions are used to create empty nodes of the different
  * The following definitions are used to create empty nodes of the different
  * node types. The memory is managed by the nodestore. Therefore, the node has
  * node types. The memory is managed by the nodestore. Therefore, the node has
@@ -58,7 +58,7 @@ void UA_NodeStore_deleteNode(UA_Node *node);
 
 
 /**
 /**
  * Insert / Get / Replace / Remove
  * Insert / Get / Replace / Remove
- * ------------------------------- */
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */
 /* Inserts a new node into the nodestore. If the nodeid is zero, then a fresh
 /* Inserts a new node into the nodestore. If the nodeid is zero, then a fresh
  * numeric nodeid from namespace 1 is assigned. If insertion fails, the node is
  * numeric nodeid from namespace 1 is assigned. If insertion fails, the node is
  * deleted. */
  * deleted. */
@@ -83,7 +83,7 @@ UA_StatusCode UA_NodeStore_remove(UA_NodeStore *ns, const UA_NodeId *nodeid);
 
 
 /**
 /**
  * Iteration
  * Iteration
- * ---------
+ * ^^^^^^^^^
  * The following definitions are used to call a callback for every node in the
  * The following definitions are used to call a callback for every node in the
  * nodestore. */
  * nodestore. */
 typedef void (*UA_NodeStore_nodeVisitor)(const UA_Node *node);
 typedef void (*UA_NodeStore_nodeVisitor)(const UA_Node *node);

+ 1 - 0
src/server/ua_nodestore_concurrent.c

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

Diferenças do arquivo suprimidas por serem muito extensas
+ 347 - 312
src/server/ua_server.c


+ 7 - 6
src/server/ua_server_binary.c

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

+ 112 - 7
src/server/ua_server_internal.h

@@ -92,6 +92,13 @@ struct UA_Server {
     UA_ServerConfig config;
     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*);
 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
 /* Calls callback on the node. In the multithreaded case, the node is copied before and replaced in
@@ -105,17 +112,115 @@ UA_StatusCode UA_Server_delayedCallback(UA_Server *server, UA_ServerCallback cal
 UA_StatusCode UA_Server_delayedFree(UA_Server *server, void *data);
 UA_StatusCode UA_Server_delayedFree(UA_Server *server, void *data);
 void UA_Server_deleteAllRepeatedJobs(UA_Server *server);
 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
 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
 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);
+
+
 
 
 /* Periodic task to clean up the discovery registry */
 /* Periodic task to clean up the discovery registry */
 void UA_Discovery_cleanupTimedOut(UA_Server *server, UA_DateTime now);
 void UA_Discovery_cleanupTimedOut(UA_Server *server, UA_DateTime now);

+ 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
  * 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
 /* 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
  * Discovery Service Set
@@ -55,7 +68,7 @@ void Service_OpenSecureChannel(UA_Server *server, UA_Connection *connection,
                                const UA_OpenSecureChannelRequest *request,
                                const UA_OpenSecureChannelRequest *request,
                                UA_OpenSecureChannelResponse *response);
                                UA_OpenSecureChannelResponse *response);
 
 
-/** Used to terminate a SecureChannel. */
+/* Used to terminate a SecureChannel. */
 void Service_CloseSecureChannel(UA_Server *server, UA_SecureChannel *channel);
 void Service_CloseSecureChannel(UA_Server *server, UA_SecureChannel *channel);
 
 
 /**
 /**
@@ -103,43 +116,24 @@ void Service_AddNodes(UA_Server *server, UA_Session *session,
                       const UA_AddNodesRequest *request,
                       const UA_AddNodesRequest *request,
                       UA_AddNodesResponse *response);
                       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. */
 /* Used to add one or more References to one or more Nodes. */
 void Service_AddReferences(UA_Server *server, UA_Session *session,
 void Service_AddReferences(UA_Server *server, UA_Session *session,
                            const UA_AddReferencesRequest *request,
                            const UA_AddReferencesRequest *request,
                            UA_AddReferencesResponse *response);
                            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. */
 /* Used to delete one or more Nodes from the AddressSpace. */
 void Service_DeleteNodes(UA_Server *server, UA_Session *session,
 void Service_DeleteNodes(UA_Server *server, UA_Session *session,
                          const UA_DeleteNodesRequest *request,
                          const UA_DeleteNodesRequest *request,
                          UA_DeleteNodesResponse *response);
                          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. */
 /* Used to delete one or more References of a Node. */
 void Service_DeleteReferences(UA_Server *server, UA_Session *session,
 void Service_DeleteReferences(UA_Server *server, UA_Session *session,
                               const UA_DeleteReferencesRequest *request,
                               const UA_DeleteReferencesRequest *request,
                               UA_DeleteReferencesResponse *response);
                               UA_DeleteReferencesResponse *response);
 
 
-UA_StatusCode Service_DeleteReferences_single(UA_Server *server,
-                      UA_Session *session, const UA_DeleteReferencesItem *item);
-
 /**
 /**
+ * .. _view-services:
+ *
  * View Service Set
  * View Service Set
  * ----------------
  * ----------------
  * Clients use the browse Services of the View Service Set to navigate through
  * Clients use the browse Services of the View Service Set to navigate through
@@ -152,11 +146,6 @@ void Service_Browse(UA_Server *server, UA_Session *session,
                     const UA_BrowseRequest *request,
                     const UA_BrowseRequest *request,
                     UA_BrowseResponse *response);
                     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
 /* 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
  * 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
  * context means that the Server is not able to return a larger response or that
@@ -171,11 +160,6 @@ void Service_TranslateBrowsePathsToNodeIds(UA_Server *server, UA_Session *sessio
              const UA_TranslateBrowsePathsToNodeIdsRequest *request,
              const UA_TranslateBrowsePathsToNodeIdsRequest *request,
              UA_TranslateBrowsePathsToNodeIdsResponse *response);
              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
 /* 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
  * repeatedly (e.g. Write, Call). It allows Servers to set up anything needed so
  * that the access operations will be more efficient. */
  * that the access operations will be more efficient. */
@@ -216,10 +200,6 @@ void Service_Read(UA_Server *server, UA_Session *session,
                   const UA_ReadRequest *request,
                   const UA_ReadRequest *request,
                   UA_ReadResponse *response);
                   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
 /* 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
  * 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
  * allows Clients to write the entire set of indexed values as a composite, to
@@ -228,18 +208,17 @@ void Service_Write(UA_Server *server, UA_Session *session,
                    const UA_WriteRequest *request,
                    const UA_WriteRequest *request,
                    UA_WriteResponse *response);
                    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_HistoryRead */
 /* Not Implemented: Service_HistoryUpdate */
 /* Not Implemented: Service_HistoryUpdate */
 
 
 /**
 /**
+ * .. _method-services:
+ *
  * Method Service Set
  * Method Service Set
  * ------------------
  * ------------------
  * The Method Service Set defines the means to invoke methods. A method shall be
  * 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
 /* 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
  * 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
  * of the method's execution cannot be returned to the Client and are
@@ -248,19 +227,12 @@ void Service_Call(UA_Server *server, UA_Session *session,
                   const UA_CallRequest *request,
                   const UA_CallRequest *request,
                   UA_CallResponse *response);
                   UA_CallResponse *response);
 
 
-void Service_Call_single(UA_Server *server, UA_Session *session,
-                         const UA_CallMethodRequest *request,
-                         UA_CallMethodResult *result);
-#endif
-
 /**
 /**
  * MonitoredItem Service Set
  * MonitoredItem Service Set
  * -------------------------
  * -------------------------
  * Clients define MonitoredItems to subscribe to data and Events. Each
  * Clients define MonitoredItems to subscribe to data and Events. Each
  * MonitoredItem identifies the item to be monitored and the Subscription to use
  * 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. */
  * 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
 /* Used to create and add one or more MonitoredItems to a Subscription. A
  * MonitoredItem is deleted automatically by the Server when the Subscription is
  * MonitoredItem is deleted automatically by the Server when the Subscription is
  * deleted. Deleting a MonitoredItem causes its entire set of triggered item
  * deleted. Deleting a MonitoredItem causes its entire set of triggered item
@@ -288,14 +260,10 @@ void Service_SetMonitoringMode(UA_Server *server, UA_Session *session,
 
 
 /* Not Implemented: Service_SetTriggering */
 /* Not Implemented: Service_SetTriggering */
 
 
-#endif
-
 /**
 /**
  * Subscription Service Set
  * Subscription Service Set
  * ------------------------
  * ------------------------
  * Subscriptions are used to report Notifications to the Client. */
  * Subscriptions are used to report Notifications to the Client. */
-#ifdef UA_ENABLE_SUBSCRIPTIONS
-
 /* Used to create a Subscription. Subscriptions monitor a set of MonitoredItems
 /* Used to create a Subscription. Subscriptions monitor a set of MonitoredItems
  * for Notifications and return them to the Client in response to Publish
  * for Notifications and return them to the Client in response to Publish
  * requests. */
  * requests. */
@@ -338,8 +306,6 @@ void Service_DeleteSubscriptions(UA_Server *server, UA_Session *session,
 
 
 /* Not Implemented: Service_TransferSubscription */
 /* Not Implemented: Service_TransferSubscription */
 
 
-#endif
-
 #ifdef __cplusplus
 #ifdef __cplusplus
 } // extern "C"
 } // extern "C"
 #endif
 #endif

Diferenças do arquivo suprimidas por serem muito extensas
+ 589 - 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
 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;
         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;
         return UA_STATUSCODE_BADINTERNALERROR;
-    if(UA_Variant_isScalar(&argRequirements->value.variant.value))
+    if(UA_Variant_isScalar(&argRequirements->value.data.value.value))
         argReqsSize = 1;
         argReqsSize = 1;
     if(argReqsSize > argsSize)
     if(argReqsSize > argsSize)
         return UA_STATUSCODE_BADARGUMENTSMISSING;
         return UA_STATUSCODE_BADARGUMENTSMISSING;
@@ -117,14 +42,18 @@ argConformsToDefinition(UA_Server *server, const UA_VariableNode *argRequirement
 
 
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     for(size_t i = 0; i < argReqsSize; i++)
     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;
     return retval;
 }
 }
 
 
 void
 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) {
                     UA_CallMethodResult *result) {
-
     /* Get/verify the method node */
     /* Get/verify the method node */
     const UA_MethodNode *methodCalled =
     const UA_MethodNode *methodCalled =
         (const UA_MethodNode*)UA_NodeStore_get(server->nodestore, &request->methodId);
         (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;
         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_Boolean found = false;
     UA_NodeId hasComponentNodeId = UA_NODEID_NUMERIC(0,UA_NS0ID_HASCOMPONENT);
     UA_NodeId hasComponentNodeId = UA_NODEID_NUMERIC(0,UA_NS0ID_HASCOMPONENT);
     UA_NodeId hasSubTypeNodeId = UA_NODEID_NUMERIC(0,UA_NS0ID_HASSUBTYPE);
     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?)
             //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;
                 break;
-            }
         }
         }
     }
     }
-    if(!found)
+    if(!found) {
         result->statusCode = UA_STATUSCODE_BADMETHODINVALID;
         result->statusCode = UA_STATUSCODE_BADMETHODINVALID;
-    if(result->statusCode != UA_STATUSCODE_GOOD)
         return;
         return;
+    }
 
 
     /* Verify Input Argument count, types and sizes */
     /* 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;
             result->statusCode = UA_STATUSCODE_BADINVALIDARGUMENT;
             return;
             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)
         if(result->statusCode != UA_STATUSCODE_GOOD)
             return;
             return;
     }
     }
 
 
     /* Allocate the output arguments */
     /* 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) {
         if(!result->outputArguments) {
             result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
             result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
             return;
             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)
 #if defined(UA_ENABLE_METHODCALLS) && defined(UA_ENABLE_SUBSCRIPTIONS)
     methodCallSession = session;
     methodCallSession = session;
 #endif
 #endif
-
-    /* Call the method */
     result->statusCode = methodCalled->attachedMethod(methodCalled->methodHandle, withObject->nodeId,
     result->statusCode = methodCalled->attachedMethod(methodCalled->methodHandle, withObject->nodeId,
                                                       request->inputArgumentsSize, request->inputArguments,
                                                       request->inputArgumentsSize, request->inputArguments,
                                                       result->outputArgumentsSize, result->outputArguments);
                                                       result->outputArgumentsSize, result->outputArguments);
-
 #if defined(UA_ENABLE_METHODCALLS) && defined(UA_ENABLE_SUBSCRIPTIONS)
 #if defined(UA_ENABLE_METHODCALLS) && defined(UA_ENABLE_SUBSCRIPTIONS)
     methodCallSession = NULL;
     methodCallSession = NULL;
 #endif
 #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");
     UA_LOG_DEBUG_SESSION(server->config.logger, session, "Processing CallRequest");
     if(request->methodsToCallSize <= 0) {
     if(request->methodsToCallSize <= 0) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
         response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;

Diferenças do arquivo suprimidas por serem muito extensas
+ 687 - 677
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_Double requestedPublishingInterval,
                         UA_UInt32 requestedLifetimeCount,
                         UA_UInt32 requestedLifetimeCount,
                         UA_UInt32 requestedMaxKeepAliveCount,
                         UA_UInt32 requestedMaxKeepAliveCount,
-                        UA_UInt32 maxNotificationsPerPublish, UA_Byte priority)
-{
+                        UA_UInt32 maxNotificationsPerPublish, UA_Byte priority) {
     /* deregister the job if required */
     /* deregister the job if required */
     UA_StatusCode retval = Subscription_unregisterPublishJob(server, subscription);
     UA_StatusCode retval = Subscription_unregisterPublishJob(server, subscription);
     if(retval != UA_STATUSCODE_GOOD)
     if(retval != UA_STATUSCODE_GOOD)
@@ -53,12 +52,12 @@ setSubscriptionSettings(UA_Server *server, UA_Subscription *subscription,
 void
 void
 Service_CreateSubscription(UA_Server *server, UA_Session *session,
 Service_CreateSubscription(UA_Server *server, UA_Session *session,
                            const UA_CreateSubscriptionRequest *request,
                            const UA_CreateSubscriptionRequest *request,
-                           UA_CreateSubscriptionResponse *response)
-{
+                           UA_CreateSubscriptionResponse *response) {
     /* Create the subscription */
     /* Create the subscription */
     UA_Subscription *newSubscription = UA_Subscription_new(session, response->subscriptionId);
     UA_Subscription *newSubscription = UA_Subscription_new(session, response->subscriptionId);
     if(!newSubscription) {
     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;
         response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
         return;
         return;
     }
     }
@@ -78,15 +77,18 @@ Service_CreateSubscription(UA_Server *server, UA_Session *session,
     response->revisedLifetimeCount = newSubscription->lifeTimeCount;
     response->revisedLifetimeCount = newSubscription->lifeTimeCount;
     response->revisedMaxKeepAliveCount = newSubscription->maxKeepAliveCount;
     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,
                          "with a publishing interval of %f ms", response->subscriptionId,
                          newSubscription->publishingInterval);
                          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);
     UA_Subscription *sub = UA_Session_getSubscriptionByID(session, request->subscriptionId);
     if(!sub) {
     if(!sub) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
         response->responseHeader.serviceResult = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
@@ -103,10 +105,12 @@ void Service_ModifySubscription(UA_Server *server, UA_Session *session,
     return;
     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) {
     if(request->subscriptionIdsSize <= 0) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
         response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
         return;
         return;
@@ -121,7 +125,8 @@ void Service_SetPublishingMode(UA_Server *server, UA_Session *session,
 
 
     response->resultsSize = size;
     response->resultsSize = size;
     for(size_t i = 0; i < size; i++) {
     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) {
         if(!sub) {
             response->results[i] = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
             response->results[i] = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
             continue;
             continue;
@@ -139,38 +144,68 @@ void Service_SetPublishingMode(UA_Server *server, UA_Session *session,
 
 
 static void
 static void
 setMonitoredItemSettings(UA_Server *server, UA_MonitoredItem *mon,
 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);
     MonitoredItem_unregisterSampleJob(server, mon);
     mon->monitoringMode = monitoringMode;
     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;
     mon->samplingInterval = samplingInterval;
     UA_BOUNDEDVALUE_SETWBOUNDS(server->config.samplingIntervalLimits,
     UA_BOUNDEDVALUE_SETWBOUNDS(server->config.samplingIntervalLimits,
         samplingInterval, mon->samplingInterval);
         samplingInterval, mon->samplingInterval);
-    /* Check for nan */
-    if(samplingInterval != samplingInterval)
+    if(samplingInterval != samplingInterval) /* Check for nan */
         mon->samplingInterval = server->config.samplingIntervalLimits.min;
         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,
     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)
     if(monitoringMode == UA_MONITORINGMODE_REPORTING)
         MonitoredItem_registerSampleJob(server, mon);
         MonitoredItem_registerSampleJob(server, mon);
 }
 }
 
 
 static const UA_String binaryEncoding = {sizeof("Default Binary")-1, (UA_Byte*)"Default Binary"};
 static const UA_String binaryEncoding = {sizeof("Default Binary")-1, (UA_Byte*)"Default Binary"};
+
 static void
 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_TimestampsToReturn timestampsToReturn,
                                     const UA_MonitoredItemCreateRequest *request,
                                     const UA_MonitoredItemCreateRequest *request,
                                     UA_MonitoredItemCreateResult *result) {
                                     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 v;
     UA_DataValue_init(&v);
     UA_DataValue_init(&v);
     Service_Read_single(server, session, timestampsToReturn, &request->itemToMonitor, &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 &&
     if(v.hasStatus && (v.status >> 30) > 1 &&
        v.status != UA_STATUSCODE_BADRESOURCEUNAVAILABLE &&
        v.status != UA_STATUSCODE_BADRESOURCEUNAVAILABLE &&
        v.status != UA_STATUSCODE_BADCOMMUNICATIONERROR &&
        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 */
     /* 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;
         result->statusCode = UA_STATUSCODE_BADDATAENCODINGINVALID;
         return;
         return;
     }
     }
@@ -201,7 +237,8 @@ Service_CreateMonitoredItems_single(UA_Server *server, UA_Session *session, UA_S
         result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
         result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
         return;
         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) {
     if(retval != UA_STATUSCODE_GOOD) {
         result->statusCode = retval;
         result->statusCode = retval;
         MonitoredItem_delete(server, newMon);
         MonitoredItem_delete(server, newMon);
@@ -212,10 +249,7 @@ Service_CreateMonitoredItems_single(UA_Server *server, UA_Session *session, UA_S
     newMon->itemId = ++(sub->lastMonitoredItemId);
     newMon->itemId = ++(sub->lastMonitoredItemId);
     newMon->timestampsToReturn = timestampsToReturn;
     newMon->timestampsToReturn = timestampsToReturn;
     setMonitoredItemSettings(server, newMon, request->monitoringMode,
     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);
     LIST_INSERT_HEAD(&sub->MonitoredItems, newMon, listEntry);
 
 
     /* Create the first sample */
     /* Create the first sample */
@@ -233,9 +267,10 @@ void
 Service_CreateMonitoredItems(UA_Server *server, UA_Session *session,
 Service_CreateMonitoredItems(UA_Server *server, UA_Session *session,
                              const UA_CreateMonitoredItemsRequest *request,
                              const UA_CreateMonitoredItemsRequest *request,
                              UA_CreateMonitoredItemsResponse *response) {
                              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) {
     if(request->timestampsToReturn > UA_TIMESTAMPSTORETURN_NEITHER) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADTIMESTAMPSTORETURNINVALID;
         response->responseHeader.serviceResult = UA_STATUSCODE_BADTIMESTAMPSTORETURNINVALID;
         return;
         return;
@@ -278,10 +313,7 @@ Service_ModifyMonitoredItems_single(UA_Server *server, UA_Session *session, UA_S
     }
     }
 
 
     setMonitoredItemSettings(server, mon, mon->monitoringMode,
     setMonitoredItemSettings(server, mon, mon->monitoringMode,
-                             request->requestedParameters.clientHandle,
-                             request->requestedParameters.samplingInterval,
-                             request->requestedParameters.queueSize,
-                             request->requestedParameters.discardOldest);
+                             &request->requestedParameters);
     result->revisedSamplingInterval = mon->samplingInterval;
     result->revisedSamplingInterval = mon->samplingInterval;
     result->revisedQueueSize = mon->maxQueueSize;
     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,
 void Service_ModifyMonitoredItems(UA_Server *server, UA_Session *session,
                                   const UA_ModifyMonitoredItemsRequest *request,
                                   const UA_ModifyMonitoredItemsRequest *request,
                                   UA_ModifyMonitoredItemsResponse *response) {
                                   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 */
     /* check if the timestampstoreturn is valid */
     if(request->timestampsToReturn > UA_TIMESTAMPSTORETURN_NEITHER) {
     if(request->timestampsToReturn > UA_TIMESTAMPSTORETURN_NEITHER) {
@@ -297,6 +330,7 @@ void Service_ModifyMonitoredItems(UA_Server *server, UA_Session *session,
         return;
         return;
     }
     }
 
 
+    /* Get the subscription */
     UA_Subscription *sub = UA_Session_getSubscriptionByID(session, request->subscriptionId);
     UA_Subscription *sub = UA_Session_getSubscriptionByID(session, request->subscriptionId);
     if(!sub) {
     if(!sub) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
         response->responseHeader.serviceResult = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
@@ -347,12 +381,19 @@ void Service_SetMonitoringMode(UA_Server *server, UA_Session *session,
     response->resultsSize = request->monitoredItemIdsSize;
     response->resultsSize = request->monitoredItemIdsSize;
 
 
     for(size_t i = 0; i < response->resultsSize; i++) {
     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;
             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 *response = &entry->response;
     UA_PublishResponse_init(response);
     UA_PublishResponse_init(response);
     response->responseHeader.requestHandle = request->requestHeader.requestHandle;
     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 */
     /* Delete Acknowledged Subscription Messages */
     for(size_t i = 0; i < request->subscriptionAcknowledgementsSize; i++) {
     for(size_t i = 0; i < request->subscriptionAcknowledgementsSize; i++) {
@@ -450,9 +494,10 @@ void
 Service_DeleteSubscriptions(UA_Server *server, UA_Session *session,
 Service_DeleteSubscriptions(UA_Server *server, UA_Session *session,
                             const UA_DeleteSubscriptionsRequest *request,
                             const UA_DeleteSubscriptionsRequest *request,
                             UA_DeleteSubscriptionsResponse *response) {
                             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;
         response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
         return;
         return;
     }
     }

+ 101 - 46
src/server/ua_subscription.c

@@ -1,5 +1,6 @@
 #include "ua_subscription.h"
 #include "ua_subscription.h"
 #include "ua_server_internal.h"
 #include "ua_server_internal.h"
+#include "ua_types_encoding_binary.h"
 #include "ua_services.h"
 #include "ua_services.h"
 #include "ua_nodestore.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) {
 void UA_MoniteredItem_SampleCallback(UA_Server *server, UA_MonitoredItem *monitoredItem) {
     UA_Subscription *sub = monitoredItem->subscription;
     UA_Subscription *sub = monitoredItem->subscription;
     if(monitoredItem->monitoredItemType != UA_MONITOREDITEMTYPE_CHANGENOTIFY) {
     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);
                              sub->subscriptionID, monitoredItem->itemId);
         return;
         return;
     }
     }
 
 
     MonitoredItem_queuedValue *newvalue = UA_malloc(sizeof(MonitoredItem_queuedValue));
     MonitoredItem_queuedValue *newvalue = UA_malloc(sizeof(MonitoredItem_queuedValue));
     if(!newvalue) {
     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;
         return;
     }
     }
     UA_DataValue_init(&newvalue->value);
     UA_DataValue_init(&newvalue->value);
     newvalue->clientHandle = monitoredItem->clientHandle;
     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 */
     /* Read the value */
     UA_ReadValueId rvid;
     UA_ReadValueId rvid;
     UA_ReadValueId_init(&rvid);
     UA_ReadValueId_init(&rvid);
     rvid.nodeId = monitoredItem->monitoredNodeId;
     rvid.nodeId = monitoredItem->monitoredNodeId;
     rvid.attributeId = monitoredItem->attributeID;
     rvid.attributeId = monitoredItem->attributeID;
     rvid.indexRange = monitoredItem->indexRange;
     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_ByteString newValueAsByteString;
     UA_StatusCode retval = UA_ByteString_allocBuffer(&newValueAsByteString, binsize);
     UA_StatusCode retval = UA_ByteString_allocBuffer(&newValueAsByteString, binsize);
     if(retval != UA_STATUSCODE_GOOD) {
     if(retval != UA_STATUSCODE_GOOD) {
@@ -81,26 +106,37 @@ void UA_MoniteredItem_SampleCallback(UA_Server *server, UA_MonitoredItem *monito
         return;
         return;
     }
     }
     size_t encodingOffset = 0;
     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);
                              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 ||
     if(retval != UA_STATUSCODE_GOOD ||
        (monitoredItem->lastSampledValue.data &&
        (monitoredItem->lastSampledValue.data &&
         UA_String_equal(&newValueAsByteString, &monitoredItem->lastSampledValue))) {
         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",
                              "MonitoredItem %u | Do not sample an unchanged value",
                              sub->subscriptionID, monitoredItem->itemId);
                              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);
                          "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) {
     if(monitoredItem->currentQueueSize >= monitoredItem->maxQueueSize) {
         MonitoredItem_queuedValue *queueItem;
         MonitoredItem_queuedValue *queueItem;
         if(monitoredItem->discardOldest)
         if(monitoredItem->discardOldest)
@@ -109,13 +145,11 @@ void UA_MoniteredItem_SampleCallback(UA_Server *server, UA_MonitoredItem *monito
             queueItem = TAILQ_LAST(&monitoredItem->queue, QueueOfQueueDataValues);
             queueItem = TAILQ_LAST(&monitoredItem->queue, QueueOfQueueDataValues);
 
 
         if(!queueItem) {
         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);
         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
     /* 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 tempv = newvalue->value.value;
         UA_Variant_copy(&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);
     UA_ByteString_deleteMembers(&monitoredItem->lastSampledValue);
     monitoredItem->lastSampledValue = newValueAsByteString;
     monitoredItem->lastSampledValue = newValueAsByteString;
+
+    /* Add the sample to the queue for publication */
     TAILQ_INSERT_TAIL(&monitoredItem->queue, newvalue, listEntry);
     TAILQ_INSERT_TAIL(&monitoredItem->queue, newvalue, listEntry);
     monitoredItem->currentQueueSize++;
     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);
                                                     &mon->sampleJobGuid);
     if(retval == UA_STATUSCODE_GOOD)
     if(retval == UA_STATUSCODE_GOOD)
         mon->sampleJobIsRegistered = true;
         mon->sampleJobIsRegistered = true;
@@ -301,7 +348,8 @@ void UA_Subscription_publishCallback(UA_Server *server, UA_Subscription *sub) {
         message->notificationDataSize = 1;
         message->notificationDataSize = 1;
         UA_ExtensionObject *data = message->notificationData;
         UA_ExtensionObject *data = message->notificationData;
         UA_DataChangeNotification *dcn = UA_DataChangeNotification_new();
         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;
         dcn->monitoredItemsSize = notifications;
         size_t l = 0;
         size_t l = 0;
         UA_MonitoredItem *mon;
         UA_MonitoredItem *mon;
@@ -319,10 +367,12 @@ void UA_Subscription_publishCallback(UA_Server *server, UA_Subscription *sub) {
                 mon->currentQueueSize--;
                 mon->currentQueueSize--;
                 mon_l++;
                 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",
                                  "%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;
             l += mon_l;
         }
         }
         data->encoding = UA_EXTENSIONOBJECT_DECODED;
         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];
         data->content.decoded.type = &UA_TYPES[UA_TYPES_DATACHANGENOTIFICATION];
 
 
         /* Put the notification message into the retransmission queue */
         /* Put the notification message into the retransmission queue */
-        UA_NotificationMessageEntry *retransmission = malloc(sizeof(UA_NotificationMessageEntry));
+        UA_NotificationMessageEntry *retransmission =
+            malloc(sizeof(UA_NotificationMessageEntry));
         if(retransmission) {
         if(retransmission) {
-            UA_NotificationMessage_copy(&response->notificationMessage, &retransmission->message);
+            UA_NotificationMessage_copy(&response->notificationMessage,
+                                        &retransmission->message);
             LIST_INSERT_HEAD(&sub->retransmissionQueue, retransmission, listEntry);
             LIST_INSERT_HEAD(&sub->retransmissionQueue, retransmission, listEntry);
         } else {
         } 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 */
     /* Send the response */
     UA_LOG_DEBUG_SESSION(server->config.logger, sub->session,
     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_SecureChannel_sendBinaryMessage(sub->session->channel, requestId, response,
                                        &UA_TYPES[UA_TYPES_PUBLISHRESPONSE]);
                                        &UA_TYPES[UA_TYPES_PUBLISHRESPONSE]);
 
 
@@ -379,10 +432,12 @@ UA_StatusCode Subscription_registerPublishJob(UA_Server *server, UA_Subscription
     if(!sub->publishingEnabled)
     if(!sub->publishingEnabled)
         return UA_STATUSCODE_GOOD;
         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);
                                                     &sub->publishJobGuid);
     if(retval == UA_STATUSCODE_GOOD)
     if(retval == UA_STATUSCODE_GOOD)
         sub->publishJobIsRegistered = true;
         sub->publishJobIsRegistered = true;

+ 1 - 0
src/server/ua_subscription.h

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

+ 1 - 1
src/ua_connection.c

@@ -113,7 +113,7 @@ UA_Connection_completeMessages(UA_Connection *connection, UA_ByteString * UA_RES
         retval = UA_ByteString_allocBuffer(&connection->incompleteMessage, incomplete_length);
         retval = UA_ByteString_allocBuffer(&connection->incompleteMessage, incomplete_length);
         if(retval != UA_STATUSCODE_GOOD)
         if(retval != UA_STATUSCODE_GOOD)
             goto cleanup;
             goto cleanup;
-        memcpy(&connection->incompleteMessage.data, &message->data[complete_until], incomplete_length);
+        memcpy(connection->incompleteMessage.data, &message->data[complete_until], incomplete_length);
         message->length = complete_until;
         message->length = complete_until;
     }
     }
 
 

+ 3 - 2
src/ua_session.c

@@ -30,7 +30,7 @@ void UA_Session_init(UA_Session *session) {
     session->timeout = 0;
     session->timeout = 0;
     UA_DateTime_init(&session->validTill);
     UA_DateTime_init(&session->validTill);
     session->channel = NULL;
     session->channel = NULL;
-    session->availableContinuationPoints = MAXCONTINUATIONPOINTS;
+    session->availableContinuationPoints = UA_MAXCONTINUATIONPOINTS;
     LIST_INIT(&session->continuationPoints);
     LIST_INIT(&session->continuationPoints);
 #ifdef UA_ENABLE_SUBSCRIPTIONS
 #ifdef UA_ENABLE_SUBSCRIPTIONS
     LIST_INIT(&session->serverSubscriptions);
     LIST_INIT(&session->serverSubscriptions);
@@ -107,7 +107,7 @@ UA_UInt32 UA_Session_getUniqueSubscriptionID(UA_Session *session) {
 }
 }
 
 
 void UA_Session_answerPublishRequestsWithoutSubscription(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))
     if(LIST_FIRST(&session->serverSubscriptions))
         return;
         return;
 
 
@@ -121,6 +121,7 @@ void UA_Session_answerPublishRequestsWithoutSubscription(UA_Session *session) {
         response->responseHeader.timestamp = UA_DateTime_now();
         response->responseHeader.timestamp = UA_DateTime_now();
         UA_SecureChannel_sendBinaryMessage(session->channel, requestId, response,
         UA_SecureChannel_sendBinaryMessage(session->channel, requestId, response,
                                            &UA_TYPES[UA_TYPES_PUBLISHRESPONSE]);
                                            &UA_TYPES[UA_TYPES_PUBLISHRESPONSE]);
+        UA_PublishResponse_deleteMembers(response);
         UA_free(pre);
         UA_free(pre);
     }
     }
 }
 }

+ 3 - 1
src/ua_session.h

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

+ 6 - 8
src/ua_types.c

@@ -16,10 +16,12 @@
 /* Static definition of NULL type instances */
 /* Static definition of NULL type instances */
 UA_EXPORT const UA_String UA_STRING_NULL = {.length = 0, .data = NULL };
 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_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_NodeId UA_NODEID_NULL = {0, UA_NODEIDTYPE_NUMERIC, {0}};
 UA_EXPORT const UA_ExpandedNodeId UA_EXPANDEDNODEID_NULL = {
 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 };
     .namespaceUri = {.length = 0, .data = NULL}, .serverIndex = 0 };
 
 
 static void UA_deleteMembers_noInit(void *p, const UA_DataType *type);
 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) {
     if(v->arrayDimensionsSize > 0) {
         dims_count = v->arrayDimensionsSize;
         dims_count = v->arrayDimensionsSize;
         dims = (UA_UInt32*)v->arrayDimensions;
         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];
             elements *= dims[i];
-        }
         if(elements != v->arrayLength)
         if(elements != v->arrayLength)
             return UA_STATUSCODE_BADINTERNALERROR;
             return UA_STATUSCODE_BADINTERNALERROR;
     }
     }
@@ -611,7 +609,7 @@ UA_Variant_copyRange(const UA_Variant *orig_src, UA_Variant *dst,
         dst->arrayDimensionsSize = thisrange.dimensionsSize;
         dst->arrayDimensionsSize = thisrange.dimensionsSize;
         for(size_t k = 0; k < thisrange.dimensionsSize; k++)
         for(size_t k = 0; k < thisrange.dimensionsSize; k++)
             dst->arrayDimensions[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;
     return UA_STATUSCODE_GOOD;
 }
 }

+ 2 - 2
src/ua_types_encoding_binary.h

@@ -5,12 +5,12 @@
 
 
 typedef UA_StatusCode (*UA_exchangeEncodeBuffer)(void *handle, UA_ByteString *buf, size_t offset);
 typedef UA_StatusCode (*UA_exchangeEncodeBuffer)(void *handle, UA_ByteString *buf, size_t offset);
 
 
-UA_StatusCode
+UA_StatusCode UA_EXPORT
 UA_encodeBinary(const void *src, const UA_DataType *type,
 UA_encodeBinary(const void *src, const UA_DataType *type,
                 UA_exchangeEncodeBuffer exchangeBufferCallback, void *exchangeBufferCallbackHandle,
                 UA_exchangeEncodeBuffer exchangeBufferCallback, void *exchangeBufferCallbackHandle,
                 UA_ByteString *dst, size_t *offset) UA_FUNC_ATTR_WARN_UNUSED_RESULT;
                 UA_ByteString *dst, size_t *offset) UA_FUNC_ATTR_WARN_UNUSED_RESULT;
 
 
-UA_StatusCode
+UA_StatusCode UA_EXPORT
 UA_decodeBinary(const UA_ByteString *src, size_t *offset, void *dst,
 UA_decodeBinary(const UA_ByteString *src, size_t *offset, void *dst,
                 const UA_DataType *type) UA_FUNC_ATTR_WARN_UNUSED_RESULT;
                 const UA_DataType *type) UA_FUNC_ATTR_WARN_UNUSED_RESULT;
 
 

+ 13 - 8
src/ua_util.h

@@ -47,15 +47,20 @@
 /* Thread Local Storage */
 /* Thread Local Storage */
 /************************/
 /************************/
 
 
-#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
-# define UA_THREAD_LOCAL _Thread_local /* C11 */
-#elif defined(__GNUC__)
-# define UA_THREAD_LOCAL __thread /* GNU extension */
-#elif defined(_MSC_VER)
-# define UA_THREAD_LOCAL __declspec(thread) /* MSVC extension */
-#else
+#ifdef UA_ENABLE_MULTITHREADING
+# if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
+#  define UA_THREAD_LOCAL _Thread_local /* C11 */
+# elif defined(__GNUC__)
+#  define UA_THREAD_LOCAL __thread /* GNU extension */
+# elif defined(_MSC_VER)
+#  define UA_THREAD_LOCAL __declspec(thread) /* MSVC extension */
+# else
+#  warning The compiler does not allow thread-local variables. The library can be built, but will not be thread safe.
+# endif
+#endif
+
+#ifndef UA_THREAD_LOCAL
 # define UA_THREAD_LOCAL
 # define UA_THREAD_LOCAL
-# warning The compiler does not allow thread-local variables. The library can be built, but will not be thread safe.
 #endif
 #endif
 
 
 /*************************/
 /*************************/

+ 96 - 78
tests/check_services_attributes.c

@@ -17,14 +17,18 @@
 #endif
 #endif
 
 
 static UA_StatusCode
 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 */
     /* VariableNode */
     UA_VariableAttributes vattr;
     UA_VariableAttributes vattr;
@@ -38,37 +42,30 @@ static UA_Server* makeTestSequence(void) {
     UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
     UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
     UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
     UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
     UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
     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 */
     /* DataSource VariableNode */
     UA_VariableAttributes_init(&vattr);
     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.description = UA_LOCALIZEDTEXT("en_US","temperature");
     vattr.displayName = 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 */
     /* VariableNode with array */
     UA_VariableAttributes_init(&vattr);
     UA_VariableAttributes_init(&vattr);
     UA_Int32 myIntegerArray[9] = {1,2,3,4,5,6,7,8,9};
     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_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.arrayDimensions = myIntegerDimensions;
     vattr.value.arrayDimensionsSize = 2;
     vattr.value.arrayDimensionsSize = 2;
     vattr.displayName = UA_LOCALIZEDTEXT("locale","myarray");
     vattr.displayName = UA_LOCALIZEDTEXT("locale","myarray");
@@ -76,31 +73,34 @@ static UA_Server* makeTestSequence(void) {
     myIntegerNodeId = UA_NODEID_STRING(1, "myarray");
     myIntegerNodeId = UA_NODEID_STRING(1, "myarray");
     parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
     parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
     parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
     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 */
     /* ObjectNode */
     UA_ObjectAttributes obj_attr;
     UA_ObjectAttributes obj_attr;
     UA_ObjectAttributes_init(&obj_attr);
     UA_ObjectAttributes_init(&obj_attr);
     obj_attr.description = UA_LOCALIZEDTEXT("en_US","Demo");
     obj_attr.description = UA_LOCALIZEDTEXT("en_US","Demo");
     obj_attr.displayName = 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 */
     /* ViewNode */
     UA_ViewAttributes view_attr;
     UA_ViewAttributes view_attr;
     UA_ViewAttributes_init(&view_attr);
     UA_ViewAttributes_init(&view_attr);
     view_attr.description = UA_LOCALIZEDTEXT("en_US", "Viewtest");
     view_attr.description = UA_LOCALIZEDTEXT("en_US", "Viewtest");
     view_attr.displayName = 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
 #ifdef UA_ENABLE_METHODCALLS
     /* MethodNode */
     /* MethodNode */
@@ -108,13 +108,12 @@ static UA_Server* makeTestSequence(void) {
     UA_MethodAttributes_init(&ma);
     UA_MethodAttributes_init(&ma);
     ma.description = UA_LOCALIZEDTEXT("en_US", "Methodtest");
     ma.description = UA_LOCALIZEDTEXT("en_US", "Methodtest");
     ma.displayName = UA_LOCALIZEDTEXT("en_US", "Methodtest");
     ma.displayName = UA_LOCALIZEDTEXT("en_US", "Methodtest");
-    UA_QualifiedName qualifiedName = UA_QUALIFIEDNAME_ALLOC(0, "Methodtest");
-    UA_Server_addMethodNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_METHODNODE),
-                            UA_NODEID_NUMERIC(0, 3),
-                            UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
-                            qualifiedName, ma,
-                            NULL, NULL, 0, NULL, 0, NULL, NULL);
-    UA_QualifiedName_deleteMembers(&qualifiedName);
+    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
 #endif
 
 
     return server;
     return server;
@@ -124,7 +123,8 @@ static UA_VariableNode* makeCompareSequence(void) {
     UA_VariableNode *node = UA_NodeStore_newVariableNode();
     UA_VariableNode *node = UA_NodeStore_newVariableNode();
 
 
     UA_Int32 myInteger = 42;
     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");
     const UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer");
     UA_QualifiedName_copy(&myIntegerName,&node->browseName);
     UA_QualifiedName_copy(&myIntegerName,&node->browseName);
@@ -437,11 +437,13 @@ START_TEST(ReadSingleAttributeDataTypeWithoutTimestamp) {
     rReq.nodesToRead[0].nodeId = UA_NODEID_STRING_ALLOC(1, "the.answer");
     rReq.nodesToRead[0].nodeId = UA_NODEID_STRING_ALLOC(1, "the.answer");
     rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_DATATYPE;
     rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_DATATYPE;
     Service_Read_single(server, &adminSession, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0], &resp);
     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(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);
     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->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_ReadRequest_deleteMembers(&rReq);
     UA_DataValue_deleteMembers(&resp);
     UA_DataValue_deleteMembers(&resp);
     UA_Server_delete(server);
     UA_Server_delete(server);
@@ -609,23 +611,6 @@ START_TEST(ReadSingleAttributeUserExecutableWithoutTimestamp) {
 #endif
 #endif
 } END_TEST
 } 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) {
 START_TEST(ReadSingleDataSourceAttributeValueWithoutTimestamp) {
     UA_Server *server = makeTestSequence();
     UA_Server *server = makeTestSequence();
     UA_DataValue resp;
     UA_DataValue resp;
@@ -637,7 +622,7 @@ START_TEST(ReadSingleDataSourceAttributeValueWithoutTimestamp) {
     rReq.nodesToRead[0].nodeId = UA_NODEID_STRING_ALLOC(1, "cpu.temperature");
     rReq.nodesToRead[0].nodeId = UA_NODEID_STRING_ALLOC(1, "cpu.temperature");
     rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_VALUE;
     rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_VALUE;
     Service_Read_single(server, &adminSession, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0], &resp);
     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_Server_delete(server);
     UA_ReadRequest_deleteMembers(&rReq);
     UA_ReadRequest_deleteMembers(&rReq);
     UA_DataValue_deleteMembers(&resp);
     UA_DataValue_deleteMembers(&resp);
@@ -654,7 +639,8 @@ START_TEST(ReadSingleDataSourceAttributeDataTypeWithoutTimestamp) {
     rReq.nodesToRead[0].nodeId = UA_NODEID_STRING_ALLOC(1, "cpu.temperature");
     rReq.nodesToRead[0].nodeId = UA_NODEID_STRING_ALLOC(1, "cpu.temperature");
     rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_DATATYPE;
     rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_DATATYPE;
     Service_Read_single(server, &adminSession, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0], &resp);
     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_Server_delete(server);
     UA_ReadRequest_deleteMembers(&rReq);
     UA_ReadRequest_deleteMembers(&rReq);
     UA_DataValue_deleteMembers(&resp);
     UA_DataValue_deleteMembers(&resp);
@@ -671,7 +657,7 @@ START_TEST (ReadSingleDataSourceAttributeArrayDimensionsWithoutTimestamp) {
     rReq.nodesToRead[0].nodeId = UA_NODEID_STRING_ALLOC(1, "cpu.temperature");
     rReq.nodesToRead[0].nodeId = UA_NODEID_STRING_ALLOC(1, "cpu.temperature");
     rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_ARRAYDIMENSIONS;
     rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_ARRAYDIMENSIONS;
     Service_Read_single(server, &adminSession, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0], &resp);
     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_Server_delete(server);
     UA_ReadRequest_deleteMembers(&rReq);
     UA_ReadRequest_deleteMembers(&rReq);
     UA_DataValue_deleteMembers(&resp);
     UA_DataValue_deleteMembers(&resp);
@@ -862,6 +848,7 @@ START_TEST(WriteSingleAttributeValue) {
     wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
     wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
     wValue.attributeId = UA_ATTRIBUTEID_VALUE;
     wValue.attributeId = UA_ATTRIBUTEID_VALUE;
     UA_StatusCode retval = Service_Write_single(server, &adminSession, &wValue);
     UA_StatusCode retval = Service_Write_single(server, &adminSession, &wValue);
+    ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
 
 
     UA_DataValue resp;
     UA_DataValue resp;
     UA_DataValue_init(&resp);
     UA_DataValue_init(&resp);
@@ -871,12 +858,42 @@ START_TEST(WriteSingleAttributeValue) {
     id.attributeId = UA_ATTRIBUTEID_VALUE;
     id.attributeId = UA_ATTRIBUTEID_VALUE;
     Service_Read_single(server, &adminSession, UA_TIMESTAMPSTORETURN_NEITHER, &id, &resp);
     Service_Read_single(server, &adminSession, UA_TIMESTAMPSTORETURN_NEITHER, &id, &resp);
     ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
     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);
     ck_assert_int_eq(20, *(UA_Int32*)resp.value.data);
     UA_DataValue_deleteMembers(&resp);
     UA_DataValue_deleteMembers(&resp);
     UA_Server_delete(server);
     UA_Server_delete(server);
 } END_TEST
 } END_TEST
 
 
+START_TEST(WriteSingleAttributeValueRangeFromScalar) {
+    UA_Server *server = makeTestSequence();
+    UA_WriteValue wValue;
+    UA_WriteValue_init(&wValue);
+    UA_Int32 myInteger = 20;
+    UA_Variant_setScalar(&wValue.value.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
+    wValue.value.hasValue = true;
+    wValue.nodeId = UA_NODEID_STRING(1, "myarray");
+    wValue.indexRange = UA_STRING("0,0");
+    wValue.attributeId = UA_ATTRIBUTEID_VALUE;
+    UA_StatusCode retval = Service_Write_single(server, &adminSession, &wValue);
+    ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
+    UA_Server_delete(server);
+} END_TEST
+
+START_TEST(WriteSingleAttributeValueRangeFromArray) {
+    UA_Server *server = makeTestSequence();
+    UA_WriteValue wValue;
+    UA_WriteValue_init(&wValue);
+    UA_Int32 myInteger = 20;
+    UA_Variant_setArray(&wValue.value.value, &myInteger, 1, &UA_TYPES[UA_TYPES_INT32]);
+    wValue.value.hasValue = true;
+    wValue.nodeId = UA_NODEID_STRING(1, "myarray");
+    wValue.indexRange = UA_STRING("0,0");
+    wValue.attributeId = UA_ATTRIBUTEID_VALUE;
+    UA_StatusCode retval = Service_Write_single(server, &adminSession, &wValue);
+    ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
+    UA_Server_delete(server);
+} END_TEST
+
 START_TEST(WriteSingleAttributeDataType) {
 START_TEST(WriteSingleAttributeDataType) {
     UA_Server *server = makeTestSequence();
     UA_Server *server = makeTestSequence();
     UA_WriteValue wValue;
     UA_WriteValue wValue;
@@ -888,7 +905,7 @@ START_TEST(WriteSingleAttributeDataType) {
     wValue.value.hasValue = true;
     wValue.value.hasValue = true;
     UA_Variant_setScalar(&wValue.value.value, &typeId, &UA_TYPES[UA_TYPES_NODEID]);
     UA_Variant_setScalar(&wValue.value.value, &typeId, &UA_TYPES[UA_TYPES_NODEID]);
     UA_StatusCode retval = Service_Write_single(server, &adminSession, &wValue);
     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);
     UA_Server_delete(server);
 } END_TEST
 } END_TEST
 
 
@@ -903,7 +920,7 @@ START_TEST(WriteSingleAttributeValueRank) {
     wValue.value.hasValue = true;
     wValue.value.hasValue = true;
     UA_StatusCode retval = Service_Write_single(server, &adminSession, &wValue);
     UA_StatusCode retval = Service_Write_single(server, &adminSession, &wValue);
     // Returns attributeInvalid, since variant/value may be writable
     // 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);
     UA_Server_delete(server);
 } END_TEST
 } END_TEST
 
 
@@ -911,14 +928,14 @@ START_TEST(WriteSingleAttributeArrayDimensions) {
     UA_Server *server = makeTestSequence();
     UA_Server *server = makeTestSequence();
     UA_WriteValue wValue;
     UA_WriteValue wValue;
     UA_WriteValue_init(&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.nodeId = UA_NODEID_STRING(1, "the.answer");
     wValue.attributeId = UA_ATTRIBUTEID_ARRAYDIMENSIONS;
     wValue.attributeId = UA_ATTRIBUTEID_ARRAYDIMENSIONS;
     wValue.value.hasValue = true;
     wValue.value.hasValue = true;
     UA_StatusCode retval = Service_Write_single(server, &adminSession, &wValue);
     UA_StatusCode retval = Service_Write_single(server, &adminSession, &wValue);
     // Returns attributeInvalid, since variant/value may be writable
     // 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);
     UA_Server_delete(server);
 } END_TEST
 } END_TEST
 
 
@@ -1047,7 +1064,6 @@ static Suite * testSuite_services_attributes(void) {
     tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeHistorizingWithoutTimestamp);
     tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeHistorizingWithoutTimestamp);
     tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeExecutableWithoutTimestamp);
     tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeExecutableWithoutTimestamp);
     tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeUserExecutableWithoutTimestamp);
     tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeUserExecutableWithoutTimestamp);
-    tcase_add_test(tc_readSingleAttributes, ReadSingleDataSourceAttributeDataTypeWithoutTimestampFromBrokenSource);
     tcase_add_test(tc_readSingleAttributes, ReadSingleDataSourceAttributeValueWithoutTimestamp);
     tcase_add_test(tc_readSingleAttributes, ReadSingleDataSourceAttributeValueWithoutTimestamp);
     tcase_add_test(tc_readSingleAttributes, ReadSingleDataSourceAttributeDataTypeWithoutTimestamp);
     tcase_add_test(tc_readSingleAttributes, ReadSingleDataSourceAttributeDataTypeWithoutTimestamp);
     tcase_add_test(tc_readSingleAttributes, ReadSingleDataSourceAttributeArrayDimensionsWithoutTimestamp);
     tcase_add_test(tc_readSingleAttributes, ReadSingleDataSourceAttributeArrayDimensionsWithoutTimestamp);
@@ -1069,6 +1085,8 @@ static Suite * testSuite_services_attributes(void) {
     tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeEventNotifier);
     tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeEventNotifier);
     tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeValue);
     tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeValue);
     tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeDataType);
     tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeDataType);
+    tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeValueRangeFromScalar);
+    tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeValueRangeFromArray);
     tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeValueRank);
     tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeValueRank);
     tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeArrayDimensions);
     tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeArrayDimensions);
     tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeAccessLevel);
     tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeAccessLevel);

+ 13 - 17
tests/check_session.c

@@ -5,8 +5,7 @@
 #include "server/ua_services.h"
 #include "server/ua_services.h"
 #include "check.h"
 #include "check.h"
 
 
-START_TEST(Session_init_ShallWork)
-{
+START_TEST(Session_init_ShallWork) {
     UA_Session session;
     UA_Session session;
     UA_Session_init(&session);
     UA_Session_init(&session);
 
 
@@ -16,23 +15,22 @@ START_TEST(Session_init_ShallWork)
     UA_ApplicationDescription_init(&tmpAppDescription);
     UA_ApplicationDescription_init(&tmpAppDescription);
     UA_DateTime tmpDateTime;
     UA_DateTime tmpDateTime;
     UA_DateTime_init(&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_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
 END_TEST
 
 
-START_TEST(Session_updateLifetime_ShallWork)
-{
+START_TEST(Session_updateLifetime_ShallWork) {
     UA_Session session;
     UA_Session session;
     UA_Session_init(&session);
     UA_Session_init(&session);
     UA_DateTime tmpDateTime;
     UA_DateTime tmpDateTime;
@@ -68,5 +66,3 @@ int main(void) {
 
 
     return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
     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[1] = UA_STRING_ALLOC("_62541");
     srcArray[2] = UA_STRING_ALLOC("opc ua");
     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;
     dimensions[0] = 3;
 
 
     UA_Variant value, copiedValue;
     UA_Variant value, copiedValue;
@@ -1373,9 +1373,9 @@ START_TEST(UA_Variant_copyShallWorkOn2DArrayExample) {
     srcArray[4] = 4;
     srcArray[4] = 4;
     srcArray[5] = 5;
     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[0] = dim1;
     dimensions[1] = dim2;
     dimensions[1] = dim2;
 
 

+ 1 - 1
tests/check_types_range.c

@@ -23,7 +23,7 @@ START_TEST(parseRange) {
 
 
 START_TEST(parseRangeMinEqualMax) {
 START_TEST(parseRangeMinEqualMax) {
     UA_NumericRange range;
     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);
     UA_StatusCode retval = parse_numericrange(&str, &range);
     ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
     ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
     ck_assert_int_eq(range.dimensionsSize,2);
     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
 # - Find the last line beginning with "#ifdef" -> end of the documentation
 
 
 remove_keyword = [" UA_EXPORT", " UA_FUNC_ATTR_WARN_UNUSED_RESULT",
 remove_keyword = [" UA_EXPORT", " UA_FUNC_ATTR_WARN_UNUSED_RESULT",
-                  " UA_FUNC_ATTR_MALLOC"]
+                  " UA_FUNC_ATTR_MALLOC", " UA_RESTRICT "]
 
 
 def clean_comment(line):
 def clean_comment(line):
     m = re.search("^( \* |/\*\* )(.*?)( \*/)?$", line)
     m = re.search("^( \* |/\*\* )(.*?)( \*/)?$", line)

+ 6 - 8
tools/generate_datatypes.py

@@ -414,21 +414,19 @@ extern "C" {
 #endif
 #endif
 
 
 #include "ua_types.h"
 #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("#define " + outname.upper() + "_COUNT %s" % (str(len(selected_types))))
 printh("extern UA_EXPORT const UA_DataType " + outname.upper() + "[" + outname.upper() + "_COUNT];")
 printh("extern UA_EXPORT const UA_DataType " + outname.upper() + "[" + outname.upper() + "_COUNT];")
 
 
 i = 0
 i = 0
 for t in iter_types(types):
 for t in iter_types(types):
     printh("\n/**\n * " +  t.name)
     printh("\n/**\n * " +  t.name)
-    printh(" * " + "-" * len(t.name))
+    printh(" * " + "^" * len(t.name))
     if t.description == "":
     if t.description == "":
         printh(" */")
         printh(" */")
     else:
     else:

+ 0 - 0
tools/pyUANamespace/NodeID_AssumeExternal.txt


+ 0 - 0
tools/pyUANamespace/NodeID_Blacklist.txt


+ 0 - 0
tools/pyUANamespace/NodeID_Blacklist_FullNS0.txt


Diferenças do arquivo suprimidas por serem muito extensas
+ 1514 - 0
tools/pyUANamespace/NodeID_NameSpace0_All.txt


+ 15 - 5
tools/pyUANamespace/open62541_MacroHelper.py

@@ -199,11 +199,13 @@ class open62541_MacroHelper():
     code.append("UA_%sAttributes_init(&attr);" %  nodetype);
     code.append("UA_%sAttributes_init(&attr);" %  nodetype);
     code.append("attr.displayName = UA_LOCALIZEDTEXT(\"\", \"" + str(node.displayName()) + "\");")
     code.append("attr.displayName = UA_LOCALIZEDTEXT(\"\", \"" + str(node.displayName()) + "\");")
     code.append("attr.description = UA_LOCALIZEDTEXT(\"\", \"" + str(node.description()) + "\");")
     code.append("attr.description = UA_LOCALIZEDTEXT(\"\", \"" + str(node.description()) + "\");")
-
+    
     if nodetype == "Variable":
     if nodetype == "Variable":
       code.append("attr.accessLevel = %s;"     % str(node.accessLevel()))
       code.append("attr.accessLevel = %s;"     % str(node.accessLevel()))
       code.append("attr.userAccessLevel = %s;" % str(node.userAccessLevel()))
       code.append("attr.userAccessLevel = %s;" % str(node.userAccessLevel()))
-
+    if nodetype in ["Variable", "VariableType"]:
+      code.append("attr.valueRank = %s;"       % str(node.valueRank()))
+      
     if nodetype in ["Variable", "VariableType"]:
     if nodetype in ["Variable", "VariableType"]:
       code = code + node.printOpen62541CCode_SubtypeEarly(bootstrapping = False)
       code = code + node.printOpen62541CCode_SubtypeEarly(bootstrapping = False)
     elif nodetype == "Method":
     elif nodetype == "Method":
@@ -213,8 +215,16 @@ class open62541_MacroHelper():
         code.append("attr.userExecutable = true;")
         code.append("attr.userExecutable = true;")
 
 
     code.append("UA_NodeId nodeId = " + str(self.getCreateNodeIDMacro(node)) + ";")
     code.append("UA_NodeId nodeId = " + str(self.getCreateNodeIDMacro(node)) + ";")
-    if nodetype in ["Object", "Variable"]:
-      code.append("UA_NodeId typeDefinition = UA_NODEID_NULL;") #due to the current API we cannot set types here since the API will generate nodes with random IDs
+    if nodetype in ["Object", "Variable", "VariableType"]:
+      typeDefinition = None
+      for r in node.getReferences():
+        if r.isForward() and r.referenceType().id().ns == 0 and r.referenceType().id().i == 40:
+          typeDefinition = r.target()
+      if typeDefinition == None:
+        # FIXME: We might want to resort to BaseXYTTypes here?
+        code.append("UA_NodeId typeDefinition = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE);")
+      else:
+        code.append("UA_NodeId typeDefinition = " + str(self.getCreateNodeIDMacro(typeDefinition)) + ";")
     code.append("UA_NodeId parentNodeId = " + str(self.getCreateNodeIDMacro(parentNode)) + ";")
     code.append("UA_NodeId parentNodeId = " + str(self.getCreateNodeIDMacro(parentNode)) + ";")
     code.append("UA_NodeId parentReferenceNodeId = " + str(self.getCreateNodeIDMacro(parentReference.referenceType())) + ";")
     code.append("UA_NodeId parentReferenceNodeId = " + str(self.getCreateNodeIDMacro(parentReference.referenceType())) + ";")
     extrNs = node.browseName().split(":")
     extrNs = node.browseName().split(":")
@@ -227,7 +237,7 @@ class open62541_MacroHelper():
     # Djikstra (check that arguments have not been printed). (@ichrispa)
     # Djikstra (check that arguments have not been printed). (@ichrispa)
     code.append("UA_Server_add%sNode(server, nodeId, parentNodeId, parentReferenceNodeId, nodeName" % nodetype)
     code.append("UA_Server_add%sNode(server, nodeId, parentNodeId, parentReferenceNodeId, nodeName" % nodetype)
 
 
-    if nodetype in ["Object", "Variable"]:
+    if nodetype in ["Object", "Variable", "VariableType"]:
       code.append("       , typeDefinition")
       code.append("       , typeDefinition")
     if nodetype != "Method":
     if nodetype != "Method":
       code.append("       , attr, NULL, NULL);")
       code.append("       , attr, NULL, NULL);")

+ 3 - 0
tools/pyUANamespace/ua_namespace.py

@@ -653,6 +653,9 @@ class opcua_namespace():
     header.append('#include "open62541.h"')
     header.append('#include "open62541.h"')
     header.append('#define NULL ((void *)0)')
     header.append('#define NULL ((void *)0)')
     header.append('#endif')
     header.append('#endif')
+    header.append('#ifndef UA_ENCODINGOFFSET_BINARY')
+    header.append('#define UA_ENCODINGOFFSET_BINARY 2')
+    header.append('#endif')
 
 
     code.append('#include "'+outfilename+'.h"')
     code.append('#include "'+outfilename+'.h"')
     code.append("UA_INLINE void "+outfilename+"(UA_Server *server) {")
     code.append("UA_INLINE void "+outfilename+"(UA_Server *server) {")

+ 4 - 4
tools/pyUANamespace/ua_node_types.py

@@ -1105,8 +1105,8 @@ class opcua_node_variable_t(opcua_node_t):
     code.append(self.getCodePrintableID() + "->accessLevel = (UA_Int32) " + str(self.accessLevel()) + ";")
     code.append(self.getCodePrintableID() + "->accessLevel = (UA_Int32) " + str(self.accessLevel()) + ";")
     code.append(self.getCodePrintableID() + "->valueRank = (UA_Int32) " + str(self.valueRank()) + ";")
     code.append(self.getCodePrintableID() + "->valueRank = (UA_Int32) " + str(self.valueRank()) + ";")
     # The variant is guaranteed to exist by SubtypeEarly()
     # The variant is guaranteed to exist by SubtypeEarly()
-    code.append(self.getCodePrintableID() + "->value.variant.value = *" + self.getCodePrintableID() + "_variant;")
-    code.append(self.getCodePrintableID() + "->valueSource = UA_VALUESOURCE_VARIANT;")
+    code.append("UA_Variant_copy(" + self.getCodePrintableID() + "_variant, &" + self.getCodePrintableID() + "->value.data.value.value );")
+    code.append(self.getCodePrintableID() + "->valueSource = UA_VALUESOURCE_DATA;")
     return code
     return code
 
 
 class opcua_node_method_t(opcua_node_t):
 class opcua_node_method_t(opcua_node_t):
@@ -1360,8 +1360,8 @@ class opcua_node_variableType_t(opcua_node_t):
       code.append(self.getCodePrintableID() + "->isAbstract = false;")
       code.append(self.getCodePrintableID() + "->isAbstract = false;")
 
 
     # The variant is guaranteed to exist by SubtypeEarly()
     # The variant is guaranteed to exist by SubtypeEarly()
-    code.append(self.getCodePrintableID() + "->value.variant.value = *" + self.getCodePrintableID() + "_variant;")
-    code.append(self.getCodePrintableID() + "->valueSource = UA_VALUESOURCE_VARIANT;")
+    code.append("UA_Variant_copy(" + self.getCodePrintableID() + "_variant, &" + self.getCodePrintableID() + "->value.data.value.value );")
+    code.append(self.getCodePrintableID() + "->valueSource = UA_VALUESOURCE_DATA;")
     return code
     return code
 
 
 class opcua_node_dataType_t(opcua_node_t):
 class opcua_node_dataType_t(opcua_node_t):

+ 3 - 0
tools/schema/datatypes_minimal.txt

@@ -161,3 +161,6 @@ SetMonitoringModeResponse
 RegisteredServer
 RegisteredServer
 RegisterServerRequest
 RegisterServerRequest
 RegisterServerResponse
 RegisterServerResponse
+DataChangeTrigger
+DeadbandType
+DataChangeFilter