Browse Source

Merge branch '0.2'

Julius Pfrommer 8 years ago
parent
commit
fbac663ef4
45 changed files with 1375 additions and 711 deletions
  1. 24 0
      .github/ISSUE_TEMPLATE
  2. 7 3
      .travis.yml
  3. 48 28
      CMakeLists.txt
  4. 51 82
      appveyor.yml
  5. 255 0
      deps/ms_stdint.h
  6. 1 1
      deps/pcg_basic.h
  7. 2 0
      doc/CMakeLists.txt
  8. 1 1
      doc/building.rst
  9. 4 2
      doc/conf.py
  10. 48 9
      doc/index.rst
  11. 136 0
      doc/protocol.rst
  12. 1 0
      doc/toc.rst
  13. BIN
      doc/ua-wireshark.png
  14. 8 8
      include/ua_client_highlevel.h
  15. 50 105
      include/ua_config.h.in
  16. 26 5
      include/ua_server.h
  17. 22 8
      include/ua_types.h
  18. 4 4
      plugins/ua_accesscontrol_default.c
  19. 9 1
      plugins/ua_config_standard.c
  20. 3 1
      plugins/ua_network_tcp.c
  21. 12 12
      src/client/ua_client.c
  22. 24 10
      src/server/ua_nodes.h
  23. 4 2
      src/server/ua_server.c
  24. 2 2
      src/server/ua_server_internal.h
  25. 78 35
      src/server/ua_server_worker.c
  26. 2 7
      src/server/ua_services.h
  27. 183 161
      src/server/ua_services_attribute.c
  28. 132 118
      src/server/ua_services_nodemanagement.c
  29. 3 13
      src/server/ua_services_subscription.c
  30. 53 20
      src/server/ua_subscription.c
  31. 11 4
      src/server/ua_subscription.h
  32. 1 1
      src/ua_connection.c
  33. 30 23
      src/ua_types.c
  34. 9 9
      src/ua_types_encoding_binary.c
  35. 63 3
      src/ua_util.h
  36. 19 14
      tests/CMakeLists.txt
  37. 1 0
      tests/check_services_attributes.c
  38. 1 1
      tools/amalgamate.py
  39. 10 5
      tools/c2rst.py
  40. 18 0
      tools/cmake/FindValgrind.cmake
  41. 2 2
      tools/generate_datatypes.py
  42. 1 0
      tools/pyUANamespace/ua_namespace.py
  43. 11 8
      tools/travis/travis_linux_script.sh
  44. 2 2
      tools/travis/travis_push_doc.sh
  45. 3 1
      tools/travis/travis_push_release.sh

+ 24 - 0
.github/ISSUE_TEMPLATE

@@ -0,0 +1,24 @@
+Description
+===========
+
+
+
+Background Information / Reproduction Steps
+===========================================
+
+See here for the kinds of information we need to reproduce the bug and how to obtain it:
+https://github.com/open62541/open62541/wiki/Writing-Good-Issue-Reports
+
+
+
+Checklist
+=========
+Please provide the following information:
+
+ - [ ] open62541 Version (release number or git tag):
+ - [ ] Other OPC UA SDKs used (client or server): 
+ - [ ] Operating system:
+ - [ ] Logs (with `UA_LOGLEVEL` set as low as necessary) attached
+ - [ ] Wireshark network dump attached
+ - [ ] Self-contained code example attached
+ - [ ] Critical issue

+ 7 - 3
.travis.yml

@@ -43,18 +43,22 @@ addons:
       - clang-3.7
       - clang-3.7
       - cmake
       - cmake
       - gcc-4.8
       - gcc-4.8
-      - gcc-mingw-w64-i686
       - gcc-multilib
       - gcc-multilib
       - g++-4.8
       - g++-4.8
+      - g++-multilib
+      - mingw-w64
+      - g++-mingw-w64-x86-64
+      - g++-mingw-w64-i686
       - libc6-dbg # for valgrind compilation
       - libc6-dbg # for valgrind compilation
       - libsubunit-dev
       - libsubunit-dev
       - libx11-dev
       - libx11-dev
-      - mingw-w64
       - wget
       - wget
       - xutils-dev
       - xutils-dev
       - zip
       - zip
       - graphviz
       - graphviz
-    # - libsubunit-dev #for check_0.10.0
+      - texlive-latex-recommended
+      - texlive-latex-extra
+      - texlive-fonts-recommended
   coverity_scan:
   coverity_scan:
     project:
     project:
       name: acplt/open62541
       name: acplt/open62541

+ 48 - 28
CMakeLists.txt

@@ -1,33 +1,31 @@
 cmake_minimum_required(VERSION 2.8.11)
 cmake_minimum_required(VERSION 2.8.11)
-project(open62541 C)
-set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/tools/cmake")
+project(open62541)
 # set(CMAKE_VERBOSE_MAKEFILE ON)
 # set(CMAKE_VERBOSE_MAKEFILE ON)
 
 
-######################
-# Check Dependencies #
-######################
-
+set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/tools/cmake")
 find_package(PythonInterp REQUIRED)
 find_package(PythonInterp REQUIRED)
 find_package(Git)
 find_package(Git)
+
+###########
+# Version #
+###########
+
+set(OPEN62541_VER_MAJOR 0)
+set(OPEN62541_VER_MINOR 2)
+set(OPEN62541_VER_PATCH 0)
+set(OPEN62541_VER_LABEL "-rc2") # Appended to the X.Y.Z version format. For example "-rc1" or an empty string
+
+# Set OPEN62541_VER_COMMIT
 if(GIT_FOUND)
 if(GIT_FOUND)
   execute_process(COMMAND ${GIT_EXECUTABLE} describe --always --tags
   execute_process(COMMAND ${GIT_EXECUTABLE} describe --always --tags
                   RESULT_VARIABLE res_var OUTPUT_VARIABLE GIT_COM_ID WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
                   RESULT_VARIABLE res_var OUTPUT_VARIABLE GIT_COM_ID WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
-  execute_process(COMMAND ${GIT_EXECUTABLE} describe --abbrev=0 --always --tags
-                  RESULT_VARIABLE res_var OUTPUT_VARIABLE GIT_REL_ID WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
-    if(NOT ${res_var} EQUAL 0)
-        set(GIT_COMMIT_ID "commit id unknown")
-        set(GIT_RELEASE_ID "release unknown")
-        message(STATUS "Git failed (not a repo, or no tags). Build will not contain git revision info." )
-    else()
-        string(REPLACE "\n" "" GIT_COMMIT_ID ${GIT_COM_ID} )
-        string(REPLACE "\n" "" GIT_RELEASE_ID ${GIT_REL_ID} )
+    if(${res_var} EQUAL 0)
+        string(REPLACE "\n" "" OPEN62541_VER_COMMIT ${GIT_COM_ID} )
     endif()
     endif()
-else()
-    set(GIT_COMMIT_ID "commit id unknown")
-    set(GIT_RELEASE_ID "release unknown")
-    message(STATUS "Git not found. Build will not contain git revision info." )
 endif()
 endif()
-message(STATUS "Git version: "  ${GIT_COMMIT_ID})
+if(NOT ${OPEN62541_VER_COMMIT})
+    set(OPEN62541_VER_COMMIT "undefined")
+endif()
 
 
 ############
 ############
 # Settings #
 # Settings #
@@ -44,7 +42,6 @@ set(UA_LOGLEVEL 300 CACHE STRING "Level at which logs shall be reported")
 option(UA_ENABLE_METHODCALLS "Enable the Method service set" ON)
 option(UA_ENABLE_METHODCALLS "Enable the Method service set" ON)
 option(UA_ENABLE_NODEMANAGEMENT "Enable dynamic addition and removal of nodes at runtime" ON)
 option(UA_ENABLE_NODEMANAGEMENT "Enable dynamic addition and removal of nodes at runtime" ON)
 option(UA_ENABLE_SUBSCRIPTIONS "Enable subscriptions support." ON)
 option(UA_ENABLE_SUBSCRIPTIONS "Enable subscriptions support." ON)
-option(UA_ENABLE_MULTITHREADING "Enable multithreading" OFF)
 option(UA_ENABLE_DISCOVERY "Enable Discovery Service (LDS)" ON)
 option(UA_ENABLE_DISCOVERY "Enable Discovery Service (LDS)" ON)
 option(UA_ENABLE_AMALGAMATION "Concatenate the library to a single file open62541.h/.c" OFF)
 option(UA_ENABLE_AMALGAMATION "Concatenate the library to a single file open62541.h/.c" OFF)
 option(UA_ENABLE_COVERAGE "Enable gcov coverage" OFF)
 option(UA_ENABLE_COVERAGE "Enable gcov coverage" OFF)
@@ -58,6 +55,9 @@ if(UA_ENABLE_COVERAGE)
 endif()
 endif()
 
 
 # Advanced options
 # Advanced options
+option(UA_ENABLE_MULTITHREADING "Enable multithreading (experimental)" OFF)
+mark_as_advanced(UA_ENABLE_MULTITHREADING)
+
 option(UA_ENABLE_STATUSCODE_DESCRIPTIONS "Enable conversion of StatusCode to human-readable error message" ON)
 option(UA_ENABLE_STATUSCODE_DESCRIPTIONS "Enable conversion of StatusCode to human-readable error message" ON)
 mark_as_advanced(UA_ENABLE_STATUSCODE_DESCRIPTIONS)
 mark_as_advanced(UA_ENABLE_STATUSCODE_DESCRIPTIONS)
 
 
@@ -73,10 +73,13 @@ mark_as_advanced(UA_ENABLE_GENERATE_NAMESPACE0)
 option(UA_ENABLE_EXTERNAL_NAMESPACES "Enable namespace handling by an external component (experimental)" OFF)
 option(UA_ENABLE_EXTERNAL_NAMESPACES "Enable namespace handling by an external component (experimental)" OFF)
 mark_as_advanced(UA_ENABLE_EXTERNAL_NAMESPACES)
 mark_as_advanced(UA_ENABLE_EXTERNAL_NAMESPACES)
 
 
-option(UA_ENABLE_NONSTANDARD_STATELESS "Enable stateless extension" OFF)
+option(UA_ENABLE_VALGRIND_UNIT_TESTS "Use Valgrind to detect memory leaks when running the unit tests" OFF)
+mark_as_advanced(UA_ENABLE_VALGRIND_UNIT_TESTS)
+
+option(UA_ENABLE_NONSTANDARD_STATELESS "Enable stateless extension (non-standard)" OFF)
 mark_as_advanced(UA_ENABLE_NONSTANDARD_STATELESS)
 mark_as_advanced(UA_ENABLE_NONSTANDARD_STATELESS)
 
 
-option(UA_ENABLE_NONSTANDARD_UDP "Enable udp extension" OFF)
+option(UA_ENABLE_NONSTANDARD_UDP "Enable udp extension (non-standard)" OFF)
 mark_as_advanced(UA_ENABLE_NONSTANDARD_UDP)
 mark_as_advanced(UA_ENABLE_NONSTANDARD_UDP)
 if(UA_ENABLE_NONSTANDARD_UDP)
 if(UA_ENABLE_NONSTANDARD_UDP)
   set(UA_ENABLE_NONSTANDARD_STATELESS ON)
   set(UA_ENABLE_NONSTANDARD_STATELESS ON)
@@ -90,6 +93,7 @@ option(UA_BUILD_EXAMPLES_NODESET_COMPILER "Generate an OPC UA information model
 
 
 # Advanced Build Targets
 # Advanced Build Targets
 option(UA_BUILD_SELFSIGNED_CERTIFICATE "Generate self-signed certificate" OFF)
 option(UA_BUILD_SELFSIGNED_CERTIFICATE "Generate self-signed certificate" OFF)
+mark_as_advanced(UA_BUILD_SELFSIGNED_CERTIFICATE)
 
 
 # Building shared libs (dll, so). This option is written into ua_config.h.
 # Building shared libs (dll, so). This option is written into ua_config.h.
 set(UA_DYNAMIC_LINKING OFF)
 set(UA_DYNAMIC_LINKING OFF)
@@ -97,6 +101,10 @@ if(BUILD_SHARED_LIBS)
   set(UA_DYNAMIC_LINKING ON)
   set(UA_DYNAMIC_LINKING ON)
 endif()
 endif()
 
 
+# Force compilation with as C++
+option(UA_COMPILE_AS_CXX "Force compilation with a C++ compiler" OFF)
+mark_as_advanced(UA_COMPILE_AS_CXX)
+
 #####################
 #####################
 # Compiler Settings #
 # Compiler Settings #
 #####################
 #####################
@@ -161,9 +169,12 @@ endif()
 
 
 file(MAKE_DIRECTORY "${PROJECT_BINARY_DIR}/src_generated")
 file(MAKE_DIRECTORY "${PROJECT_BINARY_DIR}/src_generated")
 configure_file("include/ua_config.h.in" "${PROJECT_BINARY_DIR}/src_generated/ua_config.h")
 configure_file("include/ua_config.h.in" "${PROJECT_BINARY_DIR}/src_generated/ua_config.h")
-include_directories(${PROJECT_BINARY_DIR}/src_generated)
+include_directories(${PROJECT_BINARY_DIR}/src_generated
+                    ${PROJECT_SOURCE_DIR}/include
+                    ${PROJECT_SOURCE_DIR}/deps)
 
 
 set(exported_headers ${PROJECT_BINARY_DIR}/src_generated/ua_config.h
 set(exported_headers ${PROJECT_BINARY_DIR}/src_generated/ua_config.h
+                     ${PROJECT_SOURCE_DIR}/deps/ms_stdint.h
                      ${PROJECT_SOURCE_DIR}/include/ua_constants.h
                      ${PROJECT_SOURCE_DIR}/include/ua_constants.h
                      ${PROJECT_SOURCE_DIR}/include/ua_types.h
                      ${PROJECT_SOURCE_DIR}/include/ua_types.h
                      ${PROJECT_BINARY_DIR}/src_generated/ua_nodeids.h
                      ${PROJECT_BINARY_DIR}/src_generated/ua_nodeids.h
@@ -332,7 +343,7 @@ add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/ua_namespaceinit_g
 add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/open62541.h
 add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/open62541.h
                    PRE_BUILD
                    PRE_BUILD
                    COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tools/amalgamate.py
                    COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tools/amalgamate.py
-                           ${GIT_COMMIT_ID} ${CMAKE_CURRENT_BINARY_DIR}/open62541.h ${exported_headers}
+                           ${OPEN62541_VER_COMMIT} ${CMAKE_CURRENT_BINARY_DIR}/open62541.h ${exported_headers}
                    DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tools/amalgamate.py
                    DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tools/amalgamate.py
                            ${exported_headers}
                            ${exported_headers}
                            ${internal_headers})
                            ${internal_headers})
@@ -340,7 +351,7 @@ add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/open62541.h
 add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/open62541.c
 add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/open62541.c
                    PRE_BUILD
                    PRE_BUILD
                    COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tools/amalgamate.py
                    COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tools/amalgamate.py
-                           ${GIT_COMMIT_ID} ${CMAKE_CURRENT_BINARY_DIR}/open62541.c ${internal_headers}
+                           ${OPEN62541_VER_COMMIT} ${CMAKE_CURRENT_BINARY_DIR}/open62541.c ${internal_headers}
                            ${PROJECT_SOURCE_DIR}/src/server/ua_nodestore_hash.inc ${lib_sources}
                            ${PROJECT_SOURCE_DIR}/src/server/ua_nodestore_hash.inc ${lib_sources}
                    DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tools/amalgamate.py ${internal_headers}
                    DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tools/amalgamate.py ${internal_headers}
                            ${PROJECT_SOURCE_DIR}/src/server/ua_nodestore_hash.inc ${lib_sources})
                            ${PROJECT_SOURCE_DIR}/src/server/ua_nodestore_hash.inc ${lib_sources})
@@ -369,11 +380,17 @@ add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/nodeset.h ${PROJEC
 if(UA_ENABLE_AMALGAMATION)
 if(UA_ENABLE_AMALGAMATION)
     add_library(open62541-object OBJECT ${PROJECT_BINARY_DIR}/open62541.c ${PROJECT_BINARY_DIR}/open62541.h)
     add_library(open62541-object OBJECT ${PROJECT_BINARY_DIR}/open62541.c ${PROJECT_BINARY_DIR}/open62541.h)
     target_include_directories(open62541-object PRIVATE ${PROJECT_BINARY_DIR})
     target_include_directories(open62541-object PRIVATE ${PROJECT_BINARY_DIR})
+    if(UA_COMPILE_AS_CXX)
+        set_source_files_properties(${PROJECT_BINARY_DIR}/open62541.c PROPERTIES LANGUAGE CXX)
+    endif()
 else()
 else()
     add_definitions(-DUA_NO_AMALGAMATION)
     add_definitions(-DUA_NO_AMALGAMATION)
     add_library(open62541-object OBJECT ${lib_sources} ${internal_headers} ${exported_headers})
     add_library(open62541-object OBJECT ${lib_sources} ${internal_headers} ${exported_headers})
-    target_include_directories(open62541-object PRIVATE ${PROJECT_SOURCE_DIR}/include ${PROJECT_SOURCE_DIR}/src
-                                                        ${PROJECT_SOURCE_DIR}/plugins ${PROJECT_SOURCE_DIR}/deps)
+    target_include_directories(open62541-object PRIVATE ${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/plugins)
+
+    if(UA_COMPILE_AS_CXX)
+        set_source_files_properties(${lib_sources} PROPERTIES LANGUAGE CXX)
+    endif()
 endif()
 endif()
 add_library(open62541 $<TARGET_OBJECTS:open62541-object>)
 add_library(open62541 $<TARGET_OBJECTS:open62541-object>)
 target_link_libraries(open62541 ${open62541_LIBRARIES})
 target_link_libraries(open62541 ${open62541_LIBRARIES})
@@ -381,6 +398,9 @@ target_link_libraries(open62541 ${open62541_LIBRARIES})
 target_compile_definitions(open62541-object PRIVATE -DUA_DYNAMIC_LINKING_EXPORT)
 target_compile_definitions(open62541-object PRIVATE -DUA_DYNAMIC_LINKING_EXPORT)
 target_compile_definitions(open62541 PRIVATE -DUA_DYNAMIC_LINKING_EXPORT)
 target_compile_definitions(open62541 PRIVATE -DUA_DYNAMIC_LINKING_EXPORT)
 
 
+# Generate properly versioned shared library links on Linux
+SET_TARGET_PROPERTIES(open62541 PROPERTIES SOVERSION 0 VERSION "${OPEN62541_VER_MAJOR}.${OPEN62541_VER_MINOR}.${OPEN62541_VER_PATCH}")
+
 if(WIN32)
 if(WIN32)
     target_link_libraries(open62541 ws2_32)
     target_link_libraries(open62541 ws2_32)
 endif()
 endif()

+ 51 - 82
appveyor.yml

@@ -1,102 +1,71 @@
 version: '{build}'
 version: '{build}'
 
 
-os: Visual Studio 2015 RC
-
 clone_folder: c:\projects\open62541
 clone_folder: c:\projects\open62541
 clone_depth: 20
 clone_depth: 20
 
 
 environment:
 environment:
-  matrix:
-  - Compiler: msvc
-    Arch: x86
-  - Compiler: msvc
-    Arch: x64
-  - Compiler: mingw
-    Arch: x86
-  - Compiler: mingw
-    Arch: x64
-#    cygwin cmake stopped working on 05.07.2016 -- commented out until a fix appears
-#  - Compiler: cygwin
-#    Arch: x86
+    global:
+        CYG_ARCH: x86
+        CYG_ROOT: C:/cygwin
+        CYG_SETUP_URL: http://cygwin.com/setup-x86.exe
+        CYG_MIRROR: http://cygwin.mirror.constant.com
+        CYG_CACHE: C:\cygwin\var\cache\setup
+        CYG_BASH: C:/cygwin/bin/bash
 
 
-#
-# Initialisation prior to pulling the Mono repository
-# Attempt to ensure we don't try to convert line endings to Win32 CRLF as this will cause build to fail
-#
-init:
-  - git config --global core.autocrlf input
+    matrix:
+        - CC_NAME: MinGW Makefiles
+          CC_SHORTNAME: mingw
+          MAKE: mingw32-make
+          FORCE_CXX: OFF
+        # - CC_NAME: Visual Studio 9 2008
+        #   CC_SHORTNAME: vs2008
+        #   MAKE: msbuild open62541.sln /m
+        #   FORCE_CXX: ON
+        - CC_NAME: Visual Studio 12 2013
+          CC_SHORTNAME: vs2013
+          MAKE: msbuild open62541.sln /m
+          FORCE_CXX: OFF
+        - CC_NAME: Visual Studio 12 2013 Win64
+          CC_SHORTNAME: vs2013-x64
+          MAKE: msbuild open62541.sln /m
+          FORCE_CXX: OFF
 
 
+cache:
+  - '%CYG_CACHE%'
+
+init:
+  - git config --global core.autocrlf input # Attempt to ensure we don't try to convert line endings to Win32 CRLF as this will cause build to fail
 
 
-#
 # Install needed build dependencies
 # Install needed build dependencies
-#
 install:
 install:
   - git submodule update --init --recursive
   - git submodule update --init --recursive
+  - if not exist "%CYG_ROOT%" mkdir "%CYG_ROOT%"
+  - ps: echo "Installing Cygwin from $env:CYG_SETUP_URL to $env:CYG_ROOT/setup-x86.exe"
+  - appveyor DownloadFile %CYG_SETUP_URL% -FileName %CYG_ROOT%/setup-x86.exe
+  - ps: echo "Downloaded. Now ready to install."
+  - cmd: '"%CYG_ROOT%/setup-x86.exe" --quiet-mode --no-shortcuts --only-site -R "%CYG_ROOT%" -s "%CYG_MIRROR%" -l "%CYG_CACHE%" --packages cmake,python'
+  - cmd: '%CYG_BASH% -lc "cygcheck -dc cygwin"'
 
 
 before_build:
 before_build:
   # Workaround for CMake not wanting sh.exe on PATH for MinGW
   # Workaround for CMake not wanting sh.exe on PATH for MinGW
   - set PATH=%PATH:C:\Program Files\Git\usr\bin;=%
   - set PATH=%PATH:C:\Program Files\Git\usr\bin;=%
 
 
 build_script:
 build_script:
-  - ps: |
-        cd c:\projects\open62541
-        md build
-        cd build
-        if ($env:Compiler -eq "mingw") {
-          if ($env:Arch -eq "x64") {
-            echo "Testing MinGW64"
-            $env:Path = "C:\mingw-w64\i686-5.3.0-posix-dwarf-rt_v4-rev0\mingw32\bin;" + $env:Path
-          } else {
-            echo "Testing MinGW32"
-            $env:Path = "C:\MinGW\bin;" + $env:Path
-          }
-          cmake -DUA_BUILD_EXAMPLES:BOOL=ON -G"MinGW Makefiles" ..
-          mingw32-make -j4
-        } elseif ($env:Compiler -eq "cygwin") {
-          echo "Testing cygwin"
-          $env:Path = "C:\cygwin\bin;" + $env:Path
-          C:\cygwin\bin\bash -lc "cygcheck -dc cygwin"
-          C:\cygwin\bin\bash --login -lc "cmake.exe --version"
-          C:\cygwin\bin\bash --login -lc "cd /cygdrive/c/projects/open62541/build; cmake -DUA_BUILD_EXAMPLES:BOOL=ON -G\"Unix Makefiles\" .."
-          C:\cygwin\bin\bash --login -lc "cd /cygdrive/c/projects/open62541/build; make -j"
-        } else {
-          if ($env:Arch -eq "x64") {
-            echo "Testing MSVC with amalgamation (x64)"
-            cd ..
-            md build64
-            cd build64
-            cmake -DUA_BUILD_EXAMPLES:BOOL=ON -DUA_ENABLE_AMALGAMATION:BOOL=ON -G"Visual Studio 12 2013 Win64" ..
-            msbuild open62541.sln /m
-            copy C:\projects\open62541\build64\open62541.c C:\projects\open62541\build64\Debug\open62541.c
-            copy C:\projects\open62541\build64\open62541.h C:\projects\open62541\build64\Debug\open62541.h
-            copy C:\projects\open62541\build64\examples\server_cert.der C:\projects\open62541\build64\Debug\server_cert.der
-          } else {
-            echo "Testing MSVC without amalgamation (x86)"
-            cmake -DUA_BUILD_EXAMPLES:BOOL=ON -G"Visual Studio 12 2013" ..
-            msbuild open62541.sln /m
-            cd ..
-            Remove-Item .\build -Force -Recurse
-            md build
-            cd build
-
-            echo "Testing MSVC with amalgamation (x86)"
-            cmake -DUA_BUILD_EXAMPLES:BOOL=ON -DUA_ENABLE_AMALGAMATION:BOOL=ON -G"Visual Studio 12 2013" ..
-            msbuild open62541.sln /m
-            copy C:\projects\open62541\build\open62541.c C:\projects\open62541\build\Debug\open62541.c
-            copy C:\projects\open62541\build\open62541.h C:\projects\open62541\build\Debug\open62541.h
-            copy C:\projects\open62541\build\examples\server_cert.der C:\projects\open62541\build\Debug\server_cert.der
-          }
-        }
-        echo "Build done"
+  - cd c:\projects\open62541
+  - md build
+  - cd build
+  - echo "Testing %CC_NAME%"
+  - cmake -DUA_BUILD_EXAMPLES:BOOL=ON -DUA_COMPILE_AS_CXX:BOOL=%FORCE_CXX% -G"%CC_NAME%" ..
+  - '%MAKE%'
+  - cd ..
+  - rd /s /q build
+  - md build
+  - cd build
+  - echo "Testing %CC_NAME% with amalgamation"
+  - cmake -DUA_BUILD_EXAMPLES:BOOL=ON -DUA_ENABLE_AMALGAMATION:BOOL=ON -DUA_COMPILE_AS_CXX:BOOL=%FORCE_CXX% -G"%CC_NAME%" ..
+  - '%MAKE%'
+  - cd ..
 
 
 after_build:
 after_build:
-  - ps: |
-        if ($env:Compiler -eq "msvc") {
-          if ($env:Arch -eq "x64") {
-            7z a open62541-win64.zip C:\projects\open62541\build64\Debug\*
-            appveyor PushArtifact open62541-win64.zip
-          } else {
-            7z a open62541-win32.zip C:\projects\open62541\build\Debug\*
-            appveyor PushArtifact open62541-win32.zip
-          }
-        }
+  - 7z a open62541-%CC_SHORTNAME%.zip %APPVEYOR_BUILD_FOLDER%\build*
+  - appveyor PushArtifact open62541-%CC_SHORTNAME%.zip

+ 255 - 0
deps/ms_stdint.h

@@ -0,0 +1,255 @@
+// ISO C9x  compliant stdint.h for Microsoft Visual Studio
+// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 
+// 
+//  Copyright (c) 2006-2013 Alexander Chemeris
+// 
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// 
+//   1. Redistributions of source code must retain the above copyright notice,
+//      this list of conditions and the following disclaimer.
+// 
+//   2. Redistributions in binary form must reproduce the above copyright
+//      notice, this list of conditions and the following disclaimer in the
+//      documentation and/or other materials provided with the distribution.
+// 
+//   3. Neither the name of the product nor the names of its contributors may
+//      be used to endorse or promote products derived from this software
+//      without specific prior written permission.
+// 
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// 
+///////////////////////////////////////////////////////////////////////////////
+
+#if !defined(_MSC_VER) || _MSC_VER >= 1600 // [
+#include <stdint.h>
+#else
+
+#ifndef _MSC_STDINT_H_ // [
+#define _MSC_STDINT_H_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif
+
+#include <limits.h>
+
+// For Visual Studio 6 in C++ mode and for many Visual Studio versions when
+// compiling for ARM we should wrap <wchar.h> include with 'extern "C++" {}'
+// or compiler give many errors like this:
+//   error C2733: second C linkage of overloaded function 'wmemchr' not allowed
+#ifdef __cplusplus
+extern "C" {
+#endif
+#  include <wchar.h>
+#ifdef __cplusplus
+}
+#endif
+
+// Define _W64 macros to mark types changing their size, like intptr_t.
+#ifndef _W64
+#  if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300
+#     define _W64 __w64
+#  else
+#     define _W64
+#  endif
+#endif
+
+
+// 7.18.1 Integer types
+
+// 7.18.1.1 Exact-width integer types
+
+// Visual Studio 6 and Embedded Visual C++ 4 doesn't
+// realize that, e.g. char has the same size as __int8
+// so we give up on __intX for them.
+#if (_MSC_VER < 1300)
+   typedef signed char       int8_t;
+   typedef signed short      int16_t;
+   typedef signed int        int32_t;
+   typedef unsigned char     uint8_t;
+   typedef unsigned short    uint16_t;
+   typedef unsigned int      uint32_t;
+#else
+   typedef signed __int8     int8_t;
+   typedef signed __int16    int16_t;
+   typedef signed __int32    int32_t;
+   typedef unsigned __int8   uint8_t;
+   typedef unsigned __int16  uint16_t;
+   typedef unsigned __int32  uint32_t;
+#endif
+typedef signed __int64       int64_t;
+typedef unsigned __int64     uint64_t;
+
+
+// 7.18.1.2 Minimum-width integer types
+typedef int8_t    int_least8_t;
+typedef int16_t   int_least16_t;
+typedef int32_t   int_least32_t;
+typedef int64_t   int_least64_t;
+typedef uint8_t   uint_least8_t;
+typedef uint16_t  uint_least16_t;
+typedef uint32_t  uint_least32_t;
+typedef uint64_t  uint_least64_t;
+
+// 7.18.1.3 Fastest minimum-width integer types
+typedef int8_t    int_fast8_t;
+typedef int16_t   int_fast16_t;
+typedef int32_t   int_fast32_t;
+typedef int64_t   int_fast64_t;
+typedef uint8_t   uint_fast8_t;
+typedef uint16_t  uint_fast16_t;
+typedef uint32_t  uint_fast32_t;
+typedef uint64_t  uint_fast64_t;
+
+// 7.18.1.4 Integer types capable of holding object pointers
+#ifdef _WIN64 // [
+   typedef signed __int64    intptr_t;
+   typedef unsigned __int64  uintptr_t;
+#else // _WIN64 ][
+   typedef _W64 signed int   intptr_t;
+   typedef _W64 unsigned int uintptr_t;
+#endif // _WIN64 ]
+
+// 7.18.1.5 Greatest-width integer types
+typedef int64_t   intmax_t;
+typedef uint64_t  uintmax_t;
+
+
+// 7.18.2 Limits of specified-width integer types
+
+#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [   See footnote 220 at page 257 and footnote 221 at page 259
+
+// 7.18.2.1 Limits of exact-width integer types
+#define INT8_MIN     ((int8_t)_I8_MIN)
+#define INT8_MAX     _I8_MAX
+#define INT16_MIN    ((int16_t)_I16_MIN)
+#define INT16_MAX    _I16_MAX
+#define INT32_MIN    ((int32_t)_I32_MIN)
+#define INT32_MAX    _I32_MAX
+#define INT64_MIN    ((int64_t)_I64_MIN)
+#define INT64_MAX    _I64_MAX
+#define UINT8_MAX    _UI8_MAX
+#define UINT16_MAX   _UI16_MAX
+#define UINT32_MAX   _UI32_MAX
+#define UINT64_MAX   _UI64_MAX
+
+// 7.18.2.2 Limits of minimum-width integer types
+#define INT_LEAST8_MIN    INT8_MIN
+#define INT_LEAST8_MAX    INT8_MAX
+#define INT_LEAST16_MIN   INT16_MIN
+#define INT_LEAST16_MAX   INT16_MAX
+#define INT_LEAST32_MIN   INT32_MIN
+#define INT_LEAST32_MAX   INT32_MAX
+#define INT_LEAST64_MIN   INT64_MIN
+#define INT_LEAST64_MAX   INT64_MAX
+#define UINT_LEAST8_MAX   UINT8_MAX
+#define UINT_LEAST16_MAX  UINT16_MAX
+#define UINT_LEAST32_MAX  UINT32_MAX
+#define UINT_LEAST64_MAX  UINT64_MAX
+
+// 7.18.2.3 Limits of fastest minimum-width integer types
+#define INT_FAST8_MIN    INT8_MIN
+#define INT_FAST8_MAX    INT8_MAX
+#define INT_FAST16_MIN   INT16_MIN
+#define INT_FAST16_MAX   INT16_MAX
+#define INT_FAST32_MIN   INT32_MIN
+#define INT_FAST32_MAX   INT32_MAX
+#define INT_FAST64_MIN   INT64_MIN
+#define INT_FAST64_MAX   INT64_MAX
+#define UINT_FAST8_MAX   UINT8_MAX
+#define UINT_FAST16_MAX  UINT16_MAX
+#define UINT_FAST32_MAX  UINT32_MAX
+#define UINT_FAST64_MAX  UINT64_MAX
+
+// 7.18.2.4 Limits of integer types capable of holding object pointers
+#ifdef _WIN64 // [
+#  define INTPTR_MIN   INT64_MIN
+#  define INTPTR_MAX   INT64_MAX
+#  define UINTPTR_MAX  UINT64_MAX
+#else // _WIN64 ][
+#  define INTPTR_MIN   INT32_MIN
+#  define INTPTR_MAX   INT32_MAX
+#  define UINTPTR_MAX  UINT32_MAX
+#endif // _WIN64 ]
+
+// 7.18.2.5 Limits of greatest-width integer types
+#define INTMAX_MIN   INT64_MIN
+#define INTMAX_MAX   INT64_MAX
+#define UINTMAX_MAX  UINT64_MAX
+
+// 7.18.3 Limits of other integer types
+
+#ifdef _WIN64 // [
+#  define PTRDIFF_MIN  _I64_MIN
+#  define PTRDIFF_MAX  _I64_MAX
+#else  // _WIN64 ][
+#  define PTRDIFF_MIN  _I32_MIN
+#  define PTRDIFF_MAX  _I32_MAX
+#endif  // _WIN64 ]
+
+#define SIG_ATOMIC_MIN  INT_MIN
+#define SIG_ATOMIC_MAX  INT_MAX
+
+#ifndef SIZE_MAX // [
+#  ifdef _WIN64 // [
+#     define SIZE_MAX  _UI64_MAX
+#  else // _WIN64 ][
+#     define SIZE_MAX  _UI32_MAX
+#  endif // _WIN64 ]
+#endif // SIZE_MAX ]
+
+// WCHAR_MIN and WCHAR_MAX are also defined in <wchar.h>
+#ifndef WCHAR_MIN // [
+#  define WCHAR_MIN  0
+#endif  // WCHAR_MIN ]
+#ifndef WCHAR_MAX // [
+#  define WCHAR_MAX  _UI16_MAX
+#endif  // WCHAR_MAX ]
+
+#define WINT_MIN  0
+#define WINT_MAX  _UI16_MAX
+
+#endif // __STDC_LIMIT_MACROS ]
+
+
+// 7.18.4 Limits of other integer types
+
+#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [   See footnote 224 at page 260
+
+// 7.18.4.1 Macros for minimum-width integer constants
+
+#define INT8_C(val)  val##i8
+#define INT16_C(val) val##i16
+#define INT32_C(val) val##i32
+#define INT64_C(val) val##i64
+
+#define UINT8_C(val)  val##ui8
+#define UINT16_C(val) val##ui16
+#define UINT32_C(val) val##ui32
+#define UINT64_C(val) val##ui64
+
+// 7.18.4.2 Macros for greatest-width integer constants
+// These #ifndef's are needed to prevent collisions with <boost/cstdint.hpp>.
+// Check out Issue 9 for the details.
+#ifndef INTMAX_C //   [
+#  define INTMAX_C   INT64_C
+#endif // INTMAX_C    ]
+#ifndef UINTMAX_C //  [
+#  define UINTMAX_C  UINT64_C
+#endif // UINTMAX_C   ]
+
+#endif // __STDC_CONSTANT_MACROS ]
+
+#endif // _MSC_STDINT_H_ ]
+
+#endif // !defined(_MSC_VER) || _MSC_VER >= 1600 ]

+ 1 - 1
deps/pcg_basic.h

@@ -24,7 +24,7 @@
 #ifndef PCG_BASIC_H_INCLUDED
 #ifndef PCG_BASIC_H_INCLUDED
 #define PCG_BASIC_H_INCLUDED 1
 #define PCG_BASIC_H_INCLUDED 1
 
 
-#include <stdint.h>
+#include "ms_stdint.h"
 
 
 #if __cplusplus
 #if __cplusplus
 extern "C" {
 extern "C" {

+ 2 - 0
doc/CMakeLists.txt

@@ -53,6 +53,7 @@ add_custom_target(doc_latex ${SPHINX_EXECUTABLE}
           ${DOC_SRC_DIR}/server.rst ${DOC_SRC_DIR}/client.rst ${DOC_SRC_DIR}/client_highlevel.rst
           ${DOC_SRC_DIR}/server.rst ${DOC_SRC_DIR}/client.rst ${DOC_SRC_DIR}/client_highlevel.rst
           ${DOC_SRC_DIR}/log.rst ${DOC_SRC_DIR}/connection.rst ${DOC_SRC_DIR}/services.rst
           ${DOC_SRC_DIR}/log.rst ${DOC_SRC_DIR}/connection.rst ${DOC_SRC_DIR}/services.rst
           ${DOC_SRC_DIR}/nodestore.rst ${DOC_SRC_DIR}/information_modelling.rst
           ${DOC_SRC_DIR}/nodestore.rst ${DOC_SRC_DIR}/information_modelling.rst
+          ${DOC_SRC_DIR}/protocol.rst
   COMMENT "Building LaTeX sources for documentation with Sphinx")
   COMMENT "Building LaTeX sources for documentation with Sphinx")
 add_dependencies(doc_latex open62541)
 add_dependencies(doc_latex open62541)
 
 
@@ -70,5 +71,6 @@ add_custom_target(doc ${SPHINX_EXECUTABLE}
           ${DOC_SRC_DIR}/server.rst ${DOC_SRC_DIR}/client.rst ${DOC_SRC_DIR}/client_highlevel.rst
           ${DOC_SRC_DIR}/server.rst ${DOC_SRC_DIR}/client.rst ${DOC_SRC_DIR}/client_highlevel.rst
           ${DOC_SRC_DIR}/log.rst ${DOC_SRC_DIR}/connection.rst ${DOC_SRC_DIR}/services.rst
           ${DOC_SRC_DIR}/log.rst ${DOC_SRC_DIR}/connection.rst ${DOC_SRC_DIR}/services.rst
           ${DOC_SRC_DIR}/nodestore.rst ${DOC_SRC_DIR}/information_modelling.rst
           ${DOC_SRC_DIR}/nodestore.rst ${DOC_SRC_DIR}/information_modelling.rst
+          ${DOC_SRC_DIR}/protocol.rst
   COMMENT "Building HTML documentation with Sphinx")
   COMMENT "Building HTML documentation with Sphinx")
 add_dependencies(doc open62541)
 add_dependencies(doc open62541)

+ 1 - 1
doc/building.rst

@@ -88,7 +88,7 @@ Building on OS X
 Follow Ubuntu instructions without the ``apt-get`` commands as these are taken care of by the above packages.
 Follow Ubuntu instructions without the ``apt-get`` commands as these are taken care of by the above packages.
 
 
 Building on OpenBSD
 Building on OpenBSD
--------------------
+^^^^^^^^^^^^^^^^^^^
 The procedure below works on OpenBSD 5.8 with gcc version 4.8.4, cmake version 3.2.3 and Python version 2.7.10.
 The procedure below works on OpenBSD 5.8 with gcc version 4.8.4, cmake version 3.2.3 and Python version 2.7.10.
 
 
 - Install a recent gcc, python and cmake:
 - Install a recent gcc, python and cmake:

+ 4 - 2
doc/conf.py

@@ -31,6 +31,8 @@ import shlex
 # ones.
 # ones.
 extensions = ['sphinx.ext.graphviz']
 extensions = ['sphinx.ext.graphviz']
 
 
+numfig = True
+
 # Add any paths that contain templates here, relative to this directory.
 # Add any paths that contain templates here, relative to this directory.
 templates_path = ['_templates']
 templates_path = ['_templates']
 
 
@@ -55,9 +57,9 @@ author = u'The open62541 authors'
 # built documents.
 # built documents.
 #
 #
 # The short X.Y version.
 # The short X.Y version.
-version = "${GIT_RELEASE_ID}"
+version = "${OPEN62541_VER_MAJOR}.${OPEN62541_VER_MINOR}"
 # The full version, including alpha/beta/rc tags.
 # The full version, including alpha/beta/rc tags.
-release = "${GIT_COMMIT_ID}"
+release = "${OPEN62541_VER_MAJOR}.${OPEN62541_VER_MINOR}.${OPEN62541_VER_PATCH}${OPEN62541_VER_LABEL}"
 # The full version, including alpha/beta/rc tags.
 # The full version, including alpha/beta/rc tags.
 
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # The language for content autogenerated by Sphinx. Refer to documentation

File diff suppressed because it is too large
+ 48 - 9
doc/index.rst


+ 136 - 0
doc/protocol.rst

@@ -0,0 +1,136 @@
+.. _protocol:
+
+Protocol
+========
+
+In this section, we give an overview on the OPC UA binary protocol. We focus on
+binary since that is what has been implemented in open62541. The TCP-based
+binary protocol is by far the most common transport layer for OPC UA. The
+general concepts also translate to HTTP and SOAP-based communication defined in
+the standard. Communication in OPC UA is best understood by starting with the
+following key principles:
+
+Request / Response
+  All communication is based on the Request/Response pattern. Only clients can
+  send a request to a server. And servers can only send responses to a request.
+  Usually, the server is hosted on the (physical) device, such as a sensor or a
+  machine tool.
+
+Asynchronous Responses
+  A server does not have to immediately respond to requests and responses may be
+  sent in a different order. This keeps the server responsive when it takes time
+  until a specific request has been processed (e.g. a method call or when
+  reading from a sensor with delay). Furthermore, Subscriptions (aka
+  push-notifications) are implemented via special requests where the response is
+  delayed until a notification is generated.
+
+Establishing a Connection
+-------------------------
+
+A client-server connection in OPC UA consists of three nested levels: The raw
+connection, a SecureChannel and the Session. For full details, see Part 6 of the
+OPC UA standard.
+
+Raw Connection
+  The raw connection is created by opening a TCP connection to the corresponding
+  hostname and port and an initial HEL/ACK handshake. The handshake establishes
+  the basic settings of the connection, such as the maximum message length.
+
+SecureChannel
+  SecureChannels are created on top of the raw TCP connection. A SecureChannel
+  is established with an *OpenSecureChannel* request and response message pair.
+  **Attention!** Even though a SecureChannel is mandatory, encryption might
+  still be disabled. The *SecurityMode* of a SecureChannel can be either
+  ``None``, ``Sign``, or ``SignAndEncrypt``. As of version 0.2 of open6251,
+  message signing and encryption is still under ongoing development.
+
+  With message signing or encryption enabled, the *OpenSecureChannel* messages
+  are encrypted using an asymmetric encryption algorithm (public-key
+  cryptography) [#key-mgmnt]_. As part of the *OpenSecureChannel* messages,
+  client and server establish a common secret over an initially unsecure
+  channel. For subsequent messages, the common secret is used for symmetric
+  encryption, which has the advantage of being much faster.
+
+  Different *SecurityPolicies* -- defined in part 7 of the OPC UA standard --
+  specify the algorithms for asymmetric and symmetric encryption, encryption key
+  lengths, hash functions for message signing, and so on. Example
+  SecurityPolicies are ``None`` for transmission of cleartext and
+  ``Basic256Sha256`` which mandates a variant of RSA with SHA256 certificate
+  hashing for asymmetric encryption and AES256 for symmetric encryption.
+
+  The possible SecurityPolicies of a server are described with a list of
+  *Endpoints*. An endpoint jointly defines the SecurityMode, SecurityPolicy and
+  means for authenticating a session (discussed in the next section) in order to
+  connect to a certain server. The *GetEndpoints* service returns a list of
+  available endpoints. This service can usually be invoked without a session and
+  from an unencrypted SecureChannel. This allows clients to first discover
+  available endpoints and then use an appropriate SecurityPolicy that might be
+  required to open a session.
+
+Session
+  Sessions are created on top of a SecureChannel. This ensures that users may
+  authenticate without sending their credentials, such as username and password,
+  in cleartext. Currently defined authentication mechanisms are anonymous login,
+  username/password, Kerberos and x509 certificates. The latter requires that
+  the request message is accompanied by a signature to prove that the sender is
+  in posession of the private key with which the certificate was created.
+
+  There are two message exchanges required to establish a session:
+  *CreateSession* and *ActicateSession*. The ActivateSession service can be used
+  to switch an existing session to a different SecureChannel. This is important,
+  for example when the connection broke down and the existing session is
+  reused with a new SecureChannel.
+
+.. [#key-mgmnt] This entails that the client and server exchange so-called
+   public keys. The public keys might come with a certificate from a key-signing
+   authority or be verified against an external key repository. But we will not
+   discuss certificate management in detail in this section.
+
+Structure of a protocol message
+-------------------------------
+
+For the following introduction to the structure of OPC UA protocol messages,
+consider the example OPC UA binary conversation, recorded and displayed with the
+`Wireshark <https://www.wireshark.org/>`_ tool, shown in :numref:`ua-wireshark`.
+
+.. _ua-wireshark:
+
+.. figure:: ua-wireshark.png
+   :figwidth: 100 %
+   :alt: OPC UA conversation in Wireshark
+
+   OPC UA conversation displayed in Wireshark
+
+The top part of the Wireshark window shows the messages from the conversation in
+order. The green line contains the applied filter. Here, we want to see the OPC
+UA protocol messages only. The first messages (from TCP packets 49 to 56) show
+the client opening an unencrypted SecureChannel and retrieving the server's
+endpoints. Then, starting with packet 63, a new connection and SecureChannel are
+created in conformance with one of the endpoints. On top of this SecureChannel,
+the client can then create and activate a session. The following *ReadRequest*
+message is selected and covered in more detail in the bottom windows.
+
+The bottom left window shows the structure of the selected *ReadRequest*
+message. The purpose of the message is invoking the *Read* :ref:`service
+<services>`. The message is structured into a header and a message body. Note
+that we do not consider encryption or signing of messages here.
+
+Message Header
+  As stated before, OPC UA defines an asynchronous protocol. So responses may be
+  out of order. The message header contains some basic information, such as the
+  length of the message, as well as necessary information to relate messages to
+  a SecureChannel and each request to the corresponding response. "Chunking"
+  refers to the splitting and reassembling of messages that are longer than the
+  maximum network packet size.
+
+Message Body
+  Every OPC UA :ref:`service <services>` has a signature in the form of a
+  request and response data structure. These are defined according to the OPC UA
+  protocol :ref:`type system <types>`. See especially the :ref:`auto-generated
+  type definitions<generated-types>` for the data types corresponding to service
+  requests and responses. The message body begins with the identifier of the
+  following data type. Then, the main payload of the message follows.
+
+The bottom right window shows the binary payload of the selected *ReadRequest*
+message. The message header is highlighted in light-grey. The message body in
+blue highlighting shows the encoded *ReadRequest* data structure.

+ 1 - 0
doc/toc.rst

@@ -6,6 +6,7 @@ open62541 Documentation
    index
    index
    building
    building
    tutorials
    tutorials
+   protocol
    types
    types
    information_modelling
    information_modelling
    services
    services

BIN
doc/ua-wireshark.png


+ 8 - 8
include/ua_client_highlevel.h

@@ -168,18 +168,18 @@ UA_Client_readArrayDimensionsAttribute(UA_Client *client, const UA_NodeId nodeId
 
 
 static UA_INLINE UA_StatusCode
 static UA_INLINE UA_StatusCode
 UA_Client_readAccessLevelAttribute(UA_Client *client, const UA_NodeId nodeId,
 UA_Client_readAccessLevelAttribute(UA_Client *client, const UA_NodeId nodeId,
-                                   UA_UInt32 *outAccessLevel) {
+                                   UA_Byte *outAccessLevel) {
     return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_ACCESSLEVEL,
     return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_ACCESSLEVEL,
-                                     outAccessLevel, &UA_TYPES[UA_TYPES_UINT32]);
+                                     outAccessLevel, &UA_TYPES[UA_TYPES_BYTE]);
 }
 }
 
 
 static UA_INLINE UA_StatusCode
 static UA_INLINE UA_StatusCode
 UA_Client_readUserAccessLevelAttribute(UA_Client *client, const UA_NodeId nodeId,
 UA_Client_readUserAccessLevelAttribute(UA_Client *client, const UA_NodeId nodeId,
-                                       UA_UInt32 *outUserAccessLevel) {
+                                       UA_Byte *outUserAccessLevel) {
     return __UA_Client_readAttribute(client, &nodeId,
     return __UA_Client_readAttribute(client, &nodeId,
                                      UA_ATTRIBUTEID_USERACCESSLEVEL,
                                      UA_ATTRIBUTEID_USERACCESSLEVEL,
                                      outUserAccessLevel,
                                      outUserAccessLevel,
-                                     &UA_TYPES[UA_TYPES_UINT32]);
+                                     &UA_TYPES[UA_TYPES_BYTE]);
 }
 }
 
 
 static UA_INLINE UA_StatusCode
 static UA_INLINE UA_StatusCode
@@ -349,18 +349,18 @@ UA_Client_writeArrayDimensionsAttribute(UA_Client *client, const UA_NodeId nodeI
 
 
 static UA_INLINE UA_StatusCode
 static UA_INLINE UA_StatusCode
 UA_Client_writeAccessLevelAttribute(UA_Client *client, const UA_NodeId nodeId,
 UA_Client_writeAccessLevelAttribute(UA_Client *client, const UA_NodeId nodeId,
-                                    const UA_UInt32 *newAccessLevel) {
+                                    const UA_Byte *newAccessLevel) {
     return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_ACCESSLEVEL,
     return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_ACCESSLEVEL,
-                                      newAccessLevel, &UA_TYPES[UA_TYPES_UINT32]);
+                                      newAccessLevel, &UA_TYPES[UA_TYPES_BYTE]);
 }
 }
 
 
 static UA_INLINE UA_StatusCode
 static UA_INLINE UA_StatusCode
 UA_Client_writeUserAccessLevelAttribute(UA_Client *client, const UA_NodeId nodeId,
 UA_Client_writeUserAccessLevelAttribute(UA_Client *client, const UA_NodeId nodeId,
-                                        const UA_UInt32 *newUserAccessLevel) {
+                                        const UA_Byte *newUserAccessLevel) {
     return __UA_Client_writeAttribute(client, &nodeId,
     return __UA_Client_writeAttribute(client, &nodeId,
                                       UA_ATTRIBUTEID_USERACCESSLEVEL,
                                       UA_ATTRIBUTEID_USERACCESSLEVEL,
                                       newUserAccessLevel,
                                       newUserAccessLevel,
-                                      &UA_TYPES[UA_TYPES_UINT32]);
+                                      &UA_TYPES[UA_TYPES_BYTE]);
 }
 }
 
 
 static UA_INLINE UA_StatusCode
 static UA_INLINE UA_StatusCode

+ 50 - 105
include/ua_config.h.in

@@ -23,7 +23,11 @@ extern "C" {
 /**
 /**
  * Library Version
  * Library Version
  * --------------- */
  * --------------- */
-#define UA_GIT_COMMIT_ID "${GIT_COMMIT_ID}"
+#define UA_OPEN62541_VER_MAJOR ${OPEN62541_VER_MAJOR}
+#define UA_OPEN62541_VER_MINOR ${OPEN62541_VER_MINOR}
+#define UA_OPEN62541_VER_PATCH ${OPEN62541_VER_PATCH}
+#define UA_OPEN62541_VER_LABEL "${OPEN62541_VER_LABEL}" /* Release candidate label, etc. */
+#define UA_OPEN62541_VER_COMMIT "${OPEN62541_VER_COMMIT}"
 
 
 /**
 /**
  * Options
  * Options
@@ -55,8 +59,51 @@ extern "C" {
 #ifndef _DEFAULT_SOURCE
 #ifndef _DEFAULT_SOURCE
 # define _DEFAULT_SOURCE
 # define _DEFAULT_SOURCE
 #endif
 #endif
+
 #include <stddef.h>
 #include <stddef.h>
-#include <stdint.h>
+#include "ms_stdint.h" /* Includes stdint.h or workaround for older Visual Studios */
+
+#ifndef __cplusplus
+# include <stdbool.h> /* C99 Boolean */
+/* Manual Boolean:
+typedef uint8_t bool;
+#define true 1
+#define false 0 */
+#endif
+
+/* Manually define some function on libc */
+#ifndef UA_ENABLE_EMBEDDED_LIBC
+# include <string.h>
+#else
+  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);
+#endif
+
+/* Memory Management */
+#include <stdlib.h>
+#ifdef _WIN32
+# ifndef __clang__
+#  include <malloc.h>
+# endif
+#endif
+
+#define UA_free(ptr) free(ptr)
+#define UA_malloc(size) malloc(size)
+#define UA_calloc(num, size) calloc(num, size)
+#define UA_realloc(ptr, size) realloc(ptr, size)
+
+#ifndef NO_ALLOCA
+# if defined(__GNUC__) || defined(__clang__)
+#  define UA_alloca(size) __builtin_alloca (size)
+# elif defined(_WIN32)
+#  define UA_alloca(SIZE) _alloca(SIZE)
+# else
+#  include <alloca.h>
+#  define UA_alloca(SIZE) alloca(SIZE)
+# endif
+#endif
 
 
 /**
 /**
  * Function Export
  * Function Export
@@ -123,96 +170,6 @@ extern "C" {
 # define UA_FUNC_ATTR_WARN_UNUSED_RESULT
 # define UA_FUNC_ATTR_WARN_UNUSED_RESULT
 #endif
 #endif
 
 
-/**
- * Memory Management
- * -----------------
- * Replace the macros for custom memory allocators if necessary */
-#include <stdlib.h>
-#ifdef _WIN32
-# ifndef __clang__
-#  include <malloc.h>
-# endif
-#endif
-
-#define UA_free(ptr) free(ptr)
-#define UA_malloc(size) malloc(size)
-#define UA_calloc(num, size) calloc(num, size)
-#define UA_realloc(ptr, size) realloc(ptr, size)
-
-#ifndef NO_ALLOCA
-# if defined(__GNUC__) || defined(__clang__)
-#  define UA_alloca(size) __builtin_alloca (size)
-# elif defined(_WIN32)
-#  define UA_alloca(SIZE) _alloca(SIZE)
-# else
-#  include <alloca.h>
-#  define UA_alloca(SIZE) alloca(SIZE)
-# endif
-#endif
-
-/**
- * Atomic Operations
- * -----------------
- * Atomic operations that synchronize across processor cores (for
- * multithreading). Only the inline-functions defined next are used. Replace
- * with architecture-specific operations if necessary. */
-
-#ifndef UA_ENABLE_MULTITHREADING
-# define UA_atomic_sync()
-#else
-# ifdef _MSC_VER /* Visual Studio */
-#  define UA_atomic_sync() _ReadWriteBarrier()
-# else /* GCC/Clang */
-#  define UA_atomic_sync() __sync_synchronize()
-# endif
-#endif
-
-static UA_INLINE void *
-UA_atomic_xchg(void * volatile * addr, void *newptr) {
-#ifndef UA_ENABLE_MULTITHREADING
-    void *old = *addr;
-    *addr = newptr;
-    return old;
-#else
-# ifdef _MSC_VER /* Visual Studio */
-    return _InterlockedExchangePointer(addr, newptr);
-# else /* GCC/Clang */
-    return __sync_lock_test_and_set(addr, newptr);
-# endif
-#endif
-}
-
-static UA_INLINE void *
-UA_atomic_cmpxchg(void * volatile * addr, void *expected, void *newptr) {
-#ifndef UA_ENABLE_MULTITHREADING
-    void *old = *addr;
-    if(old == expected) {
-        *addr = newptr;
-    }
-    return old;
-#else
-# ifdef _MSC_VER /* Visual Studio */
-    return _InterlockedCompareExchangePointer(addr, expected, newptr);
-# else /* GCC/Clang */
-    return __sync_val_compare_and_swap(addr, expected, newptr);
-# endif
-#endif
-}
-
-static UA_INLINE uint32_t
-UA_atomic_add(volatile uint32_t *addr, uint32_t increase) {
-#ifndef UA_ENABLE_MULTITHREADING
-    *addr += increase;
-    return *addr;
-#else
-# ifdef _MSC_VER /* Visual Studio */
-    return _InterlockedExchangeAdd(addr, increase) + increase;
-# else /* GCC/Clang */
-    return __sync_add_and_fetch(addr, increase);
-# endif
-#endif
-}
-
 /**
 /**
  * Binary Encoding Overlays
  * Binary Encoding Overlays
  * ------------------------
  * ------------------------
@@ -224,7 +181,7 @@ UA_atomic_add(volatile uint32_t *addr, uint32_t increase) {
  * Integer Endianness
  * Integer Endianness
  * ^^^^^^^^^^^^^^^^^^ */
  * ^^^^^^^^^^^^^^^^^^ */
 #if defined(_WIN32) || (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
 #if defined(_WIN32) || (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
-                        (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) /* little endian detected */
+                        (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
 # define UA_BINARY_OVERLAYABLE_INTEGER true
 # define UA_BINARY_OVERLAYABLE_INTEGER true
 #elif defined(__ANDROID__) /* Andoid */
 #elif defined(__ANDROID__) /* Andoid */
 # include <endian.h>
 # include <endian.h>
@@ -282,18 +239,6 @@ UA_atomic_add(volatile uint32_t *addr, uint32_t increase) {
 # define UA_BINARY_OVERLAYABLE_FLOAT false
 # define UA_BINARY_OVERLAYABLE_FLOAT false
 #endif
 #endif
 
 
-/**
- * Embed unavailable libc functions
- * -------------------------------- */
-#ifdef UA_ENABLE_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
-
 #ifdef __cplusplus
 #ifdef __cplusplus
 } // extern "C"
 } // extern "C"
 #endif
 #endif

+ 26 - 5
include/ua_server.h

@@ -171,10 +171,11 @@ typedef struct {
     UA_UInt32Range lifeTimeCountLimits;
     UA_UInt32Range lifeTimeCountLimits;
     UA_UInt32Range keepAliveCountLimits;
     UA_UInt32Range keepAliveCountLimits;
     UA_UInt32 maxNotificationsPerPublish;
     UA_UInt32 maxNotificationsPerPublish;
+    UA_UInt32 maxRetransmissionQueueSize; /* 0 -> unlimited size */
 
 
     /* Limits for MonitoredItems */
     /* Limits for MonitoredItems */
     UA_DoubleRange samplingIntervalLimits;
     UA_DoubleRange samplingIntervalLimits;
-    UA_UInt32Range queueSizeLimits;
+    UA_UInt32Range queueSizeLimits; /* Negotiated with the client */
 
 
 #ifdef UA_ENABLE_DISCOVERY
 #ifdef UA_ENABLE_DISCOVERY
     /* Discovery */
     /* Discovery */
@@ -185,7 +186,6 @@ typedef struct {
     // The server will still be removed depending on the state of the semaphore file.
     // The server will still be removed depending on the state of the semaphore file.
     UA_UInt32 discoveryCleanupTimeout;
     UA_UInt32 discoveryCleanupTimeout;
 #endif
 #endif
-
 } UA_ServerConfig;
 } UA_ServerConfig;
 
 
 /* Add a new namespace to the server. Returns the index of the new namespace */
 /* Add a new namespace to the server. Returns the index of the new namespace */
@@ -393,7 +393,7 @@ UA_Server_readArrayDimensions(UA_Server *server, const UA_NodeId nodeId,
 
 
 static UA_INLINE UA_StatusCode
 static UA_INLINE UA_StatusCode
 UA_Server_readAccessLevel(UA_Server *server, const UA_NodeId nodeId,
 UA_Server_readAccessLevel(UA_Server *server, const UA_NodeId nodeId,
-                          UA_UInt32 *outAccessLevel) {
+                          UA_Byte *outAccessLevel) {
     return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_ACCESSLEVEL,
     return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_ACCESSLEVEL,
                             outAccessLevel);
                             outAccessLevel);
 }
 }
@@ -535,9 +535,9 @@ UA_Server_writeArrayDimensions(UA_Server *server, const UA_NodeId nodeId,
 
 
 static UA_INLINE UA_StatusCode
 static UA_INLINE UA_StatusCode
 UA_Server_writeAccessLevel(UA_Server *server, const UA_NodeId nodeId,
 UA_Server_writeAccessLevel(UA_Server *server, const UA_NodeId nodeId,
-                           const UA_UInt32 accessLevel) {
+                           const UA_Byte accessLevel) {
     return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_ACCESSLEVEL,
     return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_ACCESSLEVEL,
-                             &UA_TYPES[UA_TYPES_UINT32], &accessLevel);
+                             &UA_TYPES[UA_TYPES_BYTE], &accessLevel);
 }
 }
 
 
 static UA_INLINE UA_StatusCode
 static UA_INLINE UA_StatusCode
@@ -693,9 +693,30 @@ UA_Server_setVariableNode_dataSource(UA_Server *server, const UA_NodeId nodeId,
  * Value Callbacks can be attached to variable and variable type nodes. If
  * Value Callbacks can be attached to variable and variable type nodes. If
  * not-null, they are called before reading and after writing respectively. */
  * not-null, they are called before reading and after writing respectively. */
 typedef struct {
 typedef struct {
+    /* Pointer to user-provided data for the callback */
     void *handle;
     void *handle;
+
+    /* Called before the value attribute is read. It is possible to write into the
+     * value attribute during onRead (using the write service). The node is
+     * re-opened afterwards so that changes are considered in the following read
+     * operation.
+     *
+     * @param handle Points to user-provided data for the callback.
+     * @param nodeid The identifier of the node.
+     * @param data Points to the current node value.
+     * @param range Points to the numeric range the client wants to read from
+     *        (or NULL). */
     void (*onRead)(void *handle, const UA_NodeId nodeid,
     void (*onRead)(void *handle, const UA_NodeId nodeid,
                    const UA_Variant *data, const UA_NumericRange *range);
                    const UA_Variant *data, const UA_NumericRange *range);
+
+    /* Called after writing the value attribute. The node is re-opened after
+     * writing so that the new value is visible in the callback.
+     *
+     * @param handle Points to user-provided data for the callback.
+     * @param nodeid The identifier of the node.
+     * @param data Points to the current node value (after writing).
+     * @param range Points to the numeric range the client wants to write to (or
+     *        NULL). */
     void (*onWrite)(void *handle, const UA_NodeId nodeid,
     void (*onWrite)(void *handle, const UA_NodeId nodeid,
                     const UA_Variant *data, const UA_NumericRange *range);
                     const UA_Variant *data, const UA_NumericRange *range);
 } UA_ValueCallback;
 } UA_ValueCallback;

+ 22 - 8
include/ua_types.h

@@ -20,9 +20,10 @@ extern "C" {
 
 
 #include "ua_config.h"
 #include "ua_config.h"
 #include "ua_constants.h"
 #include "ua_constants.h"
-#include <stdbool.h>
 
 
 /**
 /**
+ * .. _types:
+ *
  * Data Types
  * Data Types
  * ==========
  * ==========
  *
  *
@@ -508,15 +509,17 @@ typedef struct UA_NumericRange UA_NumericRange;
 
 
 #define UA_EMPTY_ARRAY_SENTINEL ((void*)0x01)
 #define UA_EMPTY_ARRAY_SENTINEL ((void*)0x01)
 
 
-typedef struct {
-    const UA_DataType *type;      /* The data type description */
-    enum {
+typedef enum {
         UA_VARIANT_DATA,          /* The data has the same lifecycle as the
         UA_VARIANT_DATA,          /* The data has the same lifecycle as the
                                      variant */
                                      variant */
         UA_VARIANT_DATA_NODELETE, /* The data is "borrowed" by the variant and
         UA_VARIANT_DATA_NODELETE, /* The data is "borrowed" by the variant and
                                      shall not be deleted at the end of the
                                      shall not be deleted at the end of the
                                      variant's lifecycle. */
                                      variant's lifecycle. */
-    } storageType;
+} UA_VariantStorageType;
+
+typedef struct {
+    const UA_DataType *type;      /* The data type description */
+    UA_VariantStorageType storageType;
     size_t arrayLength;           /* The number of elements in the data array */
     size_t arrayLength;           /* The number of elements in the data array */
     void *data;                   /* Points to the scalar or array data */
     void *data;                   /* Points to the scalar or array data */
     size_t arrayDimensionsSize;   /* The number of dimensions */
     size_t arrayDimensionsSize;   /* The number of dimensions */
@@ -652,8 +655,7 @@ UA_Variant_setRangeCopy(UA_Variant *v, const void *array,
  * unknown to the receiver. See the section on :ref:`generic-types` on how types
  * unknown to the receiver. See the section on :ref:`generic-types` on how types
  * are described. If the received data type is unkown, the encoded string and
  * are described. If the received data type is unkown, the encoded string and
  * target NodeId is stored instead of the decoded value. */
  * target NodeId is stored instead of the decoded value. */
-typedef struct {
-    enum {
+typedef enum {
         UA_EXTENSIONOBJECT_ENCODED_NOBODY     = 0,
         UA_EXTENSIONOBJECT_ENCODED_NOBODY     = 0,
         UA_EXTENSIONOBJECT_ENCODED_BYTESTRING = 1,
         UA_EXTENSIONOBJECT_ENCODED_BYTESTRING = 1,
         UA_EXTENSIONOBJECT_ENCODED_XML        = 2,
         UA_EXTENSIONOBJECT_ENCODED_XML        = 2,
@@ -661,7 +663,10 @@ typedef struct {
         UA_EXTENSIONOBJECT_DECODED_NODELETE   = 4 /* Don't delete the content
         UA_EXTENSIONOBJECT_DECODED_NODELETE   = 4 /* Don't delete the content
                                                      together with the
                                                      together with the
                                                      ExtensionObject */
                                                      ExtensionObject */
-    } encoding;
+} UA_ExtensionObjectEncoding;
+
+typedef struct {
+    UA_ExtensionObjectEncoding encoding;
     union {
     union {
         struct {
         struct {
             UA_NodeId typeId;   /* The nodeid of the datatype */
             UA_NodeId typeId;   /* The nodeid of the datatype */
@@ -764,6 +769,15 @@ struct UA_DataType {
     UA_DataTypeMember *members;
     UA_DataTypeMember *members;
 };
 };
 
 
+/**
+ * Builtin data types can be accessed as UA_TYPES[UA_TYPES_XXX], where XXX is
+ * the name of the data type. If only the NodeId of a type is known, use the
+ * following method to retrieve the data type description. */
+/* Returns the data type description for the type's identifier or NULL if no
+ * matching data type was found. */
+const UA_DataType UA_EXPORT *
+UA_findDataType(const UA_NodeId *typeId);
+
 /** The following functions are used for generic handling of data types. */
 /** The following functions are used for generic handling of data types. */
 
 
 /* Allocates and initializes a variable of type dataType
 /* Allocates and initializes a variable of type dataType

+ 4 - 4
plugins/ua_accesscontrol_default.c

@@ -10,8 +10,8 @@
 #define USERNAME_POLICY "open62541-username-policy"
 #define USERNAME_POLICY "open62541-username-policy"
 
 
 #define UA_STRING_STATIC(s) {sizeof(s)-1, (UA_Byte*)s}
 #define UA_STRING_STATIC(s) {sizeof(s)-1, (UA_Byte*)s}
-const UA_String ap = UA_STRING_STATIC(ANONYMOUS_POLICY);
-const UA_String up = UA_STRING_STATIC(USERNAME_POLICY);
+const UA_String anonymous_policy = UA_STRING_STATIC(ANONYMOUS_POLICY);
+const UA_String username_policy = UA_STRING_STATIC(USERNAME_POLICY);
 
 
 UA_StatusCode
 UA_StatusCode
 activateSession_default(const UA_NodeId *sessionId, const UA_ExtensionObject *userIdentityToken,
 activateSession_default(const UA_NodeId *sessionId, const UA_ExtensionObject *userIdentityToken,
@@ -28,7 +28,7 @@ activateSession_default(const UA_NodeId *sessionId, const UA_ExtensionObject *us
         /* Compatibility notice: Siemens OPC Scout v10 provides an empty
         /* Compatibility notice: Siemens OPC Scout v10 provides an empty
          * policyId. This is not compliant. For compatibility we will assume
          * policyId. This is not compliant. For compatibility we will assume
          * that empty policyId == ANONYMOUS_POLICY */
          * that empty policyId == ANONYMOUS_POLICY */
-        if(token->policyId.data && !UA_String_equal(&token->policyId, &ap))
+        if(token->policyId.data && !UA_String_equal(&token->policyId, &anonymous_policy))
             return UA_STATUSCODE_BADIDENTITYTOKENINVALID;
             return UA_STATUSCODE_BADIDENTITYTOKENINVALID;
 
 
         *sessionHandle = NULL;
         *sessionHandle = NULL;
@@ -39,7 +39,7 @@ activateSession_default(const UA_NodeId *sessionId, const UA_ExtensionObject *us
     if(enableUsernamePasswordLogin &&
     if(enableUsernamePasswordLogin &&
        userIdentityToken->content.decoded.type == &UA_TYPES[UA_TYPES_USERNAMEIDENTITYTOKEN]) {
        userIdentityToken->content.decoded.type == &UA_TYPES[UA_TYPES_USERNAMEIDENTITYTOKEN]) {
         const UA_UserNameIdentityToken *token = userIdentityToken->content.decoded.data;
         const UA_UserNameIdentityToken *token = userIdentityToken->content.decoded.data;
-        if(!UA_String_equal(&token->policyId, &up))
+        if(!UA_String_equal(&token->policyId, &username_policy))
             return UA_STATUSCODE_BADIDENTITYTOKENINVALID;
             return UA_STATUSCODE_BADIDENTITYTOKENINVALID;
 
 
         /* empty username and password */
         /* empty username and password */

+ 9 - 1
plugins/ua_config_standard.c

@@ -30,6 +30,9 @@ const UA_EXPORT UA_ConnectionConfig UA_ConnectionConfig_standard = {
 
 
 #define UA_STRING_STATIC(s) {sizeof(s)-1, (UA_Byte*)s}
 #define UA_STRING_STATIC(s) {sizeof(s)-1, (UA_Byte*)s}
 #define UA_STRING_STATIC_NULL {0, NULL}
 #define UA_STRING_STATIC_NULL {0, NULL}
+#define STRINGIFY(arg) #arg
+#define VERSION(MAJOR, MINOR, PATCH, LABEL) \
+    STRINGIFY(MAJOR) "." STRINGIFY(MINOR) "." STRINGIFY(PATCH) LABEL
 
 
 /* Access Control. The following definitions are defined as "extern" in
 /* Access Control. The following definitions are defined as "extern" in
    ua_accesscontrol_default.h */
    ua_accesscontrol_default.h */
@@ -51,7 +54,10 @@ const UA_EXPORT UA_ServerConfig UA_ServerConfig_standard = {
         .productUri = UA_STRING_STATIC(PRODUCT_URI),
         .productUri = UA_STRING_STATIC(PRODUCT_URI),
         .manufacturerName = UA_STRING_STATIC(MANUFACTURER_NAME),
         .manufacturerName = UA_STRING_STATIC(MANUFACTURER_NAME),
         .productName = UA_STRING_STATIC(PRODUCT_NAME),
         .productName = UA_STRING_STATIC(PRODUCT_NAME),
-        .softwareVersion = UA_STRING_STATIC(UA_GIT_COMMIT_ID),
+        .softwareVersion = UA_STRING_STATIC(VERSION(UA_OPEN62541_VER_MAJOR,
+                                                    UA_OPEN62541_VER_MINOR,
+                                                    UA_OPEN62541_VER_PATCH,
+                                                    UA_OPEN62541_VER_LABEL)),
         .buildNumber = UA_STRING_STATIC(__DATE__ " " __TIME__),
         .buildNumber = UA_STRING_STATIC(__DATE__ " " __TIME__),
         .buildDate = 0 },
         .buildDate = 0 },
     .applicationDescription = {
     .applicationDescription = {
@@ -99,6 +105,7 @@ const UA_EXPORT UA_ServerConfig UA_ServerConfig_standard = {
     .lifeTimeCountLimits = { .max = 15000, .min = 3 },
     .lifeTimeCountLimits = { .max = 15000, .min = 3 },
     .keepAliveCountLimits = { .max = 100, .min = 1 },
     .keepAliveCountLimits = { .max = 100, .min = 1 },
     .maxNotificationsPerPublish = 1000,
     .maxNotificationsPerPublish = 1000,
+    .maxRetransmissionQueueSize = 0, /* unlimited */
 
 
     /* Limits for MonitoredItems */
     /* Limits for MonitoredItems */
     .samplingIntervalLimits = { .min = 50.0, .max = 24.0 * 3600.0 * 1000.0 },
     .samplingIntervalLimits = { .min = 50.0, .max = 24.0 * 3600.0 * 1000.0 },
@@ -126,6 +133,7 @@ const UA_EXPORT UA_ClientConfig UA_ClientConfig_standard = {
     },
     },
     .connectionFunc = UA_ClientConnectionTCP
     .connectionFunc = UA_ClientConnectionTCP
 };
 };
+
 /****************************************/
 /****************************************/
 /* Default Client Subscription Settings */
 /* Default Client Subscription Settings */
 /****************************************/
 /****************************************/

+ 3 - 1
plugins/ua_network_tcp.c

@@ -18,7 +18,9 @@
 #include <string.h> // memset
 #include <string.h> // memset
 #include <errno.h>
 #include <errno.h>
 #ifdef _WIN32
 #ifdef _WIN32
-# include <malloc.h>
+# ifndef __clang__
+#  include <malloc.h>
+# endif
 /* Fix redefinition of SLIST_ENTRY on mingw winnt.h */
 /* Fix redefinition of SLIST_ENTRY on mingw winnt.h */
 # ifdef SLIST_ENTRY
 # ifdef SLIST_ENTRY
 #  undef SLIST_ENTRY
 #  undef SLIST_ENTRY

+ 12 - 12
src/client/ua_client.c

@@ -38,9 +38,9 @@ static void UA_Client_init(UA_Client* client, UA_ClientConfig config) {
     memset(client, 0, sizeof(UA_Client));
     memset(client, 0, sizeof(UA_Client));
 
 
     client->state = UA_CLIENTSTATE_READY;
     client->state = UA_CLIENTSTATE_READY;
-    client->connection = UA_malloc(sizeof(UA_Connection));
+    client->connection = (UA_Connection*)UA_malloc(sizeof(UA_Connection));
     memset(client->connection, 0, sizeof(UA_Connection));
     memset(client->connection, 0, sizeof(UA_Connection));
-    client->channel = UA_malloc(sizeof(UA_SecureChannel));
+    client->channel = (UA_SecureChannel*)UA_malloc(sizeof(UA_SecureChannel));
     UA_SecureChannel_init(client->channel);
     UA_SecureChannel_init(client->channel);
     client->channel->connection = client->connection;
     client->channel->connection = client->connection;
     client->authenticationMethod = UA_CLIENTAUTHENTICATION_NONE;
     client->authenticationMethod = UA_CLIENTAUTHENTICATION_NONE;
@@ -52,7 +52,7 @@ static void UA_Client_init(UA_Client* client, UA_ClientConfig config) {
 }
 }
 
 
 UA_Client * UA_Client_new(UA_ClientConfig config) {
 UA_Client * UA_Client_new(UA_ClientConfig config) {
-    UA_Client *client = UA_calloc(1, sizeof(UA_Client));
+    UA_Client *client = (UA_Client*)UA_calloc(1, sizeof(UA_Client));
     if(!client)
     if(!client)
         return NULL;
         return NULL;
 
 
@@ -367,14 +367,14 @@ static UA_StatusCode ActivateSession(UA_Client *client) {
 
 
     //manual ExtensionObject encoding of the identityToken
     //manual ExtensionObject encoding of the identityToken
     if(client->authenticationMethod == UA_CLIENTAUTHENTICATION_NONE) {
     if(client->authenticationMethod == UA_CLIENTAUTHENTICATION_NONE) {
-        UA_AnonymousIdentityToken* identityToken = UA_malloc(sizeof(UA_AnonymousIdentityToken));
+        UA_AnonymousIdentityToken* identityToken = UA_AnonymousIdentityToken_new();
         UA_AnonymousIdentityToken_init(identityToken);
         UA_AnonymousIdentityToken_init(identityToken);
         UA_String_copy(&client->token.policyId, &identityToken->policyId);
         UA_String_copy(&client->token.policyId, &identityToken->policyId);
         request.userIdentityToken.encoding = UA_EXTENSIONOBJECT_DECODED;
         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_ANONYMOUSIDENTITYTOKEN];
         request.userIdentityToken.content.decoded.data = identityToken;
         request.userIdentityToken.content.decoded.data = identityToken;
     } else {
     } else {
-        UA_UserNameIdentityToken* identityToken = UA_malloc(sizeof(UA_UserNameIdentityToken));
+        UA_UserNameIdentityToken* identityToken = UA_UserNameIdentityToken_new();
         UA_UserNameIdentityToken_init(identityToken);
         UA_UserNameIdentityToken_init(identityToken);
         UA_String_copy(&client->token.policyId, &identityToken->policyId);
         UA_String_copy(&client->token.policyId, &identityToken->policyId);
         UA_String_copy(&client->username, &identityToken->userName);
         UA_String_copy(&client->username, &identityToken->userName);
@@ -718,6 +718,11 @@ static void
 processServiceResponse(struct ResponseDescription *rd, UA_SecureChannel *channel,
 processServiceResponse(struct ResponseDescription *rd, UA_SecureChannel *channel,
                        UA_MessageType messageType, UA_UInt32 requestId,
                        UA_MessageType messageType, UA_UInt32 requestId,
                        UA_ByteString *message) {
                        UA_ByteString *message) {
+	const UA_NodeId expectedNodeId =
+        UA_NODEID_NUMERIC(0, rd->responseType->binaryEncodingId);
+    const UA_NodeId serviceFaultNodeId =
+        UA_NODEID_NUMERIC(0, UA_TYPES[UA_TYPES_SERVICEFAULT].binaryEncodingId);
+
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     UA_ResponseHeader *respHeader = (UA_ResponseHeader*)rd->response;
     UA_ResponseHeader *respHeader = (UA_ResponseHeader*)rd->response;
     rd->processed = true;
     rd->processed = true;
@@ -741,10 +746,6 @@ processServiceResponse(struct ResponseDescription *rd, UA_SecureChannel *channel
     }
     }
 
 
     /* Check that the response type matches */
     /* Check that the response type matches */
-    const UA_NodeId expectedNodeId =
-        UA_NODEID_NUMERIC(0, rd->responseType->binaryEncodingId);
-    const UA_NodeId serviceFaultNodeId =
-        UA_NODEID_NUMERIC(0, UA_TYPES[UA_TYPES_SERVICEFAULT].binaryEncodingId);
     size_t offset = 0;
     size_t offset = 0;
     UA_NodeId responseId;
     UA_NodeId responseId;
     retval = UA_NodeId_decodeBinary(message, &offset, &responseId);
     retval = UA_NodeId_decodeBinary(message, &offset, &responseId);
@@ -799,7 +800,7 @@ __UA_Client_Service(UA_Client *client, const void *r, const UA_DataType *request
 
 
     /* Handling request parameters */
     /* Handling request parameters */
     //here const *r is 'violated'
     //here const *r is 'violated'
-    UA_RequestHeader *request = (void*)(uintptr_t)r;
+    UA_RequestHeader *request = (UA_RequestHeader*)(uintptr_t)r;
     UA_NodeId_copy(&client->authenticationToken, &request->authenticationToken);
     UA_NodeId_copy(&client->authenticationToken, &request->authenticationToken);
     request->timestamp = UA_DateTime_now();
     request->timestamp = UA_DateTime_now();
     request->requestHandle = ++client->requestHandle;
     request->requestHandle = ++client->requestHandle;
@@ -821,8 +822,7 @@ __UA_Client_Service(UA_Client *client, const void *r, const UA_DataType *request
 
 
     /* Prepare the response and the structure we give into processServiceResponse */
     /* Prepare the response and the structure we give into processServiceResponse */
     UA_init(response, responseType);
     UA_init(response, responseType);
-    struct ResponseDescription rd = (struct ResponseDescription){
-        client, false, requestId, response, responseType};
+    struct ResponseDescription rd = {client, false, requestId, response, responseType};
 
 
     /* Retrieve the response */
     /* Retrieve the response */
     UA_DateTime maxDate = UA_DateTime_nowMonotonic() + (client->config.timeout * UA_MSEC_TO_DATETIME);
     UA_DateTime maxDate = UA_DateTime_nowMonotonic() + (client->config.timeout * UA_MSEC_TO_DATETIME);

+ 24 - 10
src/server/ua_nodes.h

@@ -24,10 +24,9 @@ extern "C" {
  * Reference are triples of the form ``(source-nodeid, referencetype-nodeid,
  * Reference are triples of the form ``(source-nodeid, referencetype-nodeid,
  * target-nodeid)``. An example reference between nodes is a
  * target-nodeid)``. An example reference between nodes is a
  * ``hasTypeDefinition`` reference between a Variable and its VariableType. Some
  * ``hasTypeDefinition`` reference between a Variable and its VariableType. Some
- * ReferenceTypes are *hierarchic* and must not form *directed loops*. Every
- * node (except ``Root``) must have at least one hierarchic reference to a
- * parent. See the section on :ref:`ReferenceTypes <referencetypenode>` for more
- * details on possible references and their semantics.
+ * ReferenceTypes are *hierarchic* and must not form *directed loops*. See the
+ * section on :ref:`ReferenceTypes <referencetypenode>` for more details on
+ * possible references and their semantics.
  *
  *
  * The structures defined in this section are *not user-facing*. The interaction
  * The structures defined in this section are *not user-facing*. The interaction
  * with the information model is possible only via the OPC UA :ref:`services`.
  * with the information model is possible only via the OPC UA :ref:`services`.
@@ -250,11 +249,8 @@ typedef struct {
  *   source and target node
  *   source and target node
  *
  *
  * The figure below shows the hierarchy of the standard ReferenceTypes (arrows
  * The figure below shows the hierarchy of the standard ReferenceTypes (arrows
- * indicate a ``hasSubType`` relation). Please refer to Part 3 of the OPC UA
- * specification for the full semantics of each ReferenceType. The ReferenceType
- * hierarchy can be extended with user-defined ReferenceTypes. Many Companion
- * Specifications for OPC UA define new ReferenceTypes to be used in their
- * domain of interest.
+ * indicate a ``hasSubType`` relation). Refer to Part 3 of the OPC UA
+ * specification for the full semantics of each ReferenceType.
  *
  *
  * .. graphviz::
  * .. graphviz::
  *
  *
@@ -320,7 +316,25 @@ typedef struct {
  *    {rank=same alwaysgeneratesevent hasproperty}
  *    {rank=same alwaysgeneratesevent hasproperty}
  *
  *
  *    }
  *    }
- */
+ *
+ * The ReferenceType hierarchy can be extended with user-defined ReferenceTypes.
+ * Many Companion Specifications for OPC UA define new ReferenceTypes to be used
+ * in their domain of interest.
+ *
+ * For the following example of custom ReferenceTypes, we attempt to model the
+ * structure of a technical system. For this, we introduce two custom
+ * ReferenceTypes. First, the hierarchical ``contains`` ReferenceType indicates
+ * that a system (represented by an OPC UA object) contains a component (or
+ * subsystem). This gives rise to a tree-structure of containment relations. For
+ * example, the motor (object) is contained in the car and the crankshaft is
+ * contained in the motor. Second, the symmetric ``connectedTo`` ReferenceType
+ * indicates that two components are connected. For example, the motor's
+ * crankshaft is connected to the gear box. Connections are independent of the
+ * containment hierarchy and can induce a general graph-structure. Further
+ * subtypes of ``connectedTo`` could be used to differentiate between physical,
+ * electrical and information related connections. A client can then learn the
+ * layout of a (physical) system represented in an OPC UA information model
+ * based on a common understanding of just two custom reference types. */
 typedef struct {
 typedef struct {
     UA_NODE_BASEATTRIBUTES
     UA_NODE_BASEATTRIBUTES
     UA_Boolean isAbstract;
     UA_Boolean isAbstract;

+ 4 - 2
src/server/ua_server.c

@@ -179,6 +179,7 @@ addNodeInternalWithType(UA_Server *server, UA_Node *node, const UA_NodeId parent
 
 
 // delete any children of an instance without touching the object itself
 // delete any children of an instance without touching the object itself
 static void deleteInstanceChildren(UA_Server *server, UA_NodeId *objectNodeId) {
 static void deleteInstanceChildren(UA_Server *server, UA_NodeId *objectNodeId) {
+    UA_RCU_LOCK();
   UA_BrowseDescription bDes;
   UA_BrowseDescription bDes;
   UA_BrowseDescription_init(&bDes);
   UA_BrowseDescription_init(&bDes);
   UA_NodeId_copy(objectNodeId, &bDes.nodeId );
   UA_NodeId_copy(objectNodeId, &bDes.nodeId );
@@ -208,6 +209,7 @@ static void deleteInstanceChildren(UA_Server *server, UA_NodeId *objectNodeId) {
     }
     }
   }
   }
   UA_BrowseResult_deleteMembers(&bRes); 
   UA_BrowseResult_deleteMembers(&bRes); 
+  UA_RCU_UNLOCK();
 }
 }
 
 
 /**********/
 /**********/
@@ -431,7 +433,7 @@ GetMonitoredItems(void *handle, const UA_NodeId *objectId,
 
 
     UA_UInt32 sizeOfOutput = 0;
     UA_UInt32 sizeOfOutput = 0;
     UA_MonitoredItem* monitoredItem;
     UA_MonitoredItem* monitoredItem;
-    LIST_FOREACH(monitoredItem, &subscription->MonitoredItems, listEntry) {
+    LIST_FOREACH(monitoredItem, &subscription->monitoredItems, listEntry) {
         ++sizeOfOutput;
         ++sizeOfOutput;
     }
     }
     if(sizeOfOutput==0)
     if(sizeOfOutput==0)
@@ -440,7 +442,7 @@ GetMonitoredItems(void *handle, const UA_NodeId *objectId,
     UA_UInt32* clientHandles = UA_Array_new(sizeOfOutput, &UA_TYPES[UA_TYPES_UINT32]);
     UA_UInt32* clientHandles = UA_Array_new(sizeOfOutput, &UA_TYPES[UA_TYPES_UINT32]);
     UA_UInt32* serverHandles = UA_Array_new(sizeOfOutput, &UA_TYPES[UA_TYPES_UINT32]);
     UA_UInt32* serverHandles = UA_Array_new(sizeOfOutput, &UA_TYPES[UA_TYPES_UINT32]);
     UA_UInt32 i = 0;
     UA_UInt32 i = 0;
-    LIST_FOREACH(monitoredItem, &subscription->MonitoredItems, listEntry) {
+    LIST_FOREACH(monitoredItem, &subscription->monitoredItems, listEntry) {
         clientHandles[i] = monitoredItem->clientHandle;
         clientHandles[i] = monitoredItem->clientHandle;
         serverHandles[i] = monitoredItem->itemId;
         serverHandles[i] = monitoredItem->itemId;
         ++i;
         ++i;

+ 2 - 2
src/server/ua_server_internal.h

@@ -192,7 +192,7 @@ getNodeType(UA_Server *server, const UA_Node *node);
 /***************************************/
 /***************************************/
 
 
 UA_StatusCode
 UA_StatusCode
-readValueAttribute(const UA_VariableNode *vn, UA_DataValue *v);
+readValueAttribute(UA_Server *server, const UA_VariableNode *vn, UA_DataValue *v);
 
 
 UA_StatusCode
 UA_StatusCode
 typeCheckValue(UA_Server *server, const UA_NodeId *variableDataTypeId,
 typeCheckValue(UA_Server *server, const UA_NodeId *variableDataTypeId,
@@ -211,7 +211,7 @@ compatibleArrayDimensions(size_t constraintArrayDimensionsSize,
                           const UA_UInt32 *testArrayDimensions);
                           const UA_UInt32 *testArrayDimensions);
 
 
 UA_StatusCode
 UA_StatusCode
-writeValueRankAttribute(UA_VariableNode *node, UA_Int32 valueRank,
+writeValueRankAttribute(UA_Server *server, UA_VariableNode *node, UA_Int32 valueRank,
                         UA_Int32 constraintValueRank);
                         UA_Int32 constraintValueRank);
 
 
 UA_StatusCode
 UA_StatusCode

+ 78 - 35
src/server/ua_server_worker.c

@@ -30,6 +30,10 @@
  * [1] Fraser, K. 2003. Practical lock freedom. Ph.D. thesis. Computer Laboratory, University of Cambridge.
  * [1] Fraser, K. 2003. Practical lock freedom. Ph.D. thesis. Computer Laboratory, University of Cambridge.
  * [2] Hart, T. E., McKenney, P. E., Brown, A. D., & Walpole, J. (2007). Performance of memory reclamation
  * [2] Hart, T. E., McKenney, P. E., Brown, A. D., & Walpole, J. (2007). Performance of memory reclamation
  *     for lockless synchronization. Journal of Parallel and Distributed Computing, 67(12), 1270-1285.
  *     for lockless synchronization. Journal of Parallel and Distributed Computing, 67(12), 1270-1285.
+ *
+ * Future Plans: Use work-stealing to load-balance between cores.
+ * [3] Le, Nhat Minh, et al. "Correct and efficient work-stealing for weak
+ *     memory models." ACM SIGPLAN Notices. Vol. 48. No. 8. ACM, 2013.
  */
  */
 
 
 #define MAXTIMEOUT 50 // max timeout in millisec until the next main loop iteration
 #define MAXTIMEOUT 50 // max timeout in millisec until the next main loop iteration
@@ -153,21 +157,31 @@ struct RepeatedJob {
 
 
 /* internal. call only from the main loop. */
 /* internal. call only from the main loop. */
 static void
 static void
-addRepeatedJob(UA_Server *server, struct RepeatedJob * UA_RESTRICT rj)
-{
-    /* search for matching entry */
-    struct RepeatedJob *lastRj = NULL; /* Add after this entry or at LIST_HEAD if NULL */
-    struct RepeatedJob *tempRj = LIST_FIRST(&server->repeatedJobs);
-    while(tempRj) {
-        if(tempRj->nextTime > rj->nextTime)
+addRepeatedJob(UA_Server *server, struct RepeatedJob * UA_RESTRICT rj) {
+    /* Search for the best position on the repeatedJobs sorted list. The goal is
+     * to have many repeated jobs with the same repetition interval in a
+     * "block". This helps to reduce the (linear) search to find the next entry
+     * in the repeatedJobs list when dispatching the repeated jobs.
+     * For this, we search between "nexttime_max - 1s" and "nexttime_max" for
+     * entries with the same repetition interval and adjust the "nexttime".
+     * Otherwise, add entry after the first element before "nexttime_max". */
+    UA_DateTime nextTime_max = UA_DateTime_nowMonotonic() + (UA_Int64) rj->interval;
+
+    struct RepeatedJob *afterRj = NULL;
+    struct RepeatedJob *tmpRj;
+    LIST_FOREACH(tmpRj, &server->repeatedJobs, next) {
+        if(tmpRj->nextTime >= nextTime_max)
             break;
             break;
-        lastRj = tempRj;
-        tempRj = LIST_NEXT(lastRj, next);
+        if(tmpRj->interval == rj->interval &&
+           tmpRj->nextTime > (nextTime_max - UA_SEC_TO_DATETIME))
+            nextTime_max = tmpRj->nextTime; /* break in the next iteration */
+        afterRj = tmpRj;
     }
     }
 
 
     /* add the repeated job */
     /* add the repeated job */
-    if(lastRj)
-        LIST_INSERT_AFTER(lastRj, rj, next);
+    rj->nextTime = nextTime_max;
+    if(afterRj)
+        LIST_INSERT_AFTER(afterRj, rj, next);
     else
     else
         LIST_INSERT_HEAD(&server->repeatedJobs, rj, next);
         LIST_INSERT_HEAD(&server->repeatedJobs, rj, next);
 }
 }
@@ -184,7 +198,8 @@ UA_Server_addRepeatedJob(UA_Server *server, UA_Job job,
     struct RepeatedJob *rj = UA_malloc(sizeof(struct RepeatedJob));
     struct RepeatedJob *rj = UA_malloc(sizeof(struct RepeatedJob));
     if(!rj)
     if(!rj)
         return UA_STATUSCODE_BADOUTOFMEMORY;
         return UA_STATUSCODE_BADOUTOFMEMORY;
-    rj->nextTime = UA_DateTime_nowMonotonic() + (UA_Int64) interval;
+    /* done inside addRepeatedJob:
+     * rj->nextTime = UA_DateTime_nowMonotonic() + interval; */
     rj->interval = interval;
     rj->interval = interval;
     rj->id = UA_Guid_random();
     rj->id = UA_Guid_random();
     rj->job = job;
     rj->job = job;
@@ -209,10 +224,12 @@ UA_Server_addRepeatedJob(UA_Server *server, UA_Job job,
     return UA_STATUSCODE_GOOD;
     return UA_STATUSCODE_GOOD;
 }
 }
 
 
-/* Returns the next datetime when a repeated job is scheduled */
+/* - Dispatches all repeated jobs that have timed out
+ * - Reinserts dispatched job at their new position in the sorted list
+ * - Returns the next datetime when a repeated job is scheduled */
 static UA_DateTime
 static UA_DateTime
-processRepeatedJobs(UA_Server *server, UA_DateTime current) {
-    /* Find the last job that is executed after this iteration */
+processRepeatedJobs(UA_Server *server, UA_DateTime current, UA_Boolean *dispatched) {
+    /* Find the last job that is executed in this iteration */
     struct RepeatedJob *lastNow = NULL, *tmp;
     struct RepeatedJob *lastNow = NULL, *tmp;
     LIST_FOREACH(tmp, &server->repeatedJobs, next) {
     LIST_FOREACH(tmp, &server->repeatedJobs, next) {
         if(tmp->nextTime > current)
         if(tmp->nextTime > current)
@@ -220,23 +237,28 @@ processRepeatedJobs(UA_Server *server, UA_DateTime current) {
         lastNow = tmp;
         lastNow = tmp;
     }
     }
 
 
-    /* Iterate over the list of elements (sorted according to the next execution timestamp) */
+    /* Keep pointer to the previously dispatched job to avoid linear search for
+     * "batched" jobs with the same nexttime and interval */
+    struct RepeatedJob tmp_last;
+    tmp_last.nextTime = current-1; /* never matches. just to avoid if(last_added && ...) */
+    struct RepeatedJob *last_dispatched = &tmp_last;
+
+    /* Iterate over the list of elements (sorted according to the nextTime timestamp) */
     struct RepeatedJob *rj, *tmp_rj;
     struct RepeatedJob *rj, *tmp_rj;
     LIST_FOREACH_SAFE(rj, &server->repeatedJobs, next, tmp_rj) {
     LIST_FOREACH_SAFE(rj, &server->repeatedJobs, next, tmp_rj) {
         if(rj->nextTime > current)
         if(rj->nextTime > current)
             break;
             break;
 
 
-        UA_assert(lastNow); /* at least one element at the current time */
-
         /* Dispatch/process job */
         /* Dispatch/process job */
 #ifdef UA_ENABLE_MULTITHREADING
 #ifdef UA_ENABLE_MULTITHREADING
         dispatchJob(server, &rj->job);
         dispatchJob(server, &rj->job);
+        *dispatched = true;
 #else
 #else
         struct RepeatedJob **previousNext = rj->next.le_prev;
         struct RepeatedJob **previousNext = rj->next.le_prev;
         processJob(server, &rj->job);
         processJob(server, &rj->job);
         /* See if the current job was deleted during processJob. That means the
         /* See if the current job was deleted during processJob. That means the
-           le_next field of the previous repeated job (could also be the list
-           head) does no longer point to the current repeated job */
+         * le_next field of the previous repeated job (could also be the list
+         * head) does no longer point to the current repeated job */
         if((void*)*previousNext != (void*)rj) {
         if((void*)*previousNext != (void*)rj) {
             UA_LOG_DEBUG(server->config.logger, UA_LOGCATEGORY_SERVER,
             UA_LOG_DEBUG(server->config.logger, UA_LOGCATEGORY_SERVER,
                          "The current repeated job removed itself");
                          "The current repeated job removed itself");
@@ -246,23 +268,40 @@ processRepeatedJobs(UA_Server *server, UA_DateTime current) {
 
 
         /* Set the time for the next execution */
         /* Set the time for the next execution */
         rj->nextTime += (UA_Int64)rj->interval;
         rj->nextTime += (UA_Int64)rj->interval;
+
+        /* Prevent an infinite loop when the repeated jobs took more time than
+         * rj->interval */
         if(rj->nextTime < current)
         if(rj->nextTime < current)
-            rj->nextTime = current + 1; /* prevent to rerun the job right now
-                                           when the repeated jobs took more time
-                                           than rj->interval */
-
-        /* Keep the list sorted */
-        struct RepeatedJob *prev_rj = lastNow;
-        while(true) {
-            struct RepeatedJob *n = LIST_NEXT(prev_rj, next);
-            if(!n || n->nextTime >= rj->nextTime)
-                break;
-            prev_rj = n;
+            rj->nextTime = current + 1;
+
+        /* Find new position for rj to keep the list sorted */
+        struct RepeatedJob *prev_rj;
+        if(last_dispatched->nextTime == rj->nextTime) {
+            /* We "batch" repeatedJobs with the same interval in
+             * addRepeatedJobs. So this might occur quite often. */
+            UA_assert(last_dispatched != &tmp_last);
+            prev_rj = last_dispatched;
+        } else {
+            /* Find the position by a linear search starting at the first
+             * possible job */
+            UA_assert(lastNow); /* Not NULL. Otherwise, we never reach this point. */
+            prev_rj = lastNow;
+            while(true) {
+                struct RepeatedJob *n = LIST_NEXT(prev_rj, next);
+                if(!n || n->nextTime >= rj->nextTime)
+                    break;
+                prev_rj = n;
+            }
         }
         }
+
+        /* Add entry */
         if(prev_rj != rj) {
         if(prev_rj != rj) {
             LIST_REMOVE(rj, next);
             LIST_REMOVE(rj, next);
             LIST_INSERT_AFTER(prev_rj, rj, next);
             LIST_INSERT_AFTER(prev_rj, rj, next);
         }
         }
+
+        /* Update last_dispatched and loop */
+        last_dispatched = rj;
     }
     }
 
 
     /* Check if the next repeated job is sooner than the usual timeout */
     /* Check if the next repeated job is sooner than the usual timeout */
@@ -556,7 +595,8 @@ UA_UInt16 UA_Server_run_iterate(UA_Server *server, UA_Boolean waitInternal) {
 #endif
 #endif
     /* Process repeated work */
     /* Process repeated work */
     UA_DateTime now = UA_DateTime_nowMonotonic();
     UA_DateTime now = UA_DateTime_nowMonotonic();
-    UA_DateTime nextRepeated = processRepeatedJobs(server, now);
+    UA_Boolean dispatched = false; /* to wake up worker threads */
+    UA_DateTime nextRepeated = processRepeatedJobs(server, now, &dispatched);
 
 
     UA_UInt16 timeout = 0;
     UA_UInt16 timeout = 0;
     if(waitInternal)
     if(waitInternal)
@@ -591,18 +631,21 @@ UA_UInt16 UA_Server_run_iterate(UA_Server *server, UA_Boolean waitInternal) {
         for(size_t j = 0; j < jobsSize; ++j) {
         for(size_t j = 0; j < jobsSize; ++j) {
 #ifdef UA_ENABLE_MULTITHREADING
 #ifdef UA_ENABLE_MULTITHREADING
             dispatchJob(server, &jobs[j]);
             dispatchJob(server, &jobs[j]);
+            dispatched = true;
 #else
 #else
             processJob(server, &jobs[j]);
             processJob(server, &jobs[j]);
 #endif
 #endif
         }
         }
 
 
-        if(jobsSize > 0) {
 #ifdef UA_ENABLE_MULTITHREADING
 #ifdef UA_ENABLE_MULTITHREADING
-            /* Wake up worker threads */
+        /* Wake up worker threads */
+        if(dispatched)
             pthread_cond_broadcast(&server->dispatchQueue_condition);
             pthread_cond_broadcast(&server->dispatchQueue_condition);
 #endif
 #endif
+
+        /* Clean up jobs list */
+        if(jobsSize > 0)
             UA_free(jobs);
             UA_free(jobs);
-        }
     }
     }
 
 
 #ifndef UA_ENABLE_MULTITHREADING
 #ifndef UA_ENABLE_MULTITHREADING

+ 2 - 7
src/server/ua_services.h

@@ -28,8 +28,8 @@ extern "C" {
  * are exposed to end users. Please see part 4 of the OPC UA standard for the
  * are exposed to end users. Please see part 4 of the OPC UA standard for the
  * authoritative definition of the service and their behaviour. */
  * authoritative definition of the service and their behaviour. */
 /* Most services take as input the server, the current session and pointers to
 /* Most services take as input the server, the current session and pointers to
-   the request and response structures. Possible error codes are returned as
-   part of the response. */
+ * the request and response structures. Possible error codes are returned as
+ * part of the response. */
 typedef void (*UA_Service)(UA_Server*, UA_Session*,
 typedef void (*UA_Service)(UA_Server*, UA_Session*,
                            const void *request, void *response);
                            const void *request, void *response);
 
 
@@ -61,7 +61,6 @@ void Service_RegisterServer(UA_Server *server, UA_Session *session,
  * This Service Set defines Services used to open a communication channel that
  * This Service Set defines Services used to open a communication channel that
  * ensures the confidentiality and Integrity of all Messages exchanged with the
  * ensures the confidentiality and Integrity of all Messages exchanged with the
  * Server. */
  * Server. */
-
 /* Open or renew a SecureChannel that can be used to ensure Confidentiality and
 /* Open or renew a SecureChannel that can be used to ensure Confidentiality and
  * Integrity for Message exchange during a Session. */
  * Integrity for Message exchange during a Session. */
 void Service_OpenSecureChannel(UA_Server *server, UA_Connection *connection,
 void Service_OpenSecureChannel(UA_Server *server, UA_Connection *connection,
@@ -76,7 +75,6 @@ void Service_CloseSecureChannel(UA_Server *server, UA_SecureChannel *channel);
  * -------------------
  * -------------------
  * This Service Set defines Services for an application layer connection
  * This Service Set defines Services for an application layer connection
  * establishment in the context of a Session. */
  * establishment in the context of a Session. */
-
 /* Used by an OPC UA Client to create a Session and the Server returns two
 /* Used by an OPC UA Client to create a Session and the Server returns two
  * values which uniquely identify the Session. The first value is the sessionId
  * values which uniquely identify the Session. The first value is the sessionId
  * which is used to identify the Session in the audit logs and in the Server's
  * which is used to identify the Session in the audit logs and in the Server's
@@ -110,7 +108,6 @@ void Service_CloseSession(UA_Server *server, UA_Session *session,
  * References between them. All added Nodes continue to exist in the
  * References between them. All added Nodes continue to exist in the
  * AddressSpace even if the Client that created them disconnects from the
  * AddressSpace even if the Client that created them disconnects from the
  * Server. */
  * Server. */
-
 /* Used to add one or more Nodes into the AddressSpace hierarchy. */
 /* Used to add one or more Nodes into the AddressSpace hierarchy. */
 void Service_AddNodes(UA_Server *server, UA_Session *session,
 void Service_AddNodes(UA_Server *server, UA_Session *session,
                       const UA_AddNodesRequest *request,
                       const UA_AddNodesRequest *request,
@@ -138,7 +135,6 @@ void Service_DeleteReferences(UA_Server *server, UA_Session *session,
  * ----------------
  * ----------------
  * Clients use the browse Services of the View Service Set to navigate through
  * Clients use the browse Services of the View Service Set to navigate through
  * the AddressSpace or through a View which is a subset of the AddressSpace. */
  * the AddressSpace or through a View which is a subset of the AddressSpace. */
-
 /* Used to discover the References of a specified Node. The browse can be
 /* Used to discover the References of a specified Node. The browse can be
  * further limited by the use of a View. This Browse Service also supports a
  * further limited by the use of a View. This Browse Service also supports a
  * primitive filtering capability. */
  * primitive filtering capability. */
@@ -191,7 +187,6 @@ void Service_UnregisterNodes(UA_Server *server, UA_Session *session,
  * ---------------------
  * ---------------------
  * This Service Set provides Services to access Attributes that are part of
  * This Service Set provides Services to access Attributes that are part of
  * Nodes. */
  * Nodes. */
-
 /* Used to read one or more Attributes of one or more Nodes. For constructed
 /* Used to read one or more Attributes of one or more Nodes. For constructed
  * Attribute values whose elements are indexed, such as an array, this Service
  * Attribute values whose elements are indexed, such as an array, this Service
  * allows Clients to read the entire set of indexed values as a composite, to
  * allows Clients to read the entire set of indexed values as a composite, to

+ 183 - 161
src/server/ua_services_attribute.c

@@ -35,15 +35,6 @@ typeEquivalence(const UA_DataType *t) {
     return TYPE_EQUIVALENCE_NONE;
     return TYPE_EQUIVALENCE_NONE;
 }
 }
 
 
-static const UA_DataType *
-findDataType(const UA_NodeId *typeId) {
-    for(size_t i = 0; i < UA_TYPES_COUNT; ++i) {
-        if(UA_TYPES[i].typeId.identifier.numeric == typeId->identifier.numeric)
-            return &UA_TYPES[i];
-    }
-    return NULL;
-}
-
 /* Test whether a valurank and the given arraydimensions are compatible. zero
 /* Test whether a valurank and the given arraydimensions are compatible. zero
  * array dimensions indicate a scalar */
  * array dimensions indicate a scalar */
 static UA_StatusCode
 static UA_StatusCode
@@ -103,6 +94,43 @@ compatibleArrayDimensions(size_t constraintArrayDimensionsSize,
     return UA_STATUSCODE_GOOD;
     return UA_STATUSCODE_GOOD;
 }
 }
 
 
+/* Returns the pointer to a datavalue with a possibly transformed type to match
+   the description */
+static const UA_Variant *
+convertToMatchingValue(UA_Server *server, const UA_Variant *value,
+                       const UA_NodeId *targetDataTypeId, UA_Variant *editableValue) {
+    const UA_DataType *targetDataType = UA_findDataType(targetDataTypeId);
+    if(!targetDataType)
+        return NULL;
+
+    /* A string is written to a byte array. the valuerank and array
+       dimensions are checked later */
+    if(targetDataType == &UA_TYPES[UA_TYPES_BYTE] &&
+       value->type == &UA_TYPES[UA_TYPES_BYTESTRING] &&
+       UA_Variant_isScalar(value)) {
+        UA_ByteString *str = (UA_ByteString*)value->data;
+        editableValue->storageType = UA_VARIANT_DATA_NODELETE;
+        editableValue->type = &UA_TYPES[UA_TYPES_BYTE];
+        editableValue->arrayLength = str->length;
+        editableValue->data = str->data;
+        return editableValue;
+    }
+
+    /* An enum was sent as an int32, or an opaque type as a bytestring. This
+     * is detected with the typeIndex indicating the "true" datatype. */
+    enum type_equivalence te1 = typeEquivalence(targetDataType);
+    enum type_equivalence te2 = typeEquivalence(value->type);
+    if(te1 != TYPE_EQUIVALENCE_NONE && te1 == te2) {
+        *editableValue = *value;
+        editableValue->storageType = UA_VARIANT_DATA_NODELETE;
+        editableValue->type = targetDataType;
+        return editableValue;
+    }
+
+    /* No more possible equivalencies */
+    return NULL;
+}
+
 /* Test whether the value matches a variable definition given by
 /* Test whether the value matches a variable definition given by
  * - datatype
  * - datatype
  * - valueranke
  * - valueranke
@@ -111,77 +139,51 @@ compatibleArrayDimensions(size_t constraintArrayDimensionsSize,
  * byte array to bytestring or uint32 to some enum. If editableValue is non-NULL,
  * byte array to bytestring or uint32 to some enum. If editableValue is non-NULL,
  * we try to create a matching variant that points to the original data. */
  * we try to create a matching variant that points to the original data. */
 UA_StatusCode
 UA_StatusCode
-typeCheckValue(UA_Server *server, const UA_NodeId *variableDataTypeId,
-               UA_Int32 variableValueRank, size_t variableArrayDimensionsSize,
-               const UA_UInt32 *variableArrayDimensions, const UA_Variant *value,
+typeCheckValue(UA_Server *server, const UA_NodeId *targetDataTypeId,
+               UA_Int32 targetValueRank, size_t targetArrayDimensionsSize,
+               const UA_UInt32 *targetArrayDimensions, const UA_Variant *value,
                const UA_NumericRange *range, UA_Variant *editableValue) {
                const UA_NumericRange *range, UA_Variant *editableValue) {
-    /* No content is only allowed for BaseDataType */
-    const UA_NodeId *valueDataTypeId;
+    /* Empty variant is only allowed for BaseDataType */
     UA_NodeId basedatatype = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATATYPE);
     UA_NodeId basedatatype = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATATYPE);
-    if(value->type)
-        valueDataTypeId = &value->type->typeId;
-    else
-        valueDataTypeId = &basedatatype;
-    
-    /* See if the types match. The nodeid on the wire may be != the nodeid in
-     * the node for opaque types, enums and bytestrings. value contains the
-     * correct type definition after the following paragraph */
-    if(!UA_NodeId_equal(valueDataTypeId, variableDataTypeId)) {
-        /* contains the value a subtype of the required type? */
-        const UA_NodeId subtypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE);
-        if(isNodeInTree(server->nodestore, valueDataTypeId,
-                        variableDataTypeId, &subtypeId, 1))
+    if(!value->type) {
+        if(UA_NodeId_equal(targetDataTypeId, &basedatatype))
             goto check_array;
             goto check_array;
-
-        const UA_DataType *variableDataType = findDataType(variableDataTypeId);
-        const UA_DataType *valueDataType = findDataType(valueDataTypeId);
-        if(!editableValue || !variableDataType || !valueDataType)
+        else
             return UA_STATUSCODE_BADTYPEMISMATCH;
             return UA_STATUSCODE_BADTYPEMISMATCH;
+    }
 
 
-        /* a string is written to a byte array. the valuerank and array
-           dimensions are checked later */
-        if(variableDataType == &UA_TYPES[UA_TYPES_BYTE] &&
-           valueDataType == &UA_TYPES[UA_TYPES_BYTESTRING] &&
-           !range && UA_Variant_isScalar(value)) {
-            UA_ByteString *str = (UA_ByteString*)value->data;
-            editableValue->storageType = UA_VARIANT_DATA_NODELETE;
-            editableValue->type = &UA_TYPES[UA_TYPES_BYTE];
-            editableValue->arrayLength = str->length;
-            editableValue->data = str->data;
-            value = editableValue;
-            goto check_array;
-        }
+    /* See if the types match. The nodeid on the wire may be != the nodeid in
+     * the node for opaque types, enums and bytestrings. value contains the
+     * correct type definition after the following paragraph */
+    if(UA_NodeId_equal(&value->type->typeId, targetDataTypeId))
+        goto check_array;
 
 
-        /* An enum was sent as an int32, or an opaque type as a bytestring. This
-         * is detected with the typeIndex indicating the "true" datatype. */
-        enum type_equivalence te1 = typeEquivalence(variableDataType);
-        enum type_equivalence te2 = typeEquivalence(valueDataType);
-        if(te1 != TYPE_EQUIVALENCE_NONE && te1 == te2) {
-            *editableValue = *value;
-            editableValue->storageType = UA_VARIANT_DATA_NODELETE;
-            editableValue->type = variableDataType;
-            value = editableValue;
-            goto check_array;
-        }
+    /* Has the value a subtype of the required type? */
+    const UA_NodeId subtypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE);
+    if(isNodeInTree(server->nodestore, &value->type->typeId, targetDataTypeId, &subtypeId, 1))
+        goto check_array;
 
 
-        /* No more possible equivalencies */
+    /* Try to convert to a matching value if this is wanted */
+    if(!editableValue)
+        return UA_STATUSCODE_BADTYPEMISMATCH;
+    value = convertToMatchingValue(server, value, targetDataTypeId, editableValue);
+    if(!value)
         return UA_STATUSCODE_BADTYPEMISMATCH;
         return UA_STATUSCODE_BADTYPEMISMATCH;
-    }
 
 
  check_array:
  check_array:
-    if(range) /* array dimensions are checked when writing the range */
+    if(range) /* array dimensions are checked later when writing the range */
         return UA_STATUSCODE_GOOD;
         return UA_STATUSCODE_GOOD;
 
 
     /* See if the array dimensions match. When arrayDimensions are defined, they
     /* See if the array dimensions match. When arrayDimensions are defined, they
      * already hold the valuerank. */
      * already hold the valuerank. */
-    if(variableArrayDimensionsSize > 0)
-        return compatibleArrayDimensions(variableArrayDimensionsSize,
-                                         variableArrayDimensions,
+    if(targetArrayDimensionsSize > 0)
+        return compatibleArrayDimensions(targetArrayDimensionsSize,
+                                         targetArrayDimensions,
                                          value->arrayDimensionsSize,
                                          value->arrayDimensionsSize,
                                          value->arrayDimensions);
                                          value->arrayDimensions);
 
 
     /* Check if the valuerank allows for the value dimension */
     /* Check if the valuerank allows for the value dimension */
-    return compatibleValueRankValue(variableValueRank, value);
+    return compatibleValueRankValue(targetValueRank, value);
 }
 }
 
 
 /*****************************/
 /*****************************/
@@ -234,7 +236,7 @@ writeArrayDimensionsAttribute(UA_Server *server, UA_VariableNode *node,
     /* Check if the current value is compatible with the array dimensions */
     /* Check if the current value is compatible with the array dimensions */
     UA_DataValue value;
     UA_DataValue value;
     UA_DataValue_init(&value);
     UA_DataValue_init(&value);
-    retval = readValueAttribute(node, &value);
+    retval = readValueAttribute(server, node, &value);
     if(retval != UA_STATUSCODE_GOOD)
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
         return retval;
     if(value.hasValue) {
     if(value.hasValue) {
@@ -270,18 +272,18 @@ writeValueRankAttributeWithVT(UA_Server *server, UA_VariableNode *node,
     const UA_VariableTypeNode *vt = getVariableNodeType(server, node);
     const UA_VariableTypeNode *vt = getVariableNodeType(server, node);
     if(!vt)
     if(!vt)
         return UA_STATUSCODE_BADINTERNALERROR;
         return UA_STATUSCODE_BADINTERNALERROR;
-    return writeValueRankAttribute(node, valueRank, vt->valueRank);
+    return writeValueRankAttribute(server, node, valueRank, vt->valueRank);
 }
 }
 
 
 UA_StatusCode
 UA_StatusCode
-writeValueRankAttribute(UA_VariableNode *node, UA_Int32 valueRank,
+writeValueRankAttribute(UA_Server *server, UA_VariableNode *node, UA_Int32 valueRank,
                         UA_Int32 constraintValueRank) {
                         UA_Int32 constraintValueRank) {
     /* If this is a variabletype, there must be no instances or subtypes of it
     /* If this is a variabletype, there must be no instances or subtypes of it
        when we do the change */
        when we do the change */
     if(node->nodeClass == UA_NODECLASS_VARIABLETYPE &&
     if(node->nodeClass == UA_NODECLASS_VARIABLETYPE &&
        UA_Node_hasSubTypeOrInstances((const UA_Node*)node))
        UA_Node_hasSubTypeOrInstances((const UA_Node*)node))
         return UA_STATUSCODE_BADINTERNALERROR;
         return UA_STATUSCODE_BADINTERNALERROR;
-        
+
     /* Check if the valuerank of the variabletype allows the change. */
     /* Check if the valuerank of the variabletype allows the change. */
     switch(constraintValueRank) {
     switch(constraintValueRank) {
     case -3: /* the value can be a scalar or a one dimensional array */
     case -3: /* the value can be a scalar or a one dimensional array */
@@ -313,7 +315,7 @@ writeValueRankAttribute(UA_VariableNode *node, UA_Int32 valueRank,
            dimensions zero indicate a scalar for compatibleValueRankArrayDimensions. */
            dimensions zero indicate a scalar for compatibleValueRankArrayDimensions. */
         UA_DataValue value;
         UA_DataValue value;
         UA_DataValue_init(&value);
         UA_DataValue_init(&value);
-        retval = readValueAttribute(node, &value);
+        retval = readValueAttribute(server, node, &value);
         if(retval != UA_STATUSCODE_GOOD)
         if(retval != UA_STATUSCODE_GOOD)
             return retval;
             return retval;
         if(!value.hasValue || value.value.data == NULL)
         if(!value.hasValue || value.value.data == NULL)
@@ -364,7 +366,7 @@ writeDataTypeAttribute(UA_Server *server, UA_VariableNode *node,
     /* Check if the current value would match the new type */
     /* Check if the current value would match the new type */
     UA_DataValue value;
     UA_DataValue value;
     UA_DataValue_init(&value);
     UA_DataValue_init(&value);
-    UA_StatusCode retval = readValueAttribute(node, &value);
+    UA_StatusCode retval = readValueAttribute(server, node, &value);
     if(retval != UA_STATUSCODE_GOOD)
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
         return retval;
     if(value.hasValue) {
     if(value.hasValue) {
@@ -378,7 +380,7 @@ writeDataTypeAttribute(UA_Server *server, UA_VariableNode *node,
             return retval;
             return retval;
         }
         }
     }
     }
-    
+
     /* Replace the datatype nodeid */
     /* Replace the datatype nodeid */
     UA_NodeId dtCopy = node->dataType;
     UA_NodeId dtCopy = node->dataType;
     retval = UA_NodeId_copy(dataType, &node->dataType);
     retval = UA_NodeId_copy(dataType, &node->dataType);
@@ -387,7 +389,7 @@ writeDataTypeAttribute(UA_Server *server, UA_VariableNode *node,
         return retval;
         return retval;
     }
     }
     UA_NodeId_deleteMembers(&dtCopy);
     UA_NodeId_deleteMembers(&dtCopy);
-    return UA_STATUSCODE_GOOD; 
+    return UA_STATUSCODE_GOOD;
 }
 }
 
 
 /*******************/
 /*******************/
@@ -395,41 +397,55 @@ writeDataTypeAttribute(UA_Server *server, UA_VariableNode *node,
 /*******************/
 /*******************/
 
 
 static UA_StatusCode
 static UA_StatusCode
-readValueAttributeComplete(const UA_VariableNode *vn, UA_TimestampsToReturn timestamps,
-                           const UA_ReadValueId *id, UA_DataValue *v) {
+readValueAttributeFromNode(UA_Server *server, const UA_VariableNode *vn, UA_DataValue *v,
+                           UA_NumericRange *rangeptr) {
+    if(vn->value.data.callback.onRead) {
+        vn->value.data.callback.onRead(vn->value.data.callback.handle,
+                                       vn->nodeId, &vn->value.data.value.value, rangeptr);
+#ifdef UA_ENABLE_MULTITHREADING
+        /* Reopen the node to see the changes (multithreading only) */
+        vn = (const UA_VariableNode*)UA_NodeStore_get(server->nodestore, &vn->nodeId);
+#endif
+    }
+    if(rangeptr)
+        return UA_Variant_copyRange(&vn->value.data.value.value, &v->value, *rangeptr);
+    *v = vn->value.data.value;
+    v->value.storageType = UA_VARIANT_DATA_NODELETE;
+    return UA_STATUSCODE_GOOD;
+}
+
+static UA_StatusCode
+readValueAttributeFromDataSource(const UA_VariableNode *vn, UA_DataValue *v,
+                                 UA_TimestampsToReturn timestamps,
+                                 UA_NumericRange *rangeptr) {
+    if(!vn->value.dataSource.read)
+        return UA_STATUSCODE_BADINTERNALERROR;
+    UA_Boolean sourceTimeStamp = (timestamps == UA_TIMESTAMPSTORETURN_SOURCE ||
+                                  timestamps == UA_TIMESTAMPSTORETURN_BOTH);
+    return vn->value.dataSource.read(vn->value.dataSource.handle, vn->nodeId,
+                                     sourceTimeStamp, rangeptr, v);
+}
+
+static UA_StatusCode
+readValueAttributeComplete(UA_Server *server, const UA_VariableNode *vn,
+                           UA_TimestampsToReturn timestamps, const UA_String *indexRange,
+                           UA_DataValue *v) {
     /* Compute the index range */
     /* Compute the index range */
     UA_NumericRange range;
     UA_NumericRange range;
     UA_NumericRange *rangeptr = NULL;
     UA_NumericRange *rangeptr = NULL;
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
-    if(id->indexRange.length > 0) {
-        retval = parse_numericrange(&id->indexRange, &range);
+    if(indexRange && indexRange->length > 0) {
+        retval = parse_numericrange(indexRange, &range);
         if(retval != UA_STATUSCODE_GOOD)
         if(retval != UA_STATUSCODE_GOOD)
             return retval;
             return retval;
         rangeptr = &range;
         rangeptr = &range;
     }
     }
 
 
-    if(vn->valueSource == UA_VALUESOURCE_DATA) {
-        /* Data in the node */
-        if(vn->value.data.callback.onRead)
-            vn->value.data.callback.onRead(vn->value.data.callback.handle,
-                                           vn->nodeId, &v->value, rangeptr);
-        if(!rangeptr) {
-            *v = vn->value.data.value;
-            v->value.storageType = UA_VARIANT_DATA_NODELETE;
-        } else {
-            retval = UA_Variant_copyRange(&vn->value.data.value.value, &v->value, range);
-        }
-    } else {
-        /* Data in a value source */
-        if(!vn->value.dataSource.read) {
-            retval = UA_STATUSCODE_BADINTERNALERROR;
-        } else {
-            UA_Boolean sourceTimeStamp = (timestamps == UA_TIMESTAMPSTORETURN_SOURCE ||
-                                          timestamps == UA_TIMESTAMPSTORETURN_BOTH);
-            retval = vn->value.dataSource.read(vn->value.dataSource.handle, vn->nodeId,
-                                               sourceTimeStamp, rangeptr, v);
-        }
-    }
+    /* Read the value */
+    if(vn->valueSource == UA_VALUESOURCE_DATA)
+        retval = readValueAttributeFromNode(server, vn, v, rangeptr);
+    else
+        retval = readValueAttributeFromDataSource(vn, v, timestamps, rangeptr);
 
 
     /* Clean up */
     /* Clean up */
     if(rangeptr)
     if(rangeptr)
@@ -438,56 +454,52 @@ readValueAttributeComplete(const UA_VariableNode *vn, UA_TimestampsToReturn time
 }
 }
 
 
 UA_StatusCode
 UA_StatusCode
-readValueAttribute(const UA_VariableNode *vn, UA_DataValue *v) {
-    UA_TimestampsToReturn timestamps = UA_TIMESTAMPSTORETURN_NEITHER;
-    UA_ReadValueId id;
-    UA_ReadValueId_init(&id);
-    return readValueAttributeComplete(vn, timestamps, &id, v);
+readValueAttribute(UA_Server *server, const UA_VariableNode *vn, UA_DataValue *v) {
+    return readValueAttributeComplete(server, vn, UA_TIMESTAMPSTORETURN_NEITHER, NULL, v);
 }
 }
 
 
 static UA_StatusCode
 static UA_StatusCode
-writeValueAttributeAfterTypeCheck(UA_VariableNode *node, const UA_DataValue *value,
-                                  const UA_NumericRange *rangeptr) {
-    UA_StatusCode retval = UA_STATUSCODE_GOOD;
-    if(!rangeptr) {
-        /* Replace without a range */
-        UA_DataValue old_value = node->value.data.value; /* keep the pointers for restoring */
-        retval = UA_DataValue_copy(value, &node->value.data.value);
-        if(retval == UA_STATUSCODE_GOOD)
-            UA_DataValue_deleteMembers(&old_value);
-        else
-            node->value.data.value = old_value; 
-    } else {
-        /* Write into a range */
-        if(value->status == UA_STATUSCODE_GOOD) {
-            if(!node->value.data.value.hasValue || !value->hasValue)
-                return UA_STATUSCODE_BADINDEXRANGEINVALID;
-
-            /* Make scalar a one-entry array for range matching */
-            UA_Variant editableValue;
-            const UA_Variant *v = &value->value;
-            if(UA_Variant_isScalar(&value->value)) {
-                editableValue = value->value;
-                editableValue.arrayLength = 1;
-                v = &editableValue;
-            }
-                
-            /* Write the value */
-            retval = UA_Variant_setRangeCopy(&node->value.data.value.value,
-                                             v->data, v->arrayLength, *rangeptr);
-            if(retval != UA_STATUSCODE_GOOD)
-                return retval;
-        }
+writeValueAttributeWithoutRange(UA_VariableNode *node, const UA_DataValue *value) {
+    UA_DataValue old_value = node->value.data.value; /* keep the pointers for restoring */
+    UA_StatusCode retval = UA_DataValue_copy(value, &node->value.data.value);
+    if(retval == UA_STATUSCODE_GOOD)
+        UA_DataValue_deleteMembers(&old_value);
+    else
+        node->value.data.value = old_value;
+    return retval;
+}
 
 
-        /* Write the status and timestamps */
-        node->value.data.value.hasStatus = value->hasStatus;
-        node->value.data.value.status = value->status;
-        node->value.data.value.hasSourceTimestamp = value->hasSourceTimestamp;
-        node->value.data.value.sourceTimestamp = value->sourceTimestamp;
-        node->value.data.value.hasSourcePicoseconds = value->hasSourcePicoseconds;
-        node->value.data.value.sourcePicoseconds = value->sourcePicoseconds;
+static UA_StatusCode
+writeValueAttributeWithRange(UA_VariableNode *node, const UA_DataValue *value,
+                             const UA_NumericRange *rangeptr) {
+    /* Value on both sides? */
+    if(value->status != node->value.data.value.status ||
+       !value->hasValue || !node->value.data.value.hasValue)
+        return UA_STATUSCODE_BADINDEXRANGEINVALID;
+
+    /* Make scalar a one-entry array for range matching */
+    UA_Variant editableValue;
+    const UA_Variant *v = &value->value;
+    if(UA_Variant_isScalar(&value->value)) {
+        editableValue = value->value;
+        editableValue.arrayLength = 1;
+        v = &editableValue;
     }
     }
-    return retval;
+
+    /* Write the value */
+    UA_StatusCode retval = UA_Variant_setRangeCopy(&node->value.data.value.value,
+                                                   v->data, v->arrayLength, *rangeptr);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
+
+    /* Write the status and timestamps */
+    node->value.data.value.hasStatus = value->hasStatus;
+    node->value.data.value.status = value->status;
+    node->value.data.value.hasSourceTimestamp = value->hasSourceTimestamp;
+    node->value.data.value.sourceTimestamp = value->sourceTimestamp;
+    node->value.data.value.hasSourcePicoseconds = value->hasSourcePicoseconds;
+    node->value.data.value.sourcePicoseconds = value->sourcePicoseconds;
+    return UA_STATUSCODE_GOOD;
 }
 }
 
 
 UA_StatusCode
 UA_StatusCode
@@ -509,30 +521,41 @@ writeValueAttribute(UA_Server *server, UA_VariableNode *node,
     UA_DataValue editableValue = *value;
     UA_DataValue editableValue = *value;
     editableValue.value.storageType = UA_VARIANT_DATA_NODELETE;
     editableValue.value.storageType = UA_VARIANT_DATA_NODELETE;
 
 
-    /* Set the source timestamp if there is none */
-    if(!editableValue.hasSourceTimestamp) {
-        editableValue.sourceTimestamp = UA_DateTime_now();
-        editableValue.hasSourceTimestamp = true;
-    }
-
     /* Type checking. May change the type of editableValue */
     /* Type checking. May change the type of editableValue */
     if(value->hasValue) {
     if(value->hasValue) {
         retval = typeCheckValue(server, &node->dataType, node->valueRank,
         retval = typeCheckValue(server, &node->dataType, node->valueRank,
                                 node->arrayDimensionsSize, node->arrayDimensions,
                                 node->arrayDimensionsSize, node->arrayDimensions,
                                 &value->value, rangeptr, &editableValue.value);
                                 &value->value, rangeptr, &editableValue.value);
-        if(retval != UA_STATUSCODE_GOOD) {
-            UA_LOG_DEBUG(server->config.logger, UA_LOGCATEGORY_SERVER,
-                         "The new value does not match the variable definiton");
+        if(retval != UA_STATUSCODE_GOOD)
             goto cleanup;
             goto cleanup;
-        }
+    }
+
+    /* Set the source timestamp if there is none */
+    if(!editableValue.hasSourceTimestamp) {
+        editableValue.sourceTimestamp = UA_DateTime_now();
+        editableValue.hasSourceTimestamp = true;
     }
     }
 
 
     /* Ok, do it */
     /* Ok, do it */
     if(node->valueSource == UA_VALUESOURCE_DATA) {
     if(node->valueSource == UA_VALUESOURCE_DATA) {
-        retval = writeValueAttributeAfterTypeCheck(node, &editableValue, rangeptr);
-        if(retval == UA_STATUSCODE_GOOD && node->value.data.callback.onWrite)
-            node->value.data.callback.onWrite(node->value.data.callback.handle, node->nodeId,
-                                              &node->value.data.value.value, rangeptr);
+        if(!rangeptr)
+            retval = writeValueAttributeWithoutRange(node, &editableValue);
+        else
+            retval = writeValueAttributeWithRange(node, &editableValue, rangeptr);
+
+        /* Callback after writing */
+        if(retval == UA_STATUSCODE_GOOD && node->value.data.callback.onWrite) {
+            const UA_VariableNode *writtenNode;
+#ifdef UA_ENABLE_MULTITHREADING
+            /* Reopen the node to see the changes (multithreading only) */
+            writtenNode = (const UA_VariableNode*)UA_NodeStore_get(server->nodestore, &node->nodeId);
+#else
+            writtenNode = node; /* The node is written in-situ (TODO: this might
+                                   change with the nodestore plugin approach) */
+#endif
+            writtenNode->value.data.callback.onWrite(writtenNode->value.data.callback.handle, writtenNode->nodeId,
+                                                     &writtenNode->value.data.value.value, rangeptr);
+        }
     } else {
     } else {
         if(node->value.dataSource.write)
         if(node->value.dataSource.write)
             retval = node->value.dataSource.write(node->value.dataSource.handle,
             retval = node->value.dataSource.write(node->value.dataSource.handle,
@@ -601,7 +624,6 @@ writeIsAbstractAttribute(UA_Node *node, UA_Boolean value) {
 /****************/
 /****************/
 
 
 static const UA_String binEncoding = {sizeof("DefaultBinary")-1, (UA_Byte*)"DefaultBinary"};
 static const UA_String binEncoding = {sizeof("DefaultBinary")-1, (UA_Byte*)"DefaultBinary"};
-/* clang complains about unused variables */
 /* static const UA_String xmlEncoding = {sizeof("DefaultXml")-1, (UA_Byte*)"DefaultXml"}; */
 /* static const UA_String xmlEncoding = {sizeof("DefaultXml")-1, (UA_Byte*)"DefaultXml"}; */
 
 
 #define CHECK_NODECLASS(CLASS)                                  \
 #define CHECK_NODECLASS(CLASS)                                  \
@@ -624,8 +646,8 @@ void Service_Read_single(UA_Server *server, UA_Session *session,
            return;
            return;
     }
     }
 
 
-    /* index range for a non-value */
-    if(id->indexRange.length > 0 && id->attributeId != UA_ATTRIBUTEID_VALUE){
+    /* Index range for an attribute other than value */
+    if(id->indexRange.length > 0 && id->attributeId != UA_ATTRIBUTEID_VALUE) {
         v->hasStatus = true;
         v->hasStatus = true;
         v->status = UA_STATUSCODE_BADINDEXRANGENODATA;
         v->status = UA_STATUSCODE_BADINDEXRANGENODATA;
         return;
         return;
@@ -639,7 +661,7 @@ void Service_Read_single(UA_Server *server, UA_Session *session,
         return;
         return;
     }
     }
 
 
-    /* Read the value */
+    /* Read the attribute */
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     switch(id->attributeId) {
     switch(id->attributeId) {
     case UA_ATTRIBUTEID_NODEID:
     case UA_ATTRIBUTEID_NODEID:
@@ -692,8 +714,8 @@ void Service_Read_single(UA_Server *server, UA_Session *session,
         break;
         break;
     case UA_ATTRIBUTEID_VALUE:
     case UA_ATTRIBUTEID_VALUE:
         CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
         CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
-        retval = readValueAttributeComplete((const UA_VariableNode*)node,
-                                            timestamps, id, v);
+        retval = readValueAttributeComplete(server, (const UA_VariableNode*)node,
+                                            timestamps, &id->indexRange, v);
         break;
         break;
     case UA_ATTRIBUTEID_DATATYPE:
     case UA_ATTRIBUTEID_DATATYPE:
         CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
         CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);

+ 132 - 118
src/server/ua_services_nodemanagement.c

@@ -1,6 +1,83 @@
 #include "ua_server_internal.h"
 #include "ua_server_internal.h"
 #include "ua_services.h"
 #include "ua_services.h"
 
 
+/**********************/
+/* Consistency Checks */
+/**********************/
+
+/* Check if the requested parent node exists, has the right node class and is
+ * referenced with an allowed (hierarchical) reference type. For "type" nodes,
+ * only hasSubType references are allowed. */
+static UA_StatusCode
+checkParentReference(UA_Server *server, UA_Session *session, UA_NodeClass nodeClass,
+                     const UA_NodeId *parentNodeId, const UA_NodeId *referenceTypeId) {
+    /* See if the parent exists */
+    const UA_Node *parent = UA_NodeStore_get(server->nodestore, parentNodeId);
+    if(!parent) {
+        UA_LOG_DEBUG_SESSION(server->config.logger, session,
+                             "AddNodes: Parent node not found");
+        return UA_STATUSCODE_BADPARENTNODEIDINVALID;
+    }
+
+    /* Check the referencetype exists */
+    const UA_ReferenceTypeNode *referenceType =
+        (const UA_ReferenceTypeNode*)UA_NodeStore_get(server->nodestore, referenceTypeId);
+    if(!referenceType) {
+        UA_LOG_DEBUG_SESSION(server->config.logger, session,
+                             "AddNodes: Reference type to the parent not found");
+        return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
+    }
+
+    /* Check if the referencetype is a reference type node */
+    if(referenceType->nodeClass != UA_NODECLASS_REFERENCETYPE) {
+        UA_LOG_DEBUG_SESSION(server->config.logger, session,
+                             "AddNodes: Reference type to the parent invalid");
+        return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
+    }
+
+    /* Check that the reference type is not abstract */
+    if(referenceType->isAbstract == true) {
+        UA_LOG_DEBUG_SESSION(server->config.logger, session,
+                             "AddNodes: Abstract reference type to the parent invalid");
+        return UA_STATUSCODE_BADREFERENCENOTALLOWED;
+    }
+
+    /* Check hassubtype relation for type nodes */
+    const UA_NodeId subtypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE);
+    if(nodeClass == UA_NODECLASS_DATATYPE ||
+       nodeClass == UA_NODECLASS_VARIABLETYPE ||
+       nodeClass == UA_NODECLASS_OBJECTTYPE ||
+       nodeClass == UA_NODECLASS_REFERENCETYPE) {
+        /* type needs hassubtype reference to the supertype */
+        if(!UA_NodeId_equal(referenceTypeId, &subtypeId)) {
+            UA_LOG_DEBUG_SESSION(server->config.logger, session,
+                                 "AddNodes: New type node need to have a "
+                                 "hassubtype reference");
+            return UA_STATUSCODE_BADREFERENCENOTALLOWED;
+        }
+        /* supertype needs to be of the same node type  */
+        if(parent->nodeClass != nodeClass) {
+            UA_LOG_DEBUG_SESSION(server->config.logger, session,
+                                 "AddNodes: New type node needs to be of the same "
+                                 "node type as the parent");
+            return UA_STATUSCODE_BADPARENTNODEIDINVALID;
+        }
+        return UA_STATUSCODE_GOOD;
+    }
+
+    /* Test if the referencetype is hierarchical */
+    const UA_NodeId hierarchicalReference =
+        UA_NODEID_NUMERIC(0, UA_NS0ID_HIERARCHICALREFERENCES);
+    if(!isNodeInTree(server->nodestore, referenceTypeId,
+                     &hierarchicalReference, &subtypeId, 1)) {
+        UA_LOG_DEBUG_SESSION(server->config.logger, session,
+                             "AddNodes: Reference type is not hierarchical");
+        return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
+    }
+
+    return UA_STATUSCODE_GOOD;
+}
+
 /************/
 /************/
 /* Add Node */
 /* Add Node */
 /************/
 /************/
@@ -31,7 +108,7 @@ copyExistingVariable(UA_Server *server, UA_Session *session, const UA_NodeId *va
     /* Get the current value */
     /* Get the current value */
     UA_DataValue value;
     UA_DataValue value;
     UA_DataValue_init(&value);
     UA_DataValue_init(&value);
-    UA_StatusCode retval = readValueAttribute(node, &value);
+    UA_StatusCode retval = readValueAttribute(server, node, &value);
     if(retval != UA_STATUSCODE_GOOD)
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
         return retval;
 
 
@@ -326,76 +403,6 @@ copyChildNodesToNode(UA_Server* server, UA_Session* session,
     return retval;
     return retval;
 }
 }
 
 
-static UA_StatusCode
-checkParentReference(UA_Server *server, UA_Session *session, UA_NodeClass nodeClass,
-                     const UA_NodeId *parentNodeId, const UA_NodeId *referenceTypeId) {
-    /* See if the parent exists */
-    const UA_Node *parent = UA_NodeStore_get(server->nodestore, parentNodeId);
-    if(!parent) {
-        UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                             "AddNodes: Parent node not found");
-        return UA_STATUSCODE_BADPARENTNODEIDINVALID;
-    }
-
-    /* Check the referencetype exists */
-    const UA_ReferenceTypeNode *referenceType =
-        (const UA_ReferenceTypeNode*)UA_NodeStore_get(server->nodestore, referenceTypeId);
-    if(!referenceType) {
-        UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                             "AddNodes: Reference type to the parent not found");
-        return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
-    }
-
-    /* Check if the referencetype is a reference type node */
-    if(referenceType->nodeClass != UA_NODECLASS_REFERENCETYPE) {
-        UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                             "AddNodes: Reference type to the parent invalid");
-        return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
-    }
-
-    /* Check that the reference type is not abstract */
-    if(referenceType->isAbstract == true) {
-        UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                             "AddNodes: Abstract reference type to the parent invalid");
-        return UA_STATUSCODE_BADREFERENCENOTALLOWED;
-    }
-
-    /* Check hassubtype relation for type nodes */
-    const UA_NodeId subtypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE);
-    if(nodeClass == UA_NODECLASS_DATATYPE ||
-       nodeClass == UA_NODECLASS_VARIABLETYPE ||
-       nodeClass == UA_NODECLASS_OBJECTTYPE ||
-       nodeClass == UA_NODECLASS_REFERENCETYPE) {
-        /* type needs hassubtype reference to the supertype */
-        if(!UA_NodeId_equal(referenceTypeId, &subtypeId)) {
-            UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                                 "AddNodes: New type node need to have a "
-                                 "hassubtype reference");
-            return UA_STATUSCODE_BADREFERENCENOTALLOWED;
-        }
-        /* supertype needs to be of the same node type  */
-        if(parent->nodeClass != nodeClass) {
-            UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                                 "AddNodes: New type node needs to be of the same "
-                                 "node type as the parent");
-            return UA_STATUSCODE_BADPARENTNODEIDINVALID;
-        }
-        return UA_STATUSCODE_GOOD;
-    }
-
-    /* Test if the referencetype is hierarchical */
-    const UA_NodeId hierarchicalReference =
-        UA_NODEID_NUMERIC(0, UA_NS0ID_HIERARCHICALREFERENCES);
-    if(!isNodeInTree(server->nodestore, referenceTypeId,
-                     &hierarchicalReference, &subtypeId, 1)) {
-        UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                             "AddNodes: Reference type is not hierarchical");
-        return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
-    }
-
-    return UA_STATUSCODE_GOOD;
-}
-
 UA_StatusCode
 UA_StatusCode
 Service_AddNodes_existing(UA_Server *server, UA_Session *session, UA_Node *node,
 Service_AddNodes_existing(UA_Server *server, UA_Session *session, UA_Node *node,
                           const UA_NodeId *parentNodeId, const UA_NodeId *referenceTypeId,
                           const UA_NodeId *parentNodeId, const UA_NodeId *referenceTypeId,
@@ -488,9 +495,9 @@ Service_AddNodes_existing(UA_Server *server, UA_Session *session, UA_Node *node,
     return retval;
     return retval;
 }
 }
 
 
-/***********************************************/
-/* Create a node from an attribute description */
-/***********************************************/
+/*******************************************/
+/* Create nodes from attribute description */
+/*******************************************/
 
 
 static UA_StatusCode
 static UA_StatusCode
 copyStandardAttributes(UA_Node *node, const UA_AddNodesItem *item,
 copyStandardAttributes(UA_Node *node, const UA_AddNodesItem *item,
@@ -551,7 +558,7 @@ copyCommonVariableAttributes(UA_Server *server, UA_VariableNode *node,
 
 
     /* Set the valuerank */
     /* Set the valuerank */
     if(attr->valueRank != 0 || !UA_Variant_isScalar(&attr->value))
     if(attr->valueRank != 0 || !UA_Variant_isScalar(&attr->value))
-        retval = writeValueRankAttribute(node, attr->valueRank, vt->valueRank);
+        retval = writeValueRankAttribute(server, node, attr->valueRank, vt->valueRank);
     else /* workaround common error where the valuerank is left as 0 */
     else /* workaround common error where the valuerank is left as 0 */
         node->valueRank = vt->valueRank;
         node->valueRank = vt->valueRank;
     if(retval != UA_STATUSCODE_GOOD)
     if(retval != UA_STATUSCODE_GOOD)
@@ -567,7 +574,7 @@ copyCommonVariableAttributes(UA_Server *server, UA_VariableNode *node,
 }
 }
 
 
 static UA_StatusCode
 static UA_StatusCode
-variableNodeFromAttributes(UA_Server *server, UA_VariableNode *vnode,
+copyVariableNodeAttributes(UA_Server *server, UA_VariableNode *vnode,
                            const UA_AddNodesItem *item,
                            const UA_AddNodesItem *item,
                            const UA_VariableAttributes *attr) {
                            const UA_VariableAttributes *attr) {
     vnode->accessLevel = attr->accessLevel;
     vnode->accessLevel = attr->accessLevel;
@@ -577,7 +584,7 @@ variableNodeFromAttributes(UA_Server *server, UA_VariableNode *vnode,
 }
 }
 
 
 static UA_StatusCode
 static UA_StatusCode
-variableTypeNodeFromAttributes(UA_Server *server, UA_VariableTypeNode *vtnode,
+copyVariableTypeNodeAttributes(UA_Server *server, UA_VariableTypeNode *vtnode,
                                const UA_AddNodesItem *item,
                                const UA_AddNodesItem *item,
                                const UA_VariableTypeAttributes *attr) {
                                const UA_VariableTypeAttributes *attr) {
     vtnode->isAbstract = attr->isAbstract;
     vtnode->isAbstract = attr->isAbstract;
@@ -586,13 +593,13 @@ variableTypeNodeFromAttributes(UA_Server *server, UA_VariableTypeNode *vtnode,
 }
 }
 
 
 static UA_StatusCode
 static UA_StatusCode
-objectNodeFromAttributes(UA_ObjectNode *onode, const UA_ObjectAttributes *attr) {
+copyObjectNodeAttributes(UA_ObjectNode *onode, const UA_ObjectAttributes *attr) {
     onode->eventNotifier = attr->eventNotifier;
     onode->eventNotifier = attr->eventNotifier;
     return UA_STATUSCODE_GOOD;
     return UA_STATUSCODE_GOOD;
 }
 }
 
 
 static UA_StatusCode
 static UA_StatusCode
-referenceTypeNodeFromAttributes(UA_ReferenceTypeNode *rtnode,
+copyReferenceTypeNodeAttributes(UA_ReferenceTypeNode *rtnode,
                                 const UA_ReferenceTypeAttributes *attr) {
                                 const UA_ReferenceTypeAttributes *attr) {
     rtnode->isAbstract = attr->isAbstract;
     rtnode->isAbstract = attr->isAbstract;
     rtnode->symmetric = attr->symmetric;
     rtnode->symmetric = attr->symmetric;
@@ -600,21 +607,21 @@ referenceTypeNodeFromAttributes(UA_ReferenceTypeNode *rtnode,
 }
 }
 
 
 static UA_StatusCode
 static UA_StatusCode
-objectTypeNodeFromAttributes(UA_ObjectTypeNode *otnode,
+copyObjectTypeNodeAttributes(UA_ObjectTypeNode *otnode,
                              const UA_ObjectTypeAttributes *attr) {
                              const UA_ObjectTypeAttributes *attr) {
     otnode->isAbstract = attr->isAbstract;
     otnode->isAbstract = attr->isAbstract;
     return UA_STATUSCODE_GOOD;
     return UA_STATUSCODE_GOOD;
 }
 }
 
 
 static UA_StatusCode
 static UA_StatusCode
-viewNodeFromAttributes(UA_ViewNode *vnode, const UA_ViewAttributes *attr) {
+copyViewNodeAttributes(UA_ViewNode *vnode, const UA_ViewAttributes *attr) {
     vnode->containsNoLoops = attr->containsNoLoops;
     vnode->containsNoLoops = attr->containsNoLoops;
     vnode->eventNotifier = attr->eventNotifier;
     vnode->eventNotifier = attr->eventNotifier;
     return UA_STATUSCODE_GOOD;
     return UA_STATUSCODE_GOOD;
 }
 }
 
 
 static UA_StatusCode
 static UA_StatusCode
-dataTypeNodeFromAttributes(UA_DataTypeNode *dtnode,
+copyDataTypeNodeAttributes(UA_DataTypeNode *dtnode,
                            const UA_DataTypeAttributes *attr) {
                            const UA_DataTypeAttributes *attr) {
     dtnode->isAbstract = attr->isAbstract;
     dtnode->isAbstract = attr->isAbstract;
     return UA_STATUSCODE_GOOD;
     return UA_STATUSCODE_GOOD;
@@ -622,77 +629,83 @@ dataTypeNodeFromAttributes(UA_DataTypeNode *dtnode,
 
 
 #define CHECK_ATTRIBUTES(TYPE)                                          \
 #define CHECK_ATTRIBUTES(TYPE)                                          \
     if(item->nodeAttributes.content.decoded.type != &UA_TYPES[UA_TYPES_##TYPE]) { \
     if(item->nodeAttributes.content.decoded.type != &UA_TYPES[UA_TYPES_##TYPE]) { \
-        result->statusCode = UA_STATUSCODE_BADNODEATTRIBUTESINVALID;    \
+        retval = UA_STATUSCODE_BADNODEATTRIBUTESINVALID;                \
         break;                                                          \
         break;                                                          \
     }
     }
 
 
-static void
-Service_AddNodes_single(UA_Server *server, UA_Session *session,
-                        const UA_AddNodesItem *item, UA_AddNodesResult *result,
-                        UA_InstantiationCallback *instantiationCallback) {
+static UA_StatusCode
+createNodeFromAttributes(UA_Server *server, const UA_AddNodesItem *item, UA_Node **newNode) {
+    /* Check that we can read the attributes */
     if(item->nodeAttributes.encoding < UA_EXTENSIONOBJECT_DECODED ||
     if(item->nodeAttributes.encoding < UA_EXTENSIONOBJECT_DECODED ||
-       !item->nodeAttributes.content.decoded.type) {
-        result->statusCode = UA_STATUSCODE_BADNODEATTRIBUTESINVALID;
-        return;
-    }
+       !item->nodeAttributes.content.decoded.type)
+        return UA_STATUSCODE_BADNODEATTRIBUTESINVALID;
 
 
     /* Create the node */
     /* Create the node */
-    UA_Node *node = UA_NodeStore_newNode(item->nodeClass);
-    if(!node) {
-        // todo: case where the nodeclass is faulty
-        result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
-        return;
-    }
+    // todo: error case where the nodeclass is faulty
+    void *node = UA_NodeStore_newNode(item->nodeClass);
+    if(!node)
+        return UA_STATUSCODE_BADOUTOFMEMORY;
 
 
+    /* Copy the attributes into the node */
     void *data = item->nodeAttributes.content.decoded.data;
     void *data = item->nodeAttributes.content.decoded.data;
-    result->statusCode = copyStandardAttributes(node, item, data);
+    UA_StatusCode retval = copyStandardAttributes(node, item, data);
     switch(item->nodeClass) {
     switch(item->nodeClass) {
     case UA_NODECLASS_OBJECT:
     case UA_NODECLASS_OBJECT:
         CHECK_ATTRIBUTES(OBJECTATTRIBUTES);
         CHECK_ATTRIBUTES(OBJECTATTRIBUTES);
-        result->statusCode |= objectNodeFromAttributes((UA_ObjectNode*)node, data);
+        retval |= copyObjectNodeAttributes(node, data);
         break;
         break;
     case UA_NODECLASS_VARIABLE:
     case UA_NODECLASS_VARIABLE:
         CHECK_ATTRIBUTES(VARIABLEATTRIBUTES);
         CHECK_ATTRIBUTES(VARIABLEATTRIBUTES);
-        result->statusCode |= variableNodeFromAttributes(server, (UA_VariableNode*)node,
-                                                         item, data);
+        retval |= copyVariableNodeAttributes(server, node, item, data);
         break;
         break;
     case UA_NODECLASS_OBJECTTYPE:
     case UA_NODECLASS_OBJECTTYPE:
         CHECK_ATTRIBUTES(OBJECTTYPEATTRIBUTES);
         CHECK_ATTRIBUTES(OBJECTTYPEATTRIBUTES);
-        result->statusCode |= objectTypeNodeFromAttributes((UA_ObjectTypeNode*)node, data);
+        retval |= copyObjectTypeNodeAttributes(node, data);
         break;
         break;
     case UA_NODECLASS_VARIABLETYPE:
     case UA_NODECLASS_VARIABLETYPE:
         CHECK_ATTRIBUTES(VARIABLETYPEATTRIBUTES);
         CHECK_ATTRIBUTES(VARIABLETYPEATTRIBUTES);
-        result->statusCode |= variableTypeNodeFromAttributes(server, (UA_VariableTypeNode*)node,
-                                                             item, data);
+        retval |= copyVariableTypeNodeAttributes(server, node, item, data);
         break;
         break;
     case UA_NODECLASS_REFERENCETYPE:
     case UA_NODECLASS_REFERENCETYPE:
         CHECK_ATTRIBUTES(REFERENCETYPEATTRIBUTES);
         CHECK_ATTRIBUTES(REFERENCETYPEATTRIBUTES);
-        result->statusCode |= referenceTypeNodeFromAttributes((UA_ReferenceTypeNode*)node, data);
+        retval |= copyReferenceTypeNodeAttributes(node, data);
         break;
         break;
     case UA_NODECLASS_DATATYPE:
     case UA_NODECLASS_DATATYPE:
         CHECK_ATTRIBUTES(DATATYPEATTRIBUTES);
         CHECK_ATTRIBUTES(DATATYPEATTRIBUTES);
-        result->statusCode |= dataTypeNodeFromAttributes((UA_DataTypeNode*)node, data);
+        retval |= copyDataTypeNodeAttributes(node, data);
         break;
         break;
     case UA_NODECLASS_VIEW:
     case UA_NODECLASS_VIEW:
         CHECK_ATTRIBUTES(VIEWATTRIBUTES);
         CHECK_ATTRIBUTES(VIEWATTRIBUTES);
-        result->statusCode |= viewNodeFromAttributes((UA_ViewNode*)node, data);
+        retval |= copyViewNodeAttributes(node, data);
         break;
         break;
     case UA_NODECLASS_METHOD:
     case UA_NODECLASS_METHOD:
     case UA_NODECLASS_UNSPECIFIED:
     case UA_NODECLASS_UNSPECIFIED:
     default:
     default:
-        result->statusCode = UA_STATUSCODE_BADNODECLASSINVALID;
+        retval = UA_STATUSCODE_BADNODECLASSINVALID;
     }
     }
 
 
-    if(result->statusCode == UA_STATUSCODE_GOOD) {
-        result->statusCode = Service_AddNodes_existing(server, session, node, &item->parentNodeId.nodeId,
-                                                       &item->referenceTypeId, &item->typeDefinition.nodeId,
-                                                       instantiationCallback, &result->addedNodeId);
-    } else {
-        UA_LOG_DEBUG_SESSION(server->config.logger, session, "AddNodes: Could not "
-                             "prepare the new node with status code %s",
-                             UA_StatusCode_name(result->statusCode));
+    if(retval == UA_STATUSCODE_GOOD)
+        *newNode = node;
+    else
         UA_NodeStore_deleteNode(node);
         UA_NodeStore_deleteNode(node);
-    }
+    return retval;
+}
+
+static void
+Service_AddNodes_single(UA_Server *server, UA_Session *session,
+                        const UA_AddNodesItem *item, UA_AddNodesResult *result,
+                        UA_InstantiationCallback *instantiationCallback) {
+    /* Create the node from the attributes*/
+    UA_Node *node;
+    result->statusCode = createNodeFromAttributes(server, item, &node);
+    if(result->statusCode != UA_STATUSCODE_GOOD)
+        return;
+
+    /* Run consistency checks and add the node */
+    UA_assert(node != NULL);
+    result->statusCode = Service_AddNodes_existing(server, session, node, &item->parentNodeId.nodeId,
+                                                   &item->referenceTypeId, &item->typeDefinition.nodeId,
+                                                   instantiationCallback, &result->addedNodeId);
 }
 }
 
 
 void Service_AddNodes(UA_Server *server, UA_Session *session,
 void Service_AddNodes(UA_Server *server, UA_Session *session,
@@ -817,6 +830,7 @@ UA_Server_addDataSourceVariableNode(UA_Server *server, const UA_NodeId requested
     }
     }
 
 
     /* Copy attributes into node */
     /* Copy attributes into node */
+    UA_RCU_LOCK();
     UA_AddNodesItem item;
     UA_AddNodesItem item;
     UA_AddNodesItem_init(&item);
     UA_AddNodesItem_init(&item);
     item.requestedNewNodeId.nodeId = requestedNewNodeId;
     item.requestedNewNodeId.nodeId = requestedNewNodeId;
@@ -831,13 +845,13 @@ UA_Server_addDataSourceVariableNode(UA_Server *server, const UA_NodeId requested
     UA_DataValue_deleteMembers(&value);
     UA_DataValue_deleteMembers(&value);
     if(retval != UA_STATUSCODE_GOOD) {
     if(retval != UA_STATUSCODE_GOOD) {
         UA_NodeStore_deleteNode((UA_Node*)node);
         UA_NodeStore_deleteNode((UA_Node*)node);
+        UA_RCU_UNLOCK();
         return retval;
         return retval;
     }
     }
 
 
     /* Add the node */
     /* Add the node */
     UA_AddNodesResult result;
     UA_AddNodesResult result;
     UA_AddNodesResult_init(&result);
     UA_AddNodesResult_init(&result);
-    UA_RCU_LOCK();
     retval = Service_AddNodes_existing(server, &adminSession, (UA_Node*)node, &parentNodeId,
     retval = Service_AddNodes_existing(server, &adminSession, (UA_Node*)node, &parentNodeId,
                                        &referenceTypeId, &typeDefinition, NULL, outNewNodeId);
                                        &referenceTypeId, &typeDefinition, NULL, outNewNodeId);
     UA_RCU_UNLOCK();
     UA_RCU_UNLOCK();

+ 3 - 13
src/server/ua_services_subscription.c

@@ -246,7 +246,7 @@ Service_CreateMonitoredItems_single(UA_Server *server, UA_Session *session,
     newMon->timestampsToReturn = timestampsToReturn;
     newMon->timestampsToReturn = timestampsToReturn;
     setMonitoredItemSettings(server, newMon, request->monitoringMode,
     setMonitoredItemSettings(server, newMon, request->monitoringMode,
                              &request->requestedParameters);
                              &request->requestedParameters);
-    LIST_INSERT_HEAD(&sub->MonitoredItems, newMon, listEntry);
+    LIST_INSERT_HEAD(&sub->monitoredItems, newMon, listEntry);
 
 
     /* Create the first sample */
     /* Create the first sample */
     if(request->monitoringMode == UA_MONITORINGMODE_REPORTING)
     if(request->monitoringMode == UA_MONITORINGMODE_REPORTING)
@@ -439,17 +439,7 @@ Service_Publish(UA_Server *server, UA_Session *session,
             continue;
             continue;
         }
         }
         /* Remove the acked transmission from the retransmission queue */
         /* Remove the acked transmission from the retransmission queue */
-        response->results[i] = UA_STATUSCODE_BADSEQUENCENUMBERUNKNOWN;
-        UA_NotificationMessageEntry *pre, *pre_tmp;
-        LIST_FOREACH_SAFE(pre, &sub->retransmissionQueue, listEntry, pre_tmp) {
-            if(pre->message.sequenceNumber == ack->sequenceNumber) {
-                LIST_REMOVE(pre, listEntry);
-                response->results[i] = UA_STATUSCODE_GOOD;
-                UA_NotificationMessage_deleteMembers(&pre->message);
-                UA_free(pre);
-                break;
-            }
-        }
+        response->results[i] = UA_Subscription_removeRetransmissionMessage(sub, ack->sequenceNumber);
     }
     }
 
 
     /* Queue the publish response */
     /* Queue the publish response */
@@ -570,7 +560,7 @@ void Service_Republish(UA_Server *server, UA_Session *session, const UA_Republis
 
 
     /* Find the notification in the retransmission queue  */
     /* Find the notification in the retransmission queue  */
     UA_NotificationMessageEntry *entry;
     UA_NotificationMessageEntry *entry;
-    LIST_FOREACH(entry, &sub->retransmissionQueue, listEntry) {
+    TAILQ_FOREACH(entry, &sub->retransmissionQueue, listEntry) {
         if(entry->message.sequenceNumber == request->retransmitSequenceNumber)
         if(entry->message.sequenceNumber == request->retransmitSequenceNumber)
             break;
             break;
     }
     }

+ 53 - 20
src/server/ua_subscription.c

@@ -260,8 +260,9 @@ UA_Subscription * UA_Subscription_new(UA_Session *session, UA_UInt32 subscriptio
     new->currentLifetimeCount = 0;
     new->currentLifetimeCount = 0;
     new->lastMonitoredItemId = 0;
     new->lastMonitoredItemId = 0;
     new->state = UA_SUBSCRIPTIONSTATE_NORMAL; /* The first publish response is sent immediately */
     new->state = UA_SUBSCRIPTIONSTATE_NORMAL; /* The first publish response is sent immediately */
-    LIST_INIT(&new->retransmissionQueue);
-    LIST_INIT(&new->MonitoredItems);
+    LIST_INIT(&new->monitoredItems);
+    TAILQ_INIT(&new->retransmissionQueue);
+    new->retransmissionQueueSize = 0;
     return new;
     return new;
 }
 }
 
 
@@ -270,24 +271,25 @@ void UA_Subscription_deleteMembers(UA_Subscription *subscription, UA_Server *ser
 
 
     /* Delete monitored Items */
     /* Delete monitored Items */
     UA_MonitoredItem *mon, *tmp_mon;
     UA_MonitoredItem *mon, *tmp_mon;
-    LIST_FOREACH_SAFE(mon, &subscription->MonitoredItems, listEntry, tmp_mon) {
+    LIST_FOREACH_SAFE(mon, &subscription->monitoredItems, listEntry, tmp_mon) {
         LIST_REMOVE(mon, listEntry);
         LIST_REMOVE(mon, listEntry);
         MonitoredItem_delete(server, mon);
         MonitoredItem_delete(server, mon);
     }
     }
 
 
     /* Delete Retransmission Queue */
     /* Delete Retransmission Queue */
     UA_NotificationMessageEntry *nme, *nme_tmp;
     UA_NotificationMessageEntry *nme, *nme_tmp;
-    LIST_FOREACH_SAFE(nme, &subscription->retransmissionQueue, listEntry, nme_tmp) {
-        LIST_REMOVE(nme, listEntry);
+    TAILQ_FOREACH_SAFE(nme, &subscription->retransmissionQueue, listEntry, nme_tmp) {
+        TAILQ_REMOVE(&subscription->retransmissionQueue, nme, listEntry);
         UA_NotificationMessage_deleteMembers(&nme->message);
         UA_NotificationMessage_deleteMembers(&nme->message);
         UA_free(nme);
         UA_free(nme);
     }
     }
+    subscription->retransmissionQueueSize = 0;
 }
 }
 
 
 UA_MonitoredItem *
 UA_MonitoredItem *
 UA_Subscription_getMonitoredItem(UA_Subscription *sub, UA_UInt32 monitoredItemID) {
 UA_Subscription_getMonitoredItem(UA_Subscription *sub, UA_UInt32 monitoredItemID) {
     UA_MonitoredItem *mon;
     UA_MonitoredItem *mon;
-    LIST_FOREACH(mon, &sub->MonitoredItems, listEntry) {
+    LIST_FOREACH(mon, &sub->monitoredItems, listEntry) {
         if(mon->itemId == monitoredItemID)
         if(mon->itemId == monitoredItemID)
             break;
             break;
     }
     }
@@ -298,7 +300,7 @@ UA_StatusCode
 UA_Subscription_deleteMonitoredItem(UA_Server *server, UA_Subscription *sub,
 UA_Subscription_deleteMonitoredItem(UA_Server *server, UA_Subscription *sub,
                                     UA_UInt32 monitoredItemID) {
                                     UA_UInt32 monitoredItemID) {
     UA_MonitoredItem *mon;
     UA_MonitoredItem *mon;
-    LIST_FOREACH(mon, &sub->MonitoredItems, listEntry) {
+    LIST_FOREACH(mon, &sub->monitoredItems, listEntry) {
         if(mon->itemId == monitoredItemID) {
         if(mon->itemId == monitoredItemID) {
             LIST_REMOVE(mon, listEntry);
             LIST_REMOVE(mon, listEntry);
             MonitoredItem_delete(server, mon);
             MonitoredItem_delete(server, mon);
@@ -313,7 +315,7 @@ countQueuedNotifications(UA_Subscription *sub, UA_Boolean *moreNotifications) {
     size_t notifications = 0;
     size_t notifications = 0;
     if(sub->publishingEnabled) {
     if(sub->publishingEnabled) {
         UA_MonitoredItem *mon;
         UA_MonitoredItem *mon;
-        LIST_FOREACH(mon, &sub->MonitoredItems, listEntry) {
+        LIST_FOREACH(mon, &sub->monitoredItems, listEntry) {
             MonitoredItem_queuedValue *qv;
             MonitoredItem_queuedValue *qv;
             TAILQ_FOREACH(qv, &mon->queue, listEntry) {
             TAILQ_FOREACH(qv, &mon->queue, listEntry) {
                 if(notifications >= sub->notificationsPerPublish) {
                 if(notifications >= sub->notificationsPerPublish) {
@@ -327,6 +329,40 @@ countQueuedNotifications(UA_Subscription *sub, UA_Boolean *moreNotifications) {
     return notifications;
     return notifications;
 }
 }
 
 
+static void
+UA_Subscription_addRetransmissionMessage(UA_Server *server, UA_Subscription *sub,
+                                         UA_NotificationMessageEntry *entry) {
+    /* Release the oldest entry if there is not enough space */
+    if(server->config.maxRetransmissionQueueSize > 0 &&
+       sub->retransmissionQueueSize >= server->config.maxRetransmissionQueueSize) {
+        UA_NotificationMessageEntry *lastentry =
+            TAILQ_LAST(&sub->retransmissionQueue, UA_ListOfNotificationMessages);
+        TAILQ_REMOVE(&sub->retransmissionQueue, lastentry, listEntry);
+        --sub->retransmissionQueueSize;
+        UA_NotificationMessage_deleteMembers(&lastentry->message);
+        UA_free(lastentry);
+    }
+
+    /* Add entry */
+    TAILQ_INSERT_HEAD(&sub->retransmissionQueue, entry, listEntry);
+    ++sub->retransmissionQueueSize;
+}
+
+UA_StatusCode
+UA_Subscription_removeRetransmissionMessage(UA_Subscription *sub, UA_UInt32 sequenceNumber) {
+    UA_NotificationMessageEntry *entry, *entry_tmp;
+    TAILQ_FOREACH_SAFE(entry, &sub->retransmissionQueue, listEntry, entry_tmp) {
+        if(entry->message.sequenceNumber != sequenceNumber)
+            continue;
+        TAILQ_REMOVE(&sub->retransmissionQueue, entry, listEntry);
+        --sub->retransmissionQueueSize;
+        UA_NotificationMessage_deleteMembers(&entry->message);
+        UA_free(entry);
+        return UA_STATUSCODE_GOOD;
+    }
+    return UA_STATUSCODE_BADSEQUENCENUMBERUNKNOWN;
+}
+
 static UA_StatusCode
 static UA_StatusCode
 prepareNotificationMessage(UA_Subscription *sub, UA_NotificationMessage *message,
 prepareNotificationMessage(UA_Subscription *sub, UA_NotificationMessage *message,
                            size_t notifications) {
                            size_t notifications) {
@@ -356,7 +392,7 @@ prepareNotificationMessage(UA_Subscription *sub, UA_NotificationMessage *message
     /* Move notifications into the response .. the point of no return */
     /* Move notifications into the response .. the point of no return */
     size_t l = 0;
     size_t l = 0;
     UA_MonitoredItem *mon;
     UA_MonitoredItem *mon;
-    LIST_FOREACH(mon, &sub->MonitoredItems, listEntry) {
+    LIST_FOREACH(mon, &sub->monitoredItems, listEntry) {
         MonitoredItem_queuedValue *qv, *qv_tmp;
         MonitoredItem_queuedValue *qv, *qv_tmp;
         TAILQ_FOREACH_SAFE(qv, &mon->queue, listEntry, qv_tmp) {
         TAILQ_FOREACH_SAFE(qv, &mon->queue, listEntry, qv_tmp) {
             if(l >= notifications)
             if(l >= notifications)
@@ -466,23 +502,20 @@ void UA_Subscription_publishCallback(UA_Server *server, UA_Subscription *sub) {
          * be done here, so that the message itself is included in the available
          * be done here, so that the message itself is included in the available
          * sequence numbers for acknowledgement. */
          * sequence numbers for acknowledgement. */
         retransmission->message = response->notificationMessage;
         retransmission->message = response->notificationMessage;
-        LIST_INSERT_HEAD(&sub->retransmissionQueue, retransmission, listEntry);
+        UA_Subscription_addRetransmissionMessage(server, sub, retransmission);
     }
     }
 
 
     /* Get the available sequence numbers from the retransmission queue */
     /* Get the available sequence numbers from the retransmission queue */
-    size_t available = 0;
-    UA_NotificationMessageEntry *nme;
-    LIST_FOREACH(nme, &sub->retransmissionQueue, listEntry)
-        ++available;
-    // cppcheck-suppress knownConditionTrueFalse
+    size_t available = sub->retransmissionQueueSize;
     if(available > 0) {
     if(available > 0) {
         response->availableSequenceNumbers = UA_alloca(available * sizeof(UA_UInt32));
         response->availableSequenceNumbers = UA_alloca(available * sizeof(UA_UInt32));
         response->availableSequenceNumbersSize = available;
         response->availableSequenceNumbersSize = available;
-    }
-    size_t i = 0;
-    LIST_FOREACH(nme, &sub->retransmissionQueue, listEntry) {
-        response->availableSequenceNumbers[i] = nme->message.sequenceNumber;
-        ++i;
+        size_t i = 0;
+        UA_NotificationMessageEntry *nme;
+        TAILQ_FOREACH(nme, &sub->retransmissionQueue, listEntry) {
+            response->availableSequenceNumbers[i] = nme->message.sequenceNumber;
+            ++i;
+        }
     }
     }
 
 
     /* Send the response */
     /* Send the response */

+ 11 - 4
src/server/ua_subscription.h

@@ -63,7 +63,7 @@ UA_StatusCode MonitoredItem_unregisterSampleJob(UA_Server *server, UA_MonitoredI
 /****************/
 /****************/
 
 
 typedef struct UA_NotificationMessageEntry {
 typedef struct UA_NotificationMessageEntry {
-    LIST_ENTRY(UA_NotificationMessageEntry) listEntry;
+    TAILQ_ENTRY(UA_NotificationMessageEntry) listEntry;
     UA_NotificationMessage message;
     UA_NotificationMessage message;
 } UA_NotificationMessageEntry;
 } UA_NotificationMessageEntry;
 
 
@@ -83,7 +83,7 @@ struct UA_Subscription {
     UA_Session *session;
     UA_Session *session;
     UA_UInt32 lifeTimeCount;
     UA_UInt32 lifeTimeCount;
     UA_UInt32 maxKeepAliveCount;
     UA_UInt32 maxKeepAliveCount;
-    UA_Double publishingInterval;     // [ms]
+    UA_Double publishingInterval; /* in ms */
     UA_UInt32 subscriptionID;
     UA_UInt32 subscriptionID;
     UA_UInt32 notificationsPerPublish;
     UA_UInt32 notificationsPerPublish;
     UA_Boolean publishingEnabled;
     UA_Boolean publishingEnabled;
@@ -100,8 +100,12 @@ struct UA_Subscription {
     UA_Guid publishJobGuid;
     UA_Guid publishJobGuid;
     UA_Boolean publishJobIsRegistered;
     UA_Boolean publishJobIsRegistered;
 
 
-    LIST_HEAD(UA_ListOfUAMonitoredItems, UA_MonitoredItem) MonitoredItems;
-    LIST_HEAD(UA_ListOfNotificationMessages, UA_NotificationMessageEntry) retransmissionQueue;
+    /* MonitoredItems */
+    LIST_HEAD(UA_ListOfUAMonitoredItems, UA_MonitoredItem) monitoredItems;
+
+    /* Retransmission Queue */
+    TAILQ_HEAD(UA_ListOfNotificationMessages, UA_NotificationMessageEntry) retransmissionQueue;
+    UA_UInt32 retransmissionQueueSize;
 };
 };
 
 
 UA_Subscription *UA_Subscription_new(UA_Session *session, UA_UInt32 subscriptionID);
 UA_Subscription *UA_Subscription_new(UA_Session *session, UA_UInt32 subscriptionID);
@@ -118,6 +122,9 @@ UA_Subscription_getMonitoredItem(UA_Subscription *sub, UA_UInt32 monitoredItemID
 
 
 void UA_Subscription_publishCallback(UA_Server *server, UA_Subscription *sub);
 void UA_Subscription_publishCallback(UA_Server *server, UA_Subscription *sub);
 
 
+UA_StatusCode
+UA_Subscription_removeRetransmissionMessage(UA_Subscription *sub, UA_UInt32 sequenceNumber);
+
 void
 void
 UA_Subscription_answerPublishRequestsNoSubscription(UA_Server *server,
 UA_Subscription_answerPublishRequestsNoSubscription(UA_Server *server,
                                                     UA_NodeId *sessionToken);
                                                     UA_NodeId *sessionToken);

+ 1 - 1
src/ua_connection.c

@@ -18,7 +18,7 @@ UA_Connection_completeMessages(UA_Connection *connection, UA_ByteString * UA_RES
      * After this block, connection->incompleteMessage is always empty. */
      * After this block, connection->incompleteMessage is always empty. */
     if(connection->incompleteMessage.length > 0) {
     if(connection->incompleteMessage.length > 0) {
         size_t length = connection->incompleteMessage.length + message->length;
         size_t length = connection->incompleteMessage.length + message->length;
-        UA_Byte *data = UA_realloc(connection->incompleteMessage.data, length);
+        UA_Byte *data = (UA_Byte*)UA_realloc(connection->incompleteMessage.data, length);
         if(!data) {
         if(!data) {
             retval = UA_STATUSCODE_BADOUTOFMEMORY;
             retval = UA_STATUSCODE_BADOUTOFMEMORY;
             goto cleanup;
             goto cleanup;

+ 30 - 23
src/ua_types.c

@@ -13,16 +13,24 @@
  * UA_DataType structure. The UA_DataType structures as well as all non-builtin
  * UA_DataType structure. The UA_DataType structures as well as all non-builtin
  * datatypes are autogenerated. */
  * datatypes are autogenerated. */
 
 
-/* Static definition of NULL type instances */
-const UA_String UA_STRING_NULL = {.length = 0, .data = NULL };
-const UA_ByteString UA_BYTESTRING_NULL = {.length = 0, .data = NULL };
-const UA_Guid UA_GUID_NULL = {.data1 = 0, .data2 = 0, .data3 = 0,
-                              .data4 = {0,0,0,0,0,0,0,0}};
-const UA_NodeId UA_NODEID_NULL = {0, UA_NODEIDTYPE_NUMERIC, {0}};
-const UA_ExpandedNodeId UA_EXPANDEDNODEID_NULL = {
-       .nodeId = {.namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC,
-                  .identifier.numeric = 0 },
-       .namespaceUri = {.length = 0, .data = NULL}, .serverIndex = 0 };
+/* Global definition of NULL type instances. These are always zeroed out, as
+ * mandated by the C/C++ standard for global values with no initializer. */
+const UA_String UA_STRING_NULL;
+const UA_ByteString UA_BYTESTRING_NULL;
+const UA_Guid UA_GUID_NULL;
+const UA_NodeId UA_NODEID_NULL;
+const UA_ExpandedNodeId UA_EXPANDEDNODEID_NULL;
+
+/* TODO: The standard-defined types are ordered. See if binary search is more
+ * efficient. */
+const UA_DataType *
+UA_findDataType(const UA_NodeId *typeId) {
+    for(size_t i = 0; i < UA_TYPES_COUNT; ++i) {
+        if(UA_TYPES[i].typeId.identifier.numeric == typeId->identifier.numeric)
+            return &UA_TYPES[i];
+    }
+    return NULL;
+}
 
 
 /***************************/
 /***************************/
 /* Random Number Generator */
 /* Random Number Generator */
@@ -52,11 +60,11 @@ UA_String_fromChars(char const src[]) {
     UA_String str = UA_STRING_NULL;
     UA_String str = UA_STRING_NULL;
     size_t length = strlen(src);
     size_t length = strlen(src);
     if(length > 0) {
     if(length > 0) {
-        str.data = UA_malloc(length);
+        str.data = (UA_Byte*)UA_malloc(length);
         if(!str.data)
         if(!str.data)
             return str;
             return str;
     } else {
     } else {
-        str.data = UA_EMPTY_ARRAY_SENTINEL;
+        str.data = (UA_Byte*)UA_EMPTY_ARRAY_SENTINEL;
     }
     }
     memcpy(str.data, src, length);
     memcpy(str.data, src, length);
     str.length = length;
     str.length = length;
@@ -113,7 +121,7 @@ UA_String
 UA_DateTime_toString(UA_DateTime t) {
 UA_DateTime_toString(UA_DateTime t) {
     UA_String str = UA_STRING_NULL;
     UA_String str = UA_STRING_NULL;
     // length of the string is 31 (plus \0 at the end)
     // length of the string is 31 (plus \0 at the end)
-    if(!(str.data = UA_malloc(32)))
+    if(!(str.data = (UA_Byte*)UA_malloc(32)))
         return str;
         return str;
     str.length = 31;
     str.length = 31;
     UA_DateTimeStruct tSt = UA_DateTime_toStruct(t);
     UA_DateTimeStruct tSt = UA_DateTime_toStruct(t);
@@ -171,7 +179,7 @@ UA_ByteString_allocBuffer(UA_ByteString *bs, size_t length) {
     UA_ByteString_init(bs);
     UA_ByteString_init(bs);
     if(length == 0)
     if(length == 0)
         return UA_STATUSCODE_GOOD;
         return UA_STATUSCODE_GOOD;
-    if(!(bs->data = UA_malloc(length)))
+    if(!(bs->data = (UA_Byte*)UA_malloc(length)))
         return UA_STATUSCODE_BADOUTOFMEMORY;
         return UA_STATUSCODE_BADOUTOFMEMORY;
     bs->length = length;
     bs->length = length;
     return UA_STATUSCODE_GOOD;
     return UA_STATUSCODE_GOOD;
@@ -375,16 +383,16 @@ UA_Variant_setScalar(UA_Variant *v, void * UA_RESTRICT p,
 UA_StatusCode
 UA_StatusCode
 UA_Variant_setScalarCopy(UA_Variant *v, const void *p,
 UA_Variant_setScalarCopy(UA_Variant *v, const void *p,
                          const UA_DataType *type) {
                          const UA_DataType *type) {
-    void *new = UA_malloc(type->memSize);
-    if(!new)
+    void *n = UA_malloc(type->memSize);
+    if(!n)
         return UA_STATUSCODE_BADOUTOFMEMORY;
         return UA_STATUSCODE_BADOUTOFMEMORY;
-    UA_StatusCode retval = UA_copy(p, new, type);
+    UA_StatusCode retval = UA_copy(p, n, type);
     if(retval != UA_STATUSCODE_GOOD) {
     if(retval != UA_STATUSCODE_GOOD) {
-        UA_free(new);
+        UA_free(n);
         //cppcheck-suppress memleak
         //cppcheck-suppress memleak
         return retval;
         return retval;
     }
     }
-    UA_Variant_setScalar(v, new, type);
+    UA_Variant_setScalar(v, n, type);
     //cppcheck-suppress memleak
     //cppcheck-suppress memleak
     return UA_STATUSCODE_GOOD;
     return UA_STATUSCODE_GOOD;
 }
 }
@@ -513,8 +521,7 @@ UA_Variant_copyRange(const UA_Variant *orig_src, UA_Variant *dst,
      * with in the "scalar" type that may define an array by itself (string,
      * with in the "scalar" type that may define an array by itself (string,
      * variant, ...). */
      * variant, ...). */
     UA_NumericRange thisrange, nextrange;
     UA_NumericRange thisrange, nextrange;
-    UA_NumericRangeDimension scalarThisDimension = (UA_NumericRangeDimension){
-        .min = 0, .max = 0}; /* a single entry */
+    UA_NumericRangeDimension scalarThisDimension = {0,0}; /* a single entry */
     if(isScalar) {
     if(isScalar) {
         /* Replace scalar src with array of length 1 */
         /* Replace scalar src with array of length 1 */
         arraySrc = *src;
         arraySrc = *src;
@@ -618,7 +625,7 @@ UA_Variant_copyRange(const UA_Variant *orig_src, UA_Variant *dst,
     dst->arrayLength = count;
     dst->arrayLength = count;
     if(src->arrayDimensionsSize > 0) {
     if(src->arrayDimensionsSize > 0) {
         dst->arrayDimensions =
         dst->arrayDimensions =
-            UA_Array_new(thisrange.dimensionsSize, &UA_TYPES[UA_TYPES_UINT32]);
+            (UA_UInt32*)UA_Array_new(thisrange.dimensionsSize, &UA_TYPES[UA_TYPES_UINT32]);
         if(!dst->arrayDimensions) {
         if(!dst->arrayDimensions) {
             Variant_deletemembers(dst, NULL);
             Variant_deletemembers(dst, NULL);
             return UA_STATUSCODE_BADOUTOFMEMORY;
             return UA_STATUSCODE_BADOUTOFMEMORY;
@@ -740,7 +747,7 @@ DiagnosticInfo_copy(UA_DiagnosticInfo const *src, UA_DiagnosticInfo *dst,
     if(src->hasAdditionalInfo)
     if(src->hasAdditionalInfo)
        retval = UA_String_copy(&src->additionalInfo, &dst->additionalInfo);
        retval = UA_String_copy(&src->additionalInfo, &dst->additionalInfo);
     if(src->hasInnerDiagnosticInfo && src->innerDiagnosticInfo) {
     if(src->hasInnerDiagnosticInfo && src->innerDiagnosticInfo) {
-        dst->innerDiagnosticInfo = UA_malloc(sizeof(UA_DiagnosticInfo));
+        dst->innerDiagnosticInfo = (UA_DiagnosticInfo*)UA_malloc(sizeof(UA_DiagnosticInfo));
         if(dst->innerDiagnosticInfo) {
         if(dst->innerDiagnosticInfo) {
             retval |= DiagnosticInfo_copy(src->innerDiagnosticInfo,
             retval |= DiagnosticInfo_copy(src->innerDiagnosticInfo,
                                           dst->innerDiagnosticInfo, NULL);
                                           dst->innerDiagnosticInfo, NULL);

+ 9 - 9
src/ua_types_encoding_binary.c

@@ -40,13 +40,13 @@
 
 
 /* Jumptables for de-/encoding and computing the buffer length */
 /* Jumptables for de-/encoding and computing the buffer length */
 typedef UA_StatusCode (*UA_encodeBinarySignature)(const void *UA_RESTRICT src, const UA_DataType *type);
 typedef UA_StatusCode (*UA_encodeBinarySignature)(const void *UA_RESTRICT src, const UA_DataType *type);
-static const UA_encodeBinarySignature encodeBinaryJumpTable[UA_BUILTIN_TYPES_COUNT + 1];
+extern const UA_encodeBinarySignature encodeBinaryJumpTable[UA_BUILTIN_TYPES_COUNT + 1];
 
 
 typedef UA_StatusCode (*UA_decodeBinarySignature)(void *UA_RESTRICT dst, const UA_DataType *type);
 typedef UA_StatusCode (*UA_decodeBinarySignature)(void *UA_RESTRICT dst, const UA_DataType *type);
-static const UA_decodeBinarySignature decodeBinaryJumpTable[UA_BUILTIN_TYPES_COUNT + 1];
+extern const UA_decodeBinarySignature decodeBinaryJumpTable[UA_BUILTIN_TYPES_COUNT + 1];
 
 
 typedef size_t (*UA_calcSizeBinarySignature)(const void *UA_RESTRICT p, const UA_DataType *contenttype);
 typedef size_t (*UA_calcSizeBinarySignature)(const void *UA_RESTRICT p, const UA_DataType *contenttype);
-static const UA_calcSizeBinarySignature calcSizeBinaryJumpTable[UA_BUILTIN_TYPES_COUNT + 1];
+extern const UA_calcSizeBinarySignature calcSizeBinaryJumpTable[UA_BUILTIN_TYPES_COUNT + 1];
 
 
 /* We give pointers to the current position and the last position in the buffer
 /* We give pointers to the current position and the last position in the buffer
    instead of a string with an offset. */
    instead of a string with an offset. */
@@ -831,11 +831,11 @@ ExtensionObject_decodeBinary(UA_ExtensionObject *dst, const UA_DataType *_) {
     }
     }
 
 
     if(encoding == UA_EXTENSIONOBJECT_ENCODED_NOBODY) {
     if(encoding == UA_EXTENSIONOBJECT_ENCODED_NOBODY) {
-        dst->encoding = encoding;
+        dst->encoding = (UA_ExtensionObjectEncoding)encoding;
         dst->content.encoded.typeId = typeId;
         dst->content.encoded.typeId = typeId;
         dst->content.encoded.body = UA_BYTESTRING_NULL;
         dst->content.encoded.body = UA_BYTESTRING_NULL;
     } else if(encoding == UA_EXTENSIONOBJECT_ENCODED_XML) {
     } else if(encoding == UA_EXTENSIONOBJECT_ENCODED_XML) {
-        dst->encoding = encoding;
+        dst->encoding = (UA_ExtensionObjectEncoding)encoding;
         dst->content.encoded.typeId = typeId;
         dst->content.encoded.typeId = typeId;
         retval = ByteString_decodeBinary(&dst->content.encoded.body);
         retval = ByteString_decodeBinary(&dst->content.encoded.body);
     } else {
     } else {
@@ -1140,7 +1140,7 @@ DiagnosticInfo_decodeBinary(UA_DiagnosticInfo *dst, const UA_DataType *_) {
     }
     }
     if(encodingMask & 0x40) {
     if(encodingMask & 0x40) {
         /* innerDiagnosticInfo is allocated on the heap */
         /* innerDiagnosticInfo is allocated on the heap */
-        dst->innerDiagnosticInfo = UA_calloc(1, sizeof(UA_DiagnosticInfo));
+        dst->innerDiagnosticInfo = (UA_DiagnosticInfo*)UA_calloc(1, sizeof(UA_DiagnosticInfo));
         if(!dst->innerDiagnosticInfo)
         if(!dst->innerDiagnosticInfo)
             return UA_STATUSCODE_BADOUTOFMEMORY;
             return UA_STATUSCODE_BADOUTOFMEMORY;
         dst->hasInnerDiagnosticInfo = true;
         dst->hasInnerDiagnosticInfo = true;
@@ -1159,7 +1159,7 @@ UA_encodeBinaryInternal(const void *src, const UA_DataType *type);
 static UA_StatusCode
 static UA_StatusCode
 UA_decodeBinaryInternal(void *dst, const UA_DataType *type);
 UA_decodeBinaryInternal(void *dst, const UA_DataType *type);
 
 
-static const UA_encodeBinarySignature encodeBinaryJumpTable[UA_BUILTIN_TYPES_COUNT + 1] = {
+const UA_encodeBinarySignature encodeBinaryJumpTable[UA_BUILTIN_TYPES_COUNT + 1] = {
     (UA_encodeBinarySignature)Boolean_encodeBinary,
     (UA_encodeBinarySignature)Boolean_encodeBinary,
     (UA_encodeBinarySignature)Byte_encodeBinary, // SByte
     (UA_encodeBinarySignature)Byte_encodeBinary, // SByte
     (UA_encodeBinarySignature)Byte_encodeBinary,
     (UA_encodeBinarySignature)Byte_encodeBinary,
@@ -1244,7 +1244,7 @@ UA_encodeBinary(const void *src, const UA_DataType *type,
     return retval;
     return retval;
 }
 }
 
 
-static const UA_decodeBinarySignature decodeBinaryJumpTable[UA_BUILTIN_TYPES_COUNT + 1] = {
+const UA_decodeBinarySignature decodeBinaryJumpTable[UA_BUILTIN_TYPES_COUNT + 1] = {
     (UA_decodeBinarySignature)Boolean_decodeBinary,
     (UA_decodeBinarySignature)Boolean_decodeBinary,
     (UA_decodeBinarySignature)Byte_decodeBinary, // SByte
     (UA_decodeBinarySignature)Byte_decodeBinary, // SByte
     (UA_decodeBinarySignature)Byte_decodeBinary,
     (UA_decodeBinarySignature)Byte_decodeBinary,
@@ -1514,7 +1514,7 @@ DiagnosticInfo_calcSizeBinary(const UA_DiagnosticInfo *src, UA_DataType *_) {
     return s;
     return s;
 }
 }
 
 
-static const UA_calcSizeBinarySignature calcSizeBinaryJumpTable[UA_BUILTIN_TYPES_COUNT + 1] = {
+const UA_calcSizeBinarySignature calcSizeBinaryJumpTable[UA_BUILTIN_TYPES_COUNT + 1] = {
     (UA_calcSizeBinarySignature)calcSizeBinaryMemSize, // Boolean
     (UA_calcSizeBinarySignature)calcSizeBinaryMemSize, // Boolean
     (UA_calcSizeBinarySignature)calcSizeBinaryMemSize, // Byte
     (UA_calcSizeBinarySignature)calcSizeBinaryMemSize, // Byte
     (UA_calcSizeBinarySignature)calcSizeBinaryMemSize,
     (UA_calcSizeBinarySignature)calcSizeBinaryMemSize,

+ 63 - 3
src/ua_util.h

@@ -7,6 +7,9 @@
 #include <assert.h>
 #include <assert.h>
 #define UA_assert(ignore) assert(ignore)
 #define UA_assert(ignore) assert(ignore)
 
 
+/* BSD Queue Macros */
+#include "queue.h"
+
 /* container_of */
 /* container_of */
 #define container_of(ptr, type, member) \
 #define container_of(ptr, type, member) \
     (type *)((uintptr_t)ptr - offsetof(type,member))
     (type *)((uintptr_t)ptr - offsetof(type,member))
@@ -23,12 +26,69 @@
 #  warning The compiler does not allow thread-local variables. The library can be built, but will not be thread-safe.
 #  warning The compiler does not allow thread-local variables. The library can be built, but will not be thread-safe.
 # endif
 # endif
 #endif
 #endif
-
 #ifndef UA_THREAD_LOCAL
 #ifndef UA_THREAD_LOCAL
 # define UA_THREAD_LOCAL
 # define UA_THREAD_LOCAL
 #endif
 #endif
 
 
-/* BSD Queue Macros */
-#include "queue.h"
+/* Atomic Operations
+ * -----------------
+ * Atomic operations that synchronize across processor cores (for
+ * multithreading). Only the inline-functions defined next are used. Replace
+ * with architecture-specific operations if necessary. */
+#ifndef UA_ENABLE_MULTITHREADING
+# define UA_atomic_sync()
+#else
+# ifdef _MSC_VER /* Visual Studio */
+#  define UA_atomic_sync() _ReadWriteBarrier()
+# else /* GCC/Clang */
+#  define UA_atomic_sync() __sync_synchronize()
+# endif
+#endif
+
+static UA_INLINE void *
+UA_atomic_xchg(void * volatile * addr, void *newptr) {
+#ifndef UA_ENABLE_MULTITHREADING
+    void *old = *addr;
+    *addr = newptr;
+    return old;
+#else
+# ifdef _MSC_VER /* Visual Studio */
+    return _InterlockedExchangePointer(addr, newptr);
+# else /* GCC/Clang */
+    return __sync_lock_test_and_set(addr, newptr);
+# endif
+#endif
+}
+
+static UA_INLINE void *
+UA_atomic_cmpxchg(void * volatile * addr, void *expected, void *newptr) {
+#ifndef UA_ENABLE_MULTITHREADING
+    void *old = *addr;
+    if(old == expected) {
+        *addr = newptr;
+    }
+    return old;
+#else
+# ifdef _MSC_VER /* Visual Studio */
+    return _InterlockedCompareExchangePointer(addr, expected, newptr);
+# else /* GCC/Clang */
+    return __sync_val_compare_and_swap(addr, expected, newptr);
+# endif
+#endif
+}
+
+static UA_INLINE uint32_t
+UA_atomic_add(volatile uint32_t *addr, uint32_t increase) {
+#ifndef UA_ENABLE_MULTITHREADING
+    *addr += increase;
+    return *addr;
+#else
+# ifdef _MSC_VER /* Visual Studio */
+    return _InterlockedExchangeAdd(addr, increase) + increase;
+# else /* GCC/Clang */
+    return __sync_add_and_fetch(addr, increase);
+# endif
+#endif
+}
 
 
 #endif /* UA_UTIL_H_ */
 #endif /* UA_UTIL_H_ */

+ 19 - 14
tests/CMakeLists.txt

@@ -7,11 +7,14 @@ include_directories(${CHECK_INCLUDE_DIRS})
 
 
 find_package(Check REQUIRED)
 find_package(Check REQUIRED)
 find_package(Threads REQUIRED)
 find_package(Threads REQUIRED)
+if(UA_ENABLE_VALGRIND_UNIT_TESTS)
+    find_package(Valgrind REQUIRED)
+endif()
 
 
 set(LIBS ${CHECK_LIBRARIES} ${open62541_LIBRARIES} open62541)
 set(LIBS ${CHECK_LIBRARIES} ${open62541_LIBRARIES} open62541)
 if(NOT WIN32)
 if(NOT WIN32)
   list(APPEND LIBS pthread m)
   list(APPEND LIBS pthread m)
-  if (NOT APPLE)
+  if(NOT APPLE)
     list(APPEND LIBS rt subunit)
     list(APPEND LIBS rt subunit)
   endif()
   endif()
 else()
 else()
@@ -25,16 +28,14 @@ if(CMAKE_COMPILER_IS_GNUCC OR "x${CMAKE_C_COMPILER_ID}" STREQUAL "xClang")
     add_definitions(-Wno-sign-conversion)
     add_definitions(-Wno-sign-conversion)
 endif()
 endif()
 
 
-# Valgrind definition
-set(UA_TEST_WITH_VALGRIND ON)
-SET(VALGRIND_FLAGS --quiet --trace-children=yes --leak-check=full)
+# Unit Test Definition Macro
+set(VALGRIND_FLAGS --quiet --trace-children=yes --leak-check=full)
 macro(add_test_valgrind TEST_NAME)
 macro(add_test_valgrind TEST_NAME)
-    IF(UA_TEST_WITH_VALGRIND)
-        add_test(${TEST_NAME}
-                valgrind --error-exitcode=1 ${VALGRIND_FLAGS} ${ARGN} )
-    ELSE()
+    if(UA_ENABLE_VALGRIND_UNIT_TESTS)
+        add_test(${TEST_NAME} valgrind --error-exitcode=1 ${VALGRIND_FLAGS} ${ARGN})
+    else()
         add_test(${TEST_NAME} ${ARGN})
         add_test(${TEST_NAME} ${ARGN})
-    ENDIF()
+    endif()
 endmacro()
 endmacro()
 
 
 # the unit test are built directly on the open62541 object files. so they can
 # the unit test are built directly on the open62541 object files. so they can
@@ -56,6 +57,12 @@ add_executable(check_chunking check_chunking.c $<TARGET_OBJECTS:open62541-object
 target_link_libraries(check_chunking ${LIBS})
 target_link_libraries(check_chunking ${LIBS})
 add_test_valgrind(chunking ${CMAKE_CURRENT_BINARY_DIR}/check_chunking)
 add_test_valgrind(chunking ${CMAKE_CURRENT_BINARY_DIR}/check_chunking)
 
 
+add_executable(check_utils check_utils.c $<TARGET_OBJECTS:open62541-object>)
+target_link_libraries(check_utils ${LIBS})
+add_test_valgrind(check_utils ${CMAKE_CURRENT_BINARY_DIR}/check_utils)
+
+# Test Server
+
 add_executable(check_services_view check_services_view.c $<TARGET_OBJECTS:open62541-object>)
 add_executable(check_services_view check_services_view.c $<TARGET_OBJECTS:open62541-object>)
 target_link_libraries(check_services_view ${LIBS})
 target_link_libraries(check_services_view ${LIBS})
 add_test_valgrind(services_view ${CMAKE_CURRENT_BINARY_DIR}/check_services_view)
 add_test_valgrind(services_view ${CMAKE_CURRENT_BINARY_DIR}/check_services_view)
@@ -92,7 +99,7 @@ add_executable(check_discovery check_discovery.c $<TARGET_OBJECTS:open62541-obje
 target_link_libraries(check_discovery ${LIBS})
 target_link_libraries(check_discovery ${LIBS})
 add_test_valgrind(discovery ${CMAKE_CURRENT_BINARY_DIR}/check_discovery)
 add_test_valgrind(discovery ${CMAKE_CURRENT_BINARY_DIR}/check_discovery)
 
 
-# test with canned interactions from files
+# Test server with network dumps from files
 
 
 add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/client_HELOPN.bin
 add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/client_HELOPN.bin
                           ${CMAKE_CURRENT_BINARY_DIR}/client_CLO.bin
                           ${CMAKE_CURRENT_BINARY_DIR}/client_CLO.bin
@@ -141,6 +148,8 @@ add_test_valgrind(check_server_binary_messages_write ${CMAKE_CURRENT_BINARY_DIR}
                                            ${CMAKE_CURRENT_BINARY_DIR}/client_Write.bin
                                            ${CMAKE_CURRENT_BINARY_DIR}/client_Write.bin
                                            ${CMAKE_CURRENT_BINARY_DIR}/client_CLO.bin)
                                            ${CMAKE_CURRENT_BINARY_DIR}/client_CLO.bin)
 
 
+# Test Client
+
 add_executable(check_client check_client.c $<TARGET_OBJECTS:open62541-object>)
 add_executable(check_client check_client.c $<TARGET_OBJECTS:open62541-object>)
 target_link_libraries(check_client ${LIBS})
 target_link_libraries(check_client ${LIBS})
 add_test_valgrind(check_client ${CMAKE_CURRENT_BINARY_DIR}/check_client)
 add_test_valgrind(check_client ${CMAKE_CURRENT_BINARY_DIR}/check_client)
@@ -148,7 +157,3 @@ add_test_valgrind(check_client ${CMAKE_CURRENT_BINARY_DIR}/check_client)
 add_executable(check_client_subscriptions check_client_subscriptions.c $<TARGET_OBJECTS:open62541-object>)
 add_executable(check_client_subscriptions check_client_subscriptions.c $<TARGET_OBJECTS:open62541-object>)
 target_link_libraries(check_client_subscriptions ${LIBS})
 target_link_libraries(check_client_subscriptions ${LIBS})
 add_test_valgrind(check_client_subscriptions ${CMAKE_CURRENT_BINARY_DIR}/check_client_subscriptions)
 add_test_valgrind(check_client_subscriptions ${CMAKE_CURRENT_BINARY_DIR}/check_client_subscriptions)
-
-add_executable(check_utils check_utils.c $<TARGET_OBJECTS:open62541-object>)
-target_link_libraries(check_utils ${LIBS})
-add_test_valgrind(check_utils ${CMAKE_CURRENT_BINARY_DIR}/check_utils)

+ 1 - 0
tests/check_services_attributes.c

@@ -150,6 +150,7 @@ START_TEST(ReadSingleAttributeValueWithoutTimestamp) {
     rReq.nodesToRead[0].nodeId = UA_NODEID_STRING_ALLOC(1, "the.answer");
     rReq.nodesToRead[0].nodeId = UA_NODEID_STRING_ALLOC(1, "the.answer");
     rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_VALUE;
     rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_VALUE;
     Service_Read_single(server, &adminSession, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0], &resp);
     Service_Read_single(server, &adminSession, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0], &resp);
+    ck_assert_int_eq(resp.status, UA_STATUSCODE_GOOD);
     ck_assert_int_eq(0, resp.value.arrayLength);
     ck_assert_int_eq(0, resp.value.arrayLength);
     ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_INT32], resp.value.type);
     ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_INT32], resp.value.type);
     ck_assert_int_eq(42, *(UA_Int32* )resp.value.data);
     ck_assert_int_eq(42, *(UA_Int32* )resp.value.data);

+ 1 - 1
tools/amalgamate.py

@@ -75,7 +75,7 @@ if not is_c:
 } // extern "C"
 } // extern "C"
 #endif
 #endif
 
 
-#endif /* %s */''' % (outname.upper() + u"_H_"))
+#endif /* %s */\n''' % (outname.upper() + u"_H_"))
 file.close()
 file.close()
 
 
 print ("The size of "+args.outfile+" is "+ str(os.path.getsize(args.outfile))+" Bytes.")
 print ("The size of "+args.outfile+" is "+ str(os.path.getsize(args.outfile))+" Bytes.")

+ 10 - 5
tools/c2rst.py

@@ -48,11 +48,16 @@ def first_line(c):
 def last_line(c):
 def last_line(c):
     "Searches for the latest ifdef (closing the include guard)"
     "Searches for the latest ifdef (closing the include guard)"
     last = 1
     last = 1
-    for i in range(1, len(c)):
+    for i in range(len(c)-1,1,-1):
         m = re.search("^#ifdef", c[i])
         m = re.search("^#ifdef", c[i])
         if m:
         if m:
             last = i
             last = i
-    return last
+            break
+    # skip empty lines at the end
+    for i in range(last-1,1,-1):
+        if len(c[i].strip()) > 0:
+            return i
+    return 1
 
 
 if len(sys.argv) < 2:
 if len(sys.argv) < 2:
     print("Usage: python c2rst.py input.c/h output.rst")
     print("Usage: python c2rst.py input.c/h output.rst")
@@ -63,7 +68,8 @@ with open(sys.argv[1]) as f:
 
 
 with open(sys.argv[2], 'w') as rst:
 with open(sys.argv[2], 'w') as rst:
     in_doc = False
     in_doc = False
-    for i in range(first_line(c), last_line(c)):
+    last = last_line(c)
+    for i in range(first_line(c), last+1):
         line = c[i]
         line = c[i]
         doc_start = False
         doc_start = False
         doc_end = False
         doc_end = False
@@ -84,7 +90,6 @@ with open(sys.argv[2], 'w') as rst:
                 line = "   " + line
                 line = "   " + line
             rst.write(clean_line(line))
             rst.write(clean_line(line))
 
 
-        if doc_end:
+        if doc_end and i < last:
             rst.write("\n.. code-block:: c\n\n")
             rst.write("\n.. code-block:: c\n\n")
             in_doc = False
             in_doc = False
-    rst.write("\n")

+ 18 - 0
tools/cmake/FindValgrind.cmake

@@ -0,0 +1,18 @@
+# Find Valgrind.
+#
+# This module defines:
+#  VALGRIND_INCLUDE_DIR, where to find valgrind/memcheck.h, etc.
+#  VALGRIND_PROGRAM, the valgrind executable.
+#  VALGRIND_FOUND, If false, do not try to use valgrind.
+#
+# If you have valgrind installed in a non-standard place, you can define
+# VALGRIND_PREFIX to tell cmake where it is.
+
+find_path(VALGRIND_INCLUDE_DIR memcheck.h
+  /usr/include /usr/include/valgrind /usr/local/include /usr/local/include/valgrind
+  ${VALGRIND_PREFIX}/include ${VALGRIND_PREFIX}/include/valgrind)
+find_program(VALGRIND_PROGRAM NAMES valgrind PATH /usr/bin /usr/local/bin ${VALGRIND_PREFIX}/bin)
+
+find_package_handle_standard_args(VALGRIND DEFAULT_MSG VALGRIND_INCLUDE_DIR VALGRIND_PROGRAM)
+
+mark_as_advanced(VALGRIND_INCLUDE_DIR VALGRIND_PROGRAM)

+ 2 - 2
tools/generate_datatypes.py

@@ -461,7 +461,7 @@ extern "C" {
 
 
 #include "''' + outname + '''_generated.h"
 #include "''' + outname + '''_generated.h"
 
 
-#if defined(__GNUC__) && __GNUC__ <= 4
+#if defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 6
 # pragma GCC diagnostic push
 # pragma GCC diagnostic push
 # pragma GCC diagnostic ignored "-Wmissing-field-initializers"
 # pragma GCC diagnostic ignored "-Wmissing-field-initializers"
 # pragma GCC diagnostic ignored "-Wmissing-braces"
 # pragma GCC diagnostic ignored "-Wmissing-braces"
@@ -473,7 +473,7 @@ for t in iter_types(types):
     printf(t.functions_c())
     printf(t.functions_c())
 
 
 printf('''
 printf('''
-#if defined(__GNUC__) && __GNUC__ <= 4
+#if defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 6
 # pragma GCC diagnostic pop
 # pragma GCC diagnostic pop
 #endif
 #endif
 
 

+ 1 - 0
tools/pyUANamespace/ua_namespace.py

@@ -676,6 +676,7 @@ class opcua_namespace():
     code.append('#include "'+outfilename+'.h"')
     code.append('#include "'+outfilename+'.h"')
     code.append("UA_INLINE UA_StatusCode "+outfilename+"(UA_Server *server) {")
     code.append("UA_INLINE UA_StatusCode "+outfilename+"(UA_Server *server) {")
     code.append('UA_StatusCode retval = UA_STATUSCODE_GOOD; ')
     code.append('UA_StatusCode retval = UA_STATUSCODE_GOOD; ')
+    code.append('if(retval == UA_STATUSCODE_GOOD){retval = UA_STATUSCODE_GOOD;} //ensure that retval is used');
 
 
     # Before printing nodes, we need to request additional namespace arrays from the server
     # Before printing nodes, we need to request additional namespace arrays from the server
     for nsid in self.namespaceIdentifiers:
     for nsid in self.namespaceIdentifiers:

+ 11 - 8
tools/travis/travis_linux_script.sh

@@ -10,7 +10,7 @@ if [ $ANALYZE = "true" ]; then
         scan-build-3.7 -enable-checker security.FloatLoopCounter \
         scan-build-3.7 -enable-checker security.FloatLoopCounter \
           -enable-checker security.insecureAPI.UncheckedReturn \
           -enable-checker security.insecureAPI.UncheckedReturn \
           --status-bugs -v \
           --status-bugs -v \
-          make -j 8
+          make -j
         cd .. && rm build -rf
         cd .. && rm build -rf
 
 
         mkdir -p build
         mkdir -p build
@@ -19,7 +19,7 @@ if [ $ANALYZE = "true" ]; then
         scan-build-3.7 -enable-checker security.FloatLoopCounter \
         scan-build-3.7 -enable-checker security.FloatLoopCounter \
           -enable-checker security.insecureAPI.UncheckedReturn \
           -enable-checker security.insecureAPI.UncheckedReturn \
           --status-bugs -v \
           --status-bugs -v \
-          make -j 8
+          make -j
         cd .. && rm build -rf
         cd .. && rm build -rf
     else
     else
         cppcheck --template "{file}({line}): {severity} ({id}): {message}" \
         cppcheck --template "{file}({line}): {severity} ({id}): {message}" \
@@ -41,8 +41,10 @@ else
     cd build
     cd build
     cmake -DCMAKE_BUILD_TYPE=Release -DUA_BUILD_EXAMPLES=ON -DUA_BUILD_DOCUMENTATION=ON -DUA_BUILD_SELFSIGNED_CERTIFICATE=ON ..
     cmake -DCMAKE_BUILD_TYPE=Release -DUA_BUILD_EXAMPLES=ON -DUA_BUILD_DOCUMENTATION=ON -DUA_BUILD_SELFSIGNED_CERTIFICATE=ON ..
     make doc
     make doc
+    make doc_pdf
     make selfsigned
     make selfsigned
     cp -r doc ../../
     cp -r doc ../../
+    cp -r doc_latex ../../
     cp ./examples/server_cert.der ../../
     cp ./examples/server_cert.der ../../
     cd .. && rm build -rf
     cd .. && rm build -rf
     
     
@@ -50,7 +52,7 @@ else
     mkdir -p build
     mkdir -p build
     cd build
     cd build
     cmake -DCMAKE_BUILD_TYPE=Debug -DUA_ENABLE_GENERATE_NAMESPACE0=On -DUA_BUILD_EXAMPLES=ON  ..
     cmake -DCMAKE_BUILD_TYPE=Debug -DUA_ENABLE_GENERATE_NAMESPACE0=On -DUA_BUILD_EXAMPLES=ON  ..
-    make -j8
+    make -j
     cd .. && rm build -rf
     cd .. && rm build -rf
     
     
     # cross compilation only with gcc
     # cross compilation only with gcc
@@ -59,7 +61,7 @@ else
         mkdir -p build && cd build
         mkdir -p build && cd build
         cmake -DCMAKE_TOOLCHAIN_FILE=../tools/cmake/Toolchain-mingw32.cmake -DUA_ENABLE_AMALGAMATION=ON -DCMAKE_BUILD_TYPE=Release -DUA_BUILD_EXAMPLES=ON ..
         cmake -DCMAKE_TOOLCHAIN_FILE=../tools/cmake/Toolchain-mingw32.cmake -DUA_ENABLE_AMALGAMATION=ON -DCMAKE_BUILD_TYPE=Release -DUA_BUILD_EXAMPLES=ON ..
         make -j
         make -j
-        zip -r open62541-win32.zip ../../doc ../../server_cert.der ../LICENSE ../AUTHORS ../README.md examples/server.exe examples/client.exe libopen62541.dll.a open62541.h open62541.c
+        zip -r open62541-win32.zip ../../doc ../../doc_latex/open62541.pdf ../../server_cert.der ../LICENSE ../AUTHORS ../README.md examples/server.exe examples/client.exe libopen62541.dll.a open62541.h open62541.c
         cp open62541-win32.zip ..
         cp open62541-win32.zip ..
         cd .. && rm build -rf
         cd .. && rm build -rf
 
 
@@ -67,7 +69,7 @@ else
         mkdir -p build && cd build
         mkdir -p build && cd build
         cmake -DCMAKE_TOOLCHAIN_FILE=../tools/cmake/Toolchain-mingw64.cmake -DUA_ENABLE_AMALGAMATION=ON -DCMAKE_BUILD_TYPE=Release -DUA_BUILD_EXAMPLES=ON ..
         cmake -DCMAKE_TOOLCHAIN_FILE=../tools/cmake/Toolchain-mingw64.cmake -DUA_ENABLE_AMALGAMATION=ON -DCMAKE_BUILD_TYPE=Release -DUA_BUILD_EXAMPLES=ON ..
         make -j
         make -j
-        zip -r open62541-win64.zip ../../doc ../../server_cert.der ../LICENSE ../AUTHORS ../README.md examples/server.exe examples/client.exe libopen62541.dll.a open62541.h open62541.c
+        zip -r open62541-win64.zip ../../doc ../../doc_latex/open62541.pdf ../../server_cert.der ../LICENSE ../AUTHORS ../README.md examples/server.exe examples/client.exe libopen62541.dll.a open62541.h open62541.c
         cp open62541-win64.zip ..
         cp open62541-win64.zip ..
         cd .. && rm build -rf
         cd .. && rm build -rf
 
 
@@ -75,7 +77,7 @@ else
         mkdir -p build && cd build
         mkdir -p build && cd build
         cmake -DCMAKE_TOOLCHAIN_FILE=../tools/cmake/Toolchain-gcc-m32.cmake -DUA_ENABLE_AMALGAMATION=ON -DCMAKE_BUILD_TYPE=Release -DUA_BUILD_EXAMPLES=ON ..
         cmake -DCMAKE_TOOLCHAIN_FILE=../tools/cmake/Toolchain-gcc-m32.cmake -DUA_ENABLE_AMALGAMATION=ON -DCMAKE_BUILD_TYPE=Release -DUA_BUILD_EXAMPLES=ON ..
         make -j
         make -j
-        tar -pczf open62541-linux32.tar.gz ../../doc ../../server_cert.der ../LICENSE ../AUTHORS ../README.md examples/server examples/client libopen62541.a open62541.h open62541.c
+        tar -pczf open62541-linux32.tar.gz ../../doc ../../doc_latex/open62541.pdf ../../server_cert.der ../LICENSE ../AUTHORS ../README.md examples/server examples/client libopen62541.a open62541.h open62541.c
         cp open62541-linux32.tar.gz ..
         cp open62541-linux32.tar.gz ..
         cd .. && rm build -rf
         cd .. && rm build -rf
     fi
     fi
@@ -84,7 +86,7 @@ else
     mkdir -p build && cd build
     mkdir -p build && cd build
     cmake -DCMAKE_BUILD_TYPE=Release -DUA_ENABLE_AMALGAMATION=ON -DUA_BUILD_EXAMPLES=ON ..
     cmake -DCMAKE_BUILD_TYPE=Release -DUA_ENABLE_AMALGAMATION=ON -DUA_BUILD_EXAMPLES=ON ..
     make -j
     make -j
-    tar -pczf open62541-linux64.tar.gz ../../doc ../../server_cert.der ../LICENSE ../AUTHORS ../README.md examples/server examples/client libopen62541.a open62541.h open62541.c
+    tar -pczf open62541-linux64.tar.gz ../../doc ../../doc_latex/open62541.pdf ../../server_cert.der ../LICENSE ../AUTHORS ../README.md examples/server examples/client libopen62541.a open62541.h open62541.c
     cp open62541-linux64.tar.gz ..
     cp open62541-linux64.tar.gz ..
     cp open62541.h .. # copy single file-release
     cp open62541.h .. # copy single file-release
     cp open62541.c .. # copy single file-release
     cp open62541.c .. # copy single file-release
@@ -114,11 +116,12 @@ else
     make -j
     make -j
     cd .. && rm build -rf
     cd .. && rm build -rf
 
 
-    #this run inclides full examples and methodcalls
     echo "Debug build and unit tests (64 bit)"
     echo "Debug build and unit tests (64 bit)"
     mkdir -p build && cd build
     mkdir -p build && cd build
     cmake -DCMAKE_BUILD_TYPE=Debug -DUA_BUILD_EXAMPLES=ON -DUA_BUILD_UNIT_TESTS=ON -DUA_ENABLE_COVERAGE=ON ..
     cmake -DCMAKE_BUILD_TYPE=Debug -DUA_BUILD_EXAMPLES=ON -DUA_BUILD_UNIT_TESTS=ON -DUA_ENABLE_COVERAGE=ON ..
     make -j && make test ARGS="-V"
     make -j && make test ARGS="-V"
+    cmake -DCMAKE_BUILD_TYPE=Debug -DUA_BUILD_EXAMPLES=ON -DUA_BUILD_UNIT_TESTS=ON -DUA_ENABLE_COVERAGE=ON -DUA_ENABLE_VALGRIND_UNIT_TESTS=ON ..
+    make -j && make test ARGS="-V"
     echo "Run valgrind to see if the server leaks memory (just starting up and closing..)"
     echo "Run valgrind to see if the server leaks memory (just starting up and closing..)"
     (valgrind --leak-check=yes --error-exitcode=3 ./examples/server & export pid=$!; sleep 2; kill -INT $pid; wait $pid);
     (valgrind --leak-check=yes --error-exitcode=3 ./examples/server & export pid=$!; sleep 2; kill -INT $pid; wait $pid);
     # only run coveralls on main repo, otherwise it fails uploading the files
     # only run coveralls on main repo, otherwise it fails uploading the files

+ 2 - 2
tools/travis/travis_push_doc.sh

@@ -5,9 +5,9 @@ git clone --depth=5 -b gh-pages https://$GITAUTH@github.com/open62541/open62541-
 cd open62541-www
 cd open62541-www
 
 
 rm -r -f ./doc/current/* || true # ignore result
 rm -r -f ./doc/current/* || true # ignore result
-mkdir ./doc/current
 cp -r ../../doc/* ./doc/current/
 cp -r ../../doc/* ./doc/current/
-git add -A ./doc/current
+cp -r ../../doc_latex/open62541.pdf ./doc/open62541-current.pdf
+git add -A ./doc
 git config --global user.email "open62541-travis-ci@users.noreply.github.com"
 git config --global user.email "open62541-travis-ci@users.noreply.github.com"
 git config --global user.name "Open62541 travis-ci"
 git config --global user.name "Open62541 travis-ci"
 git config --global push.default simple
 git config --global push.default simple

+ 3 - 1
tools/travis/travis_push_release.sh

@@ -25,9 +25,11 @@ if [ ! -e "$TAG.zip" ]; then
     #create a zip for single-file release and copy the files
     #create a zip for single-file release and copy the files
     cp ../../open62541.c .
     cp ../../open62541.c .
     cp ../../open62541.h .
     cp ../../open62541.h .
-    zip -r "$TAG.zip" open62541.c open62541.h
+    cp ../../../doc_latex/open62541.pdf .
+    zip -r "$TAG.zip" open62541.c open62541.h open62541.pdf
     rm open62541.c
     rm open62541.c
     rm open62541.h
     rm open62541.h
+    rm open62541.pdf
     git add "$TAG.zip"
     git add "$TAG.zip"
 
 
     echo "$TAG.zip" | cat - raw.txt > temp && mv temp raw.txt
     echo "$TAG.zip" | cat - raw.txt > temp && mv temp raw.txt