Explorar o código

Merge remote-tracking branch 'refs/remotes/acplt/master'

Oleksiy Vasylyev %!s(int64=9) %!d(string=hai) anos
pai
achega
9eb8b117ec
Modificáronse 100 ficheiros con 10063 adicións e 12714 borrados
  1. 16 85
      .travis.yml
  2. 74 48
      CMakeLists.txt
  3. 23 11
      README.md
  4. 1 1
      appveyor.yml
  5. 0 39
      bindings/CMakeLists.txt
  6. 0 64
      bindings/open62541.i
  7. 35 0
      deps/libc_string.c
  8. 116 0
      deps/pcg_basic.c
  9. 78 0
      deps/pcg_basic.h
  10. 122 9
      doc/building.rst
  11. 601 143
      doc/datatypes.rst
  12. 3 3
      doc/mainpage.dox
  13. 1 1
      doc/tutorial_client_firstSteps.rst
  14. 1 1
      doc/tutorial_noderelations.rst
  15. 5 5
      doc/tutorials.rst
  16. 3 3
      examples/CMakeLists.txt
  17. 96 103
      examples/client.c
  18. 8 12
      examples/client_firstSteps.c
  19. 561 511
      examples/client_legacy.c
  20. 4 2
      examples/client_stateless.c
  21. 9 18
      examples/logger_stdout.c
  22. 9 2
      examples/logger_stdout.h
  23. 180 158
      examples/networklayer_tcp.c
  24. 4 2
      examples/networklayer_tcp.h
  25. 92 95
      examples/networklayer_udp.c
  26. 6 6
      examples/networklayer_udp.h
  27. 247 234
      examples/server.c
  28. 28 11
      examples/server.cpp
  29. 19 17
      examples/server_datasource.c
  30. 1 2
      examples/server_firstSteps.c
  31. 40 26
      examples/server_method.c
  32. 1 2
      examples/server_nodeset.c
  33. 103 0
      examples/server_readspeed.c
  34. 1 2
      examples/server_repeated_job.c
  35. 15 12
      examples/server_udp.c
  36. 26 10
      examples/server_variable.c
  37. 346 60
      include/ua_client.h
  38. 294 0
      include/ua_client_highlevel.h
  39. 40 0
      include/ua_config.h.in
  40. 47 26
      include/ua_connection.h
  41. 56 0
      include/ua_job.h
  42. 3 1
      include/ua_log.h
  43. 356 476
      include/ua_server.h
  44. 96 0
      include/ua_server_external_ns.h
  45. 3 2
      include/ua_statuscodes.h
  46. 368 338
      include/ua_types.h
  47. 0 51
      ports/WAGO-750-860.patch
  48. 252 851
      src/client/ua_client.c
  49. 261 0
      src/client/ua_client_highlevel.c
  50. 308 0
      src/client/ua_client_highlevel_subscriptions.c
  51. 42 7
      src/client/ua_client_internal.h
  52. 0 281
      src/ongoing/generateSam.c
  53. 0 85
      src/ongoing/ua_namespace_xml.c
  54. 0 41
      src/ongoing/ua_namespace_xml.h
  55. 0 447
      src/ongoing/ua_types_encoding_xml.c
  56. 0 77
      src/ongoing/ua_types_encoding_xml.h
  57. 0 980
      src/ongoing/ua_xml.c
  58. 0 134
      src/ongoing/ua_xml.h
  59. 0 210
      src/ongoing/xml2ns0.c
  60. 139 66
      src/server/ua_nodes.c
  61. 27 7
      src/server/ua_nodes.h
  62. 82 168
      src/server/ua_nodestore.c
  63. 16 12
      src/server/ua_nodestore.h
  64. 69 142
      src/server/ua_nodestore_concurrent.c
  65. 1 1
      src/server/ua_nodestore_hash.inc
  66. 86 89
      src/server/ua_securechannel_manager.c
  67. 24 12
      src/server/ua_securechannel_manager.h
  68. 628 437
      src/server/ua_server.c
  69. 0 1716
      src/server/ua_server_addressspace.c
  70. 268 221
      src/server/ua_server_binary.c
  71. 18 16
      src/server/ua_server_internal.h
  72. 71 46
      src/server/ua_server_worker.c
  73. 107 76
      src/server/ua_services.h
  74. 478 432
      src/server/ua_services_attribute.c
  75. 100 105
      src/server/ua_services_call.c
  76. 8 7
      src/server/ua_services_discovery.c
  77. 823 235
      src/server/ua_services_nodemanagement.c
  78. 26 6
      src/server/ua_services_securechannel.c
  79. 107 75
      src/server/ua_services_session.c
  80. 77 24
      src/server/ua_services_subscription.c
  81. 196 234
      src/server/ua_services_view.c
  82. 30 23
      src/server/ua_session_manager.c
  83. 14 10
      src/server/ua_session_manager.h
  84. 90 148
      src/server/ua_subscription.c
  85. 10 13
      src/server/ua_subscription.h
  86. 23 27
      src/server/ua_subscription_manager.c
  87. 97 68
      src/ua_connection.c
  88. 21 17
      src/ua_securechannel.c
  89. 1 1
      src/ua_securechannel.h
  90. 15 15
      src/ua_session.c
  91. 1 1
      src/ua_session.h
  92. 447 919
      src/ua_types.c
  93. 740 911
      src/ua_types_encoding_binary.c
  94. 3 75
      src/ua_types_encoding_binary.h
  95. 43 49
      src/ua_util.h
  96. 6 8
      tests/CMakeLists.txt
  97. 0 115
      tests/check_base64.c
  98. 153 468
      tests/check_builtin.c
  99. 17 14
      tests/check_memory.c
  100. 0 0
      tests/check_nodestore.c

+ 16 - 85
.travis.yml

@@ -1,7 +1,11 @@
 language: c
 compiler:
 - gcc
-- clang
+#- clang
+os:
+- linux
+- osx
+sudo: true
 env:
   global:
   - secure: nSunY54Wp5HkQCHHbKwlwpbaKyqRVIu/0EnhaoJSwhM1wqerQV/E5d/2JelO9/tZgbungAO7wk/fjutRMVc7d378RTIPwS8vHpvZfEoGhCFsLoTOlqESzsZFBup2H5t1lpQ23jRHDOxlLdJy2lz5U+zd1YnYgDXqdDFjegsIYdo=
@@ -15,93 +19,20 @@ addons:
     build_command_prepend: mkdir build && cd build && cmake ..
     build_command: make
     branch_pattern: coverity_scan
+matrix:
+  # This excludes OSX GCC builds.
+  exclude:
+    - os: osx
+      compiler: gcc
 before_install:
-- sudo apt-get install binutils-mingw-w64-i686 gcc-mingw-w64-i686 mingw-w64
-- sudo add-apt-repository ppa:kalakris/cmake -y
-- sudo apt-get update -qq
-- sudo apt-get install -qq --no-install-recommends build-essential cmake python-lxml gcc-multilib graphviz doxygen wget zip
-- sudo apt-get install libsubunit-dev #for check_0.10.0
-- wget http://ftp.de.debian.org/debian/pool/main/c/check/check_0.10.0-2_amd64.deb
-- sudo dpkg -i check_0.10.0-2_amd64.deb
-- wget https://launchpad.net/ubuntu/+source/liburcu/0.8.5-1ubuntu1/+build/6513813/+files/liburcu2_0.8.5-1ubuntu1_amd64.deb
-- wget https://launchpad.net/ubuntu/+source/liburcu/0.8.5-1ubuntu1/+build/6513813/+files/liburcu-dev_0.8.5-1ubuntu1_amd64.deb
-- sudo dpkg -i liburcu2_0.8.5-1ubuntu1_amd64.deb
-- sudo dpkg -i liburcu-dev_0.8.5-1ubuntu1_amd64.deb
-- sudo pip install cpp-coveralls
-- sudo pip install Pygments
-- sudo pip install sphinx
-- sudo pip install breathe
-- sudo pip install sphinx_rtd_theme
+- "if [ ${TRAVIS_OS_NAME} == 'linux' ]; then . ./tools/travis_linux_before_install.sh; fi"
+- "if [ ${TRAVIS_OS_NAME} == 'osx' ]; then . ./tools/travis_osx_before_install.sh; fi"
 script:
-- echo "Checking the applicability of patches"
-- # ./tools/.checkPorts.sh
-- echo "Documentation and certificate build"
-- mkdir -p build
-- cd build
-- cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_DOCUMENTATION=ON -DGENERATE_SELFSIGNED=ON ..
-- make doc
-- make selfsigned
-- cp -r doc ..
-- cp server_cert.der ..
-- echo "Testing builds"
-- cd .. && rm build -rf && mkdir -p build && cd build
-- echo "Cross compile release build for MinGW 32 bit"
-- cmake -DCMAKE_TOOLCHAIN_FILE=../cmake/Toolchain-mingw32.cmake -DENABLE_AMALGAMATION=ON -DCMAKE_BUILD_TYPE=Release -DBUILD_EXAMPLESERVER=ON -DBUILD_EXAMPLECLIENT=ON ..
-- make 
-- cp ../README.md .
-- cp ../LICENSE .
-- cp ../AUTHORS .
-- cp -r ../doc .
-- cp ../server_cert.der .
-- zip -r open62541-win32.zip doc server_cert.der LICENSE AUTHORS README.md server_static.exe server.exe client.exe client_static.exe libopen62541.dll libopen62541.dll.a open62541.h open62541.c
-- cp open62541-win32.zip ..
-- cd .. && rm build -rf && mkdir -p build && cd build
-- echo "Cross compile release build for MinGW 64 bit"
-- cmake -DCMAKE_TOOLCHAIN_FILE=../cmake/Toolchain-mingw64.cmake -DENABLE_AMALGAMATION=ON -DCMAKE_BUILD_TYPE=Release -DBUILD_EXAMPLESERVER=ON -DBUILD_EXAMPLECLIENT=ON ..
-- make 
-- cp ../README.md .
-- cp ../LICENSE .
-- cp ../AUTHORS .
-- cp -r ../doc .
-- cp ../server_cert.der .
-- zip -r open62541-win64.zip doc server_cert.der LICENSE AUTHORS README.md server_static.exe server.exe client.exe client_static.exe libopen62541.dll libopen62541.dll.a open62541.h open62541.c
-- cp open62541-win64.zip ..
-- cd .. && rm build -rf && mkdir -p build && cd build
-- echo "Cross compile release build for 32-bit linux"
-- cmake -DCMAKE_TOOLCHAIN_FILE=../cmake/Toolchain-gcc-m32.cmake -DENABLE_AMALGAMATION=ON -DCMAKE_BUILD_TYPE=Release -DBUILD_EXAMPLESERVER=ON -DBUILD_EXAMPLECLIENT=ON ..
-- make 
-- tar -pczf open62541-linux32.tar.gz ../doc ../server_cert.der ../LICENSE ../AUTHORS ../README.md server_static server client_static client libopen62541.so open62541.h open62541.c
-- cp open62541-linux32.tar.gz ..
-- cd .. && rm build -rf && mkdir -p build && cd build
-- echo "Compile release build for 64-bit linux"
-- cmake -DCMAKE_BUILD_TYPE=Release -DENABLE_AMALGAMATION=ON -DBUILD_EXAMPLESERVER=ON -DBUILD_EXAMPLECLIENT=ON ..
-- make 
-- tar -pczf open62541-linux64.tar.gz ../doc ../server_cert.der ../LICENSE ../AUTHORS ../README.md server_static server client_static client libopen62541.so open62541.h open62541.c
-- cp open62541-linux64.tar.gz ..
-- cp open62541.h .. #copy single file-release
-- cp open62541.c .. #copy single file-release
-- cd .. && rm build -rf && mkdir -p build && cd build
-- echo "Upgrade to gcc 4.8"
-- sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y
-- sudo apt-get update -qq
-- sudo apt-get install -qq gcc-4.8 valgrind
-- sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 20
-- sudo update-alternatives --config gcc
-- echo "Compile multithreaded version"
-- cmake -DENABLE_MULTITHREADING=ON -DBUILD_EXAMPLESERVER=ON ..
-- make 
-- cd .. && rm build -rf && mkdir -p build && cd build
-- echo "Debug build and unit tests (64 bit)"
-- cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_DEMO_NODESET=ON -DBUILD_UNIT_TESTS=ON -DBUILD_EXAMPLESERVER=ON -DENABLE_COVERAGE=ON ..
-- make && make test
-- echo "Run valgrind to see if the server leaks memory (just starting up and closing..)"
-- if [[ ! ( ${TRAVIS_OS_NAME} == "linux" && ${CC} == "clang") ]]; then
-  (valgrind --error-exitcode=3 ./server & export pid=$!; sleep 2; kill -INT $pid; wait $pid);
-  (coveralls --gcov /usr/bin/gcov-4.8 -E '.*\.h' -E '.*CMakeCXXCompilerId\.cpp' -E '.*CMakeCCompilerId\.c' -r ../ ; exit 0);
-  fi
-- cd .. 
+- "if [ ${TRAVIS_OS_NAME} == 'linux' ]; then . ./tools/travis_linux_script.sh; fi"
+- "if [ ${TRAVIS_OS_NAME} == 'osx' ]; then . ./tools/travis_osx_script.sh; fi"
 after_success:
-- if [[ ( ${TRAVIS_OS_NAME} == "linux" && ${CC} == "gcc" && ${TRAVIS_BRANCH} == "master") ]]; then ./tools/.deployGH.sh; fi
+- if [[ ( ${TRAVIS_OS_NAME} == "linux" && ${CC} == "gcc" && ${TRAVIS_BRANCH} == "master") ]]; then ./tools/.deployGH_doc.sh; fi
+- if [[ ( ${TRAVIS_OS_NAME} == "linux" && ${CC} == "gcc" ) ]]; then ./tools/.deployGH_release.sh; fi
 - if [[ ( ${TRAVIS_OS_NAME} == "linux" && ${CC} == "gcc" && ${TRAVIS_BRANCH} == "master") ]]; then ./tools/.coverity.sh; fi
 before_deploy:
 - rm build -rf && mkdir -p build && cd build

+ 74 - 48
CMakeLists.txt

@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.8)
+cmake_minimum_required(VERSION 2.8.11)
 # set(CMAKE_VERBOSE_MAKEFILE ON)
 
 project(open62541 C)
@@ -43,12 +43,12 @@ endif()
 if(CMAKE_COMPILER_IS_GNUCC OR "x${CMAKE_C_COMPILER_ID}" STREQUAL "xClang")
     add_definitions(-std=c99 -pipe -Wall -Wextra -Werror -Wformat -Wno-unused-parameter
                       -Wno-unused-function -Wno-unused-label -Wpointer-arith -Wreturn-type -Wsign-compare -Wmultichar
-                      -Wcast-qual -Wmissing-prototypes -Wstrict-prototypes # -Wshadow -Wconversion
+                      -Wcast-qual -Wmissing-prototypes -Wstrict-prototypes #-Wshadow #-Wconversion
                       -Winit-self -Wuninitialized -Wformat-security -Wformat-nonliteral)
     # binary size reduction settings
     add_definitions(-ffunction-sections -fdata-sections -fno-stack-protector -fno-unwind-tables
                     -fno-asynchronous-unwind-tables -fno-math-errno -fmerge-all-constants -fno-ident)
-  if (APPLE)
+  if(APPLE)
       set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -Wl,-dead_strip")
       set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-dead_strip")
   else()
@@ -56,19 +56,18 @@ if(CMAKE_COMPILER_IS_GNUCC OR "x${CMAKE_C_COMPILER_ID}" STREQUAL "xClang")
       set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--gc-sections")
   endif()
   set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") # cmake sets -rdynamic by default
-	if(NOT WIN32)
-     if(NOT CYGWIN)
+	if(NOT WIN32 AND NOT CYGWIN)
 	    add_definitions(-fvisibility=hidden -fPIC)
-      if (NOT APPLE)
-          set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -Wl,-z,norelro -Wl,--hash-style=gnu -Wl,--build-id=none")
-      endif()
+      if(NOT APPLE)
+        # these settings reduce the binary size by ~2kb
+        set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -Wl,-z,norelro -Wl,--hash-style=gnu -Wl,--build-id=none")
       endif()
 	endif()
     if(CMAKE_BUILD_TYPE STREQUAL "MinSizeRel" OR CMAKE_BUILD_TYPE STREQUAL "Release")
         set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -s")
         set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -s")
     endif()
-    if (APPLE)
+    if(APPLE)
         set(CMAKE_MACOSX_RPATH 1)
         set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_DARWIN_C_SOURCE=1")
     endif()
@@ -83,16 +82,22 @@ set(exported_headers ${PROJECT_BINARY_DIR}/src_generated/ua_config.h
                      ${PROJECT_SOURCE_DIR}/include/ua_types.h
                      ${PROJECT_BINARY_DIR}/src_generated/ua_nodeids.h
                      ${PROJECT_BINARY_DIR}/src_generated/ua_types_generated.h
+                     ${PROJECT_SOURCE_DIR}/include/ua_job.h
                      ${PROJECT_SOURCE_DIR}/include/ua_connection.h
                      ${PROJECT_SOURCE_DIR}/include/ua_log.h
                      ${PROJECT_SOURCE_DIR}/include/ua_server.h
+                     ${PROJECT_SOURCE_DIR}/include/ua_server_external_ns.h
                      ${PROJECT_SOURCE_DIR}/include/ua_client.h
+                     ${PROJECT_SOURCE_DIR}/include/ua_client_highlevel.h
                      ${PROJECT_SOURCE_DIR}/examples/networklayer_tcp.h
                      ${PROJECT_SOURCE_DIR}/examples/logger_stdout.h)
 set(internal_headers ${PROJECT_SOURCE_DIR}/src/ua_util.h
                      ${PROJECT_SOURCE_DIR}/deps/queue.h
-                     ${PROJECT_BINARY_DIR}/src_generated/ua_transport_generated.h
+                     ${PROJECT_SOURCE_DIR}/deps/pcg_basic.h
                      ${PROJECT_SOURCE_DIR}/src/ua_types_encoding_binary.h
+                     ${PROJECT_BINARY_DIR}/src_generated/ua_types_generated_encoding_binary.h
+                     ${PROJECT_BINARY_DIR}/src_generated/ua_transport_generated.h
+                     ${PROJECT_BINARY_DIR}/src_generated/ua_transport_generated_encoding_binary.h
                      ${PROJECT_SOURCE_DIR}/src/ua_securechannel.h
                      ${PROJECT_SOURCE_DIR}/src/server/ua_nodes.h
                      ${PROJECT_SOURCE_DIR}/src/ua_session.h
@@ -110,7 +115,6 @@ set(lib_sources ${PROJECT_SOURCE_DIR}/src/ua_types.c
                 ${PROJECT_SOURCE_DIR}/src/ua_securechannel.c
                 ${PROJECT_SOURCE_DIR}/src/ua_session.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_server.c
-                ${PROJECT_SOURCE_DIR}/src/server/ua_server_addressspace.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_worker.c
@@ -123,36 +127,54 @@ set(lib_sources ${PROJECT_SOURCE_DIR}/src/ua_types.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_services_nodemanagement.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_services_view.c
                 ${PROJECT_SOURCE_DIR}/src/client/ua_client.c
+                ${PROJECT_SOURCE_DIR}/src/client/ua_client_highlevel.c
                 ${PROJECT_SOURCE_DIR}/examples/networklayer_tcp.c
-                ${PROJECT_SOURCE_DIR}/examples/logger_stdout.c)
+                ${PROJECT_SOURCE_DIR}/examples/logger_stdout.c
+                ${PROJECT_SOURCE_DIR}/deps/pcg_basic.c)
                 ##TODO: make client stuff optional
 
 ## generate code from xml definitions
 file(MAKE_DIRECTORY "${PROJECT_BINARY_DIR}/src_generated")
 
+option(ENABLE_TYPEINTROSPECTION "Add the type and member names to the internal structures" OFF)
+mark_as_advanced(ENABLE_TYPEINTROSPECTION)
+if(ENABLE_TYPEINTROSPECTION)
+  set(generate_typeintrospection "--typeintrospection")
+else()
+  set(generate_typeintrospection "")
+endif()
+
+option(UA_EMBEDDED_LIBC "Target has no libc, use internal definitions" OFF)
+if(UA_EMBEDDED_LIBC)
+  list(APPEND lib_sources ${PROJECT_SOURCE_DIR}/deps/libc_string.c)
+endif()
+
 option(ENABLE_SUBSCRIPTIONS "Enable compilation of subscription and monitoring support." OFF)
 if(ENABLE_SUBSCRIPTIONS)
   set(ENABLE_SUBSCRIPTIONS ON) #to propagate it to the config file
   list(APPEND lib_sources ${PROJECT_SOURCE_DIR}/src/server/ua_services_subscription.c
                           ${PROJECT_SOURCE_DIR}/src/server/ua_subscription.c
-                          ${PROJECT_SOURCE_DIR}/src/server/ua_subscription_manager.c)
+                          ${PROJECT_SOURCE_DIR}/src/server/ua_subscription_manager.c
+                          ${PROJECT_SOURCE_DIR}/src/client/ua_client_highlevel_subscriptions.c)
   ##append subscription headers at before ua_session.
   list(FIND internal_headers "${PROJECT_SOURCE_DIR}/src/ua_session.h" UaSessionPos)
   list(INSERT internal_headers  ${UaSessionPos} ${PROJECT_SOURCE_DIR}/src/server/ua_subscription.h
-                          ${PROJECT_SOURCE_DIR}/src/server/ua_subscription_manager.h)
+                                                ${PROJECT_SOURCE_DIR}/src/server/ua_subscription_manager.h)
 
   add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/ua_types_generated.c
                             ${PROJECT_BINARY_DIR}/src_generated/ua_types_generated.h
+                            ${PROJECT_BINARY_DIR}/src_generated/ua_types_generated_encoding_binary.h
                      PRE_BUILD
-                     COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/generate_datatypes.py --enable-subscription-types=1 --typedescriptions ${PROJECT_SOURCE_DIR}/tools/schema/NodeIds.csv 0 ${PROJECT_SOURCE_DIR}/tools/schema/Opc.Ua.Types.bsd ${PROJECT_BINARY_DIR}/src_generated/ua_types
+                     COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/generate_datatypes.py --enable-subscription-types=1 ${generate_typeintrospection} --typedescriptions ${PROJECT_SOURCE_DIR}/tools/schema/NodeIds.csv 0 ${PROJECT_SOURCE_DIR}/tools/schema/Opc.Ua.Types.bsd ${PROJECT_BINARY_DIR}/src_generated/ua_types
                      DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tools/generate_datatypes.py
                              ${CMAKE_CURRENT_SOURCE_DIR}/tools/schema/Opc.Ua.Types.bsd
                              ${CMAKE_CURRENT_SOURCE_DIR}/tools/schema/NodeIds.csv)
 else()
 add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/ua_types_generated.c
                           ${PROJECT_BINARY_DIR}/src_generated/ua_types_generated.h
+                          ${PROJECT_BINARY_DIR}/src_generated/ua_types_generated_encoding_binary.h
                    PRE_BUILD
-                   COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/generate_datatypes.py --typedescriptions ${PROJECT_SOURCE_DIR}/tools/schema/NodeIds.csv 0 ${PROJECT_SOURCE_DIR}/tools/schema/Opc.Ua.Types.bsd ${PROJECT_BINARY_DIR}/src_generated/ua_types
+                   COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/generate_datatypes.py ${generate_typeintrospection} --typedescriptions ${PROJECT_SOURCE_DIR}/tools/schema/NodeIds.csv 0 ${PROJECT_SOURCE_DIR}/tools/schema/Opc.Ua.Types.bsd ${PROJECT_BINARY_DIR}/src_generated/ua_types
                    DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tools/generate_datatypes.py
                            ${CMAKE_CURRENT_SOURCE_DIR}/tools/schema/Opc.Ua.Types.bsd
                            ${CMAKE_CURRENT_SOURCE_DIR}/tools/schema/NodeIds.csv)
@@ -160,8 +182,9 @@ endif()
 
 add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/ua_transport_generated.c
                           ${PROJECT_BINARY_DIR}/src_generated/ua_transport_generated.h
+                          ${PROJECT_BINARY_DIR}/src_generated/ua_transport_generated_encoding_binary.h
                    PRE_BUILD
-                   COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/generate_datatypes.py --ns0-types-xml ${PROJECT_SOURCE_DIR}/tools/schema/Opc.Ua.Types.bsd 1 ${PROJECT_SOURCE_DIR}/tools/schema/Custom.Opc.Ua.Transport.bsd ${PROJECT_BINARY_DIR}/src_generated/ua_transport
+                   COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/generate_datatypes.py ${generate_typeintrospection} --ns0-types-xml ${PROJECT_SOURCE_DIR}/tools/schema/Opc.Ua.Types.bsd 1 ${PROJECT_SOURCE_DIR}/tools/schema/Custom.Opc.Ua.Transport.bsd ${PROJECT_BINARY_DIR}/src_generated/ua_transport
                    DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tools/generate_datatypes.py
                            ${CMAKE_CURRENT_SOURCE_DIR}/tools/schema/Custom.Opc.Ua.Transport.bsd)
 
@@ -219,9 +242,9 @@ if(ENABLE_EXTERNAL_NAMESPACES)
 endif()
 
 ## enable dynamic nodeset
-option(ENABLE_ADDNODES "Enable dynamic addition of nodes" ON)
-if(ENABLE_ADDNODES)
-    add_definitions(-DENABLE_ADDNODES )
+option(ENABLE_NODEMANAGEMENT "Enable dynamic addition and removal of nodes" ON)
+if(ENABLE_NODEMANAGEMENT)
+    add_definitions(-DENABLE_NODEMANAGEMENT)
 endif()
 
 ## set the precompiler flags
@@ -234,12 +257,17 @@ if(EXTENSION_UDP)
 	message(STATUS "Extensions: enabling udp")
 	add_definitions(-DEXTENSION_UDP)
 	add_executable(exampleServerUDP $<TARGET_OBJECTS:open62541-object> examples/networklayer_udp.c examples/server_udp.c)
-  target_link_libraries(exampleServerUDP urcu-cds urcu urcu-common)
+  if(ENABLE_MULTITHREADING)
+  	target_link_libraries(exampleServerUDP urcu-cds urcu urcu-common)
+  endif()
   if (NOT APPLE)
       target_link_libraries(exampleServerUDP rt)
   endif()
 endif()
 
+option(ENABLE_LEGACY "Enable LEGACY code" OFF)
+mark_as_advanced(ENABLE_LEGACY)
+
 option(EXTENSION_STATELESS "Enable stateless extension" OFF)
 if(EXTENSION_STATELESS)
 	message(STATUS "Extensions: enabling stateless")
@@ -268,8 +296,8 @@ else()
     include_directories(${PROJECT_SOURCE_DIR}/include)
     include_directories(${PROJECT_SOURCE_DIR}/deps)
     include_directories(${PROJECT_SOURCE_DIR}/src)
-    include_directories(${PROJECT_BINARY_DIR}/src_generated)
 endif()
+include_directories(${PROJECT_BINARY_DIR}/src_generated) #needed to locate an include of examples/server_nodeset.c
 target_compile_definitions(open62541-object PRIVATE -DUA_DYNAMIC_LINKING)
 
 add_library(open62541 SHARED $<TARGET_OBJECTS:open62541-object>)
@@ -286,13 +314,6 @@ if(ENABLE_MULTITHREADING)
   target_link_libraries(open62541-static urcu-cds urcu urcu-common pthread)
 endif()
 
-# build language bindings for the library
-option(ENABLE_BINDING_LUA "Build Lua bindings" OFF)
-option(ENABLE_BINDING_PYTHON "Build Python bindings" OFF)
-if(ENABLE_BINDING_LUA OR ENABLE_BINDING_PYTHON)
-    add_subdirectory(bindings)
-endif()
-
 # coverage
 option(ENABLE_COVERAGE "Enable gcov coverage" OFF)
 if(ENABLE_COVERAGE)
@@ -361,12 +382,16 @@ if(BUILD_EXAMPLECLIENT)
         target_link_libraries(client_static urcu-cds urcu urcu-common pthread)
         target_link_libraries(client urcu-cds urcu urcu-common pthread)
     endif()
-    if(EXTENSION_STATELESS)
+    if(EXTENSION_STATELESS AND NOT ENABLE_AMALGAMATION)
         add_executable(client_stateless examples/client_stateless.c ${client_source})
         if(ENABLE_MULTITHREADING)
             target_link_libraries(client_stateless urcu-cds urcu urcu-common pthread)
         endif()
     endif()
+    if(ENABLE_LEGACY AND NOT ENABLE_AMALGAMATION)
+    	add_executable(client_legacy ${PROJECT_SOURCE_DIR}/examples/client_legacy.c $<TARGET_OBJECTS:open62541-object>)
+		target_link_libraries(client_legacy ${LIBS})
+	endif()
 endif()
 
 # build unit tests
@@ -394,13 +419,16 @@ if(BUILD_EXAMPLES)
 	if(ENABLE_MULTITHREADING)
 		list(APPEND LIBS urcu-cds urcu urcu-common)
 	endif(ENABLE_MULTITHREADING)
-
+	
     add_executable(server_variable ${PROJECT_SOURCE_DIR}/examples/server_variable.c $<TARGET_OBJECTS:open62541-object>)
 	target_link_libraries(server_variable ${LIBS})
 
 	add_executable(server_datasource ${PROJECT_SOURCE_DIR}/examples/server_datasource.c $<TARGET_OBJECTS:open62541-object>)
 	target_link_libraries(server_datasource ${LIBS})
 
+	# add_executable(server_readspeed ${PROJECT_SOURCE_DIR}/examples/server_readspeed.c $<TARGET_OBJECTS:open62541-object>)
+	# target_link_libraries(server_readspeed ${LIBS})
+
 	add_executable(server_firstSteps ${PROJECT_SOURCE_DIR}/examples/server_firstSteps.c $<TARGET_OBJECTS:open62541-object>)
 	target_link_libraries(server_firstSteps ${LIBS})
 
@@ -410,23 +438,21 @@ if(BUILD_EXAMPLES)
 	add_executable(server_repeated_job ${PROJECT_SOURCE_DIR}/examples/server_repeated_job.c $<TARGET_OBJECTS:open62541-object>)
 	target_link_libraries(server_repeated_job ${LIBS})
 
-	if(NOT ENABLE_AMALGAMATION AND NOT MSVC)
-		add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/nodeset.h ${PROJECT_BINARY_DIR}/src_generated/nodeset.c
-				       PRE_BUILD
-				       COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/generate_open62541CCode.py -i ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/NodeID_Blacklist_FullNS0.txt ${PROJECT_SOURCE_DIR}/tools/schema/namespace0/Opc.Ua.NodeSet2.xml ${PROJECT_SOURCE_DIR}/examples/server_nodeset.xml ${PROJECT_BINARY_DIR}/src_generated/nodeset
-				       DEPENDS ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/generate_open62541CCode.py
-				               ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/logger.py
-				               ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/open62541_MacroHelper.py
-				               ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/ua_builtin_types.py
-				               ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/ua_constants.py
-				               ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/ua_namespace.py
-				               ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/ua_node_types.py
-				               ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/NodeID_Blacklist_FullNS0.txt
-				               ${PROJECT_SOURCE_DIR}/examples/server_nodeset.xml)
-				       
-		add_executable(server_nodeset ${PROJECT_SOURCE_DIR}/examples/server_nodeset.c ${PROJECT_BINARY_DIR}/src_generated/nodeset.c $<TARGET_OBJECTS:open62541-object>)
-		target_link_libraries(server_nodeset ${LIBS})
-	endif()
+	add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/nodeset.h ${PROJECT_BINARY_DIR}/src_generated/nodeset.c
+					   PRE_BUILD
+					   COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/generate_open62541CCode.py -i ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/NodeID_Blacklist_FullNS0.txt ${PROJECT_SOURCE_DIR}/tools/schema/namespace0/Opc.Ua.NodeSet2.xml ${PROJECT_SOURCE_DIR}/examples/server_nodeset.xml ${PROJECT_BINARY_DIR}/src_generated/nodeset
+					   DEPENDS ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/generate_open62541CCode.py
+					           ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/logger.py
+					           ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/open62541_MacroHelper.py
+					           ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/ua_builtin_types.py
+					           ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/ua_constants.py
+					           ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/ua_namespace.py
+					           ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/ua_node_types.py
+					           ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/NodeID_Blacklist_FullNS0.txt
+					           ${PROJECT_SOURCE_DIR}/examples/server_nodeset.xml)
+					   
+	add_executable(server_nodeset ${PROJECT_SOURCE_DIR}/examples/server_nodeset.c ${PROJECT_BINARY_DIR}/src_generated/nodeset.c $<TARGET_OBJECTS:open62541-object>)
+	target_link_libraries(server_nodeset ${LIBS})
 
 	if(ENABLE_METHODCALLS)
 	  add_executable(server_method ${PROJECT_SOURCE_DIR}/examples/server_method.c $<TARGET_OBJECTS:open62541-object>)

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 23 - 11
README.md


+ 1 - 1
appveyor.yml

@@ -2,7 +2,7 @@ version: '{build}'
 os: Visual Studio 2015 RC
 clone_folder: c:\projects\open62541
 before_build:
-- python -m pip install lxml
+- set STATICBUILD=true && python -m pip install lxml
 build_script:
 - cd c:\projects\open62541
 - md build

+ 0 - 39
bindings/CMakeLists.txt

@@ -1,39 +0,0 @@
-FIND_PACKAGE(SWIG REQUIRED)
-INCLUDE(UseSWIG)
-
-if(ENABLE_BINDING_LUA)
-    FIND_PACKAGE(Lua51)
-    file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/lua)
-    add_custom_command (OUTPUT ${PROJECT_BINARY_DIR}/lua/lua_wrap.c 
-                        COMMAND ${SWIG_EXECUTABLE} -lua
-                        -I${PROJECT_SOURCE_DIR}/include
-                        -o ${PROJECT_BINARY_DIR}/lua/lua_wrap.c
-                        ${PROJECT_SOURCE_DIR}/bindings/open62541.i
-                        DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/open62541.i)
-    add_library(open62541-lua SHARED ${PROJECT_BINARY_DIR}/lua/lua_wrap.c)
-    set_target_properties(open62541-lua PROPERTIES COMPILE_FLAGS "-Wno-error")
-    target_include_directories(open62541-lua PUBLIC ${LUA_INCLUDE_DIR} ${PROJECT_SOURCE_DIR}/include
-                                                    ${PROJECT_BINARY_DIR}/src_generated)
-    target_link_libraries(open62541-lua open62541 lua)
-    set_target_properties(open62541-lua PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lua
-                                                   OUTPUT_NAME open62541 PREFIX "")
-endif()
-
-if(ENABLE_BINDING_PYTHON)
-    FIND_PACKAGE(PythonLibs)
-    file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/python)
-    add_custom_command (OUTPUT ${PROJECT_BINARY_DIR}/python/python_wrap.c 
-                        COMMAND ${SWIG_EXECUTABLE} -python
-                        -I${PROJECT_SOURCE_DIR}/include
-                        -o ${PROJECT_BINARY_DIR}/python/python_wrap.c
-                        ${PROJECT_SOURCE_DIR}/bindings/open62541.i
-                        DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/open62541.i)
-    add_library(open62541-python SHARED ${PROJECT_BINARY_DIR}/python/python_wrap.c)
-    set_target_properties(open62541-python PROPERTIES COMPILE_FLAGS "-Wno-error")
-    target_include_directories(open62541-python PUBLIC ${PYTHON_INCLUDE_DIR} ${PROJECT_SOURCE_DIR}/include
-                                                       ${PROJECT_BINARY_DIR}/src_generated)
-    target_link_libraries(open62541-python open62541 python)
-    set_target_properties(open62541-python PROPERTIES
-                                           LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/python
-                                           OUTPUT_NAME _open62541 PREFIX "")
-endif()

+ 0 - 64
bindings/open62541.i

@@ -1,64 +0,0 @@
-%module open62541
-%{
-#include "ua_types.h"
-#include "ua_server.h"
-%}
-
-#if defined(SWIGPYTHON)
-%{
-/* avoid no previous prototype warning/error */
-#if PY_VERSION_HEX >= 0x03000000
-PyObject*
-#else
-void
-#endif
-SWIG_init (void);
-%}
-#endif
-
-#if defined(SWIGLUA)
-%{
-/* avoid no previous prototype warning/error */
-SWIGEXPORT int SWIG_init (lua_State* L);
-%}
-#endif
-
-%define UA_TYPE_HANDLING_FUNCTIONS_SWIG(TYPE)
-    TYPE * TYPE##_new(void);
-    void TYPE##_init(TYPE * p);
-    void TYPE##_delete(TYPE * p);
-    void TYPE##_deleteMembers(TYPE * p);
-    UA_StatusCode TYPE##_copy(const TYPE *src, TYPE *dst);
-%enddef
-
-%define UA_EXPORT
-%enddef
-
-UA_TYPE_HANDLING_FUNCTIONS_SWIG(UA_Boolean)
-UA_TYPE_HANDLING_FUNCTIONS_SWIG(UA_SByte)
-UA_TYPE_HANDLING_FUNCTIONS_SWIG(UA_Byte)
-UA_TYPE_HANDLING_FUNCTIONS_SWIG(UA_Int16)
-UA_TYPE_HANDLING_FUNCTIONS_SWIG(UA_UInt16)
-UA_TYPE_HANDLING_FUNCTIONS_SWIG(UA_Int32)
-UA_TYPE_HANDLING_FUNCTIONS_SWIG(UA_UInt32)
-UA_TYPE_HANDLING_FUNCTIONS_SWIG(UA_Int64)
-UA_TYPE_HANDLING_FUNCTIONS_SWIG(UA_UInt64)
-UA_TYPE_HANDLING_FUNCTIONS_SWIG(UA_Float)
-UA_TYPE_HANDLING_FUNCTIONS_SWIG(UA_Double)
-UA_TYPE_HANDLING_FUNCTIONS_SWIG(UA_String)
-UA_TYPE_HANDLING_FUNCTIONS_SWIG(UA_DateTime)
-UA_TYPE_HANDLING_FUNCTIONS_SWIG(UA_Guid)
-UA_TYPE_HANDLING_FUNCTIONS_SWIG(UA_ByteString)
-UA_TYPE_HANDLING_FUNCTIONS_SWIG(UA_XmlElement)
-UA_TYPE_HANDLING_FUNCTIONS_SWIG(UA_NodeId)
-UA_TYPE_HANDLING_FUNCTIONS_SWIG(UA_ExpandedNodeId)
-UA_TYPE_HANDLING_FUNCTIONS_SWIG(UA_StatusCode)
-UA_TYPE_HANDLING_FUNCTIONS_SWIG(UA_QualifiedName)
-UA_TYPE_HANDLING_FUNCTIONS_SWIG(UA_LocalizedText)
-UA_TYPE_HANDLING_FUNCTIONS_SWIG(UA_ExtensionObject)
-UA_TYPE_HANDLING_FUNCTIONS_SWIG(UA_DataValue)
-UA_TYPE_HANDLING_FUNCTIONS_SWIG(UA_Variant)
-UA_TYPE_HANDLING_FUNCTIONS_SWIG(UA_DiagnosticInfo)
-
-%include "ua_types.h"
-%include "ua_server.h"

+ 35 - 0
deps/libc_string.c

@@ -0,0 +1,35 @@
+/*
+ * 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 <stddef.h>
+
+void *memcpy(void * UA_RESTRICT dest, const void *UA_RESTRICT src, size_t n) {
+	unsigned char *d = dest;
+	const unsigned char *s = src;
+	while(n--)
+        *d++ = *s++;
+	return dest;
+}
+
+void *memset(void *dest, int c, size_t n) {
+    unsigned char c8 = n;
+    unsigned char *target = dest;
+    while(n--)
+        *target++ = c8;
+    return dest;
+}
+
+size_t strlen(const char *str) {
+    size_t len = 0;
+    for(const char *s = str; *s; s++)
+        len += 1;
+    return len;
+}
+
+int memcmp(const void *vl, const void *vr, size_t n) {
+    const unsigned char *l = vl, *r = vr;
+    for(; n && *l == *r; n--, l++, r++);
+    return n ? *l-*r : 0;
+}

+ 116 - 0
deps/pcg_basic.c

@@ -0,0 +1,116 @@
+/*
+ * PCG Random Number Generation for C.
+ *
+ * Copyright 2014 Melissa O'Neill <oneill@pcg-random.org>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * For additional information about the PCG random number generation scheme,
+ * including its license and other licensing options, visit
+ *
+ *       http://www.pcg-random.org
+ */
+
+/*
+ * This code is derived from the full C implementation, which is in turn
+ * derived from the canonical C++ PCG implementation. The C++ version
+ * has many additional features and is preferable if you can use C++ in
+ * your project.
+ */
+
+#include "pcg_basic.h"
+
+// state for global RNGs
+
+static pcg32_random_t pcg32_global = PCG32_INITIALIZER;
+
+// pcg32_srandom(initstate, initseq)
+// pcg32_srandom_r(rng, initstate, initseq):
+//     Seed the rng.  Specified in two parts, state initializer and a
+//     sequence selection constant (a.k.a. stream id)
+
+void pcg32_srandom_r(pcg32_random_t* rng, uint64_t initstate, uint64_t initseq)
+{
+    rng->state = 0U;
+    rng->inc = (initseq << 1u) | 1u;
+    pcg32_random_r(rng);
+    rng->state += initstate;
+    pcg32_random_r(rng);
+}
+
+void pcg32_srandom(uint64_t seed, uint64_t seq)
+{
+    pcg32_srandom_r(&pcg32_global, seed, seq);
+}
+
+// pcg32_random()
+// pcg32_random_r(rng)
+//     Generate a uniformly distributed 32-bit random number
+
+uint32_t pcg32_random_r(pcg32_random_t* rng)
+{
+    uint64_t oldstate = rng->state;
+    rng->state = oldstate * 6364136223846793005ULL + rng->inc;
+    uint32_t xorshifted = ((oldstate >> 18u) ^ oldstate) >> 27u;
+    uint32_t rot = oldstate >> 59u;
+    return (xorshifted >> rot) | (xorshifted << ((-rot) & 31));
+}
+
+uint32_t pcg32_random()
+{
+    return pcg32_random_r(&pcg32_global);
+}
+
+
+// pcg32_boundedrand(bound):
+// pcg32_boundedrand_r(rng, bound):
+//     Generate a uniformly distributed number, r, where 0 <= r < bound
+
+uint32_t pcg32_boundedrand_r(pcg32_random_t* rng, uint32_t bound)
+{
+    // To avoid bias, we need to make the range of the RNG a multiple of
+    // bound, which we do by dropping output less than a threshold.
+    // A naive scheme to calculate the threshold would be to do
+    //
+    //     uint32_t threshold = 0x100000000ull % bound;
+    //
+    // but 64-bit div/mod is slower than 32-bit div/mod (especially on
+    // 32-bit platforms).  In essence, we do
+    //
+    //     uint32_t threshold = (0x100000000ull-bound) % bound;
+    //
+    // because this version will calculate the same modulus, but the LHS
+    // value is less than 2^32.
+
+    uint32_t threshold = -bound % bound;
+
+    // Uniformity guarantees that this loop will terminate.  In practice, it
+    // should usually terminate quickly; on average (assuming all bounds are
+    // equally likely), 82.25% of the time, we can expect it to require just
+    // one iteration.  In the worst case, someone passes a bound of 2^31 + 1
+    // (i.e., 2147483649), which invalidates almost 50% of the range.  In 
+    // practice, bounds are typically small and only a tiny amount of the range
+    // is eliminated.
+    for (;;) {
+        uint32_t r = pcg32_random_r(rng);
+        if (r >= threshold)
+            return r % bound;
+    }
+}
+
+
+uint32_t pcg32_boundedrand(uint32_t bound)
+{
+    return pcg32_boundedrand_r(&pcg32_global, bound);
+}
+

+ 78 - 0
deps/pcg_basic.h

@@ -0,0 +1,78 @@
+/*
+ * PCG Random Number Generation for C.
+ *
+ * Copyright 2014 Melissa O'Neill <oneill@pcg-random.org>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * For additional information about the PCG random number generation scheme,
+ * including its license and other licensing options, visit
+ *
+ *     http://www.pcg-random.org
+ */
+
+/*
+ * This code is derived from the full C implementation, which is in turn
+ * derived from the canonical C++ PCG implementation. The C++ version
+ * has many additional features and is preferable if you can use C++ in
+ * your project.
+ */
+
+#ifndef PCG_BASIC_H_INCLUDED
+#define PCG_BASIC_H_INCLUDED 1
+
+#include <inttypes.h>
+
+#if __cplusplus
+extern "C" {
+#endif
+
+struct pcg_state_setseq_64 {    // Internals are *Private*.
+    uint64_t state;             // RNG state.  All values are possible.
+    uint64_t inc;               // Controls which RNG sequence (stream) is
+                                // selected. Must *always* be odd.
+};
+typedef struct pcg_state_setseq_64 pcg32_random_t;
+
+// If you *must* statically initialize it, here's one.
+
+#define PCG32_INITIALIZER   { 0x853c49e6748fea9bULL, 0xda3e39cb94b95bdbULL }
+
+// pcg32_srandom(initstate, initseq)
+// pcg32_srandom_r(rng, initstate, initseq):
+//     Seed the rng.  Specified in two parts, state initializer and a
+//     sequence selection constant (a.k.a. stream id)
+
+void pcg32_srandom(uint64_t initstate, uint64_t initseq);
+void pcg32_srandom_r(pcg32_random_t* rng, uint64_t initstate,
+                     uint64_t initseq);
+
+// pcg32_random()
+// pcg32_random_r(rng)
+//     Generate a uniformly distributed 32-bit random number
+
+uint32_t pcg32_random(void);
+uint32_t pcg32_random_r(pcg32_random_t* rng);
+
+// pcg32_boundedrand(bound):
+// pcg32_boundedrand_r(rng, bound):
+//     Generate a uniformly distributed number, r, where 0 <= r < bound
+
+uint32_t pcg32_boundedrand(uint32_t bound);
+uint32_t pcg32_boundedrand_r(pcg32_random_t* rng, uint32_t bound);
+
+#if __cplusplus
+}
+#endif
+
+#endif // PCG_BASIC_H_INCLUDED

+ 122 - 9
doc/building.rst

@@ -35,18 +35,20 @@ Building with CMake on Ubuntu or Debian
    ccmake ..
    make
 
-Building with CMake on Windows (Visual Studio)
-----------------------------------------------
+Building with CMake on Windows
+------------------------------
 
-- Get and install Python 2.7.x (Python 3.x should work, too) and CMake: https://python.org/downloads, http://www.cmake.org/cmake/resources/software.html
-- Get and install Visual Studio 2015 Preview: https://www.visualstudio.com/downloads/visual-studio-2015-ctp-vs
-- Download the open62541 sources (using git or as a zipfile from github)
-- Open a command shell (cmd) with Administrator rights and run
-
-.. code-block:: bat
+Here we explain the build process for Visual Studio. To build with MinGW, just
+replace the compiler selection in the call to CMake.
 
-   <path-to-python>\Scripts\pip.exe install lxml
+- Download and install
 
+  - Python 2.7.x (Python 3.x should work, too): https://python.org/downloads
+  - Python lxml: https://pypi.python.org/pypi/lxml
+  - CMake: http://www.cmake.org/cmake/resources/software.html
+  - Microsoft Visual Studio 2015 Community Edition: https://www.visualstudio.com/products/visual-studio-community-vs
+    
+- Download the open62541 sources (using git or as a zipfile from github)
 - Open a command shell (cmd) and run
 
 .. code-block:: bat
@@ -58,7 +60,118 @@ Building with CMake on Windows (Visual Studio)
    :: You can use use cmake-gui for a graphical user-interface to select single features
 
 - Then open "build\open62541.sln" in Visual Studio 2015 and build as usual
+
+Building on OS X
+----------------
+
+- Download and install
+
+  - Xcode: https://itunes.apple.com/us/app/xcode/id497799835?ls=1&mt=12
+  - Homebrew: http://brew.sh/
+  - Pip (a package manager for python, may be preinstalled): ``sudo easy_install pip``
+
+- Run the following in a shell
+
+.. code-block:: bash
+
+   brew install cmake
+   brew install libxml2
+   pip install lxml
+   brew install check # for unit tests
+   brew install userspace-rcu # for multi-threading support
+   brew install graphviz doxygen # for documentation generation
+   pip install sphinx # for documentation generation
+
+Follow Ubuntu instructions without the ``apt-get`` commands as these are taken care of by the above packages.
    
 Build Options
 -------------
 
+Generic CMake options
+~~~~~~~~~~~~~~~~~~~~~
+
+**CMAKE_BUILD_TYPE**
+  - RelWithDebInfo: -O2 optimization with debug symbols
+  - Release: -O2 optimization without debug symbols
+  - Debug: -O0 optimization with debug symbols
+  - MinSizeRel: -Os optimization without debug symbols
+
+BUILD_* group
+~~~~~~~~~~~~~
+
+By default only the shared object libopen62541.so or the library open62541.dll
+and open62541.dll.a resp. open62541.lib are build. Additional artifacts can be
+specified by the following options:
+
+**BUILD_DOCUMENTATION**
+   Generate documentation with doxygen
+**BUILD_EXAMPLECLIENT**
+   Compile example clients from client.c. There are a static and a dynamic binary client and client_static, respectively
+**BUILD_EXAMPLESERVER**
+   Compile example server from server.c There are a static and a dynamic binary server and server_static, respectively
+**BUILD_UNIT_TESTS**
+   Compile unit tests with Check framework. The tests can be executed with make test
+**BUILD_EXAMPLES**
+   Compile specific examples from https://github.com/acplt/open62541/blob/master/examples/
+
+ENABLE_* group
+~~~~~~~~~~~~~~
+
+This group contains build options related to the supported OPC UA features.
+
+**ENABLE_NODEMANAGEMENT**
+   Node management services (adding and removing nodes and references) in server and client
+**ENABLE_AMALGAMATION**
+   Compile a single-file release files open62541.c and open62541.h
+**ENABLE_COVERAGE**
+   Measure the coverage of unit tests
+**ENABLE_EXTERNAL_NAMESPACES**
+   Enable external namespaces in server
+**ENABLE_GNERATE_NAMESPACE0**
+   Enable automatic generation of NS0
+**GENERATE_NAMESPACE0_FILE**
+   File for NS0 generation from namespace0 folder. Default value is Opc.Ua.NodeSet2.xml
+**ENABLE_METHODCALL**
+   Enable method calls in server and client
+**ENABLE_MULTITHREADING**
+   Enable multi-threading support (experimental)
+**ENABLE_SUBSCRIPTIONS**
+   Enable subscriptions
+**GENERATE_SELFSIGNED**
+   Generate a self-signed certificate for the server (openSSL required)
+
+EXTENSION_* group
+~~~~~~~~~~~~~~~~~
+
+Extensions mark experimental extensions that are not compliant with IEC 62541 or
+other OPC UA clients
+
+**EXTENSION_STATELESS**
+   Stateless service calls
+**EXTENSION_UDP**
+   UDP network layer
+
+UA_* group
+~~~~~~~~~~
+
+**UA_LOGLEVEL**
+   The level of logging events that are reported
+   - 600: Fatal and all below
+   - 500: Error and all below
+   - 400: Error and all below
+   - 300: Info and all below
+   - 200: Debug and all below
+   - 100: Trace and all below
+
+C precompiler configuration options
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Options that are not inherited from the CMake configuration are defined in
+ua_config.h. Usually there is no need to adjust them.
+
+**UA_NON_LITTLEENDIAN_ARCHITECTURE**
+   Big-endian or mixed endian platform
+**UA_MIXED_ENDIAN**
+   Mixed-endian platform (e.g., ARM7TDMI)
+**UA_ALIGNED_MEMORY_ACCESS**
+   Platform with aligned memory access only (some ARM processors, e.g. Cortex M3/M4 ARM7TDMI etc.)

+ 601 - 143
doc/datatypes.rst

@@ -1,219 +1,677 @@
 Data Types
 ==========
 
-Generic Data Type Handling
---------------------------
+Introduction
+------------
+
+In open62541, all data types share the same basic API for creation, copying and
+deletion. The following functions are present for all 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* if it succeeded.
+``void T_deleteMembers(T *ptr)``
+  Delete the dynamically allocated content of the data type, but not the data type itself.
+``void T_delete(T *ptr)``
+  Delete the content of the data type and the memory for the data type itself.
+
+The builtin data types
+----------------------
+
+OPC UA defines 25 builtin data types. All other data types are combinations of
+the 25 builtin data types.
+
+UA_Boolean
+^^^^^^^^^^
 
-All data types are combinations of the 25 builtin data types show below. Types
-are described in the UA_DataType structure.
+A two-state logical value (true or false).
 
-.. doxygenstruct:: UA_DataType
-   :members:
+.. code-block:: c
 
-.. c:var:: const UA_DataType UA_TYPES[UA_TYPES_COUNT]
+  typedef bool UA_Boolean;
+  #define UA_TRUE true
+  #define UA_FALSE false
 
-  The datatypes defined in the standard are stored in the ``UA_TYPES`` array.
-  A typical function call is ``UA_Array_new(&data_ptr, 20, &UA_TYPES[UA_TYPES_STRING])``.
+UA_SByte
+^^^^^^^^
 
-.. doxygenfunction:: UA_new
-.. doxygenfunction:: UA_init
-.. doxygenfunction:: UA_copy
-.. doxygenfunction:: UA_deleteMembers
-.. doxygenfunction:: UA_delete
+An integer value between -128 and 127.
 
-For all datatypes, there are also macros with syntactic sugar over calling the
-generic functions with a pointer into the ``UA_TYPES`` array.
+.. code-block:: c
 
-.. c:function:: <typename>_new()
+  typedef int8_t UA_SByte;
 
-  Allocates the memory for the type and runs _init on the returned variable.
-  Returns null if no memory could be allocated.
+UA_Byte
+^^^^^^^
 
-.. c:function:: <typename>_init(<typename> *value)
+An integer value between 0 and 256.
 
-  Sets all members of the type to a default value, usually zero. Arrays (e.g.
-  for strings) are set to a length of -1.
+.. code-block:: c
 
-.. c:function:: <typename>_copy(<typename> *src, <typename> *dst)
+  typedef uint8_t UA_Byte;
 
-  Copies a datatype. This performs a deep copy iterating over the members.
-  Copying into variants with an external data source is not permitted. If
-  copying fails, a deleteMembers is performed and an error code returned.
+UA_Int16
+^^^^^^^^
 
-.. c:function:: <typename>_deleteMembers(<typename> *value)
+An integer value between -32,768 and 32,767.
 
-   Frees the memory of dynamically sized members of a datatype (e.g. arrays).
+.. code-block:: c
 
-.. c:function:: <typename>_delete(<typename> *value)
+  typedef int16_t UA_Int16;
 
-   Frees the memory of the datatype and its dynamically allocated members.
+UA_UInt16
+^^^^^^^^^
 
-Array Handling
---------------
-   
-.. doxygenfunction:: UA_Array_new
-.. doxygenfunction:: UA_Array_copy
-.. doxygenfunction:: UA_Array_delete
+An integer value between 0 and 65,535.
 
-Builtin Data Types
-------------------
+.. code-block:: c
 
-Number-Types
-^^^^^^^^^^^^
+  typedef uint16_t UA_UInt16;
 
-.. doxygentypedef:: UA_Boolean
-.. doxygentypedef:: UA_SByte
-.. doxygentypedef:: UA_Byte
-.. doxygentypedef:: UA_Int16
-.. doxygentypedef:: UA_UInt16
-.. doxygentypedef:: UA_Int32
-.. doxygentypedef:: UA_UInt32
-.. doxygentypedef:: UA_Int64
-.. doxygentypedef:: UA_UInt64
-.. doxygentypedef:: UA_Float
-.. doxygentypedef:: UA_Double
+UA_Int32
+^^^^^^^^
 
-UA_String
+An integer value between -2,147,483,648 and 2,147,483,647.
+
+.. code-block:: c
+
+  typedef int32_t UA_Int32;
+
+UA_UInt32
+^^^^^^^^^
+
+An integer value between 0 and 4,294,967,295.
+
+.. code-block:: c
+
+  typedef uint32_t UA_UInt32;
+
+UA_Int64
+^^^^^^^^
+
+An integer value between -10,223,372,036,854,775,808 and 9,223,372,036,854,775,807.
+
+.. code-block:: c
+
+  typedef int64_t UA_Int64;
+
+UA_UInt64
 ^^^^^^^^^
-.. doxygenstruct:: UA_String
-  :members:
 
-.. c:macro:: UA_STRING_NULL
+An integer value between 0 and 18,446,744,073,709,551,615.
 
-  The empty string
+.. code-block:: c
 
-.. c:macro:: UA_STRING(CHARS)
-     
-  Creates an UA_String from an array of ``char``. The characters are not copied
-  on the heap. Instead, the string points into the existing array.
+  typedef uint64_t UA_UInt64;
 
-.. c:macro:: UA_STRING_ALLOC(CHARS)
-     
-  Creates an UA_String from an array of ``char``. The characters are copied on
-  the heap.
+UA_Float
+^^^^^^^^
 
-.. doxygenfunction:: UA_String_equal
-.. doxygenfunction:: UA_String_copyprintf
+An IEEE single precision (32 bit) floating point value.
+
+.. code-block:: c
+
+  typedef float UA_Float;
+
+UA_Double
+^^^^^^^^^
+
+An IEEE double precision (64 bit) floating point value.
+
+.. code-block:: c
+
+  typedef double UA_Double;
 
 UA_DateTime
 ^^^^^^^^^^^
-.. doxygentypedef:: UA_DateTime
-.. doxygenfunction:: UA_DateTime_now(void)
-.. doxygenfunction:: UA_DateTime_toString
-.. doxygenfunction:: UA_DateTime_toStruct
+
+An instance in time. A DateTime value is encoded as a 64-bit signed integer
+which represents the number of 100 nanosecond intervals since January 1, 1601
+(UTC).
+
+.. code-block:: c
+
+  typedef UA_Int64 UA_DateTime;
+
+The following functions and definitions are used with UA_DateTime.
+
+.. code-block:: c
+
+  UA_DateTime UA_DateTime_now(void);
+
+  typedef struct UA_DateTimeStruct {
+      UA_UInt16 nanoSec;
+      UA_UInt16 microSec;
+      UA_UInt16 milliSec;
+      UA_UInt16 sec;
+      UA_UInt16 min;
+      UA_UInt16 hour;
+      UA_UInt16 day;
+      UA_UInt16 month;
+      UA_UInt16 year;
+  } UA_DateTimeStruct;
+
+  UA_DateTimeStruct UA_EXPORT UA_DateTime_toStruct(UA_DateTime time);
+
+  UA_String UA_EXPORT UA_DateTime_toString(UA_DateTime time);
 
 UA_Guid
 ^^^^^^^
-.. doxygenstruct:: UA_Guid
-.. doxygenfunction:: UA_Guid_equal
-.. doxygenfunction:: UA_Guid_random
-   
+
+A 16 byte value that can be used as a globally unique identifier.
+
+.. code-block:: c
+
+  typedef struct {
+      UA_UInt32 data1;
+      UA_UInt16 data2;
+      UA_UInt16 data3;
+      UA_Byte   data4[8];
+  } UA_Guid;
+
+The following functions and definitions are used with UA_Guid.
+
+.. code-block:: c
+
+  UA_Boolean UA_Guid_equal(const UA_Guid *g1, const UA_Guid *g2);
+
+  UA_Guid UA_Guid_random(UA_UInt32 *seed);
+
+UA_String
+^^^^^^^^^
+
+A sequence of Unicode characters. See also the section :ref:`array-handling` for
+the usage of arrays in open62541.
+
+.. code-block:: c
+
+  typedef struct {
+      size_t length; // The length of the string
+      UA_Byte *data; // The string's content (not null-terminated)
+  } UA_String;
+
+The following functions and definitions are used with UA_String.
+
+.. code-block:: c
+
+  extern const UA_String UA_STRING_NULL;
+
+  UA_String UA_STRING(char *chars);
+
+  #define UA_STRING_ALLOC(CHARS) UA_String_fromChars(CHARS)
+    
+  /** Copies the content on the heap. Returns a null-string when alloc fails */
+  UA_String UA_String_fromChars(char const src[]);
+
+  UA_Boolean UA_String_equal(const UA_String *s1, const UA_String *s2);
+
+Here's a small example for the usage of UA_String.
+
+.. code-block:: c
+
+  /* The definition of UA_String copied from ua_types.h */ 
+  typedef struct {
+      size_t length; ///< The length of the string
+      UA_Byte *data; ///< The string's content (not null-terminated)
+  } UA_String;
+
+  UA_String s1 = UA_STRING("test1");       /* s1 points to the statically allocated string buffer */
+  UA_String_init(&s1);                     /* Reset s1 (no memleak due to the statically allocated buffer) */
+  
+  UA_String s2 = UA_STRING_ALLOC("test2"); /* s2 points to a new copy of the string buffer (with malloc) */
+  UA_String_deleteMembers(&s2);            /* Free the content of s2, but not s2 itself */
+  
+  UA_String *s3 = UA_String_new();         /* The string s3 is malloced and initialized */
+  *s3 = UA_STRING_ALLOC("test3");          /* s3 points to a new copy of the string buffer */
+  
+  UA_String s4;
+  UA_copy(s3, &s4);                        /* Copy the content of s3 to s4 */
+  
+  UA_String_delete(s3);                    /* Free the string buffer and the string itself */
+  UA_String_deleteMembers(&s4);            /* Again, delete only the string buffer */
+
 UA_ByteString
 ^^^^^^^^^^^^^
-Bytestring are just a redefinition of strings. The semantic difference is that
-ByteStrings may hold non-UTF8 data.
 
-.. doxygentypedef:: UA_ByteString
+A sequence of octets.
 
-.. c:macro:: UA_BYTESTRING_NULL
+.. code-block:: c
 
-   The empty ByteString
+  typedef UA_String UA_ByteString;
 
-.. c:function:: UA_Boolean UA_ByteString_equal(const UA_ByteString *s1, const UA_ByteString *s2)
+UA_XmlEelement
+^^^^^^^^^^^^^^
 
-   Compares two ByteStrings.
-   
-UA_XmlElement
-^^^^^^^^^^^^^
-XmlElements are just a redefinition of strings.
+An XML element.
+
+.. code-block:: c
 
-.. doxygentypedef:: UA_XmlElement
+  typedef UA_String UA_XmlElement;
 
 UA_NodeId
 ^^^^^^^^^
-.. doxygenstruct:: UA_NodeId
-  :members:
 
-.. doxygenfunction:: UA_NodeId_equal
-.. doxygenfunction:: UA_NodeId_isNull
+An identifier for a node in the address space of an OPC UA Server.
+
+.. code-block:: c
+
+  enum UA_NodeIdType {
+      UA_NODEIDTYPE_NUMERIC    = 0, // On the wire, this can be 0, 1 or 2 (shortened numeric nodeids)
+      UA_NODEIDTYPE_STRING     = 3,
+      UA_NODEIDTYPE_GUID       = 4,
+      UA_NODEIDTYPE_BYTESTRING = 5
+  };
+
+  typedef struct {
+      UA_UInt16 namespaceIndex;
+      enum UA_NodeIdType identifierType;
+      union {
+          UA_UInt32     numeric;
+          UA_String     string;
+          UA_Guid       guid;
+          UA_ByteString byteString;
+      } identifier;
+  } UA_NodeId;
+
+The following functions and definitions are used with UA_NodeId.
+
+.. code-block:: c
+
+  UA_Boolean UA_NodeId_isNull(const UA_NodeId *p);
+
+  UA_Boolean UA_NodeId_equal(const UA_NodeId *n1, const UA_NodeId *n2);
 
-.. c:macro:: UA_NODEID_NULL
+  extern const UA_NodeId UA_NODEID_NULL;
 
-  The null NodeId
+  UA_NodeId UA_NODEID_NUMERIC(UA_UInt16 nsIndex, UA_Int32 identifier);
 
-.. c:macro:: UA_NODEID_NUMERIC(NSID, NUMERICID)
-.. c:macro:: UA_NODEID_STRING(NSID, CHARS)
-.. c:macro:: UA_NODEID_STRING_ALLOC(NSID, CHARS)
-.. c:macro:: UA_NODEID_GUID(NSID, GUID)
-.. c:macro:: UA_NODEID_BYTESTRING(NSID, CHARS)
-.. c:macro:: UA_NODEID_BYTESTRING_ALLOC(NSID, CHARS)
+  UA_NodeId UA_NODEID_STRING(UA_UInt16 nsIndex, char *chars) {
+
+  UA_NodeId UA_NODEID_STRING_ALLOC(UA_UInt16 nsIndex, const char *chars);
+
+  UA_NodeId UA_NODEID_GUID(UA_UInt16 nsIndex, UA_Guid guid);
+
+  UA_NodeId UA_NODEID_BYTESTRING(UA_UInt16 nsIndex, char *chars);
+
+  UA_NodeId UA_NODEID_BYTESTRING_ALLOC(UA_UInt16 nsIndex, const char *chars);
 
 UA_ExpandedNodeId
 ^^^^^^^^^^^^^^^^^
-.. doxygenstruct:: UA_ExpandedNodeId
-  :members:
 
-.. doxygenfunction:: UA_ExpandedNodeId_isNull
-.. c:macro:: UA_EXPANDEDNODEID_NUMERIC(NSID, NUMERICID)
-  
-UA_StatusCode
-^^^^^^^^^^^^^
-Many functions in open62541 return either ``UA_STATUSCODE_GOOD`` or an error code.
+A NodeId that allows the namespace URI to be specified instead of an index.
+
+.. code-block:: c
+
+  typedef struct {
+      UA_NodeId nodeId;
+      UA_String namespaceUri;
+      UA_UInt32 serverIndex;
+  } UA_ExpandedNodeId;
 
-.. doxygentypedef:: UA_StatusCode
+The following functions and definitions are used with UA_ExpandedNodeId.
+
+.. code-block:: c
+
+  UA_ExpandedNodeId UA_EXPANDEDNODEID_NUMERIC(UA_UInt16 nsIndex, UA_Int32 identifier);
+
+  UA_ExpandedNodeId UA_EXPANDEDNODEID_STRING(UA_UInt16 nsIndex, char *chars);
+
+  UA_ExpandedNodeId UA_EXPANDEDNODEID_STRING_ALLOC(UA_UInt16 nsIndex, const char *chars);
+
+  UA_ExpandedNodeId UA_EXPANDEDNODEID_STRING_GUID(UA_UInt16 nsIndex, UA_Guid guid);
+
+  UA_ExpandedNodeId UA_EXPANDEDNODEID_BYTESTRING(UA_UInt16 nsIndex, char *chars);
+
+  UA_ExpandedNodeId UA_EXPANDEDNODEID_BYTESTRING_ALLOC(UA_UInt16 nsIndex, const char *chars);
 
 UA_QualifiedName
-^^^^^^^^^^^^^^^^   
-.. doxygenstruct:: UA_QualifiedName
-  :members:
+^^^^^^^^^^^^^^^^
+
+A name qualified by a namespace.
+
+.. code-block:: c
+
+  typedef struct {
+      UA_UInt16 namespaceIndex;
+      UA_String name;
+  } UA_QualifiedName;
+
+The following functions and definitions are used with UA_QualifiedName.
+
+.. code-block:: c
+
+  UA_QualifiedName UA_QUALIFIEDNAME(UA_UInt16 nsIndex, char *chars);
+
+  UA_QualifiedName UA_QUALIFIEDNAME_ALLOC(UA_UInt16 nsIndex, const char *chars);
 
-.. c:macro:: UA_QUALIFIEDNAME(NSID, CHARS)
-.. c:macro:: UA_QUALIFIEDNAME_ALLOC(NSID, CHARS)
-  
 UA_LocalizedText
 ^^^^^^^^^^^^^^^^
-.. doxygenstruct:: UA_LocalizedText
-  :members:
 
-.. c:macro:: UA_LOCALIZEDTEXT(LOCALE, TEXT)
-   Takes two arrays of ``char`` as the input.
+Human readable text with an optional locale identifier.
 
-.. c:macro:: UA_LOCALIZEDTEXT_ALLOC(LOCALE, TEXT)
-  
-UA_ExtensionObject
-^^^^^^^^^^^^^^^^^^
+.. code-block:: c
 
-.. doxygenstruct:: UA_ExtensionObject
-  :members:
+  typedef struct {
+      UA_String locale;
+      UA_String text;
+  } UA_LocalizedText;
+                
+The following functions and definitions are used with UA_LocalizedText.
 
-UA_DataValue
-^^^^^^^^^^^^
+.. code-block:: c
+
+  UA_LocalizedText UA_LOCALIZEDTEXT(char *locale, char *text);
+
+  UA_LocalizedText UA_LOCALIZEDTEXT_ALLOC(const char *locale, const char *text);
+
+UA_ExtensionObject
+^^^^^^^^^^^^^^^^^^
 
-.. doxygenstruct:: UA_DataValue
-  :members:
-  :undoc-members:
+A structure that contains an application specific data type that may not be
+recognized by the receiver.
+
+.. code-block:: c
+
+  typedef struct {
+      enum {
+          UA_EXTENSIONOBJECT_ENCODED_NOBODY     = 0,
+          UA_EXTENSIONOBJECT_ENCODED_BYTESTRING = 1,
+          UA_EXTENSIONOBJECT_ENCODED_XML        = 2,
+          UA_EXTENSIONOBJECT_DECODED            = 3, ///< There is a pointer to the decoded data
+          UA_EXTENSIONOBJECT_DECODED_NODELETE   = 4  ///< Don't delete the decoded data at the lifecycle end
+      } 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;
 
 UA_Variant
 ^^^^^^^^^^
 
-.. doxygenstruct:: UA_Variant
-  :members:
+Stores (arrays of) any data type. Please see section :ref:`generic-handling` for
+the usage of UA_DataType. The semantics of the arrayLength field is explained in
+section :ref:`array-handling`.
+
+.. code-block:: c
+
+  typedef struct {
+      const UA_DataType *type; // The data type description
+      enum {
+          UA_VARIANT_DATA,          /* The data has the same lifecycle as the variant */
+          UA_VARIANT_DATA_NODELETE, /* The data is "borrowed" by the variant and shall not be
+                                       deleted at the end of the variant's lifecycle. */
+      } 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_UInt32 *arrayDimensions; // The length of each dimension of the data-array
+  } UA_Variant;
+
+  /* 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 {
+      size_t dimensionsSize;
+      struct UA_NumericRangeDimension {
+          UA_UInt32 min;
+          UA_UInt32 max;
+      } *dimensions;
+  } UA_NumericRange;
+
+
+The following functions and definitions are used with UA_Variant.
+
+.. code-block:: c
+
+  /**
+   * Returns true if the variant contains a scalar value. Note that empty
+   * variants contain an array of length -1 (undefined).
+   *
+   * @param v The variant
+   * @return Does the variant contain a scalar value.
+   */
+  UA_Boolean UA_Variant_isScalar(const UA_Variant *v);
+
+  /**
+   * Set the variant to a scalar value that already resides in memory. The value
+   * takes on the lifecycle of the variant and is deleted with it.
+   *
+   * @param v The variant
+   * @param p A pointer to the value data
+   * @param type The datatype of the value in question
+   */
+  UA_Variant_setScalar(UA_Variant *v, void * UA_RESTRICT p, const UA_DataType *type);
+
+  /**
+   * Set the variant to a scalar value that is copied from an existing variable.
+   *
+   * @param v The variant
+   * @param p A pointer to the value data
+   * @param type The datatype of the value
+   * @return Indicates whether the operation succeeded or returns an error code
+   */
+  UA_StatusCode UA_Variant_setScalarCopy(UA_Variant *v, const void *p, const UA_DataType *type);
+
+  /**
+   * Set the variant to an array that already resides in memory. The array takes
+   * on the lifecycle of the variant and is deleted with it.
+   *
+   * @param v The variant
+   * @param array A pointer to the array data
+   * @param arraySize The size of the array
+   * @param type The datatype of the array
+   */
+  void UA_Variant_setArray(UA_Variant *v, void * UA_RESTRICT array,
+                           size_t arraySize, const UA_DataType *type);
+
+  /**
+   * Set the variant to an array that is copied from an existing array.
+   *
+   * @param v The variant
+   * @param array A pointer to the array data
+   * @param arraySize The size of the array
+   * @param type The datatype of the array
+   * @return Indicates whether the operation succeeded or returns an error code
+   */
+  UA_StatusCode UA_Variant_setArrayCopy(UA_Variant *v, const void *array,
+                                        size_t arraySize, const UA_DataType *type);
+
+  /**
+   * Copy the variant, but use only a subset of the (multidimensional) array
+   * into a variant. Returns an error code if the variant is not an array or if
+   * the indicated range does not fit.
+   *
+   * @param src The source variant
+   * @param dst The target variant
+   * @param range The range of the copied data
+   * @return Returns UA_STATUSCODE_GOOD or an error code
+   */
+  UA_StatusCode UA_Variant_copyRange(const UA_Variant *src, UA_Variant *dst,
+                                     const UA_NumericRange range);
+
+  /**
+   * Insert a range of data into an existing variant. The data array can't be
+   * reused afterwards if it contains types without a fixed size (e.g. strings)
+   * since the members are moved into the variant and take on its lifecycle.
+   *
+   * @param v The variant
+   * @param dataArray The data array. The type must match the variant
+   * @param dataArraySize The length of the data array. This is checked to match the range size.
+   * @param range The range of where the new data is inserted
+   * @return Returns UA_STATUSCODE_GOOD or an error code
+   */
+  UA_StatusCode UA_Variant_setRange(UA_Variant *v, void * UA_RESTRICT array,
+                                    size_t arraySize, const UA_NumericRange range);
+
+  /**
+   * Deep-copy a range of data into an existing variant.
+   *
+   * @param v The variant
+   * @param dataArray The data array. The type must match the variant
+   * @param dataArraySize The length of the data array. This is checked to match the range size.
+   * @param range The range of where the new data is inserted
+   * @return Returns UA_STATUSCODE_GOOD or an error code
+   */
+  UA_StatusCode UA_Variant_setRangeCopy(UA_Variant *v, const void *array,
+                                        size_t arraySize, const UA_NumericRange range);
 
-.. doxygenfunction:: UA_Variant_isScalar
-.. doxygenfunction:: UA_Variant_setScalar
-.. doxygenfunction:: UA_Variant_setScalarCopy
-.. doxygenfunction:: UA_Variant_setArray
-.. doxygenfunction:: UA_Variant_setArrayCopy
+UA_DataValue
+^^^^^^^^^^^^
 
-.. doxygenstruct:: UA_NumericRange
-   :undoc-members:
+A data value with an associated status code and timestamps.
+
+.. code-block:: c
+   
+  typedef struct {
+      UA_Boolean    hasValue             : 1;
+      UA_Boolean    hasStatus            : 1;
+      UA_Boolean    hasSourceTimestamp   : 1;
+      UA_Boolean    hasServerTimestamp   : 1;
+      UA_Boolean    hasSourcePicoseconds : 1;
+      UA_Boolean    hasServerPicoseconds : 1;
+      UA_Variant    value;
+      UA_StatusCode status;
+      UA_DateTime   sourceTimestamp;
+      UA_Int16      sourcePicoseconds;
+      UA_DateTime   serverTimestamp;
+      UA_Int16      serverPicoseconds;
+  } UA_DataValue;
 
-.. doxygenfunction:: UA_Variant_setRange
-.. doxygenfunction:: UA_Variant_setRangeCopy
-        
 UA_DiagnosticInfo
 ^^^^^^^^^^^^^^^^^
 
-.. doxygenstruct:: UA_DiagnosticInfo
-  :members:
-  :undoc-members:
+A structure that contains detailed error and diagnostic information associated
+with a StatusCode.
+
+.. code-block:: c
+
+  typedef struct UA_DiagnosticInfo {
+      UA_Boolean    hasSymbolicId          : 1;
+      UA_Boolean    hasNamespaceUri        : 1;
+      UA_Boolean    hasLocalizedText       : 1;
+      UA_Boolean    hasLocale              : 1;
+      UA_Boolean    hasAdditionalInfo      : 1;
+      UA_Boolean    hasInnerStatusCode     : 1;
+      UA_Boolean    hasInnerDiagnosticInfo : 1;
+      UA_Int32      symbolicId;
+      UA_Int32      namespaceUri;
+      UA_Int32      localizedText;
+      UA_Int32      locale;
+      UA_String     additionalInfo;
+      UA_StatusCode innerStatusCode;
+      struct UA_DiagnosticInfo *innerDiagnosticInfo;
+  } UA_DiagnosticInfo;
+
+.. _generic-handling:
+
+Generic Data Type Handling
+--------------------------
+
+All standard-defined data types are described with an ``UA_DataType`` structure.
+In addition to the 25 builtin data types, OPC UA defines many more. But they are
+mere combinations of the builtin data types. We handle all types in a unified
+way by storing their internal structure. So it is not necessary to define
+specialized functions for all additional types.
+
+The ``UA_TYPES`` array contains the description of every standard-defined data
+type.
+
+.. code-block:: c
+
+  extern const UA_DataType UA_TYPES[UA_TYPES_COUNT];
+
+The following is an excerpt from ``ua_types_generated.h`` with the definition of
+OPC UA read requests. This file is auto-generated from the XML-description of
+the OPC UA data types that is part of the ISO/IEC 62541 standard.
+
+.. code-block:: c
+
+  typedef struct {
+      UA_RequestHeader requestHeader;
+      UA_Double maxAge;
+      UA_TimestampsToReturn timestampsToReturn;
+      size_t nodesToReadSize;
+      UA_ReadValueId *nodesToRead;
+  } UA_ReadRequest;
+
+  #define UA_TYPES_READREQUEST 118
+  
+  static UA_INLINE void UA_ReadRequest_init(UA_ReadRequest *p) {
+      memset(p, 0, sizeof(UA_ReadRequest)); }
+
+  static UA_INLINE void UA_ReadRequest_delete(UA_ReadRequest *p) {
+      UA_delete(p, &UA_TYPES[UA_TYPES_READREQUEST]); }
+
+  static UA_INLINE void UA_ReadRequest_deleteMembers(UA_ReadRequest *p) {
+      UA_deleteMembers(p, &UA_TYPES[UA_TYPES_READREQUEST]); }
+
+  static UA_INLINE UA_ReadRequest * UA_ReadRequest_new(void) {
+      return (UA_ReadRequest*) UA_new(&UA_TYPES[UA_TYPES_READREQUEST]); }
+
+  static UA_INLINE UA_StatusCode
+  UA_ReadRequest_copy(const UA_ReadRequest *src, UA_ReadRequest *dst) {
+      return UA_copy(src, dst, &UA_TYPES[UA_TYPES_READREQUEST]); }
+
+.. _array-handling:
+
+Array Handling
+--------------
+
+In OPC UA, all arrays can be undefined, have length 0 or a positive length. In
+data structures, all arrays are represented by a ``size_t`` length field and an
+appropriate pointer directly afterwards. In order to distinguish between
+undefined and length-zero arrays, we define the following.
+
+.. code-block:: c
+
+  #define UA_EMPTY_ARRAY_SENTINEL ((void*)0x01)
+
+- size == 0 and data == NULL: The array is undefined
+- size == 0 and data == ``UA_EMPTY_ARRAY_SENTINEL``: The array has length 0
+- size > 0: The array at the given memory address has the given size
+
+The following functions are defined for array handling.
+
+.. code-block:: c
+
+  /**
+   * 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_Array_new(size_t size, const UA_DataType *type);
+
+  /**
+   * Allocates and copies an array. dst is set to (void*)0 if not enough memory is available.
+   *
+   * @param src The memory location of the source array
+   * @param src_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 whether copying succeeded
+   */
+  UA_StatusCode UA_Array_copy(const void *src, size_t src_size, void **dst,
+                              const UA_DataType *type);
+
+  /**
+   * 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_Array_delete(void *p, size_t size, const UA_DataType *type); 

+ 3 - 3
doc/mainpage.dox

@@ -11,12 +11,12 @@ late, OPC UA is marketed as the one standard for non-realtime industrial communi
 
 We believe that it is best to understand OPC UA <em>from the inside out</em>, 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 </em>open62541</em> implementation of the
+principles are realized in detail. Examples are given based on the <em>open62541</em> implementation of the
 standard.
 
 <h2>OPC UA, a collection of services</h2>
 
-In OPC-UA, all communication is based on service calls, each consisting of a request and a response
+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 <em>Call</em> service to invoke user-defined
 methods on the server.
@@ -96,7 +96,7 @@ open62541, these are defined in ua_types.h.
 <tr> <td>UInt64</td> <td>ExpandedNodeId</td> <td> </td> </tr>
 </table>
 
-The builtin datatypes are combined to more complex structures. When the structure contains an array,
+The builtin datatypes are combined into 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.
 

+ 1 - 1
doc/tutorial_client_firstSteps.rst

@@ -155,7 +155,7 @@ Let us extend the client with with an action reading node's value:
          UA_Variant_isScalar(&rResp.results[0].value) &&
          rResp.results[0].value.type == &UA_TYPES[UA_TYPES_DATETIME]) {
              raw_date = *(UA_DateTime*)rResp.results[0].value.data;
-             printf("raw date is: %llu\n", raw_date);
+             printf("raw date is: %" PRId64 "\n", raw_date);
       }
       
       UA_ReadRequest_deleteMembers(&rReq);

+ 1 - 1
doc/tutorial_noderelations.rst

@@ -247,7 +247,7 @@ After adding you XML file to CMakeLists.txt, rerun cmake in your build directory
   [ 10%] Generating src_generated/ua_transport_generated.c, src_generated/ua_transport_generated.h
   [ 13%] Generating src_generated/ua_namespaceinit_generated.c, src_generated/ua_namespaceinit_generated.h
 
-At this point, the make process will most likely hang for 30-60s until the namespace is parsed, checked, linked and finally generated (be pacient). It sould continue as follows::
+At this point, the make process will most likely hang for 30-60s until the namespace is parsed, checked, linked and finally generated (be patient). It should continue as follows::
   
   Scanning dependencies of target open62541-object
   [ 17%] Building C object CMakeFiles/open62541-object.dir/src/ua_types.c.o

+ 5 - 5
doc/tutorials.rst

@@ -1,7 +1,7 @@
 Tutorials
 =========
 
-This section contains sructured tutorials
+This section contains structured tutorials
    
 **Tutorial 1: First steps with open62541-server**
 
@@ -31,7 +31,7 @@ Contents:
 
 * Reading a variable
 
-* Introduction to stings
+* Introduction to strings
 
 **Tutorial 3: Adding nodes to a server and connecting nodes to user-defined values**
 
@@ -39,13 +39,13 @@ Contents:
 
 Contents:
 
-* Intorduction to Variants
+* Introduction to Variants
 
 * Adding user-defined nodes to a server
 
-* Connecting node to a variable
+* Connecting a node to a variable
 
-* Connecting node to a callback function
+* Connecting a node to a callback function
 
 **Tutorial 4: Adding server-side methods**
 

+ 3 - 3
examples/CMakeLists.txt

@@ -14,6 +14,9 @@ if(ENABLE_MULTITHREADING)
     list(APPEND LIBS urcu-cds urcu urcu-common)
 endif(ENABLE_MULTITHREADING)
 
+# add_executable(server_readspeed server_readspeed.c)
+# target_link_libraries(server_readspeed ${LIBS})
+
 add_executable(server_variable server_variable.c)
 target_link_libraries(server_variable ${LIBS})
 
@@ -26,9 +29,6 @@ target_link_libraries(server_firstSteps ${LIBS})
 add_executable(client_firstSteps client_firstSteps.c)
 target_link_libraries(client_firstSteps ${LIBS})
 
-add_executable(server_repeated_job server_repeated_job.c)
-target_link_libraries(server_repeated_job ${LIBS})
-
 if(NOT ENABLE_AMALGAMATION)
 	add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/nodeset.h ${PROJECT_BINARY_DIR}/src_generated/nodeset.c
 		           PRE_BUILD

+ 96 - 103
examples/client.c

@@ -1,6 +1,7 @@
 #ifdef UA_NO_AMALGAMATION
 # include "ua_types.h"
 # include "ua_client.h"
+# include "ua_client_highlevel.h"
 # include "ua_nodeids.h"
 # include "networklayer_tcp.h"
 # include "logger_stdout.h"
@@ -13,14 +14,13 @@
 
 #include <stdio.h>
 
-void handler_TheAnswerChanged(UA_UInt32 handle, UA_DataValue *value);
-void handler_TheAnswerChanged(UA_UInt32 handle, UA_DataValue *value) {
+static void handler_TheAnswerChanged(UA_UInt32 handle, UA_DataValue *value) {
     printf("The Answer has changed!\n");
     return;
 }
 
 int main(int argc, char *argv[]) {
-    UA_Client *client = UA_Client_new(UA_ClientConfig_standard, Logger_Stdout_new());
+    UA_Client *client = UA_Client_new(UA_ClientConfig_standard, Logger_Stdout);
     UA_StatusCode retval = UA_Client_connect(client, ClientNetworkLayerTCP_connect,
                                              "opc.tcp://localhost:16664");
 
@@ -39,20 +39,21 @@ int main(int argc, char *argv[]) {
     bReq.nodesToBrowse[0].nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER); //browse objects folder
     bReq.nodesToBrowse[0].resultMask = UA_BROWSERESULTMASK_ALL; //return everything
 
-    UA_BrowseResponse bResp = UA_Client_browse(client, &bReq);
+    UA_BrowseResponse bResp = UA_Client_Service_browse(client, bReq);
     printf("%-9s %-16s %-16s %-16s\n", "NAMESPACE", "NODEID", "BROWSE NAME", "DISPLAY NAME");
-    for (int i = 0; i < bResp.resultsSize; ++i) {
-        for (int j = 0; j < bResp.results[i].referencesSize; ++j) {
+    for (size_t i = 0; i < bResp.resultsSize; ++i) {
+        for (size_t j = 0; j < bResp.results[i].referencesSize; ++j) {
             UA_ReferenceDescription *ref = &(bResp.results[i].references[j]);
             if(ref->nodeId.nodeId.identifierType == UA_NODEIDTYPE_NUMERIC) {
                 printf("%-9d %-16d %-16.*s %-16.*s\n", ref->browseName.namespaceIndex,
-                       ref->nodeId.nodeId.identifier.numeric, ref->browseName.name.length,
-                       ref->browseName.name.data, ref->displayName.text.length, ref->displayName.text.data);
+                       ref->nodeId.nodeId.identifier.numeric, (int)ref->browseName.name.length,
+                       ref->browseName.name.data, (int)ref->displayName.text.length,
+                       ref->displayName.text.data);
             } else if(ref->nodeId.nodeId.identifierType == UA_NODEIDTYPE_STRING) {
                 printf("%-9d %-16.*s %-16.*s %-16.*s\n", ref->browseName.namespaceIndex,
-                       ref->nodeId.nodeId.identifier.string.length, ref->nodeId.nodeId.identifier.string.data,
-                       ref->browseName.name.length, ref->browseName.name.data, ref->displayName.text.length,
-                       ref->displayName.text.data);
+                       (int)ref->nodeId.nodeId.identifier.string.length, ref->nodeId.nodeId.identifier.string.data,
+                       (int)ref->browseName.name.length, ref->browseName.name.data,
+                       (int)ref->displayName.text.length, ref->displayName.text.data);
             }
             //TODO: distinguish further types
         }
@@ -62,23 +63,24 @@ int main(int argc, char *argv[]) {
     
 #ifdef ENABLE_SUBSCRIPTIONS
     // Create a subscription with interval 0 (immediate)...
-    UA_Int32 subId = UA_Client_newSubscription(client, 0);
-    if (subId)
+    UA_UInt32 subId;
+    UA_Client_Subscriptions_new(client, UA_SubscriptionSettings_standard, &subId);
+    if(subId)
         printf("Create subscription succeeded, id %u\n", subId);
     
     // .. and monitor TheAnswer
-    UA_NodeId monitorThis;
-    monitorThis = UA_NODEID_STRING_ALLOC(1, "the.answer");
-    UA_UInt32 monId = UA_Client_monitorItemChanges(client, subId, monitorThis, UA_ATTRIBUTEID_VALUE, &handler_TheAnswerChanged );
+    UA_NodeId monitorThis = UA_NODEID_STRING(1, "the.answer");
+    UA_UInt32 monId;
+    UA_Client_Subscriptions_addMonitoredItem(client, subId, monitorThis,
+                                             UA_ATTRIBUTEID_VALUE, &handler_TheAnswerChanged, &monId);
     if (monId)
         printf("Monitoring 'the.answer', id %u\n", subId);
-    UA_NodeId_deleteMembers(&monitorThis);
     
     // First Publish always generates data (current value) and call out handler.
-    UA_Client_doPublish(client);
+    UA_Client_Subscriptions_manuallySendPublishRequest(client);
     
     // This should not generate anything
-    UA_Client_doPublish(client);
+    UA_Client_Subscriptions_manuallySendPublishRequest(client);
 #endif
     
     UA_Int32 value = 0;
@@ -91,7 +93,7 @@ int main(int argc, char *argv[]) {
     rReq.nodesToRead[0].nodeId = UA_NODEID_STRING_ALLOC(1, "the.answer"); /* assume this node exists */
     rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_VALUE;
 
-    UA_ReadResponse rResp = UA_Client_read(client, &rReq);
+    UA_ReadResponse rResp = UA_Client_Service_read(client, rReq);
     if(rResp.responseHeader.serviceResult == UA_STATUSCODE_GOOD &&
        rResp.resultsSize > 0 && rResp.results[0].hasValue &&
        UA_Variant_isScalar(&rResp.results[0].value) &&
@@ -117,7 +119,7 @@ int main(int argc, char *argv[]) {
     wReq.nodesToWrite[0].value.value.storageType = UA_VARIANT_DATA_NODELETE; //do not free the integer on deletion
     wReq.nodesToWrite[0].value.value.data = &value;
     
-    UA_WriteResponse wResp = UA_Client_write(client, &wReq);
+    UA_WriteResponse wResp = UA_Client_Service_write(client, wReq);
     if(wResp.responseHeader.serviceResult == UA_STATUSCODE_GOOD)
             printf("the new value is: %i\n", value);
     UA_WriteRequest_deleteMembers(&wReq);
@@ -125,10 +127,10 @@ int main(int argc, char *argv[]) {
 
 #ifdef ENABLE_SUBSCRIPTIONS
     // Take another look at the.answer... this should call the handler.
-    UA_Client_doPublish(client);
+    UA_Client_Subscriptions_manuallySendPublishRequest(client);
     
     // Delete our subscription (which also unmonitors all items)
-    if(!UA_Client_removeSubscription(client, subId))
+    if(!UA_Client_Subscriptions_remove(client, subId))
         printf("Subscription removed\n");
 #endif
     
@@ -137,19 +139,17 @@ int main(int argc, char *argv[]) {
        FIXME: Provide a namespace 0 independant example on the server side
      */
     UA_Variant input;
-    
     UA_String argString = UA_STRING("Hello Server");
     UA_Variant_init(&input);
     UA_Variant_setScalarCopy(&input, &argString, &UA_TYPES[UA_TYPES_STRING]);
     
     UA_Int32 outputSize;
     UA_Variant *output;
-    
-    retval = UA_Client_CallServerMethod(client, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
-                                        UA_NODEID_NUMERIC(1, 62541), 1, &input, &outputSize, &output);
+    retval = UA_Client_call(client, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+                            UA_NODEID_NUMERIC(1, 62541), 1, &input, &outputSize, &output);
     if(retval == UA_STATUSCODE_GOOD) {
         printf("Method call was successfull, and %i returned values available.\n", outputSize);
-        UA_Array_delete(output, &UA_TYPES[UA_TYPES_VARIANT], outputSize);
+        UA_Array_delete(output, outputSize, &UA_TYPES[UA_TYPES_VARIANT]);
     } else {
         printf("Method call was unsuccessfull, and %x returned values available.\n", retval);
     }
@@ -157,81 +157,74 @@ int main(int argc, char *argv[]) {
 
 #endif
 
-#ifdef ENABLE_ADDNODES 
-    /* Create a new object type node */
-    // New ReferenceType
-    UA_AddNodesResponse *adResp = UA_Client_createReferenceTypeNode(client,
-        UA_EXPANDEDNODEID_NUMERIC(1, 12133), // Assign this NodeId (will fail if client is called multiple times)
-        UA_QUALIFIEDNAME(0, "NewReference"),
-        UA_LOCALIZEDTEXT("en_US", "TheNewReference"),
-        UA_LOCALIZEDTEXT("en_US", "References something that might or might not exist."),
-        UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
-        UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
-        (UA_UInt32) 0, (UA_UInt32) 0, 
-        UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
-        UA_LOCALIZEDTEXT("en_US", "IsNewlyReferencedBy"));
-    if (adResp->resultsSize > 0 && adResp->results[0].statusCode == UA_STATUSCODE_GOOD ) {
-        printf("Created 'NewReference' with numeric NodeID %u\n", adResp->results[0].addedNodeId.identifier.numeric );
-    }
-    UA_AddNodesResponse_deleteMembers(adResp);
-    free(adResp);
-    
-    // New ObjectType
-    adResp = UA_Client_createObjectTypeNode(client,    
-        UA_EXPANDEDNODEID_NUMERIC(1, 12134), // Assign this NodeId (will fail if client is called multiple times)
-        UA_QUALIFIEDNAME(0, "NewObjectType"),
-        UA_LOCALIZEDTEXT("en_US", "TheNewObjectType"),
-        UA_LOCALIZEDTEXT("en_US", "Put innovative description here."),
-        UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
-        UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
-        (UA_UInt32) 0, (UA_UInt32) 0, 
-        UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER));
-        if (adResp->resultsSize > 0 && adResp->results[0].statusCode == UA_STATUSCODE_GOOD ) {
-        printf("Created 'NewObjectType' with numeric NodeID %u\n", adResp->results[0].addedNodeId.identifier.numeric );
-    }
-    
-    // New Object
-    adResp = UA_Client_createObjectNode(client,    
-        UA_EXPANDEDNODEID_NUMERIC(1, 0), // Assign new/random NodeID  
-        UA_QUALIFIEDNAME(0, "TheNewGreatNodeBrowseName"),
-        UA_LOCALIZEDTEXT("en_US", "TheNewGreatNode"),
-        UA_LOCALIZEDTEXT("de_DE", "Hier koennte Ihre Webung stehen!"),
-        UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
-        UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
-        (UA_UInt32) 0, (UA_UInt32) 0, 
-        UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER));
-    if (adResp->resultsSize > 0 && adResp->results[0].statusCode == UA_STATUSCODE_GOOD ) {
-        printf("Created 'NewObject' with numeric NodeID %u\n", adResp->results[0].addedNodeId.identifier.numeric );
-    }
-    
-    UA_AddNodesResponse_deleteMembers(adResp);
-    free(adResp);
-    
-    // New Integer Variable
-    UA_Variant *theValue = UA_Variant_new();
-    UA_Int32 *theValueDate = UA_Int32_new();
-    *theValueDate = 1234;
-    theValue->type = &UA_TYPES[UA_TYPES_INT32];
-    theValue->data = theValueDate;
-    
-    adResp = UA_Client_createVariableNode(client,
-        UA_EXPANDEDNODEID_NUMERIC(1, 0), // Assign new/random NodeID  
-        UA_QUALIFIEDNAME(0, "VariableNode"),
-        UA_LOCALIZEDTEXT("en_US", "TheNewVariableNode"),
-        UA_LOCALIZEDTEXT("en_US", "This integer is just amazing - it has digits and everything."),
-        UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
-        UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
-        (UA_UInt32) 0, (UA_UInt32) 0, 
-        UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
-        UA_NODEID_NUMERIC(0, UA_NS0ID_INT32),
-        theValue);
-    if (adResp->resultsSize > 0 && adResp->results[0].statusCode == UA_STATUSCODE_GOOD ) {
-        printf("Created 'NewVariable' with numeric NodeID %u\n", adResp->results[0].addedNodeId.identifier.numeric );
-    }
-    UA_AddNodesResponse_deleteMembers(adResp);
-    free(adResp);
-    free(theValue);
-    /* Done creating a new node*/
+#ifdef ENABLE_NODEMANAGEMENT 
+    /* New ReferenceType */
+    UA_NodeId ref_id;
+    UA_ReferenceTypeAttributes ref_attr;
+    UA_ReferenceTypeAttributes_init(&ref_attr);
+    ref_attr.displayName = UA_LOCALIZEDTEXT("en_US", "NewReference");
+    ref_attr.description = UA_LOCALIZEDTEXT("en_US", "References something that might or might not exist");
+    ref_attr.inverseName = UA_LOCALIZEDTEXT("en_US", "IsNewlyReferencedBy");
+    retval = UA_Client_addReferenceTypeNode(client,
+                                            UA_NODEID_NUMERIC(1, 12133),
+                                            UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
+                                            UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
+                                            UA_QUALIFIEDNAME(1, "NewReference"),
+                                            ref_attr, &ref_id);
+    if(retval == UA_STATUSCODE_GOOD )
+        printf("Created 'NewReference' with numeric NodeID %u\n", ref_id.identifier.numeric);
+    
+    /* New ObjectType */
+    UA_NodeId objt_id;
+    UA_ObjectTypeAttributes objt_attr;
+    UA_ObjectTypeAttributes_init(&objt_attr);
+    objt_attr.displayName = UA_LOCALIZEDTEXT("en_US", "TheNewObjectType");
+    objt_attr.description = UA_LOCALIZEDTEXT("en_US", "Put innovative description here");
+    retval = UA_Client_addObjectTypeNode(client,
+                                         UA_NODEID_NUMERIC(1, 12134),
+                                         UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE),
+                                         UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
+                                         UA_QUALIFIEDNAME(1, "NewObjectType"),
+                                         objt_attr, &objt_id);
+    if(retval == UA_STATUSCODE_GOOD)
+        printf("Created 'NewObjectType' with numeric NodeID %u\n", objt_id.identifier.numeric);
+    
+    /* New Object */
+    UA_NodeId obj_id;
+    UA_ObjectAttributes obj_attr;
+    UA_ObjectAttributes_init(&obj_attr);
+    obj_attr.displayName = UA_LOCALIZEDTEXT("en_US", "TheNewGreatNode");
+    obj_attr.description = UA_LOCALIZEDTEXT("de_DE", "Hier koennte Ihre Webung stehen!");
+    retval = UA_Client_addObjectNode(client,
+                                     UA_NODEID_NUMERIC(1, 0),
+                                     UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+                                     UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
+                                     UA_QUALIFIEDNAME(1, "TheGreatNode"),
+                                     UA_NODEID_NUMERIC(1, 12134),
+                                     obj_attr, &obj_id);
+    if(retval == UA_STATUSCODE_GOOD )
+        printf("Created 'NewObject' with numeric NodeID %u\n", obj_id.identifier.numeric);
+    
+    /* New Integer Variable */
+    UA_NodeId var_id;
+    UA_VariableAttributes var_attr;
+    UA_VariableAttributes_init(&var_attr);
+    var_attr.displayName = UA_LOCALIZEDTEXT("en_US", "TheNewVariableNode");
+    var_attr.description =
+        UA_LOCALIZEDTEXT("en_US", "This integer is just amazing - it has digits and everything.");
+    UA_Int32 int_value = 1234;
+    /* This does not copy the value */
+    UA_Variant_setScalar(&var_attr.value, &int_value, &UA_TYPES[UA_TYPES_INT32]);
+    var_attr.dataType = UA_TYPES[UA_TYPES_INT32].typeId;
+    retval = UA_Client_addVariableNode(client,
+                                       UA_NODEID_NUMERIC(1, 0), // Assign new/random NodeID  
+                                       UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+                                       UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
+                                       UA_QUALIFIEDNAME(0, "VariableNode"),
+                                       UA_NODEID_NULL, // no variable type
+                                       var_attr, &var_id);
+    if(retval == UA_STATUSCODE_GOOD )
+        printf("Created 'NewVariable' with numeric NodeID %u\n", var_id.identifier.numeric);
 #endif
     UA_Client_disconnect(client);
     UA_Client_delete(client);

+ 8 - 12
examples/client_firstSteps.c

@@ -13,7 +13,7 @@
 #endif
 
 int main(void) {
-    UA_Client *client = UA_Client_new(UA_ClientConfig_standard, Logger_Stdout_new());
+    UA_Client *client = UA_Client_new(UA_ClientConfig_standard, Logger_Stdout);
     UA_StatusCode retval = UA_Client_connect(client, ClientNetworkLayerTCP_connect, "opc.tcp://localhost:16664");
     if(retval != UA_STATUSCODE_GOOD) {
         UA_Client_delete(client);
@@ -22,33 +22,29 @@ int main(void) {
 
     //variables to store data
     UA_DateTime raw_date = 0;
-    UA_String* string_date = UA_String_new();
+    UA_String string_date;
 
     UA_ReadRequest rReq;
     UA_ReadRequest_init(&rReq);
-    rReq.nodesToRead = UA_Array_new(&UA_TYPES[UA_TYPES_READVALUEID], 1);
+    rReq.nodesToRead = UA_Array_new(1, &UA_TYPES[UA_TYPES_READVALUEID]);
     rReq.nodesToReadSize = 1;
     rReq.nodesToRead[0].nodeId = UA_NODEID_NUMERIC(0, 2258);
     rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_VALUE;
 
-    UA_ReadResponse rResp = UA_Client_read(client, &rReq);
+    UA_ReadResponse rResp = UA_Client_Service_read(client, rReq);
     if(rResp.responseHeader.serviceResult == UA_STATUSCODE_GOOD &&
             rResp.resultsSize > 0 && rResp.results[0].hasValue &&
             UA_Variant_isScalar(&rResp.results[0].value) &&
             rResp.results[0].value.type == &UA_TYPES[UA_TYPES_DATETIME]) {
         raw_date = *(UA_DateTime*)rResp.results[0].value.data;
-        #ifdef _WIN32
-           printf("raw date is: %I64u\n", raw_date);
-        #else
-           printf("raw date is: %llu\n", raw_date);
-        #endif
-        UA_DateTime_toString(raw_date, string_date);
-        printf("string date is: %.*s\n", string_date->length, string_date->data);
+        printf("raw date is: %" PRId64 "\n", raw_date);
+        string_date = UA_DateTime_toString(raw_date);
+        printf("string date is: %.*s\n", (int)string_date.length, string_date.data);
     }
 
     UA_ReadRequest_deleteMembers(&rReq);
     UA_ReadResponse_deleteMembers(&rResp);
-    UA_String_delete(string_date);
+    UA_String_deleteMembers(&string_date);
 
     UA_Client_disconnect(client);
     UA_Client_delete(client);

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 561 - 511
examples/client_legacy.c


+ 4 - 2
examples/client_stateless.c

@@ -8,9 +8,11 @@
 #include <unistd.h> // for close
 #include <stdlib.h> // pulls in declaration of malloc, free
 
+//this stuff can not be build with amalgamation options, since it need internal APIs
 #include "ua_util.h"
-#include "ua_types_encoding_binary.h"
-#include "ua_transport_generated.h"
+#include "ua_transport_generated_encoding_binary.h"
+#include "ua_types_generated_encoding_binary.h"
+
 
 int main(int argc , char *argv[])
 {

+ 9 - 18
examples/logger_stdout.c

@@ -6,36 +6,27 @@
 #include <stdio.h>
 #include <stdarg.h>
 #include "logger_stdout.h"
-
-static void print_time(void) {
-	UA_DateTime now = UA_DateTime_now();
-	UA_ByteString str;
-	UA_DateTime_toString(now, &str);
-	printf("%.27s", str.data); //a bit hacky way not to display nanoseconds
-	UA_ByteString_deleteMembers(&str);
-}
+#include "ua_types_generated_encoding_binary.h"
 
 const char *LogLevelNames[6] = {"trace", "debug", "info", "warning", "error", "fatal"};
-const char *LogCategoryNames[4] = {"communication", "server", "client", "userland"};
+const char *LogCategoryNames[6] = {"network", "channel", "session", "server", "client", "userland"};
 
 #if ((__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4 || defined(__clang__))
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
 #endif
-static void Logger_Stdout(UA_LogLevel level, UA_LogCategory category, const char *msg, ...) {
-    printf("[");
-    print_time();
+
+void Logger_Stdout(UA_LogLevel level, UA_LogCategory category, const char *msg, ...) {
+	UA_String time = UA_DateTime_toString(UA_DateTime_now());
+    printf("[%.23s] %s/%s\t", time.data, LogLevelNames[level], LogCategoryNames[category]);
+	UA_ByteString_deleteMembers(&time);
     va_list ap;
     va_start(ap, msg);
-    printf("] %s/%s\t", LogLevelNames[level], LogCategoryNames[category]);
     vprintf(msg, ap);
-    printf("\n");
     va_end(ap);
+    printf("\n");
 }
+
 #if ((__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4 || defined(__clang__))
 #pragma GCC diagnostic pop
 #endif
-
-UA_Logger Logger_Stdout_new(void) {
-	return Logger_Stdout;
-}

+ 9 - 2
examples/logger_stdout.h

@@ -9,7 +9,14 @@
 #include "ua_types.h"
 #include "ua_log.h"
 
-/** Initialises the logger for the current thread. */
-UA_EXPORT UA_Logger Logger_Stdout_new(void);
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+UA_EXPORT void Logger_Stdout(UA_LogLevel level, UA_LogCategory category, const char *msg, ...);
+
+#ifdef __cplusplus
+}
+#endif
 
 #endif /* LOGGER_STDOUT_H_ */

+ 180 - 158
examples/networklayer_tcp.c

@@ -6,6 +6,7 @@
 #include "networklayer_tcp.h"
 
 #include <stdlib.h> // malloc, free
+#include <stdio.h> // snprintf
 #include <string.h> // memset
 #include <errno.h>
 
@@ -43,75 +44,90 @@
 /* Generic Socket Functions */
 /****************************/
 
-static void socket_close(UA_Connection *connection) {
+static void
+socket_close(UA_Connection *connection) {
     connection->state = UA_CONNECTION_CLOSED;
     shutdown(connection->sockfd,2);
     CLOSESOCKET(connection->sockfd);
 }
 
-static UA_StatusCode socket_write(UA_Connection *connection, UA_ByteString *buf, size_t buflen) {
+static UA_StatusCode
+socket_write(UA_Connection *connection, UA_ByteString *buf) {
     size_t nWritten = 0;
-    while (nWritten < buflen) {
+    while(buf->length > 0 && nWritten < (size_t)buf->length) {
         UA_Int32 n = 0;
         do {
 #ifdef _WIN32
-            n = send((SOCKET)connection->sockfd, (const char*)buf->data, buflen, 0);
-            if(n < 0 && WSAGetLastError() != WSAEINTR && WSAGetLastError() != WSAEWOULDBLOCK){
+            n = send((SOCKET)connection->sockfd, (const char*)buf->data, (size_t)buf->length, 0);
+            const int last_error = WSAGetLastError();
+            if(n < 0 && last_error != WSAEINTR && last_error != WSAEWOULDBLOCK) {
                 connection->close(connection);
                 socket_close(connection);
+                UA_ByteString_deleteMembers(buf);
                 return UA_STATUSCODE_BADCONNECTIONCLOSED;
             }
 #else
-            n = send(connection->sockfd, (const char*)buf->data, buflen, MSG_NOSIGNAL);
-            if(n == -1L && errno != EINTR && errno != EAGAIN){
+            n = send(connection->sockfd, (const char*)buf->data, (size_t)buf->length, MSG_NOSIGNAL);
+            if(n == -1L && errno != EINTR && errno != EAGAIN) {
+                connection->close(connection);
                 socket_close(connection);
+                UA_ByteString_deleteMembers(buf);
                 return UA_STATUSCODE_BADCONNECTIONCLOSED;
             }
 #endif
         } while (n == -1L);
         nWritten += n;
     }
-#ifdef UA_MULTITHREADING
     UA_ByteString_deleteMembers(buf);
-#endif
     return UA_STATUSCODE_GOOD;
 }
 
-static UA_StatusCode socket_recv(UA_Connection *connection, UA_ByteString *response, UA_UInt32 timeout) {
+static UA_StatusCode
+socket_recv(UA_Connection *connection, UA_ByteString *response, UA_UInt32 timeout) {
     response->data = malloc(connection->localConf.recvBufferSize);
     if(!response->data) {
-        UA_ByteString_init(response);
-        return UA_STATUSCODE_GOOD; /* not enough memory retry */
+        response->length = 0;
+        return UA_STATUSCODE_BADOUTOFMEMORY; /* not enough memory retry */
     }
-    struct timeval tmptv = {0, timeout * 1000};
-    if(0 != setsockopt(connection->sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tmptv, sizeof(struct timeval))){
-		free(response->data);
-        UA_ByteString_init(response);
-        socket_close(connection);
-        return UA_STATUSCODE_BADINTERNALERROR;
+
+    if(timeout > 0) {
+        /* currently, only the client uses timeouts */
+#ifndef _WIN32
+        int timeout_usec = timeout * 1000;
+        struct timeval tmptv = {timeout_usec / 1000000, timeout_usec % 1000000};
+        int ret = setsockopt(connection->sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tmptv, sizeof(struct timeval));
+#else
+        DWORD timeout_dw = timeout;
+        int ret = setsockopt(connection->sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout_dw, sizeof(DWORD));
+#endif
+        if(0 != ret) {
+            UA_ByteString_deleteMembers(response);
+            socket_close(connection);
+            return UA_STATUSCODE_BADCONNECTIONCLOSED;
+        }
     }
+
     int ret = recv(connection->sockfd, (char*)response->data, connection->localConf.recvBufferSize, 0);
 	if(ret == 0) {
-		free(response->data);
-        UA_ByteString_init(response);
+        /* server has closed the connection */
+        UA_ByteString_deleteMembers(response);
         socket_close(connection);
-        return UA_STATUSCODE_BADCONNECTIONCLOSED; /* ret == 0 -> server has closed the connection */
+        return UA_STATUSCODE_BADCONNECTIONCLOSED;
 	} else if(ret < 0) {
-        free(response->data);
-        UA_ByteString_init(response);
+        UA_ByteString_deleteMembers(response);
 #ifdef _WIN32
-		if(WSAGetLastError() == WSAEINTR || WSAGetLastError() == WSAEWOULDBLOCK) {
+        const int last_error = WSAGetLastError();
+        if(last_error == WSAEINTR || last_error == WSAEWOULDBLOCK)
 #else
-		if(errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) {
+		if(errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)
 #endif
             return UA_STATUSCODE_GOOD; /* retry */
-        } else {
+        else {
             socket_close(connection);
             return UA_STATUSCODE_BADCONNECTIONCLOSED;
         }
     }
     response->length = ret;
-    *response = UA_Connection_completeMessages(connection, *response);
     return UA_STATUSCODE_GOOD;
 }
 
@@ -130,7 +146,7 @@ static UA_StatusCode socket_set_nonblocking(UA_Int32 sockfd) {
 
 static void FreeConnectionCallback(UA_Server *server, void *ptr) {
     UA_Connection_deleteMembers((UA_Connection*)ptr);
-     free(ptr);
+    free(ptr);
  }
 
 /***************************/
@@ -170,15 +186,12 @@ static void FreeConnectionCallback(UA_Server *server, void *ptr) {
 #define MAXBACKLOG 100
 
 typedef struct {
+    UA_ServerNetworkLayer layer;
+    
     /* config */
-    UA_Logger *logger;
     UA_UInt32 port;
     UA_ConnectionConfig conf; /* todo: rename to localconf. */
 
-#ifndef UA_MULTITHREADING
-    UA_ByteString buffer; // message buffer that is reused
-#endif
-
     /* open sockets and connections */
     fd_set fdset;
     UA_Int32 serversockfd;
@@ -190,37 +203,30 @@ typedef struct {
     } *mappings;
 } ServerNetworkLayerTCP;
 
-static UA_StatusCode ServerNetworkLayerGetBuffer(UA_Connection *connection, UA_ByteString *buf) {
-#ifdef UA_MULTITHREADING
-    return UA_ByteString_newMembers(buf, connection->remoteConf.recvBufferSize);
-#else
-    ServerNetworkLayerTCP *layer = connection->handle;
-    *buf = layer->buffer;
-    return UA_STATUSCODE_GOOD;
-#endif
+static UA_StatusCode
+ServerNetworkLayerGetSendBuffer(UA_Connection *connection, size_t length, UA_ByteString *buf) {
+    if(length > connection->remoteConf.recvBufferSize)
+        return UA_STATUSCODE_BADCOMMUNICATIONERROR;
+    return UA_ByteString_allocBuffer(buf, length);
 }
 
-static void ServerNetworkLayerReleaseBuffer(UA_Connection *connection, UA_ByteString *buf) {
-#ifdef UA_MULTITHREADING
+static void
+ServerNetworkLayerReleaseSendBuffer(UA_Connection *connection, UA_ByteString *buf) {
+    UA_ByteString_deleteMembers(buf);
+}
+
+static void
+ServerNetworkLayerReleaseRecvBuffer(UA_Connection *connection, UA_ByteString *buf) {
     UA_ByteString_deleteMembers(buf);
-#endif
 }
 
 /* after every select, we need to reset the sockets we want to listen on */
 static void setFDSet(ServerNetworkLayerTCP *layer) {
     FD_ZERO(&layer->fdset);
-#ifdef _WIN32
     FD_SET((UA_UInt32)layer->serversockfd, &layer->fdset);
-#else
-    FD_SET(layer->serversockfd, &layer->fdset);
-#endif
     layer->highestfd = layer->serversockfd;
     for(size_t i = 0; i < layer->mappingsSize; i++) {
-#ifdef _WIN32
         FD_SET((UA_UInt32)layer->mappings[i].sockfd, &layer->fdset);
-#else
-        FD_SET(layer->mappings[i].sockfd, &layer->fdset);
-#endif
         if(layer->mappings[i].sockfd > layer->highestfd)
             layer->highestfd = layer->mappings[i].sockfd;
     }
@@ -236,8 +242,12 @@ static void ServerNetworkLayerTCP_closeConnection(UA_Connection *connection) {
         return;
     connection->state = UA_CONNECTION_CLOSED;
 #endif
-    shutdown(connection->sockfd, 2); /* only shut down here. this triggers the select, where the socket
-                                        is closed in the main thread */
+    ServerNetworkLayerTCP *layer = connection->handle;
+    UA_LOG_INFO(layer->layer.logger, UA_LOGCATEGORY_NETWORK, "Closing the Connection %i",
+                connection->sockfd);
+    /* only "shutdown" here. this triggers the select, where the socket is
+       "closed" in the mainloop */
+    shutdown(connection->sockfd, 2);
 }
 
 /* call only from the single networking thread */
@@ -245,18 +255,26 @@ static UA_StatusCode ServerNetworkLayerTCP_add(ServerNetworkLayerTCP *layer, UA_
     UA_Connection *c = malloc(sizeof(UA_Connection));
     if(!c)
         return UA_STATUSCODE_BADINTERNALERROR;
+
+    struct sockaddr_in addr;
+    socklen_t addrlen = sizeof(struct sockaddr_in);
+    getsockname(newsockfd, (struct sockaddr*)&addr, &addrlen);
+    UA_LOG_INFO(layer->layer.logger, UA_LOGCATEGORY_NETWORK, "New Connection %i over TCP from %s:%d",
+                newsockfd, inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
     UA_Connection_init(c);
     c->sockfd = newsockfd;
     c->handle = layer;
     c->localConf = layer->conf;
-    c->write = socket_write;
+    c->send = socket_write;
     c->close = ServerNetworkLayerTCP_closeConnection;
-    c->getBuffer = ServerNetworkLayerGetBuffer;
-    c->releaseBuffer = ServerNetworkLayerReleaseBuffer;
+    c->getSendBuffer = ServerNetworkLayerGetSendBuffer;
+    c->releaseSendBuffer = ServerNetworkLayerReleaseSendBuffer;
+    c->releaseRecvBuffer = ServerNetworkLayerReleaseRecvBuffer;
     c->state = UA_CONNECTION_OPENING;
-    struct ConnectionMapping *nm =
-        realloc(layer->mappings, sizeof(struct ConnectionMapping)*(layer->mappingsSize+1));
+    struct ConnectionMapping *nm;
+    nm = realloc(layer->mappings, sizeof(struct ConnectionMapping)*(layer->mappingsSize+1));
     if(!nm) {
+        UA_LOG_ERROR(layer->layer.logger, UA_LOGCATEGORY_NETWORK, "No memory for a new Connection");
         free(c);
         return UA_STATUSCODE_BADINTERNALERROR;
     }
@@ -266,18 +284,17 @@ static UA_StatusCode ServerNetworkLayerTCP_add(ServerNetworkLayerTCP *layer, UA_
     return UA_STATUSCODE_GOOD;
 }
 
-static UA_StatusCode ServerNetworkLayerTCP_start(UA_ServerNetworkLayer *nl, UA_Logger *logger) {
-    ServerNetworkLayerTCP *layer = nl->handle;
-    layer->logger = logger;
+static UA_StatusCode ServerNetworkLayerTCP_start(ServerNetworkLayerTCP *layer, UA_Logger logger) {
+    layer->layer.logger = logger;
 #ifdef _WIN32
     if((layer->serversockfd = socket(PF_INET, SOCK_STREAM,0)) == (UA_Int32)INVALID_SOCKET) {
-        UA_LOG_WARNING((*layer->logger), UA_LOGCATEGORY_COMMUNICATION, "Error opening socket, code: %d",
+        UA_LOG_WARNING(layer->layer.logger, UA_LOGCATEGORY_NETWORK, "Error opening socket, code: %d",
                        WSAGetLastError());
         return UA_STATUSCODE_BADINTERNALERROR;
     }
 #else
     if((layer->serversockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
-        UA_LOG_WARNING((*layer->logger), UA_LOGCATEGORY_COMMUNICATION, "Error opening socket");
+        UA_LOG_WARNING(layer->layer.logger, UA_LOGCATEGORY_NETWORK, "Error opening socket");
         return UA_STATUSCODE_BADINTERNALERROR;
     }
 #endif
@@ -286,37 +303,34 @@ static UA_StatusCode ServerNetworkLayerTCP_start(UA_ServerNetworkLayer *nl, UA_L
          .sin_port = htons(layer->port), .sin_zero = {0}};
     int optval = 1;
     if(setsockopt(layer->serversockfd, SOL_SOCKET,
-                  SO_REUSEADDR, (const char *)&optval,
-                  sizeof(optval)) == -1) {
-        UA_LOG_WARNING((*layer->logger), UA_LOGCATEGORY_COMMUNICATION, "Error during setting of socket options");
+                  SO_REUSEADDR, (const char *)&optval, sizeof(optval)) == -1) {
+        UA_LOG_WARNING(layer->layer.logger, UA_LOGCATEGORY_NETWORK,
+                       "Error during setting of socket options");
         CLOSESOCKET(layer->serversockfd);
         return UA_STATUSCODE_BADINTERNALERROR;
     }
     if(bind(layer->serversockfd, (const struct sockaddr *)&serv_addr,
             sizeof(serv_addr)) < 0) {
-        UA_LOG_WARNING((*layer->logger), UA_LOGCATEGORY_COMMUNICATION, "Error during socket binding");
+        UA_LOG_WARNING(layer->layer.logger, UA_LOGCATEGORY_NETWORK, "Error during socket binding");
         CLOSESOCKET(layer->serversockfd);
         return UA_STATUSCODE_BADINTERNALERROR;
     }
     socket_set_nonblocking(layer->serversockfd);
     listen(layer->serversockfd, MAXBACKLOG);
-    UA_LOG_INFO((*layer->logger), UA_LOGCATEGORY_COMMUNICATION, "Listening on %.*s",
-                nl->discoveryUrl.length, nl->discoveryUrl.data);
+    UA_LOG_INFO(layer->layer.logger, UA_LOGCATEGORY_NETWORK, "TCP network layer listening on %.*s",
+                layer->layer.discoveryUrl.length, layer->layer.discoveryUrl.data);
     return UA_STATUSCODE_GOOD;
 }
 
-static UA_Int32 ServerNetworkLayerTCP_getJobs(UA_ServerNetworkLayer *nl, UA_Job **jobs, UA_UInt16 timeout) {
-    ServerNetworkLayerTCP *layer = nl->handle;
+static size_t
+ServerNetworkLayerTCP_getJobs(ServerNetworkLayerTCP *layer, UA_Job **jobs, UA_UInt16 timeout) {
     setFDSet(layer);
     struct timeval tmptv = {0, timeout};
     UA_Int32 resultsize;
- repeat_select:
     resultsize = select(layer->highestfd+1, &layer->fdset, NULL, NULL, &tmptv);
     if(resultsize < 0) {
-        if(errno == EINTR)
-            goto repeat_select;
         *jobs = NULL;
-        return resultsize;
+        return 0;
     }
 
     /* accept new connections (can only be a single one) */
@@ -341,35 +355,40 @@ static UA_Int32 ServerNetworkLayerTCP_getJobs(UA_ServerNetworkLayer *nl, UA_Job
         return 0;
 
     /* read from established sockets */
-    UA_Int32 j = 0;
+    size_t j = 0;
     UA_ByteString buf = UA_BYTESTRING_NULL;
-    for(size_t i = 0; i < layer->mappingsSize && j < resultsize; i++) {
+    for(size_t i = 0; i < layer->mappingsSize && j < (size_t)resultsize; i++) {
         if(!(FD_ISSET(layer->mappings[i].sockfd, &layer->fdset)))
             continue;
-        if(socket_recv(layer->mappings[i].connection, &buf, 0) == UA_STATUSCODE_GOOD) {
-            if(!buf.data)
+        UA_StatusCode retval = socket_recv(layer->mappings[i].connection, &buf, 0);
+        if(retval == UA_STATUSCODE_GOOD) {
+            UA_Boolean realloced = UA_FALSE;
+            retval = UA_Connection_completeMessages(layer->mappings[i].connection, &buf, &realloced);
+            if(retval != UA_STATUSCODE_GOOD || buf.length == 0)
                 continue;
-            js[j].type = UA_JOBTYPE_BINARYMESSAGE;
-            js[j].job.binaryMessage.message = buf;
             js[j].job.binaryMessage.connection = layer->mappings[i].connection;
-        } else {
+            js[j].job.binaryMessage.message = buf;
+            if(!realloced)
+                js[j].type = UA_JOBTYPE_BINARYMESSAGE_NETWORKLAYER;
+            else
+                js[j].type = UA_JOBTYPE_BINARYMESSAGE_ALLOCATED;
+            j++;
+        } else if (retval == UA_STATUSCODE_BADCONNECTIONCLOSED) {
             UA_Connection *c = layer->mappings[i].connection;
-            /* the socket is already closed */
+            /* the socket was closed from remote */
             js[j].type = UA_JOBTYPE_DETACHCONNECTION;
             js[j].job.closeConnection = layer->mappings[i].connection;
             layer->mappings[i] = layer->mappings[layer->mappingsSize-1];
             layer->mappingsSize--;
             j++;
-            i--; // iterate over the same index again
-            js[j].type = UA_JOBTYPE_DELAYEDMETHODCALL;
+            js[j].type = UA_JOBTYPE_METHODCALL_DELAYED;
             js[j].job.methodCall.method = FreeConnectionCallback;
             js[j].job.methodCall.data = c;
+            j++;
         }
-        j++;
     }
 
-    if (j == 0)
-    {
+    if(j == 0) {
     	free(js);
     	js = NULL;
     }
@@ -378,8 +397,12 @@ static UA_Int32 ServerNetworkLayerTCP_getJobs(UA_ServerNetworkLayer *nl, UA_Job
     return j;
 }
 
-static UA_Int32 ServerNetworkLayerTCP_stop(UA_ServerNetworkLayer *nl, UA_Job **jobs) {
-    ServerNetworkLayerTCP *layer = nl->handle;
+static size_t
+ServerNetworkLayerTCP_stop(ServerNetworkLayerTCP *layer, UA_Job **jobs) {
+    UA_LOG_INFO(layer->layer.logger, UA_LOGCATEGORY_NETWORK,
+                "Shutting down the TCP network layer with %d open connection(s)", layer->mappingsSize);
+    shutdown(layer->serversockfd,2);
+    CLOSESOCKET(layer->serversockfd);
     UA_Job *items = malloc(sizeof(UA_Job) * layer->mappingsSize * 2);
     if(!items)
         return 0;
@@ -387,7 +410,7 @@ static UA_Int32 ServerNetworkLayerTCP_stop(UA_ServerNetworkLayer *nl, UA_Job **j
         socket_close(layer->mappings[i].connection);
         items[i*2].type = UA_JOBTYPE_DETACHCONNECTION;
         items[i*2].job.closeConnection = layer->mappings[i].connection;
-        items[(i*2)+1].type = UA_JOBTYPE_DELAYEDMETHODCALL;
+        items[(i*2)+1].type = UA_JOBTYPE_METHODCALL_DELAYED;
         items[(i*2)+1].job.methodCall.method = FreeConnectionCallback;
         items[(i*2)+1].job.methodCall.data = layer->mappings[i].connection;
     }
@@ -399,114 +422,118 @@ static UA_Int32 ServerNetworkLayerTCP_stop(UA_ServerNetworkLayer *nl, UA_Job **j
 }
 
 /* run only when the server is stopped */
-static void ServerNetworkLayerTCP_deleteMembers(UA_ServerNetworkLayer *nl) {
-    ServerNetworkLayerTCP *layer = nl->handle;
-#ifndef UA_MULTITHREADING
-    UA_ByteString_deleteMembers(&layer->buffer);
-#endif
-    for(size_t i = 0; i < layer->mappingsSize; i++)
-        free(layer->mappings[i].connection);
+static void ServerNetworkLayerTCP_deleteMembers(ServerNetworkLayerTCP *layer) {
     free(layer->mappings);
-    free(layer);
 }
 
-UA_ServerNetworkLayer ServerNetworkLayerTCP_new(UA_ConnectionConfig conf, UA_UInt32 port) {
+UA_ServerNetworkLayer * ServerNetworkLayerTCP_new(UA_ConnectionConfig conf, UA_UInt32 port) {
 #ifdef _WIN32
     WORD wVersionRequested;
     WSADATA wsaData;
     wVersionRequested = MAKEWORD(2, 2);
     WSAStartup(wVersionRequested, &wsaData);
 #endif
-    UA_ServerNetworkLayer nl;
-    memset(&nl, 0, sizeof(UA_ServerNetworkLayer));
-
     ServerNetworkLayerTCP *layer = malloc(sizeof(ServerNetworkLayerTCP));
-    if(!layer){
-        return nl;
-    }
+    if(!layer)
+        return NULL;
+    memset(layer, 0, sizeof(ServerNetworkLayerTCP));
     layer->conf = conf;
     layer->mappingsSize = 0;
     layer->mappings = NULL;
     layer->port = port;
     char hostname[256];
-    gethostname(hostname, 255);
-    UA_String_copyprintf("opc.tcp://%s:%d", &nl.discoveryUrl, hostname, port);
-
-#ifndef UA_MULTITHREADING
-    layer->buffer = (UA_ByteString){.length = conf.maxMessageSize, .data = malloc(conf.maxMessageSize)};
+    if(gethostname(hostname, 255) == 0) {
+        char discoveryUrl[256];
+        UA_String str;
+#ifndef _MSC_VER
+        str.length = snprintf(discoveryUrl, 255, "opc.tcp://%s:%d", hostname, port);
+#else
+        str.length = _snprintf_s(discoveryUrl, 255, _TRUNCATE, "opc.tcp://%s:%d", hostname, port);
 #endif
+        str.data = (UA_Byte*)discoveryUrl;
+        UA_String_copy(&str, &layer->layer.discoveryUrl);
+    }
 
-    nl.handle = layer;
-    nl.start = ServerNetworkLayerTCP_start;
-    nl.getJobs = ServerNetworkLayerTCP_getJobs;
-    nl.stop = ServerNetworkLayerTCP_stop;
-    nl.deleteMembers = ServerNetworkLayerTCP_deleteMembers;
-    return nl;
+    layer->layer.start = (UA_StatusCode(*)(UA_ServerNetworkLayer*,UA_Logger))ServerNetworkLayerTCP_start;
+    layer->layer.getJobs = (size_t(*)(UA_ServerNetworkLayer*,UA_Job**,UA_UInt16))ServerNetworkLayerTCP_getJobs;
+    layer->layer.stop = (size_t(*)(UA_ServerNetworkLayer*, UA_Job**))ServerNetworkLayerTCP_stop;
+    layer->layer.deleteMembers = (void(*)(UA_ServerNetworkLayer*))ServerNetworkLayerTCP_deleteMembers;
+    return &layer->layer;
 }
 
 /***************************/
 /* Client NetworkLayer TCP */
 /***************************/
 
-static UA_StatusCode ClientNetworkLayerGetBuffer(UA_Connection *connection, UA_ByteString *buf) {
-#ifndef UA_MULTITHREADING
-    *buf = *(UA_ByteString*)connection->handle;
-    return UA_STATUSCODE_GOOD;
-#else
-    return UA_ByteString_newMembers(buf, connection->remoteConf.recvBufferSize);
-#endif
+static UA_StatusCode
+ClientNetworkLayerGetBuffer(UA_Connection *connection, size_t length, UA_ByteString *buf) {
+    if(length > connection->remoteConf.recvBufferSize)
+        return UA_STATUSCODE_BADCOMMUNICATIONERROR;
+    if(connection->state == UA_CONNECTION_CLOSED)
+        return UA_STATUSCODE_BADCONNECTIONCLOSED;
+    return UA_ByteString_allocBuffer(buf, connection->remoteConf.recvBufferSize);
 }
 
-static void ClientNetworkLayerReleaseBuffer(UA_Connection *connection, UA_ByteString *buf) {
-#ifdef UA_MULTITHREADING
+static void
+ClientNetworkLayerReleaseBuffer(UA_Connection *connection, UA_ByteString *buf) {
     UA_ByteString_deleteMembers(buf);
-#endif
 }
 
-static void ClientNetworkLayerClose(UA_Connection *connection) {
-#ifndef UA_MULTITHREADING
-    if(connection->handle)
-        UA_ByteString_delete(connection->handle);
-    connection->handle = NULL;
-#endif
+static void
+ClientNetworkLayerClose(UA_Connection *connection) {
+#ifdef UA_MULTITHREADING
+    if(uatomic_xchg(&connection->state, UA_CONNECTION_CLOSED) == UA_CONNECTION_CLOSED)
+        return;
+#else
     if(connection->state == UA_CONNECTION_CLOSED)
         return;
     connection->state = UA_CONNECTION_CLOSED;
+#endif
     socket_close(connection);
 }
 
 /* we have no networklayer. instead, attach the reusable buffer to the handle */
-UA_Connection ClientNetworkLayerTCP_connect(UA_ConnectionConfig localConf, char *endpointUrl,
-                                            UA_Logger *logger) {
+UA_Connection
+ClientNetworkLayerTCP_connect(UA_ConnectionConfig localConf, const char *endpointUrl, UA_Logger logger) {
     UA_Connection connection;
     UA_Connection_init(&connection);
     connection.localConf = localConf;
 
-#ifndef UA_MULTITHREADING
-    connection.handle = UA_ByteString_new();
-    UA_ByteString_newMembers(connection.handle, localConf.maxMessageSize);
-#endif
+    //socket_set_nonblocking(connection.sockfd);
+    connection.send = socket_write;
+    connection.recv = socket_recv;
+    connection.close = ClientNetworkLayerClose;
+    connection.getSendBuffer = ClientNetworkLayerGetBuffer;
+    connection.releaseSendBuffer = ClientNetworkLayerReleaseBuffer;
+    connection.releaseRecvBuffer = ClientNetworkLayerReleaseBuffer;
 
     size_t urlLength = strlen(endpointUrl);
     if(urlLength < 11 || urlLength >= 512) {
-        UA_LOG_WARNING((*logger), UA_LOGCATEGORY_COMMUNICATION, "Server url size invalid");
+        UA_LOG_WARNING((*logger), UA_LOGCATEGORY_NETWORK, "Server url size invalid");
         return connection;
     }
     if(strncmp(endpointUrl, "opc.tcp://", 10) != 0) {
-        UA_LOG_WARNING((*logger), UA_LOGCATEGORY_COMMUNICATION, "Server url does not begin with opc.tcp://");
+        UA_LOG_WARNING((*logger), UA_LOGCATEGORY_NETWORK, "Server url does not begin with opc.tcp://");
         return connection;
     }
 
     UA_UInt16 portpos = 9;
-    UA_UInt16 port = 0;
-    for(;portpos < urlLength-1; portpos++) {
+    UA_UInt16 port;
+    for(port = 0; portpos < urlLength-1; portpos++) {
         if(endpointUrl[portpos] == ':') {
-            port = atoi(&endpointUrl[portpos+1]);
+            char *endPtr = NULL;
+            unsigned long int tempulong = strtoul(&endpointUrl[portpos+1], &endPtr, 10);
+            if (ERANGE != errno &&
+                tempulong < UINT16_MAX &&
+                endPtr != &endpointUrl[portpos+1])
+            {
+                port = (UA_UInt16)tempulong;
+            }
             break;
         }
     }
     if(port == 0) {
-        UA_LOG_WARNING((*logger), UA_LOGCATEGORY_COMMUNICATION, "Port invalid");
+        UA_LOG_WARNING((*logger), UA_LOGCATEGORY_NETWORK, "Port invalid");
         return connection;
     }
 
@@ -523,12 +550,12 @@ UA_Connection ClientNetworkLayerTCP_connect(UA_ConnectionConfig localConf, char
 #else
     if((connection.sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
 #endif
-        UA_LOG_WARNING((*logger), UA_LOGCATEGORY_COMMUNICATION, "Could not create socket");
+        UA_LOG_WARNING((*logger), UA_LOGCATEGORY_NETWORK, "Could not create socket");
         return connection;
     }
     struct hostent *server = gethostbyname(hostname);
-    if(server == NULL) {
-        UA_LOG_WARNING((*logger), UA_LOGCATEGORY_COMMUNICATION, "DNS lookup of %s failed", hostname);
+    if(!server) {
+        UA_LOG_WARNING((*logger), UA_LOGCATEGORY_NETWORK, "DNS lookup of %s failed", hostname);
         return connection;
     }
     struct sockaddr_in server_addr;
@@ -539,22 +566,17 @@ UA_Connection ClientNetworkLayerTCP_connect(UA_ConnectionConfig localConf, char
     connection.state = UA_CONNECTION_OPENING;
     if(connect(connection.sockfd, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0) {
         ClientNetworkLayerClose(&connection);
-        UA_LOG_WARNING((*logger), UA_LOGCATEGORY_COMMUNICATION, "Connection failed");
+        UA_LOG_WARNING((*logger), UA_LOGCATEGORY_NETWORK, "Connection failed");
         return connection;
     }
 
 #ifdef SO_NOSIGPIPE
     int val = 1;
-    if (setsockopt(connection.sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void*)&val, sizeof(val)) < 0) {
-    UA_LOG_WARNING((*logger), UA_LOGCATEGORY_COMMUNICATION, "Couldn't set SO_NOSIGPIPE");
+    if(setsockopt(connection.sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void*)&val, sizeof(val)) < 0) {
+        UA_LOG_WARNING((*logger), UA_LOGCATEGORY_NETWORK, "Couldn't set SO_NOSIGPIPE");
+        return connection;
     }
 #endif
 
-    //socket_set_nonblocking(connection.sockfd);
-    connection.write = socket_write;
-    connection.recv = socket_recv;
-    connection.close = ClientNetworkLayerClose;
-    connection.getBuffer = ClientNetworkLayerGetBuffer;
-    connection.releaseBuffer = ClientNetworkLayerReleaseBuffer;
     return connection;
 }

+ 4 - 2
examples/networklayer_tcp.h

@@ -14,8 +14,10 @@ extern "C" {
 #include "ua_client.h"
 
 /** @brief Create the TCP networklayer and listen to the specified port */
-UA_EXPORT UA_ServerNetworkLayer ServerNetworkLayerTCP_new(UA_ConnectionConfig conf, UA_UInt32 port);
-UA_EXPORT UA_Connection ClientNetworkLayerTCP_connect(UA_ConnectionConfig conf, char *endpointUrl, UA_Logger *logger);
+UA_ServerNetworkLayer UA_EXPORT * ServerNetworkLayerTCP_new(UA_ConnectionConfig conf, UA_UInt32 port);
+
+UA_Connection UA_EXPORT
+ClientNetworkLayerTCP_connect(UA_ConnectionConfig conf, const char *endpointUrl, UA_Logger logger);
 
 #ifdef __cplusplus
 } // extern "C"

+ 92 - 95
examples/networklayer_udp.c

@@ -32,17 +32,17 @@
 #define MAXBACKLOG 100
 
 #ifdef _WIN32
-# error fixme: udp not yet implemented for windows
+# error udp not yet implemented for windows
 #endif
 
 /*****************************/
 /* Generic Buffer Management */
 /*****************************/
 
-static UA_StatusCode GetMallocedBuffer(UA_Connection *connection, UA_ByteString *buf, size_t minSize) {
-    if(minSize > connection->remoteConf.recvBufferSize)
-        return UA_STATUSCODE_BADINTERNALERROR;
-    return UA_ByteString_newMembers(buf, minSize);
+static UA_StatusCode GetMallocedBuffer(UA_Connection *connection, size_t length, UA_ByteString *buf) {
+    if(length > connection->remoteConf.recvBufferSize)
+        return UA_STATUSCODE_BADCOMMUNICATIONERROR;
+    return UA_ByteString_newMembers(buf, connection->remoteConf.recvBufferSize);
 }
 
 static void ReleaseMallocedBuffer(UA_Connection *connection, UA_ByteString *buf) {
@@ -62,50 +62,46 @@ typedef struct {
 } UDPConnection;
 
 typedef struct {
-    UA_Server *server;
+    UA_ServerNetworkLayer layer;
 	UA_ConnectionConfig conf;
 	fd_set fdset;
 	UA_Int32 serversockfd;
     UA_UInt32 port;
-    UA_Logger *logger;
 } ServerNetworkLayerUDP;
 
 /** Accesses only the sockfd in the handle. Can be run from parallel threads. */
-static void writeCallbackUDP(UDPConnection *handle, UA_ByteStringArray gather_buf) {
-	UA_UInt32 total_len = 0, nWritten = 0;
-	struct iovec iov[gather_buf.stringsSize];
-	for(UA_UInt32 i=0;i<gather_buf.stringsSize;i++) {
-		iov[i] = (struct iovec) {.iov_base = gather_buf.strings[i].data,
-                                 .iov_len = gather_buf.strings[i].length};
-		total_len += gather_buf.strings[i].length;
-	}
+static UA_StatusCode sendUDP(UA_Connection *connection, UA_ByteString *buf) {
+    UDPConnection *udpc = (UDPConnection*)connection;
+    ServerNetworkLayerUDP *layer = (ServerNetworkLayerUDP*)connection->handle;
+	size_t nWritten = 0;
 	struct sockaddr_in *sin = NULL;
-	if (handle->from.sa_family == AF_INET) {
+
+	if (udpc->from.sa_family == AF_INET) {
 #if ((__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4 || defined(__clang__))
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wcast-align"
 #endif
-	    sin = (struct sockaddr_in *) &(handle->from);
+	    sin = (struct sockaddr_in *) &udpc->from;
 #if ((__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4 || defined(__clang__))
 #pragma GCC diagnostic pop
 #endif
 	} else {
-		//FIXME:
-		return;
-	}
-	struct msghdr message = {.msg_name = sin, .msg_namelen = handle->fromlen, .msg_iov = iov,
-							 .msg_iovlen = gather_buf.stringsSize, .msg_control = NULL,
-							 .msg_controllen = 0, .msg_flags = 0};
-	while (nWritten < total_len) {
-		UA_Int32 n = 0;
-		do {
-            n = sendmsg(((ServerNetworkLayerUDP*)handle->connection.handle)->serversockfd, &message, 0);
-            if(n == -1L) {
-            	printf("ERROR:%i\n", errno);
-            }
-        } while (n == -1L && errno == EINTR);
+        UA_ByteString_deleteMembers(buf);
+		return UA_STATUSCODE_BADINTERNALERROR;
+    }
+
+	while (nWritten < (size_t)buf->length) {
+		UA_Int32 n = sendto(layer->serversockfd, buf->data, buf->length, 0,
+                            (struct sockaddr*)sin, sizeof(struct sockaddr_in));
+        if(n == -1L) {
+            UA_LOG_WARNING(layer->layer.logger, UA_LOGCATEGORY_NETWORK, "UDP send error %i", errno);
+            UA_ByteString_deleteMembers(buf);
+            return UA_STATUSCODE_BADINTERNALERROR;
+        }
         nWritten += n;
 	}
+    UA_ByteString_deleteMembers(buf);
+    return UA_STATUSCODE_GOOD;
 }
 
 static UA_StatusCode socket_set_nonblocking(UA_Int32 sockfd) {
@@ -120,14 +116,15 @@ static void setFDSet(ServerNetworkLayerUDP *layer) {
 	FD_SET(layer->serversockfd, &layer->fdset);
 }
 
-static void closeConnectionUDP(UDPConnection *handle) {
+static void closeConnectionUDP(UA_Connection *handle) {
 	free(handle);
 }
 
-static UA_StatusCode ServerNetworkLayerUDP_start(ServerNetworkLayerUDP *layer, UA_Logger *logger) {
-    layer->logger = logger;
-    if((layer->serversockfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
-		perror("ERROR opening socket");
+static UA_StatusCode ServerNetworkLayerUDP_start(ServerNetworkLayerUDP *layer, UA_Logger logger) {
+    layer->layer.logger = logger;
+    layer->serversockfd = socket(PF_INET, SOCK_DGRAM, 0);
+    if(layer->serversockfd < 0) {
+		UA_LOG_WARNING(layer->layer.logger, UA_LOGCATEGORY_NETWORK, "Error opening socket");
 		return UA_STATUSCODE_BADINTERNALERROR;
 	} 
 	const struct sockaddr_in serv_addr =
@@ -135,98 +132,98 @@ static UA_StatusCode ServerNetworkLayerUDP_start(ServerNetworkLayerUDP *layer, U
          .sin_port = htons(layer->port), .sin_zero = {0}};
 	int optval = 1;
 	if(setsockopt(layer->serversockfd, SOL_SOCKET,
-                  SO_REUSEADDR, (const char *)&optval,
-                  sizeof(optval)) == -1) {
-		perror("setsockopt");
+                  SO_REUSEADDR, (const char *)&optval, sizeof(optval)) == -1) {
+        UA_LOG_WARNING(layer->layer.logger, UA_LOGCATEGORY_NETWORK, "Could not setsockopt");
 		CLOSESOCKET(layer->serversockfd);
 		return UA_STATUSCODE_BADINTERNALERROR;
 	}
 	if(bind(layer->serversockfd, (const struct sockaddr *)&serv_addr,
             sizeof(serv_addr)) < 0) {
-		perror("binding");
+        UA_LOG_WARNING(layer->layer.logger, UA_LOGCATEGORY_NETWORK, "Could not bind the socket");
 		CLOSESOCKET(layer->serversockfd);
 		return UA_STATUSCODE_BADINTERNALERROR;
 	}
 	socket_set_nonblocking(layer->serversockfd);
-    printf("Listening for UDP connections on %s:%d", inet_ntoa(serv_addr.sin_addr),
-           ntohs(serv_addr.sin_port));
+    UA_LOG_WARNING(layer->layer.logger, UA_LOGCATEGORY_NETWORK, "Listening for UDP connections on %s:%d",
+                   inet_ntoa(serv_addr.sin_addr), ntohs(serv_addr.sin_port));
     return UA_STATUSCODE_GOOD;
 }
 
-static UA_Int32 ServerNetworkLayerUDP_getWork(ServerNetworkLayerUDP *layer, UA_WorkItem **workItems,
-                                              UA_UInt16 timeout) {
-    UA_WorkItem *items = NULL;
+static size_t ServerNetworkLayerUDP_getJobs(ServerNetworkLayerUDP *layer, UA_Job **jobs, UA_UInt16 timeout) {
+    UA_Job *items = NULL;
     setFDSet(layer);
     struct timeval tmptv = {0, timeout};
     UA_Int32 resultsize = select(layer->serversockfd+1, &layer->fdset, NULL, NULL, &tmptv);
     if(resultsize <= 0 || !FD_ISSET(layer->serversockfd, &layer->fdset)) {
-        *workItems = items;
+        *jobs = items;
         return 0;
     }
-    items = malloc(sizeof(UA_WorkItem)*(resultsize));
+    items = malloc(sizeof(UA_Job)*resultsize);
 	// read from established sockets
     UA_Int32 j = 0;
 	UA_ByteString buf = {-1, NULL};
-		if(!buf.data) {
-			buf.data = malloc(sizeof(UA_Byte) * layer->conf.recvBufferSize);
-			if(!buf.data)
-				printf("malloc failed");
-		}
-		struct sockaddr sender;
-		socklen_t sendsize = sizeof(sender);
-		bzero(&sender, sizeof(sender));
-        buf.length = recvfrom(layer->serversockfd, buf.data, layer->conf.recvBufferSize, 0, &sender, &sendsize);
-        if (buf.length <= 0) {
-        } else {
-            UDPConnection *c = malloc(sizeof(UDPConnection));
-        	if(!c)
-        		return UA_STATUSCODE_BADINTERNALERROR;
-            c->from = sender;
-            c->fromlen = sendsize;
-            c->connection.state = UA_CONNECTION_OPENING;
-            c->connection.localConf = layer->conf;
-            c->connection.channel = NULL;
-            c->connection.close = (void (*)(UA_Connection*))closeConnectionUDP;
-            c->connection.write = (UA_StatusCode (*)(UA_Connection*, UA_ByteStringArray))writeCallbackUDP;
-            c->connection.getBuffer = GetMallocedBuffer;
-            c->connection.releaseBuffer = ReleaseMallocedBuffer;
-            c->connection.handle = layer;
-            items[j].type = UA_WORKITEMTYPE_BINARYMESSAGE;
-            items[j].work.binaryMessage.message = buf;
-            items[j].work.binaryMessage.connection = (UA_Connection*)c;
-            buf.data = NULL;
-            j++;
-        }
+    if(!buf.data) {
+        buf.data = malloc(sizeof(UA_Byte) * layer->conf.recvBufferSize);
+        if(!buf.data)
+            UA_LOG_WARNING(layer->layer.logger, UA_LOGCATEGORY_NETWORK, "malloc failed");
+    }
+    struct sockaddr sender;
+    socklen_t sendsize = sizeof(sender);
+    bzero(&sender, sizeof(sender));
+    buf.length = recvfrom(layer->serversockfd, buf.data, layer->conf.recvBufferSize, 0, &sender, &sendsize);
+    if (buf.length <= 0) {
+    } else {
+        UDPConnection *c = malloc(sizeof(UDPConnection));
+        if(!c)
+            return UA_STATUSCODE_BADINTERNALERROR;
+        UA_Connection_init(&c->connection);
+        c->from = sender;
+        c->fromlen = sendsize;
+        // c->sockfd = newsockfd;
+        c->connection.getSendBuffer = GetMallocedBuffer;
+        c->connection.releaseSendBuffer = ReleaseMallocedBuffer;
+        c->connection.releaseRecvBuffer = ReleaseMallocedBuffer;
+        c->connection.handle = layer;
+        c->connection.send = sendUDP;
+        c->connection.close = closeConnectionUDP;
+        c->connection.localConf = layer->conf;
+        c->connection.state = UA_CONNECTION_OPENING;
+
+        items[j].type = UA_JOBTYPE_BINARYMESSAGE_NETWORKLAYER;
+        items[j].job.binaryMessage.message = buf;
+        items[j].job.binaryMessage.connection = (UA_Connection*)c;
+        buf.data = NULL;
+        j++;
+    }
     if(buf.data)
         free(buf.data);
     if(j == 0) {
         free(items);
-        *workItems = NULL;
+        *jobs = NULL;
     } else
-        *workItems = items;
+        *jobs = items;
     return j;
 }
 
-static UA_Int32 ServerNetworkLayerUDP_stop(ServerNetworkLayerUDP * layer, UA_WorkItem **workItems) {
+static UA_Int32 ServerNetworkLayerUDP_stop(ServerNetworkLayerUDP * layer, UA_Job **jobs) {
 	CLOSESOCKET(layer->serversockfd);
 	return 0;
 }
 
-static void ServerNetworkLayerUDP_delete(ServerNetworkLayerUDP *layer) {
-	free(layer);
+static void ServerNetworkLayerUDP_deleteMembers(ServerNetworkLayerUDP *layer) {
 }
 
-UA_ServerNetworkLayer ServerNetworkLayerUDP_new(UA_ConnectionConfig conf, UA_UInt32 port) {
-    ServerNetworkLayerUDP *udplayer = malloc(sizeof(ServerNetworkLayerUDP));
-	udplayer->conf = conf;
-    udplayer->port = port;
-
-    UA_ServerNetworkLayer nl;
-    nl.nlHandle = udplayer;
-    nl.start = (UA_StatusCode (*)(void*, UA_Logger *logger))ServerNetworkLayerUDP_start;
-    nl.getWork = (UA_Int32 (*)(void*, UA_WorkItem**, UA_UInt16)) ServerNetworkLayerUDP_getWork;
-    nl.stop = (UA_Int32 (*)(void*, UA_WorkItem**)) ServerNetworkLayerUDP_stop;
-    nl.free = (void (*)(void*))ServerNetworkLayerUDP_delete;
-    nl.discoveryUrl = NULL;
-    return nl;
+UA_ServerNetworkLayer * ServerNetworkLayerUDP_new(UA_ConnectionConfig conf, UA_UInt32 port) {
+    ServerNetworkLayerUDP *layer = malloc(sizeof(ServerNetworkLayerUDP));
+    if(!layer)
+        return NULL;
+    memset(layer, 0, sizeof(ServerNetworkLayerUDP));
+
+	layer->conf = conf;
+    layer->port = port;
+    layer->layer.start = (UA_StatusCode(*)(UA_ServerNetworkLayer*,UA_Logger))ServerNetworkLayerUDP_start;
+    layer->layer.getJobs = (size_t(*)(UA_ServerNetworkLayer*,UA_Job**,UA_UInt16))ServerNetworkLayerUDP_getJobs;
+    layer->layer.stop = (size_t(*)(UA_ServerNetworkLayer*, UA_Job**))ServerNetworkLayerUDP_stop;
+    layer->layer.deleteMembers = (void(*)(UA_ServerNetworkLayer*))ServerNetworkLayerUDP_deleteMembers;
+    return &layer->layer;
 }

+ 6 - 6
examples/networklayer_udp.h

@@ -10,14 +10,14 @@
 extern "C" {
 #endif
 
-#ifdef NOT_AMALGATED
-# include "ua_server.h"
-#else
-# include "open62541.h"
-#endif
+#include "ua_server.h"
+#include "ua_client.h"
 
 /** @brief Create the UDP networklayer and listen to the specified port */
-UA_ServerNetworkLayer ServerNetworkLayerUDP_new(UA_ConnectionConfig conf, UA_UInt32 port);
+UA_ServerNetworkLayer UA_EXPORT * ServerNetworkLayerUDP_new(UA_ConnectionConfig conf, UA_UInt32 port);
+
+UA_Connection UA_EXPORT
+ClientNetworkLayerUDP_connect(UA_ConnectionConfig conf, char endpointUrl[], UA_Logger logger);
 
 #ifdef __cplusplus
 } // extern "C"

+ 247 - 234
examples/server.c

@@ -41,28 +41,25 @@
 /****************************/
 
 UA_Boolean running = 1;
-UA_Logger logger;
+UA_Logger logger = Logger_Stdout;
 
 /*************************/
 /* Read-only data source */
 /*************************/
-static UA_StatusCode readTimeData(void *handle, UA_Boolean sourceTimeStamp, const UA_NumericRange *range, UA_DataValue *value) {
+static UA_StatusCode
+readTimeData(void *handle, const UA_NodeId nodeId, UA_Boolean sourceTimeStamp,
+             const UA_NumericRange *range, UA_DataValue *value) {
     if(range) {
         value->hasStatus = UA_TRUE;
         value->status = UA_STATUSCODE_BADINDEXRANGEINVALID;
         return UA_STATUSCODE_GOOD;
     }
-	UA_DateTime *currentTime = UA_DateTime_new();
-	if(!currentTime)
-		return UA_STATUSCODE_BADOUTOFMEMORY;
-	*currentTime = UA_DateTime_now();
-	value->value.type = &UA_TYPES[UA_TYPES_DATETIME];
-	value->value.arrayLength = -1;
-	value->value.data = currentTime;
+	UA_DateTime currentTime = UA_DateTime_now();
+    UA_Variant_setScalarCopy(&value->value, &currentTime, &UA_TYPES[UA_TYPES_DATETIME]);
 	value->hasValue = UA_TRUE;
 	if(sourceTimeStamp) {
 		value->hasSourceTimestamp = UA_TRUE;
-		value->sourceTimestamp = *currentTime;
+		value->sourceTimestamp = currentTime;
 	}
 	return UA_STATUSCODE_GOOD;
 }
@@ -72,35 +69,29 @@ static UA_StatusCode readTimeData(void *handle, UA_Boolean sourceTimeStamp, cons
 /*      Only on Linux        */
 /*****************************/
 FILE* temperatureFile = NULL;
-static UA_StatusCode readTemperature(void *handle, UA_Boolean sourceTimeStamp, const UA_NumericRange *range, UA_DataValue *value) {
+static UA_StatusCode
+readTemperature(void *handle, const UA_NodeId nodeId, UA_Boolean sourceTimeStamp,
+                const UA_NumericRange *range, UA_DataValue *value) {
     if(range) {
         value->hasStatus = UA_TRUE;
         value->status = UA_STATUSCODE_BADINDEXRANGEINVALID;
         return UA_STATUSCODE_GOOD;
     }
 
-	UA_Double* currentTemperature = UA_Double_new();
-
-	if(!currentTemperature)
-		return UA_STATUSCODE_BADOUTOFMEMORY;
-
 	rewind(temperatureFile);
 	fflush(temperatureFile);
 
-	if(fscanf(temperatureFile, "%lf", currentTemperature) != 1){
+	UA_Double currentTemperature;
+	if(fscanf(temperatureFile, "%lf", &currentTemperature) != 1){
 		UA_LOG_WARNING(logger, UA_LOGCATEGORY_USERLAND, "Can not parse temperature");
 		exit(1);
 	}
 
-	*currentTemperature /= 1000.0;
+	currentTemperature /= 1000.0;
 
 	value->sourceTimestamp = UA_DateTime_now();
 	value->hasSourceTimestamp = UA_TRUE;
-	value->value.type = &UA_TYPES[UA_TYPES_DOUBLE];
-	value->value.arrayLength = -1;
-	value->value.data = currentTemperature;
-	value->value.arrayDimensionsSize = -1;
-	value->value.arrayDimensions = NULL;
+    UA_Variant_setScalarCopy(&value->value, &currentTemperature, &UA_TYPES[UA_TYPES_DOUBLE]);
 	value->hasValue = UA_TRUE;
 	return UA_STATUSCODE_GOOD;
 }
@@ -115,24 +106,29 @@ FILE* triggerFile = NULL;
 FILE* ledFile = NULL;
 UA_Boolean ledStatus = 0;
 
-static UA_StatusCode readLedStatus(void *handle, UA_Boolean sourceTimeStamp, const UA_NumericRange *range, UA_DataValue *value) {
-  if(range)
-    return UA_STATUSCODE_BADINDEXRANGEINVALID;
+static UA_StatusCode
+readLedStatus(void *handle, UA_NodeId nodeid, UA_Boolean sourceTimeStamp,
+              const UA_NumericRange *range, UA_DataValue *value) {
+    if(range)
+        return UA_STATUSCODE_BADINDEXRANGEINVALID;
 
-  value->hasValue = UA_TRUE;
-  UA_StatusCode retval = UA_Variant_setScalarCopy(&value->value, &ledStatus, &UA_TYPES[UA_TYPES_BOOLEAN]);
+    value->hasValue = UA_TRUE;
+    UA_StatusCode retval = UA_Variant_setScalarCopy(&value->value, &ledStatus,
+                                                    &UA_TYPES[UA_TYPES_BOOLEAN]);
 
-  if(retval != UA_STATUSCODE_GOOD)
-    return retval;
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
   
-  if(sourceTimeStamp) {
-          value->sourceTimestamp = UA_DateTime_now();
-          value->hasSourceTimestamp = UA_TRUE;
-  }
-  return UA_STATUSCODE_GOOD;
+    if(sourceTimeStamp) {
+        value->sourceTimestamp = UA_DateTime_now();
+        value->hasSourceTimestamp = UA_TRUE;
+    }
+    return UA_STATUSCODE_GOOD;
 }
 
-static UA_StatusCode writeLedStatus(void *handle, const UA_Variant *data, const UA_NumericRange *range) {
+static UA_StatusCode
+writeLedStatus(void *handle, const UA_NodeId nodeid,
+               const UA_Variant *data, const UA_NumericRange *range) {
     if(range)
         return UA_STATUSCODE_BADINDEXRANGEINVALID;
 
@@ -159,9 +155,11 @@ static UA_StatusCode writeLedStatus(void *handle, const UA_Variant *data, const
 }
 
 #ifdef ENABLE_METHODCALLS
-static UA_StatusCode getMonitoredItems(const UA_NodeId objectId, const UA_Variant *input, UA_Variant *output, void *handle) {
+static UA_StatusCode
+getMonitoredItems(void *methodHandle, const UA_NodeId objectId,
+                  size_t inputSize, const UA_Variant *input,
+                  size_t outputSize, UA_Variant *output) {
     UA_String tmp = UA_STRING("Hello World");
-    //UA_Server *theServer = (UA_Server *) handle; // Commented, would result in "unused variable" error
     UA_Variant_setScalarCopy(output, &tmp, &UA_TYPES[UA_TYPES_STRING]);
     printf("getMonitoredItems was called\n");
     return UA_STATUSCODE_GOOD;
@@ -169,7 +167,7 @@ static UA_StatusCode getMonitoredItems(const UA_NodeId objectId, const UA_Varian
 #endif
 
 static void stopHandler(int sign) {
-    UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "Received Ctrl-C\n");
+    UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "Received Ctrl-C");
 	running = 0;
 }
 
@@ -197,234 +195,249 @@ static UA_ByteString loadCertificate(void) {
     return certificate;
 }
 
-UA_StatusCode nodeIter(UA_NodeId childId, UA_Boolean isInverse, UA_NodeId referenceTypeId, void *handle);
-UA_StatusCode nodeIter(UA_NodeId childId, UA_Boolean isInverse, UA_NodeId referenceTypeId, void *handle) {  
-  /*printf("References ns=%d;i=%d using i=%d ", childId.namespaceIndex, childId.identifier.numeric, referenceTypeId.identifier.numeric);
-  if (isInverse == UA_TRUE) {
-    printf(" (inverse)");
-  }
-  printf("\n");*/
-  
-  return UA_STATUSCODE_GOOD;
+static UA_StatusCode
+nodeIter(UA_NodeId childId, UA_Boolean isInverse, UA_NodeId referenceTypeId, void *handle) {  
+    return UA_STATUSCODE_GOOD;
 }
 
 int main(int argc, char** argv) {
-  signal(SIGINT, stopHandler)   /* catches ctrl-c */
+    signal(SIGINT, stopHandler); /* catches ctrl-c */
 #ifdef UA_MULTITHREADING
-  pthread_rwlock_init(&writeLock, 0);
+    pthread_rwlock_init(&writeLock, 0);
 #endif
 
-  UA_Server *server = UA_Server_new(UA_ServerConfig_standard);
-  logger = Logger_Stdout_new();
-  UA_Server_setLogger(server, logger);
-  UA_ByteString certificate = loadCertificate();
-  UA_Server_setServerCertificate(server, certificate);
-  UA_ByteString_deleteMembers(&certificate);
-  UA_Server_addNetworkLayer(server, ServerNetworkLayerTCP_new(UA_ConnectionConfig_standard, 16664));
-
-  // add node with the datetime data source
-  UA_NodeId nodeId_currentTime;
-  UA_DataSource dateDataSource = (UA_DataSource) {.handle = NULL, .read = readTimeData, .write = NULL};
-  const UA_QualifiedName dateName = UA_QUALIFIEDNAME(1, "current time");
-  const UA_LocalizedText dateNameBrowseName = UA_LOCALIZEDTEXT("en_US","current time");
-  UA_Server_addDataSourceVariableNode(server, UA_NODEID_NULL, dateName, dateNameBrowseName, dateNameBrowseName, 0, 0,
-
-                                  UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
-                                  UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
-
-                                  dateDataSource,
-
-                                  &nodeId_currentTime);
+    UA_Server *server = UA_Server_new(UA_ServerConfig_standard);
+    UA_Server_setLogger(server, logger);
+    UA_ByteString certificate = loadCertificate();
+    UA_Server_setServerCertificate(server, certificate);
+    UA_ByteString_deleteMembers(&certificate);
+    UA_Server_addNetworkLayer(server, ServerNetworkLayerTCP_new(UA_ConnectionConfig_standard, 16664));
+
+    // add node with the datetime data source
+    UA_DataSource dateDataSource = (UA_DataSource) {.handle = NULL, .read = readTimeData, .write = NULL};
+    UA_VariableAttributes v_attr;
+    UA_VariableAttributes_init(&v_attr);
+    v_attr.description = UA_LOCALIZEDTEXT("en_US","current time");
+    v_attr.displayName = UA_LOCALIZEDTEXT("en_US","current time");
+    const UA_QualifiedName dateName = UA_QUALIFIEDNAME(1, "current time");
+    UA_NodeId dataSourceId;
+    UA_Server_addDataSourceVariableNode(server, UA_NODEID_NULL,
+                                        UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+                                        UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), dateName,
+                                        UA_NODEID_NULL, v_attr, dateDataSource, &dataSourceId);
 
-  // Get and reattach the datasource
-  UA_DataSource *dataSourceCopy = NULL;
-  UA_Server_getAttribute_DataSource(server, nodeId_currentTime, &dataSourceCopy);
-  if (dataSourceCopy == NULL)
-    UA_LOG_WARNING(logger, UA_LOGCATEGORY_USERLAND, "The returned dataSource is invalid");
-  else if (dataSourceCopy->read != dateDataSource.read)
-    UA_LOG_WARNING(logger, UA_LOGCATEGORY_USERLAND, "The returned dataSource is not the same as we set?");
-  else
-    UA_Server_setAttribute_DataSource(server, nodeId_currentTime, dataSourceCopy);
-  free(dataSourceCopy);
 #ifndef _WIN32
-  //cpu temperature monitoring for linux machines
-  if((temperatureFile = fopen("/sys/class/thermal/thermal_zone0/temp", "r"))) {
-          // add node with the data source
-          UA_DataSource temperatureDataSource = (UA_DataSource) {.handle = NULL, .read = readTemperature, .write = NULL};
-          const UA_QualifiedName tempName = UA_QUALIFIEDNAME(1, "cpu temperature");
-          const UA_LocalizedText tempNameBrowseName = UA_LOCALIZEDTEXT("en_US","temperature");
-          UA_Server_addDataSourceVariableNode(server, UA_NODEID_NULL, tempName, tempNameBrowseName, tempNameBrowseName, 0, 0,
-
+    /* cpu temperature monitoring for linux machines */
+    const char *temperatureFileName = "/sys/class/thermal/thermal_zone0/temp"; // RaspberryPi
+    // const char *temperatureFileName = "/sys/class/hwmon/hwmon0/device/temp1_input"; // Beaglebone
+    // const char *temperatureFileName = "/sys/class/thermal/thermal_zone3/temp"; // Intel Edison Alternative 1
+    // const char *temperatureFileName = "/sys/class/thermal/thermal_zone4/temp"; // Intel Edison Alternative 2
+    if((temperatureFile = fopen(temperatureFileName, "r"))) {
+        // add node with the data source
+        UA_DataSource temperatureDataSource = (UA_DataSource) {
+            .handle = NULL, .read = readTemperature, .write = NULL};
+        const UA_QualifiedName tempName = UA_QUALIFIEDNAME(1, "cpu temperature");
+        UA_VariableAttributes_init(&v_attr);
+        v_attr.description = UA_LOCALIZEDTEXT("en_US","temperature");
+        v_attr.displayName = UA_LOCALIZEDTEXT("en_US","temperature");
+        UA_Server_addDataSourceVariableNode(server, UA_NODEID_NULL,
                                             UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
-                                            UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
-
-                                            temperatureDataSource,
-
-                                            NULL);
-  }
-
-  //LED control for rpi
-  if(access("/sys/class/leds/led0/trigger", F_OK ) != -1 || access("/sys/class/leds/led0/brightness", F_OK ) != -1) {
-    if((triggerFile = fopen("/sys/class/leds/led0/trigger", "w")) && (ledFile = fopen("/sys/class/leds/led0/brightness", "w"))) {
-      //setting led mode to manual
-      fprintf(triggerFile, "%s", "none");
-      fflush(triggerFile);
-
-      //turning off led initially
-      fprintf(ledFile, "%s", "1");
-      fflush(ledFile);
-
-      // add node with the LED status data source
-      UA_DataSource ledStatusDataSource = (UA_DataSource) {.handle = NULL, .read = readLedStatus, .write = writeLedStatus};
-      const UA_QualifiedName statusName = UA_QUALIFIEDNAME(0, "status LED");
-      const UA_LocalizedText statusNameBrowseName = UA_LOCALIZEDTEXT("en_US","status LED");
-      UA_Server_addDataSourceVariableNode(server, UA_NODEID_NULL, statusName, statusNameBrowseName, statusNameBrowseName, 0, 0,
-
-                                        UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
-                                        UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
-
-                                        ledStatusDataSource,
+                                            UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), tempName,
+                                            UA_NODEID_NULL, v_attr, temperatureDataSource, NULL);
+    }
 
-                                        NULL);
-    } else {
-      UA_LOG_WARNING(logger, UA_LOGCATEGORY_USERLAND, "[Raspberry Pi] LED file exist, but I have no access (try to run server with sudo)");
+    /* LED control for rpi */
+    if(access("/sys/class/leds/led0/trigger", F_OK ) != -1 ||
+       access("/sys/class/leds/led0/brightness", F_OK ) != -1) {
+        if((triggerFile = fopen("/sys/class/leds/led0/trigger", "w")) &&
+           (ledFile = fopen("/sys/class/leds/led0/brightness", "w"))) {
+            //setting led mode to manual
+            fprintf(triggerFile, "%s", "none");
+            fflush(triggerFile);
+
+            //turning off led initially
+            fprintf(ledFile, "%s", "1");
+            fflush(ledFile);
+
+            // add node with the LED status data source
+            UA_DataSource ledStatusDataSource = (UA_DataSource) {
+                .handle = NULL, .read = readLedStatus, .write = writeLedStatus};
+            UA_VariableAttributes_init(&v_attr);
+            v_attr.description = UA_LOCALIZEDTEXT("en_US","status LED");
+            v_attr.displayName = UA_LOCALIZEDTEXT("en_US","status LED");
+            const UA_QualifiedName statusName = UA_QUALIFIEDNAME(0, "status LED");
+            UA_Server_addDataSourceVariableNode(server, UA_NODEID_NULL,
+                                                UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+                                                UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), statusName,
+                                                UA_NODEID_NULL, v_attr, ledStatusDataSource, NULL);
+        } else
+            UA_LOG_WARNING(logger, UA_LOGCATEGORY_USERLAND,
+                           "[Raspberry Pi] LED file exist, but is not accessible (try to run server with sudo)");
     }
-  }
 #endif
 
-  // add a static variable node to the adresspace
-  UA_Variant *myIntegerVariant = UA_Variant_new();
-  UA_Int32 myInteger = 42;
-  UA_Variant_setScalarCopy(myIntegerVariant, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
-  const UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer");
-  const UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
-  UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
-  UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
-  UA_Server_addVariableNode(server, myIntegerNodeId, myIntegerName, UA_LOCALIZEDTEXT("en_US", "the answer"), UA_LOCALIZEDTEXT("en_US", "the answer"),  0, 0,
-                            parentNodeId, parentReferenceNodeId, myIntegerVariant, NULL);
-  /**************/
-  /* Demo Nodes */
-  /**************/
+    // add a static variable node to the adresspace
+    UA_VariableAttributes myVar;
+    UA_VariableAttributes_init(&myVar);
+    myVar.description = UA_LOCALIZEDTEXT("en_US", "the answer");
+    myVar.displayName = UA_LOCALIZEDTEXT("en_US", "the answer");
+    UA_Int32 myInteger = 42;
+    UA_Variant_setScalarCopy(&myVar.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
+    const UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer");
+    const UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
+    UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
+    UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
+    UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId, parentReferenceNodeId,
+                              myIntegerName, UA_NODEID_NULL, myVar, NULL);
+    UA_Variant_deleteMembers(&myVar.value);
+
+    /**************/
+    /* Demo Nodes */
+    /**************/
 
 #define DEMOID 50000
-  UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, DEMOID), UA_QUALIFIEDNAME(1, "Demo"), UA_LOCALIZEDTEXT("en_US","Demo"),
-                          UA_LOCALIZEDTEXT("en_US","Demo"), 0, 0, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
-                          UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), NULL);
+    UA_ObjectAttributes object_attr;
+    UA_ObjectAttributes_init(&object_attr);
+    object_attr.description = UA_LOCALIZEDTEXT("en_US","Demo");
+    object_attr.displayName = UA_LOCALIZEDTEXT("en_US","Demo");
+    UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, DEMOID),
+                            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), object_attr, NULL);
 
 #define SCALARID 50001
-  UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, SCALARID), UA_QUALIFIEDNAME(1, "Scalar"), UA_LOCALIZEDTEXT("en_US","Scalar"),
-                          UA_LOCALIZEDTEXT("en_US","Scalar"), 0, 0, UA_NODEID_NUMERIC(1, DEMOID), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
-                          UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), NULL);
+    object_attr.description = UA_LOCALIZEDTEXT("en_US","Scalar");
+    object_attr.displayName = UA_LOCALIZEDTEXT("en_US","Scalar");
+    UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, SCALARID),
+                            UA_NODEID_NUMERIC(1, DEMOID), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
+                            UA_QUALIFIEDNAME(1, "Scalar"),
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), object_attr, NULL);
 
 #define ARRAYID 50002
-  UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, ARRAYID), UA_QUALIFIEDNAME(1, "Array"), UA_LOCALIZEDTEXT("en_US","Array"),
-                          UA_LOCALIZEDTEXT("en_US","Array"), 0, 0, UA_NODEID_NUMERIC(1, DEMOID), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
-                          UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), NULL);
+    object_attr.description = UA_LOCALIZEDTEXT("en_US","Array");
+    object_attr.displayName = UA_LOCALIZEDTEXT("en_US","Array");
+    UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, ARRAYID),
+                            UA_NODEID_NUMERIC(1, DEMOID), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
+                            UA_QUALIFIEDNAME(1, "Array"),
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), object_attr, NULL);
 
 #define MATRIXID 50003
-  UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, MATRIXID), UA_QUALIFIEDNAME(1, "Matrix"), UA_LOCALIZEDTEXT("en_US","Matrix"),
-                          UA_LOCALIZEDTEXT("en_US","Matrix"), 0, 0, UA_NODEID_NUMERIC(1, DEMOID), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
-                          UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), NULL);
-
-  UA_UInt32 id = 51000; //running id in namespace 0
-  for(UA_UInt32 type = 0; UA_IS_BUILTIN(type); type++) {
-    if(type == UA_TYPES_VARIANT || type == UA_TYPES_DIAGNOSTICINFO)
-        continue;
-    //add a scalar node for every built-in type
-    void *value = UA_new(&UA_TYPES[type]);
-    UA_Variant *variant = UA_Variant_new();
-    UA_Variant_setScalar(variant, value, &UA_TYPES[type]);
-    char name[15];
-    sprintf(name, "%02d", type);
-    UA_QualifiedName qualifiedName = UA_QUALIFIEDNAME(1, name);
-    UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, ++id), qualifiedName, UA_LOCALIZEDTEXT("en_US",name), UA_LOCALIZEDTEXT("en_US",name), 0, 0,
-                              UA_NODEID_NUMERIC(1, SCALARID), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), variant, NULL);
-
-    //add an array node for every built-in type
-    UA_Variant *arrayvar = UA_Variant_new();
-    UA_Variant_setArray(arrayvar, UA_Array_new(&UA_TYPES[type], 10), 10, &UA_TYPES[type]);
-    UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, ++id), qualifiedName, UA_LOCALIZEDTEXT("en_US",name), UA_LOCALIZEDTEXT("en_US",name), 0, 0,
-                              UA_NODEID_NUMERIC(1, ARRAYID), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), arrayvar, NULL);
-
-    //add an matrix node for every built-in type
-    arrayvar = UA_Variant_new();
-    void* myMultiArray = UA_Array_new(&UA_TYPES[type],9);
-    arrayvar->arrayDimensions = UA_Array_new(&UA_TYPES[UA_TYPES_INT32],2);
-    arrayvar->arrayDimensions[0] = 3;
-    arrayvar->arrayDimensions[1] = 3;
-    arrayvar->arrayDimensionsSize = 2;
-    arrayvar->arrayLength = 9;
-    arrayvar->data = myMultiArray;
-    arrayvar->type = &UA_TYPES[type];
-    UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, ++id), qualifiedName, UA_LOCALIZEDTEXT("en_US",""), UA_LOCALIZEDTEXT("en_US",""),
-                              0, 0, UA_NODEID_NUMERIC(1, MATRIXID), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), arrayvar, NULL);
-  }
+    object_attr.description = UA_LOCALIZEDTEXT("en_US","Matrix");
+    object_attr.displayName = UA_LOCALIZEDTEXT("en_US","Matrix");
+    UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, MATRIXID), UA_NODEID_NUMERIC(1, DEMOID),
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_QUALIFIEDNAME(1, "Matrix"),
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), object_attr, NULL);
+
+    UA_UInt32 id = 51000; // running id in namespace 0
+    for(UA_UInt32 type = 0; type < UA_TYPES_DIAGNOSTICINFO; type++) {
+        if(type == UA_TYPES_VARIANT || type == UA_TYPES_DIAGNOSTICINFO)
+            continue;
+
+        UA_VariableAttributes attr;
+        UA_VariableAttributes_init(&attr);
+        char name[15];
+        sprintf(name, "%02d", type);
+        attr.displayName = UA_LOCALIZEDTEXT("en_US",name);
+        UA_QualifiedName qualifiedName = UA_QUALIFIEDNAME(1, name);
+
+        /* add a scalar node for every built-in type */
+        void *value = UA_new(&UA_TYPES[type]);
+        UA_Variant_setScalar(&attr.value, value, &UA_TYPES[type]);
+        UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, ++id),
+                                  UA_NODEID_NUMERIC(1, SCALARID),
+                                  UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
+                                  qualifiedName, UA_NODEID_NULL, attr, NULL);
+        UA_Variant_deleteMembers(&attr.value);
+
+        /* add an array node for every built-in type */
+        UA_Variant_setArray(&attr.value, UA_Array_new(10, &UA_TYPES[type]),
+                            10, &UA_TYPES[type]);
+        UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, ++id),
+                                  UA_NODEID_NUMERIC(1, ARRAYID),
+                                  UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
+                                  qualifiedName, UA_NODEID_NULL, attr, NULL);
+        UA_Variant_deleteMembers(&attr.value);
+
+        /* add an matrix node for every built-in type */
+        void* myMultiArray = UA_Array_new(9, &UA_TYPES[type]);
+        attr.value.arrayDimensions = UA_Array_new(2, &UA_TYPES[UA_TYPES_INT32]);
+        attr.value.arrayDimensions[0] = 3;
+        attr.value.arrayDimensions[1] = 3;
+        attr.value.arrayDimensionsSize = 2;
+        attr.value.arrayLength = 9;
+        attr.value.data = myMultiArray;
+        attr.value.type = &UA_TYPES[type];
+        UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, ++id),
+                                  UA_NODEID_NUMERIC(1, MATRIXID),
+                                  UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
+                                  qualifiedName, UA_NODEID_NULL, attr, NULL);
+        UA_Variant_deleteMembers(&attr.value);
+    }
 
 #ifdef ENABLE_METHODCALLS
-  UA_Argument inputArguments;
-  UA_Argument_init(&inputArguments);
-  inputArguments.arrayDimensionsSize = -1;
-  inputArguments.arrayDimensions = NULL;
-  inputArguments.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
-  inputArguments.description = UA_LOCALIZEDTEXT("en_US", "A String");
-  inputArguments.name = UA_STRING("Input an integer");
-  inputArguments.valueRank = -1;
-
-  UA_Argument outputArguments;
-  UA_Argument_init(&outputArguments);
-  outputArguments.arrayDimensionsSize = -1;
-  outputArguments.arrayDimensions = NULL;
-  outputArguments.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
-  outputArguments.description = UA_LOCALIZEDTEXT("en_US", "A String");
-  outputArguments.name = UA_STRING("Input an integer");
-  outputArguments.valueRank = -1;
-
-  UA_NodeId methodId; // Retrieve the actual ID if this node if a random id as in UA_NODEID_NUMERIC(1,0) is used
-  UA_Server_addMethodNode(server, UA_NODEID_NUMERIC(1,62541), UA_QUALIFIEDNAME(1,"ping"), UA_LOCALIZEDTEXT("en_US", "ping"),
-                          UA_LOCALIZEDTEXT("en_US", "Return a single argument as passed by the caller"),
-                          UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
-                          0, 0,
-                          &getMonitoredItems, // Call this method
-                          (void *) server,    // Pass our server pointer as a handle to the method
-                          1, &inputArguments, 1, &outputArguments, &methodId);
-  
-  // Dettach the method from the methodNode
-  UA_Server_setAttribute_method(server, UA_NODEID_NUMERIC(1,62541), NULL, NULL);
-  
-  // Reaettach the method from the methodNode
-  UA_Server_setAttribute_method(server, UA_NODEID_NUMERIC(1,62541), &getMonitoredItems, (void *) server);
+    UA_Argument inputArguments;
+    UA_Argument_init(&inputArguments);
+    inputArguments.arrayDimensionsSize = 0;
+    inputArguments.arrayDimensions = NULL;
+    inputArguments.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
+    inputArguments.description = UA_LOCALIZEDTEXT("en_US", "A String");
+    inputArguments.name = UA_STRING("Input an integer");
+    inputArguments.valueRank = -1;
+
+    UA_Argument outputArguments;
+    UA_Argument_init(&outputArguments);
+    outputArguments.arrayDimensionsSize = 0;
+    outputArguments.arrayDimensions = NULL;
+    outputArguments.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
+    outputArguments.description = UA_LOCALIZEDTEXT("en_US", "A String");
+    outputArguments.name = UA_STRING("Input an integer");
+    outputArguments.valueRank = -1;
+
+    UA_MethodAttributes addmethodattributes;
+    UA_MethodAttributes_init(&addmethodattributes);
+    addmethodattributes.description = UA_LOCALIZEDTEXT("en_US", "Return a single argument as passed by the caller");
+    addmethodattributes.displayName = UA_LOCALIZEDTEXT("en_US", "ping");
+    addmethodattributes.executable = UA_TRUE;
+    addmethodattributes.userExecutable = UA_TRUE;
+    UA_Server_addMethodNode(server, UA_NODEID_NUMERIC(1,62541),
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
+                            UA_QUALIFIEDNAME(1,"ping"), addmethodattributes,
+                            &getMonitoredItems, // Call this method
+                            (void *) server,    // Pass our server pointer as a handle to the method
+                            1, &inputArguments, 1, &outputArguments, NULL);
 #endif
    
-  // Example for iterating over all nodes referenced by "Objects":
-  //printf("Nodes connected to 'Objects':\n=============================\n");
-  UA_Server_forEachChildNodeCall(server, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), nodeIter, NULL);
+    // Example for iterating over all nodes referenced by "Objects":
+    //printf("Nodes connected to 'Objects':\n=============================\n");
+    UA_Server_forEachChildNodeCall(server, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), nodeIter, NULL);
   
-  // Some easy localization
-  UA_LocalizedText objectsName = UA_LOCALIZEDTEXT("de_DE", "Objekte");
-  UA_Server_setAttribute_displayName(server, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), &objectsName);
+    // Some easy localization
+    UA_LocalizedText objectsName = UA_LOCALIZEDTEXT("de_DE", "Objekte");
+    UA_Server_writeDisplayNameAttribute(server, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), objectsName);
   
-  //start server
-  UA_StatusCode retval = UA_Server_run(server, 1, &running); //blocks until running=false
+    //start server
+    UA_StatusCode retval = UA_Server_run(server, 1, &running); //blocks until running=false
 
-  //ctrl-c received -> clean up
-  UA_Server_delete(server);
+    //ctrl-c received -> clean up
+    UA_Server_delete(server);
 
-  if(temperatureFile)
-          fclose(temperatureFile);
+    if(temperatureFile)
+        fclose(temperatureFile);
 
-  if(triggerFile) {
-          fseek(triggerFile, 0, SEEK_SET);
-          //setting led mode to default
-          fprintf(triggerFile, "%s", "mmc0");
-          fclose(triggerFile);
-  }
+    if(triggerFile) {
+        fseek(triggerFile, 0, SEEK_SET);
+        //setting led mode to default
+        fprintf(triggerFile, "%s", "mmc0");
+        fclose(triggerFile);
+    }
   
-  if(ledFile)
-    fclose(ledFile);
+    if(ledFile)
+        fclose(ledFile);
 
 #ifdef UA_MULTITHREADING
-  pthread_rwlock_destroy(&writeLock);
+    pthread_rwlock_destroy(&writeLock);
 #endif
 
-  return retval;
+    return retval;
 }

+ 28 - 11
examples/server.cpp

@@ -2,6 +2,8 @@
  * This work is licensed under a Creative Commons CCZero 1.0 Universal License.
  * See http://creativecommons.org/publicdomain/zero/1.0/ for more information.
  */
+
+#include <signal.h>
 #include <iostream>
 #include <cstring>
 
@@ -21,26 +23,41 @@
 
 using namespace std;
 
-int main()
-{
+UA_Boolean running = 1;
+UA_Logger logger = Logger_Stdout;
+
+static void stopHandler(int sign) {
+    UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "received ctrl-c");
+    running = 0;
+}
+
+int main() {
+    signal(SIGINT, stopHandler); /* catches ctrl-c */
+
     UA_Server *server = UA_Server_new(UA_ServerConfig_standard);
     UA_Server_addNetworkLayer(server, ServerNetworkLayerTCP_new(UA_ConnectionConfig_standard, 16664));
-
-    UA_Logger logger = Logger_Stdout_new();
     UA_Server_setLogger(server, logger);
 
     // add a variable node to the adresspace
-    UA_Variant *myIntegerVariant = UA_Variant_new();
+    UA_VariableAttributes attr;
+    UA_VariableAttributes_init(&attr);
     UA_Int32 myInteger = 42;
-    UA_Variant_setScalarCopy(myIntegerVariant, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
-    UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer");
-    UA_NodeId myIntegerNodeId = UA_NODEID_NULL; /* assign a random free nodeid */
+    UA_Variant_setScalarCopy(&attr.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
+    attr.description = UA_LOCALIZEDTEXT_ALLOC("en_US","the answer");
+    attr.displayName = UA_LOCALIZEDTEXT_ALLOC("en_US","the answer");
+    UA_NodeId myIntegerNodeId = UA_NODEID_STRING_ALLOC(1, "the.answer");
+    UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME_ALLOC(1, "the answer");
     UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
     UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
-    UA_Server_addVariableNode(server, myIntegerVariant, myIntegerName,
-                              myIntegerNodeId, parentNodeId, parentReferenceNodeId);
+    UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId,
+                              parentReferenceNodeId, myIntegerName,
+                              UA_NODEID_NULL, attr, NULL);
+
+    /* allocations on the heap need to be freed */
+    UA_VariableAttributes_deleteMembers(&attr);
+    UA_NodeId_deleteMembers(&myIntegerNodeId);
+    UA_QualifiedName_deleteMembers(&myIntegerName);
 
-    UA_Boolean running = UA_TRUE;
     UA_StatusCode retval = UA_Server_run(server, 1, &running);
 	UA_Server_delete(server);
 

+ 19 - 17
examples/server_datasource.c

@@ -16,24 +16,28 @@
 #endif
 
 UA_Boolean running = 1;
-UA_Logger logger;
+UA_Logger logger = Logger_Stdout;
 
 static void stopHandler(int sign) {
     UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "received ctrl-c");
     running = 0;
 }
 
-static UA_StatusCode readInteger(void *handle, UA_Boolean sourceTimeStamp, const UA_NumericRange *range, UA_DataValue *dataValue) {
+static UA_StatusCode readInteger(void *handle, const UA_NodeId nodeid, UA_Boolean sourceTimeStamp, const UA_NumericRange *range, UA_DataValue *dataValue) {
     dataValue->hasValue = UA_TRUE;
     UA_Variant_setScalarCopy(&dataValue->value, (UA_UInt32*)handle, &UA_TYPES[UA_TYPES_INT32]);
+    //note that this is only possible if the identifier is a string - but we are sure to have one here
+    UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "Node read %.*s",nodeid.identifier.string.length, nodeid.identifier.string.data);
     UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "read value %i", *(UA_UInt32*)handle);
     return UA_STATUSCODE_GOOD;
 }
 
-static UA_StatusCode writeInteger(void *handle, const UA_Variant *data, const UA_NumericRange *range){
+static UA_StatusCode writeInteger(void *handle, const UA_NodeId nodeid, const UA_Variant *data, const UA_NumericRange *range){
     if(UA_Variant_isScalar(data) && data->type == &UA_TYPES[UA_TYPES_INT32] && data->data){
         *(UA_UInt32*)handle = *(UA_Int32*)data->data;
     }
+    //note that this is only possible if the identifier is a string - but we are sure to have one here
+    UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "Node written %.*s",nodeid.identifier.string.length, nodeid.identifier.string.data);
     UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "written value %i", *(UA_UInt32*)handle);
     return UA_STATUSCODE_GOOD;
 }
@@ -43,27 +47,25 @@ int main(int argc, char** argv) {
     signal(SIGINT, stopHandler); /* catches ctrl-c */
 
     UA_Server *server = UA_Server_new(UA_ServerConfig_standard);
-    logger = Logger_Stdout_new();
     UA_Server_setLogger(server, logger);
     UA_Server_addNetworkLayer(server, ServerNetworkLayerTCP_new(UA_ConnectionConfig_standard, 16664));
 
     UA_Int32 myInteger = 42;
 
     /* add a variable node to the address space */
-    UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer"); /* UA_NODEID_NULL would assign a random free nodeid */
+    UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
     UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer");
-    UA_LocalizedText myIntegerBrowseName = UA_LOCALIZEDTEXT("en_US","the answer");
-
-    UA_DataSource dateDataSource = (UA_DataSource) {.handle = &myInteger, .read = readInteger, .write = writeInteger};
-
-    UA_Server_addDataSourceVariableNode(server, myIntegerNodeId, myIntegerName, myIntegerBrowseName, myIntegerBrowseName, 0, 0,
-
-                                    UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
-                                    UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
-
-                                    dateDataSource,
-
-                                    NULL);
+    UA_DataSource dateDataSource = (UA_DataSource) {
+        .handle = &myInteger, .read = readInteger, .write = writeInteger};
+    UA_VariableAttributes attr;
+    UA_VariableAttributes_init(&attr);
+    attr.description = UA_LOCALIZEDTEXT("en_US","the answer");
+    attr.displayName = UA_LOCALIZEDTEXT("en_US","the answer");
+
+    UA_Server_addDataSourceVariableNode(server, myIntegerNodeId,
+                                        UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+                                        UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
+                                        myIntegerName, UA_NODEID_NULL, attr, dateDataSource, NULL);
 
     UA_StatusCode retval = UA_Server_run(server, 1, &running);
     UA_Server_delete(server);

+ 1 - 2
examples/server_firstSteps.c

@@ -14,7 +14,7 @@
 #endif
 
 UA_Boolean running;
-UA_Logger logger;
+UA_Logger logger = Logger_Stdout;
 
 static void stopHandler(int signal) {
     running = UA_FALSE;
@@ -25,7 +25,6 @@ int main(void) {
     signal(SIGTERM, stopHandler);
 
     UA_Server *server = UA_Server_new(UA_ServerConfig_standard);
-    logger = Logger_Stdout_new();
     UA_Server_setLogger(server, logger);
     UA_Server_addNetworkLayer(server, ServerNetworkLayerTCP_new(UA_ConnectionConfig_standard, 16664));
     running = UA_TRUE;

+ 40 - 26
examples/server_method.c

@@ -16,9 +16,11 @@
 #endif
 
 UA_Boolean running = UA_TRUE;
-UA_Logger logger;
+UA_Logger logger = Logger_Stdout;
 
-static UA_StatusCode helloWorldMethod(const UA_NodeId objectId, const UA_Variant *input, UA_Variant *output, void *handle) {
+static UA_StatusCode
+helloWorldMethod(const UA_NodeId objectId, const UA_Variant *input,
+                 UA_Variant *output, void *handle) {
         UA_String *inputStr = (UA_String*)input->data;
         UA_String tmp = UA_STRING_ALLOC("Hello ");
         if(inputStr->length > 0) {
@@ -32,15 +34,12 @@ static UA_StatusCode helloWorldMethod(const UA_NodeId objectId, const UA_Variant
         return UA_STATUSCODE_GOOD;
 } 
 
-static UA_StatusCode IncInt32ArrayValuesMethod(const UA_NodeId objectId,
-                                         const UA_Variant *input, UA_Variant *output, void *handle) {
-
-
-	UA_Variant_setArrayCopy(output,input->data,5,&UA_TYPES[UA_TYPES_INT32]);
-	for(int i = 0; i< input->arrayLength; i++){
+static UA_StatusCode
+IncInt32ArrayValuesMethod(const UA_NodeId objectId, const UA_Variant *input,
+                          UA_Variant *output, void *handle) {
+	UA_Variant_setArrayCopy(output, input->data, 5, &UA_TYPES[UA_TYPES_INT32]);
+	for(size_t i = 0; i< input->arrayLength; i++)
 		((UA_Int32*)output->data)[i] = ((UA_Int32*)input->data)[i] + 1;
-	}
-
 	return UA_STATUSCODE_GOOD;
 }
 
@@ -48,12 +47,12 @@ static void stopHandler(int sign) {
     UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "received ctrl-c");
     running = 0;
 }
+
 int main(int argc, char** argv) {
     signal(SIGINT, stopHandler); /* catches ctrl-c */
 
     /* initialize the server */
     UA_Server *server = UA_Server_new(UA_ServerConfig_standard);
-    logger = Logger_Stdout_new();
     UA_Server_setLogger(server, logger);
     UA_Server_addNetworkLayer(server, ServerNetworkLayerTCP_new(UA_ConnectionConfig_standard, 16664));
 
@@ -61,7 +60,7 @@ int main(int argc, char** argv) {
     /* add the method node with the callback */
     UA_Argument inputArguments;
     UA_Argument_init(&inputArguments);
-    inputArguments.arrayDimensionsSize = -1;
+    inputArguments.arrayDimensionsSize = 0;
     inputArguments.arrayDimensions = NULL;
     inputArguments.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
     inputArguments.description = UA_LOCALIZEDTEXT("en_US", "A String");
@@ -70,17 +69,25 @@ int main(int argc, char** argv) {
 
     UA_Argument outputArguments;
     UA_Argument_init(&outputArguments);
-    outputArguments.arrayDimensionsSize = -1;
+    outputArguments.arrayDimensionsSize = 0;
     outputArguments.arrayDimensions = NULL;
     outputArguments.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
     outputArguments.description = UA_LOCALIZEDTEXT("en_US", "A String");
     outputArguments.name = UA_STRING("MyOutput");
     outputArguments.valueRank = -1;
         
-    UA_Server_addMethodNode(server, UA_NODEID_NUMERIC(1,62541), UA_QUALIFIEDNAME(1, "hello world"), 
-                            UA_LOCALIZEDTEXT("en_US","Hello World"), UA_LOCALIZEDTEXT("en_US","Say `Hello World`"),
-                            UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
-                            0, 0, &helloWorldMethod, NULL, 1, &inputArguments, 1, &outputArguments, NULL);
+    UA_MethodAttributes helloAttr;
+    UA_MethodAttributes_init(&helloAttr);
+    helloAttr.description = UA_LOCALIZEDTEXT("en_US","Say `Hello World`");
+    helloAttr.displayName = UA_LOCALIZEDTEXT("en_US","Hello World");
+    helloAttr.executable = UA_TRUE;
+    helloAttr.userExecutable = UA_TRUE;
+    UA_Server_addMethodNode(server, UA_NODEID_NUMERIC(1,62541),
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
+                            UA_QUALIFIEDNAME(1, "hello world"), 
+                            helloAttr, &helloWorldMethod, NULL,
+                            1, &inputArguments, 1, &outputArguments, NULL);
 
     //END OF EXAMPLE 1
 
@@ -105,20 +112,27 @@ int main(int argc, char** argv) {
     pOutputDimensions[0] = 5;
     outputArguments.arrayDimensions = pOutputDimensions;
     outputArguments.dataType = UA_TYPES[UA_TYPES_INT32].typeId;
-    outputArguments.description = UA_LOCALIZEDTEXT("en_US",
-                    "increment each array index");
-    outputArguments.name = UA_STRING(
-                    "output is the array, each index is incremented by one");
+    outputArguments.description = UA_LOCALIZEDTEXT("en_US", "increment each array index");
+    outputArguments.name = UA_STRING("output is the array, each index is incremented by one");
     outputArguments.valueRank = 1;
 
-    UA_Server_addMethodNode(server, UA_NODEID_STRING(1, "IncInt32ArrayValues"), UA_QUALIFIEDNAME(1, "IncInt32ArrayValues"),
-                            UA_LOCALIZEDTEXT("en_US","1dArrayExample"), UA_LOCALIZEDTEXT("en_US","1dArrayExample"),
-                            UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), 
-                            0, 0, &IncInt32ArrayValuesMethod, NULL, 1, &inputArguments, 1, &outputArguments, NULL);
+    
+    UA_MethodAttributes incAttr;
+    UA_MethodAttributes_init(&incAttr);
+    incAttr.description = UA_LOCALIZEDTEXT("en_US","1dArrayExample");
+    incAttr.displayName = UA_LOCALIZEDTEXT("en_US","1dArrayExample");
+    incAttr.executable = UA_TRUE;
+    incAttr.userExecutable = UA_TRUE;
+    UA_Server_addMethodNode(server, UA_NODEID_STRING(1, "IncInt32ArrayValues"),
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), 
+                            UA_QUALIFIEDNAME(1, "IncInt32ArrayValues"),
+                            incAttr, &IncInt32ArrayValuesMethod, NULL,
+                            1, &inputArguments, 1, &outputArguments, NULL);
     //END OF EXAMPLE 2
 
     /* start server */
-    UA_StatusCode retval = UA_Server_run(server, 1, &running); //blocks until running=false
+    UA_StatusCode retval = UA_Server_run(server, 1, &running);
 
     /* ctrl-c received -> clean up */
     UA_UInt32_delete(pInputDimensions);

+ 1 - 2
examples/server_nodeset.c

@@ -19,7 +19,7 @@
 #include "nodeset.h"
 
 UA_Boolean running = UA_TRUE;
-UA_Logger logger;
+UA_Logger logger = Logger_Stdout;
 
 static void stopHandler(int sign) {
     UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "received ctrl-c");
@@ -31,7 +31,6 @@ int main(int argc, char** argv) {
 
     /* initialize the server */
     UA_Server *server = UA_Server_new(UA_ServerConfig_standard);
-    logger = Logger_Stdout_new();
     UA_Server_setLogger(server, logger);
     UA_Server_addNetworkLayer(server, ServerNetworkLayerTCP_new(UA_ConnectionConfig_standard, 16664));
 

+ 103 - 0
examples/server_readspeed.c

@@ -0,0 +1,103 @@
+/*
+ * 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_types_generated.h"
+# include "ua_server.h"
+# include "logger_stdout.h"
+# include "networklayer_tcp.h"
+#else
+# include "open62541.h"
+#endif
+
+#include <time.h>
+#include <stdio.h>
+#include "server/ua_services.h"
+#include "ua_types_encoding_binary.h"
+
+UA_Boolean running = 1;
+UA_Logger logger = Logger_Stdout;
+
+static void stopHandler(int sign) {
+    UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "received ctrl-c");
+    running = 0;
+}
+
+int main(int argc, char** argv) {
+    signal(SIGINT, stopHandler); /* catches ctrl-c */
+
+    UA_Server *server = UA_Server_new(UA_ServerConfig_standard);
+    UA_Server_setLogger(server, logger);
+
+    /* add a variable node to the address space */
+    UA_VariableAttributes attr;
+    UA_VariableAttributes_init(&attr);
+    UA_Int32 myInteger = 42;
+    UA_Variant_setScalar(&attr.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
+    attr.description = UA_LOCALIZEDTEXT("en_US","the answer");
+    attr.displayName = UA_LOCALIZEDTEXT("en_US","the answer");
+    UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
+    UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer");
+    UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
+    UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
+    UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId,
+                              parentReferenceNodeId, myIntegerName,
+                              UA_NODEID_NULL, attr, NULL);
+
+    UA_ReadRequest request;
+    UA_ReadRequest_init(&request);
+    UA_ReadValueId rvi;
+    rvi.nodeId = myIntegerNodeId;
+    rvi.attributeId = UA_ATTRIBUTEID_VALUE;
+    rvi.indexRange = UA_STRING_NULL;
+    rvi.dataEncoding = UA_QUALIFIEDNAME(0, "DefaultBinary");
+    request.timestampsToReturn = UA_TIMESTAMPSTORETURN_NEITHER;
+    request.nodesToReadSize = 1;
+    request.nodesToRead = &rvi;
+
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    UA_ByteString request_msg;
+    retval |= UA_ByteString_allocBuffer(&request_msg, 1000);
+    UA_ByteString response_msg;
+    retval |= UA_ByteString_allocBuffer(&response_msg, 1000);
+    size_t offset = 0;
+    retval |= UA_encodeBinary(&request, &UA_TYPES[UA_TYPES_READREQUEST], &request_msg, &offset);
+    
+    clock_t begin, end;
+    begin = clock();
+
+    UA_ReadRequest rq;
+    UA_ReadResponse rr;
+
+    for(int i = 0; i < 900000; i++) {
+        offset = 0;
+        retval |= UA_decodeBinary(&request_msg, &offset, &rq, &UA_TYPES[UA_TYPES_READREQUEST]);
+
+        UA_ReadResponse_init(&rr);
+        Service_Read(server, &adminSession, &rq, &rr);
+
+        offset = 0;
+        retval |= UA_encodeBinary(&rr, &UA_TYPES[UA_TYPES_READRESPONSE], &response_msg, &offset);
+
+        UA_ReadRequest_deleteMembers(&rq);
+        UA_ReadResponse_deleteMembers(&rr);
+    }
+
+    end = clock();
+    double time_spent = (double)(end - begin) / CLOCKS_PER_SEC;
+    printf("duration was %f s\n", time_spent);
+    printf("retval is %i\n", retval);
+
+    UA_ByteString_deleteMembers(&request_msg);
+    UA_ByteString_deleteMembers(&response_msg);
+
+    retval |= UA_Server_run(server, 1, &running);
+    UA_Server_delete(server);
+
+    return retval;
+}

+ 1 - 2
examples/server_repeated_job.c

@@ -15,7 +15,7 @@
 #endif
 
 UA_Boolean running = 1;
-UA_Logger logger;
+UA_Logger logger = Logger_Stdout;
 
 static void stopHandler(int sign) {
     UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "received ctrl-c");
@@ -30,7 +30,6 @@ int main(int argc, char** argv) {
     signal(SIGINT, stopHandler); /* catches ctrl-c */
 
     UA_Server *server = UA_Server_new(UA_ServerConfig_standard);
-    logger = Logger_Stdout_new();
     UA_Server_setLogger(server, logger);
     UA_Server_addNetworkLayer(server, ServerNetworkLayerTCP_new(UA_ConnectionConfig_standard, 16664));
 

+ 15 - 12
examples/server_udp.c

@@ -3,14 +3,13 @@
  * See http://creativecommons.org/publicdomain/zero/1.0/ for more information.
  */
 #include <time.h>
-#include "ua_types.h"
-
 #include <stdio.h>
 #include <stdlib.h> 
 #include <signal.h>
 
 // provided by the open62541 lib
-#include "ua_server.h"
+# include "ua_types.h"
+# include "ua_server.h"
 
 // provided by the user, implementations available in the /examples folder
 #include "logger_stdout.h"
@@ -26,20 +25,24 @@ static void stopHandler(int sign) {
 int main(int argc, char** argv) {
 	signal(SIGINT, stopHandler); /* catches ctrl-c */
 
-	UA_Server *server = UA_Server_new();
-    UA_Server_addNetworkLayer(server, ServerNetworkLayerUDP_new(UA_ConnectionConfig_standard, 16664));
+	UA_Server *server = UA_Server_new(UA_ServerConfig_standard);
+    UA_ServerNetworkLayer *nl = ServerNetworkLayerUDP_new(UA_ConnectionConfig_standard, 16664);
+    UA_Server_addNetworkLayer(server, nl);
 
 	// add a variable node to the adresspace
-    UA_Variant *myIntegerVariant = UA_Variant_new();
+    UA_VariableAttributes attr;
+    UA_VariableAttributes_init(&attr);
     UA_Int32 myInteger = 42;
-    UA_Variant_setScalarCopy(myIntegerVariant, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
-    const UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer");
-    const UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
+    UA_Variant_setScalar(&attr.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
+    attr.description = UA_LOCALIZEDTEXT("en_US","the answer");
+    attr.displayName = UA_LOCALIZEDTEXT("en_US","the answer");
+    UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
+    UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer");
     UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
     UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
-    UA_Server_addVariableNode(server, myIntegerVariant, myIntegerName,
-                              myIntegerNodeId, parentNodeId, parentReferenceNodeId);
-
+    UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId,
+                              parentReferenceNodeId, myIntegerName,
+                              UA_NODEID_NULL, attr, NULL);
 
     UA_StatusCode retval = UA_Server_run(server, 1, &running);
 	UA_Server_delete(server);

+ 26 - 10
examples/server_variable.c

@@ -15,34 +15,50 @@
 #endif
 
 UA_Boolean running = 1;
-UA_Logger logger;
+UA_Logger logger = Logger_Stdout;
 
 static void stopHandler(int sign) {
     UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "received ctrl-c");
     running = 0;
 }
 
+static void onRead(void *handle, const UA_NodeId nodeid, const UA_Variant *data,
+                   const UA_NumericRange *range) {
+    UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND,
+                "onRead; handle is: %i", (uintptr_t)handle);
+}
+
+static void onWrite(void *h, const UA_NodeId nodeid, const UA_Variant *data,
+                    const UA_NumericRange *range) {
+    UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "onWrite; handle: %i", (uintptr_t)h);
+}
+
 int main(int argc, char** argv) {
     signal(SIGINT, stopHandler); /* catches ctrl-c */
 
     UA_Server *server = UA_Server_new(UA_ServerConfig_standard);
-    logger = Logger_Stdout_new();
     UA_Server_setLogger(server, logger);
-    UA_Server_addNetworkLayer(server, ServerNetworkLayerTCP_new(UA_ConnectionConfig_standard, 16664));
+    UA_ServerNetworkLayer *nl;
+    nl = ServerNetworkLayerTCP_new(UA_ConnectionConfig_standard, 16664);
+    UA_Server_addNetworkLayer(server, nl);
 
     /* add a variable node to the address space */
-    UA_Variant *myIntegerVariant = UA_Variant_new();
+    UA_VariableAttributes attr;
+    UA_VariableAttributes_init(&attr);
     UA_Int32 myInteger = 42;
-    UA_Variant_setScalarCopy(myIntegerVariant, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
-    //NOTE: the link between myInteger and the value of the node is lost here, you can safely reuse myInteger
+    UA_Variant_setScalar(&attr.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
+    attr.description = UA_LOCALIZEDTEXT("en_US","the answer");
+    attr.displayName = UA_LOCALIZEDTEXT("en_US","the answer");
+    UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
     UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer");
-    UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer"); /* UA_NODEID_NULL would assign a random free nodeid */
-    UA_LocalizedText myIntegerBrowseName = UA_LOCALIZEDTEXT("en_US","the answer");
     UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
     UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
+    UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId,
+                              parentReferenceNodeId, myIntegerName,
+                              UA_NODEID_NULL, attr, NULL);
 
-    UA_Server_addVariableNode(server, myIntegerNodeId, myIntegerName, myIntegerBrowseName, myIntegerBrowseName, 0, 0,
-                              parentNodeId, parentReferenceNodeId, myIntegerVariant, NULL);
+    UA_ValueCallback callback = {(void*)7, onRead, onWrite};
+    UA_Server_setVariableNode_valueCallback(server, myIntegerNodeId, callback);
 
     UA_StatusCode retval = UA_Server_run(server, 1, &running);
     UA_Server_delete(server);

+ 346 - 60
include/ua_client.h

@@ -14,13 +14,6 @@ extern "C" {
 struct UA_Client;
 typedef struct UA_Client UA_Client;
 
-/**
- * The client networklayer is defined by a single function that fills a UA_Connection struct after
- * successfully connecting.
- */
-typedef UA_Connection (*UA_ConnectClientConnection)(UA_ConnectionConfig localConf, char *endpointUrl,
-                                                    UA_Logger *logger);
-
 typedef struct UA_ClientConfig {
     UA_Int32 timeout; //sync response timeout
     UA_Int32 secureChannelLifeTime; // lifetime in ms (then the channel needs to be renewed)
@@ -29,77 +22,370 @@ typedef struct UA_ClientConfig {
 } UA_ClientConfig;
 
 extern UA_EXPORT const UA_ClientConfig UA_ClientConfig_standard;
+
+/**
+ * Creates and initialize a Client object
+ *
+ * @param config for the new client. You can use UA_ClientConfig_standard which has sane defaults
+ * @param logger function pointer to a logger function. See examples/logger_stdout.c for a simple implementation
+ * @return return the new Client object
+ */
 UA_Client UA_EXPORT * UA_Client_new(UA_ClientConfig config, UA_Logger logger);
 
-UA_EXPORT void UA_Client_reset(UA_Client* client);
-UA_EXPORT void UA_Client_init(UA_Client* client, UA_ClientConfig config, UA_Logger logger);
-UA_EXPORT void UA_Client_deleteMembers(UA_Client* client);
-UA_EXPORT void UA_Client_delete(UA_Client* client);
+/**
+ * resets a Client object
+ *
+ * @param client to reset
+ * @return Void
+ */
+void UA_EXPORT UA_Client_reset(UA_Client* client);
+
+/**
+ * delete a Client object
+ *
+ * @param client to delete
+ * @return Void
+ */
+void UA_EXPORT UA_Client_delete(UA_Client* client);
 
-UA_StatusCode UA_EXPORT UA_Client_connect(UA_Client *client, UA_ConnectClientConnection connFunc, char *endpointUrl);
+/*************************/
+/* Manage the Connection */
+/*************************/
+
+typedef UA_Connection (*UA_ConnectClientConnection)(UA_ConnectionConfig localConf, const char *endpointUrl,
+                                                    UA_Logger logger);
+
+/**
+ * start a connection to the selected server
+ *
+ * @param client to use
+ * @param connection function. You can use ClientNetworkLayerTCP_connect from examples/networklayer_tcp.h
+ * @param endpointURL to connect (for example "opc.tcp://localhost:16664")
+ * @return Indicates whether the operation succeeded or returns an error code
+ */
+UA_StatusCode UA_EXPORT
+UA_Client_connect(UA_Client *client, UA_ConnectClientConnection connFunc, const char *endpointUrl);
+
+/**
+ * close a connection to the selected server
+ *
+ * @param client to use
+ * @return Indicates whether the operation succeeded or returns an error code
+ */
 UA_StatusCode UA_EXPORT UA_Client_disconnect(UA_Client *client);
 
-UA_StatusCode UA_EXPORT UA_Client_renewSecureChannel(UA_Client *client);
+/**
+ * renew a secure channel if needed
+ *
+ * @param client to use
+ * @return Indicates whether the operation succeeded or returns an error code
+ */
+UA_StatusCode UA_EXPORT UA_Client_manuallyRenewSecureChannel(UA_Client *client);
 
-/* Attribute Service Set */
-UA_ReadResponse UA_EXPORT UA_Client_read(UA_Client *client, UA_ReadRequest *request);
-UA_WriteResponse UA_EXPORT UA_Client_write(UA_Client *client, UA_WriteRequest *request);
+/****************/
+/* Raw Services */
+/****************/
 
-/* View Service Set */    
-UA_BrowseResponse UA_EXPORT UA_Client_browse(UA_Client *client, UA_BrowseRequest *request);
-UA_BrowseNextResponse UA_EXPORT UA_Client_browseNext(UA_Client *client, UA_BrowseNextRequest *request);
-UA_TranslateBrowsePathsToNodeIdsResponse UA_EXPORT
-    UA_Client_translateTranslateBrowsePathsToNodeIds(UA_Client *client,
-                                                     UA_TranslateBrowsePathsToNodeIdsRequest *request);
+/* Don't use this function. There are typed versions. */
+void UA_EXPORT
+__UA_Client_Service(UA_Client *client, const void *request, const UA_DataType *requestType,
+                    void *response, const UA_DataType *responseType);
 
 /* NodeManagement Service Set */
-UA_AddNodesResponse UA_EXPORT UA_Client_addNodes(UA_Client *client, UA_AddNodesRequest *request);
-UA_AddReferencesResponse UA_EXPORT
-    UA_Client_addReferences(UA_Client *client, UA_AddReferencesRequest *request);
 
-UA_DeleteNodesResponse UA_EXPORT UA_Client_deleteNodes(UA_Client *client, UA_DeleteNodesRequest *request);
-UA_DeleteReferencesResponse UA_EXPORT
-    UA_Client_deleteReferences(UA_Client *client, UA_DeleteReferencesRequest *request);
+/**
+ * performs a service
+ *
+ * @param client to use
+ * @param request to use
+ * @return returns the response which has the UA_StatusCode in a UA_ResponseHeader
+ */
+static UA_INLINE UA_AddNodesResponse
+UA_Client_Service_addNodes(UA_Client *client, const UA_AddNodesRequest request) {
+    UA_AddNodesResponse response;
+    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_ADDNODESREQUEST],
+                        &response, &UA_TYPES[UA_TYPES_ADDNODESRESPONSE]);
+    return response; }
 
+/**
+ * performs a service
+ *
+ * @param client to use
+ * @param request to use
+ * @return returns the response which has the UA_StatusCode in a UA_ResponseHeader
+ */
+static UA_INLINE UA_AddReferencesResponse
+UA_Client_Service_addReferences(UA_Client *client, const UA_AddReferencesRequest request) {
+    UA_AddReferencesResponse response;
+    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_ADDNODESREQUEST],
+                        &response, &UA_TYPES[UA_TYPES_ADDNODESRESPONSE]);
+    return response; }
 
-/* Client-Side Macro/Procy functions */
-#ifdef ENABLE_METHODCALLS
-UA_CallResponse UA_EXPORT UA_Client_call(UA_Client *client, UA_CallRequest *request);
-UA_StatusCode UA_EXPORT UA_Client_CallServerMethod(UA_Client *client, UA_NodeId objectNodeId, UA_NodeId methodNodeId,
-                                                   UA_Int32 inputSize, const UA_Variant *input,
-                                                   UA_Int32 *outputSize, UA_Variant **output);
-#endif
-    
-UA_AddNodesResponse UA_EXPORT *UA_Client_createObjectNode(  UA_Client *client, UA_ExpandedNodeId reqId, UA_QualifiedName browseName, UA_LocalizedText displayName, 
-                                                            UA_LocalizedText description, UA_ExpandedNodeId parentNodeId, UA_NodeId referenceTypeId,
-                                                            UA_UInt32 userWriteMask, UA_UInt32 writeMask, UA_ExpandedNodeId typeDefinition);
+/**
+ * performs a service
+ *
+ * @param client to use
+ * @param request to use
+ * @return returns the response which has the UA_StatusCode in a UA_ResponseHeader
+ */
+static UA_INLINE UA_DeleteNodesResponse
+UA_Client_Service_deleteNodes(UA_Client *client, const UA_DeleteNodesRequest request) {
+    UA_DeleteNodesResponse response;
+    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_DELETENODESREQUEST],
+                        &response, &UA_TYPES[UA_TYPES_DELETENODESRESPONSE]);
+    return response; }
+
+/**
+ * performs a service
+ *
+ * @param client to use
+ * @param request to use
+ * @return returns the response which has the UA_StatusCode in a UA_ResponseHeader
+ */
+static UA_INLINE UA_DeleteReferencesResponse
+UA_Client_Service_deleteReferences(UA_Client *client, const UA_DeleteReferencesRequest request) {
+    UA_DeleteReferencesResponse response;
+    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_DELETENODESREQUEST],
+                        &response, &UA_TYPES[UA_TYPES_DELETENODESRESPONSE]);
+    return response; }
 
-UA_AddNodesResponse UA_EXPORT *UA_Client_createVariableNode(UA_Client *client, UA_ExpandedNodeId reqId, UA_QualifiedName browseName, UA_LocalizedText displayName, 
-                                                            UA_LocalizedText description, UA_ExpandedNodeId parentNodeId, UA_NodeId referenceTypeId,
-                                                            UA_UInt32 userWriteMask, UA_UInt32 writeMask, UA_ExpandedNodeId typeDefinition, 
-                                                            UA_NodeId dataType, UA_Variant *value );
+/* View Service Set */
 
-UA_AddNodesResponse UA_EXPORT *UA_Client_createReferenceTypeNode(UA_Client *client, UA_ExpandedNodeId reqId, UA_QualifiedName browseName, UA_LocalizedText displayName, 
-                                                            UA_LocalizedText description, UA_ExpandedNodeId parentNodeId, UA_NodeId referenceTypeId,
-                                                            UA_UInt32 userWriteMask, UA_UInt32 writeMask, UA_ExpandedNodeId typeDefinition,
-                                                            UA_LocalizedText inverseName );
+/**
+ * performs a service
+ *
+ * @param client to use
+ * @param request to use
+ * @return returns the response which has the UA_StatusCode in a UA_ResponseHeader
+ */
+static UA_INLINE UA_BrowseResponse
+UA_Client_Service_browse(UA_Client *client, const UA_BrowseRequest request) {
+    UA_BrowseResponse response;
+    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_BROWSEREQUEST],
+                        &response, &UA_TYPES[UA_TYPES_BROWSERESPONSE]);
+    return response; }
+
+/**
+ * performs a service
+ *
+ * @param client to use
+ * @param request to use
+ * @return returns the response which has the UA_StatusCode in a UA_ResponseHeader
+ */
+static UA_INLINE UA_BrowseNextResponse
+UA_Client_Service_browseNext(UA_Client *client, const UA_BrowseNextRequest request) {
+    UA_BrowseNextResponse response;
+    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_BROWSENEXTREQUEST],
+                        &response, &UA_TYPES[UA_TYPES_BROWSENEXTRESPONSE]);
+    return response; }
 
-UA_AddNodesResponse UA_EXPORT *UA_Client_createObjectTypeNode(UA_Client *client, UA_ExpandedNodeId reqId, UA_QualifiedName browseName, UA_LocalizedText displayName, 
-                                                            UA_LocalizedText description, UA_ExpandedNodeId parentNodeId, UA_NodeId referenceTypeId,
-                                                            UA_UInt32 userWriteMask, UA_UInt32 writeMask, UA_ExpandedNodeId typeDefinition);
+/**
+ * performs a service
+ *
+ * @param client to use
+ * @param request to use
+ * @return returns the response which has the UA_StatusCode in a UA_ResponseHeader
+ */
+static UA_INLINE UA_TranslateBrowsePathsToNodeIdsResponse
+UA_Client_Service_translateBrowsePathsToNodeIds(UA_Client *client,
+                                                const UA_TranslateBrowsePathsToNodeIdsRequest request) {
+    UA_TranslateBrowsePathsToNodeIdsResponse response;
+    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_TRANSLATEBROWSEPATHSTONODEIDSREQUEST],
+                        &response, &UA_TYPES[UA_TYPES_TRANSLATEBROWSEPATHSTONODEIDSRESPONSE]);
+    return response; }
 
+/**
+ * performs a service
+ *
+ * @param client to use
+ * @param request to use
+ * @return returns the response which has the UA_StatusCode in a UA_ResponseHeader
+ */
+static UA_INLINE UA_RegisterNodesResponse
+UA_Client_Service_registerNodes(UA_Client *client, const UA_RegisterNodesRequest request) {
+    UA_RegisterNodesResponse response;
+    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_REGISTERNODESREQUEST],
+                        &response, &UA_TYPES[UA_TYPES_REGISTERNODESRESPONSE]);
+    return response; }
+
+/**
+ * performs a service
+ *
+ * @param client to use
+ * @param request to use
+ * @return returns the response which has the UA_StatusCode in a UA_ResponseHeader
+ */
+static UA_INLINE UA_UnregisterNodesResponse
+UA_Client_Service_unregisterNodes(UA_Client *client, const UA_UnregisterNodesRequest request) {
+    UA_UnregisterNodesResponse response;
+    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_UNREGISTERNODESREQUEST],
+                        &response, &UA_TYPES[UA_TYPES_UNREGISTERNODESRESPONSE]);
+    return response; }
+
+/* Query Service Set */
+
+/**
+ * performs a service
+ *
+ * @param client to use
+ * @param request to use
+ * @return returns the response which has the UA_StatusCode in a UA_ResponseHeader
+ */
+static UA_INLINE UA_QueryFirstResponse
+UA_Client_Service_queryFirst(UA_Client *client, const UA_QueryFirstRequest request) {
+    UA_QueryFirstResponse response;
+    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_QUERYFIRSTREQUEST],
+                        &response, &UA_TYPES[UA_TYPES_QUERYFIRSTRESPONSE]);
+    return response; }
+
+/**
+ * performs a service
+ *
+ * @param client to use
+ * @param request to use
+ * @return returns the response which has the UA_StatusCode in a UA_ResponseHeader
+ */
+static UA_INLINE UA_QueryNextResponse
+UA_Client_Service_queryNext(UA_Client *client, const UA_QueryNextRequest request) {
+    UA_QueryNextResponse response;
+    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_QUERYFIRSTREQUEST],
+                        &response, &UA_TYPES[UA_TYPES_QUERYFIRSTRESPONSE]);
+    return response; }
+
+/* Attribute Service Set */
+
+/**
+ * performs a service
+ *
+ * @param client to use
+ * @param request to use
+ * @return returns the response which has the UA_StatusCode in a UA_ResponseHeader
+ */
+static UA_INLINE UA_ReadResponse
+UA_Client_Service_read(UA_Client *client, const UA_ReadRequest request) {
+    UA_ReadResponse response;
+    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_READREQUEST],
+                        &response, &UA_TYPES[UA_TYPES_READRESPONSE]);
+    return response; }
+
+/**
+ * performs a service
+ *
+ * @param client to use
+ * @param request to use
+ * @return returns the response which has the UA_StatusCode in a UA_ResponseHeader
+ */
+static UA_INLINE UA_WriteResponse
+UA_Client_Service_write(UA_Client *client, const UA_WriteRequest request) {
+    UA_WriteResponse response;
+    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_WRITEREQUEST],
+                        &response, &UA_TYPES[UA_TYPES_WRITERESPONSE]);
+    return response; }
+
+/* Method Service Set */
+
+/**
+ * performs a service
+ *
+ * @param client to use
+ * @param request to use
+ * @return returns the response which has the UA_StatusCode in a UA_ResponseHeader
+ */
+static UA_INLINE UA_CallResponse
+UA_Client_Service_call(UA_Client *client, const UA_CallRequest request) {
+    UA_CallResponse response;
+    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_CALLREQUEST],
+                        &response, &UA_TYPES[UA_TYPES_CALLRESPONSE]);
+    return response; }
 
 #ifdef ENABLE_SUBSCRIPTIONS
-UA_Int32      UA_EXPORT UA_Client_newSubscription(UA_Client *client, UA_Int32 publishInterval);
-UA_StatusCode UA_EXPORT UA_Client_removeSubscription(UA_Client *client, UA_UInt32 subscriptionId);
-//void UA_EXPORT UA_Client_modifySubscription(UA_Client *client);
-void UA_EXPORT UA_Client_doPublish(UA_Client *client);
-
-UA_UInt32     UA_EXPORT UA_Client_monitorItemChanges(UA_Client *client, UA_UInt32 subscriptionId,
-                                                     UA_NodeId nodeId, UA_UInt32 attributeID,
-                                                     void *handlingFunction);
-UA_StatusCode UA_EXPORT UA_Client_unMonitorItemChanges(UA_Client *client, UA_UInt32 subscriptionId,
-                                                       UA_UInt32 monitoredItemId );
+/* MonitoredItem Service Set */
+
+/**
+ * performs a service
+ *
+ * @param client to use
+ * @param request to use
+ * @return returns the response which has the UA_StatusCode in a UA_ResponseHeader
+ */
+static UA_INLINE UA_CreateMonitoredItemsResponse
+UA_Client_Service_createMonitoredItems(UA_Client *client, const UA_CreateMonitoredItemsRequest request) {
+    UA_CreateMonitoredItemsResponse response;
+    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_CREATEMONITOREDITEMSREQUEST],
+                        &response, &UA_TYPES[UA_TYPES_CREATEMONITOREDITEMSRESPONSE]);
+    return response; }
+
+/**
+ * performs a service
+ *
+ * @param client to use
+ * @param request to use
+ * @return returns the response which has the UA_StatusCode in a UA_ResponseHeader
+ */
+static UA_INLINE UA_DeleteMonitoredItemsResponse
+UA_Client_Service_deleteMonitoredItems(UA_Client *client, const UA_DeleteMonitoredItemsRequest request) {
+    UA_DeleteMonitoredItemsResponse response;
+    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_DELETEMONITOREDITEMSREQUEST],
+                        &response, &UA_TYPES[UA_TYPES_DELETEMONITOREDITEMSRESPONSE]);
+    return response; }
+
+/* Subscription Service Set */
+
+/**
+ * performs a service
+ *
+ * @param client to use
+ * @param request to use
+ * @return returns the response which has the UA_StatusCode in a UA_ResponseHeader
+ */
+static UA_INLINE UA_CreateSubscriptionResponse
+UA_Client_Service_createSubscription(UA_Client *client, const UA_CreateSubscriptionRequest request) {
+    UA_CreateSubscriptionResponse response;
+    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_CREATESUBSCRIPTIONREQUEST],
+                        &response, &UA_TYPES[UA_TYPES_CREATESUBSCRIPTIONRESPONSE]);
+    return response; }
+
+/**
+ * performs a service
+ *
+ * @param client to use
+ * @param request to use
+ * @return returns the response which has the UA_StatusCode in a UA_ResponseHeader
+ */
+static UA_INLINE UA_ModifySubscriptionResponse
+UA_Client_Service_modifySubscription(UA_Client *client, const UA_ModifySubscriptionRequest request) {
+    UA_ModifySubscriptionResponse response;
+    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_MODIFYSUBSCRIPTIONREQUEST],
+                        &response, &UA_TYPES[UA_TYPES_MODIFYSUBSCRIPTIONRESPONSE]);
+    return response; }
+
+/**
+ * performs a service
+ *
+ * @param client to use
+ * @param request to use
+ * @return returns the response which has the UA_StatusCode in a UA_ResponseHeader
+ */
+static UA_INLINE UA_DeleteSubscriptionsResponse
+UA_Client_Service_deleteSubscriptions(UA_Client *client, const UA_DeleteSubscriptionsRequest request) {
+    UA_DeleteSubscriptionsResponse response;
+    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_DELETESUBSCRIPTIONSREQUEST],
+                        &response, &UA_TYPES[UA_TYPES_DELETESUBSCRIPTIONSRESPONSE]);
+    return response; }
+
+/**
+ * performs a service
+ *
+ * @param client to use
+ * @param request to use
+ * @return returns the response which has the UA_StatusCode in a UA_ResponseHeader
+ */
+static UA_INLINE UA_PublishResponse
+UA_Client_Service_publish(UA_Client *client, const UA_PublishRequest request) {
+    UA_PublishResponse response;
+    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_PUBLISHREQUEST],
+                        &response, &UA_TYPES[UA_TYPES_PUBLISHRESPONSE]);
+    return response; }
 #endif
 
 #ifdef __cplusplus

+ 294 - 0
include/ua_client_highlevel.h

@@ -0,0 +1,294 @@
+#ifndef UA_CLIENT_HIGHLEVEL_H_
+#define UA_CLIENT_HIGHLEVEL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "ua_client.h"
+
+/**
+ * Get the namespace-index of a namespace-URI
+ *
+ * @param client The UA_Client struct for this connection
+ * @param namespaceUri The interested namespace URI
+ * @param namespaceIndex The namespace index of the URI. The value is unchanged in case of an error
+ * @return Indicates whether the operation succeeded or returns an error code
+ */
+UA_StatusCode UA_EXPORT UA_Client_NamespaceGetIndex(UA_Client *client, UA_String *namespaceUri, UA_UInt16 *namespaceIndex);
+
+/*******************/
+/* Node Management */
+/*******************/
+
+UA_StatusCode UA_EXPORT
+UA_Client_addReference(UA_Client *client, const UA_NodeId sourceNodeId, const UA_NodeId referenceTypeId,
+                       UA_Boolean isForward, const UA_String targetServerUri,
+                       const UA_ExpandedNodeId targetNodeId, UA_NodeClass targetNodeClass);
+
+UA_StatusCode UA_EXPORT
+UA_Client_deleteReference(UA_Client *client, const UA_NodeId sourceNodeId, const UA_NodeId referenceTypeId,
+                          UA_Boolean isForward, const UA_ExpandedNodeId targetNodeId,
+                          UA_Boolean deleteBidirectional);
+
+UA_StatusCode UA_EXPORT
+UA_Client_deleteNode(UA_Client *client, const UA_NodeId nodeId, UA_Boolean deleteTargetReferences);
+    
+/* Don't call this function, use the typed versions */
+UA_StatusCode UA_EXPORT
+__UA_Client_addNode(UA_Client *client, const UA_NodeClass nodeClass,
+                    const UA_NodeId requestedNewNodeId, const UA_NodeId parentNodeId,
+                    const UA_NodeId referenceTypeId, const UA_QualifiedName browseName,
+                    const UA_NodeId typeDefinition, const UA_NodeAttributes *attr,
+                    const UA_DataType *attributeType, UA_NodeId *outNewNodeId);
+
+static UA_INLINE UA_StatusCode
+UA_Client_addVariableNode(UA_Client *client, const UA_NodeId requestedNewNodeId,
+                          const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
+                          const UA_QualifiedName browseName, const UA_NodeId typeDefinition,
+                          const UA_VariableAttributes attr, UA_NodeId *outNewNodeId) {
+    return __UA_Client_addNode(client, UA_NODECLASS_VARIABLE, requestedNewNodeId,
+                               parentNodeId, referenceTypeId, browseName, typeDefinition,
+                               (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],
+                               outNewNodeId); }
+
+static UA_INLINE UA_StatusCode
+UA_Client_addVariableTypeNode(UA_Client *client, const UA_NodeId requestedNewNodeId,
+                              const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
+                              const UA_QualifiedName browseName, const UA_VariableTypeAttributes attr,
+                              UA_NodeId *outNewNodeId) {
+    return __UA_Client_addNode(client, UA_NODECLASS_VARIABLETYPE, requestedNewNodeId,
+                               parentNodeId, referenceTypeId, browseName, UA_NODEID_NULL,
+                               (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLETYPEATTRIBUTES],
+                               outNewNodeId); }
+
+static UA_INLINE UA_StatusCode
+UA_Client_addObjectNode(UA_Client *client, const UA_NodeId requestedNewNodeId,
+                        const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
+                        const UA_QualifiedName browseName, const UA_NodeId typeDefinition,
+                        const UA_ObjectAttributes attr, UA_NodeId *outNewNodeId) {
+    return __UA_Client_addNode(client, UA_NODECLASS_OBJECT, requestedNewNodeId,
+                               parentNodeId, referenceTypeId, browseName, typeDefinition,
+                               (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],
+                               outNewNodeId); }
+
+static UA_INLINE UA_StatusCode
+UA_Client_addObjectTypeNode(UA_Client *client, const UA_NodeId requestedNewNodeId,
+                            const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
+                            const UA_QualifiedName browseName, const UA_ObjectTypeAttributes attr,
+                            UA_NodeId *outNewNodeId) {
+    return __UA_Client_addNode(client, UA_NODECLASS_OBJECTTYPE, requestedNewNodeId,
+                               parentNodeId, referenceTypeId, browseName, UA_NODEID_NULL,
+                               (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTTYPEATTRIBUTES],
+                               outNewNodeId); }
+
+static UA_INLINE UA_StatusCode
+UA_Client_addViewNode(UA_Client *client, const UA_NodeId requestedNewNodeId,
+                      const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
+                      const UA_QualifiedName browseName, const UA_ViewAttributes attr,
+                      UA_NodeId *outNewNodeId) {
+    return __UA_Client_addNode(client, UA_NODECLASS_VIEW, requestedNewNodeId,
+                               parentNodeId, referenceTypeId, browseName, UA_NODEID_NULL,
+                               (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VIEWATTRIBUTES],
+                               outNewNodeId); }
+
+static UA_INLINE UA_StatusCode
+UA_Client_addReferenceTypeNode(UA_Client *client, const UA_NodeId requestedNewNodeId,
+                               const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
+                               const UA_QualifiedName browseName, const UA_ReferenceTypeAttributes attr,
+                               UA_NodeId *outNewNodeId) {
+    return __UA_Client_addNode(client, UA_NODECLASS_REFERENCETYPE, requestedNewNodeId,
+                               parentNodeId, referenceTypeId, browseName, UA_NODEID_NULL,
+                               (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_REFERENCETYPEATTRIBUTES],
+                               outNewNodeId); }
+
+static UA_INLINE UA_StatusCode
+UA_Client_addDataTypeNode(UA_Client *client, const UA_NodeId requestedNewNodeId,
+                          const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
+                          const UA_QualifiedName browseName, const UA_DataTypeAttributes attr,
+                          UA_NodeId *outNewNodeId) {
+    return __UA_Client_addNode(client, UA_NODECLASS_DATATYPE, requestedNewNodeId,
+                               parentNodeId, referenceTypeId, browseName, UA_NODEID_NULL,
+                               (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],
+                               outNewNodeId); }
+
+static UA_INLINE UA_StatusCode
+UA_Client_addMethodNode(UA_Client *client, const UA_NodeId requestedNewNodeId,
+                          const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
+                          const UA_QualifiedName browseName, const UA_MethodAttributes attr,
+                          UA_NodeId *outNewNodeId) {
+    return __UA_Client_addNode(client, UA_NODECLASS_METHOD, requestedNewNodeId,
+                               parentNodeId, referenceTypeId, browseName, UA_NODEID_NULL,
+                               (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_METHODATTRIBUTES],
+                               outNewNodeId); }
+
+/*************************/
+/* Attribute Service Set */
+/*************************/
+
+/* Don't use this function. There are typed versions for every supported attribute. */
+UA_StatusCode UA_EXPORT
+__UA_Client_readAttribute(UA_Client *client, UA_NodeId nodeId, UA_AttributeId attributeId,
+                          void *out, const UA_DataType *outDataType);
+  
+static UA_INLINE UA_StatusCode
+UA_Client_readNodeIdAttribute(UA_Client *client, UA_NodeId nodeId, UA_NodeId *outNodeId) {
+    return __UA_Client_readAttribute(client, nodeId, UA_ATTRIBUTEID_NODEID,
+                                     outNodeId, &UA_TYPES[UA_TYPES_NODEID]); }
+
+static UA_INLINE UA_StatusCode
+UA_Client_readNodeClassAttribute(UA_Client *client, UA_NodeId nodeId, UA_NodeClass *outNodeClass) {
+    return __UA_Client_readAttribute(client, nodeId, UA_ATTRIBUTEID_NODECLASS,
+                                     outNodeClass, &UA_TYPES[UA_TYPES_NODECLASS]); }
+
+static UA_INLINE UA_StatusCode
+UA_Client_readBrowseNameAttribute(UA_Client *client, UA_NodeId nodeId, UA_QualifiedName *outBrowseName) {
+    return __UA_Client_readAttribute(client, nodeId, UA_ATTRIBUTEID_BROWSENAME,
+                                     outBrowseName, &UA_TYPES[UA_TYPES_QUALIFIEDNAME]); }
+
+static UA_INLINE UA_StatusCode
+UA_Client_readDisplayNameAttribute(UA_Client *client, UA_NodeId nodeId, UA_LocalizedText *outDisplayName) {
+    return __UA_Client_readAttribute(client, nodeId, UA_ATTRIBUTEID_DISPLAYNAME,
+                                     outDisplayName, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]); }
+
+static UA_INLINE UA_StatusCode
+UA_Client_readDescriptionAttribute(UA_Client *client, UA_NodeId nodeId, UA_LocalizedText *outDescription) {
+    return __UA_Client_readAttribute(client, nodeId, UA_ATTRIBUTEID_DESCRIPTION,
+                                     outDescription, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]); }
+
+static UA_INLINE UA_StatusCode
+UA_Client_readWriteMaskAttribute(UA_Client *client, UA_NodeId nodeId, UA_UInt32 *outWriteMask) {
+    return __UA_Client_readAttribute(client, nodeId, UA_ATTRIBUTEID_WRITEMASK,
+                                     outWriteMask, &UA_TYPES[UA_TYPES_UINT32]); }
+
+static UA_INLINE UA_StatusCode
+UA_Client_readUserWriteMaskAttribute(UA_Client *client, UA_NodeId nodeId, UA_UInt32 *outUserWriteMask) {
+    return __UA_Client_readAttribute(client, nodeId, UA_ATTRIBUTEID_USERWRITEMASK,
+                                     outUserWriteMask, &UA_TYPES[UA_TYPES_UINT32]); }
+
+static UA_INLINE UA_StatusCode
+UA_Client_readIsAbstractAttribute(UA_Client *client, UA_NodeId nodeId, UA_Boolean *outIsAbstract) {
+    return __UA_Client_readAttribute(client, nodeId, UA_ATTRIBUTEID_ISABSTRACT,
+                                     outIsAbstract, &UA_TYPES[UA_TYPES_BOOLEAN]); }
+
+static UA_INLINE UA_StatusCode
+UA_Client_readSymmetricAttribute(UA_Client *client, UA_NodeId nodeId, UA_Boolean *outSymmetric) {
+    return __UA_Client_readAttribute(client, nodeId, UA_ATTRIBUTEID_SYMMETRIC,
+                                     outSymmetric, &UA_TYPES[UA_TYPES_BOOLEAN]); }
+
+static UA_INLINE UA_StatusCode
+UA_Client_readInverseNameAttribute(UA_Client *client, UA_NodeId nodeId, UA_LocalizedText *outInverseName) {
+    return __UA_Client_readAttribute(client, nodeId, UA_ATTRIBUTEID_INVERSENAME,
+                                     outInverseName, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]); }
+
+static UA_INLINE UA_StatusCode
+UA_Client_readContainsNoLoopsAttribute(UA_Client *client, UA_NodeId nodeId, UA_Boolean *outContainsNoLoops) {
+    return __UA_Client_readAttribute(client, nodeId, UA_ATTRIBUTEID_CONTAINSNOLOOPS,
+                                     outContainsNoLoops, &UA_TYPES[UA_TYPES_BOOLEAN]); }
+
+static UA_INLINE UA_StatusCode
+UA_Client_readEventNotifierAttribute(UA_Client *client, UA_NodeId nodeId, UA_Byte *outEventNotifier) {
+    return __UA_Client_readAttribute(client, nodeId, UA_ATTRIBUTEID_EVENTNOTIFIER,
+                                     outEventNotifier, &UA_TYPES[UA_TYPES_BYTE]); }
+
+static UA_INLINE UA_StatusCode
+UA_Client_readValueAttribute(UA_Client *client, UA_NodeId nodeId, UA_Variant *outValue) {
+    return __UA_Client_readAttribute(client, nodeId, UA_ATTRIBUTEID_VALUE,
+                                     outValue, &UA_TYPES[UA_TYPES_VARIANT]); }
+
+static UA_INLINE UA_StatusCode
+UA_Client_readDataTypeAttribute(UA_Client *client, UA_NodeId nodeId, UA_NodeId *outDataType) {
+    return __UA_Client_readAttribute(client, nodeId, UA_ATTRIBUTEID_DATATYPE,
+                                     outDataType, &UA_TYPES[UA_TYPES_NODEID]); }
+
+static UA_INLINE UA_StatusCode
+UA_Client_readValueRankAttribute(UA_Client *client, UA_NodeId nodeId, UA_Int32 *outValueRank) {
+    return __UA_Client_readAttribute(client, nodeId, UA_ATTRIBUTEID_VALUERANK,
+                                     outValueRank, &UA_TYPES[UA_TYPES_INT32]); }
+
+// todo: fetch an array
+static UA_INLINE UA_StatusCode
+UA_Client_readArrayDimensionsAttribute(UA_Client *client, UA_NodeId nodeId, UA_Int32 *outArrayDimensions) {
+    return __UA_Client_readAttribute(client, nodeId, UA_ATTRIBUTEID_ARRAYDIMENSIONS,
+                                     outArrayDimensions, &UA_TYPES[UA_TYPES_INT32]); }
+
+static UA_INLINE UA_StatusCode
+UA_Client_readAccessLevelAttribute(UA_Client *client, UA_NodeId nodeId, UA_UInt32 *outAccessLevel) {
+    return __UA_Client_readAttribute(client, nodeId, UA_ATTRIBUTEID_ACCESSLEVEL,
+                                     outAccessLevel, &UA_TYPES[UA_TYPES_UINT32]); }
+
+static UA_INLINE UA_StatusCode
+UA_Client_readUserAccessLevelAttribute(UA_Client *client, UA_NodeId nodeId, UA_UInt32 *outUserAccessLevel) {
+    return __UA_Client_readAttribute(client, nodeId, UA_ATTRIBUTEID_USERACCESSLEVEL,
+                                     outUserAccessLevel, &UA_TYPES[UA_TYPES_UINT32]); }
+
+static UA_INLINE UA_StatusCode
+UA_Client_readMinimumSamplingIntervalAttribute(UA_Client *client, UA_NodeId nodeId,
+                                               UA_Double *outMinimumSamplingInterval) {
+    return __UA_Client_readAttribute(client, nodeId, UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL,
+                                     outMinimumSamplingInterval, &UA_TYPES[UA_TYPES_DOUBLE]); }
+
+static UA_INLINE UA_StatusCode
+UA_Client_readHistorizingAttribute(UA_Client *client, UA_NodeId nodeId, UA_Boolean *outHistorizing) {
+    return __UA_Client_readAttribute(client, nodeId, UA_ATTRIBUTEID_HISTORIZING,
+                                     outHistorizing, &UA_TYPES[UA_TYPES_BOOLEAN]); }
+
+static UA_INLINE UA_StatusCode
+UA_Client_readExecutableAttribute(UA_Client *client, UA_NodeId nodeId, UA_Boolean *outExecutable) {
+    return __UA_Client_readAttribute(client, nodeId, UA_ATTRIBUTEID_EXECUTABLE,
+                                     outExecutable, &UA_TYPES[UA_TYPES_BOOLEAN]); }
+
+static UA_INLINE UA_StatusCode
+UA_Client_readUserExecutableAttribute(UA_Client *client, UA_NodeId nodeId, UA_Boolean *outUserExecutable) {
+    return __UA_Client_readAttribute(client, nodeId, UA_ATTRIBUTEID_USEREXECUTABLE,
+                                     outUserExecutable, &UA_TYPES[UA_TYPES_BOOLEAN]); }
+
+/**********************/
+/* Method Service Set */
+/**********************/
+
+UA_StatusCode UA_EXPORT
+UA_Client_call(UA_Client *client, const UA_NodeId objectId, const UA_NodeId methodId,
+               UA_Int32 inputSize, const UA_Variant *input, UA_Int32 *outputSize, UA_Variant **output);
+
+/**************************/
+/* Subscriptions Handling */
+/**************************/
+
+#ifdef ENABLE_SUBSCRIPTIONS
+
+typedef struct {
+    UA_Double requestedPublishingInterval;
+    UA_UInt32 requestedLifetimeCount;
+    UA_UInt32 requestedMaxKeepAliveCount;
+    UA_UInt32 maxNotificationsPerPublish;
+    UA_Boolean publishingEnabled;
+    UA_Byte priority;
+} UA_SubscriptionSettings;
+
+extern const UA_EXPORT UA_SubscriptionSettings UA_SubscriptionSettings_standard;
+    
+UA_StatusCode UA_EXPORT
+UA_Client_Subscriptions_new(UA_Client *client, UA_SubscriptionSettings settings,
+                            UA_UInt32 *newSubscriptionId);
+    
+UA_StatusCode UA_EXPORT
+UA_Client_Subscriptions_remove(UA_Client *client, UA_UInt32 subscriptionId);
+
+void UA_EXPORT UA_Client_Subscriptions_manuallySendPublishRequest(UA_Client *client);
+
+UA_StatusCode UA_EXPORT
+UA_Client_Subscriptions_addMonitoredItem(UA_Client *client, UA_UInt32 subscriptionId,
+                                         UA_NodeId nodeId, UA_UInt32 attributeID,
+                                         void *handlingFunction, UA_UInt32 *newMonitoredItemId);
+
+UA_StatusCode UA_EXPORT
+UA_Client_Subscriptions_removeMonitoredItem(UA_Client *client, UA_UInt32 subscriptionId,
+                                            UA_UInt32 monitoredItemId);
+#endif
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* UA_CLIENT_HIGHLEVEL_H_ */

+ 40 - 0
include/ua_config.h.in

@@ -9,6 +9,8 @@
 #cmakedefine UA_MULTITHREADING
 #cmakedefine ENABLE_METHODCALLS
 #cmakedefine ENABLE_SUBSCRIPTIONS
+#cmakedefine ENABLE_TYPEINTROSPECTION
+#cmakedefine UA_EMBEDDED_LIBC
 
 /* Function Export */
 #ifdef _WIN32
@@ -67,5 +69,43 @@
 # define UA_ALIGNED_MEMORY_ACCESS
 #endif
 
+/* Inline Functions */
+#ifdef _MSC_VER
+# define UA_INLINE __inline
+#else
+# define UA_INLINE inline
+#endif
+
+/* Define non-aliasing pointers */
+#ifdef _MSC_VER
+# define UA_RESTRICT __restrict
+#elif defined(__GNUC__)
+# define UA_RESTRICT __restrict__
+#else
+# define UA_RESTRICT restrict
+#endif
+
+/* Function attributes */
+#ifdef __GNUC__
+# define UA_FUNC_ATTR_MALLOC __attribute__((malloc))
+# define UA_FUNC_ATTR_PURE __attribute__ ((pure))
+# define UA_FUNC_ATTR_CONST __attribute__((const))
+# define UA_FUNC_ATTR_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
+#else
+# define UA_FUNC_ATTR_MALLOC
+# define UA_FUNC_ATTR_PURE
+# define UA_FUNC_ATTR_CONST
+# define UA_FUNC_ATTR_WARN_UNUSED_RESULT
+#endif
+
+#include <stddef.h>
+#ifdef UA_EMBEDDED_LIBC
+  void *memcpy(void *UA_RESTRICT dest, const void *UA_RESTRICT src, size_t n);
+  void *memset(void *dest, int c, size_t n);
+  size_t strlen(const char *s);
+  int memcmp(const void *vl, const void *vr, size_t n);
+#else
+# include <string.h>
+#endif
 
 #endif /* UA_CONFIG_H_ */

+ 47 - 26
include/ua_connection.h

@@ -21,12 +21,7 @@ extern "C" {
 #endif
 
 #include "ua_types.h"
-
-/**
- * @defgroup communication Communication
- *
- * @{
- */
+#include "ua_job.h"
 
 typedef enum UA_ConnectionState {
     UA_CONNECTION_OPENING, ///< The socket is open, but the HEL/ACK handshake is not done
@@ -48,9 +43,6 @@ extern const UA_EXPORT UA_ConnectionConfig UA_ConnectionConfig_standard;
 struct UA_SecureChannel;
 typedef struct UA_SecureChannel UA_SecureChannel;
 
-struct UA_Connection;
-typedef struct UA_Connection UA_Connection;
-
 /**
  * The connection to a single client (or server). The connection is defined independent of the
  * underlying network layer implementation. This allows a plugging-in custom implementations (e.g.
@@ -60,29 +52,42 @@ struct UA_Connection {
     UA_ConnectionState state;
     UA_ConnectionConfig localConf;
     UA_ConnectionConfig remoteConf;
-    UA_SecureChannel *channel; ///> The securechannel that is attached to this connection (or null)
-    UA_Int32 sockfd; ///> Most connectivity solutions run on sockets. Having the socket id here simplifies the design.
-    void *handle; ///> A pointer to the networklayer
-    UA_ByteString incompleteMessage; ///> Half-received messages (tcp is a streaming protocol) get stored here
-    UA_StatusCode (*getBuffer)(UA_Connection *connection, UA_ByteString *buf); ///> Get a buffer of the maximum remote recv size
-    void (*releaseBuffer)(UA_Connection *connection, UA_ByteString *buf); ///> Release the buffer manually
+    UA_SecureChannel *channel; ///< The securechannel that is attached to this connection (or null)
+    UA_Int32 sockfd; ///< Most connectivity solutions run on sockets. Having the socket id here
+                     ///  simplifies the design.
+    void *handle; ///< A pointer to the networklayer
+    UA_ByteString incompleteMessage; ///< A half-received message (TCP is a streaming protocol) is stored here
+
+    /** Get a buffer for sending */
+    UA_StatusCode (*getSendBuffer)(UA_Connection *connection, size_t length, UA_ByteString *buf);
+
+    /** Release the send buffer manually */
+    void (*releaseSendBuffer)(UA_Connection *connection, UA_ByteString *buf);
+
     /**
      * Sends a message over the connection.
      * @param connection The connection
-     * @param buf The message buffer is potentially reused (or freed) internally if sending succeeds.
-     * @param buflen Since the buffer is potentially reused, we provide a separate content length.
+     * @param buf The message buffer is always released (freed) internally
      * @return Returns an error code or UA_STATUSCODE_GOOD.
      */
-    UA_StatusCode (*write)(UA_Connection *connection, UA_ByteString *buf, size_t buflen);
+    UA_StatusCode (*send)(UA_Connection *connection, UA_ByteString *buf);
+
    /**
      * Receive a message from the remote connection
 	 * @param connection The connection
-	 * @param response The response string. It is allocated by the connection and needs to be freed with connection->releaseBuffer
+	 * @param response The response string. It is allocated by the connection and needs to be freed
+              with connection->releaseBuffer
      * @param timeout Timeout of the recv operation in milliseconds
-     * @return Returns UA_STATUSCODE_BADCOMMUNICATIONERROR if the recv operation can be repeated, UA_STATUSCODE_GOOD if it succeeded and
-     * UA_STATUSCODE_BADCONNECTIONCLOSED if the connection was closed.
+     * @return Returns UA_STATUSCODE_BADCOMMUNICATIONERROR if the recv operation can be repeated,
+     *         UA_STATUSCODE_GOOD if it succeeded and UA_STATUSCODE_BADCONNECTIONCLOSED if the
+     *         connection was closed.
 	 */
     UA_StatusCode (*recv)(UA_Connection *connection, UA_ByteString *response, UA_UInt32 timeout);
+
+    /** Release the buffer of a received message */
+    void (*releaseRecvBuffer)(UA_Connection *connection, UA_ByteString *buf);
+
+    /** Close the connection */
     void (*close)(UA_Connection *connection);
 };
 
@@ -92,11 +97,27 @@ void UA_EXPORT UA_Connection_deleteMembers(UA_Connection *connection);
 void UA_EXPORT UA_Connection_detachSecureChannel(UA_Connection *connection);
 void UA_EXPORT UA_Connection_attachSecureChannel(UA_Connection *connection, UA_SecureChannel *channel);
 
-/** Returns a string of complete message (the length entry is decoded for that).
-    If the received message is incomplete, it is retained in the connection. */
-UA_ByteString UA_EXPORT UA_Connection_completeMessages(UA_Connection *connection, UA_ByteString received);
-
-/** @} */
+/**
+ * The network layer may receive chopped up messages since TCP is a streaming
+ * protocol. Furthermore, the networklayer may operate on ringbuffers or
+ * statically assigned memory.
+ *
+ * If an entire message is received, it is forwarded directly. But the memory
+ * needs to be freed with the networklayer-specific mechanism. If a half message
+ * is received, we copy it into a local buffer. Then, the stack-specific free
+ * needs to be used.
+ *
+ * @param connection The connection
+ * @param message The received message. The content may be overwritten when a
+ *        previsouly received buffer is completed.
+ * @param realloced The Boolean value is set to true if the outgoing message has
+ *        been reallocated from the network layer.
+ * @return Returns UA_STATUSCODE_GOOD or an error code. When an error occurs, the ingoing message
+ *         and the current buffer in the connection are freed.
+ */
+UA_StatusCode UA_EXPORT
+UA_Connection_completeMessages(UA_Connection *connection, UA_ByteString * UA_RESTRICT message,
+                               UA_Boolean * UA_RESTRICT realloced);
 
 #ifdef __cplusplus
 } // extern "C"

+ 56 - 0
include/ua_job.h

@@ -0,0 +1,56 @@
+ /*
+ * Copyright (C) 2014 the contributors as stated in the AUTHORS file
+ *
+ * This file is part of open62541. open62541 is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU Lesser General
+ * Public License, version 3 (as published by the Free Software Foundation) with
+ * a static linking exception as stated in the LICENSE file provided with
+ * open62541.
+ *
+ * open62541 is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ */
+
+#ifndef UA_JOB_H_
+#define UA_JOB_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct UA_Connection;
+typedef struct UA_Connection UA_Connection;
+
+struct UA_Server;
+typedef struct UA_Server UA_Server;
+
+/** Jobs describe work that is executed once or repeatedly in the server */
+typedef struct {
+    enum {
+        UA_JOBTYPE_NOTHING, ///< Guess what?
+        UA_JOBTYPE_DETACHCONNECTION, ///< Detach the connection from the secure channel (but don't delete it)
+        UA_JOBTYPE_BINARYMESSAGE_NETWORKLAYER, ///< The binary message is memory managed by the networklayer
+        UA_JOBTYPE_BINARYMESSAGE_ALLOCATED, ///< The binary message was relocated away from the networklayer
+        UA_JOBTYPE_METHODCALL, ///< Call the method as soon as possible
+        UA_JOBTYPE_METHODCALL_DELAYED, ///< Call the method as soon as all previous jobs have finished
+    } type;
+    union {
+        UA_Connection *closeConnection;
+        struct {
+            UA_Connection *connection;
+            UA_ByteString message;
+        } binaryMessage;
+        struct {
+            void *data;
+            void (*method)(UA_Server *server, void *data);
+        } methodCall;
+    } job;
+} UA_Job;
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* UA_JOB_H_ */

+ 3 - 1
include/ua_log.h

@@ -40,7 +40,9 @@ typedef enum {
 } UA_LogLevel;
 
 typedef enum {
-    UA_LOGCATEGORY_COMMUNICATION,
+    UA_LOGCATEGORY_NETWORK,
+    UA_LOGCATEGORY_SECURECHANNEL,
+    UA_LOGCATEGORY_SESSION,
     UA_LOGCATEGORY_SERVER,
     UA_LOGCATEGORY_CLIENT,
     UA_LOGCATEGORY_USERLAND

+ 356 - 476
include/ua_server.h

@@ -24,14 +24,13 @@ extern "C" {
 #include "ua_types.h"
 #include "ua_types_generated.h"
 #include "ua_nodeids.h"
-#include "ua_connection.h"
 #include "ua_log.h"
-  
-/**
- * @defgroup server Server
- *
- * @{
- */
+#include "ua_job.h"
+#include "ua_connection.h"
+
+/*********************************/
+/* Initialize and run the server */
+/*********************************/
 
 typedef struct UA_ServerConfig {
     UA_Boolean  Login_enableAnonymous;
@@ -47,40 +46,119 @@ typedef struct UA_ServerConfig {
 
 extern UA_EXPORT const UA_ServerConfig UA_ServerConfig_standard;
 
-struct UA_Server;
-typedef struct UA_Server UA_Server;
-
 UA_Server UA_EXPORT * UA_Server_new(UA_ServerConfig config);
 void UA_EXPORT UA_Server_setServerCertificate(UA_Server *server, UA_ByteString certificate);
 void UA_EXPORT UA_Server_delete(UA_Server *server);
 
 /** Sets the logger used by the server */
 void UA_EXPORT UA_Server_setLogger(UA_Server *server, UA_Logger logger);
-UA_Logger UA_EXPORT UA_Server_getLogger(UA_Server *server);
 
 /**
  * Runs the main loop of the server. In each iteration, this calls into the networklayers to see if
  * jobs have arrived and checks if repeated jobs need to be triggered.
  *
  * @param server The server object
- *
  * @param nThreads The number of worker threads. Is ignored if MULTITHREADING is not activated.
- *
  * @param running Points to a boolean value on the heap. When running is set to false, the worker
- * threads and the main loop close and the server is shut down.
- *
+ *        threads and the main loop close and the server is shut down.
  * @return Indicates whether the server shut down cleanly
- *
  */
 UA_StatusCode UA_EXPORT UA_Server_run(UA_Server *server, UA_UInt16 nThreads, UA_Boolean *running);
 
-/* The prologue part of UA_Server_run (no need to use if you call UA_Server_run) */
+/** The prologue part of UA_Server_run (no need to use if you call UA_Server_run) */
 UA_StatusCode UA_EXPORT UA_Server_run_startup(UA_Server *server, UA_UInt16 nThreads, UA_Boolean *running);
-/* The epilogue part of UA_Server_run (no need to use if you call UA_Server_run) */
+
+/** The epilogue part of UA_Server_run (no need to use if you call UA_Server_run) */
 UA_StatusCode UA_EXPORT UA_Server_run_shutdown(UA_Server *server, UA_UInt16 nThreads);
-/* One iteration of UA_Server_run (no need to use if you call UA_Server_run) */
+
+/** One iteration of UA_Server_run (no need to use if you call UA_Server_run) */
 UA_StatusCode UA_EXPORT UA_Server_run_mainloop(UA_Server *server, UA_Boolean *running);
 
+/**
+ * @param server The server object.
+ * @param job The job that shall be added.
+ * @param interval The job shall be repeatedly executed with the given interval (in ms). The
+ *        interval must be larger than 5ms. The first execution occurs at now() + interval at the
+ *        latest.
+ * @param jobId Set to the guid of the repeated job. This can be used to cancel the job later on. If
+ *        the pointer is null, the guid is not set.
+ * @return Upon success, UA_STATUSCODE_GOOD is returned. An error code otherwise.
+ */
+UA_StatusCode UA_EXPORT UA_Server_addRepeatedJob(UA_Server *server, UA_Job job,
+                                                 UA_UInt32 interval, UA_Guid *jobId);
+
+/**
+ * Remove repeated job. The entry will be removed asynchronously during the next iteration of the
+ * server main loop.
+ *
+ * @param server The server object.
+ * @param jobId The id of the job that shall be removed.
+ * @return Upon sucess, UA_STATUSCODE_GOOD is returned. An error code otherwise.
+ */
+UA_StatusCode UA_EXPORT UA_Server_removeRepeatedJob(UA_Server *server, UA_Guid jobId);
+
+/**
+ * Interface to the binary network layers. This structure is returned from the
+ * function that initializes the network layer. The layer is already bound to a
+ * specific port and listening. The functions in the structure are never called
+ * in parallel but only sequentially from the server's main loop. So the network
+ * layer does not need to be thread-safe.
+ */
+typedef struct UA_ServerNetworkLayer {
+    UA_String discoveryUrl;
+    UA_Logger logger; ///< Set during _start
+
+    /**
+     * Starts listening on the the networklayer.
+     *
+     * @param nl The network layer
+     * @param logger The logger
+     * @return Returns UA_STATUSCODE_GOOD or an error code.
+     */
+    UA_StatusCode (*start)(struct UA_ServerNetworkLayer *nl, UA_Logger logger);
+    
+    /**
+     * Gets called from the main server loop and returns the jobs (accumulated messages and close
+     * events) for dispatch.
+     *
+     * @param nl The network layer
+     * @param jobs When the returned integer is >0, *jobs points to an array of UA_Job of the
+     * returned size.
+     * @param timeout The timeout during which an event must arrive in microseconds
+     * @return The size of the jobs array. If the result is negative, an error has occurred.
+     */
+    size_t (*getJobs)(struct UA_ServerNetworkLayer *nl, UA_Job **jobs, UA_UInt16 timeout);
+
+    /**
+     * Closes the network connection and returns all the jobs that need to be finished before the
+     * network layer can be safely deleted.
+     *
+     * @param nl The network layer
+     * @param jobs When the returned integer is >0, jobs points to an array of UA_Job of the
+     * returned size.
+     * @return The size of the jobs array. If the result is negative, an error has occurred.
+     */
+    size_t (*stop)(struct UA_ServerNetworkLayer *nl, UA_Job **jobs);
+
+    /** Deletes the network layer. Call only after a successful shutdown. */
+    void (*deleteMembers)(struct UA_ServerNetworkLayer *nl);
+} UA_ServerNetworkLayer;
+
+/**
+ * Adds a network layer to the server. The network layer is destroyed together
+ * with the server. Do not use it after adding it as it might be moved around on
+ * the heap.
+ */
+void UA_EXPORT UA_Server_addNetworkLayer(UA_Server *server, UA_ServerNetworkLayer *networkLayer);
+
+/** @brief Add a new namespace to the server. Returns the index of the new namespace */
+UA_UInt16 UA_EXPORT UA_Server_addNamespace(UA_Server *server, const char* name);
+
+
+/**********************/
+/* Set Node Callbacks */
+/**********************/
+
 /**
  * Datasources are the interface to local data providers. It is expected that
  * the read and release callbacks are implemented. The write callback can be set
@@ -93,6 +171,7 @@ typedef struct {
      * Copies the data from the source into the provided value.
      *
      * @param handle An optional pointer to user-defined data for the specific data source
+     * @param nodeid Id of the read node
      * @param includeSourceTimeStamp If true, then the datasource is expected to set the source
      *        timestamp in the returned value
      * @param range If not null, then the datasource shall return only a selection of the (nonscalar)
@@ -102,534 +181,335 @@ typedef struct {
      * @return Returns a status code for logging. Error codes intended for the original caller are set
      *         in the value. If an error is returned, then no releasing of the value is done.
      */
-    UA_StatusCode (*read)(void *handle, UA_Boolean includeSourceTimeStamp, const UA_NumericRange *range, UA_DataValue *value);
+    UA_StatusCode (*read)(void *handle, const UA_NodeId nodeid, UA_Boolean includeSourceTimeStamp,
+                          const UA_NumericRange *range, UA_DataValue *value);
 
     /**
      * Write into a data source. The write member of UA_DataSource can be empty if the operation
      * is unsupported.
      *
      * @param handle An optional pointer to user-defined data for the specific data source
+     * @param nodeid Id of the node being written to
      * @param data The data to be written into the data source
      * @param range An optional data range. If the data source is scalar or does not support writing
      *        of ranges, then an error code is returned.
      * @return Returns a status code that is returned to the user
      */
-    UA_StatusCode (*write)(void *handle, const UA_Variant *data, const UA_NumericRange *range);
+    UA_StatusCode (*write)(void *handle, const UA_NodeId nodeid,
+                           const UA_Variant *data, const UA_NumericRange *range);
 } UA_DataSource;
 
-/** @brief Add a new namespace to the server. Returns the index of the new namespace */
-UA_UInt16 UA_EXPORT UA_Server_addNamespace(UA_Server *server, const char* name);
-
-/** Add a reference to the server's address space */
-UA_StatusCode UA_EXPORT UA_Server_addReference(UA_Server *server, const UA_NodeId sourceId,
-                                               const UA_NodeId refTypeId, const UA_ExpandedNodeId targetId);
-
-/** Deletes a node from the nodestore.
- *
- * @param server The server object
- * @param nodeId ID of the node to be deleted
-
- * @return Return UA_STATUSCODE_GOOD if the node was deleted or an appropriate errorcode if the node was not found
- *         or cannot be deleted.
- */
 UA_StatusCode UA_EXPORT
-UA_Server_deleteNode(UA_Server *server, UA_NodeId nodeId);
+UA_Server_setVariableNode_dataSource(UA_Server *server, const UA_NodeId nodeId,
+                                     const UA_DataSource dataSource);
 
-#define UA_SERVER_DELETENODEALIAS_DECL(TYPE) \
-UA_StatusCode UA_EXPORT UA_Server_delete##TYPE##Node(UA_Server *server, UA_NodeId nodeId);
+/* Value Callbacks can be attach to variable and variable type nodes. If
+   not-null, they are called before reading and after writing respectively */
+typedef struct {
+    void *handle;
+    void (*onRead)(void *handle, const UA_NodeId nodeid,
+                   const UA_Variant *data, const UA_NumericRange *range);
+    void (*onWrite)(void *handle, const UA_NodeId nodeid,
+                    const UA_Variant *data, const UA_NumericRange *range);
+} UA_ValueCallback;
 
-UA_SERVER_DELETENODEALIAS_DECL(Object)
-UA_SERVER_DELETENODEALIAS_DECL(Variable)
-UA_SERVER_DELETENODEALIAS_DECL(ReferenceType)
-UA_SERVER_DELETENODEALIAS_DECL(View)
-UA_SERVER_DELETENODEALIAS_DECL(VariableType)
-UA_SERVER_DELETENODEALIAS_DECL(DataType)
+UA_StatusCode UA_EXPORT
+UA_Server_setVariableNode_valueCallback(UA_Server *server, const UA_NodeId nodeId,
+                                        const UA_ValueCallback callback);
 
-#ifdef ENABLE_METHODCALLS
-UA_SERVER_DELETENODEALIAS_DECL(Method)
-#endif
+/* The lifecycle management allows to track the instantiation and deletion of
+   object nodes derived from object types. */
+typedef struct {
+    void * (*constructor)(const UA_NodeId instance); ///< Returns the instance handle attached to the node
+    void (*destructor)(const UA_NodeId instance, void *instanceHandle);
+} UA_ObjectLifecycleManagement;
 
-/** Deletes a copied instance of a node by deallocating it and all its attributes. This assumes that the node was
- * priorly copied using getNodeCopy. To delete nodes that are located in the nodestore, use UA_Server_deleteNode()
- * instead.
- *
- * @param server The server object
- * @param node   A copy of any node-type struct created with getNodeCopy; must *not* be managed by the nodestore.
- * 
- * @return Return UA_STATUSCODE_GOOD if the node was deleted or an appropriate errorcode if the node was not found
- *         or cannot be deleted.
- */
-UA_StatusCode UA_EXPORT 
-UA_Server_deleteNodeCopy(UA_Server *server, void **node);
+UA_StatusCode UA_EXPORT
+UA_Server_setObjectTypeNode_instanceLifecycleManagement(UA_Server *server, UA_NodeId nodeId,
+                                                        UA_ObjectLifecycleManagement olm);
 
-/** Creates a deep copy of a node located in the nodestore and returns it to the userspace. Note that any manipulation
- * of this copied node is not reflected by the server, but otherwise not accessible attributes of the node's struct
- * can be examined in bulk. node->nodeClass can be used to cast the node to a specific node type. Use 
- * UA_Server_deleteNodeCopy() to deallocate this node.
- *
- * @param server The server object
- * @param nodeId ID of the node copy to be copied
- * @param copyInto Pointer to a NULL pointer that will hold the copy of the node on a successfull return.
- * 
- * @return Return UA_STATUSCODE_GOOD if the node was copied or an appropriate errorcode if the node was not found
- *         or cannot be copied.
- */
-UA_StatusCode UA_EXPORT 
-UA_Server_getNodeCopy(UA_Server *server, UA_NodeId nodeId, void **copyInto);
+/* Iterate over all nodes referenced by parentNodeId by calling the callback
+   function for each child node */
+typedef UA_StatusCode (*UA_NodeIteratorCallback)(UA_NodeId childId, UA_Boolean isInverse,
+                                                 UA_NodeId referenceTypeId, void *handle);
 
-/** A new variable Node with a value passed in variant.
- *
- * @param server The server object
- * @param nodeId        The requested nodeId of the new node. Use the numeric id with i=0 to get a new ID from the server.
- * @param browseName    The qualified name of this node
- * @param displayName   The localized text shown when displaying the node
- * @param description   The localized human readable description
- * @param userWriteMask Bitmask defining the user write permissions
- * @param writeMask     Bitmask defining the write permissions
- * @param parentNodeId  The node under which this node exists ("parent")
- * @param referenceTypeId Reference type used by the parent to reference this node
- * @param value         A variant containing the value to be assigned to this node.
- * @param createdNodeId Pointer to a NULL pointer that will hold the copy of the nodeId on a successfull return.
- * 
- * @return Return UA_STATUSCODE_GOOD if the node was created or an appropriate error code if not.
- */
 UA_StatusCode UA_EXPORT
-UA_Server_addVariableNode(UA_Server *server, const UA_NodeId nodeId, const UA_QualifiedName browseName,
-                          const UA_LocalizedText displayName, const UA_LocalizedText description, const UA_UInt32 userWriteMask, const UA_UInt32 writeMask,
+UA_Server_forEachChildNodeCall(UA_Server *server, UA_NodeId parentNodeId,
+                               UA_NodeIteratorCallback callback, void *handle);
 
-                          const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
+/*******************/
+/* Node Management */
+/*******************/
 
-                          UA_Variant *value,
+UA_StatusCode UA_EXPORT
+UA_Server_addReference(UA_Server *server, const UA_NodeId sourceId, const UA_NodeId refTypeId,
+                       const UA_ExpandedNodeId targetId, UA_Boolean isForward);
 
-                          UA_NodeId *createdNodeId);
+UA_StatusCode UA_EXPORT
+UA_Server_deleteNode(UA_Server *server, const UA_NodeId nodeId, UA_Boolean deleteReferences);
+    
+UA_StatusCode UA_EXPORT
+UA_Server_deleteReference(UA_Server *server, const UA_NodeId sourceNodeId, const UA_NodeId referenceTypeId,
+                          UA_Boolean isForward, const UA_ExpandedNodeId targetNodeId,
+                          UA_Boolean deleteBidirectional);
 
-// Missing: eventNotifier
+/* Don't use this function. There are typed versions as inline functions. */
 UA_StatusCode UA_EXPORT
-UA_Server_addObjectNode(UA_Server *server, const UA_NodeId nodeId, const UA_QualifiedName browseName,
-                        const UA_LocalizedText displayName, const UA_LocalizedText description, const UA_UInt32 userWriteMask, const UA_UInt32 writeMask,
+__UA_Server_addNode(UA_Server *server, const UA_NodeClass nodeClass, const UA_NodeId requestedNewNodeId,
+                    const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
+                    const UA_QualifiedName browseName, const UA_NodeId typeDefinition,
+                    const UA_NodeAttributes *attr, const UA_DataType *attributeType, UA_NodeId *outNewNodeId);
 
+static UA_INLINE UA_StatusCode
+UA_Server_addVariableNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
+                          const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
+                          const UA_QualifiedName browseName, const UA_NodeId typeDefinition,
+                          const UA_VariableAttributes attr, UA_NodeId *outNewNodeId) {
+    return __UA_Server_addNode(server, UA_NODECLASS_VARIABLE, requestedNewNodeId, parentNodeId,
+                               referenceTypeId, browseName, typeDefinition, (const UA_NodeAttributes*)&attr,
+                               &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES], outNewNodeId); }
+
+static UA_INLINE UA_StatusCode
+UA_Server_addVariableTypeNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
+                              const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
+                              const UA_QualifiedName browseName, const UA_VariableTypeAttributes attr,
+                              UA_NodeId *outNewNodeId) {
+    return __UA_Server_addNode(server, UA_NODECLASS_VARIABLETYPE, requestedNewNodeId, parentNodeId,
+                               referenceTypeId, browseName, UA_NODEID_NULL, (const UA_NodeAttributes*)&attr,
+                               &UA_TYPES[UA_TYPES_VARIABLETYPEATTRIBUTES], outNewNodeId); }
+
+static UA_INLINE UA_StatusCode
+UA_Server_addObjectNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
                         const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
-
-                        const UA_ExpandedNodeId typeDefinition,
-
-                        UA_NodeId *createdNodeId);
-
-// Missing: isAbstract, symmetric
-UA_StatusCode UA_EXPORT 
-UA_Server_addReferenceTypeNode(UA_Server *server, const UA_NodeId nodeId, const UA_QualifiedName browseName,
-                               const UA_LocalizedText displayName, const UA_LocalizedText description, const UA_UInt32 userWriteMask, const UA_UInt32 writeMask,
-
+                        const UA_QualifiedName browseName, const UA_NodeId typeDefinition,
+                        const UA_ObjectAttributes attr, UA_NodeId *outNewNodeId) {
+    return __UA_Server_addNode(server, UA_NODECLASS_OBJECT, requestedNewNodeId, parentNodeId,
+                               referenceTypeId, browseName, typeDefinition, (const UA_NodeAttributes*)&attr,
+                               &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES], outNewNodeId); }
+
+static UA_INLINE UA_StatusCode
+UA_Server_addObjectTypeNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
+                            const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
+                            const UA_QualifiedName browseName, const UA_ObjectTypeAttributes attr,
+                            UA_NodeId *outNewNodeId) {
+    return __UA_Server_addNode(server, UA_NODECLASS_OBJECTTYPE, requestedNewNodeId, parentNodeId,
+                               referenceTypeId, browseName, UA_NODEID_NULL, (const UA_NodeAttributes*)&attr,
+                               &UA_TYPES[UA_TYPES_OBJECTTYPEATTRIBUTES], outNewNodeId); }
+
+static UA_INLINE UA_StatusCode
+UA_Server_addViewNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
+                      const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
+                      const UA_QualifiedName browseName, const UA_ViewAttributes attr,
+                      UA_NodeId *outNewNodeId) {
+    return __UA_Server_addNode(server, UA_NODECLASS_VIEW, requestedNewNodeId, parentNodeId,
+                               referenceTypeId, browseName, UA_NODEID_NULL, (const UA_NodeAttributes*)&attr,
+                               &UA_TYPES[UA_TYPES_VIEWATTRIBUTES], outNewNodeId); }
+
+static UA_INLINE UA_StatusCode
+UA_Server_addReferenceTypeNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
                                const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
-
-                               const UA_ExpandedNodeId typeDefinition,
-                               const UA_LocalizedText inverseName,
-
-                               UA_NodeId *createdNodeId );
+                               const UA_QualifiedName browseName, const UA_ReferenceTypeAttributes attr,
+                               UA_NodeId *outNewNodeId) {
+    return __UA_Server_addNode(server, UA_NODECLASS_REFERENCETYPE, requestedNewNodeId, parentNodeId,
+                               referenceTypeId, browseName, UA_NODEID_NULL, (const UA_NodeAttributes*)&attr,
+                               &UA_TYPES[UA_TYPES_REFERENCETYPEATTRIBUTES], outNewNodeId); }
+
+static UA_INLINE UA_StatusCode
+UA_Server_addDataTypeNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
+                          const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
+                          const UA_QualifiedName browseName, const UA_DataTypeAttributes attr,
+                          UA_NodeId *outNewNodeId) {
+    return __UA_Server_addNode(server, UA_NODECLASS_DATATYPE, requestedNewNodeId, parentNodeId,
+                               referenceTypeId, browseName, UA_NODEID_NULL, (const UA_NodeAttributes*)&attr,
+                               &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES], outNewNodeId); }
 
 UA_StatusCode UA_EXPORT
-UA_Server_addObjectTypeNode(UA_Server *server, const UA_NodeId nodeId, const UA_QualifiedName browseName,
-                            const UA_LocalizedText displayName, const UA_LocalizedText description,  const UA_UInt32 userWriteMask, const UA_UInt32 writeMask,
-
-                            const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
-
-                            const UA_ExpandedNodeId typeDefinition,
-                            const UA_Boolean isAbstract,
-
-                            UA_NodeId *createdNodeId );
-
-UA_StatusCode UA_EXPORT 
-UA_Server_addVariableTypeNode(UA_Server *server, const UA_NodeId nodeId, const UA_QualifiedName browseName,
-                              const UA_LocalizedText displayName, const UA_LocalizedText description, const UA_UInt32 userWriteMask, const UA_UInt32 writeMask,
+UA_Server_addDataSourceVariableNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
+                                    const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
+                                    const UA_QualifiedName browseName, const UA_NodeId typeDefinition,
+                                    const UA_VariableAttributes attr, const UA_DataSource dataSource,
+                                    UA_NodeId *outNewNodeId);
 
-                              const UA_NodeId parentNodeId,
-                              const UA_NodeId referenceTypeId,
-
-                              UA_Variant *value,
-                              const UA_Int32 valueRank,
-                              const UA_Boolean isAbstract,
-
-                              UA_NodeId *createdNodeId);
+#ifdef ENABLE_METHODCALLS
+typedef UA_StatusCode (*UA_MethodCallback)(void *methodHandle, const UA_NodeId objectId,
+                                           size_t inputSize, const UA_Variant *input,
+                                           size_t outputSize, UA_Variant *output);
 
 UA_StatusCode UA_EXPORT
-UA_Server_addDataTypeNode(UA_Server *server, const UA_NodeId nodeId, const UA_QualifiedName browseName,
-                          const UA_LocalizedText displayName, const UA_LocalizedText description, const UA_UInt32 userWriteMask, const UA_UInt32 writeMask,
-
-                          const UA_NodeId parentNodeId,
-                          const UA_NodeId referenceTypeId,
-
-                          const UA_ExpandedNodeId typeDefinition,
-                          const UA_Boolean isAbstract,
-
-                          UA_NodeId *createdNodeId);
-
+UA_Server_addMethodNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
+                        const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
+                        const UA_QualifiedName browseName, const UA_MethodAttributes attr,
+                        UA_MethodCallback method, void *handle,
+                        UA_Int32 inputArgumentsSize, const UA_Argument* inputArguments, 
+                        UA_Int32 outputArgumentsSize, const UA_Argument* outputArguments,
+                        UA_NodeId *outNewNodeId);
+#endif
 
+/*************************/
+/* Write Node Attributes */
+/*************************/
+
+/* The following node attributes cannot be changed once the node is created
+   - NodeClass
+   - NodeId
+   - Symmetric
+   - ContainsNoLoop
+   
+   The following attributes cannot be written, as there is no "user" in the server
+   - UserWriteMask
+   - UserAccessLevel
+   - UserExecutable
+
+   The following attributes are currently taken from the value variant:
+   TODO: Handle them independent from the variable, ensure that the implicit constraints hold
+   - DataType
+   - ValueRank
+   - ArrayDimensions
+   
+   - Historizing is currently unsupported
+  */
+
+/* Don't use this function. There are typed versions with no additional overhead. */
 UA_StatusCode UA_EXPORT
-UA_Server_addViewNode(UA_Server *server, const UA_NodeId nodeId, const UA_QualifiedName browseName,
-                      const UA_LocalizedText displayName, const UA_LocalizedText description, const UA_UInt32 userWriteMask, const UA_UInt32 writeMask,
+__UA_Server_writeAttribute(UA_Server *server, const UA_NodeId nodeId, const UA_AttributeId attributeId, const UA_DataType *type, const void *value);
 
-                      const UA_NodeId parentNodeId,
-                      const UA_NodeId referenceTypeId,
+static UA_INLINE UA_StatusCode
+UA_Server_writeBrowseNameAttribute(UA_Server *server, const UA_NodeId nodeId, const UA_QualifiedName browseName) {
+    return __UA_Server_writeAttribute(server, nodeId, UA_ATTRIBUTEID_BROWSENAME, &UA_TYPES[UA_TYPES_QUALIFIEDNAME], &browseName); }
 
-                      const UA_ExpandedNodeId typeDefinition,
+static UA_INLINE UA_StatusCode
+UA_Server_writeDisplayNameAttribute(UA_Server *server, const UA_NodeId nodeId, const UA_LocalizedText displayName) {
+    return __UA_Server_writeAttribute(server, nodeId, UA_ATTRIBUTEID_DISPLAYNAME, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], &displayName); }
 
-                      UA_NodeId *createdNodeId);
+static UA_INLINE UA_StatusCode
+UA_Server_writeDescriptionAttribute(UA_Server *server, const UA_NodeId nodeId, const UA_LocalizedText description) {
+    return __UA_Server_writeAttribute(server, nodeId, UA_ATTRIBUTEID_DESCRIPTION, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], &description); }
 
-UA_StatusCode UA_EXPORT
-UA_Server_addDataSourceVariableNode(UA_Server *server, const UA_NodeId nodeId, const UA_QualifiedName browseName,
-                                    const UA_LocalizedText displayName, const UA_LocalizedText description,  const UA_UInt32 userWriteMask, const UA_UInt32 writeMask,
+static UA_INLINE UA_StatusCode
+UA_Server_writeWriteMaskAttribute(UA_Server *server, const UA_NodeId nodeId, const UA_UInt32 writeMask) {
+    return __UA_Server_writeAttribute(server, nodeId, UA_ATTRIBUTEID_WRITEMASK, &UA_TYPES[UA_TYPES_UINT32], &writeMask); }
 
-                                    const UA_NodeId parentNodeId,
-                                    const UA_NodeId referenceTypeId,
+static UA_INLINE UA_StatusCode
+UA_Server_writeIsAbstractAttribute(UA_Server *server, const UA_NodeId nodeId, const UA_Boolean isAbstract) {
+    return __UA_Server_writeAttribute(server, nodeId, UA_ATTRIBUTEID_ISABSTRACT, &UA_TYPES[UA_TYPES_BOOLEAN], &isAbstract); }
 
-                                    const UA_DataSource dataSource,
+static UA_INLINE UA_StatusCode
+UA_Server_writeInverseNameAttribute(UA_Server *server, const UA_NodeId nodeId, const UA_LocalizedText inverseName) {
+    return __UA_Server_writeAttribute(server, nodeId, UA_ATTRIBUTEID_INVERSENAME, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], &inverseName); }
 
-                                    UA_NodeId *createdNodeId);
+static UA_INLINE UA_StatusCode
+UA_Server_writeEventNotifierAttribute(UA_Server *server, const UA_NodeId nodeId, const UA_Byte eventNotifier) {
+    return __UA_Server_writeAttribute(server, nodeId, UA_ATTRIBUTEID_EVENTNOTIFIER, &UA_TYPES[UA_TYPES_BYTE], &eventNotifier); }
 
-/* --------------------- */
-UA_StatusCode UA_EXPORT
-UA_Server_addMonodirectionalReference(UA_Server *server, UA_NodeId sourceNodeId,
-                                      UA_ExpandedNodeId targetNodeId, UA_NodeId referenceTypeId,
-                                      UA_Boolean isforward);
+static UA_INLINE UA_StatusCode
+UA_Server_writeValueAttribute(UA_Server *server, const UA_NodeId nodeId, const UA_Variant value) {
+    return __UA_Server_writeAttribute(server, nodeId, UA_ATTRIBUTEID_VALUE, &UA_TYPES[UA_TYPES_VARIANT], &value); }
 
-#ifdef ENABLE_METHODCALLS
-typedef UA_StatusCode (*UA_MethodCallback)(const UA_NodeId objectId, const UA_Variant *input,
-                                           UA_Variant *output, void *handle);
-/** Creates a serverside method including input- and output variable descriptions
- * 
- * @param server The server object.
- * 
- * @param browseName BrowseName to be used for the new method.
- * 
- * @param nodeId Requested NodeId for the new method. If a numeric ID with i=0 is used, the server will assign a random unused id.
- * 
- * @param parentNodeId Parent node containing this method. Note that an ObjectNode needs to reference the method with hasProperty in order for the method to be callable.
- * 
- * @param referenceTypeId Reference type ID to be used by the parent to reference the new method.
- * 
- * @param method Userspace Method/Function of type UA_MethodCallback to be called when a client invokes the method using the Call Service Set.
- * 
- * @param inputArgumentsSize Number of input arguments expected to be passed by a calling client.
- * 
- * @param inputArguments Description of input arguments expected to be passed by a calling client.
- * 
- * @param outputArgumentsSize Description of output arguments expected to be passed by a calling client.
- * 
- * @param outputArguments Description of output arguments expected to be passed by a calling client.
- * 
- * @param createdNodeId Actual nodeId of the new method node if UA_StatusCode indicates success. Can be used to determine the random unique ID assigned by the server if i=0 was passed as a nodeId.
- * 
- */
-UA_StatusCode UA_EXPORT
-UA_Server_addMethodNode(UA_Server *server, const UA_NodeId nodeId, const UA_QualifiedName browseName,
-                        UA_LocalizedText displayName, UA_LocalizedText description, const UA_NodeId parentNodeId, 
-                        const UA_NodeId referenceTypeId, UA_UInt32 userWriteMask, UA_UInt32 writeMask, 
-                        UA_MethodCallback method, void *handle, UA_Int32 inputArgumentsSize, const UA_Argument *inputArguments, 
-                        UA_Int32 outputArgumentsSize, const UA_Argument *outputArguments,
-                        UA_NodeId *createdNodeId);
-#endif
+static UA_INLINE UA_StatusCode
+UA_Server_writeAccessLevelAttribute(UA_Server *server, const UA_NodeId nodeId, const UA_UInt32 accessLevel) {
+    return __UA_Server_writeAttribute(server, nodeId, UA_ATTRIBUTEID_ACCESSLEVEL, &UA_TYPES[UA_TYPES_UINT32], &accessLevel); }
 
-#ifndef _HAVE_UA_NODEITERATORCALLBACK_D
-#define _HAVE_UA_NODEITERATORCALLBACK_D
-typedef UA_StatusCode (*UA_NodeIteratorCallback)(UA_NodeId childId, UA_Boolean isInverse, UA_NodeId referenceTypeId, void *handle);
-#endif
+static UA_INLINE UA_StatusCode
+UA_Server_writeMinimumSamplingIntervalAttribute(UA_Server *server, const UA_NodeId nodeId, const UA_Double miniumSamplingInterval) {
+    return __UA_Server_writeAttribute(server, nodeId, UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL, &UA_TYPES[UA_TYPES_DOUBLE], &miniumSamplingInterval); }
 
-/** Iterate over all nodes referenced by parentNodeId by calling the callback function for each child node
- * 
- * @param server The server object.
- *
- * @param parentNodeId The NodeId of the parent whose references are to be iterated over
- *
- * @param callback The function of type UA_NodeIteratorCallback to be called for each referenced child
- *
- * @return Upon success, UA_STATUSCODE_GOOD is returned. An error code otherwise.
- */
-UA_StatusCode UA_EXPORT UA_Server_forEachChildNodeCall(UA_Server *server, UA_NodeId parentNodeId, UA_NodeIteratorCallback callback, void *handle);
-
-UA_StatusCode UA_EXPORT UA_Server_setAttributeValue(UA_Server *server, UA_NodeId nodeId, UA_AttributeId attributeId, void *value);
-// Attribute specific macros for setAttribute_are defined in ua_server_addressspace.c
-#define UA_Server_setAttribute_nodeId(SERVER, NODEID, VALUE) UA_Server_setAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_NODEID, (UA_NodeId *) VALUE);
-#define UA_Server_setAttribute_nodeClass(SERVER, NODEID, VALUE) UA_Server_setAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_NODECLASS, (UA_NodeClass *) VALUE);
-#define UA_Server_setAttribute_browseName(SERVER, NODEID, VALUE) UA_Server_setAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_BROWSENAME, (UA_QualifiedName *) VALUE);
-#define UA_Server_setAttribute_displayName(SERVER, NODEID, VALUE) UA_Server_setAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_DISPLAYNAME, (UA_LocalizedText *) VALUE);
-#define UA_Server_setAttribute_description(SERVER, NODEID, VALUE) UA_Server_setAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_DESCRIPTION, (UA_LocalizedText *) VALUE);
-#define UA_Server_setAttribute_writeMask(SERVER, NODEID, VALUE) UA_Server_setAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_WRITEMASK, (UA_UInt32 *) VALUE);
-#define UA_Server_setAttribute_userWriteMask(SERVER, NODEID, VALUE) UA_Server_setAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_USERWRITEMASK, (UA_UInt32 *) VALUE);
-#define UA_Server_setAttribute_isAbstract(SERVER, NODEID, VALUE) UA_Server_setAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_ISABSTRACT, (UA_Boolean *) VALUE);
-#define UA_Server_setAttribute_symmetric(SERVER, NODEID, VALUE) UA_Server_setAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_SYMMETRIC, (UA_Boolean *) VALUE);
-#define UA_Server_setAttribute_inverseName(SERVER, NODEID, VALUE) UA_Server_setAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_INVERSENAME, (UA_LocalizedText *) VALUE);
-#define UA_Server_setAttribute_containsNoLoops(SERVER, NODEID, VALUE) UA_Server_setAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_CONTAINSNOLOOPS, (UA_Boolean *) VALUE);
-#define UA_Server_setAttribute_eventNotifier(SERVER, NODEID, VALUE) UA_Server_setAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_EVENTNOTIFIER, (UA_Byte *) VALUE);
-#define UA_Server_setAttribute_value(SERVER, NODEID, VALUE) UA_Server_setAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_VALUE, (UA_Variant *) VALUE);
-#define UA_Server_setAttribute_dataType(SERVER, NODEID, VALUE) UA_Server_setAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_DATATYPE, (UA_NodeId *) VALUE);
-#define UA_Server_setAttribute_valueRank(SERVER, NODEID, VALUE) UA_Server_setAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_VALUERANK, (UA_Int32 *) VALUE);
-#define UA_Server_setAttribute_arrayDimensions(SERVER, NODEID, VALUE) UA_Server_setAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_ARRAYDIMENSIONS, (UA_Int32 *) VALUE);
-#define UA_Server_setAttribute_accessLevel(SERVER, NODEID, VALUE) UA_Server_setAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_ACCESSLEVEL, (UA_UInt32 *) VALUE);
-#define UA_Server_setAttribute_userAccessLevel(SERVER, NODEID, VALUE) UA_Server_setAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_USERACCESSLEVEL, (UA_UInt32 *) VALUE);
-#define UA_Server_setAttribute_minimumSamplingInterval(SERVER, NODEID, VALUE) UA_Server_setAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL, (UA_Double *) VALUE);
-#define UA_Server_setAttribute_historizing(SERVER, NODEID, VALUE) UA_Server_setAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_HISTORIZING, (UA_Boolean *) VALUE);
-#define UA_Server_setAttribute_executable(SERVER, NODEID, VALUE) UA_Server_setAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_EXECUTABLE, (UA_Boolean *) VALUE);
-#define UA_Server_setAttribute_userExecutable(SERVER, NODEID, VALUE) UA_Server_setAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_USEREXECUTABLE, (UA_Boolean *) VALUE);
+static UA_INLINE UA_StatusCode
+UA_Server_writeExecutableAttribute(UA_Server *server, const UA_NodeId nodeId, const UA_Boolean executable) {
+    return __UA_Server_writeAttribute(server, nodeId, UA_ATTRIBUTEID_EXECUTABLE, &UA_TYPES[UA_TYPES_BOOLEAN], &executable); }
 
-#ifdef ENABLE_METHODCALLS
-UA_StatusCode UA_EXPORT
-UA_Server_setAttribute_method(UA_Server *server, UA_NodeId methodNodeId, UA_MethodCallback method, void *handle);
-#endif
+/************************/
+/* Read Node Attributes */
+/************************/
 
-UA_StatusCode UA_EXPORT
-UA_Server_setAttribute_DataSource(UA_Server *server, UA_NodeId nodeId, UA_DataSource *value);
+/* The following attributes cannot be read, since the "admin" user always has all rights.
+   - UserWriteMask
+   - UserAccessLevel
+   - UserExecutable
+*/
 
+/* Don't use this function. There are typed versions for every supported attribute. */
 UA_StatusCode UA_EXPORT
-UA_Server_getAttributeValue(UA_Server *server, UA_NodeId nodeId, UA_AttributeId attributeId, void **value);
-#define UA_Server_getAttribute_nodeId(SERVER, NODEID, VALUE) UA_Server_getAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_NODEID, (UA_NodeId **) VALUE);
-#define UA_Server_getAttribute_nodeClass(SERVER, NODEID, VALUE) UA_Server_getAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_NODECLASS, (UA_NodeClass **) VALUE);
-#define UA_Server_getAttribute_browseName(SERVER, NODEID, VALUE) UA_Server_getAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_BROWSENAME, (UA_QualifiedName **) VALUE);
-#define UA_Server_getAttribute_displayName(SERVER, NODEID, VALUE) UA_Server_getAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_DISPLAYNAME, (UA_LocalizedText **) VALUE);
-#define UA_Server_getAttribute_description(SERVER, NODEID, VALUE) UA_Server_getAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_DESCRIPTION, (UA_LocalizedText **) VALUE);
-#define UA_Server_getAttribute_writeMask(SERVER, NODEID, VALUE) UA_Server_getAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_WRITEMASK, (UA_UInt32 **) VALUE);
-#define UA_Server_getAttribute_userWriteMask(SERVER, NODEID, VALUE) UA_Server_getAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_USERWRITEMASK, (UA_UInt32 **) VALUE);
-#define UA_Server_getAttribute_isAbstract(SERVER, NODEID, VALUE) UA_Server_getAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_ISABSTRACT, (UA_Boolean **) VALUE);
-#define UA_Server_getAttribute_symmetric(SERVER, NODEID, VALUE) UA_Server_getAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_SYMMETRIC, (UA_Boolean **) VALUE);
-#define UA_Server_getAttribute_inverseName(SERVER, NODEID, VALUE) UA_Server_getAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_INVERSENAME, (UA_LocalizedText **) VALUE);
-#define UA_Server_getAttribute_containsNoLoops(SERVER, NODEID, VALUE) UA_Server_getAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_CONTAINSNOLOOPS, (UA_Boolean **) VALUE);
-#define UA_Server_getAttribute_eventNotifier(SERVER, NODEID, VALUE) UA_Server_getAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_EVENTNOTIFIER, (UA_Byte **) VALUE);
-#define UA_Server_getAttribute_value(SERVER, NODEID, VALUE) UA_Server_getAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_VALUE, (UA_Variant **) VALUE);
-#define UA_Server_getAttribute_dataType(SERVER, NODEID, VALUE) UA_Server_getAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_DATATYPE, (UA_NodeId **) VALUE);
-#define UA_Server_getAttribute_valueRank(SERVER, NODEID, VALUE) UA_Server_getAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_VALUERANK, (UA_Int32 **) VALUE);
-#define UA_Server_getAttribute_arrayDimensions(SERVER, NODEID, VALUE) UA_Server_getAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_ARRAYDIMENSIONS, (UA_Int32 **) VALUE);
-#define UA_Server_getAttribute_accessLevel(SERVER, NODEID, VALUE) UA_Server_getAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_ACCESSLEVEL, (UA_UInt32 **) VALUE);
-#define UA_Server_getAttribute_userAccessLevel(SERVER, NODEID, VALUE) UA_Server_getAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_USERACCESSLEVEL, (UA_UInt32 **) VALUE);
-#define UA_Server_getAttribute_minimumSamplingInterval(SERVER, NODEID, VALUE) UA_Server_getAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL, (UA_Double **) VALUE);
-#define UA_Server_getAttribute_historizing(SERVER, NODEID, VALUE) UA_Server_getAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_HISTORIZING, (UA_Boolean **) VALUE);
-#define UA_Server_getAttribute_executable(SERVER, NODEID, VALUE) UA_Server_getAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_EXECUTABLE, (UA_Boolean **) VALUE);
-#define UA_Server_getAttribute_userExecutable(SERVER, NODEID, VALUE) UA_Server_getAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_USEREXECUTABLE, (UA_Boolean **) VALUE);
+__UA_Server_readAttribute(UA_Server *server, UA_NodeId nodeId, UA_AttributeId attributeId, void *v);
+  
+static UA_INLINE UA_StatusCode
+UA_Server_readNodeIdAttribute(UA_Server *server, UA_NodeId nodeId, UA_NodeId *outNodeId) {
+    return __UA_Server_readAttribute(server, nodeId, UA_ATTRIBUTEID_NODEID, outNodeId); }
 
-#ifdef ENABLE_METHODCALLS
-UA_StatusCode UA_EXPORT
-UA_Server_getAttribute_method(UA_Server *server, UA_NodeId methodNodeId, UA_MethodCallback *method);
-#endif
+static UA_INLINE UA_StatusCode
+UA_Server_readNodeClassAttribute(UA_Server *server, UA_NodeId nodeId, UA_NodeClass *outNodeClass) {
+    return __UA_Server_readAttribute(server, nodeId, UA_ATTRIBUTEID_NODECLASS, outNodeClass); }
 
-UA_StatusCode UA_EXPORT
-UA_Server_getAttribute_DataSource(UA_Server *server, UA_NodeId nodeId, UA_DataSource **value);
+static UA_INLINE UA_StatusCode
+UA_Server_readBrowseNameAttribute(UA_Server *server, UA_NodeId nodeId, UA_QualifiedName *outBrowseName) {
+    return __UA_Server_readAttribute(server, nodeId, UA_ATTRIBUTEID_BROWSENAME, outBrowseName); }
 
-/** Jobs describe work that is executed once or repeatedly. */
-typedef struct {
-    enum {
-        UA_JOBTYPE_NOTHING,
-        UA_JOBTYPE_DETACHCONNECTION,
-        UA_JOBTYPE_BINARYMESSAGE,
-        UA_JOBTYPE_METHODCALL,
-        UA_JOBTYPE_DELAYEDMETHODCALL,
-    } type;
-    union {
-        UA_Connection *closeConnection;
-        struct {
-            UA_Connection *connection;
-            UA_ByteString message;
-        } binaryMessage;
-        struct {
-            void *data;
-            void (*method)(UA_Server *server, void *data);
-        } methodCall;
-    } job;
-} UA_Job;
+static UA_INLINE UA_StatusCode
+UA_Server_readDisplayNameAttribute(UA_Server *server, UA_NodeId nodeId, UA_LocalizedText *outDisplayName) {
+    return __UA_Server_readAttribute(server, nodeId, UA_ATTRIBUTEID_DISPLAYNAME, outDisplayName); }
 
-/**
- * @param server The server object.
- *
- * @param job Pointer to the job that shall be added. The pointer is not freed but copied to an
- *        internal representation.
- *
- * @param interval The job shall be repeatedly executed with the given interval (in ms). The
- *        interval must be larger than 5ms. The first execution occurs at now() + interval at the
- *        latest.
- *
- * @param jobId Set to the guid of the repeated job. This can be used to cancel the job later on. If
- *        the pointer is null, the guid is not set.
- *
- * @return Upon success, UA_STATUSCODE_GOOD is returned. An error code otherwise.
- */
-UA_StatusCode UA_EXPORT UA_Server_addRepeatedJob(UA_Server *server, UA_Job job, UA_UInt32 interval,
-                                                 UA_Guid *jobId);
+static UA_INLINE UA_StatusCode
+UA_Server_readDescriptionAttribute(UA_Server *server, UA_NodeId nodeId, UA_LocalizedText *outDescription) {
+    return __UA_Server_readAttribute(server, nodeId, UA_ATTRIBUTEID_DESCRIPTION, outDescription); }
 
-/**
- * Remove repeated job. The entry will be removed asynchronously during the
- * next iteration of the server main loop.
- *
- * @param server The server object.
- *
- * @param jobId The id of the job that shall be removed.
- *
- * @return Upon sucess, UA_STATUSCODE_GOOD is returned. An error code otherwise.
- */
-UA_StatusCode UA_EXPORT UA_Server_removeRepeatedJob(UA_Server *server, UA_Guid jobId);
+static UA_INLINE UA_StatusCode
+UA_Server_readWriteMaskAttribute(UA_Server *server, UA_NodeId nodeId, UA_UInt32 *outWriteMask) {
+    return __UA_Server_readAttribute(server, nodeId, UA_ATTRIBUTEID_WRITEMASK, outWriteMask); }
 
-/**
- * Interface to the binary network layers. This structure is returned from the
- * function that initializes the network layer. The layer is already bound to a
- * specific port and listening. The functions in the structure are never called
- * in parallel but only sequentially from the server's main loop. So the network
- * layer does not need to be thread-safe.
- */
-typedef struct UA_ServerNetworkLayer {
-    void *handle;
-    UA_String discoveryUrl;
+static UA_INLINE UA_StatusCode
+UA_Server_readIsAbstractAttribute(UA_Server *server, UA_NodeId nodeId, UA_Boolean *outIsAbstract) {
+    return __UA_Server_readAttribute(server, nodeId, UA_ATTRIBUTEID_ISABSTRACT, outIsAbstract); }
 
-    /**
-     * Starts listening on the the networklayer.
-     *
-     * @param nl The network layer
-     * @param logger The logger
-     * @return Returns UA_STATUSCODE_GOOD or an error code.
-     */
-    UA_StatusCode (*start)(struct UA_ServerNetworkLayer *nl, UA_Logger *logger);
-    
-    /**
-     * Gets called from the main server loop and returns the jobs (accumulated messages and close
-     * events) for dispatch.
-     *
-     * @param nl The network layer
-     * @param jobs When the returned integer is positive, *jobs points to an array of UA_Job of the
-     * returned size.
-     * @param timeout The timeout during which an event must arrive in microseconds
-     * @return The size of the jobs array. If the result is negative, an error has occurred.
-     */
-    UA_Int32 (*getJobs)(struct UA_ServerNetworkLayer *nl, UA_Job **jobs, UA_UInt16 timeout);
+static UA_INLINE UA_StatusCode
+UA_Server_readSymmetricAttribute(UA_Server *server, UA_NodeId nodeId, UA_Boolean *outSymmetric) {
+    return __UA_Server_readAttribute(server, nodeId, UA_ATTRIBUTEID_SYMMETRIC, outSymmetric); }
 
-    /**
-     * Closes the network connection and returns all the jobs that need to be finished before the
-     * network layer can be safely deleted.
-     *
-     * @param nl The network layer
-     * @param jobs When the returned integer is positive, jobs points to an array of UA_Job of the
-     * returned size.
-     * @return The size of the jobs array. If the result is negative, an error has occurred.
-     */
-    UA_Int32 (*stop)(struct UA_ServerNetworkLayer *nl, UA_Job **jobs);
+static UA_INLINE UA_StatusCode
+UA_Server_readInverseNameAttribute(UA_Server *server, UA_NodeId nodeId, UA_LocalizedText *outInverseName) {
+    return __UA_Server_readAttribute(server, nodeId, UA_ATTRIBUTEID_INVERSENAME, outInverseName); }
 
-    /** Deletes the network layer. Call only after a successful shutdown. */
-    void (*deleteMembers)(struct UA_ServerNetworkLayer *nl);
-} UA_ServerNetworkLayer;
+static UA_INLINE UA_StatusCode
+UA_Server_readContainsNoLoopAttribute(UA_Server *server, UA_NodeId nodeId, UA_Boolean *outContainsNoLoops) {
+    return __UA_Server_readAttribute(server, nodeId, UA_ATTRIBUTEID_CONTAINSNOLOOPS, outContainsNoLoops); }
 
-/**
- * Adds a network layer to the server. The network layer is destroyed together
- * with the server. Do not use it after adding it as it might be moved around on
- * the heap.
- */
-void UA_EXPORT UA_Server_addNetworkLayer(UA_Server *server, UA_ServerNetworkLayer networkLayer);
+static UA_INLINE UA_StatusCode
+UA_Server_readEventNotifierAttribute(UA_Server *server, UA_NodeId nodeId, UA_Byte *outEventNotifier) {
+    return __UA_Server_readAttribute(server, nodeId, UA_ATTRIBUTEID_EVENTNOTIFIER, outEventNotifier); }
 
-/** @} */
+static UA_INLINE UA_StatusCode
+UA_Server_readValueAttribute(UA_Server *server, UA_NodeId nodeId, UA_Variant *outValue) {
+    return __UA_Server_readAttribute(server, nodeId, UA_ATTRIBUTEID_VALUE, outValue); }
 
-#ifndef __cplusplus /* the external nodestore does not work with c++ so far */
+static UA_INLINE UA_StatusCode
+UA_Server_readDataTypeAttribute(UA_Server *server, UA_NodeId nodeId, UA_NodeId *outDataType) {
+    return __UA_Server_readAttribute(server, nodeId, UA_ATTRIBUTEID_DATATYPE, outDataType); }
 
-/**
- * @ingroup nodestore
- *
- * @defgroup external_nodestore External Nodestore
- *
- * @brief An external application that manages its own data and data model
- *
- * To plug in outside data sources, one can use
- *
- * - VariableNodes with a data source (functions that are called for read and write access)
- * - An external nodestore that is mapped to specific namespaces
- *
- * If no external nodestore is defined for a nodeid, it is always looked up in
- * the "local" nodestore of open62541. Namespace Zero is always in the local
- * nodestore.
- *
- * @{
- */
+static UA_INLINE UA_StatusCode
+UA_Server_readValueRankAttribute(UA_Server *server, UA_NodeId nodeId, UA_Int32 *outValueRank) {
+    return __UA_Server_readAttribute(server, nodeId, UA_ATTRIBUTEID_VALUERANK, outValueRank); }
 
-typedef UA_Int32 (*UA_ExternalNodeStore_addNodes)
-(void *ensHandle, const UA_RequestHeader *requestHeader, UA_AddNodesItem *nodesToAdd, UA_UInt32 *indices,
- UA_UInt32 indicesSize, UA_AddNodesResult* addNodesResults, UA_DiagnosticInfo *diagnosticInfos);
-
-typedef UA_Int32 (*UA_ExternalNodeStore_addReferences)
-(void *ensHandle, const UA_RequestHeader *requestHeader, UA_AddReferencesItem* referencesToAdd,
- UA_UInt32 *indices,UA_UInt32 indicesSize, UA_StatusCode *addReferencesResults,
- UA_DiagnosticInfo *diagnosticInfos);
-
-typedef UA_Int32 (*UA_ExternalNodeStore_deleteNodes)
-(void *ensHandle, const UA_RequestHeader *requestHeader, UA_DeleteNodesItem *nodesToDelete, UA_UInt32 *indices,
- UA_UInt32 indicesSize, UA_StatusCode *deleteNodesResults, UA_DiagnosticInfo *diagnosticInfos);
-
-typedef UA_Int32 (*UA_ExternalNodeStore_deleteReferences)
-(void *ensHandle, const UA_RequestHeader *requestHeader, UA_DeleteReferencesItem *referenceToDelete,
- UA_UInt32 *indices, UA_UInt32 indicesSize, UA_StatusCode deleteReferencesresults,
- UA_DiagnosticInfo *diagnosticInfos);
-
-typedef UA_Int32 (*UA_ExternalNodeStore_readNodes)
-(void *ensHandle, const UA_RequestHeader *requestHeader, UA_ReadValueId *readValueIds, UA_UInt32 *indices,
- UA_UInt32 indicesSize,UA_DataValue *readNodesResults, UA_Boolean timeStampToReturn,
- UA_DiagnosticInfo *diagnosticInfos);
-
-typedef UA_Int32 (*UA_ExternalNodeStore_writeNodes)
-(void *ensHandle, const UA_RequestHeader *requestHeader, UA_WriteValue *writeValues, UA_UInt32 *indices,
- UA_UInt32 indicesSize, UA_StatusCode *writeNodesResults, UA_DiagnosticInfo *diagnosticInfo);
-
-typedef UA_Int32 (*UA_ExternalNodeStore_browseNodes)
-(void *ensHandle, const UA_RequestHeader *requestHeader, UA_BrowseDescription *browseDescriptions,
- UA_UInt32 *indices, UA_UInt32 indicesSize, UA_UInt32 requestedMaxReferencesPerNode,
- UA_BrowseResult *browseResults, UA_DiagnosticInfo *diagnosticInfos);
-
-typedef UA_Int32 (*UA_ExternalNodeStore_translateBrowsePathsToNodeIds)
-(void *ensHandle, const UA_RequestHeader *requestHeader, UA_BrowsePath *browsePath,
- UA_UInt32 *indices, UA_UInt32 indicesSize, UA_BrowsePathResult *browsePathResults, UA_DiagnosticInfo *diagnosticInfos);
-
-typedef UA_Int32 (*UA_ExternalNodeStore_delete)(void *ensHandle);
-
-typedef struct UA_ExternalNodeStore {
-    void *ensHandle;
-	UA_ExternalNodeStore_addNodes addNodes;
-	UA_ExternalNodeStore_deleteNodes deleteNodes;
-	UA_ExternalNodeStore_writeNodes writeNodes;
-	UA_ExternalNodeStore_readNodes readNodes;
-	UA_ExternalNodeStore_browseNodes browseNodes;
-	UA_ExternalNodeStore_translateBrowsePathsToNodeIds translateBrowsePathsToNodeIds;
-	UA_ExternalNodeStore_addReferences addReferences;
-	UA_ExternalNodeStore_deleteReferences deleteReferences;
-	UA_ExternalNodeStore_delete destroy;
-} UA_ExternalNodeStore;
-
-#ifdef UA_EXTERNAL_NAMESPACES
-UA_StatusCode UA_EXPORT
-UA_Server_addExternalNamespace(UA_Server *server, UA_UInt16 namespaceIndex, const UA_String *url, UA_ExternalNodeStore *nodeStore);
-#endif /* UA_EXTERNAL_NAMESPACES*/
-/** @} */
+// todo: fetch an array with a length field
+static UA_INLINE UA_StatusCode
+UA_Server_readArrayDimensionsAttribute(UA_Server *server, UA_NodeId nodeId, UA_Int32 *outArrayDimensions) {
+    return __UA_Server_readAttribute(server, nodeId, UA_ATTRIBUTEID_ARRAYDIMENSIONS, outArrayDimensions); }
 
-#endif /* external nodestore */
+static UA_INLINE UA_StatusCode
+UA_Server_readAccessLevelAttribute(UA_Server *server, UA_NodeId nodeId, UA_UInt32 *outAccessLevel) {
+    return __UA_Server_readAttribute(server, nodeId, UA_ATTRIBUTEID_ACCESSLEVEL, outAccessLevel); }
 
-#ifndef _HAVE_UA_INSTANTIONCALLBACK_D
-#define _HAVE_UA_INSTANTIONCALLBACK_D
-typedef UA_StatusCode (*UA_InstantiationCallback)(UA_NodeId objectId, UA_NodeId definitionId, void *handle);
-#endif
+static UA_INLINE UA_StatusCode
+UA_Server_readMinimumSamplingIntervalAttribute(UA_Server *server, UA_NodeId nodeId, UA_Double *outMinimumSamplingInterval) {
+    return __UA_Server_readAttribute(server, nodeId, UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL, outMinimumSamplingInterval); }
 
-typedef struct arrayOfNodeIds_s {
-  UA_Int32  size;
-  UA_NodeId *ids;
-} arrayOfNodeIds;
+static UA_INLINE UA_StatusCode
+UA_Server_readHistorizingAttribute(UA_Server *server, UA_NodeId nodeId, UA_Boolean *outHistorizing) {
+    return __UA_Server_readAttribute(server, nodeId, UA_ATTRIBUTEID_HISTORIZING, outHistorizing); }
 
-UA_StatusCode UA_EXPORT
-UA_Server_appendInstanceOfSupertype(UA_Server *server, UA_NodeId nodeId, UA_NodeId appendToNodeId, 
-                                    arrayOfNodeIds *subtypeRefs, arrayOfNodeIds *componentRefs, 
-                                    UA_InstantiationCallback callback, arrayOfNodeIds *instantiatedTypes, 
-                                    void *handle);
-
-void UA_EXPORT
-UA_Server_addInstanceOf_instatiateChildNode(UA_Server *server, 
-                                                 arrayOfNodeIds *subtypeRefs, arrayOfNodeIds *componentRefs, arrayOfNodeIds *typedefRefs,
-                                                 UA_NodeId objectRoot, UA_InstantiationCallback callback, void *typeDefNode,
-                                                 UA_Boolean instantiateObjects, arrayOfNodeIds *instantiatedTypes, void *handle);
-                                                 
-UA_StatusCode UA_EXPORT
-UA_Server_addInstanceOf(UA_Server *server, UA_NodeId nodeId, const UA_QualifiedName browseName,
-                        UA_LocalizedText displayName, UA_LocalizedText description, const UA_NodeId parentNodeId, 
-                        const UA_NodeId referenceTypeId, UA_UInt32 userWriteMask, UA_UInt32 writeMask, 
-                        const UA_ExpandedNodeId typeDefinition, UA_InstantiationCallback callback, void *handle, 
-                        UA_NodeId *createdNodeId);
+static UA_INLINE UA_StatusCode
+UA_Server_readExecutableAttribute(UA_Server *server, UA_NodeId nodeId, UA_Boolean *outExecutable) {
+    return __UA_Server_readAttribute(server, nodeId, UA_ATTRIBUTEID_EXECUTABLE, outExecutable); }
 
 #ifdef __cplusplus
-} // extern "C"
+}
 #endif
 
 #endif /* UA_SERVER_H_ */

+ 96 - 0
include/ua_server_external_ns.h

@@ -0,0 +1,96 @@
+ /*
+ * Copyright (C) 2014 the contributors as stated in the AUTHORS file
+ *
+ * This file is part of open62541. open62541 is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU Lesser General
+ * Public License, version 3 (as published by the Free Software Foundation) with
+ * a static linking exception as stated in the LICENSE file provided with
+ * open62541.
+ *
+ * open62541 is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ */
+
+#ifndef UA_SERVER_EXTERNAL_NS_H_
+#define UA_SERVER_EXTERNAL_NS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * An external application that manages its own data and data model. To plug in
+ * outside data sources, one can use
+ *
+ * - VariableNodes with a data source (functions that are called for read and write access)
+ * - An external nodestore that is mapped to specific namespaces
+ *
+ * If no external nodestore is defined for a nodeid, it is always looked up in
+ * the "local" nodestore of open62541. Namespace Zero is always in the local
+ * nodestore.
+ *
+ * @{
+ */
+
+typedef UA_Int32 (*UA_ExternalNodeStore_addNodes)
+(void *ensHandle, const UA_RequestHeader *requestHeader, UA_AddNodesItem *nodesToAdd, UA_UInt32 *indices,
+ UA_UInt32 indicesSize, UA_AddNodesResult* addNodesResults, UA_DiagnosticInfo *diagnosticInfos);
+
+typedef UA_Int32 (*UA_ExternalNodeStore_addReferences)
+(void *ensHandle, const UA_RequestHeader *requestHeader, UA_AddReferencesItem* referencesToAdd,
+ UA_UInt32 *indices,UA_UInt32 indicesSize, UA_StatusCode *addReferencesResults,
+ UA_DiagnosticInfo *diagnosticInfos);
+
+typedef UA_Int32 (*UA_ExternalNodeStore_deleteNodes)
+(void *ensHandle, const UA_RequestHeader *requestHeader, UA_DeleteNodesItem *nodesToDelete, UA_UInt32 *indices,
+ UA_UInt32 indicesSize, UA_StatusCode *deleteNodesResults, UA_DiagnosticInfo *diagnosticInfos);
+
+typedef UA_Int32 (*UA_ExternalNodeStore_deleteReferences)
+(void *ensHandle, const UA_RequestHeader *requestHeader, UA_DeleteReferencesItem *referenceToDelete,
+ UA_UInt32 *indices, UA_UInt32 indicesSize, UA_StatusCode deleteReferencesresults,
+ UA_DiagnosticInfo *diagnosticInfos);
+
+typedef UA_Int32 (*UA_ExternalNodeStore_readNodes)
+(void *ensHandle, const UA_RequestHeader *requestHeader, UA_ReadValueId *readValueIds, UA_UInt32 *indices,
+ UA_UInt32 indicesSize,UA_DataValue *readNodesResults, UA_Boolean timeStampToReturn,
+ UA_DiagnosticInfo *diagnosticInfos);
+
+typedef UA_Int32 (*UA_ExternalNodeStore_writeNodes)
+(void *ensHandle, const UA_RequestHeader *requestHeader, UA_WriteValue *writeValues, UA_UInt32 *indices,
+ UA_UInt32 indicesSize, UA_StatusCode *writeNodesResults, UA_DiagnosticInfo *diagnosticInfo);
+
+typedef UA_Int32 (*UA_ExternalNodeStore_browseNodes)
+(void *ensHandle, const UA_RequestHeader *requestHeader, UA_BrowseDescription *browseDescriptions,
+ UA_UInt32 *indices, UA_UInt32 indicesSize, UA_UInt32 requestedMaxReferencesPerNode,
+ UA_BrowseResult *browseResults, UA_DiagnosticInfo *diagnosticInfos);
+
+typedef UA_Int32 (*UA_ExternalNodeStore_translateBrowsePathsToNodeIds)
+(void *ensHandle, const UA_RequestHeader *requestHeader, UA_BrowsePath *browsePath, UA_UInt32 *indices,
+ UA_UInt32 indicesSize, UA_BrowsePathResult *browsePathResults, UA_DiagnosticInfo *diagnosticInfos);
+
+typedef UA_Int32 (*UA_ExternalNodeStore_delete)(void *ensHandle);
+
+typedef struct UA_ExternalNodeStore {
+    void *ensHandle;
+	UA_ExternalNodeStore_addNodes addNodes;
+	UA_ExternalNodeStore_deleteNodes deleteNodes;
+	UA_ExternalNodeStore_writeNodes writeNodes;
+	UA_ExternalNodeStore_readNodes readNodes;
+	UA_ExternalNodeStore_browseNodes browseNodes;
+	UA_ExternalNodeStore_translateBrowsePathsToNodeIds translateBrowsePathsToNodeIds;
+	UA_ExternalNodeStore_addReferences addReferences;
+	UA_ExternalNodeStore_deleteReferences deleteReferences;
+	UA_ExternalNodeStore_delete destroy;
+} UA_ExternalNodeStore;
+
+UA_StatusCode UA_EXPORT
+UA_Server_addExternalNamespace(UA_Server *server, UA_UInt16 namespaceIndex, const UA_String *url,
+                               UA_ExternalNodeStore *nodeStore);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UA_SERVER_EXTERNAL_NS_H_ */

+ 3 - 2
include/ua_statuscodes.h

@@ -1,7 +1,8 @@
 #ifndef OPCUA_STATUSCODES_H_
 #define OPCUA_STATUSCODES_H_
 
-enum UA_StatusCode {
+/** A numeric identifier for a error or condition that is associated with a value or an operation. */
+typedef enum {
 	UA_STATUSCODE_GOOD = 0x00,
 	UA_STATUSCODE_BADUNEXPECTEDERROR = 0x80010000, // An unexpected error occurred.
 	UA_STATUSCODE_BADINTERNALERROR = 0x80020000, // An internal error occurred as a result of a programming or configuration error.
@@ -219,6 +220,6 @@ enum UA_StatusCode {
 	UA_STATUSCODE_BADWOULDBLOCK = 0x80b50000, // Non blocking behaviour is required and the operation would block.
 	UA_STATUSCODE_BADSYNTAXERROR = 0x80b60000, // A value had an invalid syntax.
 	UA_STATUSCODE_BADMAXCONNECTIONSREACHED = 0x80b70000 // The operation could not be finished because all available connections are in use.
-};
+} UA_StatusCode;
 
 #endif /* UA_STATUSCODES_H_ */

+ 368 - 338
include/ua_types.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 the contributors as stated in the AUTHORS file
+ * Copyright (C) 2013-2015 the contributors as stated in the AUTHORS file
  *
  * This file is part of open62541. open62541 is free software: you can
  * redistribute it and/or modify it under the terms of the GNU Lesser General
@@ -20,76 +20,123 @@
 extern "C" {
 #endif
 
-#include <stdint.h>
+#ifndef UA_FFI_BINDINGS
 #include <stdbool.h>
-#include <stddef.h>
-#include <string.h>
+#include <inttypes.h>
+#endif
+
 #include "ua_config.h"
+#include "ua_statuscodes.h"
 
-/** @brief A two-state logical value (true or false). */
+/* Indicates that an array has the length 0 (NULL indicates an non-defined array of length -1)*/
+#define UA_EMPTY_ARRAY_SENTINEL ((void*)0x01)
+
+/****************************/
+/* Builtin Type Definitions */
+/****************************/
+
+#define UA_BUILTIN_TYPES_COUNT 25
+
+/** Boolean: A two-state logical value (true or false) */
 typedef bool UA_Boolean;
 #define UA_TRUE true
 #define UA_FALSE false
 
-/** @brief An integer value between -129 and 127. */
+/** SByte: An integer value between -128 and 127 */
 typedef int8_t UA_SByte;
 #define UA_SBYTE_MAX 127
-#define UA_SBYTE_MIN -128
+#define UA_SBYTE_MIN (-128)
 
-/** @brief An integer value between 0 and 256. */
+/** Byte: An integer value between 0 and 256 */
 typedef uint8_t UA_Byte;
 #define UA_BYTE_MAX 256
 #define UA_BYTE_MIN 0
 
-/** @brief An integer value between -32 768 and 32 767. */
+/** Int16: An integer value between -32 768 and 32 767 */
 typedef int16_t UA_Int16;
 #define UA_INT16_MAX 32767
-#define UA_INT16_MIN -32768
+#define UA_INT16_MIN (-32768)
 
-/** @brief An integer value between 0 and 65 535. */
+/** UInt16: An integer value between 0 and 65 535 */
 typedef uint16_t UA_UInt16;
 #define UA_UINT16_MAX 65535
 #define UA_UINT16_MIN 0
 
-/** @brief An integer value between -2 147 483 648 and 2 147 483 647. */
+/** Int32: An integer value between -2 147 483 648 and 2 147 483 647 */
 typedef int32_t UA_Int32;
 #define UA_INT32_MAX 2147483647
-#define UA_INT32_MIN -2147483648
+#define UA_INT32_MIN (-2147483648)
 
-/** @brief An integer value between 0 and 429 4967 295. */
+/** UInt32: An integer value between 0 and 4 294 967 295 */
 typedef uint32_t UA_UInt32;
 #define UA_UINT32_MAX 4294967295
 #define UA_UINT32_MIN 0
 
-/** @brief An integer value between -10 223 372 036 854 775 808 and 9 223 372 036 854 775 807 */
+/** Int64: An integer value between -10 223 372 036 854 775 808 and 9 223 372 036 854 775 807 */
 typedef int64_t UA_Int64;
 #define UA_INT64_MAX (int64_t)9223372036854775807
-#define UA_INT64_MIN (int64_t)-9223372036854775808
+#define UA_INT64_MIN ((int64_t)-9223372036854775808)
 
-/** @brief An integer value between 0 and 18 446 744 073 709 551 615. */
+/** UInt64: An integer value between 0 and 18 446 744 073 709 551 615 */
 typedef uint64_t UA_UInt64;
 #define UA_UINT64_MAX (int64_t)18446744073709551615
 #define UA_UINT64_MIN (int64_t)0
 
-/** @brief An IEEE single precision (32 bit) floating point value. */
+/** Float: An IEEE single precision (32 bit) floating point value */
 typedef float UA_Float;
 
-/** @brief An IEEE double precision (64 bit) floating point value. */
+/** Double: An IEEE double precision (64 bit) floating point value */
 typedef double UA_Double;
 
-/** @brief A sequence of Unicode characters. */
+/********************************************/
+/* String: A sequence of Unicode characters */
+/********************************************/
 typedef struct {
-    UA_Int32 length; ///< The length of the string
-    UA_Byte *data; ///< The string's content (not null-terminated)
+    size_t length; // The length of the string
+    UA_Byte *data; // The string's content (not null-terminated)
 } UA_String;
 
-/** @brief An instance in time.
- * A DateTime value is encoded as a 64-bit signed integer which represents the
- * number of 100 nanosecond intervals since January 1, 1601 (UTC).
- */
-typedef UA_Int64 UA_DateTime; // 100 nanosecond resolution
+UA_EXPORT extern const UA_String UA_STRING_NULL;
 
-/** @brief A 16 byte value that can be used as a globally unique identifier. */
+static UA_INLINE UA_String UA_STRING(char *chars) {
+    UA_String str; str.length = strlen(chars);
+    str.data = (UA_Byte*)chars; return str; }
+
+#define UA_STRING_ALLOC(CHARS) UA_String_fromChars(CHARS)
+    
+/** Copies the content on the heap. Returns a null-string when alloc fails */
+UA_String UA_EXPORT UA_String_fromChars(char const src[]) UA_FUNC_ATTR_WARN_UNUSED_RESULT;
+
+UA_Boolean UA_EXPORT UA_String_equal(const UA_String *s1, const UA_String *s2);
+
+/*********************************/
+/* DateTime: An instance in time */
+/*********************************/
+/* A DateTime value is encoded as a 64-bit signed integer which represents the
+   number of 100 nanosecond intervals since January 1, 1601 (UTC) */
+typedef UA_Int64 UA_DateTime;
+
+UA_DateTime UA_EXPORT UA_DateTime_now(void); ///> The current time
+
+typedef struct UA_DateTimeStruct {
+    UA_UInt16 nanoSec;
+    UA_UInt16 microSec;
+    UA_UInt16 milliSec;
+    UA_UInt16 sec;
+    UA_UInt16 min;
+    UA_UInt16 hour;
+    UA_UInt16 day;
+    UA_UInt16 month;
+    UA_UInt16 year;
+} UA_DateTimeStruct;
+
+UA_DateTimeStruct UA_EXPORT UA_DateTime_toStruct(UA_DateTime time);
+
+UA_String UA_EXPORT UA_DateTime_toString(UA_DateTime time);
+
+/**************************************************************************/
+/* Guid: A 16 byte value that can be used as a globally unique identifier */
+/**************************************************************************/
 typedef struct {
     UA_UInt32 data1;
     UA_UInt16 data2;
@@ -97,280 +144,202 @@ typedef struct {
     UA_Byte   data4[8];
 } UA_Guid;
 
-/** @brief A sequence of octets. */
+UA_Boolean UA_EXPORT UA_Guid_equal(const UA_Guid *g1, const UA_Guid *g2);
+
+UA_Guid UA_EXPORT UA_Guid_random(UA_UInt32 *seed);
+
+/************************************/
+/* ByteString: A sequence of octets */
+/************************************/
 typedef UA_String UA_ByteString;
 
-/** @brief An XML element. */
+static UA_INLINE UA_Boolean UA_ByteString_equal(const UA_ByteString *string1, const UA_ByteString *string2) {
+    return UA_String_equal((const UA_String*)string1, (const UA_String*)string2); }
+
+/* Allocates memory of size length for the bytestring. The content is not set to zero. */
+UA_StatusCode UA_EXPORT UA_ByteString_allocBuffer(UA_ByteString *bs, size_t length) UA_FUNC_ATTR_WARN_UNUSED_RESULT;
+
+UA_EXPORT extern const UA_ByteString UA_BYTESTRING_NULL;
+
+static UA_INLINE UA_ByteString UA_BYTESTRING(char *chars) {
+    UA_ByteString str; str.length = strlen(chars); str.data = (UA_Byte*)chars; return str; }
+
+static UA_INLINE UA_ByteString UA_BYTESTRING_ALLOC(const char *chars) {
+    UA_String str = UA_String_fromChars(chars); UA_ByteString bstr;
+    bstr.length = str.length; bstr.data = str.data; return bstr;
+}
+
+/******************************/
+/* XmlElement: An XML element */
+/******************************/
 typedef UA_String UA_XmlElement;
 
-/** @brief An identifier for a node in the address space of an OPC UA Server. */
-/* The shortened numeric types are introduced during encoding. */
+/*****************************************************************************/
+/* NodeId: An identifier for a node in the address space of an OPC UA Server */
+/*****************************************************************************/
 enum UA_NodeIdType {
-    UA_NODEIDTYPE_NUMERIC    = 2,
+    UA_NODEIDTYPE_NUMERIC    = 0, ///< On the wire, this can be 0,1,2 for numeric nodeids of different sizes
     UA_NODEIDTYPE_STRING     = 3,
     UA_NODEIDTYPE_GUID       = 4,
     UA_NODEIDTYPE_BYTESTRING = 5
 };
 
 typedef struct {
-    UA_UInt16 namespaceIndex; ///< The namespace index of the node
-    enum UA_NodeIdType identifierType; ///< The type of the node identifier
+    UA_UInt16 namespaceIndex;
+    enum UA_NodeIdType identifierType;
     union {
         UA_UInt32     numeric;
         UA_String     string;
         UA_Guid       guid;
         UA_ByteString byteString;
-    } identifier; ///< The node identifier
+    } identifier;
 } UA_NodeId;
 
-/** @brief A NodeId that allows the namespace URI to be specified instead of an index. */
-typedef struct {
-    UA_NodeId nodeId; ///< The nodeid without extensions
-    UA_String namespaceUri; ///< The Uri of the namespace (tindex in the nodeId is ignored)
-    UA_UInt32 serverIndex;  ///< The index of the server
-} UA_ExpandedNodeId;
+UA_EXPORT extern const UA_NodeId UA_NODEID_NULL;
 
-#include "ua_statuscodes.h"
-/** @brief A numeric identifier for a error or condition that is associated with a value or an operation. */
-typedef enum UA_StatusCode UA_StatusCode; // StatusCodes aren't an enum(=int) since 32 unsigned bits are needed. See also ua_statuscodes.h */
+static UA_INLINE UA_Boolean UA_NodeId_isNull(const UA_NodeId *p) {
+    return (p->namespaceIndex == 0 && p->identifierType == UA_NODEIDTYPE_NUMERIC &&
+            p->identifier.numeric == 0);
+}
 
-/** @brief A name qualified by a namespace. */
-typedef struct {
-    UA_UInt16 namespaceIndex; ///< The namespace index
-    UA_String name; ///< The actual name
-} UA_QualifiedName;
+UA_Boolean UA_EXPORT UA_NodeId_equal(const UA_NodeId *n1, const UA_NodeId *n2);
 
-/** @brief Human readable text with an optional locale identifier. */
-typedef struct {
-    UA_String locale; ///< The locale (e.g. "en-US")
-    UA_String text; ///< The actual text
-} UA_LocalizedText;
+static UA_INLINE UA_NodeId UA_NODEID_NUMERIC(UA_UInt16 nsIndex, UA_Int32 identifier) {
+    UA_NodeId id; id.namespaceIndex = nsIndex; id.identifierType = UA_NODEIDTYPE_NUMERIC;
+    id.identifier.numeric = identifier; return id; }
 
-/** @brief A structure that contains an application specific data type that may
-    not be recognized by the receiver. */
-typedef struct {
-    UA_NodeId typeId; ///< The nodeid of the datatype
-    enum {
-        UA_EXTENSIONOBJECT_ENCODINGMASK_NOBODYISENCODED  = 0,
-        UA_EXTENSIONOBJECT_ENCODINGMASK_BODYISBYTESTRING = 1,
-        UA_EXTENSIONOBJECT_ENCODINGMASK_BODYISXML        = 2
-    } encoding; ///< The encoding of the contained data
-    UA_ByteString body; ///< The bytestring of the encoded data
-} UA_ExtensionObject;
+static UA_INLINE UA_NodeId UA_NODEID_STRING(UA_UInt16 nsIndex, char *chars) {
+    UA_NodeId id; id.namespaceIndex = nsIndex; id.identifierType = UA_NODEIDTYPE_STRING;
+    id.identifier.string = UA_STRING(chars); return id; }
 
-struct UA_DataType;
-typedef struct UA_DataType UA_DataType; 
+static UA_INLINE UA_NodeId UA_NODEID_STRING_ALLOC(UA_UInt16 nsIndex, const char *chars) {
+    UA_NodeId id; id.namespaceIndex = nsIndex; id.identifierType = UA_NODEIDTYPE_STRING;
+    id.identifier.string = UA_STRING_ALLOC(chars); return id; }
 
-/** @brief Variants store (arrays of) any data type. Either they provide a pointer to the data in
- *   memory, or functions from which the data can be accessed. Variants are replaced together with
- *   the data they store (exception: use a data source).
- *
- * Variant semantics:
- *  - arrayLength = -1 && data == NULL: empty variant
- *  - arrayLength = -1 && data == !NULL: variant holds a single element (a scalar)
- *  - arrayLength >= 0: variant holds an array of the appropriate length
- *                      data can be NULL if arrayLength == 0
- */
+static UA_INLINE UA_NodeId UA_NODEID_GUID(UA_UInt16 nsIndex, UA_Guid guid) {
+    UA_NodeId id; id.namespaceIndex = nsIndex; id.identifierType = UA_NODEIDTYPE_GUID;
+    id.identifier.guid = guid; return id; }
 
-typedef struct {
-    const UA_DataType *type; ///< The nodeid of the datatype
-    enum {
-        UA_VARIANT_DATA, ///< The data is "owned" by this variant (copied and deleted together)
-        UA_VARIANT_DATA_NODELETE, /**< The data is "borrowed" by the variant and shall not be
-                                       deleted at the end of this variant's lifecycle. It is not
-                                       possible to overwrite borrowed data due to concurrent access.
-                                       Use a custom datasource with a mutex. */
-    } storageType; ///< Shall the data be deleted together with the variant
-    UA_Int32  arrayLength;  ///< the number of elements in the data-pointer
-    void     *data; ///< points to the scalar or array data
-    UA_Int32  arrayDimensionsSize; ///< the number of dimensions the data-array has
-    UA_Int32 *arrayDimensions; ///< the length of each dimension of the data-array
-} UA_Variant;
+static UA_INLINE UA_NodeId UA_NODEID_BYTESTRING(UA_UInt16 nsIndex, char *chars) {
+    UA_NodeId id; id.namespaceIndex = nsIndex; id.identifierType = UA_NODEIDTYPE_BYTESTRING;
+    id.identifier.byteString = UA_BYTESTRING(chars); return id; }
 
-/** @brief A data value with an associated status code and timestamps. */
+static UA_INLINE UA_NodeId UA_NODEID_BYTESTRING_ALLOC(UA_UInt16 nsIndex, const char *chars) {
+    UA_NodeId id; id.namespaceIndex = nsIndex; id.identifierType = UA_NODEIDTYPE_BYTESTRING;
+    id.identifier.byteString = UA_BYTESTRING_ALLOC(chars); return id; }
+
+/**********************************************************************************************/
+/* ExpandedNodeId: A NodeId that allows the namespace URI to be specified instead of an index */
+/**********************************************************************************************/
 typedef struct {
-    UA_Boolean    hasValue : 1;
-    UA_Boolean    hasStatus : 1;
-    UA_Boolean    hasSourceTimestamp : 1;
-    UA_Boolean    hasServerTimestamp : 1;
-    UA_Boolean    hasSourcePicoseconds : 1;
-    UA_Boolean    hasServerPicoseconds : 1;
-    UA_Variant    value;
-    UA_StatusCode status;
-    UA_DateTime   sourceTimestamp;
-    UA_Int16      sourcePicoseconds;
-    UA_DateTime   serverTimestamp;
-    UA_Int16      serverPicoseconds;
-} UA_DataValue;
+    UA_NodeId nodeId;
+    UA_String namespaceUri;
+    UA_UInt32 serverIndex;
+} UA_ExpandedNodeId;
 
-/** @brief A structure that contains detailed error and diagnostic information associated with a StatusCode. */
-typedef struct UA_DiagnosticInfo {
-    UA_Boolean    hasSymbolicId : 1;
-    UA_Boolean    hasNamespaceUri : 1;
-    UA_Boolean    hasLocalizedText : 1;
-    UA_Boolean    hasLocale : 1;
-    UA_Boolean    hasAdditionalInfo : 1;
-    UA_Boolean    hasInnerStatusCode : 1;
-    UA_Boolean    hasInnerDiagnosticInfo : 1;
-    UA_Int32      symbolicId;
-    UA_Int32      namespaceUri;
-    UA_Int32      localizedText;
-    UA_Int32      locale;
-    UA_String     additionalInfo;
-    UA_StatusCode innerStatusCode;
-    struct UA_DiagnosticInfo *innerDiagnosticInfo;
-} UA_DiagnosticInfo;
+static UA_INLINE UA_ExpandedNodeId UA_EXPANDEDNODEID_NUMERIC(UA_UInt16 nsIndex, UA_Int32 identifier) {
+    UA_ExpandedNodeId id; id.nodeId = UA_NODEID_NUMERIC(nsIndex, identifier);
+    id.serverIndex = 0; id.namespaceUri = UA_STRING_NULL; return id; }
 
-#define UA_TYPE_HANDLING_FUNCTIONS(TYPE)                             \
-    TYPE UA_EXPORT * TYPE##_new(void);                               \
-    void UA_EXPORT TYPE##_init(TYPE * p);                            \
-    void UA_EXPORT TYPE##_delete(TYPE * p);                          \
-    void UA_EXPORT TYPE##_deleteMembers(TYPE * p);                   \
-    UA_StatusCode UA_EXPORT TYPE##_copy(const TYPE *src, TYPE *dst);
-
-/* Functions for all types */
-UA_TYPE_HANDLING_FUNCTIONS(UA_Boolean)
-UA_TYPE_HANDLING_FUNCTIONS(UA_SByte)
-UA_TYPE_HANDLING_FUNCTIONS(UA_Byte)
-UA_TYPE_HANDLING_FUNCTIONS(UA_Int16)
-UA_TYPE_HANDLING_FUNCTIONS(UA_UInt16)
-UA_TYPE_HANDLING_FUNCTIONS(UA_Int32)
-UA_TYPE_HANDLING_FUNCTIONS(UA_UInt32)
-UA_TYPE_HANDLING_FUNCTIONS(UA_Int64)
-UA_TYPE_HANDLING_FUNCTIONS(UA_UInt64)
-UA_TYPE_HANDLING_FUNCTIONS(UA_Float)
-UA_TYPE_HANDLING_FUNCTIONS(UA_Double)
-UA_TYPE_HANDLING_FUNCTIONS(UA_String)
-#define UA_DateTime_new UA_Int64_new
-#define UA_DateTime_init UA_Int64_init
-#define UA_DateTime_delete UA_Int64_delete
-#define UA_DateTime_deleteMembers UA_Int64_deleteMembers
-#define UA_DateTime_copy UA_Int64_copy
-UA_TYPE_HANDLING_FUNCTIONS(UA_Guid)
-#define UA_ByteString_new UA_String_new
-#define UA_ByteString_init UA_String_init
-#define UA_ByteString_delete UA_String_delete
-#define UA_ByteString_deleteMembers UA_String_deleteMembers
-#define UA_ByteString_copy UA_String_copy
-#define UA_XmlElement_new UA_String_new
-#define UA_XmlElement_init UA_String_init
-#define UA_XmlElement_delete UA_String_delete
-#define UA_XmlElement_deleteMembers UA_String_deleteMembers
-#define UA_XmlElement_copy UA_String_copy
-UA_TYPE_HANDLING_FUNCTIONS(UA_NodeId)
-UA_TYPE_HANDLING_FUNCTIONS(UA_ExpandedNodeId)
-#define UA_StatusCode_new() UA_Int32_new()
-#define UA_StatusCode_init(p) UA_Int32_init((UA_Int32*)p)
-#define UA_StatusCode_delete(p) UA_Int32_delete((UA_Int32*)p)
-#define UA_StatusCode_deleteMembers(p) UA_Int32_deleteMembers((UA_Int32*)p)
-#define UA_StatusCode_copy(p, q) UA_Int32_copy((UA_Int32*)p, (UA_Int32*)q)
-UA_TYPE_HANDLING_FUNCTIONS(UA_QualifiedName)
-UA_TYPE_HANDLING_FUNCTIONS(UA_LocalizedText)
-UA_TYPE_HANDLING_FUNCTIONS(UA_ExtensionObject)
-UA_TYPE_HANDLING_FUNCTIONS(UA_DataValue)
-UA_TYPE_HANDLING_FUNCTIONS(UA_Variant)
-UA_TYPE_HANDLING_FUNCTIONS(UA_DiagnosticInfo)
-
-/**********************************************/
-/* Custom functions for the builtin datatypes */
-/**********************************************/
-
-/* String */
-/** Copy a (zero terminated) char-array into a UA_String. Memory for the string data is
-    allocated. If the memory cannot be allocated, a null-string is returned. */
-UA_String UA_EXPORT UA_String_fromChars(char const src[]);
-
-#define UA_STRING_NULL (UA_String) {-1, (UA_Byte*)0 }
-#define UA_STRING(CHARS) (UA_String) {strlen(CHARS), (UA_Byte*)CHARS }
-#define UA_STRING_ALLOC(CHARS) UA_String_fromChars(CHARS)
+static UA_INLINE UA_ExpandedNodeId UA_EXPANDEDNODEID_STRING(UA_UInt16 nsIndex, char *chars) {
+    UA_ExpandedNodeId id; id.nodeId = UA_NODEID_STRING(nsIndex, chars);
+    id.serverIndex = 0; id.namespaceUri = UA_STRING_NULL; return id; }
 
-/** Compares two strings */
-UA_Boolean UA_EXPORT UA_String_equal(const UA_String *string1, const UA_String *string2);
+static UA_INLINE UA_ExpandedNodeId UA_EXPANDEDNODEID_STRING_ALLOC(UA_UInt16 nsIndex, const char *chars) {
+    UA_ExpandedNodeId id; id.nodeId = UA_NODEID_STRING_ALLOC(nsIndex, chars);
+    id.serverIndex = 0; id.namespaceUri = UA_STRING_NULL; return id; }
 
-/** Printf a char-array into a UA_String. Memory for the string data is allocated. */
-UA_StatusCode UA_EXPORT UA_String_copyprintf(char const fmt[], UA_String *dst, ...);
+static UA_INLINE UA_ExpandedNodeId UA_EXPANDEDNODEID_STRING_GUID(UA_UInt16 nsIndex, UA_Guid guid) {
+    UA_ExpandedNodeId id; id.nodeId = UA_NODEID_GUID(nsIndex, guid);
+    id.serverIndex = 0; id.namespaceUri = UA_STRING_NULL; return id; }
 
-/* DateTime */
-/** Returns the current time */
-UA_DateTime UA_EXPORT UA_DateTime_now(void);
+static UA_INLINE UA_ExpandedNodeId UA_EXPANDEDNODEID_BYTESTRING(UA_UInt16 nsIndex, char *chars) {
+    UA_ExpandedNodeId id; id.nodeId = UA_NODEID_BYTESTRING(nsIndex, chars);
+    id.serverIndex = 0; id.namespaceUri = UA_STRING_NULL; return id; }
 
-typedef struct UA_DateTimeStruct {
-    UA_Int16 nanoSec;
-    UA_Int16 microSec;
-    UA_Int16 milliSec;
-    UA_Int16 sec;
-    UA_Int16 min;
-    UA_Int16 hour;
-    UA_Int16 day;
-    UA_Int16 month;
-    UA_Int16 year;
-} UA_DateTimeStruct;
+static UA_INLINE UA_ExpandedNodeId UA_EXPANDEDNODEID_BYTESTRING_ALLOC(UA_UInt16 nsIndex, const char *chars) {
+    UA_ExpandedNodeId id; id.nodeId = UA_NODEID_BYTESTRING_ALLOC(nsIndex, chars);
+    id.serverIndex = 0; id.namespaceUri = UA_STRING_NULL; return id; }
 
-UA_DateTimeStruct UA_EXPORT UA_DateTime_toStruct(UA_DateTime time);
-UA_StatusCode UA_EXPORT UA_DateTime_toString(UA_DateTime time, UA_String *timeString);
+/**************************************************/
+/* QualifiedName: A name qualified by a namespace */
+/**************************************************/
+typedef struct {
+    UA_UInt16 namespaceIndex;
+    UA_String name;
+} UA_QualifiedName;
 
-/* Guid */
-/** Compares two guids */
-UA_Boolean UA_EXPORT UA_Guid_equal(const UA_Guid *g1, const UA_Guid *g2);
-    
-/** Returns a randomly generated guid. Do not use for security-critical entropy! */
-UA_Guid UA_EXPORT UA_Guid_random(UA_UInt32 *seed);
+static UA_INLINE UA_QualifiedName UA_QUALIFIEDNAME(UA_UInt16 nsIndex, char *chars) {
+    UA_QualifiedName qn; qn.namespaceIndex = nsIndex; qn.name = UA_STRING(chars); return qn; }
 
-/* ByteString */
-#define UA_BYTESTRING_NULL (UA_ByteString) {-1, (UA_Byte*)0 }
-#define UA_ByteString_equal(string1, string2) \
-    UA_String_equal((const UA_String*) string1, (const UA_String*)string2)
+static UA_INLINE UA_QualifiedName UA_QUALIFIEDNAME_ALLOC(UA_UInt16 nsIndex, const char *chars) {
+    UA_QualifiedName qn; qn.namespaceIndex = nsIndex; qn.name = UA_STRING_ALLOC(chars); return qn; }
 
-/** Allocates memory of size length for the bytestring. The content is not set to zero. */
-UA_StatusCode UA_EXPORT UA_ByteString_newMembers(UA_ByteString *p, UA_Int32 length);
+/*************************************************************************/
+/* LocalizedText: Human readable text with an optional locale identifier */
+/*************************************************************************/
+typedef struct {
+    UA_String locale;
+    UA_String text;
+} UA_LocalizedText;
 
-/* NodeId */
-/** Compares two nodeids */
-UA_Boolean UA_EXPORT UA_NodeId_equal(const UA_NodeId *n1, const UA_NodeId *n2);
+static UA_INLINE UA_LocalizedText UA_LOCALIZEDTEXT(char *locale, char *text) {
+    UA_LocalizedText lt; lt.locale = UA_STRING(locale);
+    lt.text = UA_STRING(text); return lt; }
 
-/** Is the nodeid a null-nodeid? */
-UA_Boolean UA_EXPORT UA_NodeId_isNull(const UA_NodeId *p);
-
-UA_NodeId UA_EXPORT UA_NodeId_fromInteger(UA_UInt16 nsIndex, UA_Int32 identifier);
-UA_NodeId UA_EXPORT UA_NodeId_fromCharString(UA_UInt16 nsIndex, char identifier[]);
-UA_NodeId UA_EXPORT UA_NodeId_fromCharStringCopy(UA_UInt16 nsIndex, char const identifier[]);
-UA_NodeId UA_EXPORT UA_NodeId_fromString(UA_UInt16 nsIndex, UA_String identifier);
-UA_NodeId UA_EXPORT UA_NodeId_fromStringCopy(UA_UInt16 nsIndex, UA_String identifier);
-UA_NodeId UA_EXPORT UA_NodeId_fromGuid(UA_UInt16 nsIndex, UA_Guid identifier);
-UA_NodeId UA_EXPORT UA_NodeId_fromCharByteString(UA_UInt16 nsIndex, char identifier[]);
-UA_NodeId UA_EXPORT UA_NodeId_fromCharByteStringCopy(UA_UInt16 nsIndex, char const identifier[]);
-UA_NodeId UA_EXPORT UA_NodeId_fromByteString(UA_UInt16 nsIndex, UA_ByteString identifier);
-UA_NodeId UA_EXPORT UA_NodeId_fromByteStringCopy(UA_UInt16 nsIndex, UA_ByteString identifier);
-
-#define UA_NODEID_NUMERIC(NSID, NUMERICID) UA_NodeId_fromInteger(NSID, NUMERICID)
-#define UA_NODEID_STRING(NSID, CHARS) UA_NodeId_fromCharString(NSID, CHARS)
-#define UA_NODEID_STRING_ALLOC(NSID, CHARS) UA_NodeId_fromCharStringCopy(NSID, CHARS)
-#define UA_NODEID_GUID(NSID, GUID) UA_NodeId_fromGuid(NSID, GUID)
-#define UA_NODEID_BYTESTRING(NSID, CHARS) UA_NodeId_fromCharByteString(NSID, CHARS)
-#define UA_NODEID_BYTESTRING_ALLOC(NSID, CHARS) UA_NodeId_fromCharByteStringCopy(NSID, CHARS)
-#define UA_NODEID_NULL UA_NODEID_NUMERIC(0,0)
-
-/* ExpandedNodeId */
-UA_Boolean UA_EXPORT UA_ExpandedNodeId_isNull(const UA_ExpandedNodeId *p);
-
-#define UA_EXPANDEDNODEID_NUMERIC(NSID, NUMERICID) (UA_ExpandedNodeId) {            \
-        .nodeId = {.namespaceIndex = NSID, .identifierType = UA_NODEIDTYPE_NUMERIC, \
-                   .identifier.numeric = NUMERICID },                                   \
-        .serverIndex = 0, .namespaceUri = {.data = (UA_Byte*)0, .length = -1} }
-    
-/* QualifiedName */
-#define UA_QUALIFIEDNAME(NSID, CHARS) (const UA_QualifiedName) {    \
-        .namespaceIndex = NSID, .name = UA_STRING(CHARS) }
-#define UA_QUALIFIEDNAME_ALLOC(NSID, CHARS) (UA_QualifiedName) {    \
-        .namespaceIndex = NSID, .name = UA_STRING_ALLOC(CHARS) }
+static UA_INLINE UA_LocalizedText UA_LOCALIZEDTEXT_ALLOC(const char *locale, const char *text) {
+    UA_LocalizedText lt; lt.locale = UA_STRING_ALLOC(locale);
+    lt.text = UA_STRING_ALLOC(text); return lt; }
+
+/* Forward Declaration of UA_DataType */
+struct UA_DataType;
+typedef struct UA_DataType UA_DataType; 
 
-/* LocalizedText */
-#define UA_LOCALIZEDTEXT(LOCALE, TEXT) (const UA_LocalizedText) {       \
-        .locale = UA_STRING(LOCALE), .text = UA_STRING(TEXT) }
-#define UA_LOCALIZEDTEXT_ALLOC(LOCALE, TEXT) (UA_LocalizedText) {       \
-        .locale = UA_STRING_ALLOC(LOCALE), .text = UA_STRING_ALLOC(TEXT) }
+/********************************************************************************/
+/* ExtensionObject: A structure that contains an application specific data type */
+/* that may not be recognized by the receiver                                   */
+/********************************************************************************/
+typedef struct {
+    enum {
+        UA_EXTENSIONOBJECT_ENCODED_NOBODY     = 0,
+        UA_EXTENSIONOBJECT_ENCODED_BYTESTRING = 1,
+        UA_EXTENSIONOBJECT_ENCODED_XML        = 2,
+        UA_EXTENSIONOBJECT_DECODED            = 3, ///< There is a pointer to the decoded data
+        UA_EXTENSIONOBJECT_DECODED_NODELETE   = 4  ///< Don't delete the decoded data at the lifecycle end
+    } 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: Stores (arrays of) any data type */
+/*********************************************/
+ /* Variant semantics:
+    - arrayLength == 0 && data == NULL: no existing data
+    - arrayLength == 0 && data == 0x01: array of length 0
+    - arrayLength == 0 && data > 0x01: scalar value
+    - arrayLength > 0: array of the given length
+ */
+typedef struct {
+    const UA_DataType *type; // The data type description
+    enum {
+        UA_VARIANT_DATA,          /* The data has the same lifecycle as the variant */
+        UA_VARIANT_DATA_NODELETE, /* The data is "borrowed" by the variant and shall not be
+                                     deleted at the end of the variant's lifecycle. */
+    } 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_UInt32 *arrayDimensions; // The length of each dimension of the data-array
+} UA_Variant;
 
 /**
  * Returns true if the variant contains a scalar value. Note that empty variants contain an array of
@@ -379,7 +348,8 @@ UA_Boolean UA_EXPORT UA_ExpandedNodeId_isNull(const UA_ExpandedNodeId *p);
  * @param v The variant
  * @return Does the variant contain a scalar value.
  */
-UA_Boolean UA_EXPORT UA_Variant_isScalar(const UA_Variant *v);
+static UA_INLINE UA_Boolean UA_Variant_isScalar(const UA_Variant *v) {
+    return (v->arrayLength == 0 && v->data > UA_EMPTY_ARRAY_SENTINEL); }
     
 /**
  * Set the variant to a scalar value that already resides in memory. The value takes on the
@@ -388,9 +358,8 @@ UA_Boolean UA_EXPORT UA_Variant_isScalar(const UA_Variant *v);
  * @param v The variant
  * @param p A pointer to the value data
  * @param type The datatype of the value in question
- * @return Indicates whether the operation succeeded or returns an error code
  */
-UA_StatusCode UA_EXPORT UA_Variant_setScalar(UA_Variant *v, void *p, const UA_DataType *type);
+void UA_EXPORT UA_Variant_setScalar(UA_Variant *v, void * UA_RESTRICT p, const UA_DataType *type);
 
 /**
  * Set the variant to a scalar value that is copied from an existing variable.
@@ -408,32 +377,31 @@ UA_StatusCode UA_EXPORT UA_Variant_setScalarCopy(UA_Variant *v, const void *p, c
  *
  * @param v The variant
  * @param array A pointer to the array data
- * @param elements The size of the array
+ * @param arraySize The size of the array
  * @param type The datatype of the array
- * @return Indicates whether the operation succeeded or returns an error code
  */
-UA_StatusCode UA_EXPORT UA_Variant_setArray(UA_Variant *v, void *array, UA_Int32 elements,
-                                            const UA_DataType *type);
+void UA_EXPORT
+UA_Variant_setArray(UA_Variant *v, void * UA_RESTRICT array, size_t arraySize, const UA_DataType *type);
 
 /**
  * Set the variant to an array that is copied from an existing array.
  *
  * @param v The variant
  * @param array A pointer to the array data
- * @param elements The size of the array
+ * @param arraySize The size of the array
  * @param type The datatype of the array
  * @return Indicates whether the operation succeeded or returns an error code
  */
-UA_StatusCode UA_EXPORT UA_Variant_setArrayCopy(UA_Variant *v, const void *array, UA_Int32 elements,
-                                                const UA_DataType *type);
-
-/** @brief NumericRanges are used select a subset in 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). */
+UA_StatusCode UA_EXPORT
+UA_Variant_setArrayCopy(UA_Variant *v, const void *array, size_t arraySize, const UA_DataType *type);
+
+/* 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_Int32 dimensionsSize;
+    size_t dimensionsSize;
     struct UA_NumericRangeDimension {
         UA_UInt32 min;
         UA_UInt32 max;
@@ -449,12 +417,13 @@ typedef struct {
  * @param range The range of the copied data
  * @return Returns UA_STATUSCODE_GOOD or an error code
  */
-UA_StatusCode UA_EXPORT UA_Variant_copyRange(const UA_Variant *src, UA_Variant *dst, UA_NumericRange range);
+UA_StatusCode UA_EXPORT
+UA_Variant_copyRange(const UA_Variant *src, UA_Variant *dst, const UA_NumericRange range);
 
 /**
  * Insert a range of data into an existing variant. The data array can't be reused afterwards if it
- * contains types without a fixed size (e.g. strings) since they take on the lifetime of the
- * variant.
+ * contains types without a fixed size (e.g. strings) since the members are moved into the variant
+ * and take on its lifecycle.
  *
  * @param v The variant
  * @param dataArray The data array. The type must match the variant
@@ -462,8 +431,8 @@ UA_StatusCode UA_EXPORT UA_Variant_copyRange(const UA_Variant *src, UA_Variant *
  * @param range The range of where the new data is inserted
  * @return Returns UA_STATUSCODE_GOOD or an error code
  */
-UA_StatusCode UA_EXPORT UA_Variant_setRange(UA_Variant *v, void *dataArray, UA_Int32 dataArraySize,
-                                            const UA_NumericRange range);
+UA_StatusCode UA_EXPORT
+UA_Variant_setRange(UA_Variant *v, void * UA_RESTRICT array, size_t arraySize, const UA_NumericRange range);
 
 /**
  * Deep-copy a range of data into an existing variant.
@@ -474,62 +443,98 @@ UA_StatusCode UA_EXPORT UA_Variant_setRange(UA_Variant *v, void *dataArray, UA_I
  * @param range The range of where the new data is inserted
  * @return Returns UA_STATUSCODE_GOOD or an error code
  */
-UA_StatusCode UA_EXPORT UA_Variant_setRangeCopy(UA_Variant *v, const void *dataArray, UA_Int32 dataArraySize,
-                                                const UA_NumericRange range);
+UA_StatusCode UA_EXPORT
+UA_Variant_setRangeCopy(UA_Variant *v, const void *array, size_t arraySize, const UA_NumericRange range);
 
-/****************************/
-/* Structured Type Handling */
-/****************************/
+/**************************************************************************/
+/* DataValue: A data value with an associated status code and timestamps. */
+/**************************************************************************/
+typedef struct {
+    UA_Boolean    hasValue             : 1;
+    UA_Boolean    hasStatus            : 1;
+    UA_Boolean    hasSourceTimestamp   : 1;
+    UA_Boolean    hasServerTimestamp   : 1;
+    UA_Boolean    hasSourcePicoseconds : 1;
+    UA_Boolean    hasServerPicoseconds : 1;
+    UA_Variant    value;
+    UA_StatusCode status;
+    UA_DateTime   sourceTimestamp;
+    UA_Int16      sourcePicoseconds;
+    UA_DateTime   serverTimestamp;
+    UA_Int16      serverPicoseconds;
+} UA_DataValue;
 
-#define UA_MAX_TYPE_MEMBERS 13 // Maximum number of members per complex type
+/***************************************************************************/
+/* DiagnosticInfo: A structure that contains detailed error and diagnostic */
+/* information associated with a StatusCode.                               */
+/***************************************************************************/
+typedef struct UA_DiagnosticInfo {
+    UA_Boolean    hasSymbolicId          : 1;
+    UA_Boolean    hasNamespaceUri        : 1;
+    UA_Boolean    hasLocalizedText       : 1;
+    UA_Boolean    hasLocale              : 1;
+    UA_Boolean    hasAdditionalInfo      : 1;
+    UA_Boolean    hasInnerStatusCode     : 1;
+    UA_Boolean    hasInnerDiagnosticInfo : 1;
+    UA_Int32      symbolicId;
+    UA_Int32      namespaceUri;
+    UA_Int32      localizedText;
+    UA_Int32      locale;
+    UA_String     additionalInfo;
+    UA_StatusCode innerStatusCode;
+    struct UA_DiagnosticInfo *innerDiagnosticInfo;
+} UA_DiagnosticInfo;
 
-#ifndef _WIN32
-# define UA_BITFIELD(SIZE) : SIZE
-#else
-# define UA_BITFIELD(SIZE)
-#endif
+/*************************/
+/* Generic Type Handling */
+/*************************/
 
-#define UA_IS_BUILTIN(ID) (ID <= UA_TYPES_DIAGNOSTICINFO)
+#define UA_MAX_TYPE_MEMBERS 13 // Maximum number of members per structured type
 
 typedef struct {
-    UA_UInt16 memberTypeIndex UA_BITFIELD(9); ///< Index of the member in the datatypetable
-    UA_Boolean namespaceZero UA_BITFIELD(1); /**< The type of the member is defined in namespace
-                                                  zero. In this implementation, types from custom
-                                                  namespace may contain members from the same
-                                                  namespace or ns0 only.*/
-    UA_Byte padding UA_BITFIELD(5); /**< How much padding is there before this member element? For
-                                         arrays this is split into 2 bytes padding before the
-                                         length index (max 4 bytes) and 3 bytes padding before the
-                                         pointer (max 8 bytes) */
-    UA_Boolean isArray UA_BITFIELD(1); ///< The member is an array of the given type
+#ifdef ENABLE_TYPEINTROSPECTION
+    const char *memberName;
+#endif
+    UA_UInt16   memberTypeIndex;   ///< Index of the member in the datatypetable
+    UA_Byte     padding;           /**< How much padding is there before this member element? For arrays this is
+                                        split into 2 bytes padding before the length index (max 4 bytes) and 3
+                                        bytes padding before the pointer (max 8 bytes) */
+    UA_Boolean  namespaceZero : 1; /**< The type of the member is defined in namespace zero. In this
+                                        implementation, types from custom namespace may contain
+                                        members from the same namespace or ns0 only.*/
+    UA_Boolean  isArray       : 1; ///< The member is an array of the given type
 } UA_DataTypeMember;
     
 struct UA_DataType {
-    UA_NodeId typeId; ///< The nodeid of the type
-    ptrdiff_t memSize UA_BITFIELD(16); ///< Size of the struct in memory
-    UA_UInt16 typeIndex UA_BITFIELD(13); ///< Index of the type in the datatypetable
-    UA_Boolean namespaceZero UA_BITFIELD(1); ///< The type is defined in namespace zero
-    UA_Boolean fixedSize UA_BITFIELD(1); ///< The type (and its members) contains no pointers
-    UA_Boolean zeroCopyable UA_BITFIELD(1); ///< Can the type be copied directly off the stream?
-    UA_Byte membersSize; ///< How many members does the type have?
+#ifdef ENABLE_TYPEINTROSPECTION
+    const char *typeName;
+#endif
+    UA_NodeId   typeId;           ///< The nodeid of the type
+    UA_UInt16   memSize;          ///< Size of the struct in memory
+    UA_UInt16   typeIndex;        ///< Index of the type in the datatypetable
+    UA_Byte     membersSize;      ///< How many members does the type have?
+    UA_Boolean  builtin      : 1; ///< The type is "builtin" and has dedicated de- and encoding functions
+    UA_Boolean  fixedSize    : 1; ///< The type (and its members) contains no pointers
+    UA_Boolean  zeroCopyable : 1; ///< The type can be copied directly off the stream (given that the endianness matches)
     UA_DataTypeMember members[UA_MAX_TYPE_MEMBERS];
 };
 
 /**
  * Allocates and initializes a variable of type dataType
  *
- * @param dataType The datatype description
+ * @param type The datatype description
  * @return Returns the memory location of the variable or (void*)0 if no memory is available
  */
-void UA_EXPORT * UA_new(const UA_DataType *dataType);
+void UA_EXPORT * UA_new(const UA_DataType *type) UA_FUNC_ATTR_MALLOC;
 
 /**
  * Initializes a variable to default values
  *
  * @param p The memory location of the variable
- * @param dataType The datatype description
+ * @param type The datatype description
  */
-void UA_EXPORT UA_init(void *p, const UA_DataType *dataType);
+static UA_INLINE void UA_init(void *p, const UA_DataType *type) {
+    memset(p, 0, type->memSize); }
 
 /**
  * Copies the content of two variables. If copying fails (e.g. because no memory was available for
@@ -537,30 +542,30 @@ void UA_EXPORT UA_init(void *p, const UA_DataType *dataType);
  *
  * @param src The memory location of the source variable
  * @param dst The memory location of the destination variable
- * @param dataType The datatype description
+ * @param type The datatype description
  * @return Indicates whether the operation succeeded or returns an error code
  */
-UA_StatusCode UA_EXPORT UA_copy(const void *src, void *dst, const UA_DataType *dataType);
+UA_StatusCode UA_EXPORT
+UA_copy(const void *src, void *dst, const UA_DataType *type) UA_FUNC_ATTR_WARN_UNUSED_RESULT;
 
 /**
- * Deletes the dynamically assigned content of a variable (e.g. a member-array). Afterwards, the
- * variable can be safely deleted without causing memory leaks. But the variable is not initialized
- * and may contain old data that is not memory-relevant.
+ * Deletes the dynamically assigned content of a variable (e.g. a member-array).
+ * Afterwards, the variable can be safely deleted without causing memory leaks.
+ * But the variable is not initialized and may contain old data that is not
+ * memory-relevant.
  *
  * @param p The memory location of the variable
- * @param dataType The datatype description of the variable
+ * @param type The datatype description of the variable
  */
-void UA_EXPORT UA_deleteMembers(void *p, const UA_DataType *dataType);
-
-void UA_EXPORT UA_deleteMembersUntil(void *p, const UA_DataType *dataType, UA_Int32 lastMember);
+void UA_EXPORT UA_deleteMembers(void *p, const UA_DataType *type);
 
 /**
  * Deletes (frees) a variable and all of its content.
  *
  * @param p The memory location of the variable
- * @param dataType The datatype description of the variable
+ * @param type The datatype description of the variable
  */
-void UA_EXPORT UA_delete(void *p, const UA_DataType *dataType);
+void UA_EXPORT UA_delete(void *p, const UA_DataType *type);
 
 /********************/
 /* Array operations */
@@ -571,31 +576,37 @@ void UA_EXPORT UA_delete(void *p, const UA_DataType *dataType);
 /**
  * Allocates and initializes an array of variables of a specific type
  *
- * @param dataType The datatype description
- * @param elements The number of elements in the array
+ * @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(const UA_DataType *dataType, UA_Int32 elements);
+void UA_EXPORT * UA_Array_new(size_t size, const UA_DataType *type) UA_FUNC_ATTR_MALLOC;
 
 /**
  * Allocates and copies an array. dst is set to (void*)0 if not enough memory is available.
  *
  * @param src The memory location of the source array
- * @param dst The memory location where the pointer to the destination array is written
- * @param dataType The datatype of the array members
- * @param elements The size of the array
- * @return Indicates whether the operation succeeded or returns an error code
+ * @param src_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 whether copying succeeded
  */
-UA_StatusCode UA_EXPORT UA_Array_copy(const void *src, void **dst, const UA_DataType *dataType, UA_Int32 elements);
+UA_StatusCode UA_EXPORT
+UA_Array_copy(const void *src, size_t src_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 dataType The datatype of the array members
- * @param elements The size 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, const UA_DataType *dataType, UA_Int32 elements);
+void UA_EXPORT UA_Array_delete(void *p, size_t size, const UA_DataType *type);
+
+/**********************/
+/* Node Attribute Ids */
+/**********************/
 
 /* These are not generated from XML. Server *and* client need them. */
 typedef enum {
@@ -623,6 +634,25 @@ typedef enum {
     UA_ATTRIBUTEID_USEREXECUTABLE          = 22
 } UA_AttributeId;
 
+typedef enum {
+    UA_ACCESSLEVELMASK_READ = 0x01,
+    UA_ACCESSLEVELMASK_WRITE = 0x02,
+    UA_ACCESSLEVELMASK_HISTORYREAD = 0x4,
+    UA_ACCESSLEVELMASK_HISTORYWRITE = 0x08,
+    UA_ACCESSLEVELMASK_SEMANTICCHANGE = 0x10
+} UA_AccessLevelMask;
+
+/***************************/
+/* Random Number Generator */
+/***************************/
+
+/**
+ * If UA_MULTITHREADING is enabled, then the seed is stored in thread local storage. The seed is
+ * initialized for every thread in the server/client.
+ */
+UA_EXPORT void UA_random_seed(UA_UInt64 seed);
+UA_EXPORT UA_UInt32 UA_random(void);
+
 #ifdef __cplusplus
 } // extern "C"
 #endif

+ 0 - 51
ports/WAGO-750-860.patch

@@ -1,51 +0,0 @@
-From 97515341454f26fcd14e50d5980f5f0361307814 Mon Sep 17 00:00:00 2001
-From: FlorianPalm <f.palm@plt.rwth-aachen.de>
-Date: Mon, 12 Jan 2015 18:45:01 +0100
-Subject: [PATCH] fixes to the patch
-
----
- CMakeLists.txt | 19 ++++++++-----------
- src/ua_util.h  |  2 +-
- 2 files changed, 9 insertions(+), 12 deletions(-)
-
-diff --git a/CMakeLists.txt b/CMakeLists.txt
-index c785182..9e66da0 100644
---- a/CMakeLists.txt
-+++ b/CMakeLists.txt
-@@ -47,19 +47,16 @@ set(lib_sources src/ua_types.c
- 
- # compiler flags
- if(CMAKE_COMPILER_IS_GNUCC OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang")
--add_definitions(-std=c99 -pipe -Wall -Wextra -Werror -Wformat
--                -Wno-unused-parameter -Wno-unused-function -Wno-unused-label -Wpointer-arith -Wreturn-type -Wsign-compare -Wmultichar
--                -Wshadow -Wcast-qual -Wmissing-prototypes -Wstrict-prototypes # -Wconversion
--                -Winit-self -Wuninitialized -Wno-deprecated -Wformat-security -ffunction-sections -fdata-sections)
-+add_definitions(-std=c99 -fomit-frame-pointer -pipe -msoft-float -fno-builtin -DEMBED -fno-strict-aliasing)
-     if(NOT "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang")
-         add_definitions(-Wformat-nonliteral)
--        set (CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -Wl,--gc-sections")
--        set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--gc-sections")
-+        set (CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -Wl,-elf2flt -msoft-float")
-+        set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS}  -Wl,-elf2flt -msoft-float")
-     else()
-         add_definitions(-Wno-gnu-statement-expression)
-     endif()
- 	if(NOT WIN32)
--	    add_definitions(-fstack-protector -fPIC -fvisibility=hidden)
-+	   # add_definitions(-fPIC)
- 	endif()
- endif()
- 
-@@ -90,11 +90,7 @@ else()
- endif()
- 
- add_library(open62541-objects OBJECT ${lib_sources})
--add_library(open62541 SHARED $<TARGET_OBJECTS:open62541-objects>)
--add_library(open62541-static STATIC $<TARGET_OBJECTS:open62541-objects>)
--SET_TARGET_PROPERTIES(open62541-static PROPERTIES OUTPUT_NAME open62541 CLEAN_DIRECT_OUTPUT 1) # static version that exports all symbols
--target_compile_definitions(open62541-objects PRIVATE UA_DYNAMIC_LINKING)
--target_compile_definitions(open62541-static PRIVATE UA_DYNAMIC_LINKING)
-+add_library(open62541 $<TARGET_OBJECTS:open62541-objects>)
- 
- ## logging
- set(UA_LOGLEVEL 300 CACHE STRING "Level at which logs shall be reported")

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 252 - 851
src/client/ua_client.c


+ 261 - 0
src/client/ua_client_highlevel.c

@@ -0,0 +1,261 @@
+#include "ua_client.h"
+#include "ua_nodeids.h"
+#include "ua_client_highlevel.h"
+#include "ua_types_encoding_binary.h"
+#include "ua_util.h"
+
+UA_StatusCode
+UA_Client_NamespaceGetIndex(UA_Client *client, UA_String *namespaceUri, UA_UInt16 *namespaceIndex) {
+	UA_ReadRequest request;
+	UA_ReadRequest_init(&request);
+    UA_ReadValueId id;
+	id.attributeId = UA_ATTRIBUTEID_VALUE;
+	id.nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_NAMESPACEARRAY);
+	request.nodesToRead = &id;
+	request.nodesToReadSize = 1;
+
+	UA_ReadResponse response = UA_Client_Service_read(client, request);
+
+	UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    if(response.responseHeader.serviceResult != UA_STATUSCODE_GOOD)
+        retval = response.responseHeader.serviceResult;
+    else if(response.resultsSize != 1 || !response.results[0].hasValue)
+        retval = UA_STATUSCODE_BADNODEATTRIBUTESINVALID;
+    else if(response.results[0].value.type != &UA_TYPES[UA_TYPES_STRING])
+        retval = UA_STATUSCODE_BADTYPEMISMATCH;
+
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_ReadResponse_deleteMembers(&response);
+        return retval;
+    }
+
+    retval = UA_STATUSCODE_BADNOTFOUND;
+    UA_String *ns = response.results[0].value.data;
+    for(size_t i = 0; i < response.results[0].value.arrayLength; i++){
+        if(UA_String_equal(namespaceUri, &ns[i])) {
+            *namespaceIndex = i;
+            retval = UA_STATUSCODE_GOOD;
+            break;
+        }
+    }
+
+    UA_ReadResponse_deleteMembers(&response);
+	return retval;
+}
+
+/*******************/
+/* Node Management */
+/*******************/
+
+UA_StatusCode UA_EXPORT
+UA_Client_addReference(UA_Client *client, const UA_NodeId sourceNodeId, const UA_NodeId referenceTypeId,
+                       UA_Boolean isForward, const UA_String targetServerUri,
+                       const UA_ExpandedNodeId targetNodeId, UA_NodeClass targetNodeClass) {
+    UA_AddReferencesItem item;
+    UA_AddReferencesItem_init(&item);
+    item.sourceNodeId = sourceNodeId;
+    item.referenceTypeId = referenceTypeId;
+    item.isForward = isForward;
+    item.targetServerUri = targetServerUri;
+    item.targetNodeId = targetNodeId;
+    item.targetNodeClass = targetNodeClass;
+    UA_AddReferencesRequest request;
+    UA_AddReferencesRequest_init(&request);
+    request.referencesToAdd = &item;
+    request.referencesToAddSize = 1;
+    UA_AddReferencesResponse response = UA_Client_Service_addReferences(client, request);
+    UA_StatusCode retval = response.responseHeader.serviceResult;
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_AddReferencesResponse_deleteMembers(&response);
+        return retval;
+    }
+    if(response.resultsSize != 1) {
+        UA_AddReferencesResponse_deleteMembers(&response);
+        return UA_STATUSCODE_BADUNEXPECTEDERROR;
+    }
+    retval = response.results[0];
+    UA_AddReferencesResponse_deleteMembers(&response);
+    return retval;
+}
+
+UA_StatusCode UA_EXPORT
+UA_Client_deleteReference(UA_Client *client, const UA_NodeId sourceNodeId, const UA_NodeId referenceTypeId,
+                          UA_Boolean isForward, const UA_ExpandedNodeId targetNodeId,
+                          UA_Boolean deleteBidirectional) {
+    UA_DeleteReferencesItem item;
+    UA_DeleteReferencesItem_init(&item);
+    item.sourceNodeId = sourceNodeId;
+    item.referenceTypeId = referenceTypeId;
+    item.isForward = isForward;
+    item.targetNodeId = targetNodeId;
+    item.deleteBidirectional = deleteBidirectional;
+    UA_DeleteReferencesRequest request;
+    UA_DeleteReferencesRequest_init(&request);
+    request.referencesToDelete = &item;
+    request.referencesToDeleteSize = 1;
+    UA_DeleteReferencesResponse response = UA_Client_Service_deleteReferences(client, request);
+    UA_StatusCode retval = response.responseHeader.serviceResult;
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_DeleteReferencesResponse_deleteMembers(&response);
+        return retval;
+    }
+    if(response.resultsSize != 1) {
+        UA_DeleteReferencesResponse_deleteMembers(&response);
+        return UA_STATUSCODE_BADUNEXPECTEDERROR;
+    }
+    retval = response.results[0];
+    UA_DeleteReferencesResponse_deleteMembers(&response);
+    return retval;
+}
+
+UA_StatusCode
+UA_Client_deleteNode(UA_Client *client, const UA_NodeId nodeId, UA_Boolean deleteTargetReferences) {
+    UA_DeleteNodesItem item;
+    UA_DeleteNodesItem_init(&item);
+    item.nodeId = nodeId;
+    item.deleteTargetReferences = deleteTargetReferences;
+    UA_DeleteNodesRequest request;
+    UA_DeleteNodesRequest_init(&request);
+    request.nodesToDelete = &item;
+    request.nodesToDeleteSize = 1;
+    UA_DeleteNodesResponse response = UA_Client_Service_deleteNodes(client, request);
+    UA_StatusCode retval = response.responseHeader.serviceResult;
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_DeleteNodesResponse_deleteMembers(&response);
+        return retval;
+    }
+    if(response.resultsSize != 1) {
+        UA_DeleteNodesResponse_deleteMembers(&response);
+        return UA_STATUSCODE_BADUNEXPECTEDERROR;
+    }
+    retval = response.results[0];
+    UA_DeleteNodesResponse_deleteMembers(&response);
+    return retval;
+}
+
+UA_StatusCode
+__UA_Client_addNode(UA_Client *client, const UA_NodeClass nodeClass, const UA_NodeId requestedNewNodeId,
+                    const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
+                    const UA_QualifiedName browseName, const UA_NodeId typeDefinition,
+                    const UA_NodeAttributes *attr, const UA_DataType *attributeType, UA_NodeId *outNewNodeId) {
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    UA_AddNodesRequest request;
+    UA_AddNodesRequest_init(&request);
+    UA_AddNodesItem item;
+    UA_AddNodesItem_init(&item);
+    item.parentNodeId.nodeId = parentNodeId;
+    item.referenceTypeId = referenceTypeId;
+    item.requestedNewNodeId.nodeId = requestedNewNodeId;
+    item.browseName = browseName;
+    item.nodeClass = nodeClass;
+    item.typeDefinition.nodeId = typeDefinition;
+    item.nodeAttributes.encoding = UA_EXTENSIONOBJECT_DECODED_NODELETE;
+    item.nodeAttributes.content.decoded.type = attributeType;
+    item.nodeAttributes.content.decoded.data = (void*)(uintptr_t)attr; // hack. is not written into.
+    request.nodesToAdd = &item;
+    request.nodesToAddSize = 1;
+    UA_AddNodesResponse response = UA_Client_Service_addNodes(client, request);
+    if(response.responseHeader.serviceResult != UA_STATUSCODE_GOOD) {
+        retval = response.responseHeader.serviceResult;
+        UA_AddNodesResponse_deleteMembers(&response);
+        return retval;
+    }
+    if(response.resultsSize != 1) {
+        UA_AddNodesResponse_deleteMembers(&response);
+        return UA_STATUSCODE_BADUNEXPECTEDERROR;
+    }
+    if(outNewNodeId && response.results[0].statusCode == UA_STATUSCODE_GOOD) {
+        *outNewNodeId = response.results[0].addedNodeId;
+        UA_NodeId_init(&response.results[0].addedNodeId);
+    }
+    retval = response.results[0].statusCode;
+    UA_AddNodesResponse_deleteMembers(&response);
+    return retval;
+}
+
+/********/
+/* Call */
+/********/
+
+UA_StatusCode
+UA_Client_call(UA_Client *client, const UA_NodeId objectId, const UA_NodeId methodId, UA_Int32 inputSize,
+               const UA_Variant *input, UA_Int32 *outputSize, UA_Variant **output) {
+    UA_CallRequest request;
+    UA_CallRequest_init(&request);
+    UA_CallMethodRequest item;
+    UA_CallMethodRequest_init(&item);
+    item.methodId = methodId;
+    item.objectId = objectId;
+    item.inputArguments = (void*)(uintptr_t)input; // cast const...
+    item.inputArgumentsSize = inputSize;
+    request.methodsToCall = &item;
+    request.methodsToCallSize = 1;
+    UA_CallResponse response = UA_Client_Service_call(client, request);
+    UA_StatusCode retval = response.responseHeader.serviceResult;
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_CallResponse_deleteMembers(&response);
+        return retval;
+    }
+    if(response.resultsSize != 1) {
+        UA_CallResponse_deleteMembers(&response);
+        return UA_STATUSCODE_BADUNEXPECTEDERROR;
+    }
+    retval = response.results[0].statusCode;
+    if(retval == UA_STATUSCODE_GOOD) {
+        *output = response.results[0].outputArguments;
+        *outputSize = response.results[0].outputArgumentsSize;
+        response.results[0].outputArguments = NULL;
+        response.results[0].outputArgumentsSize = 0;
+    }
+    UA_CallResponse_deleteMembers(&response);
+    return retval;
+}
+
+/**************/
+/* Attributes */
+/**************/
+
+UA_StatusCode 
+__UA_Client_readAttribute(UA_Client *client, UA_NodeId nodeId, UA_AttributeId attributeId,
+                          void *out, const UA_DataType *outDataType) {
+    UA_ReadValueId item;
+    UA_ReadValueId_init(&item);
+    item.nodeId = nodeId;
+    item.attributeId = attributeId;
+    UA_ReadRequest request;
+    UA_ReadRequest_init(&request);
+    request.nodesToRead = &item;
+    request.nodesToReadSize = 1;
+    UA_ReadResponse response = UA_Client_Service_read(client, request);
+    UA_StatusCode retval = response.responseHeader.serviceResult;
+    if(retval == UA_STATUSCODE_GOOD && response.resultsSize != 1)
+        retval = UA_STATUSCODE_BADUNEXPECTEDERROR;
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_ReadResponse_deleteMembers(&response);
+        return retval;
+    }
+
+    UA_DataValue *res = response.results;
+    if(res->hasStatus != UA_STATUSCODE_GOOD)
+        retval = res->hasStatus;
+    else if(!res->hasValue || !UA_Variant_isScalar(&res->value))
+        retval = UA_STATUSCODE_BADUNEXPECTEDERROR;
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_ReadResponse_deleteMembers(&response);
+        return retval;
+    }
+
+    if(attributeId == UA_ATTRIBUTEID_VALUE) {
+        memcpy(out, &res->value, sizeof(UA_Variant));
+        UA_Variant_init(&res->value);
+    } else if(res->value.type != outDataType) {
+        retval = UA_STATUSCODE_BADUNEXPECTEDERROR;
+    } else {
+        memcpy(out, res->value.data, res->value.type->memSize);
+        UA_free(res->value.data);
+        res->value.data = NULL;
+    }
+
+    UA_ReadResponse_deleteMembers(&response);
+    return retval;
+}

+ 308 - 0
src/client/ua_client_highlevel_subscriptions.c

@@ -0,0 +1,308 @@
+#include "ua_client_highlevel.h"
+#include "ua_client_internal.h"
+#include "ua_util.h"
+#include "ua_types_generated_encoding_binary.h"
+
+const UA_SubscriptionSettings UA_SubscriptionSettings_standard = {
+    .requestedPublishingInterval = 0.0,
+    .requestedLifetimeCount = 100,
+    .requestedMaxKeepAliveCount = 10,
+    .maxNotificationsPerPublish = 10,
+    .publishingEnabled = UA_TRUE,
+    .priority = 0
+};
+
+UA_StatusCode UA_Client_Subscriptions_new(UA_Client *client, UA_SubscriptionSettings settings,
+                                          UA_UInt32 *newSubscriptionId) {
+    UA_CreateSubscriptionRequest request;
+    UA_CreateSubscriptionRequest_init(&request);
+    request.requestedPublishingInterval = settings.requestedPublishingInterval;
+    request.requestedLifetimeCount = settings.requestedLifetimeCount;
+    request.requestedMaxKeepAliveCount = settings.requestedMaxKeepAliveCount;
+    request.maxNotificationsPerPublish = settings.maxNotificationsPerPublish;
+    request.publishingEnabled = settings.publishingEnabled;
+    request.priority = settings.priority;
+    
+    UA_CreateSubscriptionResponse response = UA_Client_Service_createSubscription(client, request);
+    UA_StatusCode retval = response.responseHeader.serviceResult;
+    if(retval == UA_STATUSCODE_GOOD) {
+        UA_Client_Subscription *newSub = UA_malloc(sizeof(UA_Client_Subscription));
+        LIST_INIT(&newSub->MonitoredItems);
+        newSub->LifeTime = response.revisedLifetimeCount;
+        newSub->KeepAliveCount = response.revisedMaxKeepAliveCount;
+        newSub->PublishingInterval = response.revisedPublishingInterval;
+        newSub->SubscriptionID = response.subscriptionId;
+        newSub->NotificationsPerPublish = request.maxNotificationsPerPublish;
+        newSub->Priority = request.priority;
+        if(newSubscriptionId)
+            *newSubscriptionId = newSub->SubscriptionID;
+        LIST_INSERT_HEAD(&client->subscriptions, newSub, listEntry);
+    }
+    
+    UA_CreateSubscriptionResponse_deleteMembers(&response);
+    return retval;
+}
+
+UA_StatusCode UA_Client_Subscriptions_remove(UA_Client *client, UA_UInt32 subscriptionId) {
+    UA_Client_Subscription *sub;
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    
+    LIST_FOREACH(sub, &client->subscriptions, listEntry) {
+        if(sub->SubscriptionID == subscriptionId)
+            break;
+    }
+    
+    // Problem? We do not have this subscription registeres. Maybe the server should
+    // be consulted at this point?
+    if(!sub)
+        return UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
+    
+    UA_DeleteSubscriptionsRequest request;
+    UA_DeleteSubscriptionsRequest_init(&request);
+    request.subscriptionIdsSize = 1;
+    request.subscriptionIds = (UA_UInt32 *) UA_malloc(sizeof(UA_UInt32));
+    *request.subscriptionIds = sub->SubscriptionID;
+    
+    UA_Client_MonitoredItem *mon, *tmpmon;
+    LIST_FOREACH_SAFE(mon, &sub->MonitoredItems, listEntry, tmpmon) {
+        retval |= UA_Client_Subscriptions_removeMonitoredItem(client, sub->SubscriptionID,
+                                                              mon->MonitoredItemId);
+    }
+    if(retval != UA_STATUSCODE_GOOD) {
+	    UA_DeleteSubscriptionsRequest_deleteMembers(&request);
+        return retval;
+    }
+    
+    UA_DeleteSubscriptionsResponse response = UA_Client_Service_deleteSubscriptions(client, request);
+    if(response.resultsSize > 0)
+        retval = response.results[0];
+    else
+        retval = response.responseHeader.serviceResult;
+    
+    if(retval == UA_STATUSCODE_GOOD) {
+        LIST_REMOVE(sub, listEntry);
+        UA_free(sub);
+    }
+    UA_DeleteSubscriptionsRequest_deleteMembers(&request);
+    UA_DeleteSubscriptionsResponse_deleteMembers(&response);
+    return retval;
+}
+
+UA_StatusCode
+UA_Client_Subscriptions_addMonitoredItem(UA_Client *client, UA_UInt32 subscriptionId,
+                                         UA_NodeId nodeId, UA_UInt32 attributeID,
+                                         void *handlingFunction, UA_UInt32 *newMonitoredItemId) {
+    UA_Client_Subscription *sub;
+    LIST_FOREACH(sub, &client->subscriptions, listEntry) {
+        if(sub->SubscriptionID == subscriptionId)
+            break;
+    }
+    if(!sub)
+        return UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
+    
+    UA_CreateMonitoredItemsRequest request;
+    UA_CreateMonitoredItemsRequest_init(&request);
+    request.subscriptionId = subscriptionId;
+    UA_MonitoredItemCreateRequest item;
+    UA_MonitoredItemCreateRequest_init(&item);
+    item.itemToMonitor.nodeId = nodeId;
+    item.itemToMonitor.attributeId = attributeID;
+    item.monitoringMode = UA_MONITORINGMODE_REPORTING;
+    item.requestedParameters.clientHandle = ++(client->monitoredItemHandles);
+    item.requestedParameters.samplingInterval = sub->PublishingInterval;
+    item.requestedParameters.discardOldest = UA_TRUE;
+    item.requestedParameters.queueSize = 1;
+    request.itemsToCreate = &item;
+    request.itemsToCreateSize = 1;
+    // Filter can be left void for now, only changes are supported (UA_Expert does the same with changeItems)
+    
+    UA_CreateMonitoredItemsResponse response = UA_Client_Service_createMonitoredItems(client, request);
+    
+    UA_StatusCode retval;
+    // slight misuse of retval here to check if the deletion was successfull.
+    if(response.resultsSize == 0)
+        retval = response.responseHeader.serviceResult;
+    else
+        retval = response.results[0].statusCode;
+    
+    if(retval == UA_STATUSCODE_GOOD) {
+        UA_Client_MonitoredItem *newMon = UA_malloc(sizeof(UA_Client_MonitoredItem));
+        newMon->MonitoringMode = UA_MONITORINGMODE_REPORTING;
+        UA_NodeId_copy(&nodeId, &newMon->monitoredNodeId); 
+        newMon->AttributeID = attributeID;
+        newMon->ClientHandle = client->monitoredItemHandles;
+        newMon->SamplingInterval = sub->PublishingInterval;
+        newMon->QueueSize = 1;
+        newMon->DiscardOldest = UA_TRUE;
+        newMon->handler = handlingFunction;
+        newMon->MonitoredItemId = response.results[0].monitoredItemId;
+        LIST_INSERT_HEAD(&sub->MonitoredItems, newMon, listEntry);
+        *newMonitoredItemId = newMon->MonitoredItemId;
+    }
+    
+    UA_CreateMonitoredItemsResponse_deleteMembers(&response);
+    return retval;
+}
+
+UA_StatusCode
+UA_Client_Subscriptions_removeMonitoredItem(UA_Client *client, UA_UInt32 subscriptionId,
+                                            UA_UInt32 monitoredItemId) {
+    UA_Client_Subscription *sub;
+    LIST_FOREACH(sub, &client->subscriptions, listEntry) {
+        if(sub->SubscriptionID == subscriptionId)
+            break;
+    }
+    if(!sub)
+        return UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
+    
+    UA_Client_MonitoredItem *mon;
+    LIST_FOREACH(mon, &sub->MonitoredItems, listEntry) {
+        if(mon->MonitoredItemId == monitoredItemId)
+            break;
+    }
+    if(!mon)
+        return UA_STATUSCODE_BADMONITOREDITEMIDINVALID;
+    
+    UA_DeleteMonitoredItemsRequest request;
+    UA_DeleteMonitoredItemsRequest_init(&request);
+    request.subscriptionId = sub->SubscriptionID;
+    request.monitoredItemIdsSize = 1;
+    request.monitoredItemIds = (UA_UInt32 *) UA_malloc(sizeof(UA_UInt32));
+    request.monitoredItemIds[0] = mon->MonitoredItemId;
+    
+    UA_DeleteMonitoredItemsResponse response = UA_Client_Service_deleteMonitoredItems(client, request);
+
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    if(response.resultsSize > 1)
+        retval = response.results[0];
+    else
+        retval = response.responseHeader.serviceResult;
+    
+    if(retval == UA_STATUSCODE_GOOD) {
+        LIST_REMOVE(mon, listEntry);
+        UA_NodeId_deleteMembers(&mon->monitoredNodeId);
+        UA_free(mon);
+    }
+    
+    UA_DeleteMonitoredItemsRequest_deleteMembers(&request);
+    UA_DeleteMonitoredItemsResponse_deleteMembers(&response);
+    return retval;
+}
+
+static UA_Boolean
+UA_Client_processPublishRx(UA_Client *client, UA_PublishResponse response) {
+    if(response.responseHeader.serviceResult != UA_STATUSCODE_GOOD)
+        return UA_FALSE;
+    
+    // Check if the server has acknowledged any of our ACKS
+    // Note that a list of serverside status codes may be send without valid publish data, i.e. 
+    // during keepalives or no data availability
+    UA_Client_NotificationsAckNumber *ack, *tmpAck;
+    size_t i = 0;
+    LIST_FOREACH_SAFE(ack, &client->pendingNotificationsAcks, listEntry, tmpAck) {
+        if(response.results[i] == UA_STATUSCODE_GOOD ||
+           response.results[i] == UA_STATUSCODE_BADSEQUENCENUMBERINVALID) {
+            LIST_REMOVE(ack, listEntry);
+            UA_free(ack);
+        }
+        i++;
+    }
+    
+    if(response.subscriptionId == 0)
+        return UA_FALSE;
+    
+    UA_Client_Subscription *sub;
+    LIST_FOREACH(sub, &client->subscriptions, listEntry) {
+        if(sub->SubscriptionID == response.subscriptionId)
+            break;
+    }
+    if(!sub)
+        return UA_FALSE;
+    
+    UA_NotificationMessage msg = response.notificationMessage;
+    UA_Client_MonitoredItem *mon;
+    for(size_t k = 0; k < msg.notificationDataSize; k++) {
+        if(msg.notificationData[k].encoding != UA_EXTENSIONOBJECT_DECODED)
+            continue;
+        
+        if(msg.notificationData[k].content.decoded.type == &UA_TYPES[UA_TYPES_DATACHANGENOTIFICATION]) {
+            // This is a dataChangeNotification
+            UA_DataChangeNotification *dataChangeNotification = msg.notificationData[k].content.decoded.data;
+            for(size_t i = 0; i < dataChangeNotification->monitoredItemsSize; i++) {
+            UA_MonitoredItemNotification *mitemNot = &dataChangeNotification->monitoredItems[i];
+                // find this client handle
+                LIST_FOREACH(mon, &sub->MonitoredItems, listEntry) {
+                    if(mon->ClientHandle == mitemNot->clientHandle) {
+                        mon->handler(mitemNot->clientHandle, &mitemNot->value);
+                        break;
+                    }
+                }
+            }
+            continue;
+        }
+
+        /* if(msg.notificationData[k].typeId.namespaceIndex == 0 && */
+        /*    msg.notificationData[k].typeId.identifier.numeric == 820 ) { */
+        /*     //FIXME: This is a statusChangeNotification (not supported yet) */
+        /*     continue; */
+        /* } */
+
+        /* if(msg.notificationData[k].typeId.namespaceIndex == 0 && */
+        /*    msg.notificationData[k].typeId.identifier.numeric == 916 ) { */
+        /*     //FIXME: This is an EventNotification */
+        /*     continue; */
+        /* } */
+    }
+    
+    /* We processed this message, add it to the list of pending acks (but make
+       sure it's not in the list first) */
+    LIST_FOREACH(tmpAck, &client->pendingNotificationsAcks, listEntry) {
+        if(tmpAck->subAck.sequenceNumber == msg.sequenceNumber &&
+            tmpAck->subAck.subscriptionId == response.subscriptionId)
+            break;
+    }
+
+    if(!tmpAck) {
+        tmpAck = UA_malloc(sizeof(UA_Client_NotificationsAckNumber));
+        tmpAck->subAck.sequenceNumber = msg.sequenceNumber;
+        tmpAck->subAck.subscriptionId = sub->SubscriptionID;
+        LIST_INSERT_HEAD(&client->pendingNotificationsAcks, tmpAck, listEntry);
+    }
+    
+    return response.moreNotifications;
+}
+
+void UA_Client_Subscriptions_manuallySendPublishRequest(UA_Client *client) {
+    UA_Boolean moreNotifications = UA_TRUE;
+    do {
+        UA_PublishRequest request;
+        UA_PublishRequest_init(&request);
+        request.subscriptionAcknowledgementsSize = 0;
+
+        UA_Client_NotificationsAckNumber *ack;
+        LIST_FOREACH(ack, &client->pendingNotificationsAcks, listEntry)
+            request.subscriptionAcknowledgementsSize++;
+        if(request.subscriptionAcknowledgementsSize > 0) {
+            request.subscriptionAcknowledgements = UA_malloc(sizeof(UA_SubscriptionAcknowledgement) *
+                                                             request.subscriptionAcknowledgementsSize);
+            if(!request.subscriptionAcknowledgements)
+                return;
+        }
+        
+        int index = 0 ;
+        LIST_FOREACH(ack, &client->pendingNotificationsAcks, listEntry) {
+            request.subscriptionAcknowledgements[index].sequenceNumber = ack->subAck.sequenceNumber;
+            request.subscriptionAcknowledgements[index].subscriptionId = ack->subAck.subscriptionId;
+            index++;
+        }
+        
+        UA_PublishResponse response = UA_Client_Service_publish(client, request);
+        if(response.responseHeader.serviceResult == UA_STATUSCODE_GOOD)
+            moreNotifications = UA_Client_processPublishRx(client, response);
+        else
+            moreNotifications = UA_FALSE;
+        
+        UA_PublishResponse_deleteMembers(&response);
+        UA_PublishRequest_deleteMembers(&request);
+    } while(moreNotifications == UA_TRUE);
+}

+ 42 - 7
src/client/ua_client_internal.h

@@ -1,9 +1,15 @@
 #ifndef UA_CLIENT_INTERNAL_H_
 #define UA_CLIENT_INTERNAL_H_
 
+#include "ua_securechannel.h"
 #include "queue.h"
 
+/**************************/
+/* Subscriptions Handling */
+/**************************/
+
 #ifdef ENABLE_SUBSCRIPTIONS
+
 typedef struct UA_Client_NotificationsAckNumber_s {
     UA_SubscriptionAcknowledgement subAck;
     LIST_ENTRY(UA_Client_NotificationsAckNumber_s) listEntry;
@@ -33,15 +39,44 @@ typedef struct UA_Client_Subscription_s {
     LIST_HEAD(UA_ListOfClientMonitoredItems, UA_Client_MonitoredItem_s) MonitoredItems;
 } UA_Client_Subscription;
 
-UA_CreateSubscriptionResponse   UA_Client_createSubscription(UA_Client *client, UA_CreateSubscriptionRequest *request);
-UA_ModifySubscriptionResponse   UA_Client_modifySubscription(UA_Client *client, UA_ModifySubscriptionRequest *request);
-UA_DeleteSubscriptionsResponse  UA_Client_deleteSubscriptions(UA_Client *client, UA_DeleteSubscriptionsRequest *request);
-UA_CreateMonitoredItemsResponse UA_Client_createMonitoredItems(UA_Client *client, UA_CreateMonitoredItemsRequest *request);
-UA_DeleteMonitoredItemsResponse UA_Client_deleteMonitoredItems(UA_Client *client, UA_DeleteMonitoredItemsRequest *request);
-UA_PublishResponse              UA_Client_publish(UA_Client *client, UA_PublishRequest *request);
+#endif
+
+/**********/
+/* Client */
+/**********/
 
+typedef enum {
+    UA_CLIENTSTATE_READY,
+    UA_CLIENTSTATE_CONNECTED,
+    UA_CLIENTSTATE_ERRORED
+} UA_Client_State;
 
-UA_Boolean UA_Client_processPublishRx(UA_Client *client, UA_PublishResponse response);
+struct UA_Client {
+    /* State */ //maybe it should be visible to user
+    UA_Client_State state;
+
+    /* Connection */
+    UA_Connection connection;
+    UA_SecureChannel channel;
+    UA_String endpointUrl;
+    UA_UInt32 requestId;
+
+    /* Session */
+    UA_UserTokenPolicy token;
+    UA_NodeId sessionId;
+    UA_NodeId authenticationToken;
+    UA_UInt32 requestHandle;
+    
+#ifdef ENABLE_SUBSCRIPTIONS
+    UA_Int32 monitoredItemHandles;
+    LIST_HEAD(UA_ListOfUnacknowledgedNotificationNumbers, UA_Client_NotificationsAckNumber_s) pendingNotificationsAcks;
+    LIST_HEAD(UA_ListOfClientSubscriptionItems, UA_Client_Subscription_s) subscriptions;
 #endif
+    
+    /* Config */
+    UA_Logger logger;
+    UA_ClientConfig config;
+    UA_DateTime scExpiresAt;
+};
 
 #endif /* UA_CLIENT_INTERNAL_H_ */

+ 0 - 281
src/ongoing/generateSam.c

@@ -1,281 +0,0 @@
-#include "ua_xml.h"
-#include "ua_namespace.h"
-#include "ua_namespace_xml.h"
-#include "ua_types_generated.h"
-#include <ctype.h> // tolower
-
-/** @brief we need a variable global to the module to make it possible for the visitors to access the namespace */
-static Namespace* theNamespace;
-
-UA_Int32 UA_Node_getParent(const UA_Node* node, const UA_Node** parent) {
-	UA_Int32 i = 0;
-	DBG(printf("// UA_Node_getParent - node={i=%d}",node->nodeId.identifier.numeric));
-	// FIXME: the data model is crap! we should have a single memory location which holds the parent NodeId
-	for (; i < node->referencesSize; i++ ) {
-		UA_Int32 refId = node->references[i].referenceTypeId.identifier.numeric;
-		UA_Int32 isInverse = node->references[i].isInverse;
-		if (isInverse && (refId == 47 || refId == 46)) {
-			UA_Int32 retval = Namespace_get(theNamespace, &(node->references[i].targetId.nodeId),parent);
-			if (retval == UA_SUCCESS) {
-				DBG(printf(" has parent={i=%d}\n",(*parent)->nodeId.identifier.numeric));
-			} else {
-				DBG(printf(" has non-existing parent={i=%d}\n", node->references[i].targetId.nodeId.identifier.numeric));
-			}
-			return retval;
-		}
-	}
-	// there is no parent, we are root
-	DBG(printf(" is root\n"));
-	*parent = UA_NULL;
-	return UA_SUCCESS;
-}
-
-/** @brief recurse down to root and return root node */
-UA_Int32 UA_Node_getRoot(const UA_Node* node, const UA_Node** root) {
-	UA_Int32 retval = UA_SUCCESS;
-	const UA_Node* parent = UA_NULL;
-	if ( (retval = UA_Node_getParent(node,&parent)) == UA_SUCCESS ) {
-		if (parent != UA_NULL) {	// recurse down to root node
-			retval = UA_Node_getRoot(parent,root);
-		} else {					// node is root, terminate recursion
-			*root = node;
-		}
-	}
-	return retval;
-}
-
-/** @brief check if VariableNode needs a memory object. This is the
- * case if the parent is of type object and the root is type object
- **/
-_Bool UA_VariableNode_needsObject(const UA_VariableNode* node) {
-	const UA_Node* parent = UA_NULL;
-	if ( UA_Node_getParent((UA_Node*)node,&parent) == UA_SUCCESS ) {
-		if (parent == UA_NULL)
-			return UA_TRUE;
-		if (parent->nodeClass == UA_NODECLASS_OBJECT ) {
-			const UA_Node* root;
-			if (UA_Node_getRoot(parent,&root) == UA_SUCCESS)
-				if (root == UA_NULL || root->nodeClass == UA_NODECLASS_OBJECT )
-					return UA_TRUE;
-		}
-	}
-	return UA_FALSE;
-}
-
-/** @brief recurse down to root and get full qualified name */
-UA_Int32 UA_Node_getPath(const UA_Node* node, UA_list_List* list) {
-	UA_Int32 retval = UA_SUCCESS;
-	const UA_Node* parent = UA_NULL;
-	if ( (retval = UA_Node_getParent(node,&parent)) == UA_SUCCESS ) {
-		if (parent != UA_NULL) {
-			// recurse down to root node
-			UA_Int32 retval = UA_Node_getPath(parent,list);
-			// and add our own name when we come back
-			if (retval == UA_SUCCESS) {
-				UA_list_addPayloadToBack(list,(void*)&(node->browseName.name));
-				DBG(printf("// UA_Node_getPath - add id={i=%d},class=%d",node->nodeId.identifier.numeric,node->nodeClass));
-				DBG(UA_String_printf(",name=",&(node->browseName.name)));
-			}
-		} else {
-			// node is root, terminate recursion by adding own name
-			UA_list_addPayloadToBack(list,(void*)&node->browseName.name);
-			DBG(printf("// UA_Node_getPath - add id={i=%d},class=%d",node->nodeId.identifier.numeric,node->nodeClass));
-			DBG(UA_String_printf(",name=",&(node->browseName.name)));
-		}
-	}
-	return retval;
-}
-
-
-/** @brief some macros to lowercase the first character without copying around */
-#define F_cls "%c%.*s"
-#define LC_cls(str) tolower((str).data[0]), (str).length-1, &((str).data[1])
-
-void listPrintName(void * payload) {
-	UA_ByteString* name = (UA_ByteString*) payload;
-	if (name->length > 0) {
-		printf("_" F_cls, LC_cls(*name));
-	}
-}
-
-/** @brief declares all the top level objects in the server's application memory */
-void sam_declareAttribute(UA_Node const * node) {
-	if (node->nodeClass == UA_NODECLASS_VARIABLE && UA_VariableNode_needsObject((UA_VariableNode*)node)) {
-		UA_list_List list; UA_list_init(&list);
-		UA_Int32 retval = UA_Node_getPath(node,&list);
-		if (retval == UA_SUCCESS) {
-			UA_VariableNode* vn = (UA_VariableNode*) node;
-			printf("\t%s ", UA_.types[UA_ns0ToVTableIndex(&vn->nodeId)].name);
-			UA_list_iteratePayload(&list,listPrintName);
-			printf("; // i=%d\n", node->nodeId.identifier.numeric);
-		} else {
-			printf("// could not determine path for i=%d\n",node->nodeId.identifier.numeric);
-		}
-		UA_list_destroy(&list,UA_NULL);
-	}
-}
-
-/** @brief declares all the buffers for string variables
- * FIXME: shall traverse down to the root object and create a unique name such as cstr_serverState_buildInfo_version
- */
-void sam_declareBuffer(UA_Node const * node) {
-	if (node != UA_NULL && node->nodeClass == UA_NODECLASS_VARIABLE) {
-		UA_VariableNode* vn = (UA_VariableNode*) node;
-		switch (vn->dataType.identifier.numeric) {
-		case UA_BYTESTRING_NS0:
-		case UA_STRING_NS0:
-		case UA_LOCALIZEDTEXT_NS0:
-		case UA_QUALIFIEDNAME_NS0:
-		printf("UA_Byte cstr_" F_cls "[] = \"\"\n",LC_cls(vn->browseName.name));
-		break;
-		default:
-		break;
-		}
-	}
-}
-
-/** @brief assigns the c-strings to the ua type strings.
- * FIXME: traverse down to top level objects and create a unique name such as cstr_serverState_buildInfo_version
- */
-void sam_assignBuffer(UA_Node const * node) {
-	if (node != UA_NULL && node->nodeClass == UA_NODECLASS_VARIABLE) {
-		UA_VariableNode* vn = (UA_VariableNode*) node;
-		switch (vn->dataType.identifier.numeric) {
-		case UA_BYTESTRING_NS0:
-		case UA_STRING_NS0:
-		printf("\tSAM_ASSIGN_CSTRING(cstr_" F_cls ",sam." F_cls ");\n",LC_cls(vn->browseName.name),LC_cls(vn->browseName.name));
-		break;
-		case UA_LOCALIZEDTEXT_NS0:
-		printf("\tSAM_ASSIGN_CSTRING(cstr_" F_cls ",sam." F_cls ".text);\n",LC_cls(vn->browseName.name),LC_cls(vn->browseName.name));
-		break;
-		case UA_QUALIFIEDNAME_NS0:
-		printf("\tSAM_ASSIGN_CSTRING(cstr_" F_cls ",sam." F_cls ".name);\n",LC_cls(vn->browseName.name),LC_cls(vn->browseName.name));
-		break;
-		default:
-		break;
-		}
-	}
-}
-
-void sam_attachToNamespace(UA_Node const * node) {
-	if (node != UA_NULL && node->nodeClass == UA_NODECLASS_VARIABLE) {
-		UA_VariableNode* vn = (UA_VariableNode*) node;
-		printf("\tsam_attach(ns,%d,%s,&sam." F_cls ");\n",node->nodeId.identifier.numeric,UA_.types[UA_ns0ToVTableIndex(&vn->dataType)].name, LC_cls(vn->browseName.name));
-	}
-}
-
-UA_Int32 Namespace_getNumberOfComponents(Namespace const * ns, UA_NodeId const * id, UA_Int32* number) {
-	UA_Int32 retval = UA_SUCCESS;
-	UA_Node const * node;
-	if ((retval = Namespace_get(ns,id,&node)) != UA_SUCCESS)
-		return UA_ERR_INVALID_VALUE;
-	UA_Int32 i, n;
-	for (i = 0, n = 0; i < node->referencesSize; i++ ) {
-		if (node->references[i].referenceTypeId.identifier.numeric == 47 && node->references[i].isInverse != UA_TRUE) {
-			n++;
-		}
-	}
-	Namespace_releaseManagedNode(node);
-	*number = n;
-	return retval;
-}
-
-UA_Int32 Namespace_getComponent(Namespace const * ns, UA_NodeId const * id, UA_Int32 idx, UA_NodeId** result) {
-	UA_Int32 retval = UA_SUCCESS;
-
-	UA_Node const * node;
-	if ((retval = Namespace_get(ns,id,&node)) != UA_SUCCESS)
-		return retval;
-
-	UA_Int32 i, n;
-	for (i = 0, n = 0; i < node->referencesSize; i++ ) {
-		if (node->references[i].referenceTypeId.identifier.numeric == 47 && node->references[i].isInverse != UA_TRUE) {
-			n++;
-			if (n == idx) {
-				*result = &(node->references[i].targetId.nodeId);
-				Namespace_releaseManagedNode(node);
-				return retval;
-			}
-		}
-	}
-	Namespace_releaseManagedNode(node);
-	return UA_ERR_INVALID_VALUE;
-}
-
-
-UA_Int32 UAX_NodeId_encodeBinaryByMetaData(Namespace const * ns, UA_NodeId const * id, UA_UInt32* pos, UA_ByteString *dst) {
-	UA_Int32 i, retval = UA_SUCCESS;
-	if (UA_NodeId_isBasicType(id)) {
-		const UA_Node * result;
-		if ((retval = Namespace_get(ns,id,&result)) == UA_SUCCESS) {
-			UA_Variant_encodeBinary(&((UA_VariableNode *) result)->value,dst,pos);
-			Namespace_releaseManagedNode(result);
-		}
-	} else {
-		UA_Int32 nComp = 0;
-		if ((retval = Namespace_getNumberOfComponents(ns,id,&nComp)) == UA_SUCCESS) {
-			for (i=0; i < nComp; i++) {
-				UA_NodeId* comp = UA_NULL;
-				Namespace_getComponent(ns,id,i,&comp);
-				UAX_NodeId_encodeBinaryByMetaData(ns,comp, pos, dst);
-			}
-		}
-	}
-	return retval;
-}
-
-UA_Int32 UAX_NodeId_encodeBinary(Namespace const * ns, UA_NodeId const * id, UA_ByteString *dst, UA_UInt32* offset) {
-	UA_Int32 retval = UA_SUCCESS;
-	UA_Node const * node;
-	if ((retval = Namespace_get(ns,id,&node)) == UA_SUCCESS) {
-		if (node->nodeClass == UA_NODECLASS_VARIABLE) {
-			retval = UA_Variant_encodeBinary(&((UA_VariableNode*) node)->value,dst,offset);
-		}
-		Namespace_releaseManagedNode(node);
-	}
-	return retval;
-}
-
-
-/** @ brief poor man's text template processor
- * for p in patterns: print p.s, iterate over namespace with p.v */
-typedef struct pattern {
-		char* s;
-		Namespace_nodeVisitor v;
-} pattern;
-
-pattern p[] = {
-{ "/** server application memory - generated but manually adapted */\n",UA_NULL },
-{ "#define SAM_ASSIGN_CSTRING(src,dst) do { dst.length = strlen(src)-1; dst.data = (UA_Byte*) src; } while(0)\n",UA_NULL },
-{ "struct sam {\n", sam_declareAttribute },
-{ "} sam;\n", UA_NULL },
-{ UA_NULL, sam_declareBuffer },
-{ "void sam_init(Namespace* ns) {\n", sam_assignBuffer },
-{ UA_NULL, sam_attachToNamespace },
-{ "}\n", UA_NULL },
-{UA_NULL, UA_NULL} // terminal node : both elements UA_NULL
-};
-
-int main(int argc, char** argv) {
-	if (argc != 2) {
-		printf("usage: %s filename\n",argv[0]);
-	} else {
-		Namespace* ns;
-		if (Namespace_loadFromFile(&ns,0,"ROOT",argv[1]) != UA_SUCCESS) {
-			printf("error loading file {%s}\n", argv[1]);
-		} else {
-			theNamespace = ns;
-			for (pattern* pi = &p[0]; pi->s != UA_NULL || pi->v != UA_NULL; ++pi) {
-				if (pi->s) {
-					printf("%s",pi->s);
-				}
-				if (pi->v) {
-					Namespace_iterate(ns, pi->v);
-				}
-			}
-			// FIXME: crashes with a seg fault
-			// Namespace_delete(ns);
-		}
-	}
-	return 0;
-}

+ 0 - 85
src/ongoing/ua_namespace_xml.c

@@ -1,85 +0,0 @@
-#include "ua_namespace_xml.h"
-#include <fcntl.h> // open, O_RDONLY
-
-typedef UA_Int32 (*XML_Stack_Loader) (char* buf, int len);
-
-#define XML_BUFFER_LEN 1024
-UA_Int32 Namespace_loadXml(Namespace **ns,UA_UInt32 nsid,const char* rootName, XML_Stack_Loader getNextBufferFull) {
-	UA_Int32 retval = UA_SUCCESS;
-	char buf[XML_BUFFER_LEN];
-	int len; /* len is the number of bytes in the current bufferful of data */
-
-	XML_Stack s;
-	XML_Stack_init(&s, 0, rootName);
-
-	UA_NodeSet n;
-	UA_NodeSet_init(&n, 0);
-	*ns = n.ns;
-
-	XML_Stack_addChildHandler(&s, "UANodeSet", strlen("UANodeSet"), (XML_decoder) UA_NodeSet_decodeXmlFromStack, UA_INVALIDTYPE, &n);
-	XML_Parser parser = XML_ParserCreate(UA_NULL);
-	XML_SetUserData(parser, &s);
-	XML_SetElementHandler(parser, XML_Stack_startElement, XML_Stack_endElement);
-	XML_SetCharacterDataHandler(parser, XML_Stack_handleText);
-	while ((len = getNextBufferFull(buf, XML_BUFFER_LEN)) > 0) {
-		if (XML_Parse(parser, buf, len, (len < XML_BUFFER_LEN)) == XML_STATUS_ERROR) {
-			retval = UA_ERR_INVALID_VALUE;
-			break;
-		}
-	}
-	XML_ParserFree(parser);
-
-	DBG_VERBOSE(printf("Namespace_loadXml - aliases addr=%p, size=%d\n", (void*) &(n.aliases), n.aliases.size));
-	DBG_VERBOSE(UA_NodeSetAliases_println("Namespace_loadXml - elements=", &n.aliases));
-
-	return retval;
-}
-
-static int theFile = 0;
-UA_Int32 readFromTheFile(char*buf,int len) {
-	return read(theFile,buf,len);
-}
-
-/** @brief load a namespace from an XML-File
- *
- * @param[in/out] ns the address of the namespace ptr
- * @param[in] namespaceIndex the numeric id of the namespace
- * @param[in] rootName the name of the root element of the hierarchy (not used?)
- * @param[in] fileName the name of an existing file, e.g. Opc.Ua.NodeSet2.xml
- */
-UA_Int32 Namespace_loadFromFile(Namespace **ns,UA_UInt32 nsid,const char* rootName,const char* fileName) {
-	if (fileName == UA_NULL)
-		theFile = 0; // stdin
-	else if ((theFile = open(fileName, O_RDONLY)) == -1)
-		return UA_ERR_INVALID_VALUE;
-
-	UA_Int32 retval = Namespace_loadXml(ns,nsid,rootName,readFromTheFile);
-	close(theFile);
-	return retval;
-}
-
-static const char* theBuffer = UA_NULL;
-static const char* theBufferEnd = UA_NULL;
-UA_Int32 readFromTheBuffer(char*buf,int len) {
-	if (len == 0) return 0;
-	if (theBuffer + XML_BUFFER_LEN > theBufferEnd)
-		len = theBufferEnd - theBuffer + 1;
-	else
-		len = XML_BUFFER_LEN;
-	memcpy(buf,theBuffer,len);
-	theBuffer = theBuffer + len;
-	return len;
-}
-
-/** @brief load a namespace from a string
- *
- * @param[in/out] ns the address of the namespace ptr
- * @param[in] namespaceIndex the numeric id of the namespace
- * @param[in] rootName the name of the root element of the hierarchy (not used?)
- * @param[in] buffer the xml string
- */
-UA_Int32 Namespace_loadFromString(Namespace **ns,UA_UInt32 nsid,const char* rootName,const char* buffer) {
-	theBuffer = buffer;
-	theBufferEnd = buffer + strlen(buffer) - 1;
-	return Namespace_loadXml(ns,nsid,rootName,readFromTheBuffer);
-}

+ 0 - 41
src/ongoing/ua_namespace_xml.h

@@ -1,41 +0,0 @@
-#ifndef __UA_NAMESPACE_XML_H
-#define __UA_NAMESPACE_XML_H
-
-#include "ua_xml.h"
-#include "ua_types_generated.h"
-#include "ua_namespace.h"
-
-void print_node(UA_Node const * node);
-UA_Int32 UA_NodeSetAlias_init(UA_NodeSetAlias* p);
-UA_Int32 UA_NodeSetAlias_new(UA_NodeSetAlias** p);
-//UA_Int32 UA_NodeSetAlias_decodeXML(XML_Stack* s, XML_Attr* attr, UA_NodeSetAlias* dst, _Bool isStart);
-
-UA_Int32 UA_NodeSetAliases_init(UA_NodeSetAliases* p);
-UA_Int32 UA_NodeSetAliases_new(UA_NodeSetAliases** p);
-UA_Int32 UA_NodeSetAliases_println(cstring label, UA_NodeSetAliases *p);
-//UA_Int32 UA_NodeSetAliases_decodeXML(XML_Stack* s, XML_Attr* attr, UA_NodeSetAliases* dst, _Bool isStart);
-
-typedef struct UA_NodeSet {
-	Namespace* ns;
-	UA_NodeSetAliases aliases;
-} UA_NodeSet;
-
-/** @brief init typed array with size=-1 and an UA_INVALIDTYPE */
-UA_Int32 UA_NodeSet_init(UA_NodeSet* p, UA_UInt32 nsid);
-UA_Int32 UA_NodeSet_new(UA_NodeSet** p, UA_UInt32 nsid);
-UA_Int32 UA_NodeId_copycstring(cstring src, UA_NodeId* dst, UA_NodeSetAliases* aliases);
-UA_Int32 UA_NodeSet_decodeXmlFromStack(XML_Stack* s, XML_Attr* attr, UA_NodeSet* dst, _Bool isStart);
-
-UA_Int32 UA_ExpandedNodeId_copycstring(cstring src, UA_ExpandedNodeId* dst, UA_NodeSetAliases* aliases);
-
-/** @brief load a namespace from an XML-File
- *
- * @param[in/out] ns the address of the namespace ptr
- * @param[in] namespaceIndex the numeric id of the namespace
- * @param[in] rootName the name of the root element of the hierarchy (not used?)
- * @param[in] fileName the name of an existing file, e.g. Opc.Ua.NodeSet2.xml
- */
-UA_Int32 Namespace_loadFromFile(Namespace **ns,UA_UInt32 namespaceIndex,const char* rootName,const char* fileName);
-UA_Int32 Namespace_loadFromString(Namespace **ns,UA_UInt32 nsid,const char* rootName,const char* buffer);
-
-#endif /* __UA_NAMESPACE_XML_H */

+ 0 - 447
src/ongoing/ua_types_encoding_xml.c

@@ -1,447 +0,0 @@
-#include "ua_types_encoding_xml.h"
-#include "ua_util.h"
-#include "ua_namespace_0.h"
-#include "ua_xml.h"
-
-/* Boolean */
-
-UA_Int32 UA_Boolean_copycstring(cstring src, UA_Boolean *dst) {
-	*dst = UA_FALSE;
-	if(0 == strncmp(src, "true", 4) || 0 == strncmp(src, "TRUE", 4))
-		*dst = UA_TRUE;
-	return UA_SUCCESS;
-}
-
-UA_TYPE_METHOD_CALCSIZEXML_NOTIMPL(UA_Boolean)
-UA_TYPE_METHOD_ENCODEXML_NOTIMPL(UA_Boolean)
-UA_TYPE_METHOD_DECODEXML_NOTIMPL(UA_Boolean)
-/* UA_Int32 UA_Boolean_decodeXML(XML_Stack *s, XML_Attr *attr, UA_Boolean *dst, _Bool isStart) { */
-/* 	DBG_VERBOSE(printf("UA_Boolean entered with dst=%p,isStart=%d\n", (void * )dst, isStart)); */
-/* 	if(isStart) { */
-/* 		if(dst == UA_NULL) { */
-/* 			UA_Boolean_new(&dst); */
-/* 			s->parent[s->depth - 1].children[s->parent[s->depth - 1].activeChild].obj = (void *)dst; */
-/* 		} */
-/* 		UA_Boolean_copycstring((cstring)attr[1], dst); */
-/* 	} */
-/* 	return UA_SUCCESS; */
-/* } */
-
-/* SByte */
-UA_TYPE_METHOD_CALCSIZEXML_NOTIMPL(UA_SByte)
-UA_TYPE_METHOD_ENCODEXML_NOTIMPL(UA_SByte)
-UA_TYPE_METHOD_DECODEXML_NOTIMPL(UA_SByte)
-
-/* Byte */
-UA_TYPE_METHOD_CALCSIZEXML_NOTIMPL(UA_Byte)
-UA_TYPE_METHOD_ENCODEXML_NOTIMPL(UA_Byte)
-UA_TYPE_METHOD_DECODEXML_NOTIMPL(UA_Byte)
-
-/* Int16 */
-UA_TYPE_METHOD_CALCSIZEXML_NOTIMPL(UA_Int16)
-UA_TYPE_METHOD_ENCODEXML_NOTIMPL(UA_Int16)
-
-UA_Int32 UA_Int16_copycstring(cstring src, UA_Int16 *dst) {
-	*dst = atoi(src);
-	return UA_SUCCESS;
-}
-
-UA_Int32 UA_UInt16_copycstring(cstring src, UA_UInt16 *dst) {
-	*dst = atoi(src);
-	return UA_SUCCESS;
-}
-
-UA_Int32 UA_Int16_decodeXmlFromStack(XML_Stack *s, XML_Attr *attr, UA_Int16 *dst, _Bool isStart) {
-	DBG_VERBOSE(printf("UA_Int32 entered with dst=%p,isStart=%d\n", (void * )dst, isStart));
-	if(isStart) {
-		if(dst == UA_NULL) {
-			UA_Int16_new(&dst);
-			s->parent[s->depth - 1].children[s->parent[s->depth - 1].activeChild].obj = (void *)dst;
-		}
-		UA_Int16_copycstring((cstring)attr[1], dst);
-	}
-	return UA_SUCCESS;
-}
-
-UA_TYPE_DECODEXML_FROM_BYTESTRING(UA_Int16)
-
-/* UInt16 */
-UA_TYPE_METHOD_CALCSIZEXML_NOTIMPL(UA_UInt16)
-UA_TYPE_METHOD_ENCODEXML_NOTIMPL(UA_UInt16)
-UA_TYPE_METHOD_DECODEXML_NOTIMPL(UA_UInt16)
-
-/* Int32 */
-UA_TYPE_METHOD_CALCSIZEXML_NOTIMPL(UA_Int32)
-UA_TYPE_METHOD_ENCODEXML_NOTIMPL(UA_Int32)
-
-UA_Int32 UA_Int32_decodeXmlFromStack(XML_Stack *s, XML_Attr *attr, UA_Int32 *dst, _Bool isStart) {
-	DBG_VERBOSE(printf("UA_Int32 entered with dst=%p,isStart=%d\n", (void * )dst, isStart));
-	if(isStart) {
-		if(dst == UA_NULL) {
-			UA_Int32_new(&dst);
-			s->parent[s->depth - 1].children[s->parent[s->depth - 1].activeChild].obj = (void *)dst;
-		}
-		*dst = atoi(attr[1]);
-	}
-	return UA_SUCCESS;
-}
-UA_TYPE_DECODEXML_FROM_BYTESTRING(UA_Int32)
-
-/* UInt32 */
-UA_TYPE_METHOD_CALCSIZEXML_NOTIMPL(UA_UInt32)
-UA_TYPE_METHOD_ENCODEXML_NOTIMPL(UA_UInt32)
-UA_TYPE_METHOD_DECODEXML_NOTIMPL(UA_UInt32)
-
-/* Int64 */
-UA_TYPE_METHOD_CALCSIZEXML_NOTIMPL(UA_Int64)
-UA_TYPE_METHOD_ENCODEXML_NOTIMPL(UA_Int64)
-UA_TYPE_METHOD_DECODEXML_NOTIMPL(UA_Int64)
-
-/* UInt64 */
-UA_TYPE_METHOD_CALCSIZEXML_NOTIMPL(UA_UInt64)
-UA_TYPE_METHOD_ENCODEXML_NOTIMPL(UA_UInt64)
-UA_TYPE_METHOD_DECODEXML_NOTIMPL(UA_UInt64)
-
-/* Float */
-UA_TYPE_METHOD_CALCSIZEXML_NOTIMPL(UA_Float)
-UA_TYPE_METHOD_ENCODEXML_NOTIMPL(UA_Float)
-UA_TYPE_METHOD_DECODEXML_NOTIMPL(UA_Float)
-
-/* Double */
-UA_TYPE_METHOD_CALCSIZEXML_NOTIMPL(UA_Double)
-UA_TYPE_METHOD_ENCODEXML_NOTIMPL(UA_Double)
-UA_TYPE_METHOD_DECODEXML_NOTIMPL(UA_Double)
-
-/* String */
-UA_TYPE_METHOD_CALCSIZEXML_NOTIMPL(UA_String)
-UA_TYPE_METHOD_ENCODEXML_NOTIMPL(UA_String)
-
-UA_Int32 UA_String_decodeXmlFromStack(XML_Stack *s, XML_Attr *attr, UA_String *dst, _Bool isStart) {
-	DBG_VERBOSE(printf("UA_String entered with dst=%p,isStart=%d\n", (void * )dst, isStart));
-	UA_UInt32 i;
-	if(isStart) {
-		if(dst == UA_NULL) {
-			UA_String_new(&dst);
-			s->parent[s->depth - 1].children[s->parent[s->depth - 1].activeChild].obj = (void *)dst;
-		}
-		s->parent[s->depth].len = 0;
-		XML_Stack_addChildHandler(s, "Data", strlen("Data"), (XML_decoder)UA_Text_decodeXmlFromStack, UA_BYTE,
-		                          &(dst->data));
-		XML_Stack_addChildHandler(s, "Length", strlen("Length"), (XML_decoder)UA_Int32_decodeXmlFromStack, UA_INT32,
-		                          &(dst->length));
-		XML_Stack_handleTextAsElementOf(s, "Data", 0);
-
-		// set attributes
-		for(i = 0;attr[i];i += 2) {
-			if(0 == strncmp("Data", attr[i], strlen("Data")))
-				UA_String_copycstring(attr[i + 1], dst);
-			else
-				printf("UA_String_decodeXml - Unknown attribute - name=%s, value=%s\n", attr[i], attr[i+1]);
-		}
-	} else {
-		switch(s->parent[s->depth - 1].activeChild) {
-		case 0:
-			if(dst != UA_NULL && dst->data != UA_NULL && dst->length == -1)
-				dst->length = strlen((char const *)dst->data);
-			break;
-		}
-	}
-	return UA_SUCCESS;
-}
-
-UA_TYPE_DECODEXML_FROM_BYTESTRING(UA_String)
-
-/* DateTime */
-UA_TYPE_METHOD_CALCSIZEXML_NOTIMPL(UA_DateTime)
-UA_TYPE_METHOD_ENCODEXML_NOTIMPL(UA_DateTime)
-UA_TYPE_METHOD_DECODEXML_NOTIMPL(UA_DateTime)
-
-/* Guid */
-UA_TYPE_METHOD_CALCSIZEXML_NOTIMPL(UA_Guid)
-UA_TYPE_METHOD_ENCODEXML_NOTIMPL(UA_Guid)
-UA_TYPE_METHOD_DECODEXML_NOTIMPL(UA_Guid)
-
-/* ByteString */
-UA_TYPE_METHOD_CALCSIZEXML_NOTIMPL(UA_ByteString)
-UA_TYPE_METHOD_ENCODEXML_NOTIMPL(UA_ByteString)
-UA_TYPE_METHOD_DECODEXML_NOTIMPL(UA_ByteString)
-
-/* XmlElement */
-UA_TYPE_METHOD_CALCSIZEXML_NOTIMPL(UA_XmlElement)
-UA_TYPE_METHOD_ENCODEXML_NOTIMPL(UA_XmlElement)
-UA_TYPE_METHOD_DECODEXML_NOTIMPL(UA_XmlElement)
-
-/* NodeId */
-UA_TYPE_METHOD_CALCSIZEXML_NOTIMPL(UA_NodeId)
-UA_TYPE_METHOD_ENCODEXML_NOTIMPL(UA_NodeId)
-
-UA_Int32 UA_NodeId_decodeXmlFromStack(XML_Stack *s, XML_Attr *attr, UA_NodeId *dst, _Bool isStart) {
-	DBG_VERBOSE(printf("UA_NodeId entered with dst=%p,isStart=%d\n", (void * )dst, isStart));
-	UA_UInt32 i;
-	if(isStart) {
-		if(dst == UA_NULL) {
-			UA_NodeId_new(&dst);
-			s->parent[s->depth - 1].children[s->parent[s->depth - 1].activeChild].obj = (void *)dst;
-		}
-		s->parent[s->depth].len = 0;
-		XML_Stack_addChildHandler(s, "Namespace", strlen(
-		                              "Namespace"), (XML_decoder)UA_Int16_decodeXmlFromStack, UA_INT16,
-		                          &(dst->namespaceIndex));
-		XML_Stack_addChildHandler(s, "Numeric", strlen(
-		                              "Numeric"), (XML_decoder)UA_Int32_decodeXmlFromStack, UA_INT32,
-		                          &(dst->identifier.numeric));
-		XML_Stack_addChildHandler(s, "Identifier", strlen(
-		                              "Identifier"), (XML_decoder)UA_String_decodeXmlFromStack, UA_STRING, UA_NULL);
-		XML_Stack_handleTextAsElementOf(s, "Data", 2);
-
-		// set attributes
-		for(i = 0; attr[i]; i += 2) {
-		if(0 == strncmp("Namespace", attr[i], strlen("Namespace")))
-			dst->namespaceIndex = atoi(attr[i + 1]);
-		else if(0 == strncmp("Numeric", attr[i], strlen("Numeric"))) {
-			dst->identifierType = UA_NODEIDTYPE_NUMERIC;
-			dst->identifier.numeric = atoi(attr[i + 1]);
-		} else
-			printf("UA_NodeId_decodeXml - Unknown attribute name=%s, value=%s\n", attr[i], attr[i+1]);
-		}
-	} else {
-		if(s->parent[s->depth - 1].activeChild == 2)
-			UA_NodeId_copycstring((cstring)((UA_String *)attr)->data, dst, s->aliases);
-	}
-	return UA_SUCCESS;
-}
-
-UA_TYPE_DECODEXML_FROM_BYTESTRING(UA_NodeId)
-
-/* ExpandedNodeId */
-UA_TYPE_METHOD_CALCSIZEXML_NOTIMPL(UA_ExpandedNodeId)
-UA_TYPE_METHOD_ENCODEXML_NOTIMPL(UA_ExpandedNodeId)
-
-UA_Int32 UA_ExpandedNodeId_decodeXmlFromStack(XML_Stack *s, XML_Attr *attr, UA_ExpandedNodeId *dst, _Bool isStart) {
-	DBG_VERBOSE(printf("UA_ExpandedNodeId entered with dst=%p,isStart=%d\n", (void * )dst, isStart));
-	UA_UInt32 i;
-	if(isStart) {
-		if(dst == UA_NULL) {
-			UA_ExpandedNodeId_new(&dst);
-			s->parent[s->depth - 1].children[s->parent[s->depth - 1].activeChild].obj = (void *)dst;
-		}
-		s->parent[s->depth].len = 0;
-		XML_Stack_addChildHandler(s, "NodeId", strlen(
-		                              "NodeId"), (XML_decoder)UA_NodeId_decodeXmlFromStack, UA_NODEID, &(dst->nodeId));
-		XML_Stack_addChildHandler(s, "Namespace", strlen("Namespace"),
-								  (XML_decoder)UA_Int16_decodeXmlFromStack, UA_INT16,
-		                          &(dst->nodeId.namespaceIndex));
-		XML_Stack_addChildHandler(s, "Numeric", strlen("Numeric"), (XML_decoder)UA_Int32_decodeXmlFromStack, UA_INT32,
-		                          &(dst->nodeId.identifier.numeric));
-		XML_Stack_addChildHandler(s, "Id", strlen("Id"), (XML_decoder)UA_String_decodeXmlFromStack, UA_STRING, UA_NULL);
-		XML_Stack_handleTextAsElementOf(s, "Data", 3);
-
-		// set attributes
-		for(i = 0; attr[i]; i += 2) {
-		if(0 == strncmp("Namespace", attr[i], strlen("Namespace")))
-			UA_UInt16_copycstring((cstring)attr[i + 1], &(dst->nodeId.namespaceIndex));
-		else if(0 == strncmp("Numeric", attr[i], strlen("Numeric"))) {
-		UA_NodeId_copycstring((cstring)attr[i + 1], &(dst->nodeId), s->aliases);
-		} else if(0 == strncmp("NodeId", attr[i], strlen("NodeId")))
-			UA_NodeId_copycstring((cstring)attr[i + 1], &(dst->nodeId), s->aliases);
-		else
-			printf("UA_ExpandedNodeId_decodeXml - unknown attribute name=%s, value=%s\n", attr[i], attr[i+1]);
-		}
-	}
-	return UA_SUCCESS;
-}
-
-UA_TYPE_DECODEXML_FROM_BYTESTRING(UA_ExpandedNodeId)
-
-/* StatusCode */
-UA_TYPE_METHOD_CALCSIZEXML_NOTIMPL(UA_StatusCode)
-UA_TYPE_METHOD_ENCODEXML_NOTIMPL(UA_StatusCode)
-UA_TYPE_METHOD_DECODEXML_NOTIMPL(UA_StatusCode)
-/* UA_Int32 UA_StatusCode_decodeXML(XML_Stack *s, XML_Attr *attr, UA_StatusCode *dst, _Bool isStart) { */
-/* 	DBG_VERBOSE(printf("UA_StatusCode_decodeXML entered with dst=%p,isStart=%d\n", (void * )dst, isStart)); */
-/* 	return UA_ERR_NOT_IMPLEMENTED; */
-/* } */
-
-/* QualifiedName */
-UA_TYPE_METHOD_CALCSIZEXML_NOTIMPL(UA_QualifiedName)
-UA_TYPE_METHOD_ENCODEXML_NOTIMPL(UA_QualifiedName)
-
-UA_Int32 UA_QualifiedName_decodeXmlFromStack(XML_Stack *s, XML_Attr *attr, UA_QualifiedName *dst, _Bool isStart) {
-	DBG_VERBOSE(printf("UA_QualifiedName entered with dst=%p,isStart=%d\n", (void * )dst, isStart));
-	UA_UInt32 i;
-	if(isStart) {
-		if(dst == UA_NULL) {
-			UA_QualifiedName_new(&dst);
-			s->parent[s->depth - 1].children[s->parent[s->depth - 1].activeChild].obj = (void *)dst;
-		}
-		s->parent[s->depth].len = 0;
-		XML_Stack_addChildHandler(s, "Name", strlen("Name"), (XML_decoder)UA_String_decodeXmlFromStack, UA_STRING,
-		                          &(dst->name));
-		XML_Stack_addChildHandler(s, "NamespaceIndex", strlen(
-		                              "NamespaceIndex"), (XML_decoder)UA_Int16_decodeXmlFromStack, UA_STRING,
-		                          &(dst->namespaceIndex));
-		XML_Stack_handleTextAsElementOf(s, "Data", 0);
-
-		// set attributes
-		for(i = 0;attr[i];i += 2) {
-			if(0 == strncmp("NamespaceIndex", attr[i], strlen("NamespaceIndex")))
-				dst->namespaceIndex = atoi(attr[i + 1]);
-			else if(0 == strncmp("Name", attr[i], strlen("Name")))
-				UA_String_copycstring(attr[i + 1], &(dst->name));
-			else
-				perror("Unknown attribute");
-		}
-	}
-	return UA_SUCCESS;
-}
-
-UA_TYPE_DECODEXML_FROM_BYTESTRING(UA_QualifiedName)
-
-/* LocalizedText */
-UA_TYPE_METHOD_CALCSIZEXML_NOTIMPL(UA_LocalizedText)
-UA_TYPE_METHOD_ENCODEXML_NOTIMPL(UA_LocalizedText)
-
-UA_Int32 UA_LocalizedText_decodeXmlFromStack(XML_Stack *s, XML_Attr *attr, UA_LocalizedText *dst, _Bool isStart) {
-	DBG_VERBOSE(printf("UA_LocalizedText entered with dst=%p,isStart=%d\n", (void * )dst, isStart));
-	UA_UInt32 i;
-	if(isStart) {
-		if(dst == UA_NULL) {
-			UA_LocalizedText_new(&dst);
-			s->parent[s->depth - 1].children[s->parent[s->depth - 1].activeChild].obj = (void *)dst;
-		}
-		// s->parent[s->depth].len = 0;
-		XML_Stack_addChildHandler(s, "Text", strlen("Text"), (XML_decoder)UA_String_decodeXmlFromStack, UA_STRING,
-		                          &(dst->text));
-		XML_Stack_addChildHandler(s, "Locale", strlen(
-		                              "Locale"), (XML_decoder)UA_String_decodeXmlFromStack, UA_STRING, &(dst->locale));
-		XML_Stack_handleTextAsElementOf(s, "Data", 0);
-
-		// set attributes
-		for(i = 0;attr[i];i += 2) {
-			if(0 == strncmp("Text", attr[i], strlen("Text"))) {
-				UA_String_copycstring(attr[i + 1], &dst->text);
-			} else if(0 == strncmp("Locale", attr[i], strlen("Locale"))) {
-				UA_String_copycstring(attr[i + 1], &dst->locale);
-			} else
-				perror("Unknown attribute");
-		}
-	} else {
-		switch(s->parent[s->depth - 1].activeChild) {
-		case 0:
-			//dst->encodingMask |= UA_LOCALIZEDTEXT_ENCODINGMASKTYPE_TEXT;
-			break;
-
-		case 1:
-			//dst->encodingMask |= UA_LOCALIZEDTEXT_ENCODINGMASKTYPE_LOCALE;
-			break;
-
-		default:
-			break;
-		}
-	}
-	return UA_SUCCESS;
-}
-
-UA_TYPE_DECODEXML_FROM_BYTESTRING(UA_LocalizedText)
-
-/* ExtensionObject */
-UA_TYPE_METHOD_CALCSIZEXML_NOTIMPL(UA_ExtensionObject)
-UA_TYPE_METHOD_ENCODEXML_NOTIMPL(UA_ExtensionObject)
-
-UA_Int32 UA_ExtensionObject_decodeXmlFromStack(XML_Stack *s, XML_Attr *attr, UA_ExtensionObject *dst, _Bool isStart) {
-	DBG_VERBOSE(printf("UA_ExtensionObject entered with dst=%p,isStart=%d\n", (void * )dst, isStart));
-	UA_UInt32 i;
-
-	if(isStart) {
-		// create a new object if called with UA_NULL
-		if(dst == UA_NULL) {
-			UA_ExtensionObject_new(&dst);
-			s->parent[s->depth - 1].children[s->parent[s->depth - 1].activeChild].obj = (void *)dst;
-		}
-
-		s->parent[s->depth].len = 0;
-		XML_Stack_addChildHandler(s, "TypeId", strlen(
-		                              "TypeId"), (XML_decoder)UA_NodeId_decodeXmlFromStack, UA_NODEID, &(dst->typeId));
-		// XML_Stack_addChildHandler(s, "Body", strlen("Body"), (XML_decoder) UA_Body_decodeXml, UA_LOCALIZEDTEXT, UA_NULL);
-
-		// set attributes
-		for(i = 0;attr[i];i += 2) {
-			{
-				DBG_ERR(XML_Stack_print(s));
-				DBG_ERR(printf("%s - unknown attribute\n", attr[i]));
-			}
-		}
-	}
-	return UA_SUCCESS;
-}
-
-UA_TYPE_DECODEXML_FROM_BYTESTRING(UA_ExtensionObject)
-
-/* DataValue */
-UA_TYPE_METHOD_CALCSIZEXML_NOTIMPL(UA_DataValue)
-UA_TYPE_METHOD_ENCODEXML_NOTIMPL(UA_DataValue)
-UA_TYPE_METHOD_DECODEXML_NOTIMPL(UA_DataValue)
-
-/* Variant */
-UA_TYPE_METHOD_CALCSIZEXML_NOTIMPL(UA_Variant)
-UA_TYPE_METHOD_ENCODEXML_NOTIMPL(UA_Variant)
-
-UA_Int32 UA_Variant_decodeXmlFromStack(XML_Stack *s, XML_Attr *attr, UA_Variant *dst, _Bool isStart) {
-	DBG_VERBOSE(printf("UA_Variant entered with dst=%p,isStart=%d\n", (void * )dst, isStart));
-	UA_UInt32 i;
-
-	if(isStart) {
-		// create a new object if called with UA_NULL
-		if(dst == UA_NULL) {
-			UA_Variant_new(&dst);
-			s->parent[s->depth - 1].children[s->parent[s->depth - 1].activeChild].obj = (void *)dst;
-		}
-
-		s->parent[s->depth].len = 0;
-		XML_Stack_addChildHandler(s, "ListOfExtensionObject", strlen(
-		                              "ListOfExtensionObject"), (XML_decoder)UA_TypedArray_decodeXmlFromStack,
-		                          UA_EXTENSIONOBJECT, UA_NULL);
-		XML_Stack_addChildHandler(s, "ListOfLocalizedText", strlen(
-		                              "ListOfLocalizedText"), (XML_decoder)UA_TypedArray_decodeXmlFromStack,
-		                          UA_LOCALIZEDTEXT, UA_NULL);
-
-		// set attributes
-		for(i = 0;attr[i];i += 2) {
-			{
-				DBG_ERR(XML_Stack_print(s));
-				DBG_ERR(printf("%s - unknown attribute\n", attr[i]));
-			}
-		}
-	} else {
-		if(s->parent[s->depth - 1].activeChild == 0 && attr != UA_NULL ) {  // ExtensionObject
-			UA_TypedArray *array = (UA_TypedArray *)attr;
-			DBG_VERBOSE(printf("UA_Variant_decodeXml - finished array: references=%p, size=%d\n", (void *)array,
-			                   (array == UA_NULL) ? -1 : array->size));
-			dst->arrayLength  = array->size;
-			dst->data         = array->elements;
-			dst->vt = &UA_.types[UA_EXTENSIONOBJECT];
-			s->parent[s->depth - 1].children[s->parent[s->depth - 1].activeChild].obj = UA_NULL;
-		} else if(s->parent[s->depth - 1].activeChild == 1 && attr != UA_NULL ) {  // LocalizedText
-			UA_TypedArray *array = (UA_TypedArray *)attr;
-			DBG_VERBOSE(printf("UA_Variant_decodeXml - finished array: references=%p, size=%d\n", (void *)array,
-			                   (array == UA_NULL) ? -1 : array->size));
-			dst->arrayLength  = array->size;
-			dst->data         = array->elements;
-			dst->vt = &UA_.types[UA_LOCALIZEDTEXT];
-			s->parent[s->depth - 1].children[s->parent[s->depth - 1].activeChild].obj = UA_NULL;
-		}
-	}
-	return UA_SUCCESS;
-}
-
-UA_TYPE_DECODEXML_FROM_BYTESTRING(UA_Variant)
-
-/* DiagnosticInfo */
-UA_TYPE_METHOD_CALCSIZEXML_NOTIMPL(UA_DiagnosticInfo)
-UA_TYPE_METHOD_ENCODEXML_NOTIMPL(UA_DiagnosticInfo)
-UA_TYPE_METHOD_DECODEXML_NOTIMPL(UA_DiagnosticInfo)
-
-/* InvalidType */
-UA_TYPE_METHOD_CALCSIZEXML_NOTIMPL(UA_InvalidType)
-UA_TYPE_METHOD_ENCODEXML_NOTIMPL(UA_InvalidType)
-UA_TYPE_METHOD_DECODEXML_NOTIMPL(UA_InvalidType)

+ 0 - 77
src/ongoing/ua_types_encoding_xml.h

@@ -1,77 +0,0 @@
-#ifndef UA_TYPES_ENCODING_XML_H_
-#define UA_TYPES_ENCODING_XML_H_
-
-#include "ua_xml.h"
-#include "ua_types.h"
-
-#define UA_TYPE_XML_ENCODING(TYPE)							\
-	UA_Int32 TYPE##_calcSizeXml(const void * p); \
-    UA_Int32 TYPE##_encodeXml(const TYPE *src, UA_ByteString *dst, UA_UInt32 *offset); \
-	UA_Int32 TYPE##_decodeXml(UA_ByteString *src, UA_UInt32 *offset, TYPE *dst); \
-	UA_Int32 TYPE##_encodeXmlToStack(const TYPE *src, XML_Stack *s, XML_Attr *attr); \
-	UA_Int32 TYPE##_decodeXmlFromStack(XML_Stack* s, XML_Attr* attr, TYPE* dst, UA_Boolean isStart);
-
-#define UA_TYPE_METHOD_CALCSIZEXML_NOTIMPL(TYPE) \
-	UA_Int32 TYPE##_calcSizeXml(const void * p) { \
-	return -1; \
- }
-
-#define UA_TYPE_METHOD_ENCODEXML_NOTIMPL(TYPE) \
-    UA_Int32 TYPE##_encodeXml(const TYPE *src, UA_ByteString *dst, UA_UInt32 *offset) { \
-        return UA_ERR_NOT_IMPLEMENTED; \
-	}																	\
-	UA_Int32 TYPE##_encodeXmlToStack(const TYPE *src, XML_Stack *s, XML_Attr *attr) { \
-																					 DBG_VERBOSE(printf(#TYPE "_encodeXML entered with src=%p\n", (void* ) src)); \
-     return UA_ERR_NOT_IMPLEMENTED;\
- }
-
-#define UA_TYPE_METHOD_DECODEXML_NOTIMPL(TYPE) \
-	UA_Int32 TYPE##_decodeXml(UA_ByteString *src, UA_UInt32 *offset, TYPE *dst) { \
-        return UA_ERR_NOT_IMPLEMENTED;									\
-	}																	\
-																		\
- UA_Int32 TYPE##_decodeXmlFromStack(XML_Stack* s, XML_Attr* attr, TYPE* dst, UA_Boolean isStart) { \
-																								  DBG_VERBOSE(printf(#TYPE "_decodeXML entered with dst=%p,isStart=%d\n", (void* ) dst, (_Bool) isStart)); \
-     return UA_ERR_NOT_IMPLEMENTED; \
- }
-
-#define UA_TYPE_DECODEXML_FROM_BYTESTRING(TYPE) \
-	UA_Int32 TYPE##_decodeXml(UA_ByteString *src, UA_UInt32 *offset, TYPE *dst) { \
-	/* // Init Stack here \
-	UA_Stack *stack; \
-	UA_Attr *attr; \
-	TYPE##decodeXmlFromStack(stack, attr, dst, UA_TRUE); \
-	*/ \
-	return UA_ERR_NOT_IMPLEMENTED; \
-} 
-
-UA_TYPE_XML_ENCODING(UA_Boolean)
-UA_TYPE_XML_ENCODING(UA_SByte)
-UA_TYPE_XML_ENCODING(UA_Byte)
-UA_TYPE_XML_ENCODING(UA_Int16)
-UA_TYPE_XML_ENCODING(UA_UInt16)
-UA_TYPE_XML_ENCODING(UA_Int32)
-UA_TYPE_XML_ENCODING(UA_UInt32)
-UA_TYPE_XML_ENCODING(UA_Int64)
-UA_TYPE_XML_ENCODING(UA_UInt64)
-UA_TYPE_XML_ENCODING(UA_Float)
-UA_TYPE_XML_ENCODING(UA_Double)
-UA_TYPE_XML_ENCODING(UA_String)
-UA_TYPE_XML_ENCODING(UA_DateTime)
-UA_TYPE_XML_ENCODING(UA_Guid)
-UA_TYPE_XML_ENCODING(UA_ByteString)
-UA_TYPE_XML_ENCODING(UA_XmlElement)
-UA_TYPE_XML_ENCODING(UA_NodeId)
-UA_TYPE_XML_ENCODING(UA_ExpandedNodeId)
-UA_TYPE_XML_ENCODING(UA_StatusCode)
-UA_TYPE_XML_ENCODING(UA_QualifiedName)
-UA_TYPE_XML_ENCODING(UA_LocalizedText)
-UA_TYPE_XML_ENCODING(UA_ExtensionObject)
-UA_TYPE_XML_ENCODING(UA_DataValue)
-UA_TYPE_XML_ENCODING(UA_Variant)
-UA_TYPE_XML_ENCODING(UA_DiagnosticInfo)
-
-/* Not built-in types */
-UA_TYPE_XML_ENCODING(UA_InvalidType)
-
-#endif /* UA_TYPES_ENCODING_XML_H_ */

+ 0 - 980
src/ongoing/ua_xml.c

@@ -1,980 +0,0 @@
-#include "ua_types_generated.h"
-#include "ua_types_encoding_xml.h"
-#include "ua_xml.h"
-#include "ua_namespace_xml.h"
-#include <fcntl.h> // open, O_RDONLY
-
-UA_Int32 UA_TypedArray_init(UA_TypedArray *p) {
-	p->size     = -1;
-	p->vt       = &UA_.types[UA_INVALIDTYPE];
-	p->elements = UA_NULL;
-	return UA_SUCCESS;
-}
-
-UA_Int32 UA_TypedArray_new(UA_TypedArray **p) {
-	UA_alloc((void ** )p, sizeof(UA_TypedArray));
-	UA_TypedArray_init(*p);
-	return UA_SUCCESS;
-}
-
-UA_Int32 UA_TypedArray_setType(UA_TypedArray *p, UA_Int32 type) {
-	UA_Int32 retval = UA_ERR_INVALID_VALUE;
-	if(type >= UA_BOOLEAN && type <= UA_INVALIDTYPE) {
-		p->vt  = &UA_.types[type];
-		retval = UA_SUCCESS;
-	}
-	return retval;
-}
-
-// FIXME: We might want to have these classes and their methods
-// defined in opcua.h via generate_builtin and the plugin-concept
-// or in ua_basictypes.c
-
-UA_Int32 UA_NodeSetAlias_init(UA_NodeSetAlias *p) {
-	UA_String_init(&(p->alias));
-	UA_String_init(&(p->value));
-	return UA_SUCCESS;
-}
-
-UA_Int32 UA_NodeSetAlias_new(UA_NodeSetAlias **p) {
-	UA_alloc((void ** )p, sizeof(UA_NodeSetAlias));
-	UA_NodeSetAlias_init(*p);
-	return UA_SUCCESS;
-}
-
-UA_Int32 UA_NodeSetAliases_init(UA_NodeSetAliases *p) {
-	p->size    = -1;
-	p->aliases = UA_NULL;
-	return UA_SUCCESS;
-}
-
-UA_Int32 UA_NodeSetAliases_new(UA_NodeSetAliases **p) {
-	UA_alloc((void ** )p, sizeof(UA_NodeSetAliases));
-	UA_NodeSetAliases_init(*p);
-	return UA_SUCCESS;
-}
-
-UA_Int32 UA_NodeSetAliases_println(cstring label, UA_NodeSetAliases *p) {
-	UA_Int32 i;
-	for(i = 0;i < p->size;i++) {
-		UA_NodeSetAlias *a = p->aliases[i];
-		printf("%s{addr=%p", label, (void *)a);
-		if(a)
-			printf(",alias='%.*s', value='%.*s'", a->alias.length, a->alias.data, a->value.length, a->value.data);
-		printf("}\n");
-	}
-	return UA_SUCCESS;
-}
-
-UA_Int32 UA_NodeSet_init(UA_NodeSet *p, UA_UInt32 nsid) {
-	Namespace_new(&p->ns, nsid);
-	p->aliases.size    = -1;
-	p->aliases.aliases = UA_NULL;
-	return UA_SUCCESS;
-}
-
-UA_Int32 UA_NodeSet_new(UA_NodeSet **p, UA_UInt32 nsid) {
-	UA_alloc((void ** )p, sizeof(UA_NodeSet));
-	UA_NodeSet_init(*p, nsid);
-	return UA_SUCCESS;
-}
-
-UA_Int32 _UA_NodeId_copycstring(cstring src, UA_NodeId* dst, UA_NodeSetAliases* aliases) {
-	UA_Int32 retval = UA_SUCCESS;
-	if (src != UA_NULL && dst != UA_NULL ) {
-		if (src[0] == 'i' && src[1] == '=') { // namespace zero numeric identifier
-			dst->identifier.numeric = atoi(&src[2]);
-		} else if (src[0] == 'n' && src[1] == 's' && src[2] == '=') { // namespace
-			dst->namespaceIndex = atoi(&src[3]);
-			src = strchr(&src[3],';');
-			if (src != UA_NULL)
-				retval = _UA_NodeId_copycstring(src+1,dst,aliases);  // +1 to start beyond ;
-			else
-				retval = UA_ERR_INVALID_VALUE;
-		} else if (aliases != UA_NULL ) { // try for aliases
-			UA_Int32 i;
-			for (i = 0; i < aliases->size && dst->identifier.numeric == 0; ++i) {
-				if (0 == strncmp((char const*) src, (char const*) aliases->aliases[i]->alias.data,
-								aliases->aliases[i]->alias.length)) {
-					_UA_NodeId_copycstring((cstring)aliases->aliases[i]->alias.data,dst,UA_NULL); // substitute text of alias
-				}
-			}
-		} else {
-			retval = UA_ERR_NOT_IMPLEMENTED;
-		}
-	} else {
-		retval = UA_ERR_INVALID_VALUE;
-	}
-	DBG_VERBOSE(printf("UA_NodeId_copycstring src=%s,id=%d\n", src, dst->identifier.numeric));
-	return retval;
-}
-
-UA_Int32 UA_NodeId_copycstring(cstring src, UA_NodeId* dst, UA_NodeSetAliases* aliases) {
-	dst->identifierType = UA_NODEIDTYPE_NUMERIC;
-	dst->namespaceIndex = 0;
-	dst->identifier.numeric = 0;
-	return _UA_NodeId_copycstring(src,dst,aliases);
-}
-
-UA_Int32 UA_ReferenceNode_println(cstring label, UA_ReferenceNode *a) {
-	printf("%s{referenceType=%d, target=%d, isInverse=%d}\n",
-	       label,
-	       a->referenceTypeId.identifier.numeric,
-	       a->targetId.nodeId.identifier.numeric,
-	       a->isInverse);
-	return UA_SUCCESS;
-}
-
-UA_Int32 UA_ExpandedNodeId_copycstring(cstring src, UA_ExpandedNodeId *dst, UA_NodeSetAliases *aliases) {
-	dst->nodeId.namespaceIndex     = 0;
-	dst->nodeId.identifierType     = UA_NODEIDTYPE_NUMERIC;
-	dst->nodeId.identifier.numeric = 0;
-	UA_NodeId_copycstring(src, &(dst->nodeId), aliases);
-	DBG_VERBOSE(printf("UA_ExpandedNodeId_copycstring src=%s,id=%d\n", src, dst->nodeId.identifier.numeric));
-	return UA_SUCCESS;
-}
-
-void XML_Stack_init(XML_Stack* p, UA_UInt32 nsid, cstring name) {
-	unsigned int i, j;
-	p->depth = 0;
-	for(i = 0;i < XML_STACK_MAX_DEPTH;i++) {
-		p->parent[i].name = UA_NULL;
-		p->parent[i].len  = 0;
-		p->parent[i].activeChild   = -1;
-		p->parent[i].textAttrib    = UA_NULL;
-		p->parent[i].textAttribIdx = -1;
-		/* p->parent[i].totalGatherLength = -1; */
-		/* UA_list_init(&(p->parent[i].textGatherList)); */
-		for (j = 0; j < XML_STACK_MAX_CHILDREN; j++) {
-			p->parent[i].children[j].name = UA_NULL;
-			p->parent[i].children[j].length         = -1;
-			p->parent[i].children[j].elementHandler = UA_NULL;
-			p->parent[i].children[j].type           = UA_INVALIDTYPE;
-			p->parent[i].children[j].obj = UA_NULL;
-		}
-	}
-	/* p->nsid = nsid; */
-	p->parent[0].name = name;
-}
-
-char path_buffer[1024];
-char *XML_Stack_path(XML_Stack *s) {
-	UA_Int32 i;
-	char    *p = &path_buffer[0];
-	for(i = 0;i <= s->depth;i++) {
-		strcpy(p, s->parent[i].name);
-		p += strlen(s->parent[i].name);
-		*p = '/';
-		p++;
-	}
-	*p = 0;
-	return &path_buffer[0];
-}
-
-void XML_Stack_print(XML_Stack *s) {
-	printf("%s", XML_Stack_path(s));
-}
-
-// FIXME: we might want to calculate textAttribIdx from a string and the information given on the stack
-void XML_Stack_handleTextAsElementOf(XML_Stack *p, cstring textAttrib, unsigned int textAttribIdx) {
-	p->parent[p->depth].textAttrib    = textAttrib;
-	p->parent[p->depth].textAttribIdx = textAttribIdx;
-}
-
-void XML_Stack_addChildHandler(XML_Stack *p, cstring name, UA_Int32 length, XML_decoder handler, UA_Int32 type,
-                               void *dst) {
-	unsigned int len = p->parent[p->depth].len;
-	p->parent[p->depth].children[len].name = name;
-	p->parent[p->depth].children[len].length         = length;
-	p->parent[p->depth].children[len].elementHandler = handler;
-	p->parent[p->depth].children[len].type           = type;
-	p->parent[p->depth].children[len].obj = dst;
-	p->parent[p->depth].len++;
-}
-
-UA_Int32 UA_Text_decodeXmlFromStack(XML_Stack *s, XML_Attr *attr, UA_Byte **dst, _Bool isStart) {
-	DBG_VERBOSE(printf("UA_String entered with dst=%p,isStart=%d\n", (void * )dst, isStart));
-	UA_UInt32 i;
-	if(isStart) {
-		if(dst == UA_NULL) {
-			UA_alloc((void **)&dst, sizeof(void *));
-			s->parent[s->depth - 1].children[s->parent[s->depth - 1].activeChild].obj = (void *)dst;
-		}
-		// set attributes
-		for(i = 0;attr[i];i += 2) {
-			if(0 == strncmp("Data", attr[i], strlen("Data"))) {
-				char *tmp;
-				UA_alloc((void **)&tmp, strlen(attr[i+1])+1);
-				strncpy(tmp, attr[i+1], strlen(attr[i+1]));
-				tmp[strlen(attr[i+1])] = 0;
-				*dst = (UA_Byte *)tmp;
-			} else
-				printf("UA_Text_decodeXml - Unknown attribute - name=%s, value=%s\n", attr[i], attr[i+1]);
-		}
-	}
-	return UA_SUCCESS;
-}
-
-// Gets generated in ua_types_generated.h and .c. But currently only as stubs
-/* UA_Int32 UA_ReferenceNode_decodeXmlFromStack(XML_Stack* s, XML_Attr* attr, UA_ReferenceNode* dst, _Bool isStart) { */
-/* 	DBG_VERBOSE(printf("UA_ReferenceNode_decodeXML entered with dst=%p,isStart=%d\n", (void* ) dst, isStart)); */
-/* 	if (isStart) { */
-/* 		// create if necessary */
-/* 		if (dst == UA_NULL) { */
-/* 			UA_ReferenceNode_new(&dst); */
-/* 			s->parent[s->depth - 1].children[s->parent[s->depth - 1].activeChild].obj = (void*) dst; */
-/* 		} */
-/* 		// set handlers */
-/* 		s->parent[s->depth].len = 0; */
-/* 		XML_Stack_addChildHandler(s, "ReferenceType", strlen("ReferenceType"),(XML_decoder) UA_NodeId_decodeXmlFromStack, UA_NODEID, &(dst->referenceTypeId)); */
-/* 		XML_Stack_addChildHandler(s, "IsForward", strlen("IsForward"), (XML_decoder) UA_Boolean_decodeXmlFromStack, UA_BOOLEAN, &(dst->isInverse)); */
-/* 		XML_Stack_addChildHandler(s, "Target", strlen("Target"), (XML_decoder) UA_ExpandedNodeId_decodeXmlFromStack, UA_EXPANDEDNODEID, &(dst->targetId)); */
-/* 		XML_Stack_handleTextAsElementOf(s, "NodeId", 2); */
-
-/* 		// set attributes */
-/* 		UA_Int32 i; */
-/* 		for (i = 0; attr[i]; i += 2) { */
-/* 			if (0 == strncmp("ReferenceType", attr[i], strlen("ReferenceType"))) { */
-/* 				UA_NodeId_copycstring(attr[i + 1], &(dst->referenceTypeId), s->aliases); */
-/* 			} else if (0 == strncmp("IsForward", attr[i], strlen("IsForward"))) { */
-/* 				UA_Boolean_copycstring(attr[i + 1], &(dst->isInverse)); */
-/* 				dst->isInverse = !dst->isInverse; */
-/* 			} else if (0 == strncmp("Target", attr[i], strlen("Target"))) { */
-/* 				UA_ExpandedNodeId_copycstring(attr[i + 1], &(dst->targetId), s->aliases); */
-/* 			} else { */
-/* 				DBG_ERR(XML_Stack_print(s));DBG_ERR(printf("%s - unknown attribute\n", attr[i])); */
-/* 			} */
-/* 		} */
-/* 	} */
-/* 	return UA_SUCCESS; */
-/* } */
-
-UA_Int32 UA_TypedArray_decodeXmlFromStack(XML_Stack* s, XML_Attr* attr, UA_TypedArray* dst, _Bool isStart) {
-	UA_Int32 type = s->parent[s->depth - 1].children[s->parent[s->depth - 1].activeChild].type;
-	/* DBG_VERBOSE(printf("UA_TypedArray_decodeXML - entered with dst=%p,isStart=%d,type={%d,%s},name=%s\n", (void* ) dst, isStart,type,UA_[type].name, names)); */
-	if (isStart) {
-		if (dst == UA_NULL) {
-			UA_TypedArray_new(&dst);
-			UA_TypedArray_setType(dst, type);
-			s->parent[s->depth - 1].children[s->parent[s->depth - 1].activeChild].obj = dst;
-		}
-		// need to map from ArrayName to member name
-		// References - Reference
-		// Aliases - Alias
-		// ListOfXX - XX
-		DBG(UA_Int32 length = 0;
-			cstring names  = s->parent[s->depth - 1].children[s->parent[s->depth - 1].activeChild].name;
-			if (0 == strncmp("ListOf",names,strlen("ListOf"))) {
-				names = &names[strlen("ListOf")];
-				length = strlen(names);
-			} else if ( 0 == strncmp("References",names,strlen("References"))){
-				length = strlen(names) - 1;
-			} else if ( 0 == strncmp("Aliases",names,strlen("Aliases"))){
-				length = strlen(names) - 2;
-			}
-			printf("UA_TypedArray_decodeXML - add handler for {%.*s}\n", length, names));
-		// this is problematic as the standard encoding functions in the vtable take a bytestring as the input
-		/* XML_Stack_addChildHandler(s, names, length, (XML_decoder) UA_.types[type].encodings[1].decode, type, UA_NULL); */
-	} else {
-		// sub element is ready, add to array
-		if (dst->size < 0 || dst->size == 0) {
-			dst->size = 1;
-			UA_alloc((void** )&(dst->elements), dst->size * sizeof(void*));
-			DBG(printf("UA_TypedArray_decodeXML - allocate elements:dst=%p, aliases=%p, size=%d\n", (void* )dst, (void* )(dst->elements),dst->size));
-		} else {
-			dst->size++;
-			dst->elements = realloc(dst->elements, dst->size * sizeof(void*));
-			DBG(printf("UA_TypedArray_decodeXML - reallocate elements:dst=%p, aliases=%p, size=%d\n", (void* )dst,(void* )(dst->elements), dst->size));
-		}
-		// index starts with 0, therefore size-1
-		DBG_VERBOSE(printf("UA_TypedArray_decodeXML - assign element[%d], src=%p\n", dst->size - 1, (void* )attr));
-		/* dst->elements[dst->size - 1] = (void*) attr; */
-		DBG_VERBOSE(printf("UA_TypedArray_decodeXML - clear %p\n",(void* ) (s->parent[s->depth - 1].children[s->parent[s->depth - 1].activeChild].obj)));
-		s->parent[s->depth - 1].children[s->parent[s->depth - 1].activeChild].obj = UA_NULL;
-	}
-	return UA_SUCCESS;
-}
-
-// Gets generated in ua_types_generated.h and .c. But currently only as stubs
-/* UA_Int32 UA_DataTypeNode_decodeXmlFromStack(XML_Stack* s, XML_Attr* attr, UA_DataTypeNode* dst, _Bool isStart) { */
-/* 	DBG_VERBOSE(printf("UA_DataTypeNode entered with dst=%p,isStart=%d\n", (void* ) dst, isStart)); */
-/* 	UA_UInt32 i; */
-
-/* 	if (isStart) { */
-/* 		// create a new object if called with UA_NULL */
-/* 		if (dst == UA_NULL) { */
-/* 			UA_DataTypeNode_new(&dst); */
-/* 			s->parent[s->depth - 1].children[s->parent[s->depth - 1].activeChild].obj = (void*) dst; */
-/* 		} */
-
-/* 		s->parent[s->depth].len = 0; */
-/* 		XML_Stack_addChildHandler(s, "DisplayName", strlen("DisplayName"), (XML_decoder) UA_LocalizedText_decodeXmlFromStack, UA_LOCALIZEDTEXT, &(dst->displayName)); */
-/* 		XML_Stack_addChildHandler(s, "Description", strlen("Description"),(XML_decoder) UA_LocalizedText_decodeXmlFromStack, UA_LOCALIZEDTEXT, &(dst->description)); */
-/* 		XML_Stack_addChildHandler(s, "BrowseName", strlen("BrowseName"),(XML_decoder) UA_QualifiedName_decodeXmlFromStack, UA_QUALIFIEDNAME, &(dst->description)); */
-/* 		XML_Stack_addChildHandler(s, "IsAbstract", strlen("IsAbstract"),(XML_decoder) UA_Boolean_decodeXmlFromStack, UA_BOOLEAN, &(dst->description)); */
-/* 		XML_Stack_addChildHandler(s, "References", strlen("References"),(XML_decoder) UA_TypedArray_decodeXmlFromStack, UA_REFERENCENODE, UA_NULL); */
-
-/* 		// set missing default attributes */
-/* 		dst->nodeClass = UA_NODECLASS_DATATYPE; */
-
-/* 		// set attributes */
-/* 		for (i = 0; attr[i]; i += 2) { */
-/* 			if (0 == strncmp("NodeId", attr[i], strlen("NodeId"))) { */
-/* 				UA_NodeId_copycstring(attr[i + 1], &(dst->nodeId), s->aliases); */
-/* 			} else if (0 == strncmp("BrowseName", attr[i], strlen("BrowseName"))) { */
-/* 				UA_String_copycstring(attr[i + 1], &(dst->browseName.name)); */
-/* 				dst->browseName.namespaceIndex = 0; */
-/* 			} else if (0 == strncmp("DisplayName", attr[i], strlen("DisplayName"))) { */
-/* 				UA_String_copycstring(attr[i + 1], &(dst->displayName.text)); */
-/* 				dst->displayName.encodingMask = UA_LOCALIZEDTEXT_ENCODINGMASKTYPE_TEXT; */
-/* 			} else if (0 == strncmp("IsAbstract", attr[i], strlen("IsAbstract"))) { */
-/* 				UA_Boolean_copycstring(attr[i + 1], &(dst->isAbstract)); */
-/* 				dst->displayName.encodingMask = UA_LOCALIZEDTEXT_ENCODINGMASKTYPE_TEXT; */
-/* 			} else if (0 == strncmp("Description", attr[i], strlen("Description"))) { */
-/* 				UA_String_copycstring(attr[i + 1], &(dst->description.text)); */
-/* 				dst->description.encodingMask = UA_LOCALIZEDTEXT_ENCODINGMASKTYPE_TEXT; */
-/* 			} else { */
-/* 				DBG_ERR(XML_Stack_print(s));DBG_ERR(printf("%s - unknown attribute\n", attr[i])); */
-/* 			} */
-/* 		} */
-/* 	} else { */
-/* 		switch (s->parent[s->depth - 1].activeChild) { */
-/* 		case 4: // References */
-/* 			if (attr != UA_NULL) { */
-/* 				UA_TypedArray* array = (UA_TypedArray *) attr; */
-/* 				DBG_VERBOSE(printf("finished aliases: references=%p, size=%d\n",(void*)array,(array==UA_NULL)?-1:array->size)); */
-/* 				dst->referencesSize = array->size; */
-/* 				/\* dst->references = (UA_ReferenceNode**) array->elements; *\/ */
-/* 			} */
-/* 		break; */
-/* 		} */
-/* 	} */
-/* 	return UA_SUCCESS; */
-/* } */
-
-// Gets generated in ua_types_generated.h and .c. But currently only as stubs
-/* UA_Int32 UA_ObjectTypeNode_decodeXmlFromStack(XML_Stack* s, XML_Attr* attr, UA_ObjectTypeNode* dst, _Bool isStart) { */
-/* 	DBG_VERBOSE(printf("UA_DataTypeNode entered with dst=%p,isStart=%d\n", (void* ) dst, isStart)); */
-/* 	UA_UInt32 i; */
-
-/* 	if (isStart) { */
-/* 		// create a new object if called with UA_NULL */
-/* 		if (dst == UA_NULL) { */
-/* 			UA_ObjectTypeNode_new(&dst); */
-/* 			s->parent[s->depth - 1].children[s->parent[s->depth - 1].activeChild].obj = (void*) dst; */
-/* 		} */
-
-/* 		s->parent[s->depth].len = 0; */
-/* 		XML_Stack_addChildHandler(s, "DisplayName", strlen("DisplayName"), (XML_decoder) UA_LocalizedText_decodeXmlFromStack, UA_LOCALIZEDTEXT, &(dst->displayName)); */
-/* 		XML_Stack_addChildHandler(s, "Description", strlen("Description"),(XML_decoder) UA_LocalizedText_decodeXmlFromStack, UA_LOCALIZEDTEXT, &(dst->description)); */
-/* 		XML_Stack_addChildHandler(s, "BrowseName", strlen("BrowseName"),(XML_decoder) UA_QualifiedName_decodeXmlFromStack, UA_QUALIFIEDNAME, &(dst->description)); */
-/* 		XML_Stack_addChildHandler(s, "IsAbstract", strlen("IsAbstract"),(XML_decoder) UA_Boolean_decodeXmlFromStack, UA_BOOLEAN, &(dst->description)); */
-/* 		XML_Stack_addChildHandler(s, "References", strlen("References"),(XML_decoder) UA_TypedArray_decodeXmlFromStack, UA_REFERENCENODE, UA_NULL); */
-
-/* 		// set missing default attributes */
-/* 		dst->nodeClass = UA_NODECLASS_OBJECTTYPE; */
-
-/* 		// set attributes */
-/* 		for (i = 0; attr[i]; i += 2) { */
-/* 			if (0 == strncmp("NodeId", attr[i], strlen("NodeId"))) { */
-/* 				UA_NodeId_copycstring(attr[i + 1], &(dst->nodeId), s->aliases); */
-/* 			} else if (0 == strncmp("BrowseName", attr[i], strlen("BrowseName"))) { */
-/* 				UA_String_copycstring(attr[i + 1], &(dst->browseName.name)); */
-/* 				dst->browseName.namespaceIndex = 0; */
-/* 			} else if (0 == strncmp("DisplayName", attr[i], strlen("DisplayName"))) { */
-/* 				UA_String_copycstring(attr[i + 1], &(dst->displayName.text)); */
-/* 				dst->displayName.encodingMask = UA_LOCALIZEDTEXT_ENCODINGMASKTYPE_TEXT; */
-/* 			} else if (0 == strncmp("IsAbstract", attr[i], strlen("IsAbstract"))) { */
-/* 				UA_Boolean_copycstring(attr[i + 1], &(dst->isAbstract)); */
-/* 				dst->displayName.encodingMask = UA_LOCALIZEDTEXT_ENCODINGMASKTYPE_TEXT; */
-/* 			} else if (0 == strncmp("Description", attr[i], strlen("Description"))) { */
-/* 				UA_String_copycstring(attr[i + 1], &(dst->description.text)); */
-/* 				dst->description.encodingMask = UA_LOCALIZEDTEXT_ENCODINGMASKTYPE_TEXT; */
-/* 			} else { */
-/* 				DBG_ERR(XML_Stack_print(s));DBG_ERR(printf("%s - unknown attribute\n", attr[i])); */
-/* 			} */
-/* 		} */
-/* 	} else { */
-/* 		switch (s->parent[s->depth - 1].activeChild) { */
-/* 		case 4: // References */
-/* 			if (attr != UA_NULL) { */
-/* 				UA_TypedArray* array = (UA_TypedArray *) attr; */
-/* 				DBG_VERBOSE(printf("finished aliases: references=%p, size=%d\n",(void*)array,(array==UA_NULL)?-1:array->size)); */
-/* 				dst->referencesSize = array->size; */
-/* 				/\* dst->references = (UA_ReferenceNode**) array->elements; *\/ */
-/* 			} */
-/* 		break; */
-/* 		} */
-/* 	} */
-/* 	return UA_SUCCESS; */
-/* } */
-
-// Gets generated in ua_types_generated.h and .c. But currently only as stubs
-/* UA_Int32 UA_VariableTypeNode_decodeXmlFromStack(XML_Stack* s, XML_Attr* attr, UA_VariableTypeNode* dst, _Bool isStart) { */
-/* 	DBG_VERBOSE(printf("UA_DataTypeNode entered with dst=%p,isStart=%d\n", (void* ) dst, isStart)); */
-/* 	UA_UInt32 i; */
-
-/* 	if (isStart) { */
-/* 		// create a new object if called with UA_NULL */
-/* 		if (dst == UA_NULL) { */
-/* 			UA_VariableTypeNode_new(&dst); */
-/* 			s->parent[s->depth - 1].children[s->parent[s->depth - 1].activeChild].obj = (void*) dst; */
-/* 		} */
-
-/* 		s->parent[s->depth].len = 0; */
-/* 		XML_Stack_addChildHandler(s, "DisplayName", strlen("DisplayName"), (XML_decoder) UA_LocalizedText_decodeXmlFromStack, UA_LOCALIZEDTEXT, &(dst->displayName)); */
-/* 		XML_Stack_addChildHandler(s, "Description", strlen("Description"),(XML_decoder) UA_LocalizedText_decodeXmlFromStack, UA_LOCALIZEDTEXT, &(dst->description)); */
-/* 		XML_Stack_addChildHandler(s, "BrowseName", strlen("BrowseName"),(XML_decoder) UA_QualifiedName_decodeXmlFromStack, UA_QUALIFIEDNAME, &(dst->description)); */
-/* 		XML_Stack_addChildHandler(s, "IsAbstract", strlen("IsAbstract"),(XML_decoder) UA_Boolean_decodeXmlFromStack, UA_BOOLEAN, &(dst->description)); */
-/* 		XML_Stack_addChildHandler(s, "References", strlen("References"),(XML_decoder) UA_TypedArray_decodeXmlFromStack, UA_REFERENCENODE, UA_NULL); */
-
-/* 		// set missing default attributes */
-/* 		dst->nodeClass = UA_NODECLASS_VARIABLETYPE; */
-
-/* 		// set attributes */
-/* 		for (i = 0; attr[i]; i += 2) { */
-/* 			if (0 == strncmp("NodeId", attr[i], strlen("NodeId"))) { */
-/* 				UA_NodeId_copycstring(attr[i + 1], &(dst->nodeId), s->aliases); */
-/* 			} else if (0 == strncmp("BrowseName", attr[i], strlen("BrowseName"))) { */
-/* 				UA_String_copycstring(attr[i + 1], &(dst->browseName.name)); */
-/* 				dst->browseName.namespaceIndex = 0; */
-/* 			} else if (0 == strncmp("DisplayName", attr[i], strlen("DisplayName"))) { */
-/* 				UA_String_copycstring(attr[i + 1], &(dst->displayName.text)); */
-/* 				dst->displayName.encodingMask = UA_LOCALIZEDTEXT_ENCODINGMASKTYPE_TEXT; */
-/* 			} else if (0 == strncmp("IsAbstract", attr[i], strlen("IsAbstract"))) { */
-/* 				UA_Boolean_copycstring(attr[i + 1], &(dst->isAbstract)); */
-/* 				dst->displayName.encodingMask = UA_LOCALIZEDTEXT_ENCODINGMASKTYPE_TEXT; */
-/* 			} else if (0 == strncmp("Description", attr[i], strlen("Description"))) { */
-/* 				UA_String_copycstring(attr[i + 1], &(dst->description.text)); */
-/* 				dst->description.encodingMask = UA_LOCALIZEDTEXT_ENCODINGMASKTYPE_TEXT; */
-/* 			} else { */
-/* 				DBG_ERR(XML_Stack_print(s));DBG_ERR(printf("%s - unknown attribute\n", attr[i])); */
-/* 			} */
-/* 		} */
-/* 	} else { */
-/* 		switch (s->parent[s->depth - 1].activeChild) { */
-/* 		case 4: // References */
-/* 			if (attr != UA_NULL) { */
-/* 				UA_TypedArray* array = (UA_TypedArray *) attr; */
-/* 				DBG_VERBOSE(printf("finished aliases: references=%p, size=%d\n",(void*)array,(array==UA_NULL)?-1:array->size)); */
-/* 				dst->referencesSize = array->size; */
-/* 				/\* dst->references = (UA_ReferenceNode**) array->elements; *\/ */
-/* 			} */
-/* 		break; */
-/* 		} */
-/* 	} */
-/* 	return UA_SUCCESS; */
-/* } */
-
-// Gets generated in ua_types_generated.h and .c. But currently only as stubs
-/* UA_Int32 UA_ObjectNode_decodeXmlFromStack(XML_Stack* s, XML_Attr* attr, UA_ObjectNode* dst, _Bool isStart) { */
-/* 	DBG_VERBOSE(printf("UA_ObjectNode entered with dst=%p,isStart=%d\n", (void* ) dst, isStart)); */
-/* 	UA_UInt32 i; */
-
-/* 	if (isStart) { */
-/* 		// create a new object if called with UA_NULL */
-/* 		if (dst == UA_NULL) { */
-/* 			UA_ObjectNode_new(&dst); */
-/* 			s->parent[s->depth - 1].children[s->parent[s->depth - 1].activeChild].obj = (void*) dst; */
-/* 		} */
-
-/* 		XML_Stack_addChildHandler(s, "DisplayName", strlen("DisplayName"), (XML_decoder) UA_LocalizedText_decodeXmlFromStack, UA_LOCALIZEDTEXT, &(dst->displayName)); */
-/* 		XML_Stack_addChildHandler(s, "Description", strlen("Description"), (XML_decoder) UA_LocalizedText_decodeXmlFromStack, UA_LOCALIZEDTEXT, &(dst->description)); */
-/* 		XML_Stack_addChildHandler(s, "BrowseName", strlen("BrowseName"), (XML_decoder) UA_QualifiedName_decodeXmlFromStack, UA_QUALIFIEDNAME, &(dst->browseName)); */
-/* 		XML_Stack_addChildHandler(s, "SymbolicName", strlen("SymbolicName"), (XML_decoder) UA_QualifiedName_decodeXmlFromStack, UA_QUALIFIEDNAME,&(dst->browseName)); */
-/* 		XML_Stack_addChildHandler(s, "References", strlen("References"), (XML_decoder) UA_TypedArray_decodeXmlFromStack, UA_REFERENCENODE, UA_NULL); */
-
-/* 		// set missing default attributes */
-/* 		dst->nodeClass = UA_NODECLASS_OBJECT; */
-
-/* 		// set attributes */
-/* 		for (i = 0; attr[i]; i += 2) { */
-/* 			if (0 == strncmp("NodeId", attr[i], strlen("NodeId"))) { */
-/* 				UA_NodeId_copycstring(attr[i + 1], &(dst->nodeId), s->aliases); */
-/* 			} else if (0 == strncmp("SymbolicName", attr[i], strlen("SymbolicName"))) { */
-/* 				UA_String_copycstring(attr[i + 1], &(dst->browseName.name)); */
-/* 				dst->browseName.namespaceIndex = 0; */
-/* 			} else if (0 == strncmp("BrowseName", attr[i], strlen("BrowseName"))) { */
-/* 				UA_String_copycstring(attr[i + 1], &(dst->browseName.name)); */
-/* 				dst->browseName.namespaceIndex = 0; */
-/* 			} else if (0 == strncmp("DisplayName", attr[i], strlen("DisplayName"))) { */
-/* 				UA_String_copycstring(attr[i + 1], &(dst->displayName.text)); */
-/* 				dst->displayName.encodingMask = UA_LOCALIZEDTEXT_ENCODINGMASKTYPE_TEXT; */
-/* 			} else if (0 == strncmp("Description", attr[i], strlen("Description"))) { */
-/* 				UA_String_copycstring(attr[i + 1], &(dst->description.text)); */
-/* 				dst->description.encodingMask = UA_LOCALIZEDTEXT_ENCODINGMASKTYPE_TEXT; */
-/* 			} else { */
-/* 				DBG_ERR(XML_Stack_print(s));DBG_ERR(printf("%s - unknown attribute\n", attr[i])); */
-/* 			} */
-/* 		} */
-/* 	} else { */
-/* 		if (s->parent[s->depth - 1].activeChild == 4 && attr != UA_NULL ) { // References Array */
-/* 			UA_TypedArray* array = (UA_TypedArray*) attr; */
-/* 			DBG(printf("UA_ObjectNode_decodeXML finished references: references=%p, size=%d\n",(void*)array,(array==UA_NULL)?-1:array->size)); */
-/* 			dst->referencesSize = array->size; */
-/* 			/\* dst->references = (UA_ReferenceNode**) array->elements; *\/ */
-/* 			s->parent[s->depth - 1].children[s->parent[s->depth - 1].activeChild].obj = UA_NULL; */
-/* 		} */
-/* 	} */
-/* 	return UA_SUCCESS; */
-/* } */
-
-
-// Gets generated in ua_types_generated.h and .c. But currently only as stubs
-/* UA_Int32 UA_Variant_decodeXmlFromStack(XML_Stack* s, XML_Attr* attr, UA_Variant* dst, _Bool isStart) { */
-/* 	DBG_VERBOSE(printf("UA_Variant entered with dst=%p,isStart=%d\n", (void* ) dst, isStart)); */
-/* 	UA_UInt32 i; */
-
-/* 	if (isStart) { */
-/* 		// create a new object if called with UA_NULL */
-/* 		if (dst == UA_NULL) { */
-/* 			UA_Variant_new(&dst); */
-/* 			s->parent[s->depth - 1].children[s->parent[s->depth - 1].activeChild].obj = (void*) dst; */
-/* 		} */
-
-/* 		s->parent[s->depth].len = 0; */
-/* 		XML_Stack_addChildHandler(s, "ListOfExtensionObject", strlen("ListOfExtensionObject"), (XML_decoder) UA_TypedArray_decodeXmlFromStack, UA_EXTENSIONOBJECT, UA_NULL); */
-/* 		XML_Stack_addChildHandler(s, "ListOfLocalizedText", strlen("ListOfLocalizedText"), (XML_decoder) UA_TypedArray_decodeXmlFromStack, UA_LOCALIZEDTEXT, UA_NULL); */
-
-/* 		// set attributes */
-/* 		for (i = 0; attr[i]; i += 2) { */
-/* 			{ */
-/* 				DBG_ERR(XML_Stack_print(s)); */
-/* 				DBG_ERR(printf("%s - unknown attribute\n", attr[i])); */
-/* 			} */
-/* 		} */
-/* 	} else { */
-/* 		if (s->parent[s->depth - 1].activeChild == 0 && attr != UA_NULL ) { // ExtensionObject */
-/* 			UA_TypedArray* array = (UA_TypedArray*) attr; */
-/* 			DBG_VERBOSE(printf("UA_Variant_decodeXML - finished array: references=%p, size=%d\n",(void*)array,(array==UA_NULL)?-1:array->size)); */
-/* 			dst->arrayLength = array->size; */
-/* 			dst->data = array->elements; */
-/* 			dst->vt = &UA_.types[UA_EXTENSIONOBJECT]; */
-/* 			dst->encodingMask = UA_EXTENSIONOBJECT_NS0 & UA_VARIANT_ENCODINGMASKTYPE_ARRAY; */
-/* 			s->parent[s->depth - 1].children[s->parent[s->depth - 1].activeChild].obj = UA_NULL; */
-/* 		} else if (s->parent[s->depth - 1].activeChild == 1 && attr != UA_NULL ) { // LocalizedText */
-/* 			UA_TypedArray* array = (UA_TypedArray*) attr; */
-/* 			DBG_VERBOSE(printf("UA_Variant_decodeXML - finished array: references=%p, size=%d\n",(void*)array,(array==UA_NULL)?-1:array->size)); */
-/* 			dst->arrayLength = array->size; */
-/* 			dst->data = array->elements; */
-/* 			dst->vt = &UA_.types[UA_LOCALIZEDTEXT]; */
-/* 			dst->encodingMask = UA_LOCALIZEDTEXT_NS0 & UA_VARIANT_ENCODINGMASKTYPE_ARRAY; */
-/* 			s->parent[s->depth - 1].children[s->parent[s->depth - 1].activeChild].obj = UA_NULL; */
-/* 		} */
-/* 	} */
-/* 	return UA_SUCCESS; */
-/* } */
-
-// Gets generated in ua_types_generated.h and .c. But currently only as stubs
-/* UA_Int32 UA_ExtensionObject_decodeXmlFromStack(XML_Stack* s, XML_Attr* attr, UA_ExtensionObject* dst, _Bool isStart) { */
-/* 	DBG_VERBOSE(printf("UA_ExtensionObject entered with dst=%p,isStart=%d\n", (void* ) dst, isStart)); */
-/* 	UA_UInt32 i; */
-
-/* 	if (isStart) { */
-/* 		// create a new object if called with UA_NULL */
-/* 		if (dst == UA_NULL) { */
-/* 			UA_ExtensionObject_new(&dst); */
-/* 			s->parent[s->depth - 1].children[s->parent[s->depth - 1].activeChild].obj = (void*) dst; */
-/* 		} */
-
-/* 		s->parent[s->depth].len = 0; */
-/* 		XML_Stack_addChildHandler(s, "TypeId", strlen("TypeId"), (XML_decoder) UA_NodeId_decodeXmlFromStack, UA_NODEID, &(dst->typeId)); */
-/* 		// XML_Stack_addChildHandler(s, "Body", strlen("Body"), (XML_decoder) UA_Body_decodeXML, UA_LOCALIZEDTEXT, UA_NULL); */
-
-/* 		// set attributes */
-/* 		for (i = 0; attr[i]; i += 2) { */
-/* 			{ */
-/* 				DBG_ERR(XML_Stack_print(s)); */
-/* 				DBG_ERR(printf("%s - unknown attribute\n", attr[i])); */
-/* 			} */
-/* 		} */
-/* 	} */
-/* 	return UA_SUCCESS; */
-/* } */
-
-_Bool UA_NodeId_isBuiltinType(UA_NodeId* nodeid) {
-	return (nodeid->namespaceIndex == 0 && nodeid->identifier.numeric >= UA_BOOLEAN_NS0 &&
-			nodeid->identifier.numeric <= UA_DIAGNOSTICINFO_NS0
-			);
-}
-
-// Gets generated in ua_types_generated.h and .c. But currently only as stubs
-/* UA_Int32 UA_VariableNode_decodeXmlFromStack(XML_Stack* s, XML_Attr* attr, UA_VariableNode* dst, _Bool isStart) { */
-/* 	DBG_VERBOSE(printf("UA_VariableNode entered with dst=%p,isStart=%d\n", (void* ) dst, isStart)); */
-/* 	UA_UInt32 i; */
-
-/* 	if (isStart) { */
-/* 		// create a new object if called with UA_NULL */
-/* 		if (dst == UA_NULL) { */
-/* 			UA_VariableNode_new(&dst); */
-/* 			s->parent[s->depth - 1].children[s->parent[s->depth - 1].activeChild].obj = (void*) dst; */
-/* 		} */
-
-/* 		s->parent[s->depth].len = 0; */
-/* 		XML_Stack_addChildHandler(s, "DisplayName", strlen("DisplayName"), (XML_decoder) UA_LocalizedText_decodeXmlFromStack, UA_LOCALIZEDTEXT, */
-/* 				&(dst->displayName)); */
-/* 		XML_Stack_addChildHandler(s, "Description", strlen("Description"),(XML_decoder) UA_LocalizedText_decodeXmlFromStack, UA_LOCALIZEDTEXT, */
-/* 				&(dst->description)); */
-/* 		XML_Stack_addChildHandler(s, "DataType", strlen("DataType"),(XML_decoder) UA_NodeId_decodeXmlFromStack, UA_NODEID, &(dst->dataType)); */
-/* 		XML_Stack_addChildHandler(s, "ValueRank", strlen("ValueRank"),(XML_decoder) UA_Int32_decodeXmlFromStack, UA_INT32, &(dst->valueRank)); */
-/* 		XML_Stack_addChildHandler(s, "Value", strlen("Value"),(XML_decoder) UA_Variant_decodeXmlFromStack, UA_VARIANT, &(dst->value)); */
-/* 		XML_Stack_addChildHandler(s, "References", strlen("References"), (XML_decoder) UA_TypedArray_decodeXmlFromStack, UA_REFERENCENODE, */
-/* 		UA_NULL); */
-
-/* 		// set missing default attributes */
-/* 		dst->nodeClass = UA_NODECLASS_VARIABLE; */
-
-/* 		// set attributes */
-/* 		for (i = 0; attr[i]; i += 2) { */
-/* 			if (0 == strncmp("NodeId", attr[i], strlen("NodeId"))) { */
-/* 				UA_NodeId_copycstring(attr[i + 1], &(dst->nodeId), s->aliases); */
-/* 			} else if (0 == strncmp("DataType", attr[i], strlen("DataType"))) { */
-/* 				UA_NodeId_copycstring(attr[i + 1], &(dst->dataType), s->aliases); */
-/* 				if (UA_NodeId_isBuiltinType(&(dst->dataType))) { */
-/* 					dst->value.encodingMask = dst->dataType.identifier.numeric; */
-/* 					dst->value.vt = &UA_.types[UA_ns0ToVTableIndex(&dst->dataType)]; */
-/* 				} else { */
-/* 					dst->value.encodingMask = UA_EXTENSIONOBJECT_NS0; */
-/* 					dst->value.vt = &UA_.types[UA_EXTENSIONOBJECT]; */
-/* 				} */
-/* 			} else if (0 == strncmp("ValueRank", attr[i], strlen("ValueRank"))) { */
-/* 				dst->valueRank = atoi(attr[i + 1]); */
-/* 			} else if (0 == strncmp("ParentNodeId", attr[i], strlen("ParentNodeId"))) { */
-/* 				// FIXME: this seems to be redundant to the hasProperty-reference */
-/* 			} else if (0 == strncmp("BrowseName", attr[i], strlen("BrowseName"))) { */
-/* 				UA_String_copycstring(attr[i + 1], &(dst->browseName.name)); */
-/* 				dst->browseName.namespaceIndex = 0; */
-/* 			} else if (0 == strncmp("DisplayName", attr[i], strlen("DisplayName"))) { */
-/* 				UA_String_copycstring(attr[i + 1], &(dst->displayName.text)); */
-/* 				dst->displayName.encodingMask = UA_LOCALIZEDTEXT_ENCODINGMASKTYPE_TEXT; */
-/* 			} else if (0 == strncmp("Description", attr[i], strlen("Description"))) { */
-/* 				UA_String_copycstring(attr[i + 1], &(dst->description.text)); */
-/* 				dst->description.encodingMask = UA_LOCALIZEDTEXT_ENCODINGMASKTYPE_TEXT; */
-/* 			} else { */
-/* 				DBG_ERR(XML_Stack_print(s)); */
-/* 				DBG_ERR(printf("%s - unknown attribute\n", attr[i])); */
-/* 			} */
-/* 		} */
-/* 	} else { */
-/* 		if (s->parent[s->depth - 1].activeChild == 5 && attr != UA_NULL) { // References */
-/* 			UA_TypedArray* array = (UA_TypedArray*) attr; */
-/* 			DBG(printf("UA_VariableNode_decodeXML - finished references=%p, size=%d\n",(void*)array,(array==UA_NULL)?-1:array->size)); */
-/* 			dst->referencesSize = array->size; */
-/* 			/\* dst->references = (UA_ReferenceNode**) array->elements; *\/ */
-/* 			s->parent[s->depth - 1].children[s->parent[s->depth - 1].activeChild].obj = UA_NULL; */
-/* 		} */
-/* 	} */
-/* 	return UA_SUCCESS; */
-/* } */
-
-#ifdef DEBUG
-void print_node(UA_Node const * node) {
-	if (node != UA_NULL) {
-		UA_NodeId_printf("node.nodeId=", &(node->nodeId));
-		printf("\t.browseName='%.*s'\n", node->browseName.name.length, node->browseName.name.data);
-		printf("\t.displayName='%.*s'\n", node->displayName.text.length, node->displayName.text.data);
-		printf("\t.description='%.*s%s'\n", node->description.text.length > 40 ? 40 : node->description.text.length,
-		       node->description.text.data, node->description.text.length > 40 ? "..." : "");
-		printf("\t.nodeClass=%d\n", node->nodeClass);
-		printf("\t.writeMask=%d\n", node->writeMask);
-		printf("\t.userWriteMask=%d\n", node->userWriteMask);
-		printf("\t.references.size=%d\n", node->referencesSize);
-		UA_Int32 i;
-		for(i = 0;i < node->referencesSize;i++) {
-			printf("\t\t.element[%d]", i);
-			UA_ReferenceNode_println("=", &node->references[i]);
-		}
-		switch(node->nodeClass) {
-		case UA_NODECLASS_VARIABLE:
-		{
-			UA_VariableNode const *p = (UA_VariableNode const *)node;
-			printf("\t----- UA_VariableNode ----- \n");
-			UA_NodeId_printf("\t.dataType=", &(p->dataType));
-			printf("\t.valueRank=%d\n", p->valueRank);
-			printf("\t.accessLevel=%d\n", p->accessLevel);
-			printf("\t.userAccessLevel=%d\n", p->userAccessLevel);
-			printf("\t.arrayDimensionsSize=%d\n", p->arrayDimensionsSize);
-			printf("\t.minimumSamplingInterval=%f\n", p->minimumSamplingInterval);
-			printf("\t.historizing=%d\n", p->historizing);
-			printf("\t----- UA_Variant ----- \n");
-			printf("\t.value.type.name=%s\n", p->value.vt->name);
-			printf("\t.value.array.length=%d\n", p->value.arrayLength);
-			UA_Int32 i;
-			UA_Byte *data = (UA_Byte *)p->value.data;
-			for(i = 0;i < p->value.arrayLength || (p->value.arrayLength == -1 && i == 0);++i) {
-				UA_Byte *currentData = data + (i*p->value.vt->memSize);
-				printf("\t.value.array.element[%d]=%p", i, currentData);
-				switch(p->value.vt->typeId.identifier.numeric) {
-				case UA_LOCALIZEDTEXT_NS0:
-				{
-					if(p->value.data != UA_NULL) {
-						UA_LocalizedText *ltp = (UA_LocalizedText *)currentData;
-						printf(",locale={%d,{%.*s}},text={%d,{%.*s}}", ltp->locale.length,
-							   ltp->locale.length, ltp->locale.data, ltp->text.length,
-							   ltp->text.length, ltp->text.data);
-					}
-				}
-				break;
-
-				case UA_EXTENSIONOBJECT_NS0:
-				{
-					if(p->value.data != UA_NULL) {
-						UA_ExtensionObject *eo = (UA_ExtensionObject *)currentData;
-						if(eo == UA_NULL)
-							printf(",(null)");
-						else
-							printf(",enc=%d,typeId={i=%d}", eo->encoding, eo->typeId.identifier.numeric);
-					}
-				}
-				break;
-
-				default:
-					break;
-				}
-				printf("\n");
-			}
-		}
-		break;
-
-		// case UA_NODECLASS_DATATYPE:
-		default:
-			break;
-		}
-	}
-}
-#endif
-
-UA_Int32 UA_NodeSetAlias_decodeXmlFromStack(XML_Stack *s, XML_Attr *attr, UA_NodeSetAlias *dst, _Bool isStart) {
-	DBG_VERBOSE(printf("UA_NodeSetAlias entered with dst=%p,isStart=%d\n", (void * )dst, isStart));
-	if(isStart) {
-		// create if necessary
-		if(dst == UA_NULL) {
-			UA_NodeSetAlias_new(&dst);
-			s->parent[s->depth - 1].children[s->parent[s->depth - 1].activeChild].obj = (void *)dst;
-		}
-		// set handlers
-		s->parent[s->depth].len = 0;
-		XML_Stack_addChildHandler(s, "Alias", strlen("Alias"), (XML_decoder)UA_String_decodeXmlFromStack, UA_STRING,
-		                          &(dst->alias));
-		XML_Stack_addChildHandler(s, "Value", strlen("Value"), (XML_decoder)UA_String_decodeXmlFromStack, UA_STRING,
-		                          &(dst->value));
-		XML_Stack_handleTextAsElementOf(s, "Data", 1);
-
-		// set attributes
-		UA_Int32 i;
-		for(i = 0;attr[i];i += 2) {
-			if(0 == strncmp("Alias", attr[i], strlen("Alias")))
-				UA_String_copycstring(attr[i + 1], &(dst->alias));
-			else if(0 == strncmp("Value", attr[i], strlen("Value")))
-				UA_String_copycstring(attr[i + 1], &(dst->value));
-			else {
-				DBG_ERR(XML_Stack_print(s)); DBG_ERR(printf("%s - unknown attribute\n", attr[i]));
-			}
-		}
-	}
-	return UA_SUCCESS;
-}
-
-UA_Int32 UA_NodeSetAliases_decodeXmlFromStack(XML_Stack *s, XML_Attr *attr, UA_NodeSetAliases *dst, _Bool isStart) {
-	DBG_VERBOSE(printf("UA_NodeSetALiases entered with dst=%p,isStart=%d\n", (void * )dst, isStart));
-	if(isStart) {
-		if(dst == UA_NULL) {
-			UA_NodeSetAliases_new(&dst);
-			s->parent[s->depth - 1].children[s->parent[s->depth - 1].activeChild].obj = (void *)dst;
-		}
-		s->parent[s->depth].len = 0;
-		XML_Stack_addChildHandler(s, "Alias", strlen(
-		                              "Alias"), (XML_decoder)UA_NodeSetAlias_decodeXmlFromStack, UA_INVALIDTYPE,
-		                          UA_NULL);
-	} else {
-		// sub element is ready, add to array
-		if(dst->size < 0 || dst->size == 0) {
-			dst->size = 1;
-			UA_alloc((void ** )&(dst->aliases), dst->size * sizeof(UA_NodeSetAlias *));
-			DBG_VERBOSE(
-			    printf("allocate aliases:dst=%p, aliases=%p, size=%d\n", (void * )dst, (void * )(dst->aliases),
-			           dst->size));
-		} else {
-			dst->size++;
-			dst->aliases = realloc(dst->aliases, dst->size * sizeof(UA_NodeSetAlias *));
-			DBG_VERBOSE(
-			    printf("reallocate aliases:dst=%p, aliases=%p, size=%d\n", (void * )dst, (void * )(dst->aliases),
-			           dst->size));
-		}
-		// index starts with 0, therefore size-1
-		DBG_VERBOSE(printf("assign alias:dst=%p, src=%p\n", (void * )dst->aliases[dst->size - 1], (void * )attr));
-		dst->aliases[dst->size - 1] = (UA_NodeSetAlias *)attr;
-		DBG_VERBOSE(printf("UA_NodeSetAliases clears %p\n",
-		                   (void * )(s->parent[s->depth - 1].children[s->parent[s->depth - 1].activeChild].obj)));
-		s->parent[s->depth - 1].children[s->parent[s->depth - 1].activeChild].obj = UA_NULL;
-	}
-	return UA_SUCCESS;
-}
-
-UA_Int32 UA_NodeSet_decodeXmlFromStack(XML_Stack *s, XML_Attr *attr, UA_NodeSet *dst, _Bool isStart) {
-	DBG_VERBOSE(printf("UA_NodeSet entered with dst=%p,isStart=%d\n", (void * )dst, isStart));
-	if(isStart) {
-		if(dst == UA_NULL) {
-			UA_NodeSet_new(&dst, 99); // we don't really need the namespaceid for this..'
-			s->parent[s->depth - 1].children[s->parent[s->depth - 1].activeChild].obj = (void *)dst;
-		}
-		s->parent[s->depth].len = 0;
-		XML_Stack_addChildHandler(s, "Aliases", strlen(
-		                              "Aliases"), (XML_decoder)UA_NodeSetAliases_decodeXmlFromStack, UA_INVALIDTYPE,
-		                          &(dst->aliases));
-		XML_Stack_addChildHandler(s, "UADataType", strlen(
-		                              "UADataType"), (XML_decoder)UA_DataTypeNode_decodeXmlFromStack, UA_DATATYPENODE,
-		                          UA_NULL);
-		XML_Stack_addChildHandler(s, "UAVariableType", strlen(
-		                              "UAVariableType"), (XML_decoder)UA_VariableTypeNode_decodeXmlFromStack,
-		                          UA_VARIABLETYPENODE, UA_NULL);
-		XML_Stack_addChildHandler(s, "UAVariable", strlen(
-		                              "UAVariable"), (XML_decoder)UA_VariableNode_decodeXmlFromStack, UA_VARIABLENODE,
-		                          UA_NULL);
-		XML_Stack_addChildHandler(s, "UAObjectType", strlen(
-		                              "UAObjectType"), (XML_decoder)UA_ObjectTypeNode_decodeXmlFromStack,
-		                          UA_OBJECTTYPENODE, UA_NULL);
-		XML_Stack_addChildHandler(s, "UAObject", strlen(
-		                              "UAObject"), (XML_decoder)UA_ObjectNode_decodeXmlFromStack, UA_OBJECTNODE,
-		                          UA_NULL);
-	} else {
-		if(s->parent[s->depth - 1].activeChild == 0 && attr != UA_NULL) {
-			UA_NodeSetAliases *aliases = (UA_NodeSetAliases *)attr;
-			DBG(printf("UA_NodeSet_decodeXml - finished aliases: aliases=%p, size=%d\n", (void *)aliases,
-			           (aliases == UA_NULL) ? -1 : aliases->size));
-			s->aliases = aliases;
-			s->parent[s->depth - 1].children[s->parent[s->depth - 1].activeChild].obj = UA_NULL;
-		} else {
-			UA_Node* node = (UA_Node*) attr;
-			DBG(printf("UA_NodeSet_decodeXML - finished node: node=%p\n", (void* )node));
-			Namespace_insert(dst->ns, &node, 0);
-			DBG(printf("UA_NodeSet_decodeXml - Inserting "));
-			s->parent[s->depth - 1].children[s->parent[s->depth - 1].activeChild].obj = UA_NULL;
-		}
-	}
-	return UA_SUCCESS;
-}
-
-/** lookup if element is a known child of parent, if yes go for it otherwise ignore */
-void XML_Stack_startElement(void *data, const char *el, const char **attr) {
-	XML_Stack  *s = (XML_Stack *)data;
-	int         i;
-
-// scan expected children
-	XML_Parent *cp = &s->parent[s->depth];
-	for(i = 0;i < cp->len;i++) {
-		if(0 == strncmp(cp->children[i].name, el, cp->children[i].length)) {
-			DBG_VERBOSE(XML_Stack_print(s));
-			DBG_VERBOSE(printf("%s - processing\n", el));
-
-			cp->activeChild = i;
-
-			s->depth++;
-			s->parent[s->depth].name = el;
-			s->parent[s->depth].len  = 0;
-			s->parent[s->depth].textAttribIdx = -1;
-			s->parent[s->depth].activeChild   = -1;
-
-			// finally call the elementHandler and return
-			cp->children[i].elementHandler(data, attr, cp->children[i].obj, TRUE);
-			return;
-		}
-	}
-// if we come here we rejected the processing of el
-	DBG_VERBOSE(XML_Stack_print(s));
-	DBG_VERBOSE(printf("%s - rejected\n", el));
-	s->depth++;
-	s->parent[s->depth].name = el;
-// this should be sufficient to reject the children as well
-	s->parent[s->depth].len  = 0;
-}
-
-UA_Int32 XML_isSpace(cstring s, int len) {
-	int i;
-	for(i = 0;i < len;i++) {
-		if(!isspace(s[i]))
-			return UA_FALSE;
-	}
-	return UA_TRUE;
-}
-
-/* gather text */
-void XML_Stack_handleText(void * data, const char *txt, int len) {
-	/* XML_Stack* s = (XML_Stack*) data; */
-
-	if (len > 0 && !XML_isSpace(txt, len)) {
-		/* XML_Parent* cp = &(s->parent[s->depth]); // determine current element */
-		UA_ByteString src = { len+1, (UA_Byte*) txt };
-		UA_ByteString *dst;
-		UA_ByteString_new(&dst);	// alloc dst
-		UA_ByteString_copy(&src,dst); // alloc dst->data and copy txt
-		dst->data[len] = 0; // add terminating zero to handle single line efficiently
-		/* UA_list_addPayloadToBack(&(cp->textGatherList), (void*) dst); */
-		/* if (cp->totalGatherLength == -1) { */
-		/* 	cp->totalGatherLength = len; */
-		/* } else { */
-		/* 	cp->totalGatherLength += len; */
-		/* } */
-	}
-}
-
-char* theGatherBuffer;
-void textGatherListTotalLength(void* payload) {
-	UA_ByteString* b = (UA_ByteString*) payload;
-	#ifdef DEBUG
-	UA_ByteString_printf("\t",b);
-	#endif
-	UA_memcpy(theGatherBuffer,b->data,b->length-1); // remove trailing zero
-	theGatherBuffer += (b->length-1);
-}
-/** if we are an activeChild of a parent we call the child-handler */
-void XML_Stack_endElement(void *data, const char *el) {
-	/* XML_Stack *s = (XML_Stack *)data; */
-
-	/* XML_Parent* ce = &(s->parent[s->depth]); */
-	/* if (ce->textAttribIdx >= 0 && ce->totalGatherLength > 0 ) { */
-	/* 	ce->activeChild = ce->textAttribIdx; */
-	/* 	char* buf; */
-	/* 	if (UA_list_getFirst(&(ce->textGatherList)) == UA_list_getLast(&(ce->textGatherList)) ) { */
-	/* 		buf = (char*) ((UA_ByteString*) UA_list_getFirst(&(ce->textGatherList))->payload)->data; */
-	/* 	} else { */
-	/* 		printf("XML_Stack_endElement - more than one text snippet with total length=%d:\n",ce->totalGatherLength); */
-	/* 		UA_alloc((void**)&theGatherBuffer,ce->totalGatherLength+1); */
-	/* 		buf = theGatherBuffer; */
-	/* 		UA_list_iteratePayload(&(ce->textGatherList), textGatherListTotalLength); */
-	/* 		buf[ce->totalGatherLength] = 0; */
-	/* 		printf("XML_Stack_endElement - gatherBuffer %s:\n",buf); */
-	/* 	} */
-	/* 	XML_Attr attr[3] = { ce->textAttrib, buf, UA_NULL }; */
-	/* 	DBG(printf("handleText @ %s calls start elementHandler %s with dst=%p, buf={%s}\n", XML_Stack_path(s),ce->children[ce->activeChild].name, ce->children[ce->activeChild].obj, buf)); */
-	/* 	ce->children[ce->activeChild].elementHandler(s, attr, ce->children[ce->activeChild].obj, TRUE); */
-	/* 	if (s->parent[s->depth-1].activeChild > 0) { */
-	/* 		XML_child* c = &(s->parent[s->depth-1].children[s->parent[s->depth-1].activeChild]); */
-	/* 		c->elementHandler(s, UA_NULL, c->obj, FALSE); */
-	/* 	} */
-	/* 	if (UA_list_getFirst(&(ce->textGatherList)) != UA_list_getLast(&(ce->textGatherList)) ) { */
-	/* 		UA_free(buf); */
-	/* 	} */
-	/* 	UA_list_destroy(&(ce->textGatherList),(UA_list_PayloadVisitor) UA_ByteString_delete); */
-	/* 	UA_list_init(&(ce->textGatherList)); // don't know if destroy leaves the list in usable state... */
-	/* 	ce->totalGatherLength = -1; */
-	/* } */
-
-	/* // the parent of the parent (pop) of the element knows the elementHandler, therefore depth-2! */
-	/* if (s->depth > 1) { */
-	/* 	// inform parents elementHandler that everything is done */
-	/* 	XML_Parent *cp  = &(s->parent[s->depth - 1]); */
-	/* 	XML_Parent *cpp = &(s->parent[s->depth - 2]); */
-	/* 	if(cpp->activeChild >= 0 && cp->activeChild >= 0) { */
-	/* 		DBG_VERBOSE(XML_Stack_print(s)); */
-	/* 		DBG_VERBOSE( */
-	/* 		    printf(" - inform pop %s, arg=%p\n", cpp->children[cpp->activeChild].name, */
-	/* 		           (void * )cp->children[cp->activeChild].obj)); */
-	/* 		cpp->children[cpp->activeChild].elementHandler(s, (XML_Attr *)cp->children[cp->activeChild].obj, */
-	/* 		                                               cpp->children[cpp->activeChild].obj, FALSE); */
-	/* 	} */
-	/* 	// reset */
-	/* 	cp->activeChild = -1; */
-	/* } */
-	/* s->depth--; */
-}

+ 0 - 134
src/ongoing/ua_xml.h

@@ -1,134 +0,0 @@
-#ifndef __UA_XML_H
-#define __UA_XML_H
-
-#include <expat.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h> // strlen
-#include <ctype.h>  // isspace
-#include <unistd.h> // read
-
-#include "ua_types.h"
-
-struct XML_Stack;
-typedef char const *const XML_Attr;
-typedef char const *cstring;
-#define XML_STACK_MAX_DEPTH 10
-#define XML_STACK_MAX_CHILDREN 40
-typedef UA_Int32 (*XML_decoder)(struct XML_Stack *s, XML_Attr *attr, void *dst, UA_Boolean isStart);
-
-/** @brief A readable shortcut for NodeIds. A list of aliases is intensively used in the namespace0-xml-files */
-typedef struct UA_NodeSetAlias {
-	UA_String alias;
-	UA_String value;
-} UA_NodeSetAlias;
-//UA_TYPE_PROTOTYPES(UA_NodeSetAlias)
-
-/** @brief UA_NodeSetAliases - a list of aliases */
-typedef struct UA_NodeSetAliases {
-	UA_Int32 size;
-	UA_NodeSetAlias **aliases;
-} UA_NodeSetAliases;
-//UA_TYPE_PROTOTYPES(UA_NodeSetAliases)
-
-typedef struct XML_child {
-	cstring     name;
-	UA_Int32    length;
-	UA_Int32    type;
-	XML_decoder elementHandler;
-	void       *obj;
-} XML_child;
-
-typedef struct XML_Parent {
-	cstring   name;
-	int       textAttribIdx;  // -1 - not set
-	cstring   textAttrib;
-	int       activeChild;    // -1 - no active child
-	int       len;            // -1 - empty set
-	XML_child children[XML_STACK_MAX_CHILDREN];
-} XML_Parent;
-
-typedef struct XML_Stack {
-	int depth;
-	XML_Parent parent[XML_STACK_MAX_DEPTH];
-	UA_NodeSetAliases *aliases;  // shall point to the aliases of the NodeSet after reading
-} XML_Stack;
-
-UA_Int32 UA_Boolean_copycstring(cstring src, UA_Boolean *dst);
-UA_Int32 UA_Int16_copycstring(cstring src, UA_Int16 *dst);
-UA_Int32 UA_UInt16_copycstring(cstring src, UA_UInt16 *dst);
-UA_Boolean UA_NodeId_isBuiltinType(UA_NodeId *nodeid);
-
-/** @brief an object to hold a typed array */
-typedef struct UA_TypedArray {
-	UA_Int32         size;
-	UA_VTable_Entry *vt;
-	void *elements;
-} UA_TypedArray;
-
-/** @brief init typed array with size=-1 and an UA_INVALIDTYPE */
-UA_Int32 UA_TypedArray_init(UA_TypedArray *p);
-
-/** @brief allocate memory for the array header only */
-UA_Int32 UA_TypedArray_new(UA_TypedArray **p);
-UA_Int32 UA_TypedArray_setType(UA_TypedArray *p, UA_Int32 type);
-//UA_Int32 UA_TypedArray_decodeXML(XML_Stack *s, XML_Attr *attr, UA_TypedArray *dst, UA_Boolean isStart);
-
-UA_Int32 UA_NodeSetAlias_init(UA_NodeSetAlias* p);
-UA_Int32 UA_NodeSetAlias_new(UA_NodeSetAlias** p);
-
-UA_Int32 UA_NodeSetAliases_init(UA_NodeSetAliases* p);
-UA_Int32 UA_NodeSetAliases_new(UA_NodeSetAliases** p);
-UA_Int32 UA_NodeSetAliases_println(cstring label, UA_NodeSetAliases *p);
-
-UA_Int32 UA_ExpandedNodeId_copycstring(cstring src, UA_ExpandedNodeId* dst, UA_NodeSetAliases* aliases);
-
-void XML_Stack_init(XML_Stack* p, UA_UInt32 nsid, cstring name);
-void XML_Stack_print(XML_Stack* s);
-
-/** @brief add a reference to a handler (@see XML_Stack_addChildHandler) for text data
- *
- * Assume a XML structure such as
- *     <LocalizedText>
- *          <Locale></Locale>
- *          <Text>Server</Text>
- *     </LocalizedText>
- * which might be abbreviated as
- *     <LocalizedText>Server</LocalizedText>
- *
- * We would add two (@ref XML_Stack_addChildHandler), one for Locale (index 0) and one for Text (index 1),
- * both to be handled by (@ref UA_String_decodeXML) with elements "Data" and "Length". To handle the
- * abbreviation we add
- *      XML_Stack_handleTextAsElementOf(s,"Data",1)
- *
- * @param[in] s the stack
- * @param[in] textAttrib the name of the element of the handler at position textAttribIdx
- * @param[in] textAttribIdx the index of the handler
- */
-void XML_Stack_handleTextAsElementOf(XML_Stack *p, cstring textAttrib, unsigned int textAttribIdx);
-
-/** @brief make a handler known to the XML-stack on the current level
- *
- * The current level is given by s->depth, the maximum number of children is a predefined constant.
- * A combination of type=UA_INVALIDTYPE and dst=UA_NULL is valid for special handlers only
- *
- * @param[in] s the stack
- * @param[in] name the name of the element
- * @param[in] nameLength the length of the element name
- * @param[in] handler the decoder routine for this element
- * @param[in] type the open62541-type of the element, UA_INVALIDTYPE if not in the VTable
- * @param[out] dst the address of the object for the data, handlers will allocate object if UA_NULL
- */
-void XML_Stack_addChildHandler(XML_Stack *p, cstring name, UA_Int32 nameLength, XML_decoder handler, UA_Int32 type,
-                               void *dst);
-
-void XML_Stack_startElement(void *data, const char *el, const char **attr);
-UA_Int32 XML_isSpace(cstring s, int len);
-void XML_Stack_handleText(void *data, const char *txt, int len);
-void XML_Stack_endElement(void *data, const char *el);
-
-UA_Int32 UA_Text_decodeXmlFromStack(XML_Stack *s, XML_Attr *attr, UA_Byte **dst, _Bool isStart);
-UA_Int32 UA_NodeId_copycstring(cstring src, UA_NodeId *dst, UA_NodeSetAliases *aliases);
-UA_Int32 UA_TypedArray_decodeXmlFromStack(XML_Stack *s, XML_Attr *attr, UA_TypedArray *dst, _Bool isStart);
-
-#endif // __UA_XML_H__

+ 0 - 210
src/ongoing/xml2ns0.c

@@ -1,210 +0,0 @@
-#include <fcntl.h> // open, O_RDONLY
-#include "ua_types.h"
-//#include "ua_types_generated.h"
-#include "ua_namespace.h"
-#include "ua_xml.h"
-#include "ua_namespace_xml.h"
-#include "ua_types_encoding_xml.h"
-#include "util/ua_util.h"
-
-// FIXME: most of the following code should be generated as a template from the ns0-defining xml-files
-/** @brief sam (abbr. for server_application_memory) is a flattened memory structure of the UAVariables in ns0 */
-struct sam {
-	UA_ServerStatusDataType serverStatus;
-} sam;
-
-char* productName = "xml2ns0";
-char* productUri = "http://open62541.org/xml2ns0/";
-char* manufacturerName = "open62541";
-char* softwareVersion = "0.01";
-char* buildNumber = "999-" __DATE__ "-001" ;
-
-#define SAM_ASSIGN_CSTRING(src,dst) do { \
-	dst.length = strlen(src)-1; \
-	dst.data = (UA_Byte*) src; \
-} while(0)
-
-void sam_attach(Namespace *ns,UA_UInt32 ns0id,UA_Int32 type, void* p) {
-	UA_NodeId nodeid;
-	nodeid.namespace = 0; // we cannot access ns->namespaceIndex;
-	nodeid.identifier.numeric = ns0id;
-	nodeid.encodingByte = UA_NODEIDTYPE_FOURBYTE;
-	const UA_Node* result;
-	UA_Int32 retval = Namespace_get(ns,&nodeid,&result);
-	if (retval == UA_SUCCESS) {
-		if (result->nodeId.identifier.numeric == ns0id) {
-			if (result->nodeClass == UA_NODECLASS_VARIABLE) {
-				UA_VariableNode* variable = (UA_VariableNode*) result;
-				if (variable->dataType.identifier.numeric == UA_.types[type].typeId.identifier.numeric) {
-					variable->value.arrayLength = 1;
-					variable->value.data = p;
-				} else {
-					UA_alloc((void**)&variable->value.data, sizeof(UA_ExtensionObject));
-					UA_ExtensionObject* eo = (UA_ExtensionObject*) variable->value.data;
-					eo->typeId = variable->dataType;
-					eo->encoding = UA_EXTENSIONOBJECT_ENCODINGMASK_BODYISBYTESTRING;
-					// FIXME: This code is valid for ns0 and numeric identifiers only
-					eo->body.length = UA_.types[UA_ns0ToVTableIndex(&variable->dataType)].memSize;
-					eo->body.data = p;
-				}
-			}
-		}
-		Namespace_releaseManagedNode(result);
-	}
-}
-	/* 	} else { */
-	/* 		perror("Namespace_getWritable returned wrong node class"); */
-	/* 	} */
-	/* } else { */
-	/* 	printf("retrieving node={i=%d} returned node={i=%d}\n", ns0id, result->nodeId.identifier.numeric); */
-	/* } */
-	/*        Namespace_Entry_Lock_release(lock); */
-	/* 	} else { */
-	/* 	printf("retrieving node={i=%d} returned error code %d\n",ns0id,retval); */
-	/* } */
-
-void sam_init(Namespace* ns) {
-	// Initialize the strings
-	SAM_ASSIGN_CSTRING(productName,sam.serverStatus.buildInfo.productName);
-	SAM_ASSIGN_CSTRING(productUri,sam.serverStatus.buildInfo.productUri);
-	SAM_ASSIGN_CSTRING(manufacturerName,sam.serverStatus.buildInfo.manufacturerName);
-	SAM_ASSIGN_CSTRING(softwareVersion,sam.serverStatus.buildInfo.softwareVersion);
-	SAM_ASSIGN_CSTRING(buildNumber,sam.serverStatus.buildInfo.buildNumber);
-
-	// Attach server application memory to ns0
-	sam_attach(ns,2256,UA_SERVERSTATUSDATATYPE,&sam.serverStatus); // this is the head of server status!
-	sam.serverStatus.startTime = sam.serverStatus.currentTime = UA_DateTime_now();
-	sam_attach(ns,2257,UA_DATETIME, &sam.serverStatus.startTime);
-	sam_attach(ns,2258,UA_DATETIME, &sam.serverStatus.currentTime);
-	sam_attach(ns,2259,UA_SERVERSTATE, &sam.serverStatus.state);
-	sam_attach(ns,2260,UA_BUILDINFO, &sam.serverStatus.buildInfo); // start of build Info
-	sam_attach(ns,2261,UA_STRING, &sam.serverStatus.buildInfo.productName);
-	sam_attach(ns,2262,UA_STRING, &sam.serverStatus.buildInfo.productUri);
-	sam_attach(ns,2263,UA_STRING, &sam.serverStatus.buildInfo.manufacturerName);
-	sam_attach(ns,2264,UA_STRING, &sam.serverStatus.buildInfo.softwareVersion);
-	sam_attach(ns,2265,UA_STRING, &sam.serverStatus.buildInfo.buildNumber);
-	sam_attach(ns,2266,UA_DATETIME, &sam.serverStatus.buildInfo.buildDate);
-	sam_attach(ns,2992,UA_UINT32, &sam.serverStatus.secondsTillShutdown);
-	sam_attach(ns,2993,UA_LOCALIZEDTEXT,&sam.serverStatus.shutdownReason);
-}
-
-UA_Int32 Namespace_getNumberOfComponents(Namespace const * ns, UA_NodeId const * id, UA_Int32* number) {
-	UA_Int32 retval = UA_SUCCESS;
-	UA_Node const * node;
-	if ((retval = Namespace_get(ns,id,&node)) != UA_SUCCESS)
-		return retval;
-	if (node == UA_NULL)
-		return UA_ERR_INVALID_VALUE;
-	UA_Int32 i, n;
-	for (i = 0, n = 0; i < node->referencesSize; i++ ) {
-		if (node->references[i].referenceTypeId.identifier.numeric == 47 && node->references[i].isInverse != UA_TRUE) {
-			n++;
-		}
-	}
-	Namespace_releaseManagedNode(node);
-	*number = n;
-	return retval;
-}
-
-UA_Int32 Namespace_getComponent(Namespace const * ns, UA_NodeId const * id, UA_Int32 idx, UA_NodeId** result) {
-	UA_Int32 retval = UA_SUCCESS;
-
-	UA_Node const * node;
-	if ((retval = Namespace_get(ns,id,&node)) != UA_SUCCESS)
-		return retval;
-
-	UA_Int32 i, n;
-	for (i = 0, n = 0; i < node->referencesSize; i++ ) {
-		if (node->references[i].referenceTypeId.identifier.numeric == 47 && node->references[i].isInverse != UA_TRUE) {
-			n++;
-			if (n == idx) {
-				*result = &(node->references[i].targetId.nodeId);
-				Namespace_releaseManagedNode(node);
-				return retval;
-			}
-		}
-	}
-
-	Namespace_releaseManagedNode(node);
-	return UA_ERR_INVALID_VALUE;
-}
-
-
-UA_Int32 UAX_NodeId_encodeBinaryByMetaData(Namespace const * ns, UA_NodeId const * id, UA_ByteString *dst, UA_UInt32* offset) {
-	UA_Int32 i, retval = UA_SUCCESS;
-	if (UA_NodeId_isBasicType(id)) {
-		UA_Node const * result;
-		if ((retval = Namespace_get(ns,id,&result)) == UA_SUCCESS) {
-			UA_Variant_encodeBinary(&((UA_VariableNode *) result)->value,dst,offset);
-			Namespace_releaseManagedNode(result);
-		}
-	} else {
-		UA_Int32 nComp = 0;
-		if ((retval = Namespace_getNumberOfComponents(ns,id,&nComp)) == UA_SUCCESS) {
-			for (i=0; i < nComp; i++) {
-				UA_NodeId* comp = UA_NULL;
-				Namespace_getComponent(ns,id,i,&comp);
-				UAX_NodeId_encodeBinaryByMetaData(ns,comp, dst, offset);
-			}
-		}
-	}
-	return retval;
-}
-
-UA_Int32 UAX_NodeId_encodeBinary(Namespace const * ns, UA_NodeId const * id, UA_ByteString *dst, UA_UInt32* offset) {
-	UA_Int32 retval = UA_SUCCESS;
-	UA_Node const * node;
-	if ((retval = Namespace_get(ns,id,&node)) == UA_SUCCESS) {
-		if (node->nodeClass == UA_NODECLASS_VARIABLE) {
-			retval = UA_Variant_encodeBinary(&((UA_VariableNode*) node)->value,dst,offset);
-		}
-		Namespace_releaseManagedNode(node);
-	}
-	return retval;
-}
-
-int main() {
-	Namespace* ns;
-	UA_Int32 retval;
-
-	retval = Namespace_loadFromFile(&ns, 0, "ROOT", UA_NULL);
-
-	sam_init(ns);
-	// Namespace_iterate(ns, print_node);
-
-	// encoding buffer
-	char buf[1024];
-	UA_ByteString buffer = { 1024, (UA_Byte*) buf };
-	UA_UInt32 pos = 0;
-
-	UA_NodeId nodeid;
-	nodeid.encodingByte = UA_NODEIDTYPE_FOURBYTE;
-	nodeid.namespace = 0;
-	nodeid.identifier.numeric = 2256; // ServerStatus
-
-	UA_Int32 i=0;
-	retval=UA_SUCCESS;
-	UA_DateTime tStart = UA_DateTime_now();
-	// encoding takes roundabout 10 µs on my virtual machine with -O0, so 1E5 takes a second
-	for (i=0;i<1E5 && retval == UA_SUCCESS;i++) {
-		pos = 0;
-		sam.serverStatus.currentTime = UA_DateTime_now();
-		retval |= UAX_NodeId_encodeBinary(ns,&nodeid,&buffer,&pos);
-	}
-	UA_DateTime tEnd = UA_DateTime_now();
-	// tStart, tEnd count in 100 ns steps, so 10 steps = 1 µs
-	UA_Double tDelta = ( tEnd - tStart ) / ( 10.0 * i );
-
-	printf("encode server node %d times, retval=%d: time/enc=%f us, enc/s=%f\n",i, retval, tDelta, 1.0E6 / tDelta);
-	DBG(buffer.length=pos);
-	DBG(UA_ByteString_printx(", result=", &buffer));
-
-	// Design alternative two : use meta data
-	// pos = 0;
-	// buffer.length = 1024;
-	// UAX_NodeId_encodeBinary(n.ns,&nodeid,&pos,&buffer);
-	// buffer.length = pos;
-	// UA_ByteString_printx("namespace based encoder result=", &buffer);
-
-	return 0;
-}

+ 139 - 66
src/server/ua_nodes.c

@@ -2,29 +2,16 @@
 #include "ua_util.h"
 
 /* UA_Node */
-static void UA_Node_init(UA_Node *p) {
-	UA_NodeId_init(&p->nodeId);
-	UA_NodeClass_init(&p->nodeClass);
-	UA_QualifiedName_init(&p->browseName);
-	UA_LocalizedText_init(&p->displayName);
-	UA_LocalizedText_init(&p->description);
-	UA_UInt32_init(&p->writeMask);
-	UA_UInt32_init(&p->userWriteMask);
-	p->referencesSize = -1;
-	p->references = UA_NULL;
-}
-
 static void UA_Node_deleteMembers(UA_Node *p) {
 	UA_NodeId_deleteMembers(&p->nodeId);
 	UA_QualifiedName_deleteMembers(&p->browseName);
 	UA_LocalizedText_deleteMembers(&p->displayName);
 	UA_LocalizedText_deleteMembers(&p->description);
-	UA_Array_delete(p->references, &UA_TYPES[UA_TYPES_REFERENCENODE], p->referencesSize);
+	UA_Array_delete(p->references, p->referencesSize, &UA_TYPES[UA_TYPES_REFERENCENODE]);
 }
 
 static UA_StatusCode UA_Node_copy(const UA_Node *src, UA_Node *dst) {
 	UA_StatusCode retval = UA_STATUSCODE_GOOD;
-	UA_Node_init(dst);
 	retval |= UA_NodeId_copy(&src->nodeId, &dst->nodeId);
 	dst->nodeClass = src->nodeClass;
 	retval |= UA_QualifiedName_copy(&src->browseName, &dst->browseName);
@@ -32,19 +19,123 @@ static UA_StatusCode UA_Node_copy(const UA_Node *src, UA_Node *dst) {
 	retval |= UA_LocalizedText_copy(&src->description, &dst->description);
 	dst->writeMask = src->writeMask;
 	dst->userWriteMask = src->userWriteMask;
-	dst->referencesSize = src->referencesSize;
-	retval |= UA_Array_copy(src->references, (void**)&dst->references, &UA_TYPES[UA_TYPES_REFERENCENODE],
-                            src->referencesSize);
-	if(retval)
+	if(retval != UA_STATUSCODE_GOOD) {
     	UA_Node_deleteMembers(dst);
+        return retval;
+    }
+	retval |= UA_Array_copy(src->references, src->referencesSize, (void**)&dst->references,
+                            &UA_TYPES[UA_TYPES_REFERENCENODE]);
+	if(retval == UA_STATUSCODE_GOOD)
+        dst->referencesSize = src->referencesSize;
 	return retval;
 }
 
+
+void UA_Node_deleteMembersAnyNodeClass(UA_Node *node) {
+    switch(node->nodeClass) {
+    case UA_NODECLASS_OBJECT:
+        UA_ObjectNode_deleteMembers((UA_ObjectNode*)node);
+        break;
+    case UA_NODECLASS_VARIABLE:
+        UA_VariableNode_deleteMembers((UA_VariableNode*)node);
+        break;
+    case UA_NODECLASS_METHOD:
+        UA_MethodNode_deleteMembers((UA_MethodNode*)node);
+        break;
+    case UA_NODECLASS_OBJECTTYPE:
+        UA_ObjectTypeNode_deleteMembers((UA_ObjectTypeNode*)node);
+        break;
+    case UA_NODECLASS_VARIABLETYPE:
+        UA_VariableTypeNode_deleteMembers((UA_VariableTypeNode*)node);
+        break;
+    case UA_NODECLASS_REFERENCETYPE:
+        UA_ReferenceTypeNode_deleteMembers((UA_ReferenceTypeNode*)node);
+        break;
+    case UA_NODECLASS_DATATYPE:
+        UA_DataTypeNode_deleteMembers((UA_DataTypeNode*)node);
+        break;
+    case UA_NODECLASS_VIEW:
+        UA_ViewNode_deleteMembers((UA_ViewNode*)node);
+        break;
+    default:
+        break;
+    }
+}
+
+void UA_Node_deleteAnyNodeClass(UA_Node *node) {
+    UA_Node_deleteMembersAnyNodeClass(node);
+    UA_free(node);
+}
+
+typedef UA_Node *(*UA_NewNodeFunction)(void);
+typedef UA_StatusCode (*UA_CopyNodeFunction)(const UA_Node *src, UA_Node *dst);
+typedef void (*UA_DeleteNodeFunction)(UA_Node *p);
+
+UA_Node * UA_Node_copyAnyNodeClass(const UA_Node *node) {
+    UA_NewNodeFunction newNode;
+    UA_CopyNodeFunction copyNode;
+    UA_DeleteNodeFunction deleteNode;
+
+    switch(node->nodeClass) {
+    case UA_NODECLASS_OBJECT:
+        newNode = (UA_NewNodeFunction)UA_ObjectNode_new;
+        copyNode = (UA_CopyNodeFunction)UA_ObjectNode_copy;
+        deleteNode = (UA_DeleteNodeFunction)UA_ObjectNode_delete;
+        break;
+    case UA_NODECLASS_VARIABLE:
+        newNode = (UA_NewNodeFunction)UA_VariableNode_new;
+        copyNode = (UA_CopyNodeFunction)UA_VariableNode_copy;
+        deleteNode = (UA_DeleteNodeFunction)UA_VariableNode_delete;
+        break;
+    case UA_NODECLASS_METHOD:
+        newNode = (UA_NewNodeFunction)UA_MethodNode_new;
+        copyNode = (UA_CopyNodeFunction)UA_MethodNode_copy;
+        deleteNode = (UA_DeleteNodeFunction)UA_MethodNode_delete;
+        break;
+    case UA_NODECLASS_OBJECTTYPE:
+        newNode = (UA_NewNodeFunction)UA_ObjectTypeNode_new;
+        copyNode = (UA_CopyNodeFunction)UA_ObjectTypeNode_copy;
+        deleteNode = (UA_DeleteNodeFunction)UA_ObjectTypeNode_delete;
+        break;
+    case UA_NODECLASS_VARIABLETYPE:
+        newNode = (UA_NewNodeFunction)UA_VariableTypeNode_new;
+        copyNode = (UA_CopyNodeFunction)UA_VariableTypeNode_copy;
+        deleteNode = (UA_DeleteNodeFunction)UA_VariableTypeNode_delete;
+        break;
+    case UA_NODECLASS_REFERENCETYPE:
+        newNode = (UA_NewNodeFunction)UA_ReferenceTypeNode_new;
+        copyNode = (UA_CopyNodeFunction)UA_ReferenceTypeNode_copy;
+        deleteNode = (UA_DeleteNodeFunction)UA_ReferenceTypeNode_delete;
+        break;
+    case UA_NODECLASS_DATATYPE:
+        newNode = (UA_NewNodeFunction)UA_DataTypeNode_new;
+        copyNode = (UA_CopyNodeFunction)UA_DataTypeNode_copy;
+        deleteNode = (UA_DeleteNodeFunction)UA_DataTypeNode_delete;
+        break;
+    case UA_NODECLASS_VIEW:
+        newNode = (UA_NewNodeFunction)UA_ViewNode_new;
+        copyNode = (UA_CopyNodeFunction)UA_ViewNode_copy;
+        deleteNode = (UA_DeleteNodeFunction)UA_ViewNode_delete;
+        break;
+    default:
+        return NULL;
+        break;
+    }
+
+    UA_Node *copy = newNode();
+    if(!copy)
+        return NULL;
+    if(copyNode(node, copy) != UA_STATUSCODE_GOOD) {
+        deleteNode(copy);
+        return NULL;
+    }
+    return copy;
+}
+
 /* UA_ObjectNode */
 void UA_ObjectNode_init(UA_ObjectNode *p) {
-	UA_Node_init((UA_Node*)p);
+    memset(p, 0, sizeof(UA_ObjectNode));
     p->nodeClass = UA_NODECLASS_OBJECT;
-    p->eventNotifier = 0;
 }
 
 UA_ObjectNode * UA_ObjectNode_new(void) {
@@ -65,14 +156,14 @@ void UA_ObjectNode_delete(UA_ObjectNode *p) {
 
 UA_StatusCode UA_ObjectNode_copy(const UA_ObjectNode *src, UA_ObjectNode *dst) {
     dst->eventNotifier = src->eventNotifier;
+    dst->instanceHandle = src->instanceHandle;
 	return UA_Node_copy((const UA_Node*)src, (UA_Node*)dst);
 }
 
 /* UA_ObjectTypeNode */
 void UA_ObjectTypeNode_init(UA_ObjectTypeNode *p) {
-	UA_Node_init((UA_Node*)p);
+    memset(p, 0, sizeof(UA_ObjectTypeNode));
     p->nodeClass = UA_NODECLASS_OBJECTTYPE;
-    p->isAbstract = UA_FALSE;
 }
 
 UA_ObjectTypeNode * UA_ObjectTypeNode_new(void) {
@@ -93,20 +184,17 @@ void UA_ObjectTypeNode_delete(UA_ObjectTypeNode *p) {
 
 UA_StatusCode UA_ObjectTypeNode_copy(const UA_ObjectTypeNode *src, UA_ObjectTypeNode *dst) {
     dst->isAbstract = src->isAbstract;
+    dst->lifecycleManagement = src->lifecycleManagement;
 	return UA_Node_copy((const UA_Node*)src, (UA_Node*)dst);
 }
 
 /* UA_VariableNode */
 void UA_VariableNode_init(UA_VariableNode *p) {
-	UA_Node_init((UA_Node*)p);
+    memset(p, 0, sizeof(UA_VariableNode));
     p->nodeClass = UA_NODECLASS_VARIABLE;
-    p->valueSource = UA_VALUESOURCE_VARIANT;
-    UA_Variant_init(&p->value.variant);
     p->valueRank = -2; // scalar or array of any dimension
-    p->accessLevel = 0;
-    p->userAccessLevel = 0;
-    p->minimumSamplingInterval = 0.0;
-    p->historizing = UA_FALSE;
+    p->accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
+    p->userAccessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
 }
 
 UA_VariableNode * UA_VariableNode_new(void) {
@@ -119,7 +207,7 @@ UA_VariableNode * UA_VariableNode_new(void) {
 void UA_VariableNode_deleteMembers(UA_VariableNode *p) {
     UA_Node_deleteMembers((UA_Node*)p);
     if(p->valueSource == UA_VALUESOURCE_VARIANT)
-        UA_Variant_deleteMembers(&p->value.variant);
+        UA_Variant_deleteMembers(&p->value.variant.value);
 }
 
 void UA_VariableNode_delete(UA_VariableNode *p) {
@@ -128,15 +216,15 @@ void UA_VariableNode_delete(UA_VariableNode *p) {
 }
 
 UA_StatusCode UA_VariableNode_copy(const UA_VariableNode *src, UA_VariableNode *dst) {
-    UA_VariableNode_init(dst);
 	UA_StatusCode retval = UA_Node_copy((const UA_Node*)src, (UA_Node*)dst);
     dst->valueRank = src->valueRank;
     dst->valueSource = src->valueSource;
-    if(src->valueSource == UA_VALUESOURCE_VARIANT)
-        retval = UA_Variant_copy(&src->value.variant, &dst->value.variant);
-    else
+    if(src->valueSource == UA_VALUESOURCE_VARIANT) {
+        retval = UA_Variant_copy(&src->value.variant.value, &dst->value.variant.value);
+        dst->value.variant.callback = src->value.variant.callback;
+    } else
         dst->value.dataSource = src->value.dataSource;
-    if(retval) {
+    if(retval != UA_STATUSCODE_GOOD) {
         UA_VariableNode_deleteMembers(dst);
         return retval;
     }
@@ -149,12 +237,9 @@ UA_StatusCode UA_VariableNode_copy(const UA_VariableNode *src, UA_VariableNode *
 
 /* UA_VariableTypeNode */
 void UA_VariableTypeNode_init(UA_VariableTypeNode *p) {
-	UA_Node_init((UA_Node*)p);
+    memset(p, 0, sizeof(UA_VariableTypeNode));
     p->nodeClass = UA_NODECLASS_VARIABLETYPE;
-    p->valueSource = UA_VALUESOURCE_VARIANT;
-    UA_Variant_init(&p->value.variant);
     p->valueRank = -2; // scalar or array of any dimension
-    p->isAbstract = UA_FALSE;
 }
 
 UA_VariableTypeNode * UA_VariableTypeNode_new(void) {
@@ -167,7 +252,7 @@ UA_VariableTypeNode * UA_VariableTypeNode_new(void) {
 void UA_VariableTypeNode_deleteMembers(UA_VariableTypeNode *p) {
     UA_Node_deleteMembers((UA_Node*)p);
     if(p->valueSource == UA_VALUESOURCE_VARIANT)
-        UA_Variant_deleteMembers(&p->value.variant);
+        UA_Variant_deleteMembers(&p->value.variant.value);
 }
 
 void UA_VariableTypeNode_delete(UA_VariableTypeNode *p) {
@@ -176,15 +261,15 @@ void UA_VariableTypeNode_delete(UA_VariableTypeNode *p) {
 }
 
 UA_StatusCode UA_VariableTypeNode_copy(const UA_VariableTypeNode *src, UA_VariableTypeNode *dst) {
-    UA_VariableTypeNode_init(dst);
 	UA_StatusCode retval = UA_Node_copy((const UA_Node*)src, (UA_Node*)dst);
     dst->valueRank = src->valueRank;
     dst->valueSource = src->valueSource;
-    if(src->valueSource == UA_VALUESOURCE_VARIANT)
-        UA_Variant_copy(&src->value.variant, &dst->value.variant);
-    else
+    if(src->valueSource == UA_VALUESOURCE_VARIANT){
+        UA_Variant_copy(&src->value.variant.value, &dst->value.variant.value);
+        dst->value.variant.callback = src->value.variant.callback;
+    } else
         dst->value.dataSource = src->value.dataSource;
-    if(retval) {
+    if(retval != UA_STATUSCODE_GOOD) {
         UA_VariableTypeNode_deleteMembers(dst);
         return retval;
     }
@@ -194,11 +279,8 @@ UA_StatusCode UA_VariableTypeNode_copy(const UA_VariableTypeNode *src, UA_Variab
 
 /* UA_ReferenceTypeNode */
 void UA_ReferenceTypeNode_init(UA_ReferenceTypeNode *p) {
-	UA_Node_init((UA_Node*)p);
+    memset(p, 0, sizeof(UA_ReferenceTypeNode));
     p->nodeClass = UA_NODECLASS_REFERENCETYPE;
-    p->isAbstract = UA_FALSE;
-    p->symmetric = UA_FALSE;
-    UA_LocalizedText_init(&p->inverseName);
 }
 
 UA_ReferenceTypeNode * UA_ReferenceTypeNode_new(void) {
@@ -220,10 +302,10 @@ void UA_ReferenceTypeNode_delete(UA_ReferenceTypeNode *p) {
 
 UA_StatusCode UA_ReferenceTypeNode_copy(const UA_ReferenceTypeNode *src, UA_ReferenceTypeNode *dst) {
     UA_StatusCode retval = UA_Node_copy((const UA_Node*)src, (UA_Node*)dst);
-    if(retval)
+    if(retval != UA_STATUSCODE_GOOD)
         return retval;
     retval = UA_LocalizedText_copy(&src->inverseName, &dst->inverseName);
-    if(retval) {
+    if(retval != UA_STATUSCODE_GOOD) {
         UA_ReferenceTypeNode_deleteMembers(dst);
         return retval;
     }
@@ -234,14 +316,8 @@ UA_StatusCode UA_ReferenceTypeNode_copy(const UA_ReferenceTypeNode *src, UA_Refe
 
 /* UA_MethodNode */
 void UA_MethodNode_init(UA_MethodNode *p) {
-    UA_Node_init((UA_Node*)p);
+    memset(p, 0, sizeof(UA_MethodNode));
     p->nodeClass = UA_NODECLASS_METHOD;
-    p->executable = UA_FALSE;
-    p->userExecutable = UA_FALSE;
-#ifdef ENABLE_METHODCALLS
-    p->methodHandle        = UA_NULL;
-    p->attachedMethod      = UA_NULL;
-#endif
 }
 
 UA_MethodNode * UA_MethodNode_new(void) {
@@ -253,7 +329,7 @@ UA_MethodNode * UA_MethodNode_new(void) {
 
 void UA_MethodNode_deleteMembers(UA_MethodNode *p) {
 #ifdef ENABLE_METHODCALLS
-    p->attachedMethod = UA_NULL;
+    p->attachedMethod = NULL;
 #endif
     UA_Node_deleteMembers((UA_Node*)p);
 }
@@ -261,8 +337,8 @@ void UA_MethodNode_deleteMembers(UA_MethodNode *p) {
 void UA_MethodNode_delete(UA_MethodNode *p) {
     UA_MethodNode_deleteMembers(p);
 #ifdef ENABLE_METHODCALLS
-    p->methodHandle   = UA_NULL;
-    p->attachedMethod = UA_NULL;
+    p->methodHandle   = NULL;
+    p->attachedMethod = NULL;
 #endif
     UA_free(p);
 }
@@ -282,10 +358,8 @@ UA_StatusCode UA_MethodNode_copy(const UA_MethodNode *src, UA_MethodNode *dst) {
 
 /* UA_ViewNode */
 void UA_ViewNode_init(UA_ViewNode *p) {
-    UA_Node_init((UA_Node*)p);
+    memset(p, 0, sizeof(UA_ViewNode));
     p->nodeClass = UA_NODECLASS_VIEW;
-    p->containsNoLoops = UA_FALSE;
-    p->eventNotifier = 0;
 }
 
 UA_ViewNode * UA_ViewNode_new(void) {
@@ -312,9 +386,8 @@ UA_StatusCode UA_ViewNode_copy(const UA_ViewNode *src, UA_ViewNode *dst) {
 
 /* UA_DataTypeNode */
 void UA_DataTypeNode_init(UA_DataTypeNode *p) {
-	UA_Node_init((UA_Node*)p);
+    memset(p, 0, sizeof(UA_DataTypeNode));
     p->nodeClass = UA_NODECLASS_DATATYPE;
-    p->isAbstract = UA_FALSE;
 }
 
 UA_DataTypeNode * UA_DataTypeNode_new(void) {

+ 27 - 7
src/server/ua_nodes.h

@@ -5,6 +5,13 @@
 #include "ua_types_generated.h"
 #include "ua_types_encoding_binary.h"
 
+#define UA_TYPE_HANDLING_FUNCTIONS(TYPE)                             \
+    TYPE UA_EXPORT * TYPE##_new(void);                               \
+    void UA_EXPORT TYPE##_init(TYPE * p);                            \
+    void UA_EXPORT TYPE##_delete(TYPE * p);                          \
+    void UA_EXPORT TYPE##_deleteMembers(TYPE * p);                   \
+    UA_StatusCode UA_EXPORT TYPE##_copy(const TYPE *src, TYPE *dst);
+
 #define UA_STANDARD_NODEMEMBERS                 \
     UA_NodeId nodeId;                           \
     UA_NodeClass nodeClass;                     \
@@ -13,13 +20,17 @@
     UA_LocalizedText description;               \
     UA_UInt32 writeMask;                        \
     UA_UInt32 userWriteMask;                    \
-    UA_Int32 referencesSize;                    \
+    size_t referencesSize;                      \
     UA_ReferenceNode *references;
 
 typedef struct {
     UA_STANDARD_NODEMEMBERS
 } UA_Node;
 
+void UA_Node_deleteAnyNodeClass(UA_Node *node);
+void UA_Node_deleteMembersAnyNodeClass(UA_Node *node);
+UA_Node * UA_Node_copyAnyNodeClass(const UA_Node *node);
+
 /**************/
 /* ObjectNode */
 /**************/
@@ -27,6 +38,7 @@ typedef struct {
 typedef struct {
     UA_STANDARD_NODEMEMBERS
     UA_Byte eventNotifier;
+    void *instanceHandle;
 } UA_ObjectNode;
 UA_TYPE_HANDLING_FUNCTIONS(UA_ObjectNode)
 
@@ -37,6 +49,7 @@ UA_TYPE_HANDLING_FUNCTIONS(UA_ObjectNode)
 typedef struct {
     UA_STANDARD_NODEMEMBERS
     UA_Boolean isAbstract;
+    UA_ObjectLifecycleManagement lifecycleManagement;
 } UA_ObjectTypeNode;
 UA_TYPE_HANDLING_FUNCTIONS(UA_ObjectTypeNode)
 
@@ -58,7 +71,10 @@ typedef struct {
                              n = -3:  the value can be a scalar or a one dimensional array. */
     UA_ValueSource valueSource;
     union {
-        UA_Variant variant;
+        struct {
+        UA_Variant value;
+        UA_ValueCallback callback;
+        } variant;
         UA_DataSource dataSource;
     } value;
     /* <--- similar to variabletypenodes up to there--->*/
@@ -80,7 +96,10 @@ typedef struct {
     UA_Int32 valueRank;
     UA_ValueSource valueSource;
     union {
-        UA_Variant variant;
+        struct {
+            UA_Variant value;
+            UA_ValueCallback callback;
+        } variant;
         UA_DataSource dataSource;
     } value;
     /* <--- similar to variablenodes up to there--->*/
@@ -100,9 +119,9 @@ typedef struct {
 } UA_ReferenceTypeNode;
 UA_TYPE_HANDLING_FUNCTIONS(UA_ReferenceTypeNode)
 
-/***********************/
-/* ReferenceMethodNode */
-/***********************/
+/**************/
+/* MethodNode */
+/**************/
 
 typedef struct {
     UA_STANDARD_NODEMEMBERS
@@ -121,8 +140,9 @@ UA_TYPE_HANDLING_FUNCTIONS(UA_MethodNode)
 
 typedef struct {
     UA_STANDARD_NODEMEMBERS
-    UA_Boolean containsNoLoops;
     UA_Byte eventNotifier;
+    /* <-- the same as objectnode until here --> */
+    UA_Boolean containsNoLoops;
 } UA_ViewNode;
 UA_TYPE_HANDLING_FUNCTIONS(UA_ViewNode)
 

+ 82 - 168
src/server/ua_nodestore.c

@@ -2,25 +2,28 @@
 #include "ua_util.h"
 #include "ua_statuscodes.h"
 
-/* It could happen that we want to delete a node even though a function higher
-   in the call-chain still has a reference. So we count references and delete
-   once the count falls to zero. That means we copy every node to a new place
-   where it is right behind the refcount integer.
-
-   Since we copy nodes on the heap, we make the alloc for the nodeEntry bigger
-   to accommodate for the different nodeclasses (the nodeEntry may have an
-   overlength "tail"). */
-#define ALIVE_BIT (1 << 15) /* Alive bit in the refcount */
-struct nodeEntry {
-    UA_UInt16 refcount;
-    UA_Node node; // could be const, but then we cannot free it without compilers warnings
-};
+#define UA_NODESTORE_MINSIZE 64
+
+typedef struct {
+    UA_Boolean taken;
+    union {
+        UA_Node node;
+        UA_ObjectNode objectNode;
+        UA_ObjectTypeNode objectTypeNode;
+        UA_VariableNode variableNode;
+        UA_VariableTypeNode variableTypeNode;
+        UA_ReferenceTypeNode referenceTypeNode;
+        UA_MethodNode methodeNode;
+        UA_ViewNode viewNode;
+        UA_DataTypeNode dataTypeNode;
+    } node;
+} UA_NodeStoreEntry;
 
 struct UA_NodeStore {
-    struct nodeEntry **entries;
-    UA_UInt32          size;
-    UA_UInt32          count;
-    UA_UInt32          sizePrimeIndex;
+    UA_NodeStoreEntry *entries;
+    UA_UInt32 size;
+    UA_UInt32 count;
+    UA_UInt32 sizePrimeIndex;
 };
 
 #include "ua_nodestore_hash.inc"
@@ -50,18 +53,19 @@ static UA_Int16 higher_prime_index(hash_t n) {
 
 /* Returns UA_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, struct nodeEntry ***entry) {
+static UA_Boolean
+containsNodeId(const UA_NodeStore *ns, const UA_NodeId *nodeid, UA_NodeStoreEntry **entry) {
     hash_t         h     = hash(nodeid);
     UA_UInt32      size  = ns->size;
     hash_t         index = mod(h, size);
-    struct nodeEntry **e = &ns->entries[index];
+    UA_NodeStoreEntry *e = &ns->entries[index];
 
-    if(*e == UA_NULL) {
+    if(!e->taken) {
         *entry = e;
         return UA_FALSE;
     }
 
-    if(UA_NodeId_equal(&(*e)->node.nodeId, nodeid)) {
+    if(UA_NodeId_equal(&e->node.node.nodeId, nodeid)) {
         *entry = e;
         return UA_TRUE;
     }
@@ -71,15 +75,12 @@ static UA_Boolean containsNodeId(const UA_NodeStore *ns, const UA_NodeId *nodeid
         index += hash2;
         if(index >= size)
             index -= size;
-
         e = &ns->entries[index];
-
-        if(*e == UA_NULL) {
+        if(!e->taken) {
             *entry = e;
             return UA_FALSE;
         }
-
-        if(UA_NodeId_equal(&(*e)->node.nodeId, nodeid)) {
+        if(UA_NodeId_equal(&e->node.node.nodeId, nodeid)) {
             *entry = e;
             return UA_TRUE;
         }
@@ -93,31 +94,29 @@ static UA_Boolean containsNodeId(const UA_NodeStore *ns, const UA_NodeId *nodeid
    repeatedly inserts the table elements. The occupancy of the table after the
    call will be about 50%. */
 static UA_StatusCode expand(UA_NodeStore *ns) {
-    UA_Int32 osize = ns->size;
-    UA_Int32 count = ns->count;
+    UA_UInt32 osize = ns->size;
+    UA_UInt32 count = ns->count;
     /* Resize only when table after removal of unused elements is either too full or too empty.  */
-    if(count * 2 < osize && (count * 8 > osize || osize <= 32))
+    if(count * 2 < osize && (count * 8 > osize || osize <= UA_NODESTORE_MINSIZE))
         return UA_STATUSCODE_GOOD;
 
-
     UA_UInt32 nindex = higher_prime_index(count * 2);
     UA_Int32 nsize = primes[nindex];
-    struct nodeEntry **nentries;
-    if(!(nentries = UA_malloc(sizeof(struct nodeEntry *) * nsize)))
+    UA_NodeStoreEntry *nentries;
+    if(!(nentries = UA_calloc(nsize, sizeof(UA_NodeStoreEntry))))
         return UA_STATUSCODE_BADOUTOFMEMORY;
 
-    UA_memset(nentries, 0, nsize * sizeof(struct nodeEntry *));
-    struct nodeEntry **oentries = ns->entries;
+    UA_NodeStoreEntry *oentries = ns->entries;
     ns->entries = nentries;
     ns->size    = nsize;
     ns->sizePrimeIndex = nindex;
 
     // recompute the position of every entry and insert the pointer
-    for(UA_Int32 i=0, j=0;i<osize && j<count;i++) {
-        if(!oentries[i])
+    for(size_t i = 0, j = 0; i < osize && j < count; i++) {
+        if(!oentries[i].taken)
             continue;
-        struct nodeEntry **e;
-        containsNodeId(ns, &(*oentries[i]).node.nodeId, &e);  /* We know this returns an empty entry here */
+        UA_NodeStoreEntry *e;
+        containsNodeId(ns, &oentries[i].node.node.nodeId, &e);  /* We know this returns an empty entry here */
         *e = oentries[i];
         j++;
     }
@@ -127,44 +126,14 @@ static UA_StatusCode expand(UA_NodeStore *ns) {
 }
 
 /* Marks the entry dead and deletes if necessary. */
-static void deleteEntry(struct nodeEntry *entry) {
-    if(entry->refcount > 0)
-        return;
-
-    switch(entry->node.nodeClass) {
-    case UA_NODECLASS_OBJECT:
-        UA_ObjectNode_deleteMembers((UA_ObjectNode*)&entry->node);
-        break;
-    case UA_NODECLASS_VARIABLE:
-        UA_VariableNode_deleteMembers((UA_VariableNode*)&entry->node);
-        break;
-    case UA_NODECLASS_METHOD:
-        UA_MethodNode_deleteMembers((UA_MethodNode *)&entry->node);
-        break;
-    case UA_NODECLASS_OBJECTTYPE:
-        UA_ObjectTypeNode_deleteMembers((UA_ObjectTypeNode*)&entry->node);
-        break;
-    case UA_NODECLASS_VARIABLETYPE:
-        UA_VariableTypeNode_deleteMembers((UA_VariableTypeNode*)&entry->node);
-        break;
-    case UA_NODECLASS_REFERENCETYPE:
-        UA_ReferenceTypeNode_deleteMembers((UA_ReferenceTypeNode*)&entry->node);
-        break;
-    case UA_NODECLASS_DATATYPE:
-        UA_DataTypeNode_deleteMembers((UA_DataTypeNode*)&entry->node);
-        break;
-    case UA_NODECLASS_VIEW:
-        UA_ViewNode_deleteMembers((UA_ViewNode*)&entry->node);
-        break;
-    default:
-        UA_assert(UA_FALSE);
-        break;
-    }
-    UA_free(entry);
+static UA_INLINE void
+deleteEntry(UA_NodeStoreEntry *entry) {
+    UA_Node_deleteMembersAnyNodeClass(&entry->node.node);
+    entry->taken = UA_FALSE;
 }
 
 /** Copies the node into the entry. Then free the original node (but not its content). */
-static struct nodeEntry * nodeEntryFromNode(UA_Node *node) {
+static void fillEntry(UA_NodeStoreEntry *entry, UA_Node *node) {
     size_t nodesize = 0;
     switch(node->nodeClass) {
     case UA_NODECLASS_OBJECT:
@@ -194,14 +163,9 @@ static struct nodeEntry * nodeEntryFromNode(UA_Node *node) {
     default:
         UA_assert(UA_FALSE);
     }
-
-    struct nodeEntry *newEntry;
-    if(!(newEntry = UA_malloc(sizeof(struct nodeEntry) - sizeof(UA_Node) + nodesize)))
-        return UA_NULL;
-
-    UA_memcpy(&newEntry->node, node, nodesize);
+    memcpy(&entry->node, node, nodesize);
     UA_free(node);
-    return newEntry;
+    entry->taken = UA_TRUE;
 }
 
 /**********************/
@@ -211,153 +175,103 @@ static struct nodeEntry * nodeEntryFromNode(UA_Node *node) {
 UA_NodeStore * UA_NodeStore_new(void) {
     UA_NodeStore *ns;
     if(!(ns = UA_malloc(sizeof(UA_NodeStore))))
-        return UA_NULL;
-
-    ns->sizePrimeIndex = higher_prime_index(32);
+        return NULL;
+    ns->sizePrimeIndex = higher_prime_index(UA_NODESTORE_MINSIZE);
     ns->size = primes[ns->sizePrimeIndex];
     ns->count = 0;
-    if(!(ns->entries = UA_malloc(sizeof(struct nodeEntry *) * ns->size))) {
+    if(!(ns->entries = UA_calloc(ns->size, sizeof(UA_NodeStoreEntry)))) {
         UA_free(ns);
-        return UA_NULL;
+        return NULL;
     }
-    UA_memset(ns->entries, 0, ns->size * sizeof(struct nodeEntry *));
     return ns;
 }
 
 void UA_NodeStore_delete(UA_NodeStore *ns) {
     UA_UInt32 size = ns->size;
-    struct nodeEntry **entries = ns->entries;
+    UA_NodeStoreEntry *entries = ns->entries;
     for(UA_UInt32 i = 0;i < size;i++) {
-        if(entries[i] != UA_NULL) {
-            entries[i]->refcount &= ~ALIVE_BIT; // mark dead
-            deleteEntry(entries[i]);
-            entries[i] = UA_NULL;
-            ns->count--;
-        }
+        if(entries[i].taken)
+            deleteEntry(&entries[i]);
     }
-
     UA_free(ns->entries);
     UA_free(ns);
 }
 
-UA_StatusCode UA_NodeStore_insert(UA_NodeStore *ns, UA_Node *node, const UA_Node **inserted) {
+UA_StatusCode UA_NodeStore_insert(UA_NodeStore *ns, UA_Node *node, UA_Node **inserted) {
     if(ns->size * 3 <= ns->count * 4) {
         if(expand(ns) != UA_STATUSCODE_GOOD)
             return UA_STATUSCODE_BADINTERNALERROR;
     }
-    // get a free slot
-    struct nodeEntry **slot;
-    //FIXME: a bit dirty workaround of preserving namespace
-    //namespace index is assumed to be valid
+
+    UA_NodeStoreEntry *entry;
     UA_NodeId tempNodeid;
-    UA_NodeId_copy(&node->nodeId, &tempNodeid);
+    tempNodeid = node->nodeId;
     tempNodeid.namespaceIndex = 0;
     if(UA_NodeId_isNull(&tempNodeid)) {
-        // find a unique nodeid that is not taken
-        node->nodeId.identifierType = UA_NODEIDTYPE_NUMERIC;
-
-        if(node->nodeId.namespaceIndex==0) //original request for ns=0 should yield ns=1
-            node->nodeId.namespaceIndex=1;
-
+        /* find a free nodeid */
+        if(node->nodeId.namespaceIndex == 0) //original request for ns=0 should yield ns=1
+            node->nodeId.namespaceIndex = 1;
         UA_Int32 identifier = ns->count+1; // start value
         UA_Int32 size = ns->size;
         hash_t increase = mod2(identifier, size);
         while(UA_TRUE) {
             node->nodeId.identifier.numeric = identifier;
-            if(!containsNodeId(ns, &node->nodeId, &slot))
+            if(!containsNodeId(ns, &node->nodeId, &entry))
                 break;
             identifier += increase;
             if(identifier >= size)
                 identifier -= size;
         }
     } else {
-        UA_NodeId_deleteMembers(&tempNodeid);
-        if(containsNodeId(ns, &node->nodeId, &slot))
+        if(containsNodeId(ns, &node->nodeId, &entry))
             return UA_STATUSCODE_BADNODEIDEXISTS;
     }
 
-    struct nodeEntry *entry = nodeEntryFromNode(node);
-    if(!entry)
-        return UA_STATUSCODE_BADOUTOFMEMORY;
-
-    *slot = entry;
+    fillEntry(entry, node);
     ns->count++;
-
-    if(inserted) {
-        entry->refcount = ALIVE_BIT + 1;
-        *inserted = &entry->node;
-    } else {
-        entry->refcount = ALIVE_BIT;
-    }
-
+    if(inserted)
+        *inserted = &entry->node.node;
     return UA_STATUSCODE_GOOD;
 }
 
-UA_StatusCode UA_NodeStore_replace(UA_NodeStore *ns, const UA_Node *oldNode, UA_Node *node,
-                                   const UA_Node **inserted) {
-    struct nodeEntry **slot;
-    const UA_NodeId *nodeId = &node->nodeId;
-    if(!containsNodeId(ns, nodeId, &slot))
+UA_StatusCode
+UA_NodeStore_replace(UA_NodeStore *ns, UA_Node *oldNode,
+                     UA_Node *node, UA_Node **inserted) {
+    UA_NodeStoreEntry *slot;
+    if(!containsNodeId(ns, &node->nodeId, &slot))
         return UA_STATUSCODE_BADNODEIDUNKNOWN;
-
-    // you try to replace an obsolete node (without threading this can't happen
-    // if the user doesn't do it deliberately in his code)
-    if(&(*slot)->node != oldNode)
+    /* that is not the node you are looking for */
+    if(&slot->node.node != oldNode)
         return UA_STATUSCODE_BADINTERNALERROR;
-
-    struct nodeEntry *entry = nodeEntryFromNode(node);
-    if(!entry)
-        return UA_STATUSCODE_BADOUTOFMEMORY;
-
-    (*slot)->refcount &= ~ALIVE_BIT; // mark dead
-    *slot = entry;
-
-    if(inserted) {
-        entry->refcount = ALIVE_BIT + 1;
-        *inserted = &entry->node;
-    } else {
-        entry->refcount = ALIVE_BIT;
-    }
+    deleteEntry(slot);
+    fillEntry(slot, node);
+    if(inserted)
+        *inserted = &slot->node.node;
     return UA_STATUSCODE_GOOD;
 }
 
-const UA_Node * UA_NodeStore_get(const UA_NodeStore *ns, const UA_NodeId *nodeid) {
-    struct nodeEntry **slot;
+UA_Node * UA_NodeStore_get(const UA_NodeStore *ns, const UA_NodeId *nodeid) {
+    UA_NodeStoreEntry *slot;
     if(!containsNodeId(ns, nodeid, &slot))
-        return UA_NULL;
-    (*slot)->refcount++;
-    return &(*slot)->node;
+        return NULL;
+    return &slot->node.node;
 }
 
 UA_StatusCode UA_NodeStore_remove(UA_NodeStore *ns, const UA_NodeId *nodeid) {
-    struct nodeEntry **slot;
+    UA_NodeStoreEntry *slot;
     if(!containsNodeId(ns, nodeid, &slot))
         return UA_STATUSCODE_BADNODEIDUNKNOWN;
-
-    // Check before if deleting the node makes the UA_NodeStore inconsistent.
-    (*slot)->refcount &= ~ALIVE_BIT; // mark dead
-    deleteEntry(*slot);
-    *slot = UA_NULL;
+    deleteEntry(slot);
     ns->count--;
-
     /* Downsize the hashmap if it is very empty */
     if(ns->count * 8 < ns->size && ns->size > 32)
         expand(ns); // this can fail. we just continue with the bigger hashmap.
-
     return UA_STATUSCODE_GOOD;
 }
 
 void UA_NodeStore_iterate(const UA_NodeStore *ns, UA_NodeStore_nodeVisitor visitor) {
     for(UA_UInt32 i = 0;i < ns->size;i++) {
-        if(ns->entries[i] != UA_NULL)
-            visitor(&ns->entries[i]->node);
+        if(ns->entries[i].taken)
+            visitor(&ns->entries[i].node.node);
     }
 }
-
-void UA_NodeStore_release(const UA_Node *managed) {
-    /* We know what we are doing here and remove a compiler warning. Nobody has
-       a reference to the const pointer, so we can free it. */
-    struct nodeEntry *entry = (struct nodeEntry *) ((uintptr_t)managed - offsetof(struct nodeEntry, node));
-    entry->refcount--;
-    deleteEntry(entry);
-}

+ 16 - 12
src/server/ua_nodestore.h

@@ -25,13 +25,21 @@
  * @{
  */
 
+/* For multithreading, nodes in the nodestore are immutable */
+#ifdef UA_MULTITHREADING
+# define UA_MT_CONST const
+#else
+# define UA_MT_CONST
+#endif
+
 struct UA_NodeStore;
 typedef struct UA_NodeStore UA_NodeStore;
 
 /** Create a new nodestore */
 UA_NodeStore * UA_NodeStore_new(void);
 
-/** Delete the nodestore and all nodes in it */
+/** Delete the nodestore and all nodes in it. Do not call from a read-side
+    critical section (multithreading). */
 void UA_NodeStore_delete(UA_NodeStore *ns);
 
 /**
@@ -41,14 +49,16 @@ void UA_NodeStore_delete(UA_NodeStore *ns);
  * is not NULL, then a pointer to the managed node is returned (and must be
  * released).
  */
-UA_StatusCode UA_NodeStore_insert(UA_NodeStore *ns, UA_Node *node, const UA_Node **inserted);
+UA_StatusCode UA_NodeStore_insert(UA_NodeStore *ns, UA_Node *node, UA_MT_CONST UA_Node **inserted);
 
 /**
  * Replace an existing node in the nodestore. If the node was already replaced,
- * UA_STATUSCODE_BADINTERNALERROR is returned. If inserted is not NULL, a
- * pointer to the managed (immutable) node is returned.
+ * UA_STATUSCODE_BADINTERNALERROR is returned. A pointer to the inserted node is
+ * returned. It is important that oldNode is not used afterwards in the same
+ * thread.
  */
-UA_StatusCode UA_NodeStore_replace(UA_NodeStore *ns, const UA_Node *oldNode, UA_Node *node, const UA_Node **inserted);
+UA_StatusCode UA_NodeStore_replace(UA_NodeStore *ns, UA_MT_CONST UA_Node *oldNode,
+                                   UA_Node *node, UA_MT_CONST UA_Node **inserted);
 
 /**
  * Remove a node from the nodestore. Always succeeds, even if the node was not
@@ -62,13 +72,7 @@ UA_StatusCode UA_NodeStore_remove(UA_NodeStore *ns, const UA_NodeId *nodeid);
  * entirely. After the node is no longer used, it needs to be released to decrease
  * the reference count.
  */
-const UA_Node * UA_NodeStore_get(const UA_NodeStore *ns, const UA_NodeId *nodeid);
-
-/**
- * Release a managed node. Do never call this with a node that isn't managed by a
- * nodestore.
- */
-void UA_NodeStore_release(const UA_Node *managed);
+UA_MT_CONST UA_Node * UA_NodeStore_get(const UA_NodeStore *ns, const UA_NodeId *nodeid);
 
 /**
  * A function that can be evaluated on all entries in a nodestore via

+ 69 - 142
src/server/ua_nodestore_concurrent.c

@@ -1,113 +1,90 @@
 #include "ua_util.h"
 #include "ua_nodestore.h"
 
-#define ALIVE_BIT (1 << 15) /* Alive bit in the refcount */
-
 struct nodeEntry {
-    struct cds_lfht_node htn;      /* contains next-ptr for urcu-hashmap */
-    struct rcu_head      rcu_head; /* For call-rcu */
-    UA_UInt16 refcount;            /* Counts the amount of readers on it [alive-bit, 15 counter-bits] */
-    UA_Node node;                  /* Might be cast from any _bigger_ UA_Node* type. Allocate enough memory! */
+    struct cds_lfht_node htn; ///< Contains the next-ptr for urcu-hashmap
+    struct rcu_head rcu_head; ///< For call-rcu
+    UA_Node node; ///< Might be cast from any _bigger_ UA_Node* type. Allocate enough memory!
 };
 
 struct UA_NodeStore {
-    struct cds_lfht *ht; /* Hash table */
+    struct cds_lfht *ht;
 };
 
 #include "ua_nodestore_hash.inc"
 
-static void node_deleteMembers(UA_Node *node) {
-    switch(node->nodeClass) {
+static void deleteEntry(struct rcu_head *head) {
+    struct nodeEntry *entry = caa_container_of(head, struct nodeEntry, rcu_head);
+    switch(entry->node.nodeClass) {
     case UA_NODECLASS_OBJECT:
-        UA_ObjectNode_deleteMembers((UA_ObjectNode *)node);
+        UA_ObjectNode_deleteMembers((UA_ObjectNode*)&entry->node);
         break;
     case UA_NODECLASS_VARIABLE:
-        UA_VariableNode_deleteMembers((UA_VariableNode *)node);
+        UA_VariableNode_deleteMembers((UA_VariableNode*)&entry->node);
         break;
     case UA_NODECLASS_METHOD:
-        UA_MethodNode_deleteMembers((UA_MethodNode *)node);
+        UA_MethodNode_deleteMembers((UA_MethodNode*)&entry->node);
         break;
     case UA_NODECLASS_OBJECTTYPE:
-        UA_ObjectTypeNode_deleteMembers((UA_ObjectTypeNode *)node);
+        UA_ObjectTypeNode_deleteMembers((UA_ObjectTypeNode*)&entry->node);
         break;
     case UA_NODECLASS_VARIABLETYPE:
-        UA_VariableTypeNode_deleteMembers((UA_VariableTypeNode *)node);
+        UA_VariableTypeNode_deleteMembers((UA_VariableTypeNode*)&entry->node);
         break;
     case UA_NODECLASS_REFERENCETYPE:
-        UA_ReferenceTypeNode_deleteMembers((UA_ReferenceTypeNode *)node);
+        UA_ReferenceTypeNode_deleteMembers((UA_ReferenceTypeNode*)&entry->node);
         break;
     case UA_NODECLASS_DATATYPE:
-        UA_DataTypeNode_deleteMembers((UA_DataTypeNode *)node);
+        UA_DataTypeNode_deleteMembers((UA_DataTypeNode*)&entry->node);
         break;
     case UA_NODECLASS_VIEW:
-        UA_ViewNode_deleteMembers((UA_ViewNode *)node);
+        UA_ViewNode_deleteMembers((UA_ViewNode*)&entry->node);
         break;
     default:
         UA_assert(UA_FALSE);
         break;
     }
+    free(entry);
 }
 
 /* We are in a rcu_read lock. So the node will not be freed under our feet. */
 static int compare(struct cds_lfht_node *htn, const void *orig) {
     const UA_NodeId *origid = (const UA_NodeId *)orig;
-    const UA_NodeId *newid  = &((struct nodeEntry *)htn)->node.nodeId;   /* The htn is first in the entry structure. */
+    /* The htn is first in the entry structure. */
+    const UA_NodeId *newid  = &((struct nodeEntry *)htn)->node.nodeId;
     return UA_NodeId_equal(newid, origid);
 }
 
-/* The entry was removed from the hashtable. No more readers can get it. Since
-   all readers using the node for a longer time (outside the rcu critical
-   section) increased the refcount, we only need to wait for the refcount
-   to reach zero. */
-static void markDead(struct rcu_head *head) {
-    struct nodeEntry *entry = (struct nodeEntry*) ((uintptr_t)head - offsetof(struct nodeEntry, rcu_head)); 
-    uatomic_and(&entry->refcount, ~ALIVE_BIT); // set the alive bit to zero
-    if(uatomic_read(&entry->refcount) > 0)
-        return;
-
-    node_deleteMembers(&entry->node);
-    UA_free(entry);
-}
-
-/* Free the entry if it is dead and nobody uses it anymore */
-void UA_NodeStore_release(const UA_Node *managed) {
-    struct nodeEntry *entry = (struct nodeEntry*) ((uintptr_t)managed - offsetof(struct nodeEntry, node)); 
-    if(uatomic_add_return(&entry->refcount, -1) == 0) {
-        node_deleteMembers(&entry->node);
-        UA_free(entry);
-    }
-}
-
 UA_NodeStore * UA_NodeStore_new() {
     UA_NodeStore *ns;
     if(!(ns = UA_malloc(sizeof(UA_NodeStore))))
-        return UA_NULL;
+        return NULL;
 
     /* 32 is the minimum size for the hashtable. */
     ns->ht = cds_lfht_new(32, 32, 0, CDS_LFHT_AUTO_RESIZE, NULL);
     if(!ns->ht) {
         UA_free(ns);
-        return UA_NULL;
+        ns = NULL;
     }
     return ns;
 }
 
+/* do not call with read-side critical section held!! */
 void UA_NodeStore_delete(UA_NodeStore *ns) {
-    struct cds_lfht      *ht = ns->ht;
+    struct cds_lfht *ht = ns->ht;
     struct cds_lfht_iter  iter;
-
-    rcu_read_lock();
     cds_lfht_first(ht, &iter);
+    rcu_read_lock();
     while(iter.node) {
         if(!cds_lfht_del(ht, iter.node)) {
-            struct nodeEntry *entry = (struct nodeEntry*) ((uintptr_t)iter.node - offsetof(struct nodeEntry, htn)); 
-            call_rcu(&entry->rcu_head, markDead);
+            /* points to the htn entry, which is first */
+            struct nodeEntry *entry = (struct nodeEntry*) iter.node;
+            call_rcu(&entry->rcu_head, deleteEntry);
         }
         cds_lfht_next(ht, &iter);
     }
     rcu_read_unlock();
-    cds_lfht_destroy(ht, UA_NULL);
-
+    cds_lfht_destroy(ht, NULL);
     UA_free(ns);
 }
 
@@ -146,25 +123,18 @@ UA_StatusCode UA_NodeStore_insert(UA_NodeStore *ns, UA_Node *node, const UA_Node
     struct nodeEntry *entry;
     if(!(entry = UA_malloc(sizeof(struct nodeEntry) - sizeof(UA_Node) + nodesize)))
         return UA_STATUSCODE_BADOUTOFMEMORY;
-    UA_memcpy((void*)&entry->node, node, nodesize);
+    UA_Node *newNode = &entry->node;
+    memcpy(newNode, node, nodesize);
 
     cds_lfht_node_init(&entry->htn);
-    entry->refcount = ALIVE_BIT;
-    if(inserted) // increase the counter before adding the node
-        entry->refcount++;
-
     struct cds_lfht_node *result;
-    //FIXME: a bit dirty workaround of preserving namespace
     //namespace index is assumed to be valid
     UA_NodeId tempNodeid;
-    UA_NodeId_copy(&node->nodeId, &tempNodeid);
+    tempNodeid = node->nodeId;
     tempNodeid.namespaceIndex = 0;
     if(!UA_NodeId_isNull(&tempNodeid)) {
         hash_t h = hash(&node->nodeId);
-        rcu_read_lock();
-        result = cds_lfht_add_unique(ns->ht, h, compare, &entry->node.nodeId, &entry->htn);
-        rcu_read_unlock();
-
+        result = cds_lfht_add_unique(ns->ht, h, compare, &newNode->nodeId, &entry->htn);
         /* If the nodeid exists already */
         if(result != &entry->htn) {
             UA_free(entry);
@@ -172,29 +142,26 @@ UA_StatusCode UA_NodeStore_insert(UA_NodeStore *ns, UA_Node *node, const UA_Node
         }
     } else {
         /* create a unique nodeid */
-        ((UA_Node *)&entry->node)->nodeId.identifierType = UA_NODEIDTYPE_NUMERIC;
-        if(((UA_Node *)&entry->node)->nodeId.namespaceIndex == 0) //original request for ns=0 should yield ns=1
-            ((UA_Node *)&entry->node)->nodeId.namespaceIndex = 1;
-        if(((UA_Node *)&entry->node)->nodeClass==UA_NODECLASS_VARIABLE){ //set namespaceIndex in browseName in case id is generated
-        	((UA_VariableNode*)&entry->node)->browseName.namespaceIndex=((UA_Node *)&entry->node)->nodeId.namespaceIndex;
-        }
+        newNode->nodeId.identifierType = UA_NODEIDTYPE_NUMERIC;
+        if(newNode->nodeId.namespaceIndex == 0) // original request for ns=0 should yield ns=1
+            newNode->nodeId.namespaceIndex = 1;
+        /* set namespaceIndex in browseName in case id is generated */
+        if(newNode->nodeClass == UA_NODECLASS_VARIABLE)
+        	((UA_VariableNode*)newNode)->browseName.namespaceIndex = newNode->nodeId.namespaceIndex;
 
         unsigned long identifier;
         long before, after;
-        rcu_read_lock();
         cds_lfht_count_nodes(ns->ht, &before, &identifier, &after); // current amount of nodes stored
         identifier++;
 
-        ((UA_Node *)&entry->node)->nodeId.identifier.numeric = identifier;
+        newNode->nodeId.identifier.numeric = identifier;
         while(UA_TRUE) {
-            hash_t nhash = hash(&entry->node.nodeId);
-            result = cds_lfht_add_unique(ns->ht, nhash, compare, &entry->node.nodeId, &entry->htn);
+            hash_t h = hash(&newNode->nodeId);
+            result = cds_lfht_add_unique(ns->ht, h, compare, &newNode->nodeId, &entry->htn);
             if(result == &entry->htn)
                 break;
-
-            ((UA_Node *)&entry->node)->nodeId.identifier.numeric += (identifier * 2654435761);
+            newNode->nodeId.identifier.numeric += (identifier * 2654435761);
         }
-        rcu_read_unlock();
     }
 
     UA_free(node);
@@ -205,6 +172,18 @@ UA_StatusCode UA_NodeStore_insert(UA_NodeStore *ns, UA_Node *node, const UA_Node
 
 UA_StatusCode UA_NodeStore_replace(UA_NodeStore *ns, const UA_Node *oldNode, UA_Node *node,
                                    const UA_Node **inserted) {
+    /* Get the current version */
+    hash_t h = hash(&node->nodeId);
+    struct cds_lfht_iter iter;
+    cds_lfht_lookup(ns->ht, h, compare, &node->nodeId, &iter);
+    if(!iter.node)
+        return UA_STATUSCODE_BADNODEIDUNKNOWN;
+
+    /* We try to replace an obsolete version of the node */
+    struct nodeEntry *oldEntry = (struct nodeEntry*)iter.node;
+    if(&oldEntry->node != oldNode)
+        return UA_STATUSCODE_BADINTERNALERROR;
+    
     size_t nodesize;
     /* Copy the node into the entry. Then reset the original node. It shall no longer be used. */
     switch(node->nodeClass) {
@@ -239,44 +218,17 @@ UA_StatusCode UA_NodeStore_replace(UA_NodeStore *ns, const UA_Node *oldNode, UA_
     struct nodeEntry *newEntry;
     if(!(newEntry = UA_malloc(sizeof(struct nodeEntry) - sizeof(UA_Node) + nodesize)))
         return UA_STATUSCODE_BADOUTOFMEMORY;
-    UA_memcpy((void*)&newEntry->node, node, nodesize);
-
+    memcpy((void*)&newEntry->node, node, nodesize);
     cds_lfht_node_init(&newEntry->htn);
-    newEntry->refcount = ALIVE_BIT;
-    if(inserted) // increase the counter before adding the node
-        newEntry->refcount++;
-
-    hash_t h = hash(&node->nodeId);
-    struct cds_lfht_iter iter;
-    rcu_read_lock();
-    cds_lfht_lookup(ns->ht, h, compare, &node->nodeId, &iter);
 
-    /* No node found that can be replaced */
-    if(!iter.node) {
-        rcu_read_unlock();
-        UA_free(newEntry);
-        return UA_STATUSCODE_BADNODEIDUNKNOWN;
-    }
-
-    struct nodeEntry *oldEntry = (struct nodeEntry*) ((uintptr_t)iter.node - offsetof(struct nodeEntry, htn)); 
-    /* The node we found is obsolete*/
-    if(&oldEntry->node != oldNode) {
-        rcu_read_unlock();
-        UA_free(newEntry);
-        return UA_STATUSCODE_BADINTERNALERROR;
-    }
-
-    /* The old node is replaced by a managed node. */
     if(cds_lfht_replace(ns->ht, &iter, h, compare, &node->nodeId, &newEntry->htn) != 0) {
         /* Replacing failed. Maybe the node got replaced just before this thread tried to.*/
-        rcu_read_unlock();
         UA_free(newEntry);
         return UA_STATUSCODE_BADINTERNALERROR;
     }
         
     /* If an entry got replaced, mark it as dead. */
-    call_rcu(&oldEntry->rcu_head, markDead);
-    rcu_read_unlock();
+    call_rcu(&oldEntry->rcu_head, deleteEntry);
     UA_free(node);
 
     if(inserted)
@@ -285,58 +237,33 @@ UA_StatusCode UA_NodeStore_replace(UA_NodeStore *ns, const UA_Node *oldNode, UA_
 }
 
 UA_StatusCode UA_NodeStore_remove(UA_NodeStore *ns, const UA_NodeId *nodeid) {
-    hash_t nhash = hash(nodeid);
+    hash_t h = hash(nodeid);
     struct cds_lfht_iter iter;
-
-    rcu_read_lock();
-    /* If this fails, then the node has already been removed. */
-    cds_lfht_lookup(ns->ht, nhash, compare, &nodeid, &iter);
-    if(!iter.node || cds_lfht_del(ns->ht, iter.node) != 0) {
-        rcu_read_unlock();
+    cds_lfht_lookup(ns->ht, h, compare, &nodeid, &iter);
+    if(!iter.node || cds_lfht_del(ns->ht, iter.node) != 0)
         return UA_STATUSCODE_BADNODEIDUNKNOWN;
-    }
-
-    struct nodeEntry *entry = (struct nodeEntry*) ((uintptr_t)iter.node - offsetof(struct nodeEntry, htn)); 
-    call_rcu(&entry->rcu_head, markDead);
-    rcu_read_unlock();
-
+    struct nodeEntry *entry = (struct nodeEntry*)iter.node;
+    call_rcu(&entry->rcu_head, deleteEntry);
     return UA_STATUSCODE_GOOD;
 }
 
 const UA_Node * UA_NodeStore_get(const UA_NodeStore *ns, const UA_NodeId *nodeid) {
-    hash_t nhash = hash(nodeid);
+    hash_t h = hash(nodeid);
     struct cds_lfht_iter iter;
-
-    rcu_read_lock();
-    cds_lfht_lookup(ns->ht, nhash, compare, nodeid, &iter);
-    struct nodeEntry *found_entry = (struct nodeEntry *)cds_lfht_iter_get_node(&iter);
-
-    if(!found_entry) {
-        rcu_read_unlock();
-        return UA_NULL;
-    }
-
-    /* This is done within a read-lock. The node will not be marked dead within a read-lock. */
-    uatomic_inc(&found_entry->refcount);
-    rcu_read_unlock();
+    cds_lfht_lookup(ns->ht, h, compare, nodeid, &iter);
+    struct nodeEntry *found_entry = (struct nodeEntry*)iter.node;
+    if(!found_entry)
+        return NULL;
     return &found_entry->node;
 }
 
 void UA_NodeStore_iterate(const UA_NodeStore *ns, UA_NodeStore_nodeVisitor visitor) {
-    struct cds_lfht     *ht = ns->ht;
+    struct cds_lfht *ht = ns->ht;
     struct cds_lfht_iter iter;
-
-    rcu_read_lock();
     cds_lfht_first(ht, &iter);
-    while(iter.node != UA_NULL) {
-        struct nodeEntry *found_entry = (struct nodeEntry *)cds_lfht_iter_get_node(&iter);
-        uatomic_inc(&found_entry->refcount);
-        const UA_Node      *node = &found_entry->node;
-        rcu_read_unlock();
-        visitor(node);
-        UA_NodeStore_release((const UA_Node *)node);
-        rcu_read_lock();
+    while(iter.node != NULL) {
+        struct nodeEntry *found_entry = (struct nodeEntry*)iter.node;
+        visitor(&found_entry->node);
         cds_lfht_next(ht, &iter);
     }
-    rcu_read_unlock();
 }

+ 1 - 1
src/server/ua_nodestore_hash.inc

@@ -5,7 +5,7 @@ static hash_t mod2(hash_t h, hash_t size) { return 1 + (h % (size - 2)); }
 
 /* Based on Murmur-Hash 3 by Austin Appleby (public domain, freely usable) */
 static hash_t hash_array(const UA_Byte *data, UA_UInt32 len, UA_UInt32 seed) {
-    if(data == UA_NULL)
+    if(data == NULL)
         return 0;
 
     const int32_t   nblocks = len / 4;

+ 86 - 89
src/server/ua_securechannel_manager.c

@@ -2,131 +2,124 @@
 #include "ua_session.h"
 #include "ua_statuscodes.h"
 
-UA_StatusCode UA_SecureChannelManager_init(UA_SecureChannelManager *cm, UA_UInt32 maxChannelCount,
-                                           UA_UInt32 tokenLifetime, UA_UInt32 startChannelId,
-                                           UA_UInt32 startTokenId) {
+UA_StatusCode
+UA_SecureChannelManager_init(UA_SecureChannelManager *cm, size_t maxChannelCount, UA_UInt32 tokenLifetime,
+                             UA_UInt32 startChannelId, UA_UInt32 startTokenId) {
     LIST_INIT(&cm->channels);
-    cm->lastChannelId      = startChannelId;
-    cm->lastTokenId        = startTokenId;
+    cm->lastChannelId = startChannelId;
+    cm->lastTokenId = startTokenId;
     cm->maxChannelLifetime = tokenLifetime;
-    cm->maxChannelCount    = maxChannelCount;
+    cm->maxChannelCount = maxChannelCount;
+    cm->currentChannelCount = 0;
     return UA_STATUSCODE_GOOD;
 }
 
 void UA_SecureChannelManager_deleteMembers(UA_SecureChannelManager *cm) {
-    channel_list_entry *current = LIST_FIRST(&cm->channels);
-    while(current) {
-        LIST_REMOVE(current, pointers);
-        UA_SecureChannel_deleteMembersCleanup(&current->channel);
-        UA_free(current);
-        current = LIST_FIRST(&cm->channels);
+    channel_list_entry *entry, *temp;
+    LIST_FOREACH_SAFE(entry, &cm->channels, pointers, temp) {
+        LIST_REMOVE(entry, pointers);
+        UA_SecureChannel_deleteMembersCleanup(&entry->channel);
+        UA_free(entry);
     }
 }
 
+/* remove channels that were not renewed or who have no connection attached */
 void UA_SecureChannelManager_cleanupTimedOut(UA_SecureChannelManager *cm, UA_DateTime now) {
-    channel_list_entry *entry = LIST_FIRST(&cm->channels);
-    /* remove channels that were not renewed or who have no connection attached */
-    while(entry) {
-        if(entry->channel.securityToken.createdAt +
-            (UA_DateTime)entry->channel.securityToken.revisedLifetime*10000 > now &&
-            entry->channel.connection) {
-            entry = LIST_NEXT(entry, pointers);
-        }else if(entry->channel.nextSecurityToken.tokenId > 0 &&
-                 entry->channel.nextSecurityToken.createdAt +
-                (UA_DateTime)entry->channel.nextSecurityToken.revisedLifetime*10000 > now &&
-                entry->channel.connection){
-            UA_SecureChannel_revolveTokens(&entry->channel);
-            entry = LIST_NEXT(entry, pointers);
-        }
-        else {
-            channel_list_entry *next = LIST_NEXT(entry, pointers);
+    channel_list_entry *entry, *temp;
+    LIST_FOREACH_SAFE(entry, &cm->channels, pointers, temp) {
+        UA_DateTime timeout =
+            entry->channel.securityToken.createdAt +
+            ((UA_DateTime)entry->channel.securityToken.revisedLifetime * 10000);
+        if(timeout < now || !entry->channel.connection) {
             LIST_REMOVE(entry, pointers);
             UA_SecureChannel_deleteMembersCleanup(&entry->channel);
+#ifndef UA_MULTITHREADING
+            cm->currentChannelCount--;
+#else
+            cm->currentChannelCount = uatomic_add_return(
+                    &cm->currentChannelCount, -1);
+#endif
             UA_free(entry);
-            entry = next;
+        } else if(entry->channel.nextSecurityToken.tokenId > 0) {
+            UA_SecureChannel_revolveTokens(&entry->channel);
         }
     }
 }
 
-UA_StatusCode UA_SecureChannelManager_open(UA_SecureChannelManager *cm, UA_Connection *conn,
-                                           const UA_OpenSecureChannelRequest *request,
-                                           UA_OpenSecureChannelResponse *response) {
-    switch(request->securityMode) {
-    case UA_MESSAGESECURITYMODE_NONE:
-        break;
-    case UA_MESSAGESECURITYMODE_INVALID:
-    case UA_MESSAGESECURITYMODE_SIGN:
-    case UA_MESSAGESECURITYMODE_SIGNANDENCRYPT:
-    default:
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADSECURITYMODEREJECTED;
-        return response->responseHeader.serviceResult;
-    }
-
+UA_StatusCode
+UA_SecureChannelManager_open(UA_SecureChannelManager *cm, UA_Connection *conn,
+                             const UA_OpenSecureChannelRequest *request,
+                             UA_OpenSecureChannelResponse *response) {
+    if(request->securityMode != UA_MESSAGESECURITYMODE_NONE)
+        return UA_STATUSCODE_BADSECURITYMODEREJECTED;
+    if(cm->currentChannelCount >= cm->maxChannelCount)
+        return UA_STATUSCODE_BADOUTOFMEMORY;
     channel_list_entry *entry = UA_malloc(sizeof(channel_list_entry));
     if(!entry)
         return UA_STATUSCODE_BADOUTOFMEMORY;
+#ifndef UA_MULTITHREADING
+    cm->currentChannelCount++;
+#else
+    cm->currentChannelCount = uatomic_add_return(&cm->currentChannelCount, 1);
+#endif
 
     UA_SecureChannel_init(&entry->channel);
     response->responseHeader.stringTableSize = 0;
-    response->responseHeader.timestamp       = UA_DateTime_now();
+    response->responseHeader.timestamp = UA_DateTime_now();
     response->serverProtocolVersion = 0;
 
-    entry->channel.securityToken.channelId       = cm->lastChannelId++;
-    entry->channel.securityToken.tokenId         = cm->lastTokenId++;
-    entry->channel.securityToken.createdAt       = UA_DateTime_now();
-    entry->channel.securityToken.revisedLifetime = (request->requestedLifetime > cm->maxChannelLifetime) ?
-                                                   cm->maxChannelLifetime : request->requestedLifetime;
-    //FIXME: pragmatic workaround to get clients requesting lifetime of 0 working
-    if(entry->channel.securityToken.revisedLifetime == 0) {
+    entry->channel.securityToken.channelId = cm->lastChannelId++;
+    entry->channel.securityToken.tokenId = cm->lastTokenId++;
+    entry->channel.securityToken.createdAt = UA_DateTime_now();
+    entry->channel.securityToken.revisedLifetime =
+            (request->requestedLifetime > cm->maxChannelLifetime) ?
+                    cm->maxChannelLifetime : request->requestedLifetime;
+    /* pragmatic workaround to get clients requesting lifetime of 0 working */
+    if(entry->channel.securityToken.revisedLifetime == 0)
         entry->channel.securityToken.revisedLifetime = cm->maxChannelLifetime;
-        //FIXME: I'd log it, but there is no pointer to the logger
-        // printf("Warning: client requests token lifetime of 0 in OpenSecureChannelRequest setting it to %llu\n", cm->maxChannelLifetime);
-    }
 
     UA_ByteString_copy(&request->clientNonce, &entry->channel.clientNonce);
-    entry->channel.serverAsymAlgSettings.securityPolicyUri =
-        UA_STRING_ALLOC("http://opcfoundation.org/UA/SecurityPolicy#None");
+    entry->channel.serverAsymAlgSettings.securityPolicyUri = UA_STRING_ALLOC(
+            "http://opcfoundation.org/UA/SecurityPolicy#None");
 
     UA_SecureChannel_generateNonce(&entry->channel.serverNonce);
     UA_ByteString_copy(&entry->channel.serverNonce, &response->serverNonce);
-    UA_ChannelSecurityToken_copy(&entry->channel.securityToken, &response->securityToken);
+    UA_ChannelSecurityToken_copy(&entry->channel.securityToken,
+            &response->securityToken);
 
     UA_Connection_attachSecureChannel(conn, &entry->channel);
     LIST_INSERT_HEAD(&cm->channels, entry, pointers);
-
     return UA_STATUSCODE_GOOD;
 }
 
-UA_StatusCode UA_SecureChannelManager_renew(UA_SecureChannelManager *cm, UA_Connection *conn,
-                                            const UA_OpenSecureChannelRequest *request,
-                                            UA_OpenSecureChannelResponse *response)
-{
+UA_StatusCode
+UA_SecureChannelManager_renew(UA_SecureChannelManager *cm, UA_Connection *conn,
+                              const UA_OpenSecureChannelRequest *request,
+                              UA_OpenSecureChannelResponse *response) {
     UA_SecureChannel *channel = conn->channel;
-    if(channel == UA_NULL)
+    if(!channel)
         return UA_STATUSCODE_BADINTERNALERROR;
 
-    //if no new security token is already issued
-    if(channel->nextSecurityToken.tokenId == 0){
-        channel->nextSecurityToken.channelId       = channel->securityToken.channelId;
-        //FIXME: UaExpert seems not to use new the new tokenid
-        channel->nextSecurityToken.tokenId         = cm->lastTokenId++;
-        //channel->nextSecurityToken.tokenId         = channel->securityToken.tokenId;
-        channel->nextSecurityToken.createdAt       = UA_DateTime_now();
-        channel->nextSecurityToken.revisedLifetime = (request->requestedLifetime > cm->maxChannelLifetime) ?
-                                                 cm->maxChannelLifetime : request->requestedLifetime;
-
-        //FIXME: pragmatic workaround to get clients requesting lifetime of 0 working
-        if(channel->nextSecurityToken.revisedLifetime == 0){
+    /* if no security token is already issued */
+    if(channel->nextSecurityToken.tokenId == 0) {
+        channel->nextSecurityToken.channelId = channel->securityToken.channelId;
+        //FIXME: UaExpert seems not to use the new tokenid
+        channel->nextSecurityToken.tokenId = cm->lastTokenId++;
+        //channel->nextSecurityToken.tokenId = channel->securityToken.tokenId;
+        channel->nextSecurityToken.createdAt = UA_DateTime_now();
+        channel->nextSecurityToken.revisedLifetime =
+                (request->requestedLifetime > cm->maxChannelLifetime) ?
+                        cm->maxChannelLifetime : request->requestedLifetime;
+
+        /* pragmatic workaround to get clients requesting lifetime of 0 working */
+        if(channel->nextSecurityToken.revisedLifetime == 0)
             channel->nextSecurityToken.revisedLifetime = cm->maxChannelLifetime;
-            //FIXME: I'd log it, but there is no pointer to the logger
-            // printf("Warning: client requests token lifetime of 0 in renewing SecureChannel setting it to %llu\n", cm->maxChannelLifetime);
-        }
-
     }
+
     if(channel->clientNonce.data)
         UA_ByteString_deleteMembers(&channel->clientNonce);
-    UA_ByteString_copy(&request->clientNonce, &channel->clientNonce);
 
+    UA_ByteString_copy(&request->clientNonce, &channel->clientNonce);
     UA_ByteString_copy(&channel->serverNonce, &response->serverNonce);
     UA_ChannelSecurityToken_copy(&channel->nextSecurityToken, &response->securityToken);
     return UA_STATUSCODE_GOOD;
@@ -138,19 +131,23 @@ UA_SecureChannel * UA_SecureChannelManager_get(UA_SecureChannelManager *cm, UA_U
         if(entry->channel.securityToken.channelId == channelId)
             return &entry->channel;
     }
-    return UA_NULL;
+    return NULL;
 }
 
 UA_StatusCode UA_SecureChannelManager_close(UA_SecureChannelManager *cm, UA_UInt32 channelId) {
     channel_list_entry *entry;
     LIST_FOREACH(entry, &cm->channels, pointers) {
-        if(entry->channel.securityToken.channelId == channelId)
-            break;
+        if(entry->channel.securityToken.channelId == channelId) {
+            LIST_REMOVE(entry, pointers);
+            UA_SecureChannel_deleteMembersCleanup(&entry->channel);
+            UA_free(entry);
+#ifndef UA_MULTITHREADING
+            cm->currentChannelCount--;
+#else
+            cm->currentChannelCount = uatomic_add_return(&cm->currentChannelCount, -1);
+#endif
+            return UA_STATUSCODE_GOOD;
+        }
     }
-    if(!entry)
-        return UA_STATUSCODE_BADINTERNALERROR;
-    LIST_REMOVE(entry, pointers);
-    UA_SecureChannel_deleteMembersCleanup(&entry->channel);
-    UA_free(entry);
-    return UA_STATUSCODE_GOOD;
+    return UA_STATUSCODE_BADINTERNALERROR;
 }

+ 24 - 12
src/server/ua_securechannel_manager.h

@@ -13,7 +13,8 @@ typedef struct channel_list_entry {
 
 typedef struct UA_SecureChannelManager {
     LIST_HEAD(channel_list, channel_list_entry) channels; // doubly-linked list of channels
-    UA_Int32    maxChannelCount;
+    size_t    maxChannelCount;
+    size_t currentChannelCount;
     UA_DateTime maxChannelLifetime;
     UA_MessageSecurityMode securityMode;
     UA_DateTime channelLifeTime;
@@ -21,18 +22,29 @@ typedef struct UA_SecureChannelManager {
     UA_UInt32   lastTokenId;
 } UA_SecureChannelManager;
 
-UA_StatusCode UA_SecureChannelManager_init(UA_SecureChannelManager *cm, UA_UInt32 maxChannelCount,
-                                           UA_UInt32 tokenLifetime, UA_UInt32 startChannelId,
-                                           UA_UInt32 startTokenId);
+UA_StatusCode
+UA_SecureChannelManager_init(UA_SecureChannelManager *cm, size_t maxChannelCount,
+                             UA_UInt32 tokenLifetime, UA_UInt32 startChannelId,
+                             UA_UInt32 startTokenId);
+
 void UA_SecureChannelManager_deleteMembers(UA_SecureChannelManager *cm);
+
 void UA_SecureChannelManager_cleanupTimedOut(UA_SecureChannelManager *cm, UA_DateTime now);
-UA_StatusCode UA_SecureChannelManager_open(UA_SecureChannelManager *cm, UA_Connection *conn,
-                                           const UA_OpenSecureChannelRequest *request,
-                                           UA_OpenSecureChannelResponse *response);
-UA_StatusCode UA_SecureChannelManager_renew(UA_SecureChannelManager *cm, UA_Connection *conn,
-                                            const UA_OpenSecureChannelRequest *request,
-                                            UA_OpenSecureChannelResponse *response);
-UA_SecureChannel * UA_SecureChannelManager_get(UA_SecureChannelManager *cm, UA_UInt32 channelId);
-UA_StatusCode UA_SecureChannelManager_close(UA_SecureChannelManager *cm, UA_UInt32 channelId);
+
+UA_StatusCode
+UA_SecureChannelManager_open(UA_SecureChannelManager *cm, UA_Connection *conn,
+                             const UA_OpenSecureChannelRequest *request,
+                             UA_OpenSecureChannelResponse *response);
+
+UA_StatusCode
+UA_SecureChannelManager_renew(UA_SecureChannelManager *cm, UA_Connection *conn,
+                              const UA_OpenSecureChannelRequest *request,
+                              UA_OpenSecureChannelResponse *response);
+
+UA_SecureChannel *
+UA_SecureChannelManager_get(UA_SecureChannelManager *cm, UA_UInt32 channelId);
+
+UA_StatusCode
+UA_SecureChannelManager_close(UA_SecureChannelManager *cm, UA_UInt32 channelId);
 
 #endif /* UA_CHANNEL_MANAGER_H_ */

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 628 - 437
src/server/ua_server.c


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 1716
src/server/ua_server_addressspace.c


+ 268 - 221
src/server/ua_server_binary.c

@@ -1,11 +1,11 @@
 #include "ua_util.h"
 #include "ua_server_internal.h"
-#include "ua_types_encoding_binary.h"
-#include "ua_transport_generated.h"
 #include "ua_services.h"
-#include "ua_statuscodes.h"
 #include "ua_securechannel_manager.h"
 #include "ua_session_manager.h"
+#include "ua_types_generated_encoding_binary.h"
+#include "ua_transport_generated.h"
+#include "ua_transport_generated_encoding_binary.h"
 
 /** Max size of messages that are allocated on the stack */
 #define MAX_STACK_MESSAGE 65536
@@ -42,18 +42,18 @@ static void processHEL(UA_Connection *connection, const UA_ByteString *msg, size
     ackHeader.messageSize =  8 + 20; /* ackHeader + ackMessage */
 
     UA_ByteString ack_msg;
-    if(connection->getBuffer(connection, &ack_msg) != UA_STATUSCODE_GOOD)
+    if(connection->getSendBuffer(connection, connection->remoteConf.recvBufferSize,
+                                 &ack_msg) != UA_STATUSCODE_GOOD)
         return;
 
     size_t tmpPos = 0;
     UA_TcpMessageHeader_encodeBinary(&ackHeader, &ack_msg, &tmpPos);
     UA_TcpAcknowledgeMessage_encodeBinary(&ackMessage, &ack_msg, &tmpPos);
-    if(connection->write(connection, &ack_msg, ackHeader.messageSize) != UA_STATUSCODE_GOOD)
-        connection->releaseBuffer(connection, &ack_msg);
+    ack_msg.length = ackHeader.messageSize;
+    connection->send(connection, &ack_msg);
 }
 
-static void processOPN(UA_Connection *connection, UA_Server *server, const UA_ByteString *msg,
-                       size_t *pos) {
+static void processOPN(UA_Connection *connection, UA_Server *server, const UA_ByteString *msg, size_t *pos) {
     if(connection->state != UA_CONNECTION_ESTABLISHED) {
         connection->close(connection);
         return;
@@ -112,7 +112,7 @@ static void processOPN(UA_Connection *connection, UA_Server *server, const UA_By
                                                UA_ENCODINGOFFSET_BINARY);
 
     UA_ByteString resp_msg;
-    retval = connection->getBuffer(connection, &resp_msg);
+    retval = connection->getSendBuffer(connection, connection->remoteConf.recvBufferSize, &resp_msg);
     if(retval != UA_STATUSCODE_GOOD) {
         UA_OpenSecureChannelResponse_deleteMembers(&p);
         UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
@@ -126,16 +126,14 @@ static void processOPN(UA_Connection *connection, UA_Server *server, const UA_By
     retval |= UA_OpenSecureChannelResponse_encodeBinary(&p, &resp_msg, &tmpPos);
 
     if(retval != UA_STATUSCODE_GOOD) {
-        connection->releaseBuffer(connection, &resp_msg);
+        connection->releaseSendBuffer(connection, &resp_msg);
         connection->close(connection);
     } else {
         respHeader.messageHeader.messageSize = tmpPos;
         tmpPos = 0;
         UA_SecureConversationMessageHeader_encodeBinary(&respHeader, &resp_msg, &tmpPos);
-
-        if(connection->write(connection, &resp_msg,
-                             respHeader.messageHeader.messageSize) != UA_STATUSCODE_GOOD)
-            connection->releaseBuffer(connection, &resp_msg);
+        resp_msg.length = respHeader.messageHeader.messageSize;
+        connection->send(connection, &resp_msg);
     }
 
     UA_OpenSecureChannelResponse_deleteMembers(&p);
@@ -148,246 +146,291 @@ static void init_response_header(const UA_RequestHeader *p, UA_ResponseHeader *r
     r->timestamp = UA_DateTime_now();
 }
 
-/* The request/response are casted to the header (first element of their struct) */
-static void invoke_service(UA_Server *server, UA_SecureChannel *channel, UA_UInt32 requestId,
-                           UA_RequestHeader *request, const UA_DataType *responseType,
-                           void (*service)(UA_Server*, UA_Session*, void*, void*)) {
-    UA_ResponseHeader *response = UA_alloca(responseType->memSize);
-    UA_init(response, responseType);
-    init_response_header(request, response);
-    /* try to get the session from the securechannel first */
-    UA_Session *session = UA_SecureChannel_getSession(channel, &request->authenticationToken);
-    if(!session || session->channel != channel) {
-        response->serviceResult = UA_STATUSCODE_BADSESSIONIDINVALID;
-    } else if(session->activated == UA_FALSE) {
-        response->serviceResult = UA_STATUSCODE_BADSESSIONNOTACTIVATED;
-        /* the session is invalidated FIXME: do this delayed*/
-        UA_SessionManager_removeSession(&server->sessionManager, server, &request->authenticationToken);
-    } else {
-        UA_Session_updateLifetime(session);
-        service(server, session, request, response);
-    }
-    UA_StatusCode retval = UA_SecureChannel_sendBinaryMessage(channel, requestId, response, responseType);
-    if(retval != UA_STATUSCODE_GOOD) {
-        if(retval == UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED)
-            response->serviceResult = UA_STATUSCODE_BADRESPONSETOOLARGE;
-        else
-            response->serviceResult = retval;
-        UA_SecureChannel_sendBinaryMessage(channel, requestId, response, &UA_TYPES[UA_TYPES_SERVICEFAULT]);
-    }
-    UA_deleteMembers(response, responseType);
-}
-
-#define INVOKE_SERVICE(REQUEST, RESPONSETYPE) do {                      \
-        UA_##REQUEST##Request p;                                        \
-        if(UA_##REQUEST##Request_decodeBinary(msg, pos, &p))            \
-            return;                                                     \
-        invoke_service(server, clientChannel, sequenceHeader.requestId, \
-                       &p.requestHeader, &UA_TYPES[RESPONSETYPE],       \
-                       (void (*)(UA_Server*, UA_Session*, void*,void*))Service_##REQUEST); \
-        UA_##REQUEST##Request_deleteMembers(&p);                        \
-} while(0)
-
-static void processMSG(UA_Connection *connection, UA_Server *server, const UA_ByteString *msg, size_t *pos) {
-    /* Read in the securechannel */
-    UA_UInt32 secureChannelId;
-    UA_StatusCode retval = UA_UInt32_decodeBinary(msg, pos, &secureChannelId);
-    if(retval != UA_STATUSCODE_GOOD)
-        return;
-
-    /* the anonymous channel is used e.g. to allow getEndpoints without a channel */
-    UA_SecureChannel *clientChannel = connection->channel;
-    UA_SecureChannel anonymousChannel;
-    if(!clientChannel) {
-        UA_SecureChannel_init(&anonymousChannel);
-        anonymousChannel.connection = connection;
-        clientChannel = &anonymousChannel;
-#ifdef EXTENSION_STATELESS
-        UA_SecureChannel_attachSession(&anonymousChannel, &anonymousSession);
-#endif
-    }
-
-    /* Read the security header */
-    UA_UInt32 tokenId = 0;
-    UA_SequenceHeader sequenceHeader;
-    retval = UA_UInt32_decodeBinary(msg, pos, &tokenId);
-    retval |= UA_SequenceHeader_decodeBinary(msg, pos, &sequenceHeader);
-#ifndef EXTENSION_STATELESS
-    if(retval != UA_STATUSCODE_GOOD || tokenId==0) //0 is invalid
-#else
-    if(retval != UA_STATUSCODE_GOOD)
-#endif
-        return;
-
-    if(clientChannel != &anonymousChannel){
-        if(tokenId!=clientChannel->securityToken.tokenId){
-            //is client using a newly issued token?
-            if(tokenId==clientChannel->nextSecurityToken.tokenId){ //tokenId is not 0
-                UA_SecureChannel_revolveTokens(clientChannel);
-            }else{
-                //FIXME: how to react to this, what do we have to return? Or just kill the channel
-            }
-        }
-    }
-
-    /* Read the request type */
-    UA_NodeId requestType;
-    if(UA_NodeId_decodeBinary(msg, pos, &requestType) != UA_STATUSCODE_GOOD)
-        return;
-    if(requestType.identifierType != UA_NODEIDTYPE_NUMERIC) {
-        UA_NodeId_deleteMembers(&requestType);
-        return;
-    }
-
-    switch(requestType.identifier.numeric - UA_ENCODINGOFFSET_BINARY) {
-    case UA_NS0ID_GETENDPOINTSREQUEST: {
-        UA_GetEndpointsRequest p;
-        UA_GetEndpointsResponse r;
-        if(UA_GetEndpointsRequest_decodeBinary(msg, pos, &p))
-            return;
-        UA_GetEndpointsResponse_init(&r);
-        init_response_header(&p.requestHeader, &r.responseHeader);
-        Service_GetEndpoints(server, &p, &r);
-        UA_GetEndpointsRequest_deleteMembers(&p);
-        UA_SecureChannel_sendBinaryMessage(clientChannel, sequenceHeader.requestId, &r,
-                                           &UA_TYPES[UA_TYPES_GETENDPOINTSRESPONSE]);
-        UA_GetEndpointsResponse_deleteMembers(&r);
+static void
+getServicePointers(UA_UInt32 requestTypeId, const UA_DataType **requestType,
+                   const UA_DataType **responseType, UA_Service *service) {
+    switch(requestTypeId - UA_ENCODINGOFFSET_BINARY) {
+    case UA_NS0ID_GETENDPOINTSREQUEST:
+        *service = (UA_Service)Service_GetEndpoints;
+        *requestType = &UA_TYPES[UA_TYPES_GETENDPOINTSREQUEST];
+        *responseType = &UA_TYPES[UA_TYPES_GETENDPOINTSRESPONSE];
         break;
-    }
-
-    case UA_NS0ID_FINDSERVERSREQUEST: {
-        UA_FindServersRequest  p;
-        UA_FindServersResponse r;
-        if(UA_FindServersRequest_decodeBinary(msg, pos, &p))
-            return;
-        UA_FindServersResponse_init(&r);
-        init_response_header(&p.requestHeader, &r.responseHeader);
-        Service_FindServers(server, &p, &r);
-        UA_FindServersRequest_deleteMembers(&p);
-        UA_SecureChannel_sendBinaryMessage(clientChannel, sequenceHeader.requestId, &r,
-                                           &UA_TYPES[UA_TYPES_FINDSERVERSRESPONSE]);
-        UA_FindServersResponse_deleteMembers(&r);
+    case UA_NS0ID_FINDSERVERSREQUEST:
+        *service = (UA_Service)Service_FindServers;
+        *requestType = &UA_TYPES[UA_TYPES_FINDSERVERSREQUEST];
+        *responseType = &UA_TYPES[UA_TYPES_FINDSERVERSRESPONSE];
         break;
-    }
-
-    case UA_NS0ID_CREATESESSIONREQUEST: {
-        UA_CreateSessionRequest  p;
-        UA_CreateSessionResponse r;
-        if(UA_CreateSessionRequest_decodeBinary(msg, pos, &p))
-            return;
-        UA_CreateSessionResponse_init(&r);
-        init_response_header(&p.requestHeader, &r.responseHeader);
-        Service_CreateSession(server, clientChannel, &p, &r);
-        UA_CreateSessionRequest_deleteMembers(&p);
-        UA_SecureChannel_sendBinaryMessage(clientChannel, sequenceHeader.requestId, &r,
-                                           &UA_TYPES[UA_TYPES_CREATESESSIONRESPONSE]);
-        UA_CreateSessionResponse_deleteMembers(&r);
+    case UA_NS0ID_CREATESESSIONREQUEST:
+        *service = (UA_Service)Service_CreateSession;
+        *requestType = &UA_TYPES[UA_TYPES_CREATESESSIONREQUEST];
+        *responseType = &UA_TYPES[UA_TYPES_CREATESESSIONRESPONSE];
         break;
-    }
-
-    case UA_NS0ID_ACTIVATESESSIONREQUEST: {
-        UA_ActivateSessionRequest  p;
-        UA_ActivateSessionResponse r;
-        if(UA_ActivateSessionRequest_decodeBinary(msg, pos, &p))
-            return;
-        UA_ActivateSessionResponse_init(&r);
-        init_response_header(&p.requestHeader, &r.responseHeader);
-        Service_ActivateSession(server, clientChannel, &p, &r);
-        UA_ActivateSessionRequest_deleteMembers(&p);
-        UA_SecureChannel_sendBinaryMessage(clientChannel, sequenceHeader.requestId, &r,
-                                           &UA_TYPES[UA_TYPES_ACTIVATESESSIONRESPONSE]);
-        UA_ActivateSessionResponse_deleteMembers(&r);
+    case UA_NS0ID_ACTIVATESESSIONREQUEST:
+        *service = (UA_Service)Service_ActivateSession;
+        *requestType = &UA_TYPES[UA_TYPES_ACTIVATESESSIONREQUEST];
+        *responseType = &UA_TYPES[UA_TYPES_ACTIVATESESSIONRESPONSE];
         break;
-    }
-    
     case UA_NS0ID_CLOSESESSIONREQUEST:
-        INVOKE_SERVICE(CloseSession, UA_TYPES_CLOSESESSIONRESPONSE);
+        *service = (UA_Service)Service_CloseSession;
+        *requestType = &UA_TYPES[UA_TYPES_CLOSESESSIONREQUEST];
+        *responseType = &UA_TYPES[UA_TYPES_CLOSESESSIONRESPONSE];
         break;
     case UA_NS0ID_READREQUEST:
-        INVOKE_SERVICE(Read, UA_TYPES_READRESPONSE);
+        *service = (UA_Service)Service_Read;
+        *requestType = &UA_TYPES[UA_TYPES_READREQUEST];
+        *responseType = &UA_TYPES[UA_TYPES_READRESPONSE];
         break;
     case UA_NS0ID_WRITEREQUEST:
-        INVOKE_SERVICE(Write, UA_TYPES_WRITERESPONSE);
+        *service = (UA_Service)Service_Write;
+        *requestType = &UA_TYPES[UA_TYPES_WRITEREQUEST];
+        *responseType = &UA_TYPES[UA_TYPES_WRITERESPONSE];
         break;
     case UA_NS0ID_BROWSEREQUEST:
-        INVOKE_SERVICE(Browse, UA_TYPES_BROWSERESPONSE);
+        *service = (UA_Service)Service_Browse;
+        *requestType = &UA_TYPES[UA_TYPES_BROWSEREQUEST];
+        *responseType = &UA_TYPES[UA_TYPES_BROWSERESPONSE];
         break;
     case UA_NS0ID_BROWSENEXTREQUEST:
-        INVOKE_SERVICE(BrowseNext, UA_TYPES_BROWSENEXTRESPONSE);
-        break;
-    case UA_NS0ID_ADDREFERENCESREQUEST:
-        INVOKE_SERVICE(AddReferences, UA_TYPES_ADDREFERENCESRESPONSE);
+        *service = (UA_Service)Service_BrowseNext;
+        *requestType = &UA_TYPES[UA_TYPES_BROWSENEXTREQUEST];
+        *responseType = &UA_TYPES[UA_TYPES_BROWSENEXTRESPONSE];
         break;
     case UA_NS0ID_REGISTERNODESREQUEST:
-        INVOKE_SERVICE(RegisterNodes, UA_TYPES_REGISTERNODESRESPONSE);
+        *service = (UA_Service)Service_RegisterNodes;
+        *requestType = &UA_TYPES[UA_TYPES_REGISTERNODESREQUEST];
+        *responseType = &UA_TYPES[UA_TYPES_REGISTERNODESRESPONSE];
         break;
     case UA_NS0ID_UNREGISTERNODESREQUEST:
-        INVOKE_SERVICE(UnregisterNodes, UA_TYPES_UNREGISTERNODESRESPONSE);
+        *service = (UA_Service)Service_UnregisterNodes;
+        *requestType = &UA_TYPES[UA_TYPES_UNREGISTERNODESREQUEST];
+        *responseType = &UA_TYPES[UA_TYPES_UNREGISTERNODESRESPONSE];
         break;
     case UA_NS0ID_TRANSLATEBROWSEPATHSTONODEIDSREQUEST:
-        INVOKE_SERVICE(TranslateBrowsePathsToNodeIds, UA_TYPES_TRANSLATEBROWSEPATHSTONODEIDSRESPONSE);
+        *service = (UA_Service)Service_TranslateBrowsePathsToNodeIds;
+        *requestType = &UA_TYPES[UA_TYPES_TRANSLATEBROWSEPATHSTONODEIDSREQUEST];
+        *responseType = &UA_TYPES[UA_TYPES_TRANSLATEBROWSEPATHSTONODEIDSRESPONSE];
         break;
-#ifdef ENABLE_SUBSCRIPTIONS    
+
+#ifdef ENABLE_SUBSCRIPTIONS
     case UA_NS0ID_CREATESUBSCRIPTIONREQUEST:
-        INVOKE_SERVICE(CreateSubscription, UA_TYPES_CREATESUBSCRIPTIONRESPONSE);
+        *service = (UA_Service)Service_CreateSubscription;
+        *requestType = &UA_TYPES[UA_TYPES_CREATESUBSCRIPTIONREQUEST];
+        *responseType = &UA_TYPES[UA_TYPES_CREATESUBSCRIPTIONRESPONSE];
         break;
     case UA_NS0ID_PUBLISHREQUEST:
-        INVOKE_SERVICE(Publish, UA_TYPES_PUBLISHRESPONSE);
+        *service = (UA_Service)Service_Publish;
+        *requestType = &UA_TYPES[UA_TYPES_PUBLISHREQUEST];
+        *responseType = &UA_TYPES[UA_TYPES_PUBLISHRESPONSE];
+        break;
+    case UA_NS0ID_REPUBLISHREQUEST:
+        *service = (UA_Service)Service_Republish;
+        *requestType = &UA_TYPES[UA_TYPES_REPUBLISHREQUEST];
+        *responseType = &UA_TYPES[UA_TYPES_REPUBLISHRESPONSE];
         break;
     case UA_NS0ID_MODIFYSUBSCRIPTIONREQUEST:
-        INVOKE_SERVICE(ModifySubscription, UA_TYPES_MODIFYSUBSCRIPTIONRESPONSE);
+        *service = (UA_Service)Service_ModifySubscription;
+        *requestType = &UA_TYPES[UA_TYPES_MODIFYSUBSCRIPTIONREQUEST];
+        *responseType = &UA_TYPES[UA_TYPES_MODIFYSUBSCRIPTIONRESPONSE];
         break;
     case UA_NS0ID_DELETESUBSCRIPTIONSREQUEST:
-        INVOKE_SERVICE(DeleteSubscriptions, UA_TYPES_DELETESUBSCRIPTIONSRESPONSE);
+        *service = (UA_Service)Service_DeleteSubscriptions;
+        *requestType = &UA_TYPES[UA_TYPES_DELETESUBSCRIPTIONSREQUEST];
+        *responseType = &UA_TYPES[UA_TYPES_DELETESUBSCRIPTIONSRESPONSE];
         break;
     case UA_NS0ID_CREATEMONITOREDITEMSREQUEST:
-        INVOKE_SERVICE(CreateMonitoredItems, UA_TYPES_CREATEMONITOREDITEMSRESPONSE);
+        *service = (UA_Service)Service_CreateMonitoredItems;
+        *requestType = &UA_TYPES[UA_TYPES_CREATEMONITOREDITEMSREQUEST];
+        *responseType = &UA_TYPES[UA_TYPES_CREATEMONITOREDITEMSRESPONSE];
         break;
     case UA_NS0ID_DELETEMONITOREDITEMSREQUEST:
-        INVOKE_SERVICE(DeleteMonitoredItems, UA_TYPES_DELETEMONITOREDITEMSRESPONSE);
+        *service = (UA_Service)Service_DeleteMonitoredItems;
+        *requestType = &UA_TYPES[UA_TYPES_DELETEMONITOREDITEMSREQUEST];
+        *responseType = &UA_TYPES[UA_TYPES_DELETEMONITOREDITEMSRESPONSE];
         break;
 #endif
+
 #ifdef ENABLE_METHODCALLS
     case UA_NS0ID_CALLREQUEST:
-        INVOKE_SERVICE(Call, UA_TYPES_CALLRESPONSE);
+        *service = (UA_Service)Service_Call;
+        *requestType = &UA_TYPES[UA_TYPES_CALLREQUEST];
+        *responseType = &UA_TYPES[UA_TYPES_CALLRESPONSE];
 	break;
 #endif
-#ifdef ENABLE_ADDNODES 
+
+#ifdef ENABLE_NODEMANAGEMENT
     case UA_NS0ID_ADDNODESREQUEST:
-        INVOKE_SERVICE(AddNodes, UA_TYPES_ADDNODESRESPONSE);
+        *service = (UA_Service)Service_AddNodes;
+        *requestType = &UA_TYPES[UA_TYPES_ADDNODESREQUEST];
+        *responseType = &UA_TYPES[UA_TYPES_ADDNODESRESPONSE];
+        break;
+    case UA_NS0ID_ADDREFERENCESREQUEST:
+        *service = (UA_Service)Service_AddReferences;
+        *requestType = &UA_TYPES[UA_TYPES_ADDREFERENCESREQUEST];
+        *responseType = &UA_TYPES[UA_TYPES_ADDREFERENCESRESPONSE];
         break;
     case UA_NS0ID_DELETENODESREQUEST:
-      INVOKE_SERVICE(DeleteNodes, UA_TYPES_DELETENODESRESPONSE);
-      break;
+        *service = (UA_Service)Service_DeleteNodes;
+        *requestType = &UA_TYPES[UA_TYPES_DELETENODESREQUEST];
+        *responseType = &UA_TYPES[UA_TYPES_DELETENODESRESPONSE];
+        break;
+    case UA_NS0ID_DELETEREFERENCESREQUEST:
+        *service = (UA_Service)Service_DeleteReferences;
+        *requestType = &UA_TYPES[UA_TYPES_DELETEREFERENCESREQUEST];
+        *responseType = &UA_TYPES[UA_TYPES_DELETEREFERENCESRESPONSE];
+        break;
 #endif
-    default: {
-        if(requestType.namespaceIndex == 0 && requestType.identifier.numeric==787)
-            UA_LOG_INFO(server->logger, UA_LOGCATEGORY_COMMUNICATION,
-                        "Client requested a subscription that are not supported, the message will be skipped");
-        else
-            UA_LOG_INFO(server->logger, UA_LOGCATEGORY_COMMUNICATION, "Unknown request: NodeId(ns=%d, i=%d)",
-                        requestType.namespaceIndex, requestType.identifier.numeric);
-        UA_RequestHeader p;
-        UA_ServiceFault r;
-        if(UA_RequestHeader_decodeBinary(msg, pos, &p) != UA_STATUSCODE_GOOD)
+
+    default:
+        break;
+    }
+}
+
+static void
+sendError(UA_SecureChannel *channel, const UA_ByteString *msg, size_t pos,
+          UA_UInt32 requestId, UA_StatusCode error) {
+    UA_RequestHeader p;
+    if(UA_RequestHeader_decodeBinary(msg, &pos, &p) != UA_STATUSCODE_GOOD)
+        return;
+    UA_ResponseHeader r;
+    UA_ResponseHeader_init(&r);
+    init_response_header(&p, &r);
+    r.serviceResult = error;
+    UA_SecureChannel_sendBinaryMessage(channel, requestId, &r,
+                                       &UA_TYPES[UA_TYPES_SERVICEFAULT]);
+    UA_RequestHeader_deleteMembers(&p);
+    UA_ResponseHeader_deleteMembers(&r);
+}
+
+static void
+processMSG(UA_Connection *connection, UA_Server *server, const UA_ByteString *msg, size_t *pos) {
+    /* If we cannot decode these, don't respond */
+    UA_UInt32 secureChannelId = 0;
+    UA_UInt32 tokenId = 0;
+    UA_SequenceHeader sequenceHeader;
+    UA_NodeId requestTypeId;
+    UA_StatusCode retval = UA_UInt32_decodeBinary(msg, pos, &secureChannelId);
+    retval |= UA_UInt32_decodeBinary(msg, pos, &tokenId);
+    retval |= UA_SequenceHeader_decodeBinary(msg, pos, &sequenceHeader);
+    retval = UA_NodeId_decodeBinary(msg, pos, &requestTypeId);
+    if(retval != UA_STATUSCODE_GOOD)
+        return;
+
+    UA_SecureChannel *channel = connection->channel;
+    UA_SecureChannel anonymousChannel;
+    if(!channel) {
+        UA_SecureChannel_init(&anonymousChannel);
+        anonymousChannel.connection = connection;
+        channel = &anonymousChannel;
+    }
+
+    /* Test if the secure channel is ok */
+    if(secureChannelId != channel->securityToken.channelId)
+        return;
+    if(tokenId != channel->securityToken.tokenId) {
+        if(tokenId != channel->nextSecurityToken.tokenId) {
+            /* close the securechannel but keep the connection open */
+            UA_LOG_INFO(server->logger, UA_LOGCATEGORY_SECURECHANNEL,
+                        "Request with a wrong security token. Closing the SecureChannel %i.",
+                        channel->securityToken.channelId);
+            Service_CloseSecureChannel(server, channel->securityToken.channelId);
             return;
-        UA_ServiceFault_init(&r);
-        init_response_header(&p, &r.responseHeader);
-        r.responseHeader.serviceResult = UA_STATUSCODE_BADSERVICEUNSUPPORTED;
-#ifdef EXTENSION_STATELESS
-        if(retval != UA_STATUSCODE_GOOD)
-            r.responseHeader.serviceResult = retval;
+        }
+        UA_SecureChannel_revolveTokens(channel);
+    }
+
+    /* Test if the service type nodeid has the right format */
+    if(requestTypeId.identifierType != UA_NODEIDTYPE_NUMERIC ||
+       requestTypeId.namespaceIndex != 0) {
+        UA_NodeId_deleteMembers(&requestTypeId);
+        sendError(channel, msg, *pos, sequenceHeader.requestId, UA_STATUSCODE_BADSERVICEUNSUPPORTED);
+        return;
+    }
+
+    /* Get the service pointers */
+    UA_Service service = NULL;
+    const UA_DataType *requestType = NULL;
+    const UA_DataType *responseType = NULL;
+    getServicePointers(requestTypeId.identifier.numeric, &requestType, &responseType, &service);
+    if(!service) {
+        /* The service is not supported */
+        if(requestTypeId.identifier.numeric==787)
+            UA_LOG_INFO(server->logger, UA_LOGCATEGORY_SERVER,
+                        "Client requested a subscription that are not supported, "
+                        "the message will be skipped");
+        else
+            UA_LOG_INFO(server->logger, UA_LOGCATEGORY_SERVER, "Unknown request: NodeId(ns=%d, i=%d)",
+                        requestTypeId.namespaceIndex, requestTypeId.identifier.numeric);
+        sendError(channel, msg, *pos, sequenceHeader.requestId, UA_STATUSCODE_BADSERVICEUNSUPPORTED);
+        return;
+    }
+
+    /* Most services can only be called with a valid securechannel */
+#ifndef EXTENSION_STATELESS
+    if(channel == &anonymousChannel &&
+       requestType->typeIndex > UA_TYPES_OPENSECURECHANNELREQUEST) {
+        sendError(channel, msg, *pos, sequenceHeader.requestId, UA_STATUSCODE_BADSECURECHANNELIDINVALID);
+        return;
+    }
 #endif
-        UA_SecureChannel_sendBinaryMessage(clientChannel, sequenceHeader.requestId, &r,
-                                           &UA_TYPES[UA_TYPES_SERVICEFAULT]);
-        UA_RequestHeader_deleteMembers(&p);
-        UA_ServiceFault_deleteMembers(&r);
-        break;
+
+    /* Decode the request */
+    void *request = UA_alloca(requestType->memSize);
+    size_t oldpos = *pos;
+    retval = UA_decodeBinary(msg, pos, request, requestType);
+    if(retval != UA_STATUSCODE_GOOD) {
+        sendError(channel, msg, oldpos, sequenceHeader.requestId, retval);
+        return;
     }
+
+    /* Find the matching session */
+    UA_Session *session =
+        UA_SecureChannel_getSession(channel, &((UA_RequestHeader*)request)->authenticationToken);
+    UA_Session anonymousSession;
+    if(!session) {
+        UA_Session_init(&anonymousSession);
+        anonymousSession.channel = channel;
+        anonymousSession.activated = UA_TRUE;
+        session = &anonymousSession;
+    }
+
+    /* Test if the session is valid */
+    if(!session->activated && requestType->typeIndex != UA_TYPES_ACTIVATESESSIONREQUEST) {
+        UA_LOG_INFO(server->logger, UA_LOGCATEGORY_SERVER, "Client tries to call a service with a non-activated session");
+        sendError(channel, msg, *pos, sequenceHeader.requestId, UA_STATUSCODE_BADSESSIONNOTACTIVATED);
+        return;
     }
+#ifndef EXTENSION_STATELESS
+    if(session == &anonymousSession &&
+       requestType->typeIndex > UA_TYPES_ACTIVATESESSIONREQUEST) {
+        UA_LOG_INFO(server->logger, UA_LOGCATEGORY_SERVER, "Client tries to call a service without a session");
+        sendError(channel, msg, *pos, sequenceHeader.requestId, UA_STATUSCODE_BADSESSIONIDINVALID);
+        return;
+    }
+#endif
+
+    /* Call the service */
+    UA_Session_updateLifetime(session);
+    void *response = UA_alloca(responseType->memSize);
+    UA_init(response, responseType);
+    init_response_header(request, response);
+    service(server, session, request, response);
+
+    /* Send the response */
+    retval = UA_SecureChannel_sendBinaryMessage(channel, sequenceHeader.requestId,
+                                                response, responseType);
+    if(retval != UA_STATUSCODE_GOOD) {
+        /* e.g. UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED */
+        sendError(channel, msg, oldpos, sequenceHeader.requestId, retval);
+    }
+
+    /* Clean up */
+    UA_deleteMembers(request, requestType);
+    UA_deleteMembers(response, responseType);
+    return;
 }
 
-static void processCLO(UA_Connection *connection, UA_Server *server, const UA_ByteString *msg, size_t *pos) {
+static void
+processCLO(UA_Connection *connection, UA_Server *server, const UA_ByteString *msg, size_t *pos) {
     UA_UInt32 secureChannelId;
     UA_StatusCode retval = UA_UInt32_decodeBinary(msg, pos, &secureChannelId);
     if(retval != UA_STATUSCODE_GOOD || !connection->channel ||
@@ -396,20 +439,22 @@ static void processCLO(UA_Connection *connection, UA_Server *server, const UA_By
     Service_CloseSecureChannel(server, secureChannelId);
 }
 
-void UA_Server_processBinaryMessage(UA_Server *server, UA_Connection *connection, UA_ByteString *msg) {
-    if(msg->length <= 0)
-        return;
+/**
+ * process binary message received from Connection
+ * dose not modify UA_ByteString you have to free it youself.
+ * use of connection->getSendBuffer() and connection->sent() to answer Message
+ */
+void UA_Server_processBinaryMessage(UA_Server *server, UA_Connection *connection, const UA_ByteString *msg) {
     size_t pos = 0;
     UA_TcpMessageHeader tcpMessageHeader;
     do {
         if(UA_TcpMessageHeader_decodeBinary(msg, &pos, &tcpMessageHeader)) {
-            UA_LOG_INFO(server->logger, UA_LOGCATEGORY_COMMUNICATION, "Decoding of message header failed");
+            UA_LOG_INFO(server->logger, UA_LOGCATEGORY_NETWORK, "Decoding of message header failed");
             connection->close(connection);
             break;
         }
 
         size_t targetpos = pos - 8 + tcpMessageHeader.messageSize;
-
         switch(tcpMessageHeader.messageTypeAndFinal & 0xffffff) {
         case UA_MESSAGETYPEANDFINAL_HELF & 0xffffff:
             processHEL(connection, msg, &pos);
@@ -419,27 +464,29 @@ void UA_Server_processBinaryMessage(UA_Server *server, UA_Connection *connection
             break;
         case UA_MESSAGETYPEANDFINAL_MSGF & 0xffffff:
 #ifndef EXTENSION_STATELESS
-            if(connection->state != UA_CONNECTION_ESTABLISHED){
+            if(connection->state != UA_CONNECTION_ESTABLISHED) {
                 connection->close(connection);
-                UA_ByteString_deleteMembers(msg);
                 return;
-            }else
+            }
 #endif
-                processMSG(connection, server, msg, &pos);
+            processMSG(connection, server, msg, &pos);
             break;
         case UA_MESSAGETYPEANDFINAL_CLOF & 0xffffff:
             processCLO(connection, server, msg, &pos);
             connection->close(connection);
-            UA_ByteString_deleteMembers(msg);
             return;
+        default:
+            UA_LOG_INFO(server->logger, UA_LOGCATEGORY_NETWORK,
+                        "Unknown request type on Connection %i", connection->sockfd);
         }
 
         UA_TcpMessageHeader_deleteMembers(&tcpMessageHeader);
         if(pos != targetpos) {
-            UA_LOG_INFO(server->logger, UA_LOGCATEGORY_COMMUNICATION,
-                        "The message was not entirely processed, skipping to the end");
+            UA_LOG_INFO(server->logger, UA_LOGCATEGORY_NETWORK,
+                        "Message on Connection %i was not entirely processed. "
+                        "Arrived at position %i, skip after the announced length to position %i",
+                        connection->sockfd, pos, targetpos);
             pos = targetpos;
         }
-    } while(msg->length > (UA_Int32)pos);
-    UA_ByteString_deleteMembers(msg);
+    } while(msg->length > pos);
 }

+ 18 - 16
src/server/ua_server_internal.h

@@ -3,6 +3,7 @@
 
 #include "ua_util.h"
 #include "ua_server.h"
+#include "ua_server_external_ns.h"
 #include "ua_session_manager.h"
 #include "ua_securechannel_manager.h"
 #include "ua_nodestore.h"
@@ -15,6 +16,7 @@
 #define ANONYMOUS_POLICY "open62541-anonymous-policy"
 #define USERNAME_POLICY "open62541-username-policy"
 
+#ifdef UA_EXTERNAL_NAMESPACES
 /** Mapping of namespace-id and url to an external nodestore. For namespaces
     that have no mapping defined, the internal nodestore is used by default. */
 typedef struct UA_ExternalNamespace {
@@ -22,6 +24,7 @@ typedef struct UA_ExternalNamespace {
 	UA_String url;
 	UA_ExternalNodeStore externalNodeStore;
 } UA_ExternalNamespace;
+#endif
 
 struct UA_Server {
     /* Config */
@@ -33,12 +36,12 @@ struct UA_Server {
     UA_DateTime startTime;
     UA_DateTime buildDate;
     UA_ApplicationDescription description;
-    UA_Int32 endpointDescriptionsSize;
+    size_t endpointDescriptionsSize;
     UA_EndpointDescription *endpointDescriptions;
 
     /* Communication */
     size_t networkLayersSize;
-    UA_ServerNetworkLayer *networkLayers;
+    UA_ServerNetworkLayer **networkLayers;
 
     /* Security */
     UA_ByteString serverCertificate;
@@ -49,8 +52,11 @@ struct UA_Server {
     UA_NodeStore *nodestore;
     size_t namespacesSize;
     UA_String *namespaces;
+
+#ifdef UA_EXTERNAL_NAMESPACES
     size_t externalNamespacesSize;
     UA_ExternalNamespace *externalNamespaces;
+#endif
      
     /* Jobs with a repetition interval */
     LIST_HEAD(RepeatedJobsList, RepeatedJobs) repeatedJobs;
@@ -74,23 +80,19 @@ struct UA_Server {
 #endif
 };
 
-void UA_Server_processBinaryMessage(UA_Server *server, UA_Connection *connection, UA_ByteString *msg);
-
-UA_AddNodesResult UA_Server_addNodeWithSession(UA_Server *server, UA_Session *session, UA_Node *node,
-                                               const UA_ExpandedNodeId parentNodeId,
-                                               const UA_NodeId referenceTypeId);
-
-UA_AddNodesResult UA_Server_addNode(UA_Server *server, UA_Node *node, const UA_ExpandedNodeId parentNodeId,
-                                    const UA_NodeId referenceTypeId);
+/* The node is assumed to be "finished", i.e. no instantiation from inheritance is necessary */
+void UA_Server_addExistingNode(UA_Server *server, UA_Session *session, UA_Node *node,
+                               const UA_NodeId *parentNodeId, const UA_NodeId *referenceTypeId,
+                               UA_AddNodesResult *result);
 
-UA_StatusCode UA_Server_addReferenceWithSession(UA_Server *server, UA_Session *session,
-                                                const UA_AddReferencesItem *item);
+typedef UA_StatusCode (*UA_EditNodeCallback)(UA_Server*, UA_Session*, UA_Node*, const void*);
 
-UA_StatusCode deleteOneWayReferenceWithSession(UA_Server *server, UA_Session *session, 
-                                               const UA_DeleteReferencesItem *item);
+/* Calls callback on the node. In the multithreaded case, the node is copied before and replaced in
+   the nodestore. */
+UA_StatusCode UA_Server_editNode(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId,
+                                 UA_EditNodeCallback callback, const void *data);
 
-UA_StatusCode addOneWayReferenceWithSession(UA_Server *server, UA_Session *session, 
-                                            const UA_AddReferencesItem *item);
+void UA_Server_processBinaryMessage(UA_Server *server, UA_Connection *connection, const UA_ByteString *msg);
 
 UA_StatusCode UA_Server_addDelayedJob(UA_Server *server, UA_Job job);
 

+ 71 - 46
src/server/ua_server_worker.c

@@ -42,19 +42,33 @@
 #define MAXTIMEOUT 50000 // max timeout in microsec until the next main loop iteration
 #define BATCHSIZE 20 // max number of jobs that are dispatched at once to workers
 
-static void processJobs(UA_Server *server, UA_Job *jobs, size_t jobsSize) {
-    for(size_t i = 0; i < jobsSize; i++) {
+/**
+ * server:		UA server context
+ * jobs: 		pointer to array of jobs or NULL if jobsSize == -1
+ * jobsSize: 	nr. of valid jobs or -1
+*/
+static void processJobs(UA_Server *server, UA_Job *jobs, UA_Int32 jobsSize) {
+    for (UA_Int32 i = 0; i < jobsSize; i++) {
         UA_Job *job = &jobs[i];
         switch(job->type) {
-        case UA_JOBTYPE_BINARYMESSAGE:
-            UA_Server_processBinaryMessage(server, job->job.binaryMessage.connection,
-                                           &job->job.binaryMessage.message);
+        case UA_JOBTYPE_NOTHING:
             break;
         case UA_JOBTYPE_DETACHCONNECTION:
             UA_Connection_detachSecureChannel(job->job.closeConnection);
             break;
+        case UA_JOBTYPE_BINARYMESSAGE_NETWORKLAYER:
+            UA_Server_processBinaryMessage(server, job->job.binaryMessage.connection,
+                                           &job->job.binaryMessage.message);
+            UA_Connection *connection = job->job.binaryMessage.connection;
+            connection->releaseRecvBuffer(connection, &job->job.binaryMessage.message);
+            break;
+        case UA_JOBTYPE_BINARYMESSAGE_ALLOCATED:
+            UA_Server_processBinaryMessage(server, job->job.binaryMessage.connection,
+                                           &job->job.binaryMessage.message);
+            UA_ByteString_deleteMembers(&job->job.binaryMessage.message);
+            break;
         case UA_JOBTYPE_METHODCALL:
-        case UA_JOBTYPE_DELAYEDMETHODCALL:
+        case UA_JOBTYPE_METHODCALL_DELAYED:
             job->job.methodCall.method(server, job->job.methodCall.data);
             break;
         default:
@@ -94,7 +108,7 @@ static void dispatchJobs(UA_Server *server, UA_Job *jobs, size_t jobsSize) {
         struct DispatchJobsList *wln = UA_malloc(sizeof(struct DispatchJobsList));
         if(startIndex > 0) {
             wln->jobs = UA_malloc(size * sizeof(UA_Job));
-            UA_memcpy(wln->jobs, &jobs[startIndex], size * sizeof(UA_Job));
+            memcpy(wln->jobs, &jobs[startIndex], size * sizeof(UA_Job));
             wln->jobsSize = size;
         } else {
             /* forward the original array */
@@ -115,6 +129,9 @@ struct workerStartData {
 
 /** Waits until jobs arrive in the dispatch queue and processes them. */
 static void * workerLoop(struct workerStartData *startInfo) {
+    /* Initialized the (thread local) random seed */
+    UA_random_seed((uintptr_t)startInfo);
+
    	rcu_register_thread();
     UA_UInt32 *c = UA_malloc(sizeof(UA_UInt32));
     uatomic_set(c, 0);
@@ -131,9 +148,11 @@ static void * workerLoop(struct workerStartData *startInfo) {
         struct DispatchJobsList *wln = (struct DispatchJobsList*)
             cds_wfcq_dequeue_blocking(&server->dispatchQueue_head, &server->dispatchQueue_tail);
         if(wln) {
+            UA_RCU_LOCK();
             processJobs(server, wln->jobs, wln->jobsSize);
             UA_free(wln->jobs);
             UA_free(wln);
+            UA_RCU_UNLOCK();
         } else {
             /* sleep until a work arrives (and wakes up all worker threads) */
             #if defined(__APPLE__) || defined(__MACH__) // OS X does not have clock_gettime, use clock_get_time
@@ -159,7 +178,7 @@ static void * workerLoop(struct workerStartData *startInfo) {
    	rcu_unregister_thread();
 
     /* we need to return _something_ for pthreads */
-    return UA_NULL;
+    return NULL;
 }
 
 static void emptyDispatchQueue(UA_Server *server) {
@@ -204,9 +223,10 @@ struct AddRepeatedJob {
 
 /* internal. call only from the main loop. */
 static UA_StatusCode addRepeatedJob(UA_Server *server, struct AddRepeatedJob * UA_RESTRICT arw) {
-    struct RepeatedJobs *matchingTw = UA_NULL; // add the item here
-    struct RepeatedJobs *lastTw = UA_NULL; // if there is no repeated job, add a new one this entry
+    struct RepeatedJobs *matchingTw = NULL; // add the item here
+    struct RepeatedJobs *lastTw = NULL; // if there is no repeated job, add a new one this entry
     struct RepeatedJobs *tempTw;
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
 
     /* search for matching entry */
     UA_DateTime firstTime = UA_DateTime_now() + arw->interval;
@@ -227,25 +247,17 @@ static UA_StatusCode addRepeatedJob(UA_Server *server, struct AddRepeatedJob * U
         matchingTw = UA_realloc(matchingTw, sizeof(struct RepeatedJobs) +
                                 (sizeof(struct IdentifiedJob) * (matchingTw->jobsSize + 1)));
         if(!matchingTw) {
-#ifdef UA_MULTITHREADING
-            UA_free(arw);
-#endif
-            return UA_STATUSCODE_BADOUTOFMEMORY;
+            retval = UA_STATUSCODE_BADOUTOFMEMORY;
+            goto cleanup;
         }
-
-        /* point the realloced struct */
-        if(matchingTw->pointers.le_next)
-            matchingTw->pointers.le_next->pointers.le_prev = &matchingTw->pointers.le_next;
-        if(matchingTw->pointers.le_prev)
-            *matchingTw->pointers.le_prev = matchingTw;
+        /* link the reallocated tw into the list */
+        LIST_REPLACE(matchingTw, matchingTw, pointers);
     } else {
         /* create a new entry */
         matchingTw = UA_malloc(sizeof(struct RepeatedJobs) + sizeof(struct IdentifiedJob));
         if(!matchingTw) {
-#ifdef UA_MULTITHREADING
-            UA_free(arw);
-#endif
-            return UA_STATUSCODE_BADOUTOFMEMORY;
+            retval = UA_STATUSCODE_BADOUTOFMEMORY;
+            goto cleanup;
         }
         matchingTw->jobsSize = 0;
         matchingTw->nextTime = firstTime;
@@ -257,10 +269,12 @@ static UA_StatusCode addRepeatedJob(UA_Server *server, struct AddRepeatedJob * U
     }
     matchingTw->jobs[matchingTw->jobsSize] = arw->job;
     matchingTw->jobsSize++;
+
+ cleanup:
 #ifdef UA_MULTITHREADING
     UA_free(arw);
 #endif
-    return UA_STATUSCODE_GOOD;
+    return retval;
 }
 
 UA_StatusCode UA_Server_addRepeatedJob(UA_Server *server, UA_Job job, UA_UInt32 interval, UA_Guid *jobId) {
@@ -308,14 +322,11 @@ UA_StatusCode UA_Server_addRepeatedJob(UA_Server *server, UA_Job job, UA_UInt32
 /* Returns the timeout until the next repeated job in ms */
 static UA_UInt16 processRepeatedJobs(UA_Server *server) {
     UA_DateTime current = UA_DateTime_now();
-    struct RepeatedJobs *next = LIST_FIRST(&server->repeatedJobs);
-    struct RepeatedJobs *tw = UA_NULL;
+    struct RepeatedJobs *tw = NULL;
 
-    while(next) {
-        tw = next;
+    while((tw = LIST_FIRST(&server->repeatedJobs)) != NULL) {
         if(tw->nextTime > current)
             break;
-        next = LIST_NEXT(tw, pointers);
 
 #ifdef UA_MULTITHREADING
         // copy the entry and insert at the new location
@@ -329,10 +340,17 @@ static UA_UInt16 processRepeatedJobs(UA_Server *server) {
         dispatchJobs(server, jobsCopy, tw->jobsSize); // frees the job pointer
 #else
         for(size_t i=0;i<tw->jobsSize;i++)
+            //processJobs may sort the list but dont delete entries
             processJobs(server, &tw->jobs[i].job, 1); // does not free the job ptr
 #endif
+
+        /* set the time for the next execution */
         tw->nextTime += tw->interval;
-        struct RepeatedJobs *prevTw = tw; // after which tw do we insert?
+        if(tw->nextTime < current)
+            tw->nextTime = current;
+
+        //start iterating the list from the beginning
+        struct RepeatedJobs *prevTw = LIST_FIRST(&server->repeatedJobs); // after which tw do we insert?
         while(UA_TRUE) {
             struct RepeatedJobs *n = LIST_NEXT(prevTw, pointers);
             if(!n || n->nextTime > tw->nextTime)
@@ -346,10 +364,11 @@ static UA_UInt16 processRepeatedJobs(UA_Server *server) {
     }
 
     // check if the next repeated job is sooner than the usual timeout
+    // calc in 32 bit must be ok
     struct RepeatedJobs *first = LIST_FIRST(&server->repeatedJobs);
-    UA_UInt16 timeout = MAXTIMEOUT;
+    UA_UInt32 timeout = MAXTIMEOUT;
     if(first) {
-        timeout = (first->nextTime - current)/10;
+        timeout = (UA_UInt32)((first->nextTime - current) / 10);
         if(timeout > MAXTIMEOUT)
             return MAXTIMEOUT;
     }
@@ -399,8 +418,8 @@ UA_StatusCode UA_Server_removeRepeatedJob(UA_Server *server, UA_Guid jobId) {
 }
 
 void UA_Server_deleteAllRepeatedJobs(UA_Server *server) {
-    struct RepeatedJobs *current;
-    while((current = LIST_FIRST(&server->repeatedJobs))) {
+    struct RepeatedJobs *current, *temp;
+    LIST_FOREACH_SAFE(current, &server->repeatedJobs, pointers, temp) {
         LIST_REMOVE(current, pointers);
         UA_free(current);
     }
@@ -416,7 +435,7 @@ void UA_Server_deleteAllRepeatedJobs(UA_Server *server) {
 
 struct DelayedJobs {
     struct DelayedJobs *next;
-    UA_UInt32 *workerCounters; // initially UA_NULL until the counter are set
+    UA_UInt32 *workerCounters; // initially NULL until the counter are set
     UA_UInt32 jobsCount; // the size of the array is DELAYEDJOBSSIZE, the count may be less
     UA_Job jobs[DELAYEDJOBSSIZE]; // when it runs full, a new delayedJobs entry is created
 };
@@ -442,7 +461,7 @@ static void addDelayedJob(UA_Server *server, UA_Job *job) {
             return;
         }
         dj->jobsCount = 0;
-        dj->workerCounters = UA_NULL;
+        dj->workerCounters = NULL;
         dj->next = server->delayedJobs;
         server->delayedJobs = dj;
 
@@ -505,7 +524,7 @@ static void dispatchDelayedJobs(UA_Server *server, void *data /* not used, but n
     /* process and free all delayed jobs from here on */
     while(dw) {
         processJobs(server, dw->jobs, dw->jobsCount);
-        struct DelayedJobs *next = uatomic_xchg(&beforedw->next, UA_NULL);
+        struct DelayedJobs *next = uatomic_xchg(&beforedw->next, NULL);
         UA_free(dw);
         UA_free(dw->workerCounters);
         dw = next;
@@ -549,18 +568,18 @@ UA_StatusCode result = UA_STATUSCODE_GOOD;
         struct workerStartData *startData = UA_malloc(sizeof(struct workerStartData));
         startData->server = server;
         startData->workerCounter = &server->workerCounters[i];
-        pthread_create(&server->thr[i], UA_NULL, (void* (*)(void*))workerLoop, startData);
+        pthread_create(&server->thr[i], NULL, (void* (*)(void*))workerLoop, startData);
     }
 
     /* try to execute the delayed callbacks every 10 sec */
     UA_Job processDelayed = {.type = UA_JOBTYPE_METHODCALL,
-                             .job.methodCall = {.method = dispatchDelayedJobs, .data = UA_NULL} };
-    UA_Server_addRepeatedJob(server, processDelayed, 10000, UA_NULL);
+                             .job.methodCall = {.method = dispatchDelayedJobs, .data = NULL} };
+    UA_Server_addRepeatedJob(server, processDelayed, 10000, NULL);
 #endif
 
     /* Start the networklayers */
     for(size_t i = 0; i < server->networkLayersSize; i++)
-        result |= server->networkLayers[i].start(&server->networkLayers[i], &server->logger);
+        result |= server->networkLayers[i]->start(server->networkLayers[i], server->logger);
 
     return result;
 }
@@ -575,7 +594,7 @@ UA_StatusCode UA_Server_run_mainloop(UA_Server *server, UA_Boolean *running) {
 
     /* Get work from the networklayer */
     for(size_t i = 0; i < server->networkLayersSize; i++) {
-        UA_ServerNetworkLayer *nl = &server->networkLayers[i];
+        UA_ServerNetworkLayer *nl = server->networkLayers[i];
         UA_Job *jobs;
         UA_Int32 jobsSize;
         if(*running) {
@@ -584,12 +603,12 @@ UA_StatusCode UA_Server_run_mainloop(UA_Server *server, UA_Boolean *running) {
             else
                 jobsSize = nl->getJobs(nl, &jobs, 0);
         } else
-            jobsSize = server->networkLayers[i].stop(nl, &jobs);
+            jobsSize = server->networkLayers[i]->stop(nl, &jobs);
 
 #ifdef UA_MULTITHREADING
         /* Filter out delayed work */
         for(UA_Int32 k=0;k<jobsSize;k++) {
-            if(jobs[k].type != UA_JOBTYPE_DELAYEDMETHODCALL)
+            if(jobs[k].type != UA_JOBTYPE_METHODCALL_DELAYED)
                 continue;
             addDelayedJob(server, &jobs[k]);
             jobs[k].type = UA_JOBTYPE_NOTHING;
@@ -611,10 +630,16 @@ UA_StatusCode UA_Server_run_mainloop(UA_Server *server, UA_Boolean *running) {
 }
 
 UA_StatusCode UA_Server_run_shutdown(UA_Server *server, UA_UInt16 nThreads){
+    UA_Job *stopJobs;
+    for(size_t i = 0; i < server->networkLayersSize; i++) {
+        size_t stopJobsSize = server->networkLayers[i]->stop(server->networkLayers[i], &stopJobs);
+        processJobs(server, stopJobs, stopJobsSize);
+        UA_free(stopJobs);
+    }
 #ifdef UA_MULTITHREADING
     /* Wait for all worker threads to finish */
     for(UA_UInt32 i=0;i<nThreads;i++) {
-        pthread_join(server->thr[i], UA_NULL);
+        pthread_join(server->thr[i], NULL);
         UA_free(server->workerCounters[i]);
     }
     UA_free(server->workerCounters);

+ 107 - 76
src/server/ua_services.h

@@ -1,10 +1,14 @@
 #ifndef UA_SERVICES_H_
 #define UA_SERVICES_H_
 
+#include "ua_util.h"
 #include "ua_types.h"
 #include "ua_types_generated.h"
 #include "ua_server.h"
 #include "ua_session.h"
+#include "ua_nodes.h"
+
+typedef void (*UA_Service)(UA_Server*, UA_Session*, const void*, void*);
 
 /**
  * @ingroup server
@@ -23,15 +27,15 @@
  *
  * @{
  */
-// Service_FindServers
-void Service_FindServers(UA_Server                    *server,
-                          const UA_FindServersRequest *request,
-                          UA_FindServersResponse      *response);
+void Service_FindServers(UA_Server *server, UA_Session *session,
+                         const UA_FindServersRequest *request,
+                         UA_FindServersResponse *response);
 /**
  * Returns the Endpoints supported by a Server and all of the configuration
  * information required to establish a SecureChannel and a Session.
  */
-void Service_GetEndpoints(UA_Server *server, const UA_GetEndpointsRequest *request,
+void Service_GetEndpoints(UA_Server *server, UA_Session *session,
+                          const UA_GetEndpointsRequest *request,
                           UA_GetEndpointsResponse *response);
 // Service_RegisterServer
 /** @} */
@@ -75,8 +79,9 @@ void Service_CloseSecureChannel(UA_Server *server, UA_Int32 channelId);
  * address space. The second is the authenticationToken which is used to
  * associate an incoming request with a Session.
  */
-void Service_CreateSession(UA_Server *server, UA_SecureChannel *channel,
-                           const UA_CreateSessionRequest *request, UA_CreateSessionResponse *response);
+void Service_CreateSession(UA_Server *server, UA_Session *session,
+                           const UA_CreateSessionRequest *request,
+                           UA_CreateSessionResponse *response);
 
 /**
  * Used by the Client to submit its SoftwareCertificates to the Server for
@@ -85,11 +90,13 @@ void Service_CreateSession(UA_Server *server, UA_SecureChannel *channel,
  * any other Service request after CreateSession. Failure to do so shall cause
  * the Server to close the Session.
  */
-void Service_ActivateSession(UA_Server *server, UA_SecureChannel *channel,
-                             const UA_ActivateSessionRequest *request, UA_ActivateSessionResponse *response);
+void Service_ActivateSession(UA_Server *server, UA_Session *session,
+                             const UA_ActivateSessionRequest *request,
+                             UA_ActivateSessionResponse *response);
 
 /** Used to terminate a Session. */
-void Service_CloseSession(UA_Server *server, UA_Session *session, const UA_CloseSessionRequest *request,
+void Service_CloseSession(UA_Server *server, UA_Session *session,
+                          const UA_CloseSessionRequest *request,
                           UA_CloseSessionResponse *response);
 // Service_Cancel
 /** @} */
@@ -105,20 +112,32 @@ void Service_CloseSession(UA_Server *server, UA_Session *session, const UA_Close
  */
 
 /** Used to add one or more Nodes into the AddressSpace hierarchy. */
-void Service_AddNodes(UA_Server *server, UA_Session *session, const UA_AddNodesRequest *request,
+void Service_AddNodes(UA_Server *server, UA_Session *session,
+                      const UA_AddNodesRequest *request,
                       UA_AddNodesResponse *response);
+void Service_AddNodes_single(UA_Server *server, UA_Session *session, const UA_AddNodesItem *item,
+                             UA_AddNodesResult *result);
 
 /** Used to add one or more References to one or more Nodes. */
-void Service_AddReferences(UA_Server *server, UA_Session *session, const UA_AddReferencesRequest *request,
+void Service_AddReferences(UA_Server *server, UA_Session *session,
+                           const UA_AddReferencesRequest *request,
                            UA_AddReferencesResponse *response);
+UA_StatusCode Service_AddReferences_single(UA_Server *server, UA_Session *session,
+                                           const UA_AddReferencesItem *item);
 
 /** Used to delete one or more Nodes from the AddressSpace. */
-void Service_DeleteNodes(UA_Server *server, UA_Session *session, const UA_DeleteNodesRequest *request,
+void Service_DeleteNodes(UA_Server *server, UA_Session *session,
+                         const UA_DeleteNodesRequest *request,
                          UA_DeleteNodesResponse *response);
+UA_StatusCode Service_DeleteNodes_single(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId,
+                                         UA_Boolean deleteReferences);
 
 /** Used to delete one or more References of a Node. */
-void Service_DeleteReferences(UA_Server *server, UA_Session *session, const UA_DeleteReferencesRequest *request,
+void Service_DeleteReferences(UA_Server *server, UA_Session *session,
+                              const UA_DeleteReferencesRequest *request,
                               UA_DeleteReferencesResponse *response);
+UA_StatusCode Service_DeleteReferences_single(UA_Server *server, UA_Session *session,
+                                              const UA_DeleteReferencesItem *item);
 
 /** @} */
 
@@ -136,9 +155,14 @@ void Service_DeleteReferences(UA_Server *server, UA_Session *session, const UA_D
  * further limited by the use of a View. This Browse Service also supports a
  * primitive filtering capability.
  */
-void Service_Browse(UA_Server *server, UA_Session *session, const UA_BrowseRequest *request,
+void Service_Browse(UA_Server *server, UA_Session *session,
+                    const UA_BrowseRequest *request,
                     UA_BrowseResponse *response);
 
+void Service_Browse_single(UA_Server *server, UA_Session *session,
+                           struct ContinuationPointEntry *cp, const UA_BrowseDescription *descr,
+                           UA_UInt32 maxrefs, UA_BrowseResult *result);
+
 /**
  * Used to request the next set of Browse or BrowseNext response information
  * that is too large to be sent in a single response. "Too large" in this
@@ -146,19 +170,22 @@ void Service_Browse(UA_Server *server, UA_Session *session, const UA_BrowseReque
  * the number of results to return exceeds the maximum number of results to
  * return that was specified by the Client in the original Browse request.
  */
-void Service_BrowseNext(UA_Server *server, UA_Session *session, const UA_BrowseNextRequest *request,
+void Service_BrowseNext(UA_Server *server, UA_Session *session,
+                        const UA_BrowseNextRequest *request,
                         UA_BrowseNextResponse *response);
 
 /** Used to translate textual node paths to their respective ids. */
 void Service_TranslateBrowsePathsToNodeIds(UA_Server *server, UA_Session *session,
                                            const UA_TranslateBrowsePathsToNodeIdsRequest *request,
                                            UA_TranslateBrowsePathsToNodeIdsResponse *response);
+void Service_TranslateBrowsePathsToNodeIds_single(UA_Server *server, UA_Session *session,
+                                                  const UA_BrowsePath *path, UA_BrowsePathResult *result);
 
-// Service_RegisterNodes
-void Service_RegisterNodes(UA_Server *server, UA_Session *session, const UA_RegisterNodesRequest *request,
+void Service_RegisterNodes(UA_Server *server, UA_Session *session,
+                           const UA_RegisterNodesRequest *request,
                            UA_RegisterNodesResponse *response);
-// Service_UnregisterNodes
-void Service_UnregisterNodes(UA_Server *server, UA_Session *session, const UA_UnregisterNodesRequest *request,
+void Service_UnregisterNodes(UA_Server *server, UA_Session *session,
+                             const UA_UnregisterNodesRequest *request,
                              UA_UnregisterNodesResponse *response);
 /** @} */
 
@@ -190,7 +217,7 @@ void Service_UnregisterNodes(UA_Server *server, UA_Session *session, const UA_Un
 
 /* Mock-Up of the function signature for Unit Tests */
 #ifdef BUILD_UNIT_TESTS
-UA_StatusCode parse_numericrange(const UA_String str, UA_NumericRange *range);
+UA_StatusCode parse_numericrange(const UA_String *str, UA_NumericRange *range);
 #endif
 
 /**
@@ -199,14 +226,14 @@ UA_StatusCode parse_numericrange(const UA_String str, UA_NumericRange *range);
  * allows Clients to read the entire set of indexed values as a composite, to
  * read individual elements or to read ranges of elements of the composite.
  */
-void Service_Read(UA_Server *server, UA_Session *session, const UA_ReadRequest *request,
-                  UA_ReadResponse *response);
-
-/* Mock-Up of the function signature for Unit Tests */
-#ifdef BUILD_UNIT_TESTS
-void readValue(UA_Server *server, UA_TimestampsToReturn timestamps,
-                      const UA_ReadValueId *id, UA_DataValue *v);
-#endif
+void
+Service_Read(UA_Server *server, UA_Session *session,
+             const UA_ReadRequest *request,
+             UA_ReadResponse *response);
+void
+Service_Read_single(UA_Server *server, UA_Session *session,
+                    UA_TimestampsToReturn timestamps,
+                    const UA_ReadValueId *id, UA_DataValue *v);
 
 // Service_HistoryRead
 /**
@@ -215,13 +242,14 @@ void readValue(UA_Server *server, UA_TimestampsToReturn timestamps,
  * allows Clients to write the entire set of indexed values as a composite, to
  * write individual elements or to write ranges of elements of the composite.
  */
-void Service_Write(UA_Server *server, UA_Session *session, const UA_WriteRequest *request,
-                   UA_WriteResponse *response);
+void
+Service_Write(UA_Server *server, UA_Session *session,
+              const UA_WriteRequest *request,
+              UA_WriteResponse *response);
 
-/* Mock-Up of the function signature for Unit Tests */
-#ifdef BUILD_UNIT_TESTS
-UA_StatusCode writeValue(UA_Server *server, UA_WriteValue *wvalue);
-#endif
+/** Single attribute writes are exposed to the userspace. The wvalue may be destroyed (deleteMembers) */
+UA_StatusCode
+Service_Write_single(UA_Server *server, UA_Session *session, const UA_WriteValue *wvalue);
 
 // Service_HistoryUpdate
 /** @} */
@@ -234,7 +262,12 @@ UA_StatusCode writeValue(UA_Server *server, UA_WriteValue *wvalue);
  *
  * @{
  */
-// Service_Call
+#ifdef ENABLE_METHODCALLS
+void
+Service_Call(UA_Server *server, UA_Session *session,
+             const UA_CallRequest *request,
+             UA_CallResponse *response);
+#endif
 /** @} */
 
 #ifdef ENABLE_SUBSCRIPTIONS
@@ -255,15 +288,17 @@ UA_StatusCode writeValue(UA_Server *server, UA_WriteValue *wvalue);
  * links to be deleted, but has no effect on the MonitoredItems referenced by
  * the triggered items.
  */
-void Service_CreateMonitoredItems(UA_Server *server, UA_Session *session,
-                                       const UA_CreateMonitoredItemsRequest *request, 
-                                       UA_CreateMonitoredItemsResponse *response);
+void
+Service_CreateMonitoredItems(UA_Server *server, UA_Session *session,
+                             const UA_CreateMonitoredItemsRequest *request, 
+                             UA_CreateMonitoredItemsResponse *response);
 // Service_ModifyMonitoredItems
 // Service_SetMonitoringMode
 // Service_SetTriggering
-void Service_DeleteMonitoredItems(UA_Server *server, UA_Session *session,
-                                  const UA_DeleteMonitoredItemsRequest *request,
-                                  UA_DeleteMonitoredItemsResponse *response);
+void
+Service_DeleteMonitoredItems(UA_Server *server, UA_Session *session,
+                             const UA_DeleteMonitoredItemsRequest *request,
+                             UA_DeleteMonitoredItemsResponse *response);
                                       
 /** @} */
 
@@ -275,44 +310,40 @@ void Service_DeleteMonitoredItems(UA_Server *server, UA_Session *session,
  * @{
  */
     
-void Service_CreateSubscription(UA_Server *server, UA_Session *session,
-                                const UA_CreateSubscriptionRequest *request,
-                                UA_CreateSubscriptionResponse *response);
-
-void Service_ModifySubscription(UA_Server *server, UA_Session *session,
-                                const UA_ModifySubscriptionRequest *request,
-                                UA_ModifySubscriptionResponse *response);
-
-void Service_DeleteSubscriptions(UA_Server *server, UA_Session *session,
-                                 const UA_DeleteSubscriptionsRequest *request,
-                                 UA_DeleteSubscriptionsResponse *response);
+void
+Service_CreateSubscription(UA_Server *server, UA_Session *session,
+                           const UA_CreateSubscriptionRequest *request,
+                           UA_CreateSubscriptionResponse *response);
+
+void
+Service_ModifySubscription(UA_Server *server, UA_Session *session,
+                           const UA_ModifySubscriptionRequest *request,
+                           UA_ModifySubscriptionResponse *response);
+
+void
+Service_DeleteSubscriptions(UA_Server *server, UA_Session *session,
+                            const UA_DeleteSubscriptionsRequest *request,
+                            UA_DeleteSubscriptionsResponse *response);
                                      
-void Service_Publish(UA_Server *server, UA_Session *session,
-                     const UA_PublishRequest *request, UA_PublishResponse *response);
-                         
-//~ Service_ModifySubscription
-//~ Service_SetPublishingMode
-//~ UA_Int32 Service_SetPublishingMode(UA_Server *server, UA_Session *session,
-                                    //~ const UA_SetPublishingModeRequest *request,
-                                    //~ UA_SetPublishingModeResponse *response);
-// Service_Republish
+void
+Service_Publish(UA_Server *server, UA_Session *session,
+                const UA_PublishRequest *request,
+                UA_PublishResponse *response);
+
+void
+Service_Republish(UA_Server *server, UA_Session *session,
+                  const UA_RepublishRequest *request,
+                  UA_RepublishResponse *response);
+
+// Service_ModifySubscription
+// Service_SetPublishingMode
+// UA_Int32 Service_SetPublishingMode(UA_Server *server, UA_Session *session,
+                                  // const UA_SetPublishingModeRequest *request,
+                                  // UA_SetPublishingModeResponse *response);
 // Service_TransferSubscription
 // Service_DeleteSubscription
 /** @} */
 #endif
 
-#ifdef ENABLE_METHODCALLS
-/**
- * @name Call Service Set
- *
- * Calls are used to execute serverside methods.
- *
- * @{
- */
-void Service_Call(UA_Server *server, UA_Session *session,
-                  const UA_CallRequest *request,
-                  UA_CallResponse *response);
-/** @} */
-#endif
 #endif /* UA_SERVICES_H_ */
-/** @} */
+/** @} */

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 478 - 432
src/server/ua_services_attribute.c


+ 100 - 105
src/server/ua_services_call.c

@@ -5,15 +5,15 @@
 #include "ua_nodestore.h"
 #include "ua_nodes.h"
 
-static const UA_VariableNode *getArgumentsVariableNode(UA_Server *server, const UA_MethodNode *ofMethod,
-                                                       UA_String withBrowseName) {
-    const UA_Node *refTarget;
+static const UA_VariableNode *
+getArgumentsVariableNode(UA_Server *server, const UA_MethodNode *ofMethod,
+                         UA_String withBrowseName) {
     UA_NodeId hasProperty = UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY);
-    
-    for(UA_Int32 i = 0; i < ofMethod->referencesSize; i++) {
+    for(size_t i = 0; i < ofMethod->referencesSize; i++) {
         if(ofMethod->references[i].isInverse == UA_FALSE && 
             UA_NodeId_equal(&hasProperty, &ofMethod->references[i].referenceTypeId)) {
-            refTarget = UA_NodeStore_get(server->nodestore, &ofMethod->references[i].targetId.nodeId);
+            const UA_Node *refTarget =
+                UA_NodeStore_get(server->nodestore, &ofMethod->references[i].targetId.nodeId);
             if(!refTarget)
                 continue;
             if(refTarget->nodeClass == UA_NODECLASS_VARIABLE && 
@@ -21,120 +21,122 @@ static const UA_VariableNode *getArgumentsVariableNode(UA_Server *server, const
                 UA_String_equal(&withBrowseName, &refTarget->browseName.name)) {
                 return (const UA_VariableNode*) refTarget;
             }
-            UA_NodeStore_release(refTarget);
         }
     }
-    return UA_NULL;
+    return NULL;
 }
 
-static UA_StatusCode statisfySignature(UA_Variant *var, UA_Argument arg) {
-    if(!UA_NodeId_equal(&var->type->typeId, &arg.dataType) )
+static UA_StatusCode
+satisfySignature(const UA_Variant *var, const UA_Argument *arg) {
+    if(!UA_NodeId_equal(&var->type->typeId, &arg->dataType) )
         return UA_STATUSCODE_BADINVALIDARGUMENT;
     
-    // Note: The namespace compiler will compile nodes with their actual array dimensions, never -1
-    if(arg.arrayDimensionsSize > 0 && var->arrayDimensionsSize > 0)
-        if(var->arrayDimensionsSize != arg.arrayDimensionsSize) 
+    // 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;
         
-        // Continue with jpfr's statisfySignature from here on
-        /* 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. */
-        UA_Boolean scalar = UA_Variant_isScalar(var);
-        if(arg.valueRank == 0 && scalar)
-            return UA_STATUSCODE_BADINVALIDARGUMENT;
-        if(arg.valueRank == -1 && !scalar)
+    UA_UInt32 *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_UInt32 fakeDims;
+    if(!scalar && !varDims) {
+        fakeDims = 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;
-        if(arg.valueRank == -3 && var->arrayDimensionsSize > 1)
+        break;
+    case -2:
+        break;
+    case -1:
+        if(!scalar)
             return UA_STATUSCODE_BADINVALIDARGUMENT;
-        if(arg.valueRank > 1 && var->arrayDimensionsSize != arg.arrayDimensionsSize)
+        break;
+    case 0:
+        if(scalar || !varDims)
             return UA_STATUSCODE_BADINVALIDARGUMENT;
-        
-        //variants do not always encode the dimension flag (e.g. 1d array)
-       if(var->arrayDimensionsSize==-1 && arg.arrayDimensionsSize == 1 &&
-    		   var->arrayLength > 0 && arg.arrayDimensions[0] == (UA_UInt32)var->arrayLength ){
-    	   return UA_STATUSCODE_GOOD;
-       }else{
-         if(arg.valueRank >= 1 && var->arrayDimensionsSize != arg.arrayDimensionsSize)
+        break;
+    default:
+        break;
+    }
+
+    /* do the array dimensions match? */
+    if(arg->arrayDimensionsSize != varDimsSize)
+        return UA_STATUSCODE_BADINVALIDARGUMENT;
+    for(size_t i = 0; i < varDimsSize; i++) {
+        if(arg->arrayDimensions[i] != varDims[i])
             return UA_STATUSCODE_BADINVALIDARGUMENT;
-         if(arg.arrayDimensionsSize >= 1) {
-            if(arg.arrayDimensionsSize != var->arrayDimensionsSize)
-                return UA_STATUSCODE_BADINVALIDARGUMENT;
-            for(UA_Int32 i = 0; i < arg.arrayDimensionsSize; i++) {
-                if(arg.arrayDimensions[i] != (UA_UInt32) var->arrayDimensions[i])
-                    return UA_STATUSCODE_BADINVALIDARGUMENT;
-            }
-         }
     }
     return UA_STATUSCODE_GOOD;
 }
 
-static UA_StatusCode argConformsToDefinition(UA_CallMethodRequest *rs, const UA_VariableNode *argDefinition) {
-    if(argDefinition->value.variant.type != &UA_TYPES[UA_TYPES_ARGUMENT] &&
-        argDefinition->value.variant.type != &UA_TYPES[UA_TYPES_EXTENSIONOBJECT])
+static UA_StatusCode
+argConformsToDefinition(const UA_VariableNode *argRequirements, size_t argsSize, const UA_Variant *args) {
+    if(argRequirements->value.variant.value.type != &UA_TYPES[UA_TYPES_ARGUMENT])
+        return UA_STATUSCODE_BADINTERNALERROR;
+    UA_Argument *argReqs = (UA_Argument*)argRequirements->value.variant.value.data;
+    size_t argReqsSize = argRequirements->value.variant.value.arrayLength;
+    if(argRequirements->valueSource != UA_VALUESOURCE_VARIANT)
         return UA_STATUSCODE_BADINTERNALERROR;
-    if(rs->inputArgumentsSize < argDefinition->value.variant.arrayLength) 
+    if(UA_Variant_isScalar(&argRequirements->value.variant.value))
+        argReqsSize = 1;
+    if(argReqsSize > argsSize)
         return UA_STATUSCODE_BADARGUMENTSMISSING;
-    if(rs->inputArgumentsSize > argDefinition->value.variant.arrayLength)
+    if(argReqsSize != argsSize)
         return UA_STATUSCODE_BADINVALIDARGUMENT;
-    
-    const UA_ExtensionObject *thisArgDefExtObj;
-    UA_Variant *var;
-    UA_Argument arg;
-    size_t decodingOffset = 0;
+
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
-    UA_NodeId ArgumentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ARGUMENT + UA_ENCODINGOFFSET_BINARY);
-    for(int i = 0; i<rs->inputArgumentsSize; i++) {
-        var = &rs->inputArguments[i];
-        if(argDefinition->value.variant.type == &UA_TYPES[UA_TYPES_EXTENSIONOBJECT]) {
-            thisArgDefExtObj = &((const UA_ExtensionObject *) (argDefinition->value.variant.data))[i];
-            decodingOffset = 0;
-            
-            if(!UA_NodeId_equal(&ArgumentNodeId, &thisArgDefExtObj->typeId))
-                return UA_STATUSCODE_BADINTERNALERROR;
-                
-            UA_decodeBinary(&thisArgDefExtObj->body, &decodingOffset, &arg, &UA_TYPES[UA_TYPES_ARGUMENT]);
-        } else if(argDefinition->value.variant.type == &UA_TYPES[UA_TYPES_ARGUMENT])
-            arg = ((UA_Argument *) argDefinition->value.variant.data)[i];
-        retval |= statisfySignature(var, arg);
-    }
+    for(size_t i = 0; i < argReqsSize; i++)
+        retval |= satisfySignature(&args[i], &argReqs[i]);
     return retval;
 }
 
-static void callMethod(UA_Server *server, UA_Session *session, UA_CallMethodRequest *request,
-                       UA_CallMethodResult *result) {
-    const UA_MethodNode *methodCalled = (const UA_MethodNode*) UA_NodeStore_get(server->nodestore,
-                                                                                &request->methodId);
+static void
+callMethod(UA_Server *server, UA_Session *session, UA_CallMethodRequest *request,
+           UA_CallMethodResult *result) {
+    const UA_MethodNode *methodCalled =
+        (const UA_MethodNode*)UA_NodeStore_get(server->nodestore, &request->methodId);
     if(!methodCalled) {
         result->statusCode = UA_STATUSCODE_BADMETHODINVALID;
         return;
     }
     
-    const UA_ObjectNode *withObject = (const UA_ObjectNode *) UA_NodeStore_get(server->nodestore,
-                                                                               &request->objectId);
+    const UA_ObjectNode *withObject =
+        (const UA_ObjectNode*)UA_NodeStore_get(server->nodestore, &request->objectId);
     if(!withObject) {
         result->statusCode = UA_STATUSCODE_BADNODEIDINVALID;
-        goto releaseMethodReturn;
+        return;
     }
     
     if(methodCalled->nodeClass != UA_NODECLASS_METHOD) {
         result->statusCode = UA_STATUSCODE_BADNODECLASSINVALID;
-        goto releaseBothReturn;
+        return;
     }
     
     if(withObject->nodeClass != UA_NODECLASS_OBJECT && withObject->nodeClass != UA_NODECLASS_OBJECTTYPE) {
         result->statusCode = UA_STATUSCODE_BADNODECLASSINVALID;
-        goto releaseBothReturn;
+        return;
     }
     
     /* Verify method/object relations */
     // Object must have a hasComponent reference (or any inherited referenceType from sayd reference) 
     // to be valid for a methodCall...
     result->statusCode = UA_STATUSCODE_BADMETHODINVALID;
-    for(UA_Int32 i = 0; i < withObject->referencesSize; i++) {
+    for(size_t i = 0; i < withObject->referencesSize; i++) {
         if(withObject->references[i].referenceTypeId.identifier.numeric == UA_NS0ID_HASCOMPONENT) {
             // FIXME: Not checking any subtypes of HasComponent at the moment
             if(UA_NodeId_equal(&withObject->references[i].targetId.nodeId, &methodCalled->nodeId)) {
@@ -144,56 +146,48 @@ static void callMethod(UA_Server *server, UA_Session *session, UA_CallMethodRequ
         }
     }
     if(result->statusCode != UA_STATUSCODE_GOOD)
-        goto releaseBothReturn;
+        return;
         
     /* Verify method executable */
     if(methodCalled->executable == UA_FALSE || methodCalled->userExecutable == UA_FALSE) {
         result->statusCode = UA_STATUSCODE_BADNOTWRITABLE; // There is no NOTEXECUTABLE?
-        goto releaseBothReturn;
+        return;
     }
 
     /* Verify Input Argument count, types and sizes */
-    const UA_VariableNode *inputArguments = getArgumentsVariableNode(server, methodCalled,
-                                                                     UA_STRING("InputArguments"));
+    const UA_VariableNode *inputArguments;
+    inputArguments = getArgumentsVariableNode(server, methodCalled, UA_STRING("InputArguments"));
     if(inputArguments) {
-        // Expects arguments
-        result->statusCode = argConformsToDefinition(request, inputArguments);
-        UA_NodeStore_release((const UA_Node*)inputArguments);
+        result->statusCode = argConformsToDefinition(inputArguments, request->inputArgumentsSize,
+                                                     request->inputArguments);
         if(result->statusCode != UA_STATUSCODE_GOOD)
-            goto releaseBothReturn;
+            return;
     } else if(request->inputArgumentsSize > 0) {
-        // Expects no arguments, but got some
         result->statusCode = UA_STATUSCODE_BADINVALIDARGUMENT;
-        goto releaseBothReturn;
+        return;
     }
 
-    const UA_VariableNode *outputArguments = getArgumentsVariableNode(server, methodCalled,
-                                                                      UA_STRING("OutputArguments"));
+    const UA_VariableNode *outputArguments =
+        getArgumentsVariableNode(server, methodCalled, UA_STRING("OutputArguments"));
     if(!outputArguments) {
         // A MethodNode must have an OutputArguments variable (which may be empty)
         result->statusCode = UA_STATUSCODE_BADINTERNALERROR;
-        goto releaseBothReturn;
+        return;
     }
     
-    // Call method if available
+    /* Call method if available */
     if(methodCalled->attachedMethod) {
-        result->outputArguments = UA_Array_new(&UA_TYPES[UA_TYPES_VARIANT],
-                                               outputArguments->value.variant.arrayLength);
-        result->outputArgumentsSize = outputArguments->value.variant.arrayLength;
-        result->statusCode = methodCalled->attachedMethod(withObject->nodeId, request->inputArguments,
-                                                          result->outputArguments, methodCalled->methodHandle);
+        result->outputArguments = UA_Array_new(outputArguments->value.variant.value.arrayLength,
+                                               &UA_TYPES[UA_TYPES_VARIANT]);
+        result->outputArgumentsSize = outputArguments->value.variant.value.arrayLength;
+        result->statusCode = methodCalled->attachedMethod(methodCalled->methodHandle, withObject->nodeId,
+                                                          request->inputArgumentsSize, request->inputArguments,
+                                                          result->outputArgumentsSize, result->outputArguments);
     }
     else
         result->statusCode = UA_STATUSCODE_BADNOTWRITABLE; // There is no NOTEXECUTABLE?
     
-    /* FIXME: Verify Output Argument count, types and sizes */
-    if(outputArguments)
-        UA_NodeStore_release((const UA_Node*)outputArguments);
-
- releaseBothReturn:
-    UA_NodeStore_release((const UA_Node*)withObject);
- releaseMethodReturn:
-    UA_NodeStore_release((const UA_Node*)methodCalled);
+    /* TODO: Verify Output Argument count, types and sizes */
 }
 
 void Service_Call(UA_Server *server, UA_Session *session, const UA_CallRequest *request,
@@ -203,13 +197,14 @@ void Service_Call(UA_Server *server, UA_Session *session, const UA_CallRequest *
         return;
     }
 
-    response->results = UA_Array_new(&UA_TYPES[UA_TYPES_CALLMETHODRESULT], request->methodsToCallSize);
+    response->results = UA_Array_new(request->methodsToCallSize,
+                                     &UA_TYPES[UA_TYPES_CALLMETHODRESULT]);
     if(!response->results) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
         return;
     }
     response->resultsSize = request->methodsToCallSize;
     
-    for(UA_Int32 i = 0; i < request->methodsToCallSize;i++)
+    for(size_t i = 0; i < request->methodsToCallSize;i++)
         callMethod(server, session, &request->methodsToCall[i], &response->results[i]);
 }

+ 8 - 7
src/server/ua_services_discovery.c

@@ -2,7 +2,8 @@
 #include "ua_services.h"
 #include "ua_util.h"
 
-void Service_FindServers(UA_Server *server, const UA_FindServersRequest *request, UA_FindServersResponse *response) {
+void Service_FindServers(UA_Server *server, UA_Session *session,
+                         const UA_FindServersRequest *request, UA_FindServersResponse *response) {
     response->servers = UA_malloc(sizeof(UA_ApplicationDescription));
     if(!response->servers) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
@@ -16,7 +17,8 @@ void Service_FindServers(UA_Server *server, const UA_FindServersRequest *request
 	response->serversSize = 1;
 }
 
-void Service_GetEndpoints(UA_Server *server, const UA_GetEndpointsRequest *request, UA_GetEndpointsResponse *response) {
+void Service_GetEndpoints(UA_Server *server, UA_Session *session,
+                          const UA_GetEndpointsRequest *request, UA_GetEndpointsResponse *response) {
     /* test if the supported binary profile shall be returned */
 #ifdef NO_ALLOCA
 	UA_Boolean relevant_endpoints[server->endpointDescriptionsSize];
@@ -24,14 +26,14 @@ void Service_GetEndpoints(UA_Server *server, const UA_GetEndpointsRequest *reque
 	UA_Boolean *relevant_endpoints = UA_alloca(sizeof(UA_Boolean)*server->endpointDescriptionsSize);
 #endif /*NO_ALLOCA */
     size_t relevant_count = 0;
-    for(UA_Int32 j = 0; j < server->endpointDescriptionsSize; j++) {
+    for(size_t j = 0; j < server->endpointDescriptionsSize; j++) {
         relevant_endpoints[j] = UA_FALSE;
         if(request->profileUrisSize <= 0) {
             relevant_endpoints[j] = UA_TRUE;
             relevant_count++;
             continue;
         }
-        for(UA_Int32 i = 0; i < request->profileUrisSize; i++) {
+        for(size_t i = 0; i < request->profileUrisSize; i++) {
             if(UA_String_equal(&request->profileUris[i], &server->endpointDescriptions->transportProfileUri)) {
                 relevant_endpoints[j] = UA_TRUE;
                 relevant_count++;
@@ -53,7 +55,7 @@ void Service_GetEndpoints(UA_Server *server, const UA_GetEndpointsRequest *reque
 
     size_t k = 0;
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
-    for(UA_Int32 j = 0; j < server->endpointDescriptionsSize && retval == UA_STATUSCODE_GOOD; j++) {
+    for(size_t j = 0; j < server->endpointDescriptionsSize && retval == UA_STATUSCODE_GOOD; j++) {
         if(relevant_endpoints[j] != UA_TRUE)
             continue;
         retval = UA_EndpointDescription_copy(&server->endpointDescriptions[j], &response->endpoints[k]);
@@ -62,9 +64,8 @@ void Service_GetEndpoints(UA_Server *server, const UA_GetEndpointsRequest *reque
 
     if(retval != UA_STATUSCODE_GOOD) {
         response->responseHeader.serviceResult = retval;
-        UA_Array_delete(response->endpoints, &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION], --k);
+        UA_Array_delete(response->endpoints, --k, &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]);
         return;
     }
     response->endpointsSize = relevant_count;
 }
-

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 823 - 235
src/server/ua_services_nodemanagement.c


+ 26 - 6
src/server/ua_services_securechannel.c

@@ -6,14 +6,34 @@ void Service_OpenSecureChannel(UA_Server *server, UA_Connection *connection,
                                const UA_OpenSecureChannelRequest *request,
                                UA_OpenSecureChannelResponse *response) {
     // todo: if(request->clientProtocolVersion != protocolVersion)
-    if(request->requestType == UA_SECURITYTOKENREQUESTTYPE_ISSUE)
-        UA_SecureChannelManager_open(&server->secureChannelManager, connection, request, response);
-    else
-        UA_SecureChannelManager_renew(&server->secureChannelManager, connection, request, response);
+    if(request->requestType == UA_SECURITYTOKENREQUESTTYPE_ISSUE) {
+        response->responseHeader.serviceResult =
+            UA_SecureChannelManager_open(&server->secureChannelManager, connection, request, response);
+
+        if(response->responseHeader.serviceResult == UA_STATUSCODE_GOOD)
+            UA_LOG_DEBUG(server->logger, UA_LOGCATEGORY_SECURECHANNEL,
+                         "Opened SecureChannel %i on Connection %i",
+                         response->securityToken.channelId, connection->sockfd);
+        else
+            UA_LOG_DEBUG(server->logger, UA_LOGCATEGORY_SECURECHANNEL,
+                         "Opening SecureChannel on Connection %i failed", connection->sockfd);
+    } else {
+        response->responseHeader.serviceResult =
+            UA_SecureChannelManager_renew(&server->secureChannelManager, connection, request, response);
+
+        if(response->responseHeader.serviceResult == UA_STATUSCODE_GOOD)
+            UA_LOG_DEBUG(server->logger, UA_LOGCATEGORY_SECURECHANNEL,
+                         "Renewed SecureChannel %i on Connection %i",
+                         response->securityToken.channelId, connection->sockfd);
+        else
+            UA_LOG_DEBUG(server->logger, UA_LOGCATEGORY_SECURECHANNEL,
+                         "Renewing SecureChannel on Connection %i failed", connection->sockfd);
+    }
 }
 
+/* The server does not send a CloseSecureChannel response */
 void Service_CloseSecureChannel(UA_Server *server, UA_Int32 channelId) {
-	//Sten: this service is a bit assymmetric to OpenSecureChannel since CloseSecureChannelRequest does not contain any indormation
+    UA_LOG_DEBUG(server->logger, UA_LOGCATEGORY_SECURECHANNEL,
+                 "Closing SecureChannel %i", channelId);
     UA_SecureChannelManager_close(&server->secureChannelManager, channelId);
-    // 62451 Part 6 Chapter 7.1.4 - The server does not send a CloseSecureChannel response
 }

+ 107 - 75
src/server/ua_services_session.c

@@ -1,29 +1,37 @@
 #include "ua_services.h"
 #include "ua_server_internal.h"
 #include "ua_session_manager.h"
-#include "ua_statuscodes.h"
-#include "ua_util.h"
+#include "ua_types_generated_encoding_binary.h"
 
-void Service_CreateSession(UA_Server *server, UA_SecureChannel *channel,
+void Service_CreateSession(UA_Server *server, UA_Session *session,
                            const UA_CreateSessionRequest *request,
                            UA_CreateSessionResponse *response) {
+    UA_SecureChannel *channel = session->channel;
+    if(channel->securityToken.channelId == 0) {
+        response->responseHeader.serviceResult = UA_STATUSCODE_BADSECURECHANNELIDINVALID;
+        return;
+    }
     response->responseHeader.serviceResult =
-        UA_Array_copy(server->endpointDescriptions, (void**)&response->serverEndpoints,
-                      &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION], server->endpointDescriptionsSize);
+        UA_Array_copy(server->endpointDescriptions, server->endpointDescriptionsSize,
+                      (void**)&response->serverEndpoints, &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]);
     if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD)
         return;
     response->serverEndpointsSize = server->endpointDescriptionsSize;
 
 	UA_Session *newSession;
-    response->responseHeader.serviceResult = UA_SessionManager_createSession(&server->sessionManager,
-                                                                             channel, request, &newSession);
-	if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD)
+    response->responseHeader.serviceResult =
+        UA_SessionManager_createSession(&server->sessionManager, channel, request, &newSession);
+	if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) {
+        UA_LOG_DEBUG(server->logger, UA_LOGCATEGORY_SESSION,
+                     "Processing CreateSessionRequest on SecureChannel %i failed",
+                     channel->securityToken.channelId);
 		return;
+    }
 
     //TODO get maxResponseMessageSize internally
     newSession->maxResponseMessageSize = request->maxResponseMessageSize;
     response->sessionId = newSession->sessionId;
-    response->revisedSessionTimeout = newSession->timeout;
+    response->revisedSessionTimeout = (UA_Double)newSession->timeout;
     response->authenticationToken = newSession->authenticationToken;
     response->responseHeader.serviceResult = UA_String_copy(&request->sessionName, &newSession->sessionName);
     if(server->endpointDescriptions)
@@ -33,96 +41,120 @@ void Service_CreateSession(UA_Server *server, UA_SecureChannel *channel,
         UA_SessionManager_removeSession(&server->sessionManager, server, &newSession->authenticationToken);
          return;
     }
+    UA_LOG_DEBUG(server->logger, UA_LOGCATEGORY_SESSION,
+                 "Processing CreateSessionRequest on SecureChannel %i succeeded, created Session (ns=%i,i=%i)",
+                 channel->securityToken.channelId, response->sessionId.namespaceIndex,
+                 response->sessionId.identifier.numeric);
 }
 
-void Service_ActivateSession(UA_Server *server, UA_SecureChannel *channel,
-                             const UA_ActivateSessionRequest *request,
-                             UA_ActivateSessionResponse *response) {
+void
+Service_ActivateSession(UA_Server *server, UA_Session *session,
+                        const UA_ActivateSessionRequest *request,
+                        UA_ActivateSessionResponse *response) {
+    UA_SecureChannel *channel = session->channel;
     // make the channel know about the session
 	UA_Session *foundSession =
-        UA_SessionManager_getSession(&server->sessionManager,
-                                     (const UA_NodeId*)&request->requestHeader.authenticationToken);
+        UA_SessionManager_getSession(&server->sessionManager, &request->requestHeader.authenticationToken);
 
-	if(foundSession == UA_NULL) {
+	if(!foundSession) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADSESSIONIDINVALID;
+        UA_LOG_DEBUG(server->logger, UA_LOGCATEGORY_SESSION,
+                     "Processing ActivateSessionRequest on SecureChannel %i, but no session found for the authentication token",
+                     channel->securityToken.channelId);
         return;
-	} else if(foundSession->validTill < UA_DateTime_now()) {
+	}
+
+    if(foundSession->validTill < UA_DateTime_now()) {
+        UA_LOG_DEBUG(server->logger, UA_LOGCATEGORY_SESSION,
+                     "Processing ActivateSessionRequest on SecureChannel %i, but the session has timed out",
+                     channel->securityToken.channelId);
         response->responseHeader.serviceResult = UA_STATUSCODE_BADSESSIONIDINVALID;
         return;
 	}
 
-    UA_UserIdentityToken token;
-    UA_UserIdentityToken_init(&token);
-    size_t offset = 0;
-    UA_UserIdentityToken_decodeBinary(&request->userIdentityToken.body, &offset, &token);
+    if(request->userIdentityToken.encoding < UA_EXTENSIONOBJECT_DECODED ||
+       (request->userIdentityToken.content.decoded.type != &UA_TYPES[UA_TYPES_ANONYMOUSIDENTITYTOKEN] &&
+        request->userIdentityToken.content.decoded.type != &UA_TYPES[UA_TYPES_USERNAMEIDENTITYTOKEN])) {
+        UA_LOG_DEBUG(server->logger, UA_LOGCATEGORY_SESSION,
+                     "Invalided UserIdentityToken on SecureChannel %i for Session (ns=%i,i=%i)",
+                     channel->securityToken.channelId, foundSession->sessionId.namespaceIndex,
+                     foundSession->sessionId.identifier.numeric);
+        response->responseHeader.serviceResult = UA_STATUSCODE_BADINTERNALERROR;
+        return;
+    }
 
-    UA_UserNameIdentityToken username_token;
-    UA_UserNameIdentityToken_init(&username_token);
+    UA_LOG_DEBUG(server->logger, UA_LOGCATEGORY_SESSION,
+                 "Processing ActivateSessionRequest on SecureChannel %i for Session (ns=%i,i=%i)",
+                 channel->securityToken.channelId, foundSession->sessionId.namespaceIndex,
+                 foundSession->sessionId.identifier.numeric);
 
     UA_String ap = UA_STRING(ANONYMOUS_POLICY);
     UA_String up = UA_STRING(USERNAME_POLICY);
-    //(Compatibility notice)
-    //Siemens OPC Scout v10 provides an empty policyId, this is not okay
-    //For compatibility we will assume that empty policyId == ANONYMOUS_POLICY
-    //if(token.policyId.data == UA_NULL) {
-    //    /* 1) no policy defined */
-    //    response->responseHeader.serviceResult = UA_STATUSCODE_BADIDENTITYTOKENINVALID;
-    //} else
-    //(End Compatibility notice)
-    if(server->config.Login_enableAnonymous && (token.policyId.data == UA_NULL || UA_String_equal(&token.policyId, &ap))) {
-        /* 2) anonymous logins */
+
+    /* Compatibility notice: Siemens OPC Scout v10 provides an empty policyId,
+       this is not okay For compatibility we will assume that empty policyId ==
+       ANONYMOUS_POLICY
+       if(token.policyId->data == NULL)
+           response->responseHeader.serviceResult = UA_STATUSCODE_BADIDENTITYTOKENINVALID;
+    */
+
+    /* anonymous login */
+    if(server->config.Login_enableAnonymous &&
+       request->userIdentityToken.content.decoded.type == &UA_TYPES[UA_TYPES_ANONYMOUSIDENTITYTOKEN]) {
+        const UA_AnonymousIdentityToken *token = request->userIdentityToken.content.decoded.data;
+        if(token->policyId.data && !UA_String_equal(&token->policyId, &ap)) {
+            response->responseHeader.serviceResult = UA_STATUSCODE_BADIDENTITYTOKENINVALID;
+            return;
+        }
         if(foundSession->channel && foundSession->channel != channel)
             UA_SecureChannel_detachSession(foundSession->channel, foundSession);
         UA_SecureChannel_attachSession(channel, foundSession);
         foundSession->activated = UA_TRUE;
         UA_Session_updateLifetime(foundSession);
-    } else if(server->config.Login_enableUsernamePassword && UA_String_equal(&token.policyId, &up)) {
-        /* 3) username logins */
-        offset = 0;
-        UA_UserNameIdentityToken_decodeBinary(&request->userIdentityToken.body, &offset, &username_token);
-        if(username_token.encryptionAlgorithm.data != UA_NULL) {
-            /* 3.1) we only support encryption */
+        return;
+    }
+
+    /* username login */
+    if(server->config.Login_enableUsernamePassword &&
+       request->userIdentityToken.content.decoded.type == &UA_TYPES[UA_TYPES_USERNAMEIDENTITYTOKEN]) {
+        const UA_UserNameIdentityToken *token = request->userIdentityToken.content.decoded.data;
+        if(!UA_String_equal(&token->policyId, &up)) {
             response->responseHeader.serviceResult = UA_STATUSCODE_BADIDENTITYTOKENINVALID;
-        } else  if(username_token.userName.length == -1 && username_token.password.length == -1){
-            /* 3.2) empty username and password */
+            return;
+        }
+        if(token->encryptionAlgorithm.length > 0) {
+            /* we don't support encryption */
             response->responseHeader.serviceResult = UA_STATUSCODE_BADIDENTITYTOKENINVALID;
-        } else {
-            /* 3.3) ok, trying to match the username */
-            UA_UInt32 i = 0;
-            for(; i < server->config.Login_loginsCount; ++i) {
-                UA_String user = UA_STRING(server->config.Login_usernames[i]);
-                UA_String pw = UA_STRING(server->config.Login_passwords[i]);
-                if(UA_String_equal(&username_token.userName, &user) &&
-                   UA_String_equal(&username_token.password, &pw)) {
-                    /* success - activate */
-                    if(foundSession->channel && foundSession->channel != channel)
-                        UA_SecureChannel_detachSession(foundSession->channel, foundSession);
-                    UA_SecureChannel_attachSession(channel, foundSession);
-                    foundSession->activated = UA_TRUE;
-                    UA_Session_updateLifetime(foundSession);
-                    break;
-                }
-            }
-            /* no username/pass matched */
-            if(i >= server->config.Login_loginsCount)
-                response->responseHeader.serviceResult = UA_STATUSCODE_BADUSERACCESSDENIED;
+            return;
+        }
+        /* ok, trying to match the username */
+        for(size_t i = 0; i < server->config.Login_loginsCount; ++i) {
+            UA_String user = UA_STRING(server->config.Login_usernames[i]);
+            UA_String pw = UA_STRING(server->config.Login_passwords[i]);
+            if(!UA_String_equal(&token->userName, &user) || !UA_String_equal(&token->password, &pw))
+                continue;
+            /* success - activate */
+            if(foundSession->channel && foundSession->channel != channel)
+                UA_SecureChannel_detachSession(foundSession->channel, foundSession);
+            UA_SecureChannel_attachSession(channel, foundSession);
+            foundSession->activated = UA_TRUE;
+            UA_Session_updateLifetime(foundSession);
+            return;
         }
-    } else {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADIDENTITYTOKENINVALID;
+        /* no match */
+        response->responseHeader.serviceResult = UA_STATUSCODE_BADUSERACCESSDENIED;
+        return;
     }
-    UA_UserIdentityToken_deleteMembers(&token);
-    UA_UserNameIdentityToken_deleteMembers(&username_token);
-    return;
+    response->responseHeader.serviceResult = UA_STATUSCODE_BADIDENTITYTOKENINVALID;
 }
 
-void Service_CloseSession(UA_Server *server, UA_Session *session, const UA_CloseSessionRequest *request,
-                          UA_CloseSessionResponse *response) {
-	UA_Session *foundSession =
-        UA_SessionManager_getSession(&server->sessionManager,
-		                             (const UA_NodeId*)&request->requestHeader.authenticationToken);
-	if(foundSession == UA_NULL)
-		response->responseHeader.serviceResult = UA_STATUSCODE_BADSESSIONIDINVALID;
-	else 
-        response->responseHeader.serviceResult =
-            UA_SessionManager_removeSession(&server->sessionManager, server, &session->authenticationToken);
+void
+Service_CloseSession(UA_Server *server, UA_Session *session,
+                     const UA_CloseSessionRequest *request,
+                     UA_CloseSessionResponse *response) {
+    UA_LOG_DEBUG(server->logger, UA_LOGCATEGORY_SESSION,
+                 "Processing CloseSessionRequest for Session (ns=%i,i=%i)",
+                 session->sessionId.namespaceIndex, session->sessionId.identifier.numeric);
+    response->responseHeader.serviceResult =
+        UA_SessionManager_removeSession(&server->sessionManager, server, &session->authenticationToken);
 }

+ 77 - 24
src/server/ua_services_subscription.c

@@ -17,11 +17,15 @@ void Service_CreateSubscription(UA_Server *server, UA_Session *session,
                                 UA_CreateSubscriptionResponse *response) {
     response->subscriptionId = SubscriptionManager_getUniqueUIntID(&session->subscriptionManager);
     UA_Subscription *newSubscription = UA_Subscription_new(response->subscriptionId);
+    if(!newSubscription) {
+        response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
+        return;
+    }
     
     /* set the publishing interval */
     UA_BOUNDEDVALUE_SETWBOUNDS(session->subscriptionManager.globalPublishingInterval,
                                request->requestedPublishingInterval, response->revisedPublishingInterval);
-    newSubscription->publishingInterval = response->revisedPublishingInterval;
+    newSubscription->publishingInterval = (UA_DateTime)response->revisedPublishingInterval;
     
     /* set the subscription lifetime (deleted when no publish requests arrive within this time) */
     UA_BOUNDEDVALUE_SETWBOUNDS(session->subscriptionManager.globalLifeTimeCount,
@@ -63,7 +67,6 @@ static void createMonitoredItems(UA_Server *server, UA_Session *session, UA_Subs
     UA_MonitoredItem *newMon = UA_MonitoredItem_new();
     if(!newMon) {
         result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
-        UA_NodeStore_release(target);
         return;
     }
 
@@ -71,7 +74,6 @@ static void createMonitoredItems(UA_Server *server, UA_Session *session, UA_Subs
     if(retval != UA_STATUSCODE_GOOD) {
         result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
         MonitoredItem_delete(newMon);
-        UA_NodeStore_release(target);
         return;
     }
 
@@ -83,7 +85,7 @@ static void createMonitoredItems(UA_Server *server, UA_Session *session, UA_Subs
     UA_BOUNDEDVALUE_SETWBOUNDS(session->subscriptionManager.globalSamplingInterval,
                                request->requestedParameters.samplingInterval,
                                result->revisedSamplingInterval);
-    newMon->samplingInterval = result->revisedSamplingInterval;
+    newMon->samplingInterval = (UA_UInt32)result->revisedSamplingInterval;
 
     /* set the queue size */
     UA_BOUNDEDVALUE_SETWBOUNDS(session->subscriptionManager.globalQueueSize,
@@ -100,8 +102,6 @@ static void createMonitoredItems(UA_Server *server, UA_Session *session, UA_Subs
 
     // todo: add a job that samples the value (for fixed intervals)
     // todo: add a pointer to the monitoreditem to the variable, so that events get propagated
-    
-    UA_NodeStore_release(target);
 }
 
 void Service_CreateMonitoredItems(UA_Server *server, UA_Session *session,
@@ -119,14 +119,14 @@ void Service_CreateMonitoredItems(UA_Server *server, UA_Session *session,
         return;
     }
 
-    response->results = UA_Array_new(&UA_TYPES[UA_TYPES_MONITOREDITEMCREATERESULT], request->itemsToCreateSize);
+    response->results = UA_Array_new(request->itemsToCreateSize, &UA_TYPES[UA_TYPES_MONITOREDITEMCREATERESULT]);
     if(!response->results) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
         return;
     }
     response->resultsSize = request->itemsToCreateSize;
 
-    for(UA_Int32 i = 0; i<request->itemsToCreateSize; i++)
+    for(size_t i = 0; i < request->itemsToCreateSize; i++)
         createMonitoredItems(server, session, sub, &request->itemsToCreate[i], &response->results[i]);
 }
 
@@ -139,7 +139,7 @@ void Service_Publish(UA_Server *server, UA_Session *session, const UA_PublishReq
     // Delete Acknowledged Subscription Messages
     response->resultsSize = request->subscriptionAcknowledgementsSize;
     response->results     = UA_malloc(sizeof(UA_StatusCode)*(response->resultsSize));
-    for(UA_Int32 i = 0; i < request->subscriptionAcknowledgementsSize; i++) {
+    for(size_t i = 0; i < request->subscriptionAcknowledgementsSize; i++) {
         response->results[i] = UA_STATUSCODE_GOOD;
         UA_Subscription *sub =
             SubscriptionManager_getSubscriptionByID(&session->subscriptionManager,
@@ -148,7 +148,7 @@ void Service_Publish(UA_Server *server, UA_Session *session, const UA_PublishReq
             response->results[i] = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
             continue;
         }
-        if(Subscription_deleteUnpublishedNotification(request->subscriptionAcknowledgements[i].sequenceNumber, sub) == 0)
+        if(Subscription_deleteUnpublishedNotification(request->subscriptionAcknowledgements[i].sequenceNumber, false, sub) == 0)
             response->results[i] = UA_STATUSCODE_BADSEQUENCENUMBERINVALID;
     }
     
@@ -167,18 +167,31 @@ void Service_Publish(UA_Server *server, UA_Session *session, const UA_PublishReq
             Subscription_updateNotifications(sub);
         }
         
-        if(Subscription_queuedNotifications(sub) <= 0)
+        if(sub->unpublishedNotificationsSize == 0)
             continue;
         
+        // This subscription has notifications in its queue (top NotificationMessage exists in the queue). 
+        // Due to republish, we need to check if there are any unplublished notifications first ()
+        UA_unpublishedNotification *notification = NULL;
+        LIST_FOREACH(notification, &sub->unpublishedNotifications, listEntry) {
+            if (notification->publishedOnce == UA_FALSE)
+                break;
+        }
+        if (notification == NULL)
+            continue;
+    
+        // We found an unpublished notification message in this subscription, which we will now publish.
         response->subscriptionId = sub->subscriptionID;
-        Subscription_copyTopNotificationMessage(&response->notificationMessage, sub);
-        if(sub->unpublishedNotifications.lh_first->notification->sequenceNumber > sub->sequenceNumber) {
+        Subscription_copyNotificationMessage(&response->notificationMessage, notification);
+        // Mark this notification as published
+        notification->publishedOnce = UA_TRUE;
+        if(notification->notification.sequenceNumber > sub->sequenceNumber) {
             // If this is a keepalive message, its seqNo is the next seqNo to be used for an actual msg.
             response->availableSequenceNumbersSize = 0;
             // .. and must be deleted
-            Subscription_deleteUnpublishedNotification(sub->sequenceNumber + 1, sub);
+            Subscription_deleteUnpublishedNotification(sub->sequenceNumber + 1, false, sub);
         } else {
-            response->availableSequenceNumbersSize = Subscription_queuedNotifications(sub);
+            response->availableSequenceNumbersSize = sub->unpublishedNotificationsSize;
             response->availableSequenceNumbers = Subscription_getAvailableSequenceNumbers(sub);
         }	  
         // FIXME: This should be in processMSG();
@@ -195,8 +208,8 @@ void Service_Publish(UA_Server *server, UA_Session *session, const UA_PublishReq
         response->subscriptionId = sub->subscriptionID;
         sub->keepAliveCount.currentValue=sub->keepAliveCount.minValue;
         Subscription_generateKeepAlive(sub);
-        Subscription_copyTopNotificationMessage(&response->notificationMessage, sub);
-        Subscription_deleteUnpublishedNotification(sub->sequenceNumber + 1, sub);
+        Subscription_copyNotificationMessage(&response->notificationMessage, sub->unpublishedNotifications.lh_first);
+        Subscription_deleteUnpublishedNotification(sub->sequenceNumber + 1, false, sub);
     }
     
     // FIXME: This should be in processMSG();
@@ -215,7 +228,7 @@ void Service_ModifySubscription(UA_Server *server, UA_Session *session,
     
     UA_BOUNDEDVALUE_SETWBOUNDS(session->subscriptionManager.globalPublishingInterval,
                                request->requestedPublishingInterval, response->revisedPublishingInterval);
-    sub->publishingInterval = response->revisedPublishingInterval;
+    sub->publishingInterval = (UA_DateTime)response->revisedPublishingInterval;
     
     UA_BOUNDEDVALUE_SETWBOUNDS(session->subscriptionManager.globalLifeTimeCount,
                                request->requestedLifetimeCount, response->revisedLifetimeCount);
@@ -248,9 +261,10 @@ void Service_DeleteSubscriptions(UA_Server *server, UA_Session *session,
     }
     response->resultsSize = request->subscriptionIdsSize;
 
-    for(UA_Int32 i = 0; i < request->subscriptionIdsSize; i++)
-        response->results[i] = SubscriptionManager_deleteSubscription(server, &session->subscriptionManager,
-                                                                      request->subscriptionIds[i]);
+    for(size_t i = 0; i < request->subscriptionIdsSize; i++)
+        response->results[i] =
+            SubscriptionManager_deleteSubscription(server, &session->subscriptionManager,
+                                                   request->subscriptionIds[i]);
 } 
 
 void Service_DeleteMonitoredItems(UA_Server *server, UA_Session *session,
@@ -270,7 +284,46 @@ void Service_DeleteMonitoredItems(UA_Server *server, UA_Session *session,
     }
     response->resultsSize = request->monitoredItemIdsSize;
 
-    for(UA_Int32 i = 0; i < request->monitoredItemIdsSize; i++)
-        response->results[i] = SubscriptionManager_deleteMonitoredItem(manager, sub->subscriptionID,
-                                                                       request->monitoredItemIds[i]);
+    for(size_t i = 0; i < request->monitoredItemIdsSize; i++)
+        response->results[i] =
+            SubscriptionManager_deleteMonitoredItem(manager, sub->subscriptionID,
+                                                    request->monitoredItemIds[i]);
+}
+
+void Service_Republish(UA_Server *server, UA_Session *session,
+                                const UA_RepublishRequest *request,
+                                UA_RepublishResponse *response) {
+    UA_SubscriptionManager *manager = &session->subscriptionManager;
+    UA_Subscription *sub = SubscriptionManager_getSubscriptionByID(manager, request->subscriptionId);
+    if (!sub) {
+        response->responseHeader.serviceResult = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
+        return;
+    }
+    
+    // Find the notification in question
+    UA_unpublishedNotification *notification;
+    LIST_FOREACH(notification, &sub->unpublishedNotifications, listEntry) {
+      if (notification->notification.sequenceNumber == request->retransmitSequenceNumber)
+	break;
+    }
+    if (!notification) {
+      response->responseHeader.serviceResult = UA_STATUSCODE_BADSEQUENCENUMBERINVALID;
+      return;
+    }
+    
+    // FIXME: By spec, this notification has to be in the "retransmit queue", i.e. publishedOnce must be
+    //        true. If this is not tested, the client just gets what he asks for... hence this part is
+    //        commented:
+    /* Check if the notification is in the published queue
+    if (notification->publishedOnce == UA_FALSE) {
+      response->responseHeader.serviceResult = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
+      return;
+    }
+    */
+    // Retransmit 
+    Subscription_copyNotificationMessage(&response->notificationMessage, notification);
+    // Mark this notification as published
+    notification->publishedOnce = UA_TRUE;
+    
+    return;
 }

+ 196 - 234
src/server/ua_services_view.c

@@ -4,16 +4,11 @@
 #include "ua_nodestore.h"
 #include "ua_util.h"
 
-static UA_StatusCode fillrefdescr(UA_NodeStore *ns, const UA_Node *curr, UA_ReferenceNode *ref,
-                                  UA_UInt32 mask, UA_ReferenceDescription *descr)
-{
-    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+static UA_StatusCode
+fillReferenceDescription(UA_NodeStore *ns, const UA_Node *curr, UA_ReferenceNode *ref,
+                         UA_UInt32 mask, UA_ReferenceDescription *descr) {
     UA_ReferenceDescription_init(descr);
-    retval |= UA_NodeId_copy(&curr->nodeId, &descr->nodeId.nodeId);
-    //TODO: ExpandedNodeId is mocked up
-    descr->nodeId.serverIndex = 0;
-    descr->nodeId.namespaceUri.length = -1;
-
+    UA_StatusCode retval = UA_NodeId_copy(&curr->nodeId, &descr->nodeId.nodeId);
     if(mask & UA_BROWSERESULTMASK_REFERENCETYPEID)
         retval |= UA_NodeId_copy(&ref->referenceTypeId, &descr->referenceTypeId);
     if(mask & UA_BROWSERESULTMASK_ISFORWARD)
@@ -26,7 +21,7 @@ static UA_StatusCode fillrefdescr(UA_NodeStore *ns, const UA_Node *curr, UA_Refe
         retval |= UA_LocalizedText_copy(&curr->displayName, &descr->displayName);
     if(mask & UA_BROWSERESULTMASK_TYPEDEFINITION){
         if(curr->nodeClass == UA_NODECLASS_OBJECT || curr->nodeClass == UA_NODECLASS_VARIABLE) {
-            for(UA_Int32 i = 0; i < curr->referencesSize; i++) {
+            for(size_t i = 0; i < curr->referencesSize; i++) {
                 UA_ReferenceNode *refnode = &curr->references[i];
                 if(refnode->referenceTypeId.identifier.numeric != UA_NS0ID_HASTYPEDEFINITION)
                     continue;
@@ -35,109 +30,101 @@ static UA_StatusCode fillrefdescr(UA_NodeStore *ns, const UA_Node *curr, UA_Refe
             }
         }
     }
-
-    if(retval)
-        UA_ReferenceDescription_deleteMembers(descr);
     return retval;
 }
 
+#ifdef UA_EXTERNAL_NAMESPACES
+static const UA_Node *
+returnRelevantNodeExternal(UA_ExternalNodeStore *ens, const UA_BrowseDescription *descr,
+                           const UA_ReferenceNode *reference) {
+    /*	prepare a read request in the external nodestore	*/
+    UA_ReadValueId *readValueIds = UA_Array_new(&UA_TYPES[UA_TYPES_READVALUEID], 6);
+    UA_UInt32 *indices = UA_Array_new(&UA_TYPES[UA_TYPES_UINT32], 6);
+    UA_UInt32 indicesSize = 6;
+    UA_DataValue *readNodesResults = UA_Array_new(&UA_TYPES[UA_TYPES_DATAVALUE], 6);
+    UA_DiagnosticInfo *diagnosticInfos = UA_Array_new(&UA_TYPES[UA_TYPES_DIAGNOSTICINFO], 6);
+    for(UA_UInt32 i = 0; i < 6; i++) {
+        readValueIds[i].nodeId = reference->targetId.nodeId;
+        indices[i] = i;
+    }
+    readValueIds[0].attributeId = UA_ATTRIBUTEID_NODECLASS;
+    readValueIds[1].attributeId = UA_ATTRIBUTEID_BROWSENAME;
+    readValueIds[2].attributeId = UA_ATTRIBUTEID_DISPLAYNAME;
+    readValueIds[3].attributeId = UA_ATTRIBUTEID_DESCRIPTION;
+    readValueIds[4].attributeId = UA_ATTRIBUTEID_WRITEMASK;
+    readValueIds[5].attributeId = UA_ATTRIBUTEID_USERWRITEMASK;
+
+    ens->readNodes(ens->ensHandle, NULL, readValueIds, indices,
+                   indicesSize, readNodesResults, UA_FALSE, diagnosticInfos);
+
+    /* create and fill a dummy nodeStructure */
+    UA_Node *node = (UA_Node*) UA_ObjectNode_new();
+    UA_NodeId_copy(&(reference->targetId.nodeId), &(node->nodeId));
+    if(readNodesResults[0].status == UA_STATUSCODE_GOOD)
+        UA_NodeClass_copy((UA_NodeClass*)readNodesResults[0].value.data, &(node->nodeClass));
+    if(readNodesResults[1].status == UA_STATUSCODE_GOOD)
+        UA_QualifiedName_copy((UA_QualifiedName*)readNodesResults[1].value.data, &(node->browseName));
+    if(readNodesResults[2].status == UA_STATUSCODE_GOOD)
+        UA_LocalizedText_copy((UA_LocalizedText*)readNodesResults[2].value.data, &(node->displayName));
+    if(readNodesResults[3].status == UA_STATUSCODE_GOOD)
+        UA_LocalizedText_copy((UA_LocalizedText*)readNodesResults[3].value.data, &(node->description));
+    if(readNodesResults[4].status == UA_STATUSCODE_GOOD)
+        UA_UInt32_copy((UA_UInt32*)readNodesResults[4].value.data, &(node->writeMask));
+    if(readNodesResults[5].status == UA_STATUSCODE_GOOD)
+        UA_UInt32_copy((UA_UInt32*)readNodesResults[5].value.data, &(node->userWriteMask));
+    UA_Array_delete(readValueIds, &UA_TYPES[UA_TYPES_READVALUEID], 6);
+    UA_Array_delete(indices, &UA_TYPES[UA_TYPES_UINT32], 6);
+    UA_Array_delete(readNodesResults, &UA_TYPES[UA_TYPES_DATAVALUE], 6);
+    UA_Array_delete(diagnosticInfos, &UA_TYPES[UA_TYPES_DIAGNOSTICINFO], 6);
+    if(node && descr->nodeClassMask != 0 && (node->nodeClass & descr->nodeClassMask) == 0) {
+        UA_ObjectNode_delete((UA_ObjectNode*)node);
+        return NULL;
+    }
+    return node;
+}
+#endif
+
 /* Tests if the node is relevant to the browse request and shall be returned. If
    so, it is retrieved from the Nodestore. If not, null is returned. */
-static const UA_Node *relevant_node(UA_Server *server, UA_NodeStore *ns, const UA_BrowseDescription *descr,
-                                    UA_Boolean return_all, UA_ReferenceNode *reference,
-                                    UA_NodeId *relevant, size_t relevant_count, UA_Boolean *isExternal)
-{
-    if(reference->isInverse == UA_TRUE && descr->browseDirection == UA_BROWSEDIRECTION_FORWARD)
-        return UA_NULL;
-    else if(reference->isInverse == UA_FALSE && descr->browseDirection == UA_BROWSEDIRECTION_INVERSE)
-        return UA_NULL;
-
+static const UA_Node *
+returnRelevantNode(UA_Server *server, const UA_BrowseDescription *descr, UA_Boolean return_all,
+                   const UA_ReferenceNode *reference, const UA_NodeId *relevant, size_t relevant_count,
+                   UA_Boolean *isExternal) {
+    /* reference in the right direction? */
+    if(reference->isInverse && descr->browseDirection == UA_BROWSEDIRECTION_FORWARD)
+        return NULL;
+    if(!reference->isInverse && descr->browseDirection == UA_BROWSEDIRECTION_INVERSE)
+        return NULL;
+
+    /* is the reference part of the hierarchy of references we look for? */
     if(!return_all) {
+        UA_Boolean is_relevant = UA_FALSE;
         for(size_t i = 0; i < relevant_count; i++) {
-            if(UA_NodeId_equal(&reference->referenceTypeId, &relevant[i]))
-                goto is_relevant;
+            if(UA_NodeId_equal(&reference->referenceTypeId, &relevant[i])) {
+                is_relevant = UA_TRUE;
+                break;
+            }
         }
-        return UA_NULL;
+        if(!is_relevant)
+            return NULL;
     }
-is_relevant: ;
-	*isExternal = UA_FALSE;
+
 #ifdef UA_EXTERNAL_NAMESPACES
-	const UA_Node *node = NULL;
-	size_t nsIndex;
-	for(nsIndex = 0; nsIndex < server->externalNamespacesSize; nsIndex++) {
+    /* return the node from an external namespace*/
+	for(size_t nsIndex = 0; nsIndex < server->externalNamespacesSize; nsIndex++) {
 		if(reference->targetId.nodeId.namespaceIndex != server->externalNamespaces[nsIndex].index)
 			continue;
-		else{
-			*isExternal = UA_TRUE;
-			break;
-		}
-	}
-	if(*isExternal == UA_FALSE){
-		node = UA_NodeStore_get(ns, &reference->targetId.nodeId);
-	} else {
-		/*	prepare a read request in the external nodestore	*/
-		UA_ExternalNodeStore *ens = &server->externalNamespaces[nsIndex].externalNodeStore;
-		UA_ReadValueId *readValueIds = UA_Array_new(&UA_TYPES[UA_TYPES_READVALUEID], 6);
-		UA_UInt32 *indices = UA_Array_new(&UA_TYPES[UA_TYPES_UINT32], 6);
-		UA_UInt32 indicesSize = 6;
-		UA_DataValue *readNodesResults = UA_Array_new(&UA_TYPES[UA_TYPES_DATAVALUE], 6);
-		UA_DiagnosticInfo *diagnosticInfos = UA_Array_new(&UA_TYPES[UA_TYPES_DIAGNOSTICINFO], 6);
-		for(UA_UInt32 i = 0; i<6; i++){
-			readValueIds[i].nodeId = reference->targetId.nodeId;
-			UA_String_init(&(readValueIds[i].indexRange));
-		    UA_QualifiedName_init(&(readValueIds[i].dataEncoding));
-		    indices[i] = i;
-		    UA_DataValue_init(&(readNodesResults[i]));
-		    UA_DiagnosticInfo_init(&(diagnosticInfos[i]));
-		}
-		readValueIds[0].attributeId = UA_ATTRIBUTEID_NODECLASS;
-		readValueIds[1].attributeId = UA_ATTRIBUTEID_BROWSENAME;
-		readValueIds[2].attributeId = UA_ATTRIBUTEID_DISPLAYNAME;
-		readValueIds[3].attributeId = UA_ATTRIBUTEID_DESCRIPTION;
-		readValueIds[4].attributeId = UA_ATTRIBUTEID_WRITEMASK;
-		readValueIds[5].attributeId = UA_ATTRIBUTEID_USERWRITEMASK;
-
-		ens->readNodes(ens->ensHandle, NULL, readValueIds,
-				indices, indicesSize, readNodesResults, UA_FALSE, diagnosticInfos);
-		/*	create and fill a dummy nodeStructure	*/
-		UA_Node *tempNode = (UA_Node*) UA_ObjectNode_new();
-		UA_NodeId_copy(&(reference->targetId.nodeId), &(tempNode->nodeId));
-		if(readNodesResults[0].status == UA_STATUSCODE_GOOD){
-			UA_NodeClass_copy((UA_NodeClass*)readNodesResults[0].value.data, &(tempNode->nodeClass));
-		}
-		if(readNodesResults[1].status == UA_STATUSCODE_GOOD){
-			UA_QualifiedName_copy((UA_QualifiedName*)readNodesResults[1].value.data, &(tempNode->browseName));
-		}
-		if(readNodesResults[2].status == UA_STATUSCODE_GOOD){
-			UA_LocalizedText_copy((UA_LocalizedText*)readNodesResults[2].value.data, &(tempNode->displayName));
-		}
-		if(readNodesResults[3].status == UA_STATUSCODE_GOOD){
-			UA_LocalizedText_copy((UA_LocalizedText*)readNodesResults[3].value.data, &(tempNode->description));
-		}
-		if(readNodesResults[4].status == UA_STATUSCODE_GOOD){
-			UA_UInt32_copy((UA_UInt32*)readNodesResults[4].value.data, &(tempNode->writeMask));
-		}
-		if(readNodesResults[5].status == UA_STATUSCODE_GOOD){
-			UA_UInt32_copy((UA_UInt32*)readNodesResults[5].value.data, &(tempNode->userWriteMask));
-		}
-		UA_Array_delete(readValueIds, &UA_TYPES[UA_TYPES_READVALUEID], 6);
-		UA_Array_delete(indices, &UA_TYPES[UA_TYPES_UINT32], 6);
-		UA_Array_delete(readNodesResults, &UA_TYPES[UA_TYPES_DATAVALUE], 6);
-		UA_Array_delete(diagnosticInfos, &UA_TYPES[UA_TYPES_DIAGNOSTICINFO], 6);
-		node = tempNode;
-	}
-#else
-    const UA_Node *node = UA_NodeStore_get(ns, &reference->targetId.nodeId);
-#endif
-    if(node && descr->nodeClassMask != 0 && (node->nodeClass & descr->nodeClassMask) == 0) {
-#ifdef UA_EXTERNAL_NAMESPACES
-    	if(*isExternal == UA_TRUE){
-    		UA_ObjectNode_delete((UA_ObjectNode*)node);
-    	} else
-#endif
-    	UA_NodeStore_release(node);
-        return UA_NULL;
+        *isExternal = UA_TRUE;
+        return returnRelevantNodeExternal(&server->externalNamespaces[nsIndex].externalNodeStore,
+                                          descr, reference);
     }
+#endif
+
+    /* return from the internal nodestore */
+    const UA_Node *node = UA_NodeStore_get(server->nodestore, &reference->targetId.nodeId);
+    if(node && descr->nodeClassMask != 0 && (node->nodeClass & descr->nodeClassMask) == 0)
+        return NULL;
+    *isExternal = UA_FALSE;
     return node;
 }
 
@@ -147,16 +134,13 @@ is_relevant: ;
  * 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 (emulated recursion).
  */
-static UA_StatusCode findsubtypes(UA_NodeStore *ns, const UA_NodeId *root, UA_NodeId **reftypes,
-                                  size_t *reftypes_count) {
+static UA_StatusCode
+findSubTypes(UA_NodeStore *ns, const UA_NodeId *root, UA_NodeId **reftypes, size_t *reftypes_count) {
     const UA_Node *node = UA_NodeStore_get(ns, root);
     if(!node)
         return UA_STATUSCODE_BADNOMATCH;
-    if(node->nodeClass != UA_NODECLASS_REFERENCETYPE)  {
-        UA_NodeStore_release(node);
+    if(node->nodeClass != UA_NODECLASS_REFERENCETYPE)
         return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
-    }
-    UA_NodeStore_release(node);
 
     size_t results_size = 20; // probably too big, but saves mallocs
     UA_NodeId *results = UA_malloc(sizeof(UA_NodeId) * results_size);
@@ -175,7 +159,7 @@ static UA_StatusCode findsubtypes(UA_NodeStore *ns, const UA_NodeId *root, UA_No
         node = UA_NodeStore_get(ns, &results[index]);
         if(!node || node->nodeClass != UA_NODECLASS_REFERENCETYPE)
             continue;
-        for(UA_Int32 i = 0; i < node->referencesSize; i++) {
+        for(size_t i = 0; i < node->referencesSize; i++) {
             if(node->references[i].referenceTypeId.identifier.numeric != UA_NS0ID_HASSUBTYPE ||
                node->references[i].isInverse == UA_TRUE)
                 continue;
@@ -196,11 +180,10 @@ static UA_StatusCode findsubtypes(UA_NodeStore *ns, const UA_NodeId *root, UA_No
                 break;
             }
         }
-        UA_NodeStore_release(node);
     } while(++index <= last && retval == UA_STATUSCODE_GOOD);
 
-    if(retval) {
-        UA_Array_delete(results, &UA_TYPES[UA_TYPES_NODEID], last);
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_Array_delete(results, last, &UA_TYPES[UA_TYPES_NODEID]);
         return retval;
     }
 
@@ -209,31 +192,31 @@ static UA_StatusCode findsubtypes(UA_NodeStore *ns, const UA_NodeId *root, UA_No
     return UA_STATUSCODE_GOOD;
 }
 
-static void removeCp(struct ContinuationPointEntry *cp, UA_Session* session){
-    session->availableContinuationPoints++;
+static void removeCp(struct ContinuationPointEntry *cp, UA_Session* session) {
+    LIST_REMOVE(cp, pointers);
     UA_ByteString_deleteMembers(&cp->identifier);
     UA_BrowseDescription_deleteMembers(&cp->browseDescription);
-    LIST_REMOVE(cp, pointers);
     UA_free(cp);
+    session->availableContinuationPoints++;
 }
 
 /**
  * Results for a single browsedescription. This is the inner loop for both Browse and BrowseNext
  * @param session Session to save continuationpoints
  * @param ns The nodstore where the to-be-browsed node can be found
- * @param cp If cp points to a continuationpoint, we continue from there.
+ * @param cp If cp is not null, we continue from here
  *           If cp is null, we can add a new continuation point if possible and necessary.
  * @param descr If no cp is set, we take the browsedescription from there
  * @param maxrefs The maximum number of references the client has requested
  * @param result The entry in the request
  */
-static void browse(UA_Server *server, UA_Session *session, UA_NodeStore *ns, struct ContinuationPointEntry *cp,
-                   const UA_BrowseDescription *descr, UA_UInt32 maxrefs, UA_BrowseResult *result) {
-    UA_UInt32 continuationIndex = 0;
+void
+Service_Browse_single(UA_Server *server, UA_Session *session, struct ContinuationPointEntry *cp,
+                      const UA_BrowseDescription *descr, UA_UInt32 maxrefs, UA_BrowseResult *result) { 
     size_t referencesCount = 0;
-    UA_Int32 referencesIndex = 0;
-    
+    size_t referencesIndex = 0;
     /* set the browsedescription if a cp is given */
+    UA_UInt32 continuationIndex = 0;
     if(cp) {
         descr = &cp->browseDescription;
         maxrefs = cp->maxReferences;
@@ -250,44 +233,39 @@ static void browse(UA_Server *server, UA_Session *session, UA_NodeStore *ns, str
     
     /* get the references that match the browsedescription */
     size_t relevant_refs_size = 0;
-    UA_NodeId *relevant_refs = UA_NULL;
+    UA_NodeId *relevant_refs = NULL;
     UA_Boolean all_refs = UA_NodeId_isNull(&descr->referenceTypeId);
     if(!all_refs) {
         if(descr->includeSubtypes) {
-            result->statusCode = findsubtypes(ns, &descr->referenceTypeId, &relevant_refs, &relevant_refs_size);
+            result->statusCode = findSubTypes(server->nodestore, &descr->referenceTypeId,
+                                              &relevant_refs, &relevant_refs_size);
             if(result->statusCode != UA_STATUSCODE_GOOD)
                 return;
         } else {
-            const UA_Node *rootRef = UA_NodeStore_get(ns, &descr->referenceTypeId);
-            if(!rootRef) {
-                result->statusCode = UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
-                return;
-            } else if(rootRef->nodeClass != UA_NODECLASS_REFERENCETYPE) {
-                UA_NodeStore_release(rootRef);
+            const UA_Node *rootRef = UA_NodeStore_get(server->nodestore, &descr->referenceTypeId);
+            if(!rootRef || rootRef->nodeClass != UA_NODECLASS_REFERENCETYPE) {
                 result->statusCode = UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
                 return;
             }
-            UA_NodeStore_release(rootRef);
             relevant_refs = (UA_NodeId*)(uintptr_t)&descr->referenceTypeId;
             relevant_refs_size = 1;
         }
     }
 
     /* get the node */
-    const UA_Node *node = UA_NodeStore_get(ns, &descr->nodeId);
+    const UA_Node *node = UA_NodeStore_get(server->nodestore, &descr->nodeId);
     if(!node) {
         result->statusCode = UA_STATUSCODE_BADNODEIDUNKNOWN;
         if(!all_refs && descr->includeSubtypes)
-            UA_Array_delete(relevant_refs, &UA_TYPES[UA_TYPES_NODEID], relevant_refs_size);
+            UA_Array_delete(relevant_refs, relevant_refs_size, &UA_TYPES[UA_TYPES_NODEID]);
         return;
     }
 
     /* if the node has no references, just return */
     if(node->referencesSize <= 0) {
         result->referencesSize = 0;
-        UA_NodeStore_release(node);
         if(!all_refs && descr->includeSubtypes)
-            UA_Array_delete(relevant_refs, &UA_TYPES[UA_TYPES_NODEID], relevant_refs_size);
+            UA_Array_delete(relevant_refs, relevant_refs_size, &UA_TYPES[UA_TYPES_NODEID]);
         return;
     }
 
@@ -295,7 +273,11 @@ static void browse(UA_Server *server, UA_Session *session, UA_NodeStore *ns, str
     UA_UInt32 real_maxrefs = maxrefs;
     if(real_maxrefs == 0)
         real_maxrefs = node->referencesSize;
-    result->references = UA_malloc(sizeof(UA_ReferenceDescription) * real_maxrefs);
+    if(node->referencesSize <= 0)
+        real_maxrefs = 0;
+    else if(real_maxrefs > node->referencesSize)
+        real_maxrefs = node->referencesSize;
+    result->references = UA_Array_new(real_maxrefs, &UA_TYPES[UA_TYPES_REFERENCEDESCRIPTION]);
     if(!result->references) {
         result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
         goto cleanup;
@@ -304,57 +286,46 @@ static void browse(UA_Server *server, UA_Session *session, UA_NodeStore *ns, str
     /* loop over the node's references */
     size_t skipped = 0;
     UA_Boolean isExternal = UA_FALSE;
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
     for(; referencesIndex < node->referencesSize && referencesCount < real_maxrefs; referencesIndex++) {
     	isExternal = UA_FALSE;
-    	const UA_Node *current = relevant_node(server, ns, descr, all_refs, &node->references[referencesIndex],
-                                               relevant_refs, relevant_refs_size, &isExternal);
+    	const UA_Node *current =
+            returnRelevantNode(server, descr, all_refs, &node->references[referencesIndex],
+                               relevant_refs, relevant_refs_size, &isExternal);
         if(!current)
             continue;
+
         if(skipped < continuationIndex) {
-#ifdef UA_EXTERNAL_NAMESPACES
-        	if(isExternal == UA_TRUE){
-        		/*	relevant_node returns a node malloced with UA_ObjectNode_new if it is external (there is no UA_Node_new function)	*/
-        		UA_ObjectNode_delete((UA_ObjectNode*)current);
-        	} else {
-        		UA_NodeStore_release(current);
-        	}
-#else
-        	UA_NodeStore_release(current);
-#endif
             skipped++;
-            continue;
-        }
-        UA_StatusCode retval = fillrefdescr(ns, current, &node->references[referencesIndex], descr->resultMask,
-                                            &result->references[referencesCount]);
-#ifdef UA_EXTERNAL_NAMESPACES
-        if(isExternal == UA_TRUE){
-        	UA_ObjectNode_delete((UA_ObjectNode*)current);
         } else {
-        	UA_NodeStore_release(current);
+            retval |= fillReferenceDescription(server->nodestore, current, &node->references[referencesIndex],
+                                               descr->resultMask, &result->references[referencesCount]);
+            referencesCount++;
         }
-#else
-        	UA_NodeStore_release(current);
+#ifdef UA_EXTERNAL_NAMESPACES
+        /* relevant_node returns a node malloced with UA_ObjectNode_new
+           if it is external (there is no UA_Node_new function) */
+        if(isExternal == UA_TRUE)
+        	UA_ObjectNode_delete((UA_ObjectNode*)(uintptr_t)current);
 #endif
-        if(retval != UA_STATUSCODE_GOOD) {
-            UA_Array_delete(result->references, &UA_TYPES[UA_TYPES_REFERENCEDESCRIPTION], referencesCount);
-            result->references = UA_NULL;
-            result->referencesSize = 0;
-            result->statusCode = UA_STATUSCODE_UNCERTAINNOTALLNODESAVAILABLE;
-            goto cleanup;
-        }
-        referencesCount++;
     }
 
     result->referencesSize = referencesCount;
     if(referencesCount == 0) {
         UA_free(result->references);
-        result->references = UA_NULL;
+        result->references = NULL;
+    }
+
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_Array_delete(result->references, result->referencesSize, &UA_TYPES[UA_TYPES_REFERENCEDESCRIPTION]);
+        result->references = NULL;
+        result->referencesSize = 0;
+        result->statusCode = retval;
     }
 
     cleanup:
-    UA_NodeStore_release(node);
     if(!all_refs && descr->includeSubtypes)
-        UA_Array_delete(relevant_refs, &UA_TYPES[UA_TYPES_NODEID], relevant_refs_size);
+        UA_Array_delete(relevant_refs, relevant_refs_size, &UA_TYPES[UA_TYPES_NODEID]);
     if(result->statusCode != UA_STATUSCODE_GOOD)
         return;
 
@@ -370,7 +341,8 @@ static void browse(UA_Server *server, UA_Session *session, UA_NodeStore *ns, str
         }
     } else if(maxrefs != 0 && referencesCount >= maxrefs) {
         /* create a cp */
-        if(session->availableContinuationPoints <= 0 || !(cp = UA_malloc(sizeof(struct ContinuationPointEntry)))) {
+        if(session->availableContinuationPoints <= 0 ||
+           !(cp = UA_malloc(sizeof(struct ContinuationPointEntry)))) {
             result->statusCode = UA_STATUSCODE_BADNOCONTINUATIONPOINTS;
             return;
         }
@@ -392,6 +364,9 @@ static void browse(UA_Server *server, UA_Session *session, UA_NodeStore *ns, str
 
 void Service_Browse(UA_Server *server, UA_Session *session, const UA_BrowseRequest *request,
                     UA_BrowseResponse *response) {
+    UA_LOG_DEBUG(server->logger, UA_LOGCATEGORY_SESSION,
+                 "Processing BrowseRequest for Session (ns=%i,i=%i)",
+                 session->sessionId.namespaceIndex, session->sessionId.identifier.numeric);
     if(!UA_NodeId_isNull(&request->view.viewId)) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADVIEWIDUNKNOWN;
         return;
@@ -403,7 +378,7 @@ void Service_Browse(UA_Server *server, UA_Session *session, const UA_BrowseReque
     }
 
     size_t size = request->nodesToBrowseSize;
-    response->results = UA_Array_new(&UA_TYPES[UA_TYPES_BROWSERESULT], size);
+    response->results = UA_Array_new(size, &UA_TYPES[UA_TYPES_BROWSERESULT]);
     if(!response->results) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
         return;
@@ -418,7 +393,7 @@ void Service_Browse(UA_Server *server, UA_Session *session, const UA_BrowseReque
     UA_Boolean *isExternal = UA_alloca(sizeof(UA_Boolean) * size);
     UA_UInt32 *indices = UA_alloca(sizeof(UA_UInt32) * size);
 #endif /*NO_ALLOCA */
-    UA_memset(isExternal, UA_FALSE, sizeof(UA_Boolean) * size);
+    memset(isExternal, UA_FALSE, sizeof(UA_Boolean) * size);
     for(size_t j = 0; j < server->externalNamespacesSize; j++) {
         size_t indexSize = 0;
         for(size_t i = 0; i < size; i++) {
@@ -440,55 +415,40 @@ void Service_Browse(UA_Server *server, UA_Session *session, const UA_BrowseReque
 #ifdef UA_EXTERNAL_NAMESPACES
         if(!isExternal[i])
 #endif
-            browse(server, session, server->nodestore, UA_NULL, &request->nodesToBrowse[i],
-                   request->requestedMaxReferencesPerNode, &response->results[i]);
+            Service_Browse_single(server, session, NULL, &request->nodesToBrowse[i],
+                                  request->requestedMaxReferencesPerNode, &response->results[i]);
     }
 }
 
 void Service_BrowseNext(UA_Server *server, UA_Session *session, const UA_BrowseNextRequest *request,
                         UA_BrowseNextResponse *response) {
+    UA_LOG_DEBUG(server->logger, UA_LOGCATEGORY_SESSION,
+                 "Processing BrowseNextRequest for Session (ns=%i,i=%i)",
+                 session->sessionId.namespaceIndex, session->sessionId.identifier.numeric);
    if(request->continuationPointsSize <= 0) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
         return;
    }
    size_t size = request->continuationPointsSize;
-   if(!request->releaseContinuationPoints) {
-       /* continue with the cp */
-       response->results = UA_Array_new(&UA_TYPES[UA_TYPES_BROWSERESULT], size);
-       if(!response->results) {
-           response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-           return;
-       }
-       response->resultsSize = size;
-       for(size_t i = 0; i < size; i++) {
-           struct ContinuationPointEntry *cp;
-           LIST_FOREACH(cp, &session->continuationPoints, pointers) {
-               if(UA_ByteString_equal(&cp->identifier, &request->continuationPoints[i])) {
-                   browse(server, session, server->nodestore, cp, UA_NULL, 0, &response->results[i]);
-                   break;
-               }
-           }
-           if(!cp)
-               response->results[i].statusCode = UA_STATUSCODE_BADCONTINUATIONPOINTINVALID;
-       }
-   } else {
-       /* remove the cp */
-       response->results = UA_Array_new(&UA_TYPES[UA_TYPES_BROWSERESULT], size);
-       if(!response->results) {
-           response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-           return;
-       }
-       response->resultsSize = size;
-       for(size_t i = 0; i < size; i++) {
-           struct ContinuationPointEntry *cp = UA_NULL;
-           LIST_FOREACH(cp, &session->continuationPoints, pointers) {
-               if(UA_ByteString_equal(&cp->identifier, &request->continuationPoints[i])) {
+   response->results = UA_Array_new(size, &UA_TYPES[UA_TYPES_BROWSERESULT]);
+   if(!response->results) {
+       response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
+       return;
+   }
+
+   response->resultsSize = size;
+   for(size_t i = 0; i < size; i++) {
+       response->results[i].statusCode = UA_STATUSCODE_BADCONTINUATIONPOINTINVALID;
+       struct ContinuationPointEntry *cp, *temp;
+       LIST_FOREACH_SAFE(cp, &session->continuationPoints, pointers, temp) {
+           if(UA_ByteString_equal(&cp->identifier, &request->continuationPoints[i])) {
+               response->results[i].statusCode = UA_STATUSCODE_GOOD;
+               if(!request->releaseContinuationPoints)
+                   Service_Browse_single(server, session, cp, NULL, 0, &response->results[i]);
+               else
                    removeCp(cp, session);
-                   break;
-               }
+               break;
            }
-           if(!cp)
-               response->results[i].statusCode = UA_STATUSCODE_BADCONTINUATIONPOINTINVALID;
        }
    }
 }
@@ -499,15 +459,11 @@ void Service_BrowseNext(UA_Server *server, UA_Session *session, const UA_BrowseN
 
 static UA_StatusCode
 walkBrowsePath(UA_Server *server, UA_Session *session, const UA_Node *node, const UA_RelativePath *path,
-               UA_Int32 pathindex, UA_BrowsePathTarget **targets, UA_Int32 *targets_size,
-               UA_Int32 *target_count)
-{
+               size_t pathindex, UA_BrowsePathTarget **targets, size_t *targets_size,
+               size_t *target_count) {
     const UA_RelativePathElement *elem = &path->elements[pathindex];
-    if(elem->targetName.name.length == -1)
-        return UA_STATUSCODE_BADBROWSENAMEINVALID;
-
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
-    UA_NodeId *reftypes;
+    UA_NodeId *reftypes = NULL;
     size_t reftypes_count = 1; // all_refs or no subtypes => 1
     UA_Boolean all_refs = UA_FALSE;
     if(UA_NodeId_isNull(&elem->referenceTypeId))
@@ -515,12 +471,12 @@ walkBrowsePath(UA_Server *server, UA_Session *session, const UA_Node *node, cons
     else if(!elem->includeSubtypes)
         reftypes = (UA_NodeId*)(uintptr_t)&elem->referenceTypeId; // ptr magic due to const cast
     else {
-        retval = findsubtypes(server->nodestore, &elem->referenceTypeId, &reftypes, &reftypes_count);
+        retval = findSubTypes(server->nodestore, &elem->referenceTypeId, &reftypes, &reftypes_count);
         if(retval != UA_STATUSCODE_GOOD)
             return retval;
     }
 
-    for(UA_Int32 i = 0; i < node->referencesSize && retval == UA_STATUSCODE_GOOD; i++) {
+    for(size_t i = 0; i < node->referencesSize && retval == UA_STATUSCODE_GOOD; i++) {
         UA_Boolean match = all_refs;
         for(size_t j = 0; j < reftypes_count && !match; j++) {
             if(node->references[i].isInverse == elem->isInverse &&
@@ -538,7 +494,6 @@ walkBrowsePath(UA_Server *server, UA_Session *session, const UA_Node *node, cons
         // test the browsename
         if(elem->targetName.namespaceIndex != next->browseName.namespaceIndex ||
            !UA_String_equal(&elem->targetName.name, &next->browseName.name)) {
-            UA_NodeStore_release(next);
             continue;
         }
 
@@ -546,7 +501,6 @@ walkBrowsePath(UA_Server *server, UA_Session *session, const UA_Node *node, cons
             // recursion if the path is longer
             retval = walkBrowsePath(server, session, next, path, pathindex + 1,
                                     targets, targets_size, target_count);
-            UA_NodeStore_release(next);
         } else {
             // add the browsetarget
             if(*target_count >= *targets_size) {
@@ -554,7 +508,6 @@ walkBrowsePath(UA_Server *server, UA_Session *session, const UA_Node *node, cons
                 newtargets = UA_realloc(targets, sizeof(UA_BrowsePathTarget) * (*targets_size) * 2);
                 if(!newtargets) {
                     retval = UA_STATUSCODE_BADOUTOFMEMORY;
-                    UA_NodeStore_release(next);
                     break;
                 }
                 *targets = newtargets;
@@ -564,7 +517,6 @@ walkBrowsePath(UA_Server *server, UA_Session *session, const UA_Node *node, cons
             UA_BrowsePathTarget *res = *targets;
             UA_ExpandedNodeId_init(&res[*target_count].targetId);
             retval = UA_NodeId_copy(&next->nodeId, &res[*target_count].targetId.nodeId);
-            UA_NodeStore_release(next);
             if(retval != UA_STATUSCODE_GOOD)
                 break;
             res[*target_count].remainingPathIndex = UA_UINT32_MAX;
@@ -573,18 +525,18 @@ walkBrowsePath(UA_Server *server, UA_Session *session, const UA_Node *node, cons
     }
 
     if(!all_refs && elem->includeSubtypes)
-        UA_Array_delete(reftypes, &UA_TYPES[UA_TYPES_NODEID], (UA_Int32)reftypes_count);
+        UA_Array_delete(reftypes, reftypes_count, &UA_TYPES[UA_TYPES_NODEID]);
     return retval;
 }
 
-static void translateBrowsePath(UA_Server *server, UA_Session *session, const UA_BrowsePath *path,
-                                UA_BrowsePathResult *result) {
+void Service_TranslateBrowsePathsToNodeIds_single(UA_Server *server, UA_Session *session,
+                                                  const UA_BrowsePath *path, UA_BrowsePathResult *result) {
     if(path->relativePath.elementsSize <= 0) {
         result->statusCode = UA_STATUSCODE_BADNOTHINGTODO;
         return;
     }
         
-    UA_Int32 arraySize = 10;
+    size_t arraySize = 10;
     result->targets = UA_malloc(sizeof(UA_BrowsePathTarget) * arraySize);
     if(!result->targets) {
         result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
@@ -595,32 +547,33 @@ static void translateBrowsePath(UA_Server *server, UA_Session *session, const UA
     if(!firstNode) {
         result->statusCode = UA_STATUSCODE_BADNODEIDUNKNOWN;
         UA_free(result->targets);
-        result->targets = UA_NULL;
+        result->targets = NULL;
         return;
     }
     result->statusCode = walkBrowsePath(server, session, firstNode, &path->relativePath, 0,
                                         &result->targets, &arraySize, &result->targetsSize);
-    UA_NodeStore_release(firstNode);
     if(result->targetsSize == 0 && result->statusCode == UA_STATUSCODE_GOOD)
         result->statusCode = UA_STATUSCODE_BADNOMATCH;
     if(result->statusCode != UA_STATUSCODE_GOOD) {
-        UA_Array_delete(result->targets, &UA_TYPES[UA_TYPES_BROWSEPATHTARGET], result->targetsSize);
-        result->targets = UA_NULL;
-        result->targetsSize = -1;
+        UA_Array_delete(result->targets, result->targetsSize, &UA_TYPES[UA_TYPES_BROWSEPATHTARGET]);
+        result->targets = NULL;
+        result->targetsSize = 0;
     }
 }
 
 void Service_TranslateBrowsePathsToNodeIds(UA_Server *server, UA_Session *session,
                                            const UA_TranslateBrowsePathsToNodeIdsRequest *request,
                                            UA_TranslateBrowsePathsToNodeIdsResponse *response) {
+    UA_LOG_DEBUG(server->logger, UA_LOGCATEGORY_SESSION,
+                 "Processing TranslateBrowsePathsToNodeIdsRequest for Session (ns=%i,i=%i)",
+                 session->sessionId.namespaceIndex, session->sessionId.identifier.numeric);
 	if(request->browsePathsSize <= 0) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
         return;
     }
 
     size_t size = request->browsePathsSize;
-
-    response->results = UA_Array_new(&UA_TYPES[UA_TYPES_BROWSEPATHRESULT], size);
+    response->results = UA_Array_new(size, &UA_TYPES[UA_TYPES_BROWSEPATHRESULT]);
     if(!response->results) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
         return;
@@ -634,7 +587,7 @@ void Service_TranslateBrowsePathsToNodeIds(UA_Server *server, UA_Session *sessio
     UA_Boolean *isExternal = UA_alloca(sizeof(UA_Boolean) * size);
     UA_UInt32 *indices = UA_alloca(sizeof(UA_UInt32) * size);
 #endif /*NO_ALLOCA */
-    UA_memset(isExternal, UA_FALSE, sizeof(UA_Boolean) * size);
+    memset(isExternal, UA_FALSE, sizeof(UA_Boolean) * size);
     for(size_t j = 0; j < server->externalNamespacesSize; j++) {
     	size_t indexSize = 0;
     	for(size_t i = 0;i < size;i++) {
@@ -657,20 +610,25 @@ void Service_TranslateBrowsePathsToNodeIds(UA_Server *server, UA_Session *sessio
 #ifdef UA_EXTERNAL_NAMESPACES
     	if(!isExternal[i])
 #endif
-    		translateBrowsePath(server, session, &request->browsePaths[i], &response->results[i]);
+    		Service_TranslateBrowsePathsToNodeIds_single(server, session, &request->browsePaths[i],
+                                                         &response->results[i]);
     }
 }
 
 void Service_RegisterNodes(UA_Server *server, UA_Session *session, const UA_RegisterNodesRequest *request,
                            UA_RegisterNodesResponse *response) {
+    UA_LOG_DEBUG(server->logger, UA_LOGCATEGORY_SESSION,
+                 "Processing RegisterNodesRequest for Session (ns=%i,i=%i)",
+                 session->sessionId.namespaceIndex, session->sessionId.identifier.numeric);
+
 	//TODO: hang the nodeids to the session if really needed
 	response->responseHeader.timestamp = UA_DateTime_now();
     if(request->nodesToRegisterSize <= 0)
         response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
     else {
         response->responseHeader.serviceResult =
-            UA_Array_copy(request->nodesToRegister, (void**)&response->registeredNodeIds, &UA_TYPES[UA_TYPES_NODEID],
-                request->nodesToRegisterSize);
+            UA_Array_copy(request->nodesToRegister, request->nodesToRegisterSize,
+                          (void**)&response->registeredNodeIds, &UA_TYPES[UA_TYPES_NODEID]);
         if(response->responseHeader.serviceResult == UA_STATUSCODE_GOOD)
             response->registeredNodeIdsSize = request->nodesToRegisterSize;
     }
@@ -678,6 +636,10 @@ void Service_RegisterNodes(UA_Server *server, UA_Session *session, const UA_Regi
 
 void Service_UnregisterNodes(UA_Server *server, UA_Session *session, const UA_UnregisterNodesRequest *request,
                              UA_UnregisterNodesResponse *response) {
+    UA_LOG_DEBUG(server->logger, UA_LOGCATEGORY_SESSION,
+                 "Processing UnRegisterNodesRequest for Session (ns=%i,i=%i)",
+                 session->sessionId.namespaceIndex, session->sessionId.identifier.numeric);
+
 	//TODO: remove the nodeids from the session if really needed
 	response->responseHeader.timestamp = UA_DateTime_now();
 	if(request->nodesToUnregisterSize==0)

+ 30 - 23
src/server/ua_session_manager.c

@@ -2,8 +2,9 @@
 #include "ua_statuscodes.h"
 #include "ua_util.h"
 
-UA_StatusCode UA_SessionManager_init(UA_SessionManager *sessionManager, UA_UInt32 maxSessionCount,
-                                    UA_UInt32 maxSessionLifeTime, UA_UInt32 startSessionId) {
+UA_StatusCode
+UA_SessionManager_init(UA_SessionManager *sessionManager, UA_UInt32 maxSessionCount,
+                       UA_UInt32 maxSessionLifeTime, UA_UInt32 startSessionId) {
     LIST_INIT(&sessionManager->sessions);
     sessionManager->maxSessionCount = maxSessionCount;
     sessionManager->lastSessionId   = startSessionId;
@@ -13,44 +14,44 @@ UA_StatusCode UA_SessionManager_init(UA_SessionManager *sessionManager, UA_UInt3
 }
 
 void UA_SessionManager_deleteMembers(UA_SessionManager *sessionManager, UA_Server *server) {
-    session_list_entry *current;
-    while((current = LIST_FIRST(&sessionManager->sessions))) {
+    session_list_entry *current, *temp;
+    LIST_FOREACH_SAFE(current, &sessionManager->sessions, pointers, temp) {
         LIST_REMOVE(current, pointers);
         UA_Session_deleteMembersCleanup(&current->session, server);
         UA_free(current);
     }
 }
 
-void UA_SessionManager_cleanupTimedOut(UA_SessionManager *sessionManager, UA_Server* server, UA_DateTime now) {
-    session_list_entry *sentry = LIST_FIRST(&sessionManager->sessions);
-    while(sentry) {
+void UA_SessionManager_cleanupTimedOut(UA_SessionManager *sessionManager,
+                                       UA_Server* server, UA_DateTime now) {
+    session_list_entry *sentry, *temp;
+    LIST_FOREACH_SAFE(sentry, &sessionManager->sessions, pointers, temp) {
         if(sentry->session.validTill < now) {
-            session_list_entry *next = LIST_NEXT(sentry, pointers);
             LIST_REMOVE(sentry, pointers);
             UA_Session_deleteMembersCleanup(&sentry->session, server);
             UA_free(sentry);
             sessionManager->currentSessionCount--;
-            sentry = next;
-        } else {
-            sentry = LIST_NEXT(sentry, pointers);
         }
     }
 }
 
-UA_Session * UA_SessionManager_getSession(UA_SessionManager *sessionManager, const UA_NodeId *token) {
-    session_list_entry *current = UA_NULL;
+UA_Session *
+UA_SessionManager_getSession(UA_SessionManager *sessionManager, const UA_NodeId *token) {
+    session_list_entry *current = NULL;
     LIST_FOREACH(current, &sessionManager->sessions, pointers) {
-        if(UA_NodeId_equal(&current->session.authenticationToken, token))
-            break;
+        if(UA_NodeId_equal(&current->session.authenticationToken, token)) {
+            if(UA_DateTime_now() > current->session.validTill)
+                return NULL;
+            return &current->session;
+        }
     }
-    if(!current || UA_DateTime_now() > current->session.validTill)
-        return UA_NULL;
-    return &current->session;
+    return NULL;
 }
 
 /** Creates and adds a session. But it is not yet attached to a secure channel. */
-UA_StatusCode UA_SessionManager_createSession(UA_SessionManager *sessionManager, UA_SecureChannel *channel,
-                                              const UA_CreateSessionRequest *request, UA_Session **session) {
+UA_StatusCode
+UA_SessionManager_createSession(UA_SessionManager *sessionManager, UA_SecureChannel *channel,
+                                const UA_CreateSessionRequest *request, UA_Session **session) {
     if(sessionManager->currentSessionCount >= sessionManager->maxSessionCount)
         return UA_STATUSCODE_BADTOOMANYSESSIONS;
 
@@ -61,27 +62,33 @@ UA_StatusCode UA_SessionManager_createSession(UA_SessionManager *sessionManager,
     sessionManager->currentSessionCount++;
     UA_Session_init(&newentry->session);
     newentry->session.sessionId = UA_NODEID_NUMERIC(1, sessionManager->lastSessionId++);
-    UA_UInt32 randSeed = sessionManager->lastSessionId + UA_DateTime_now();
+    UA_UInt32 randSeed = (UA_UInt32)(sessionManager->lastSessionId + UA_DateTime_now());
     newentry->session.authenticationToken = UA_NODEID_GUID(1, UA_Guid_random(&randSeed));
+
     if(request->requestedSessionTimeout <= sessionManager->maxSessionLifeTime &&
        request->requestedSessionTimeout > 0)
-        newentry->session.timeout = request->requestedSessionTimeout;
+        newentry->session.timeout = (UA_Int64)request->requestedSessionTimeout;
     else
         newentry->session.timeout = sessionManager->maxSessionLifeTime; // todo: remove when the CTT is fixed
+
     UA_Session_updateLifetime(&newentry->session);
     LIST_INSERT_HEAD(&sessionManager->sessions, newentry, pointers);
     *session = &newentry->session;
     return UA_STATUSCODE_GOOD;
 }
 
-UA_StatusCode UA_SessionManager_removeSession(UA_SessionManager *sessionManager, UA_Server* server, const UA_NodeId *token) {
+UA_StatusCode
+UA_SessionManager_removeSession(UA_SessionManager *sessionManager,
+                                UA_Server* server, const UA_NodeId *token) {
     session_list_entry *current;
     LIST_FOREACH(current, &sessionManager->sessions, pointers) {
         if(UA_NodeId_equal(&current->session.authenticationToken, token))
             break;
     }
+
     if(!current)
         return UA_STATUSCODE_BADSESSIONIDINVALID;
+
     LIST_REMOVE(current, pointers);
     UA_Session_deleteMembersCleanup(&current->session, server);
     UA_free(current);

+ 14 - 10
src/server/ua_session_manager.h

@@ -1,7 +1,7 @@
 #ifndef UA_SESSION_MANAGER_H_
 #define UA_SESSION_MANAGER_H_
 
-#include "../deps/queue.h"
+#include "queue.h"
 #include "ua_server.h"
 #include "ua_util.h"
 #include "ua_session.h"
@@ -16,23 +16,27 @@ typedef struct UA_SessionManager {
     UA_UInt32    maxSessionCount;
     UA_Int32     lastSessionId;
     UA_UInt32    currentSessionCount;
-    UA_DateTime  maxSessionLifeTime;
+    UA_DateTime  maxSessionLifeTime;    // time in [ms]
 } UA_SessionManager;
 
-UA_StatusCode UA_SessionManager_init(UA_SessionManager *sessionManager, UA_UInt32 maxSessionCount,
-                                    UA_UInt32 maxSessionLifeTime, UA_UInt32 startSessionId);
+UA_StatusCode
+UA_SessionManager_init(UA_SessionManager *sessionManager, UA_UInt32 maxSessionCount,
+                       UA_UInt32 maxSessionLifeTime, UA_UInt32 startSessionId);
 
 void UA_SessionManager_deleteMembers(UA_SessionManager *sessionManager, UA_Server *server);
 
 void UA_SessionManager_cleanupTimedOut(UA_SessionManager *sessionManager, UA_Server *server, UA_DateTime now);
 
-UA_StatusCode UA_SessionManager_createSession(UA_SessionManager *sessionManager,
-                                              UA_SecureChannel *channel, const UA_CreateSessionRequest *request,
-                                              UA_Session **session);
+UA_StatusCode
+UA_SessionManager_createSession(UA_SessionManager *sessionManager,
+                                UA_SecureChannel *channel, const UA_CreateSessionRequest *request,
+                                UA_Session **session);
 
-UA_StatusCode UA_SessionManager_removeSession(UA_SessionManager *sessionManager, UA_Server *server, const UA_NodeId *token);
+UA_StatusCode
+UA_SessionManager_removeSession(UA_SessionManager *sessionManager,
+                                UA_Server *server, const UA_NodeId *token);
 
-/** Finds the session which is identified by the authentication token */
-UA_Session * UA_SessionManager_getSession(UA_SessionManager *sessionManager, const UA_NodeId *token);
+UA_Session *
+UA_SessionManager_getSession(UA_SessionManager *sessionManager, const UA_NodeId *token);
 
 #endif /* UA_SESSION_MANAGER_H_ */

+ 90 - 148
src/server/ua_subscription.c

@@ -9,81 +9,64 @@
 UA_Subscription *UA_Subscription_new(UA_Int32 subscriptionID) {
     UA_Subscription *new = UA_malloc(sizeof(UA_Subscription));
     if(!new)
-        return UA_NULL;
+        return NULL;
     new->subscriptionID = subscriptionID;
     new->lastPublished  = 0;
-    new->sequenceNumber = 0;
+    new->sequenceNumber = 1;
     memset(&new->timedUpdateJobGuid, 0, sizeof(UA_Guid));
-    new->timedUpdateJob          = UA_NULL;
+    new->timedUpdateJob          = NULL;
     new->timedUpdateIsRegistered = UA_FALSE;
     LIST_INIT(&new->MonitoredItems);
     LIST_INIT(&new->unpublishedNotifications);
+    new->unpublishedNotificationsSize = 0;
     return new;
 }
 
 void UA_Subscription_deleteMembers(UA_Subscription *subscription, UA_Server *server) {
-    UA_unpublishedNotification *not, *tmp_not;
-    UA_MonitoredItem *mon, *tmp_mon;
-    
     // Just in case any parallel process attempts to access this subscription
     // while we are deleting it... make it vanish.
     subscription->subscriptionID = 0;
     
     // Delete monitored Items
+    UA_MonitoredItem *mon, *tmp_mon;
     LIST_FOREACH_SAFE(mon, &subscription->MonitoredItems, listEntry, tmp_mon) {
         LIST_REMOVE(mon, listEntry);
         MonitoredItem_delete(mon);
     }
     
     // Delete unpublished Notifications
-    LIST_FOREACH_SAFE(not, &subscription->unpublishedNotifications, listEntry, tmp_not) {
-        Subscription_deleteUnpublishedNotification(not->notification->sequenceNumber, subscription);
-    }
+    Subscription_deleteUnpublishedNotification(0, true, subscription);
     
     // Unhook/Unregister any timed work assiociated with this subscription
-    if(subscription->timedUpdateJob != UA_NULL){
+    if(subscription->timedUpdateJob) {
         Subscription_unregisterUpdateJob(server, subscription);
         UA_free(subscription->timedUpdateJob);
     }
 }
 
-UA_UInt32 Subscription_queuedNotifications(UA_Subscription *subscription) {
-    if(!subscription)
-        return 0;
-
-    UA_UInt32 j = 0;
-    UA_unpublishedNotification *i;
-    LIST_FOREACH(i, &subscription->unpublishedNotifications, listEntry)
-        j++;
-    return j;
-}
-
 void Subscription_generateKeepAlive(UA_Subscription *subscription) {
     if(subscription->keepAliveCount.currentValue > subscription->keepAliveCount.minValue &&
        subscription->keepAliveCount.currentValue <= subscription->keepAliveCount.maxValue)
         return;
 
-    UA_unpublishedNotification *msg = UA_malloc(sizeof(UA_unpublishedNotification));
+    UA_unpublishedNotification *msg = UA_calloc(1,sizeof(UA_unpublishedNotification));
     if(!msg)
         return;
-    msg->notification = UA_NULL;
-    msg->notification = UA_malloc(sizeof(UA_NotificationMessage));
-    msg->notification->notificationData = UA_NULL;
+    msg->notification.notificationData = NULL;
     // KeepAlive uses next message, but does not increment counter
-    msg->notification->sequenceNumber = subscription->sequenceNumber + 1;
-    msg->notification->publishTime    = UA_DateTime_now();
-    msg->notification->notificationDataSize = 0;
+    msg->notification.sequenceNumber = subscription->sequenceNumber + 1;
+    msg->notification.publishTime    = UA_DateTime_now();
+    msg->notification.notificationDataSize = 0;
     LIST_INSERT_HEAD(&subscription->unpublishedNotifications, msg, listEntry);
+    subscription->unpublishedNotificationsSize += 1;
     subscription->keepAliveCount.currentValue = subscription->keepAliveCount.maxValue;
 }
 
 void Subscription_updateNotifications(UA_Subscription *subscription) {
     UA_MonitoredItem *mon;
     //MonitoredItem_queuedValue *queuedValue;
-    UA_unpublishedNotification *msg = NULL, *tempmsg;
+    UA_unpublishedNotification *msg;
     UA_UInt32 monItemsChangeT = 0, monItemsStatusT = 0, monItemsEventT = 0;
-    UA_DataChangeNotification *changeNotification;
-    size_t notificationOffset;
     
     if(!subscription || subscription->lastPublished + subscription->publishingInterval > UA_DateTime_now())
         return;
@@ -103,46 +86,42 @@ void Subscription_updateNotifications(UA_Subscription *subscription) {
     }
     
     // FIXME: This is hardcoded to 100 because it is not covered by the spec but we need to protect the server!
-    if(Subscription_queuedNotifications(subscription) >= 10) {
+    if(subscription->unpublishedNotificationsSize >= 10) {
         // Remove last entry
-        LIST_FOREACH_SAFE(msg, &subscription->unpublishedNotifications, listEntry, tempmsg)
-            Subscription_deleteUnpublishedNotification(msg->notification->sequenceNumber, subscription);
+        Subscription_deleteUnpublishedNotification(0, true, subscription);
     }
     
     if(monItemsChangeT == 0 && monItemsEventT == 0 && monItemsStatusT == 0) {
         // Decrement KeepAlive
         subscription->keepAliveCount.currentValue--;
         // +- Generate KeepAlive msg if counter overruns
-        Subscription_generateKeepAlive(subscription);
+        if (subscription->keepAliveCount.currentValue < subscription->keepAliveCount.minValue)
+          Subscription_generateKeepAlive(subscription);
+        
         return;
     }
     
-    msg = (UA_unpublishedNotification *) UA_malloc(sizeof(UA_unpublishedNotification));
-    msg->notification = UA_malloc(sizeof(UA_NotificationMessage));
-    INITPOINTER(msg->notification->notificationData);
-    msg->notification->sequenceNumber = subscription->sequenceNumber++;
-    msg->notification->publishTime    = UA_DateTime_now();
+    msg = UA_calloc(1, sizeof(UA_unpublishedNotification));
+    msg->notification.sequenceNumber = subscription->sequenceNumber++;
+    msg->notification.publishTime = UA_DateTime_now();
     
     // NotificationData is an array of Change, Status and Event messages, each containing the appropriate
     // list of Queued values from all monitoredItems of that type
-    msg->notification->notificationDataSize = ISNOTZERO(monItemsChangeT);
+    msg->notification.notificationDataSize = !!monItemsChangeT; // 1 if the pointer is not null, else 0
     // + ISNOTZERO(monItemsEventT) + ISNOTZERO(monItemsStatusT);
-    msg->notification->notificationData = UA_Array_new(&UA_TYPES[UA_TYPES_EXTENSIONOBJECT], 
-                                                       msg->notification->notificationDataSize);
+    msg->notification.notificationData =
+        UA_Array_new(msg->notification.notificationDataSize, &UA_TYPES[UA_TYPES_EXTENSIONOBJECT]);
     
-    for(int notmsgn=0; notmsgn < msg->notification->notificationDataSize; notmsgn++) {
+    for(size_t notmsgn = 0; notmsgn < msg->notification.notificationDataSize; notmsgn++) {
         // Set the notification message type and encoding for each of 
         //   the three possible NotificationData Types
-        msg->notification->notificationData[notmsgn].encoding = 1; // Encoding is always binary
-        msg->notification->notificationData[notmsgn].typeId = UA_NODEID_NUMERIC(0, 811);
+
+        /* msg->notification->notificationData[notmsgn].encoding = 1; // Encoding is always binary */
+        /* msg->notification->notificationData[notmsgn].typeId = UA_NODEID_NUMERIC(0, 811); */
       
         if(notmsgn == 0) {
-            // Construct a DataChangeNotification
-            changeNotification = UA_malloc(sizeof(UA_DataChangeNotification));
-	
-            // Create one DataChangeNotification for each queue item held in each monitoredItems queue:
-            changeNotification->monitoredItems = UA_Array_new(&UA_TYPES[UA_TYPES_MONITOREDITEMNOTIFICATION],
-                                                              monItemsChangeT);
+            UA_DataChangeNotification *changeNotification = UA_DataChangeNotification_new();
+            changeNotification->monitoredItems = UA_Array_new(monItemsChangeT, &UA_TYPES[UA_TYPES_MONITOREDITEMNOTIFICATION]);
 	
             // Scan all monitoredItems in this subscription and have their queue transformed into an Array of
             // the propper NotificationMessageType (Status, Change, Event)
@@ -154,27 +133,10 @@ void Subscription_updateNotifications(UA_Subscription *subscription) {
                 monItemsChangeT += MonitoredItem_QueueToDataChangeNotifications(&changeNotification->monitoredItems[monItemsChangeT], mon);
                 MonitoredItem_ClearQueue(mon);
             }
-            
-            changeNotification->monitoredItemsSize  = monItemsChangeT;
-            changeNotification->diagnosticInfosSize = 0;
-            changeNotification->diagnosticInfos     = UA_NULL;
-        
-            msg->notification->notificationData[notmsgn].body.length =
-                UA_calcSizeBinary(changeNotification, &UA_TYPES[UA_TYPES_DATACHANGENOTIFICATION]);
-            msg->notification->notificationData[notmsgn].body.data   =
-                UA_calloc(msg->notification->notificationData[notmsgn].body.length, sizeof(UA_Byte));
-        
-            notificationOffset = 0;
-            UA_encodeBinary(changeNotification, &UA_TYPES[UA_TYPES_DATACHANGENOTIFICATION],
-                            &msg->notification->notificationData[notmsgn].body, &notificationOffset);
-	
-            // FIXME: Not properly freed!
-            for(unsigned int i=0; i<monItemsChangeT; i++) {
-                UA_MonitoredItemNotification *thisNotification = &(changeNotification->monitoredItems[i]);
-                UA_DataValue_deleteMembers(&(thisNotification->value));
-            }
-            UA_free(changeNotification->monitoredItems);
-            UA_free(changeNotification);
+            changeNotification->monitoredItemsSize = monItemsChangeT;
+            msg->notification.notificationData[notmsgn].encoding = UA_EXTENSIONOBJECT_DECODED;
+            msg->notification.notificationData[notmsgn].content.decoded.type = &UA_TYPES[UA_TYPES_DATACHANGENOTIFICATION];
+            msg->notification.notificationData[notmsgn].content.decoded.data = changeNotification;
         } else if(notmsgn == 1) {
             // FIXME: Constructing a StatusChangeNotification is not implemented
         } else if(notmsgn == 2) {
@@ -182,63 +144,48 @@ void Subscription_updateNotifications(UA_Subscription *subscription) {
         }
     }
     LIST_INSERT_HEAD(&subscription->unpublishedNotifications, msg, listEntry);
+    subscription->unpublishedNotificationsSize += 1;
 }
 
 UA_UInt32 *Subscription_getAvailableSequenceNumbers(UA_Subscription *sub) {
-    UA_UInt32 *seqArray = UA_malloc(sizeof(UA_UInt32) * Subscription_queuedNotifications(sub));
+    UA_UInt32 *seqArray = UA_malloc(sizeof(UA_UInt32) * sub->unpublishedNotificationsSize);
     if(!seqArray)
-        return UA_NULL;
+        return NULL;
   
     int i = 0;
     UA_unpublishedNotification *not;
     LIST_FOREACH(not, &sub->unpublishedNotifications, listEntry) {
-        seqArray[i] = not->notification->sequenceNumber;
+        seqArray[i] = not->notification.sequenceNumber;
         i++;
     }
     return seqArray;
 }
 
-void Subscription_copyTopNotificationMessage(UA_NotificationMessage *dst, UA_Subscription *sub) {
+void Subscription_copyNotificationMessage(UA_NotificationMessage *dst, UA_unpublishedNotification *src) {
     if(!dst)
         return;
     
-    if(Subscription_queuedNotifications(sub) == 0) {
-      dst->notificationDataSize = 0;
-      dst->publishTime = UA_DateTime_now();
-      dst->sequenceNumber = 0;
-      return;
-    }
-    
-    UA_NotificationMessage *latest = LIST_FIRST(&sub->unpublishedNotifications)->notification;
+    UA_NotificationMessage *latest = &src->notification;
     dst->notificationDataSize = latest->notificationDataSize;
     dst->publishTime = latest->publishTime;
     dst->sequenceNumber = latest->sequenceNumber;
     
     if(latest->notificationDataSize == 0)
         return;
-    
-    dst->notificationData = (UA_ExtensionObject *) UA_malloc(sizeof(UA_ExtensionObject));
-    dst->notificationData->encoding = latest->notificationData->encoding;
-    dst->notificationData->typeId   = latest->notificationData->typeId;
-    UA_ByteString_copy(&latest->notificationData->body,
-                       &dst->notificationData->body);
+
+    dst->notificationData = UA_ExtensionObject_new();
+    UA_ExtensionObject_copy(latest->notificationData, dst->notificationData);
 }
 
-UA_UInt32 Subscription_deleteUnpublishedNotification(UA_UInt32 seqNo, UA_Subscription *sub) {
+UA_UInt32 Subscription_deleteUnpublishedNotification(UA_UInt32 seqNo, UA_Boolean bDeleteAll, UA_Subscription *sub) {
     UA_UInt32 deletedItems = 0;
     UA_unpublishedNotification *not, *tmp;
     LIST_FOREACH_SAFE(not, &sub->unpublishedNotifications, listEntry, tmp) {
-        if(not->notification->sequenceNumber != seqNo)
+        if(!bDeleteAll && not->notification.sequenceNumber != seqNo)
             continue;
         LIST_REMOVE(not, listEntry);
-        if(not->notification) {
-            if(not->notification->notificationData) {
-                if(not->notification->notificationData->body.data)
-                    UA_free(not->notification->notificationData->body.data);
-                UA_free(not->notification->notificationData);
-            }
-            UA_free(not->notification);
-        }
+        sub->unpublishedNotificationsSize -= 1;
+        UA_NotificationMessage_deleteMembers(&not->notification);
         UA_free(not);
         deletedItems++;
     }
@@ -267,9 +214,8 @@ static void Subscription_timedUpdateNotificationsJob(UA_Server *server, void *da
     Subscription_updateNotifications(sub);
 }
 
-
 UA_StatusCode Subscription_createdUpdateJob(UA_Server *server, UA_Guid jobId, UA_Subscription *sub) {
-    if(server == UA_NULL || sub == UA_NULL)
+    if(server == NULL || sub == NULL)
         return UA_STATUSCODE_BADSERVERINDEXINVALID;
         
     UA_Job *theWork;
@@ -287,38 +233,28 @@ UA_StatusCode Subscription_createdUpdateJob(UA_Server *server, UA_Guid jobId, UA
 }
 
 UA_StatusCode Subscription_registerUpdateJob(UA_Server *server, UA_Subscription *sub) {
-    if(server == UA_NULL || sub == UA_NULL)
-        return UA_STATUSCODE_BADSERVERINDEXINVALID;
-    
     if(sub->publishingInterval <= 5 ) 
         return UA_STATUSCODE_BADNOTSUPPORTED;
     
-    // Practically enough, the client sends a uint32 in ms, which we store as datetime, which here is required in as uint32 in ms as the interval
-#ifdef _MSC_VER
-    UA_Int32 retval = UA_Server_addRepeatedJob(server, *(sub->timedUpdateJob), sub->publishingInterval,
-                                               &sub->timedUpdateJobGuid);
-#else
+    /* Practically enough, the client sends a uint32 in ms, which we store as
+       datetime, which here is required in as uint32 in ms as the interval */
     UA_StatusCode retval = UA_Server_addRepeatedJob(server, *sub->timedUpdateJob, sub->publishingInterval,
                                                     &sub->timedUpdateJobGuid);
-#endif
-    if(!retval)
+    if(retval == UA_STATUSCODE_GOOD)
         sub->timedUpdateIsRegistered = UA_TRUE;
     return retval;
 }
 
 UA_StatusCode Subscription_unregisterUpdateJob(UA_Server *server, UA_Subscription *sub) {
-    if(server == UA_NULL || sub == UA_NULL)
-        return UA_STATUSCODE_BADSERVERINDEXINVALID;
-    UA_Int32 retval = UA_Server_removeRepeatedJob(server, sub->timedUpdateJobGuid);
     sub->timedUpdateIsRegistered = UA_FALSE;
-    return retval;
+    return UA_Server_removeRepeatedJob(server, sub->timedUpdateJobGuid);
 }
 
 /*****************/
 /* MonitoredItem */
 /*****************/
 
-UA_MonitoredItem *UA_MonitoredItem_new() {
+UA_MonitoredItem * UA_MonitoredItem_new() {
     UA_MonitoredItem *new = (UA_MonitoredItem *) UA_malloc(sizeof(UA_MonitoredItem));
     new->queueSize   = (UA_UInt32_BoundedValue) { .minValue = 0, .maxValue = 0, .currentValue = 0};
     new->lastSampled = 0;
@@ -326,7 +262,7 @@ UA_MonitoredItem *UA_MonitoredItem_new() {
     new->monitoredItemType = MONITOREDITEM_TYPE_CHANGENOTIFY;
     TAILQ_INIT(&new->queue);
     UA_NodeId_init(&new->monitoredNodeId);
-    INITPOINTER(new->lastSampledValue.data );
+    new->lastSampledValue.data = 0;
     return new;
 }
 
@@ -352,16 +288,11 @@ int MonitoredItem_QueueToDataChangeNotifications(UA_MonitoredItemNotification *d
     // Count instead of relying on the items currentValue
     TAILQ_FOREACH(queueItem, &monitoredItem->queue, listEntry) {
         dst[queueSize].clientHandle = monitoredItem->clientHandle;
+        UA_DataValue_copy(&queueItem->value, &dst[queueSize].value);
+
         dst[queueSize].value.hasServerPicoseconds = UA_FALSE;
         dst[queueSize].value.hasServerTimestamp   = UA_TRUE;
         dst[queueSize].value.serverTimestamp      = UA_DateTime_now();
-        dst[queueSize].value.hasSourcePicoseconds = UA_FALSE;
-        dst[queueSize].value.hasSourceTimestamp   = UA_TRUE;
-        dst[queueSize].value.sourceTimestamp      = UA_DateTime_now();
-        dst[queueSize].value.hasValue             = UA_TRUE;
-        dst[queueSize].value.status = UA_STATUSCODE_GOOD;
-    
-        UA_Variant_copy(&(queueItem->value), &(dst[queueSize].value.value));
     
         // Do not create variants with no type -> will make calcSizeBinary() segfault.
         if(dst[queueSize].value.value.type)
@@ -374,14 +305,14 @@ void MonitoredItem_ClearQueue(UA_MonitoredItem *monitoredItem) {
     MonitoredItem_queuedValue *val, *val_tmp;
     TAILQ_FOREACH_SAFE(val, &monitoredItem->queue, listEntry, val_tmp) {
         TAILQ_REMOVE(&monitoredItem->queue, val, listEntry);
-        UA_Variant_deleteMembers(&val->value);
+        UA_DataValue_deleteMembers(&val->value);
         UA_free(val);
     }
     monitoredItem->queueSize.currentValue = 0;
 }
 
 UA_Boolean MonitoredItem_CopyMonitoredValueToVariant(UA_UInt32 attributeID, const UA_Node *src,
-                                                     UA_Variant *dst) {
+                                                     UA_DataValue *dst) {
     UA_Boolean samplingError = UA_TRUE; 
     UA_DataValue sourceDataValue;
     UA_DataValue_init(&sourceDataValue);
@@ -390,31 +321,38 @@ UA_Boolean MonitoredItem_CopyMonitoredValueToVariant(UA_UInt32 attributeID, cons
     // FIXME: Not all attributeIDs can be monitored yet
     switch(attributeID) {
     case UA_ATTRIBUTEID_NODEID:
-        UA_Variant_setScalarCopy(dst, (const UA_NodeId*)&src->nodeId, &UA_TYPES[UA_TYPES_NODEID]);
+        UA_Variant_setScalarCopy(&dst->value, (const UA_NodeId*)&src->nodeId, &UA_TYPES[UA_TYPES_NODEID]);
+        dst->hasValue = UA_TRUE;
         samplingError = UA_FALSE;
         break;
     case UA_ATTRIBUTEID_NODECLASS:
-        UA_Variant_setScalarCopy(dst, (const UA_Int32*)&src->nodeClass, &UA_TYPES[UA_TYPES_INT32]);
+        UA_Variant_setScalarCopy(&dst->value, (const UA_Int32*)&src->nodeClass, &UA_TYPES[UA_TYPES_INT32]);
+        dst->hasValue = UA_TRUE;
         samplingError = UA_FALSE;
         break;
     case UA_ATTRIBUTEID_BROWSENAME:
-        UA_Variant_setScalarCopy(dst, (const UA_String*)&src->browseName, &UA_TYPES[UA_TYPES_QUALIFIEDNAME]);
+        UA_Variant_setScalarCopy(&dst->value, (const UA_String*)&src->browseName, &UA_TYPES[UA_TYPES_QUALIFIEDNAME]);
+        dst->hasValue = UA_TRUE;
         samplingError = UA_FALSE;
         break;
     case UA_ATTRIBUTEID_DISPLAYNAME:
-        UA_Variant_setScalarCopy(dst, (const UA_String*)&src->displayName, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
+        UA_Variant_setScalarCopy(&dst->value, (const UA_String*)&src->displayName, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
+        dst->hasValue = UA_TRUE;
         samplingError = UA_FALSE;
         break;
     case UA_ATTRIBUTEID_DESCRIPTION:
-        UA_Variant_setScalarCopy(dst, (const UA_String*)&src->displayName, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
+        UA_Variant_setScalarCopy(&dst->value, (const UA_String*)&src->displayName, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
+        dst->hasValue = UA_TRUE;
         samplingError = UA_FALSE;
         break;
     case UA_ATTRIBUTEID_WRITEMASK:
-        UA_Variant_setScalarCopy(dst, (const UA_String*)&src->writeMask, &UA_TYPES[UA_TYPES_UINT32]);
+        UA_Variant_setScalarCopy(&dst->value, (const UA_String*)&src->writeMask, &UA_TYPES[UA_TYPES_UINT32]);
+        dst->hasValue = UA_TRUE;
         samplingError = UA_FALSE;
         break;
     case UA_ATTRIBUTEID_USERWRITEMASK:
-        UA_Variant_setScalarCopy(dst, (const UA_String*)&src->writeMask, &UA_TYPES[UA_TYPES_UINT32]);
+        UA_Variant_setScalarCopy(&dst->value, (const UA_String*)&src->writeMask, &UA_TYPES[UA_TYPES_UINT32]);
+        dst->hasValue = UA_TRUE;
         samplingError = UA_FALSE;
         break;
     case UA_ATTRIBUTEID_ISABSTRACT:
@@ -431,20 +369,22 @@ UA_Boolean MonitoredItem_CopyMonitoredValueToVariant(UA_UInt32 attributeID, cons
         if(src->nodeClass == UA_NODECLASS_VARIABLE) {
             const UA_VariableNode *vsrc = (const UA_VariableNode*)src;
             if(srcAsVariableNode->valueSource == UA_VALUESOURCE_VARIANT) {
-                UA_Variant_copy(&vsrc->value.variant, dst);
+                UA_Variant_copy(&vsrc->value.variant.value, &dst->value);
+                dst->hasValue = UA_TRUE;
+                //no onRead callback here since triggered by a subscription
                 samplingError = UA_FALSE;
             } else {
                 if(srcAsVariableNode->valueSource != UA_VALUESOURCE_DATASOURCE)
                     break;
                 // todo: handle numeric ranges
-                if(srcAsVariableNode->value.dataSource.read(vsrc->value.dataSource.handle, UA_TRUE, UA_NULL,
+                if(srcAsVariableNode->value.dataSource.read(vsrc->value.dataSource.handle, vsrc->nodeId, UA_TRUE, NULL,
                                                             &sourceDataValue) != UA_STATUSCODE_GOOD)
                     break;
-                UA_Variant_copy(&sourceDataValue.value, dst);
+                UA_DataValue_copy(&sourceDataValue, dst);
                 if(sourceDataValue.value.data) {
                     UA_deleteMembers(sourceDataValue.value.data, sourceDataValue.value.type);
                     UA_free(sourceDataValue.value.data);
-                    sourceDataValue.value.data = UA_NULL;
+                    sourceDataValue.value.data = NULL;
                 }
                 UA_DataValue_deleteMembers(&sourceDataValue);
                 samplingError = UA_FALSE;
@@ -492,9 +432,9 @@ void MonitoredItem_QueuePushDataValue(UA_Server *server, UA_MonitoredItem *monit
     if(!newvalue)
         return;
 
-    newvalue->listEntry.tqe_next = UA_NULL;
-    newvalue->listEntry.tqe_prev = UA_NULL;
-    UA_Variant_init(&newvalue->value);
+    newvalue->listEntry.tqe_next = NULL;
+    newvalue->listEntry.tqe_prev = NULL;
+    UA_DataValue_init(&newvalue->value);
 
     // Verify that the *Node being monitored is still valid
     // Looking up the in the nodestore is only necessary if we suspect that it is changed during writes
@@ -507,9 +447,9 @@ void MonitoredItem_QueuePushDataValue(UA_Server *server, UA_MonitoredItem *monit
   
     UA_Boolean samplingError = MonitoredItem_CopyMonitoredValueToVariant(monitoredItem->attributeID, target,
                                                                          &newvalue->value);
-    UA_NodeStore_release(target);
-    if(samplingError != UA_FALSE || !newvalue->value.type) {
-        UA_Variant_deleteMembers(&newvalue->value);
+
+    if(samplingError != UA_FALSE || !newvalue->value.value.type) {
+        UA_DataValue_deleteMembers(&newvalue->value);
         UA_free(newvalue);
         return;
     }
@@ -527,9 +467,11 @@ void MonitoredItem_QueuePushDataValue(UA_Server *server, UA_MonitoredItem *monit
     }
   
     // encode the data to find if its different to the previous
-    newValueAsByteString.length = UA_calcSizeBinary(&newvalue->value, &UA_TYPES[UA_TYPES_VARIANT]);
+    newValueAsByteString.length = 512; // Todo: Hack! We should make a copy of the value, not encode it. UA_calcSizeBinary(&newvalue->value, &UA_TYPES[UA_TYPES_DATAVALUE]);
     newValueAsByteString.data   = UA_malloc(newValueAsByteString.length);
-    UA_encodeBinary(&newvalue->value, &UA_TYPES[UA_TYPES_VARIANT], &newValueAsByteString, &encodingOffset);
+    UA_StatusCode retval = UA_encodeBinary(&newvalue->value, &UA_TYPES[UA_TYPES_DATAVALUE], &newValueAsByteString, &encodingOffset);
+    if(retval != UA_STATUSCODE_GOOD)
+        UA_ByteString_deleteMembers(&newValueAsByteString);
   
     if(!monitoredItem->lastSampledValue.data) { 
         UA_ByteString_copy(&newValueAsByteString, &monitoredItem->lastSampledValue);
@@ -539,7 +481,7 @@ void MonitoredItem_QueuePushDataValue(UA_Server *server, UA_MonitoredItem *monit
         UA_free(newValueAsByteString.data);
     } else {
         if(UA_String_equal(&newValueAsByteString, &monitoredItem->lastSampledValue) == UA_TRUE) {
-            UA_Variant_deleteMembers(&newvalue->value);
+            UA_DataValue_deleteMembers(&newvalue->value);
             UA_free(newvalue);
             UA_String_deleteMembers(&newValueAsByteString);
             return;

+ 10 - 13
src/server/ua_subscription.h

@@ -6,9 +6,6 @@
 #include "ua_types_generated.h"
 #include "ua_nodes.h"
 
-#define INITPOINTER(src) (src) = NULL;
-#define ISNOTZERO(value) ((value == 0) ? 0 : 1)
-
 /*****************/
 /* MonitoredItem */
 /*****************/
@@ -32,7 +29,7 @@ typedef enum {
 } UA_MONITOREDITEM_TYPE;
 
 typedef struct MonitoredItem_queuedValue {
-    UA_Variant value;
+    UA_DataValue value;
     TAILQ_ENTRY(MonitoredItem_queuedValue) listEntry;
 } MonitoredItem_queuedValue;
 
@@ -45,7 +42,7 @@ typedef struct UA_MonitoredItem {
     UA_NodeId monitoredNodeId; 
     UA_UInt32 attributeID;
     UA_UInt32 clientHandle;
-    UA_UInt32 samplingInterval;
+    UA_UInt32 samplingInterval; // [ms]
     UA_UInt32_BoundedValue queueSize;
     UA_Boolean discardOldest;
     UA_DateTime lastSampled;
@@ -60,7 +57,7 @@ void MonitoredItem_delete(UA_MonitoredItem *monitoredItem);
 void MonitoredItem_QueuePushDataValue(UA_Server *server, UA_MonitoredItem *monitoredItem);
 void MonitoredItem_ClearQueue(UA_MonitoredItem *monitoredItem);
 UA_Boolean MonitoredItem_CopyMonitoredValueToVariant(UA_UInt32 attributeID, const UA_Node *src,
-                                                     UA_Variant *dst);
+                                                     UA_DataValue *dst);
 int MonitoredItem_QueueToDataChangeNotifications(UA_MonitoredItemNotification *dst,
                                                  UA_MonitoredItem *monitoredItem);
 
@@ -69,15 +66,16 @@ int MonitoredItem_QueueToDataChangeNotifications(UA_MonitoredItemNotification *d
 /****************/
 
 typedef struct UA_unpublishedNotification {
+    UA_Boolean publishedOnce;
     LIST_ENTRY(UA_unpublishedNotification) listEntry;
-    UA_NotificationMessage *notification;
+    UA_NotificationMessage notification;
 } UA_unpublishedNotification;
 
 typedef struct UA_Subscription {
     LIST_ENTRY(UA_Subscription) listEntry;
     UA_UInt32_BoundedValue lifeTime;
     UA_Int32_BoundedValue keepAliveCount;
-    UA_DateTime publishingInterval;
+    UA_DateTime publishingInterval;     // [ms] may be UA_Int32
     UA_DateTime lastPublished;
     UA_Int32 subscriptionID;
     UA_Int32 notificationsPerPublish;
@@ -88,21 +86,20 @@ typedef struct UA_Subscription {
     UA_Job *timedUpdateJob;
     UA_Boolean timedUpdateIsRegistered;
     LIST_HEAD(UA_ListOfUnpublishedNotifications, UA_unpublishedNotification) unpublishedNotifications;
+    size_t unpublishedNotificationsSize;
     LIST_HEAD(UA_ListOfUAMonitoredItems, UA_MonitoredItem) MonitoredItems;
 } UA_Subscription;
 
 UA_Subscription *UA_Subscription_new(UA_Int32 subscriptionID);
 void UA_Subscription_deleteMembers(UA_Subscription *subscription, UA_Server *server);
 void Subscription_updateNotifications(UA_Subscription *subscription);
-UA_UInt32 Subscription_queuedNotifications(UA_Subscription *subscription);
 UA_UInt32 *Subscription_getAvailableSequenceNumbers(UA_Subscription *sub);
-void Subscription_copyTopNotificationMessage(UA_NotificationMessage *dst, UA_Subscription *sub);
-UA_UInt32 Subscription_deleteUnpublishedNotification(UA_UInt32 seqNo, UA_Subscription *sub);
 void Subscription_generateKeepAlive(UA_Subscription *subscription);
+void Subscription_copyTopNotificationMessage(UA_NotificationMessage *dst, UA_Subscription *sub);
+UA_UInt32 Subscription_deleteUnpublishedNotification(UA_UInt32 seqNo, UA_Boolean bDeleteAll, UA_Subscription *sub);
+void Subscription_copyNotificationMessage(UA_NotificationMessage *dst, UA_unpublishedNotification *src);
 UA_StatusCode Subscription_createdUpdateJob(UA_Server *server, UA_Guid jobId, UA_Subscription *sub);
 UA_StatusCode Subscription_registerUpdateJob(UA_Server *server, UA_Subscription *sub);
 UA_StatusCode Subscription_unregisterUpdateJob(UA_Server *server, UA_Subscription *sub);
 
-static void Subscription_timedUpdateNotificationsJob(UA_Server *server, void *data);
-
 #endif /* UA_SUBSCRIPTION_H_ */

+ 23 - 27
src/server/ua_subscription_manager.c

@@ -21,39 +21,35 @@ void SubscriptionManager_init(UA_Session *session) {
     // Counter
     // NOTE: On a 32 bit plattform, assigning 64 bit (2 regs) is allowed by the compiler, but shifting though multiple
     //       regs is usually not. To support both 32 and 64bit, the struct splits the 64Bit timestamp into two parts.
-    union {
+    typedef union {
         struct {
             UA_UInt32 ui32h;
             UA_UInt32 ui32l;
-        };
+        } individual;
         UA_UInt64 ui64;
-    } guidInitH;
+    } guidInit;
+    guidInit guidInitH;
     guidInitH.ui64 = (UA_UInt64) UA_DateTime_now();
-    manager->lastJobGuid.data1 = guidInitH.ui32h;
-    manager->lastJobGuid.data2 = (UA_UInt16) (guidInitH.ui32l >> 16);
-    manager->lastJobGuid.data3 = (UA_UInt16) (guidInitH.ui32l);
-    union {
-        struct {
-            UA_UInt32 ui32h;
-            UA_UInt32 ui32l;
-        };
-        UA_UInt64 ui64;
-    } guidInitL;
+    manager->lastJobGuid.data1 = guidInitH.individual.ui32h;
+    manager->lastJobGuid.data2 = (UA_UInt16) (guidInitH.individual.ui32l >> 16);
+    manager->lastJobGuid.data3 = (UA_UInt16) (guidInitH.individual.ui32l);
+
+    guidInit guidInitL;
     guidInitL.ui64 = (UA_UInt64) UA_DateTime_now();
-    manager->lastJobGuid.data4[0] = (UA_Byte) guidInitL.ui32l;
-    manager->lastJobGuid.data4[1] = (UA_Byte) (guidInitL.ui32l >> 8); 
-    manager->lastJobGuid.data4[2] = (UA_Byte) (guidInitL.ui32l >> 16);
-    manager->lastJobGuid.data4[3] = (UA_Byte) (guidInitL.ui32l >> 24);
-    manager->lastJobGuid.data4[4] = (UA_Byte) (manager->lastJobGuid.data4[0]) ^ (guidInitL.ui32h);
-    manager->lastJobGuid.data4[5] = (UA_Byte) (manager->lastJobGuid.data4[0]) ^ (guidInitL.ui32h >> 8);
-    manager->lastJobGuid.data4[6] = (UA_Byte) (manager->lastJobGuid.data4[1]) ^ (guidInitL.ui32h >> 16);
-    manager->lastJobGuid.data4[7] = (UA_Byte) (manager->lastJobGuid.data4[0]) ^ (guidInitL.ui32h >> 24);
+    manager->lastJobGuid.data4[0] = (UA_Byte) guidInitL.individual.ui32l;
+    manager->lastJobGuid.data4[1] = (UA_Byte) (guidInitL.individual.ui32l >> 8);
+    manager->lastJobGuid.data4[2] = (UA_Byte) (guidInitL.individual.ui32l >> 16);
+    manager->lastJobGuid.data4[3] = (UA_Byte) (guidInitL.individual.ui32l >> 24);
+    manager->lastJobGuid.data4[4] = (UA_Byte) (manager->lastJobGuid.data4[0]) ^ (guidInitL.individual.ui32h);
+    manager->lastJobGuid.data4[5] = (UA_Byte) (manager->lastJobGuid.data4[0]) ^ (guidInitL.individual.ui32h >> 8);
+    manager->lastJobGuid.data4[6] = (UA_Byte) (manager->lastJobGuid.data4[1]) ^ (guidInitL.individual.ui32h >> 16);
+    manager->lastJobGuid.data4[7] = (UA_Byte) (manager->lastJobGuid.data4[0]) ^ (guidInitL.individual.ui32h >> 24);
 }
 
 void SubscriptionManager_deleteMembers(UA_Session *session, UA_Server *server) {
-    UA_SubscriptionManager *manager = &(session->subscriptionManager);
-    UA_Subscription *current;
-    while((current = LIST_FIRST(&manager->serverSubscriptions))) {
+    UA_SubscriptionManager *manager = &session->subscriptionManager;
+    UA_Subscription *current, *temp;
+    LIST_FOREACH_SAFE(current, &manager->serverSubscriptions, listEntry, temp) {
         LIST_REMOVE(current, listEntry);
         UA_Subscription_deleteMembers(current, server);
         UA_free(current);
@@ -80,10 +76,10 @@ UA_Int32 SubscriptionManager_deleteMonitoredItem(UA_SubscriptionManager *manager
     if(!sub)
         return UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
     
-    UA_MonitoredItem *mon;
-    LIST_FOREACH(mon, &sub->MonitoredItems, listEntry) {
+    UA_MonitoredItem *mon, *tmp_mon;
+    LIST_FOREACH_SAFE(mon, &sub->MonitoredItems, listEntry, tmp_mon) {
         if (mon->itemId == monitoredItemID) {
-            // FIXME!! don't we need to remove the list entry?
+            LIST_REMOVE(mon, listEntry);
             MonitoredItem_delete(mon);
             return UA_STATUSCODE_GOOD;
         }

+ 97 - 68
src/ua_connection.c

@@ -1,6 +1,7 @@
 #include "ua_util.h"
 #include "ua_connection.h"
 #include "ua_types_encoding_binary.h"
+#include "ua_types_generated_encoding_binary.h"
 #include "ua_securechannel.h"
 
 // max message size is 64k
@@ -12,113 +13,141 @@ void UA_Connection_init(UA_Connection *connection) {
     connection->state = UA_CONNECTION_CLOSED;
     connection->localConf = UA_ConnectionConfig_standard;
     connection->remoteConf = UA_ConnectionConfig_standard;
-    connection->channel = UA_NULL;
+    connection->channel = NULL;
     connection->sockfd = 0;
-    connection->handle = UA_NULL;
+    connection->handle = NULL;
     UA_ByteString_init(&connection->incompleteMessage);
-    connection->write = UA_NULL;
-    connection->close = UA_NULL;
-    connection->recv = UA_NULL;
-    connection->getBuffer = UA_NULL;
-    connection->releaseBuffer = UA_NULL;
+    connection->send = NULL;
+    connection->close = NULL;
+    connection->recv = NULL;
+    connection->getSendBuffer = NULL;
+    connection->releaseSendBuffer = NULL;
+    connection->releaseRecvBuffer = NULL;
 }
 
 void UA_Connection_deleteMembers(UA_Connection *connection) {
     UA_ByteString_deleteMembers(&connection->incompleteMessage);
 }
 
-UA_ByteString UA_Connection_completeMessages(UA_Connection *connection, UA_ByteString received)
-{
-    if(received.length == -1)
-        return received;
-
-    /* concat the existing incomplete message with the new message */
-    UA_ByteString current;
-    if(connection->incompleteMessage.length < 0)
-        current = received;
-    else {
-        current.data = UA_realloc(connection->incompleteMessage.data,
-                                  connection->incompleteMessage.length + received.length);
-        if(!current.data) {
+UA_StatusCode
+UA_Connection_completeMessages(UA_Connection *connection, UA_ByteString * UA_RESTRICT message,
+                              UA_Boolean * UA_RESTRICT realloced) {
+    UA_ByteString *current = message;
+    *realloced = UA_FALSE;
+    if(connection->incompleteMessage.length > 0) {
+        /* concat the existing incomplete message with the new message */
+        UA_Byte *data = UA_realloc(connection->incompleteMessage.data,
+                                   connection->incompleteMessage.length + message->length);
+        if(!data) {
             /* not enough memory */
             UA_ByteString_deleteMembers(&connection->incompleteMessage);
-            connection->incompleteMessage.length = -1;
-            UA_ByteString_deleteMembers(&received);
-            received.length = -1;
-            return received;
+            connection->releaseRecvBuffer(connection, message);
+            return UA_STATUSCODE_BADOUTOFMEMORY;
         }
-        UA_memcpy(current.data + connection->incompleteMessage.length, received.data, received.length);
-        current.length = connection->incompleteMessage.length + received.length;
-        UA_ByteString_deleteMembers(&received);
-        UA_ByteString_init(&connection->incompleteMessage);
+        memcpy(&data[connection->incompleteMessage.length], message->data, message->length);
+        connection->incompleteMessage.data = data;
+        connection->incompleteMessage.length += message->length;
+        connection->releaseRecvBuffer(connection, message);
+        current = &connection->incompleteMessage;
+        *realloced = UA_TRUE;
     }
 
-    /* find the first non-complete message */
-    size_t end_pos = 0; // the end of the last complete message
-    while(current.length - end_pos >= 16) {
-        if(!(current.data[0] == 'M' && current.data[1] == 'S' && current.data[2] == 'G') &&
-           !(current.data[0] == 'O' && current.data[1] == 'P' && current.data[2] == 'N') &&
-           !(current.data[0] == 'H' && current.data[1] == 'E' && current.data[2] == 'L') &&
-           !(current.data[0] == 'A' && current.data[1] == 'C' && current.data[2] == 'K') &&
-           !(current.data[0] == 'C' && current.data[1] == 'L' && current.data[2] == 'O')) {
-            current.length = end_pos; // throw the remaining bytestring away
+    /* the while loop sets pos to the first element after the last complete message. if a message
+       contains garbage, the buffer length is set to contain only the "good" messages before. */
+    size_t pos = 0;
+    size_t delete_at = current->length-1; // garbled message after this point
+    while(current->length - pos >= 16) {
+        UA_UInt32 msgtype = current->data[pos] + (current->data[pos+1] << 8) + (current->data[pos+2] << 16);
+        if(msgtype != ('M' + ('S' << 8) + ('G' << 16)) &&
+           msgtype != ('O' + ('P' << 8) + ('N' << 16)) &&
+           msgtype != ('H' + ('E' << 8) + ('L' << 16)) &&
+           msgtype != ('A' + ('C' << 8) + ('K' << 16)) &&
+           msgtype != ('C' + ('L' << 8) + ('O' << 16))) {
+            /* the message type is not recognized */
+            delete_at = pos; // throw the remaining message away
             break;
         }
         UA_Int32 length = 0;
-        size_t pos = end_pos + 4;
-        UA_StatusCode retval = UA_Int32_decodeBinary(&current, &pos, &length);
-        if(retval != UA_STATUSCODE_GOOD || length < 16 ||
-           length > (UA_Int32)connection->localConf.maxMessageSize) {
-            current.length = end_pos; // throw the remaining bytestring away
+        size_t length_pos = pos + 4;
+        UA_StatusCode retval = UA_Int32_decodeBinary(current, &length_pos, &length);
+        if(retval != UA_STATUSCODE_GOOD || length < 16 || length > (UA_Int32)connection->localConf.maxMessageSize) {
+            /* the message size is not allowed. throw the remaining bytestring away */
+            delete_at = pos;
             break;
         }
-        if(length + (UA_Int32)end_pos > current.length)
-            break; // the message is incomplete
-        end_pos += length;
+        if(length + pos > current->length)
+            break; /* the message is incomplete. keep the beginning */
+        pos += length;
+    }
+
+    /* throw the message away */
+    if(delete_at == 0) {
+        if(!*realloced) {
+            connection->releaseRecvBuffer(connection, message);
+            *realloced = UA_TRUE;
+        } else
+            UA_ByteString_deleteMembers(current);
+        return UA_STATUSCODE_GOOD;
     }
 
-    if(current.length == 0) {
-        /* throw everything away */
-        UA_ByteString_deleteMembers(&current);
-        current.length = -1;
-        return current;
+    /* no complete message at all */
+    if(pos == 0) {
+        if(!*realloced) {
+            /* store the buffer in the connection */
+            UA_ByteString_copy(current, &connection->incompleteMessage);
+            connection->releaseRecvBuffer(connection, message);
+            *realloced = UA_TRUE;
+        } 
+        return UA_STATUSCODE_GOOD;
     }
 
-    if(end_pos == 0) {
-        /* no complete message in current */
-        connection->incompleteMessage = current;
-        UA_ByteString_init(&current);
-    } else if(current.length != (UA_Int32)end_pos) {
-        /* there is an incomplete message at the end of current */
-        connection->incompleteMessage.data = UA_malloc(current.length - end_pos);
-        if(connection->incompleteMessage.data) {
-            UA_memcpy(connection->incompleteMessage.data, &current.data[end_pos], current.length - end_pos);
-            connection->incompleteMessage.length = current.length - end_pos;
+    /* there remains an incomplete message at the end */
+    if(current->length != pos) {
+        UA_Byte *data = UA_malloc(current->length - pos);
+        if(!data) {
+            UA_ByteString_deleteMembers(&connection->incompleteMessage);
+            if(!*realloced) {
+                connection->releaseRecvBuffer(connection, message);
+                *realloced = UA_TRUE;
+            }
+            return UA_STATUSCODE_BADOUTOFMEMORY;
         }
-        current.length = end_pos;
+        size_t newlength = current->length - pos;
+        memcpy(data, &current->data[pos], newlength);
+        current->length = pos;
+        if(*realloced)
+            *message = *current;
+        connection->incompleteMessage.data = data;
+        connection->incompleteMessage.length = newlength;
+        return UA_STATUSCODE_GOOD;
+    }
+
+    if(current == &connection->incompleteMessage) {
+        *message = *current;
+        connection->incompleteMessage = UA_BYTESTRING_NULL;
     }
-    return current;
+    return UA_STATUSCODE_GOOD;
 }
 
 void UA_Connection_detachSecureChannel(UA_Connection *connection) {
 #ifdef UA_MULTITHREADING
     UA_SecureChannel *channel = connection->channel;
     if(channel)
-        uatomic_cmpxchg(&channel->connection, connection, UA_NULL);
-    uatomic_set(&connection->channel, UA_NULL);
+        uatomic_cmpxchg(&channel->connection, connection, NULL);
+    uatomic_set(&connection->channel, NULL);
 #else
     if(connection->channel)
-        connection->channel->connection = UA_NULL;
-    connection->channel = UA_NULL;
+        connection->channel->connection = NULL;
+    connection->channel = NULL;
 #endif
 }
 
 void UA_Connection_attachSecureChannel(UA_Connection *connection, UA_SecureChannel *channel) {
 #ifdef UA_MULTITHREADING
-    if(uatomic_cmpxchg(&channel->connection, UA_NULL, connection) == UA_NULL)
+    if(uatomic_cmpxchg(&channel->connection, NULL, connection) == NULL)
         uatomic_set(&connection->channel, channel);
 #else
-    if(channel->connection != UA_NULL)
+    if(channel->connection != NULL)
         return;
     channel->connection = connection;
     connection->channel = channel;

+ 21 - 17
src/ua_securechannel.c

@@ -1,8 +1,9 @@
 #include "ua_util.h"
 #include "ua_securechannel.h"
 #include "ua_session.h"
-#include "ua_statuscodes.h"
 #include "ua_types_encoding_binary.h"
+#include "ua_types_generated_encoding_binary.h"
+#include "ua_transport_generated_encoding_binary.h"
 
 void UA_SecureChannel_init(UA_SecureChannel *channel) {
     UA_MessageSecurityMode_init(&channel->securityMode);
@@ -13,7 +14,7 @@ void UA_SecureChannel_init(UA_SecureChannel *channel) {
     UA_ByteString_init(&channel->clientNonce);
     UA_ByteString_init(&channel->serverNonce);
     channel->sequenceNumber = 0;
-    channel->connection = UA_NULL;
+    channel->connection = NULL;
     LIST_INIT(&channel->sessions);
 }
 
@@ -31,9 +32,12 @@ void UA_SecureChannel_deleteMembersCleanup(UA_SecureChannel *channel) {
             c->close(c);
     }
     /* just remove the pointers and free the linked list (not the sessions) */
-    struct SessionEntry *se;
-    while((se = LIST_FIRST(&channel->sessions))) {
-        UA_SecureChannel_detachSession(channel, se->session); /* the se is deleted inside */
+    struct SessionEntry *se, *temp;
+    LIST_FOREACH_SAFE(se, &channel->sessions, pointers, temp) {
+        if(se->session)
+            se->session->channel = NULL;
+        LIST_REMOVE(se, pointers);
+        UA_free(se);
     }
 }
 
@@ -52,12 +56,12 @@ void UA_SecureChannel_attachSession(UA_SecureChannel *channel, UA_Session *sessi
         return;
     se->session = session;
 #ifdef UA_MULTITHREADING
-    if(uatomic_cmpxchg(&session->channel, UA_NULL, channel) != UA_NULL) {
+    if(uatomic_cmpxchg(&session->channel, NULL, channel) != NULL) {
         UA_free(se);
         return;
     }
 #else
-    if(session->channel != UA_NULL) {
+    if(session->channel != NULL) {
         UA_free(se);
         return;
     }
@@ -68,9 +72,9 @@ void UA_SecureChannel_attachSession(UA_SecureChannel *channel, UA_Session *sessi
 
 void UA_SecureChannel_detachSession(UA_SecureChannel *channel, UA_Session *session) {
     if(session)
-        session->channel = UA_NULL;
-    struct SessionEntry *se;
-    LIST_FOREACH(se, &channel->sessions, pointers) {
+        session->channel = NULL;
+    struct SessionEntry *se, *temp;
+    LIST_FOREACH_SAFE(se, &channel->sessions, pointers, temp) {
         if(se->session != session)
             continue;
         LIST_REMOVE(se, pointers);
@@ -86,7 +90,7 @@ UA_Session * UA_SecureChannel_getSession(UA_SecureChannel *channel, UA_NodeId *t
             break;
     }
     if(!se)
-        return UA_NULL;
+        return NULL;
     return se->session;
 }
 
@@ -124,7 +128,8 @@ UA_StatusCode UA_SecureChannel_sendBinaryMessage(UA_SecureChannel *channel, UA_U
     seqHeader.requestId = requestId;
 
     UA_ByteString message;
-    UA_StatusCode retval = connection->getBuffer(connection, &message);
+    UA_StatusCode retval = connection->getSendBuffer(connection, connection->remoteConf.recvBufferSize,
+                                                     &message);
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
 
@@ -133,7 +138,7 @@ UA_StatusCode UA_SecureChannel_sendBinaryMessage(UA_SecureChannel *channel, UA_U
     retval |= UA_encodeBinary(content, contentType, &message, &messagePos);
 
     if(retval != UA_STATUSCODE_GOOD) {
-        connection->releaseBuffer(connection, &message);
+        connection->releaseSendBuffer(connection, &message);
         return retval;
     }
 
@@ -149,9 +154,8 @@ UA_StatusCode UA_SecureChannel_sendBinaryMessage(UA_SecureChannel *channel, UA_U
     UA_SecureConversationMessageHeader_encodeBinary(&respHeader, &message, &messagePos);
     UA_SymmetricAlgorithmSecurityHeader_encodeBinary(&symSecHeader, &message, &messagePos);
     UA_SequenceHeader_encodeBinary(&seqHeader, &message, &messagePos);
-    
-    retval = connection->write(connection, &message, respHeader.messageHeader.messageSize);
-    if(retval != UA_STATUSCODE_GOOD)
-        connection->releaseBuffer(connection, &message);
+    message.length = respHeader.messageHeader.messageSize;
+
+    retval = connection->send(connection, &message);
     return retval;
 }

+ 1 - 1
src/ua_securechannel.h

@@ -2,7 +2,7 @@
 #define UA_SECURECHANNEL_H_
 
 #include "queue.h"
-#include "ua_types_generated.h"
+#include "ua_types.h"
 #include "ua_transport_generated.h"
 #include "ua_connection.h"
 

+ 15 - 15
src/ua_session.c

@@ -3,32 +3,32 @@
 #include "ua_statuscodes.h"
 
 UA_Session anonymousSession = {
-    .clientDescription =  {.applicationUri = {-1, UA_NULL}, .productUri = {-1, UA_NULL},
-                           .applicationName = {.locale = {-1, UA_NULL}, .text = {-1, UA_NULL}},
+    .clientDescription =  {.applicationUri = {0, NULL}, .productUri = {0, NULL},
+                           .applicationName = {.locale = {0, NULL}, .text = {0, NULL}},
                            .applicationType = UA_APPLICATIONTYPE_CLIENT,
-                           .gatewayServerUri = {-1, UA_NULL}, .discoveryProfileUri = {-1, UA_NULL},
-                           .discoveryUrlsSize = -1, .discoveryUrls = UA_NULL},
+                           .gatewayServerUri = {0, NULL}, .discoveryProfileUri = {0, NULL},
+                           .discoveryUrlsSize = 0, .discoveryUrls = NULL},
     .sessionName = {sizeof("Anonymous Session")-1, (UA_Byte*)"Anonymous Session"},
     .authenticationToken = {.namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC,
                             .identifier.numeric = 0}, 
     .sessionId = {.namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC, .identifier.numeric = 0},
     .maxRequestMessageSize = UA_UINT32_MAX, .maxResponseMessageSize = UA_UINT32_MAX,
-    .timeout = UA_INT64_MAX, .validTill = UA_INT64_MAX, .channel = UA_NULL,
-    .continuationPoints = {UA_NULL}};
+    .timeout = UA_INT64_MAX, .validTill = UA_INT64_MAX, .channel = NULL,
+    .continuationPoints = {NULL}};
 
 UA_Session adminSession = {
-    .clientDescription =  {.applicationUri = {-1, UA_NULL}, .productUri = {-1, UA_NULL},
-                           .applicationName = {.locale = {-1, UA_NULL}, .text = {-1, UA_NULL}},
+    .clientDescription =  {.applicationUri = {0, NULL}, .productUri = {0, NULL},
+                           .applicationName = {.locale = {0, NULL}, .text = {0, NULL}},
                            .applicationType = UA_APPLICATIONTYPE_CLIENT,
-                           .gatewayServerUri = {-1, UA_NULL}, .discoveryProfileUri = {-1, UA_NULL},
-                           .discoveryUrlsSize = -1, .discoveryUrls = UA_NULL},
+                           .gatewayServerUri = {0, NULL}, .discoveryProfileUri = {0, NULL},
+                           .discoveryUrlsSize = 0, .discoveryUrls = NULL},
     .sessionName = {sizeof("Administrator Session")-1, (UA_Byte*)"Administrator Session"},
     .authenticationToken = {.namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC,
                             .identifier.numeric = 1},
     .sessionId = {.namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC, .identifier.numeric = 1},
     .maxRequestMessageSize = UA_UINT32_MAX, .maxResponseMessageSize = UA_UINT32_MAX,
-    .timeout = UA_INT64_MAX, .validTill = UA_INT64_MAX, .channel = UA_NULL,
-    .continuationPoints = {UA_NULL}};
+    .timeout = UA_INT64_MAX, .validTill = UA_INT64_MAX, .channel = NULL,
+    .continuationPoints = {NULL}};
 
 void UA_Session_init(UA_Session *session) {
     UA_ApplicationDescription_init(&session->clientDescription);
@@ -40,7 +40,7 @@ void UA_Session_init(UA_Session *session) {
     session->maxResponseMessageSize = 0;
     session->timeout = 0;
     UA_DateTime_init(&session->validTill);
-    session->channel = UA_NULL;
+    session->channel = NULL;
 #ifdef ENABLE_SUBSCRIPTIONS
     SubscriptionManager_init(session);
 #endif
@@ -53,8 +53,8 @@ void UA_Session_deleteMembersCleanup(UA_Session *session, UA_Server* server) {
     UA_NodeId_deleteMembers(&session->authenticationToken);
     UA_NodeId_deleteMembers(&session->sessionId);
     UA_String_deleteMembers(&session->sessionName);
-    struct ContinuationPointEntry *cp;
-    while((cp = LIST_FIRST(&session->continuationPoints))) {
+    struct ContinuationPointEntry *cp, *temp;
+    LIST_FOREACH_SAFE(cp, &session->continuationPoints, pointers, temp) {
         LIST_REMOVE(cp, pointers);
         UA_ByteString_deleteMembers(&cp->identifier);
         UA_BrowseDescription_deleteMembers(&cp->browseDescription);

+ 1 - 1
src/ua_session.h

@@ -34,7 +34,7 @@ struct UA_Session {
     UA_NodeId         sessionId;
     UA_UInt32         maxRequestMessageSize;
     UA_UInt32         maxResponseMessageSize;
-    UA_Int64          timeout;
+    UA_Int64          timeout; // [ms]
     UA_DateTime       validTill;
     #ifdef ENABLE_SUBSCRIPTIONS
         UA_SubscriptionManager subscriptionManager;

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 447 - 919
src/ua_types.c


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 740 - 911
src/ua_types_encoding_binary.c


+ 3 - 75
src/ua_types_encoding_binary.h

@@ -2,83 +2,11 @@
 #define UA_TYPES_ENCODING_BINARY_H_
 
 #include "ua_types.h"
-#include "ua_util.h"
 
-/**
- * @ingroup types
- *
- * @defgroup encoding_binary Binary Encoding
- *
- * @brief Functions for binary en- and decoding of built-in datatypes as defined
- * in the standard. The encoding of the remaining datatypes is autogenerated
- * from XML descriptions.
- *
- * All datatypes have similar functions with a common postfix. DO NOT CALL THESE
- * FUNCTIONS WITH NULL-POINTERS IF IT IS NOT EXPLICITLY PERMITTED.
- *
- * - _encode: Encodes a variable into a bytestring. If an error occurs
- *   (indicated by the return value), the bytestring may be left in an
- *   inconsistent state.
- *
- * - _decode: Decodes a variable stored in a bytestring. The destination is
- *   cleaned up (init) before decoding into it. If an error occurs (indicated by
- *   the return value), the destination is cleaned up (deleteMembers, but no
- *   init) before returning.
- *
- * @{
- */
+UA_StatusCode UA_encodeBinary(const void *src, const UA_DataType *type, UA_ByteString *dst,
+                              size_t *UA_RESTRICT offset) UA_FUNC_ATTR_WARN_UNUSED_RESULT;
 
-#define UA_TYPE_BINARY_ENCODING(TYPE)                                   \
-    UA_StatusCode TYPE##_encodeBinary(TYPE const *src, UA_ByteString *dst, size_t *UA_RESTRICT offset); \
-    UA_StatusCode TYPE##_decodeBinary(UA_ByteString const *src, size_t *UA_RESTRICT offset, TYPE *dst);
-
-UA_TYPE_BINARY_ENCODING(UA_Boolean)
-UA_TYPE_BINARY_ENCODING(UA_Byte)
-#define UA_SByte_encodeBinary(src, dst, offset) UA_Byte_encodeBinary((const UA_Byte *)src, dst, offset)
-#define UA_SByte_decodeBinary(src, offset, dst) UA_Byte_decodeBinary(src, offset, (UA_Byte *)dst)
-UA_TYPE_BINARY_ENCODING(UA_UInt16)
-#define UA_Int16_encodeBinary(src, dst, offset) UA_UInt16_encodeBinary((const UA_UInt16 *)src, dst, offset)
-#define UA_Int16_decodeBinary(src, offset, dst) UA_UInt16_decodeBinary(src, offset, (UA_UInt16 *)dst)
-UA_TYPE_BINARY_ENCODING(UA_UInt32)
-#define UA_Int32_encodeBinary(src, dst, offset) UA_UInt32_encodeBinary((const UA_UInt32 *)src, dst, offset)
-#define UA_Int32_decodeBinary(src, offset, dst) UA_UInt32_decodeBinary(src, offset, (UA_UInt32 *)dst)
-UA_TYPE_BINARY_ENCODING(UA_UInt64)
-#define UA_Int64_encodeBinary(src, dst, offset) UA_UInt64_encodeBinary((const UA_UInt64 *)src, dst, offset)
-#define UA_Int64_decodeBinary(src, offset, dst) UA_UInt64_decodeBinary(src, offset, (UA_UInt64 *)dst)
-#ifdef UA_MIXED_ENDIAN
- UA_TYPE_BINARY_ENCODING(UA_Float)
- UA_TYPE_BINARY_ENCODING(UA_Double)
-#else
- #define UA_Float_encodeBinary(src, dst, offset) UA_UInt32_encodeBinary((const UA_UInt32 *)src, dst, offset)
- #define UA_Float_decodeBinary(src, offset, dst) UA_UInt32_decodeBinary(src, offset, (UA_UInt32 *)dst)
- #define UA_Double_encodeBinary(src, dst, offset) UA_UInt64_encodeBinary((const UA_UInt64 *)src, dst, offset)
- #define UA_Double_decodeBinary(src, offset, dst) UA_UInt64_decodeBinary(src, offset, (UA_UInt64 *)dst)
-#endif
-UA_TYPE_BINARY_ENCODING(UA_String)
-#define UA_DateTime_encodeBinary(src, dst, offset) UA_UInt64_encodeBinary((const UA_UInt64 *)src, dst, offset)
-#define UA_DateTime_decodeBinary(src, offset, dst) UA_UInt64_decodeBinary(src, offset, (UA_UInt64 *)dst)
-UA_TYPE_BINARY_ENCODING(UA_Guid)
-#define UA_ByteString_encodeBinary(src, dst, offset) UA_String_encodeBinary((const UA_String *)src, dst, offset)
-#define UA_ByteString_decodeBinary(src, offset, dst) UA_String_decodeBinary(src, offset, (UA_String *)dst)
-#define UA_XmlElement_encodeBinary(src, dst, offset) UA_String_encodeBinary((const UA_String *)src, dst, offset)
-#define UA_XmlElement_decodeBinary(src, offset, dst) UA_String_decodeBinary(src, offset, (UA_String *)dst)
-UA_TYPE_BINARY_ENCODING(UA_NodeId)
-UA_TYPE_BINARY_ENCODING(UA_ExpandedNodeId)
-#define UA_StatusCode_encodeBinary(src, dst, offset) UA_UInt32_encodeBinary((const UA_UInt32 *)src, dst, offset)
-#define UA_StatusCode_decodeBinary(src, offset, dst) UA_UInt32_decodeBinary(src, offset, (UA_UInt32 *)dst)
-UA_TYPE_BINARY_ENCODING(UA_QualifiedName)
-UA_TYPE_BINARY_ENCODING(UA_LocalizedText)
-UA_TYPE_BINARY_ENCODING(UA_ExtensionObject)
-UA_TYPE_BINARY_ENCODING(UA_DataValue)
-UA_TYPE_BINARY_ENCODING(UA_Variant)
-UA_TYPE_BINARY_ENCODING(UA_DiagnosticInfo)
-
-UA_StatusCode UA_encodeBinary(const void *src, const UA_DataType *dataType, UA_ByteString *dst,
-                              size_t *UA_RESTRICT offset);
 UA_StatusCode UA_decodeBinary(const UA_ByteString *src, size_t * UA_RESTRICT offset, void *dst,
-                              const UA_DataType *dataType);
-size_t UA_calcSizeBinary(const void *p, const UA_DataType *dataType);
-
-/// @}
+                              const UA_DataType *type) UA_FUNC_ATTR_WARN_UNUSED_RESULT;
 
 #endif /* UA_TYPES_ENCODING_BINARY_H_ */

+ 43 - 49
src/ua_util.h

@@ -3,50 +3,23 @@
 
 #include "ua_config.h"
 
+/* Subtract from nodeids to get from the encoding to the content */
+#define UA_ENCODINGOFFSET_XML 1
+#define UA_ENCODINGOFFSET_BINARY 2
+
+#include <assert.h> // assert
+#define UA_assert(ignore) assert(ignore)
+
 /*********************/
 /* Memory Management */
 /*********************/
 
+/* Replace the macros with functions for custom allocators if necessary */
 #include <stdlib.h> // malloc, free
-#include <string.h> // memcpy
-#include <assert.h> // assert
-
 #ifdef _WIN32
 # include <malloc.h>
 #endif
 
-/* Visual Studio needs __restrict */
-#ifdef _MSC_VER
-# define UA_RESTRICT __restrict
-#else
-# define UA_RESTRICT restrict
-#endif
-
-/* Visual Studio does not know fnct/unistd file access results */
-#ifdef _MSC_VER
-    #ifndef R_OK
-        #define R_OK    4               /* Test for read permission.  */
-    #endif
-    #ifndef R_OK
-        #define W_OK    2               /* Test for write permission.  */
-    #endif
-    #ifndef X_OK
-        #define X_OK    1               /* Test for execute permission.  */
-    #endif
-    #ifndef F_OK
-        #define F_OK    0               /* Test for existence.  */
-    #endif
-#endif
-
-#define UA_NULL ((void *)0)
-
-// subtract from nodeids to get from the encoding to the content
-#define UA_ENCODINGOFFSET_XML 1
-#define UA_ENCODINGOFFSET_BINARY 2
-
-#define UA_assert(ignore) assert(ignore)
-
-/* Replace the macros with functions for custom allocators if necessary */
 #ifndef UA_free
 # define UA_free(ptr) free(ptr)
 #endif
@@ -60,9 +33,6 @@
 # define UA_realloc(ptr, size) realloc(ptr, size)
 #endif
 
-#define UA_memcpy(dst, src, size) memcpy(dst, src, size)
-#define UA_memset(ptr, value, size) memset(ptr, value, size)
-
 #ifndef NO_ALLOCA
 # ifdef __GNUC__
 #  define UA_alloca(size) __builtin_alloca (size)
@@ -74,26 +44,33 @@
 # endif
 #endif
 
+/************************/
+/* Thread Local Storage */
+/************************/
+
+#ifdef UA_MULTITHREADING
+# ifdef __GNUC__
+#  define UA_THREAD_LOCAL __thread
+# elif defined(_MSC_VER)
+#  define UA_THREAD_LOCAL __declspec(thread)
+# else
+#  error No thread local storage keyword defined for this compiler
+# endif
+#else
+# define UA_THREAD_LOCAL
+#endif
+
 /********************/
 /* System Libraries */
 /********************/
 
-#include <stdarg.h> // va_start, va_end
 #include <time.h>
-#include <stdio.h> // printf
-#include <inttypes.h>
-
 #ifdef _WIN32
 # include <winsock2.h> //needed for amalgation
 # include <windows.h>
 # undef SLIST_ENTRY
-# define RAND(SEED) (UA_UInt32)rand()
 #else
-  #ifdef __CYGWIN__
-  extern int rand_r (unsigned int *__seed);
-  #endif
-  # include <sys/time.h>
-  # define RAND(SEED) (UA_UInt32)rand_r(SEED)
+# include <sys/time.h>
 #endif
 
 /*************************/
@@ -108,7 +85,24 @@
 # include <urcu/wfcqueue.h>
 # include <urcu/uatomic.h>
 # include <urcu/rculfhash.h>
-#include <urcu/lfstack.h>
+# include <urcu/lfstack.h>
+# ifdef NDEBUG
+# define UA_RCU_LOCK() rcu_read_lock()
+# define UA_RCU_UNLOCK() rcu_read_unlock()
+# else
+extern UA_THREAD_LOCAL bool rcu_locked;
+# define UA_RCU_LOCK() do {     \
+        assert(!rcu_locked);    \
+        rcu_locked = UA_TRUE;   \
+        rcu_read_lock(); } while(0)
+# define UA_RCU_UNLOCK() do { \
+        assert(rcu_locked);   \
+        rcu_locked = UA_FALSE;    \
+        rcu_read_lock(); } while(0)
+# endif
+#else
+# define UA_RCU_LOCK()
+# define UA_RCU_UNLOCK()
 #endif
 
 #endif /* UA_UTIL_H_ */

+ 6 - 8
tests/CMakeLists.txt

@@ -8,9 +8,9 @@ find_package(Threads REQUIRED)
 
 set(LIBS ${CHECK_LIBRARIES})
 if(NOT WIN32)
-  list(APPEND LIBS pthread)
+  list(APPEND LIBS pthread m)
   if (NOT APPLE)
-    list(APPEND LIBS rt)
+    list(APPEND LIBS rt subunit)
   endif()
 else()
     list(APPEND LIBS ws2_32)
@@ -34,10 +34,6 @@ add_test(memory ${CMAKE_CURRENT_BINARY_DIR}/check_memory)
 # target_link_libraries(check_stack ${LIBS})
 # add_test(stack ${CMAKE_CURRENT_BINARY_DIR}/check_stack)
 
-# add_executable(check_base64 check_base64.c)
-# target_link_libraries(check_base64 ${LIBS})
-# add_test(base64 ${CMAKE_CURRENT_BINARY_DIR}/check_base64)
-
 add_executable(check_services_view check_services_view.c $<TARGET_OBJECTS:open62541-object>)
 target_link_libraries(check_services_view ${LIBS})
 add_test(services_view ${CMAKE_CURRENT_BINARY_DIR}/check_services_view)
@@ -46,12 +42,14 @@ add_executable(check_services_attributes check_services_attributes.c $<TARGET_OB
 target_link_libraries(check_services_attributes ${LIBS})
 add_test(services_attributes ${CMAKE_CURRENT_BINARY_DIR}/check_services_attributes)
 
+add_executable(check_services_nodemanagement check_services_nodemanagement.c $<TARGET_OBJECTS:open62541-object>)
+target_link_libraries(check_services_nodemanagement ${LIBS})
+add_test(services_nodemanagement ${CMAKE_CURRENT_BINARY_DIR}/check_services_nodemanagement)
+
 add_executable(check_nodestore check_nodestore.c $<TARGET_OBJECTS:open62541-object>)
 target_link_libraries(check_nodestore ${LIBS})
 add_test(nodestore ${CMAKE_CURRENT_BINARY_DIR}/check_nodestore)
 
-
-
 add_executable(check_session check_session.c $<TARGET_OBJECTS:open62541-object>)
 target_link_libraries(check_session ${LIBS})
 add_test(session ${CMAKE_CURRENT_BINARY_DIR}/check_session)

+ 0 - 115
tests/check_base64.c

@@ -1,115 +0,0 @@
-#include <stdlib.h> // EXIT_SUCCESS
-#include "util/ua_base64.h"
-#include "check.h"
-
-START_TEST(base64_test_2padding)
-{
-	//this is base64'd ASCII string "open62541!"
-	UA_String encodedString; UA_STRING_STATIC(encodedString, "b3BlbjYyNTQxIQ==");
-
-	//assure that we allocate exactly 10 bytes
-	ck_assert_int_eq(UA_base64_getDecodedSize(&encodedString), 10);
-
-	UA_Byte* decodedData = (UA_Byte*)malloc(UA_base64_getDecodedSize(&encodedString));
-
-	UA_base64_decode(&encodedString, decodedData);
-
-	//check the string
-	ck_assert_int_eq(decodedData[0], 'o');
-	ck_assert_int_eq(decodedData[1], 'p');
-	ck_assert_int_eq(decodedData[2], 'e');
-	ck_assert_int_eq(decodedData[3], 'n');
-	ck_assert_int_eq(decodedData[4], '6');
-	ck_assert_int_eq(decodedData[5], '2');
-	ck_assert_int_eq(decodedData[6], '5');
-	ck_assert_int_eq(decodedData[7], '4');
-	ck_assert_int_eq(decodedData[8], '1');
-	ck_assert_int_eq(decodedData[9], '!');
-
-	free(decodedData);
-}
-END_TEST
-
-START_TEST(base64_test_1padding)
-{
-
-	//this is base64'd ASCII string "open62541!!"
-	UA_String encodedString; UA_STRING_STATIC(encodedString, "b3BlbjYyNTQxISE=");
-
-	//assure that we allocate exactly 11 bytes
-	ck_assert_int_eq(UA_base64_getDecodedSize(&encodedString), 11);
-
-	UA_Byte* decodedData = (UA_Byte*)malloc(UA_base64_getDecodedSize(&encodedString));
-
-	UA_base64_decode(&encodedString, decodedData);
-
-	//check the string
-	ck_assert_int_eq(decodedData[0], 'o');
-	ck_assert_int_eq(decodedData[1], 'p');
-	ck_assert_int_eq(decodedData[2], 'e');
-	ck_assert_int_eq(decodedData[3], 'n');
-	ck_assert_int_eq(decodedData[4], '6');
-	ck_assert_int_eq(decodedData[5], '2');
-	ck_assert_int_eq(decodedData[6], '5');
-	ck_assert_int_eq(decodedData[7], '4');
-	ck_assert_int_eq(decodedData[8], '1');
-	ck_assert_int_eq(decodedData[9], '!');
-	ck_assert_int_eq(decodedData[10], '!');
-
-	free(decodedData);
-}
-END_TEST
-
-START_TEST(base64_test_0padding)
-{
-
-	//this is base64'd ASCII string "open62541"
-	UA_String encodedString; UA_STRING_STATIC(encodedString, "b3BlbjYyNTQx");
-
-	//assure that we allocate exactly 9 bytes
-	ck_assert_int_eq(UA_base64_getDecodedSize(&encodedString), 9);
-
-	UA_Byte* decodedData = (UA_Byte*)malloc(UA_base64_getDecodedSize(&encodedString));
-
-	UA_base64_decode(&encodedString, decodedData);
-
-	//check the string
-	ck_assert_int_eq(decodedData[0], 'o');
-	ck_assert_int_eq(decodedData[1], 'p');
-	ck_assert_int_eq(decodedData[2], 'e');
-	ck_assert_int_eq(decodedData[3], 'n');
-	ck_assert_int_eq(decodedData[4], '6');
-	ck_assert_int_eq(decodedData[5], '2');
-	ck_assert_int_eq(decodedData[6], '5');
-	ck_assert_int_eq(decodedData[7], '4');
-	ck_assert_int_eq(decodedData[8], '1');
-
-	free(decodedData);
-}
-END_TEST
-
-Suite*base64_testSuite(void)
-{
-	Suite *s = suite_create("base64_test");
-	TCase *tc_core = tcase_create("Core");
-	tcase_add_test(tc_core, base64_test_2padding);
-	tcase_add_test(tc_core, base64_test_1padding);
-	tcase_add_test(tc_core, base64_test_0padding);
-	suite_add_tcase(s,tc_core);
-	return s;
-}
-
-int main (void)
-{
-	int number_failed = 0;
-
-	Suite* s = base64_testSuite();
-	SRunner* sr = srunner_create(s);
-	srunner_run_all(sr,CK_NORMAL);
-	number_failed += srunner_ntests_failed(sr);
-	srunner_free(sr);
-
-	return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
-
-}
-

+ 153 - 468
tests/check_builtin.c

@@ -3,6 +3,7 @@
 #include "ua_types.h"
 #include "ua_types_encoding_binary.h"
 #include "ua_types_generated.h"
+#include "ua_types_generated_encoding_binary.h"
 //#include "ua_transport.h"
 #include "ua_util.h"
 #include "check.h"
@@ -14,303 +15,6 @@ enum UA_VARIANT_ENCODINGMASKTYPE_enum {
     UA_VARIANT_ENCODINGMASKTYPE_ARRAY       = (0x01 << 7)      // bit 7
 };
 
-START_TEST(UA_ExtensionObject_calcSizeShallWorkOnExample) {
-    // given
-    UA_Byte data[3] = { 1, 2, 3 };
-    UA_ExtensionObject extensionObject;
-
-    // empty ExtensionObject, handcoded
-    // when
-    UA_ExtensionObject_init(&extensionObject);
-    extensionObject.typeId.identifierType = UA_NODEIDTYPE_NUMERIC;
-    extensionObject.typeId.identifier.numeric = 0;
-    extensionObject.encoding = UA_EXTENSIONOBJECT_ENCODINGMASK_NOBODYISENCODED;
-    // then
-    ck_assert_int_eq(UA_calcSizeBinary(&extensionObject, &UA_TYPES[UA_TYPES_EXTENSIONOBJECT]), 1 + 1 + 1);
-
-    // ExtensionObject with ByteString-Body
-    // when
-    extensionObject.encoding    = UA_EXTENSIONOBJECT_ENCODINGMASK_BODYISBYTESTRING;
-    extensionObject.body.data   = data;
-    extensionObject.body.length = 3;
-    // then
-    ck_assert_int_eq(UA_calcSizeBinary(&extensionObject, &UA_TYPES[UA_TYPES_EXTENSIONOBJECT]), 3 + 4 + 3);
-}
-END_TEST
-
-START_TEST(UA_DataValue_calcSizeShallWorkOnExample) {
-    // given
-    UA_DataValue dataValue;
-    UA_DataValue_init(&dataValue);
-    dataValue.status       = 12;
-    dataValue.hasStatus = UA_TRUE;
-    dataValue.sourceTimestamp = 80;
-    dataValue.hasSourceTimestamp = UA_TRUE;
-    dataValue.sourcePicoseconds = 214;
-    dataValue.hasSourcePicoseconds = UA_TRUE;
-    int size = 0;
-    // when
-    size = UA_calcSizeBinary(&dataValue, &UA_TYPES[UA_TYPES_DATAVALUE]);
-    // then
-    // 1 (bitfield) + 4 (status) + 8 (timestamp) + 2 (picoseconds)
-    ck_assert_int_eq(size, 15);
-}
-END_TEST
-
-START_TEST(UA_DiagnosticInfo_calcSizeShallWorkOnExample) {
-    // given
-    UA_DiagnosticInfo diagnosticInfo;
-    UA_DiagnosticInfo_init(&diagnosticInfo);
-    diagnosticInfo.symbolicId    = 30;
-    diagnosticInfo.hasSymbolicId = UA_TRUE;
-    diagnosticInfo.namespaceUri  = 25;
-    diagnosticInfo.hasNamespaceUri = UA_TRUE;
-    diagnosticInfo.localizedText = 22;
-    diagnosticInfo.hasLocalizedText = UA_TRUE;
-    UA_Byte additionalInfoData = 'd';
-    diagnosticInfo.additionalInfo.data = &additionalInfoData; //"OPCUA";
-    diagnosticInfo.additionalInfo.length = 1;
-    diagnosticInfo.hasAdditionalInfo = UA_TRUE;
-    // when & then
-    // 1 (bitfield) + 4 (symbolic id) + 4 (namespaceuri) + 4 (localizedtext) + 5 (additionalinfo)
-    ck_assert_int_eq(UA_calcSizeBinary(&diagnosticInfo, &UA_TYPES[UA_TYPES_DIAGNOSTICINFO]), 18);
-}
-END_TEST
-
-START_TEST(UA_String_calcSizeWithNegativLengthShallReturnEncodingSize) {
-    // given
-    UA_String arg = { -1, UA_NULL };
-    // when
-    UA_UInt32 encodingSize = UA_calcSizeBinary(&arg, &UA_TYPES[UA_TYPES_STRING]);
-    // then
-    ck_assert_int_eq(encodingSize, 4);
-}
-END_TEST
-
-START_TEST(UA_String_calcSizeWithNegativLengthAndValidPointerShallReturnEncodingSize) {
-    // given
-    UA_String arg = { -1, (UA_Byte *)"OPC" };
-    // when
-    UA_UInt32 encodingSize = UA_calcSizeBinary(&arg, &UA_TYPES[UA_TYPES_STRING]);
-    // then
-    ck_assert_int_eq(encodingSize, 4);
-}
-END_TEST
-
-START_TEST(UA_String_calcSizeWithZeroLengthShallReturnEncodingSize) {
-    // given
-    UA_String arg = { 0, UA_NULL };
-    // when
-    UA_UInt32 encodingSize = UA_calcSizeBinary(&arg, &UA_TYPES[UA_TYPES_STRING]);
-    // then
-    ck_assert_int_eq(encodingSize, 4);
-}
-END_TEST
-
-START_TEST(UA_String_calcSizeWithZeroLengthAndValidPointerShallReturnEncodingSize) {
-    // given
-    UA_String arg = { 0, (UA_Byte *)"OPC" };
-    // when
-    UA_UInt32 encodingSize = UA_calcSizeBinary(&arg, &UA_TYPES[UA_TYPES_STRING]);
-    // then
-    ck_assert_int_eq(encodingSize, 4);
-}
-END_TEST
-
-START_TEST(UA_String_calcSizeShallReturnEncodingSize) {
-    // given
-    UA_String arg = { 3, (UA_Byte *)"OPC" };
-    // when
-    UA_UInt32 encodingSize = UA_calcSizeBinary(&arg, &UA_TYPES[UA_TYPES_STRING]);
-    // then
-    ck_assert_int_eq(encodingSize, 4+3);
-}
-END_TEST
-
-START_TEST(UA_NodeId_calcSizeEncodingTwoByteShallReturnEncodingSize) {
-    // given
-    UA_NodeId arg;
-    arg.identifierType = UA_NODEIDTYPE_NUMERIC;
-    arg.namespaceIndex = 0;
-    arg.identifier.numeric = 1;
-    // when
-    UA_UInt32 encodingSize = UA_calcSizeBinary(&arg, &UA_TYPES[UA_TYPES_NODEID]);
-    // then
-    ck_assert_int_eq(encodingSize, 2);
-}
-END_TEST
-
-START_TEST(UA_NodeId_calcSizeEncodingFourByteShallReturnEncodingSize) {
-    // given
-    UA_NodeId arg;
-    arg.identifierType = UA_NODEIDTYPE_NUMERIC;
-    arg.namespaceIndex = 1;
-    arg.identifier.numeric = 1;
-    // when
-    UA_UInt32 encodingSize = UA_calcSizeBinary(&arg, &UA_TYPES[UA_TYPES_NODEID]);
-    // then
-    ck_assert_int_eq(encodingSize, 4);
-}
-END_TEST
-
-START_TEST(UA_NodeId_calcSizeEncodingStringShallReturnEncodingSize) {
-    // given
-    UA_NodeId arg;
-    arg.identifierType = UA_NODEIDTYPE_STRING;
-    arg.identifier.string.length = 3;
-    arg.identifier.string.data   = (UA_Byte *)"PLT";
-    // when
-    UA_UInt32 encodingSize = UA_calcSizeBinary(&arg, &UA_TYPES[UA_TYPES_NODEID]);
-    // then
-    ck_assert_int_eq(encodingSize, 1+2+4+3);
-}
-END_TEST
-
-START_TEST(UA_NodeId_calcSizeEncodingStringNegativLengthShallReturnEncodingSize) {
-    // given
-    UA_NodeId arg;
-    arg.identifierType = UA_NODEIDTYPE_STRING;
-    arg.identifier.string.length = -1;
-    // when
-    UA_UInt32 encodingSize = UA_calcSizeBinary(&arg, &UA_TYPES[UA_TYPES_NODEID]);
-    // then
-    ck_assert_int_eq(encodingSize, 1+2+4+0);
-}
-END_TEST
-
-START_TEST(UA_NodeId_calcSizeEncodingStringZeroLengthShallReturnEncodingSize) {
-    // given
-    UA_NodeId arg;
-    arg.identifierType = UA_NODEIDTYPE_STRING;
-    arg.identifier.string.length = 0;
-    // when
-    UA_UInt32 encodingSize = UA_calcSizeBinary(&arg, &UA_TYPES[UA_TYPES_NODEID]);
-    // then
-    ck_assert_int_eq(encodingSize, 1+2+4+0);
-}
-END_TEST
-
-START_TEST(UA_ExpandedNodeId_calcSizeEncodingStringAndServerIndexShallReturnEncodingSize) {
-    // given
-    UA_ExpandedNodeId arg;
-    UA_ExpandedNodeId_init(&arg);
-    arg.nodeId.identifierType = UA_NODEIDTYPE_STRING;
-    arg.serverIndex = 1;
-    arg.nodeId.identifier.string.length = 3;
-    // when
-    UA_UInt32 encodingSize = UA_calcSizeBinary(&arg, &UA_TYPES[UA_TYPES_EXPANDEDNODEID]);
-    // then
-    ck_assert_int_eq(encodingSize, 1+2+4+3+4);
-}
-END_TEST
-
-START_TEST(UA_ExpandedNodeId_calcSizeEncodingStringAndNamespaceUriShallReturnEncodingSize) {
-    // given
-    UA_ExpandedNodeId arg;
-    UA_ExpandedNodeId_init(&arg);
-    arg.nodeId.identifierType = UA_NODEIDTYPE_STRING;
-    arg.nodeId.identifier.string.length = 3;
-    arg.namespaceUri.length = 7;
-    // when
-    UA_UInt32 encodingSize = UA_calcSizeBinary(&arg, &UA_TYPES[UA_TYPES_EXPANDEDNODEID]);
-    // then
-    ck_assert_int_eq(encodingSize, 1+2+4+3+4+7);
-}
-END_TEST
-
-START_TEST(UA_Guid_calcSizeShallReturnEncodingSize) {
-    // given
-    UA_Guid   arg;
-    // when
-    UA_UInt32 encodingSize = UA_calcSizeBinary(&arg, &UA_TYPES[UA_TYPES_GUID]);
-    // then
-    ck_assert_int_eq(encodingSize, 16);
-}
-END_TEST
-
-START_TEST(UA_LocalizedText_calcSizeTextOnlyShallReturnEncodingSize) {
-    // given
-    UA_LocalizedText arg;
-    UA_LocalizedText_init(&arg);
-    arg.text = (UA_String) {8, (UA_Byte *)"12345678"};
-    // when
-    UA_UInt32 encodingSize = UA_calcSizeBinary(&arg, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
-    // then
-    ck_assert_int_eq(encodingSize, 1+4+8);
-    // finally
-    UA_LocalizedText_init(&arg); // do not delete text
-    UA_LocalizedText_deleteMembers(&arg);
-}
-END_TEST
-
-START_TEST(UA_LocalizedText_calcSizeLocaleOnlyShallReturnEncodingSize) {
-    // given
-    UA_LocalizedText arg;
-    UA_LocalizedText_init(&arg);
-    arg.locale = (UA_String) {8, (UA_Byte *)"12345678"};
-    // when
-    UA_UInt32 encodingSize = UA_calcSizeBinary(&arg, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
-    // then
-    ck_assert_int_eq(encodingSize, 1+4+8);
-    UA_LocalizedText_init(&arg); // do not delete locale
-    UA_LocalizedText_deleteMembers(&arg);
-}
-END_TEST
-
-START_TEST(UA_LocalizedText_calcSizeTextAndLocaleShallReturnEncodingSize) {
-    // given
-    UA_LocalizedText arg;
-    UA_LocalizedText_init(&arg);
-    arg.locale = (UA_String) {8, (UA_Byte *)"12345678"};
-    arg.text = (UA_String) {8, (UA_Byte *)"12345678"};
-    // when
-    UA_UInt32 encodingSize = UA_calcSizeBinary(&arg, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
-    // then
-    ck_assert_int_eq(encodingSize, 1+4+8+4+8);
-    UA_LocalizedText_init(&arg); // do not delete locale and text
-    UA_LocalizedText_deleteMembers(&arg);
-}
-END_TEST
-
-START_TEST(UA_Variant_calcSizeFixedSizeArrayShallReturnEncodingSize) {
-    // given
-    UA_Variant arg;
-    UA_Variant_init(&arg);
-    arg.type = &UA_TYPES[UA_TYPES_INT32];
-#define ARRAY_LEN 8
-    arg.arrayLength = ARRAY_LEN;
-    UA_Int32 *data[ARRAY_LEN];
-    arg.data = (void *)data;
-
-    // when
-    UA_UInt32 encodingSize = UA_calcSizeBinary(&arg, &UA_TYPES[UA_TYPES_VARIANT]);
-
-    // then
-    ck_assert_int_eq(encodingSize, 1+4+ARRAY_LEN*4);
-#undef ARRAY_LEN
-}
-END_TEST
-
-START_TEST(UA_Variant_calcSizeVariableSizeArrayShallReturnEncodingSize) {
-    // given
-    UA_Variant arg;
-    UA_Variant_init(&arg);
-    arg.type = &UA_TYPES[UA_TYPES_STRING];
-#define ARRAY_LEN 3
-    arg.arrayLength = ARRAY_LEN;
-    UA_String strings[3];
-    strings[0] = (UA_String) {-1, UA_NULL };
-    strings[1] = (UA_String) {3, (UA_Byte *)"PLT" };
-    strings[2] = (UA_String) {47, UA_NULL };
-    arg.data   = (void *)strings;
-    // when
-    UA_UInt32 encodingSize = UA_calcSizeBinary(&arg, &UA_TYPES[UA_TYPES_VARIANT]);
-    // then
-    ck_assert_int_eq(encodingSize, 1+4+(4+0)+(4+3)+(4+47));
-#undef ARRAY_LEN
-}
-END_TEST
-
 START_TEST(UA_Byte_decodeShallCopyAndAdvancePosition) {
     // given
     UA_Byte dst;
@@ -609,8 +313,8 @@ START_TEST(UA_String_decodeWithNegativeSizeShallNotAllocateMemoryAndNullPtr) {
     UA_StatusCode retval = UA_String_decodeBinary(&src, &pos, &dst);
     // then
     ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
-    ck_assert_int_eq(dst.length, -1);
-    ck_assert_ptr_eq(dst.data, UA_NULL);
+    ck_assert_int_eq(dst.length, 0);
+    ck_assert_ptr_eq(dst.data, NULL);
 }
 END_TEST
 
@@ -626,7 +330,7 @@ START_TEST(UA_String_decodeWithZeroSizeShallNotAllocateMemoryAndNullPtr) {
     // then
     ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
     ck_assert_int_eq(dst.length, 0);
-    ck_assert_ptr_eq(dst.data, UA_NULL);
+    ck_assert_ptr_eq(dst.data, UA_EMPTY_ARRAY_SENTINEL);
 }
 END_TEST
 
@@ -697,7 +401,8 @@ START_TEST(UA_Variant_decodeWithOutArrayFlagSetShallSetVTAndAllocateMemoryForArr
     ck_assert_int_eq(pos, 5);
     //ck_assert_ptr_eq((const void *)dst.type, (const void *)&UA_TYPES[UA_TYPES_INT32]); //does not compile in gcc 4.6
     ck_assert_int_eq((uintptr_t)dst.type, (uintptr_t)&UA_TYPES[UA_TYPES_INT32]); 
-    ck_assert_int_eq(dst.arrayLength, -1);
+    ck_assert_int_eq(dst.arrayLength, 0);
+    ck_assert_int_ne((uintptr_t)dst.data, 0);
     ck_assert_int_eq(*(UA_Int32 *)dst.data, 255);
     // finally
     UA_Variant_deleteMembers(&dst);
@@ -728,56 +433,57 @@ START_TEST(UA_Variant_decodeWithArrayFlagSetShallSetVTAndAllocateMemoryForArray)
 END_TEST
 
 START_TEST(UA_Variant_decodeSingleExtensionObjectShallSetVTAndAllocateMemory){
-    // given
-    size_t pos = 0;
-    UA_Variant dst;
-    UA_NodeId tmpNodeId;
-    UA_ByteString srcByteString;
-
-    UA_NodeId_init(&tmpNodeId);
-    tmpNodeId.identifier.numeric = 22;
-    tmpNodeId.namespaceIndex = 2;
-    tmpNodeId.identifierType = UA_NODEIDTYPE_NUMERIC;
-
-    UA_ExtensionObject tmpExtensionObject;
-    UA_ExtensionObject_init(&tmpExtensionObject);
-    UA_ByteString_newMembers(&tmpExtensionObject.body,3);
-    tmpExtensionObject.body.data[0]= 10;
-    tmpExtensionObject.body.data[1]= 20;
-    tmpExtensionObject.body.data[2]= 30;
-    tmpExtensionObject.encoding = UA_EXTENSIONOBJECT_ENCODINGMASK_BODYISBYTESTRING;
-    tmpExtensionObject.typeId = tmpNodeId;
-
-    UA_Variant tmpVariant;
-    UA_Variant_init(&tmpVariant);
-    tmpVariant.arrayDimensions = UA_NULL;
-    tmpVariant.arrayDimensionsSize = -1;
-    tmpVariant.arrayLength = -1;
-    tmpVariant.storageType = UA_VARIANT_DATA_NODELETE;
-    tmpVariant.type = &UA_TYPES[UA_TYPES_EXTENSIONOBJECT];
-    tmpVariant.data = &tmpExtensionObject;
-
-    UA_ByteString_newMembers(&srcByteString,200);
-    pos = 0;
-    UA_Variant_encodeBinary(&tmpVariant,&srcByteString,&pos);
-
-    // when
-    pos = 0;
-    UA_StatusCode retval = UA_Variant_decodeBinary(&srcByteString, &pos, &dst);
-    // then
-    ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
-    ck_assert_int_eq((uintptr_t)dst.type, (uintptr_t)&UA_TYPES[UA_TYPES_EXTENSIONOBJECT]);
-    ck_assert_int_eq(dst.arrayLength, -1);
-    ck_assert_int_eq(((UA_ExtensionObject *)dst.data)->body.data[0], 10);
-    ck_assert_int_eq(((UA_ExtensionObject *)dst.data)->body.data[1], 20);
-    ck_assert_int_eq(((UA_ExtensionObject *)dst.data)->body.data[2], 30);
-    ck_assert_int_eq(((UA_ExtensionObject *)dst.data)->body.length, 3);
-
-
-    // finally
-    UA_Variant_deleteMembers(&dst);
-    UA_ByteString_deleteMembers(&srcByteString);
-    UA_ExtensionObject_deleteMembers(&tmpExtensionObject);
+    /* // given */
+    /* size_t pos = 0; */
+    /* UA_Variant dst; */
+    /* UA_NodeId tmpNodeId; */
+
+    /* UA_NodeId_init(&tmpNodeId); */
+    /* tmpNodeId.identifier.numeric = 22; */
+    /* tmpNodeId.namespaceIndex = 2; */
+    /* tmpNodeId.identifierType = UA_NODEIDTYPE_NUMERIC; */
+
+    /* UA_ExtensionObject tmpExtensionObject; */
+    /* UA_ExtensionObject_init(&tmpExtensionObject); */
+    /* tmpExtensionObject.encoding = UA_EXTENSIONOBJECT_ENCODED_BYTESTRING; */
+    /* tmpExtensionObject.content.encoded.body = UA_ByteString_withSize(3); */
+    /* tmpExtensionObject.content.encoded.body.data[0]= 10; */
+    /* tmpExtensionObject.content.encoded.body.data[1]= 20; */
+    /* tmpExtensionObject.content.encoded.body.data[2]= 30; */
+    /* tmpExtensionObject.content.encoded.typeId = tmpNodeId; */
+
+    /* UA_Variant tmpVariant; */
+    /* UA_Variant_init(&tmpVariant); */
+    /* tmpVariant.arrayDimensions = NULL; */
+    /* tmpVariant.arrayDimensionsSize = -1; */
+    /* tmpVariant.arrayLength = -1; */
+    /* tmpVariant.storageType = UA_VARIANT_DATA_NODELETE; */
+    /* tmpVariant.type = &UA_TYPES[UA_TYPES_EXTENSIONOBJECT]; */
+    /* tmpVariant.data = &tmpExtensionObject; */
+
+    /* UA_ByteString srcByteString = UA_ByteString_withSize(200); */
+    /* pos = 0; */
+    /* UA_Variant_encodeBinary(&tmpVariant,&srcByteString,&pos); */
+
+    /* // when */
+    /* pos = 0; */
+    /* UA_StatusCode retval = UA_Variant_decodeBinary(&srcByteString, &pos, &dst); */
+    /* // then */
+    /* ck_assert_int_eq(retval, UA_STATUSCODE_GOOD); */
+    /* // TODO!! */
+    /* /\* ck_assert_int_eq(dst.encoding, UA_EXTENSIONOBJECT_DECODED); *\/ */
+    /* /\* ck_assert_int_eq((uintptr_t)dst.content.decoded.type, (uintptr_t)&UA_TYPES[UA_TYPES_EXTENSIONOBJECT]); *\/ */
+    /* /\* ck_assert_int_eq(dst.arrayLength, -1); *\/ */
+    /* /\* ck_assert_int_eq(((UA_ExtensionObject *)dst.data)->body.data[0], 10); *\/ */
+    /* /\* ck_assert_int_eq(((UA_ExtensionObject *)dst.data)->body.data[1], 20); *\/ */
+    /* /\* ck_assert_int_eq(((UA_ExtensionObject *)dst.data)->body.data[2], 30); *\/ */
+    /* /\* ck_assert_int_eq(((UA_ExtensionObject *)dst.data)->body.length, 3); *\/ */
+
+
+    /* // finally */
+    /* UA_Variant_deleteMembers(&dst); */
+    /* UA_ByteString_deleteMembers(&srcByteString); */
+    /* UA_ExtensionObject_deleteMembers(&tmpExtensionObject); */
 
 }
 END_TEST
@@ -1174,12 +880,12 @@ START_TEST(UA_DataValue_encodeShallWorkOnExampleWithoutVariant) {
     src.serverTimestamp = 80;
     src.hasServerTimestamp = UA_TRUE;
 
-    UA_Byte data[] = {  0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,       0x55,
-            0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,       0x55,
-            0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,       0x55 };
+    UA_Byte data[] = { 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
+                       0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
+                       0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55 };
     UA_ByteString dst = { 24, data };
 
-    UA_Int32  retval  = 0;
+    UA_Int32 retval = 0;
     size_t pos     = 0;
 
     // when
@@ -1207,8 +913,8 @@ START_TEST(UA_DataValue_encodeShallWorkOnExampleWithVariant) {
     src.hasValue = UA_TRUE;
     src.hasServerTimestamp = UA_TRUE;
     src.value.type = &UA_TYPES[UA_TYPES_INT32];
-    src.value.arrayLength  = -1; // one element (encoded as not an array)
-    UA_Int32  vdata  = 45;
+    src.value.arrayLength  = 0; // one element (encoded as not an array)
+    UA_Int32 vdata = 45;
     src.value.data = (void *)&vdata;
 
     UA_Byte data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -1268,7 +974,7 @@ START_TEST(UA_DateTime_toStringShallWorkOnExample) {
     UA_String dst;
 
     // when
-    UA_DateTime_toString(src, &dst);
+    dst = UA_DateTime_toString(src);
     // then
     ck_assert_int_eq(dst.data[0], '0');
     ck_assert_int_eq(dst.data[1], '4');
@@ -1281,32 +987,33 @@ END_TEST
 
 START_TEST(UA_ExtensionObject_copyShallWorkOnExample) {
     // given
-    UA_Byte data[3] = { 1, 2, 3 };
+    /* UA_Byte data[3] = { 1, 2, 3 }; */
 
-    UA_ExtensionObject value, valueCopied;
-    UA_ExtensionObject_init(&value);
-    UA_ExtensionObject_init(&valueCopied);
+    /* UA_ExtensionObject value, valueCopied; */
+    /* UA_ExtensionObject_init(&value); */
+    /* UA_ExtensionObject_init(&valueCopied); */
 
-    value.typeId = UA_TYPES[UA_TYPES_BYTE].typeId;
-    value.encoding    = UA_EXTENSIONOBJECT_ENCODINGMASK_NOBODYISENCODED;
-    value.encoding    = UA_EXTENSIONOBJECT_ENCODINGMASK_BODYISBYTESTRING;
-    value.body.data   = data;
-    value.body.length = 3;
+    //Todo!!
+    /* value.typeId = UA_TYPES[UA_TYPES_BYTE].typeId; */
+    /* value.encoding    = UA_EXTENSIONOBJECT_ENCODINGMASK_NOBODYISENCODED; */
+    /* value.encoding    = UA_EXTENSIONOBJECT_ENCODINGMASK_BODYISBYTESTRING; */
+    /* value.body.data   = data; */
+    /* value.body.length = 3; */
 
-    //when
-    UA_ExtensionObject_copy(&value, &valueCopied);
+    /* //when */
+    /* UA_ExtensionObject_copy(&value, &valueCopied); */
 
-    for(UA_Int32 i = 0;i < 3;i++)
-        ck_assert_int_eq(valueCopied.body.data[i], value.body.data[i]);
+    /* for(UA_Int32 i = 0;i < 3;i++) */
+    /*     ck_assert_int_eq(valueCopied.body.data[i], value.body.data[i]); */
 
-    ck_assert_int_eq(valueCopied.encoding, value.encoding);
-    ck_assert_int_eq(valueCopied.typeId.identifierType, value.typeId.identifierType);
-    ck_assert_int_eq(valueCopied.typeId.identifier.numeric, value.typeId.identifier.numeric);
+    /* ck_assert_int_eq(valueCopied.encoding, value.encoding); */
+    /* ck_assert_int_eq(valueCopied.typeId.identifierType, value.typeId.identifierType); */
+    /* ck_assert_int_eq(valueCopied.typeId.identifier.numeric, value.typeId.identifier.numeric); */
 
-    //finally
-    value.body.data = UA_NULL; // we cannot free the static string
-    UA_ExtensionObject_deleteMembers(&value);
-    UA_ExtensionObject_deleteMembers(&valueCopied);
+    /* //finally */
+    /* value.body.data = NULL; // we cannot free the static string */
+    /* UA_ExtensionObject_deleteMembers(&value); */
+    /* UA_ExtensionObject_deleteMembers(&valueCopied); */
 }
 END_TEST
 
@@ -1325,7 +1032,9 @@ START_TEST(UA_Array_copyByteArrayShallWorkOnExample) {
     testString.length  = 5;
 
     //when
-    UA_Array_copy((const void *)testString.data, (void **)&dstArray, &UA_TYPES[UA_TYPES_BYTE], 5);
+    UA_StatusCode retval;
+    retval = UA_Array_copy((const void *)testString.data, 5, (void **)&dstArray, &UA_TYPES[UA_TYPES_BYTE]);
+    ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
     //then
     for(i = 0;i < size;i++)
         ck_assert_int_eq(testString.data[i], dstArray[i]);
@@ -1340,14 +1049,16 @@ END_TEST
 START_TEST(UA_Array_copyUA_StringShallWorkOnExample) {
     // given
     UA_Int32   i, j;
-    UA_String *srcArray = UA_Array_new(&UA_TYPES[UA_TYPES_STRING], 3);
+    UA_String *srcArray = UA_Array_new(3, &UA_TYPES[UA_TYPES_STRING]);
     UA_String *dstArray;
 
     srcArray[0] = UA_STRING_ALLOC("open");
     srcArray[1] = UA_STRING_ALLOC("62541");
     srcArray[2] = UA_STRING_ALLOC("opc ua");
     //when
-    UA_Array_copy((const void *)srcArray, (void **)&dstArray, &UA_TYPES[UA_TYPES_STRING], 3);
+    UA_StatusCode retval;
+    retval = UA_Array_copy((const void *)srcArray, 3, (void **)&dstArray, &UA_TYPES[UA_TYPES_STRING]);
+    ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
     //then
     for(i = 0;i < 3;i++) {
         for(j = 0;j < 3;j++)
@@ -1355,8 +1066,8 @@ START_TEST(UA_Array_copyUA_StringShallWorkOnExample) {
         ck_assert_int_eq(srcArray[i].length, dstArray[i].length);
     }
     //finally
-    UA_Array_delete(srcArray, &UA_TYPES[UA_TYPES_STRING], 3);
-    UA_Array_delete(dstArray, &UA_TYPES[UA_TYPES_STRING], 3);
+    UA_Array_delete(srcArray, 3, &UA_TYPES[UA_TYPES_STRING]);
+    UA_Array_delete(dstArray, 3, &UA_TYPES[UA_TYPES_STRING]);
 }
 END_TEST
 
@@ -1376,7 +1087,7 @@ START_TEST(UA_DiagnosticInfo_copyShallWorkOnExample) {
     UA_DiagnosticInfo_copy(&value, &copiedValue);
 
     //then
-    for(UA_Int32 i = 0;i < testString.length;i++)
+    for(size_t i = 0;i < testString.length;i++)
         ck_assert_int_eq(copiedValue.additionalInfo.data[i], value.additionalInfo.data[i]);
     ck_assert_int_eq(copiedValue.additionalInfo.length, value.additionalInfo.length);
 
@@ -1389,8 +1100,8 @@ START_TEST(UA_DiagnosticInfo_copyShallWorkOnExample) {
     ck_assert_int_eq(copiedValue.symbolicId, value.symbolicId);
 
     //finally
-    value.additionalInfo.data = UA_NULL; // do not delete the static string
-    value.innerDiagnosticInfo = UA_NULL; // do not delete the static innerdiagnosticinfo
+    value.additionalInfo.data = NULL; // do not delete the static string
+    value.innerDiagnosticInfo = NULL; // do not delete the static innerdiagnosticinfo
     UA_DiagnosticInfo_deleteMembers(&value);
     UA_DiagnosticInfo_deleteMembers(&copiedValue);
 
@@ -1424,15 +1135,15 @@ START_TEST(UA_ApplicationDescription_copyShallWorkOnExample) {
     //then
     ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
 
-    for(UA_Int32 i = 0;i < appString.length;i++)
+    for(size_t i = 0; i < appString.length; i++)
         ck_assert_int_eq(copiedValue.applicationUri.data[i], value.applicationUri.data[i]);
     ck_assert_int_eq(copiedValue.applicationUri.length, value.applicationUri.length);
 
-    for(UA_Int32 i = 0;i < discString.length;i++)
+    for(size_t i = 0; i < discString.length; i++)
         ck_assert_int_eq(copiedValue.discoveryProfileUri.data[i], value.discoveryProfileUri.data[i]);
     ck_assert_int_eq(copiedValue.discoveryProfileUri.length, value.discoveryProfileUri.length);
 
-    for(UA_Int32 i = 0;i < gateWayString.length;i++)
+    for(size_t i = 0; i < gateWayString.length; i++)
         ck_assert_int_eq(copiedValue.gatewayServerUri.data[i], value.gatewayServerUri.data[i]);
     ck_assert_int_eq(copiedValue.gatewayServerUri.length, value.gatewayServerUri.length);
 
@@ -1548,7 +1259,7 @@ START_TEST(UA_Variant_copyShallWorkOnSingleValueExample) {
     ck_assert_int_eq(value.arrayLength, copiedValue.arrayLength);
 
     //finally
-    ((UA_String*)value.data)->data = UA_NULL; // the string is statically allocated. do not free it.
+    ((UA_String*)value.data)->data = NULL; // the string is statically allocated. do not free it.
     UA_Variant_deleteMembers(&value);
     UA_Variant_deleteMembers(&copiedValue);
 }
@@ -1556,13 +1267,13 @@ END_TEST
 
 START_TEST(UA_Variant_copyShallWorkOn1DArrayExample) {
     // given
-    UA_String *srcArray = UA_Array_new(&UA_TYPES[UA_TYPES_STRING], 3);
+    UA_String *srcArray = UA_Array_new(3, &UA_TYPES[UA_TYPES_STRING]);
     srcArray[0] = UA_STRING_ALLOC("__open");
     srcArray[1] = UA_STRING_ALLOC("_62541");
     srcArray[2] = UA_STRING_ALLOC("opc ua");
 
-    UA_Int32 *dimensions;
-    dimensions = UA_malloc(sizeof(UA_Int32));
+    UA_UInt32 *dimensions;
+    dimensions = UA_malloc(sizeof(UA_UInt32));
     dimensions[0] = 3;
 
     UA_Variant value, copiedValue;
@@ -1604,7 +1315,7 @@ END_TEST
 
 START_TEST(UA_Variant_copyShallWorkOn2DArrayExample) {
     // given
-    UA_Int32 *srcArray = UA_Array_new(&UA_TYPES[UA_TYPES_INT32], 6);
+    UA_Int32 *srcArray = UA_Array_new(6, &UA_TYPES[UA_TYPES_INT32]);
     srcArray[0] = 0;
     srcArray[1] = 1;
     srcArray[2] = 2;
@@ -1612,7 +1323,7 @@ START_TEST(UA_Variant_copyShallWorkOn2DArrayExample) {
     srcArray[4] = 4;
     srcArray[5] = 5;
 
-    UA_Int32 *dimensions = UA_Array_new(&UA_TYPES[UA_TYPES_INT32], 2);
+    UA_UInt32 *dimensions = UA_Array_new(2, &UA_TYPES[UA_TYPES_UINT32]);
     UA_Int32 dim1 = 3;
     UA_Int32 dim2 = 2;
     dimensions[0] = dim1;
@@ -1663,85 +1374,59 @@ START_TEST(UA_Variant_copyShallWorkOn2DArrayExample) {
 END_TEST
 
 START_TEST(UA_ExtensionObject_encodeDecodeShallWorkOnExtensionObject) {
-    UA_Int32 val = 42;
-    UA_VariableAttributes varAttr;
-    UA_VariableAttributes_init(&varAttr);
-    varAttr.dataType = UA_TYPES[UA_TYPES_INT32].typeId;
-    UA_Variant_init(&varAttr.value);
-    varAttr.value.type = &UA_TYPES[UA_TYPES_INT32];
-    varAttr.value.data = &val;
-    varAttr.value.arrayLength = -1;
-    varAttr.userWriteMask = 41;
-    varAttr.specifiedAttributes |= UA_NODEATTRIBUTESMASK_DATATYPE;
-    varAttr.specifiedAttributes |= UA_NODEATTRIBUTESMASK_VALUE;
-    varAttr.specifiedAttributes |= UA_NODEATTRIBUTESMASK_USERWRITEMASK;
-
-    /* wrap it into a extension object attributes */
-    UA_ExtensionObject extensionObject;
-    UA_ExtensionObject_init(&extensionObject);
-    extensionObject.typeId = UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES].typeId;
-    UA_Byte extensionData[50];
-    extensionObject.body = (UA_ByteString){.data = extensionData, .length=50};
-    size_t posEncode = 0;
-    UA_VariableAttributes_encodeBinary(&varAttr, &extensionObject.body, &posEncode);
-    extensionObject.body.length = posEncode;
-    extensionObject.encoding = UA_EXTENSIONOBJECT_ENCODINGMASK_BODYISBYTESTRING;
-
-    UA_Byte data[50];
-    UA_ByteString dst = {.data = data, .length=50};
-
-    posEncode = 0;
-    UA_ExtensionObject_encodeBinary(&extensionObject, &dst, &posEncode);
-
-    UA_ExtensionObject extensionObjectDecoded;
-    size_t posDecode = 0;
-    UA_ExtensionObject_decodeBinary(&dst, &posDecode, &extensionObjectDecoded);
-
-    ck_assert_int_eq(posEncode, posDecode);
-    ck_assert_int_eq(extensionObjectDecoded.body.length, extensionObject.body.length);
-
-    UA_VariableAttributes varAttrDecoded;
-    UA_VariableAttributes_init(&varAttrDecoded);
-    posDecode = 0;
-    UA_VariableAttributes_decodeBinary(&extensionObjectDecoded.body, &posDecode, &varAttrDecoded);
-    ck_assert_uint_eq(41, varAttrDecoded.userWriteMask);
-    ck_assert_int_eq(-1, varAttrDecoded.value.arrayLength);
-
-    // finally
-    UA_ExtensionObject_deleteMembers(&extensionObjectDecoded);
-    UA_Variant_deleteMembers(&varAttrDecoded.value);
+    /* UA_Int32 val = 42; */
+    /* UA_VariableAttributes varAttr; */
+    /* UA_VariableAttributes_init(&varAttr); */
+    /* varAttr.dataType = UA_TYPES[UA_TYPES_INT32].typeId; */
+    /* UA_Variant_init(&varAttr.value); */
+    /* varAttr.value.type = &UA_TYPES[UA_TYPES_INT32]; */
+    /* varAttr.value.data = &val; */
+    /* varAttr.value.arrayLength = -1; */
+    /* varAttr.userWriteMask = 41; */
+    /* varAttr.specifiedAttributes |= UA_NODEATTRIBUTESMASK_DATATYPE; */
+    /* varAttr.specifiedAttributes |= UA_NODEATTRIBUTESMASK_VALUE; */
+    /* varAttr.specifiedAttributes |= UA_NODEATTRIBUTESMASK_USERWRITEMASK; */
+
+    /* /\* wrap it into a extension object attributes *\/ */
+    /* UA_ExtensionObject extensionObject; */
+    /* UA_ExtensionObject_init(&extensionObject); */
+    /* extensionObject.typeId = UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES].typeId; */
+    /* UA_Byte extensionData[50]; */
+    /* extensionObject.body = (UA_ByteString){.data = extensionData, .length=50}; */
+    /* size_t posEncode = 0; */
+    /* UA_VariableAttributes_encodeBinary(&varAttr, &extensionObject.body, &posEncode); */
+    /* extensionObject.body.length = posEncode; */
+    /* extensionObject.encoding = UA_EXTENSIONOBJECT_ENCODINGMASK_BODYISBYTESTRING; */
+
+    /* UA_Byte data[50]; */
+    /* UA_ByteString dst = {.data = data, .length=50}; */
+
+    /* posEncode = 0; */
+    /* UA_ExtensionObject_encodeBinary(&extensionObject, &dst, &posEncode); */
+
+    /* UA_ExtensionObject extensionObjectDecoded; */
+    /* size_t posDecode = 0; */
+    /* UA_ExtensionObject_decodeBinary(&dst, &posDecode, &extensionObjectDecoded); */
+
+    /* ck_assert_int_eq(posEncode, posDecode); */
+    /* ck_assert_int_eq(extensionObjectDecoded.body.length, extensionObject.body.length); */
+
+    /* UA_VariableAttributes varAttrDecoded; */
+    /* UA_VariableAttributes_init(&varAttrDecoded); */
+    /* posDecode = 0; */
+    /* UA_VariableAttributes_decodeBinary(&extensionObjectDecoded.body, &posDecode, &varAttrDecoded); */
+    /* ck_assert_uint_eq(41, varAttrDecoded.userWriteMask); */
+    /* ck_assert_int_eq(-1, varAttrDecoded.value.arrayLength); */
+
+    /* // finally */
+    /* UA_ExtensionObject_deleteMembers(&extensionObjectDecoded); */
+    /* UA_Variant_deleteMembers(&varAttrDecoded.value); */
 }
 END_TEST
 
 static Suite *testSuite_builtin(void) {
     Suite *s = suite_create("Built-in Data Types 62541-6 Table 1");
 
-    TCase *tc_calcSize = tcase_create("calcSize");
-    tcase_add_test(tc_calcSize, UA_ExtensionObject_calcSizeShallWorkOnExample);
-    tcase_add_test(tc_calcSize, UA_DataValue_calcSizeShallWorkOnExample);
-    tcase_add_test(tc_calcSize, UA_DiagnosticInfo_calcSizeShallWorkOnExample);
-    tcase_add_test(tc_calcSize, UA_String_calcSizeShallReturnEncodingSize);
-    tcase_add_test(tc_calcSize, UA_String_calcSizeWithNegativLengthShallReturnEncodingSize);
-    tcase_add_test(tc_calcSize, UA_String_calcSizeWithNegativLengthAndValidPointerShallReturnEncodingSize);
-    tcase_add_test(tc_calcSize, UA_String_calcSizeWithZeroLengthShallReturnEncodingSize);
-    tcase_add_test(tc_calcSize, UA_String_calcSizeWithZeroLengthAndValidPointerShallReturnEncodingSize);
-    tcase_add_test(tc_calcSize, UA_NodeId_calcSizeEncodingTwoByteShallReturnEncodingSize);
-    tcase_add_test(tc_calcSize, UA_NodeId_calcSizeEncodingFourByteShallReturnEncodingSize);
-    tcase_add_test(tc_calcSize, UA_NodeId_calcSizeEncodingStringShallReturnEncodingSize);
-    tcase_add_test(tc_calcSize, UA_NodeId_calcSizeEncodingStringNegativLengthShallReturnEncodingSize);
-    tcase_add_test(tc_calcSize, UA_NodeId_calcSizeEncodingStringZeroLengthShallReturnEncodingSize);
-    tcase_add_test(tc_calcSize, UA_ExpandedNodeId_calcSizeEncodingStringAndServerIndexShallReturnEncodingSize);
-    tcase_add_test(tc_calcSize, UA_ExpandedNodeId_calcSizeEncodingStringAndNamespaceUriShallReturnEncodingSize);
-    tcase_add_test(tc_calcSize, UA_Guid_calcSizeShallReturnEncodingSize);
-    tcase_add_test(tc_calcSize, UA_Guid_calcSizeShallReturnEncodingSize);
-    tcase_add_test(tc_calcSize, UA_LocalizedText_calcSizeTextOnlyShallReturnEncodingSize);
-    tcase_add_test(tc_calcSize, UA_LocalizedText_calcSizeLocaleOnlyShallReturnEncodingSize);
-    tcase_add_test(tc_calcSize, UA_LocalizedText_calcSizeTextAndLocaleShallReturnEncodingSize);
-    tcase_add_test(tc_calcSize, UA_Variant_calcSizeFixedSizeArrayShallReturnEncodingSize);
-    tcase_add_test(tc_calcSize, UA_Variant_calcSizeVariableSizeArrayShallReturnEncodingSize);
-    tcase_add_test(tc_calcSize, UA_Variant_decodeWithOutDeleteMembersShallFailInCheckMem);
-    suite_add_tcase(s, tc_calcSize);
-
     TCase *tc_decode = tcase_create("decode");
     tcase_add_test(tc_decode, UA_Byte_decodeShallCopyAndAdvancePosition);
     tcase_add_test(tc_decode, UA_Byte_decodeShallModifyOnlyCurrentPosition);

+ 17 - 14
tests/check_memory.c

@@ -1,5 +1,4 @@
 #define _XOPEN_SOURCE 500
-#include <stdio.h>
 #include <stdlib.h>
 
 #include "ua_types.h"
@@ -12,7 +11,7 @@ START_TEST(newAndEmptyObjectShallBeDeleted) {
 	// given
 	void *obj = UA_new(&UA_TYPES[_i]);
 	// then
-	ck_assert_ptr_ne(obj, UA_NULL);
+	ck_assert_ptr_ne(obj, NULL);
     // finally
 	UA_delete(obj, &UA_TYPES[_i]);
 }
@@ -26,7 +25,7 @@ START_TEST(arrayCopyShallMakeADeepCopy) {
 	a1[2] = (UA_String){3, (UA_Byte*)"ccc"};
 	// when
 	UA_String *a2;
-	UA_Int32   retval = UA_Array_copy((const void *)a1, (void **)&a2, &UA_TYPES[UA_TYPES_STRING], 3);
+	UA_Int32 retval = UA_Array_copy((const void *)a1, 3, (void **)&a2, &UA_TYPES[UA_TYPES_STRING]);
 	// then
 	ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
 	ck_assert_int_eq(a1[0].length, 1);
@@ -42,7 +41,7 @@ START_TEST(arrayCopyShallMakeADeepCopy) {
 	ck_assert_int_eq(a1[1].data[0], a2[1].data[0]);
 	ck_assert_int_eq(a1[2].data[0], a2[2].data[0]);
 	// finally
-	UA_Array_delete((void *)a2, &UA_TYPES[UA_TYPES_STRING], 3);
+	UA_Array_delete((void *)a2, 3, &UA_TYPES[UA_TYPES_STRING]);
 }
 END_TEST
 
@@ -51,8 +50,9 @@ START_TEST(encodeShallYieldDecode) {
 	UA_ByteString msg1, msg2;
 	size_t pos = 0;
 	void *obj1 = UA_new(&UA_TYPES[_i]);
-	UA_ByteString_newMembers(&msg1, 65000); // fixed buf size
-    UA_StatusCode retval = UA_encodeBinary(obj1, &UA_TYPES[_i], &msg1, &pos);
+    UA_StatusCode retval = UA_ByteString_allocBuffer(&msg1, 65000); // fixed buf size
+	ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
+    retval = UA_encodeBinary(obj1, &UA_TYPES[_i], &msg1, &pos);
     msg1.length = pos;
 	if(retval != UA_STATUSCODE_GOOD) {
 		UA_delete(obj1, &UA_TYPES[_i]);
@@ -63,8 +63,10 @@ START_TEST(encodeShallYieldDecode) {
 	// when
 	void *obj2 = UA_new(&UA_TYPES[_i]);
 	pos = 0; retval = UA_decodeBinary(&msg1, &pos, obj2, &UA_TYPES[_i]);
-	ck_assert_msg(retval == UA_STATUSCODE_GOOD, "messages differ idx=%d,nodeid=%i", _i, UA_TYPES[_i].typeId.identifier.numeric);
-	retval = UA_ByteString_newMembers(&msg2, 65000);
+	ck_assert_msg(retval == UA_STATUSCODE_GOOD, "could not decode idx=%d,nodeid=%i", _i, UA_TYPES[_i].typeId.identifier.numeric);
+	ck_assert(!memcmp(obj1, obj2, UA_TYPES[_i].memSize)); // bit identical decoding
+    assert(!memcmp(obj1, obj2, UA_TYPES[_i].memSize));
+	retval = UA_ByteString_allocBuffer(&msg2, 65000);
 	ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
 	pos = 0; retval = UA_encodeBinary(obj2, &UA_TYPES[_i], &msg2, &pos);
     msg2.length = pos;
@@ -87,8 +89,8 @@ START_TEST(decodeShallFailWithTruncatedBufferButSurvive) {
 	UA_ByteString msg1;
 	void *obj1 = UA_new(&UA_TYPES[_i]);
 	size_t pos = 0;
-	UA_ByteString_newMembers(&msg1, 65000); // fixed buf size
-    UA_StatusCode retval = UA_encodeBinary(obj1, &UA_TYPES[_i], &msg1, &pos);
+    UA_StatusCode retval = UA_ByteString_allocBuffer(&msg1, 65000); // fixed buf size
+    retval |= UA_encodeBinary(obj1, &UA_TYPES[_i], &msg1, &pos);
 	UA_delete(obj1, &UA_TYPES[_i]);
     if(retval != UA_STATUSCODE_GOOD) {
         UA_ByteString_deleteMembers(&msg1);
@@ -99,7 +101,8 @@ START_TEST(decodeShallFailWithTruncatedBufferButSurvive) {
 	pos = 0;
 	msg1.length = pos / 2;
 	//fprintf(stderr,"testing %s with half buffer\n",UA_TYPES[_i].name);
-	UA_decodeBinary(&msg1, &pos, obj2, &UA_TYPES[_i]);
+	retval = UA_decodeBinary(&msg1, &pos, obj2, &UA_TYPES[_i]);
+	ck_assert_int_ne(retval, UA_STATUSCODE_GOOD);
 	//then
 	// finally
 	//fprintf(stderr,"delete %s with half buffer\n",UA_TYPES[_i].name);
@@ -112,11 +115,11 @@ END_TEST
 
 START_TEST(decodeScalarBasicTypeFromRandomBufferShallSucceed) {
 	// given
-	void *obj1 = UA_NULL;
+	void *obj1 = NULL;
 	UA_ByteString msg1;
 	UA_Int32 retval = UA_STATUSCODE_GOOD;
 	UA_Int32 buflen = 256;
-	UA_ByteString_newMembers(&msg1, buflen); // fixed size
+	retval = UA_ByteString_allocBuffer(&msg1, buflen); // fixed size
 #ifdef _WIN32
 	srand(42);
 #else
@@ -149,7 +152,7 @@ START_TEST(decodeComplexTypeFromRandomBufferShallSurvive) {
 	UA_ByteString msg1;
 	UA_Int32 retval = UA_STATUSCODE_GOOD;
 	UA_Int32 buflen = 256;
-	UA_ByteString_newMembers(&msg1, buflen); // fixed size
+	retval = UA_ByteString_allocBuffer(&msg1, buflen); // fixed size
 #ifdef _WIN32
 	srand(42);
 #else

+ 0 - 0
tests/check_nodestore.c


Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio