Переглянути джерело

Merge branch 'master' into feature/architectures

Jose Cabral 5 роки тому
батько
коміт
ae149eb6d5
66 змінених файлів з 2376 додано та 1303 видалено
  1. 167 45
      .travis.yml
  2. 8 5
      CMakeLists.txt
  3. 72 59
      FEATURES.md
  4. 1 1
      doc/building.rst
  5. 3 3
      examples/CMakeLists.txt
  6. 11 1
      examples/nodeset/CMakeLists.txt
  7. 13 0
      examples/nodeset/server_nodeset.c
  8. 5 0
      examples/nodeset/server_nodeset.csv
  9. 1 1
      include/ua_client_config.h
  10. 2 0
      include/ua_config.h.in
  11. 46 14
      include/ua_server.h
  12. 24 0
      include/ua_server_config.h
  13. 4 0
      include/ua_types.h
  14. 29 1
      plugins/ua_config_default.c
  15. 4 0
      plugins/ua_debug_dump_pkgs.c
  16. 0 10
      plugins/ua_network_pubsub_udp.c
  17. 14 14
      src/client/ua_client_connect_async.c
  18. 185 135
      src/pubsub/ua_pubsub.c
  19. 6 3
      src/pubsub/ua_pubsub.h
  20. 4 4
      src/pubsub/ua_pubsub_networkmessage.c
  21. 2 2
      src/server/ua_mdns.c
  22. 17 0
      src/server/ua_server.c
  23. 8 5
      src/server/ua_server_binary.c
  24. 1 3
      src/server/ua_server_internal.h
  25. 63 1
      src/server/ua_server_ns0.c
  26. 4 77
      src/server/ua_server_utils.c
  27. 0 1
      src/server/ua_services_attribute.c
  28. 53 29
      src/server/ua_services_call.c
  29. 2 5
      src/server/ua_services_nodemanagement.c
  30. 2 6
      src/server/ua_services_subscription.c
  31. 71 16
      src/server/ua_services_view.c
  32. 182 128
      src/server/ua_subscription.c
  33. 19 6
      src/server/ua_subscription.h
  34. 47 71
      src/server/ua_subscription_datachange.c
  35. 195 231
      src/server/ua_subscription_events.c
  36. 82 5
      src/ua_types.c
  37. 11 11
      src/ua_types_encoding_binary.c
  38. 11 2
      src/ua_util.c
  39. 7 153
      tests/CMakeLists.txt
  40. 4 4
      tests/check_types_custom.c
  41. 3 3
      tests/fuzz/generate_corpus.sh
  42. 172 0
      tests/nodeset-compiler/CMakeLists.txt
  43. 0 0
      tests/nodeset-compiler/check_nodeset_compiler_adi.c
  44. 0 0
      tests/nodeset-compiler/check_nodeset_compiler_plc.c
  45. 45 0
      tests/nodeset-compiler/check_nodeset_objecttype.c
  46. 61 0
      tests/nodeset-compiler/objecttype.xml
  47. 4 2
      tests/server/check_discovery.c
  48. 1 1
      tests/server/check_server_userspace.c
  49. 20 20
      tests/server/check_services_attributes.c
  50. 0 2
      tests/server/check_services_subscriptions.c
  51. 14 0
      tests/server/check_services_view.c
  52. 448 0
      tests/server/check_subscription_events.c
  53. 19 19
      tests/testing-plugins/testing_policy.h
  54. 1 2
      tools/appveyor/build.ps1
  55. 10 38
      tools/certs/create_self-signed.py
  56. 1 1
      tools/certs/localhost.cnf
  57. 8 0
      tools/cmake/FindMbedTLS.cmake
  58. 8 8
      tools/generate_nodeid_descriptions.py
  59. 0 1
      tools/nodeset_compiler/backend_open62541.py
  60. 10 10
      tools/nodeset_compiler/backend_open62541_nodes.py
  61. 0 64
      tools/nodeset_compiler/constants.py
  62. 2 39
      tools/nodeset_compiler/datatypes.py
  63. 0 10
      tools/nodeset_compiler/nodes.py
  64. 134 0
      tools/schema/Opc.Ua.NodeSet2.Minimal.xml
  65. 25 23
      tools/travis/travis_linux_before_install.sh
  66. 10 8
      tools/travis/travis_linux_script.sh

+ 167 - 45
.travis.yml

@@ -1,7 +1,7 @@
-language: c
+# using c for language overwrites our compilers
+language: generic
 
-# use new build environment (docker)
-sudo: required
+sudo: false
 
 env:
   global:
@@ -16,43 +16,173 @@ dist: trusty
 matrix:
   fast_finish: true
   include:
+    #
     - os: linux
-      compiler: gcc
+      addons:
+        apt:
+          sources:
+            # see https://github.com/travis-ci/apt-source-whitelist/blob/master/ubuntu.json
+            - ubuntu-toolchain-r-test
+          packages:
+            - binutils-mingw-w64-i686
+            - build-essential
+            - check
+            - cmake
+            - gcc-multilib
+            - g++-mingw-w64-i686
+            - g++-mingw-w64-x86-64
+            - g++-multilib
+            - graphviz
+            - libsubunit-dev
+            - libx11-dev
+            - mingw-w64
+            - python-six
+            - python3-six
+            - texlive-fonts-recommended
+            - texlive-latex-extra
+            - texlive-latex-recommended
+            - valgrind
+            - wget
+            - xutils-dev
+            - zip
       env:
         - ANALYZE=false
+        - CC=gcc-4.8
+        - CXX=g++-4.8
         - PYTHON=python2
+        - MINGW=true
     - os: linux
-      compiler: clang
+      addons:
+        apt:
+          sources:
+            - ubuntu-toolchain-r-test
+          packages:
+            - check
+            - gcc-8
+            - gcc-8-multilib
+            - g++-8
+            - g++-8-multilib
+            - graphviz
+            - linux-libc-dev:i386
+            - python-six
+            - python3-six
+            - texlive-fonts-recommended
+            - texlive-latex-extra
+            - texlive-latex-recommended
+            - valgrind
       env:
         - ANALYZE=false
+        - CC=gcc-8
+        - CXX=g++-8
         - PYTHON=python2
     - os: linux
-      compiler: clang
+      addons:
+        apt:
+          sources:
+            - llvm-toolchain-trusty-6.0
+            - ubuntu-toolchain-r-test
+          packages:
+            - check
+            - clang-6.0
+            - clang-tidy-6.0
+            - graphviz
+            - python-six
+            - texlive-fonts-recommended
+            - texlive-latex-extra
+            - texlive-latex-recommended
+            - valgrind
+      env:
+        - ANALYZE=false
+        - CC=clang-6.0
+        - CXX=clang++-6.0
+        - PYTHON=python2
+    - os: linux
+      addons:
+        apt:
+          sources:
+            - llvm-toolchain-trusty-6.0
+            - ubuntu-toolchain-r-test
+          packages:
+            - check
+            - clang-6.0
+            - clang-tidy-6.0
+            - graphviz
+            - python3-six
+            - texlive-fonts-recommended
+            - texlive-latex-extra
+            - texlive-latex-recommended
+            - valgrind
       env:
         - ANALYZE=false
+        - CC=clang-6.0
+        - CXX=clang++-6.0
         - PYTHON=python3
     - os: linux
-      compiler: tcc
+      addons:
+        apt:
+          packages:
+            - check
+            - graphviz
+            - python-six
+            - python3-six
+            - texlive-fonts-recommended
+            - texlive-latex-extra
+            - texlive-latex-recommended
       env:
         - ANALYZE=false
+        - CC=tcc
         - PYTHON=python2
     - os: linux
-      compiler: gcc
-      env: ANALYZE=true
+      addons:
+        apt:
+          packages:
+            - cppcheck
+      env:
+        - ANALYZE=true
+        - CC=gcc-4.8
+        - CXX=g++-4.8
     - os: linux
-      compiler: clang
-      env: ANALYZE=true
+      addons:
+        apt:
+          sources:
+            - llvm-toolchain-trusty-6.0
+            - ubuntu-toolchain-r-test
+          packages:
+            - check
+            - clang-6.0
+            - clang-tidy-6.0
+            - graphviz
+            - python-six
+            - python3-six
+            - texlive-fonts-recommended
+            - texlive-latex-extra
+            - texlive-latex-recommended
+      env:
+        - ANALYZE=true
+        - PYTHON=python3
+        - CC=clang-6.0
+        - CXX=clang++-6.0
     #- os: linux
     #  compiler: gcc
     #  env: LINT=true
     - os: linux
-      compiler: gcc
-      env: DOCKER=true
-      services:
-        - docker
+      env:
+        - DOCKER=true
     - os: linux
-      compiler: clang
-      env: FUZZER=true
+      addons:
+        apt:
+          sources:
+            - llvm-toolchain-trusty-6.0
+            - ubuntu-toolchain-r-test
+          packages:
+            - check
+            - clang-6.0
+            - clang-tidy-6.0
+            - libfuzzer-6.0-dev
+            - python-six
+            - python3-six
+      env:
+        - FUZZER=true
     - os: osx
       compiler: clang
       # disable homebrew auto update which takes a lot of time
@@ -61,7 +191,6 @@ matrix:
         directories:
           - $HOME/Library/Caches/Homebrew
     - os: linux
-      compiler: gcc
       addons:
         sonarcloud:
           organization: open62541
@@ -69,43 +198,19 @@ matrix:
             - master
             - sonarcloud
       env:
+        - CC=gcc-4.9
+        - CXX=g++-4.9
         - SONAR=true
         - PYTHON=python2
       cache:
         directories:
           - '$HOME/.sonar/cache'
 addons:
-  apt:
-    sources:
-      # see https://github.com/travis-ci/apt-source-whitelist/blob/master/ubuntu.json
-    packages:
-      - binutils-mingw-w64-i686
-      - build-essential
-      - check
-      - cmake
-      - cppcheck
-      - gcc-multilib
-      - g++-mingw-w64-i686
-      - g++-mingw-w64-x86-64
-      - g++-multilib
-      - graphviz
-      - libsubunit-dev
-      - libx11-dev
-      - mingw-w64
-      - python-six
-      - python3-six
-      - texlive-fonts-recommended
-      - texlive-latex-extra
-      - texlive-latex-recommended
-      - valgrind
-      - wget
-      - xutils-dev
-      - zip
   coverity_scan:
     project:
       name: "open62541/open62541"
       description: "Build submitted by Travis"
-    notification_email: null@plt.rwth-aachen.de
+    notification_email: noreply@open62541.org
     build_command_prepend: "mkdir build && cd build && cmake .."
     build_command: "make"
     branch_pattern: coverity_scan
@@ -120,6 +225,23 @@ cache:
 # combine all the commands into one single command. See https://github.com/travis-ci/travis-ci/issues/1066
 before_install: |
  set -e
+
+ # set paths for locally installed libs (like liburcu)
+ export LOCAL_PKG=$HOME/install
+ mkdir -p $LOCAL_PKG/lib
+ mkdir -p $LOCAL_PKG/include
+ mkdir -p $LOCAL_PKG/bin
+ export LIBRARY_PATH=$LOCAL_PKG/lib:$LIBRARY_PATH
+ export C_INCLUDE_PATH=$LOCAL_PKG/include:$C_INCLUDE_PATH
+ export CPLUS_INCLUDE_PATH=$LOCAL_PKG/include:$CPLUS_INCLUDE_PATH
+ export PKG_CONFIG_PATH=$LOCAL_PKG/lib/pkgconfig:$PKG_CONFIG_PATH
+ export PATH=$LOCAL_PKG:$LOCAL_PKG/bin:$PATH
+ export CMAKE_PREFIX_PATH=$LOCAL_PKG:CMAKE_PREFIX_PATH
+ # set local path for python packages
+ export PATH=$PATH:$HOME/.local/bin # linux
+ export PATH=$PATH:$HOME/Library/Python #OS X
+ export PATH=$PATH:$HOME/Library/Python/2.7/bin #OS X
+
  # Exit travis if on coverity_scan branch and not first build
  test $TRAVIS_BRANCH != coverity_scan -o ${TRAVIS_JOB_NUMBER##*.} = 1 || exit 0
  if [ ${TRAVIS_OS_NAME} == "linux" ]; then echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca-; fi

+ 8 - 5
CMakeLists.txt

@@ -114,10 +114,11 @@ endif(${UA_ARCHITECTURE} STREQUAL "None")
 
 # Options
 set(UA_LOGLEVEL 300 CACHE STRING "Level at which logs shall be reported")
+option(UA_ENABLE_HISTORIZING "Enable server to provide historical access." 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_SUBSCRIPTIONS "Enable subscriptions support." ON)
-option(UA_ENABLE_SUBSCRIPTIONS_EVENTS "Enable the use of events." OFF)
+option(UA_ENABLE_SUBSCRIPTIONS_EVENTS "Enable the use of events. (EXPERIMENTAL)" OFF)
 option(UA_ENABLE_DISCOVERY "Enable Discovery Service (LDS)" ON)
 option(UA_ENABLE_DISCOVERY_MULTICAST "Enable Discovery Service with multicast support (LDS-ME)" OFF)
 option(UA_ENABLE_COVERAGE "Enable gcov coverage" OFF)
@@ -162,6 +163,8 @@ endif()
 
 option(UA_ENABLE_PUBSUB "Enable publish/subscribe (experimental)" OFF)
 mark_as_advanced(UA_ENABLE_PUBSUB)
+option(UA_ENABLE_PUBSUB_DELTAFRAMES "Enable sending of delta frames with only the changes" ON)
+mark_as_advanced(UA_ENABLE_PUBSUB_DELTAFRAMES)
 option(UA_ENABLE_PUBSUB_INFORMATIONMODEL "Enable PubSub information model twin" OFF)
 mark_as_advanced(UA_ENABLE_PUBSUB_INFORMATIONMODEL)
 if(UA_ENABLE_PUBSUB_INFORMATIONMODEL)
@@ -641,12 +644,12 @@ add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/ua_statuscodes.h
         DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tools/generate_statuscode_descriptions.py
                 ${UA_FILE_STATUSCODES})
 
-# nodeid explanation
+# Header containing defines for all NodeIds
 add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/ua_nodeids.h
         PRE_BUILD
-        COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/generate_nodeid_descriptions.py
-        ${UA_FILE_NODEIDS}  ${PROJECT_BINARY_DIR}/src_generated/ua_nodeids
-        DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tools/generate_nodeid_descriptions.py
+        COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/generate_nodeid_header.py
+        ${UA_FILE_NODEIDS}  ${PROJECT_BINARY_DIR}/src_generated/ua_nodeids NS0
+        DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tools/generate_nodeid_header.py
         ${UA_FILE_NODEIDS})
 
 # we need a custom target to avoid that the generator is called concurrently and

+ 72 - 59
FEATURES.md

@@ -1,51 +1,69 @@
 open62541 Supported Features
 ============================
 
-| __**Service**__             |                                 |                      | Comment              |
-|:----------------------------|:--------------------------------|:--------------------:|:---------------------|
-| Discovery Service Set       |                                 |                      |                      |
-|                             | FindServers()                   |  :heavy_check_mark:  |                      |
+OPC UA Stack
+------------
+
+|                                         |                      |                      |
+| --------------------------------------- |:--------------------:| -------------------- |
+| **Encoding **                           |                      |                      |
+| OPC UA Binary                           |  :heavy_check_mark:  |                      |
+| OPC UA JSON                             |      :new_moon:      | WIP for Release 0.4  |
+| OPC UA XML                              |      :new_moon:      |                      |
+| **Transport**                           |                      |                      |
+| UA-TCP UA-SC UA Binary                  |  :heavy_check_mark:  |                      |
+| OPC UA HTTPS                            |      :new_moon:      |                      |
+| SOAP-HTTP WS-SC UA Binary               |      :new_moon:      |                      |
+| SOAP-HTTP WS-SC UA XML                  |      :new_moon:      |                      |
+| SOAP-HTTP WS-SC UA XML-UA Binary        |      :new_moon:      |                      |
+| **Encryption**                          |                      |                      |
+| None                                    |  :heavy_check_mark:  |                      |
+| Basic128Rsa15                           |  :heavy_check_mark:  | master, Release 0.3  |
+| Basic256                                |  :heavy_check_mark:  | master, Release 0.3  |
+| Basic256Sha256                          |  :heavy_check_mark:  | master, Release 0.3  |
+| **Authentication**                      |                      |                      |
+| Anonymous                               |  :heavy_check_mark:  |                      |
+| User Name Password                      |  :heavy_check_mark:  |                      |
+| X509 Certificate                        |      :new_moon:      |                      |
+
+OPC UA Server
+-------------
+
+| **Service-Set**             | **Service**                     | **Support**          | **Comment**          |
+| --------------------------- | ------------------------------- |:--------------------:| -------------------- |
+| Discovery Service Set       | FindServers()                   |  :heavy_check_mark:  |                      |
 |                             | FindServersOnNetwork()          |     :full_moon:      | master, Release 0.3  |
 |                             | GetEndpoints()                  |  :heavy_check_mark:  |                      |
 |                             | RegisterServer()                |  :heavy_check_mark:  |                      |
 |                             | RegisterServer2()               |     :full_moon:      | master, Release 0.3  |
-| Secure Channel Service Set  |                                 |                      |                      |
-|                             | OpenSecureChannel()             |  :heavy_check_mark:  |                      |
+| Secure Channel Service Set  | OpenSecureChannel()             |  :heavy_check_mark:  |                      |
 |                             | CloseSecureChannel()            |  :heavy_check_mark:  |                      |
-| Session Service Set         |                                 |                      |                      |
-|                             | CreateSession()                 |  :heavy_check_mark:  |                      |
+| Session Service Set         | CreateSession()                 |  :heavy_check_mark:  |                      |
 |                             | CloseSession()                  |  :heavy_check_mark:  |                      |
 |                             | ActivateSession()               |  :heavy_check_mark:  |                      |
 |                             | Cancel()                        |      :new_moon:      |                      |
-| Node Management Service Set |                                 |                      |                      |
-|                             | AddNodes()                      |  :heavy_check_mark:  |                      |
+| Node Management Service Set | AddNodes()                      |  :heavy_check_mark:  |                      |
 |                             | AddReferences()                 |  :heavy_check_mark:  |                      |
 |                             | DeleteNodes()                   |  :heavy_check_mark:  |                      |
 |                             | DeleteReferences()              |  :heavy_check_mark:  |                      |
-| View Service Set            |                                 |                      |                      |
-|                             | Browse()                        |  :heavy_check_mark:  |                      |
+| View Service Set            | Browse()                        |  :heavy_check_mark:  |                      |
 |                             | BrowseNext()                    |  :heavy_check_mark:  |                      |
 |                             | TranslateBrowsePathsToNodeIds() |  :heavy_check_mark:  |                      |
 |                             | RegisterNodes()                 |  :heavy_check_mark:  |                      |
 |                             | UnregisterNodes()               |  :heavy_check_mark:  |                      |
-| Query Service Set           |                                 |                      |                      |
-|                             | QueryFirst()                    |      :new_moon:      |                      |
+| Query Service Set           | QueryFirst()                    |      :new_moon:      |                      |
 |                             | QueryNext()                     |      :new_moon:      |                      |
-| Attribute Service Set       |                                 |                      |                      |
-|                             | Read()                          |  :heavy_check_mark:  |                      |
+| Attribute Service Set       | Read()                          |  :heavy_check_mark:  |                      |
 |                             | Write()                         |  :heavy_check_mark:  |                      |
 |                             | HistoryRead()                   | :waning_gibbous_moon: | [WIP](https://github.com/open62541/open62541/pull/1740), Release 0.4     |
 |                             | HistoryUpdate()                 | :waning_gibbous_moon: | [WIP](https://github.com/open62541/open62541/pull/1740), Release 0.4     |
-| Method Service Set          |                                 |                      |                      |
-|                             | Call()                          |  :heavy_check_mark:  |                      |
-| MonitoredItems Service Set  |                                 |                      |                      |
-|                             | CreateMonitoredItems()          |  :heavy_check_mark:  |                      |
+| Method Service Set          | Call()                          |  :heavy_check_mark:  |                      |
+| MonitoredItems Service Set  | CreateMonitoredItems()          |  :heavy_check_mark:  | See below for Events |
 |                             | DeleteMonitoredItems()          |  :heavy_check_mark:  |                      |
 |                             | ModifyMonitoredItems()          |  :heavy_check_mark:  |                      |
 |                             | SetMonitoringMode()             |  :heavy_check_mark:  |                      |
 |                             | SetTriggering()                 |      :new_moon:      |                      |
-| Subscription Service Set    |                                 |                      |                      |
-|                             | CreateSubscription()            |  :heavy_check_mark:  |                      |
+| Subscription Service Set    | CreateSubscription()            |  :heavy_check_mark:  |                      |
 |                             | ModifySubscription()            |  :heavy_check_mark:  |                      |
 |                             | SetPublishingMode()             |  :heavy_check_mark:  |                      |
 |                             | Publish()                       |  :heavy_check_mark:  |                      |
@@ -53,45 +71,40 @@ open62541 Supported Features
 |                             | DeleteSubscriptions()           |  :heavy_check_mark:  |                      |
 |                             | TransferSubscriptions()         |      :new_moon:      |                      |
 
+| **Subscriptions**                       |                      |                      |
+| --------------------------------------- |:--------------------:| -------------------- |
+| DataChange MonitoredItems               |  :heavy_check_mark:  | master, Release 0.3  |
+| DataChange Filters                      |  :heavy_check_mark:  | master               |
+| Event MonitoredItems                    |  :heavy_check_mark:  | master               |
+| Event Filters                           |      :new_moon:      |                      |
 
-|                                         |                      |                      |
-|:----------------------------------------|:--------------------:|:---------------------|
-| **Transport**                           |                      |                      |
-| UA-TCP UA-SC UA Binary                  |  :heavy_check_mark:  | OPC.TCP - Binary     |
-| SOAP-HTTP WS-SC UA Binary               |      :new_moon:      | HTTP/HTTPS - Binary  |
-| SOAP-HTTP WS-SC UA XML                  |      :new_moon:      |                      |
-| SOAP-HTTP WS-SC UA XML-UA Binary        |      :new_moon:      |                      |
-| **Encryption**                          |                      |                      |
-| None                                    |  :heavy_check_mark:  |                      |
-| Basic128Rsa15                           |  :heavy_check_mark:  | master, Release 0.3  |
-| Basic256                                |  :heavy_check_mark:  | master               |
-| Basic256Sha256                          |  :heavy_check_mark:  | master               |
-| **Authentication**                      |                      |                      |
-| Anonymous                               |  :heavy_check_mark:  |                      |
-| User Name Password                      |  :heavy_check_mark:  |                      |
-| X509 Certificate                        |      :new_moon:      |                      |
-| **Server Facets**                       |                      |                      |
-| Core Server                             |  :heavy_check_mark:  |                      |
-| Data Access Server                      |  :heavy_check_mark:  |                      |
-| Embedded Server                         |  :heavy_check_mark:  |                      |
-| Nano Embedded Device Server             |  :heavy_check_mark:  |                      |
-| Micro Embedded Device Server            |  :heavy_check_mark:  |                      |
-| Method Server                           |  :heavy_check_mark:  |                      |
-| Embedded DataChange Subscription Server |  :heavy_check_mark:  |                      |
-| Node Management Server                  |  :heavy_check_mark:  |                      |
-| Standard DataChange Subscription Server | :waning_gibbous_moon: | Only Deadband Filter missing |
-| Event Subscription Server               |     :full_moon:      | master               |
-| **Client Facets**                       |                      |                      |
-| Base Client Behaviour                   |  :heavy_check_mark:  |                      |
-| AddressSpace Lookup                     |  :heavy_check_mark:  |                      |
-| Attribute Read                          |  :heavy_check_mark:  |                      |
-| DataChange Subscription                 |  :heavy_check_mark:  |                      |
-| DataAccess                              |  :heavy_check_mark:  |                      |
-| Discovery                               |  :heavy_check_mark:  |                      |
-| Event Subscription                      |  :heavy_check_mark:  |                      |
-| Method call                             |  :heavy_check_mark:  |                      |
-| Advanced Type                           |  :heavy_check_mark:  |                      |
 | **Discovery**                           |                      | See Discovery Service Set |
+| --------------------------------------- |:--------------------:| -------------------- |
 | Local Disovery Server                   |  :heavy_check_mark:  | master, Release 0.3  |
 | Local Discovery Server Multicast Ext.   |  :heavy_check_mark:  | master, Release 0.3  |
 | Global Discovery Server                 |      :new_moon:      |                      |
+
+OPC UA Client
+-------------
+
+- All services are supported
+- Handling of subscriptions in the background
+
+OPC UA PubSub
+-------------
+
+|                                                   |                       |                        |
+| ------------------------------------------------- |:---------------------:| ---------------------- |
+| **NetworkMessage decoding/encoding**              |                       |                        |
+| Binary (UADP)                                     |   :heavy_check_mark:  |                        |
+| JSON                                              |       :new_moon:      | WIP                    |
+| **PubSub Transport**                              |                       |                        |
+| UDP/multicast (send and receive)                  |   :heavy_check_mark:  |                        |
+| Ethernet (TSN)                                    | :waning_gibbous_moon: | Defined API to plug in custom networking implementation |
+| MQTT                                              |      :new_moon:       | WIP                    |
+| AMQP                                              |      :new_moon:       |                        |
+| **Publisher Configuration**                       |                       |                        |
+| Configure (server-side) Publisher at runtime      |  :heavy_check_mark:   |                        |
+| Configuration representation in information model |  :heavy_check_mark:   | Runtime configuration changes by editing the information model representation are possible |
+| Security Key Service Model                        |      :new_moon:       |                        |
+| **Subscriber Configuration**                      | :waning_gibbous_moon: | Manual Subscriber only |

+ 1 - 1
doc/building.rst

@@ -179,7 +179,7 @@ This group contains build options related to the supported OPC UA features.
 **UA_ENABLE_SUBSCRIPTIONS**
    Enable subscriptions
 **UA_ENABLE_SUBSCRIPTIONS_EVENTS**
-    Enable the use of events for subscriptions
+    Enable the use of events for subscriptions. This is a new feature and currently marked as EXPERIMENTAL.
 **UA_ENABLE_METHODCALLS**
    Enable the Method service set
 **UA_ENABLE_NODEMANAGEMENT**

+ 3 - 3
examples/CMakeLists.txt

@@ -110,12 +110,12 @@ endif()
 
 if(UA_BUILD_SELFSIGNED_CERTIFICATE)
   find_package(OpenSSL REQUIRED)
-  add_custom_command(OUTPUT server_cert.der ca.crt
+  add_custom_command(OUTPUT server_cert.der
                      COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/certs/create_self-signed.py ${CMAKE_CURRENT_BINARY_DIR}
                      DEPENDS ${PROJECT_SOURCE_DIR}/tools/certs/create_self-signed.py
                              ${PROJECT_SOURCE_DIR}/tools/certs/localhost.cnf)
-  add_custom_target(selfsigned ALL DEPENDS server_cert.der ca.crt)
-  add_executable(server_certificate server_certificate.c ${STATIC_OBJECTS} server_cert.der ca.crt)
+  add_custom_target(selfsigned ALL DEPENDS server_cert.der)
+  add_executable(server_certificate server_certificate.c ${STATIC_OBJECTS} server_cert.der)
   target_link_libraries(server_certificate open62541 ${open62541_LIBRARIES})
 endif()
 

+ 11 - 1
examples/nodeset/CMakeLists.txt

@@ -25,7 +25,17 @@ add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/example_nodeset.c
                    ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541_datatypes.py
                    ${PROJECT_SOURCE_DIR}/examples/nodeset/server_nodeset.xml)
 
-add_example(server_nodeset server_nodeset.c ${PROJECT_BINARY_DIR}/src_generated/example_nodeset.c)
+
+# The .csv file can be created from within UaModeler or manually
+
+add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/example_nodeset_ids.h
+                   PRE_BUILD
+                   COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/generate_nodeid_header.py
+                   ${PROJECT_SOURCE_DIR}/examples/nodeset/server_nodeset.csv  ${PROJECT_BINARY_DIR}/src_generated/example_nodeset_ids EXAMPLE_NS
+                   DEPENDS ${PROJECT_SOURCE_DIR}/tools/generate_nodeid_header.py
+                   ${PROJECT_SOURCE_DIR}/examples/nodeset/server_nodeset.csv)
+
+add_example(server_nodeset server_nodeset.c ${PROJECT_BINARY_DIR}/src_generated/example_nodeset.c ${PROJECT_BINARY_DIR}/src_generated/example_nodeset_ids.h)
 if(UA_COMPILE_AS_CXX)
     set_source_files_properties(${PROJECT_BINARY_DIR}/src_generated/example_nodeset.c PROPERTIES LANGUAGE CXX)
 endif()

+ 13 - 0
examples/nodeset/server_nodeset.c

@@ -7,6 +7,7 @@
 /* Files example_namespace.h and example_namespace.c are created from server_nodeset.xml in the
  * /src_generated directory by CMake */
 #include "example_nodeset.h"
+#include "example_nodeset_ids.h"
 
 UA_Boolean running = true;
 
@@ -29,8 +30,20 @@ int main(int argc, char** argv) {
         "Check previous output for any error.");
         retval = UA_STATUSCODE_BADUNEXPECTEDERROR;
     } else {
+
+        // Do some additional stuff with the nodes
+
+        // this will just get the namespace index, since it is already added to the server
+        UA_UInt16 nsIdx = UA_Server_addNamespace(server, "http://yourorganisation.org/test/");
+
+        UA_NodeId testInstanceId = UA_NODEID_NUMERIC(nsIdx, UA_EXAMPLE_NSID_TESTINSTANCE);
+
+        UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "The testInstance has ns=%d;id=%d",
+                    testInstanceId.namespaceIndex, testInstanceId.identifier.numeric);
+
         retval = UA_Server_run(server, &running);
     }
+
     UA_Server_delete(server);
     UA_ServerConfig_delete(config);
     return (int)retval;

+ 5 - 0
examples/nodeset/server_nodeset.csv

@@ -0,0 +1,5 @@
+testType,1001,ObjectType
+testInstance,5001,Object
+testFolder,5002,Object
+testType_Var1,6001,Variable
+testInstance_Var1,6002,Variable

+ 1 - 1
include/ua_client_config.h

@@ -55,7 +55,7 @@ typedef void (*UA_ClientAsyncServiceCallback)(UA_Client *client, void *userdata,
 /*
  * Repeated Callbacks
  * ------------------ */
-typedef UA_StatusCode (*UA_ClientCallback)(UA_Client *client, void *data);
+typedef void (*UA_ClientCallback)(UA_Client *client, void *data);
 
 UA_StatusCode
 UA_Client_addRepeatedCallback(UA_Client *Client, UA_ClientCallback callback,

+ 2 - 0
include/ua_config.h.in

@@ -28,8 +28,10 @@ extern "C" {
 #cmakedefine UA_ENABLE_NODEMANAGEMENT
 #cmakedefine UA_ENABLE_SUBSCRIPTIONS
 #cmakedefine UA_ENABLE_PUBSUB
+#cmakedefine UA_ENABLE_PUBSUB_DELTAFRAMES
 #cmakedefine UA_ENABLE_PUBSUB_INFORMATIONMODEL
 #cmakedefine UA_ENABLE_ENCRYPTION
+#cmakedefine UA_ENABLE_HISTORIZING
 #cmakedefine UA_ENABLE_SUBSCRIPTIONS_EVENTS
 
 /* Multithreading */

+ 46 - 14
include/ua_server.h

@@ -428,6 +428,19 @@ UA_BrowsePathResult UA_EXPORT
 UA_Server_translateBrowsePathToNodeIds(UA_Server *server,
                                        const UA_BrowsePath *browsePath);
 
+/* A simplified TranslateBrowsePathsToNodeIds based on the
+ * SimpleAttributeOperand type (Part 4, 7.4.4.5).
+ *
+ * This specifies a relative path using a list of BrowseNames instead of the
+ * RelativePath structure. The list of BrowseNames is equivalent to a
+ * RelativePath that specifies forward references which are subtypes of the
+ * HierarchicalReferences ReferenceType. All Nodes followed by the browsePath
+ * shall be of the NodeClass Object or Variable. */
+UA_BrowsePathResult UA_EXPORT
+UA_Server_browseSimplifiedBrowsePath(UA_Server *server, const UA_NodeId origin,
+                                     size_t browsePathSize,
+                                     const UA_QualifiedName *browsePath);
+
 #ifndef HAVE_NODEITER_CALLBACK
 #define HAVE_NODEITER_CALLBACK
 /* Iterate over all nodes referenced by parentNodeId by calling the callback
@@ -668,22 +681,26 @@ typedef struct {
      * `value->value.storageType` to `UA_VARIANT_DATA_NODELETE` to prevent the
      * memory being cleaned up. Don't forget to also set `value->hasValue` to
      * true to indicate the presence of a value.
-     *
-     * @param handle An optional pointer to user-defined data for the
-     *        specific data source
-     * @param nodeid Id of the read node
+     * 
+     * @param server The server executing the callback
+     * @param sessionId The identifier of the session
+     * @param sessionContext Additional data attached to the session in the
+     *        access control layer
+     * @param nodeId The identifier of the node being read from
+     * @param nodeContext Additional data attached to the node by the user
      * @param includeSourceTimeStamp If true, then the datasource is expected to
      *        set the source timestamp in the returned value
      * @param range If not null, then the datasource shall return only a
      *        selection of the (nonscalar) data. Set
      *        UA_STATUSCODE_BADINDEXRANGEINVALID in the value if this does not
-     *        apply.
+     *        apply
      * @param value The (non-null) DataValue that is returned to the client. The
      *        data source sets the read data, the result status and optionally a
      *        sourcetimestamp.
      * @return Returns a status code for logging. Error codes intended for the
      *         original caller are set in the value. If an error is returned,
-     *         then no releasing of the value is done. */
+     *         then no releasing of the value is done
+     */
     UA_StatusCode (*read)(UA_Server *server, const UA_NodeId *sessionId,
                           void *sessionContext, const UA_NodeId *nodeId,
                           void *nodeContext, UA_Boolean includeSourceTimeStamp,
@@ -691,14 +708,24 @@ typedef struct {
 
     /* Write into a data source. This method pointer can be NULL if the
      * operation is unsupported.
-     *
-     * @param handle An optional pointer to user-defined data for the
-     *        specific data source
-     * @param nodeid Id of the node being written to
-     * @param data The data to be written into the data source
-     * @param range An optional data range. If the data source is scalar or does
-     *        not support writing of ranges, then an error code is returned.
-     * @return Returns a status code that is returned to the user */
+     * 
+     * @param server The server executing the callback
+     * @param sessionId The identifier of the session
+     * @param sessionContext Additional data attached to the session in the
+     *        access control layer
+     * @param nodeId The identifier of the node being written to
+     * @param nodeContext Additional data attached to the node by the user
+     * @param range If not NULL, then the datasource shall return only a
+     *        selection of the (nonscalar) data. Set
+     *        UA_STATUSCODE_BADINDEXRANGEINVALID in the value if this does not
+     *        apply
+     * @param value The (non-NULL) DataValue that has been written by the client.
+     *        The data source contains the written data, the result status and
+     *        optionally a sourcetimestamp
+     * @return Returns a status code for logging. Error codes intended for the
+     *         original caller are set in the value. If an error is returned,
+     *         then no releasing of the value is done
+     */
     UA_StatusCode (*write)(UA_Server *server, const UA_NodeId *sessionId,
                            void *sessionContext, const UA_NodeId *nodeId,
                            void *nodeContext, const UA_NumericRange *range,
@@ -1216,6 +1243,11 @@ UA_Server_triggerEvent(UA_Server *server, const UA_NodeId eventNodeId, const UA_
 /* Add a new namespace to the server. Returns the index of the new namespace */
 UA_UInt16 UA_EXPORT UA_Server_addNamespace(UA_Server *server, const char* name);
 
+/* Get namespace by name from the server. */
+UA_StatusCode UA_EXPORT
+UA_Server_getNamespaceByName(UA_Server *server, const UA_String namespaceUri,
+                             size_t* foundIndex);
+
 #ifdef __cplusplus
 }
 #endif

+ 24 - 0
include/ua_server_config.h

@@ -5,6 +5,7 @@
  *    Copyright 2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer)
  *    Copyright 2017 (c) Stefan Profanter, fortiss GmbH
  *    Copyright 2017 (c) Henrik Norrman
+ *    Copyright 2018 (c) Fabian Arndt, Root-Core
  */
 
 #ifndef UA_SERVER_CONFIG_H_
@@ -165,6 +166,29 @@ struct UA_ServerConfig {
      * state of the semaphore file. */
     UA_UInt32 discoveryCleanupTimeout;
 #endif
+
+    /* Historical Access */
+#ifdef UA_ENABLE_HISTORIZING
+    UA_Boolean accessHistoryDataCapability;
+    UA_UInt32  maxReturnDataValues; /* 0 -> unlimited size */
+    
+    UA_Boolean accessHistoryEventsCapability;
+    UA_UInt32  maxReturnEventValues; /* 0 -> unlimited size */
+
+    UA_Boolean insertDataCapability;
+    UA_Boolean insertEventCapability;
+    UA_Boolean insertAnnotationsCapability;
+
+    UA_Boolean replaceDataCapability;
+    UA_Boolean replaceEventCapability;
+    
+    UA_Boolean updateDataCapability;
+    UA_Boolean updateEventCapability;
+    
+    UA_Boolean deleteRawCapability;
+    UA_Boolean deleteEventCapability;
+    UA_Boolean deleteAtTimeDataCapability;
+#endif
 };
 
 #ifdef __cplusplus

+ 4 - 0
include/ua_types.h

@@ -497,6 +497,10 @@ typedef struct  {
     UA_NumericRangeDimension *dimensions;
 } UA_NumericRange;
 
+UA_StatusCode UA_EXPORT
+UA_NumericRange_parseFromString(UA_NumericRange *range, const UA_String *str);
+
+
 /**
  * .. _variant:
  *

+ 29 - 1
plugins/ua_config_default.c

@@ -7,6 +7,7 @@
  *    Copyright 2017 (c) Stefan Profanter, fortiss GmbH
  *    Copyright 2017 (c) Thomas Stalder, Blue Time Concept SA
  *    Copyright 2018 (c) Daniel Feist, Precitec GmbH & Co. KG
+ *    Copyright 2018 (c) Fabian Arndt, Root-Core
  */
 
 #include "ua_plugin_securitypolicy.h"
@@ -280,6 +281,28 @@ createDefaultConfig(void) {
     conf->discoveryCleanupTimeout = 60 * 60;
 #endif
 
+#ifdef UA_ENABLE_HISTORIZING
+    /* conf->accessHistoryDataCapability = UA_FALSE; */
+    /* conf->maxReturnDataValues = 0; */
+
+    /* conf->accessHistoryEventsCapability = UA_FALSE; */
+    /* conf->maxReturnEventValues = 0; */
+
+    /* conf->insertDataCapability = UA_FALSE; */
+    /* conf->insertEventCapability = UA_FALSE; */
+    /* conf->insertAnnotationsCapability = UA_FALSE; */
+
+    /* conf->replaceDataCapability = UA_FALSE; */
+    /* conf->replaceEventCapability = UA_FALSE; */
+
+    /* conf->updateDataCapability = UA_FALSE; */
+    /* conf->updateEventCapability = UA_FALSE; */
+
+    /* conf->deleteRawCapability = UA_FALSE; */
+    /* conf->deleteEventCapability = UA_FALSE; */
+    /* conf->deleteAtTimeDataCapability = UA_FALSE; */
+#endif
+
     /* --> Finish setting the default static config <-- */
 
     return conf;
@@ -637,6 +660,11 @@ UA_ServerConfig_delete(UA_ServerConfig *config) {
 /* Default Client Settings */
 /***************************/
 
+static UA_INLINE void UA_ClientConnectionTCP_poll_callback(UA_Client *client, void *data) {
+    UA_ClientConnectionTCP_poll(client, data);
+}
+
+
 const UA_ClientConfig UA_ClientConfig_default = {
     5000, /* .timeout, 5 seconds */
     10 * 60 * 1000, /* .secureChannelLifeTime, 10 minutes */
@@ -650,7 +678,7 @@ const UA_ClientConfig UA_ClientConfig_default = {
     },
     UA_ClientConnectionTCP, /* .connectionFunc (for sync connection) */
     UA_ClientConnectionTCP_init, /* .initConnectionFunc (for async client) */
-    UA_ClientConnectionTCP_poll, /* .pollConnectionFunc (for async connection) */
+    UA_ClientConnectionTCP_poll_callback, /* .pollConnectionFunc (for async connection) */
     0, /* .customDataTypesSize */
     NULL, /* .customDataTypes */
 

+ 4 - 0
plugins/ua_debug_dump_pkgs.c

@@ -1,3 +1,4 @@
+
 /* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
  * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. 
  *
@@ -8,6 +9,8 @@
 
 #include <ctype.h>
 #include <stdio.h>
+
+#ifdef UA_DEBUG_DUMP_PKGS
 void UA_dump_hex_pkg(UA_Byte* buffer, size_t bufferLen) {
     printf("--------------- HEX Package Start ---------------\n");
     char ascii[17];
@@ -37,3 +40,4 @@ void UA_dump_hex_pkg(UA_Byte* buffer, size_t bufferLen) {
     printf(" |%s|\n%08zx\n", ascii, bufferLen);
     printf("--------------- HEX Package END ---------------\n");
 }
+#endif

+ 0 - 10
plugins/ua_network_pubsub_udp.c

@@ -133,16 +133,6 @@ UA_PubSubChannelUDPMC_open(const UA_PubSubConnectionConfig *connectionConfig) {
 
     UA_String hostname, path;
     UA_UInt16 networkPort;
-    //TODO replace fallback to use the existing parseEndpointUrl function. Extend parseEndpointUrl for UDP or create own parseEndpointUrl function for PubSub.
-    if(strncmp((char*)&address.url.data, "opc.udp://", 10) != 0){
-        strncpy((char*)address.url.data, "opc.tcp://", 10);
-    } else {
-        UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,
-                     "PubSub Connection creation failed. Invalid URL.");
-        UA_free(channelDataUDPMC);
-        UA_free(newChannel);
-        return NULL;
-    }
     if(UA_parseEndpointUrl(&address.url, &hostname, &networkPort, &path) != UA_STATUSCODE_GOOD){
         UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,
                      "PubSub Connection creation failed. Invalid URL.");

+ 14 - 14
src/client/ua_client_connect_async.c

@@ -41,7 +41,7 @@
 /* Open the Connection */
 /***********************/
 static UA_StatusCode
-openSecureChannelAsync(UA_Client *client, UA_Boolean renew);
+openSecureChannelAsync(UA_Client *client/*, UA_Boolean renew*/);
 
 static UA_StatusCode
 requestSession(UA_Client *client, UA_UInt32 *requestId);
@@ -92,7 +92,7 @@ processACKResponseAsync(void *application, UA_Connection *connection,
 
     /* Open a SecureChannel. TODO: Select with endpoint  */
     client->channel.connection = &client->connection;
-    client->connectStatus = openSecureChannelAsync(client, false);
+    client->connectStatus = openSecureChannelAsync(client/*, false*/);
     return client->connectStatus;
 }
 
@@ -235,10 +235,10 @@ static UA_StatusCode processOPNResponse
 
 /* OPN messges to renew the channel are sent asynchronous */
 static UA_StatusCode
-openSecureChannelAsync(UA_Client *client, UA_Boolean renew) {
+openSecureChannelAsync(UA_Client *client/*, UA_Boolean renew*/) {
     /* Check if sc is still valid */
-    if(renew && client->nextChannelRenewal - UA_DateTime_nowMonotonic () > 0)
-        return UA_STATUSCODE_GOOD;
+    /*if(renew && client->nextChannelRenewal - UA_DateTime_nowMonotonic () > 0)
+        return UA_STATUSCODE_GOOD;*/
 
     UA_Connection *conn = &client->connection;
     if(conn->state != UA_CONNECTION_ESTABLISHED)
@@ -249,15 +249,15 @@ openSecureChannelAsync(UA_Client *client, UA_Boolean renew) {
     UA_OpenSecureChannelRequest_init(&opnSecRq);
     opnSecRq.requestHeader.timestamp = UA_DateTime_now();
     opnSecRq.requestHeader.authenticationToken = client->authenticationToken;
-    if(renew) {
+    /*if(renew) {
         opnSecRq.requestType = UA_SECURITYTOKENREQUESTTYPE_RENEW;
         UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
                      "Requesting to renew the SecureChannel");
-    } else {
+    } else {*/
         opnSecRq.requestType = UA_SECURITYTOKENREQUESTTYPE_ISSUE;
         UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
                      "Requesting to open a SecureChannel");
-    }
+    //}
     opnSecRq.securityMode = client->channel.securityMode;
 
     opnSecRq.clientNonce = client->channel.localNonce;
@@ -265,7 +265,7 @@ openSecureChannelAsync(UA_Client *client, UA_Boolean renew) {
 
     /* Prepare the entry for the linked list */
     UA_UInt32 requestId = ++client->requestId;
-    AsyncServiceCall *ac = NULL;
+    /*AsyncServiceCall *ac = NULL;
     if(renew) {
         ac = (AsyncServiceCall*)UA_malloc(sizeof(AsyncServiceCall));
         if (!ac)
@@ -275,7 +275,7 @@ openSecureChannelAsync(UA_Client *client, UA_Boolean renew) {
         ac->responseType = &UA_TYPES[UA_TYPES_OPENSECURECHANNELRESPONSE];
         ac->requestId = requestId;
         ac->userdata = NULL;
-    }
+    }*/
 
     /* Send the OPN message */
     UA_StatusCode retval = UA_SecureChannel_sendAsymmetricOPNMessage (
@@ -289,8 +289,8 @@ openSecureChannelAsync(UA_Client *client, UA_Boolean renew) {
                       "Sending OPN message failed with error %s",
                       UA_StatusCode_name(retval));
         UA_Client_close(client);
-        if(renew)
-            UA_free(ac);
+        //if(renew)
+        //    UA_free(ac);
         return retval;
     }
 
@@ -298,10 +298,10 @@ openSecureChannelAsync(UA_Client *client, UA_Boolean renew) {
                   "OPN message sent");
 
     /* Store the entry for async processing and return */
-    if(renew) {
+    /*if(renew) {
         LIST_INSERT_HEAD(&client->asyncServiceCalls, ac, pointers);
         return retval;
-    }
+    }*/
     return retval;
 }
 

+ 185 - 135
src/pubsub/ua_pubsub.c

@@ -393,12 +393,15 @@ UA_DataSetWriter_deleteMembers(UA_Server *server, UA_DataSetWriter *dataSetWrite
     UA_NodeId_deleteMembers(&dataSetWriter->linkedWriterGroup);
     UA_NodeId_deleteMembers(&dataSetWriter->connectedDataSet);
     LIST_REMOVE(dataSetWriter, listEntry);
+#ifdef UA_ENABLE_PUBSUB_DELTAFRAMES
     //delete lastSamples store
     for(size_t i = 0; i < dataSetWriter->lastSamplesCount; i++){
-        UA_DataValue_delete(dataSetWriter->lastSamples[i].value);
+        UA_DataValue_deleteMembers(&dataSetWriter->lastSamples[i].value);
     }
-    LIST_REMOVE(dataSetWriter, listEntry);
     UA_free(dataSetWriter->lastSamples);
+    dataSetWriter->lastSamples = NULL;
+    dataSetWriter->lastSamplesCount = 0;
+#endif
 }
 
 /**********************************************/
@@ -527,25 +530,19 @@ UA_Server_addDataSetWriter(UA_Server *server,
     newDataSetWriter->config = tmpDataSetWriterConfig;
     //save the current version of the connected PublishedDataSet
     newDataSetWriter->connectedDataSetVersion = currentDataSetContext->dataSetMetaData.configurationVersion;
+
+#ifdef UA_ENABLE_PUBSUB_DELTAFRAMES
     //initialize the queue for the last values
-    newDataSetWriter->lastSamplesCount = currentDataSetContext->fieldSize;
     newDataSetWriter->lastSamples = (UA_DataSetWriterSample * )
-        UA_calloc(newDataSetWriter->lastSamplesCount, sizeof(UA_DataSetWriterSample));
+        UA_calloc(currentDataSetContext->fieldSize, sizeof(UA_DataSetWriterSample));
     if(!newDataSetWriter->lastSamples) {
         UA_DataSetWriterConfig_deleteMembers(&newDataSetWriter->config);
         UA_free(newDataSetWriter);
         return UA_STATUSCODE_BADOUTOFMEMORY;
     }
-    for(size_t i = 0; i < newDataSetWriter->lastSamplesCount; i++) {
-        newDataSetWriter->lastSamples[i].value = (UA_DataValue *) UA_calloc(1, sizeof(UA_DataValue));
-        if(!newDataSetWriter->lastSamples[i].value) {
-            for(size_t j = 0; j < i; j++)
-                UA_free(newDataSetWriter->lastSamples[j].value);
-            UA_DataSetWriterConfig_deleteMembers(&newDataSetWriter->config);
-            UA_free(newDataSetWriter);
-            return UA_STATUSCODE_BADOUTOFMEMORY;
-        }
-    }
+    newDataSetWriter->lastSamplesCount = currentDataSetContext->fieldSize;
+#endif
+
     //connect PublishedDataSet with DataSetWriter
     newDataSetWriter->connectedDataSet = currentDataSetContext->identifier;
     newDataSetWriter->linkedWriterGroup = wg->identifier;
@@ -642,7 +639,6 @@ void UA_DataSetField_deleteMembers(UA_DataSetField *field) {
     UA_NodeId_deleteMembers(&field->identifier);
     UA_NodeId_deleteMembers(&field->publishedDataSet);
     UA_FieldMetaData_deleteMembers(&field->fieldMetaData);
-    UA_DataValue_deleteMembers(&field->lastValue);
     LIST_REMOVE(field, listEntry);
 }
 
@@ -655,6 +651,7 @@ void UA_DataSetField_deleteMembers(UA_DataSetField *field) {
  *
  * @return UA_TRUE if the value has changed
  */
+#ifdef UA_ENABLE_PUBSUB_DELTAFRAMES
 static UA_Boolean
 valueChangedVariant(UA_Variant *oldValue, UA_Variant *newValue){
     if(! (oldValue && newValue))
@@ -695,32 +692,33 @@ valueChangedVariant(UA_Variant *oldValue, UA_Variant *newValue){
     UA_ByteString_delete(newValueEncoding);
     return compareResult;
 }
+#endif
 
 /**
  * Obtain the latest value for a specific DataSetField. This method is currently
  * called inside the DataSetMessage generation process.
  */
-static UA_StatusCode
-UA_PubSubDataSetField_sampleValue(UA_Server *server, UA_DataSetField *field) {
+static void
+UA_PubSubDataSetField_sampleValue(UA_Server *server, UA_DataSetField *field,
+                                  UA_DataValue *value) {
     /* Read the value */
     UA_ReadValueId rvid;
     UA_ReadValueId_init(&rvid);
     rvid.nodeId = field->config.field.variable.publishParameters.publishedVariable;
     rvid.attributeId = field->config.field.variable.publishParameters.attributeId;
     rvid.indexRange = field->config.field.variable.publishParameters.indexRange;
-    UA_DataValue value = UA_Server_read(server, &rvid, UA_TIMESTAMPSTORETURN_BOTH);
-    UA_DataValue_deleteMembers(&field->lastValue);
-    field->lastValue = value;
-    return UA_STATUSCODE_GOOD;
+    *value = UA_Server_read(server, &rvid, UA_TIMESTAMPSTORETURN_BOTH);
 }
 
 static UA_StatusCode
 UA_PubSubDataSetWriter_generateKeyFrameMessage(UA_Server *server, UA_DataSetMessage *dataSetMessage,
                                                UA_DataSetWriter *dataSetWriter) {
-    UA_PublishedDataSet *currentDataSet = UA_PublishedDataSet_findPDSbyId(server, dataSetWriter->connectedDataSet);
+    UA_PublishedDataSet *currentDataSet =
+        UA_PublishedDataSet_findPDSbyId(server, dataSetWriter->connectedDataSet);
     if(!currentDataSet)
         return UA_STATUSCODE_BADNOTFOUND;
-    //prepare DataSetMessageContent
+
+    /* Prepare DataSetMessageContent */
     dataSetMessage->header.dataSetMessageValid = true;
     dataSetMessage->header.dataSetMessageType = UA_DATASETMESSAGE_DATAKEYFRAME;
     dataSetMessage->data.keyFrameData.fieldCount = currentDataSet->fieldSize;
@@ -729,90 +727,116 @@ UA_PubSubDataSetWriter_generateKeyFrameMessage(UA_Server *server, UA_DataSetMess
     if(!dataSetMessage->data.keyFrameData.dataSetFields)
         return UA_STATUSCODE_BADOUTOFMEMORY;
 
-    UA_DataSetField *tmpDataSetField;
+    /* Loop over the fields */
     size_t counter = 0;
-    LIST_FOREACH(tmpDataSetField, &currentDataSet->fields, listEntry){
-        if(UA_PubSubDataSetField_sampleValue(server, tmpDataSetField) == UA_STATUSCODE_GOOD){
-            //include field into DSM
-            UA_DataValue_init(&dataSetMessage->data.keyFrameData.dataSetFields[counter]);
-            UA_DataValue_copy(&tmpDataSetField->lastValue, &dataSetMessage->data.keyFrameData.dataSetFields[counter]);
-            if((dataSetWriter->config.dataSetFieldContentMask & (unsigned int) UA_DATASETFIELDCONTENTMASK_STATUSCODE) == 0){
-                dataSetMessage->data.keyFrameData.dataSetFields[counter].hasStatus = UA_FALSE;
-            }
-            if((dataSetWriter->config.dataSetFieldContentMask & (unsigned int) UA_DATASETFIELDCONTENTMASK_SOURCETIMESTAMP) == 0){
-                dataSetMessage->data.keyFrameData.dataSetFields[counter].hasSourceTimestamp = UA_FALSE;
-                if((dataSetWriter->config.dataSetFieldContentMask & (unsigned int) UA_DATASETFIELDCONTENTMASK_SOURCEPICOSECONDS) == 0){
-                    dataSetMessage->data.keyFrameData.dataSetFields[counter].hasServerPicoseconds = UA_FALSE;
-                }
-            }
-            if((dataSetWriter->config.dataSetFieldContentMask & (unsigned int) UA_DATASETFIELDCONTENTMASK_SERVERTIMESTAMP) == 0){
-                dataSetMessage->data.keyFrameData.dataSetFields[counter].hasServerTimestamp = UA_FALSE;
-            }
-            //Update lastValue store
-            UA_DataValue_deleteMembers(dataSetWriter->lastSamples[counter].value);
-            UA_DataValue_copy(&tmpDataSetField->lastValue, dataSetWriter->lastSamples[counter++].value);
-        }
+    UA_DataSetField *dsf;
+    LIST_FOREACH(dsf, &currentDataSet->fields, listEntry) {
+        /* Sample the value */
+        UA_DataValue *dfv = &dataSetMessage->data.keyFrameData.dataSetFields[counter];
+        UA_PubSubDataSetField_sampleValue(server, dsf, dfv);
+
+        /* Deactivate statuscode? */
+        if((dataSetWriter->config.dataSetFieldContentMask & UA_DATASETFIELDCONTENTMASK_STATUSCODE) == 0)
+            dfv->hasStatus = false;
+
+        /* Deactivate timestamps */
+        if((dataSetWriter->config.dataSetFieldContentMask & UA_DATASETFIELDCONTENTMASK_SOURCETIMESTAMP) == 0)
+            dfv->hasSourceTimestamp = false;
+        if((dataSetWriter->config.dataSetFieldContentMask & UA_DATASETFIELDCONTENTMASK_SOURCEPICOSECONDS) == 0)
+            dfv->hasSourcePicoseconds = false;
+        if((dataSetWriter->config.dataSetFieldContentMask & UA_DATASETFIELDCONTENTMASK_SERVERTIMESTAMP) == 0)
+            dfv->hasServerTimestamp = false;
+        if((dataSetWriter->config.dataSetFieldContentMask & UA_DATASETFIELDCONTENTMASK_SERVERPICOSECONDS) == 0)
+            dfv->hasServerPicoseconds = false;
+
+#ifdef UA_ENABLE_PUBSUB_DELTAFRAMES
+        /* Update lastValue store */
+        UA_DataValue_deleteMembers(&dataSetWriter->lastSamples[counter].value);
+        UA_DataValue_copy(dfv, &dataSetWriter->lastSamples[counter].value);
+#endif
+
+        counter++;
     }
     return UA_STATUSCODE_GOOD;
 }
 
+#ifdef UA_ENABLE_PUBSUB_DELTAFRAMES
 static UA_StatusCode
-UA_PubSubDataSetWriter_generateDeltaFrameMessage(UA_Server *server, UA_DataSetMessage *dataSetMessage,
+UA_PubSubDataSetWriter_generateDeltaFrameMessage(UA_Server *server,
+                                                 UA_DataSetMessage *dataSetMessage,
                                                  UA_DataSetWriter *dataSetWriter) {
-    UA_PublishedDataSet *currentDataSet = UA_PublishedDataSet_findPDSbyId(server, dataSetWriter->connectedDataSet);
+    UA_PublishedDataSet *currentDataSet =
+        UA_PublishedDataSet_findPDSbyId(server, dataSetWriter->connectedDataSet);
     if(!currentDataSet)
         return UA_STATUSCODE_BADNOTFOUND;
-    //prepare DataSetMessageContent
+
+    /* Prepare DataSetMessageContent */
     memset(dataSetMessage, 0, sizeof(UA_DataSetMessage));
     dataSetMessage->header.dataSetMessageValid = true;
     dataSetMessage->header.dataSetMessageType = UA_DATASETMESSAGE_DATADELTAFRAME;
-    UA_DataSetField *tmpDataSetField;
+
+    UA_DataSetField *dsf;
     size_t counter = 0;
-    LIST_FOREACH(tmpDataSetField, &currentDataSet->fields, listEntry) {
-        if(UA_PubSubDataSetField_sampleValue(server, tmpDataSetField) == UA_STATUSCODE_GOOD) {
-            //check if the value has changed
-            if(valueChangedVariant(&dataSetWriter->lastSamples[counter].value->value, &tmpDataSetField->lastValue.value)){
-                //increase fieldCount for current delta message
-                dataSetMessage->data.deltaFrameData.fieldCount++;
-                dataSetWriter->lastSamples[counter].valeChanged = UA_TRUE;
-            } else {
-                dataSetWriter->lastSamples[counter].valeChanged = UA_FALSE;
-            }
-            //update last stored sample
-            UA_DataValue_init(dataSetWriter->lastSamples[counter].value);
-            UA_DataValue_copy(&tmpDataSetField->lastValue, dataSetWriter->lastSamples[counter++].value);
+    LIST_FOREACH(dsf, &currentDataSet->fields, listEntry) {
+        /* Sample the value */
+        UA_DataValue value;
+        UA_DataValue_init(&value);
+        UA_PubSubDataSetField_sampleValue(server, dsf, &value);
+
+        /* Check if the value has changed */
+        if(valueChangedVariant(&dataSetWriter->lastSamples[counter].value.value, &value.value)) {
+            /* increase fieldCount for current delta message */
+            dataSetMessage->data.deltaFrameData.fieldCount++;
+            dataSetWriter->lastSamples[counter].valueChanged = UA_TRUE;
+
+            /* Update last stored sample */
+            UA_DataValue_deleteMembers(&dataSetWriter->lastSamples[counter].value);
+            dataSetWriter->lastSamples[counter].value = value;
+        } else {
+            UA_DataValue_deleteMembers(&value);
+            dataSetWriter->lastSamples[counter].valueChanged = UA_FALSE;
         }
+
+        counter++;
     }
-    //allocate DeltaFrameFields
-    UA_DataSetMessage_DeltaFrameField * deltaFields = (UA_DataSetMessage_DeltaFrameField *)
+
+    /* Allocate DeltaFrameFields */
+    UA_DataSetMessage_DeltaFrameField *deltaFields = (UA_DataSetMessage_DeltaFrameField *)
             UA_calloc(dataSetMessage->data.deltaFrameData.fieldCount, sizeof(UA_DataSetMessage_DeltaFrameField));
     if(!deltaFields)
         return UA_STATUSCODE_BADOUTOFMEMORY;
 
     dataSetMessage->data.deltaFrameData.deltaFrameFields = deltaFields;
     size_t currentDeltaField = 0;
-    for(size_t i = 0; i < currentDataSet->fieldSize; i++){
-        if(dataSetWriter->lastSamples[i].valeChanged){
-            deltaFields[currentDeltaField].fieldIndex = (UA_UInt16) i;
-            UA_DataValue_copy(dataSetWriter->lastSamples[i].value, &deltaFields[currentDeltaField].fieldValue);
-            dataSetWriter->lastSamples[i].valeChanged = false;
-            if((dataSetWriter->config.dataSetFieldContentMask & (unsigned int) UA_DATASETFIELDCONTENTMASK_STATUSCODE) == 0){
-                dataSetMessage->data.deltaFrameData.deltaFrameFields[currentDeltaField].fieldValue.hasStatus = UA_FALSE;
-            }
-            if((dataSetWriter->config.dataSetFieldContentMask & (unsigned int) UA_DATASETFIELDCONTENTMASK_SOURCETIMESTAMP) == 0){
-                dataSetMessage->data.deltaFrameData.deltaFrameFields[currentDeltaField].fieldValue.hasSourceTimestamp = UA_FALSE;
-                if((dataSetWriter->config.dataSetFieldContentMask & (unsigned int) UA_DATASETFIELDCONTENTMASK_SOURCEPICOSECONDS) == 0){
-                    dataSetMessage->data.deltaFrameData.deltaFrameFields[currentDeltaField].fieldValue.hasServerPicoseconds = UA_FALSE;
-                }
-            }
-            if((dataSetWriter->config.dataSetFieldContentMask & (unsigned int) UA_DATASETFIELDCONTENTMASK_SERVERTIMESTAMP) == 0){
-                dataSetMessage->data.deltaFrameData.deltaFrameFields[currentDeltaField].fieldValue.hasServerTimestamp = UA_FALSE;
-            }
-            currentDeltaField++;
-        }
+    for(size_t i = 0; i < currentDataSet->fieldSize; i++) {
+        if(!dataSetWriter->lastSamples[i].valueChanged)
+            continue;
+
+        UA_DataSetMessage_DeltaFrameField *dff = &deltaFields[currentDeltaField];
+        
+        dff->fieldIndex = (UA_UInt16) i;
+        UA_DataValue_copy(&dataSetWriter->lastSamples[i].value, &dff->fieldValue);
+        dataSetWriter->lastSamples[i].valueChanged = false;
+
+        /* Deactivate statuscode? */
+        if((dataSetWriter->config.dataSetFieldContentMask & UA_DATASETFIELDCONTENTMASK_STATUSCODE) == 0)
+            dff->fieldValue.hasStatus = UA_FALSE;
+
+        /* Deactivate timestamps? */
+        if((dataSetWriter->config.dataSetFieldContentMask & UA_DATASETFIELDCONTENTMASK_SOURCETIMESTAMP) == 0)
+            dff->fieldValue.hasSourceTimestamp = UA_FALSE;
+        if((dataSetWriter->config.dataSetFieldContentMask & UA_DATASETFIELDCONTENTMASK_SOURCEPICOSECONDS) == 0)
+            dff->fieldValue.hasServerPicoseconds = UA_FALSE;
+        if((dataSetWriter->config.dataSetFieldContentMask & UA_DATASETFIELDCONTENTMASK_SERVERTIMESTAMP) == 0)
+            dff->fieldValue.hasServerTimestamp = UA_FALSE;
+        if((dataSetWriter->config.dataSetFieldContentMask & UA_DATASETFIELDCONTENTMASK_SERVERPICOSECONDS) == 0)
+            dff->fieldValue.hasServerPicoseconds = UA_FALSE;
+
+        currentDeltaField++;
     }
     return UA_STATUSCODE_GOOD;
 }
+#endif
 
 /**
  * Generate a DataSetMessage for the given writer.
@@ -823,102 +847,128 @@ UA_PubSubDataSetWriter_generateDeltaFrameMessage(UA_Server *server, UA_DataSetMe
 static UA_StatusCode
 UA_DataSetWriter_generateDataSetMessage(UA_Server *server, UA_DataSetMessage *dataSetMessage,
                                         UA_DataSetWriter *dataSetWriter) {
-    UA_PublishedDataSet *currentDataSet = UA_PublishedDataSet_findPDSbyId(server, dataSetWriter->connectedDataSet);
+    UA_PublishedDataSet *currentDataSet =
+        UA_PublishedDataSet_findPDSbyId(server, dataSetWriter->connectedDataSet);
     if(!currentDataSet)
         return UA_STATUSCODE_BADNOTFOUND;
+
+    /* Reset the message */
     memset(dataSetMessage, 0, sizeof(UA_DataSetMessage));
-    //currently is only UADP supported. The configuration Flags are included inside the std. defined UA_UadpDataSetWriterMessageDataType
+
+    /* Currently is only UADP supported. The configuration Flags are included
+     * inside the std. defined UA_UadpDataSetWriterMessageDataType */
+    UA_UadpDataSetWriterMessageDataType defaultUadpConfiguration;
     UA_UadpDataSetWriterMessageDataType *dataSetWriterMessageDataType = NULL;
     if((dataSetWriter->config.messageSettings.encoding == UA_EXTENSIONOBJECT_DECODED ||
         dataSetWriter->config.messageSettings.encoding == UA_EXTENSIONOBJECT_DECODED_NODELETE) &&
        (dataSetWriter->config.messageSettings.content.decoded.type == &UA_TYPES[UA_TYPES_UADPDATASETWRITERMESSAGEDATATYPE])) {
-        dataSetWriterMessageDataType = (UA_UadpDataSetWriterMessageDataType *) dataSetWriter->config.messageSettings.content.decoded.data;
+        dataSetWriterMessageDataType = (UA_UadpDataSetWriterMessageDataType *)
+            dataSetWriter->config.messageSettings.content.decoded.data;
     } else {
-        //create default flag configuration if no UadpDataSetWriterMessageDataType was passed in
-        UA_UadpDataSetWriterMessageDataType defaultUadpConfiguration;
+        /* create default flag configuration if no
+         * UadpDataSetWriterMessageDataType was passed in */
         memset(&defaultUadpConfiguration, 0, sizeof(UA_UadpDataSetWriterMessageDataType));
-        defaultUadpConfiguration.dataSetMessageContentMask = (UA_UadpDataSetMessageContentMask) ((unsigned int) UA_UADPDATASETMESSAGECONTENTMASK_TIMESTAMP |
-                                                                                                 (unsigned int) UA_UADPDATASETMESSAGECONTENTMASK_MAJORVERSION |
-                                                                                                 (unsigned int) UA_UADPDATASETMESSAGECONTENTMASK_MINORVERSION);
+        defaultUadpConfiguration.dataSetMessageContentMask = (UA_UadpDataSetMessageContentMask)
+            (UA_UADPDATASETMESSAGECONTENTMASK_TIMESTAMP | UA_UADPDATASETMESSAGECONTENTMASK_MAJORVERSION |
+             UA_UADPDATASETMESSAGECONTENTMASK_MINORVERSION);
         dataSetWriterMessageDataType = &defaultUadpConfiguration;
     }
-    if(dataSetWriterMessageDataType->networkMessageNumber != 0 || dataSetWriterMessageDataType->dataSetOffset != 0 ||
-       dataSetWriterMessageDataType->configuredSize !=0 ){
-        UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_SERVER, "Static DSM configuration not supported. Using defaults");
+
+    /* Sanity-test the configuration */
+    if(dataSetWriterMessageDataType->networkMessageNumber != 0 ||
+       dataSetWriterMessageDataType->dataSetOffset != 0 ||
+       dataSetWriterMessageDataType->configuredSize !=0 ) {
+        UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_SERVER,
+                       "Static DSM configuration not supported. Using defaults");
         dataSetWriterMessageDataType->networkMessageNumber = 0;
         dataSetWriterMessageDataType->dataSetOffset = 0;
         dataSetWriterMessageDataType->configuredSize = 0;
     }
-    //The encoding depends on the flags inside the writer config.
-    if(dataSetWriter->config.dataSetFieldContentMask & (unsigned int) UA_DATASETFIELDCONTENTMASK_RAWDATAENCODING) {
+
+    /* The field encoding depends on the flags inside the writer config.
+     * TODO: This can be moved to the encoding layer. */
+    if(dataSetWriter->config.dataSetFieldContentMask & UA_DATASETFIELDCONTENTMASK_RAWDATAENCODING) {
         dataSetMessage->header.fieldEncoding = UA_FIELDENCODING_RAWDATA;
     } else if (dataSetWriter->config.dataSetFieldContentMask &
-               ((unsigned  int) UA_DATASETFIELDCONTENTMASK_SOURCETIMESTAMP |
-                (unsigned  int) UA_DATASETFIELDCONTENTMASK_SERVERPICOSECONDS |
-                (unsigned  int) UA_DATASETFIELDCONTENTMASK_SOURCEPICOSECONDS |
-                (unsigned  int) UA_DATASETFIELDCONTENTMASK_STATUSCODE)) {
+               (UA_DATASETFIELDCONTENTMASK_SOURCETIMESTAMP | UA_DATASETFIELDCONTENTMASK_SERVERPICOSECONDS |
+                UA_DATASETFIELDCONTENTMASK_SOURCEPICOSECONDS | UA_DATASETFIELDCONTENTMASK_STATUSCODE)) {
         dataSetMessage->header.fieldEncoding = UA_FIELDENCODING_DATAVALUE;
     } else {
         dataSetMessage->header.fieldEncoding = UA_FIELDENCODING_VARIANT;
     }
-    //Std: 'The DataSetMessageContentMask defines the flags for the content of the DataSetMessage header.'
-    if(dataSetWriterMessageDataType->dataSetMessageContentMask & (unsigned int) UA_UADPDATASETMESSAGECONTENTMASK_MAJORVERSION){
+
+    /* Std: 'The DataSetMessageContentMask defines the flags for the content of the DataSetMessage header.' */
+    if(dataSetWriterMessageDataType->dataSetMessageContentMask & UA_UADPDATASETMESSAGECONTENTMASK_MAJORVERSION){
         dataSetMessage->header.configVersionMajorVersionEnabled = UA_TRUE;
-        dataSetMessage->header.configVersionMajorVersion = currentDataSet->dataSetMetaData.configurationVersion.majorVersion;
+        dataSetMessage->header.configVersionMajorVersion =
+            currentDataSet->dataSetMetaData.configurationVersion.majorVersion;
     }
-    if(dataSetWriterMessageDataType->dataSetMessageContentMask & (unsigned int) UA_UADPDATASETMESSAGECONTENTMASK_MINORVERSION){
+    if(dataSetWriterMessageDataType->dataSetMessageContentMask & UA_UADPDATASETMESSAGECONTENTMASK_MINORVERSION){
         dataSetMessage->header.configVersionMinorVersionEnabled = UA_TRUE;
-        dataSetMessage->header.configVersionMinorVersion = currentDataSet->dataSetMetaData.configurationVersion.minorVersion;
+        dataSetMessage->header.configVersionMinorVersion =
+            currentDataSet->dataSetMetaData.configurationVersion.minorVersion;
     }
-    if(dataSetWriterMessageDataType->dataSetMessageContentMask & (unsigned int) UA_UADPDATASETMESSAGECONTENTMASK_SEQUENCENUMBER) {
+
+    if(dataSetWriterMessageDataType->dataSetMessageContentMask & UA_UADPDATASETMESSAGECONTENTMASK_SEQUENCENUMBER) {
         dataSetMessage->header.dataSetMessageSequenceNrEnabled = UA_TRUE;
-        dataSetMessage->header.dataSetMessageSequenceNr = dataSetWriter->actualDataSetMessageSequenceCount;
+        dataSetMessage->header.dataSetMessageSequenceNr =
+            dataSetWriter->actualDataSetMessageSequenceCount;
     }
-    if(dataSetWriterMessageDataType->dataSetMessageContentMask & (unsigned int) UA_UADPDATASETMESSAGECONTENTMASK_TIMESTAMP) {
+
+    if(dataSetWriterMessageDataType->dataSetMessageContentMask & UA_UADPDATASETMESSAGECONTENTMASK_TIMESTAMP) {
         dataSetMessage->header.timestampEnabled = UA_TRUE;
         dataSetMessage->header.timestamp = UA_DateTime_now();
-        if(dataSetWriterMessageDataType->dataSetMessageContentMask & (unsigned int) UA_UADPDATASETMESSAGECONTENTMASK_PICOSECONDS) {
-            dataSetMessage->header.picoSecondsIncluded = UA_FALSE;
-            UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_SERVER, "DSM picosecond field is currently not supported. Using defaults");
-        }
     }
-    if(dataSetWriterMessageDataType->dataSetMessageContentMask & (unsigned int) UA_UADPDATASETMESSAGECONTENTMASK_STATUS){
-        dataSetMessage->header.statusEnabled = UA_FALSE;
-        UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_SERVER, "DSM status field is currently not supported. Using defaults");
+    /* TODO: Picoseconds resolution not supported atm */
+    if(dataSetWriterMessageDataType->dataSetMessageContentMask & UA_UADPDATASETMESSAGECONTENTMASK_PICOSECONDS) {
+        dataSetMessage->header.picoSecondsIncluded = UA_FALSE;
     }
-    if(dataSetWriter->actualDataSetMessageSequenceCount < UA_UINT16_MAX){
-        dataSetWriter->actualDataSetMessageSequenceCount++;
-    } else {
-        dataSetWriter->actualDataSetMessageSequenceCount = 0;
+
+    /* TODO: Statuscode not supported yet */
+    if(dataSetWriterMessageDataType->dataSetMessageContentMask & UA_UADPDATASETMESSAGECONTENTMASK_STATUS){
+        dataSetMessage->header.statusEnabled = UA_FALSE;
     }
-    //check if the PublishedDataSet version has changed -> if yes flush the lastValue store and send a KeyFrame.
+
+    /* Set the sequence count. Automatically rolls over to zero */
+    dataSetWriter->actualDataSetMessageSequenceCount++;
+
+#ifdef UA_ENABLE_PUBSUB_DELTAFRAMES
+    /* Check if the PublishedDataSet version has changed -> if yes flush the lastValue store and send a KeyFrame */
     if(dataSetWriter->connectedDataSetVersion.majorVersion != currentDataSet->dataSetMetaData.configurationVersion.majorVersion ||
        dataSetWriter->connectedDataSetVersion.minorVersion != currentDataSet->dataSetMetaData.configurationVersion.minorVersion) {
+        /* Remove old samples */
+        for(size_t i = 0; i < dataSetWriter->lastSamplesCount; i++)
+            UA_DataValue_deleteMembers(&dataSetWriter->lastSamples[i].value);
 
-        //realloc pds dependent memory
+        /* Realloc pds dependent memory */
         dataSetWriter->lastSamplesCount = currentDataSet->fieldSize;
-        dataSetWriter->lastSamples = (UA_DataSetWriterSample * ) UA_realloc(dataSetWriter->lastSamples,
-                                                                            sizeof(UA_DataSetWriterSample) * dataSetWriter->lastSamplesCount);
-        if(!dataSetWriter->lastSamples)
+        UA_DataSetWriterSample *newSamplesArray = (UA_DataSetWriterSample * )
+            UA_realloc(dataSetWriter->lastSamples, sizeof(UA_DataSetWriterSample) * dataSetWriter->lastSamplesCount);
+        if(!newSamplesArray)
             return UA_STATUSCODE_BADOUTOFMEMORY;
+        dataSetWriter->lastSamples = newSamplesArray;
+        memset(dataSetWriter->lastSamples, 0, sizeof(UA_DataSetWriterSample) * dataSetWriter->lastSamplesCount);
 
-        for (size_t i = 0; i < dataSetWriter->lastSamplesCount; i++) {
-            dataSetWriter->lastSamples[i].value = (UA_DataValue *) UA_calloc(1, sizeof(UA_DataValue));
-            if(!dataSetWriter->lastSamples[i].value)
-                return UA_STATUSCODE_BADOUTOFMEMORY;
-        }
         dataSetWriter->connectedDataSetVersion = currentDataSet->dataSetMetaData.configurationVersion;
         UA_PubSubDataSetWriter_generateKeyFrameMessage(server, dataSetMessage, dataSetWriter);
         dataSetWriter->deltaFrameCounter = 0;
-    } else if (currentDataSet->fieldSize == 1 || dataSetWriter->deltaFrameCounter == 0 || dataSetWriter->deltaFrameCounter > dataSetWriter->config.keyFrameCount){
-        //@info the standard defines: if a PDS contains only one fields no delta messages should be generated
-        //because they need more memory than a keyframe with 1 field.
-        UA_PubSubDataSetWriter_generateKeyFrameMessage(server, dataSetMessage, dataSetWriter);
-        dataSetWriter->deltaFrameCounter = 1;
-    } else {
+        return UA_STATUSCODE_GOOD;
+    }
+
+    /* The standard defines: if a PDS contains only one fields no delta messages
+     * should be generated because they need more memory than a keyframe with 1
+     * field. */
+    if(currentDataSet->fieldSize > 1 && dataSetWriter->deltaFrameCounter > 0 &&
+       dataSetWriter->deltaFrameCounter <= dataSetWriter->config.keyFrameCount) {
         UA_PubSubDataSetWriter_generateDeltaFrameMessage(server, dataSetMessage, dataSetWriter);
         dataSetWriter->deltaFrameCounter++;
+        return UA_STATUSCODE_GOOD;
     }
+
+    dataSetWriter->deltaFrameCounter = 1;
+#endif
+
+    UA_PubSubDataSetWriter_generateKeyFrameMessage(server, dataSetMessage, dataSetWriter);
     return UA_STATUSCODE_GOOD;
 }
 

+ 6 - 3
src/pubsub/ua_pubsub.h

@@ -68,10 +68,12 @@ UA_PubSubConnection_deleteMembers(UA_Server *server, UA_PubSubConnection *connec
 /*              DataSetWriter                 */
 /**********************************************/
 
+#ifdef UA_ENABLE_PUBSUB_DELTAFRAMES
 typedef struct UA_DataSetWriterSample{
-    UA_Boolean valeChanged;
-    UA_DataValue *value;
+    UA_Boolean valueChanged;
+    UA_DataValue value;
 } UA_DataSetWriterSample;
+#endif
 
 typedef struct UA_DataSetWriter{
     UA_DataSetWriterConfig config;
@@ -81,9 +83,11 @@ typedef struct UA_DataSetWriter{
     UA_NodeId linkedWriterGroup;
     UA_NodeId connectedDataSet;
     UA_ConfigurationVersionDataType connectedDataSetVersion;
+#ifdef UA_ENABLE_PUBSUB_DELTAFRAMES
     UA_UInt16 deltaFrameCounter;            //actual count of sent deltaFrames
     size_t lastSamplesCount;
     UA_DataSetWriterSample *lastSamples;
+#endif
     UA_UInt16 actualDataSetMessageSequenceCount;
 } UA_DataSetWriter;
 
@@ -130,7 +134,6 @@ typedef struct UA_DataSetField{
     UA_FieldMetaData fieldMetaData;
     UA_UInt64 sampleCallbackId;
     UA_Boolean sampleCallbackIsRegistered;
-    UA_DataValue lastValue;
 } UA_DataSetField;
 
 UA_StatusCode

+ 4 - 4
src/pubsub/ua_pubsub_networkmessage.c

@@ -76,7 +76,7 @@ UA_NetworkMessage_encodeBinary(const UA_NetworkMessage* src, UA_Byte **bufPos,
 
     // ExtendedFlags1
     if(UA_NetworkMessage_ExtendedFlags1Enabled(src)) {
-        v = src->publisherIdType;
+        v = (UA_Byte)src->publisherIdType;
 
         if(src->dataSetClassIdEnabled)
             v |= NM_DATASET_CLASSID_ENABLED_MASK;
@@ -99,7 +99,7 @@ UA_NetworkMessage_encodeBinary(const UA_NetworkMessage* src, UA_Byte **bufPos,
 
         // ExtendedFlags2
         if(UA_NetworkMessage_ExtendedFlags2Enabled(src)) { 
-            v = src->networkMessageType;
+            v = (UA_Byte)src->networkMessageType;
             // shift left 2 bit
             v = (UA_Byte) (v << NM_SHIFT_LEN);
 
@@ -856,7 +856,7 @@ UA_DataSetMessageHeader_encodeBinary(const UA_DataSetMessageHeader* src, UA_Byte
 
     UA_Byte v;
     // DataSetFlags1 
-    v = src->fieldEncoding;
+    v = (UA_Byte)src->fieldEncoding;
     // shift left 1 bit
     v = (UA_Byte)(v << DS_MH_SHIFT_LEN);
 
@@ -884,7 +884,7 @@ UA_DataSetMessageHeader_encodeBinary(const UA_DataSetMessageHeader* src, UA_Byte
     
     // DataSetFlags2
     if(UA_DataSetMessageHeader_DataSetFlags2Enabled(src)) {
-        v = src->dataSetMessageType;
+        v = (UA_Byte)src->dataSetMessageType;
 
         if(src->timestampEnabled)
             v |= DS_MESSAGEHEADER_TIMESTAMP_ENABLED_MASK;

+ 2 - 2
src/server/ua_mdns.c

@@ -471,8 +471,8 @@ void mdns_set_address_record(UA_Server *server, const char *fullServiceDomain,
             std::string ipv6_str(str_buffer);
 
             // Detect and skip non-external addresses
-            bool is_link_local(false);
-            bool is_special_use(false);
+            UA_Boolean is_link_local(false);
+            UA_Boolean is_special_use(false);
 
             if(0 == ipv6_str.find("fe")) {
             char c = ipv6_str[2];

+ 17 - 0
src/server/ua_server.c

@@ -1,3 +1,4 @@
+
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. 
@@ -13,6 +14,7 @@
  *    Copyright 2016 (c) Lorenz Haas
  *    Copyright 2017 (c) frax2222
  *    Copyright 2017 (c) Mark Giraud, Fraunhofer IOSB
+ *    Copyright 2018 (c) Hilscher Gesellschaft für Systemautomation mbH (Author: Martin Lang)
  */
 
 #include "ua_types.h"
@@ -65,6 +67,21 @@ UA_UInt16 UA_Server_addNamespace(UA_Server *server, const char* name) {
     return addNamespace(server, nameString);
 }
 
+UA_StatusCode 
+UA_Server_getNamespaceByName(UA_Server *server, const UA_String namespaceUri,
+                             size_t* foundIndex) {
+  for(size_t idx = 0; idx < server->namespacesSize; idx++)
+  {
+    if(UA_String_equal(&server->namespaces[idx], &namespaceUri) == true)
+    {
+      (*foundIndex) = idx;
+      return UA_STATUSCODE_GOOD;
+    }
+  }
+
+  return UA_STATUSCODE_BADNOTFOUND;
+}
+
 UA_StatusCode
 UA_Server_forEachChildNodeCall(UA_Server *server, UA_NodeId parentNodeId,
                                UA_NodeIteratorCallback callback, void *handle) {

+ 8 - 5
src/server/ua_server_binary.c

@@ -77,6 +77,7 @@ typedef enum {
 static void
 getServicePointers(UA_UInt32 requestTypeId, const UA_DataType **requestType,
                    const UA_DataType **responseType, UA_Service *service,
+                   UA_InSituService *serviceInsitu,
                    UA_Boolean *requiresSession, UA_ServiceType *serviceType) {
     switch(requestTypeId) {
     case UA_NS0ID_GETENDPOINTSREQUEST_ENCODING_DEFAULTBINARY:
@@ -114,14 +115,14 @@ getServicePointers(UA_UInt32 requestTypeId, const UA_DataType **requestType,
         break;
 #endif
     case UA_NS0ID_CREATESESSIONREQUEST_ENCODING_DEFAULTBINARY:
-        *service = (UA_Service)Service_CreateSession;
+        *service = NULL; //(UA_Service)Service_CreateSession;
         *requestType = &UA_TYPES[UA_TYPES_CREATESESSIONREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_CREATESESSIONRESPONSE];
         *requiresSession = false;
         *serviceType = UA_SERVICETYPE_CUSTOM;
         break;
     case UA_NS0ID_ACTIVATESESSIONREQUEST_ENCODING_DEFAULTBINARY:
-        *service = (UA_Service)Service_ActivateSession;
+        *service = NULL; //(UA_Service)Service_ActivateSession;
         *requestType = &UA_TYPES[UA_TYPES_ACTIVATESESSIONREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_ACTIVATESESSIONRESPONSE];
         *serviceType = UA_SERVICETYPE_CUSTOM;
@@ -132,7 +133,8 @@ getServicePointers(UA_UInt32 requestTypeId, const UA_DataType **requestType,
         *responseType = &UA_TYPES[UA_TYPES_CLOSESESSIONRESPONSE];
         break;
     case UA_NS0ID_READREQUEST_ENCODING_DEFAULTBINARY:
-        *service = (UA_Service)Service_Read;
+        *service = NULL;
+        *serviceInsitu = (UA_InSituService)Service_Read;
         *requestType = &UA_TYPES[UA_TYPES_READREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_READRESPONSE];
         *serviceType = UA_SERVICETYPE_INSITU;
@@ -400,12 +402,13 @@ processMSG(UA_Server *server, UA_SecureChannel *channel,
 
     /* Get the service pointers */
     UA_Service service = NULL;
+    UA_InSituService serviceInsitu = NULL;
     const UA_DataType *requestType = NULL;
     const UA_DataType *responseType = NULL;
     UA_Boolean sessionRequired = true;
     UA_ServiceType serviceType = UA_SERVICETYPE_NORMAL;
     getServicePointers(requestTypeId.identifier.numeric, &requestType,
-                       &responseType, &service, &sessionRequired, &serviceType);
+                       &responseType, &service, &serviceInsitu, &sessionRequired, &serviceType);
     if(!requestType) {
         if(requestTypeId.identifier.numeric == 787) {
             UA_LOG_INFO_CHANNEL(server->config.logger, channel,
@@ -561,7 +564,7 @@ processMSG(UA_Server *server, UA_SecureChannel *channel,
         retval = UA_MessageContext_encode(&mc, response, responseType);
         break;
     case UA_SERVICETYPE_INSITU:
-        retval = ((UA_InSituService)service)
+        retval = serviceInsitu
             (server, session, &mc, request, (UA_ResponseHeader*)response);
         break;
     case UA_SERVICETYPE_NORMAL:

+ 1 - 3
src/server/ua_server_internal.h

@@ -236,9 +236,7 @@ UA_Server_workerCallback(UA_Server *server, UA_ServerCallback callback, void *da
 
 /* A few global NodeId definitions */
 extern const UA_NodeId subtypeId;
-
-UA_StatusCode
-UA_NumericRange_parseFromString(UA_NumericRange *range, const UA_String *str);
+extern const UA_NodeId hierarchicalReferences;
 
 UA_UInt16 addNamespace(UA_Server *server, const UA_String name);
 

+ 63 - 1
src/server/ua_server_ns0.c

@@ -7,6 +7,7 @@
  *    Copyright 2017 (c) Thomas Bender
  *    Copyright 2017 (c) Julian Grothoff
  *    Copyright 2017 (c) Henrik Norrman
+ *    Copyright 2018 (c) Fabian Arndt, Root-Core
  */
 
 #include "ua_server_internal.h"
@@ -583,7 +584,7 @@ UA_Server_initNS0(UA_Server *server) {
                                &maxBrowseContinuationPoints, &UA_TYPES[UA_TYPES_UINT16]);
 
     /* ServerProfileArray */
-    UA_String profileArray[4];
+    UA_String profileArray[5];
     UA_UInt16 profileArraySize = 0;
 #define ADDPROFILEARRAY(x) profileArray[profileArraySize++] = UA_STRING_ALLOC(x)
     ADDPROFILEARRAY("http://opcfoundation.org/UA-Profile/Server/NanoEmbeddedDevice");
@@ -596,6 +597,9 @@ UA_Server_initNS0(UA_Server *server) {
 #ifdef UA_ENABLE_SUBSCRIPTIONS
     ADDPROFILEARRAY("http://opcfoundation.org/UA-Profile/Server/EmbeddedDataChangeSubscription");
 #endif
+#ifdef UA_ENABLE_HISTORIZING
+    ADDPROFILEARRAY("http://opcfoundation.org/UA-Profile/Server/HistoricalRawData");
+#endif
 
     retVal |= writeNs0VariableArray(server, UA_NS0ID_SERVER_SERVERCAPABILITIES_SERVERPROFILEARRAY,
                                     profileArray, profileArraySize, &UA_TYPES[UA_TYPES_STRING]);
@@ -737,6 +741,64 @@ UA_Server_initNS0(UA_Server *server) {
     retVal |= writeNs0Variable(server, UA_NS0ID_SERVER_SERVERCAPABILITIES_OPERATIONLIMITS_MAXMONITOREDITEMSPERCALL,
                                &server->config.maxMonitoredItemsPerCall, &UA_TYPES[UA_TYPES_UINT32]);
 
+#ifdef UA_ENABLE_HISTORIZING
+    /* ServerCapabilities - HistoryServerCapabilities - AccessHistoryDataCapability */
+    retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_ACCESSHISTORYDATACAPABILITY,
+                               &server->config.accessHistoryDataCapability, &UA_TYPES[UA_TYPES_BOOLEAN]);
+
+    /* ServerCapabilities - HistoryServerCapabilities - MaxReturnDataValues */
+    retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_MAXRETURNDATAVALUES,
+                               &server->config.maxReturnDataValues, &UA_TYPES[UA_TYPES_UINT32]);
+
+    /* ServerCapabilities - HistoryServerCapabilities - AccessHistoryEventsCapability */
+    retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_ACCESSHISTORYEVENTSCAPABILITY,
+                               &server->config.accessHistoryEventsCapability, &UA_TYPES[UA_TYPES_BOOLEAN]);
+
+    /* ServerCapabilities - HistoryServerCapabilities - MaxReturnEventValues */
+    retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_MAXRETURNEVENTVALUES,
+                               &server->config.maxReturnEventValues, &UA_TYPES[UA_TYPES_UINT32]);
+
+    /* ServerCapabilities - HistoryServerCapabilities - InsertDataCapability */
+    retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_INSERTDATACAPABILITY,
+                               &server->config.insertDataCapability, &UA_TYPES[UA_TYPES_BOOLEAN]);
+
+    /* ServerCapabilities - HistoryServerCapabilities - InsertEventCapability */
+    retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_INSERTEVENTCAPABILITY,
+                               &server->config.insertEventCapability, &UA_TYPES[UA_TYPES_BOOLEAN]);
+
+    /* ServerCapabilities - HistoryServerCapabilities - InsertAnnotationsCapability */
+    retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_INSERTANNOTATIONCAPABILITY,
+                               &server->config.insertAnnotationsCapability, &UA_TYPES[UA_TYPES_BOOLEAN]);
+
+    /* ServerCapabilities - HistoryServerCapabilities - ReplaceDataCapability */
+    retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_REPLACEDATACAPABILITY,
+                               &server->config.replaceDataCapability, &UA_TYPES[UA_TYPES_BOOLEAN]);
+
+    /* ServerCapabilities - HistoryServerCapabilities - ReplaceEventCapability */
+    retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_REPLACEEVENTCAPABILITY,
+                               &server->config.replaceEventCapability, &UA_TYPES[UA_TYPES_BOOLEAN]);
+
+    /* ServerCapabilities - HistoryServerCapabilities - UpdateDataCapability */
+    retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_UPDATEDATACAPABILITY,
+                               &server->config.updateDataCapability, &UA_TYPES[UA_TYPES_BOOLEAN]);
+
+    /* ServerCapabilities - HistoryServerCapabilities - UpdateEventCapability */
+    retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_UPDATEEVENTCAPABILITY,
+                               &server->config.updateEventCapability, &UA_TYPES[UA_TYPES_BOOLEAN]);
+
+    /* ServerCapabilities - HistoryServerCapabilities - DeleteRawCapability */
+    retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_DELETERAWCAPABILITY,
+                               &server->config.deleteRawCapability, &UA_TYPES[UA_TYPES_BOOLEAN]);
+
+    /* ServerCapabilities - HistoryServerCapabilities - DeleteEventCapability */
+    retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_DELETEEVENTCAPABILITY,
+                               &server->config.deleteEventCapability, &UA_TYPES[UA_TYPES_BOOLEAN]);
+
+    /* ServerCapabilities - HistoryServerCapabilities - DeleteAtTimeDataCapability */
+    retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_DELETEATTIMECAPABILITY,
+                               &server->config.deleteAtTimeDataCapability, &UA_TYPES[UA_TYPES_BOOLEAN]);
+#endif
+
 #if defined(UA_ENABLE_METHODCALLS) && defined(UA_ENABLE_SUBSCRIPTIONS)
     retVal |= UA_Server_setMethodNode_callback(server,
                         UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_GETMONITOREDITEMS), readMonitoredItems);

+ 4 - 77
src/server/ua_server_utils.c

@@ -14,83 +14,6 @@
 
 #define UA_MAX_TREE_RECURSE 50 /* How deep up/down the tree do we recurse at most? */
 
-/**********************/
-/* Parse NumericRange */
-/**********************/
-
-static size_t
-readDimension(UA_Byte *buf, size_t buflen, UA_NumericRangeDimension *dim) {
-    size_t progress = UA_readNumber(buf, buflen, &dim->min);
-    if(progress == 0)
-        return 0;
-    if(buflen <= progress + 1 || buf[progress] != ':') {
-        dim->max = dim->min;
-        return progress;
-    }
-
-    ++progress;
-    size_t progress2 = UA_readNumber(&buf[progress], buflen - progress, &dim->max);
-    if(progress2 == 0)
-        return 0;
-
-    /* invalid range */
-    if(dim->min >= dim->max)
-        return 0;
-
-    return progress + progress2;
-}
-
-UA_StatusCode
-UA_NumericRange_parseFromString(UA_NumericRange *range, const UA_String *str) {
-    size_t idx = 0;
-    size_t dimensionsMax = 0;
-    UA_NumericRangeDimension *dimensions = NULL;
-    UA_StatusCode retval = UA_STATUSCODE_GOOD;
-    size_t offset = 0;
-    while(true) {
-        /* alloc dimensions */
-        if(idx >= dimensionsMax) {
-            UA_NumericRangeDimension *newds;
-            size_t newdssize = sizeof(UA_NumericRangeDimension) * (dimensionsMax + 2);
-            newds = (UA_NumericRangeDimension*)UA_realloc(dimensions, newdssize);
-            if(!newds) {
-                retval = UA_STATUSCODE_BADOUTOFMEMORY;
-                break;
-            }
-            dimensions = newds;
-            dimensionsMax = dimensionsMax + 2;
-        }
-
-        /* read the dimension */
-        size_t progress = readDimension(&str->data[offset], str->length - offset,
-                                        &dimensions[idx]);
-        if(progress == 0) {
-            retval = UA_STATUSCODE_BADINDEXRANGEINVALID;
-            break;
-        }
-        offset += progress;
-        ++idx;
-
-        /* loop into the next dimension */
-        if(offset >= str->length)
-            break;
-
-        if(str->data[offset] != ',') {
-            retval = UA_STATUSCODE_BADINDEXRANGEINVALID;
-            break;
-        }
-        ++offset;
-    }
-
-    if(retval == UA_STATUSCODE_GOOD && idx > 0) {
-        range->dimensions = dimensions;
-        range->dimensionsSize = idx;
-    } else
-        UA_free(dimensions);
-
-    return retval;
-}
-
 /********************************/
 /* Information Model Operations */
 /********************************/
@@ -415,6 +338,10 @@ UA_Server_processServiceOperations(UA_Server *server, UA_Session *session,
     return UA_STATUSCODE_GOOD;
 }
 
+/* A few global NodeId definitions */
+const UA_NodeId subtypeId = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HASSUBTYPE}};
+const UA_NodeId hierarchicalReferences = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HIERARCHICALREFERENCES}};
+
 /*********************************/
 /* Default attribute definitions */
 /*********************************/

+ 0 - 1
src/server/ua_services_attribute.c

@@ -570,7 +570,6 @@ typeEquivalence(const UA_DataType *t) {
     return TYPE_EQUIVALENCE_NONE;
 }
 
-const UA_NodeId subtypeId = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HASSUBTYPE}};
 static const UA_NodeId enumNodeId = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_ENUMERATION}};
 
 UA_Boolean

+ 53 - 29
src/server/ua_services_call.c

@@ -4,7 +4,7 @@
  *
  *    Copyright 2015 (c) Chris Iatrou
  *    Copyright 2015-2017 (c) Florian Palm
- *    Copyright 2015-2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer)
+ *    Copyright 2015-2018 (c) Fraunhofer IOSB (Author: Julius Pfrommer)
  *    Copyright 2015-2016 (c) Sten Grüner
  *    Copyright 2015 (c) Oleksiy Vasylyev
  *    Copyright 2016 (c) LEvertz
@@ -48,9 +48,11 @@ getArgumentsVariableNode(UA_Server *server, const UA_MethodNode *ofMethod,
     return NULL;
 }
 
+/* inputArgumentResults has the length request->inputArgumentsSize */
 static UA_StatusCode
 typeCheckArguments(UA_Server *server, const UA_VariableNode *argRequirements,
-                   size_t argsSize, UA_Variant *args) {
+                   size_t argsSize, UA_Variant *args,
+                   UA_StatusCode *inputArgumentResults) {
     /* Verify that we have a Variant containing UA_Argument (scalar or array) in
      * the "InputArguments" node */
     if(argRequirements->valueSource != UA_VALUESOURCE_DATA)
@@ -71,33 +73,38 @@ typeCheckArguments(UA_Server *server, const UA_VariableNode *argRequirements,
         return UA_STATUSCODE_BADTOOMANYARGUMENTS;
 
     /* Type-check every argument against the definition */
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
     UA_Argument *argReqs = (UA_Argument*)argRequirements->value.data.value.value.data;
     for(size_t i = 0; i < argReqsSize; ++i) {
         if(!compatibleValue(server, &argReqs[i].dataType, argReqs[i].valueRank,
                             argReqs[i].arrayDimensionsSize, argReqs[i].arrayDimensions,
-                            &args[i], NULL))
-            return UA_STATUSCODE_BADTYPEMISMATCH;
+                            &args[i], NULL)) {
+            inputArgumentResults[i] = UA_STATUSCODE_BADTYPEMISMATCH;
+            retval = UA_STATUSCODE_BADINVALIDARGUMENT;
+        }
     }
-    return UA_STATUSCODE_GOOD;
+    return retval;
 }
 
+/* inputArgumentResults has the length request->inputArgumentsSize */
 static UA_StatusCode
 validMethodArguments(UA_Server *server, const UA_MethodNode *method,
-                     const UA_CallMethodRequest *request) {
+                     const UA_CallMethodRequest *request,
+                     UA_StatusCode *inputArgumentResults) {
     /* Get the input arguments node */
     const UA_VariableNode *inputArguments =
         getArgumentsVariableNode(server, method, UA_STRING("InputArguments"));
-    UA_StatusCode retval = UA_STATUSCODE_GOOD;
     if(!inputArguments) {
         if(request->inputArgumentsSize > 0)
-            retval = UA_STATUSCODE_BADINVALIDARGUMENT;
-        return retval;
+            return UA_STATUSCODE_BADTOOMANYARGUMENTS;
+        return UA_STATUSCODE_GOOD;
     }
 
     /* Verify the request */
-    retval = typeCheckArguments(server, inputArguments,
-                                request->inputArgumentsSize,
-                                request->inputArguments);
+    UA_StatusCode retval = typeCheckArguments(server, inputArguments,
+                                              request->inputArgumentsSize,
+                                              request->inputArguments,
+                                              inputArgumentResults);
 
     /* Release the input arguments node */
     server->config.nodestore.releaseNode(server->config.nodestore.context,
@@ -168,8 +175,27 @@ callWithMethodAndObject(UA_Server *server, UA_Session *session,
         return;
     }
 
+    /* Allocate the inputArgumentResults array */
+    result->inputArgumentResults = (UA_StatusCode*)
+        UA_Array_new(request->inputArgumentsSize, &UA_TYPES[UA_TYPES_STATUSCODE]);
+    if(!result->inputArgumentResults) {
+        result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
+        return;
+    }
+    result->inputArgumentResultsSize = request->inputArgumentsSize;
+
     /* Verify Input Arguments */
-    result->statusCode = validMethodArguments(server, method, request);
+    result->statusCode = validMethodArguments(server, method, request, result->inputArgumentResults);
+
+    /* Return inputArgumentResults only for BADINVALIDARGUMENT */
+    if(result->statusCode != UA_STATUSCODE_BADINVALIDARGUMENT) {
+        UA_Array_delete(result->inputArgumentResults, result->inputArgumentResultsSize,
+                        &UA_TYPES[UA_TYPES_STATUSCODE]);
+        result->inputArgumentResults = NULL;
+        result->inputArgumentResultsSize = 0;
+    }
+
+    /* Error during type-checking? */
     if(result->statusCode != UA_STATUSCODE_GOOD)
         return;
 
@@ -178,22 +204,20 @@ callWithMethodAndObject(UA_Server *server, UA_Session *session,
         getArgumentsVariableNode(server, method, UA_STRING("OutputArguments"));
 
     /* Allocate the output arguments array */
-    if(outputArguments) {
-        if(outputArguments->value.data.value.value.arrayLength > 0) {
-            result->outputArguments = (UA_Variant*)
-                UA_Array_new(outputArguments->value.data.value.value.arrayLength,
-                             &UA_TYPES[UA_TYPES_VARIANT]);
-            if(!result->outputArguments) {
-                result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
-                return;
-            }
-            result->outputArgumentsSize = outputArguments->value.data.value.value.arrayLength;
-        }
-
-        /* Release the output arguments node */
-        server->config.nodestore.releaseNode(server->config.nodestore.context,
-                                             (const UA_Node*)outputArguments);
+    size_t outputArgsSize = 0;
+    if(outputArguments)
+        outputArgsSize = outputArguments->value.data.value.value.arrayLength;
+    result->outputArguments = (UA_Variant*)
+        UA_Array_new(outputArgsSize, &UA_TYPES[UA_TYPES_VARIANT]);
+    if(!result->outputArguments) {
+        result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
+        return;
     }
+    result->outputArgumentsSize = outputArgsSize;
+
+    /* Release the output arguments node */
+    server->config.nodestore.releaseNode(server->config.nodestore.context,
+                                         (const UA_Node*)outputArguments);
 
     /* Call the method */
     result->statusCode = method->method(server, &session->sessionId, session->sessionHandle,
@@ -212,7 +236,7 @@ Operation_CallMethod(UA_Server *server, UA_Session *session, void *context,
         server->config.nodestore.getNode(server->config.nodestore.context,
                                          &request->methodId);
     if(!method) {
-        result->statusCode = UA_STATUSCODE_BADMETHODINVALID;
+        result->statusCode = UA_STATUSCODE_BADNODEIDUNKNOWN;
         return;
     }
 

+ 2 - 5
src/server/ua_services_nodemanagement.c

@@ -63,7 +63,6 @@ const UA_NodeId parentReferences[UA_PARENT_REFERENCES_COUNT] = {
     {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HASCOMPONENT}}
 };
 
-
 /* 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. */
@@ -137,10 +136,8 @@ checkParentReference(UA_Server *server, UA_Session *session, UA_NodeClass nodeCl
     }
 
     /* Test if the referencetype is hierarchical */
-    const UA_NodeId hierarchicalReference =
-        UA_NODEID_NUMERIC(0, UA_NS0ID_HIERARCHICALREFERENCES);
     if(!isNodeInTree(&server->config.nodestore, referenceTypeId,
-                     &hierarchicalReference, &subtypeId, 1)) {
+                     &hierarchicalReferences, &subtypeId, 1)) {
         UA_LOG_INFO_SESSION(server->config.logger, session,
                             "AddNodes: Reference type to the parent is not hierarchical");
         return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
@@ -643,7 +640,7 @@ static UA_StatusCode callConstructors(UA_Server *server, UA_Session *session,
 static UA_StatusCode
 addRef(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId,
        const UA_NodeId *referenceTypeId, const UA_NodeId *parentNodeId,
-       bool forward) {
+       UA_Boolean forward) {
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     UA_AddReferencesItem ref_item;
     UA_AddReferencesItem_init(&ref_item);

+ 2 - 6
src/server/ua_services_subscription.c

@@ -71,7 +71,7 @@ Service_CreateSubscription(UA_Server *server, UA_Session *session,
                            UA_CreateSubscriptionResponse *response) {
     /* Check limits for the number of subscriptions */
     if((server->config.maxSubscriptionsPerSession != 0) &&
-       (session->numPublishReq >= server->config.maxSubscriptionsPerSession)) {
+       (session->numSubscriptions >= server->config.maxSubscriptionsPerSession)) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADTOOMANYSUBSCRIPTIONS;
         return;
     }
@@ -510,12 +510,8 @@ Operation_SetMonitoringMode(UA_Server *server, UA_Session *session,
         /*  Setting the mode to DISABLED or SAMPLING causes all queued Notifications to be deleted */
         UA_Notification *notification, *notification_tmp;
         TAILQ_FOREACH_SAFE(notification, &mon->queue, listEntry, notification_tmp) {
-            TAILQ_REMOVE(&mon->queue, notification, listEntry);
-            TAILQ_REMOVE(&smc->sub->notificationQueue, notification, globalEntry);
-            --smc->sub->notificationQueueSize;
-            UA_Notification_delete(notification);
+            UA_Notification_delete(smc->sub, mon, notification);
         }
-        mon->queueSize = 0;
 
         /* Initialize lastSampledValue */
         UA_ByteString_deleteMembers(&mon->lastSampledValue);

+ 71 - 16
src/server/ua_services_view.c

@@ -19,6 +19,10 @@
 #include "ua_server_internal.h"
 #include "ua_services.h"
 
+/**********/
+/* Browse */
+/**********/
+
 /* Target node on top of the stack */
 static UA_StatusCode
 fillReferenceDescription(UA_Server *server, const UA_Node *curr,
@@ -68,6 +72,14 @@ relevantReference(UA_Server *server, UA_Boolean includeSubtypes,
     return isNodeInTree(&server->config.nodestore, testRef, rootRef, &hasSubType, 1);
 }
 
+static UA_Boolean
+matchClassMask(const UA_Node *node, UA_UInt32 nodeClassMask) {
+    if(nodeClassMask != UA_NODECLASS_UNSPECIFIED &&
+       (node->nodeClass & nodeClassMask) == 0)
+        return false;
+    return true;
+}
+
 /* Returns whether the node / continuationpoint is done */
 static UA_Boolean
 browseReferences(UA_Server *server, const UA_Node *node,
@@ -133,7 +145,7 @@ browseReferences(UA_Server *server, const UA_Node *node,
                 continue;
 
             /* Test if the node class matches */
-            if(descr->nodeClassMask != 0 && (target->nodeClass & descr->nodeClassMask) == 0) {
+            if(!matchClassMask(target, descr->nodeClassMask)) {
                 UA_Nodestore_release(server, target);
                 continue;
             }
@@ -445,7 +457,7 @@ walkBrowsePathElementReferenceTargets(UA_BrowsePathResult *result, size_t *targe
 }
 
 static void
-walkBrowsePathElement(UA_Server *server, UA_Session *session,
+walkBrowsePathElement(UA_Server *server, UA_Session *session, UA_UInt32 nodeClassMask,
                       UA_BrowsePathResult *result, size_t *targetsSize,
                       const UA_RelativePathElement *elem, UA_UInt32 elemDepth,
                       const UA_QualifiedName *targetName,
@@ -474,8 +486,14 @@ walkBrowsePathElement(UA_Server *server, UA_Session *session,
             continue;
         }
 
-        /* Test whether the current node has the target name required in the
-         * previous path element */
+        /* Test whether the node fits the class mask */
+        if(!matchClassMask(node, nodeClassMask)) {
+            UA_Nodestore_release(server, node);
+            continue;
+        }
+
+        /* Test whether the node has the target name required in the previous
+         * path element */
         if(targetName && (targetName->namespaceIndex != node->browseName.namespaceIndex ||
                           !UA_String_equal(&targetName->name, &node->browseName.name))) {
             UA_Nodestore_release(server, node);
@@ -507,7 +525,7 @@ walkBrowsePathElement(UA_Server *server, UA_Session *session,
 
 /* This assumes that result->targets has enough room for all currentCount elements */
 static void
-addBrowsePathTargets(UA_Server *server, UA_Session *session,
+addBrowsePathTargets(UA_Server *server, UA_Session *session, UA_UInt32 nodeClassMask,
                      UA_BrowsePathResult *result, const UA_QualifiedName *targetName,
                      UA_NodeId *current, size_t currentCount) {
     for(size_t i = 0; i < currentCount; i++) {
@@ -517,14 +535,18 @@ addBrowsePathTargets(UA_Server *server, UA_Session *session,
             continue;
         }
 
-        /* Test whether the current node has the target name required in the
+        /* Test whether the node fits the class mask */
+        UA_Boolean skip = !matchClassMask(node, nodeClassMask);
+
+        /* Test whether the node has the target name required in the
          * previous path element */
-        UA_Boolean valid = targetName->namespaceIndex == node->browseName.namespaceIndex &&
-            UA_String_equal(&targetName->name, &node->browseName.name);
+        if(targetName->namespaceIndex != node->browseName.namespaceIndex ||
+           !UA_String_equal(&targetName->name, &node->browseName.name))
+            skip = true;
 
         UA_Nodestore_release(server, node);
 
-        if(!valid) {
+        if(skip) {
             UA_NodeId_deleteMembers(&current[i]);
             continue;
         }
@@ -539,7 +561,7 @@ addBrowsePathTargets(UA_Server *server, UA_Session *session,
 
 static void
 walkBrowsePath(UA_Server *server, UA_Session *session, const UA_BrowsePath *path,
-               UA_BrowsePathResult *result, size_t targetsSize,
+               UA_UInt32 nodeClassMask, UA_BrowsePathResult *result, size_t targetsSize,
                UA_NodeId **current, size_t *currentSize, size_t *currentCount,
                UA_NodeId **next, size_t *nextSize, size_t *nextCount) {
     UA_assert(*currentCount == 1);
@@ -551,7 +573,7 @@ walkBrowsePath(UA_Server *server, UA_Session *session, const UA_BrowsePath *path
     /* Iterate over path elements */
     UA_assert(path->relativePath.elementsSize > 0);
     for(UA_UInt32 i = 0; i < path->relativePath.elementsSize; ++i) {
-        walkBrowsePathElement(server, session, result, &targetsSize,
+        walkBrowsePathElement(server, session, nodeClassMask, result, &targetsSize,
                               &path->relativePath.elements[i], i, targetName,
                               *current, *currentCount, next, nextSize, nextCount);
 
@@ -599,13 +621,13 @@ walkBrowsePath(UA_Server *server, UA_Session *session, const UA_BrowsePath *path
     }
 
     /* Move the elements of current to the targets */
-    addBrowsePathTargets(server, session, result, targetName, *current, *currentCount);
+    addBrowsePathTargets(server, session, nodeClassMask, result, targetName, *current, *currentCount);
     *currentCount = 0;
 }
 
 static void
 Operation_TranslateBrowsePathToNodeIds(UA_Server *server, UA_Session *session,
-                                       void *context, const UA_BrowsePath *path,
+                                       UA_UInt32 *nodeClassMask, const UA_BrowsePath *path,
                                        UA_BrowsePathResult *result) {
     if(path->relativePath.elementsSize <= 0) {
         result->statusCode = UA_STATUSCODE_BADNOTHINGTODO;
@@ -662,7 +684,7 @@ Operation_TranslateBrowsePathToNodeIds(UA_Server *server, UA_Session *session,
     currentCount = 1;
 
     /* Walk the path elements */
-    walkBrowsePath(server, session, path, result, targetsSize,
+    walkBrowsePath(server, session, path, *nodeClassMask, result, targetsSize,
                    &current, &currentSize, &currentCount,
                    &next, &nextSize, &nextCount);
 
@@ -690,7 +712,9 @@ UA_Server_translateBrowsePathToNodeIds(UA_Server *server,
                                        const UA_BrowsePath *browsePath) {
     UA_BrowsePathResult result;
     UA_BrowsePathResult_init(&result);
-    Operation_TranslateBrowsePathToNodeIds(server, &adminSession, NULL, browsePath, &result);
+    UA_UInt32 nodeClassMask = 0; /* All node classes */
+    Operation_TranslateBrowsePathToNodeIds(server, &adminSession, &nodeClassMask,
+                                           browsePath, &result);
     return result;
 }
 
@@ -707,13 +731,44 @@ Service_TranslateBrowsePathsToNodeIds(UA_Server *server, UA_Session *session,
         return;
     }
 
+    UA_UInt32 nodeClassMask = 0; /* All node classes */
     response->responseHeader.serviceResult =
         UA_Server_processServiceOperations(server, session,
                                            (UA_ServiceOperation)Operation_TranslateBrowsePathToNodeIds,
-                                           NULL, &request->browsePathsSize, &UA_TYPES[UA_TYPES_BROWSEPATH],
+                                           &nodeClassMask,
+                                           &request->browsePathsSize, &UA_TYPES[UA_TYPES_BROWSEPATH],
                                            &response->resultsSize, &UA_TYPES[UA_TYPES_BROWSEPATHRESULT]);
 }
 
+UA_BrowsePathResult
+UA_Server_browseSimplifiedBrowsePath(UA_Server *server, const UA_NodeId origin,
+                                     size_t browsePathSize, const UA_QualifiedName *browsePath) {
+    /* Construct the BrowsePath */
+    UA_BrowsePath bp;
+    UA_BrowsePath_init(&bp);
+    bp.startingNode = origin;
+    UA_STACKARRAY(UA_RelativePathElement, rpe, browsePathSize);
+    memset(rpe, 0, sizeof(UA_RelativePathElement) * browsePathSize);
+    for(size_t j = 0; j < browsePathSize; j++) {
+        rpe[j].referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HIERARCHICALREFERENCES);
+        rpe[j].includeSubtypes = true;
+        rpe[j].targetName = browsePath[j];
+    }
+    bp.relativePath.elements = rpe;
+    bp.relativePath.elementsSize = browsePathSize;
+
+    /* Browse */
+    UA_BrowsePathResult bpr;
+    UA_BrowsePathResult_init(&bpr);
+    UA_UInt32 nodeClassMask = UA_NODECLASS_OBJECT | UA_NODECLASS_VARIABLE;
+    Operation_TranslateBrowsePathToNodeIds(server, &adminSession, &nodeClassMask, &bp, &bpr);
+    return bpr;
+}
+
+/************/
+/* Register */
+/************/
+
 void Service_RegisterNodes(UA_Server *server, UA_Session *session,
                            const UA_RegisterNodesRequest *request,
                            UA_RegisterNodesResponse *response) {

+ 182 - 128
src/server/ua_subscription.c

@@ -2,7 +2,7 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. 
  *
- *    Copyright 2015-2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer)
+ *    Copyright 2015-2018 (c) Fraunhofer IOSB (Author: Julius Pfrommer)
  *    Copyright 2015 (c) Chris Iatrou
  *    Copyright 2015-2016 (c) Sten Grüner
  *    Copyright 2017-2018 (c) Thomas Stalder, Blue Time Concept SA
@@ -20,16 +20,50 @@
 
 #ifdef UA_ENABLE_SUBSCRIPTIONS /* conditional compilation */
 
-void UA_Notification_delete(UA_Notification *n) {
-    if(n->mon->monitoredItemType == UA_MONITOREDITEMTYPE_CHANGENOTIFY) {
+void
+UA_Notification_enqueue(UA_Server *server, UA_Subscription *sub,
+                        UA_MonitoredItem *mon, UA_Notification *n) {
+    /* Add to the MonitoredItem */
+    TAILQ_INSERT_TAIL(&mon->queue, n, listEntry);
+    ++mon->queueSize;
+
+    /* Add to the subscription */
+    TAILQ_INSERT_TAIL(&sub->notificationQueue, n, globalEntry);
+    ++sub->notificationQueueSize;
+    if(mon->monitoredItemType == UA_MONITOREDITEMTYPE_CHANGENOTIFY) {
+        ++sub->dataChangeNotifications;
+    } else if(mon->monitoredItemType == UA_MONITOREDITEMTYPE_EVENTNOTIFY) {
+        ++sub->eventNotifications;
+    } else if(mon->monitoredItemType == UA_MONITOREDITEMTYPE_STATUSNOTIFY) {
+        ++sub->statusChangeNotifications;
+    }
+
+    /* Ensure enough space is available in the MonitoredItem. Do this only after
+     * adding the new Notification. */
+    MonitoredItem_ensureQueueSpace(server, mon);
+}
+
+void
+UA_Notification_delete(UA_Subscription *sub, UA_MonitoredItem *mon,
+                       UA_Notification *n) {
+    if(mon->monitoredItemType == UA_MONITOREDITEMTYPE_CHANGENOTIFY) {
         UA_DataValue_deleteMembers(&n->data.value);
-    } else if (n->mon->monitoredItemType == UA_MONITOREDITEMTYPE_EVENTNOTIFY) {
-        UA_Array_delete(n->data.event.fields.eventFields, n->data.event.fields.eventFieldsSize,
-                        &UA_TYPES[UA_TYPES_VARIANT]);
+        --sub->dataChangeNotifications;
+    } else if(mon->monitoredItemType == UA_MONITOREDITEMTYPE_EVENTNOTIFY) {
+        UA_EventFieldList_deleteMembers(&n->data.event.fields);
         /* EventFilterResult currently isn't being used
-         * UA_EventFilterResult_delete(notification->data.event->result);
-         */
+         * UA_EventFilterResult_delete(notification->data.event->result); */
+        --sub->eventNotifications;
+    } else if(mon->monitoredItemType == UA_MONITOREDITEMTYPE_STATUSNOTIFY) {
+        --sub->statusChangeNotifications;
     }
+
+    TAILQ_REMOVE(&mon->queue, n, listEntry);
+    --mon->queueSize;
+
+    TAILQ_REMOVE(&sub->notificationQueue, n, globalEntry);
+    --sub->notificationQueueSize;
+
     UA_free(n);
 }
 
@@ -151,145 +185,164 @@ UA_Subscription_removeRetransmissionMessage(UA_Subscription *sub, UA_UInt32 sequ
     return UA_STATUSCODE_GOOD;
 }
 
-#ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
-/* EventChange: Iterate over the monitoredItems of the subscription, starting at mon, and
- *              move notifications into the response. */
-static void
-Events_moveNotificationsFromMonitoredItems(UA_Server *server, UA_Subscription *sub, UA_EventFieldList *efls,
-                                           size_t eflsSize) {
-    UA_StatusCode retval;
-    size_t pos = 0;
-    UA_Notification *notification, *notification_tmp;
-    TAILQ_FOREACH_SAFE(notification, &sub->notificationQueue, globalEntry, notification_tmp) {
-        if (pos >= eflsSize) {
-            return;
-        }
-        UA_MonitoredItem *mon = notification->mon;
+static UA_StatusCode
+prepareNotificationMessage(UA_Server *server, UA_Subscription *sub,
+                           UA_NotificationMessage *message, size_t notifications) {
+    UA_assert(notifications > 0);
 
-        /* Remove the notification from the queues */
-        TAILQ_REMOVE(&sub->notificationQueue, notification, globalEntry);
-        TAILQ_REMOVE(&mon->queue, notification, listEntry);
-
-        /* removing an overflowEvent should not reduce the queueSize */
-        UA_NodeId overflowId = UA_NODEID_NUMERIC(0, UA_NS0ID_SIMPLEOVERFLOWEVENTTYPE);
-        if (!(notification->data.event.fields.eventFieldsSize == 1
-                && notification->data.event.fields.eventFields->type == &UA_TYPES[UA_TYPES_NODEID]
-                && UA_NodeId_equal((UA_NodeId *)notification->data.event.fields.eventFields->data, &overflowId))) {
-            --mon->queueSize;
-            --sub->notificationQueueSize;
+    /* Allocate an ExtensionObject for events and data */
+    message->notificationData = (UA_ExtensionObject*)
+        UA_Array_new(2, &UA_TYPES[UA_TYPES_EXTENSIONOBJECT]);
+    if(!message->notificationData)
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+    message->notificationDataSize = 2;
+
+    /* Pre-allocate DataChangeNotifications */
+    size_t notificationDataIdx = 0;
+    UA_DataChangeNotification *dcn = NULL;
+    if(sub->dataChangeNotifications > 0) {
+        dcn = UA_DataChangeNotification_new();
+        if(!dcn) {
+            UA_NotificationMessage_deleteMembers(message);
+            return UA_STATUSCODE_BADOUTOFMEMORY;
         }
-
-        /* Move the content to the response */
-        UA_EventFieldList *efl = &efls[pos];
-        efl->clientHandle = mon->clientHandle;
-        efl->eventFieldsSize = notification->data.event.fields.eventFieldsSize;
-        retval = UA_Array_copy(notification->data.event.fields.eventFields,
-                               notification->data.event.fields.eventFieldsSize,
-                               (void **) &efl->eventFields, &UA_TYPES[UA_TYPES_VARIANT]);
-        if (retval != UA_STATUSCODE_GOOD) {
-            return;
+        message->notificationData->encoding = UA_EXTENSIONOBJECT_DECODED;
+        message->notificationData->content.decoded.data = dcn;
+        message->notificationData->content.decoded.type = &UA_TYPES[UA_TYPES_DATACHANGENOTIFICATION];
+
+        size_t dcnSize = sub->dataChangeNotifications;
+        if(dcnSize > notifications)
+            dcnSize = notifications;
+        dcn->monitoredItems = (UA_MonitoredItemNotification*)
+            UA_Array_new(dcnSize, &UA_TYPES[UA_TYPES_MONITOREDITEMNOTIFICATION]);
+        if(!dcn->monitoredItems) {
+            UA_NotificationMessage_deleteMembers(message);
+            return UA_STATUSCODE_BADOUTOFMEMORY;
         }
+        dcn->monitoredItemsSize = dcnSize;
+        notificationDataIdx++;
+    }
 
-        /* EventFilterResult currently isn't being used
-        UA_EventFilterResult_deleteMembers(&notification->data.event.result); */
-        UA_EventFieldList_deleteMembers(&notification->data.event.fields);
-        UA_free(notification);
+#ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
+    UA_EventNotificationList *enl = NULL;
+    UA_StatusChangeNotification *scn = NULL;
+    /* Pre-allocate either StatusChange or EventNotifications. Sending a
+     * (single) StatusChangeNotification has priority. */
+    if(sub->statusChangeNotifications > 0) {
+        scn = UA_StatusChangeNotification_new();
+        if(!scn) {
+            UA_NotificationMessage_deleteMembers(message);
+            return UA_STATUSCODE_BADOUTOFMEMORY;
+        }
+        message->notificationData[notificationDataIdx].encoding = UA_EXTENSIONOBJECT_DECODED;
+        message->notificationData[notificationDataIdx].content.decoded.data = scn;
+        message->notificationData[notificationDataIdx].content.decoded.type = &UA_TYPES[UA_TYPES_STATUSCHANGENOTIFICATION];
+        notificationDataIdx++;
+    } else if(sub->eventNotifications > 0) {
+        enl = UA_EventNotificationList_new();
+        if(!enl) {
+            UA_NotificationMessage_deleteMembers(message);
+            return UA_STATUSCODE_BADOUTOFMEMORY;
+        }
+        message->notificationData[notificationDataIdx].encoding = UA_EXTENSIONOBJECT_DECODED;
+        message->notificationData[notificationDataIdx].content.decoded.data = enl;
+        message->notificationData[notificationDataIdx].content.decoded.type = &UA_TYPES[UA_TYPES_EVENTNOTIFICATIONLIST];
+
+        size_t enlSize = sub->eventNotifications;
+        if(enlSize > notifications)
+            enlSize = notifications;
+        enl->events = (UA_EventFieldList*) UA_Array_new(enlSize, &UA_TYPES[UA_TYPES_EVENTFIELDLIST]);
+        if(!enl->events) {
+            UA_NotificationMessage_deleteMembers(message);
+            return UA_STATUSCODE_BADOUTOFMEMORY;
+        }
+        enl->eventsSize = enlSize;
+        notificationDataIdx++;
     }
-}
 #endif
 
-/* DataChange: Iterate over the monitoreditems of the subscription, starting at mon, and
- *             move notifications into the response. */
-static void
-DataChange_moveNotificationsFromMonitoredItems(UA_Subscription *sub, UA_MonitoredItemNotification *mins,
-                                               size_t minsSize) {
-    size_t pos = 0;
+    UA_assert(notificationDataIdx > 0);
+    message->notificationDataSize = notificationDataIdx;
+
+    /* <-- The point of no return --> */
+
+    size_t totalNotifications = 0; /* How many notifications were moved to the response overall? */
+    size_t dcnPos = 0; /* How many DataChangeNotifications were put into the list? */
+#ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
+    size_t enlPos = 0; /* How many EventNotifications were moved into the list */
+#endif
     UA_Notification *notification, *notification_tmp;
     TAILQ_FOREACH_SAFE(notification, &sub->notificationQueue, globalEntry, notification_tmp) {
-        if(pos >= minsSize)
-            return;
-
+        if(totalNotifications >= notifications)
+            break;
+        
         UA_MonitoredItem *mon = notification->mon;
 
-        /* Remove the notification from the queues */
-        TAILQ_REMOVE(&sub->notificationQueue, notification, globalEntry);
-        TAILQ_REMOVE(&mon->queue, notification, listEntry);
-        --mon->queueSize;
-        --sub->notificationQueueSize;
-
         /* Move the content to the response */
-        UA_MonitoredItemNotification *min = &mins[pos];
-        min->clientHandle = mon->clientHandle;
-        min->value = notification->data.value;
-        UA_free(notification);
-        ++pos;
-    }
-}
-
-static UA_StatusCode
-prepareNotificationMessage(UA_Server *server, UA_Subscription *sub, UA_NotificationMessage *message,
-                           size_t notifications) {
-    /* Array of ExtensionObject to hold different kinds of notifications
-     * (currently only DataChangeNotifications) */
-    message->notificationData = UA_ExtensionObject_new();
-    if(!message->notificationData)
-        return UA_STATUSCODE_BADOUTOFMEMORY;
-    message->notificationDataSize = 1;
-
-    UA_ExtensionObject *data = message->notificationData;
-    data->encoding = UA_EXTENSIONOBJECT_DECODED;
-    /* TODO: basing type of notificationtype off of first monitoredItem in subscription which isnt very good */
-    /* Allocate Notification */
-    if (LIST_FIRST(&sub->monitoredItems)->monitoredItemType == UA_MONITOREDITEMTYPE_CHANGENOTIFY) {
-        UA_DataChangeNotification *dcn = UA_DataChangeNotification_new();
-        if (!dcn) {
-            UA_NotificationMessage_deleteMembers(message);
-            return UA_STATUSCODE_BADOUTOFMEMORY;
+        if(mon->monitoredItemType == UA_MONITOREDITEMTYPE_CHANGENOTIFY) {
+            UA_assert(dcn != NULL); /* Have at least one change notification */
+            /* Move the content to the response */
+            UA_MonitoredItemNotification *min = &dcn->monitoredItems[dcnPos];
+            min->clientHandle = mon->clientHandle;
+            min->value = notification->data.value;
+            UA_DataValue_init(&notification->data.value); /* Reset after the value has been moved */
+            dcnPos++;
         }
-        data->content.decoded.data = dcn;
-        data->content.decoded.type = &UA_TYPES[UA_TYPES_DATACHANGENOTIFICATION];
-
-        /* Allocate array of notifications */
-        dcn->monitoredItems = (UA_MonitoredItemNotification *)
-                UA_Array_new(notifications,
-                             &UA_TYPES[UA_TYPES_MONITOREDITEMNOTIFICATION]);
-        if(!dcn->monitoredItems) {
-            UA_NotificationMessage_deleteMembers(message);
-            return UA_STATUSCODE_BADOUTOFMEMORY;
+#ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
+        else if(mon->monitoredItemType == UA_MONITOREDITEMTYPE_STATUSNOTIFY && scn) {
+            // TODO: Handling of StatusChangeNotifications
+            scn = NULL; /* At most one per PublishReponse */
+        } else if(mon->monitoredItemType == UA_MONITOREDITEMTYPE_EVENTNOTIFY && enl) {
+            UA_assert(enl != NULL); /* Have at least one event notification */
+
+            /* TODO: The following lead to crashes when we assumed notifications to be ready... */
+            /* /\* removing an overflowEvent should not reduce the queueSize *\/ */
+            /* UA_NodeId overflowId = UA_NODEID_NUMERIC(0, UA_NS0ID_SIMPLEOVERFLOWEVENTTYPE); */
+            /* if (!(notification->data.event.fields.eventFieldsSize == 1 */
+            /*       && notification->data.event.fields.eventFields->type == &UA_TYPES[UA_TYPES_NODEID] */
+            /*       && UA_NodeId_equal((UA_NodeId *)notification->data.event.fields.eventFields->data, &overflowId))) { */
+            /*     --mon->queueSize; */
+            /*     --sub->notificationQueueSize; */
+            /* } */
+
+            /* Move the content to the response */
+            UA_EventFieldList *efl = &enl->events[enlPos];
+            *efl = notification->data.event.fields;
+            UA_EventFieldList_init(&notification->data.event.fields);
+            efl->clientHandle = mon->clientHandle;
+            /* EventFilterResult currently isn't being used
+               UA_EventFilterResult_deleteMembers(&notification->data.event.result); */
+            enlPos++;
+        }
+#endif
+        else {
+            continue; /* Nothing to do */
         }
-        dcn->monitoredItemsSize = notifications;
 
-        /* Move notifications into the response .. the point of no return */
-        DataChange_moveNotificationsFromMonitoredItems(sub, dcn->monitoredItems, notifications);
+        /* Remove the notification from the queues */
+        UA_Notification_delete(sub, mon, notification);
+        totalNotifications++;
     }
-#ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
-    else if (LIST_FIRST(&sub->monitoredItems)->monitoredItemType == UA_MONITOREDITEMTYPE_EVENTNOTIFY) {
 
-        UA_EventNotificationList *enl = UA_EventNotificationList_new();
-        if (!enl) {
-            UA_NotificationMessage_deleteMembers(message);
-            return UA_STATUSCODE_BADOUTOFMEMORY;
+    /* Set sizes */
+    if(dcn) {
+        dcn->monitoredItemsSize = dcnPos;
+        if(dcnPos == 0) {
+            UA_free(dcn->monitoredItems);
+            dcn->monitoredItems = NULL;
         }
-        UA_EventNotificationList_init(enl);
-
-        data->content.decoded.data = enl;
-        data->content.decoded.type = &UA_TYPES[UA_TYPES_EVENTNOTIFICATIONLIST];
+    }
 
-        /* Allocate array of notifications */
-        enl->events = (UA_EventFieldList *) UA_Array_new(notifications, &UA_TYPES[UA_TYPES_EVENTFIELDLIST]);
-        if (!enl->events) {
-            UA_NotificationMessage_deleteMembers(message);
-            return UA_STATUSCODE_BADOUTOFMEMORY;
+#ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
+    if(enl) {
+        enl->eventsSize = enlPos;
+        if(enlPos == 0) {
+            UA_free(enl->events);
+            enl->events = NULL;
         }
-        enl->eventsSize = notifications;
-
-        /* Move the list into the response .. the point of no return */
-        Events_moveNotificationsFromMonitoredItems(server, sub, enl->events, notifications);
     }
 #endif
-    else {
-        return UA_STATUSCODE_BADNOTIMPLEMENTED;
-    }
+
     return UA_STATUSCODE_GOOD;
 }
 
@@ -333,7 +386,8 @@ UA_Subscription_publish(UA_Server *server, UA_Subscription *sub) {
         }
     }
 
-    if (sub->readyNotifications > sub->notificationQueueSize)
+    /* If there are several late publish responses... */
+    if(sub->readyNotifications > sub->notificationQueueSize)
         sub->readyNotifications = sub->notificationQueueSize;
 
     /* Count the available notifications */
@@ -360,7 +414,7 @@ UA_Subscription_publish(UA_Server *server, UA_Subscription *sub) {
                              sub->subscriptionId);
     }
 
-    /* We want to send a response. Is it possible? */
+    /* We want to send a response. Is the channel open? */
     UA_SecureChannel *channel = sub->session->header.channel;
     if(!channel || !pre) {
         UA_LOG_DEBUG_SESSION(server->config.logger, sub->session,
@@ -417,7 +471,7 @@ UA_Subscription_publish(UA_Server *server, UA_Subscription *sub) {
      * no notifications (and this is a keepalive message). */
     message->sequenceNumber = UA_Subscription_nextSequenceNumber(sub->sequenceNumber);
 
-    if(notifications != 0) {
+    if(notifications > 0) {
         /* There are notifications. So we can't reuse the sequence number. */
         sub->sequenceNumber = message->sequenceNumber;
 

+ 19 - 6
src/server/ua_subscription.h

@@ -2,7 +2,7 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. 
  *
- *    Copyright 2015-2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer)
+ *    Copyright 2015-2018 (c) Fraunhofer IOSB (Author: Julius Pfrommer)
  *    Copyright 2015 (c) Chris Iatrou
  *    Copyright 2015-2016 (c) Sten Grüner
  *    Copyright 2015 (c) Oleksiy Vasylyev
@@ -41,7 +41,6 @@ typedef enum {
     UA_MONITOREDITEMTYPE_EVENTNOTIFY = 4
 } UA_MonitoredItemType;
 
-
 struct UA_MonitoredItem;
 typedef struct UA_MonitoredItem UA_MonitoredItem;
 
@@ -64,8 +63,14 @@ typedef struct UA_Notification {
     } data;
 } UA_Notification;
 
-/* Clean up the notification. Must be removed from the lists first. */
-void UA_Notification_delete(UA_Notification *n);
+/* Ensure enough space is available; Add notification to the linked lists;
+ * Increase the counters */
+void UA_Notification_enqueue(UA_Server *server, UA_Subscription *sub,
+                             UA_MonitoredItem *mon, UA_Notification *n);
+
+/* Delete the notification. Also removes it from the linked lists. */
+void UA_Notification_delete(UA_Subscription *sub, UA_MonitoredItem *mon,
+                            UA_Notification *n);
 
 typedef TAILQ_HEAD(NotificationQueue, UA_Notification) NotificationQueue;
 
@@ -165,8 +170,16 @@ struct UA_Subscription {
 
     /* Global list of notifications from the MonitoredItems */
     NotificationQueue notificationQueue;
-    UA_UInt32 notificationQueueSize;
-    UA_UInt32 readyNotifications; /* Notifications to be sent out now (already late) */
+    UA_UInt32 notificationQueueSize; /* Total queue size */
+    UA_UInt32 dataChangeNotifications;
+    UA_UInt32 eventNotifications;
+    UA_UInt32 statusChangeNotifications;
+
+    /* Notifications to be sent out now (already late). In a regular publish
+     * callback, all queued notifications are sent out. In a late publish
+     * response, only the notifications left from the last regular publish
+     * callback are sent. */
+    UA_UInt32 readyNotifications;
 
     /* Retransmission Queue */
     ListOfNotificationMessages retransmissionQueue;

+ 47 - 71
src/server/ua_subscription_datachange.c

@@ -65,13 +65,9 @@ void UA_MonitoredItem_delete(UA_Server *server, UA_MonitoredItem *monitoredItem)
         UA_Notification *notification, *notification_tmp;
         TAILQ_FOREACH_SAFE(notification, &monitoredItem->queue,
                            listEntry, notification_tmp) {
-            /* Remove the item from the queues */
-            TAILQ_REMOVE(&monitoredItem->queue, notification, listEntry);
-            TAILQ_REMOVE(&sub->notificationQueue, notification, globalEntry);
-            --sub->notificationQueueSize;
-            UA_Notification_delete(notification);
+            /* Remove the item from the queues and free the memory */
+            UA_Notification_delete(sub, monitoredItem, notification);
         }
-        monitoredItem->queueSize = 0;
     }
 
 #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
@@ -128,67 +124,53 @@ MonitoredItem_ensureQueueSpace(UA_Server *server, UA_MonitoredItem *mon) {
         TAILQ_REMOVE(&sub->notificationQueue, after_del, globalEntry);
         TAILQ_INSERT_AFTER(&sub->notificationQueue, del, after_del, globalEntry);
 
-        /* Remove the notification from the queues */
-        TAILQ_REMOVE(&mon->queue, del, listEntry);
-        TAILQ_REMOVE(&sub->notificationQueue, del, globalEntry);
-
 #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
-        /* TODO: provide additional protection for overflowEvents according to specification */
-        /* removing an overflowEvent should not reduce the queueSize */
-        UA_NodeId overflowId = UA_NODEID_NUMERIC(0, UA_NS0ID_SIMPLEOVERFLOWEVENTTYPE);
-        if(!(del->data.event.fields.eventFieldsSize == 1 &&
-             del->data.event.fields.eventFields->type == &UA_TYPES[UA_TYPES_NODEID] &&
-             UA_NodeId_equal((UA_NodeId *)del->data.event.fields.eventFields->data, &overflowId))) {
-            --mon->queueSize;
-            --sub->notificationQueueSize;
-        }
-#else
-        --mon->queueSize;
-        --sub->notificationQueueSize;
-#endif /* UA_ENABLE_SUBSCRIPTIONS_EVENTS */
-
         /* Create an overflow notification */
-#ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
-        if(mon->monitoredItemType == UA_MONITOREDITEMTYPE_EVENTNOTIFY) {
-            /* EventFilterResult currently isn't being used
-            UA_EventFilterResult_deleteMembers(&del->data.event->result); */
-            UA_EventFieldList_deleteMembers(&del->data.event.fields);
-
-            /* cause an overflowEvent */
-            /* an overflowEvent does not care about event filters and as such
-             * will not be "triggered" correctly. Instead, a notification will
-             * be inserted into the queue which includes only the nodeId of the
-             * overflowEventType. It is up to the client to check for possible
-             * overflows. */
-            UA_Notification *overflowNotification = (UA_Notification *) UA_malloc(sizeof(UA_Notification));
-            if(!overflowNotification)
-                return UA_STATUSCODE_BADOUTOFMEMORY;
-
-            UA_EventFieldList_init(&overflowNotification->data.event.fields);
-            overflowNotification->data.event.fields.eventFields = UA_Variant_new();
-            if(!overflowNotification->data.event.fields.eventFields) {
-                UA_EventFieldList_deleteMembers(&overflowNotification->data.event.fields);
-                UA_free(overflowNotification);
-                return UA_STATUSCODE_BADOUTOFMEMORY;
-            }
-
-            UA_Variant_init(overflowNotification->data.event.fields.eventFields);
-            overflowNotification->data.event.fields.eventFieldsSize = 1;
-            UA_Variant_setScalarCopy(overflowNotification->data.event.fields.eventFields,
-                                     &overflowId, &UA_TYPES[UA_TYPES_NODEID]);
-            overflowNotification->mon = mon;
-            if(mon->discardOldest) {
-                TAILQ_INSERT_HEAD(&mon->queue, overflowNotification, listEntry);
-                TAILQ_INSERT_HEAD(&mon->subscription->notificationQueue, overflowNotification, globalEntry);
-            } else {
-                TAILQ_INSERT_TAIL(&mon->queue, overflowNotification, listEntry);
-                TAILQ_INSERT_TAIL(&mon->subscription->notificationQueue, overflowNotification, globalEntry);
-            }
-        }
+        /* if(mon->monitoredItemType == UA_MONITOREDITEMTYPE_EVENTNOTIFY) { */
+        /*     /\* EventFilterResult currently isn't being used */
+        /*     UA_EventFilterResult_deleteMembers(&del->data.event->result); *\/ */
+        /*     UA_EventFieldList_deleteMembers(&del->data.event.fields); */
+
+        /*     /\* cause an overflowEvent *\/ */
+        /*     /\* an overflowEvent does not care about event filters and as such */
+        /*      * will not be "triggered" correctly. Instead, a notification will */
+        /*      * be inserted into the queue which includes only the nodeId of the */
+        /*      * overflowEventType. It is up to the client to check for possible */
+        /*      * overflows. *\/ */
+        /*     UA_Notification *overflowNotification = (UA_Notification *) UA_malloc(sizeof(UA_Notification)); */
+        /*     if(!overflowNotification) */
+        /*         return UA_STATUSCODE_BADOUTOFMEMORY; */
+
+        /*     UA_EventFieldList_init(&overflowNotification->data.event.fields); */
+        /*     overflowNotification->data.event.fields.eventFields = UA_Variant_new(); */
+        /*     if(!overflowNotification->data.event.fields.eventFields) { */
+        /*         UA_EventFieldList_deleteMembers(&overflowNotification->data.event.fields); */
+        /*         UA_free(overflowNotification); */
+        /*         return UA_STATUSCODE_BADOUTOFMEMORY; */
+        /*     } */
+
+        /*     UA_Variant_init(overflowNotification->data.event.fields.eventFields); */
+        /*     overflowNotification->data.event.fields.eventFieldsSize = 1; */
+        /*     UA_NodeId overflowId = UA_NODEID_NUMERIC(0, UA_NS0ID_SIMPLEOVERFLOWEVENTTYPE); */
+        /*     UA_Variant_setScalarCopy(overflowNotification->data.event.fields.eventFields, */
+        /*                              &overflowId, &UA_TYPES[UA_TYPES_NODEID]); */
+        /*     overflowNotification->mon = mon; */
+        /*     if(mon->discardOldest) { */
+        /*         TAILQ_INSERT_HEAD(&mon->queue, overflowNotification, listEntry); */
+        /*         TAILQ_INSERT_HEAD(&mon->subscription->notificationQueue, overflowNotification, globalEntry); */
+        /*     } else { */
+        /*         TAILQ_INSERT_TAIL(&mon->queue, overflowNotification, listEntry); */
+        /*         TAILQ_INSERT_TAIL(&mon->subscription->notificationQueue, overflowNotification, globalEntry); */
+        /*     } */
+        /*     ++mon->queueSize; */
+        /*     ++sub->notificationQueueSize; */
+        /*     ++sub->eventNotifications; */
+        /* } */
 #endif /* UA_ENABLE_SUBSCRIPTIONS_EVENTS */
 
-        /* Free the notification */
-        UA_Notification_delete(del);
+        /* Delete the notification. This also removes the notification from the
+         * linked lists. */
+        UA_Notification_delete(sub, mon, del);
     }
 
     if(mon->monitoredItemType == UA_MONITOREDITEMTYPE_CHANGENOTIFY) {
@@ -432,14 +414,8 @@ sampleCallbackWithValue(UA_Server *server, UA_MonitoredItem *monitoredItem,
         newNotification->data.value = *value; /* Move the value to the notification */
         storedValue = true;
 
-        /* Add the notification to the end of local and global queue */
-        TAILQ_INSERT_TAIL(&monitoredItem->queue, newNotification, listEntry);
-        TAILQ_INSERT_TAIL(&sub->notificationQueue, newNotification, globalEntry);
-        ++monitoredItem->queueSize;
-        ++sub->notificationQueueSize;
-
-        /* Remove some notifications if the queue is beyond maximum capacity */
-        MonitoredItem_ensureQueueSpace(server, monitoredItem);
+        /* Enqueue the new notification */
+        UA_Notification_enqueue(server, sub, monitoredItem, newNotification);
     } else {
         /* Call the local callback if not attached to a subscription */
         UA_LocalMonitoredItem *localMon = (UA_LocalMonitoredItem*) monitoredItem;

+ 195 - 231
src/server/ua_subscription_events.c

@@ -30,7 +30,6 @@ UA_Event_generateEventId(UA_Server *server, UA_ByteString *generatedId) {
     if(!generatedId->data) {
         UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_USERLAND,
                        "Server unable to allocate memory for EventId data.");
-        UA_free(generatedId);
         return UA_STATUSCODE_BADOUTOFMEMORY;
     }
 
@@ -44,145 +43,76 @@ UA_Event_generateEventId(UA_Server *server, UA_ByteString *generatedId) {
     return UA_STATUSCODE_GOOD;
 }
 
-static UA_StatusCode
-findAllSubtypesNodeIteratorCallback(UA_NodeId parentId, UA_Boolean isInverse,
-                                    UA_NodeId referenceTypeId, void *handle) {
-    /* only subtypes of hasSubtype */
-    UA_NodeId hasSubtypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE);
-    if(isInverse || !UA_NodeId_equal(&referenceTypeId, &hasSubtypeId))
-        return UA_STATUSCODE_GOOD;
-
-    Events_nodeListElement *entry = (Events_nodeListElement *) UA_malloc(sizeof(Events_nodeListElement));
-    if(!entry)
-        return UA_STATUSCODE_BADOUTOFMEMORY;
-
-    UA_StatusCode retval = UA_NodeId_copy(&parentId, &entry->nodeId);
-    if(retval != UA_STATUSCODE_GOOD) {
-        UA_free(entry);
-        return retval;
-    }
-
-    LIST_INSERT_HEAD(&((struct getNodesHandle *) handle)->nodes, entry, listEntry);
-
-    /* recursion */
-    UA_Server_forEachChildNodeCall(((struct getNodesHandle *) handle)->server,
-                                   parentId, findAllSubtypesNodeIteratorCallback, handle);
-    return UA_STATUSCODE_GOOD;
-}
-
-/* Searches for an attribute of an event with the name 'name' and the depth from the event relativePathSize.
- * Returns the browsePathResult of searching for that node */
-static void
-UA_Event_findVariableNode(UA_Server *server, UA_QualifiedName *name, size_t relativePathSize,
-                          const UA_NodeId *event, UA_BrowsePathResult *out) {
-    /* get a list with all subtypes of aggregates */
-    struct getNodesHandle handle;
-    handle.server = server;
-    LIST_INIT(&handle.nodes);
-    UA_StatusCode retval =
-        UA_Server_forEachChildNodeCall(server, UA_NODEID_NUMERIC(0, UA_NS0ID_AGGREGATES),
-                                       findAllSubtypesNodeIteratorCallback, &handle);
-    if(retval != UA_STATUSCODE_GOOD)
-        out->statusCode = retval;
-
-    /* check if you can find the node with any of the subtypes of aggregates */
-    UA_Boolean nodeFound = UA_FALSE;
-    Events_nodeListElement *iter, *tmp_iter;
-    LIST_FOREACH_SAFE(iter, &handle.nodes, listEntry, tmp_iter) {
-        if (!nodeFound) {
-            UA_RelativePathElement rpe;
-            UA_RelativePathElement_init(&rpe);
-            rpe.referenceTypeId = iter->nodeId;
-            rpe.isInverse = false;
-            rpe.includeSubtypes = false;
-            rpe.targetName = *name;
-            /* TODO: test larger browsepath perhaps put browsepath in a loop */
-            UA_BrowsePath bp;
-            UA_BrowsePath_init(&bp);
-            bp.relativePath.elementsSize = relativePathSize;
-            bp.startingNode = *event;
-            bp.relativePath.elements = &rpe;
-
-            *out = UA_Server_translateBrowsePathToNodeIds(server, &bp);
-            if(out->statusCode == UA_STATUSCODE_GOOD)
-                nodeFound = UA_TRUE;
-        }
-
-        LIST_REMOVE(iter, listEntry);
-        UA_NodeId_deleteMembers(&iter->nodeId);
-        UA_free(iter);
-    }
-}
-
-UA_StatusCode UA_EXPORT
+UA_StatusCode
 UA_Server_createEvent(UA_Server *server, const UA_NodeId eventType, UA_NodeId *outNodeId) {
-    if (!outNodeId) {
+    if(!outNodeId) {
         UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_USERLAND, "outNodeId cannot be NULL!");
         return UA_STATUSCODE_BADINVALIDARGUMENT;
     }
 
-    /* make sure the eventType is a subtype of BaseEventType */
+    /* Make sure the eventType is a subtype of BaseEventType */
     UA_NodeId hasSubtypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE);
     UA_NodeId baseEventTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEEVENTTYPE);
-    if (!isNodeInTree(&server->config.nodestore, &eventType, &baseEventTypeId, &hasSubtypeId, 1)) {
-        UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_USERLAND, "Event type must be a subtype of BaseEventType!");
+    if(!isNodeInTree(&server->config.nodestore, &eventType, &baseEventTypeId, &hasSubtypeId, 1)) {
+        UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_USERLAND,
+                     "Event type must be a subtype of BaseEventType!");
         return UA_STATUSCODE_BADINVALIDARGUMENT;
     }
 
-    UA_ObjectAttributes oAttr = UA_ObjectAttributes_default;
-    oAttr.displayName.locale = UA_STRING_NULL;
-    oAttr.displayName.text = UA_STRING_NULL;
-    oAttr.description.locale = UA_STRING_NULL;
-    oAttr.description.text = UA_STRING_NULL;
-
+    /* Create an ObjectNode which represents the event */
     UA_QualifiedName name;
     UA_QualifiedName_init(&name);
-
-    /* create an ObjectNode which represents the event */
+    UA_NodeId newNodeId = UA_NODEID_NULL;
+    UA_ObjectAttributes oAttr = UA_ObjectAttributes_default;
     UA_StatusCode retval =
         UA_Server_addObjectNode(server,
-                                UA_NODEID_NULL, /* the user may not have control over the nodeId */
-                                UA_NODEID_NULL, /* an event does not have a parent */
-                                UA_NODEID_NULL, /* an event does not have any references */
+                                UA_NODEID_NULL, /* Set a random unused NodeId */
+                                UA_NODEID_NULL, /* No parent */
+                                UA_NODEID_NULL, /* No parent reference */
                                 name,           /* an event does not have a name */
                                 eventType,      /* the type of the event */
                                 oAttr,          /* default attributes are fine */
                                 NULL,           /* no node context */
-                                outNodeId);
+                                &newNodeId);
 
-    if (retval != UA_STATUSCODE_GOOD) {
+    if(retval != UA_STATUSCODE_GOOD) {
         UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_USERLAND,
                      "Adding event failed. StatusCode %s", UA_StatusCode_name(retval));
         return retval;
     }
 
-    /* find the eventType variableNode */
+    /* Find the eventType variable */
     name = UA_QUALIFIEDNAME(0, "EventType");
-    UA_BrowsePathResult bpr;
-    UA_BrowsePathResult_init(&bpr);
-    UA_Event_findVariableNode(server, &name, 1, outNodeId, &bpr);
+    UA_BrowsePathResult bpr = UA_Server_browseSimplifiedBrowsePath(server, newNodeId, 1, &name);
     if(bpr.statusCode != UA_STATUSCODE_GOOD || bpr.targetsSize < 1) {
+        retval = bpr.statusCode;
         UA_BrowsePathResult_deleteMembers(&bpr);
-        return bpr.statusCode;
+        UA_Server_deleteNode(server, newNodeId, true);
+        UA_NodeId_deleteMembers(&newNodeId);
+        return retval;
     }
+
+    /* Set the EventType */
     UA_Variant value;
     UA_Variant_init(&value);
-    UA_Variant_setScalarCopy(&value, &eventType, &UA_TYPES[UA_TYPES_NODEID]);
-    UA_Server_writeValue(server, bpr.targets[0].targetId.nodeId, value);
-    UA_Variant_deleteMembers(&value);
+    UA_Variant_setScalar(&value, (void*)(uintptr_t)&eventType, &UA_TYPES[UA_TYPES_NODEID]);
+    retval = UA_Server_writeValue(server, bpr.targets[0].targetId.nodeId, value);
     UA_BrowsePathResult_deleteMembers(&bpr);
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_Server_deleteNode(server, newNodeId, true);
+        UA_NodeId_deleteMembers(&newNodeId);
+        return retval;
+    }
 
-    /* the object is not put in any queues until it is triggered */
-    return retval;
+    *outNodeId = newNodeId;
+    return UA_STATUSCODE_GOOD;
 }
 
 static UA_Boolean
 isValidEvent(UA_Server *server, const UA_NodeId *validEventParent, const UA_NodeId *eventId) {
     /* find the eventType variableNode */
-    UA_BrowsePathResult bpr;
-    UA_BrowsePathResult_init(&bpr);
     UA_QualifiedName findName = UA_QUALIFIEDNAME(0, "EventType");
-    UA_Event_findVariableNode(server, &findName, 1, eventId, &bpr);
+    UA_BrowsePathResult bpr = UA_Server_browseSimplifiedBrowsePath(server, *eventId, 1, &findName);
     if(bpr.statusCode != UA_STATUSCODE_GOOD || bpr.targetsSize < 1) {
         UA_BrowsePathResult_deleteMembers(&bpr);
         return UA_FALSE;
@@ -194,26 +124,69 @@ isValidEvent(UA_Server *server, const UA_NodeId *validEventParent, const UA_Node
     return tmp;
 }
 
-
+/* static UA_StatusCode */
+/* whereClausesApply(UA_Server *server, const UA_ContentFilter whereClause, */
+/*                   UA_EventFieldList *efl, UA_Boolean *result) { */
+/*     /\* if the where clauses aren't specified leave everything as is *\/ */
+/*     if(whereClause.elementsSize == 0) { */
+/*         *result = UA_TRUE; */
+/*         return UA_STATUSCODE_GOOD; */
+/*     } */
+
+/*     /\* where clauses were specified *\/ */
+/*     UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_USERLAND, */
+/*                    "Where clauses are not supported by the server."); */
+/*     *result = UA_TRUE; */
+/*     return UA_STATUSCODE_BADNOTSUPPORTED; */
+/* } */
+
+/* Part 4: 7.4.4.5 SimpleAttributeOperand
+ * The clause can point to any attribute of nodes. Either a child of the event
+ * node and also the event type. */
 static UA_StatusCode
-whereClausesApply(UA_Server *server, const UA_ContentFilter whereClause,
-                  UA_EventFieldList *efl, UA_Boolean *result) {
-    /* if the where clauses aren't specified leave everything as is */
-    if(whereClause.elementsSize == 0) {
-        *result = UA_TRUE;
-        return UA_STATUSCODE_GOOD;
+resolveSimpleAttributeOperand(UA_Server *server, UA_Session *session, const UA_NodeId *origin,
+                              const UA_SimpleAttributeOperand *sao, UA_Variant *value) {
+    /* Prepare the ReadValueId */
+    UA_ReadValueId rvi;
+    UA_ReadValueId_init(&rvi);
+    rvi.indexRange = sao->indexRange;
+    rvi.attributeId = sao->attributeId;
+
+    /* If this list (browsePath) is empty the Node is the instance of the
+     * TypeDefinition. */
+    if(sao->browsePathSize == 0) {
+        rvi.nodeId = sao->typeDefinitionId;
+        UA_DataValue v = UA_Server_readWithSession(server, session, &rvi, UA_TIMESTAMPSTORETURN_NEITHER);
+        if(v.status == UA_STATUSCODE_GOOD && v.hasValue)
+            *value = v.value;
+        return v.status;
     }
 
-    /* where clauses were specified */
-    UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_USERLAND,
-                   "Where clauses are not supported by the server.");
-    *result = UA_TRUE;
-    return UA_STATUSCODE_BADNOTSUPPORTED;
+    /* Resolve the browse path */
+    UA_BrowsePathResult bpr =
+        UA_Server_browseSimplifiedBrowsePath(server, *origin, sao->browsePathSize, sao->browsePath);
+    if(bpr.targetsSize == 0 && bpr.statusCode == UA_STATUSCODE_GOOD)
+        bpr.statusCode = UA_STATUSCODE_BADNOTFOUND;
+    if(bpr.statusCode != UA_STATUSCODE_GOOD) {
+        UA_StatusCode retval = bpr.statusCode;
+        UA_BrowsePathResult_deleteMembers(&bpr);
+        return retval;
+    }
+
+    /* Read the first matching element. Move the value to the output. */
+    rvi.nodeId = bpr.targets[0].targetId.nodeId;
+    UA_DataValue v = UA_Server_readWithSession(server, session, &rvi, UA_TIMESTAMPSTORETURN_NEITHER);
+    if(v.status == UA_STATUSCODE_GOOD && v.hasValue)
+        *value = v.value;
+
+    UA_BrowsePathResult_deleteMembers(&bpr);
+    return v.status;
 }
 
 /* filters the given event with the given filter and writes the results into a notification */
 static UA_StatusCode
-UA_Server_filterEvent(UA_Server *server, const UA_NodeId *eventNode, UA_EventFilter *filter,
+UA_Server_filterEvent(UA_Server *server, UA_Session *session,
+                      const UA_NodeId *eventNode, UA_EventFilter *filter,
                       UA_EventNotification *notification) {
     if (filter->selectClausesSize == 0)
         return UA_STATUSCODE_BADEVENTFILTERINVALID;
@@ -234,8 +207,8 @@ UA_Server_filterEvent(UA_Server *server, const UA_NodeId *eventNode, UA_EventFil
     }
     /* EventFilterResult currently isn't being used
     notification->result.selectClauseResultsSize = filter->selectClausesSize;
-    notification->result.selectClauseResults = (UA_StatusCode *) UA_Array_new(filter->selectClausesSize,
-                                                                               &UA_TYPES[UA_TYPES_VARIANT]);
+    notification->result.selectClauseResults = (UA_StatusCode *)
+        UA_Array_new(filter->selectClausesSize, &UA_TYPES[UA_TYPES_STATUSCODE]);
     if (!notification->result->selectClauseResults) {
         UA_EventFieldList_deleteMembers(&notification->fields);
         UA_EventFilterResult_deleteMembers(&notification->result);
@@ -256,117 +229,99 @@ UA_Server_filterEvent(UA_Server *server, const UA_NodeId *eventNode, UA_EventFil
             continue;
         }
 
-        /* type is correct */
-        /* find the variable node with the data being looked for */
-        UA_BrowsePathResult bpr;
-        UA_BrowsePathResult_init(&bpr);
-        UA_Event_findVariableNode(server, filter->selectClauses[i].browsePath,
-                                  filter->selectClauses[i].browsePathSize, eventNode, &bpr);
-        if (bpr.statusCode != UA_STATUSCODE_GOOD || bpr.targetsSize < 1) {
-            UA_Variant_init(&notification->fields.eventFields[i]);
-            continue;
-        }
-
-        /* copy the value */
-        UA_Boolean whereClauseResult = UA_TRUE;
-        UA_Boolean whereClausesUsed = UA_FALSE;     /* placeholder until whereClauses are implemented */
-        UA_StatusCode retval = whereClausesApply(server, filter->whereClause,
-                                                 &notification->fields, &whereClauseResult);
-        if (retval == UA_STATUSCODE_BADNOTSUPPORTED)
-            whereClausesUsed = UA_TRUE;
-
-        if(whereClauseResult) {
-            retval = UA_Server_readValue(server, bpr.targets[0].targetId.nodeId,
-                                         &notification->fields.eventFields[i]);
-            if(retval != UA_STATUSCODE_GOOD)
-                UA_Variant_init(&notification->fields.eventFields[i]);
-
-            if(whereClausesUsed)
-                return UA_STATUSCODE_BADNOTSUPPORTED;
-        } else {
-            UA_Variant_init(&notification->fields.eventFields[i]);
-            /* TODO: better statuscode for failing at where clauses */
-            /* EventFilterResult currently isn't being used
-            notification->result.selectClauseResults[i] = UA_STATUSCODE_BADDATAUNAVAILABLE; */
-        }
-        UA_BrowsePathResult_deleteMembers(&bpr);
+        /* TODO: Put the result into the selectClausResults */
+        resolveSimpleAttributeOperand(server, session, eventNode,
+                                      &filter->selectClauses[i],
+                                      &notification->fields.eventFields[i]);
     }
+    /* UA_Boolean whereClauseResult = UA_TRUE; */
+    /* return whereClausesApply(server, filter->whereClause, &notification->fields, &whereClauseResult); */
     return UA_STATUSCODE_GOOD;
 }
 
 static UA_StatusCode
-eventSetConstants(UA_Server *server, const UA_NodeId *event,
-                  const UA_NodeId *origin, UA_ByteString *outEventId) {
-    UA_BrowsePathResult bpr;
-    UA_BrowsePathResult_init(&bpr);
-    /* set the source */
+eventSetStandardFields(UA_Server *server, const UA_NodeId *event,
+                       const UA_NodeId *origin, UA_ByteString *outEventId) {
+    /* Set the SourceNode */
+    UA_StatusCode retval;
     UA_QualifiedName name = UA_QUALIFIEDNAME(0, "SourceNode");
-    UA_Event_findVariableNode(server, &name, 1, event, &bpr);
-    if (bpr.statusCode != UA_STATUSCODE_GOOD || bpr.targetsSize < 1) {
-        UA_StatusCode tmp = bpr.statusCode;
+    UA_BrowsePathResult bpr = UA_Server_browseSimplifiedBrowsePath(server, *event, 1, &name);
+    if(bpr.statusCode != UA_STATUSCODE_GOOD || bpr.targetsSize < 1) {
+        retval = bpr.statusCode;
         UA_BrowsePathResult_deleteMembers(&bpr);
-        return tmp;
+        return retval;
     }
     UA_Variant value;
     UA_Variant_init(&value);
     UA_Variant_setScalarCopy(&value, origin, &UA_TYPES[UA_TYPES_NODEID]);
-    UA_Server_writeValue(server, bpr.targets[0].targetId.nodeId, value);
+    retval = UA_Server_writeValue(server, bpr.targets[0].targetId.nodeId, value);
     UA_Variant_deleteMembers(&value);
     UA_BrowsePathResult_deleteMembers(&bpr);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
 
-    /* set the receive time */
+    /* Set the ReceiveTime */
     name = UA_QUALIFIEDNAME(0, "ReceiveTime");
-    UA_Event_findVariableNode(server, &name, 1, event, &bpr);
-    if (bpr.statusCode != UA_STATUSCODE_GOOD || bpr.targetsSize < 1) {
-        UA_StatusCode tmp = bpr.statusCode;
+    bpr = UA_Server_browseSimplifiedBrowsePath(server, *event, 1, &name);
+    if(bpr.statusCode != UA_STATUSCODE_GOOD || bpr.targetsSize < 1) {
+        retval = bpr.statusCode;
         UA_BrowsePathResult_deleteMembers(&bpr);
-        return tmp;
+        return retval;
     }
     UA_DateTime time = UA_DateTime_now();
     UA_Variant_setScalar(&value, &time, &UA_TYPES[UA_TYPES_DATETIME]);
-    UA_Server_writeValue(server, bpr.targets[0].targetId.nodeId, value);
+    retval = UA_Server_writeValue(server, bpr.targets[0].targetId.nodeId, value);
     UA_BrowsePathResult_deleteMembers(&bpr);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
 
-    /* set the eventId attribute */
-    UA_ByteString eventId;
-    UA_ByteString_init(&eventId);
-    UA_StatusCode retval = UA_Event_generateEventId(server, &eventId);
-    if (retval != UA_STATUSCODE_GOOD) {
-        UA_ByteString_deleteMembers(&eventId);
+    /* Set the EventId */
+    UA_ByteString eventId = UA_BYTESTRING_NULL;
+    retval = UA_Event_generateEventId(server, &eventId);
+    if(retval != UA_STATUSCODE_GOOD)
         return retval;
-    }
-    if (outEventId) {
-        UA_ByteString_copy(&eventId, outEventId);
-    }
     name = UA_QUALIFIEDNAME(0, "EventId");
-    UA_Event_findVariableNode(server, &name, 1, event, &bpr);
-    if (bpr.statusCode != UA_STATUSCODE_GOOD || bpr.targetsSize < 1) {
-        UA_StatusCode tmp = bpr.statusCode;
+    bpr = UA_Server_browseSimplifiedBrowsePath(server, *event, 1, &name);
+    if(bpr.statusCode != UA_STATUSCODE_GOOD || bpr.targetsSize < 1) {
+        retval = bpr.statusCode;
+        UA_ByteString_deleteMembers(&eventId);
         UA_BrowsePathResult_deleteMembers(&bpr);
-        return tmp;
+        return retval;
     }
     UA_Variant_init(&value);
     UA_Variant_setScalar(&value, &eventId, &UA_TYPES[UA_TYPES_BYTESTRING]);
-    UA_Server_writeValue(server, bpr.targets[0].targetId.nodeId, value);
-    UA_ByteString_deleteMembers(&eventId);
+    retval = UA_Server_writeValue(server, bpr.targets[0].targetId.nodeId, value);
     UA_BrowsePathResult_deleteMembers(&bpr);
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_ByteString_deleteMembers(&eventId);
+        return retval;
+    }
+
+    /* Return the EventId */
+    if(outEventId)
+        *outEventId = eventId;
+    else
+        UA_ByteString_deleteMembers(&eventId);
 
     return UA_STATUSCODE_GOOD;
 }
 
-
-/* insert each node into the list (passed as handle) */
+/* Insert each node into the list (passed as handle) */
 static UA_StatusCode
 getParentsNodeIteratorCallback(UA_NodeId parentId, UA_Boolean isInverse,
-                               UA_NodeId referenceTypeId, void *handle) {
-    /* parents have an inverse reference */
+                               UA_NodeId referenceTypeId, struct getNodesHandle *handle) {
+    /* Parents have an inverse reference */
     if(!isInverse)
         return UA_STATUSCODE_GOOD;
 
+    /* Is this a hierarchical reference? */
+    if(!isNodeInTree(&handle->server->config.nodestore, &referenceTypeId,
+                     &hierarchicalReferences, &subtypeId, 1))
+        return UA_STATUSCODE_GOOD;
+
     Events_nodeListElement *entry = (Events_nodeListElement *) UA_malloc(sizeof(Events_nodeListElement));
-    if (!entry) {
+    if(!entry)
         return UA_STATUSCODE_BADOUTOFMEMORY;
-    }
 
     UA_StatusCode retval = UA_NodeId_copy(&parentId, &entry->nodeId);
     if(retval != UA_STATUSCODE_GOOD) {
@@ -374,15 +329,15 @@ getParentsNodeIteratorCallback(UA_NodeId parentId, UA_Boolean isInverse,
         return retval;
     }
 
-    LIST_INSERT_HEAD(&((struct getNodesHandle *) handle)->nodes, entry, listEntry);
+    LIST_INSERT_HEAD(&handle->nodes, entry, listEntry);
 
-    /* recursion */
-    UA_Server_forEachChildNodeCall(((struct getNodesHandle *) handle)->server,
-                                   parentId, getParentsNodeIteratorCallback, handle);
+    /* Recursion */
+    UA_Server_forEachChildNodeCall(handle->server, parentId, (UA_NodeIteratorCallback)getParentsNodeIteratorCallback, handle);
     return UA_STATUSCODE_GOOD;
 }
 
-/* filters an event according to the filter specified by mon and then adds it to mons notification queue */
+/* Filters an event according to the filter specified by mon and then adds it to
+ * mons notification queue */
 static UA_StatusCode
 UA_Event_addEventToMonitoredItem(UA_Server *server, const UA_NodeId *event,
                                  UA_MonitoredItem *mon) {
@@ -390,78 +345,87 @@ UA_Event_addEventToMonitoredItem(UA_Server *server, const UA_NodeId *event,
     if(!notification)
         return UA_STATUSCODE_BADOUTOFMEMORY;
 
-    /* apply the filter */
-    UA_StatusCode retval = UA_Server_filterEvent(server, event, &mon->filter.eventFilter,
+    /* Get the session */
+    UA_Subscription *sub = mon->subscription;
+    UA_Session *session = sub->session;
+
+    /* Apply the filter */
+    UA_StatusCode retval = UA_Server_filterEvent(server, session, event,
+                                                 &mon->filter.eventFilter,
                                                  &notification->data.event);
-    if (retval != UA_STATUSCODE_GOOD) {
+    if(retval != UA_STATUSCODE_GOOD) {
         UA_free(notification);
         return retval;
     }
-    notification->mon = mon;
 
-    /* add to the monitored item queue */
-    MonitoredItem_ensureQueueSpace(server, mon);
-    TAILQ_INSERT_TAIL(&mon->queue, notification, listEntry);
-    ++mon->queueSize;
-    /* add to the subscription queue */
-    TAILQ_INSERT_TAIL(&mon->subscription->notificationQueue, notification, globalEntry);
-    ++mon->subscription->notificationQueueSize;
+    /* Enqueue the notification */
+    notification->mon = mon;
+    UA_Notification_enqueue(server, mon->subscription, mon, notification);
     return UA_STATUSCODE_GOOD;
 }
 
-UA_StatusCode UA_EXPORT
+static const UA_NodeId objectsFolderId = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_OBJECTSFOLDER}};
+static const UA_NodeId parentReferences_events[2] =
+    {{0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_ORGANIZES}},
+     {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HASCOMPONENT}}};
+
+UA_StatusCode
 UA_Server_triggerEvent(UA_Server *server, const UA_NodeId eventNodeId, const UA_NodeId origin,
                        UA_ByteString *outEventId, const UA_Boolean deleteEventNode) {
-    /* make sure the origin is in the ObjectsFolder (TODO: or in the ViewsFolder) */
-    UA_NodeId objectsFolderId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
-    UA_NodeId references[2] = {
-            {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_ORGANIZES}},
-            {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HASCOMPONENT}}
-    };
-
-    if(!isNodeInTree(&server->config.nodestore, &origin, &objectsFolderId, references, 2)) {
+    /* Make sure the origin is in the ObjectsFolder (TODO: or in the ViewsFolder) */
+    if(!isNodeInTree(&server->config.nodestore, &origin, &objectsFolderId, parentReferences_events, 2)) {
         UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_USERLAND,
                      "Node for event must be in ObjectsFolder!");
         return UA_STATUSCODE_BADINVALIDARGUMENT;
     }
 
-    UA_StatusCode retval = eventSetConstants(server, &eventNodeId, &origin, outEventId);
-    if(retval != UA_STATUSCODE_GOOD)
+    UA_StatusCode retval = eventSetStandardFields(server, &eventNodeId, &origin, outEventId);
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_SERVER,
+                       "Events: Could not set the standard event fields with StatusCode %s",
+                       UA_StatusCode_name(retval));
         return retval;
+    }
 
-    /* get an array with all parents */
+    /* Get an array with all parents. The first call to
+     * getParentsNodeIteratorCallback adds the emitting node itself. */
     struct getNodesHandle parentHandle;
     parentHandle.server = server;
     LIST_INIT(&parentHandle.nodes);
-    retval = getParentsNodeIteratorCallback(origin, UA_TRUE, UA_NODEID_NULL, &parentHandle);
-    if(retval != UA_STATUSCODE_GOOD)
+    retval = getParentsNodeIteratorCallback(origin, UA_TRUE, parentReferences_events[1], &parentHandle);
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_SERVER,
+                       "Events: Could not create the list of nodes listening on the event with StatusCode %s",
+                       UA_StatusCode_name(retval));
         return retval;
+    }
 
-    /* add the event to each node's monitored items */
+    /* Add the event to each node's monitored items */
     Events_nodeListElement *parentIter, *tmp_parentIter;
     LIST_FOREACH_SAFE(parentIter, &parentHandle.nodes, listEntry, tmp_parentIter) {
         const UA_ObjectNode *node = (const UA_ObjectNode *) UA_Nodestore_get(server, &parentIter->nodeId);
-        /* SLIST_FOREACH */
-        for (UA_MonitoredItem *monIter = node->monitoredItemQueue; monIter != NULL; monIter = monIter->next) {
-            retval = UA_Event_addEventToMonitoredItem(server, &eventNodeId, monIter);
-            if (retval != UA_STATUSCODE_GOOD) {
-                UA_Nodestore_release(server, (const UA_Node *) node);
-                return retval;
+        if(node->nodeClass == UA_NODECLASS_OBJECT) {
+            for(UA_MonitoredItem *monIter = node->monitoredItemQueue; monIter != NULL; monIter = monIter->next) {
+                retval = UA_Event_addEventToMonitoredItem(server, &eventNodeId, monIter);
+                if(retval != UA_STATUSCODE_GOOD) {
+                    UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_SERVER,
+                                   "Events: Could not add the event to a listening node with StatusCode %s",
+                                   UA_StatusCode_name(retval));
+                }
             }
         }
         UA_Nodestore_release(server, (const UA_Node *) node);
 
         LIST_REMOVE(parentIter, listEntry);
-        UA_NodeId_delete(&parentIter->nodeId);
+        UA_NodeId_deleteMembers(&parentIter->nodeId);
         UA_free(parentIter);
     }
 
-    /* delete the node representation of the event */
-    if (deleteEventNode) {
+    /* Delete the node representation of the event */
+    if(deleteEventNode) {
         retval = UA_Server_deleteNode(server, eventNodeId, UA_TRUE);
         if (retval != UA_STATUSCODE_GOOD) {
-            UA_LOG_WARNING(server->config.logger,
-                           UA_LOGCATEGORY_SERVER,
+            UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_SERVER,
                            "Attempt to remove event using deleteNode failed. StatusCode %s",
                            UA_StatusCode_name(retval));
             return retval;

+ 82 - 5
src/ua_types.c

@@ -550,7 +550,7 @@ computeStrides(const UA_Variant *v, const UA_NumericRange range,
     *stride = v->arrayLength; /* So it can be copied as a contiguous block.   */
     *first = 0;
     size_t running_dimssize = 1;
-    bool found_contiguous = false;
+    UA_Boolean found_contiguous = false;
     for(size_t k = dims_count; k > 0;) {
         --k;
         size_t dimrange = 1 + realmax[k] - range.dimensions[k].min;
@@ -567,7 +567,7 @@ computeStrides(const UA_Variant *v, const UA_NumericRange range,
 }
 
 /* Is the type string-like? */
-static bool
+static UA_Boolean
 isStringLike(const UA_DataType *type) {
     if(type == &UA_TYPES[UA_TYPES_STRING] ||
        type == &UA_TYPES[UA_TYPES_BYTESTRING] ||
@@ -604,8 +604,8 @@ UA_Variant_copyRange(const UA_Variant *src, UA_Variant *dst,
                      const UA_NumericRange range) {
     if(!src->type)
         return UA_STATUSCODE_BADINVALIDARGUMENT;
-    bool isScalar = UA_Variant_isScalar(src);
-    bool stringLike = isStringLike(src->type);
+    UA_Boolean isScalar = UA_Variant_isScalar(src);
+    UA_Boolean stringLike = isStringLike(src->type);
     UA_Variant arraySrc;
 
     /* Extract the range for copying at this level. The remaining range is dealt
@@ -733,7 +733,7 @@ UA_Variant_copyRange(const UA_Variant *src, UA_Variant *dst,
  * variant and strings. This is already possible for reading... */
 static UA_StatusCode
 Variant_setRange(UA_Variant *v, void *array, size_t arraySize,
-                 const UA_NumericRange range, bool copy) {
+                 const UA_NumericRange range, UA_Boolean copy) {
     /* Compute the strides */
     size_t count, block, stride, first;
     UA_StatusCode retval = computeStrides(v, range, &count,
@@ -1109,3 +1109,80 @@ isDataTypeNumeric(const UA_DataType *type) {
             return true;
     return false;
 }
+
+/**********************/
+/* Parse NumericRange */
+/**********************/
+
+static size_t
+readDimension(UA_Byte *buf, size_t buflen, UA_NumericRangeDimension *dim) {
+    size_t progress = UA_readNumber(buf, buflen, &dim->min);
+    if(progress == 0)
+        return 0;
+    if(buflen <= progress + 1 || buf[progress] != ':') {
+        dim->max = dim->min;
+        return progress;
+    }
+
+    ++progress;
+    size_t progress2 = UA_readNumber(&buf[progress], buflen - progress, &dim->max);
+    if(progress2 == 0)
+        return 0;
+
+    /* invalid range */
+    if(dim->min >= dim->max)
+        return 0;
+
+    return progress + progress2;
+}
+
+UA_StatusCode
+UA_NumericRange_parseFromString(UA_NumericRange *range, const UA_String *str) {
+    size_t idx = 0;
+    size_t dimensionsMax = 0;
+    UA_NumericRangeDimension *dimensions = NULL;
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    size_t offset = 0;
+    while(true) {
+        /* alloc dimensions */
+        if(idx >= dimensionsMax) {
+            UA_NumericRangeDimension *newds;
+            size_t newdssize = sizeof(UA_NumericRangeDimension) * (dimensionsMax + 2);
+            newds = (UA_NumericRangeDimension*)UA_realloc(dimensions, newdssize);
+            if(!newds) {
+                retval = UA_STATUSCODE_BADOUTOFMEMORY;
+                break;
+            }
+            dimensions = newds;
+            dimensionsMax = dimensionsMax + 2;
+        }
+
+        /* read the dimension */
+        size_t progress = readDimension(&str->data[offset], str->length - offset,
+                                        &dimensions[idx]);
+        if(progress == 0) {
+            retval = UA_STATUSCODE_BADINDEXRANGEINVALID;
+            break;
+        }
+        offset += progress;
+        ++idx;
+
+        /* loop into the next dimension */
+        if(offset >= str->length)
+            break;
+
+        if(str->data[offset] != ',') {
+            retval = UA_STATUSCODE_BADINDEXRANGEINVALID;
+            break;
+        }
+        ++offset;
+    }
+
+    if(retval == UA_STATUSCODE_GOOD && idx > 0) {
+        range->dimensions = dimensions;
+        range->dimensionsSize = idx;
+    } else
+        UA_free(dimensions);
+
+    return retval;
+}

+ 11 - 11
src/ua_types_encoding_binary.c

@@ -183,7 +183,7 @@ UA_decode64(const u8 buf[8], u64 *v) {
 
 /* Boolean */
 ENCODE_BINARY(Boolean) {
-    if(ctx->pos + sizeof(bool) > ctx->end)
+    if(ctx->pos + sizeof(UA_Boolean) > ctx->end)
         return UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED;
     *ctx->pos = *(const u8*)src;
     ++ctx->pos;
@@ -191,7 +191,7 @@ ENCODE_BINARY(Boolean) {
 }
 
 DECODE_BINARY(Boolean) {
-    if(ctx->pos + sizeof(bool) > ctx->end)
+    if(ctx->pos + sizeof(UA_Boolean) > ctx->end)
         return UA_STATUSCODE_BADDECODINGERROR;
     *dst = (*ctx->pos > 0) ? true : false;
     ++ctx->pos;
@@ -968,7 +968,7 @@ DECODE_BINARY(ExtensionObject) {
 
 /* Never returns UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED */
 static status
-Variant_encodeBinaryWrapExtensionObject(const UA_Variant *src, const bool isArray, Ctx *ctx) {
+Variant_encodeBinaryWrapExtensionObject(const UA_Variant *src, const UA_Boolean isArray, Ctx *ctx) {
     /* Default to 1 for a scalar. */
     size_t length = 1;
 
@@ -1016,8 +1016,8 @@ ENCODE_BINARY(Variant) {
         return ENCODE_DIRECT(&encoding, Byte);
 
     /* Set the content type in the encoding mask */
-    const bool isBuiltin = src->type->builtin;
-    const bool isAlias = src->type->membersSize == 1
+    const UA_Boolean isBuiltin = src->type->builtin;
+    const UA_Boolean isAlias = src->type->membersSize == 1
                          && UA_TYPES[src->type->members[0].memberTypeIndex].builtin;
     if(isBuiltin)
         encoding |= UA_VARIANT_ENCODINGMASKTYPE_TYPEID_MASK & (u8)(src->type->typeIndex + 1);
@@ -1027,8 +1027,8 @@ ENCODE_BINARY(Variant) {
         encoding |= UA_VARIANT_ENCODINGMASKTYPE_TYPEID_MASK & (u8)(UA_TYPES_EXTENSIONOBJECT + 1);
 
     /* Set the array type in the encoding mask */
-    const bool isArray = src->arrayLength > 0 || src->data <= UA_EMPTY_ARRAY_SENTINEL;
-    const bool hasDimensions = isArray && src->arrayDimensionsSize > 0;
+    const UA_Boolean isArray = src->arrayLength > 0 || src->data <= UA_EMPTY_ARRAY_SENTINEL;
+    const UA_Boolean hasDimensions = isArray && src->arrayDimensionsSize > 0;
     if(isArray) {
         encoding |= UA_VARIANT_ENCODINGMASKTYPE_ARRAY;
         if(hasDimensions)
@@ -1111,7 +1111,7 @@ DECODE_BINARY(Variant) {
         return UA_STATUSCODE_GOOD;
 
     /* Does the variant contain an array? */
-    const bool isArray = (encodingByte & UA_VARIANT_ENCODINGMASKTYPE_ARRAY) > 0;
+    const UA_Boolean isArray = (encodingByte & UA_VARIANT_ENCODINGMASKTYPE_ARRAY) > 0;
 
     /* Get the datatype of the content. The type must be a builtin data type.
      * All not-builtin types are wrapped in an ExtensionObject. */
@@ -1644,9 +1644,9 @@ CALCSIZE_BINARY(Variant) {
     if(!src->type)
         return s;
 
-    bool isArray = src->arrayLength > 0 || src->data <= UA_EMPTY_ARRAY_SENTINEL;
-    bool hasDimensions = isArray && src->arrayDimensionsSize > 0;
-    bool isBuiltin = src->type->builtin;
+    UA_Boolean isArray = src->arrayLength > 0 || src->data <= UA_EMPTY_ARRAY_SENTINEL;
+    UA_Boolean hasDimensions = isArray && src->arrayDimensionsSize > 0;
+    UA_Boolean isBuiltin = src->type->builtin;
 
 
     size_t encode_index = src->type->typeIndex;

+ 11 - 2
src/ua_util.c

@@ -31,9 +31,18 @@ UA_readNumber(u8 *buf, size_t buflen, u32 *number) {
 UA_StatusCode
 UA_parseEndpointUrl(const UA_String *endpointUrl, UA_String *outHostname,
                     u16 *outPort, UA_String *outPath) {
-    /* Url must begin with "opc.tcp://" */
-    if(endpointUrl->length < 11 || strncmp((char*)endpointUrl->data, "opc.tcp://", 10) != 0)
+    /* Url must begin with "opc.tcp://" or opc.udp:// (if pubsub enabled) */
+    if(endpointUrl->length < 11) {
         return UA_STATUSCODE_BADTCPENDPOINTURLINVALID;
+    } else if (strncmp((char*)endpointUrl->data, "opc.tcp://", 10) != 0) {
+#ifdef UA_ENABLE_PUBSUB
+        if (strncmp((char*)endpointUrl->data, "opc.udp://", 10) != 0) {
+            return UA_STATUSCODE_BADTCPENDPOINTURLINVALID;
+        }
+#else
+        return UA_STATUSCODE_BADTCPENDPOINTURLINVALID;
+#endif
+    }
 
     /* Where does the hostname end? */
     size_t curr = 10;

+ 7 - 153
tests/CMakeLists.txt

@@ -2,7 +2,7 @@
 find_package(Check REQUIRED)
 set(LIBS ${CHECK_LIBRARIES} ${open62541_LIBRARIES})
 include_directories(${CHECK_INCLUDE_DIRS})
-find_package(Threads REQUIRED)
+#find_package(Threads REQUIRED)
 if(NOT MSVC AND UA_ENABLE_UNIT_TESTS_MEMCHECK)
     find_package(Valgrind REQUIRED)
 endif()
@@ -145,6 +145,10 @@ add_executable(check_monitoreditem_filter server/check_monitoreditem_filter.c $<
 target_link_libraries(check_monitoreditem_filter ${LIBS})
 add_test_valgrind(monitoreditem_filter ${TESTS_BINARY_DIR}/check_monitoreditem_filter)
 
+add_executable(check_subscription_events server/check_subscription_events.c $<TARGET_OBJECTS:open62541-object> $<TARGET_OBJECTS:open62541-testplugins>)
+target_link_libraries(check_subscription_events ${LIBS})
+add_test_valgrind(subscription_events ${TESTS_BINARY_DIR}/check_subscription_events)
+
 add_executable(check_nodestore server/check_nodestore.c $<TARGET_OBJECTS:open62541-object> $<TARGET_OBJECTS:open62541-testplugins>)
 target_link_libraries(check_nodestore ${LIBS})
 add_test_valgrind(nodestore ${TESTS_BINARY_DIR}/check_nodestore)
@@ -233,155 +237,5 @@ if(UA_ENABLE_ENCRYPTION)
     add_test_valgrind(encryption_basic256sha256 ${TESTS_BINARY_DIR}/check_encryption_basic256sha256)
 endif()
 
-#############################
-#                           #
-# Test for Nodeset Compiler #
-#                           #
-#############################
-
-# can only be tested if UA_ENABLE_FULL_NS0
-
-if (UA_ENABLE_FULL_NS0)
-
-    file(MAKE_DIRECTORY "${PROJECT_BINARY_DIR}/src_generated/tests")
-
-    # Generate types for DI namespace
-    set(UA_TYPES_OUT "ua_types_di")
-    add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/tests/${UA_TYPES_OUT}_generated.c
-                       ${PROJECT_BINARY_DIR}/src_generated/tests/${UA_TYPES_OUT}_generated.h
-                       ${PROJECT_BINARY_DIR}/src_generated/tests/${UA_TYPES_OUT}_generated_handling.h
-                       ${PROJECT_BINARY_DIR}/src_generated/tests/${UA_TYPES_OUT}_generated_encoding_binary.h
-                       PRE_BUILD
-                       COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/generate_datatypes.py
-                       --namespace=2
-                       --type-csv=${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/OpcUaDiModel.csv
-                       --type-bsd=${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/Opc.Ua.Di.Types.bsd
-                       --no-builtin
-                       ${PROJECT_BINARY_DIR}/src_generated/tests/${UA_TYPES_OUT}
-                       DEPENDS ${PROJECT_SOURCE_DIR}/tools/generate_datatypes.py
-                       ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/OpcUaDiModel.csv
-                       ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/Opc.Ua.Di.Types.bsd)
-    add_custom_target(open62541-generator-tests-types-di DEPENDS ${PROJECT_BINARY_DIR}/src_generated/tests/${UA_TYPES_OUT}_generated.c)
-
-    # Generate types for ADI namespace
-    set(UA_TYPES_OUT "ua_types_adi")
-    add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/tests/${UA_TYPES_OUT}_generated.c
-                       ${PROJECT_BINARY_DIR}/src_generated/tests/${UA_TYPES_OUT}_generated.h
-                       ${PROJECT_BINARY_DIR}/src_generated/tests/${UA_TYPES_OUT}_generated_handling.h
-                       ${PROJECT_BINARY_DIR}/src_generated/tests/${UA_TYPES_OUT}_generated_encoding_binary.h
-                       PRE_BUILD
-                       COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/generate_datatypes.py
-                       --namespace=3
-                       --type-csv=${PROJECT_SOURCE_DIR}/deps/ua-nodeset/ADI/OpcUaAdiModel.csv
-                       --type-bsd=${PROJECT_SOURCE_DIR}/deps/ua-nodeset/ADI/Opc.Ua.Adi.Types.bsd
-                       --no-builtin
-                       ${PROJECT_BINARY_DIR}/src_generated/tests/${UA_TYPES_OUT}
-                       DEPENDS ${PROJECT_SOURCE_DIR}/tools/generate_datatypes.py
-                       ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/ADI/OpcUaAdiModel.csv
-                       ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/ADI/Opc.Ua.Adi.Types.bsd)
-    add_custom_target(open62541-generator-tests-types-adi DEPENDS ${PROJECT_BINARY_DIR}/src_generated/tests/${UA_TYPES_OUT}_generated.c)
-
-    # generate DI namespace
-    add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_di.c
-                       ${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_di.h
-                       PRE_BUILD
-                       COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset_compiler.py
-                       --internal-headers
-                       --types-array=UA_TYPES
-                       --types-array=UA_TYPES_DI
-                       --existing ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/Schema/Opc.Ua.NodeSet2.xml
-                       --xml ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/Opc.Ua.Di.NodeSet2.xml
-                       ${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_di
-                       DEPENDS ${UA_NAMESPACE0_XML}
-                       ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset_compiler.py
-                       ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodes.py
-                       ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset.py
-                       ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/datatypes.py
-                       ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541.py
-                       ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541_nodes.py
-                       ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541_datatypes.py
-                       ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/Schema/Opc.Ua.NodeSet2.xml
-                       ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/Opc.Ua.Di.NodeSet2.xml
-                       )
-    add_custom_target(open62541-generator-tests-ns-di DEPENDS ${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_di.c)
-    add_dependencies(open62541-generator-tests-ns-di open62541-generator-tests-types-di)
-
-
-    # generate ADI namespace which is using DI
-    add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_adi.c
-                       ${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_adi.h
-                       PRE_BUILD
-                       COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset_compiler.py
-                       --internal-headers
-                       --types-array=UA_TYPES
-                       --types-array=UA_TYPES_DI
-                       --types-array=UA_TYPES_ADI
-                       --existing ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/Schema/Opc.Ua.NodeSet2.xml
-                       --existing ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/Opc.Ua.Di.NodeSet2.xml
-                       --xml ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/ADI/Opc.Ua.Adi.NodeSet2.xml
-                       ${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_adi
-                       DEPENDS ${UA_NAMESPACE0_XML}
-                       ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset_compiler.py
-                       ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodes.py
-                       ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset.py
-                       ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/datatypes.py
-                       ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541.py
-                       ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541_nodes.py
-                       ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541_datatypes.py
-                       ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/Schema/Opc.Ua.NodeSet2.xml
-                       ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/Opc.Ua.Di.NodeSet2.xml
-                       ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/ADI/Opc.Ua.Adi.NodeSet2.xml
-                       )
-    add_custom_target(open62541-generator-tests-ns-adi DEPENDS ${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_adi.c)
-    add_dependencies(open62541-generator-tests-ns-adi open62541-generator-tests-types-adi open62541-generator-tests-ns-di)
-
-    # generate PLCopen namespace which is using DI
-    add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_plc.c
-                       ${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_plc.h
-                       PRE_BUILD
-                       COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset_compiler.py
-                       --internal-headers
-                       --types-array=UA_TYPES
-                       --types-array=UA_TYPES_DI
-                       # PLCopen has no specific type definition, thus use the default UA_TYPES to ignore it
-                       --types-array=UA_TYPES
-                       --existing ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/Schema/Opc.Ua.NodeSet2.xml
-                       --existing ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/Opc.Ua.Di.NodeSet2.xml
-                       --xml ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/PLCopen/Opc.Ua.Plc.NodeSet2.xml
-                       ${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_plc
-                       DEPENDS ${UA_NAMESPACE0_XML}
-                       ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset_compiler.py
-                       ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodes.py
-                       ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset.py
-                       ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/datatypes.py
-                       ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541.py
-                       ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541_nodes.py
-                       ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541_datatypes.py
-                       ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/Opc.Ua.Di.NodeSet2.xml
-                       ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/PLCopen/Opc.Ua.Plc.NodeSet2.xml
-                       )
-    add_custom_target(open62541-generator-tests-ns-plc DEPENDS ${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_plc.c)
-    add_dependencies(open62541-generator-tests-ns-plc open62541-generator-tests-ns-di)
-
-
-    add_executable(check_nodeset_compiler_adi
-                   server/check_nodeset_compiler_adi.c
-                   ${PROJECT_BINARY_DIR}/src_generated/tests/ua_types_di_generated.c
-                   ${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_di.c
-                   ${PROJECT_BINARY_DIR}/src_generated/tests/ua_types_adi_generated.c
-                   ${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_adi.c
-                   $<TARGET_OBJECTS:open62541-object> $<TARGET_OBJECTS:open62541-testplugins>)
-    add_dependencies(check_nodeset_compiler_adi open62541-generator-tests-ns-adi)
-    target_link_libraries(check_nodeset_compiler_adi ${LIBS})
-    add_test_valgrind(nodeset_compiler_adi ${TESTS_BINARY_DIR}/check_nodeset_compiler_adi)
-
-    add_executable(check_nodeset_compiler_plc
-                   server/check_nodeset_compiler_plc.c
-                   ${PROJECT_BINARY_DIR}/src_generated/tests/ua_types_di_generated.c
-                   ${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_di.c
-                   ${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_plc.c
-                   $<TARGET_OBJECTS:open62541-object> $<TARGET_OBJECTS:open62541-testplugins>)
-    add_dependencies(check_nodeset_compiler_plc open62541-generator-tests-ns-plc)
-    target_link_libraries(check_nodeset_compiler_plc ${LIBS})
-    add_test_valgrind(nodeset_compiler_plc ${TESTS_BINARY_DIR}/check_nodeset_compiler_plc)
-endif()
+# Tests for Nodeset Compiler
+add_subdirectory(nodeset-compiler)

+ 4 - 4
tests/check_types_custom.c

@@ -98,7 +98,7 @@ START_TEST(parseCustomScalar) {
     size_t offset = 0;
     retval = UA_decodeBinary(&buf, &offset, &var2, &UA_TYPES[UA_TYPES_VARIANT], 1, &PointType);
     ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
-    ck_assert_ptr_eq(var2.type, &PointType);
+    ck_assert(var2.type == &PointType);
 
     Point *p2 = (Point*)var2.data;
     ck_assert(p.x == p2->x);
@@ -137,7 +137,7 @@ START_TEST(parseCustomScalarExtensionObject) {
     ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
 
     ck_assert_int_eq(eo2.encoding, UA_EXTENSIONOBJECT_DECODED);
-    ck_assert_ptr_eq(eo2.content.decoded.type, &PointType);
+    ck_assert(eo2.content.decoded.type == &PointType);
 
     Point *p2 = (Point*)eo2.content.decoded.data;
     ck_assert(p.x == p2->x);
@@ -173,13 +173,13 @@ START_TEST(parseCustomArray) {
     size_t offset = 0;
     retval = UA_decodeBinary(&buf, &offset, &var2, &UA_TYPES[UA_TYPES_VARIANT], 1, &PointType);
     ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
-    ck_assert_ptr_eq(var2.type, &UA_TYPES[UA_TYPES_EXTENSIONOBJECT]);
+    ck_assert(var2.type == &UA_TYPES[UA_TYPES_EXTENSIONOBJECT]);
     ck_assert_int_eq(var2.arrayLength, 10);
 
     for (size_t i = 0; i < 10; i++) {
         UA_ExtensionObject *eo = &((UA_ExtensionObject*)var2.data)[i];
         ck_assert_int_eq(eo->encoding, UA_EXTENSIONOBJECT_DECODED);
-        ck_assert_ptr_eq(eo->content.decoded.type, &PointType);
+        ck_assert(eo->content.decoded.type == &PointType);
         Point *p2 = (Point*)eo->content.decoded.data;
 
         // we need to cast floats to int to avoid comparison of floats

+ 3 - 3
tests/fuzz/generate_corpus.sh

@@ -27,9 +27,9 @@ if [ -z ${TRAVIS+x} ]; then
 	export CC=clang-5.0
 	export CXX=clang++-5.0
 else
-	# Travis needs an older clang
-	export CC=clang-3.9
-	export CXX=clang++-3.9
+	# Travis needs a specific
+	export CC=clang-6.0
+	export CXX=clang++-6.0
 fi
 # First build and run the unit tests without any specific fuzz settings
 cmake -DUA_BUILD_FUZZING_CORPUS=ON -DUA_BUILD_UNIT_TESTS=ON -DUA_ENABLE_DISCOVERY_MULTICAST=ON -DUA_ENABLE_ENCRYPTION=ON ..

+ 172 - 0
tests/nodeset-compiler/CMakeLists.txt

@@ -0,0 +1,172 @@
+file(MAKE_DIRECTORY "${PROJECT_BINARY_DIR}/src_generated/tests")
+include_directories("${PROJECT_BINARY_DIR}/src_generated/tests")
+
+macro(generate_dataset xml source)
+    add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/tests/${source}.c
+                              ${PROJECT_BINARY_DIR}/src_generated/tests/${source}.h
+                       PRE_BUILD
+                       COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset_compiler.py
+                               --types-array=UA_TYPES
+                               --existing ${UA_FILE_NS0}
+                               --xml ${xml}
+                               ${PROJECT_BINARY_DIR}/src_generated/tests/${source}
+                       DEPENDS ${UA_FILE_NS0} ${xml}
+                               ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset_compiler.py
+                               ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodes.py
+                               ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset.py
+                               ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/datatypes.py
+                               ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541.py
+                               ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541_nodes.py
+                               ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541_datatypes.py)
+endmacro()
+
+# Check ObjectType
+generate_dataset(${PROJECT_SOURCE_DIR}/tests/nodeset-compiler/objecttype.xml check_nodeset_objecttype_generated)
+add_executable(check_nodeset_objecttype check_nodeset_objecttype.c
+               ${PROJECT_BINARY_DIR}/src_generated/tests/check_nodeset_objecttype_generated.c
+               $<TARGET_OBJECTS:open62541-object> $<TARGET_OBJECTS:open62541-testplugins>)
+target_link_libraries(check_nodeset_objecttype ${LIBS})
+add_test_valgrind(nodeset_objecttype ${TESTS_BINARY_DIR}/check_nodeset_objecttype)
+
+###############################################
+# Test Companion Specs that need the full NS0 #
+###############################################
+
+if(UA_ENABLE_FULL_NS0)
+
+    # Generate types for DI namespace
+    set(UA_TYPES_OUT "ua_types_di")
+    add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/tests/${UA_TYPES_OUT}_generated.c
+                       ${PROJECT_BINARY_DIR}/src_generated/tests/${UA_TYPES_OUT}_generated.h
+                       ${PROJECT_BINARY_DIR}/src_generated/tests/${UA_TYPES_OUT}_generated_handling.h
+                       ${PROJECT_BINARY_DIR}/src_generated/tests/${UA_TYPES_OUT}_generated_encoding_binary.h
+                       PRE_BUILD
+                       COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/generate_datatypes.py
+                       --namespace=2
+                       --type-csv=${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/OpcUaDiModel.csv
+                       --type-bsd=${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/Opc.Ua.Di.Types.bsd
+                       --no-builtin
+                       ${PROJECT_BINARY_DIR}/src_generated/tests/${UA_TYPES_OUT}
+                       DEPENDS ${PROJECT_SOURCE_DIR}/tools/generate_datatypes.py
+                       ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/OpcUaDiModel.csv
+                       ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/Opc.Ua.Di.Types.bsd)
+    add_custom_target(open62541-generator-tests-types-di DEPENDS ${PROJECT_BINARY_DIR}/src_generated/tests/${UA_TYPES_OUT}_generated.c)
+
+    # Generate types for ADI namespace
+    set(UA_TYPES_OUT "ua_types_adi")
+    add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/tests/${UA_TYPES_OUT}_generated.c
+                       ${PROJECT_BINARY_DIR}/src_generated/tests/${UA_TYPES_OUT}_generated.h
+                       ${PROJECT_BINARY_DIR}/src_generated/tests/${UA_TYPES_OUT}_generated_handling.h
+                       ${PROJECT_BINARY_DIR}/src_generated/tests/${UA_TYPES_OUT}_generated_encoding_binary.h
+                       PRE_BUILD
+                       COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/generate_datatypes.py
+                       --namespace=3
+                       --type-csv=${PROJECT_SOURCE_DIR}/deps/ua-nodeset/ADI/OpcUaAdiModel.csv
+                       --type-bsd=${PROJECT_SOURCE_DIR}/deps/ua-nodeset/ADI/Opc.Ua.Adi.Types.bsd
+                       --no-builtin
+                       ${PROJECT_BINARY_DIR}/src_generated/tests/${UA_TYPES_OUT}
+                       DEPENDS ${PROJECT_SOURCE_DIR}/tools/generate_datatypes.py
+                       ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/ADI/OpcUaAdiModel.csv
+                       ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/ADI/Opc.Ua.Adi.Types.bsd)
+    add_custom_target(open62541-generator-tests-types-adi DEPENDS ${PROJECT_BINARY_DIR}/src_generated/tests/${UA_TYPES_OUT}_generated.c)
+
+    # generate DI namespace
+    add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_di.c
+                       ${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_di.h
+                       PRE_BUILD
+                       COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset_compiler.py
+                       --internal-headers
+                       --types-array=UA_TYPES
+                       --types-array=UA_TYPES_DI
+                       --existing ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/Schema/Opc.Ua.NodeSet2.xml
+                       --xml ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/Opc.Ua.Di.NodeSet2.xml
+                       ${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_di
+                       DEPENDS ${UA_NAMESPACE0_XML}
+                       ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset_compiler.py
+                       ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodes.py
+                       ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset.py
+                       ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/datatypes.py
+                       ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541.py
+                       ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541_nodes.py
+                       ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541_datatypes.py
+                       ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/Schema/Opc.Ua.NodeSet2.xml
+                       ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/Opc.Ua.Di.NodeSet2.xml
+                       )
+    add_custom_target(open62541-generator-tests-ns-di DEPENDS ${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_di.c)
+    add_dependencies(open62541-generator-tests-ns-di open62541-generator-tests-types-di)
+
+    # generate ADI namespace which is using DI
+    add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_adi.c
+                       ${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_adi.h
+                       PRE_BUILD
+                       COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset_compiler.py
+                       --internal-headers
+                       --types-array=UA_TYPES
+                       --types-array=UA_TYPES_DI
+                       --types-array=UA_TYPES_ADI
+                       --existing ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/Schema/Opc.Ua.NodeSet2.xml
+                       --existing ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/Opc.Ua.Di.NodeSet2.xml
+                       --xml ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/ADI/Opc.Ua.Adi.NodeSet2.xml
+                       ${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_adi
+                       DEPENDS ${UA_NAMESPACE0_XML}
+                       ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset_compiler.py
+                       ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodes.py
+                       ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset.py
+                       ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/datatypes.py
+                       ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541.py
+                       ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541_nodes.py
+                       ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541_datatypes.py
+                       ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/Schema/Opc.Ua.NodeSet2.xml
+                       ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/Opc.Ua.Di.NodeSet2.xml
+                       ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/ADI/Opc.Ua.Adi.NodeSet2.xml
+                       )
+    add_custom_target(open62541-generator-tests-ns-adi DEPENDS ${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_adi.c)
+    add_dependencies(open62541-generator-tests-ns-adi open62541-generator-tests-types-adi open62541-generator-tests-ns-di)
+
+    # generate PLCopen namespace which is using DI
+    add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_plc.c
+                       ${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_plc.h
+                       PRE_BUILD
+                       COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset_compiler.py
+                       --internal-headers
+                       --types-array=UA_TYPES
+                       --types-array=UA_TYPES_DI
+                       # PLCopen has no specific type definition, thus use the default UA_TYPES to ignore it
+                       --types-array=UA_TYPES
+                       --existing ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/Schema/Opc.Ua.NodeSet2.xml
+                       --existing ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/Opc.Ua.Di.NodeSet2.xml
+                       --xml ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/PLCopen/Opc.Ua.Plc.NodeSet2.xml
+                       ${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_plc
+                       DEPENDS ${UA_NAMESPACE0_XML}
+                       ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset_compiler.py
+                       ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodes.py
+                       ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset.py
+                       ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/datatypes.py
+                       ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541.py
+                       ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541_nodes.py
+                       ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541_datatypes.py
+                       ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/Opc.Ua.Di.NodeSet2.xml
+                       ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/PLCopen/Opc.Ua.Plc.NodeSet2.xml
+                       )
+    add_custom_target(open62541-generator-tests-ns-plc DEPENDS ${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_plc.c)
+    add_dependencies(open62541-generator-tests-ns-plc open62541-generator-tests-ns-di)
+
+    add_executable(check_nodeset_compiler_adi check_nodeset_compiler_adi.c
+                   ${PROJECT_BINARY_DIR}/src_generated/tests/ua_types_di_generated.c
+                   ${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_di.c
+                   ${PROJECT_BINARY_DIR}/src_generated/tests/ua_types_adi_generated.c
+                   ${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_adi.c
+                   $<TARGET_OBJECTS:open62541-object> $<TARGET_OBJECTS:open62541-testplugins>)
+    add_dependencies(check_nodeset_compiler_adi open62541-generator-tests-ns-adi)
+    target_link_libraries(check_nodeset_compiler_adi ${LIBS})
+    add_test_valgrind(nodeset_compiler_adi ${TESTS_BINARY_DIR}/check_nodeset_compiler_adi)
+
+    add_executable(check_nodeset_compiler_plc check_nodeset_compiler_plc.c
+                   ${PROJECT_BINARY_DIR}/src_generated/tests/ua_types_di_generated.c
+                   ${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_di.c
+                   ${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_plc.c
+                   $<TARGET_OBJECTS:open62541-object> $<TARGET_OBJECTS:open62541-testplugins>)
+    add_dependencies(check_nodeset_compiler_plc open62541-generator-tests-ns-plc)
+    target_link_libraries(check_nodeset_compiler_plc ${LIBS})
+    add_test_valgrind(nodeset_compiler_plc ${TESTS_BINARY_DIR}/check_nodeset_compiler_plc)
+endif()

tests/server/check_nodeset_compiler_adi.c → tests/nodeset-compiler/check_nodeset_compiler_adi.c


tests/server/check_nodeset_compiler_plc.c → tests/nodeset-compiler/check_nodeset_compiler_plc.c


+ 45 - 0
tests/nodeset-compiler/check_nodeset_objecttype.c

@@ -0,0 +1,45 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "open62541.h"
+#include "check_nodeset_objecttype_generated.h"
+#include "check.h"
+
+static UA_Server *server = NULL;
+static UA_ServerConfig *config = NULL;
+
+static void setup(void) {
+    config = UA_ServerConfig_new_default();
+    server = UA_Server_new(config);
+    UA_StatusCode retval = check_nodeset_objecttype_generated(server);
+    ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+}
+
+static void teardown(void) {
+    UA_Server_delete(server);
+    UA_ServerConfig_delete(config);
+}
+
+START_TEST(checkObjectTypeExists) {
+    UA_NodeClass nc = UA_NODECLASS_UNSPECIFIED;
+    UA_StatusCode retval = UA_Server_readNodeClass(server, UA_NODEID_NUMERIC(2, 1001), &nc);
+    ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+    ck_assert_int_eq(nc, UA_NODECLASS_OBJECTTYPE);
+} END_TEST
+
+int main(void) {
+    Suite *s = suite_create("XML-Generated ObjectType");
+    TCase *tc_objecttype = tcase_create("objecttype tests");
+    tcase_add_checked_fixture(tc_objecttype, setup, teardown);
+    tcase_add_test(tc_objecttype, checkObjectTypeExists);
+    suite_add_tcase(s, tc_objecttype);
+
+    SRunner *sr = srunner_create(s);
+    srunner_set_fork_status(sr, CK_NOFORK);
+    srunner_run_all(sr, CK_NORMAL);
+    int number_failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+
+    return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}

+ 61 - 0
tests/nodeset-compiler/objecttype.xml

@@ -0,0 +1,61 @@
+<UANodeSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:uax="http://opcfoundation.org/UA/2008/02/Types.xsd" xmlns="http://opcfoundation.org/UA/2011/03/UANodeSet.xsd" xmlns:s1="http://yourorganisation.org/test/" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+    <NamespaceUris>
+        <Uri>http://yourorganisation.org/test/</Uri>
+    </NamespaceUris>
+    <Aliases>
+        <Alias Alias="Double">i=11</Alias>
+        <Alias Alias="Organizes">i=35</Alias>
+        <Alias Alias="HasModellingRule">i=37</Alias>
+        <Alias Alias="HasTypeDefinition">i=40</Alias>
+        <Alias Alias="HasSubtype">i=45</Alias>
+        <Alias Alias="HasComponent">i=47</Alias>
+    </Aliases>
+    <!-- Following object has only references to nodes defined after itself -->
+    <UAObject NodeId="ns=1;i=5001" BrowseName="1:testInstance">
+        <DisplayName>testInstance</DisplayName>
+        <References>
+            <Reference ReferenceType="Organizes" IsForward="false">ns=1;i=5002</Reference>
+            <Reference ReferenceType="HasTypeDefinition">ns=1;i=1001</Reference>
+            <Reference ReferenceType="HasComponent">ns=1;i=6002</Reference>
+        </References>
+    </UAObject>
+    <UAObject NodeId="ns=1;i=5002" BrowseName="1:testFolder">
+        <DisplayName>testFolder</DisplayName>
+        <References>
+            <Reference ReferenceType="Organizes" IsForward="false">i=85</Reference>
+            <Reference ReferenceType="HasTypeDefinition">i=61</Reference>
+        </References>
+    </UAObject>
+    <UAObjectType NodeId="ns=1;i=1001" BrowseName="1:testType">
+        <DisplayName>testType</DisplayName>
+        <References>
+            <Reference ReferenceType="HasSubtype" IsForward="false">i=58</Reference>
+            <Reference ReferenceType="HasComponent">ns=1;i=6001</Reference>
+        </References>
+    </UAObjectType>
+    <UAVariable DataType="Double" ParentNodeId="ns=1;i=1001" NodeId="ns=1;i=6001" BrowseName="1:Var1" UserAccessLevel="3" AccessLevel="3">
+        <DisplayName>Var1</DisplayName>
+        <References>
+            <Reference ReferenceType="HasTypeDefinition">i=63</Reference>
+            <Reference ReferenceType="HasModellingRule">i=78</Reference>
+            <Reference ReferenceType="HasComponent" IsForward="false">ns=1;i=1001</Reference>
+        </References>
+        <Value>
+            <uax:Double>42.0</uax:Double>
+        </Value>
+    </UAVariable>
+    <UAVariable ParentNodeId="ns=1;i=5001" NodeId="ns=1;i=6002" BrowseName="1:Var1" DataType="i=7" UserAccessLevel="3" AccessLevel="3">
+        <DisplayName>Var2</DisplayName>
+        <References>
+            <Reference ReferenceType="HasTypeDefinition">i=63</Reference>
+            <Reference ReferenceType="HasComponent" IsForward="false">ns=1;i=5001</Reference>
+        </References>
+        <Value>
+            <ListOfUInt32 xmlns="http://opcfoundation.org/UA/2008/02/Types.xsd">
+                <uax:UInt32>1</uax:UInt32>
+                <uax:UInt32>2</uax:UInt32>
+                <uax:UInt32>3</uax:UInt32>
+            </ListOfUInt32>
+        </Value>
+    </UAVariable>
+</UANodeSet>

+ 4 - 2
tests/server/check_discovery.c

@@ -405,8 +405,10 @@ START_TEST(Client_find_on_network_registered) {
     ck_assert_uint_eq(gethostname(hostname, 255), 0);
 
     //DNS limits name to max 63 chars (+ \0)
-    snprintf(urls[0], 64, "LDS_test-%s", hostname);
-    snprintf(urls[1], 64, "Register_test-%s", hostname);
+    //We need this ugly casting, otherwise gcc >7.2 will complain about format-truncation, but we want it here
+    void *hostnameVoid = (void*)hostname;
+    snprintf(urls[0], 64, "LDS_test-%s", (char*)hostnameVoid);
+    snprintf(urls[1], 64, "Register_test-%s", (char*)hostnameVoid);
     expectedUris[0] = UA_STRING(urls[0]);
     expectedUris[1] = UA_STRING(urls[1]);
     FindOnNetworkAndCheck(expectedUris, 2, NULL, NULL, NULL, 0);

+ 1 - 1
tests/server/check_server_userspace.c

@@ -41,7 +41,7 @@ START_TEST(Server_addNamespace_writeService) {
     UA_Server_readValue(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_NAMESPACEARRAY),
                         &namespaces);
     ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
-    ck_assert_ptr_eq(namespaces.type, &UA_TYPES[UA_TYPES_STRING]);
+    ck_assert(namespaces.type == &UA_TYPES[UA_TYPES_STRING]);
 
     namespaces.data = UA_realloc(namespaces.data, (namespaces.arrayLength + 1) * sizeof(UA_String));
     ++namespaces.arrayLength;

+ 20 - 20
tests/server/check_services_attributes.c

@@ -164,7 +164,7 @@ START_TEST(ReadSingleAttributeValueWithoutTimestamp) {
 
     ck_assert_int_eq(resp.status, UA_STATUSCODE_GOOD);
     ck_assert_int_eq(0, resp.value.arrayLength);
-    ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_INT32], resp.value.type);
+    ck_assert(&UA_TYPES[UA_TYPES_INT32] == resp.value.type);
     ck_assert_int_eq(42, *(UA_Int32* )resp.value.data);
     UA_DataValue_deleteMembers(&resp);
 } END_TEST
@@ -202,7 +202,7 @@ START_TEST(ReadSingleAttributeValueRangeWithoutTimestamp) {
     UA_DataValue resp = UA_Server_read(server, &rvi, UA_TIMESTAMPSTORETURN_NEITHER);
 
     ck_assert_int_eq(4, resp.value.arrayLength);
-    ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_INT32], resp.value.type);
+    ck_assert(&UA_TYPES[UA_TYPES_INT32] == resp.value.type);
     UA_DataValue_deleteMembers(&resp);
 } END_TEST
 
@@ -216,7 +216,7 @@ START_TEST(ReadSingleAttributeNodeIdWithoutTimestamp) {
 
     const UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
     ck_assert_int_eq(0, resp.value.arrayLength);
-    ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_NODEID], resp.value.type);
+    ck_assert(&UA_TYPES[UA_TYPES_NODEID] == resp.value.type);
     UA_NodeId* respval = (UA_NodeId*) resp.value.data;
     ck_assert_int_eq(1, respval->namespaceIndex);
     ck_assert(UA_String_equal(&myIntegerNodeId.identifier.string, &respval->identifier.string));
@@ -232,7 +232,7 @@ START_TEST(ReadSingleAttributeNodeClassWithoutTimestamp) {
     UA_DataValue resp = UA_Server_read(server, &rvi, UA_TIMESTAMPSTORETURN_NEITHER);
 
     ck_assert_int_eq(0, resp.value.arrayLength);
-    ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_NODECLASS],resp.value.type);
+    ck_assert(&UA_TYPES[UA_TYPES_NODECLASS] == resp.value.type);
     ck_assert_int_eq(*(UA_Int32*)resp.value.data,UA_NODECLASS_VARIABLE);
     UA_DataValue_deleteMembers(&resp);
 } END_TEST
@@ -248,7 +248,7 @@ START_TEST(ReadSingleAttributeBrowseNameWithoutTimestamp) {
     UA_QualifiedName* respval = (UA_QualifiedName*) resp.value.data;
     const UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer");
     ck_assert_int_eq(0, resp.value.arrayLength);
-    ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_QUALIFIEDNAME], resp.value.type);
+    ck_assert(&UA_TYPES[UA_TYPES_QUALIFIEDNAME] == resp.value.type);
     ck_assert_int_eq(1, respval->namespaceIndex);
     ck_assert(UA_String_equal(&myIntegerName.name, &respval->name));
     UA_DataValue_deleteMembers(&resp);
@@ -266,7 +266,7 @@ START_TEST(ReadSingleAttributeDisplayNameWithoutTimestamp) {
     const UA_LocalizedText comp = UA_LOCALIZEDTEXT("locale", "the answer");
     UA_VariableNode* compNode = makeCompareSequence();
     ck_assert_int_eq(0, resp.value.arrayLength);
-    ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_LOCALIZEDTEXT], resp.value.type);
+    ck_assert(&UA_TYPES[UA_TYPES_LOCALIZEDTEXT] == resp.value.type);
     ck_assert(UA_String_equal(&comp.text, &respval->text));
     ck_assert(UA_String_equal(&compNode->displayName.locale, &respval->locale));
     UA_DataValue_deleteMembers(&resp);
@@ -284,7 +284,7 @@ START_TEST(ReadSingleAttributeDescriptionWithoutTimestamp) {
     UA_LocalizedText* respval = (UA_LocalizedText*) resp.value.data;
     UA_VariableNode* compNode = makeCompareSequence();
     ck_assert_int_eq(0, resp.value.arrayLength);
-    ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_LOCALIZEDTEXT], resp.value.type);
+    ck_assert(&UA_TYPES[UA_TYPES_LOCALIZEDTEXT] == resp.value.type);
     ck_assert(UA_String_equal(&compNode->description.locale, &respval->locale));
     ck_assert(UA_String_equal(&compNode->description.text, &respval->text));
     UA_DataValue_deleteMembers(&resp);
@@ -301,7 +301,7 @@ START_TEST(ReadSingleAttributeWriteMaskWithoutTimestamp) {
     
     UA_UInt32* respval = (UA_UInt32*) resp.value.data;
     ck_assert_int_eq(0, resp.value.arrayLength);
-    ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_UINT32], resp.value.type);
+    ck_assert(&UA_TYPES[UA_TYPES_UINT32] == resp.value.type);
     ck_assert_int_eq(0,*respval);
     UA_DataValue_deleteMembers(&resp);
 } END_TEST
@@ -331,7 +331,7 @@ START_TEST(ReadSingleAttributeIsAbstractWithoutTimestamp) {
     UA_DataValue resp = UA_Server_read(server, &rvi, UA_TIMESTAMPSTORETURN_NEITHER);
 
     ck_assert_int_eq(0, resp.value.arrayLength);
-    ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_BOOLEAN], resp.value.type);
+    ck_assert(&UA_TYPES[UA_TYPES_BOOLEAN] == resp.value.type);
     ck_assert(*(UA_Boolean* )resp.value.data==false);
     UA_DataValue_deleteMembers(&resp);
 } END_TEST
@@ -345,7 +345,7 @@ START_TEST(ReadSingleAttributeSymmetricWithoutTimestamp) {
     UA_DataValue resp = UA_Server_read(server, &rvi, UA_TIMESTAMPSTORETURN_NEITHER);
 
     ck_assert_int_eq(0, resp.value.arrayLength);
-    ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_BOOLEAN], resp.value.type);
+    ck_assert(&UA_TYPES[UA_TYPES_BOOLEAN] == resp.value.type);
     ck_assert(*(UA_Boolean* )resp.value.data==false);
     UA_DataValue_deleteMembers(&resp);
 } END_TEST
@@ -361,7 +361,7 @@ START_TEST(ReadSingleAttributeInverseNameWithoutTimestamp) {
     UA_LocalizedText* respval = (UA_LocalizedText*) resp.value.data;
     const UA_LocalizedText comp = UA_LOCALIZEDTEXT("", "OrganizedBy");
     ck_assert_int_eq(0, resp.value.arrayLength);
-    ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_LOCALIZEDTEXT],resp.value.type);
+    ck_assert(&UA_TYPES[UA_TYPES_LOCALIZEDTEXT] == resp.value.type);
     ck_assert(UA_String_equal(&comp.text, &respval->text));
     ck_assert(UA_String_equal(&comp.locale, &respval->locale));
     UA_DataValue_deleteMembers(&resp);
@@ -376,7 +376,7 @@ START_TEST(ReadSingleAttributeContainsNoLoopsWithoutTimestamp) {
     UA_DataValue resp = UA_Server_read(server, &rvi, UA_TIMESTAMPSTORETURN_NEITHER);
 
     ck_assert_int_eq(0, resp.value.arrayLength);
-    ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_BOOLEAN], resp.value.type);
+    ck_assert(&UA_TYPES[UA_TYPES_BOOLEAN] == resp.value.type);
     ck_assert(*(UA_Boolean* )resp.value.data==false);
     UA_DataValue_deleteMembers(&resp);
 } END_TEST
@@ -391,7 +391,7 @@ START_TEST(ReadSingleAttributeEventNotifierWithoutTimestamp) {
 
     ck_assert_int_eq(UA_STATUSCODE_GOOD, resp.status);
     ck_assert_int_eq(0, resp.value.arrayLength);
-    ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_BYTE],resp.value.type);
+    ck_assert(&UA_TYPES[UA_TYPES_BYTE] == resp.value.type);
     ck_assert_int_eq(*(UA_Byte*)resp.value.data, 0);
     UA_DataValue_deleteMembers(&resp);
 } END_TEST
@@ -407,7 +407,7 @@ START_TEST(ReadSingleAttributeDataTypeWithoutTimestamp) {
     ck_assert_int_eq(0, resp.value.arrayLength);
     ck_assert_int_eq(UA_STATUSCODE_GOOD, resp.status);
     ck_assert_int_eq(true, resp.hasValue);
-    ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_NODEID], resp.value.type);
+    ck_assert(&UA_TYPES[UA_TYPES_NODEID] == resp.value.type);
     UA_NodeId* respval = (UA_NodeId*)resp.value.data;
     ck_assert_int_eq(respval->namespaceIndex,0);
     ck_assert_int_eq(respval->identifier.numeric, UA_NS0ID_BASEDATATYPE);
@@ -423,7 +423,7 @@ START_TEST(ReadSingleAttributeValueRankWithoutTimestamp) {
     UA_DataValue resp = UA_Server_read(server, &rvi, UA_TIMESTAMPSTORETURN_NEITHER);
 
     ck_assert_int_eq(0, resp.value.arrayLength);
-    ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_INT32], resp.value.type);
+    ck_assert(&UA_TYPES[UA_TYPES_INT32] == resp.value.type);
     ck_assert_int_eq(-2, *(UA_Int32* )resp.value.data);
     UA_DataValue_deleteMembers(&resp);
 } END_TEST
@@ -437,7 +437,7 @@ START_TEST(ReadSingleAttributeArrayDimensionsWithoutTimestamp) {
     UA_DataValue resp = UA_Server_read(server, &rvi, UA_TIMESTAMPSTORETURN_NEITHER);
 
     ck_assert_int_eq(0, resp.value.arrayLength);
-    ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_UINT32], resp.value.type);
+    ck_assert(&UA_TYPES[UA_TYPES_UINT32] == resp.value.type);
     ck_assert_ptr_eq((UA_Int32*)resp.value.data,0);
     UA_DataValue_deleteMembers(&resp);
 } END_TEST
@@ -451,7 +451,7 @@ START_TEST(ReadSingleAttributeAccessLevelWithoutTimestamp) {
     UA_DataValue resp = UA_Server_read(server, &rvi, UA_TIMESTAMPSTORETURN_NEITHER);
 
     ck_assert_int_eq(0, resp.value.arrayLength);
-    ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_BYTE], resp.value.type);
+    ck_assert(&UA_TYPES[UA_TYPES_BYTE] == resp.value.type);
     ck_assert_int_eq(*(UA_Byte*)resp.value.data, UA_ACCESSLEVELMASK_READ); // set by default
     UA_DataValue_deleteMembers(&resp);
 } END_TEST
@@ -485,7 +485,7 @@ START_TEST(ReadSingleAttributeMinimumSamplingIntervalWithoutTimestamp) {
     UA_VariableNode *compNode = makeCompareSequence();
     UA_Double comp = (UA_Double) compNode->minimumSamplingInterval;
     ck_assert_int_eq(0, resp.value.arrayLength);
-    ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_DOUBLE], resp.value.type);
+    ck_assert(&UA_TYPES[UA_TYPES_DOUBLE] == resp.value.type);
     ck_assert(*respval == comp);
     UA_DataValue_deleteMembers(&resp);
     config->nodestore.deleteNode(config->nodestore.context, (UA_Node*)compNode);
@@ -500,7 +500,7 @@ START_TEST(ReadSingleAttributeHistorizingWithoutTimestamp) {
     UA_DataValue resp = UA_Server_read(server, &rvi, UA_TIMESTAMPSTORETURN_NEITHER);
 
     ck_assert_int_eq(0, resp.value.arrayLength);
-    ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_BOOLEAN], resp.value.type);
+    ck_assert(&UA_TYPES[UA_TYPES_BOOLEAN] == resp.value.type);
     ck_assert(*(UA_Boolean*)resp.value.data==false);
     UA_DataValue_deleteMembers(&resp);
 } END_TEST
@@ -516,7 +516,7 @@ START_TEST(ReadSingleAttributeExecutableWithoutTimestamp) {
 
     ck_assert_int_eq(true, resp.hasValue);
     ck_assert_int_eq(0, resp.value.arrayLength);
-    ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_BOOLEAN], resp.value.type);
+    ck_assert(&UA_TYPES[UA_TYPES_BOOLEAN] == resp.value.type);
     ck_assert(*(UA_Boolean*)resp.value.data==true);
     UA_DataValue_deleteMembers(&resp);
 #endif

+ 0 - 2
tests/server/check_services_subscriptions.c

@@ -2,8 +2,6 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-/* TODO add event based testing */
-
 #include "ua_server.h"
 #include "server/ua_services.h"
 #include "server/ua_server_internal.h"

+ 14 - 0
tests/server/check_services_view.c

@@ -173,6 +173,19 @@ START_TEST(Service_TranslateBrowsePathsToNodeIds) {
 }
 END_TEST
 
+START_TEST(BrowseSimplifiedBrowsePath) {
+    UA_QualifiedName objectsName = UA_QUALIFIEDNAME(0, "Objects");
+    UA_BrowsePathResult bpr =
+        UA_Server_browseSimplifiedBrowsePath(server_translate_browse,
+                                             UA_NODEID_NUMERIC(0, UA_NS0ID_ROOTFOLDER),
+                                             1, &objectsName);
+
+    ck_assert_int_eq(bpr.targetsSize, 1);
+
+    UA_BrowsePathResult_deleteMembers(&bpr);
+}
+END_TEST
+
 static Suite *testSuite_Service_TranslateBrowsePathsToNodeIds(void) {
     Suite *s = suite_create("Service_TranslateBrowsePathsToNodeIds");
     TCase *tc_browse = tcase_create("Browse Service");
@@ -183,6 +196,7 @@ static Suite *testSuite_Service_TranslateBrowsePathsToNodeIds(void) {
     TCase *tc_translate = tcase_create("TranslateBrowsePathsToNodeIds");
     tcase_add_unchecked_fixture(tc_translate, setup_server, teardown_server);
     tcase_add_test(tc_translate, Service_TranslateBrowsePathsToNodeIds);
+    tcase_add_test(tc_translate, BrowseSimplifiedBrowsePath);
 
     suite_add_tcase(s, tc_translate);
     return s;

+ 448 - 0
tests/server/check_subscription_events.c

@@ -0,0 +1,448 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ua_client_subscriptions.h"
+#include "ua_server.h"
+#include "server/ua_services.h"
+#include "server/ua_server_internal.h"
+#include "server/ua_subscription.h"
+#include "ua_config_default.h"
+#include "thread_wrapper.h"
+
+#include "check.h"
+#include "testing_clock.h"
+
+#ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
+
+static UA_Server *server;
+static UA_ServerConfig *config;
+static UA_Boolean *running;
+static THREAD_HANDLE server_thread;
+
+UA_Client *client;
+
+static UA_UInt32 subscriptionId;
+static UA_UInt32 monitoredItemId;
+static UA_NodeId eventType;
+static size_t nSelectClauses = 4;
+static UA_Boolean notificationReceived;
+static UA_SimpleAttributeOperand *selectClauses;
+
+UA_Double publishingInterval = 500.0;
+
+
+static void addNewEventType(void) {
+    UA_ObjectTypeAttributes attr = UA_ObjectTypeAttributes_default;
+    attr.displayName = UA_LOCALIZEDTEXT_ALLOC("en-US", "SimpleEventType");
+    attr.description = UA_LOCALIZEDTEXT_ALLOC("en-US", "The simple event type we created");
+
+    UA_Server_addObjectTypeNode(server, UA_NODEID_NULL,
+                                UA_NODEID_NUMERIC(0, UA_NS0ID_BASEEVENTTYPE),
+                                UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
+                                UA_QUALIFIEDNAME(0, "SimpleEventType"),
+                                attr, NULL, &eventType);
+    UA_LocalizedText_deleteMembers(&attr.displayName);
+    UA_LocalizedText_deleteMembers(&attr.description);
+}
+
+static void setupSelectClauses(void) {
+    // check for severity (set manually), message (set manually), eventType (automatic) and sourceNode (automatic)
+    selectClauses = (UA_SimpleAttributeOperand *)
+                     UA_Array_new(nSelectClauses, &UA_TYPES[UA_TYPES_SIMPLEATTRIBUTEOPERAND]);
+    if (!selectClauses)
+        return;
+
+    for (size_t i = 0; i < nSelectClauses; ++i) {
+        UA_SimpleAttributeOperand_init(&selectClauses[i]);
+        selectClauses[i].typeDefinitionId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEEVENTTYPE);
+        selectClauses[i].browsePathSize = 1;
+        selectClauses[i].attributeId = UA_ATTRIBUTEID_VALUE;
+        selectClauses[i].browsePath = (UA_QualifiedName *)
+                       UA_Array_new(selectClauses[i].browsePathSize, &UA_TYPES[UA_TYPES_QUALIFIEDNAME]);
+        if (!selectClauses[i].browsePathSize) {
+            UA_Array_delete(selectClauses, nSelectClauses, &UA_TYPES[UA_TYPES_SIMPLEATTRIBUTEOPERAND]);
+        }
+    }
+
+    selectClauses[0].browsePath[0] = UA_QUALIFIEDNAME_ALLOC(0, "Severity");
+    selectClauses[1].browsePath[0] = UA_QUALIFIEDNAME_ALLOC(0, "Message");
+    selectClauses[2].browsePath[0] = UA_QUALIFIEDNAME_ALLOC(0, "EventType");
+    selectClauses[3].browsePath[0] = UA_QUALIFIEDNAME_ALLOC(0, "SourceNode");
+}
+
+static void
+handler_events_simple(UA_Client *lclient, UA_UInt32 subId, void *subContext,
+                      UA_UInt32 monId, void *monContext,
+                      size_t nEventFields, UA_Variant *eventFields) {
+    UA_Boolean foundSeverity = UA_FALSE;
+    UA_Boolean foundMessage = UA_FALSE;
+    UA_Boolean foundType = UA_FALSE;
+    UA_Boolean foundSource = UA_FALSE;
+    ck_assert_uint_eq(*(UA_UInt32 *) monContext, monitoredItemId);
+    ck_assert_uint_eq(nEventFields, nSelectClauses);
+    // check all event fields
+    for (unsigned int i = 0; i < nEventFields; i++) {
+        // find out which attribute of the event is being looked at
+        if (UA_Variant_hasScalarType(&eventFields[i], &UA_TYPES[UA_TYPES_UINT16])) {
+            // Severity
+            ck_assert_uint_eq(*((UA_UInt16 *) (eventFields[i].data)), 1000);
+            foundSeverity = UA_TRUE;
+        } else if (UA_Variant_hasScalarType(&eventFields[i], &UA_TYPES[UA_TYPES_LOCALIZEDTEXT])) {
+            // Message
+            UA_LocalizedText comp = UA_LOCALIZEDTEXT("en-US", "Generated Event");
+            ck_assert(UA_String_equal(&((UA_LocalizedText *) eventFields[i].data)->locale, &comp.locale));
+            ck_assert(UA_String_equal(&((UA_LocalizedText *) eventFields[i].data)->text, &comp.text));
+            foundMessage = UA_TRUE;
+        } else if (UA_Variant_hasScalarType(&eventFields[i], &UA_TYPES[UA_TYPES_NODEID])) {
+            // either SourceNode or EventType
+            UA_NodeId serverId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER);
+            if (UA_NodeId_equal((UA_NodeId *)eventFields[i].data, &eventType)) {
+                // EventType
+                foundType = UA_TRUE;
+            } else if (UA_NodeId_equal((UA_NodeId *)eventFields[i].data, &serverId)) {
+                // SourceNode
+                foundSource = UA_TRUE;
+            } else {
+                ck_assert_msg(UA_FALSE, "NodeId doesn't match");
+            }
+        } else {
+            ck_assert_msg(UA_FALSE, "Field doesn't match");
+        }
+    }
+    ck_assert_uint_eq(foundMessage, UA_TRUE);
+    ck_assert_uint_eq(foundSeverity, UA_TRUE);
+    ck_assert_uint_eq(foundType, UA_TRUE);
+    ck_assert_uint_eq(foundSource, UA_TRUE);
+    notificationReceived = true;
+}
+
+// create a subscription and add a monitored item to it
+static void setupSubscription(void) {
+    // Create subscription
+    UA_CreateSubscriptionRequest request = UA_CreateSubscriptionRequest_default();
+    UA_CreateSubscriptionResponse response = UA_Client_Subscriptions_create(client, request,
+                                                                            NULL, NULL, NULL);
+    subscriptionId = response.subscriptionId;
+
+}
+
+static void removeSubscription(void) {
+    UA_DeleteSubscriptionsRequest deleteSubscriptionsRequest;
+    UA_DeleteSubscriptionsRequest_init(&deleteSubscriptionsRequest);
+    UA_UInt32 removeId = subscriptionId;
+    deleteSubscriptionsRequest.subscriptionIdsSize = 1;
+    deleteSubscriptionsRequest.subscriptionIds = &removeId;
+
+    UA_DeleteSubscriptionsResponse deleteSubscriptionsResponse;
+    UA_DeleteSubscriptionsResponse_init(&deleteSubscriptionsResponse);
+
+    Service_DeleteSubscriptions(server, &adminSession, &deleteSubscriptionsRequest,
+                                &deleteSubscriptionsResponse);
+    UA_DeleteSubscriptionsResponse_deleteMembers(&deleteSubscriptionsResponse);
+}
+
+THREAD_CALLBACK(serverloop) {
+    while (*running)
+        UA_Server_run_iterate(server, true);
+    return 0;
+}
+
+static void setup(void) {
+    running = UA_Boolean_new();
+    *running = true;
+    config = UA_ServerConfig_new_default();
+    config->maxPublishReqPerSession = 5;
+    server = UA_Server_new(config);
+    UA_Server_run_startup(server);
+    addNewEventType();
+    setupSelectClauses();
+    THREAD_CREATE(server_thread, serverloop);
+
+    client = UA_Client_new(UA_ClientConfig_default);
+    UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
+    ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+    setupSubscription();
+}
+
+static void teardown(void) {
+    removeSubscription();
+    *running = false;
+    THREAD_JOIN(server_thread);
+    UA_Server_run_shutdown(server);
+    UA_Boolean_delete(running);
+    UA_Server_delete(server);
+    UA_ServerConfig_delete(config);
+    UA_Array_delete(selectClauses, nSelectClauses, &UA_TYPES[UA_TYPES_SIMPLEATTRIBUTEOPERAND]);
+
+    UA_Client_disconnect(client);
+    UA_Client_delete(client);
+}
+
+static UA_StatusCode eventSetup(UA_NodeId *eventNodeId) {
+    UA_StatusCode retval;
+    retval = UA_Server_createEvent(server, eventType, eventNodeId);
+    ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+    // add a severity to the event
+    UA_Variant value;
+    UA_RelativePathElement rpe;
+    UA_RelativePathElement_init(&rpe);
+    rpe.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY);
+    rpe.isInverse = false;
+    rpe.includeSubtypes = false;
+    UA_BrowsePath bp;
+    UA_BrowsePath_init(&bp);
+    bp.startingNode = *eventNodeId;
+    bp.relativePath.elementsSize = 1;
+    bp.relativePath.elements = &rpe;
+    rpe.targetName = UA_QUALIFIEDNAME(0, "Severity");
+    UA_BrowsePathResult bpr = UA_Server_translateBrowsePathToNodeIds(server, &bp);
+    // number with no special meaning
+    UA_UInt16 eventSeverity = 1000;
+    UA_Variant_setScalar(&value, &eventSeverity, &UA_TYPES[UA_TYPES_UINT16]);
+    UA_Server_writeValue(server, bpr.targets[0].targetId.nodeId, value);
+    UA_BrowsePathResult_deleteMembers(&bpr);
+
+    //add a message to the event
+    rpe.targetName = UA_QUALIFIEDNAME(0, "Message");
+    bpr = UA_Server_translateBrowsePathToNodeIds(server, &bp);
+    UA_LocalizedText message = UA_LOCALIZEDTEXT("en-US", "Generated Event");
+    UA_Variant_setScalar(&value, &message, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
+    UA_Server_writeValue(server, bpr.targets[0].targetId.nodeId, value);
+    UA_BrowsePathResult_deleteMembers(&bpr);
+
+    return retval;
+}
+
+static UA_MonitoredItemCreateResult addMonitoredItem(UA_Client_EventNotificationCallback handler) {
+    UA_MonitoredItemCreateRequest item;
+    UA_MonitoredItemCreateRequest_init(&item);
+    item.itemToMonitor.nodeId = UA_NODEID_NUMERIC(0, 2253); // Root->Objects->Server
+    item.itemToMonitor.attributeId = UA_ATTRIBUTEID_EVENTNOTIFIER;
+    item.monitoringMode = UA_MONITORINGMODE_REPORTING;
+
+    UA_EventFilter filter;
+    UA_EventFilter_init(&filter);
+    filter.selectClauses = selectClauses;
+    filter.selectClausesSize = nSelectClauses;
+
+    item.requestedParameters.filter.encoding = UA_EXTENSIONOBJECT_DECODED;
+    item.requestedParameters.filter.content.decoded.data = &filter;
+    item.requestedParameters.filter.content.decoded.type = &UA_TYPES[UA_TYPES_EVENTFILTER];
+    item.requestedParameters.queueSize = 1;
+    item.requestedParameters.discardOldest = true;
+
+    return UA_Client_MonitoredItems_createEvent(client, subscriptionId,
+                                                UA_TIMESTAMPSTORETURN_BOTH, item,
+                                                &monitoredItemId, handler, NULL);
+}
+
+// ensure events are received with proper values
+START_TEST(generateEvents)
+    {
+        UA_NodeId eventNodeId;
+        UA_StatusCode retval = eventSetup(&eventNodeId);
+        ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+
+        // add a monitored item
+        UA_MonitoredItemCreateResult createResult = addMonitoredItem(handler_events_simple);
+        ck_assert_uint_eq(createResult.statusCode, UA_STATUSCODE_GOOD);
+        // trigger the event
+        retval = UA_Server_triggerEvent(server, eventNodeId, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER), NULL, UA_TRUE);
+        ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+
+        // let the client fetch the event and check if the correct values were received
+        notificationReceived = false;
+        UA_fakeSleep((UA_UInt32) publishingInterval + 100);
+        retval = UA_Client_run_iterate(client, 0);
+        ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+        ck_assert_uint_eq(notificationReceived, true);
+        ck_assert_uint_eq(createResult.revisedQueueSize, 1);
+
+        // delete the monitoredItem
+        UA_DeleteMonitoredItemsRequest deleteRequest;
+        UA_DeleteMonitoredItemsRequest_init(&deleteRequest);
+        deleteRequest.subscriptionId = subscriptionId;
+        deleteRequest.monitoredItemIds = &monitoredItemId;
+        deleteRequest.monitoredItemIdsSize = 1;
+
+        UA_DeleteMonitoredItemsResponse deleteResponse =
+                UA_Client_MonitoredItems_delete(client, deleteRequest);
+
+        ck_assert_uint_eq(deleteResponse.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
+        ck_assert_uint_eq(deleteResponse.resultsSize, 1);
+
+        UA_DeleteMonitoredItemsResponse_deleteMembers(&deleteResponse);
+    }
+END_TEST
+
+static void
+handler_events_propagate(UA_Client *lclient, UA_UInt32 subId, void *subContext,
+                         UA_UInt32 monId, void *monContext,
+                         size_t nEventFields, UA_Variant *eventFields) {
+    UA_Boolean foundSeverity = UA_FALSE;
+    UA_Boolean foundMessage = UA_FALSE;
+    UA_Boolean foundType = UA_FALSE;
+    UA_Boolean foundSource = UA_FALSE;
+    ck_assert_uint_eq(*(UA_UInt32 *) monContext, monitoredItemId);
+    ck_assert_uint_eq(nEventFields, nSelectClauses);
+    // check all event fields
+    for (unsigned int i = 0; i < nEventFields; i++) {
+        // find out which attribute of the event is being looked at
+        if (UA_Variant_hasScalarType(&eventFields[i], &UA_TYPES[UA_TYPES_UINT16])) {
+            // Severity
+            ck_assert_uint_eq(*((UA_UInt16 *) (eventFields[i].data)), 1000);
+            foundSeverity = UA_TRUE;
+        } else if (UA_Variant_hasScalarType(&eventFields[i], &UA_TYPES[UA_TYPES_LOCALIZEDTEXT])) {
+            // Message
+            UA_LocalizedText comp = UA_LOCALIZEDTEXT("en-US", "Generated Event");
+            ck_assert(UA_String_equal(&((UA_LocalizedText *) eventFields[i].data)->locale, &comp.locale));
+            ck_assert(UA_String_equal(&((UA_LocalizedText *) eventFields[i].data)->text, &comp.text));
+            foundMessage = UA_TRUE;
+        } else if (UA_Variant_hasScalarType(&eventFields[i], &UA_TYPES[UA_TYPES_NODEID])) {
+            // either SourceNode or EventType
+            UA_NodeId serverNameSpaceId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_NAMESPACES);
+            if (UA_NodeId_equal((UA_NodeId *)eventFields[i].data, &eventType)) {
+                // EventType
+                foundType = UA_TRUE;
+            } else if (UA_NodeId_equal((UA_NodeId *)eventFields[i].data, &serverNameSpaceId)) {
+                // SourceNode
+                foundSource = UA_TRUE;
+            } else {
+                ck_assert_msg(UA_FALSE, "NodeId doesn't match");
+            }
+        } else {
+            ck_assert_msg(UA_FALSE, "Field doesn't match");
+        }
+    }
+    ck_assert_uint_eq(foundMessage, UA_TRUE);
+    ck_assert_uint_eq(foundSeverity, UA_TRUE);
+    ck_assert_uint_eq(foundType, UA_TRUE);
+    ck_assert_uint_eq(foundSource, UA_TRUE);
+    notificationReceived = true;
+}
+
+START_TEST(uppropagation) {
+        // trigger first event
+        UA_NodeId eventNodeId;
+        UA_StatusCode retval = eventSetup(&eventNodeId);
+        ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+
+        //add a monitored item
+        UA_MonitoredItemCreateResult createResult = addMonitoredItem(handler_events_propagate);
+        ck_assert_uint_eq(createResult.statusCode, UA_STATUSCODE_GOOD);
+        // trigger the event on a child of server, using namespaces in this case (no reason in particular)
+        retval = UA_Server_triggerEvent(server, eventNodeId, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_NAMESPACES), NULL,
+                                        UA_TRUE);
+        ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+
+        // let the client fetch the event and check if the correct values were received
+        notificationReceived = false;
+        UA_fakeSleep((UA_UInt32) publishingInterval + 100);
+        retval = UA_Client_run_iterate(client, 0);
+        ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+        ck_assert_uint_eq(notificationReceived, true);
+        ck_assert_uint_eq(createResult.revisedQueueSize, 1);
+
+        // delete the monitoredItem
+        UA_DeleteMonitoredItemsRequest deleteRequest;
+        UA_DeleteMonitoredItemsRequest_init(&deleteRequest);
+        deleteRequest.subscriptionId = subscriptionId;
+        deleteRequest.monitoredItemIds = &monitoredItemId;
+        deleteRequest.monitoredItemIdsSize = 1;
+
+        UA_DeleteMonitoredItemsResponse deleteResponse =
+                UA_Client_MonitoredItems_delete(client, deleteRequest);
+
+        ck_assert_uint_eq(deleteResponse.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
+        ck_assert_uint_eq(deleteResponse.resultsSize, 1);
+
+        UA_DeleteMonitoredItemsResponse_deleteMembers(&deleteResponse);
+}
+END_TEST
+
+/*
+static void
+handler_events_overflow(UA_Client *lclient, UA_UInt32 subId, void *subContext,
+                        UA_UInt32 monId, void *monContext,
+                        size_t nEventFields, UA_Variant *eventFields) {
+    UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Event overflow was found");
+    ck_assert_uint_eq(*(UA_UInt32 *) monContext, monitoredItemId);
+    ck_assert_uint_eq(nEventFields, 1);
+    ck_assert(eventFields->type == &UA_TYPES[UA_TYPES_NODEID]);
+    UA_NodeId comp = UA_NODEID_NUMERIC(0, UA_NS0ID_SIMPLEOVERFLOWEVENTTYPE);
+    ck_assert((UA_NodeId_equal((UA_NodeId *)eventFields->data, &comp)));
+    notificationReceived = true;
+}
+
+// ensures an eventQueueOverflowEvent is published when appropriate
+START_TEST(eventOverflow)
+    {
+        // add a monitored item
+        UA_MonitoredItemCreateResult createResult = addMonitoredItem(handler_events_overflow);
+        ck_assert_uint_eq(createResult.statusCode, UA_STATUSCODE_GOOD);
+
+        // trigger first event
+        UA_NodeId eventNodeId;
+        UA_StatusCode retval = UA_STATUSCODE_GOOD;
+        for (int i = 0; i < 3; i++) {
+            retval = eventSetup(&eventNodeId);
+            ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+            retval = UA_Server_triggerEvent(server, eventNodeId, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER), NULL, UA_TRUE);
+            ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+        }
+
+
+        // fetch the events
+        notificationReceived = false;
+        UA_fakeSleep((UA_UInt32) publishingInterval + 100);
+        retval = UA_Client_run_iterate(client, 0);
+        ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+        ck_assert_uint_eq(notificationReceived, true);
+        ck_assert_uint_eq(createResult.revisedQueueSize, 1);
+
+        // delete the monitoredItem
+        UA_DeleteMonitoredItemsRequest deleteRequest;
+        UA_DeleteMonitoredItemsRequest_init(&deleteRequest);
+        deleteRequest.subscriptionId = subscriptionId;
+        deleteRequest.monitoredItemIds = &monitoredItemId;
+        deleteRequest.monitoredItemIdsSize = 1;
+
+        UA_DeleteMonitoredItemsResponse deleteResponse =
+                UA_Client_MonitoredItems_delete(client, deleteRequest);
+
+        ck_assert_uint_eq(deleteResponse.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
+        ck_assert_uint_eq(deleteResponse.resultsSize, 1);
+
+        UA_DeleteMonitoredItemsResponse_deleteMembers(&deleteResponse);
+    }
+END_TEST
+*/
+
+#endif // UA_ENABLE_SUBSCRIPTIONS_EVENTS
+
+// assumes subscriptions work fine with data change because of other unit test
+static Suite *testSuite_Client(void) {
+    Suite *s = suite_create("Server Subscription Events");
+    TCase *tc_server = tcase_create("Server Subscription Events");
+#ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
+    tcase_add_checked_fixture(tc_server, setup, teardown);
+    tcase_add_test(tc_server, generateEvents);
+    tcase_add_test(tc_server, uppropagation);
+//    tcase_add_test(tc_server, eventOverflow);
+#endif // UA_ENABLE_SUBSCRIPTIONS_EVENTS
+    suite_add_tcase(s, tc_server);
+
+    return s;
+}
+
+int main(void) {
+    Suite *s = testSuite_Client();
+    SRunner *sr = srunner_create(s);
+    srunner_set_fork_status(sr, CK_NOFORK);
+    srunner_run_all(sr, CK_NORMAL);
+    int number_failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+    return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+

+ 19 - 19
tests/testing-plugins/testing_policy.h

@@ -13,31 +13,31 @@ extern "C" {
 #include "ua_plugin_log.h"
 
 typedef struct funcs_called {
-    bool asym_enc;
-    bool asym_dec;
+    UA_Boolean asym_enc;
+    UA_Boolean asym_dec;
 
-    bool sym_enc;
-    bool sym_dec;
+    UA_Boolean sym_enc;
+    UA_Boolean sym_dec;
 
-    bool asym_sign;
-    bool asym_verify;
+    UA_Boolean asym_sign;
+    UA_Boolean asym_verify;
 
-    bool sym_sign;
-    bool sym_verify;
+    UA_Boolean sym_sign;
+    UA_Boolean sym_verify;
 
-    bool newContext;
-    bool deleteContext;
+    UA_Boolean newContext;
+    UA_Boolean deleteContext;
 
-    bool makeCertificateThumbprint;
-    bool generateKey;
-    bool generateNonce;
+    UA_Boolean makeCertificateThumbprint;
+    UA_Boolean generateKey;
+    UA_Boolean generateNonce;
 
-    bool setLocalSymEncryptingKey;
-    bool setLocalSymSigningKey;
-    bool setLocalSymIv;
-    bool setRemoteSymEncryptingKey;
-    bool setRemoteSymSigningKey;
-    bool setRemoteSymIv;
+    UA_Boolean setLocalSymEncryptingKey;
+    UA_Boolean setLocalSymSigningKey;
+    UA_Boolean setLocalSymIv;
+    UA_Boolean setRemoteSymEncryptingKey;
+    UA_Boolean setRemoteSymSigningKey;
+    UA_Boolean setRemoteSymIv;
 } funcs_called;
 
 typedef struct key_sizes {

+ 1 - 2
tools/appveyor/build.ps1

@@ -1,4 +1,3 @@
-$ErrorActionPreference = "Stop"
 
 try {
     cd $env:APPVEYOR_BUILD_FOLDER
@@ -71,7 +70,7 @@ try {
     Write-Host -ForegroundColor Green "`n##### Testing $env:CC_NAME with full NS0 #####`n"
     New-Item -ItemType directory -Path "build"
     cd build
-    & cmake -DUA_BUILD_EXAMPLES:BOOL=ON -DUA_ENABLE_FULL_NS0:BOOL=ON -DUA_COMPILE_AS_CXX:BOOL=$env:FORCE_CXX -G"$env:CC_NAME" ..
+    & cmake -DUA_ENABLE_SUBSCRIPTIONS_EVENTS:BOOL=ON -DUA_BUILD_EXAMPLES:BOOL=ON -DUA_ENABLE_FULL_NS0:BOOL=ON -DUA_COMPILE_AS_CXX:BOOL=$env:FORCE_CXX -G"$env:CC_NAME"  ..
     Invoke-Expression $make_cmd
     if ($LASTEXITCODE -and $LASTEXITCODE -ne 0) {
         Write-Host -ForegroundColor Red "`n`n*** Make failed. Exiting ... ***"

+ 10 - 38
tools/certs/create_self-signed.py

@@ -25,49 +25,21 @@ os.environ['HOSTNAME'] = socket.gethostname()
 openssl_conf = os.path.join(certsdir, "localhost.cnf")
 
 os.chdir(os.path.abspath(sys.argv[1]))
-os.system("""openssl genrsa -out ca.key {}""".format(keysize))
-os.system("""openssl req \
-    -x509 \
-    -new \
-    -nodes \
-    -key ca.key \
-    -days 3650 \
-    -subj "/C=DE/O=open62541/CN=open62541.org" \
-    -out ca.crt""")
+
 os.system("""openssl req \
-    -new \
-    -newkey rsa:{} \
-    -nodes \
-    -subj "/C=DE/O=open62541/CN=open62541Server@localhost" \
-    -keyout localhost.key \
-    -out localhost.csr""".format(keysize))
-os.system("""openssl x509 -req \
-    -days 3650 \
-    -in localhost.csr \
-    -CA ca.crt \
-    -CAkey ca.key \
-    -CAcreateserial \
-    -out localhost.crt \
-    -extfile {} \
-    -extensions v3_ca""".format(openssl_conf))
+     -config {} \
+     -new \
+     -nodes \
+     -x509 -sha256  \
+     -newkey rsa:{} \
+     -keyout localhost.key -days 365 \
+     -subj "/C=DE/O=open62541/CN=open62541Server@localhost"\
+     -out localhost.crt""".format(openssl_conf, keysize))
+
 os.system("openssl x509 -in localhost.crt -outform der -out server_cert.der")
 os.system("openssl rsa -inform PEM -in localhost.key -outform DER -out server_key.der")
-# Convert certificate authority(CA) file 'ca.crt' into DER encoded form
-# to provide as trust list input
-os.system("openssl x509 -in ca.crt -outform der -out ca_cert.der")
 
 os.remove("localhost.key")
 os.remove("localhost.crt")
-os.remove("localhost.csr")
-os.remove("ca.srl")
-# os.remove("ca.key")
-# os.remove("ca.crt")
-
-# if os.path.isfile(os.path.join(sys.argv[1], "server_cert.der")):
-# 	os.remove(os.path.join(sys.argv[1], "server_cert.der"))
-# shutil.move("server_cert.der", sys.argv[1])
-# if os.path.isfile(os.path.join(sys.argv[1], "ca.crt")):
-# 	os.remove(os.path.join(sys.argv[1], "ca.crt"))
-# shutil.move("ca.crt", sys.argv[1])
 
 print("Certificates generated in " + sys.argv[1])

+ 1 - 1
tools/certs/localhost.cnf

@@ -254,7 +254,7 @@ basicConstraints = CA:false
 # left out by default.
 # keyUsage = cRLSign, keyCertSign
 
-keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment, keyCertSign
 extendedKeyUsage = TLS Web Server Authentication, TLS Web Client Authentication
 
 # Some might want this also

+ 8 - 0
tools/cmake/FindMbedTLS.cmake

@@ -1,3 +1,11 @@
+#check environment variable
+if("$ENV{MBEDTLS_FOLDER_INCLUDE}")
+    set(MBEDTLS_FOLDER_INCLUDE "$ENV{MBEDTLS_FOLDER_INCLUDE}")
+endif()
+if("$ENV{MBEDTLS_FOLDER_LIBRARY}")
+    set(MBEDTLS_FOLDER_LIBRARY "$ENV{MBEDTLS_FOLDER_LIBRARY}")
+endif()
+
 find_path(MBEDTLS_INCLUDE_DIRS mbedtls/ssl.h HINTS ${MBEDTLS_FOLDER_INCLUDE})
 
 if(UA_BUILD_OSS_FUZZ)

+ 8 - 8
tools/generate_nodeid_descriptions.py

@@ -15,6 +15,7 @@ from io import open
 parser = argparse.ArgumentParser()
 parser.add_argument('statuscodes', help='path/to/Opc.Ua.NodeIds.csv')
 parser.add_argument('outfile', help='outfile w/o extension')
+parser.add_argument('namespace', help='NS0')
 args = parser.parse_args()
 
 rows = []
@@ -33,23 +34,22 @@ def printh(string):
 
 printh(u'''/*---------------------------------------------------------
  * Autogenerated -- do not modify
- * Generated from %s with script %s
+ * Generated from {0} with script {1}
  *-------------------------------------------------------*/
 
-#ifndef UA_NODEIDS_H_
-#define UA_NODEIDS_H_
+#ifndef UA_NODEIDS_{2}_H_
+#define UA_NODEIDS_{2}_H_
 
 /**
  * Namespace Zero NodeIds
  * ----------------------
  * Numeric identifiers of standard-defined nodes in namespace zero. The
- * following definitions are autogenerated from the ``NodeIds.csv`` file
- * provided with the OPC UA standard. */
-''' % (args.statuscodes, sys.argv[0]))
+ * following definitions are autogenerated from the ``{0}`` file */
+'''.format(args.statuscodes, sys.argv[0], args.namespace))
 
 for row in rows:
-    printh(u"#define UA_NS0ID_%s %s /* %s */" % (row[0].upper(), row[1], row[2]))
+    printh(u"#define UA_{namespace}ID_{name} {id} /* {description} */".format(namespace=args.namespace, name=row[0].upper(), id=row[1], description=row[2]))
 
-printh(u'''#endif /* UA_NODEIDS_H_ */ ''')
+printh(u'''#endif /* UA_NODEIDS_{0}_H_ */ '''.format(args.namespace))
 
 fh.close()

+ 0 - 1
tools/nodeset_compiler/backend_open62541.py

@@ -30,7 +30,6 @@ except ImportError:
 
 logger = logging.getLogger(__name__)
 
-from constants import *
 from nodes import *
 from nodeset import *
 from backend_open62541_nodes import generateNodeCode_begin, generateNodeCode_finish, generateReferenceCode

+ 10 - 10
tools/nodeset_compiler/backend_open62541_nodes.py

@@ -320,14 +320,14 @@ def generateValueCode(node, parentNode, nodeset, bootstrapping=True, max_string_
                                        or (len(node.value) > 1
                                            and (parentNode.valueRank != -2 or parentNode.valueRank != -3))):
         # User the following strategy for all directly mappable values a la 'UA_Type MyInt = (UA_Type) 23;'
-        if node.value[0].numericRepresentation == BUILTINTYPE_TYPEID_GUID:
+        if isinstance(node.value[0], Guid):
             logger.warn("Don't know how to print array of GUID in node " + str(parentNode.id))
-        elif node.value[0].numericRepresentation == BUILTINTYPE_TYPEID_DIAGNOSTICINFO:
+        elif isinstance(node.value[0], DiagnosticInfo):
             logger.warn("Don't know how to print array of DiagnosticInfo in node " + str(parentNode.id))
-        elif node.value[0].numericRepresentation == BUILTINTYPE_TYPEID_STATUSCODE:
+        elif isinstance(node.value[0], StatusCode):
             logger.warn("Don't know how to print array of StatusCode in node " + str(parentNode.id))
         else:
-            if node.value[0].numericRepresentation == BUILTINTYPE_TYPEID_EXTENSIONOBJECT:
+            if isinstance(node.value[0], ExtensionObject):
                 for idx, v in enumerate(node.value):
                     logger.debug("Building extObj array index " + str(idx))
                     [code1, codeCleanup1] = generateExtensionObjectSubtypeCode(v, parent=parentNode, nodeset=nodeset, arrayIndex=idx, max_string_length=max_string_length,
@@ -335,7 +335,7 @@ def generateValueCode(node, parentNode, nodeset, bootstrapping=True, max_string_
                     code = code + code1
                     codeCleanup = codeCleanup + codeCleanup1
             code.append("UA_" + node.value[0].__class__.__name__ + " " + valueName + "[" + str(len(node.value)) + "];")
-            if node.value[0].numericRepresentation == BUILTINTYPE_TYPEID_EXTENSIONOBJECT:
+            if isinstance(node.value[0], ExtensionObject):
                 for idx, v in enumerate(node.value):
                     logger.debug("Printing extObj array index " + str(idx))
                     instanceName = generateNodeValueInstanceName(v, parentNode, 0, idx)
@@ -353,21 +353,21 @@ def generateValueCode(node, parentNode, nodeset, bootstrapping=True, max_string_
                         getTypesArrayForValue(nodeset, node.value[0]) + ");")
     else:
         # User the following strategy for all directly mappable values a la 'UA_Type MyInt = (UA_Type) 23;'
-        if node.value[0].numericRepresentation == BUILTINTYPE_TYPEID_GUID:
+        if isinstance(node.value[0], Guid):
             logger.warn("Don't know how to print scalar GUID in node " + str(parentNode.id))
-        elif node.value[0].numericRepresentation == BUILTINTYPE_TYPEID_DIAGNOSTICINFO:
+        elif isinstance(node.value[0], DiagnosticInfo):
             logger.warn("Don't know how to print scalar DiagnosticInfo in node " + str(parentNode.id))
-        elif node.value[0].numericRepresentation == BUILTINTYPE_TYPEID_STATUSCODE:
+        elif isinstance(node.value[0], StatusCode):
             logger.warn("Don't know how to print scalar StatusCode in node " + str(parentNode.id))
         else:
             # The following strategy applies to all other types, in particular strings and numerics.
-            if node.value[0].numericRepresentation == BUILTINTYPE_TYPEID_EXTENSIONOBJECT:
+            if isinstance(node.value[0], ExtensionObject):
                 [code1, codeCleanup1] = generateExtensionObjectSubtypeCode(node.value[0], parent=parentNode, nodeset=nodeset, max_string_length=max_string_length,
                                                                            encode_binary_size=encode_binary_size)
                 code = code + code1
                 codeCleanup = codeCleanup + codeCleanup1
             instanceName = generateNodeValueInstanceName(node.value[0], parentNode, 0, 0)
-            if node.value[0].numericRepresentation == BUILTINTYPE_TYPEID_EXTENSIONOBJECT:
+            if isinstance(node.value[0], ExtensionObject):
                 code.append("UA_" + node.value[0].__class__.__name__ + " *" + valueName + " = " +
                             generateNodeValueCode(node.value[0], instanceName, max_string_length=max_string_length) + ";")
                 code.append(

+ 0 - 64
tools/nodeset_compiler/constants.py

@@ -1,64 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-###
-### Author:  Chris Iatrou (ichrispa@core-vector.net)
-### Version: rev 13
-###
-### This program was created for educational purposes and has been
-### contributed to the open62541 project by the author. All licensing
-### terms for this source is inherited by the terms and conditions
-### specified for by the open62541 project (see the projects readme
-### file for more information on the MPLv2 terms and restrictions).
-###
-### This program is not meant to be used in a production environment. The
-### author is not liable for any complications arising due to the use of
-### this program.
-###
-
-NODE_CLASS_GENERERIC = 0
-NODE_CLASS_OBJECT = 1
-NODE_CLASS_VARIABLE = 2
-NODE_CLASS_METHOD = 4
-NODE_CLASS_OBJECTTYPE = 8
-NODE_CLASS_VARIABLETYPE = 16
-NODE_CLASS_REFERENCETYPE = 32
-NODE_CLASS_DATATYPE = 64
-NODE_CLASS_VIEW = 128
-
-# Not in OPC-UA, but exists in XML
-NODE_CLASS_METHODTYPE = 256
-
-##
-## Numeric codes used to encode binary type fields:
-##
-BUILTINTYPE_TYPEID_EXTENSIONOBJECT = 1
-BUILTINTYPE_TYPEID_LOCALIZEDTEXT = 2
-BUILTINTYPE_TYPEID_EXPANDEDNODEID = 3
-BUILTINTYPE_TYPEID_NODEID = 4
-BUILTINTYPE_TYPEID_DATETIME = 5
-BUILTINTYPE_TYPEID_QUALIFIEDNAME = 6
-BUILTINTYPE_TYPEID_STATUSCODE = 7
-BUILTINTYPE_TYPEID_GUID = 8
-BUILTINTYPE_TYPEID_BOOLEAN = 9
-BUILTINTYPE_TYPEID_BYTE = 10
-BUILTINTYPE_TYPEID_SBYTE = 11
-BUILTINTYPE_TYPEID_INT16 = 12
-BUILTINTYPE_TYPEID_UINT16 = 13
-BUILTINTYPE_TYPEID_INT32 = 14
-BUILTINTYPE_TYPEID_UINT32 = 15
-BUILTINTYPE_TYPEID_INT64 = 16
-BUILTINTYPE_TYPEID_UINT64 = 17
-BUILTINTYPE_TYPEID_FLOAT = 18
-BUILTINTYPE_TYPEID_DOUBLE = 19
-BUILTINTYPE_TYPEID_STRING = 20
-BUILTINTYPE_TYPEID_XMLELEMENT = 21
-BUILTINTYPE_TYPEID_BYTESTRING = 22
-BUILTINTYPE_TYPEID_DIAGNOSTICINFO = 23
-BUILTINTYPE_TYPEID_NUMBER = 24
-BUILTINTYPE_TYPEID_UINTEGER = 25
-BUILTINTYPE_TYPEID_INTEGER = 26

+ 2 - 39
tools/nodeset_compiler/datatypes.py

@@ -23,7 +23,6 @@ from datetime import datetime
 logger = logging.getLogger(__name__)
 import xml.dom.minidom as dom
 
-from constants import *
 from base64 import *
 
 import six
@@ -52,7 +51,6 @@ def valueIsInternalType(valueTypeString):
 class Value(object):
     def __init__(self, xmlelement=None):
         self.value = None
-        self.numericRepresentation = 0
         self.alias = None
         self.dataType = None
         self.encodingRule = []
@@ -293,7 +291,6 @@ class Value(object):
 class Boolean(Value):
     def __init__(self, xmlelement=None):
         Value.__init__(self)
-        self.numericRepresentation = BUILTINTYPE_TYPEID_BOOLEAN
         if xmlelement:
             self.parseXML(xmlelement)
 
@@ -312,7 +309,6 @@ class Boolean(Value):
 class Number(Value):
     def __init__(self, xmlelement=None):
         Value.__init__(self)
-        self.numericRepresentation = BUILTINTYPE_TYPEID_NUMBER
         if xmlelement:
             self.parseXML(xmlelement)
 
@@ -328,77 +324,66 @@ class Number(Value):
 class Integer(Number):
     def __init__(self, xmlelement=None):
         Value.__init__(self)
-        self.numericRepresentation = BUILTINTYPE_TYPEID_INTEGER
         if xmlelement:
             self.parseXML(xmlelement)
 
 class UInteger(Number):
     def __init__(self, xmlelement=None):
         Value.__init__(self)
-        self.numericRepresentation = BUILTINTYPE_TYPEID_UINTEGER
         if xmlelement:
             self.parseXML(xmlelement)
 
 class Byte(UInteger):
     def __init__(self, xmlelement=None):
         Value.__init__(self)
-        self.numericRepresentation = BUILTINTYPE_TYPEID_BYTE
         if xmlelement:
             self.parseXML(xmlelement)
 
 class SByte(Integer):
     def __init__(self, xmlelement=None):
         Value.__init__(self)
-        self.numericRepresentation = BUILTINTYPE_TYPEID_SBYTE
         if xmlelement:
             self.parseXML(xmlelement)
 
 class Int16(Integer):
     def __init__(self, xmlelement=None):
         Value.__init__(self)
-        self.numericRepresentation = BUILTINTYPE_TYPEID_INT16
         if xmlelement:
             self.parseXML(xmlelement)
 
 class UInt16(UInteger):
     def __init__(self, xmlelement=None):
         Value.__init__(self)
-        self.numericRepresentation = BUILTINTYPE_TYPEID_UINT16
         if xmlelement:
             self.parseXML(xmlelement)
 
 class Int32(Integer):
     def __init__(self, xmlelement=None):
         Value.__init__(self)
-        self.numericRepresentation = BUILTINTYPE_TYPEID_INT32
         if xmlelement:
             self.parseXML(xmlelement)
 
 class UInt32(UInteger):
     def __init__(self, xmlelement=None):
         Value.__init__(self)
-        self.numericRepresentation = BUILTINTYPE_TYPEID_UINT32
         if xmlelement:
             self.parseXML(xmlelement)
 
 class Int64(Integer):
     def __init__(self, xmlelement=None):
         Value.__init__(self)
-        self.numericRepresentation = BUILTINTYPE_TYPEID_INT64
         if xmlelement:
             self.parseXML(xmlelement)
 
 class UInt64(UInteger):
     def __init__(self, xmlelement=None):
         Value.__init__(self)
-        self.numericRepresentation = BUILTINTYPE_TYPEID_UINT64
         if xmlelement:
             self.parseXML(xmlelement)
 
 class Float(Number):
     def __init__(self, xmlelement=None):
         Value.__init__(self)
-        self.numericRepresentation = BUILTINTYPE_TYPEID_FLOAT
         if xmlelement:
             self.parseXML(xmlelement)
 
@@ -414,14 +399,12 @@ class Float(Number):
 class Double(Float):
     def __init__(self, xmlelement=None):
         Value.__init__(self)
-        self.numericRepresentation = BUILTINTYPE_TYPEID_DOUBLE
         if xmlelement:
             self.parseXML(xmlelement)
 
 class String(Value):
     def __init__(self, xmlelement=None):
         Value.__init__(self)
-        self.numericRepresentation = BUILTINTYPE_TYPEID_STRING
         if xmlelement:
             self.parseXML(xmlelement)
 
@@ -445,12 +428,10 @@ class String(Value):
 class XmlElement(String):
     def __init__(self, xmlelement=None):
         Value.__init__(self, xmlelement)
-        self.numericRepresentation = BUILTINTYPE_TYPEID_XMLELEMENT
 
 class ByteString(Value):
     def __init__(self, xmlelement=None):
         Value.__init__(self, xmlelement)
-        self.numericRepresentation = BUILTINTYPE_TYPEID_BYTESTRING
 
     def parseXML(self, xmlvalue):
         # Expect <ByteString>value</ByteString>
@@ -466,7 +447,6 @@ class ByteString(Value):
 class ExtensionObject(Value):
     def __init__(self, xmlelement=None):
         Value.__init__(self)
-        self.numericRepresentation = BUILTINTYPE_TYPEID_EXTENSIONOBJECT
         if xmlelement:
             self.parseXML(xmlelement)
 
@@ -474,12 +454,11 @@ class ExtensionObject(Value):
         pass
 
     def __str__(self):
-        return "'" + self.alias() + "':" + self.stringRepresentation + "(" + str(self.value) + ")"
+        return "'ExtensionObject'"
 
 class LocalizedText(Value):
     def __init__(self, xmlvalue=None):
         Value.__init__(self)
-        self.numericRepresentation = BUILTINTYPE_TYPEID_LOCALIZEDTEXT
         self.locale = ''
         self.text = ''
         if xmlvalue:
@@ -510,7 +489,6 @@ class LocalizedText(Value):
 class NodeId(Value):
     def __init__(self, idstring=None):
         Value.__init__(self)
-        self.numericRepresentation = BUILTINTYPE_TYPEID_NODEID
         self.i = None
         self.b = None
         self.g = None
@@ -555,19 +533,10 @@ class NodeId(Value):
         #           </Identifier>
         #        </NodeId> or </Alias>
         if not isinstance(xmlvalue, dom.Element):
-            self.text = xmlvalue
+            self.text = xmlvalue # Alias
             return
         self.checkXML(xmlvalue)
 
-        if self.alias != None:
-            if not self.alias == xmlvalue.localName:
-                logger.warn(
-                    "Expected an aliased XML field called " + self.alias + " but got " + xmlvalue.localName + " instead. This is a parsing error of Value.__parseXMLSingleValue(), will try to continue anyway.")
-        else:
-            if not self.stringRepresentation == xmlvalue.localName:
-                logger.warn(
-                    "Expected XML field " + self.stringRepresentation + " but got " + xmlvalue.localName + " instead. This is a parsing error of Value.__parseXMLSingleValue(), will try to continue anyway.")
-
         # Catch XML <NodeId />
         if xmlvalue.firstChild == None:
             logger.error("No value is given, which is illegal for Node Types...")
@@ -608,7 +577,6 @@ class NodeId(Value):
 class ExpandedNodeId(Value):
     def __init__(self, xmlelement=None):
         Value.__init__(self)
-        self.numericRepresentation = BUILTINTYPE_TYPEID_EXPANDEDNODEID
         if xmlelement:
             self.parseXML(xmlelement)
 
@@ -619,7 +587,6 @@ class ExpandedNodeId(Value):
 class DateTime(Value):
     def __init__(self, xmlelement=None):
         Value.__init__(self)
-        self.numericRepresentation = BUILTINTYPE_TYPEID_DATETIME
         if xmlelement:
             self.parseXML(xmlelement)
 
@@ -653,7 +620,6 @@ class DateTime(Value):
 class QualifiedName(Value):
     def __init__(self, xmlelement=None):
         Value.__init__(self)
-        self.numericRepresentation = BUILTINTYPE_TYPEID_QUALIFIEDNAME
         self.ns = 0
         self.name = ''
         if xmlelement:
@@ -686,12 +652,10 @@ class QualifiedName(Value):
 class StatusCode(UInt32):
     def __init__(self, xmlelement=None):
         Value.__init__(self, xmlelement)
-        self.numericRepresentation = BUILTINTYPE_TYPEID_STATUSCODE
 
 class DiagnosticInfo(Value):
     def __init__(self, xmlelement=None):
         Value.__init__(self)
-        self.numericRepresentation = BUILTINTYPE_TYPEID_DIAGNOSTICINFO
         if xmlelement:
             self.parseXML(xmlelement)
 
@@ -702,7 +666,6 @@ class DiagnosticInfo(Value):
 class Guid(Value):
     def __init__(self, xmlelement=None):
         Value.__init__(self)
-        self.numericRepresentation = BUILTINTYPE_TYPEID_GUID
         if xmlelement:
             self.parseXML(xmlelement)
 

+ 0 - 10
tools/nodeset_compiler/nodes.py

@@ -19,7 +19,6 @@
 import sys
 import logging
 from datatypes import *
-from constants import *
 
 logger = logging.getLogger(__name__)
 
@@ -63,7 +62,6 @@ def RefOrAlias(s):
 class Node(object):
     def __init__(self):
         self.id = NodeId()
-        self.nodeClass = NODE_CLASS_GENERERIC
         self.browseName = QualifiedName()
         self.displayName = LocalizedText()
         self.description = LocalizedText()
@@ -188,7 +186,6 @@ class Node(object):
 class ReferenceTypeNode(Node):
     def __init__(self, xmlelement=None):
         Node.__init__(self)
-        self.nodeClass = NODE_CLASS_REFERENCETYPE
         self.isAbstract = False
         self.symmetric = False
         self.inverseName = ""
@@ -213,7 +210,6 @@ class ReferenceTypeNode(Node):
 class ObjectNode(Node):
     def __init__(self, xmlelement=None):
         Node.__init__(self)
-        self.nodeClass = NODE_CLASS_OBJECT
         self.eventNotifier = 0
         if xmlelement:
             self.parseXML(xmlelement)
@@ -227,7 +223,6 @@ class ObjectNode(Node):
 class VariableNode(Node):
     def __init__(self, xmlelement=None):
         Node.__init__(self)
-        self.nodeClass = NODE_CLASS_VARIABLE
         self.dataType = NodeId()
         self.valueRank = -2
         self.arrayDimensions = []
@@ -303,7 +298,6 @@ class VariableNode(Node):
 class VariableTypeNode(VariableNode):
     def __init__(self, xmlelement=None):
         VariableNode.__init__(self)
-        self.nodeClass = NODE_CLASS_VARIABLETYPE
         self.isAbstract = False
         if xmlelement:
             self.parseXML(xmlelement)
@@ -317,7 +311,6 @@ class VariableTypeNode(VariableNode):
 class MethodNode(Node):
     def __init__(self, xmlelement=None):
         Node.__init__(self)
-        self.nodeClass = NODE_CLASS_METHOD
         self.executable = True
         self.userExecutable = True
         self.methodDecalaration = None
@@ -337,7 +330,6 @@ class MethodNode(Node):
 class ObjectTypeNode(Node):
     def __init__(self, xmlelement=None):
         Node.__init__(self)
-        self.nodeClass = NODE_CLASS_OBJECTTYPE
         self.isAbstract = False
         if xmlelement:
             self.parseXML(xmlelement)
@@ -382,7 +374,6 @@ class DataTypeNode(Node):
 
     def __init__(self, xmlelement=None):
         Node.__init__(self)
-        self.nodeClass = NODE_CLASS_DATATYPE
         self.isAbstract = False
         self.__xmlDefinition__ = None
         self.__baseTypeEncoding__ = []
@@ -615,7 +606,6 @@ class DataTypeNode(Node):
 class ViewNode(Node):
     def __init__(self, xmlelement=None):
         Node.__init__(self)
-        self.nodeClass = NODE_CLASS_VIEW
         self.containsNoLoops == False
         self.eventNotifier == False
         if xmlelement:

+ 134 - 0
tools/schema/Opc.Ua.NodeSet2.Minimal.xml

@@ -1202,6 +1202,7 @@
       <Reference ReferenceType="HasProperty">i=3704</Reference>
       <Reference ReferenceType="HasComponent">i=2996</Reference>
       <Reference ReferenceType="HasComponent">i=2997</Reference>
+      <Reference ReferenceType="HasComponent">i=11192</Reference>
       <Reference ReferenceType="HasTypeDefinition">i=2013</Reference>
       <Reference ReferenceType="HasComponent" IsForward="false">i=2253</Reference>
     </References>
@@ -2059,4 +2060,137 @@ PC9vcGM6VHlwZURpY3Rpb25hcnk+</ByteString>
       <String xmlns="http://opcfoundation.org/UA/2008/02/Types.xsd">EnumValueType</String>
     </Value>
   </UAVariable>
+  <UAObjectType NodeId="i=2330" BrowseName="HistoryServerCapabilitiesType">
+    <DisplayName>HistoryServerCapabilitiesType</DisplayName>
+    <References>
+      <Reference ReferenceType="HasSubtype" IsForward="false">i=58</Reference>
+    </References>
+  </UAObjectType>
+  <UAObject NodeId="i=11192" BrowseName="HistoryServerCapabilities">
+    <DisplayName>HistoryServerCapabilities</DisplayName>
+    <References>
+      <Reference ReferenceType="HasProperty">i=11193</Reference>
+      <Reference ReferenceType="HasProperty">i=11242</Reference>
+      <Reference ReferenceType="HasProperty">i=11273</Reference>
+      <Reference ReferenceType="HasProperty">i=11274</Reference>
+      <Reference ReferenceType="HasProperty">i=11196</Reference>
+      <Reference ReferenceType="HasProperty">i=11197</Reference>
+      <Reference ReferenceType="HasProperty">i=11198</Reference>
+      <Reference ReferenceType="HasProperty">i=11199</Reference>
+      <Reference ReferenceType="HasProperty">i=11200</Reference>
+      <Reference ReferenceType="HasProperty">i=11281</Reference>
+      <Reference ReferenceType="HasProperty">i=11282</Reference>
+      <Reference ReferenceType="HasProperty">i=11283</Reference>
+      <Reference ReferenceType="HasProperty">i=11502</Reference>
+      <Reference ReferenceType="HasProperty">i=11275</Reference>
+      <Reference ReferenceType="HasComponent">i=11201</Reference>
+      <Reference ReferenceType="HasComponent" IsForward="false">i=2268</Reference>
+      <Reference ReferenceType="HasTypeDefinition">i=2330</Reference>
+    </References>
+  </UAObject>
+  <UAVariable NodeId="i=11193" BrowseName="AccessHistoryDataCapability" ParentNodeId="i=11192" DataType="Boolean">
+    <DisplayName>AccessHistoryDataCapability</DisplayName>
+    <References>
+      <Reference ReferenceType="HasTypeDefinition">i=68</Reference>
+      <Reference ReferenceType="HasProperty" IsForward="false">i=11192</Reference>
+    </References>
+  </UAVariable>
+  <UAVariable NodeId="i=11242" BrowseName="AccessHistoryEventsCapability" ParentNodeId="i=11192" DataType="Boolean">
+    <DisplayName>AccessHistoryEventsCapability</DisplayName>
+    <References>
+      <Reference ReferenceType="HasTypeDefinition">i=68</Reference>
+      <Reference ReferenceType="HasProperty" IsForward="false">i=11192</Reference>
+    </References>
+  </UAVariable>
+  <UAVariable NodeId="i=11273" BrowseName="MaxReturnDataValues" ParentNodeId="i=11192" DataType="UInt32">
+    <DisplayName>MaxReturnDataValues</DisplayName>
+    <References>
+      <Reference ReferenceType="HasTypeDefinition">i=68</Reference>
+      <Reference ReferenceType="HasProperty" IsForward="false">i=11192</Reference>
+    </References>
+  </UAVariable>
+  <UAVariable NodeId="i=11274" BrowseName="MaxReturnEventValues" ParentNodeId="i=11192" DataType="UInt32">
+    <DisplayName>MaxReturnEventValues</DisplayName>
+    <References>
+      <Reference ReferenceType="HasTypeDefinition">i=68</Reference>
+      <Reference ReferenceType="HasProperty" IsForward="false">i=11192</Reference>
+    </References>
+  </UAVariable>
+  <UAVariable NodeId="i=11196" BrowseName="InsertDataCapability" ParentNodeId="i=11192" DataType="Boolean">
+    <DisplayName>InsertDataCapability</DisplayName>
+    <References>
+      <Reference ReferenceType="HasTypeDefinition">i=68</Reference>
+      <Reference ReferenceType="HasProperty" IsForward="false">i=11192</Reference>
+    </References>
+  </UAVariable>
+  <UAVariable NodeId="i=11197" BrowseName="ReplaceDataCapability" ParentNodeId="i=11192" DataType="Boolean">
+    <DisplayName>ReplaceDataCapability</DisplayName>
+    <References>
+      <Reference ReferenceType="HasTypeDefinition">i=68</Reference>
+      <Reference ReferenceType="HasProperty" IsForward="false">i=11192</Reference>
+    </References>
+  </UAVariable>
+  <UAVariable NodeId="i=11198" BrowseName="UpdateDataCapability" ParentNodeId="i=11192" DataType="Boolean">
+    <DisplayName>UpdateDataCapability</DisplayName>
+    <References>
+      <Reference ReferenceType="HasTypeDefinition">i=68</Reference>
+      <Reference ReferenceType="HasProperty" IsForward="false">i=11192</Reference>
+    </References>
+  </UAVariable>
+  <UAVariable NodeId="i=11199" BrowseName="DeleteRawCapability" ParentNodeId="i=11192" DataType="Boolean">
+    <DisplayName>DeleteRawCapability</DisplayName>
+    <References>
+      <Reference ReferenceType="HasTypeDefinition">i=68</Reference>
+      <Reference ReferenceType="HasProperty" IsForward="false">i=11192</Reference>
+    </References>
+  </UAVariable>
+  <UAVariable NodeId="i=11200" BrowseName="DeleteAtTimeCapability" ParentNodeId="i=11192" DataType="Boolean">
+    <DisplayName>DeleteAtTimeCapability</DisplayName>
+    <References>
+      <Reference ReferenceType="HasTypeDefinition">i=68</Reference>
+      <Reference ReferenceType="HasProperty" IsForward="false">i=11192</Reference>
+    </References>
+  </UAVariable>
+  <UAVariable NodeId="i=11281" BrowseName="InsertEventCapability" ParentNodeId="i=11192" DataType="Boolean">
+    <DisplayName>InsertEventCapability</DisplayName>
+    <References>
+      <Reference ReferenceType="HasTypeDefinition">i=68</Reference>
+      <Reference ReferenceType="HasProperty" IsForward="false">i=11192</Reference>
+    </References>
+  </UAVariable>
+  <UAVariable NodeId="i=11282" BrowseName="ReplaceEventCapability" ParentNodeId="i=11192" DataType="Boolean">
+    <DisplayName>ReplaceEventCapability</DisplayName>
+    <References>
+      <Reference ReferenceType="HasTypeDefinition">i=68</Reference>
+      <Reference ReferenceType="HasProperty" IsForward="false">i=11192</Reference>
+    </References>
+  </UAVariable>
+  <UAVariable NodeId="i=11283" BrowseName="UpdateEventCapability" ParentNodeId="i=11192" DataType="Boolean">
+    <DisplayName>UpdateEventCapability</DisplayName>
+    <References>
+      <Reference ReferenceType="HasTypeDefinition">i=68</Reference>
+      <Reference ReferenceType="HasProperty" IsForward="false">i=11192</Reference>
+    </References>
+  </UAVariable>
+  <UAVariable NodeId="i=11502" BrowseName="DeleteEventCapability" ParentNodeId="i=11192" DataType="Boolean">
+    <DisplayName>DeleteEventCapability</DisplayName>
+    <References>
+      <Reference ReferenceType="HasTypeDefinition">i=68</Reference>
+      <Reference ReferenceType="HasProperty" IsForward="false">i=11192</Reference>
+    </References>
+  </UAVariable>
+  <UAVariable NodeId="i=11275" BrowseName="InsertAnnotationCapability" ParentNodeId="i=11192" DataType="Boolean">
+    <DisplayName>InsertAnnotationCapability</DisplayName>
+    <References>
+      <Reference ReferenceType="HasTypeDefinition">i=68</Reference>
+      <Reference ReferenceType="HasProperty" IsForward="false">i=11192</Reference>
+    </References>
+  </UAVariable>
+  <UAObject NodeId="i=11201" BrowseName="AggregateFunctions" ParentNodeId="i=11192">
+    <DisplayName>AggregateFunctions</DisplayName>
+    <References>
+      <Reference ReferenceType="HasTypeDefinition">i=61</Reference>
+      <Reference ReferenceType="HasComponent" IsForward="false">i=11192</Reference>
+    </References>
+  </UAObject>
 </UANodeSet>

+ 25 - 23
tools/travis/travis_linux_before_install.sh

@@ -5,27 +5,36 @@ set -ev
 if [ -z ${DOCKER+x} ] && [ -z ${SONAR+x} ]; then
 	# Only on non-docker builds required
 
-	echo "=== Installing from external package sources ===" && echo -en 'travis_fold:start:before_install.external\\r'
-
-	if [ "$CC" = "clang" ]; then
-		# the ubuntu repo has a somehow broken clang-3.9 compiler. We want to use the one from the llvm repo
-		# See https://github.com/openssl/openssl/commit/404c76f4ee1dc51c0d200e2b60a6340aadb44e38
-		sudo cp .travis-apt-pin.preferences /etc/apt/preferences.d/no-ubuntu-clang
-		curl -sSL "http://apt.llvm.org/llvm-snapshot.gpg.key" | sudo -E apt-key add -
-		echo "deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-3.9 main" | sudo tee -a /etc/apt/sources.list > /dev/null
-		sudo -E apt-add-repository -y "ppa:ubuntu-toolchain-r/test"
-		sudo -E apt-get -yq update
-		sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install clang-3.9 clang-tidy-3.9 libfuzzer-3.9-dev
-	fi
+ 	echo "=== Installing from external package sources in $LOCAL_PKG ===" && echo -en 'travis_fold:start:before_install.external\\r'
+
+   # Increase the environment version to force a rebuild of the packages
+    # The version is writen to the cache file after every build of the dependencies
+    ENV_VERSION="1"
+    ENV_INSTALLED=""
+    if [ -e $LOCAL_PKG/.build_env ]; then
+        echo "=== No cached build environment ==="
+        read -r ENV_INSTALLED < $LOCAL_PKG/.build_env
+    fi
+
+    # travis caches the $LOCAL_PKG dir. If it is loaded, we don't need to reinstall the packages
+    if [ "$ENV_VERSION" = "$ENV_INSTALLED" ]; then
+        echo "=== The build environment is current ==="
+        exit 0
+    fi
+
+    echo "=== The build environment is outdated ==="
+
+    # Clean up
+    rm -rf $LOCAL_PKG/*
 
 	if [ "$CC" = "tcc" ]; then
 		mkdir tcc_install && cd tcc_install
 		wget https://download.savannah.gnu.org/releases/tinycc/tcc-0.9.27.tar.bz2
 		tar xf tcc-0.9.27.tar.bz2
 		cd tcc-0.9.27
-		./configure
+		./configure --prefix=$LOCAL_PKG
 		make
-		sudo make install
+		make install
 		cd ../..
 		rm -rf tcc_install
 	fi
@@ -33,9 +42,9 @@ if [ -z ${DOCKER+x} ] && [ -z ${SONAR+x} ]; then
 	wget https://github.com/ARMmbed/mbedtls/archive/mbedtls-2.7.1.tar.gz
 	tar xf mbedtls-2.7.1.tar.gz
 	cd mbedtls-mbedtls-2.7.1
-	cmake -DENABLE_TESTING=Off .
+	cmake -DENABLE_TESTING=Off -DCMAKE_INSTALL_PREFIX=$LOCAL_PKG .
 	make -j
-	sudo make install
+	make install
 
 	echo -en 'travis_fold:end:script.before_install.external\\r'
 
@@ -46,11 +55,4 @@ if [ -z ${DOCKER+x} ] && [ -z ${SONAR+x} ]; then
 	pip install --user cpplint
 	echo -en 'travis_fold:end:script.before_install.python\\r'
 
-	echo "=== Installed versions are ===" && echo -en 'travis_fold:start:before_install.versions\\r'
-	clang --version
-	g++ --version
-	cppcheck --version
-	valgrind --version
-	echo -en 'travis_fold:end:script.before_install.versions\\r'
-
 fi

+ 10 - 8
tools/travis/travis_linux_script.sh

@@ -56,11 +56,11 @@ fi
 
 if [ $ANALYZE = "true" ]; then
     echo "=== Running static code analysis ===" && echo -en 'travis_fold:start:script.analyze\\r'
-    if [ "$CC" = "clang" ]; then
+    if ! case $CC in clang*) false;; esac; then
         mkdir -p build
         cd build
-        scan-build cmake -DUA_BUILD_EXAMPLES=ON -DUA_BUILD_UNIT_TESTS=ON ..
-        scan-build -enable-checker security.FloatLoopCounter \
+        scan-build-6.0 cmake -DUA_BUILD_EXAMPLES=ON -DUA_BUILD_UNIT_TESTS=ON ..
+        scan-build-6.0 -enable-checker security.FloatLoopCounter \
           -enable-checker security.insecureAPI.UncheckedReturn \
           --status-bugs -v \
           make -j
@@ -68,8 +68,8 @@ if [ $ANALYZE = "true" ]; then
 
         mkdir -p build
         cd build
-        scan-build cmake -DUA_ENABLE_AMALGAMATION=ON ..
-        scan-build -enable-checker security.FloatLoopCounter \
+        scan-build-6.0 cmake -DUA_ENABLE_AMALGAMATION=ON ..
+        scan-build-6.0 -enable-checker security.FloatLoopCounter \
           -enable-checker security.insecureAPI.UncheckedReturn \
           --status-bugs -v \
           make -j
@@ -110,13 +110,15 @@ else
     echo -e "\r\n== Full Namespace 0 Generation ==" && echo -en 'travis_fold:start:script.build.ns0\\r'
     mkdir -p build
     cd build
-    cmake -DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/$PYTHON -DCMAKE_BUILD_TYPE=Debug -DUA_ENABLE_FULL_NS0=ON -DUA_BUILD_EXAMPLES=ON  ..
+    cmake -DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/$PYTHON -DCMAKE_BUILD_TYPE=Debug -DUA_ENABLE_FULL_NS0=ON -DUA_BUILD_EXAMPLES=ON  \
+    -DUA_ENABLE_SUBSCRIPTIONS_EVENTS=ON ..
     make -j
     if [ $? -ne 0 ] ; then exit 1 ; fi
     cd .. && rm build -rf
     echo -en 'travis_fold:end:script.build.ns0\\r'
+
     # cross compilation only with gcc
-    if [ "$CC" = "gcc" ]; then
+    if ! [ -z ${MINGW+x} ]; then
         echo -e "\r\n== Cross compile release build for MinGW 32 bit =="  && echo -en 'travis_fold:start:script.build.cross_mingw32\\r'
         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 ..
@@ -239,7 +241,7 @@ else
     cmake -DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/$PYTHON -DUA_ENABLE_FULL_NS0=ON \
     -DCMAKE_BUILD_TYPE=Debug -DUA_BUILD_EXAMPLES=ON -DUA_ENABLE_PUBSUB=ON -DUA_ENABLE_ENCRYPTION=ON -DUA_ENABLE_DISCOVERY=ON \
     -DUA_ENABLE_DISCOVERY_MULTICAST=ON -DUA_BUILD_UNIT_TESTS=ON -DUA_ENABLE_COVERAGE=OFF \
-    -DUA_ENABLE_UNIT_TESTS_MEMCHECK=OFF ..
+    -DUA_ENABLE_UNIT_TESTS_MEMCHECK=OFF -DUA_ENABLE_SUBSCRIPTIONS_EVENTS=ON ..
     make -j && make test ARGS="-V"
     if [ $? -ne 0 ] ; then exit 1 ; fi
     cd .. && rm build -rf