Browse Source

Merge pull request #1238 from open62541/nodeset_compiler2

Rebase Nodeset compiler v2 on master
Stefan Profanter 7 years ago
parent
commit
9a79612905
59 changed files with 12090 additions and 41215 deletions
  1. 3 0
      .gitmodules
  2. 13 2
      .travis.yml
  3. 67 48
      CMakeLists.txt
  4. 2 1
      Dockerfile
  5. 1 0
      deps/ua-nodeset
  6. 2 28
      examples/CMakeLists.txt
  7. 124 0
      examples/nodeset/CMakeLists.txt
  8. 6 11
      examples/server_nodeset.c
  9. 16 8
      examples/server_nodeset.xml
  10. 52 0
      examples/nodeset/server_nodeset_plcopen.c
  11. 5 2
      plugins/ua_nodestore_default.c
  12. 1 1
      src/server/ua_mdns.c
  13. 34 1
      src/server/ua_nodes.c
  14. 10 6
      src/server/ua_server.c
  15. 7 6
      src/server/ua_server_internal.h
  16. 475 575
      src/server/ua_server_ns0.c
  17. 20 3
      src/server/ua_services_attribute.c
  18. 1 1
      src/server/ua_services_call.c
  19. 103 41
      src/server/ua_services_nodemanagement.c
  20. 4 4
      src/ua_types_encoding_binary.c
  21. 2 0
      src/ua_types_encoding_binary.h
  22. 153 0
      tests/CMakeLists.txt
  23. 60 0
      tests/check_nodeset_compiler_adi.c
  24. 64 0
      tests/check_nodeset_compiler_plc.c
  25. 24 2
      tests/check_types_memory.c
  26. 16 4
      tools/generate_datatypes.py
  27. 0 0
      tools/nodeset_compiler/NodeID_AssumeExternal.txt
  28. 0 0
      tools/pyUANamespace/NodeID_Blacklist.txt
  29. 61 0
      tools/nodeset_compiler/NodeID_NS0_Base.txt
  30. 7 9
      tools/pyUANamespace/README.md
  31. 149 0
      tools/nodeset_compiler/backend_graphviz.py
  32. 257 0
      tools/nodeset_compiler/backend_open62541.py
  33. 103 0
      tools/nodeset_compiler/backend_open62541_datatypes.py
  34. 522 0
      tools/nodeset_compiler/backend_open62541_nodes.py
  35. 13 10
      tools/pyUANamespace/ua_constants.py
  36. 724 0
      tools/nodeset_compiler/datatypes.py
  37. 625 0
      tools/nodeset_compiler/nodes.py
  38. 331 0
      tools/nodeset_compiler/nodeset.py
  39. 193 0
      tools/nodeset_compiler/nodeset_compiler.py
  40. 75 0
      tools/nodeset_compiler/nodeset_testing.py
  41. 0 1514
      tools/pyUANamespace/NodeID_Blacklist_FullNS0.txt
  42. 0 1514
      tools/pyUANamespace/NodeID_NameSpace0_All.txt
  43. 0 183
      tools/pyUANamespace/generate_open62541CCode.py
  44. 0 257
      tools/pyUANamespace/open62541_MacroHelper.py
  45. 0 403
      tools/pyUANamespace/open62541_XMLPreprocessor.py
  46. 0 1292
      tools/pyUANamespace/ua_builtin_types.py
  47. 0 823
      tools/pyUANamespace/ua_namespace.py
  48. 0 1771
      tools/pyUANamespace/ua_node_types.py
  49. 5544 104
      tools/schema/NodeIds.csv
  50. 1819 0
      tools/schema/Opc.Ua.NodeSet2.Minimal.xml
  51. 367 9
      tools/schema/Opc.Ua.Types.bsd
  52. 0 298
      tools/schema/datatypes_full.txt
  53. 0 4
      tools/schema/datatypes_full_generate.sh
  54. 15 0
      tools/schema/datatypes_minimal.txt
  55. 0 761
      tools/schema/namespace0/Opc.Ua.NodeSet2.Minimal.xml
  56. 0 31499
      tools/schema/namespace0/Opc.Ua.NodeSet2.xml
  57. 0 3
      tools/schema/namespace0/README.md
  58. 19 16
      tools/travis/travis_linux_script.sh
  59. 1 1
      tools/travis/travis_osx_script.sh

+ 3 - 0
.gitmodules

@@ -1,3 +1,6 @@
 [submodule "deps/mdnsd"]
 	path = deps/mdnsd
 	url = https://github.com/Pro/mdnsd.git
+[submodule "deps/ua-nodeset"]
+	path = deps/ua-nodeset
+	url = https://github.com/OPCFoundation/UA-Nodeset.git

+ 13 - 2
.travis.yml

@@ -18,7 +18,9 @@ matrix:
   include:
     - os: linux
       compiler: gcc
-      env: ANALYZE=false
+      env:
+        - ANALYZE=false
+        - PYTHON=python2
     - os: linux
       compiler: gcc
       env: ANALYZE=true
@@ -29,7 +31,14 @@ matrix:
         - docker
     - os: linux
       compiler: clang
-      env: ANALYZE=false
+      env:
+        - ANALYZE=false
+        - PYTHON=python2
+    - os: linux
+      compiler: clang
+      env:
+        - ANALYZE=false
+        - PYTHON=python3
     - os: linux
       compiler: clang
       env: ANALYZE=true
@@ -62,6 +71,8 @@ addons:
       - libsubunit-dev
       - libx11-dev
       - mingw-w64
+      - python-six
+      - python3-six
       - texlive-fonts-recommended
       - texlive-latex-extra
       - texlive-latex-recommended

+ 67 - 48
CMakeLists.txt

@@ -90,10 +90,7 @@ mark_as_advanced(UA_ENABLE_EMBEDDED_LIBC)
 option(UA_ENABLE_DETERMINISTIC_RNG "Do not seed the random number generator (e.g. for unit tests)." OFF)
 mark_as_advanced(UA_ENABLE_DETERMINISTIC_RNG)
 
-option(UA_ENABLE_GENERATE_NAMESPACE0 "Generate and load UA XML Namespace 0 definition (experimental)" OFF)
-mark_as_advanced(UA_ENABLE_GENERATE_NAMESPACE0)
-set(UA_DATATYPES_FILE ${PROJECT_SOURCE_DIR}/tools/schema/datatypes_minimal.txt CACHE FILEPATH "File containing the list of datatypes added to the server")
-mark_as_advanced(UA_DATATYPES_FILE)
+option(UA_ENABLE_FULL_NS0 "Use the full NS0 instead of a minimal Namespace 0 nodeset" OFF)
 
 option(UA_ENABLE_VALGRIND_UNIT_TESTS "Use Valgrind to detect memory leaks when running the unit tests" OFF)
 mark_as_advanced(UA_ENABLE_VALGRIND_UNIT_TESTS)
@@ -114,7 +111,6 @@ option(UA_BUILD_OSS_FUZZ "Special build switch used in oss-fuzz" OFF)
 mark_as_advanced(UA_BUILD_OSS_FUZZ)
 option(UA_DEBUG_DUMP_PKGS "Dump every package received by the server as hexdump format" OFF)
 mark_as_advanced(UA_DEBUG_DUMP_PKGS)
-option(UA_BUILD_EXAMPLES_NODESET_COMPILER "Generate an OPC UA information model from a nodeset XML (experimental)" OFF)
 
 # Advanced Build Targets
 option(UA_BUILD_SELFSIGNED_CERTIFICATE "Generate self-signed certificate" OFF)
@@ -249,10 +245,11 @@ if(UA_ENABLE_DISCOVERY_MULTICAST)
     configure_file("deps/mdnsd/libmdnsd/mdnsd_config.h.in" "${PROJECT_BINARY_DIR}/src_generated/mdnsd_config.h")
 endif()
 
-include_directories(${PROJECT_BINARY_DIR}/src_generated
-                    ${PROJECT_SOURCE_DIR}/include
+include_directories(${PROJECT_SOURCE_DIR}/include
                     ${PROJECT_SOURCE_DIR}/plugins # TODO: discovery depends on the default config
-                    ${PROJECT_SOURCE_DIR}/deps)
+                    ${PROJECT_SOURCE_DIR}/deps
+                    ${PROJECT_BINARY_DIR}
+                    ${PROJECT_BINARY_DIR}/src_generated)
 
 set(exported_headers ${PROJECT_BINARY_DIR}/src_generated/ua_config.h
                      ${PROJECT_SOURCE_DIR}/deps/ms_stdint.h
@@ -288,6 +285,7 @@ set(internal_headers ${PROJECT_SOURCE_DIR}/deps/queue.h
                      ${PROJECT_SOURCE_DIR}/src/server/ua_securechannel_manager.h
                      ${PROJECT_SOURCE_DIR}/src/server/ua_server_internal.h
                      ${PROJECT_SOURCE_DIR}/src/server/ua_services.h
+                     ${PROJECT_BINARY_DIR}/src_generated/ua_namespace0.h
                      ${PROJECT_SOURCE_DIR}/src/client/ua_client_internal.h)
 
 # TODO: make client optional
@@ -304,6 +302,7 @@ set(lib_sources ${PROJECT_SOURCE_DIR}/src/ua_types.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_nodes.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_server.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_server_ns0.c
+                ${PROJECT_BINARY_DIR}/src_generated/ua_namespace0.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_server_binary.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_server_utils.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_server_worker.c
@@ -356,13 +355,6 @@ if(UA_ENABLE_EMBEDDED_LIBC)
   list(APPEND lib_sources ${PROJECT_SOURCE_DIR}/deps/libc_string.c)
 endif()
 
-if(UA_ENABLE_GENERATE_NAMESPACE0)
-  set(GENERATE_NAMESPACE0_FILE "Opc.Ua.NodeSet2.xml" CACHE STRING "Namespace definition XML file")
-  set_property(CACHE GENERATE_NAMESPACE0_FILE PROPERTY STRINGS Opc.Ua.NodeSet2.xml Opc.Ua.NodeSet2.Minimal.xml)
-  list(APPEND internal_headers ${PROJECT_BINARY_DIR}/src_generated/ua_namespaceinit_generated.h)
-  list(APPEND lib_sources ${PROJECT_BINARY_DIR}/src_generated/ua_namespaceinit_generated.c)
-endif()
-
 if(UA_ENABLE_NONSTANDARD_UDP)
     list(APPEND exported_headers ${PROJECT_SOURCE_DIR}/plugins/ua_network_udp.h)
 endif()
@@ -388,10 +380,25 @@ endif()
 # Generate source files #
 #########################
 
-if(UA_DATATYPES_FILE STREQUAL "")
-  set(SELECTED_TYPES_TMP "")
+if (UA_ENABLE_FULL_NS0)
+    set(UA_FILE_NS0 ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/Schema/Opc.Ua.NodeSet2.xml)
+    set(UA_FILE_DATATYPES "")
+    set(UA_FILE_NODEIDS ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/Schema/NodeIds.csv)
+    set(UA_FILE_TYPES_BSD ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/Schema/Opc.Ua.Types.bsd)
+    if(NOT EXISTS "${UA_FILE_NS0}")
+        MESSAGE(FATAL_ERROR "File ${UA_FILE_NS0} not found. You probably need to initialize the git submodule for deps/ua-nodeset.")
+    endif()
 else()
-  set(SELECTED_TYPES_TMP "--selected-types=${UA_DATATYPES_FILE}")
+    set(UA_FILE_NS0 ${PROJECT_SOURCE_DIR}/tools/schema/Opc.Ua.NodeSet2.Minimal.xml)
+    set(UA_FILE_DATATYPES "${PROJECT_SOURCE_DIR}/tools/schema/datatypes_minimal.txt")
+    set(UA_FILE_NODEIDS ${PROJECT_SOURCE_DIR}/tools/schema/NodeIds.csv)
+    set(UA_FILE_TYPES_BSD ${PROJECT_SOURCE_DIR}/tools/schema/Opc.Ua.Types.bsd)
+endif()
+
+if (UA_FILE_DATATYPES STREQUAL "")
+    set(SELECTED_TYPES_TMP "")
+else()
+  set(SELECTED_TYPES_TMP "--selected-types=${UA_FILE_DATATYPES}")
 endif()
 
 # standard-defined data types
@@ -401,14 +408,14 @@ add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/ua_types_generated
                           ${PROJECT_BINARY_DIR}/src_generated/ua_types_generated_encoding_binary.h
                    PRE_BUILD
                    COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/generate_datatypes.py
-                           --type-csv=${PROJECT_SOURCE_DIR}/tools/schema/NodeIds.csv
+                           --type-csv=${UA_FILE_NODEIDS}
                            ${SELECTED_TYPES_TMP}
-                           --type-bsd=${PROJECT_SOURCE_DIR}/tools/schema/Opc.Ua.Types.bsd
+                           --type-bsd=${UA_FILE_TYPES_BSD}
                            ${PROJECT_BINARY_DIR}/src_generated/ua_types
                    DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tools/generate_datatypes.py
-                           ${PROJECT_SOURCE_DIR}/tools/schema/NodeIds.csv
-                           ${PROJECT_SOURCE_DIR}/tools/schema/Opc.Ua.Types.bsd
-                           ${SELECTED_TYPES})
+                           ${UA_FILE_NODEIDS}
+                           ${UA_FILE_TYPES_BSD}
+                           ${UA_FILE_DATATYPES})
 # we need a custom target to avoid that the generator is called concurrently and thus overwriting files while the other thread is compiling
 add_custom_target(open62541-generator-types DEPENDS
                   ${PROJECT_BINARY_DIR}/src_generated/ua_types_generated.c
@@ -425,13 +432,13 @@ add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/ua_transport_gener
                    COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/generate_datatypes.py
                            --namespace=1
                            --selected-types=${PROJECT_SOURCE_DIR}/tools/schema/datatypes_transport.txt
-                           --type-bsd=${PROJECT_SOURCE_DIR}/tools/schema/Opc.Ua.Types.bsd
+                           --type-bsd=${UA_FILE_TYPES_BSD}
                            --type-bsd=${PROJECT_SOURCE_DIR}/tools/schema/Custom.Opc.Ua.Transport.bsd
                            --no-builtin
                            ${PROJECT_BINARY_DIR}/src_generated/ua_transport
                    DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tools/generate_datatypes.py
                            ${PROJECT_SOURCE_DIR}/tools/schema/datatypes_transport.txt
-                           ${PROJECT_SOURCE_DIR}/tools/schema/Opc.Ua.Types.bsd
+                           ${UA_FILE_TYPES_BSD}
                            ${PROJECT_SOURCE_DIR}/tools/schema/Custom.Opc.Ua.Transport.bsd)
 # we need a custom target to avoid that the generator is called concurrently and thus overwriting files while the other thread is compiling
 add_custom_target(open62541-generator-transport DEPENDS
@@ -475,26 +482,40 @@ add_dependencies(open62541-amalgamation-header open62541-generator-types)
 add_dependencies(open62541-amalgamation-source open62541-generator-types
                  open62541-generator-transport open62541-generator-statuscode)
 
+
+set(NODESET_MAX_STR_LEN 0)
+if ( (MSVC) AND (MSVC_VERSION LESS 1600))
+    # Visual Studio 2008 has a maximum string length of 65535 bytes
+    set(NODESET_MAX_STR_LEN 65535)
+endif()
+
 # generated namespace 0
-add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/ua_namespaceinit_generated.c
-        ${PROJECT_BINARY_DIR}/src_generated/ua_namespaceinit_generated.h
-        PRE_BUILD
-        COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/generate_open62541CCode.py
-        -i ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/NodeID_AssumeExternal.txt
-        -s description -b ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/NodeID_Blacklist.txt
-        ${PROJECT_SOURCE_DIR}/tools/schema/namespace0/${GENERATE_NAMESPACE0_FILE}
-        ${PROJECT_BINARY_DIR}/src_generated/ua_namespaceinit_generated
-        DEPENDS ${PROJECT_SOURCE_DIR}/tools/schema/namespace0/${GENERATE_NAMESPACE0_FILE}
-        ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/generate_open62541CCode.py
-        ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/open62541_MacroHelper.py
-        ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/ua_builtin_types.py
-        ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/ua_constants.py
-        ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/ua_namespace.py
-        ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/ua_node_types.py)
+add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/ua_namespace0.c
+                          ${PROJECT_BINARY_DIR}/src_generated/ua_namespace0.h
+                   PRE_BUILD
+                   COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset_compiler.py
+                           --generate-ns0
+                           --internal-headers
+                           --max-string-length=${NODESET_MAX_STR_LEN}
+                           --ignore ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/NodeID_NS0_Base.txt
+                           --xml ${UA_FILE_NS0}
+                           ${PROJECT_BINARY_DIR}/src_generated/ua_namespace0
+                   DEPENDS ${UA_FILE_NS0}
+                           ${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)
+
+# stack protector needs to be disabled for the huge ns0 file, otherwise it may take many minutes to compile the file.
+set_source_files_properties(${PROJECT_BINARY_DIR}/src_generated/ua_namespace0.c PROPERTIES COMPILE_FLAGS -fno-stack-protector)
+
 # we need a custom target to avoid that the generator is called concurrently and thus overwriting files while the other thread is compiling
 add_custom_target(open62541-generator-namespace DEPENDS
-        ${PROJECT_BINARY_DIR}/src_generated/ua_namespaceinit_generated.c
-        ${PROJECT_BINARY_DIR}/src_generated/ua_namespaceinit_generated.h)
+        ${PROJECT_BINARY_DIR}/src_generated/ua_namespace0.c
+        ${PROJECT_BINARY_DIR}/src_generated/ua_namespace0.h)
 
 #####################
 # Build the Library #
@@ -550,12 +571,10 @@ else()
     endif()
 endif()
 
-if(UA_ENABLE_GENERATE_NAMESPACE0)
-    add_dependencies(open62541-amalgamation-source open62541-generator-namespace)
-    add_dependencies(open62541-amalgamation-header open62541-generator-namespace)
-    if(NOT UA_ENABLE_AMALGAMATION)
-        add_dependencies(open62541-object open62541-generator-namespace)
-    endif()
+add_dependencies(open62541-amalgamation-source open62541-generator-namespace)
+add_dependencies(open62541-amalgamation-header open62541-generator-namespace)
+if(NOT UA_ENABLE_AMALGAMATION)
+    add_dependencies(open62541-object open62541-generator-namespace)
 endif()
 
 # Export Symbols

+ 2 - 1
Dockerfile

@@ -1,7 +1,8 @@
 FROM alpine:3.5
-RUN apk add --no-cache cmake gcc g++ musl-dev python make && rm -rf /var/cache/apk/*
+RUN apk add --no-cache cmake gcc g++ musl-dev python py-pip make && rm -rf /var/cache/apk/*
 ADD . /tmp/open62541
 WORKDIR /tmp/open62541/build
+RUN pip install six
 RUN cmake -DUA_ENABLE_AMALGAMATION=true  \
           -DBUILD_SHARED_LIBS=true \
           /tmp/open62541 

+ 1 - 0
deps/ua-nodeset

@@ -0,0 +1 @@
+Subproject commit ddce8bb6a1792916b161a9e3ba3b6b80675e8c1e

+ 2 - 28
examples/CMakeLists.txt

@@ -78,34 +78,6 @@ if(UA_ENABLE_NODEMANAGEMENT)
     add_example(access_control_client access_control/client_access_control.c)
 endif()
 
-if(UA_BUILD_EXAMPLES_NODESET_COMPILER)
-  if(BUILD_SHARED_LIBS)
-    message(FATAL_ERROR "The nodeset compiler currently requires static linking to access internal API")
-  endif()
-
-  # example information model from nodeset xml
-  add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/nodeset.h ${PROJECT_BINARY_DIR}/src_generated/nodeset.c
-                    PRE_BUILD
-                    COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/generate_open62541CCode.py
-                                                 -i ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/NodeID_Blacklist_FullNS0.txt
-                                                 ${PROJECT_SOURCE_DIR}/tools/schema/namespace0/Opc.Ua.NodeSet2.xml
-                                                 ${PROJECT_SOURCE_DIR}/examples/server_nodeset.xml
-                                                 ${PROJECT_BINARY_DIR}/src_generated/nodeset
-                    DEPENDS ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/generate_open62541CCode.py
-                            ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/open62541_MacroHelper.py
-                            ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/ua_builtin_types.py
-                            ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/ua_constants.py
-                            ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/ua_namespace.py
-                            ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/ua_node_types.py
-                            ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/NodeID_Blacklist_FullNS0.txt
-                            ${PROJECT_SOURCE_DIR}/examples/server_nodeset.xml)
-
-  # needs internal methods which are not exported in the dynamic lib
-  add_example(server_nodeset ${PROJECT_BINARY_DIR}/src_generated/nodeset.c server_nodeset.c)
-  target_include_directories(server_nodeset PRIVATE ${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/deps) # needs an internal header
-  set_target_properties(server_nodeset PROPERTIES COMPILE_FLAGS "-Wno-pedantic -Wno-sign-conversion")
-endif()
-
 if(UA_BUILD_SELFSIGNED_CERTIFICATE)
   find_package(OpenSSL REQUIRED)
   add_custom_command(OUTPUT server_cert.der ca.crt
@@ -127,3 +99,5 @@ if(UA_ENABLE_DISCOVERY)
         add_example(discovery_server_multicast discovery/server_multicast.c)
     endif()
 endif()
+
+add_subdirectory(nodeset)

+ 124 - 0
examples/nodeset/CMakeLists.txt

@@ -0,0 +1,124 @@
+####################
+# Nodeset Examples #
+####################
+
+###################
+# Custom XML      #
+###################
+
+# generate namespace from XML file
+add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/example_nodeset.c
+                   ${PROJECT_BINARY_DIR}/src_generated/example_nodeset.h
+                   PRE_BUILD
+                   COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset_compiler.py
+                   --types-array=UA_TYPES
+                   --existing ${UA_FILE_NS0}
+                   --xml ${PROJECT_SOURCE_DIR}/examples/nodeset/server_nodeset.xml
+                   ${PROJECT_BINARY_DIR}/src_generated/example_nodeset
+                   DEPENDS ${UA_FILE_NS0}
+                   ${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}/examples/nodeset/server_nodeset.xml)
+
+add_example(server_nodeset server_nodeset.c ${PROJECT_BINARY_DIR}/src_generated/example_nodeset.c)
+if(UA_COMPILE_AS_CXX)
+    set_source_files_properties(${PROJECT_BINARY_DIR}/src_generated/example_nodeset.c PROPERTIES LANGUAGE CXX)
+endif()
+
+###################
+# PLCopen Nodeset #
+###################
+
+# PLCopen requires the full ns0 as basis
+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/${UA_TYPES_OUT}_generated.c
+                       ${PROJECT_BINARY_DIR}/src_generated/${UA_TYPES_OUT}_generated.h
+                       ${PROJECT_BINARY_DIR}/src_generated/${UA_TYPES_OUT}_generated_handling.h
+                       ${PROJECT_BINARY_DIR}/src_generated/${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/${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-types-di DEPENDS ${PROJECT_BINARY_DIR}/src_generated/${UA_TYPES_OUT}_generated.c)
+
+    # generate DI namespace
+    add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/ua_namespace_di.c
+                       ${PROJECT_BINARY_DIR}/src_generated/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/ua_namespace_di
+                       DEPENDS ${UA_FILE_NS0}
+                       ${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-ns-di DEPENDS ${PROJECT_BINARY_DIR}/src_generated/ua_namespace_di.c)
+    add_dependencies(open62541-generator-ns-di open62541-generator-types-di)
+
+    # generate PLCopen namespace which is using DI
+    add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/ua_namespace_plc.c
+                       ${PROJECT_BINARY_DIR}/src_generated/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/ua_namespace_plc
+                       DEPENDS ${UA_FILE_NS0}
+                       ${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/PLCopen/Opc.Ua.Plc.NodeSet2.xml
+                       )
+    add_custom_target(open62541-generator-ns-plc DEPENDS ${PROJECT_BINARY_DIR}/src_generated/ua_namespace_plc.c)
+    add_dependencies(open62541-generator-ns-plc open62541-generator-ns-di)
+
+    add_example(server_nodeset_plcopen server_nodeset_plcopen.c
+                ${PROJECT_BINARY_DIR}/src_generated/ua_types_di_generated.c
+                ${PROJECT_BINARY_DIR}/src_generated/ua_namespace_di.c
+                ${PROJECT_BINARY_DIR}/src_generated/ua_namespace_plc.c)
+    add_dependencies(server_nodeset_plcopen open62541-generator-ns-plc)
+    target_include_directories(server_nodeset_plcopen PRIVATE ${PROJECT_SOURCE_DIR}/src) # needs an internal header
+
+    if(UA_COMPILE_AS_CXX)
+        set_source_files_properties(${PROJECT_BINARY_DIR}/src_generated/ua_types_di_generated.c PROPERTIES LANGUAGE CXX)
+        set_source_files_properties(${PROJECT_BINARY_DIR}/src_generated/ua_namespace_di.c PROPERTIES LANGUAGE CXX)
+        set_source_files_properties(${PROJECT_BINARY_DIR}/src_generated/ua_namespace_plc.c PROPERTIES LANGUAGE CXX)
+    endif()
+endif()

+ 6 - 11
examples/server_nodeset.c

@@ -2,14 +2,11 @@
  * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
 
 #include <signal.h>
-#include "ua_server.h"
-#include "ua_plugin_log.h"
-#include "ua_log_stdout.h"
-#include "ua_config_default.h"
+#include "open62541.h"
 
-/* Files nodeset.h and nodeset.c are created from server_nodeset.xml in the
+/* Files example_namespace.h and example_namespace.c are created from server_nodeset.xml in the
  * /src_generated directory by CMake */
-#include "nodeset.h"
+#include "example_nodeset.h"
 
 UA_Boolean running = true;
 
@@ -21,21 +18,19 @@ static void stopHandler(int sign) {
 int main(int argc, char** argv) {
     signal(SIGINT, stopHandler);
     signal(SIGTERM, stopHandler);
-
-    /* initialize the server */
+    
     UA_ServerConfig *config = UA_ServerConfig_new_default();
     UA_Server *server = UA_Server_new(config);
 
     /* create nodes from nodeset */
-    if(nodeset(server) != UA_STATUSCODE_GOOD) {
+    if(example_nodeset(server) != UA_STATUSCODE_GOOD) {
         UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Namespace index for generated "
                      "nodeset does not match. The call to the generated method has to be "
                      "before any other namespace add calls.");
         UA_Server_delete(server);
         UA_ServerConfig_delete(config);
-        return UA_STATUSCODE_BADUNEXPECTEDERROR;
+        return (int)UA_STATUSCODE_BADUNEXPECTEDERROR;
     }
-
     UA_StatusCode retval = UA_Server_run(server, &running);
     UA_Server_delete(server);
     UA_ServerConfig_delete(config);

+ 16 - 8
examples/server_nodeset.xml

@@ -10,6 +10,22 @@
         <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>
@@ -25,14 +41,6 @@
             <Reference ReferenceType="HasComponent" IsForward="false">ns=1;i=1001</Reference>
         </References>
     </UAVariable>
-    <UAObject NodeId="ns=1;i=5001" BrowseName="1:testInstance">
-        <DisplayName>testInstance</DisplayName>
-        <References>
-            <Reference ReferenceType="Organizes" IsForward="false">i=85</Reference>
-            <Reference ReferenceType="HasTypeDefinition">ns=1;i=1001</Reference>
-            <Reference ReferenceType="HasComponent">ns=1;i=6002</Reference>
-        </References>
-    </UAObject>
     <UAVariable DataType="Double" ParentNodeId="ns=1;i=5001" NodeId="ns=1;i=6002" BrowseName="1:Var1" UserAccessLevel="3" AccessLevel="3">
         <DisplayName>Var1</DisplayName>
         <References>

+ 52 - 0
examples/nodeset/server_nodeset_plcopen.c

@@ -0,0 +1,52 @@
+/* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
+ * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
+
+#include <signal.h>
+#ifdef UA_NO_AMALGAMATION
+#include "ua_server.h"
+#include "ua_log_stdout.h"
+#include "ua_config_default.h"
+#else
+#include "open62541.h"
+#endif
+
+/* Files example_namespace.h and example_namespace.c are created from server_nodeset.xml in the
+ * /src_generated directory by CMake */
+#include "ua_namespace_di.h"
+#include "ua_namespace_plc.h"
+
+UA_Boolean running = true;
+
+static void stopHandler(int sign) {
+    UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "received ctrl-c");
+    running = false;
+}
+
+int main(int argc, char** argv) {
+    signal(SIGINT, stopHandler);
+    signal(SIGTERM, stopHandler);
+
+    UA_ServerConfig *config = UA_ServerConfig_new_default();
+    UA_Server *server = UA_Server_new(config);
+
+    /* create nodes from nodeset */
+    UA_StatusCode retval = ua_namespace_di(server);
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Adding the DI namespace failed. Please check previous error output.");
+        UA_Server_delete(server);
+        UA_ServerConfig_delete(config);
+        return (int)UA_STATUSCODE_BADUNEXPECTEDERROR;
+    }
+    retval |= ua_namespace_plc(server);
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Adding the PLCopen namespace failed. Please check previous error output.");
+        UA_Server_delete(server);
+        UA_ServerConfig_delete(config);
+        return (int)UA_STATUSCODE_BADUNEXPECTEDERROR;
+    }
+
+    retval = UA_Server_run(server, &running);
+    UA_Server_delete(server);
+    UA_ServerConfig_delete(config);
+    return (int)retval;
+}

+ 5 - 2
plugins/ua_nodestore_default.c

@@ -330,9 +330,12 @@ UA_NodeMap_insertNode(void *context, UA_Node *node,
     if(tempNodeid.identifierType == UA_NODEIDTYPE_NUMERIC &&
        tempNodeid.identifier.numeric == 0) {
         /* create a random nodeid */
-        UA_UInt32 identifier = ns->count+1; // start value
+		/* start at least with 50,000 to make sure we don not conflict with nodes from the spec */
+		/* E.g. adding a nodeset will create children while there are still other nodes which need to be created */
+		/* Thus the node id's may collide */
+        UA_UInt32 identifier = 50000 + ns->count+1; // start value
         UA_UInt32 size = ns->size;
-        UA_UInt32 increase = mod2(identifier, size);
+        UA_UInt32 increase = mod2(ns->count+1, size);
         while(true) {
             node->nodeId.identifier.numeric = identifier;
             slot = findFreeSlot(ns, &node->nodeId);

+ 1 - 1
src/server/ua_mdns.c

@@ -28,7 +28,7 @@
 #ifndef UA_STRDUP
 # if defined(__MINGW32__)
 static char *ua_strdup(const char *s) {
-    char *p = UA_malloc(strlen(s) + 1);
+    char *p = (char*)UA_malloc(strlen(s) + 1);
     if(p) { strcpy(p, s); }
     return p;
 }

+ 34 - 1
src/server/ua_nodes.c

@@ -3,6 +3,7 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ua_server_internal.h"
+#include "ua_types_encoding_binary.h"
 
 /* There is no UA_Node_new() method here. Creating nodes is part of the
  * NodeStore layer */
@@ -250,7 +251,39 @@ copyCommonVariableAttributes(UA_VariableNode *node,
 
     /* Copy the value */
     node->valueSource = UA_VALUESOURCE_DATA;
-    retval |= UA_Variant_copy(&attr->value, &node->value.data.value.value);
+	UA_NodeId extensionObject = UA_NODEID_NUMERIC(0, UA_NS0ID_STRUCTURE);
+	/* if we have an extension object which is still encoded (e.g. from the nodeset compiler)
+	 * we need to decode it and set the decoded value instead of the encoded object */
+	UA_Boolean valueSet = false;
+	if (attr->value.type != NULL && UA_NodeId_equal(&attr->value.type->typeId, &extensionObject)) {
+		const UA_ExtensionObject *obj = (const UA_ExtensionObject *)attr->value.data;
+		if (obj->encoding == UA_EXTENSIONOBJECT_ENCODED_BYTESTRING) {
+
+			const UA_DataType *type = UA_findDataTypeByBinary(&obj->content.encoded.typeId);
+
+			if (type) {
+				void *dst = UA_Array_new(attr->value.arrayLength, type);
+				uint8_t *tmpPos = (uint8_t *)dst;
+
+				for (size_t i=0; i<attr->value.arrayLength; i++) {
+					size_t offset =0;
+					const UA_ExtensionObject *curr = &((const UA_ExtensionObject *)attr->value.data)[i];
+					UA_StatusCode ret = UA_decodeBinary(&curr->content.encoded.body, &offset, tmpPos, type, 0, NULL);
+					if (ret != UA_STATUSCODE_GOOD) {
+						return ret;
+					}
+					tmpPos += type->memSize;
+				}
+
+				UA_Variant_setArray(&node->value.data.value.value, dst, attr->value.arrayLength, type);
+				valueSet = true;
+			}
+		}
+	}
+
+	if (!valueSet)
+		retval |= UA_Variant_copy(&attr->value, &node->value.data.value.value);
+
     node->value.data.value.hasValue = true;
 
     return retval;

+ 10 - 6
src/server/ua_server.c

@@ -232,12 +232,16 @@ UA_Server_new(const UA_ServerConfig *config) {
     server->serverOnNetworkCallbackData = NULL;
 #endif
 
-    /* Initialize Namespace 0 */
-#ifndef UA_ENABLE_GENERATE_NAMESPACE0
-    UA_Server_createNS0(server);
-#else
-    ua_namespaceinit_generated(server);
-#endif
+    /* Initialize namespace 0*/
+    UA_StatusCode retVal = UA_Server_initNS0(server);
+    if (retVal != UA_STATUSCODE_GOOD) {
+        UA_LOG_ERROR(config->logger,
+                     UA_LOGCATEGORY_SERVER,
+                     "Initialization of Namespace 0 failed with %s. See previous outputs for any error messages.",
+                     UA_StatusCode_name(retVal));
+        UA_Server_delete(server);
+        return NULL;
+    }
 
     return server;
 }

+ 7 - 6
src/server/ua_server_internal.h

@@ -121,6 +121,10 @@ struct UA_Server {
     struct cds_wfcq_tail dispatchQueue_tail; /* Dispatch queue tail for the worker threads */
 #endif
 
+    /* For bootstrapping, omit some consistency checks, creating a reference to
+     * the parent and member instantiation */
+    UA_Boolean bootstrapNS0;
+
     /* Config */
     UA_ServerConfig config;
 };
@@ -328,14 +332,13 @@ UA_Discovery_removeRecord(UA_Server *server, const UA_String *servername,
 UA_StatusCode
 Operation_addNode_begin(UA_Server *server, UA_Session *session,
                         const UA_AddNodesItem *item, void *nodeContext,
-                        UA_NodeId *outNewNodeId, UA_Boolean overrideChecks);
+                        UA_NodeId *outNewNodeId);
 
 /* Children, references, type-checking, constructors. */
 UA_StatusCode
 Operation_addNode_finish(UA_Server *server, UA_Session *session,
                          const UA_NodeId *nodeId, const UA_NodeId *parentNodeId,
-                         const UA_NodeId *referenceTypeId, const UA_NodeId *typeDefinitionId,
-                         UA_Boolean overrideChecks);
+                         const UA_NodeId *referenceTypeId, const UA_NodeId *typeDefinitionId);
 
 UA_StatusCode
 UA_Server_addMethodNode_finish(UA_Server *server, const UA_NodeId nodeId,
@@ -348,9 +351,7 @@ UA_Server_addMethodNode_finish(UA_Server *server, const UA_NodeId nodeId,
 /* Create Namespace 0 */
 /**********************/
 
-#ifndef UA_ENABLE_GENERATE_NAMESPACE0
-void UA_Server_createNS0(UA_Server *server);
-#endif
+UA_StatusCode UA_Server_initNS0(UA_Server *server);
 
 #ifdef __cplusplus
 } // extern "C"

File diff suppressed because it is too large
+ 475 - 575
src/server/ua_server_ns0.c


+ 20 - 3
src/server/ua_services_attribute.c

@@ -878,9 +878,26 @@ writeValueAttribute(UA_Server *server, UA_Session *session,
     /* Type checking. May change the type of editableValue */
     if(value->hasValue && value->value.type) {
         adjustValue(server, &adjustedValue.value, &node->dataType);
-        if(!compatibleValue(server, &node->dataType, node->valueRank,
-                            node->arrayDimensionsSize, node->arrayDimensions,
-                            &adjustedValue.value, rangeptr)) {
+
+        /* The value may be an extension object, especially the nodeset compiler uses
+         * extension objects to write variable values.
+         * If value is an extension object we check if the current node value is also an extension object.
+         */
+        UA_Boolean compatible;
+        if (value->value.type->typeId.identifierType == UA_NODEIDTYPE_NUMERIC &&
+            value->value.type->typeId.identifier.numeric == UA_NS0ID_STRUCTURE) {
+            const UA_NodeId nodeDataType = UA_NODEID_NUMERIC(0, UA_NS0ID_STRUCTURE);
+            compatible = compatibleValue(server, &nodeDataType, node->valueRank,
+                                    node->arrayDimensionsSize, node->arrayDimensions,
+                                    &adjustedValue.value, rangeptr);
+        } else {
+            compatible = compatibleValue(server, &node->dataType, node->valueRank,
+                                     node->arrayDimensionsSize, node->arrayDimensions,
+                                     &adjustedValue.value, rangeptr);
+        }
+
+
+        if(!compatible) {
             if(rangeptr)
                 UA_free(range.dimensions);
             return UA_STATUSCODE_BADTYPEMISMATCH;

+ 1 - 1
src/server/ua_services_call.c

@@ -116,7 +116,7 @@ callWithMethodAndObject(UA_Server *server, UA_Session *session,
      * every reference between the parent object and the method node if there is
      * a hasComponent (or subtype) reference */
     UA_Boolean found = false;
-    for(size_t i = 0; i < object->referencesSize; ++i) {
+    for(size_t i = 0; i < object->referencesSize && !found; ++i) {
         UA_NodeReferenceKind *rk = &object->references[i];
         if(rk->isInverse)
             continue;

+ 103 - 41
src/server/ua_services_nodemanagement.c

@@ -52,6 +52,10 @@ checkParentReference(UA_Server *server, UA_Session *session, UA_NodeClass nodeCl
        UA_NodeId_isNull(referenceTypeId))
         return UA_STATUSCODE_GOOD;
 
+    /* Omit checks during bootstrap */
+    if(server->bootstrapNS0)
+        return UA_STATUSCODE_GOOD;
+
     /* See if the parent exists */
     const UA_Node *parent = UA_Nodestore_get(server, parentNodeId);
     if(!parent) {
@@ -127,7 +131,8 @@ checkParentReference(UA_Server *server, UA_Session *session, UA_NodeClass nodeCl
 
 static UA_StatusCode
 typeCheckVariableNode(UA_Server *server, UA_Session *session,
-                      const UA_VariableNode *node, const UA_VariableTypeNode *vt) {
+                      const UA_VariableNode *node, const UA_VariableTypeNode *vt,
+                      const UA_NodeId *parentNodeId) {
     /* The value might come from a datasource, so we perform a
      * regular read. */
     UA_DataValue value;
@@ -148,12 +153,24 @@ typeCheckVariableNode(UA_Server *server, UA_Session *session,
     }
 
     /* Check valueRank against array dimensions */
-    if(!compatibleValueRankArrayDimensions(node->valueRank, arrayDims))
+    if (!(node->nodeClass == UA_NODECLASS_VARIABLETYPE && ((const UA_VariableTypeNode*)node)->isAbstract && node->valueRank == 0) &&
+        !compatibleValueRankArrayDimensions(node->valueRank, arrayDims))
         return UA_STATUSCODE_BADTYPEMISMATCH;
 
-    /* Check valueRank against the vt */
-    if(!compatibleValueRanks(node->valueRank, vt->valueRank))
-        return UA_STATUSCODE_BADTYPEMISMATCH;
+    /* If variable node is created below BaseObjectType and has its default valueRank of -2,
+     * skip the test */
+    const UA_NodeId objectTypes = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE);
+    const UA_NodeId refs[] = {
+            UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
+            UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT)
+    };
+    if(node->valueRank != vt->valueRank &&
+       node->valueRank != UA_VariableAttributes_default.valueRank &&
+       !isNodeInTree(&server->config.nodestore, parentNodeId, &objectTypes, refs , 2)) {
+        /* Check valueRank against the vt */
+        if(!compatibleValueRanks(node->valueRank, vt->valueRank))
+            return UA_STATUSCODE_BADTYPEMISMATCH;
+    }
 
     /* Check array dimensions against the vt */
     if(!compatibleArrayDimensions(vt->arrayDimensionsSize, vt->arrayDimensions,
@@ -161,7 +178,7 @@ typeCheckVariableNode(UA_Server *server, UA_Session *session,
         return UA_STATUSCODE_BADTYPEMISMATCH;
 
     /* Typecheck the value */
-    if(value.hasValue) {
+    if(!server->bootstrapNS0 && value.hasValue) {
         /* If the type-check failed write the same value again. The
          * write-service tries to convert to the correct type... */
         if(!compatibleValue(server, &node->dataType, node->valueRank,
@@ -393,7 +410,7 @@ copyChildNode(UA_Server *server, UA_Session *session,
         /* Call addnode_finish, this recursively adds members, the type
          * definition and so on */
         retval = Operation_addNode_finish(server, session, &newNodeId, destinationNodeId,
-                                          &rd->referenceTypeId, typeId, false);
+                                          &rd->referenceTypeId, typeId);
         UA_NodeId_deleteMembers(&newNodeId);
         UA_Nodestore_release(server, type);
     }
@@ -530,7 +547,7 @@ addParentRef(UA_Server *server, UA_Session *session,
 UA_StatusCode
 Operation_addNode_begin(UA_Server *server, UA_Session *session,
                         const UA_AddNodesItem *item, void *nodeContext,
-                        UA_NodeId *outNewNodeId, UA_Boolean overrideChecks) {
+                        UA_NodeId *outNewNodeId) {
     /* Check the namespaceindex */
     if(item->requestedNewNodeId.nodeId.namespaceIndex >= server->namespacesSize) {
         UA_LOG_INFO_SESSION(server->config.logger, session,
@@ -570,7 +587,7 @@ Operation_addNode_begin(UA_Server *server, UA_Session *session,
         return retval;
     }
 
-    if(overrideChecks)
+    if(server->bootstrapNS0)
         goto finished_checks;
 
     /* Use attributes from the typedefinition */
@@ -612,7 +629,7 @@ static const UA_NodeId hasSubtype = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HASSUBT
 UA_StatusCode
 Operation_addNode_finish(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId,
                          const UA_NodeId *parentNodeId, const UA_NodeId *referenceTypeId,
-                         const UA_NodeId *typeDefinitionId, UA_Boolean overrideChecks) {
+                         const UA_NodeId *typeDefinitionId) {
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     const UA_Node *type = NULL;
 
@@ -621,18 +638,24 @@ Operation_addNode_finish(UA_Server *server, UA_Session *session, const UA_NodeId
     if(!node)
         return UA_STATUSCODE_BADNODEIDUNKNOWN;
 
-    if(overrideChecks)
-        goto get_type;
-
     /* Use the typeDefinition as parent for type-nodes */
     if(node->nodeClass == UA_NODECLASS_VARIABLETYPE ||
        node->nodeClass == UA_NODECLASS_OBJECTTYPE ||
        node->nodeClass == UA_NODECLASS_REFERENCETYPE ||
        node->nodeClass == UA_NODECLASS_DATATYPE) {
-        referenceTypeId = &hasSubtype;
-        typeDefinitionId = parentNodeId;
+        if (UA_NodeId_equal(referenceTypeId, &UA_NODEID_NULL))
+            referenceTypeId = &hasSubtype;
+        const UA_Node *parentNode = UA_Nodestore_get(server, parentNodeId);
+        if (parentNode) {
+			if (parentNode->nodeClass == node->nodeClass)
+				typeDefinitionId = parentNodeId;
+			UA_Nodestore_release(server, parentNode);
+        }
     }
 
+    if(server->bootstrapNS0)
+        goto get_type;
+
     /* Check parent reference. Objects may have no parent. */
     retval = checkParentReference(server, session, node->nodeClass,
                                   parentNodeId, referenceTypeId);
@@ -664,24 +687,54 @@ Operation_addNode_finish(UA_Server *server, UA_Session *session, const UA_NodeId
         /* Get the type node */
         type = UA_Nodestore_get(server, typeDefinitionId);
         if(!type) {
+            UA_LOG_INFO_SESSION(server->config.logger, session,
+                                "AddNodes: Node type not found in nodestore");
             retval = UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
             goto cleanup;
         }
 
         /* See if the type has the correct node class. For type-nodes, we know
          * that type has the same nodeClass from checkParentReference. */
-        if(node->nodeClass == UA_NODECLASS_VARIABLE) {
+        if(!server->bootstrapNS0 && node->nodeClass == UA_NODECLASS_VARIABLE) {
             if(type->nodeClass != UA_NODECLASS_VARIABLETYPE ||
-               ((const UA_VariableTypeNode*)type)->isAbstract) {
-                retval = UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
-                goto cleanup;
+                    ((const UA_VariableTypeNode*)type)->isAbstract) {
+                /* Abstract variable is allowed if parent is a children of a base data variable */
+                const UA_NodeId variableTypes = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE);
+                /* A variable may be of an object type which again is below BaseObjectType */
+                const UA_NodeId objectTypes = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE);
+                const UA_NodeId refs[] = {
+                        UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
+                        UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT)
+                };
+                if(!isNodeInTree(&server->config.nodestore, parentNodeId,
+                                 &variableTypes, refs , 2) &&
+                   !isNodeInTree(&server->config.nodestore, parentNodeId,
+                                     &objectTypes, refs , 2)) {
+                    UA_LOG_INFO_SESSION(server->config.logger, session,
+                                        "AddNodes: Type of variable node must "
+                                                "be VariableType and not cannot be abstract");
+                    retval = UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
+                    goto cleanup;
+                }
             }
         }
-        if(node->nodeClass == UA_NODECLASS_OBJECT) {
+        if(!server->bootstrapNS0 && node->nodeClass == UA_NODECLASS_OBJECT) {
             if(type->nodeClass != UA_NODECLASS_OBJECTTYPE ||
-               ((const UA_ObjectTypeNode*)type)->isAbstract) {
-                retval = UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
-                goto cleanup;
+                    ((const UA_ObjectTypeNode*)type)->isAbstract) {
+                /* Object node created of an abstract ObjectType. Only allowed if within BaseObjectType folder */
+                const UA_NodeId objectTypes = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE);
+                const UA_NodeId refs[] = {
+                        UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
+                        UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT)
+                };
+                if(!isNodeInTree(&server->config.nodestore, parentNodeId,
+                                 &objectTypes, refs , 2)) {
+                    UA_LOG_INFO_SESSION(server->config.logger, session,
+                                        "AddNodes: Type of object node must "
+                                                "be ObjectType and not be abstract");
+                    retval = UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
+                    goto cleanup;
+                }
             }
         }
     }
@@ -693,7 +746,7 @@ Operation_addNode_finish(UA_Server *server, UA_Session *session, const UA_NodeId
                 node->nodeClass == UA_NODECLASS_VARIABLETYPE)) {
         retval = typeCheckVariableNode(server, session,
                                        (const UA_VariableNode*)node,
-                                       (const UA_VariableTypeNode*)type);
+                                       (const UA_VariableTypeNode*)type, parentNodeId);
         if(retval != UA_STATUSCODE_GOOD) {
             UA_LOG_INFO_SESSION(server->config.logger, session,
                                 "AddNodes: Type-checking the variable node "
@@ -707,13 +760,15 @@ Operation_addNode_finish(UA_Server *server, UA_Session *session, const UA_NodeId
        node->nodeClass == UA_NODECLASS_OBJECT) {
         UA_assert(type != NULL); /* see above */
         /* Add (mandatory) child nodes from the type definition */
-        retval = addChildren(server, session, node, type);
-        if(retval != UA_STATUSCODE_GOOD) {
-            UA_LOG_INFO_SESSION(server->config.logger, session,
-                                "AddNodes: Adding child nodes failed with error code %s",
-                                UA_StatusCode_name(retval));
-            goto cleanup;
-        }
+		if (!server->bootstrapNS0) {
+			retval = addChildren(server, session, node, type);
+			if(retval != UA_STATUSCODE_GOOD) {
+				UA_LOG_INFO_SESSION(server->config.logger, session,
+									"AddNodes: Adding child nodes failed with error code %s",
+									UA_StatusCode_name(retval));
+				goto cleanup;
+			}
+		}
 
         /* Add a hasTypeDefinition reference */
         retval = addTypeDefRef(server, session, node, type);
@@ -728,6 +783,13 @@ Operation_addNode_finish(UA_Server *server, UA_Session *session, const UA_NodeId
 
     /* Add reference to the parent */
     if(!UA_NodeId_isNull(parentNodeId)) {
+        if (UA_NodeId_isNull(referenceTypeId)) {
+            UA_LOG_INFO_SESSION(server->config.logger, session,
+                                "AddNodes: Reference to parent cannot be null");
+            retval = UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
+            goto cleanup;
+        }
+
         retval = addParentRef(server, session, nodeId, referenceTypeId, parentNodeId);
         if(retval != UA_STATUSCODE_GOOD) {
             UA_LOG_INFO_SESSION(server->config.logger, session,
@@ -764,14 +826,15 @@ Operation_addNode(UA_Server *server, UA_Session *session, const UA_AddNodesItem
     }
 
     result->statusCode = Operation_addNode_begin(server, session, item, nodeContext,
-                                                 &result->addedNodeId, false);
+                                                 &result->addedNodeId);
     if(result->statusCode != UA_STATUSCODE_GOOD)
         return;
 
+    /* AddNodes_finish */
     result->statusCode =
         Operation_addNode_finish(server, session, &result->addedNodeId,
                                  &item->parentNodeId.nodeId, &item->referenceTypeId,
-                                 &item->typeDefinition.nodeId, false);
+                                 &item->typeDefinition.nodeId);
 
     /* If finishing failed, the node was deleted */
     if(result->statusCode != UA_STATUSCODE_GOOD)
@@ -849,7 +912,7 @@ UA_Server_addNode_begin(UA_Server *server, const UA_NodeClass nodeClass,
     item.nodeAttributes.content.decoded.type = attributeType;
     item.nodeAttributes.content.decoded.data = (void*)(uintptr_t)attr;
     return Operation_addNode_begin(server, &adminSession, &item,
-                                   nodeContext, outNewNodeId, false);
+                                   nodeContext, outNewNodeId);
 }
 
 UA_StatusCode
@@ -858,7 +921,7 @@ UA_Server_addNode_finish(UA_Server *server, const UA_NodeId nodeId,
                          const UA_NodeId referenceTypeId,
                          const UA_NodeId typeDefinitionId) {
     return Operation_addNode_finish(server, &adminSession, &nodeId, &parentNodeId,
-                                    &referenceTypeId, &typeDefinitionId, false);
+                                    &referenceTypeId, &typeDefinitionId);
 }
 
 /****************/
@@ -1236,15 +1299,14 @@ UA_Server_addDataSourceVariableNode(UA_Server *server, const UA_NodeId requested
         outNewNodeId = &newNodeId;
         deleteNodeId = UA_TRUE;
     }
-    UA_StatusCode retval = Operation_addNode_begin(server, &adminSession,
-                                                   &item, nodeContext, outNewNodeId, true);
+    UA_StatusCode retval = Operation_addNode_begin(server, &adminSession, &item,
+                                                   nodeContext, outNewNodeId);
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
     retval = UA_Server_setVariableNode_dataSource(server, *outNewNodeId, dataSource);
     if(retval == UA_STATUSCODE_GOOD)
         retval = Operation_addNode_finish(server, &adminSession, outNewNodeId,
-                                          &parentNodeId, &referenceTypeId,
-                                          &typeDefinition, false);
+                                          &parentNodeId, &referenceTypeId, &typeDefinition);
     if(retval != UA_STATUSCODE_GOOD || deleteNodeId)
         UA_NodeId_deleteMembers(outNewNodeId);
     return retval;
@@ -1358,7 +1420,7 @@ UA_Server_addMethodNode_finish(UA_Server *server, const UA_NodeId nodeId,
 
     /* Call finish to add the parent reference */
     retval |= Operation_addNode_finish(server, &adminSession, &nodeId, &parentNodeId,
-                                       &referenceTypeId, &UA_NODEID_NULL, false);
+                                       &referenceTypeId, &UA_NODEID_NULL);
 
     if(retval != UA_STATUSCODE_GOOD) {
         UA_Server_deleteNode(server, nodeId, true);
@@ -1393,7 +1455,7 @@ UA_Server_addMethodNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
     }
 
     UA_StatusCode retval = Operation_addNode_begin(server, &adminSession, &item,
-                                                   nodeContext, outNewNodeId, false);
+                                                   nodeContext, outNewNodeId);
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
 

+ 4 - 4
src/ua_types_encoding_binary.c

@@ -854,8 +854,8 @@ LocalizedText_decodeBinary(UA_LocalizedText *dst, const UA_DataType *_) {
 
 /* The binary encoding has a different nodeid from the data type. So it is not
  * possible to reuse UA_findDataType */
-static const UA_DataType *
-findDataTypeByBinary(const UA_NodeId *typeId) {
+const UA_DataType *
+UA_findDataTypeByBinary(const UA_NodeId *typeId) {
     /* We only store a numeric identifier for the encoding nodeid of data types */
     if(typeId->identifierType != UA_NODEIDTYPE_NUMERIC)
         return NULL;
@@ -942,7 +942,7 @@ ExtensionObject_encodeBinary(UA_ExtensionObject const *src, const UA_DataType *_
 static status
 ExtensionObject_decodeBinaryContent(UA_ExtensionObject *dst, const UA_NodeId *typeId) {
     /* Lookup the datatype */
-    const UA_DataType *type = findDataTypeByBinary(typeId);
+    const UA_DataType *type = UA_findDataTypeByBinary(typeId);
 
     /* Unknown type, just take the binary content */
     if(!type) {
@@ -1105,7 +1105,7 @@ Variant_decodeBinaryUnwrapExtensionObject(UA_Variant *dst) {
 
     /* Search for the datatype. Default to ExtensionObject. */
     if(encoding == UA_EXTENSIONOBJECT_ENCODED_BYTESTRING &&
-       (dst->type = findDataTypeByBinary(&typeId)) != NULL) {
+       (dst->type = UA_findDataTypeByBinary(&typeId)) != NULL) {
         /* Jump over the length field (TODO: check if length matches) */
         g_pos += 4; 
     } else {

+ 2 - 0
src/ua_types_encoding_binary.h

@@ -39,6 +39,8 @@ UA_decodeBinary(const UA_ByteString *src, size_t *offset, void *dst,
 
 size_t UA_calcSizeBinary(void *p, const UA_DataType *type);
 
+const UA_DataType *UA_findDataTypeByBinary(const UA_NodeId *typeId);
+
 #ifdef __cplusplus
 }
 #endif

+ 153 - 0
tests/CMakeLists.txt

@@ -194,3 +194,156 @@ add_test_valgrind(check_client_subscriptions ${TESTS_BINARY_DIR}/check_client_su
 add_executable(check_client_highlevel check_client_highlevel.c $<TARGET_OBJECTS:open62541-object> $<TARGET_OBJECTS:open62541-testplugins>)
 target_link_libraries(check_client_highlevel ${LIBS})
 add_test_valgrind(check_client_highlevel ${TESTS_BINARY_DIR}/check_client_highlevel)
+
+#############################
+#                           #
+# 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
+                   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(check_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(check_nodeset_compiler_plc ${TESTS_BINARY_DIR}/check_nodeset_compiler_plc)
+endif()

+ 60 - 0
tests/check_nodeset_compiler_adi.c

@@ -0,0 +1,60 @@
+/* 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_server.h"
+#include "ua_config_default.h"
+
+#include "tests/ua_namespace_di.h"
+#include "tests/ua_namespace_adi.h"
+
+#include "check.h"
+#include "testing_clock.h"
+
+UA_Server *server = NULL;
+UA_ServerConfig *config = NULL;
+
+static void setup(void) {
+    config = UA_ServerConfig_new_default();
+    server = UA_Server_new(config);
+    UA_Server_run_startup(server);
+}
+
+static void teardown(void) {
+    UA_Server_run_shutdown(server);
+    UA_Server_delete(server);
+    UA_ServerConfig_delete(config);
+}
+
+
+START_TEST(Server_addDiNodeset) {
+    UA_StatusCode retval = ua_namespace_di(server);
+    ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+}
+END_TEST
+
+START_TEST(Server_addAdiNodeset) {
+    UA_StatusCode retval = ua_namespace_adi(server);
+    ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+}
+END_TEST
+
+static Suite* testSuite_Client(void) {
+    Suite *s = suite_create("Server Nodeset Compiler");
+    TCase *tc_server = tcase_create("Server DI and ADI nodeset");
+    tcase_add_unchecked_fixture(tc_server, setup, teardown);
+    tcase_add_test(tc_server, Server_addDiNodeset);
+    tcase_add_test(tc_server, Server_addAdiNodeset);
+    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;
+}

+ 64 - 0
tests/check_nodeset_compiler_plc.c

@@ -0,0 +1,64 @@
+/* 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_server.h"
+#include "ua_config_default.h"
+
+#include "ua_types.h"
+
+#include "tests/ua_namespace_di.h"
+#include "tests/ua_namespace_plc.h"
+
+#include "check.h"
+#include "testing_clock.h"
+
+#include "unistd.h"
+
+UA_Server *server = NULL;
+UA_ServerConfig *config = NULL;
+
+static void setup(void) {
+    config = UA_ServerConfig_new_default();
+    server = UA_Server_new(config);
+    UA_Server_run_startup(server);
+}
+
+static void teardown(void) {
+    UA_Server_run_shutdown(server);
+    UA_Server_delete(server);
+    UA_ServerConfig_delete(config);
+}
+
+
+START_TEST(Server_addDiNodeset) {
+    UA_StatusCode retval = ua_namespace_di(server);
+    ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+}
+END_TEST
+
+START_TEST(Server_addPlcNodeset) {
+    UA_StatusCode retval = ua_namespace_plc(server);
+    ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+}
+END_TEST
+
+static Suite* testSuite_Client(void) {
+    Suite *s = suite_create("Server Nodeset Compiler");
+    TCase *tc_server = tcase_create("Server DI and PLCopen nodeset");
+    tcase_add_unchecked_fixture(tc_server, setup, teardown);
+    tcase_add_test(tc_server, Server_addDiNodeset);
+    tcase_add_test(tc_server, Server_addPlcNodeset);
+    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;
+}

+ 24 - 2
tests/check_types_memory.c

@@ -14,6 +14,20 @@
 #include "ua_util.h"
 #include "check.h"
 
+// Define types to a dummy value if they are not available (e.g. not built with NS0 full)
+#ifndef UA_TYPES_UNION
+#define UA_TYPES_UNION UA_TYPES_COUNT
+#endif
+#ifndef UA_TYPES_HISTORYREADDETAILS
+#define UA_TYPES_HISTORYREADDETAILS UA_TYPES_COUNT
+#endif
+#ifndef UA_TYPES_NOTIFICATIONDATA
+#define UA_TYPES_NOTIFICATIONDATA UA_TYPES_COUNT
+#endif
+#ifndef UA_TYPES_MONITORINGFILTERRESULT
+#define UA_TYPES_MONITORINGFILTERRESULT UA_TYPES_COUNT
+#endif
+
 START_TEST(newAndEmptyObjectShallBeDeleted) {
     // given
     void *obj = UA_new(&UA_TYPES[_i]);
@@ -108,7 +122,11 @@ START_TEST(decodeShallFailWithTruncatedBufferButSurvive) {
     //Skip test for void*
     if (_i == UA_TYPES_DISCOVERYCONFIGURATION ||
             _i == UA_TYPES_FILTEROPERAND ||
-            _i == UA_TYPES_MONITORINGFILTER)
+            _i == UA_TYPES_MONITORINGFILTER ||
+            _i == UA_TYPES_UNION ||
+            _i == UA_TYPES_HISTORYREADDETAILS ||
+            _i == UA_TYPES_NOTIFICATIONDATA ||
+            _i == UA_TYPES_MONITORINGFILTERRESULT)
         return;
     // given
     UA_ByteString msg1;
@@ -212,7 +230,11 @@ START_TEST(calcSizeBinaryShallBeCorrect) {
        _i == UA_TYPES_VARIABLETYPEATTRIBUTES ||
        _i == UA_TYPES_FILTEROPERAND ||
        _i == UA_TYPES_MONITORINGFILTER ||
-       _i == UA_TYPES_DISCOVERYCONFIGURATION)
+       _i == UA_TYPES_DISCOVERYCONFIGURATION ||
+       _i == UA_TYPES_UNION ||
+       _i == UA_TYPES_HISTORYREADDETAILS ||
+       _i == UA_TYPES_NOTIFICATIONDATA ||
+       _i == UA_TYPES_MONITORINGFILTERRESULT)
         return;
     void *obj = UA_new(&UA_TYPES[_i]);
     size_t predicted_size = UA_calcSizeBinary(obj, &UA_TYPES[_i]);

+ 16 - 4
tools/generate_datatypes.py

@@ -318,6 +318,9 @@ def parseTypeDescriptions(f, namespaceid):
     input_str = f.read()
     input_str = input_str.replace('\r','')
     rows = map(lambda x:tuple(x.split(',')), input_str.split('\n'))
+
+    delay_init = []
+
     for index, row in enumerate(rows):
         if len(row) < 3:
             continue
@@ -328,10 +331,12 @@ def parseTypeDescriptions(f, namespaceid):
                 baseType = m.group(1)
                 if baseType not in types:
                     continue
-                if m.group(2) == "Xml":
-                    definitions[baseType].xmlEncodingId = row[1]
-                else:
-                    definitions[baseType].binaryEncodingId = row[1]
+
+                delay_init.append({
+                    "baseType": baseType,
+                    "encoding": m.group(2),
+                    "id": row[1]
+                })
             continue
         if row[2] != "DataType":
             continue
@@ -343,6 +348,13 @@ def parseTypeDescriptions(f, namespaceid):
             continue
         else:
             definitions[row[0]] = TypeDescription(row[0], row[1], namespaceid)
+    for i in delay_init:
+        if i["baseType"] not in definitions:
+            raise Exception("Type {} not found in definitions file.".format(i["baseType"]))
+        if i["encoding"] == "Xml":
+            definitions[i["baseType"]].xmlEncodingId = i["id"]
+        else:
+            definitions[i["baseType"]].binaryEncodingId = i["id"]
     return definitions
 
 def merge_dicts(*dict_args):

tools/pyUANamespace/NodeID_AssumeExternal.txt → tools/nodeset_compiler/NodeID_AssumeExternal.txt


+ 0 - 0
tools/pyUANamespace/NodeID_Blacklist.txt


+ 61 - 0
tools/nodeset_compiler/NodeID_NS0_Base.txt

@@ -0,0 +1,61 @@
+i=1
+i=2
+i=3
+i=4
+i=5
+i=6
+i=7
+i=8
+i=9
+i=10
+i=11
+i=12
+i=13
+i=14
+i=15
+i=16
+i=17
+i=18
+i=19
+i=20
+i=21
+i=22
+i=24
+i=26
+i=27
+i=28
+i=31
+i=32
+i=33
+i=34
+i=35
+i=36
+i=37
+i=38
+i=39
+i=40
+i=41
+i=44
+i=45
+i=45
+i=46
+i=47
+i=48
+i=49
+i=58
+i=61
+i=62
+i=63
+i=84
+i=85
+i=86
+i=87
+i=88
+i=89
+i=90
+i=91
+i=121
+i=290
+i=294
+i=295
+i=3048

+ 7 - 9
tools/pyUANamespace/README.md

@@ -3,8 +3,6 @@ pyUANamespace
 
 pyUANamespace is a collection of python 2 scripts that can parse OPC UA XML Namespace definition files and transform them into a class representation. This facilitates both reprinting the namespace in a different non-XML format (such as C-Code or DOT) and analysis of the namespace structure.
 
-
-
 ### Documentation
 
 The pyUANamespace implementation has been contributed by a research project of the chair for Process Control Systems Engineering of the TU Dresden. It was not strictly speaking created as a C generator, but could be easily modified to fullfill this role for open62541.
@@ -80,7 +78,7 @@ Further attributes may be added at a later point depending on demand.
 
 OPC UA node types, base data types and references are described in ua_node_types.py and ua_builtin_types.py. These classes are primarily intended to act as part of an AST to parse OPC UA Namespace description files. They implement a hierarchic/rekursive parsing of XML DOM objects, supplementing their respective properties from the XML description.
 
-A manager class called ua_namespace is included in the respective source file. This class does _not_ correspond to a OPC UA Namespace. It is an aggregator and manager for nodes and references which may belong to any number of namespaces. This class includes extensive parsing/validation to ensure that a complete and consistent namespace is generated.
+A manager class called NodeSet is included in the respective source file. This class does _not_ correspond to a OPC UA Namespace. It is an aggregator and manager for nodes and references which may belong to any number of namespaces. This class includes extensive parsing/validation to ensure that a complete and consistent namespace is generated.
 
 ## Namespace compilation internals
 
@@ -92,15 +90,15 @@ Compiling a namespace consists of the following steps:
 - Parse/Allocate variable values according to their dataType definitions
 
 
-Reading and parsing XML files is handled by ua_namespace.parseXML(/path/to/file.xml). It is the first part of a multipass compiler that will create all nodes contained in the XML description.
+Reading and parsing XML files is handled by NodeSet.parseXML(/path/to/file.xml). It is the first part of a multipass compiler that will create all nodes contained in the XML description.
 
 During the reading of XML files, nodes will attempt to parse any attributes they own, but not refer to any other nodes. References will be kept as XML descriptions. Any number of XML files can be read in this phase. __NOTE__: In the open62541 (this) implementation, duplicate nodes (same NodeId) are allowed. The last definition encountered will be kept. This allows overwriting specific nodes of a much larger XML file to with implementation specific nodes.
 
-The next phase of the compilation is to link all references. The phase is called by ua_namespace.linkOpenPointers(). All references will attempt to locate their target() node in the namespace and point to it.
+The next phase of the compilation is to link all references. The phase is called by NodeSet.linkOpenPointers(). All references will attempt to locate their target() node in the namespace and point to it.
 
-During the sanitation phase called by ua_namespace.sanitize(), nodes check themselves for invalid attributes. Most notably any references that could not be resolved to a node will be removed from the nodes.
+During the sanitation phase called by NodeSet.sanitize(), nodes check themselves for invalid attributes. Most notably any references that could not be resolved to a node will be removed from the nodes.
 
-When calling ua_namespace.buildEncodingRules(), dataType nodes are examined to determine if and how the can be encoded as a serialization of OPC UA builtin types as well as their aliases.
+When calling NodeSet.buildEncodingRules(), dataType nodes are examined to determine if and how the can be encoded as a serialization of OPC UA builtin types as well as their aliases.
 
 The following fragment of a variable value can illustrate the necessity for determining encoding rules:
 ```xml
@@ -123,7 +121,7 @@ LastMethodOutputArguments : Argument -> i=296
 
 DataTypes that cannot be encoded as a definite serial object (e.g. by having a member of NumericType, but not a specific one), will have their isEncodable() attribute disabled. All dataTypes that complete this node can be used to effectively determine the size and serialization properties of any variables.
 
-Having encoding rules means that data can now be parsed when a <Value> tag is encountered in a description. Calling ua_namespace.allocateVariables() will do just that for any variable node that holds XML Values.
+Having encoding rules means that data can now be parsed when a <Value> tag is encountered in a description. Calling NodeSet.allocateVariables() will do just that for any variable node that holds XML Values.
 
 The result of this compilation is a completely linked and instantiated OPC UA namespace.
 
@@ -131,7 +129,7 @@ An example compiler can be built as follows:
 ```python
 class testing:
   def __init__(self):
-    self.namespace = opcua_namespace("testing")
+    self.namespace = NodeSet("testing")
 
     log(self, "Phase 1: Reading XML file nodessets")
     self.namespace.parseXML("Opc.Ua.NodeSet2.xml")

+ 149 - 0
tools/nodeset_compiler/backend_graphviz.py

@@ -0,0 +1,149 @@
+from nodeset import *
+import graphviz as gv
+
+def nodePrintDot(node):
+    cleanname = "node_" + str(node.id).replace(";", "").replace("=", "")
+    dot = cleanname + " [label = \"{" + str(node.id) + "|" + str(node.browseName) + \
+          "}\", shape=\"record\"]"
+    for r in node.references:
+        if isinstance(r.target, Node):
+            tgtname = "node_" + str(r.target.id).replace(";", "").replace("=", "")
+            dot = dot + "\n"
+            if r.isForward == True:
+                dot = dot + cleanname + " -> " + tgtname + " [label=\"" + \
+                      str(r.referenceType.browseName) + "\"]\n"
+            else:
+                if len(r.referenceType.inverseName) == 0:
+                    logger.warn("Inverse name of reference is null " + str(r.referenceType.id))
+                dot = dot + cleanname + " -> " + tgtname + \
+                      " [label=\"" + str(r.referenceType.inverseName) + "\"]\n"
+    return dot
+
+def printDotGraphWalk(nodeset, depth=1, filename="out.dot", rootNode=None,
+                      followInverse=False, excludeNodeIds=[]):
+    """ Outputs a graphiz/dot description the nodes centered around rootNode.
+
+        References beginning from rootNode will be followed for depth steps. If
+        "followInverse = True" is passed, then inverse (not Forward) references
+        will also be followed.
+
+        Nodes can be excluded from the graph by passing a list of NodeIds as
+        string representation using excludeNodeIds (ex ["i=53", "ns=2;i=453"]).
+
+        Output is written into filename to be parsed by dot/neato/srfp...
+    """
+    iter = depth
+    processed = []
+    if rootNode == None or not isinstance(rootNode, Node) or not rootNode in nodeset.nodes:
+        root = nodeset.getRoot()
+    else:
+        root = rootNode
+
+    file = open(filename, 'w+')
+
+    if root == None:
+        return
+
+    file.write("digraph ns {\n")
+    file.write(nodePrintDot(root))
+    refs = []
+    if followInverse == True:
+        refs = root.references  # + root.getInverseReferences()
+    else:
+        for ref in root.references:
+            if ref.isForward:
+                refs.append(ref)
+    while iter > 0:
+        tmp = []
+        for ref in refs:
+            if isinstance(ref.target, NodeId):
+                tgt = nodeset.nodes[ref.target]
+                if not str(tgt.id) in excludeNodeIds:
+                    if not tgt in processed:
+                        file.write(nodePrintDot(tgt))
+                        processed.append(tgt)
+                        if ref.isForward == False and followInverse == True:
+                            for ref in tgt.inverseReferences:
+                                refs.append(ref)
+                            tmp = tmp + tgt.references  # + tgt.getInverseReferences()
+                        elif ref.isForward == True:
+                            for ref in tgt.references:
+                                refs.append(ref)
+        refs = tmp
+        iter = iter - 1
+
+    file.write("}\n")
+    file.close()
+
+def getNodeString(node):
+    return node.browseName.name + " (" + str(node.id) + ")"
+
+def getReferenceString(nodeset, ref):
+    refNode = nodeset.nodes[ref.referenceType]
+    return refNode.browseName.name
+
+def getNodeStyle(node):
+    if isinstance(node, ReferenceTypeNode):
+        return {'shape': 'box', 'style': 'filled', 'fillcolor': '1', 'colorscheme': "pastel19"}
+    if isinstance(node, VariableTypeNode):
+        return {'shape': 'box', 'style': 'filled', 'fillcolor': '2', 'colorscheme': "pastel19"}
+    if isinstance(node, ObjectTypeNode):
+        return {'shape': 'box', 'style': 'filled', 'fillcolor': '3', 'colorscheme': "pastel19"}
+    if isinstance(node, DataTypeNode):
+        return {'shape': 'box', 'style': 'filled', 'fillcolor': '4', 'colorscheme': "pastel19"}
+    if isinstance(node, VariableNode):
+        return {'shape': 'ellipse', 'style': 'rounded,filled', 'fillcolor': '5', 'colorscheme': "pastel19"}
+    if isinstance(node, ObjectNode):
+        return {'shape': 'box', 'style': 'rounded,filled', 'fillcolor': '6', 'colorscheme': "pastel19"}
+    if isinstance(node, MethodNode):
+        return {'shape': 'box', 'style': 'rounded,filled', 'fillcolor': '7', 'colorscheme': "pastel19"}
+    if isinstance(node, ViewNode):
+        return {'shape': 'box', 'style': 'rounded,filled', 'fillcolor': '8', 'colorscheme': "pastel19"}
+
+def add_edges(graph, edges):
+    for e in edges:
+        if isinstance(e[0], tuple):
+            graph.edge(*e[0], **e[1])
+        else:
+            graph.edge(*e)
+    return graph
+
+def add_nodes(graph, nodes):
+    for n in nodes:
+        if isinstance(n, tuple):
+            graph.node(n[0], **n[1])
+        else:
+            graph.node(n)
+    return graph
+
+def addReferenceToGraph(nodeset, nodeFrom, nodeTo, reference, graph):
+    add_edges(graph, [((getNodeString(nodeFrom), getNodeString(nodeTo)), {'label': getReferenceString(nodeset, reference)})])
+
+
+def addNodeToGraph(nodeset, node, graph, alreadyAdded=[], relevantReferences=[], isRoot=False, depth = 0):
+    if node.id in alreadyAdded:
+        return
+    alreadyAdded.append(node.id)
+    add_nodes(graph, [(getNodeString(node), getNodeStyle(node))])
+    for ref in node.references:
+        if ref.referenceType in relevantReferences and ref.isForward:
+            targetNode = nodeset.nodes[ref.target]
+            addNodeToGraph(nodeset, targetNode, graph, alreadyAdded, depth=depth+1, relevantReferences=relevantReferences)
+            addReferenceToGraph(nodeset, node, targetNode, ref, graph)
+
+
+def printDependencyGraph(nodeset, filename="dependencies", rootNode=None, excludeNodeIds=[]):
+    if rootNode == None or not isinstance(rootNode, Node) or not rootNode in nodeset.nodes:
+        root = nodeset.getRoot()
+    else:
+        root = rootNode
+
+    if root == None:
+        return
+
+    g = gv.Digraph(name="NodeSet Dependency", format='pdf')
+
+    alreadyAdded = []
+    addNodeToGraph(nodeset, root, g, alreadyAdded, isRoot=True, relevantReferences=nodeset.getRelevantOrderingReferences())
+
+    g.render(filename)

+ 257 - 0
tools/nodeset_compiler/backend_open62541.py

@@ -0,0 +1,257 @@
+#!/usr/bin/env/python
+# -*- coding: utf-8 -*-
+
+###
+### Authors:
+### - Chris Iatrou (ichrispa@core-vector.net)
+### - Julius Pfrommer
+### - Stefan Profanter (profanter@fortiss.org)
+###
+### 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 LGPL 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.
+###
+
+from __future__ import print_function
+import string
+from collections import deque
+from os.path import basename
+import logging
+try:
+    from StringIO import StringIO
+except ImportError:
+    from io import StringIO
+
+logger = logging.getLogger(__name__)
+
+from constants import *
+from nodes import *
+from nodeset import *
+from backend_open62541_nodes import generateNodeCode, generateReferenceCode
+
+##############
+# Sort Nodes #
+##############
+
+# Select the references that shall be generated after this node in the ordering
+# If both nodes of the reference are hidden we assume that the references between
+# those nodes are already setup. Still print if only the target node is hidden,
+# because we need that reference.
+def selectPrintRefs(nodeset, L, node):
+    printRefs = []
+    for ref in node.references:
+        targetnode = nodeset.nodes[ref.target]
+        if node.hidden and targetnode.hidden:
+            continue
+        if not targetnode.hidden and not targetnode in L:
+            continue
+        printRefs.append(ref)
+    for ref in node.inverseReferences:
+        targetnode = nodeset.nodes[ref.target]
+        if node.hidden and targetnode.hidden:
+            continue
+        if not targetnode.hidden and not targetnode in L:
+            continue
+        printRefs.append(ref)
+    return printRefs
+
+def addTypeRef(nodeset, type_refs, dataTypeId, referencedById):
+    if not dataTypeId in type_refs:
+        type_refs[dataTypeId] = [referencedById]
+    else:
+        type_refs[dataTypeId].append(referencedById)
+
+
+def reorderNodesMinDependencies(nodeset):
+    # Kahn's algorithm
+    # https://algocoding.wordpress.com/2015/04/05/topological-sorting-python/
+
+    relevant_types = nodeset.getRelevantOrderingReferences()
+
+    # determine in-degree
+    in_degree = {u.id: 0 for u in nodeset.nodes.values()}
+    dataType_refs = {}
+    hiddenCount = 0
+    for u in nodeset.nodes.values():  # of each node
+        if u.hidden:
+            hiddenCount += 1
+            continue
+        hasTypeDef = None
+        for ref in u.references:
+            if ref.referenceType.i == 40:
+                hasTypeDef = ref.target
+            elif (ref.referenceType in relevant_types and ref.isForward) and not nodeset.nodes[ref.target].hidden:
+                in_degree[ref.target] += 1
+        if hasTypeDef is not None and not nodeset.nodes[hasTypeDef].hidden:
+            # we cannot print the node u because it first needs the variable type node
+            in_degree[u.id] += 1
+
+        if isinstance(u, VariableNode) and u.dataType is not None:
+            dataTypeNode = nodeset.getDataTypeNode(u.dataType)
+            if dataTypeNode is not None and not dataTypeNode.hidden:
+                # we cannot print the node u because it first needs the data type node
+                in_degree[u.id] += 1
+                # to be able to decrement the in_degree count, we need to store it here
+                addTypeRef(nodeset, dataType_refs,dataTypeNode.id, u.id)
+
+    # collect nodes with zero in-degree
+    Q = deque()
+    for id in in_degree:
+        if in_degree[id] == 0:
+            # print referencetypenodes first
+            n = nodeset.nodes[id]
+            if isinstance(n, ReferenceTypeNode):
+                Q.append(nodeset.nodes[id])
+            else:
+                Q.appendleft(nodeset.nodes[id])
+
+    L = []  # list for order of nodes
+    while Q:
+        u = Q.pop()  # choose node of zero in-degree
+        # decide which references to print now based on the ordering
+        u.printRefs = selectPrintRefs(nodeset, L, u)
+        if u.hidden:
+            continue
+
+        L.append(u)  # and 'remove' it from graph
+
+        if isinstance(u, DataTypeNode):
+            # decrement all the nodes which depend on this datatype
+            if u.id in dataType_refs:
+                for n in dataType_refs[u.id]:
+                    if not nodeset.nodes[n].hidden:
+                        in_degree[n] -= 1
+                    if in_degree[n] == 0:
+                        Q.append(nodeset.nodes[n])
+                del dataType_refs[u.id]
+
+        for ref in u.inverseReferences:
+            if ref.referenceType.i == 40:
+                if not nodeset.nodes[ref.target].hidden:
+                    in_degree[ref.target] -= 1
+                if in_degree[ref.target] == 0:
+                    Q.append(nodeset.nodes[ref.target])
+
+        for ref in u.references:
+            if (ref.referenceType in relevant_types and ref.isForward):
+                if not nodeset.nodes[ref.target].hidden:
+                    in_degree[ref.target] -= 1
+                if in_degree[ref.target] == 0:
+                    Q.append(nodeset.nodes[ref.target])
+
+    if len(L) + hiddenCount != len(nodeset.nodes.values()):
+        stillOpen = ""
+        for id in in_degree:
+            if in_degree[id] == 0:
+                continue
+            node = nodeset.nodes[id]
+            stillOpen += node.browseName.name + "/" + str(node.id) + " = " + str(in_degree[id]) + "\r\n"
+        raise Exception("Node graph is circular on the specified references. Still open nodes:\r\n" + stillOpen)
+    return L
+
+###################
+# Generate C Code #
+###################
+
+def generateOpen62541Code(nodeset, outfilename, supressGenerationOfAttribute=[], generate_ns0=False, internal_headers=False, typesArray=[], max_string_length=0):
+    outfilebase = basename(outfilename)
+    # Printing functions
+    outfileh = open(outfilename + ".h", r"w+")
+    outfilec = StringIO()
+
+    def writeh(line):
+        print(unicode(line), end='\n', file=outfileh)
+
+    def writec(line):
+        print(unicode(line), end='\n', file=outfilec)
+
+    additionalHeaders = ""
+    if len(typesArray) > 0:
+        for arr in set(typesArray):
+            if arr == "UA_TYPES":
+                continue
+            additionalHeaders += """#include "%s_generated.h"\n""" % arr.lower()
+
+    # Print the preamble of the generated code
+    writeh("""/* WARNING: This is a generated file.
+ * Any manual changes will be overwritten. */
+
+#ifndef %s_H_
+#define %s_H_
+""" % (outfilebase.upper(), outfilebase.upper()))
+    if internal_headers:
+        writeh("""
+#ifdef UA_NO_AMALGAMATION
+#include "ua_server.h"
+#include "ua_types_encoding_binary.h"
+%s
+#else
+#include "open62541.h"
+#endif
+""" % (additionalHeaders))
+    else:
+        writeh("""
+#include "open62541.h"
+""")
+    writeh("""
+extern UA_StatusCode %s(UA_Server *server);
+
+#endif /* %s_H_ */""" % \
+           (outfilebase, outfilebase.upper()))
+
+    writec("""/* WARNING: This is a generated file.
+ * Any manual changes will be overwritten. */
+
+#include "%s.h"
+
+UA_StatusCode %s(UA_Server *server) {  // NOLINT
+
+UA_StatusCode retVal = UA_STATUSCODE_GOOD;
+""" % (outfilebase, outfilebase))
+
+    parentrefs = getSubTypesOf(nodeset, nodeset.getNodeByBrowseName("HierarchicalReferences"))
+    parentrefs = list(map(lambda x: x.id, parentrefs))
+
+    # Generate namespaces (don't worry about duplicates)
+    writec("/* Use namespace ids generated by the server */")
+    for i, nsid in enumerate(nodeset.namespaces):
+        nsid = nsid.replace("\"", "\\\"")
+        writec("UA_UInt16 ns" + str(i) + " = UA_Server_addNamespace(server, \"" + nsid + "\");")
+
+    # Loop over the sorted nodes
+    logger.info("Reordering nodes for minimal dependencies during printing")
+    sorted_nodes = reorderNodesMinDependencies(nodeset)
+    logger.info("Writing code for nodes and references")
+    for node in sorted_nodes:
+        # Print node
+        if not node.hidden:
+            writec("\n/* " + str(node.displayName) + " - " + str(node.id) + " */")
+            code = generateNodeCode(node, supressGenerationOfAttribute, generate_ns0, parentrefs, nodeset, max_string_length)
+            if code is None:
+                writec("/* Ignored. No parent */")
+                nodeset.hide_node(node.id)
+                continue
+            else:
+                writec(code)
+
+        # Print inverse references leading to this node
+        for ref in node.printRefs:
+            writec(generateReferenceCode(ref))
+
+    # Finalize the generated source
+    writec("return retVal;")
+    writec("} // closing nodeset()")
+
+    outfileh.close()
+    fullCode = outfilec.getvalue()
+    outfilec.close()
+
+    outfilec = open(outfilename + ".c", r"w+")
+    outfilec.write(fullCode)
+    outfilec.close()

+ 103 - 0
tools/nodeset_compiler/backend_open62541_datatypes.py

@@ -0,0 +1,103 @@
+from datatypes import *
+import datetime
+import re
+
+import logging
+
+logger = logging.getLogger(__name__)
+
+def generateBooleanCode(value):
+    if value:
+        return "true"
+    return "false"
+
+def splitStringLiterals(value, splitLength=500, max_string_length=0):
+    """
+    Split a string literal longer than splitLength into smaller literals.
+    E.g. "Some very long text" will be split into "Some ver" "y long te" "xt"
+    On VS2008 there is a maximum allowed length of a single string literal.
+    If maxLength is set and the string is longer than maxLength, then an
+    empty string will be returned.
+    """
+    value = value.strip()
+    if max_string_length > 0 and len(value) > max_string_length:
+        logger.info("String is longer than {}. Returning empty string.".format(max_string_length))
+        return "\"\""
+    if len(value) < splitLength or splitLength == 0:
+        return "\"" + value.replace('"', r'\"') + "\""
+    ret = ""
+    tmp = value
+    while len(tmp) > splitLength:
+        ret += "\"" + tmp[:splitLength].replace('"', r'\"') + "\" "
+        tmp = tmp[splitLength:]
+    ret += "\"" + tmp.replace('"', r'\"') + "\" "
+    return ret
+
+def generateStringCode(value, alloc=False, max_string_length=0):
+    return "UA_STRING{}({})".format("_ALLOC" if alloc else "", splitStringLiterals(value, max_string_length=max_string_length))
+
+def generateXmlElementCode(value, alloc=False, max_string_length=0):
+    return "UA_XMLELEMENT{}({})".format("_ALLOC" if alloc else "", splitStringLiterals(value, max_string_length=max_string_length))
+
+def generateByteStringCode(value, alloc=False, max_string_length=0):
+    return "UA_BYTESTRING{}({})".format("_ALLOC" if alloc else "", splitStringLiterals(value, max_string_length=max_string_length))
+
+def generateLocalizedTextCode(value, alloc=False, max_string_length=0):
+    return "UA_LOCALIZEDTEXT{}(\"{}\", {})".format("_ALLOC" if alloc else "",
+                                                       value.locale, splitStringLiterals(value.text, max_string_length=max_string_length))
+
+def generateQualifiedNameCode(value, alloc=False, max_string_length=0):
+    return "UA_QUALIFIEDNAME{}(ns{}, {})".format("_ALLOC" if alloc else "",
+                                                     str(value.ns), splitStringLiterals(value.name, max_string_length=max_string_length))
+
+def generateNodeIdCode(value):
+    if not value:
+        return "UA_NODEID_NUMERIC(0,0)"
+    if value.i != None:
+        return "UA_NODEID_NUMERIC(ns%s,%s)" % (value.ns, value.i)
+    elif value.s != None:
+        return "UA_NODEID_STRING(ns%s,%s)" % (value.ns, value.s.replace('"', r'\"'))
+    raise Exception(str(value) + " no NodeID generation for bytestring and guid..")
+
+def generateExpandedNodeIdCode(value):
+    if value.i != None:
+        return "UA_EXPANDEDNODEID_NUMERIC(ns%s, %s)" % (str(value.ns), str(value.i))
+    elif value.s != None:
+        return "UA_EXPANDEDNODEID_STRING(ns%s, %s)" % (str(value.ns), value.s.replace('"', r'\"'))
+    raise Exception(str(value) + " no NodeID generation for bytestring and guid..")
+
+def generateDateTimeCode(value):
+    epoch = datetime.datetime.utcfromtimestamp(0)
+    mSecsSinceEpoch = (value - epoch).total_seconds() * 1000.0
+    return "( (" + str(mSecsSinceEpoch) + "f * UA_MSEC_TO_DATETIME) + UA_DATETIME_UNIX_EPOCH)"
+
+def generateNodeValueCode(node, instanceName, asIndirect=False, max_string_length=0):
+    if type(node) in [Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Float, Double]:
+        return "(UA_" + node.__class__.__name__ + ") " + str(node.value)
+    elif type(node) == String:
+        return generateStringCode(node.value, asIndirect, max_string_length)
+    elif type(node) == XmlElement:
+        return generateXmlElementCode(node.value, asIndirect, max_string_length)
+    elif type(node) == ByteString:
+        # replace whitespaces between tags and remove newlines
+        return generateByteStringCode(re.sub(r">\s*<", "><", re.sub(r"[\r\n]+", "", node.value)), asIndirect, max_string_length)
+    elif type(node) == LocalizedText:
+        return generateLocalizedTextCode(node, asIndirect, max_string_length)
+    elif type(node) == NodeId:
+        return generateNodeIdCode(node)
+    elif type(node) == ExpandedNodeId:
+        return generateExpandedNodeIdCode(node)
+    elif type(node) == DateTime:
+        return generateDateTimeCode(node.value)
+    elif type(node) == QualifiedName:
+        return generateQualifiedNameCode(node.value, asIndirect, max_string_length)
+    elif type(node) == StatusCode:
+        raise Exception("generateNodeValueCode for type " + node.__class__.name + " not implemented")
+    elif type(node) == DiagnosticInfo:
+        raise Exception("generateNodeValueCode for type " + node.__class__.name + " not implemented")
+    elif type(node) == Guid:
+        raise Exception("generateNodeValueCode for type " + node.__class__.name + " not implemented")
+    elif type(node) == ExtensionObject:
+        if asIndirect == False:
+            return "*" + str(instanceName)
+        return str(instanceName)

+ 522 - 0
tools/nodeset_compiler/backend_open62541_nodes.py

@@ -0,0 +1,522 @@
+#!/usr/bin/env/python
+# -*- coding: utf-8 -*-
+
+###
+### 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 LGPL 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.
+###
+
+from nodes import *
+from backend_open62541_datatypes import *
+import re
+import datetime
+
+###########################################
+# Extract References with Special Meaning #
+###########################################
+
+import logging
+
+logger = logging.getLogger(__name__)
+
+def extractNodeParent(node, parentrefs):
+    """Return a tuple of the most likely (parent, parentReference). The
+    parentReference is removed form the inverse references list of the node.
+
+    """
+    for ref in node.inverseReferences:
+        if ref.referenceType in parentrefs:
+            node.inverseReferences.remove(ref)
+            if ref in node.printRefs:
+                node.printRefs.remove(ref)
+            return (ref.target, ref.referenceType)
+    return None, None
+
+def extractNodeType(node):
+    """Returns the most likely type of the variable- or objecttype node. The
+     isinstanceof reference is removed form the inverse references list of the
+     node.
+
+    """
+    pass
+
+def extractNodeSuperType(node):
+    """Returns the most likely supertype of the variable-, object-, or referencetype
+       node. The reference to the supertype is removed from the inverse
+       references list of the node.
+
+    """
+    pass
+
+#################
+# Generate Code #
+#################
+
+def generateNodeIdPrintable(node):
+    CodePrintable = "NODE_"
+
+    if isinstance(node.id, NodeId):
+        CodePrintable = node.__class__.__name__ + "_" + str(node.id)
+    else:
+        CodePrintable = node.__class__.__name__ + "_unknown_nid"
+
+    return re.sub('[^0-9a-z_]+', '_', CodePrintable.lower())
+
+def generateNodeValueInstanceName(node, parent, recursionDepth, arrayIndex):
+    return generateNodeIdPrintable(parent) + "_" + str(node.alias) + "_" + str(arrayIndex) + "_" + str(recursionDepth)
+
+def generateReferenceCode(reference):
+    if reference.isForward:
+        return "retVal |= UA_Server_addReference(server, %s, %s, %s, true);" % \
+               (generateNodeIdCode(reference.source),
+                generateNodeIdCode(reference.referenceType),
+                generateExpandedNodeIdCode(reference.target))
+    else:
+        return "retVal |= UA_Server_addReference(server, %s, %s, %s, false);" % \
+               (generateNodeIdCode(reference.source),
+                generateNodeIdCode(reference.referenceType),
+                generateExpandedNodeIdCode(reference.target))
+
+def generateReferenceTypeNodeCode(node):
+    code = []
+    code.append("UA_ReferenceTypeAttributes attr = UA_ReferenceTypeAttributes_default;")
+    if node.isAbstract:
+        code.append("attr.isAbstract = true;")
+    if node.symmetric:
+        code.append("attr.symmetric  = true;")
+    if node.inverseName != "":
+        code.append("attr.inverseName  = UA_LOCALIZEDTEXT(\"\", \"%s\");" % \
+                    node.inverseName)
+    return code
+
+def generateObjectNodeCode(node):
+    code = []
+    code.append("UA_ObjectAttributes attr = UA_ObjectAttributes_default;")
+    if node.eventNotifier:
+        code.append("attr.eventNotifier = true;")
+    return code
+
+def generateVariableNodeCode(node, nodeset, max_string_length):
+    code = []
+    codeCleanup = []
+    code.append("UA_VariableAttributes attr = UA_VariableAttributes_default;")
+    if node.historizing:
+        code.append("attr.historizing = true;")
+    code.append("attr.minimumSamplingInterval = %f;" % node.minimumSamplingInterval)
+    code.append("attr.userAccessLevel = %d;" % node.userAccessLevel)
+    code.append("attr.accessLevel = %d;" % node.accessLevel)
+    code.append("attr.valueRank = %d;" % node.valueRank)
+    if node.valueRank > 0:
+        code.append("attr.arrayDimensionsSize = %d;" % node.valueRank)
+        code.append("attr.arrayDimensions = (UA_UInt32 *)UA_Array_new({}, &UA_TYPES[UA_TYPES_UINT32]);".format(node.valueRank))
+        codeCleanup.append("UA_Array_delete(attr.arrayDimensions, {}, &UA_TYPES[UA_TYPES_UINT32]);".format(node.valueRank))
+        for dim in range(0, node.valueRank):
+            code.append("attr.arrayDimensions[{}] = 0;".format(dim))
+
+    if node.dataType is not None:
+        if isinstance(node.dataType, NodeId) and node.dataType.ns == 0 and node.dataType.i == 0:
+            #BaseDataType
+            dataTypeNode = nodeset.nodes[NodeId("i=24")]
+        else:
+            dataTypeNode = nodeset.getBaseDataType(nodeset.getDataTypeNode(node.dataType))
+
+        if dataTypeNode is not None:
+            code.append("attr.dataType = %s;" % generateNodeIdCode(dataTypeNode.id))
+
+            if dataTypeNode.isEncodable():
+                if node.value is not None:
+                    [code1, codeCleanup1] = generateValueCode(node.value, nodeset.nodes[node.id], nodeset, max_string_length=max_string_length)
+                    code += code1
+                    codeCleanup += codeCleanup1
+                else:
+                    code += generateValueCodeDummy(dataTypeNode, nodeset.nodes[node.id], nodeset)
+    return [code, codeCleanup]
+
+def generateVariableTypeNodeCode(node, nodeset, max_string_length):
+    code = []
+    codeCleanup = []
+    code.append("UA_VariableTypeAttributes attr = UA_VariableTypeAttributes_default;")
+    if node.historizing:
+        code.append("attr.historizing = true;")
+    if node.isAbstract:
+        code.append("attr.isAbstract = true;")
+    code.append("attr.valueRank = (UA_Int32)%s;" % str(node.valueRank))
+    if node.dataType is not None:
+        if isinstance(node.dataType, NodeId) and node.dataType.ns == 0 and node.dataType.i == 0:
+            #BaseDataType
+            dataTypeNode = nodeset.nodes[NodeId("i=24")]
+        else:
+            dataTypeNode = nodeset.getBaseDataType(nodeset.getDataTypeNode(node.dataType))
+        if dataTypeNode is not None:
+            code.append("attr.dataType = %s;" % generateNodeIdCode(dataTypeNode.id))
+            if dataTypeNode.isEncodable():
+                if node.value is not None:
+                    [code1, codeCleanup1] = generateValueCode(node.value, nodeset.nodes[node.id], nodeset, max_string_length)
+                    code += code1
+                    codeCleanup += codeCleanup1
+                else:
+                    code += generateValueCodeDummy(dataTypeNode, nodeset.nodes[node.id], nodeset)
+    return [code, codeCleanup]
+
+def generateExtensionObjectSubtypeCode(node, parent, nodeset, recursionDepth=0, arrayIndex=0, max_string_length=0):
+    code = [""]
+    codeCleanup = [""]
+
+    logger.debug("Building extensionObject for " + str(parent.id))
+    logger.debug("Value    " + str(node.value))
+    logger.debug("Encoding " + str(node.encodingRule))
+
+    instanceName = generateNodeValueInstanceName(node, parent, recursionDepth, arrayIndex)
+    # If there are any ExtensionObjects instide this ExtensionObject, we need to
+    # generate one-time-structs for them too before we can proceed;
+    for subv in node.value:
+        if isinstance(subv, list):
+            logger.error("ExtensionObject contains an ExtensionObject, which is currently not encodable!")
+
+    code.append("struct {")
+    for field in node.encodingRule:
+        ptrSym = ""
+        # If this is an Array, this is pointer to its contents with a AliasOfFieldSize entry
+        if field[2] != 0:
+            code.append("  UA_Int32 " + str(field[0]) + "Size;")
+            ptrSym = "*"
+        if len(field[1]) == 1:
+            code.append("  UA_" + str(field[1][0]) + " " + ptrSym + str(field[0]) + ";")
+        else:
+            code.append("  UA_ExtensionObject " + " " + ptrSym + str(field[0]) + ";")
+    code.append("} " + instanceName + "_struct;")
+
+    # Assign data to the struct contents
+    # Track the encoding rule definition to detect arrays and/or ExtensionObjects
+    encFieldIdx = 0
+    for subv in node.value:
+        encField = node.encodingRule[encFieldIdx]
+        encFieldIdx = encFieldIdx + 1
+        logger.debug(
+            "Encoding of field " + subv.alias + " is " + str(subv.encodingRule) + "defined by " + str(encField))
+        # Check if this is an array
+        if encField[2] == 0:
+            code.append(instanceName + "_struct." + subv.alias + " = " +
+                        generateNodeValueCode(subv, instanceName, asIndirect=False, max_string_length=max_string_length) + ";")
+        else:
+            if isinstance(subv, list):
+                # this is an array
+                code.append(instanceName + "_struct." + subv.alias + "Size = " + str(len(subv)) + ";")
+                code.append(
+                     "{0}_struct.{1} = (UA_{2}*) UA_malloc(sizeof(UA_{2})*{3});".format(
+                         instanceName, subv.alias, subv.__class__.__name__, str(len(subv))))
+                codeCleanup.append("UA_free({0}_struct.{1});".format(instanceName, subv.alias))
+                logger.debug("Encoding included array of " + str(len(subv)) + " values.")
+                for subvidx in range(0, len(subv)):
+                    subvv = subv[subvidx]
+                    logger.debug("  " + str(subvidx) + " " + str(subvv))
+                    code.append(instanceName + "_struct." + subv.alias + "[" + str(
+                        subvidx) + "] = " + generateNodeValueCode(subvv, instanceName, max_string_length=max_string_length) + ";")
+                code.append("}")
+            else:
+                code.append(instanceName + "_struct." + subv.alias + "Size = 1;")
+                code.append(
+                    "{0}_struct.{1} = (UA_{2}*) UA_malloc(sizeof(UA_{2}));".format(
+                        instanceName, subv.alias, subv.__class__.__name__))
+                codeCleanup.append("UA_free({0}_struct.{1});".format(instanceName, subv.alias))
+
+                code.append(instanceName + "_struct." + subv.alias + "[0]  = " +
+                            generateNodeValueCode(subv, instanceName, asIndirect=True, max_string_length=max_string_length) + ";")
+
+    # Allocate some memory
+    code.append("UA_ExtensionObject *" + instanceName + " =  UA_ExtensionObject_new();")
+    codeCleanup.append("UA_ExtensionObject_delete(" + instanceName + ");")
+    code.append(instanceName + "->encoding = UA_EXTENSIONOBJECT_ENCODED_BYTESTRING;")
+    #if parent.dataType.ns == 0:
+
+    binaryEncodingId = nodeset.getBinaryEncodingIdForNode(parent.dataType)
+    code.append(
+        instanceName + "->content.encoded.typeId = UA_NODEID_NUMERIC(" + str(binaryEncodingId.ns) + ", " +
+        str(binaryEncodingId.i) + ");")
+    code.append(
+        "if(UA_ByteString_allocBuffer(&" + instanceName + "->content.encoded.body, 65000) != UA_STATUSCODE_GOOD) {}")
+
+    # Encode each value as a bytestring seperately.
+    code.append("UA_Byte *pos" + instanceName + " = " + instanceName + "->content.encoded.body.data;")
+    code.append("const UA_Byte *end" + instanceName + " = &" + instanceName + "->content.encoded.body.data[65000];")
+    encFieldIdx = 0
+    code.append("{")
+    for subv in node.value:
+        encField = node.encodingRule[encFieldIdx]
+        encFieldIdx = encFieldIdx + 1
+        if encField[2] == 0:
+            code.append(
+                "retVal |= UA_encodeBinary(&" + instanceName + "_struct." + subv.alias + ", " +
+                getTypesArrayForValue(nodeset, subv) + ", &pos" + instanceName + ", &end" + instanceName + ", NULL, NULL);")
+        else:
+            if isinstance(subv, list):
+                for subvidx in range(0, len(subv)):
+                    code.append("retVal |= UA_encodeBinary(&" + instanceName + "_struct." + subv.alias + "[" +
+                                str(subvidx) + "], " + getTypesArrayForValue(nodeset, subv) + ", &pos" +
+                                instanceName + ", &end" + instanceName + ", NULL, NULL);")
+            else:
+                code.append(
+                    "retVal |= UA_encodeBinary(&" + instanceName + "_struct." + subv.alias + "[0], " +
+                    getTypesArrayForValue(nodeset, subv) + ", &pos" + instanceName + ", &end" + instanceName + ", NULL, NULL);")
+
+    code.append("}")
+    # Reallocate the memory by swapping the 65k Bytestring for a new one
+    code.append("size_t " + instanceName + "_encOffset = (uintptr_t)(" +
+                "pos" + instanceName + "-" + instanceName + "->content.encoded.body.data);")
+    code.append(instanceName + "->content.encoded.body.length = " + instanceName + "_encOffset;")
+    code.append("UA_Byte *" + instanceName + "_newBody = (UA_Byte *) UA_malloc(" + instanceName + "_encOffset );")
+    code.append("memcpy(" + instanceName + "_newBody, " + instanceName + "->content.encoded.body.data, " +
+                instanceName + "_encOffset);")
+    code.append("UA_Byte *" + instanceName + "_oldBody = " + instanceName + "->content.encoded.body.data;")
+    code.append(instanceName + "->content.encoded.body.data = " + instanceName + "_newBody;")
+    code.append("UA_free(" + instanceName + "_oldBody);")
+    code.append("")
+    return [code, codeCleanup]
+
+
+def generateValueCodeDummy(dataTypeNode, parentNode, nodeset, bootstrapping=True):
+    code = []
+    valueName = generateNodeIdPrintable(parentNode) + "_variant_DataContents"
+
+    typeBrowseNode = dataTypeNode.browseName.name
+    if typeBrowseNode == "NumericRange":
+        # in the stack we define a separate structure for the numeric range, but the value itself is just a string
+        typeBrowseNode = "String"
+
+    typeArr = dataTypeNode.typesArray + "[" + dataTypeNode.typesArray + "_" + typeBrowseNode.upper() + "]"
+    typeStr = "UA_" + typeBrowseNode
+
+    if parentNode.valueRank > 0:
+        code.append(typeStr + " *" + valueName + " = (" + typeStr + "*) UA_alloca(" + typeArr + ".memSize * " + str(parentNode.valueRank) + ");")
+        for i in range(0, parentNode.valueRank):
+            code.append("UA_init(&" + valueName + "[" + str(i) + "], &" + typeArr + ");")
+            code.append("UA_Variant_setArray( &attr.value, " + valueName + ", (UA_Int32) " +
+                        str(parentNode.valueRank) + ", &" + typeArr + ");")
+    else:
+        code.append("void *" + valueName + " = UA_alloca(" + typeArr + ".memSize);")
+        code.append("UA_init(" + valueName + ", &" + typeArr + ");")
+        code.append("UA_Variant_setScalar(&attr.value, " + valueName + ", &" + typeArr + ");")
+
+    return code
+
+def getTypesArrayForValue(nodeset, value):
+    typeNode = nodeset.getNodeByBrowseName(value.__class__.__name__)
+    if typeNode is None:
+        typesArray = "UA_TYPES"
+    else:
+        typesArray = typeNode.typesArray
+    return "&" + typesArray + "[" + typesArray + "_" + \
+                    value.__class__.__name__.upper() + "]"
+
+def generateValueCode(node, parentNode, nodeset, bootstrapping=True, max_string_length=0):
+    code = []
+    codeCleanup = []
+    valueName = generateNodeIdPrintable(parentNode) + "_variant_DataContents"
+
+    # node.value either contains a list of multiple identical BUILTINTYPES, or it
+    # contains a single builtintype (which may be a container); choose if we need
+    # to create an array or a single variable.
+    # Note that some genious defined that there are arrays of size 1, which are
+    # distinctly different then a single value, so we need to check that as well
+    # Semantics:
+    # -3: Scalar or 1-dim
+    # -2: Scalar or x-dim | x>0
+    # -1: Scalar
+    #  0: x-dim | x>0
+    #  n: n-dim | n>0
+    if (len(node.value) == 0):
+        return ["", ""]
+    if not isinstance(node.value[0], Value):
+        return ["", ""]
+
+    if parentNode.valueRank != -1 and (parentNode.valueRank >= 0
+                                       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:
+            logger.warn("Don't know how to print array of GUID in node " + str(parentNode.id))
+        elif node.value[0].numericRepresentation == BUILTINTYPE_TYPEID_DATETIME:
+            logger.warn("Don't know how to print array of DateTime in node " + str(parentNode.id))
+        elif node.value[0].numericRepresentation == BUILTINTYPE_TYPEID_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:
+            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:
+                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)
+                    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:
+                for idx, v in enumerate(node.value):
+                    logger.debug("Printing extObj array index " + str(idx))
+                    instanceName = generateNodeValueInstanceName(v, parentNode, 0, idx)
+                    code.append(
+                        valueName + "[" + str(idx) + "] = " +
+                        generateNodeValueCode(v, instanceName, max_string_length=max_string_length) + ";")
+                    # code.append("UA_free(&" +valueName + "[" + str(idx) + "]);")
+            else:
+                for idx, v in enumerate(node.value):
+                    instanceName = generateNodeValueInstanceName(v, parentNode, 0, idx)
+                    code.append(
+                        valueName + "[" + str(idx) + "] = " + generateNodeValueCode(v, instanceName, max_string_length=max_string_length) + ";")
+            code.append("UA_Variant_setArray( &attr.value, &" + valueName +
+                        ", (UA_Int32) " + str(len(node.value)) + ", " +
+                        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:
+            logger.warn("Don't know how to print scalar GUID in node " + str(parentNode.id))
+        elif node.value[0].numericRepresentation == BUILTINTYPE_TYPEID_DATETIME:
+            logger.warn("Don't know how to print scalar DateTime in node " + str(parentNode.id))
+        elif node.value[0].numericRepresentation == BUILTINTYPE_TYPEID_DIAGNOSTICINFO:
+            logger.warn("Don't know how to print scalar DiagnosticInfo in node " + str(parentNode.id))
+        elif node.value[0].numericRepresentation == BUILTINTYPE_TYPEID_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:
+                [code1, codeCleanup1] = generateExtensionObjectSubtypeCode(node.value[0], parent=parentNode, nodeset=nodeset, max_string_length=max_string_length)
+                code = code + code1
+                codeCleanup = codeCleanup + codeCleanup1
+            instanceName = generateNodeValueInstanceName(node.value[0], parentNode, 0, 0)
+            if node.value[0].numericRepresentation == BUILTINTYPE_TYPEID_EXTENSIONOBJECT:
+                code.append("UA_" + node.value[0].__class__.__name__ + " *" + valueName + " = " +
+                            generateNodeValueCode(node.value[0], instanceName, max_string_length=max_string_length) + ";")
+                code.append(
+                    "UA_Variant_setScalar( &attr.value, " + valueName + ", " +
+                    getTypesArrayForValue(nodeset, node.value[0]) + ");")
+
+                # FIXME: There is no membership definition for extensionObjects generated in this function.
+                # code.append("UA_" + node.value[0].__class__.__name__ + "_deleteMembers(" + valueName + ");")
+            else:
+                code.append("UA_" + node.value[0].__class__.__name__ + " *" + valueName + " =  UA_" + node.value[
+                    0].__class__.__name__ + "_new();")
+                code.append("*" + valueName + " = " + generateNodeValueCode(node.value[0], instanceName, asIndirect=True, max_string_length=max_string_length) + ";")
+                code.append(
+                        "UA_Variant_setScalar( &attr.value, " + valueName + ", " +
+                        getTypesArrayForValue(nodeset, node.value[0]) + ");")
+                codeCleanup.append("UA_{0}_delete({1});".format(
+                    node.value[0].__class__.__name__, valueName))
+    return [code, codeCleanup]
+
+def generateMethodNodeCode(node):
+    code = []
+    code.append("UA_MethodAttributes attr = UA_MethodAttributes_default;")
+    if node.executable:
+        code.append("attr.executable = true;")
+    if node.userExecutable:
+        code.append("attr.userExecutable = true;")
+    return code
+
+def generateObjectTypeNodeCode(node):
+    code = []
+    code.append("UA_ObjectTypeAttributes attr = UA_ObjectTypeAttributes_default;")
+    if node.isAbstract:
+        code.append("attr.isAbstract = true;")
+    return code
+
+def generateDataTypeNodeCode(node):
+    code = []
+    code.append("UA_DataTypeAttributes attr = UA_DataTypeAttributes_default;")
+    if node.isAbstract:
+        code.append("attr.isAbstract = true;")
+    return code
+
+def generateViewNodeCode(node):
+    code = []
+    code.append("UA_ViewAttributes attr = UA_ViewAttributes_default;")
+    if node.containsNoLoops:
+        code.append("attr.containsNoLoops = true;")
+    code.append("attr.eventNotifier = (UA_Byte)%s;" % str(node.eventNotifier))
+    return code
+
+def getNodeTypeDefinition(node):
+    for ref in node.references:
+        # 40 = HasTypeDefinition
+        if ref.referenceType.i == 40:
+            return ref.target
+    return None
+
+def generateSubtypeOfDefinitionCode(node):
+    for ref in node.inverseReferences:
+        # 45 = HasSubtype
+        if ref.referenceType.i == 45:
+            return generateNodeIdCode(ref.target)
+    return "UA_NODEID_NULL"
+
+def generateNodeCode(node, supressGenerationOfAttribute, generate_ns0, parentrefs, nodeset, max_string_length):
+    code = []
+    code.append("{")
+
+    codeCleanup = []
+
+    if isinstance(node, ReferenceTypeNode):
+        code.extend(generateReferenceTypeNodeCode(node))
+    elif isinstance(node, ObjectNode):
+        code.extend(generateObjectNodeCode(node))
+    elif isinstance(node, VariableNode) and not isinstance(node, VariableTypeNode):
+        [code1, codeCleanup1] = generateVariableNodeCode(node, nodeset, max_string_length)
+        code.extend(code1)
+        codeCleanup.extend(codeCleanup1)
+    elif isinstance(node, VariableTypeNode):
+        [code1, codeCleanup1] = generateVariableTypeNodeCode(node, nodeset, max_string_length)
+        code.extend(code1)
+        codeCleanup.extend(codeCleanup1)
+    elif isinstance(node, MethodNode):
+        code.extend(generateMethodNodeCode(node))
+    elif isinstance(node, ObjectTypeNode):
+        code.extend(generateObjectTypeNodeCode(node))
+    elif isinstance(node, DataTypeNode):
+        code.extend(generateDataTypeNodeCode(node))
+    elif isinstance(node, ViewNode):
+        code.extend(generateViewNodeCode(node))
+
+    code.append("attr.displayName = " + generateLocalizedTextCode(node.displayName, max_string_length) + ";")
+    code.append("attr.description = " + generateLocalizedTextCode(node.description, max_string_length) + ";")
+    code.append("attr.writeMask = %d;" % node.writeMask)
+    code.append("attr.userWriteMask = %d;" % node.userWriteMask)
+
+
+    typeDef = getNodeTypeDefinition(node)
+    isDataTypeEncodingType = typeDef is not None and typeDef.ns == 0 and typeDef.i == 76
+
+    # Object nodes of type DataTypeEncoding do not have any parent
+    if not generate_ns0 and not isDataTypeEncodingType:
+        (parentNode, parentRef) = extractNodeParent(node, parentrefs)
+        if parentNode is None or parentRef is None:
+            return None
+    else:
+        (parentNode, parentRef) = (NodeId(), NodeId())
+
+    code.append("retVal |= UA_Server_add%s(server," % node.__class__.__name__)
+    code.append(generateNodeIdCode(node.id) + ",")
+    code.append(generateNodeIdCode(parentNode) + ",")
+    code.append(generateNodeIdCode(parentRef) + ",")
+    code.append(generateQualifiedNameCode(node.browseName) + ",")
+    if isinstance(node, VariableTypeNode):
+        # we need the HasSubtype reference
+        code.append(generateSubtypeOfDefinitionCode(node) + ",")
+    elif isinstance(node, VariableNode) or isinstance(node, ObjectNode):
+        typeDefCode = "UA_NODEID_NULL" if typeDef is None else generateNodeIdCode(typeDef)
+        code.append(typeDefCode + ",")
+    code.append("attr,")
+    if isinstance(node, MethodNode):
+        code.append("NULL, 0, NULL, 0, NULL, NULL, NULL);")
+    else:
+        code.append("NULL, NULL);")
+    code.extend(codeCleanup)
+    code.append("}\n")
+    return "\n".join(code)

+ 13 - 10
tools/pyUANamespace/ua_constants.py

@@ -20,18 +20,18 @@
 ### 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
+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
+NODE_CLASS_METHODTYPE = 256
 
 ##
 ## Numeric codes used to encode binary type fields:
@@ -59,3 +59,6 @@ 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

+ 724 - 0
tools/nodeset_compiler/datatypes.py

@@ -0,0 +1,724 @@
+#!/usr/bin/env/python
+# -*- coding: utf-8 -*-
+
+###
+### 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 LGPL 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.
+###
+
+import sys
+from time import strftime, strptime
+import logging
+
+logger = logging.getLogger(__name__)
+import xml.dom.minidom as dom
+
+from constants import *
+from base64 import *
+
+import six
+
+if sys.version_info[0] >= 3:
+    # strings are already parsed to unicode
+    def unicode(s):
+        return s
+
+
+def getNextElementNode(xmlvalue):
+    if xmlvalue == None:
+        return None
+    xmlvalue = xmlvalue.nextSibling
+    while not xmlvalue == None and not xmlvalue.nodeType == xmlvalue.ELEMENT_NODE:
+        xmlvalue = xmlvalue.nextSibling
+    return xmlvalue
+
+def valueIsInternalType(valueTypeString):
+    return valueTypeString.lower() in ['boolean', 'number', 'int32', 'uint32', 'int16', 'uint16',
+                   'int64', 'uint64', 'byte', 'sbyte', 'float', 'double',
+                   'string', 'bytestring', 'localizedtext', 'statuscode',
+                   'diagnosticinfo', 'nodeid', 'guid', 'datetime',
+                   'qualifiedname', 'expandednodeid', 'xmlelement', 'integer', 'uinteger']
+
+class Value(object):
+    def __init__(self, xmlelement=None):
+        self.value = None
+        self.numericRepresentation = 0
+        self.alias = None
+        self.dataType = None
+        self.encodingRule = []
+        if xmlelement:
+            self.parseXML(xmlelement)
+
+    def getValueFieldByAlias(self, fieldname):
+        if not isinstance(self.value, list):
+            return None
+        if not isinstance(self.value[0], Value):
+            return None
+        for val in self.value:
+            if val.alias() == fieldname:
+                return val.value
+        return None
+
+    def getTypeByString(self, stringName, encodingRule):
+        stringName = str(stringName.lower())
+        if stringName == 'boolean':
+            t = Boolean()
+        elif stringName == 'number':
+            t = Number()
+        elif stringName == 'integer':
+            t = Integer()
+        elif stringName == 'uinteger':
+            t = UInteger()
+        elif stringName == 'int32':
+            t = Int32()
+        elif stringName == 'uint32':
+            t = UInt32()
+        elif stringName == 'int16':
+            t = Int16()
+        elif stringName == 'uint16':
+            t = UInt16()
+        elif stringName == 'int64':
+            t = Int64()
+        elif stringName == 'uint64':
+            t = UInt64()
+        elif stringName == 'byte':
+            t = Byte()
+        elif stringName == 'sbyte':
+            t = SByte()
+        elif stringName == 'float':
+            t = Float()
+        elif stringName == 'double':
+            t = Double()
+        elif stringName == 'string':
+            t = String()
+        elif stringName == 'bytestring':
+            t = ByteString()
+        elif stringName == 'localizedtext':
+            t = LocalizedText()
+        elif stringName == 'statuscode':
+            t = StatusCode()
+        elif stringName == 'diagnosticinfo':
+            t = DiagnosticInfo()
+        elif stringName == 'nodeid':
+            t = NodeId()
+        elif stringName == 'guid':
+            t = Guid()
+        elif stringName == 'datetime':
+            t = DateTime()
+        elif stringName == 'qualifiedname':
+            t = QualifiedName()
+        elif stringName == 'expandednodeid':
+            t = ExpandedNodeId()
+        elif stringName == 'xmlelement':
+            t = XmlElement()
+        else:
+            logger.debug("No class representing stringName " + stringName + " was found. Cannot create builtinType.")
+            return None
+        t.encodingRule = encodingRule
+        return t
+
+    def checkXML(self, xmlvalue):
+        if xmlvalue == None or xmlvalue.nodeType != xmlvalue.ELEMENT_NODE:
+            logger.error("Expected XML Element, but got junk...")
+            return
+
+    def parseXML(self, xmlvalue):
+        raise Exception("Cannot parse arbitrary value of no type.")
+
+    def parseXMLEncoding(self, xmlvalue, parentDataTypeNode):
+        self.checkXML(xmlvalue)
+        if not "value" in xmlvalue.localName.lower():
+            logger.error("Expected <Value> , but found " + xmlvalue.localName + \
+                         " instead. Value will not be parsed.")
+            return
+
+        if len(xmlvalue.childNodes) == 0:
+            logger.error("Expected childnodes for value, but none were found...")
+            return
+
+        for n in xmlvalue.childNodes:
+            if n.nodeType == n.ELEMENT_NODE:
+                xmlvalue = n
+                break
+
+        if "ListOf" in xmlvalue.localName:
+            self.value = []
+            for el in xmlvalue.childNodes:
+                if not el.nodeType == el.ELEMENT_NODE:
+                    continue
+                self.value.append(self.__parseXMLSingleValue(el, parentDataTypeNode))
+        else:
+            self.value = [self.__parseXMLSingleValue(xmlvalue, parentDataTypeNode)]
+
+    def __parseXMLSingleValue(self, xmlvalue, parentDataTypeNode, alias=None, encodingPart=None):
+        # Parse an encoding list such as enc = [[Int32], ['Duration', ['DateTime']]],
+        # returning a possibly aliased variable or list of variables.
+        # Keep track of aliases, as ['Duration', ['Hawaii', ['UtcTime', ['DateTime']]]]
+        # will be of type DateTime, but tagged as <Duration>2013-04-10 12:00 UTC</Duration>,
+        # and not as <Duration><Hawaii><UtcTime><String>2013-04-10 12:00 UTC</String>...
+
+        # Encoding may be partially handed down (iterative call). Only resort to
+        # type definition if we are not given a specific encoding to match
+        if encodingPart == None:
+            enc = parentDataTypeNode.getEncoding()
+        else:
+            enc = encodingPart
+
+        # Check the structure of the encoding list to determine if a type is to be
+        # returned or we need to descend further checking aliases or multipart types
+        # such as extension Objects.
+        if len(enc) == 1:
+            # 0: ['BuiltinType']          either builtin type
+            # 1: [ [ 'Alias', [...], n] ] or single alias for possible multipart
+            if isinstance(enc[0], six.string_types):
+                # 0: 'BuiltinType'
+                if alias != None:
+                    if not xmlvalue.localName == alias:
+                        logger.error("Expected XML element with tag " + alias + " but found " + xmlvalue.localName + " instead")
+                        return None
+                    else:
+                        t = self.getTypeByString(enc[0], enc)
+                        t.alias = alias
+                        t.parseXML(xmlvalue)
+                        return t
+                else:
+                    if not valueIsInternalType(xmlvalue.localName):
+                        logger.error("Expected XML describing builtin type " + enc[0] + " but found " + xmlvalue.localName + " instead")
+                    else:
+                        t = self.getTypeByString(enc[0], enc)
+                        t.parseXML(xmlvalue)
+                        return t
+            else:
+                # 1: ['Alias', [...], n]
+                # Let the next elif handle this
+                return self.__parseXMLSingleValue(xmlvalue, parentDataTypeNode, alias=alias, encodingPart=enc[0])
+        elif len(enc) == 3 and isinstance(enc[0], six.string_types):
+            # [ 'Alias', [...], 0 ]          aliased multipart
+            if alias == None:
+                alias = enc[0]
+            # if we have an alias and the next field is multipart, keep the alias
+            elif alias != None and len(enc[1]) > 1:
+                alias = enc[0]
+            # otherwise drop the alias
+            return self.__parseXMLSingleValue(xmlvalue, parentDataTypeNode, alias=alias, encodingPart=enc[1])
+        else:
+            # [ [...], [...], [...]] multifield of unknowns (analyse separately)
+            # create an extension object to hold multipart type
+
+            # FIXME: This implementation expects an extensionobject to be manditory for
+            #        multipart variables. Variants/Structures are not included in the
+            #        OPCUA Namespace 0 nodeset.
+            #        Consider moving this ExtensionObject specific parsing into the
+            #        builtin type and only determining the multipart type at this stage.
+            if not xmlvalue.localName == "ExtensionObject":
+                logger.error("Expected XML tag <ExtensionObject> for multipart type, but found " + xmlvalue.localName + " instead.")
+                return None
+
+            extobj = ExtensionObject()
+            extobj.encodingRule = enc
+            etype = xmlvalue.getElementsByTagName("TypeId")
+            if len(etype) == 0:
+                logger.error("Did not find <TypeId> for ExtensionObject")
+                return None
+            etype = etype[0].getElementsByTagName("Identifier")
+            if len(etype) == 0:
+                logger.error("Did not find <Identifier> for ExtensionObject")
+                return None
+
+            etype = NodeId(etype[0].firstChild.data.strip(' \t\n\r'))
+            extobj.typeId = etype
+
+            ebody = xmlvalue.getElementsByTagName("Body")
+            if len(ebody) == 0:
+                logger.error("Did not find <Body> for ExtensionObject")
+                return None
+            ebody = ebody[0]
+
+            # Body must contain an Object of type 'DataType' as defined in Variable
+            ebodypart = ebody.firstChild
+            if not ebodypart.nodeType == ebodypart.ELEMENT_NODE:
+                ebodypart = getNextElementNode(ebodypart)
+            if ebodypart == None:
+                logger.error("Expected ExtensionObject to hold a variable of type " + str(parentDataTypeNode.browseName) + " but found nothing.")
+                return None
+
+            if not ebodypart.localName == parentDataTypeNode.browseName.name:
+                logger.error("Expected ExtensionObject to hold a variable of type " + str(parentDataTypeNode.browseName) + " but found " +
+                             str(ebodypart.localName) + " instead.")
+                return None
+            extobj.alias = ebodypart.localName
+
+            ebodypart = ebodypart.firstChild
+            if not ebodypart.nodeType == ebodypart.ELEMENT_NODE:
+                ebodypart = getNextElementNode(ebodypart)
+            if ebodypart == None:
+                logger.error("Description of dataType " + str(parentDataTypeNode.browseName) + " in ExtensionObject is empty/invalid.")
+                return None
+
+            extobj.value = []
+            for e in enc:
+                if not ebodypart == None:
+                    extobj.value.append(extobj.__parseXMLSingleValue(ebodypart, parentDataTypeNode, alias=None, encodingPart=e))
+                else:
+                    logger.error("Expected encoding " + str(e) + " but found none in body.")
+                ebodypart = getNextElementNode(ebodypart)
+            return extobj
+
+    def __str__(self):
+        return self.__class__.__name__ + "(" + str(self.value) + ")"
+
+    def __repr__(self):
+        return self.__str__()
+
+#################
+# Builtin Types #
+#################
+
+class Boolean(Value):
+    def __init__(self, xmlelement=None):
+        Value.__init__(self)
+        self.numericRepresentation = BUILTINTYPE_TYPEID_BOOLEAN
+        if xmlelement:
+            self.parseXML(xmlelement)
+
+    def parseXML(self, xmlvalue):
+        # Expect <Boolean>value</Boolean> or
+        #        <Aliasname>value</Aliasname>
+        self.checkXML(xmlvalue)
+        if xmlvalue.firstChild == None:
+            self.value = "false"  # Catch XML <Boolean /> by setting the value to a default
+        else:
+            if "false" in unicode(xmlvalue.firstChild.data).lower():
+                self.value = "false"
+            else:
+                self.value = "true"
+
+class Number(Value):
+    def __init__(self, xmlelement=None):
+        Value.__init__(self)
+        self.numericRepresentation = BUILTINTYPE_TYPEID_NUMBER
+        if xmlelement:
+            self.parseXML(xmlelement)
+
+    def parseXML(self, xmlvalue):
+        # Expect <Int16>value</Int16> or any other valid number type, or
+        #        <Aliasname>value</Aliasname>
+        self.checkXML(xmlvalue)
+        if xmlvalue.firstChild == None:
+            self.value = 0  # Catch XML <Int16 /> by setting the value to a default
+        else:
+            self.value = int(unicode(xmlvalue.firstChild.data))
+
+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)
+
+    def parseXML(self, xmlvalue):
+        # Expect <Float>value</Float> or
+        #        <Aliasname>value</Aliasname>
+        self.checkXML(xmlvalue)
+        if xmlvalue.firstChild == None:
+            self.value = 0.0  # Catch XML <Float /> by setting the value to a default
+        else:
+            self.value = float(unicode(xmlvalue.firstChild.data))
+
+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)
+
+    def pack(self):
+        bin = structpack("I", len(unicode(self.value)))
+        bin = bin + str(self.value)
+        return bin
+
+    def parseXML(self, xmlvalue):
+        # Expect <String>value</String> or
+        #        <Aliasname>value</Aliasname>
+        if not isinstance(xmlvalue, dom.Element):
+            self.value = xmlvalue
+            return
+        self.checkXML(xmlvalue)
+        if xmlvalue.firstChild == None:
+            self.value = ""  # Catch XML <String /> by setting the value to a default
+        else:
+            self.value = unicode(xmlvalue.firstChild.data)
+
+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>
+        if not isinstance(xmlvalue, dom.Element):
+            self.value = xmlvalue
+            return
+        self.checkXML(xmlvalue)
+        if xmlvalue.firstChild == None:
+            self.value = []  # Catch XML <ByteString /> by setting the value to a default
+        else:
+            self.value = b64decode(xmlvalue.firstChild.data).decode("utf-8")
+
+class ExtensionObject(Value):
+    def __init__(self, xmlelement=None):
+        Value.__init__(self)
+        self.numericRepresentation = BUILTINTYPE_TYPEID_EXTENSIONOBJECT
+        if xmlelement:
+            self.parseXML(xmlelement)
+
+    def parseXML(self, xmlelement):
+        pass
+
+    def __str__(self):
+        return "'" + self.alias() + "':" + self.stringRepresentation + "(" + str(self.value) + ")"
+
+class LocalizedText(Value):
+    def __init__(self, xmlvalue=None):
+        Value.__init__(self)
+        self.numericRepresentation = BUILTINTYPE_TYPEID_LOCALIZEDTEXT
+        self.locale = ''
+        self.text = ''
+        if xmlvalue:
+            self.parseXML(xmlvalue)
+
+    def parseXML(self, xmlvalue):
+        # Expect <LocalizedText> or <AliasName>
+        #          <Locale>xx_XX</Locale>
+        #          <Text>TextText</Text>
+        #        <LocalizedText> or </AliasName>
+        if not isinstance(xmlvalue, dom.Element):
+            self.text = xmlvalue
+            return
+        self.checkXML(xmlvalue)
+        tmp = xmlvalue.getElementsByTagName("Locale")
+        if len(tmp) > 0 and tmp[0].firstChild != None:
+            self.locale = tmp[0].firstChild.data.strip(' \t\n\r')
+        tmp = xmlvalue.getElementsByTagName("Text")
+        if len(tmp) > 0 and tmp[0].firstChild != None:
+            self.text = tmp[0].firstChild.data.strip(' \t\n\r')
+
+    def __str__(self):
+        if self.locale is not None and len(self.locale) > 0:
+            return "(" + self.locale + ":" + self.text + ")"
+        else:
+            return self.text
+
+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
+        self.s = None
+        self.ns = 0
+        self.setFromIdString(idstring)
+
+    def setFromIdString(self, idstring):
+
+        if not idstring:
+            self.i = 0
+            return
+
+        # The ID will encoding itself appropriatly as string. If multiple ID's
+        # (numeric, string, guid) are defined, the order of preference for the ID
+        # string is always numeric, guid, bytestring, string. Binary encoding only
+        # applies to numeric values (UInt16).
+        idparts = idstring.strip().split(";")
+        for p in idparts:
+            if p[:2] == "ns":
+                self.ns = int(p[3:])
+            elif p[:2] == "i=":
+                self.i = int(p[2:])
+            elif p[:2] == "o=":
+                self.b = p[2:]
+            elif p[:2] == "g=":
+                tmp = []
+                self.g = p[2:].split("-")
+                for i in self.g:
+                    i = "0x" + i
+                    tmp.append(int(i, 16))
+                self.g = tmp
+            elif p[:2] == "s=":
+                self.s = p[2:]
+            else:
+                raise Exception("no valid nodeid: " + idstring)
+
+    def parseXML(self, xmlvalue):
+        # Expect <NodeId> or <Alias>
+        #           <Identifier> # It is unclear whether or not this is manditory. Identifier tags are used in Namespace 0.
+        #                ns=x;i=y or similar string representation of id()
+        #           </Identifier>
+        #        </NodeId> or </Alias>
+        if not isinstance(xmlvalue, dom.Element):
+            self.text = xmlvalue
+            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...")
+            self.value = None
+        else:
+            # Check if there is an <Identifier> tag
+            if len(xmlvalue.getElementsByTagName("Identifier")) != 0:
+                xmlvalue = xmlvalue.getElementsByTagName("Identifier")[0]
+            self.setFromIdString(unicode(xmlvalue.firstChild.data))
+
+    def __str__(self):
+        s = "ns=" + str(self.ns) + ";"
+        # Order of preference is numeric, guid, bytestring, string
+        if self.i != None:
+            return s + "i=" + str(self.i)
+        elif self.g != None:
+            s = s + "g="
+            tmp = []
+            for i in self.g:
+                tmp.append(hex(i).replace("0x", ""))
+            for i in tmp:
+                s = s + "-" + i
+            return s.replace("g=-", "g=")
+        elif self.b != None:
+            return s + "b=" + str(self.b)
+        elif self.s != None:
+            return s + "s=" + str(self.s)
+
+    def __eq__(self, nodeId2):
+        return (str(self) == str(nodeId2))
+
+    def __repr__(self):
+        return str(self)
+
+    def __hash__(self):
+        return hash(str(self))
+
+class ExpandedNodeId(Value):
+    def __init__(self, xmlelement=None):
+        Value.__init__(self)
+        self.numericRepresentation = BUILTINTYPE_TYPEID_EXPANDEDNODEID
+        if xmlelement:
+            self.parseXML(xmlelement)
+
+    def parseXML(self, xmlvalue):
+        self.checkXML(xmlvalue)
+        logger.debug("Not implemented", LOG_LEVEL_ERR)
+
+class DateTime(Value):
+    def __init__(self, xmlelement=None):
+        Value.__init__(self)
+        self.numericRepresentation = BUILTINTYPE_TYPEID_DATETIME
+        if xmlelement:
+            self.parseXML(xmlelement)
+
+    def parseXML(self, xmlvalue):
+        # Expect <DateTime> or <AliasName>
+        #        2013-08-13T21:00:05.0000L
+        #        </DateTime> or </AliasName>
+        self.checkXML(xmlvalue)
+        if xmlvalue.firstChild == None:
+            # Catch XML <DateTime /> by setting the value to a default
+            self.value = strptime(strftime("%Y-%m-%dT%H:%M%S"), "%Y-%m-%dT%H:%M%S")
+        else:
+            timestr = unicode(xmlvalue.firstChild.data)
+            # .NET tends to create this garbage %Y-%m-%dT%H:%M:%S.0000z
+            # strip everything after the "." away for a posix time_struct
+            if "." in timestr:
+                timestr = timestr[:timestr.index(".")]
+            # If the last character is not numeric, remove it
+            while len(timestr) > 0 and not timestr[-1] in "0123456789":
+                timestr = timestr[:-1]
+            try:
+                self.value = strptime(timestr, "%Y-%m-%dT%H:%M:%S")
+            except:
+                try:
+                    self.value = strptime(timestr, "%Y-%m-%d")
+                except:
+                    logger.error("Timestring format is illegible. Expected 2001-01-30T21:22:23 or 2001-01-30, but got " + \
+                                 timestr + " instead. Time will be defaultet to now()")
+                    self.value = strptime(strftime("%Y-%m-%dT%H:%M%S"), "%Y-%m-%dT%H:%M%S")
+
+class QualifiedName(Value):
+    def __init__(self, xmlelement=None):
+        Value.__init__(self)
+        self.numericRepresentation = BUILTINTYPE_TYPEID_QUALIFIEDNAME
+        self.ns = 0
+        self.name = ''
+        if xmlelement:
+            self.parseXML(xmlelement)
+
+    def parseXML(self, xmlvalue):
+        # Expect <QualifiedName> or <AliasName>
+        #           <NamespaceIndex>Int16<NamespaceIndex>
+        #           <Name>SomeString<Name>
+        #        </QualifiedName> or </AliasName>
+        if not isinstance(xmlvalue, dom.Element):
+            colonindex = xmlvalue.find(":")
+            if colonindex == -1:
+                self.name = xmlvalue
+            else:
+                self.name = xmlvalue[colonindex + 1:]
+                self.ns = int(xmlvalue[:colonindex])
+            return
+
+        self.checkXML(xmlvalue)
+        # Is a namespace index passed?
+        if len(xmlvalue.getElementsByTagName("NamespaceIndex")) != 0:
+            self.ns = int(xmlvalue.getElementsByTagName("NamespaceIndex")[0].firstChild.data)
+        if len(xmlvalue.getElementsByTagName("Name")) != 0:
+            self.name = xmlvalue.getElementsByTagName("Name")[0].firstChild.data
+
+    def __str__(self):
+        return "ns=" + str(self.ns) + ";" + str(self.name)
+
+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)
+
+    def parseXML(self, xmlvalue):
+        self.checkXML(xmlvalue)
+        logger.warn("Not implemented")
+
+class Guid(Value):
+    def __init__(self, xmlelement=None):
+        Value.__init__(self)
+        self.numericRepresentation = BUILTINTYPE_TYPEID_GUID
+        if xmlelement:
+            self.parseXML(xmlelement)
+
+    def parseXML(self, xmlvalue):
+        self.checkXML(xmlvalue)
+        if xmlvalue.firstChild == None:
+            self.value = [0, 0, 0, 0]  # Catch XML <Guid /> by setting the value to a default
+        else:
+            self.value = unicode(xmlvalue.firstChild.data)
+            self.value = self.value.replace("{", "")
+            self.value = self.value.replace("}", "")
+            self.value = self.value.split("-")
+            tmp = []
+            for g in self.value:
+                try:
+                    tmp.append(int("0x" + g, 16))
+                except:
+                    logger.error("Invalid formatting of Guid. Expected {01234567-89AB-CDEF-ABCD-0123456789AB}, got " + \
+                                 unicode(xmlvalue.firstChild.data))
+                    tmp = [0, 0, 0, 0, 0]
+            if len(tmp) != 5:
+                logger.error("Invalid formatting of Guid. Expected {01234567-89AB-CDEF-ABCD-0123456789AB}, got " + \
+                             unicode(xmlvalue.firstChild.data))
+                tmp = [0, 0, 0, 0]
+            self.value = tmp

+ 625 - 0
tools/nodeset_compiler/nodes.py

@@ -0,0 +1,625 @@
+#!/usr/bin/env/python
+# -*- coding: utf-8 -*-
+
+###
+### 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 LGPL 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.
+###
+
+import sys
+import logging
+from datatypes import *
+from constants import *
+
+logger = logging.getLogger(__name__)
+
+if sys.version_info[0] >= 3:
+    # strings are already parsed to unicode
+    def unicode(s):
+        return s
+
+class Reference(object):
+    # all either nodeids or strings with an alias
+    def __init__(self, source, referenceType, target, isForward=True, hidden=False, inferred=False):
+        self.source = source
+        self.referenceType = referenceType
+        self.target = target
+        self.isForward = isForward
+        self.hidden = hidden  # the reference is part of a nodeset that already exists
+        self.inferred = inferred
+
+    def __str__(self):
+        retval = str(self.source)
+        if not self.isForward:
+            retval = retval + "<"
+        retval = retval + "--[" + str(self.referenceType) + "]--"
+        if self.isForward:
+            retval = retval + ">"
+        return retval + str(self.target)
+
+    def __repr__(self):
+        return str(self)
+
+    def __eq__(self, other):
+        return str(self) == str(other)
+
+    def __hash__(self):
+        return hash(str(self))
+
+class Node(object):
+    def __init__(self):
+        self.id = NodeId()
+        self.nodeClass = NODE_CLASS_GENERERIC
+        self.browseName = QualifiedName()
+        self.displayName = LocalizedText()
+        self.description = LocalizedText()
+        self.symbolicName = String()
+        self.writeMask = 0
+        self.userWriteMask = 0
+        self.references = set()
+        self.inverseReferences = set()
+        self.hidden = False
+
+    def __str__(self):
+        return self.__class__.__name__ + "(" + str(self.id) + ")"
+
+    def __repr__(self):
+        return str(self)
+
+    def sanitize(self):
+        pass
+
+    def parseXML(self, xmlelement):
+        for idname in ['NodeId', 'NodeID', 'nodeid']:
+            if xmlelement.hasAttribute(idname):
+                self.id = NodeId(xmlelement.getAttribute(idname))
+
+        for (at, av) in xmlelement.attributes.items():
+            if at == "BrowseName":
+                self.browseName = QualifiedName(av)
+            elif at == "DisplayName":
+                self.displayName = LocalizedText(av)
+            elif at == "Description":
+                self.description = LocalizedText(av)
+            elif at == "WriteMask":
+                self.writeMask = int(av)
+            elif at == "UserWriteMask":
+                self.userWriteMask = int(av)
+            elif at == "EventNotifier":
+                self.eventNotifier = int(av)
+            elif at == "SymbolicName":
+                self.symbolicName = String(av)
+
+        for x in xmlelement.childNodes:
+            if x.nodeType != x.ELEMENT_NODE:
+                continue
+            if x.firstChild:
+                if x.localName == "BrowseName":
+                    self.browseName = QualifiedName(x.firstChild.data)
+                elif x.localName == "DisplayName":
+                    self.displayName = LocalizedText(x.firstChild.data)
+                elif x.localName == "Description":
+                    self.description = LocalizedText(x.firstChild.data)
+                elif x.localName == "WriteMask":
+                    self.writeMask = int(unicode(x.firstChild.data))
+                elif x.localName == "UserWriteMask":
+                    self.userWriteMask = int(unicode(x.firstChild.data))
+                if x.localName == "References":
+                    self.parseXMLReferences(x)
+
+    def parseXMLReferences(self, xmlelement):
+        for ref in xmlelement.childNodes:
+            if ref.nodeType != ref.ELEMENT_NODE:
+                continue
+            source = NodeId(str(self.id))  # deep-copy of the nodeid
+            target = NodeId(ref.firstChild.data)
+            reftype = None
+            forward = True
+            for (at, av) in ref.attributes.items():
+                if at == "ReferenceType":
+                    if '=' in av:
+                        reftype = NodeId(av)
+                    else:
+                        reftype = av  # alias, such as "HasSubType"
+                elif at == "IsForward":
+                    forward = not "false" in av.lower()
+            if forward:
+                self.references.add(Reference(source, reftype, target, forward))
+            else:
+                self.inverseReferences.add(Reference(source, reftype, target, forward))
+
+    def replaceAliases(self, aliases):
+        if str(self.id) in aliases:
+            self.id = NodeId(aliases[self.id])
+        new_refs = set()
+        for ref in self.references:
+            if str(ref.source) in aliases:
+                ref.source = NodeId(aliases[ref.source])
+            if str(ref.target) in aliases:
+                ref.target = NodeId(aliases[ref.target])
+            if str(ref.referenceType) in aliases:
+                ref.referenceType = NodeId(aliases[ref.referenceType])
+            new_refs.add(ref)
+        self.references = new_refs
+        new_inv_refs = set()
+        for ref in self.inverseReferences:
+            if str(ref.source) in aliases:
+                ref.source = NodeId(aliases[ref.source])
+            if str(ref.target) in aliases:
+                ref.target = NodeId(aliases[ref.target])
+            if str(ref.referenceType) in aliases:
+                ref.referenceType = NodeId(aliases[ref.referenceType])
+            new_inv_refs.add(ref)
+        self.inverseReferences = new_inv_refs
+
+    def replaceNamespaces(self, nsMapping):
+        self.id.ns = nsMapping[self.id.ns]
+        self.browseName.ns = nsMapping[self.browseName.ns]
+        if hasattr(self, 'dataType') and isinstance(self.dataType, NodeId):
+            self.dataType.ns = nsMapping[self.dataType.ns]
+
+        new_refs = set()
+        for ref in self.references:
+            ref.source.ns = nsMapping[ref.source.ns]
+            ref.target.ns = nsMapping[ref.target.ns]
+            ref.referenceType.ns = nsMapping[ref.referenceType.ns]
+            new_refs.add(ref)
+        self.references = new_refs
+        new_inv_refs = set()
+        for ref in self.inverseReferences:
+            ref.source.ns = nsMapping[ref.source.ns]
+            ref.target.ns = nsMapping[ref.target.ns]
+            ref.referenceType.ns = nsMapping[ref.referenceType.ns]
+            new_inv_refs.add(ref)
+        self.inverseReferences = new_inv_refs
+
+class ReferenceTypeNode(Node):
+    def __init__(self, xmlelement=None):
+        Node.__init__(self)
+        self.nodeClass = NODE_CLASS_REFERENCETYPE
+        self.isAbstract = False
+        self.symmetric = False
+        self.inverseName = ""
+        if xmlelement:
+            self.parseXML(xmlelement)
+
+    def parseXML(self, xmlelement):
+        Node.parseXML(self, xmlelement)
+        for (at, av) in xmlelement.attributes.items():
+            if at == "Symmetric":
+                self.symmetric = "false" not in av.lower()
+            elif at == "InverseName":
+                self.inverseName = str(av)
+            elif at == "IsAbstract":
+                self.isAbstract = "false" not in av.lower()
+
+        for x in xmlelement.childNodes:
+            if x.nodeType == x.ELEMENT_NODE:
+                if x.localName == "InverseName" and x.firstChild:
+                    self.inverseName = str(unicode(x.firstChild.data))
+
+class ObjectNode(Node):
+    def __init__(self, xmlelement=None):
+        Node.__init__(self)
+        self.nodeClass = NODE_CLASS_OBJECT
+        self.eventNotifier = 0
+        if xmlelement:
+            self.parseXML(xmlelement)
+
+    def parseXML(self, xmlelement):
+        Node.parseXML(self, xmlelement)
+        for (at, av) in xmlelement.attributes.items():
+            if at == "EventNotifier":
+                self.eventNotifier = int(av)
+
+class VariableNode(Node):
+    def __init__(self, xmlelement=None):
+        Node.__init__(self)
+        self.nodeClass = NODE_CLASS_VARIABLE
+        self.dataType = NodeId()
+        self.valueRank = -2
+        self.arrayDimensions = []
+        # Set access levels to read by default
+        self.accessLevel = 1
+        self.userAccessLevel = 1
+        self.minimumSamplingInterval = 0.0
+        self.historizing = False
+        self.value = None
+        self.xmlValueDef = None
+        if xmlelement:
+            self.parseXML(xmlelement)
+
+    def parseXML(self, xmlelement):
+        Node.parseXML(self, xmlelement)
+        for (at, av) in xmlelement.attributes.items():
+            if at == "ValueRank":
+                self.valueRank = int(av)
+            elif at == "AccessLevel":
+                self.accessLevel = int(av)
+            elif at == "UserAccessLevel":
+                self.userAccessLevel = int(av)
+            elif at == "MinimumSamplingInterval":
+                self.minimumSamplingInterval = float(av)
+            elif at == "DataType":
+                if "=" in av:
+                    self.dataType = NodeId(av)
+                else:
+                    self.dataType = av
+
+        for x in xmlelement.childNodes:
+            if x.nodeType != x.ELEMENT_NODE:
+                continue
+            if x.localName == "Value":
+                self.xmlValueDef = x
+            elif x.localName == "DataType":
+                self.dataType = NodeId(str(x))
+            elif x.localName == "ValueRank":
+                self.valueRank = int(unicode(x.firstChild.data))
+            elif x.localName == "ArrayDimensions":
+                self.arrayDimensions = int(unicode(x.firstChild.data))
+            elif x.localName == "AccessLevel":
+                self.accessLevel = int(unicode(x.firstChild.data))
+            elif x.localName == "UserAccessLevel":
+                self.userAccessLevel = int(unicode(x.firstChild.data))
+            elif x.localName == "MinimumSamplingInterval":
+                self.minimumSamplingInterval = float(unicode(x.firstChild.data))
+            elif x.localName == "Historizing":
+                self.historizing = "false" not in x.lower()
+
+    def allocateValue(self, nodeset):
+        dataTypeNode = nodeset.getDataTypeNode(self.dataType)
+        if dataTypeNode is None:
+            return False
+
+        # FIXME: Don't build at all or allocate "defaults"? I'm for not building at all.
+        if self.xmlValueDef == None:
+            #logger.warn("Variable " + self.browseName() + "/" + str(self.id()) + " is not initialized. No memory will be allocated.")
+            return False
+
+        self.value = Value()
+        self.value.parseXMLEncoding(self.xmlValueDef, dataTypeNode)
+
+        # Array Dimensions must accurately represent the value and will be patched
+        # reflect the exaxt dimensions attached binary stream.
+        if not isinstance(self.value, Value) or len(self.value.value) == 0:
+            self.arrayDimensions = []
+        else:
+            # Parser only permits 1-d arrays, which means we do not have to check further dimensions
+            self.arrayDimensions = [len(self.value.value)]
+        return True
+
+
+class VariableTypeNode(VariableNode):
+    def __init__(self, xmlelement=None):
+        VariableNode.__init__(self)
+        self.nodeClass = NODE_CLASS_VARIABLETYPE
+        self.isAbstract = False
+        if xmlelement:
+            self.parseXML(xmlelement)
+
+    def parseXML(self, xmlelement):
+        Node.parseXML(self, xmlelement)
+        for (at, av) in xmlelement.attributes.items():
+            if at == "IsAbstract":
+                self.isAbstract = "false" not in av.lower()
+
+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
+        if xmlelement:
+            self.parseXML(xmlelement)
+
+    def parseXML(self, xmlelement):
+        Node.parseXML(self, xmlelement)
+        for (at, av) in xmlelement.attributes.items():
+            if at == "Executable":
+                self.executable = "false" not in av.lower()
+            if at == "UserExecutable":
+                self.userExecutable = "false" not in av.lower()
+            if at == "MethodDeclarationId":
+                self.methodDeclaration = str(av)
+
+class ObjectTypeNode(Node):
+    def __init__(self, xmlelement=None):
+        Node.__init__(self)
+        self.nodeClass = NODE_CLASS_OBJECTTYPE
+        self.isAbstract = False
+        if xmlelement:
+            self.parseXML(xmlelement)
+
+    def parseXML(self, xmlelement):
+        Node.parseXML(self, xmlelement)
+        for (at, av) in xmlelement.attributes.items():
+            if at == "IsAbstract":
+                self.isAbstract = "false" not in av.lower()
+
+class DataTypeNode(Node):
+    """ DataTypeNode is a subtype of Node describing DataType nodes.
+
+        DataType contain definitions and structure information usable for Variables.
+        The format of this structure is determined by buildEncoding()
+        Two definition styles are distinguished in XML:
+        1) A DataType can be a structure of fields, each field having a name and a type.
+           The type must be either an encodable builtin node (ex. UInt32) or point to
+           another DataType node that inherits its encoding from a builtin type using
+           a inverse "hasSubtype" (hasSuperType) reference.
+        2) A DataType may be an enumeration, in which each field has a name and a numeric
+           value.
+        The definition is stored as an ordered list of tuples. Depending on which
+        definition style was used, the __definition__ will hold
+        1) A list of ("Fieldname", Node) tuples.
+        2) A list of ("Fieldname", int) tuples.
+
+        A DataType (and in consequence all Variables using it) shall be deemed not
+        encodable if any of its fields cannot be traced to an encodable builtin type.
+
+        A DataType shall be further deemed not encodable if it contains mixed structure/
+        enumaration definitions.
+
+        If encodable, the encoding can be retrieved using getEncoding().
+    """
+    __isEnum__     = False
+    __xmlDefinition__ = None
+    __baseTypeEncoding__ = []
+    __encodable__ = False
+    __encodingBuilt__ = False
+    __definition__ = []
+
+    def __init__(self, xmlelement=None):
+        Node.__init__(self)
+        self.nodeClass = NODE_CLASS_DATATYPE
+        self.isAbstract = False
+        self.__xmlDefinition__ = None
+        self.__baseTypeEncoding__ = []
+        self.__encodable__ = None
+        self.__encodingBuilt__ = False
+        self.__definition__ = []
+        self.__isEnum__     = False
+        if xmlelement:
+            self.parseXML(xmlelement)
+
+    def parseXML(self, xmlelement):
+        Node.parseXML(self, xmlelement)
+        for (at, av) in xmlelement.attributes.items():
+            if at == "IsAbstract":
+                self.isAbstract = "false" not in av.lower()
+
+        for x in xmlelement.childNodes:
+            if x.nodeType == x.ELEMENT_NODE:
+                if x.localName == "Definition":
+                    self.__xmlDefinition__ = x
+
+    def isEncodable(self):
+        """ Will return True if buildEncoding() was able to determine which builtin
+            type corresponds to all fields of this DataType.
+
+            If no encoding has been build yet, this function will call buildEncoding()
+            and return True if it succeeds.
+        """
+        return self.__encodable__
+
+    def getEncoding(self):
+        """ If the dataType is encodable, getEncoding() returns a nested list
+            containing the encoding the structure definition for this type.
+
+            If no encoding has been build yet, this function will call buildEncoding()
+            and return the encoding if buildEncoding() succeeds.
+
+            If buildEncoding() fails or has failed, an empty list will be returned.
+        """
+        if self.__encodable__ == False:
+            if self.__encodingBuilt__ == False:
+                return self.buildEncoding()
+            return []
+        else:
+            return self.__baseTypeEncoding__
+
+
+    def buildEncoding(self, nodeset, indent=0, force=False):
+        """ buildEncoding() determines the structure and aliases used for variables
+            of this DataType.
+
+            The function will parse the XML <Definition> of the dataType and extract
+            "Name"-"Type" tuples. If successfull, buildEncoding will return a nested
+            list of the following format:
+
+            [['Alias1', ['Alias2', ['BuiltinType']]], [Alias2, ['BuiltinType']], ...]
+
+            Aliases are fieldnames defined by this DataType or DataTypes referenced. A
+            list such as ['DataPoint', ['Int32']] indicates that a value will encode
+            an Int32 with the alias 'DataPoint' such as <DataPoint>12827</DataPoint>.
+            Only the first Alias of a nested list is considered valid for the BuiltinType.
+
+            Single-Elemented lists are always BuiltinTypes. Every nested list must
+            converge in a builtin type to be encodable. buildEncoding will follow
+            the first type inheritance reference (hasSupertype) of the dataType if
+            necessary;
+
+            If instead to "DataType" a numeric "Value" attribute is encountered,
+            the DataType will be considered an enumeration and all Variables using
+            it will be encoded as Int32.
+
+            DataTypes can be either structures or enumeration - mixed definitions will
+            be unencodable.
+
+            Calls to getEncoding() will be iterative. buildEncoding() can be called
+            only once per dataType, with all following calls returning the predetermined
+            value. Use of the 'force=True' parameter will force the Definition to be
+            reparsed.
+
+            After parsing, __definition__ holds the field definition as a list. Note
+            that this might deviate from the encoding, especially if inheritance was
+            used.
+        """
+
+        prefix = " " + "|"*indent+ "+"
+
+        if force==True:
+            self.__encodingBuilt__ = False
+
+        if self.__encodingBuilt__ == True:
+            if self.isEncodable():
+                logger.debug(prefix + str(self.__baseTypeEncoding__) + " (already analyzed)")
+            else:
+                logger.debug( prefix + str(self.__baseTypeEncoding__) + "(already analyzed, not encodable!)")
+            return self.__baseTypeEncoding__
+        self.__encodingBuilt__ = True # signify that we have attempted to built this type
+        self.__encodable__ = True
+
+        if indent==0:
+            logger.debug("Parsing DataType " + str(self.browseName) + " (" + str(self.id) + ")")
+
+        if valueIsInternalType(self.browseName.name):
+            self.__baseTypeEncoding__ = [self.browseName.name]
+            self.__encodable__ = True
+            logger.debug( prefix + str(self.browseName) + "*")
+            logger.debug("Encodable as: " + str(self.__baseTypeEncoding__))
+            logger.debug("")
+            return self.__baseTypeEncoding__
+
+        if self.__xmlDefinition__ == None:
+            # Check if there is a supertype available
+            for ref in self.inverseReferences:
+                # hasSubtype
+                if ref.referenceType.i == 45:
+                    targetNode = nodeset.nodes[ref.target]
+                    if targetNode is not None and isinstance(targetNode, DataTypeNode):
+                        logger.debug( prefix + "Attempting definition using supertype " + str(targetNode.browseName) + " for DataType " + " " + str(self.browseName))
+                        subenc = targetNode.buildEncoding(nodeset=nodeset, indent=indent+1)
+                        if not targetNode.isEncodable():
+                            self.__encodable__ = False
+                            break
+                        else:
+                            self.__baseTypeEncoding__ = self.__baseTypeEncoding__ + [self.browseName.name, subenc, 0]
+            if len(self.__baseTypeEncoding__) == 0:
+                logger.debug(prefix + "No viable definition for " + str(self.browseName) + " " + str(self.id) + " found.")
+                self.__encodable__ = False
+
+            if indent==0:
+                if not self.__encodable__:
+                    logger.debug("Not encodable (partial): " + str(self.__baseTypeEncoding__))
+                else:
+                    logger.debug("Encodable as: " + str(self.__baseTypeEncoding__))
+                logger.debug( "")
+
+            return self.__baseTypeEncoding__
+
+        isEnum = True
+        isSubType = True
+        hasValueRank = 0
+
+        # We need to store the definition as ordered data, but can't use orderedDict
+        # for backward compatibility with Python 2.6 and 3.4
+        enumDict = []
+        typeDict = []
+
+        # An XML Definition is provided and will be parsed... now
+        for x in self.__xmlDefinition__.childNodes:
+            if x.nodeType == x.ELEMENT_NODE:
+                fname  = ""
+                fdtype = ""
+                enumVal = ""
+                valueRank = 0
+                for at,av in x.attributes.items():
+                    if at == "DataType":
+                        fdtype = str(av)
+                        isEnum = False
+                    elif at == "Name":
+                        fname = str(av)
+                    elif at == "Value":
+                        enumVal = int(av)
+                        isSubType = False
+                    elif at == "ValueRank":
+                        valueRank = int(av)
+                        if valueRank > 0:
+                            logger.warn("Value ranks >0 not fully supported. Further steps may fail")
+                    else:
+                        logger.warn("Unknown Field Attribute " + str(at))
+                # This can either be an enumeration OR a structure, not both.
+                # Figure out which of the dictionaries gets the newly read value pair
+                if isEnum == isSubType:
+                    # This is an error
+                    logger.warn("DataType contains both enumeration and subtype (or neither)")
+                    self.__encodable__ = False
+                    break
+                elif isEnum:
+                    # This is an enumeration
+                    enumDict.append((fname, enumVal))
+                    continue
+                else:
+                    if fdtype == "":
+                        # If no datatype given use base datatype
+                        fdtype = "i=24"
+
+                    # This might be a subtype... follow the node defined as datatype to find out
+                    # what encoding to use
+                    if not NodeId(fdtype) in nodeset.nodes:
+                        raise Exception("Node {} not found in nodeset".format(NodeId(fdtype)))
+                    dtnode = nodeset.nodes[NodeId(fdtype)]
+                    # The node in the datatype element was found. we inherit its encoding,
+                    # but must still ensure that the dtnode is itself validly encodable
+                    typeDict.append([fname, dtnode])
+                    fdtype = str(dtnode.browseName.name)
+                    logger.debug( prefix + fname + " : " + fdtype + " -> " + str(dtnode.id))
+                    subenc = dtnode.buildEncoding(nodeset=nodeset, indent=indent+1)
+                    self.__baseTypeEncoding__ = self.__baseTypeEncoding__ + [[fname, subenc, valueRank]]
+                    if not dtnode.isEncodable():
+                        # If we inherit an encoding from an unencodable not, this node is
+                        # also not encodable
+                        self.__encodable__ = False
+                        break
+
+        # If we used inheritance to determine an encoding without alias, there is a
+        # the possibility that lists got double-nested despite of only one element
+        # being encoded, such as [['Int32']] or [['alias',['int32']]]. Remove that
+        # enclosing list.
+        while len(self.__baseTypeEncoding__) == 1 and isinstance(self.__baseTypeEncoding__[0], list):
+            self.__baseTypeEncoding__ = self.__baseTypeEncoding__[0]
+
+        if isEnum == True:
+            self.__baseTypeEncoding__ = self.__baseTypeEncoding__ + ['Int32']
+            self.__definition__ = enumDict
+            self.__isEnum__ = True
+            logger.debug( prefix+"Int32* -> enumeration with dictionary " + str(enumDict) + " encodable " + str(self.__encodable__))
+            return self.__baseTypeEncoding__
+
+        if indent==0:
+            if not self.__encodable__:
+                logger.debug( "Not encodable (partial): " + str(self.__baseTypeEncoding__))
+            else:
+                logger.debug( "Encodable as: " + str(self.__baseTypeEncoding__))
+                self.__isEnum__ = False
+                self.__definition__ = typeDict
+            logger.debug( "")
+        return self.__baseTypeEncoding__
+
+class ViewNode(Node):
+    def __init__(self, xmlelement=None):
+        Node.__init__(self)
+        self.nodeClass = NODE_CLASS_VIEW
+        self.containsNoLoops == False
+        self.eventNotifier == False
+        if xmlelement:
+            self.parseXML(xmlelement)
+
+    def parseXML(self, xmlelement):
+        Node.parseXML(self, xmlelement)
+        for (at, av) in xmlelement.attributes.items():
+            if at == "ContainsNoLoops":
+                self.containsNoLoops = "false" not in av.lower()
+            if at == "eventNotifier":
+                self.eventNotifier = "false" not in av.lower()

+ 331 - 0
tools/nodeset_compiler/nodeset.py

@@ -0,0 +1,331 @@
+#!/usr/bin/env/python
+# -*- coding: utf-8 -*-
+
+###
+### 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 LGPL 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.
+###
+
+from __future__ import print_function
+import sys
+import xml.dom.minidom as dom
+from struct import pack as structpack
+from time import struct_time, strftime, strptime, mktime
+import logging;
+
+logger = logging.getLogger(__name__)
+
+from nodes import *
+from opaque_type_mapping import opaque_type_mapping
+import codecs
+
+####################
+# Helper Functions #
+####################
+
+hassubtype = NodeId("ns=0;i=45")
+
+def getSubTypesOf(nodeset, node, skipNodes=[]):
+    if node in skipNodes:
+        return []
+    re = [node]
+    for ref in node.references:
+        if ref.referenceType == hassubtype and ref.isForward:
+            re = re + getSubTypesOf(nodeset, nodeset.nodes[ref.target], skipNodes=skipNodes)
+    return re
+
+def extractNamespaces(xmlfile):
+    # Extract a list of namespaces used. The first namespace is always
+    # "http://opcfoundation.org/UA/". minidom gobbles up
+    # <NamespaceUris></NamespaceUris> elements, without a decent way to reliably
+    # access this dom2 <uri></uri> elements (only attribute xmlns= are accessible
+    # using minidom). We need them for dereferencing though... This function
+    # attempts to do just that.
+
+    namespaces = ["http://opcfoundation.org/UA/"]
+    infile = open(xmlfile.name)
+    foundURIs = False
+    nsline = ""
+    line = infile.readline()
+    for line in infile:
+        if "<namespaceuris>" in line.lower():
+            foundURIs = True
+        elif "</namespaceuris>" in line.lower():
+            foundURIs = False
+            nsline = nsline + line
+            break
+        if foundURIs:
+            nsline = nsline + line
+
+    if len(nsline) > 0:
+        ns = dom.parseString(nsline).getElementsByTagName("NamespaceUris")
+        for uri in ns[0].childNodes:
+            if uri.nodeType != uri.ELEMENT_NODE:
+                continue
+            if uri.firstChild.data in namespaces:
+                continue
+            namespaces.append(uri.firstChild.data)
+    infile.close()
+    return namespaces
+
+def buildAliasList(xmlelement):
+    """Parses the <Alias> XML Element present in must XML NodeSet definitions.
+       Contents the Alias element are stored in a dictionary for further
+       dereferencing during pointer linkage (see linkOpenPointer())."""
+    aliases = {}
+    for al in xmlelement.childNodes:
+        if al.nodeType == al.ELEMENT_NODE:
+            if al.hasAttribute("Alias"):
+                aliasst = al.getAttribute("Alias")
+                aliasnd = unicode(al.firstChild.data)
+                aliases[aliasst] = aliasnd
+    return aliases
+
+class NodeSet(object):
+    """ This class handles parsing XML description of namespaces, instantiating
+        nodes, linking references, graphing the namespace and compiling a binary
+        representation.
+
+        Note that nodes assigned to this class are not restricted to having a
+        single namespace ID. This class represents the entire physical address
+        space of the binary representation and all nodes that are to be included
+        in that segment of memory.
+    """
+
+    def __init__(self):
+        self.nodes = {}
+        self.aliases = {}
+        self.namespaces = ["http://opcfoundation.org/UA/"]
+
+    def sanitize(self):
+        for n in self.nodes.values():
+            if n.sanitize() == False:
+                raise Exception("Failed to sanitize node " + str(n))
+
+        # Sanitize reference consistency
+        for n in self.nodes.values():
+            for ref in n.references:
+                if not ref.source == n.id:
+                    raise Exception("Reference " + str(ref) + " has an invalid source")
+                if not ref.referenceType in self.nodes:
+                    raise Exception("Reference " + str(ref) + " has an unknown reference type")
+                if not ref.target in self.nodes:
+                    raise Exception("Reference " + str(ref) + " has an unknown target")
+
+    def addNamespace(self, nsURL):
+        if not nsURL in self.namespaces:
+            self.namespaces.append(nsURL)
+
+    def createNamespaceMapping(self, orig_namespaces):
+        """Creates a dict that maps from the nsindex in the original nodeset to the
+           nsindex in the combined nodeset"""
+        m = {}
+        for index, name in enumerate(orig_namespaces):
+            m[index] = self.namespaces.index(name)
+        return m
+
+    def getNodeByBrowseName(self, idstring):
+        return next((n for n in self.nodes.values() if idstring == n.browseName.name), None)
+
+    def getNodeById(self, namespace, id):
+        nodeId = NodeId()
+        nodeId.ns = namespace
+        nodeId.i = id
+        return self.nodes[nodeId]
+
+    def getRoot(self):
+        return self.getNodeByBrowseName("Root")
+
+    def createNode(self, xmlelement, nsMapping, hidden=False):
+        ndtype = xmlelement.localName.lower()
+        if ndtype[:2] == "ua":
+            ndtype = ndtype[2:]
+
+        node = None
+        if ndtype == 'variable':
+            node = VariableNode(xmlelement)
+        if ndtype == 'object':
+            node = ObjectNode(xmlelement)
+        if ndtype == 'method':
+            node = MethodNode(xmlelement)
+        if ndtype == 'objecttype':
+            node = ObjectTypeNode(xmlelement)
+        if ndtype == 'variabletype':
+            node = VariableTypeNode(xmlelement)
+        if ndtype == 'methodtype':
+            node = MethodNode(xmlelement)
+        if ndtype == 'datatype':
+            node = DataTypeNode(xmlelement)
+        if ndtype == 'referencetype':
+            node = ReferenceTypeNode(xmlelement)
+
+        if node and hidden:
+            node.hidden = True
+            # References from an existing nodeset are all suppressed
+            for ref in node.references:
+                ref.hidden = True
+            for ref in node.inverseReferences:
+                ref.hidden = True
+        return node
+
+    def hide_node(self, nodeId, hidden=True):
+        if not nodeId in self.nodes:
+            return False
+        node = self.nodes[nodeId]
+        node.hidden = hidden
+        # References from an existing nodeset are all suppressed
+        for ref in node.references:
+            ref.hidden = hidden
+        for ref in node.inverseReferences:
+            ref.hidden = hidden
+        return True
+
+    def merge_dicts(self, *dict_args):
+        """
+        Given any number of dicts, shallow copy and merge into a new dict,
+        precedence goes to key value pairs in latter dicts.
+        """
+        result = {}
+        for dictionary in dict_args:
+            result.update(dictionary)
+        return result
+
+    def addNodeSet(self, xmlfile, hidden=False, typesArray="UA_TYPES"):
+        # Extract NodeSet DOM
+
+        fileContent = xmlfile.read()
+        # Remove BOM since the dom parser cannot handle it on python 3 windows
+        if fileContent.startswith( codecs.BOM_UTF8 ):
+            fileContent = fileContent.lstrip( codecs.BOM_UTF8 )
+
+        nodesets = dom.parseString(fileContent).getElementsByTagName("UANodeSet")
+        if len(nodesets) == 0 or len(nodesets) > 1:
+            raise Exception(self, self.originXML + " contains no or more then 1 nodeset")
+        nodeset = nodesets[0]
+
+        # Create the namespace mapping
+        orig_namespaces = extractNamespaces(xmlfile)  # List of namespaces used in the xml file
+        for ns in orig_namespaces:
+            self.addNamespace(ns)
+        nsMapping = self.createNamespaceMapping(orig_namespaces)
+
+        # Extract the aliases
+        for nd in nodeset.childNodes:
+            if nd.nodeType != nd.ELEMENT_NODE:
+                continue
+            ndtype = nd.localName.lower()
+            if 'aliases' in ndtype:
+                self.aliases = self.merge_dicts(self.aliases, buildAliasList(nd))
+
+        # Instantiate nodes
+        newnodes = []
+        for nd in nodeset.childNodes:
+            if nd.nodeType != nd.ELEMENT_NODE:
+                continue
+            node = self.createNode(nd, nsMapping, hidden)
+            if not node:
+                continue
+            node.replaceAliases(self.aliases)
+            node.replaceNamespaces(nsMapping)
+            node.typesArray = typesArray
+
+            # Add the node the the global dict
+            if node.id in self.nodes:
+                raise Exception("XMLElement with duplicate ID " + str(node.id))
+            self.nodes[node.id] = node
+            newnodes.append(node)
+
+        # add inverse references
+        for node in newnodes:
+            for ref in node.references:
+                newsource = self.nodes[ref.target]
+                hide = ref.hidden or (node.hidden and newsource.hidden)
+                newref = Reference(newsource.id, ref.referenceType, ref.source, False, hide, inferred=True)
+                newsource.inverseReferences.add(newref)
+            for ref in node.inverseReferences:
+                newsource = self.nodes[ref.target]
+                hide = ref.hidden or (node.hidden and newsource.hidden)
+                newref = Reference(newsource.id, ref.referenceType, ref.source, True, hide, inferred=True)
+                newsource.references.add(newref)
+
+    def getBinaryEncodingIdForNode(self, nodeId):
+        """
+        The node should have a 'HasEncoding' forward reference which points to the encoding ids.
+        These can be XML Encoding or Binary Encoding. Therefore we also need to check if the SymbolicName
+        of the target node is "DefaultBinary"
+        """
+        node = self.nodes[nodeId]
+        refId = NodeId()
+        for ref in node.references:
+            if ref.referenceType.ns == 0 and ref.referenceType.i == 38:
+                refNode = self.nodes[ref.target]
+                if refNode.symbolicName.value == "DefaultBinary":
+                    return ref.target
+        raise Exception("No DefaultBinary encoding defined for node " + str(nodeId))
+
+    def buildEncodingRules(self):
+        """ Calls buildEncoding() for all DataType nodes (opcua_node_dataType_t).
+
+            No return value
+        """
+        stat = {True: 0, False: 0}
+        for n in self.nodes.values():
+            if isinstance(n, DataTypeNode):
+                n.buildEncoding(self)
+                stat[n.isEncodable()] = stat[n.isEncodable()] + 1
+        logger.debug("Type definitions built/passed: " +  str(stat))
+
+
+    def allocateVariables(self):
+        for n in self.nodes.values():
+            if isinstance(n, VariableNode):
+                n.allocateValue(self)
+
+
+    def getBaseDataType(self, node):
+        if node is None:
+            return None
+        if node.browseName.name not in opaque_type_mapping:
+            return node
+        for ref in node.inverseReferences:
+            if ref.referenceType.i == 45:
+                return self.getBaseDataType(self.nodes[ref.target])
+        return node
+                
+    def getDataTypeNode(self, dataType):
+        if isinstance(dataType, six.string_types):
+            if not valueIsInternalType(dataType):
+                logger.error("Not a valid dataType string: " + dataType)
+                return None
+            return self.nodes[NodeId(self.aliases[dataType])]
+        if isinstance(dataType, NodeId):
+            if dataType.i == 0:
+                return None
+            dataTypeNode = self.nodes[dataType]
+            if not isinstance(dataTypeNode, DataTypeNode):
+                logger.error("Node id " + str(dataType) + " is not reference a valid dataType.")
+                return None
+            if not dataTypeNode.isEncodable():
+                logger.warn("DataType " + str(dataTypeNode.browseName) + " is not encodable.")
+            return dataTypeNode
+        return None
+
+    def getRelevantOrderingReferences(self):
+        relevant_types = getSubTypesOf(self,
+                                       self.getNodeByBrowseName("HierarchicalReferences"),
+                                       [])
+        relevant_types += getSubTypesOf(self,
+                                        self.getNodeByBrowseName("HasEncoding"),
+                                        [])
+        relevant_types = map(lambda x: x.id, relevant_types)
+        return list(relevant_types)

+ 193 - 0
tools/nodeset_compiler/nodeset_compiler.py

@@ -0,0 +1,193 @@
+#!/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/.
+
+###
+### Authors:
+### - Chris Iatrou (ichrispa@core-vector.net)
+### - Julius Pfrommer
+### - Stefan Profanter (profanter@fortiss.org)
+###
+### 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.
+###
+
+import logging
+import argparse
+from nodeset import *
+from backend_open62541 import generateOpen62541Code
+
+parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter)
+parser.add_argument('-e', '--existing',
+                    metavar="<existingNodeSetXML>",
+                    type=argparse.FileType('rb'),
+                    dest="existing",
+                    action='append',
+                    default=[],
+                    help='NodeSet XML files with nodes that are already present on the server.')
+
+parser.add_argument('-x', '--xml',
+                    metavar="<nodeSetXML>",
+                    type=argparse.FileType('rb'),
+                    action='append',
+                    dest="infiles",
+                    default=[],
+                    help='NodeSet XML files with nodes that shall be generated.')
+
+parser.add_argument('outputFile',
+                    metavar='<outputFile>',
+                    help='The path/basename for the <output file>.c and <output file>.h files to be generated. This will also be the function name used in the header and c-file.')
+
+parser.add_argument('--generate-ns0',
+                    action='store_true',
+                    dest="generate_ns0",
+                    help='Omit some consistency checks for bootstrapping namespace 0, create references to parents and type definitions manually')
+
+parser.add_argument('--internal-headers',
+                    action='store_true',
+                    dest="internal_headers",
+                    help='Include internal headers instead of amalgamated header')
+
+parser.add_argument('-b', '--blacklist',
+                    metavar="<blacklistFile>",
+                    type=argparse.FileType('r'),
+                    action='append',
+                    dest="blacklistFiles",
+                    default=[],
+                    help='Loads a list of NodeIDs stored in blacklistFile (one NodeID per line). Any of the nodeIds encountered in this file will be removed from the nodeset prior to compilation. Any references to these nodes will also be removed')
+
+parser.add_argument('-i', '--ignore',
+                    metavar="<ignoreFile>",
+                    type=argparse.FileType('r'),
+                    action='append',
+                    dest="ignoreFiles",
+                    default=[],
+                    help='Loads a list of NodeIDs stored in ignoreFile (one NodeID per line). Any of the nodeIds encountered in this file will be kept in the nodestore but not printed in the generated code')
+
+parser.add_argument('-s', '--suppress',
+                    metavar="<attribute>",
+                    action='append',
+                    dest="suppressedAttributes",
+                    choices=['description', 'browseName', 'displayName', 'writeMask', 'userWriteMask', 'nodeid'],
+                    default=[],
+                    help="Suppresses the generation of some node attributes. Currently supported options are 'description', 'browseName', 'displayName', 'writeMask', 'userWriteMask' and 'nodeid'.")
+
+parser.add_argument('-t', '--types-array',
+                    metavar="<typesArray>",
+                    action='append',
+                    type=str,
+                    dest="typesArray",
+                    default=[],
+                    help='Types array for the given namespace. Can be used mutliple times to define (in the same order as the .xml files, first for --existing, then --xml) the type arrays')
+
+parser.add_argument('--max-string-length',
+                    type=int,
+                    dest="max_string_length",
+                    default=0,
+                    help='Maximum allowed length of a string literal. If longer, it will be set to an empty string')
+
+parser.add_argument('-v', '--verbose', action='count',
+                    help='Make the script more verbose. Can be applied up to 4 times')
+
+args = parser.parse_args()
+
+# Set up logging
+logger = logging.getLogger(__name__)
+logger.setLevel(logging.INFO)
+verbosity = 0
+if args.verbose:
+    verbosity = int(args.verbose)
+if (verbosity == 1):
+    logging.basicConfig(level=logging.ERROR)
+elif (verbosity == 2):
+    logging.basicConfig(level=logging.WARNING)
+elif (verbosity == 3):
+    logging.basicConfig(level=logging.INFO)
+elif (verbosity >= 4):
+    logging.basicConfig(level=logging.DEBUG)
+else:
+    logging.basicConfig(level=logging.CRITICAL)
+
+# Create a new nodeset. The nodeset name is not significant.
+# Parse the XML files
+ns = NodeSet()
+nsCount = 0
+
+def getTypesArray(nsIdx):
+    if nsIdx < len(args.typesArray):
+        return args.typesArray[nsIdx]
+    else:
+        return "UA_TYPES"
+
+for xmlfile in args.existing:
+    logger.info("Preprocessing (existing) " + str(xmlfile.name))
+    ns.addNodeSet(xmlfile, True, typesArray=getTypesArray(nsCount))
+    nsCount +=1
+for xmlfile in args.infiles:
+    logger.info("Preprocessing " + str(xmlfile.name))
+    ns.addNodeSet(xmlfile, typesArray=getTypesArray(nsCount))
+    nsCount +=1
+
+# # We need to notify the open62541 server of the namespaces used to be able to use i.e. ns=3
+# namespaceArrayNames = preProc.getUsedNamespaceArrayNames()
+# for key in namespaceArrayNames:
+#   ns.addNamespace(key, namespaceArrayNames[key])
+
+# Remove blacklisted nodes from the nodeset
+# Doing this now ensures that unlinkable pointers will be cleanly removed
+# during sanitation.
+for blacklist in args.blacklistFiles:
+    for line in blacklist.readlines():
+        line = line.replace(" ", "")
+        id = line.replace("\n", "")
+        if ns.getNodeByIDString(id) == None:
+            logger.info("Can't blacklist node, namespace does currently not contain a node with id " + str(id))
+        else:
+            ns.removeNodeById(line)
+    blacklist.close()
+
+# Set the nodes from the ignore list to hidden. This removes them from dependency calculation
+# and from printing their generated code.
+# These nodes should be already pre-created on the server to avoid any errors during
+# creation.
+for ignoreFile in args.ignoreFiles:
+    for line in ignoreFile.readlines():
+        line = line.replace(" ", "")
+        id = line.replace("\n", "")
+        ns.hide_node(NodeId(id))
+        #if not ns.hide_node(NodeId(id)):
+        #    logger.info("Can't ignore node, namespace does currently not contain a node with id " + str(id))
+    ignoreFile.close()
+
+# Remove nodes that are not printable or contain parsing errors, such as
+# unresolvable or no references or invalid NodeIDs
+ns.sanitize()
+
+
+# Parse Datatypes in order to find out what the XML keyed values actually
+# represent.
+# Ex. <rpm>123</rpm> is not encodable
+#     only after parsing the datatypes, it is known that
+#     rpm is encoded as a double
+ns.buildEncodingRules()
+
+# Allocate/Parse the data values. In order to do this, we must have run
+# buidEncodingRules.
+ns.allocateVariables()
+
+#printDependencyGraph(ns)
+
+# Create the C code with the open62541 backend of the compiler
+logger.info("Generating Code")
+generateOpen62541Code(ns, args.outputFile, args.suppressedAttributes, args.generate_ns0, args.internal_headers, args.typesArray, args.max_string_length)
+logger.info("NodeSet generation code successfully printed")

+ 75 - 0
tools/nodeset_compiler/nodeset_testing.py

@@ -0,0 +1,75 @@
+#!/usr/bin/env python
+
+from nodeset import *
+
+class testing:
+    def __init__(self):
+        self.ns = NodeSet("testing")
+
+        logger.debug("Phase 1: Reading XML file nodessets")
+        self.ns.parseXML("Opc.Ua.NodeSet2.xml")
+        # self.ns.parseXML("Opc.Ua.NodeSet2.Part4.xml")
+        # self.ns.parseXML("Opc.Ua.NodeSet2.Part5.xml")
+        # self.ns.parseXML("Opc.Ua.SimulationNodeSet2.xml")
+
+        logger.debug("Phase 2: Linking address space references and datatypes")
+        self.ns.linkOpenPointers()
+        self.ns.sanitize()
+
+        logger.debug("Phase 3: Comprehending DataType encoding rules")
+        self.ns.buildEncodingRules()
+
+        logger.debug("Phase 4: Allocating variable value data")
+        self.ns.allocateVariables()
+
+        bin = self.ns.buildBinary()
+        f = open("binary.base64", "w+")
+        f.write(bin.encode("base64"))
+        f.close()
+
+        allnodes = self.ns.nodes
+        ns = [self.ns.getRoot()]
+
+        i = 0
+        # print "Starting depth search on " + str(len(allnodes)) + " nodes starting
+        # with from " + str(ns)
+        while (len(ns) < len(allnodes)):
+            i = i + 1
+            tmp = []
+            print("Iteration: " + str(i))
+            for n in ns:
+                tmp.append(n)
+                for r in n.getReferences():
+                    if (not r.target() in tmp):
+                        tmp.append(r.target())
+            print("...tmp, " + str(len(tmp)) + " nodes discovered")
+            ns = []
+            for n in tmp:
+                ns.append(n)
+            print("...done, " + str(len(ns)) + " nodes discovered")
+
+class testing_open62541_header:
+    def __init__(self):
+        self.ns = opcua_ns("testing")
+
+        logger.debug("Phase 1: Reading XML file nodessets")
+        self.ns.parseXML("Opc.Ua.NodeSet2.xml")
+        # self.ns.parseXML("Opc.Ua.NodeSet2.Part4.xml")
+        # self.ns.parseXML("Opc.Ua.NodeSet2.Part5.xml")
+        # self.ns.parseXML("Opc.Ua.SimulationNodeSet2.xml")
+
+        logger.debug("Phase 2: Linking address space references and datatypes")
+        self.ns.linkOpenPointers()
+        self.ns.sanitize()
+
+        logger.debug("Phase 3: Calling C Printers")
+        code = self.ns.printOpen62541Header()
+
+        codeout = open("./open62541_nodeset.c", "w+")
+        for line in code:
+            codeout.write(line + "\n")
+        codeout.close()
+        return
+
+if __name__ == '__main__':
+    tst = testing_open62541_header()

File diff suppressed because it is too large
+ 0 - 1514
tools/pyUANamespace/NodeID_Blacklist_FullNS0.txt


File diff suppressed because it is too large
+ 0 - 1514
tools/pyUANamespace/NodeID_NameSpace0_All.txt


+ 0 - 183
tools/pyUANamespace/generate_open62541CCode.py

@@ -1,183 +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 14
-###
-### 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.
-###
-
-from __future__ import print_function
-from ua_namespace import *
-import logging
-import argparse
-from open62541_XMLPreprocessor import open62541_XMLPreprocessor
-
-logger = logging.getLogger(__name__)
-
-parser = argparse.ArgumentParser(
-    description="""Parse OPC UA NamespaceXML file(s) and create C code for generating nodes in open62541
-
-generate_open62541CCode.py will first read all XML files passed on the command line, then link and check the namespace. All nodes that fulfill the basic requirements will then be printed as C-Code intended to be included in the open62541 OPC UA Server that will initialize the corresponding namespace.""",
-    formatter_class=argparse.RawDescriptionHelpFormatter)
-parser.add_argument('infiles',
-                    metavar="<namespaceXML>",
-                    nargs='+',
-                    type=argparse.FileType('r'),
-                    help='Namespace XML file(s). Note that the last definition of a node encountered will be used and all prior definitions are discarded.')
-parser.add_argument('outputFile',
-                    metavar='<outputFile>',
-                    #type=argparse.FileType('w', 0),
-                    help='The basename for the <output file>.c and <output file>.h files to be generated. This will also be the function name used in the header and c-file.')
-parser.add_argument('-i','--ignore',
-                    metavar="<ignoreFile>",
-                    type=argparse.FileType('r'),
-                    action='append',
-                    dest="ignoreFiles",
-                    default=[],
-                    help='Loads a list of NodeIDs stored in ignoreFile (one NodeID per line). The compiler will assume that these nodes have been created externally and not generate any code for them. They will however be linked to from other nodes.')
-parser.add_argument('-b','--blacklist',
-                    metavar="<blacklistFile>",
-                    type=argparse.FileType('r'),
-                    action='append',
-                    dest="blacklistFiles",
-                    default=[],
-                    help='Loads a list of NodeIDs stored in blacklistFile (one NodeID per line). Any of the nodeIds encountered in this file will be removed from the namespace prior to compilation. Any references to these nodes will also be removed')
-parser.add_argument('-s','--suppress',
-                    metavar="<attribute>",
-                    action='append',
-                    dest="suppressedAttributes",
-                    choices=['description', 'browseName', 'displayName', 'writeMask', 'userWriteMask','nodeid'],
-                    default=[],
-                    help="Suppresses the generation of some node attributes. Currently supported options are 'description', 'browseName', 'displayName', 'writeMask', 'userWriteMask' and 'nodeid'.")
-
-parser.add_argument('-v','--verbose', action='count', help='Make the script more verbose. Can be applied up to 4 times')
-
-args = parser.parse_args()
-
-level = logging.CRITICAL
-verbosity = 0
-if args.verbose:
-  verbosity = int(args.verbose)
-if (verbosity==1):
-  level = logging.ERROR
-elif (verbosity==2):
-  level = logging.WARNING
-elif (verbosity==3):
-  level = logging.INFO
-elif (verbosity>=4):
-  level = logging.DEBUG
-
-logging.basicConfig(level=level)
-logger.setLevel(logging.INFO)
-
-# Creating the header is tendious. We can skip the entire process if
-# the header exists.
-#if path.exists(argv[-1]+".c") or path.exists(argv[-1]+".h"):
-#  log(None, "File " + str(argv[-1]) + " does already exists.", LOG_LEVEL_INFO)
-#  log(None, "Header generation will be skipped. Delete the header and rerun this script if necessary.", LOG_LEVEL_INFO)
-#  exit(0)
-
-# Open the output file
-outfileh = open(args.outputFile+".h", r"w+")
-outfilec = open(args.outputFile+".c", r"w+")
-
-# Create a new namespace. Note that the namespace name is not significant.
-ns = opcua_namespace("open62541")
-
-# Clean up the XML files by removing duplicate namespaces and unwanted prefixes
-preProc = open62541_XMLPreprocessor()
-for xmlfile in args.infiles:
-  logger.info("Preprocessing " + str(xmlfile.name))
-  preProc.addDocument(xmlfile.name)
-preProc.preprocessAll()
-
-# Parse the XML files
-for xmlfile in preProc.getPreProcessedFiles():
-  logger.info("Parsing " + str(xmlfile))
-  ns.parseXML(xmlfile)
-
-# We need to notify the open62541 server of the namespaces used to be able to use i.e. ns=3
-namespaceArrayNames = preProc.getUsedNamespaceArrayNames()
-for key in namespaceArrayNames:
-  ns.addNamespace(key, namespaceArrayNames[key])
-
-# Remove any temp files - they are not needed after the AST is created
-preProc.removePreprocessedFiles()
-
-# Remove blacklisted nodes from the namespace
-# Doing this now ensures that unlinkable pointers will be cleanly removed
-# during sanitation.
-for blacklist in args.blacklistFiles:
-  for line in blacklist.readlines():
-    line = line.replace(" ","")
-    id = line.replace("\n","")
-    if ns.getNodeByIDString(id) == None:
-      logger.info("Can't blacklist node, namespace does currently not contain a node with id " + str(id))
-    else:
-      ns.removeNodeById(line)
-  blacklist.close()
-
-# Link the references in the namespace
-logger.info("Linking namespace nodes and references")
-ns.linkOpenPointers()
-
-# Remove nodes that are not printable or contain parsing errors, such as
-# unresolvable or no references or invalid NodeIDs
-ns.sanitize()
-
-# Parse Datatypes in order to find out what the XML keyed values actually
-# represent.
-# Ex. <rpm>123</rpm> is not encodable
-#     only after parsing the datatypes, it is known that
-#     rpm is encoded as a double
-logger.info("Building datatype encoding rules")
-ns.buildEncodingRules()
-
-# Allocate/Parse the data values. In order to do this, we must have run
-# buidEncodingRules.
-logger.info("Allocating variables")
-ns.allocateVariables()
-
-# Users may have manually defined some nodes in their code already (such as serverStatus).
-# To prevent those nodes from being reprinted, we will simply mark them as already
-# converted to C-Code. That way, they will still be referred to by other nodes, but
-# they will not be created themselves.
-ignoreNodes = []
-for ignore in args.ignoreFiles:
-  for line in ignore.readlines():
-    line = line.replace(" ","")
-    id = line.replace("\n","")
-    if ns.getNodeByIDString(id) == None:
-      logger.warn("Can't ignore node, Namespace does currently not contain a node with id " + str(id))
-    else:
-      ignoreNodes.append(ns.getNodeByIDString(id))
-  ignore.close()
-
-# Create the C Code
-logger.info("Generating Header")
-# Returns a tuple of (["Header","lines"],["Code","lines","generated"])
-from os.path import basename
-generatedCode = ns.printOpen62541Header(ignoreNodes, args.suppressedAttributes, outfilename=basename(args.outputFile))
-for line in generatedCode[0]:
-  outfileh.write(line+"\n")
-for line in generatedCode[1]:
-  outfilec.write(line+"\n")
-
-outfilec.close()
-outfileh.close()
-
-logger.info("Namespace generation code successfully printed")

+ 0 - 257
tools/pyUANamespace/open62541_MacroHelper.py

@@ -1,257 +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.
-###
-
-import logging
-from ua_constants import *
-import string
-
-
-logger = logging.getLogger(__name__)
-
-__unique_item_id = 0
-
-defined_typealiases = []
-
-class open62541_MacroHelper():
-  def __init__(self, supressGenerationOfAttribute=[]):
-    self.supressGenerationOfAttribute = supressGenerationOfAttribute
-
-  def getCreateExpandedNodeIDMacro(self, node):
-    if node.id().i != None:
-      return "UA_EXPANDEDNODEID_NUMERIC(" + str(node.id().ns) + ", " + str(node.id().i) + ")"
-    elif node.id().s != None:
-      return "UA_EXPANDEDNODEID_STRING("  + str(node.id().ns) + ", " + node.id().s + ")"
-    elif node.id().b != None:
-      logger.debug("NodeID Generation macro for bytestrings has not been implemented.")
-      return ""
-    elif node.id().g != None:
-      logger.debug("NodeID Generation macro for guids has not been implemented.")
-      return ""
-    else:
-      return ""
-
-  def substitutePunctuationCharacters(self, input):
-    ''' substitutePunctuationCharacters
-
-        Replace punctuation characters in input. Part of this class because it is used by
-        ua_namespace on occasion.
-
-        returns: C-printable string representation of input
-    '''
-    # No punctuation characters <>!$
-    for illegal_char in list(string.punctuation):
-        if illegal_char == '_': # underscore is allowed
-            continue
-        input = input.replace(illegal_char, "_") # map to underscore
-    return input
-
-  def getNodeIdDefineString(self, node):
-    code = []
-    extrNs = node.browseName().split(":")
-    symbolic_name = ""
-    # strip all characters that would be illegal in C-Code
-    if len(extrNs) > 1:
-        nodename = extrNs[1]
-    else:
-        nodename = extrNs[0]
-
-    symbolic_name = self.substitutePunctuationCharacters(nodename)
-    if symbolic_name != nodename :
-        logger.warn("Subsituted characters in browsename for nodeid " + str(node.id().i) + " while generating C-Code ")
-
-    if symbolic_name in defined_typealiases:
-      logger.warn(self, "Typealias definition of " + str(node.id().i) + " is non unique!")
-      extendedN = 1
-      while (symbolic_name+"_"+str(extendedN) in defined_typealiases):
-        logger.warn("Typealias definition of " + str(node.id().i) + " is non unique!")
-        extendedN+=1
-      symbolic_name = symbolic_name+"_"+str(extendedN)
-
-    defined_typealiases.append(symbolic_name)
-
-    code.append("#define UA_NS"  + str(node.id().ns) + "ID_" + symbolic_name.upper() + " " + str(node.id().i))
-    return code
-
-  def getCreateNodeIDMacro(self, node):
-    if node.id().i != None:
-      return "UA_NODEID_NUMERIC(" + str(node.id().ns) + ", " + str(node.id().i) + ")"
-    elif node.id().s != None:
-      return "UA_NODEID_STRING("  + str(node.id().ns) + ", " + node.id().s + ")"
-    elif node.id().b != None:
-      logger.debug("NodeID Generation macro for bytestrings has not been implemented.")
-      return ""
-    elif node.id().g != None:
-      logger.debug("NodeID Generation macro for guids has not been implemented.")
-      return ""
-    else:
-      return ""
-
-  def getCreateStandaloneReference(self, sourcenode, reference):
-    code = []
-
-    if reference.isForward():
-      code.append("UA_Server_addReference(server, " + self.getCreateNodeIDMacro(sourcenode) + ", " + self.getCreateNodeIDMacro(reference.referenceType()) + ", " + self.getCreateExpandedNodeIDMacro(reference.target()) + ", true);")
-    else:
-      code.append("UA_Server_addReference(server, " + self.getCreateNodeIDMacro(sourcenode) + ", " + self.getCreateNodeIDMacro(reference.referenceType()) + ", " + self.getCreateExpandedNodeIDMacro(reference.target()) + ", false);")
-    return code
-
-  def getCreateNodeNoBootstrap(self, node, parentNode, parentReference, unprintedNodes):
-    code = []
-    code.append("// Node: " + str(node) + ", " + str(node.browseName()))
-
-    if node.nodeClass() == NODE_CLASS_OBJECT:
-      nodetype = "Object"
-    elif node.nodeClass() == NODE_CLASS_VARIABLE:
-      nodetype = "Variable"
-    elif node.nodeClass() == NODE_CLASS_METHOD:
-      nodetype = "Method"
-    elif node.nodeClass() == NODE_CLASS_OBJECTTYPE:
-      nodetype = "ObjectType"
-    elif node.nodeClass() == NODE_CLASS_REFERENCETYPE:
-      nodetype = "ReferenceType"
-    elif node.nodeClass() == NODE_CLASS_VARIABLETYPE:
-      nodetype = "VariableType"
-    elif node.nodeClass() == NODE_CLASS_DATATYPE:
-      nodetype = "DataType"
-    elif node.nodeClass() == NODE_CLASS_VIEW:
-      nodetype = "View"
-    else:
-      code.append("/* undefined nodeclass */")
-      return code;
-
-    # If this is a method, construct in/outargs for addMethod
-    #inputArguments.arrayDimensionsSize = 0;
-    #inputArguments.arrayDimensions = NULL;
-    #inputArguments.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
-
-    # Node ordering should have made sure that arguments, if they exist, have not been printed yet
-    if node.nodeClass() == NODE_CLASS_METHOD:
-        inArgVal = []
-        outArgVal = []
-        code.append("UA_Argument *inputArguments = NULL;")
-        code.append("UA_Argument *outputArguments = NULL;")
-        for r in node.getReferences():
-            if r.isForward():
-                if r.target() != None and r.target().nodeClass() == NODE_CLASS_VARIABLE and r.target().browseName() == 'InputArguments':
-                    while r.target() in unprintedNodes:
-                        unprintedNodes.remove(r.target())
-                    if r.target().value() != None:
-                        inArgVal = r.target().value().value
-                elif r.target() != None and r.target().nodeClass() == NODE_CLASS_VARIABLE and r.target().browseName() == 'OutputArguments':
-                    while r.target() in unprintedNodes:
-                        unprintedNodes.remove(r.target())
-                    if r.target().value() != None:
-                        outArgVal = r.target().value().value
-        if len(inArgVal)>0:
-            code.append("")
-            code.append("inputArguments = (UA_Argument *) UA_malloc(sizeof(UA_Argument) * " + str(len(inArgVal)) + ");")
-            code.append("int inputArgumentCnt;")
-            code.append("for (inputArgumentCnt=0; inputArgumentCnt<" + str(len(inArgVal)) + "; ++inputArgumentCnt) UA_Argument_init(&inputArguments[inputArgumentCnt]); ")
-            argumentCnt = 0
-            for inArg in inArgVal:
-                if inArg.getValueFieldByAlias("Description") != None:
-                    code.append("inputArguments[" + str(argumentCnt) + "].description = UA_LOCALIZEDTEXT(\"" + str(inArg.getValueFieldByAlias("Description")[0]) + "\",\"" + str(inArg.getValueFieldByAlias("Description")[1]) + "\");")
-                if inArg.getValueFieldByAlias("Name") != None:
-                    code.append("inputArguments[" + str(argumentCnt) + "].name = UA_STRING(\"" + str(inArg.getValueFieldByAlias("Name")) + "\");")
-                if inArg.getValueFieldByAlias("ValueRank") != None:
-                    code.append("inputArguments[" + str(argumentCnt) + "].valueRank = " + str(inArg.getValueFieldByAlias("ValueRank")) + ";")
-                if inArg.getValueFieldByAlias("DataType") != None:
-                    code.append("inputArguments[" + str(argumentCnt) + "].dataType = " + str(self.getCreateNodeIDMacro(inArg.getValueFieldByAlias("DataType"))) + ";")
-                #if inArg.getValueFieldByAlias("ArrayDimensions") != None:
-                #  code.append("inputArguments[" + str(argumentCnt) + "].arrayDimensions = " + str(inArg.getValueFieldByAlias("ArrayDimensions")) + ";")
-                argumentCnt += 1
-        if len(outArgVal)>0:
-            code.append("")
-            code.append("outputArguments = (UA_Argument *) UA_malloc(sizeof(UA_Argument) * " + str(len(outArgVal)) + ");")
-            code.append("int outputArgumentCnt;")
-            code.append("for (outputArgumentCnt=0; outputArgumentCnt<" + str(len(outArgVal)) + "; ++outputArgumentCnt) UA_Argument_init(&outputArguments[outputArgumentCnt]); ")
-            argumentCnt = 0
-            for outArg in outArgVal:
-                if outArg.getValueFieldByAlias("Description") != None:
-                    code.append("outputArguments[" + str(argumentCnt) + "].description = UA_LOCALIZEDTEXT(\"" + str(outArg.getValueFieldByAlias("Description")[0]) + "\",\"" + str(outArg.getValueFieldByAlias("Description")[1]) + "\");")
-                if outArg.getValueFieldByAlias("Name") != None:
-                    code.append("outputArguments[" + str(argumentCnt) + "].name = UA_STRING(\"" + str(outArg.getValueFieldByAlias("Name")) + "\");")
-                if outArg.getValueFieldByAlias("ValueRank") != None:
-                    code.append("outputArguments[" + str(argumentCnt) + "].valueRank = " + str(outArg.getValueFieldByAlias("ValueRank")) + ";")
-                if outArg.getValueFieldByAlias("DataType") != None:
-                    code.append("outputArguments[" + str(argumentCnt) + "].dataType = " + str(self.getCreateNodeIDMacro(outArg.getValueFieldByAlias("DataType"))) + ";")
-                #if outArg.getValueFieldByAlias("ArrayDimensions") != None:
-                #  code.append("outputArguments[" + str(argumentCnt) + "].arrayDimensions = " + str(outArg.getValueFieldByAlias("ArrayDimensions")) + ";")
-                argumentCnt += 1
-
-    # print the attributes struct
-    code.append("UA_%sAttributes attr = UA_%sAttributes_default;" % (nodetype, nodetype))
-    code.append("attr.displayName = UA_LOCALIZEDTEXT(\"\", \"" + str(node.displayName()) + "\");")
-    code.append("attr.description = UA_LOCALIZEDTEXT(\"\", \"" + str(node.description()) + "\");")
-    
-    if nodetype == "Variable":
-      code.append("attr.accessLevel = %s;"     % str(node.accessLevel()))
-      code.append("attr.userAccessLevel = %s;" % str(node.userAccessLevel()))
-    if nodetype in ["Variable", "VariableType"]:
-      code.append("attr.valueRank = %s;"       % str(node.valueRank()))
-      
-    if nodetype in ["Variable", "VariableType"]:
-      code = code + node.printOpen62541CCode_SubtypeEarly(bootstrapping = False)
-    elif nodetype == "Method":
-      if node.executable():
-        code.append("attr.executable = true;")
-      if node.userExecutable():
-        code.append("attr.userExecutable = true;")
-
-    code.append("UA_NodeId nodeId = " + str(self.getCreateNodeIDMacro(node)) + ";")
-    if nodetype in ["Object", "Variable", "VariableType"]:
-      typeDefinition = None
-      for r in node.getReferences():
-        if r.isForward() and r.referenceType().id().ns == 0 and r.referenceType().id().i == 40:
-          typeDefinition = r.target()
-      if typeDefinition == None:
-        # FIXME: We might want to resort to BaseXYTTypes here?
-        code.append("UA_NodeId typeDefinition = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE);")
-      else:
-        code.append("UA_NodeId typeDefinition = " + str(self.getCreateNodeIDMacro(typeDefinition)) + ";")
-    code.append("UA_NodeId parentNodeId = " + str(self.getCreateNodeIDMacro(parentNode)) + ";")
-    code.append("UA_NodeId parentReferenceNodeId = " + str(self.getCreateNodeIDMacro(parentReference.referenceType())) + ";")
-    extrNs = node.browseName().split(":")
-    if len(extrNs) > 1:
-      code.append("UA_QualifiedName nodeName = UA_QUALIFIEDNAME(" +  str(extrNs[0]) + ", \"" + extrNs[1] + "\");")
-    else:
-      code.append("UA_QualifiedName nodeName = UA_QUALIFIEDNAME(0, \"" + str(node.browseName()) + "\");")
-
-    # In case of a MethodNode: Add in|outArg struct generation here. Mandates that namespace reordering was done using
-    # Djikstra (check that arguments have not been printed). (@ichrispa)
-    code.append("UA_Server_add%sNode(server, nodeId, parentNodeId, parentReferenceNodeId, nodeName" % nodetype)
-
-    if nodetype in ["Object", "Variable", "VariableType"]:
-      code.append("       , typeDefinition")
-    if nodetype != "Method":
-      code.append("       , attr, NULL, NULL);")
-    else:
-      code.append("       , attr, (UA_MethodCallback) NULL, " + str(len(inArgVal)) + ", inputArguments,  " + str(len(outArgVal)) + ", outputArguments, NULL, NULL);")
-      
-    #Adding a Node with typeDefinition = UA_NODEID_NULL will create a HasTypeDefinition reference to BaseDataType - remove it since 
-    #a real Reference will be add in a later step (a single HasTypeDefinition reference is assumed here)
-    #The current API does not let us specify IDs of Object's subelements.
-    if nodetype is "Object":
-      code.append("UA_Server_deleteReference(server, nodeId, UA_NODEID_NUMERIC(0, 40), true, UA_EXPANDEDNODEID_NUMERIC(0, 58), true); //remove HasTypeDefinition refs generated by addObjectNode");
-    if nodetype is "Variable":
-      code.append("UA_Server_deleteReference(server, nodeId, UA_NODEID_NUMERIC(0, 40), true, UA_EXPANDEDNODEID_NUMERIC(0, 62), true); //remove HasTypeDefinition refs generated by addVariableNode");
-    return code

+ 0 - 403
tools/pyUANamespace/open62541_XMLPreprocessor.py

@@ -1,403 +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)
-###
-### 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.
-###
-
-import logging
-from ua_constants import *
-import tempfile
-import xml.dom.minidom as dom
-import os
-import string
-from collections import Counter
-import re
-
-from ua_namespace import opcua_node_id_t
-
-
-logger = logging.getLogger(__name__)
-
-class preProcessDocument:
-  originXML = '' # Original XML passed to the preprocessor
-  targetXML = () # tuple of (fileHandle, fileName)
-  nodeset   = '' # Parsed DOM XML object
-  parseOK   = False;
-  containedNodes  = [] # contains tuples of (opcua_node_id_t, xmlelement)
-  referencedNodes = [] # contains tuples of (opcua_node_id_t, xmlelement)
-  namespaceOrder  = [] # contains xmlns:sX attributed as tuples (int ns, string name)
-  namespaceQualifiers = []      # contains all xmlns:XYZ qualifiers that might prefix value aliases (like "<uax:Int32>")
-  referencedNamesSpaceUris = [] # contains <NamespaceUris> URI elements
-
-  def __init__(self, originXML):
-    self.originXML = originXML
-    self.targetXML = tempfile.mkstemp(prefix=os.path.basename(originXML)+"_preProcessed-" ,suffix=".xml")
-    self.parseOK   = True
-    self.containedNodes  = []
-    self.referencedNodes = []
-    self.namespaceOrder  = []
-    self.referencedNamesSpaceUris = []
-    self.namespaceQualifiers = []
-    try:
-      self.nodeset = dom.parse(originXML)
-      if len(self.nodeset.getElementsByTagName("UANodeSet")) == 0 or len(self.nodeset.getElementsByTagName("UANodeSet")) > 1:
-        logger.error(self, "Document " + self.targetXML[1] + " contains no or more then 1 nodeset", LOG_LEVEL_ERROR)
-        self.parseOK   = False
-    except:
-      self.parseOK   = False
-    logger.debug("Adding new document to be preprocessed " + os.path.basename(originXML) + " as " + self.targetXML[1])
-
-  def clean(self):
-    #os.close(self.targetXML[0]) Don't -> done to flush() after finalize()
-    os.remove(self.targetXML[1])
-
-  def getTargetXMLName(self):
-    if (self.parseOK):
-      return self.targetXML[1]
-    return None
-
-  def extractNamespaceURIs(self):
-    """ extractNamespaceURIs
-
-        minidom gobbles up <NamespaceUris></NamespaceUris> elements, without a decent
-        way to reliably access this dom2 <uri></uri> elements (only attribute xmlns= are
-        accessible using minidom).  We need them for dereferencing though... This
-        function attempts to do just that.
-
-        returns: Nothing
-    """
-    infile = open(self.originXML)
-    foundURIs = False
-    nsline = ""
-    line = infile.readline()
-    for line in infile:
-      if "<namespaceuris>" in line.lower():
-        foundURIs = True
-      elif "</namespaceuris>" in line.lower():
-        foundURIs = False
-        nsline = nsline + line
-        break
-      if foundURIs:
-        nsline = nsline + line
-
-    if len(nsline) > 0:
-      ns = dom.parseString(nsline).getElementsByTagName("NamespaceUris")
-      for uri in ns[0].childNodes:
-        if uri.nodeType != uri.ELEMENT_NODE:
-          continue
-        self.referencedNamesSpaceUris.append(uri.firstChild.data)
-
-    infile.close()
-
-  def analyze(self):
-    """ analyze()
-
-        analyze will gather information about the nodes and references contained in a XML File
-        to facilitate later preprocessing stages that adresss XML dependency issues
-
-        returns: No return value
-    """
-    nodeIds = []
-    ns = self.nodeset.getElementsByTagName("UANodeSet")
-
-    # We need to find out what the namespace calls itself and other referenced, as numeric id's are pretty
-    # useless sans linked nodes. There is two information sources...
-    self.extractNamespaceURIs() # From <URI>...</URI> definitions
-
-    for key in ns[0].attributes.keys(): # from xmlns:sX attributes
-      if "xmlns:" in key:  # Any key: we will be removing these qualifiers from Values later
-        self.namespaceQualifiers.append(key.replace("xmlns:",""))
-      if "xmlns:s" in key: # get a numeric nsId and modelname/uri
-        self.namespaceOrder.append((int(key.replace("xmlns:s","")), re.sub("[A-Za-z0-9-_\.]+\.[xXsSdD]{3}$","",ns[0].getAttribute(key))))
-
-    # Get all nodeIds contained in this XML
-    for nd in ns[0].childNodes:
-      if nd.nodeType != nd.ELEMENT_NODE:
-        continue
-      if nd.hasAttribute(u'NodeId'):
-        self.containedNodes.append( (opcua_node_id_t(nd.getAttribute(u'NodeId')), nd) )
-        refs = nd.getElementsByTagName(u'References')[0]
-        for ref in refs.childNodes:
-          if ref.nodeType == ref.ELEMENT_NODE:
-            self.referencedNodes.append( (opcua_node_id_t(ref.firstChild.data), ref) )
-
-    logger.debug("Nodes: " + str(len(self.containedNodes)) + " References: " + str(len(self.referencedNodes)))
-
-  def getNamespaceId(self):
-    """ namespaceId()
-
-        Counts the namespace IDs in all nodes of this XML and picks the most used
-        namespace as the numeric identifier of this data model.
-
-        returns: Integer ID of the most propable/most used namespace in this XML
-    """
-    max = 0;
-    namespaceIdGuessed = 0;
-    idDict = {}
-
-    for ndid in self.containedNodes:
-      if not ndid[0].ns in idDict.keys():
-        idDict[ndid[0].ns] = 1
-      else:
-        idDict[ndid[0].ns] = idDict[ndid[0].ns] + 1
-
-    for entry in idDict:
-      if idDict[entry] > max:
-        max = idDict[entry]
-        namespaceIdGuessed = entry
-    #logger.debug("XML Contents are propably in namespace " + str(entry) + " (used by " + str(idDict[entry]) + " Nodes)")
-    return namespaceIdGuessed
-
-  def getReferencedNamespaceUri(self, nsId):
-    """ getReferencedNamespaceUri
-
-        returns an URL that hopefully corresponds to the nsId that was used to reference this model
-
-        return: URI string corresponding to nsId
-    """
-    # Might be the more reliable method: Get the URI from the xmlns attributes (they have numers)
-    if len(self.namespaceOrder) > 0:
-      for el in self.namespaceOrder:
-        if el[0] == nsId:
-          return el[1]
-
-    # Fallback:
-    #  Some models do not have xmlns:sX attributes, but still <URI>s (usually when they only reference NS0)
-    if len(self.referencedNamesSpaceUris) > 0  and len(self.referencedNamesSpaceUris) >= nsId-1:
-      return self.referencedNamesSpaceUris[nsId-1]
-
-    #Nope, not found.
-    return ""
-
-  def getNamespaceDependencies(self):
-    deps = []
-    for ndid in self.referencedNodes:
-      if not ndid[0].ns in deps:
-        deps.append(ndid[0].ns)
-    return deps
-
-  def finalize(self):
-    outfile = self.targetXML[0]
-    outline = self.nodeset.toxml()
-    for qualifier in self.namespaceQualifiers:
-      rq = qualifier+":"
-      outline = outline.replace(rq, "")
-    os.write(outfile, outline.encode('UTF-8'))
-    os.close(outfile)
-
-  def reassignReferencedNamespaceId(self, currentNsId, newNsId):
-    """ reassignReferencedNamespaceId
-
-        Iterates over all references in this document, find references to currentNsId and changes them to newNsId.
-        NodeIds themselves are not altered.
-
-        returns: nothing
-    """
-    for refNd in self.referencedNodes:
-      if refNd[0].ns == currentNsId:
-        refNd[1].firstChild.data = refNd[1].firstChild.data.replace("ns="+str(currentNsId), "ns="+str(newNsId))
-        refNd[0].ns = newNsId
-        refNd[0].toString()
-
-  def reassignNamespaceId(self, currentNsId, newNsId):
-    """ reassignNamespaceId
-
-        Iterates over all nodes in this document, find those in namespace currentNsId and changes them to newNsId.
-
-        returns: nothing
-    """
-
-    #change ids in aliases
-    ns = self.nodeset.getElementsByTagName("Alias")
-    for al in ns:
-      if al.nodeType == al.ELEMENT_NODE:
-        if al.hasAttribute("Alias"):
-          al.firstChild.data = al.firstChild.data.replace("ns=" + str(currentNsId), "ns=" + str(newNsId))
-
-    logger.debug("Migrating nodes /w ns index " + str(currentNsId) + " to " + str(newNsId))
-    for nd in self.containedNodes:
-      if nd[0].ns == currentNsId:
-        # In our own document, update any references to this node
-        for refNd in self.referencedNodes:
-          if refNd[0].ns == currentNsId and refNd[0] == nd[0]:
-            refNd[1].firstChild.data = refNd[1].firstChild.data.replace("ns="+str(currentNsId), "ns="+str(newNsId))
-            refNd[0].ns = newNsId
-            refNd[0].toString()
-        nd[1].setAttribute(u'NodeId', nd[1].getAttribute(u'NodeId').replace("ns="+str(currentNsId), "ns="+str(newNsId)))
-        nd[0].ns = newNsId
-        nd[0].toString()
-
-class open62541_XMLPreprocessor:
-  preProcDocuments = []
-
-  def __init__(self):
-      self.preProcDocuments = []
-
-  def addDocument(self, documentPath):
-    self.preProcDocuments.append(preProcessDocument(documentPath))
-
-  def removePreprocessedFiles(self):
-    for doc in self.preProcDocuments:
-      doc.clean()
-
-  def getPreProcessedFiles(self):
-    files = []
-    for doc in self.preProcDocuments:
-      if (doc.parseOK):
-        files.append(doc.getTargetXMLName())
-    return files
-
-  def testModelCongruencyAgainstReferences(self, doc, refs):
-    """ testModelCongruencyAgainstReferences
-
-        Counts how many of the nodes referencef in refs can be found in the model
-        doc.
-
-        returns: double corresponding to the percentage of hits
-    """
-    sspace = len(refs)
-    if sspace == 0:
-      return float(0)
-    found   = 0
-    for ref in refs:
-      for n in doc.containedNodes:
-        if str(ref) == str(n[0]):
-          print(ref, n[0])
-          found = found + 1
-          break
-    return float(found)/float(sspace)
-
-  def preprocess_assignUniqueNsIds(self):
-    nsdep  = []
-    docLst = []
-    # Search for namespace 0('s) - plural possible if user is overwriting NS0 defaults
-    # Remove them from the list of namespaces, zero does not get demangled
-    for doc in self.preProcDocuments:
-      if doc.getNamespaceId() == 0:
-        docLst.append(doc)
-    for doc in docLst:
-      self.preProcDocuments.remove(doc)
-
-    # Reassign namespace id's to be in ascending order
-    nsidx = 1 # next namespace id to assign on collision (first one will be "2")
-    for doc in self.preProcDocuments:
-      nsidx = nsidx + 1
-      nsid = doc.getNamespaceId()
-      doc.reassignNamespaceId(nsid, nsidx)
-      docLst.append(doc)
-      logger.info("Document " + doc.originXML + " is now namespace " + str(nsidx))
-    self.preProcDocuments = docLst
-
-  def getUsedNamespaceArrayNames(self):
-    """ getUsedNamespaceArrayNames
-
-        Returns the XML xmlns:s1 or <URI>[0] of each XML document (if contained/possible)
-
-        returns: dict of int:nsId -> string:url
-    """
-    nsName = {}
-    for doc in self.preProcDocuments:
-      uri = doc.getReferencedNamespaceUri(1)
-      if uri == None:
-        uri = "http://modeluri.not/retrievable/from/xml"
-      nsName[doc.getNamespaceId()] = doc.getReferencedNamespaceUri(1)
-    return nsName
-
-  def preprocess_linkDependantModels(self):
-    revertToStochastic = [] # (doc, int id), where id was not resolvable using model URIs
-
-    # Attemp to identify the model relations by using model URIs in xmlns:sX or <URI> contents
-    for doc in self.preProcDocuments:
-      nsid = doc.getNamespaceId()
-      dependencies = doc.getNamespaceDependencies()
-      for d in dependencies:
-        if d != nsid and d != 0:
-          # Attempt to identify the namespace URI this d referes to...
-          nsUri = doc.getReferencedNamespaceUri(d) # FIXME: This could actually fail and return ""!
-          logger.info("Need a namespace referenced as " + str(d) + ". Which hopefully is " + nsUri)
-          targetDoc = None
-          for tgt in self.preProcDocuments:
-            # That model, whose URI is known but its current id is not, will
-            #   refer have referred to itself as "1"
-            if tgt.getReferencedNamespaceUri(1) == nsUri:
-              targetDoc = tgt
-              break
-          if not targetDoc == None:
-            # Found the model... relink the references
-            doc.reassignReferencedNamespaceId(d, targetDoc.getNamespaceId())
-            continue
-          else:
-            revertToStochastic.append((doc, d))
-            logger.warn("Failed to reliably identify which XML/Model " + os.path.basename(doc.originXML) + " calls ns=" +str(d))
-
-    for (doc, d) in revertToStochastic:
-      logger.warn("Attempting to find stochastic match for target namespace ns=" + str(d) + " of " + os.path.basename(doc.originXML))
-      # Copy all references to the given namespace
-      refs = []
-      matches = [] # list of (match%, targetDoc) to pick from later
-      for ref in doc.referencedNodes:
-        if ref[0].ns == d:
-          refs.append(opcua_node_id_t(str(ref[0])))
-      for tDoc in self.preProcDocuments:
-        tDocId = tDoc.getNamespaceId()
-        # Scenario: If these references did target this documents namespace...
-        for r in refs:
-          r.ns = tDocId
-          r.toString()
-        # ... how many of them would be found!?
-        c = self.testModelCongruencyAgainstReferences(tDoc, refs)
-        print(c)
-        if c>0:
-          matches.append((c, tDoc))
-      best = (0, None)
-      for m in matches:
-        print(m[0])
-        if m[0] > best[0]:
-          best = m
-      if best[1] != None:
-        logger.warn("Best match (" + str(best[1]*100) + "%) for what " + os.path.basename(doc.originXML) + " refers to as ns="+str(d)+" was " + os.path.basename(best[1].originXML))
-        doc.reassignReferencedNamespaceId(d, best[1].getNamespaceId())
-      else:
-        logger.error("Failed to find a match for what " +  os.path.basename(doc.originXML) + " refers to as ns=" + str(d))
-
-  def preprocessAll(self):
-    ##
-    ## First: Gather statistics about the namespaces:
-    for doc in self.preProcDocuments:
-      doc.analyze()
-
-    # Preprocess step: Remove XML specific Naming scheme ("uax:")
-    # FIXME: Not implemented
-
-    ##
-    ## Preprocess step: Check namespace ID multiplicity and reassign IDs if necessary
-    ##
-    self.preprocess_assignUniqueNsIds()
-    self.preprocess_linkDependantModels()
-
-
-    ##
-    ## Prep step: prevent any XML from using namespace 1 (reserved for instances)
-    ## FIXME: Not implemented
-
-    ##
-    ## Final: Write modified XML tmp files
-    for doc in self.preProcDocuments:
-      doc.finalize()
-
-    return True

File diff suppressed because it is too large
+ 0 - 1292
tools/pyUANamespace/ua_builtin_types.py


+ 0 - 823
tools/pyUANamespace/ua_namespace.py

@@ -1,823 +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.
-###
-
-from __future__ import print_function
-import sys
-from time import struct_time, strftime, strptime, mktime
-from struct import pack as structpack
-
-import logging
-from ua_builtin_types import *;
-from ua_node_types import *;
-from ua_constants import *;
-from open62541_MacroHelper import open62541_MacroHelper
-
-
-logger = logging.getLogger(__name__)
-
-def getNextElementNode(xmlvalue):
-  if xmlvalue == None:
-    return None
-  xmlvalue = xmlvalue.nextSibling
-  while not xmlvalue == None and not xmlvalue.nodeType == xmlvalue.ELEMENT_NODE:
-    xmlvalue = xmlvalue.nextSibling
-  return xmlvalue
-
-###
-### Namespace Organizer
-###
-
-class opcua_namespace():
-  """ Class holding and managing a set of OPCUA nodes.
-
-      This class handles parsing XML description of namespaces, instantiating
-      nodes, linking references, graphing the namespace and compiling a binary
-      representation.
-
-      Note that nodes assigned to this class are not restricted to having a
-      single namespace ID. This class represents the entire physical address
-      space of the binary representation and all nodes that are to be included
-      in that segment of memory.
-  """
-  nodes = []
-  nodeids = {}
-  aliases = {}
-  __linkLater__ = []
-  __binaryIndirectPointers__ = []
-  name = ""
-  knownNodeTypes = ""
-  namespaceIdentifiers = {} # list of 'int':'string' giving different namespace an array-mapable name
-
-  def __init__(self, name):
-    self.nodes = []
-    self.knownNodeTypes = ['variable', 'object', 'method', 'referencetype', \
-                           'objecttype', 'variabletype', 'methodtype', \
-                           'datatype', 'referencetype', 'aliases']
-    self.name = name
-    self.nodeids = {}
-    self.aliases = {}
-    self.namespaceIdentifiers = {}
-    self.__binaryIndirectPointers__ = []
-
-  def addNamespace(self, numericId, stringURL):
-    self.namespaceIdentifiers[numericId] = stringURL
-
-  def linkLater(self, pointer):
-    """ Called by nodes or references who have parsed an XML reference to a
-        node represented by a string.
-
-        No return value
-
-        XML String representations of references have the form 'i=xy' or
-        'ns=1;s="This unique Node"'. Since during the parsing of this attribute
-        only a subset of nodes are known/parsed, this reference string cannot be
-        linked when encountered.
-
-        References register themselves with the namespace to have their target
-        attribute (string) parsed by linkOpenPointers() when all nodes are
-        created, so that target can be dereferenced an point to an actual node.
-    """
-    self.__linkLater__.append(pointer)
-
-  def getUnlinkedPointers(self):
-    """ Return the list of references registered for linking during the next call
-        of linkOpenPointers()
-    """
-    return self.__linkLater__
-
-  def unlinkedItemCount(self):
-    """ Returns the number of unlinked references that will be processed during
-        the next call of linkOpenPointers()
-    """
-    return len(self.__linkLater__)
-
-  def buildAliasList(self, xmlelement):
-    """ Parses the <Alias> XML Element present in must XML NodeSet definitions.
-
-        No return value
-
-        Contents the Alias element are stored in a dictionary for further
-        dereferencing during pointer linkage (see linkOpenPointer()).
-    """
-    if not xmlelement.tagName == "Aliases":
-      logger.error("XMLElement passed is not an Aliaslist")
-      return
-    for al in xmlelement.childNodes:
-      if al.nodeType == al.ELEMENT_NODE:
-        if al.hasAttribute("Alias"):
-          aliasst = al.getAttribute("Alias")
-          if sys.version_info[0] < 3:
-            aliasnd = unicode(al.firstChild.data)
-          else:
-            aliasnd = al.firstChild.data
-          if not aliasst in self.aliases:
-            self.aliases[aliasst] = aliasnd
-            logger.debug("Added new alias \"" + str(aliasst) + "\" == \"" + str(aliasnd) + "\"")
-          else:
-            if self.aliases[aliasst] != aliasnd:
-              logger.error("Alias definitions for " + aliasst + " differ. Have " + self.aliases[aliasst] + " but XML defines " + aliasnd + ". Keeping current definition.")
-
-  def getNodeByBrowseName(self, idstring):
-    """ Returns the first node in the nodelist whose browseName matches idstring.
-    """
-    matches = []
-    for n in self.nodes:
-      if idstring==str(n.browseName()):
-        matches.append(n)
-    if len(matches) > 1:
-      logger.error("Found multiple nodes with same ID!?")
-    if len(matches) == 0:
-      return None
-    else:
-      return matches[0]
-
-  def getNodeByIDString(self, idstring):
-    """ Returns the first node in the nodelist whose id string representation
-        matches idstring.
-    """
-    matches = []
-    for n in self.nodes:
-      if idstring==str(n.id()):
-        matches.append(n)
-    if len(matches) > 1:
-      logger.error("Found multiple nodes with same ID!?")
-    if len(matches) == 0:
-      return None
-    else:
-      return matches[0]
-
-  def createNode(self, ndtype, xmlelement):
-    """ createNode is instantiates a node described by xmlelement, its type being
-        defined by the string ndtype.
-
-        No return value
-
-        If the xmlelement is an <Alias>, the contents will be parsed and stored
-        for later dereferencing during pointer linking (see linkOpenPointers).
-
-        Recognized types are:
-        * UAVariable
-        * UAObject
-        * UAMethod
-        * UAView
-        * UAVariableType
-        * UAObjectType
-        * UAMethodType
-        * UAReferenceType
-        * UADataType
-
-        For every recognized type, an appropriate node class is added to the node
-        list of the namespace. The NodeId of the given node is created and parsing
-        of the node attributes and elements is delegated to the parseXML() and
-        parseXMLSubType() functions of the instantiated class.
-
-        If the NodeID attribute is non-unique in the node list, the creation is
-        deferred and an error is logged.
-    """
-    if not isinstance(xmlelement, dom.Element):
-      logger.error( "Error: Can not create node from invalid XMLElement")
-      return
-
-    # An ID is mandatory for everything but aliases!
-    id = None
-    for idname in ['NodeId', 'NodeID', 'nodeid']:
-      if xmlelement.hasAttribute(idname):
-        id = xmlelement.getAttribute(idname)
-    if ndtype == 'aliases':
-      self.buildAliasList(xmlelement)
-      return
-    elif id == None:
-      logger.info( "Error: XMLElement has no id, node will not be created!")
-      return
-    else:
-      id = opcua_node_id_t(id)
-
-    if str(id) in self.nodeids:
-      # Normal behavior: Do not allow duplicates, first one wins
-      #logger.error( "XMLElement with duplicate ID " + str(id) + " found, node will not be created!")
-      #return
-      # Open62541 behavior for header generation: Replace the duplicate with the new node
-      logger.info( "XMLElement with duplicate ID " + str(id) + " found, node will be replaced!")
-      nd = self.getNodeByIDString(str(id))
-      self.nodes.remove(nd)
-      self.nodeids.pop(str(nd.id()))
-
-    node = None
-    if (ndtype == 'variable'):
-      node = opcua_node_variable_t(id, self)
-    elif (ndtype == 'object'):
-      node = opcua_node_object_t(id, self)
-    elif (ndtype == 'method'):
-      node = opcua_node_method_t(id, self)
-    elif (ndtype == 'objecttype'):
-      node = opcua_node_objectType_t(id, self)
-    elif (ndtype == 'variabletype'):
-      node = opcua_node_variableType_t(id, self)
-    elif (ndtype == 'methodtype'):
-      node = opcua_node_methodType_t(id, self)
-    elif (ndtype == 'datatype'):
-      node = opcua_node_dataType_t(id, self)
-    elif (ndtype == 'referencetype'):
-      node = opcua_node_referenceType_t(id, self)
-    else:
-      logger.error( "No node constructor for type " + ndtype)
-
-    if node != None:
-      node.parseXML(xmlelement)
-
-    self.nodes.append(node)
-    self.nodeids[str(node.id())] = node
-
-  def removeNodeById(self, nodeId):
-    nd = self.getNodeByIDString(nodeId)
-    if nd == None:
-      return False
-
-    logger.debug("Removing nodeId " + str(nodeId))
-    self.nodes.remove(nd)
-    if nd.getInverseReferences() != None:
-      for ref in nd.getInverseReferences():
-        src = ref.target();
-        src.removeReferenceToNode(nd)
-
-    return True
-
-  def registerBinaryIndirectPointer(self, node):
-    """ Appends a node to the list of nodes that should be contained in the
-        first 765 bytes (255 pointer slots a 3 bytes) in the binary
-        representation (indirect referencing space).
-
-        This function is reserved for references and dataType pointers.
-    """
-    if not node in self.__binaryIndirectPointers__:
-      self.__binaryIndirectPointers__.append(node)
-    return self.__binaryIndirectPointers__.index(node)
-
-  def getBinaryIndirectPointerIndex(self, node):
-    """ Returns the slot/index of a pointer in the indirect referencing space
-        (first 765 Bytes) of the binary representation.
-    """
-    if not node in self.__binaryIndirectPointers__:
-      return -1
-    return self.__binaryIndirectPointers__.index(node)
-
-
-  def parseXML(self, xmldoc):
-    """ Reads an XML Namespace definition and instantiates node.
-
-        No return value
-
-        parseXML open the file xmldoc using xml.dom.minidom and searches for
-        the first UANodeSet Element. For every Element encountered, createNode
-        is called to instantiate a node of the appropriate type.
-    """
-    typedict = {}
-    UANodeSet = dom.parse(xmldoc).getElementsByTagName("UANodeSet")
-    if len(UANodeSet) == 0:
-      logger.error( "Error: No NodeSets found")
-      return
-    if len(UANodeSet) != 1:
-      logger.error( "Error: Found more than 1 Nodeset in XML File")
-
-    UANodeSet = UANodeSet[0]
-    for nd in UANodeSet.childNodes:
-      if nd.nodeType != nd.ELEMENT_NODE:
-        continue
-
-      ndType = nd.tagName.lower()
-      if ndType[:2] == "ua":
-        ndType = ndType[2:]
-      elif not ndType in self.knownNodeTypes:
-        logger.warn("XML Element or NodeType " + ndType + " is unknown and will be ignored")
-        continue
-
-      if not ndType in typedict:
-        typedict[ndType] = 1
-      else:
-        typedict[ndType] = typedict[ndType] + 1
-
-      self.createNode(ndType, nd)
-    logger.debug("Currently " + str(len(self.nodes)) + " nodes in address space. Type distribution for this run was: " + str(typedict))
-
-  def linkOpenPointers(self):
-    """ Substitutes symbolic NodeIds in references for actual node instances.
-
-        No return value
-
-        References that have registered themselves with linkLater() to have
-        their symbolic NodeId targets ("ns=2;i=32") substituted for an actual
-        node will be iterated by this function. For each reference encountered
-        in the list of unlinked/open references, the target string will be
-        evaluated and searched for in the node list of this namespace. If found,
-        the target attribute of the reference will be substituted for the
-        found node.
-
-        If a reference fails to get linked, it will remain in the list of
-        unlinked references. The individual items in this list can be
-        retrieved using getUnlinkedPointers().
-    """
-    linked = []
-
-    logger.debug( str(self.unlinkedItemCount()) + " pointers need to get linked.")
-    for l in self.__linkLater__:
-      targetLinked = False
-      if not l.target() == None and not isinstance(l.target(), opcua_node_t):
-        if isinstance(l.target(),str) or isinstance(l.target(),unicode):
-          # If is not a node ID, it should be an alias. Try replacing it
-          # with a proper node ID
-          if l.target() in self.aliases:
-            l.target(self.aliases[l.target()])
-          # If the link is a node ID, try to find it hopening that no ass has
-          # defined more than one kind of id for that sucker
-          if l.target()[:2] == "i=" or l.target()[:2] == "g=" or \
-             l.target()[:2] == "b=" or l.target()[:2] == "s=" or \
-             l.target()[:3] == "ns=" :
-            tgt = self.getNodeByIDString(str(l.target()))
-            if tgt == None:
-              logger.error("Failed to link pointer to target (node not found) " + l.target())
-            else:
-              l.target(tgt)
-              targetLinked = True
-          else:
-            logger.error("Failed to link pointer to target (target not Alias or Node) " + l.target())
-        else:
-          logger.error("Failed to link pointer to target (don't know dummy type + " + str(type(l.target())) + " +) " + str(l.target()))
-      else:
-        logger.error("Pointer has null target: " + str(l))
-
-
-      referenceLinked = False
-      if not l.referenceType() == None:
-        if l.referenceType() in self.aliases:
-          l.referenceType(self.aliases[l.referenceType()])
-        tgt = self.getNodeByIDString(str(l.referenceType()))
-        if tgt == None:
-          logger.error("Failed to link reference type to target (node not found) " + l.referenceType())
-        else:
-          l.referenceType(tgt)
-          referenceLinked = True
-      else:
-        referenceLinked = True
-
-      if referenceLinked == True and targetLinked == True:
-        linked.append(l)
-
-    # References marked as "not forward" must be inverted (removed from source node, assigned to target node and relinked)
-    logger.warn("Inverting reference direction for all references with isForward==False attribute (is this correct!?)")
-    for n in self.nodes:
-      for r in n.getReferences():
-        if r.isForward() == False:
-          tgt = r.target()
-          if isinstance(tgt, opcua_node_t):
-            nref = opcua_referencePointer_t(n, parentNode=tgt)
-            nref.referenceType(r.referenceType())
-            tgt.addReference(nref)
-
-    # Create inverse references for all nodes
-    logger.debug("Updating all referencedBy fields in nodes for inverse lookups.")
-    for n in self.nodes:
-      n.updateInverseReferences()
-
-    for l in linked:
-      self.__linkLater__.remove(l)
-
-    if len(self.__linkLater__) != 0:
-      logger.warn(str(len(self.__linkLater__)) + " could not be linked.")
-
-  def sanitize(self):
-    remove = []
-    logger.debug("Sanitizing nodes and references...")
-    for n in self.nodes:
-      if n.sanitize() == False:
-        remove.append(n)
-    if not len(remove) == 0:
-      logger.warn(str(len(remove)) + " nodes will be removed because they failed sanitation.")
-    # FIXME: Some variable ns=0 nodes fail because they don't have DataType fields...
-    #        How should this be handles!?
-    logger.warn("Not actually removing nodes... it's unclear if this is valid or not")
-
-  def getRoot(self):
-    """ Returns the first node instance with the browseName "Root".
-    """
-    return self.getNodeByBrowseName("Root")
-
-  def buildEncodingRules(self):
-    """ Calls buildEncoding() for all DataType nodes (opcua_node_dataType_t).
-
-        No return value
-    """
-    stat = {True: 0, False: 0}
-    for n in self.nodes:
-      if isinstance(n, opcua_node_dataType_t):
-        n.buildEncoding()
-        stat[n.isEncodable()] = stat[n.isEncodable()] + 1
-    logger.debug("Type definitions built/passed: " +  str(stat))
-
-  def allocateVariables(self):
-    for n in self.nodes:
-      if isinstance(n, opcua_node_variable_t):
-        n.allocateValue()
-
-  def printDot(self, filename="namespace.dot"):
-    """ Outputs a graphiz/dot description of all nodes in the namespace.
-
-        Output will written into filename to be parsed by dot/neato...
-
-        Note that for namespaces with more then 20 nodes the reference structure
-        will lead to a mostly illegible and huge graph. Use printDotGraphWalk()
-        for plotting specific portions of a large namespace.
-    """
-    file=open(filename, 'w+')
-
-    file.write("digraph ns {\n")
-    for n in self.nodes:
-      file.write(n.printDot())
-    file.write("}\n")
-    file.close()
-
-  def getSubTypesOf(self, tdNodes = None, currentNode = None, hasSubtypeRefNode = None):
-    # If this is a toplevel call, collect the following information as defaults
-    if tdNodes == None:
-      tdNodes = []
-    if currentNode == None:
-      currentNode = self.getNodeByBrowseName("HasTypeDefinition")
-      tdNodes.append(currentNode)
-      if len(tdNodes) < 1:
-        return []
-    if hasSubtypeRefNode == None:
-      hasSubtypeRefNode = self.getNodeByBrowseName("HasSubtype")
-      if hasSubtypeRefNode == None:
-        return tdNodes
-
-    # collect all subtypes of this node
-    for ref in currentNode.getReferences():
-      if ref.isForward() and ref.referenceType().id() == hasSubtypeRefNode.id():
-        tdNodes.append(ref.target())
-        self.getTypeDefinitionNodes(tdNodes=tdNodes, currentNode = ref.target(), hasSubtypeRefNode=hasSubtypeRefNode)
-
-    return tdNodes
-
-
-  def printDotGraphWalk(self, depth=1, filename="out.dot", rootNode=None, followInverse = False, excludeNodeIds=[]):
-    """ Outputs a graphiz/dot description the nodes centered around rootNode.
-
-        References beginning from rootNode will be followed for depth steps. If
-        "followInverse = True" is passed, then inverse (not Forward) references
-        will also be followed.
-
-        Nodes can be excluded from the graph by passing a list of NodeIds as
-        string representation using excludeNodeIds (ex ["i=53", "ns=2;i=453"]).
-
-        Output is written into filename to be parsed by dot/neato/srfp...
-    """
-    iter = depth
-    processed = []
-    if rootNode == None or \
-       not isinstance(rootNode, opcua_node_t) or \
-       not rootNode in self.nodes:
-      root = self.getRoot()
-    else:
-      root = rootNode
-
-    file=open(filename, 'w+')
-
-    if root == None:
-      return
-
-    file.write("digraph ns {\n")
-    file.write(root.printDot())
-    refs=[]
-    if followInverse == True:
-      refs = root.getReferences(); # + root.getInverseReferences()
-    else:
-      for ref in root.getReferences():
-        if ref.isForward():
-          refs.append(ref)
-    while iter > 0:
-      tmp = []
-      for ref in refs:
-        if isinstance(ref.target(), opcua_node_t):
-          tgt = ref.target()
-          if not str(tgt.id()) in excludeNodeIds:
-            if not tgt in processed:
-              file.write(tgt.printDot())
-              processed.append(tgt)
-              if ref.isForward() == False and followInverse == True:
-                tmp = tmp + tgt.getReferences(); # + tgt.getInverseReferences()
-              elif ref.isForward() == True :
-                tmp = tmp + tgt.getReferences();
-      refs = tmp
-      iter = iter - 1
-
-    file.write("}\n")
-    file.close()
-
-  def __reorder_getMinWeightNode__(self, nmatrix):
-    rcind = -1
-    rind = -1
-    minweight = -1
-    minweightnd = None
-    for row in nmatrix:
-      rcind += 1
-      if row[0] == None:
-        continue
-      w = sum(row[1:])
-      if minweight < 0:
-        rind = rcind
-        minweight = w
-        minweightnd = row[0]
-      elif w < minweight:
-        rind = rcind
-        minweight = w
-        minweightnd = row[0]
-    return (rind, minweightnd, minweight)
-
-  def reorderNodesMinDependencies(self):
-    # create a matrix represtantion of all node
-    #
-    nmatrix = []
-    for n in range(0,len(self.nodes)):
-      nmatrix.append([None] + [0]*len(self.nodes))
-
-    typeRefs = []
-    tn = self.getNodeByBrowseName("HasTypeDefinition")
-    if tn != None:
-      typeRefs.append(tn)
-      typeRefs = typeRefs + self.getSubTypesOf(currentNode=tn)
-    subTypeRefs = []
-    tn = self.getNodeByBrowseName("HasSubtype")
-    if tn  != None:
-      subTypeRefs.append(tn)
-      subTypeRefs = subTypeRefs + self.getSubTypesOf(currentNode=tn)
-
-    logger.debug("Building connectivity matrix for node order optimization.")
-    # Set column 0 to contain the node
-    for node in self.nodes:
-      nind = self.nodes.index(node)
-      nmatrix[nind][0] = node
-
-    # Determine the dependencies of all nodes
-    logger.debug("Determining node interdependencies.")
-    for node in self.nodes:
-      nind = self.nodes.index(node)
-      #print "Examining node " + str(nind) + " " + str(node)
-      for ref in node.getReferences():
-        if isinstance(ref.target(), opcua_node_t):
-          tind = self.nodes.index(ref.target())
-          # Typedefinition of this node has precedence over this node
-          if ref.referenceType() in typeRefs and ref.isForward():
-            nmatrix[nind][tind+1] += 200 # Very big weight for typedefs
-          # isSubTypeOf/typeDefinition of this node has precedence over this node
-          elif ref.referenceType() in subTypeRefs and not ref.isForward():
-            nmatrix[nind][tind+1] += 100 # Big weight for subtypes
-          # Else the target depends on us
-          elif ref.isForward():
-            nmatrix[tind][nind+1] += 1 # regular weight for dependencies
-
-    logger.debug("Using Djikstra topological sorting to determine printing order.")
-    reorder = []
-    while len(reorder) < len(self.nodes):
-      (nind, node, w) = self.__reorder_getMinWeightNode__(nmatrix)
-      #print  str(100*float(len(reorder))/len(self.nodes)) + "% " + str(w) + " " + str(node) + " " + str(node.browseName())
-      reorder.append(node)
-      for ref in node.getReferences():
-        if isinstance(ref.target(), opcua_node_t):
-          tind = self.nodes.index(ref.target())
-          if ref.referenceType() in typeRefs and ref.isForward():
-            nmatrix[nind][tind+1] -= 200
-          elif ref.referenceType() in subTypeRefs and not ref.isForward():
-            nmatrix[nind][tind+1] -= 100
-          elif ref.isForward():
-            nmatrix[tind][nind+1] -= 1
-      nmatrix[nind][0] = None
-    self.nodes = reorder
-    logger.debug("Nodes reordered.")
-    return
-
-  def printOpen62541Header(self, printedExternally=[], supressGenerationOfAttribute=[], outfilename=""):
-    unPrintedNodes = []
-    unPrintedRefs  = []
-    code = []
-    header = []
-
-    # Reorder our nodes to produce a bare minimum of bootstrapping dependencies
-    logger.debug("Reordering nodes for minimal dependencies during printing.")
-    self.reorderNodesMinDependencies()
-
-    # Some macros (UA_EXPANDEDNODEID_MACRO()...) are easily created, but
-    # bulky. This class will help to offload some code.
-    codegen = open62541_MacroHelper(supressGenerationOfAttribute=supressGenerationOfAttribute)
-
-    # Populate the unPrinted-Lists with everything we have.
-    # Every Time a nodes printfunction is called, it will pop itself and
-    #   all printed references from these lists.
-    for n in self.nodes:
-      if not n in printedExternally:
-        unPrintedNodes.append(n)
-      else:
-        logger.debug("Node " + str(n.id()) + " is being ignored.")
-    for n in unPrintedNodes:
-      for r in n.getReferences():
-        if (r.target() != None) and (r.target().id() != None) and (r.parent() != None):
-          unPrintedRefs.append(r)
-
-    logger.debug(str(len(unPrintedNodes)) + " Nodes, " + str(len(unPrintedRefs)) +  "References need to get printed.")
-    header.append("/* WARNING: This is a generated file.\n * Any manual changes will be overwritten.\n\n */")
-    code.append("/* WARNING: This is a generated file.\n * Any manual changes will be overwritten.\n\n */")
-
-    header.append('#ifndef '+outfilename.upper()+'_H_')
-    header.append('#define '+outfilename.upper()+'_H_')
-    header.append('')
-    header.append('#include "ua_server.h"')
-    header.append('#include "ua_plugin_nodestore.h"')
-    header.append('#include "ua_types_encoding_binary.h"')
-    header.append('#include "ua_types_generated_encoding_binary.h"')
-    header.append('')
-    header.append('/* Definition that (in userspace models) may be ')
-    header.append(' * - not included in the amalgamated header or')
-    header.append(' * - not part of public headers or')
-    header.append(' * - not exported in the shared object in combination with any of the above')
-    header.append(' * but are required for value encoding.')
-    header.append(' * NOTE: Userspace UA_(decode|encode)Binary /wo amalgamations requires UA_EXPORT to be appended to the appropriate definitions. */')
-    header.append('#ifndef UA_ENCODINGOFFSET_BINARY')
-    header.append('#  define UA_ENCODINGOFFSET_BINARY 2')
-    header.append('#endif')
-    
-    code.append('#include "'+outfilename+'.h"')
-    code.append("UA_INLINE UA_StatusCode "+outfilename+"(UA_Server *server) {\n")
-    code.append('UA_StatusCode retval = UA_STATUSCODE_GOOD; ')
-    code.append('if(retval == UA_STATUSCODE_GOOD){retval = UA_STATUSCODE_GOOD;} //ensure that retval is used');
-
-    # Before printing nodes, we need to request additional namespace arrays from the server
-    for nsid in self.namespaceIdentifiers:
-      if nsid == 0 or nsid==1:
-        continue
-      else:
-        name =  self.namespaceIdentifiers[nsid]
-        name = name.replace("\"","\\\"")
-        code.append("if (UA_Server_addNamespace(server, \"{0}\") != {1})\n    return UA_STATUSCODE_BADUNEXPECTEDERROR;".format(name, nsid))
-
-    # Find all references necessary to create the namespace and
-    # "Bootstrap" them so all other nodes can safely use these referencetypes whenever
-    # they can locate both source and target of the reference.
-    logger.debug("Collecting all references used in the namespace.")
-    refsUsed = []
-    for n in self.nodes:
-      # Since we are already looping over all nodes, use this chance to print NodeId defines
-      if n.id().ns != 0:
-        nc = n.nodeClass()
-        if nc != NODE_CLASS_OBJECT and nc != NODE_CLASS_VARIABLE and nc != NODE_CLASS_VIEW:
-          header = header + codegen.getNodeIdDefineString(n)
-
-      # Now for the actual references...
-      for r in n.getReferences():
-        # Only print valid references in namespace 0 (users will not want their refs bootstrapped)
-        if not r.referenceType() in refsUsed and r.referenceType() != None and r.referenceType().id().ns == 0:
-          refsUsed.append(r.referenceType())
-    logger.debug(str(len(refsUsed)) + " reference types are used in the namespace, which will now get bootstrapped.")
-    for r in refsUsed:
-      code = code + r.printOpen62541CCode(unPrintedNodes, unPrintedRefs);
-
-    header.append("extern UA_StatusCode "+outfilename+"(UA_Server *server);\n")
-    header.append("#endif /* "+outfilename.upper()+"_H_ */")
-
-    # Note to self: do NOT - NOT! - try to iterate over unPrintedNodes!
-    #               Nodes remove themselves from this list when printed.
-    logger.debug("Printing all other nodes.")
-    for n in self.nodes:
-      code = code + n.printOpen62541CCode(unPrintedNodes, unPrintedRefs, supressGenerationOfAttribute=supressGenerationOfAttribute)
-
-    if len(unPrintedNodes) != 0:
-      logger.warn("" + str(len(unPrintedNodes)) + " nodes could not be translated to code.")
-    else:
-      logger.debug("Printing suceeded for all nodes")
-
-    if len(unPrintedRefs) != 0:
-      logger.debug("Attempting to print " + str(len(unPrintedRefs)) + " unprinted references.")
-      tmprefs = []
-      for r in unPrintedRefs:
-        if  not (r.target() not in unPrintedNodes) and not (r.parent() in unPrintedNodes):
-          if not isinstance(r.parent(), opcua_node_t):
-            logger.debug("Reference has no parent!")
-          elif not isinstance(r.parent().id(), opcua_node_id_t):
-            logger.debug("Parents nodeid is not a nodeID!")
-          else:
-            if (len(tmprefs) == 0):
-              code.append("//  Creating leftover references:")
-            code = code + codegen.getCreateStandaloneReference(r.parent(), r)
-            code.append("")
-            tmprefs.append(r)
-      # Remove printed refs from list
-      for r in tmprefs:
-        unPrintedRefs.remove(r)
-      if len(unPrintedRefs) != 0:
-        logger.warn("" + str(len(unPrintedRefs)) + " references could not be translated to code.")
-    else:
-      logger.debug("Printing succeeded for all references")
-
-    code.append("return UA_STATUSCODE_GOOD;")
-    code.append("}")
-    return (header,code)
-
-###
-### Testing
-###
-
-class testing:
-  def __init__(self):
-    self.namespace = opcua_namespace("testing")
-
-    logger.debug("Phase 1: Reading XML file nodessets")
-    self.namespace.parseXML("Opc.Ua.NodeSet2.xml")
-    #self.namespace.parseXML("Opc.Ua.NodeSet2.Part4.xml")
-    #self.namespace.parseXML("Opc.Ua.NodeSet2.Part5.xml")
-    #self.namespace.parseXML("Opc.Ua.SimulationNodeSet2.xml")
-
-    logger.debug("Phase 2: Linking address space references and datatypes")
-    self.namespace.linkOpenPointers()
-    self.namespace.sanitize()
-
-    logger.debug("Phase 3: Comprehending DataType encoding rules")
-    self.namespace.buildEncodingRules()
-
-    logger.debug("Phase 4: Allocating variable value data")
-    self.namespace.allocateVariables()
-
-    bin = self.namespace.buildBinary()
-    f = open("binary.base64","w+")
-    f.write(bin.encode("base64"))
-    f.close()
-
-    allnodes = self.namespace.nodes;
-    ns = [self.namespace.getRoot()]
-
-    i = 0
-    #print "Starting depth search on " + str(len(allnodes)) + " nodes starting with from " + str(ns)
-    while (len(ns) < len(allnodes)):
-      i = i + 1;
-      tmp = [];
-      print("Iteration: " + str(i))
-      for n in ns:
-        tmp.append(n)
-        for r in n.getReferences():
-          if (not r.target() in tmp):
-           tmp.append(r.target())
-      print("...tmp, " + str(len(tmp)) + " nodes discovered")
-      ns = []
-      for n in tmp:
-        ns.append(n)
-      print("...done, " + str(len(ns)) + " nodes discovered")
-
-    logger.debug("Phase 5: Printing pretty graph")
-    self.namespace.printDotGraphWalk(depth=1, rootNode=self.namespace.getNodeByIDString("i=84"), followInverse=False, excludeNodeIds=["i=29", "i=22", "i=25"])
-    #self.namespace.printDot()
-
-class testing_open62541_header:
-  def __init__(self):
-    self.namespace = opcua_namespace("testing")
-
-    logger.debug("Phase 1: Reading XML file nodessets")
-    self.namespace.parseXML("Opc.Ua.NodeSet2.xml")
-    #self.namespace.parseXML("Opc.Ua.NodeSet2.Part4.xml")
-    #self.namespace.parseXML("Opc.Ua.NodeSet2.Part5.xml")
-    #self.namespace.parseXML("Opc.Ua.SimulationNodeSet2.xml")
-
-    logger.debug("Phase 2: Linking address space references and datatypes")
-    self.namespace.linkOpenPointers()
-    self.namespace.sanitize()
-
-    logger.debug("Phase 3: Calling C Printers")
-    code = self.namespace.printOpen62541Header()
-
-    codeout = open("./open62541_namespace.c", "w+")
-    for line in code:
-      codeout.write(line + "\n")
-    codeout.close()
-    return
-
-# Call testing routine if invoked standalone.
-# For better debugging, it is advised to import this file using an interactive
-# python shell and instantiating a namespace.
-#
-# import ua_types.py as ua; ns=ua.testing().namespace
-if __name__ == '__main__':
-  tst = testing_open62541_header()

File diff suppressed because it is too large
+ 0 - 1771
tools/pyUANamespace/ua_node_types.py


File diff suppressed because it is too large
+ 5544 - 104
tools/schema/NodeIds.csv


File diff suppressed because it is too large
+ 1819 - 0
tools/schema/Opc.Ua.NodeSet2.Minimal.xml


+ 367 - 9
tools/schema/Opc.Ua.Types.bsd

@@ -205,10 +205,19 @@
     <opc:Documentation>An image encoded in PNG format.</opc:Documentation>
   </opc:OpaqueType>
 
+  <opc:OpaqueType Name="AudioDataType">
+    <opc:Documentation>An image encoded in PNG format.</opc:Documentation>
+  </opc:OpaqueType>
+
   <opc:OpaqueType Name="BitFieldMaskDataType">
     <opc:Documentation>A mask of 32 bits that can be updated individually by using the top 32 bits as a mask.</opc:Documentation>
   </opc:OpaqueType>
 
+  <opc:StructuredType Name="KeyValuePair" BaseType="ua:ExtensionObject">
+    <opc:Field Name="Key" TypeName="ua:QualifiedName" />
+    <opc:Field Name="Value" TypeName="ua:Variant" />
+  </opc:StructuredType>
+
   <opc:EnumeratedType Name="OpenFileMode" LengthInBits="32">
     <opc:EnumeratedValue Name="Read" Value="1" />
     <opc:EnumeratedValue Name="Write" Value="2" />
@@ -216,6 +225,20 @@
     <opc:EnumeratedValue Name="Append" Value="8" />
   </opc:EnumeratedType>
 
+  <opc:EnumeratedType Name="IdentityCriteriaType" LengthInBits="32">
+    <opc:EnumeratedValue Name="UserName" Value="1" />
+    <opc:EnumeratedValue Name="Thumbprint" Value="2" />
+    <opc:EnumeratedValue Name="Scope" Value="3" />
+    <opc:EnumeratedValue Name="GroupId" Value="4" />
+    <opc:EnumeratedValue Name="Anonymous" Value="5" />
+    <opc:EnumeratedValue Name="AuthenticatedUser" Value="6" />
+  </opc:EnumeratedType>
+
+  <opc:StructuredType Name="IdentityMappingRuleType" BaseType="ua:ExtensionObject">
+    <opc:Field Name="CriteriaType" TypeName="tns:IdentityCriteriaType" />
+    <opc:Field Name="Criteria" TypeName="opc:String" />
+  </opc:StructuredType>
+
   <opc:EnumeratedType Name="TrustListMasks" LengthInBits="32">
     <opc:EnumeratedValue Name="None" Value="0" />
     <opc:EnumeratedValue Name="TrustedCertificates" Value="1" />
@@ -237,6 +260,148 @@
     <opc:Field Name="IssuerCrls" TypeName="opc:ByteString" LengthField="NoOfIssuerCrls" />
   </opc:StructuredType>
 
+  <opc:StructuredType Name="DecimalDataType" BaseType="ua:ExtensionObject">
+    <opc:Field Name="Scale" TypeName="opc:Int16" />
+    <opc:Field Name="Value" TypeName="opc:ByteString" />
+  </opc:StructuredType>
+
+  <opc:StructuredType Name="ConfigurationVersionDataType" BaseType="ua:ExtensionObject">
+    <opc:Field Name="MajorVersion" TypeName="opc:UInt32" />
+    <opc:Field Name="MinorVersion" TypeName="opc:UInt32" />
+  </opc:StructuredType>
+
+  <opc:StructuredType Name="DataSetMetaDataType" BaseType="ua:ExtensionObject">
+    <opc:Field Name="Name" TypeName="opc:String" />
+    <opc:Field Name="Description" TypeName="ua:LocalizedText" />
+    <opc:Field Name="NoOfFields" TypeName="opc:Int32" />
+    <opc:Field Name="Fields" TypeName="tns:FieldMetaData" LengthField="NoOfFields" />
+    <opc:Field Name="DataSetClassId" TypeName="opc:Guid" />
+    <opc:Field Name="NoOfNamespaces" TypeName="opc:Int32" />
+    <opc:Field Name="Namespaces" TypeName="opc:String" LengthField="NoOfNamespaces" />
+    <opc:Field Name="NoOfStructureDataTypes" TypeName="opc:Int32" />
+    <opc:Field Name="StructureDataTypes" TypeName="tns:StructureDescription" LengthField="NoOfStructureDataTypes" />
+    <opc:Field Name="NoOfEnumDataTypes" TypeName="opc:Int32" />
+    <opc:Field Name="EnumDataTypes" TypeName="tns:EnumDescription" LengthField="NoOfEnumDataTypes" />
+    <opc:Field Name="ConfigurationVersion" TypeName="tns:ConfigurationVersionDataType" />
+  </opc:StructuredType>
+
+  <opc:EnumeratedType Name="DataSetFieldFlags" LengthInBits="32">
+    <opc:EnumeratedValue Name="PromotedField" Value="1" />
+  </opc:EnumeratedType>
+
+  <opc:StructuredType Name="FieldMetaData" BaseType="ua:ExtensionObject">
+    <opc:Field Name="Name" TypeName="opc:String" />
+    <opc:Field Name="Description" TypeName="ua:LocalizedText" />
+    <opc:Field Name="FieldFlags" TypeName="tns:DataSetFieldFlags" />
+    <opc:Field Name="BuiltInType" TypeName="opc:Byte" />
+    <opc:Field Name="DataType" TypeName="ua:NodeId" />
+    <opc:Field Name="ValueRank" TypeName="opc:Int32" />
+    <opc:Field Name="NoOfArrayDimensions" TypeName="opc:Int32" />
+    <opc:Field Name="ArrayDimensions" TypeName="opc:UInt32" LengthField="NoOfArrayDimensions" />
+    <opc:Field Name="MaxStringLength" TypeName="opc:UInt32" />
+    <opc:Field Name="DataSetFieldId" TypeName="opc:Guid" />
+    <opc:Field Name="NoOfProperties" TypeName="opc:Int32" />
+    <opc:Field Name="Properties" TypeName="tns:KeyValuePair" LengthField="NoOfProperties" />
+  </opc:StructuredType>
+
+  <opc:StructuredType Name="DataTypeDescription" BaseType="ua:ExtensionObject">
+    <opc:Field Name="DataTypeId" TypeName="ua:NodeId" />
+    <opc:Field Name="Name" TypeName="ua:QualifiedName" />
+  </opc:StructuredType>
+
+  <opc:StructuredType Name="StructureDescription" BaseType="tns:DataTypeDescription">
+    <opc:Field Name="DataTypeId" TypeName="ua:NodeId" SourceType="tns:DataTypeDescription" />
+    <opc:Field Name="Name" TypeName="ua:QualifiedName" SourceType="tns:DataTypeDescription" />
+    <opc:Field Name="StructureDefinition" TypeName="tns:StructureDefinition" />
+  </opc:StructuredType>
+
+  <opc:StructuredType Name="EnumDescription" BaseType="tns:DataTypeDescription">
+    <opc:Field Name="DataTypeId" TypeName="ua:NodeId" SourceType="tns:DataTypeDescription" />
+    <opc:Field Name="Name" TypeName="ua:QualifiedName" SourceType="tns:DataTypeDescription" />
+    <opc:Field Name="EnumDefinition" TypeName="tns:EnumDefinition" />
+  </opc:StructuredType>
+
+  <opc:EnumeratedType Name="DataSetMessageContentMask" LengthInBits="32">
+    <opc:EnumeratedValue Name="FieldStatusCode" Value="1" />
+    <opc:EnumeratedValue Name="FieldSourceTimestamp" Value="2" />
+    <opc:EnumeratedValue Name="FieldServerTimestamp" Value="4" />
+    <opc:EnumeratedValue Name="FieldSourcePicoSeconds" Value="8" />
+    <opc:EnumeratedValue Name="FieldServerPicoSeconds" Value="16" />
+    <opc:EnumeratedValue Name="FieldRawDataEncoding" Value="32" />
+    <opc:EnumeratedValue Name="HeaderTimestamp" Value="65536" />
+    <opc:EnumeratedValue Name="HeaderPicoSeconds" Value="131072" />
+    <opc:EnumeratedValue Name="HeaderStatusCode" Value="262144" />
+    <opc:EnumeratedValue Name="HeaderMajorVersion" Value="524288" />
+    <opc:EnumeratedValue Name="HeaderMinorVersion" Value="1048576" />
+  </opc:EnumeratedType>
+
+  <opc:EnumeratedType Name="NetworkMessageContentMask" LengthInBits="32">
+    <opc:EnumeratedValue Name="PublisherId" Value="1" />
+    <opc:EnumeratedValue Name="GroupHeader" Value="2" />
+    <opc:EnumeratedValue Name="GroupId" Value="4" />
+    <opc:EnumeratedValue Name="GroupVersion" Value="8" />
+    <opc:EnumeratedValue Name="NetworkMessageNumber" Value="16" />
+    <opc:EnumeratedValue Name="SequenceNumber" Value="32" />
+    <opc:EnumeratedValue Name="PayloadHeader" Value="64" />
+    <opc:EnumeratedValue Name="Timestamp" Value="128" />
+    <opc:EnumeratedValue Name="Picoseconds" Value="256" />
+    <opc:EnumeratedValue Name="DataSetClassId" Value="512" />
+    <opc:EnumeratedValue Name="PromotedFields" Value="1024" />
+  </opc:EnumeratedType>
+
+  <opc:StructuredType Name="PublishedVariableDataType" BaseType="ua:ExtensionObject">
+    <opc:Field Name="PublishedVariable" TypeName="ua:NodeId" />
+    <opc:Field Name="AttributeId" TypeName="opc:UInt32" />
+    <opc:Field Name="SamplingIntervalHint" TypeName="opc:Double" />
+    <opc:Field Name="DeadbandType" TypeName="opc:UInt32" />
+    <opc:Field Name="DeadbandValue" TypeName="opc:Double" />
+    <opc:Field Name="IndexRange" TypeName="opc:String" />
+    <opc:Field Name="SubstituteValue" TypeName="ua:Variant" />
+    <opc:Field Name="NoOfMetaDataProperties" TypeName="opc:Int32" />
+    <opc:Field Name="MetaDataProperties" TypeName="ua:QualifiedName" LengthField="NoOfMetaDataProperties" />
+  </opc:StructuredType>
+
+  <opc:StructuredType Name="FieldTargetDataType" BaseType="ua:ExtensionObject">
+    <opc:Field Name="DataSetFieldId" TypeName="opc:Guid" />
+    <opc:Field Name="ReceiverIndexRange" TypeName="opc:String" />
+    <opc:Field Name="TargetNodeId" TypeName="ua:NodeId" />
+    <opc:Field Name="AttributeId" TypeName="opc:UInt32" />
+    <opc:Field Name="WriteIndexRange" TypeName="opc:String" />
+    <opc:Field Name="OverrideValueHandling" TypeName="tns:OverrideValueHandling" />
+    <opc:Field Name="OverrideValue" TypeName="ua:Variant" />
+  </opc:StructuredType>
+
+  <opc:EnumeratedType Name="OverrideValueHandling" LengthInBits="32">
+    <opc:EnumeratedValue Name="Disabled" Value="0" />
+    <opc:EnumeratedValue Name="LastUseableValue" Value="1" />
+    <opc:EnumeratedValue Name="OverrideValue" Value="2" />
+  </opc:EnumeratedType>
+
+  <opc:EnumeratedType Name="PubSubState" LengthInBits="32">
+    <opc:EnumeratedValue Name="Disabled" Value="0" />
+    <opc:EnumeratedValue Name="Paused" Value="1" />
+    <opc:EnumeratedValue Name="Operational" Value="2" />
+    <opc:EnumeratedValue Name="Error" Value="3" />
+  </opc:EnumeratedType>
+
+  <opc:EnumeratedType Name="DiagnosticsLevel" LengthInBits="32">
+    <opc:EnumeratedValue Name="Basic" Value="0" />
+    <opc:EnumeratedValue Name="Advanced" Value="1" />
+    <opc:EnumeratedValue Name="Info" Value="2" />
+    <opc:EnumeratedValue Name="Log" Value="3" />
+    <opc:EnumeratedValue Name="Debug" Value="4" />
+  </opc:EnumeratedType>
+
+  <opc:EnumeratedType Name="PubSubDiagnosticsCounterClassification" LengthInBits="32">
+    <opc:EnumeratedValue Name="Information" Value="0" />
+    <opc:EnumeratedValue Name="Error" Value="1" />
+  </opc:EnumeratedType>
+
+  <opc:EnumeratedType Name="DataSetOrderingType" LengthInBits="32">
+    <opc:EnumeratedValue Name="Undefined" Value="1" />
+    <opc:EnumeratedValue Name="AscendingWriterId" Value="2" />
+  </opc:EnumeratedType>
+
   <opc:EnumeratedType Name="IdType" LengthInBits="32">
     <opc:Documentation>The type of identifier used in a node id.</opc:Documentation>
     <opc:EnumeratedValue Name="Numeric" Value="0" />
@@ -258,6 +423,71 @@
     <opc:EnumeratedValue Name="View" Value="128" />
   </opc:EnumeratedType>
 
+  <opc:EnumeratedType Name="AccessLevelType" LengthInBits="32">
+    <opc:EnumeratedValue Name="None" Value="0" />
+    <opc:EnumeratedValue Name="CurrentRead" Value="1" />
+    <opc:EnumeratedValue Name="CurrentWrite" Value="2" />
+    <opc:EnumeratedValue Name="HistoryRead" Value="4" />
+    <opc:EnumeratedValue Name="HistoryWrite" Value="16" />
+    <opc:EnumeratedValue Name="StatusWrite" Value="32" />
+    <opc:EnumeratedValue Name="TimestampWrite" Value="64" />
+  </opc:EnumeratedType>
+
+  <opc:EnumeratedType Name="AccessLevelExType" LengthInBits="32">
+    <opc:EnumeratedValue Name="None" Value="0" />
+    <opc:EnumeratedValue Name="CurrentRead" Value="1" />
+    <opc:EnumeratedValue Name="CurrentWrite" Value="2" />
+    <opc:EnumeratedValue Name="HistoryRead" Value="4" />
+    <opc:EnumeratedValue Name="HistoryWrite" Value="16" />
+    <opc:EnumeratedValue Name="StatusWrite" Value="32" />
+    <opc:EnumeratedValue Name="TimestampWrite" Value="64" />
+    <opc:EnumeratedValue Name="NonatomicRead" Value="65536" />
+    <opc:EnumeratedValue Name="NonatomicWrite" Value="131072" />
+    <opc:EnumeratedValue Name="WriteFullArrayOnly" Value="262144" />
+  </opc:EnumeratedType>
+
+  <opc:EnumeratedType Name="EventNotifierType" LengthInBits="32">
+    <opc:EnumeratedValue Name="None" Value="0" />
+    <opc:EnumeratedValue Name="SubscribeToEvents" Value="1" />
+    <opc:EnumeratedValue Name="HistoryRead" Value="4" />
+    <opc:EnumeratedValue Name="HistoryWrite" Value="8" />
+  </opc:EnumeratedType>
+
+  <opc:StructuredType Name="RolePermissionType" BaseType="ua:ExtensionObject">
+    <opc:Field Name="RoleId" TypeName="ua:NodeId" />
+    <opc:Field Name="Permissions" TypeName="opc:UInt32" />
+  </opc:StructuredType>
+
+  <opc:EnumeratedType Name="StructureType" LengthInBits="32">
+    <opc:EnumeratedValue Name="Structure" Value="0" />
+    <opc:EnumeratedValue Name="StructureWithOptionalFields" Value="1" />
+    <opc:EnumeratedValue Name="Union" Value="2" />
+  </opc:EnumeratedType>
+
+  <opc:StructuredType Name="StructureField" BaseType="ua:ExtensionObject">
+    <opc:Field Name="Name" TypeName="opc:String" />
+    <opc:Field Name="Description" TypeName="ua:LocalizedText" />
+    <opc:Field Name="DataType" TypeName="ua:NodeId" />
+    <opc:Field Name="ValueRank" TypeName="opc:Int32" />
+    <opc:Field Name="NoOfArrayDimensions" TypeName="opc:Int32" />
+    <opc:Field Name="ArrayDimensions" TypeName="opc:UInt32" LengthField="NoOfArrayDimensions" />
+    <opc:Field Name="MaxStringLength" TypeName="opc:UInt32" />
+    <opc:Field Name="IsOptional" TypeName="opc:Boolean" />
+  </opc:StructuredType>
+
+  <opc:StructuredType Name="StructureDefinition" BaseType="tns:DataTypeDefinition">
+    <opc:Field Name="DefaultEncodingId" TypeName="ua:NodeId" />
+    <opc:Field Name="BaseDataType" TypeName="ua:NodeId" />
+    <opc:Field Name="StructureType" TypeName="tns:StructureType" />
+    <opc:Field Name="NoOfFields" TypeName="opc:Int32" />
+    <opc:Field Name="Fields" TypeName="tns:StructureField" LengthField="NoOfFields" />
+  </opc:StructuredType>
+
+  <opc:StructuredType Name="EnumDefinition" BaseType="tns:DataTypeDefinition">
+    <opc:Field Name="NoOfFields" TypeName="opc:Int32" />
+    <opc:Field Name="Fields" TypeName="tns:EnumField" LengthField="NoOfFields" />
+  </opc:StructuredType>
+
   <opc:StructuredType Name="Node" BaseType="ua:ExtensionObject">
     <opc:Documentation>Specifies the attributes which belong to all nodes.</opc:Documentation>
     <opc:Field Name="NodeId" TypeName="ua:NodeId" />
@@ -267,6 +497,11 @@
     <opc:Field Name="Description" TypeName="ua:LocalizedText" />
     <opc:Field Name="WriteMask" TypeName="opc:UInt32" />
     <opc:Field Name="UserWriteMask" TypeName="opc:UInt32" />
+    <opc:Field Name="NoOfRolePermissions" TypeName="opc:Int32" />
+    <opc:Field Name="RolePermissions" TypeName="tns:RolePermissionType" LengthField="NoOfRolePermissions" />
+    <opc:Field Name="NoOfUserRolePermissions" TypeName="opc:Int32" />
+    <opc:Field Name="UserRolePermissions" TypeName="tns:RolePermissionType" LengthField="NoOfUserRolePermissions" />
+    <opc:Field Name="AccessRestrictions" TypeName="opc:UInt16" />
     <opc:Field Name="NoOfReferences" TypeName="opc:Int32" />
     <opc:Field Name="References" TypeName="tns:ReferenceNode" LengthField="NoOfReferences" />
   </opc:StructuredType>
@@ -279,6 +514,11 @@
     <opc:Field Name="Description" TypeName="ua:LocalizedText" SourceType="tns:Node" />
     <opc:Field Name="WriteMask" TypeName="opc:UInt32" SourceType="tns:Node" />
     <opc:Field Name="UserWriteMask" TypeName="opc:UInt32" SourceType="tns:Node" />
+    <opc:Field Name="NoOfRolePermissions" TypeName="opc:Int32" />
+    <opc:Field Name="RolePermissions" TypeName="tns:RolePermissionType" LengthField="NoOfRolePermissions" />
+    <opc:Field Name="NoOfUserRolePermissions" TypeName="opc:Int32" />
+    <opc:Field Name="UserRolePermissions" TypeName="tns:RolePermissionType" LengthField="NoOfUserRolePermissions" />
+    <opc:Field Name="AccessRestrictions" TypeName="opc:UInt16" SourceType="tns:Node" />
     <opc:Field Name="NoOfReferences" TypeName="opc:Int32" />
     <opc:Field Name="References" TypeName="tns:ReferenceNode" LengthField="NoOfReferences" />
   </opc:StructuredType>
@@ -291,6 +531,11 @@
     <opc:Field Name="Description" TypeName="ua:LocalizedText" SourceType="tns:Node" />
     <opc:Field Name="WriteMask" TypeName="opc:UInt32" SourceType="tns:Node" />
     <opc:Field Name="UserWriteMask" TypeName="opc:UInt32" SourceType="tns:Node" />
+    <opc:Field Name="NoOfRolePermissions" TypeName="opc:Int32" />
+    <opc:Field Name="RolePermissions" TypeName="tns:RolePermissionType" LengthField="NoOfRolePermissions" />
+    <opc:Field Name="NoOfUserRolePermissions" TypeName="opc:Int32" />
+    <opc:Field Name="UserRolePermissions" TypeName="tns:RolePermissionType" LengthField="NoOfUserRolePermissions" />
+    <opc:Field Name="AccessRestrictions" TypeName="opc:UInt16" SourceType="tns:Node" />
     <opc:Field Name="NoOfReferences" TypeName="opc:Int32" />
     <opc:Field Name="References" TypeName="tns:ReferenceNode" LengthField="NoOfReferences" />
   </opc:StructuredType>
@@ -304,6 +549,11 @@
     <opc:Field Name="Description" TypeName="ua:LocalizedText" SourceType="tns:Node" />
     <opc:Field Name="WriteMask" TypeName="opc:UInt32" SourceType="tns:Node" />
     <opc:Field Name="UserWriteMask" TypeName="opc:UInt32" SourceType="tns:Node" />
+    <opc:Field Name="NoOfRolePermissions" TypeName="opc:Int32" />
+    <opc:Field Name="RolePermissions" TypeName="tns:RolePermissionType" LengthField="NoOfRolePermissions" />
+    <opc:Field Name="NoOfUserRolePermissions" TypeName="opc:Int32" />
+    <opc:Field Name="UserRolePermissions" TypeName="tns:RolePermissionType" LengthField="NoOfUserRolePermissions" />
+    <opc:Field Name="AccessRestrictions" TypeName="opc:UInt16" SourceType="tns:Node" />
     <opc:Field Name="NoOfReferences" TypeName="opc:Int32" />
     <opc:Field Name="References" TypeName="tns:ReferenceNode" LengthField="NoOfReferences" />
     <opc:Field Name="EventNotifier" TypeName="opc:Byte" />
@@ -318,6 +568,11 @@
     <opc:Field Name="Description" TypeName="ua:LocalizedText" SourceType="tns:Node" />
     <opc:Field Name="WriteMask" TypeName="opc:UInt32" SourceType="tns:Node" />
     <opc:Field Name="UserWriteMask" TypeName="opc:UInt32" SourceType="tns:Node" />
+    <opc:Field Name="NoOfRolePermissions" TypeName="opc:Int32" />
+    <opc:Field Name="RolePermissions" TypeName="tns:RolePermissionType" LengthField="NoOfRolePermissions" />
+    <opc:Field Name="NoOfUserRolePermissions" TypeName="opc:Int32" />
+    <opc:Field Name="UserRolePermissions" TypeName="tns:RolePermissionType" LengthField="NoOfUserRolePermissions" />
+    <opc:Field Name="AccessRestrictions" TypeName="opc:UInt16" SourceType="tns:Node" />
     <opc:Field Name="NoOfReferences" TypeName="opc:Int32" />
     <opc:Field Name="References" TypeName="tns:ReferenceNode" LengthField="NoOfReferences" />
     <opc:Field Name="IsAbstract" TypeName="opc:Boolean" />
@@ -332,6 +587,11 @@
     <opc:Field Name="Description" TypeName="ua:LocalizedText" SourceType="tns:Node" />
     <opc:Field Name="WriteMask" TypeName="opc:UInt32" SourceType="tns:Node" />
     <opc:Field Name="UserWriteMask" TypeName="opc:UInt32" SourceType="tns:Node" />
+    <opc:Field Name="NoOfRolePermissions" TypeName="opc:Int32" />
+    <opc:Field Name="RolePermissions" TypeName="tns:RolePermissionType" LengthField="NoOfRolePermissions" />
+    <opc:Field Name="NoOfUserRolePermissions" TypeName="opc:Int32" />
+    <opc:Field Name="UserRolePermissions" TypeName="tns:RolePermissionType" LengthField="NoOfUserRolePermissions" />
+    <opc:Field Name="AccessRestrictions" TypeName="opc:UInt16" SourceType="tns:Node" />
     <opc:Field Name="NoOfReferences" TypeName="opc:Int32" />
     <opc:Field Name="References" TypeName="tns:ReferenceNode" LengthField="NoOfReferences" />
     <opc:Field Name="Value" TypeName="ua:Variant" />
@@ -343,6 +603,7 @@
     <opc:Field Name="UserAccessLevel" TypeName="opc:Byte" />
     <opc:Field Name="MinimumSamplingInterval" TypeName="opc:Double" />
     <opc:Field Name="Historizing" TypeName="opc:Boolean" />
+    <opc:Field Name="AccessLevelEx" TypeName="opc:UInt32" />
   </opc:StructuredType>
 
   <opc:StructuredType Name="VariableTypeNode" BaseType="tns:TypeNode">
@@ -354,6 +615,11 @@
     <opc:Field Name="Description" TypeName="ua:LocalizedText" SourceType="tns:Node" />
     <opc:Field Name="WriteMask" TypeName="opc:UInt32" SourceType="tns:Node" />
     <opc:Field Name="UserWriteMask" TypeName="opc:UInt32" SourceType="tns:Node" />
+    <opc:Field Name="NoOfRolePermissions" TypeName="opc:Int32" />
+    <opc:Field Name="RolePermissions" TypeName="tns:RolePermissionType" LengthField="NoOfRolePermissions" />
+    <opc:Field Name="NoOfUserRolePermissions" TypeName="opc:Int32" />
+    <opc:Field Name="UserRolePermissions" TypeName="tns:RolePermissionType" LengthField="NoOfUserRolePermissions" />
+    <opc:Field Name="AccessRestrictions" TypeName="opc:UInt16" SourceType="tns:Node" />
     <opc:Field Name="NoOfReferences" TypeName="opc:Int32" />
     <opc:Field Name="References" TypeName="tns:ReferenceNode" LengthField="NoOfReferences" />
     <opc:Field Name="Value" TypeName="ua:Variant" />
@@ -373,6 +639,11 @@
     <opc:Field Name="Description" TypeName="ua:LocalizedText" SourceType="tns:Node" />
     <opc:Field Name="WriteMask" TypeName="opc:UInt32" SourceType="tns:Node" />
     <opc:Field Name="UserWriteMask" TypeName="opc:UInt32" SourceType="tns:Node" />
+    <opc:Field Name="NoOfRolePermissions" TypeName="opc:Int32" />
+    <opc:Field Name="RolePermissions" TypeName="tns:RolePermissionType" LengthField="NoOfRolePermissions" />
+    <opc:Field Name="NoOfUserRolePermissions" TypeName="opc:Int32" />
+    <opc:Field Name="UserRolePermissions" TypeName="tns:RolePermissionType" LengthField="NoOfUserRolePermissions" />
+    <opc:Field Name="AccessRestrictions" TypeName="opc:UInt16" SourceType="tns:Node" />
     <opc:Field Name="NoOfReferences" TypeName="opc:Int32" />
     <opc:Field Name="References" TypeName="tns:ReferenceNode" LengthField="NoOfReferences" />
     <opc:Field Name="IsAbstract" TypeName="opc:Boolean" />
@@ -389,6 +660,11 @@
     <opc:Field Name="Description" TypeName="ua:LocalizedText" SourceType="tns:Node" />
     <opc:Field Name="WriteMask" TypeName="opc:UInt32" SourceType="tns:Node" />
     <opc:Field Name="UserWriteMask" TypeName="opc:UInt32" SourceType="tns:Node" />
+    <opc:Field Name="NoOfRolePermissions" TypeName="opc:Int32" />
+    <opc:Field Name="RolePermissions" TypeName="tns:RolePermissionType" LengthField="NoOfRolePermissions" />
+    <opc:Field Name="NoOfUserRolePermissions" TypeName="opc:Int32" />
+    <opc:Field Name="UserRolePermissions" TypeName="tns:RolePermissionType" LengthField="NoOfUserRolePermissions" />
+    <opc:Field Name="AccessRestrictions" TypeName="opc:UInt16" SourceType="tns:Node" />
     <opc:Field Name="NoOfReferences" TypeName="opc:Int32" />
     <opc:Field Name="References" TypeName="tns:ReferenceNode" LengthField="NoOfReferences" />
     <opc:Field Name="Executable" TypeName="opc:Boolean" />
@@ -403,6 +679,11 @@
     <opc:Field Name="Description" TypeName="ua:LocalizedText" SourceType="tns:Node" />
     <opc:Field Name="WriteMask" TypeName="opc:UInt32" SourceType="tns:Node" />
     <opc:Field Name="UserWriteMask" TypeName="opc:UInt32" SourceType="tns:Node" />
+    <opc:Field Name="NoOfRolePermissions" TypeName="opc:Int32" />
+    <opc:Field Name="RolePermissions" TypeName="tns:RolePermissionType" LengthField="NoOfRolePermissions" />
+    <opc:Field Name="NoOfUserRolePermissions" TypeName="opc:Int32" />
+    <opc:Field Name="UserRolePermissions" TypeName="tns:RolePermissionType" LengthField="NoOfUserRolePermissions" />
+    <opc:Field Name="AccessRestrictions" TypeName="opc:UInt16" SourceType="tns:Node" />
     <opc:Field Name="NoOfReferences" TypeName="opc:Int32" />
     <opc:Field Name="References" TypeName="tns:ReferenceNode" LengthField="NoOfReferences" />
     <opc:Field Name="ContainsNoLoops" TypeName="opc:Boolean" />
@@ -417,9 +698,15 @@
     <opc:Field Name="Description" TypeName="ua:LocalizedText" SourceType="tns:Node" />
     <opc:Field Name="WriteMask" TypeName="opc:UInt32" SourceType="tns:Node" />
     <opc:Field Name="UserWriteMask" TypeName="opc:UInt32" SourceType="tns:Node" />
+    <opc:Field Name="NoOfRolePermissions" TypeName="opc:Int32" />
+    <opc:Field Name="RolePermissions" TypeName="tns:RolePermissionType" LengthField="NoOfRolePermissions" />
+    <opc:Field Name="NoOfUserRolePermissions" TypeName="opc:Int32" />
+    <opc:Field Name="UserRolePermissions" TypeName="tns:RolePermissionType" LengthField="NoOfUserRolePermissions" />
+    <opc:Field Name="AccessRestrictions" TypeName="opc:UInt16" SourceType="tns:Node" />
     <opc:Field Name="NoOfReferences" TypeName="opc:Int32" />
     <opc:Field Name="References" TypeName="tns:ReferenceNode" LengthField="NoOfReferences" />
     <opc:Field Name="IsAbstract" TypeName="opc:Boolean" />
+    <opc:Field Name="DataTypeDefinition" TypeName="ua:ExtensionObject" />
   </opc:StructuredType>
 
   <opc:StructuredType Name="ReferenceNode" BaseType="ua:ExtensionObject">
@@ -446,6 +733,13 @@
     <opc:Field Name="Description" TypeName="ua:LocalizedText" />
   </opc:StructuredType>
 
+  <opc:StructuredType Name="EnumField" BaseType="tns:EnumValueType">
+    <opc:Field Name="Value" TypeName="opc:Int64" SourceType="tns:EnumValueType" />
+    <opc:Field Name="DisplayName" TypeName="ua:LocalizedText" SourceType="tns:EnumValueType" />
+    <opc:Field Name="Description" TypeName="ua:LocalizedText" SourceType="tns:EnumValueType" />
+    <opc:Field Name="Name" TypeName="opc:String" />
+  </opc:StructuredType>
+
   <opc:StructuredType Name="OptionSet" BaseType="ua:ExtensionObject">
     <opc:Documentation>This abstract Structured DataType is the base DataType for all DataTypes representing a bit mask.</opc:Documentation>
     <opc:Field Name="Value" TypeName="opc:ByteString" />
@@ -539,11 +833,34 @@
     <opc:Field Name="AdditionalHeader" TypeName="ua:ExtensionObject" />
   </opc:StructuredType>
 
+  <opc:OpaqueType Name="VersionTime">
+  </opc:OpaqueType>
+
   <opc:StructuredType Name="ServiceFault" BaseType="ua:ExtensionObject">
     <opc:Documentation>The response returned by all services when there is a service level error.</opc:Documentation>
     <opc:Field Name="ResponseHeader" TypeName="tns:ResponseHeader" />
   </opc:StructuredType>
 
+  <opc:StructuredType Name="SessionlessInvokeRequestType" BaseType="ua:ExtensionObject">
+    <opc:Field Name="NoOfUrisVersion" TypeName="opc:Int32" />
+    <opc:Field Name="UrisVersion" TypeName="opc:UInt32" LengthField="NoOfUrisVersion" />
+    <opc:Field Name="NoOfNamespaceUris" TypeName="opc:Int32" />
+    <opc:Field Name="NamespaceUris" TypeName="opc:String" LengthField="NoOfNamespaceUris" />
+    <opc:Field Name="NoOfServerUris" TypeName="opc:Int32" />
+    <opc:Field Name="ServerUris" TypeName="opc:String" LengthField="NoOfServerUris" />
+    <opc:Field Name="NoOfLocaleIds" TypeName="opc:Int32" />
+    <opc:Field Name="LocaleIds" TypeName="opc:String" LengthField="NoOfLocaleIds" />
+    <opc:Field Name="ServiceId" TypeName="opc:UInt32" />
+  </opc:StructuredType>
+
+  <opc:StructuredType Name="SessionlessInvokeResponseType" BaseType="ua:ExtensionObject">
+    <opc:Field Name="NoOfNamespaceUris" TypeName="opc:Int32" />
+    <opc:Field Name="NamespaceUris" TypeName="opc:String" LengthField="NoOfNamespaceUris" />
+    <opc:Field Name="NoOfServerUris" TypeName="opc:Int32" />
+    <opc:Field Name="ServerUris" TypeName="opc:String" LengthField="NoOfServerUris" />
+    <opc:Field Name="ServiceId" TypeName="opc:UInt32" />
+  </opc:StructuredType>
+
   <opc:StructuredType Name="FindServersRequest" BaseType="ua:ExtensionObject">
     <opc:Documentation>Finds the servers known to the discovery server.</opc:Documentation>
     <opc:Field Name="RequestHeader" TypeName="tns:RequestHeader" />
@@ -882,15 +1199,18 @@
     <opc:EnumeratedValue Name="ValueRank" Value="524288" />
     <opc:EnumeratedValue Name="WriteMask" Value="1048576" />
     <opc:EnumeratedValue Name="Value" Value="2097152" />
-    <opc:EnumeratedValue Name="All" Value="4194303" />
-    <opc:EnumeratedValue Name="BaseNode" Value="1335396" />
-    <opc:EnumeratedValue Name="Object" Value="1335524" />
-    <opc:EnumeratedValue Name="ObjectTypeOrDataType" Value="1337444" />
-    <opc:EnumeratedValue Name="Variable" Value="4026999" />
-    <opc:EnumeratedValue Name="VariableType" Value="3958902" />
-    <opc:EnumeratedValue Name="Method" Value="1466724" />
-    <opc:EnumeratedValue Name="ReferenceType" Value="1371236" />
-    <opc:EnumeratedValue Name="View" Value="1335532" />
+    <opc:EnumeratedValue Name="DataTypeDefinition" Value="4194304" />
+    <opc:EnumeratedValue Name="RolePermissions" Value="8388608" />
+    <opc:EnumeratedValue Name="AccessRestrictions" Value="16777216" />
+    <opc:EnumeratedValue Name="All" Value="33554431" />
+    <opc:EnumeratedValue Name="BaseNode" Value="26501220" />
+    <opc:EnumeratedValue Name="Object" Value="26501348" />
+    <opc:EnumeratedValue Name="ObjectType" Value="26503268" />
+    <opc:EnumeratedValue Name="Variable" Value="26571383" />
+    <opc:EnumeratedValue Name="VariableType" Value="28600438" />
+    <opc:EnumeratedValue Name="Method" Value="26632548" />
+    <opc:EnumeratedValue Name="ReferenceType" Value="26537060" />
+    <opc:EnumeratedValue Name="View" Value="26501356" />
   </opc:EnumeratedType>
 
   <opc:StructuredType Name="NodeAttributes" BaseType="ua:ExtensionObject">
@@ -999,6 +1319,21 @@
     <opc:Field Name="EventNotifier" TypeName="opc:Byte" />
   </opc:StructuredType>
 
+  <opc:StructuredType Name="GenericAttributeValue" BaseType="ua:ExtensionObject">
+    <opc:Field Name="AttributeId" TypeName="opc:UInt32" />
+    <opc:Field Name="Value" TypeName="ua:Variant" />
+  </opc:StructuredType>
+
+  <opc:StructuredType Name="GenericAttributes" BaseType="tns:NodeAttributes">
+    <opc:Field Name="SpecifiedAttributes" TypeName="opc:UInt32" SourceType="tns:NodeAttributes" />
+    <opc:Field Name="DisplayName" TypeName="ua:LocalizedText" SourceType="tns:NodeAttributes" />
+    <opc:Field Name="Description" TypeName="ua:LocalizedText" SourceType="tns:NodeAttributes" />
+    <opc:Field Name="WriteMask" TypeName="opc:UInt32" SourceType="tns:NodeAttributes" />
+    <opc:Field Name="UserWriteMask" TypeName="opc:UInt32" SourceType="tns:NodeAttributes" />
+    <opc:Field Name="NoOfAttributeValues" TypeName="opc:Int32" />
+    <opc:Field Name="AttributeValues" TypeName="tns:GenericAttributeValue" LengthField="NoOfAttributeValues" />
+  </opc:StructuredType>
+
   <opc:StructuredType Name="AddNodesItem" BaseType="ua:ExtensionObject">
     <opc:Documentation>A request to add a node to the server address space.</opc:Documentation>
     <opc:Field Name="ParentNodeId" TypeName="ua:ExpandedNodeId" />
@@ -1130,6 +1465,10 @@
     <opc:EnumeratedValue Name="ValueRank" Value="524288" />
     <opc:EnumeratedValue Name="WriteMask" Value="1048576" />
     <opc:EnumeratedValue Name="ValueForVariableType" Value="2097152" />
+    <opc:EnumeratedValue Name="DataTypeDefinition" Value="4194304" />
+    <opc:EnumeratedValue Name="RolePermissions" Value="8388608" />
+    <opc:EnumeratedValue Name="AccessRestrictions" Value="16777216" />
+    <opc:EnumeratedValue Name="AccessLevelEx" Value="33554432" />
   </opc:EnumeratedType>
 
   <opc:EnumeratedType Name="BrowseDirection" LengthInBits="32">
@@ -2331,6 +2670,25 @@
     <opc:Field Name="LastMethodReturnStatus" TypeName="tns:StatusResult" />
   </opc:StructuredType>
 
+  <opc:StructuredType Name="ProgramDiagnostic2DataType" BaseType="ua:ExtensionObject">
+    <opc:Field Name="CreateSessionId" TypeName="ua:NodeId" />
+    <opc:Field Name="CreateClientName" TypeName="opc:String" />
+    <opc:Field Name="InvocationCreationTime" TypeName="opc:DateTime" />
+    <opc:Field Name="LastTransitionTime" TypeName="opc:DateTime" />
+    <opc:Field Name="LastMethodCall" TypeName="opc:String" />
+    <opc:Field Name="LastMethodSessionId" TypeName="ua:NodeId" />
+    <opc:Field Name="NoOfLastMethodInputArguments" TypeName="opc:Int32" />
+    <opc:Field Name="LastMethodInputArguments" TypeName="tns:Argument" LengthField="NoOfLastMethodInputArguments" />
+    <opc:Field Name="NoOfLastMethodOutputArguments" TypeName="opc:Int32" />
+    <opc:Field Name="LastMethodOutputArguments" TypeName="tns:Argument" LengthField="NoOfLastMethodOutputArguments" />
+    <opc:Field Name="NoOfLastMethodInputValues" TypeName="opc:Int32" />
+    <opc:Field Name="LastMethodInputValues" TypeName="ua:Variant" LengthField="NoOfLastMethodInputValues" />
+    <opc:Field Name="NoOfLastMethodOutputValues" TypeName="opc:Int32" />
+    <opc:Field Name="LastMethodOutputValues" TypeName="ua:Variant" LengthField="NoOfLastMethodOutputValues" />
+    <opc:Field Name="LastMethodCallTime" TypeName="opc:DateTime" />
+    <opc:Field Name="LastMethodReturnStatus" TypeName="tns:StatusResult" />
+  </opc:StructuredType>
+
   <opc:StructuredType Name="Annotation" BaseType="ua:ExtensionObject">
     <opc:Field Name="Message" TypeName="opc:String" />
     <opc:Field Name="UserName" TypeName="opc:String" />

+ 0 - 298
tools/schema/datatypes_full.txt

@@ -1,298 +0,0 @@
-ActivateSessionRequest
-ActivateSessionResponse
-AddNodesItem
-AddNodesRequest
-AddNodesResponse
-AddNodesResult
-AddReferencesItem
-AddReferencesRequest
-AddReferencesResponse
-AggregateConfiguration
-AggregateFilter
-AggregateFilterResult
-Annotation
-AnonymousIdentityToken
-ApplicationDescription
-ApplicationInstanceCertificate
-ApplicationType
-Argument
-AttributeOperand
-AttributeWriteMask
-AxisInformation
-AxisScaleEnumeration
-BitFieldMaskDataType
-Boolean
-BrowseDescription
-BrowseDirection
-BrowseNextRequest
-BrowseNextResponse
-BrowsePath
-BrowsePathResult
-BrowsePathTarget
-BrowseRequest
-BrowseResponse
-BrowseResult
-BrowseResultMask
-BuildInfo
-Byte
-ByteString
-ByteStringNodeId
-CallMethodRequest
-CallMethodResult
-CallRequest
-CallResponse
-CancelRequest
-CancelResponse
-ChannelSecurityToken
-CloseSecureChannelRequest
-CloseSecureChannelResponse
-CloseSessionRequest
-CloseSessionResponse
-ComplexNumberType
-ContentFilter
-ContentFilterElement
-ContentFilterElementResult
-ContentFilterResult
-ContinuationPoint
-Counter
-CreateMonitoredItemsRequest
-CreateMonitoredItemsResponse
-CreateSessionRequest
-CreateSessionResponse
-CreateSubscriptionRequest
-CreateSubscriptionResponse
-DataChangeFilter
-DataChangeNotification
-DataChangeTrigger
-DataTypeAttributes
-DataTypeNode
-DataValue
-Date
-DateString
-DateTime
-DeadbandType
-DecimalString
-DeleteAtTimeDetails
-DeleteEventDetails
-DeleteMonitoredItemsRequest
-DeleteMonitoredItemsResponse
-DeleteNodesItem
-DeleteNodesRequest
-DeleteNodesResponse
-DeleteRawModifiedDetails
-DeleteReferencesItem
-DeleteReferencesRequest
-DeleteReferencesResponse
-DeleteSubscriptionsRequest
-DeleteSubscriptionsResponse
-DiagnosticInfo
-DiscoveryConfiguration
-Double
-DoubleComplexNumberType
-Duration
-DurationString
-ElementOperand
-EndpointConfiguration
-EndpointDescription
-EndpointUrlListDataType
-EnumValueType
-EUInformation
-EventFieldList
-EventFilter
-EventFilterResult
-EventNotificationList
-ExceptionDeviationFormat
-ExpandedNodeId
-ExtensionObject
-FilterOperand
-FilterOperator
-FindServersOnNetworkRequest
-FindServersOnNetworkResponse
-FindServersRequest
-FindServersResponse
-Float
-FourByteNodeId
-GetEndpointsRequest
-GetEndpointsResponse
-Guid
-GuidNodeId
-HistoryData
-HistoryEvent
-HistoryEventFieldList
-HistoryModifiedData
-HistoryReadDetails
-HistoryReadRequest
-HistoryReadResponse
-HistoryReadResult
-HistoryReadValueId
-HistoryUpdateDetails
-HistoryUpdateRequest
-HistoryUpdateResponse
-HistoryUpdateResult
-HistoryUpdateType
-IdType
-ImageBMP
-ImageGIF
-ImageJPG
-ImagePNG
-InstanceNode
-Int16
-Int32
-Int64
-IntegerId
-IssuedIdentityToken
-LiteralOperand
-LocaleId
-LocalizedText
-MdnsDiscoveryConfiguration
-MessageSecurityMode
-MethodAttributes
-MethodNode
-ModelChangeStructureDataType
-ModelChangeStructureVerbMask
-ModificationInfo
-ModifyMonitoredItemsRequest
-ModifyMonitoredItemsResponse
-ModifySubscriptionRequest
-ModifySubscriptionResponse
-MonitoredItemCreateRequest
-MonitoredItemCreateResult
-MonitoredItemModifyRequest
-MonitoredItemModifyResult
-MonitoredItemNotification
-MonitoringFilter
-MonitoringFilterResult
-MonitoringMode
-MonitoringParameters
-NamingRuleType
-NetworkGroupDataType
-Node
-NodeAttributes
-NodeAttributesMask
-NodeClass
-NodeId
-NodeIdType
-NodeReference
-NodeTypeDescription
-NormalizedString
-NotificationData
-NotificationMessage
-NumericNodeId
-NumericRange
-ObjectAttributes
-ObjectNode
-ObjectTypeAttributes
-ObjectTypeNode
-OpenFileMode
-OpenSecureChannelRequest
-OpenSecureChannelResponse
-OptionSet
-ParsingResult
-PerformUpdateType
-ProgramDiagnosticDataType
-PublishRequest
-PublishResponse
-QualifiedName
-QueryDataDescription
-QueryDataSet
-QueryFirstRequest
-QueryFirstResponse
-QueryNextRequest
-QueryNextResponse
-Range
-ReadAtTimeDetails
-ReadEventDetails
-ReadProcessedDetails
-ReadRawModifiedDetails
-ReadRequest
-ReadResponse
-ReadValueId
-RedundancySupport
-RedundantServerDataType
-ReferenceDescription
-ReferenceNode
-ReferenceTypeAttributes
-ReferenceTypeNode
-RegisteredServer
-RegisterNodesRequest
-RegisterNodesResponse
-RegisterServer2Request
-RegisterServer2Response
-RegisterServerRequest
-RegisterServerResponse
-RelativePath
-RelativePathElement
-RepublishRequest
-RepublishResponse
-RequestHeader
-ResponseHeader
-SamplingIntervalDiagnosticsDataType
-SByte
-SecurityTokenRequestType
-SemanticChangeStructureDataType
-ServerDiagnosticsSummaryDataType
-ServerOnNetwork
-ServerState
-ServerStatusDataType
-ServiceCounterDataType
-ServiceFault
-SessionAuthenticationToken
-SessionDiagnosticsDataType
-SessionSecurityDiagnosticsDataType
-SetMonitoringModeRequest
-SetMonitoringModeResponse
-SetPublishingModeRequest
-SetPublishingModeResponse
-SetTriggeringRequest
-SetTriggeringResponse
-SignatureData
-SignedSoftwareCertificate
-SimpleAttributeOperand
-StatusChangeNotification
-StatusCode
-StatusResult
-String
-StringNodeId
-SubscriptionAcknowledgement
-SubscriptionDiagnosticsDataType
-Time
-TimestampsToReturn
-TimeString
-TimeZoneDataType
-TransferResult
-TransferSubscriptionsRequest
-TransferSubscriptionsResponse
-TranslateBrowsePathsToNodeIdsRequest
-TranslateBrowsePathsToNodeIdsResponse
-TrustListDataType
-TrustListMasks
-TwoByteNodeId
-TypeNode
-UInt16
-UInt32
-UInt64
-Union
-UnregisterNodesRequest
-UnregisterNodesResponse
-UpdateDataDetails
-UpdateEventDetails
-UpdateStructureDataDetails
-UserIdentityToken
-UserNameIdentityToken
-UserTokenPolicy
-UserTokenType
-UtcTime
-VariableAttributes
-VariableNode
-VariableTypeAttributes
-VariableTypeNode
-Variant
-ViewAttributes
-ViewDescription
-ViewNode
-WriteRequest
-WriteResponse
-WriteValue
-X509IdentityToken
-XmlElement
-XVType

+ 0 - 4
tools/schema/datatypes_full_generate.sh

@@ -1,4 +0,0 @@
-#!/bin/bash
-
-cd $( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
-egrep "<opc\:(Structured|Enumerated|Opaque)Type" Opc.Ua.Types.bsd | grep -Po 'Name="\K.*?(?=")' | sort -u datatypes_minimal.txt - > datatypes_full.txt

+ 15 - 0
tools/schema/datatypes_minimal.txt

@@ -178,4 +178,19 @@ SimpleAttributeOperand
 EventNotificationList
 EventFieldList
 StatusChangeNotification
+ServerDiagnosticsSummaryDataType
+SubscriptionDiagnosticsDataType
+SessionDiagnosticsDataType
+ServiceCounterDataType
+SessionSecurityDiagnosticsDataType
 Duration
+UtcTime
+LocaleId
+RedundancySupport
+RedundantServerDataType
+NetworkGroupDataType
+NumericRange
+EndpointUrlListDataType
+ModelChangeStructureDataType
+SemanticChangeStructureDataType
+TimeZoneDataType

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

@@ -1,761 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<UANodeSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Version="1.02" LastModified="2013-03-06T05:36:44.0862658Z" xmlns="http://opcfoundation.org/UA/2011/03/UANodeSet.xsd">
-  <Aliases>
-    <Alias Alias="Boolean">i=1</Alias>
-    <Alias Alias="SByte">i=2</Alias>
-    <Alias Alias="Byte">i=3</Alias>
-    <Alias Alias="Int16">i=4</Alias>
-    <Alias Alias="UInt16">i=5</Alias>
-    <Alias Alias="Int32">i=6</Alias>
-    <Alias Alias="UInt32">i=7</Alias>
-    <Alias Alias="Int64">i=8</Alias>
-    <Alias Alias="UInt64">i=9</Alias>
-    <Alias Alias="Float">i=10</Alias>
-    <Alias Alias="Double">i=11</Alias>
-    <Alias Alias="DateTime">i=13</Alias>
-    <Alias Alias="String">i=12</Alias>
-    <Alias Alias="ByteString">i=15</Alias>
-    <Alias Alias="Guid">i=14</Alias>
-    <Alias Alias="XmlElement">i=16</Alias>
-    <Alias Alias="NodeId">i=17</Alias>
-    <Alias Alias="ExpandedNodeId">i=18</Alias>
-    <Alias Alias="QualifiedName">i=20</Alias>
-    <Alias Alias="LocalizedText">i=21</Alias>
-    <Alias Alias="StatusCode">i=19</Alias>
-    <Alias Alias="Structure">i=22</Alias>
-    <Alias Alias="Number">i=26</Alias>
-    <Alias Alias="Integer">i=27</Alias>
-    <Alias Alias="UInteger">i=28</Alias>
-    <Alias Alias="HasComponent">i=47</Alias>
-    <Alias Alias="HasProperty">i=46</Alias>
-    <Alias Alias="Organizes">i=35</Alias>
-    <Alias Alias="HasEventSource">i=36</Alias>
-    <Alias Alias="HasNotifier">i=48</Alias>
-    <Alias Alias="HasSubtype">i=45</Alias>
-    <Alias Alias="HasTypeDefinition">i=40</Alias>
-    <Alias Alias="HasModellingRule">i=37</Alias>
-    <Alias Alias="HasEncoding">i=38</Alias>
-    <Alias Alias="HasDescription">i=39</Alias>
-  </Aliases>
-  <UAReferenceType NodeId="i=31" BrowseName="References" IsAbstract="true" Symmetric="true">
-    <DisplayName>References</DisplayName>
-    <Description>The abstract base type for all references.</Description>
-    <References />
-    <InverseName>References</InverseName>
-  </UAReferenceType>
-  <UAReferenceType NodeId="i=45" BrowseName="HasSubtype">
-    <DisplayName>HasSubtype</DisplayName>
-    <Description>The type for non-looping hierarchical references that are used to define sub types.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=34</Reference>
-    </References>
-    <InverseName>SubtypeOf</InverseName>
-  </UAReferenceType>
-  <UAReferenceType NodeId="i=33" BrowseName="HierarchicalReferences" IsAbstract="true">
-    <DisplayName>HierarchicalReferences</DisplayName>
-    <Description>The abstract base type for all hierarchical references.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=31</Reference>
-    </References>
-    <InverseName>HierarchicalReferences</InverseName>
-  </UAReferenceType>
-  <UAReferenceType NodeId="i=32" BrowseName="NonHierarchicalReferences" IsAbstract="true">
-    <DisplayName>NonHierarchicalReferences</DisplayName>
-    <Description>The abstract base type for all non-hierarchical references.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=31</Reference>
-    </References>
-    <InverseName>NonHierarchicalReferences</InverseName>
-  </UAReferenceType>
-  <UAReferenceType NodeId="i=34" BrowseName="HasChild">
-    <DisplayName>HasChild</DisplayName>
-    <Description>The abstract base type for all non-looping hierarchical references.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=33</Reference>
-    </References>
-    <InverseName>ChildOf</InverseName>
-  </UAReferenceType>
-  <UAReferenceType NodeId="i=35" BrowseName="Organizes">
-    <DisplayName>Organizes</DisplayName>
-    <Description>The type for hierarchical references that are used to organize nodes.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=33</Reference>
-    </References>
-    <InverseName>OrganizedBy</InverseName>
-  </UAReferenceType>
-  <UAReferenceType NodeId="i=36" BrowseName="HasEventSource">
-    <DisplayName>HasEventSource</DisplayName>
-    <Description>The type for non-looping hierarchical references that are used to organize event sources.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=33</Reference>
-    </References>
-    <InverseName>EventSourceOf</InverseName>
-  </UAReferenceType>
-  <UAReferenceType NodeId="i=37" BrowseName="HasModellingRule">
-    <DisplayName>HasModellingRule</DisplayName>
-    <Description>The type for references from instance declarations to modelling rule nodes.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=32</Reference>
-    </References>
-    <InverseName>ModellingRuleOf</InverseName>
-  </UAReferenceType>
-  <UAReferenceType NodeId="i=38" BrowseName="HasEncoding">
-    <DisplayName>HasEncoding</DisplayName>
-    <Description>The type for references from data type nodes to to data type encoding nodes.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=32</Reference>
-    </References>
-    <InverseName>EncodingOf</InverseName>
-  </UAReferenceType>
-  <UAReferenceType NodeId="i=39" BrowseName="HasDescription">
-    <DisplayName>HasDescription</DisplayName>
-    <Description>The type for references from data type encoding nodes to data type description nodes.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=32</Reference>
-    </References>
-    <InverseName>DescriptionOf</InverseName>
-  </UAReferenceType>
-  <UAReferenceType NodeId="i=40" BrowseName="HasTypeDefinition">
-    <DisplayName>HasTypeDefinition</DisplayName>
-    <Description>The type for references from a instance node its type defintion node.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=32</Reference>
-    </References>
-    <InverseName>TypeDefinitionOf</InverseName>
-  </UAReferenceType>
-  <UAReferenceType NodeId="i=41" BrowseName="GeneratesEvent">
-    <DisplayName>GeneratesEvent</DisplayName>
-    <Description>The type for references from a node to an event type that is raised by node.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=32</Reference>
-    </References>
-    <InverseName>GeneratesEvent</InverseName>
-  </UAReferenceType>
-  <UAReferenceType NodeId="i=44" BrowseName="Aggregates">
-    <DisplayName>Aggregates</DisplayName>
-    <Description>The type for non-looping hierarchical references that are used to aggregate nodes into complex types.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=34</Reference>
-    </References>
-    <InverseName>AggregatedBy</InverseName>
-  </UAReferenceType>
-  <UAReferenceType NodeId="i=46" BrowseName="HasProperty">
-    <DisplayName>HasProperty</DisplayName>
-    <Description>The type for non-looping hierarchical reference from a node to its property.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=44</Reference>
-    </References>
-    <InverseName>PropertyOf</InverseName>
-  </UAReferenceType>
-  <UAReferenceType NodeId="i=47" BrowseName="HasComponent">
-    <DisplayName>HasComponent</DisplayName>
-    <Description>The type for non-looping hierarchical reference from a node to its component.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=44</Reference>
-    </References>
-    <InverseName>ComponentOf</InverseName>
-  </UAReferenceType>
-  <UAReferenceType NodeId="i=48" BrowseName="HasNotifier">
-    <DisplayName>HasNotifier</DisplayName>
-    <Description>The type for non-looping hierarchical references that are used to indicate how events propagate from node to node.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=36</Reference>
-    </References>
-    <InverseName>NotifierOf</InverseName>
-  </UAReferenceType>
-  <UAReferenceType NodeId="i=49" BrowseName="HasOrderedComponent">
-    <DisplayName>HasOrderedComponent</DisplayName>
-    <Description>The type for non-looping hierarchical reference from a node to its component when the order of references matters.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=47</Reference>
-    </References>
-    <InverseName>OrderedComponentOf</InverseName>
-  </UAReferenceType>
-  <UAReferenceType NodeId="i=51" BrowseName="FromState">
-    <DisplayName>FromState</DisplayName>
-    <Description>The type for a reference to the state before a transition.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=32</Reference>
-    </References>
-    <InverseName>ToTransition</InverseName>
-  </UAReferenceType>
-  <UAReferenceType NodeId="i=52" BrowseName="ToState">
-    <DisplayName>ToState</DisplayName>
-    <Description>The type for a reference to the state after a transition.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=32</Reference>
-    </References>
-    <InverseName>FromTransition</InverseName>
-  </UAReferenceType>
-  <UAReferenceType NodeId="i=53" BrowseName="HasCause">
-    <DisplayName>HasCause</DisplayName>
-    <Description>The type for a reference to a method that can cause a transition to occur.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=32</Reference>
-    </References>
-    <InverseName>MayBeCausedBy</InverseName>
-  </UAReferenceType>
-  <UAReferenceType NodeId="i=54" BrowseName="HasEffect">
-    <DisplayName>HasEffect</DisplayName>
-    <Description>The type for a reference to an event that may be raised when a transition occurs.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=32</Reference>
-    </References>
-    <InverseName>MayBeEffectedBy</InverseName>
-  </UAReferenceType>
-  <UAReferenceType NodeId="i=56" BrowseName="HasHistoricalConfiguration">
-    <DisplayName>HasHistoricalConfiguration</DisplayName>
-    <Description>The type for a reference to the historical configuration for a data variable.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=44</Reference>
-    </References>
-    <InverseName>HistoricalConfigurationOf</InverseName>
-  </UAReferenceType>
-  
-  <UAObject NodeId="i=84" BrowseName="Root" SymbolicName="RootFolder">
-    <DisplayName>Root</DisplayName>
-    <Description>The root of the server address space.</Description>
-    <References>
-      <Reference ReferenceType="HasTypeDefinition">i=61</Reference>
-    </References>
-  </UAObject>
-  <UAObject NodeId="i=85" BrowseName="Objects" SymbolicName="ObjectsFolder">
-    <DisplayName>Objects</DisplayName>
-    <Description>The browse entry point when looking for objects in the server address space.</Description>
-    <References>
-      <Reference ReferenceType="Organizes" IsForward="false">i=84</Reference>
-      <Reference ReferenceType="HasTypeDefinition">i=61</Reference>
-    </References>
-  </UAObject>
-  <UAObject NodeId="i=86" BrowseName="Types" SymbolicName="TypesFolder">
-    <DisplayName>Types</DisplayName>
-    <Description>The browse entry point when looking for types in the server address space.</Description>
-    <References>
-      <Reference ReferenceType="Organizes" IsForward="false">i=84</Reference>
-      <Reference ReferenceType="HasTypeDefinition">i=61</Reference>
-    </References>
-  </UAObject>
-  <UAObject NodeId="i=87" BrowseName="Views" SymbolicName="ViewsFolder">
-    <DisplayName>Views</DisplayName>
-    <Description>The browse entry point when looking for views in the server address space.</Description>
-    <References>
-      <Reference ReferenceType="Organizes" IsForward="false">i=84</Reference>
-      <Reference ReferenceType="HasTypeDefinition">i=61</Reference>
-    </References>
-  </UAObject>
-  <UAObject NodeId="i=91" BrowseName="ReferenceTypes" SymbolicName="ReferenceTypesFolder">
-    <DisplayName>ReferenceTypes</DisplayName>
-    <Description>The browse entry point when looking for reference types in the server address space.</Description>
-    <References>
-      <Reference ReferenceType="Organizes" IsForward="false">i=86</Reference>
-      <Reference ReferenceType="Organizes">i=31</Reference>
-      <Reference ReferenceType="HasTypeDefinition">i=61</Reference>
-    </References>
-  </UAObject>
-  <UAObjectType NodeId="i=58" BrowseName="BaseObjectType">
-    <DisplayName>BaseObjectType</DisplayName>
-    <Description>The base type for all object nodes.</Description>
-    <References />
-  </UAObjectType>
-  <UAObjectType NodeId="i=61" BrowseName="FolderType">
-    <DisplayName>FolderType</DisplayName>
-    <Description>The type for objects that organize other nodes.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=58</Reference>
-    </References>
-  </UAObjectType>
-  <UAObjectType NodeId="i=2004" BrowseName="ServerType">
-    <DisplayName>ServerType</DisplayName>
-    <Description>Specifies the current status and capabilities of the server.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=58</Reference>
-    </References>
-  </UAObjectType>
-  <UAObjectType NodeId="i=2020" BrowseName="ServerDiagnosticsType">
-    <DisplayName>ServerDiagnosticsType</DisplayName>
-    <Description>The diagnostics information for a server.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=58</Reference>
-    </References>
-  </UAObjectType>
-  <UAObjectType NodeId="i=2013" BrowseName="ServerCapabilitiesType">
-    <DisplayName>ServerCapabilitiesType</DisplayName>
-    <Description>Describes the capabilities supported by the server.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=58</Reference>
-    </References>
-  </UAObjectType>
-  <UAVariableType NodeId="i=2138" BrowseName="ServerStatusType" DataType="i=862">
-    <DisplayName>ServerStatusType</DisplayName>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=63</Reference>
-    </References>
-  </UAVariableType>
-  <UAVariableType NodeId="i=3051" BrowseName="BuildInfoType" DataType="i=338">
-    <DisplayName>BuildInfoType</DisplayName>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=63</Reference>
-    </References>
-  </UAVariableType>
-  
-  
-  <UAObject NodeId="i=90" BrowseName="DataTypes" SymbolicName="DataTypesFolder">
-    <DisplayName>DataTypes</DisplayName>
-    <Description>The browse entry point when looking for data types in the server address space.</Description>
-    <References>
-      <Reference ReferenceType="Organizes" IsForward="false">i=86</Reference>
-      <Reference ReferenceType="Organizes">i=24</Reference>
-      <Reference ReferenceType="HasTypeDefinition">i=61</Reference>
-    </References>
-  </UAObject>
-  <UADataType NodeId="i=24" BrowseName="BaseDataType" IsAbstract="true">
-    <DisplayName>BaseDataType</DisplayName>
-    <Description>Describes a value that can have any valid DataType.</Description>
-    <References />
-  </UADataType>
-  <UADataType NodeId="i=1" BrowseName="Boolean">
-    <DisplayName>Boolean</DisplayName>
-    <Description>Describes a value that is either TRUE or FALSE.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=24</Reference>
-    </References>
-  </UADataType>
-  <UADataType NodeId="i=26" BrowseName="Number" IsAbstract="true">
-    <DisplayName>Number</DisplayName>
-    <Description>Describes a value that can have any numeric DataType.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=24</Reference>
-    </References>
-  </UADataType>
-  <UADataType NodeId="i=10" BrowseName="Float">
-    <DisplayName>Float</DisplayName>
-    <Description>Describes a value that is an IEEE 754-1985 single precision floating point number.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=26</Reference>
-    </References>
-  </UADataType>
-  <UADataType NodeId="i=11" BrowseName="Double">
-    <DisplayName>Double</DisplayName>
-    <Description>Describes a value that is an IEEE 754-1985 double precision floating point number.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=26</Reference>
-    </References>
-  </UADataType>
-  <UADataType NodeId="i=27" BrowseName="Integer" IsAbstract="true">
-    <DisplayName>Integer</DisplayName>
-    <Description>Describes a value that can have any integer DataType.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=26</Reference>
-    </References>
-  </UADataType>
-  <UADataType NodeId="i=2" BrowseName="SByte">
-    <DisplayName>SByte</DisplayName>
-    <Description>Describes a value that is an integer between -128 and 127.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=27</Reference>
-    </References>
-  </UADataType>
-  <UADataType NodeId="i=4" BrowseName="Int16">
-    <DisplayName>Int16</DisplayName>
-    <Description>Describes a value that is an integer between −32,768 and 32,767.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=27</Reference>
-    </References>
-  </UADataType>
-  <UADataType NodeId="i=6" BrowseName="Int32">
-    <DisplayName>Int32</DisplayName>
-    <Description>Describes a value that is an integer between −2,147,483,648  and 2,147,483,647.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=27</Reference>
-    </References>
-  </UADataType>
-  <UADataType NodeId="i=8" BrowseName="Int64">
-    <DisplayName>Int64</DisplayName>
-    <Description>Describes a value that is an integer between −9,223,372,036,854,775,808 and 9,223,372,036,854,775,807.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=27</Reference>
-    </References>
-  </UADataType>
-  <UADataType NodeId="i=28" BrowseName="UInteger" IsAbstract="true">
-    <DisplayName>UInteger</DisplayName>
-    <Description>Describes a value that can have any unsigned integer DataType.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=26</Reference>
-    </References>
-  </UADataType>
-  <UADataType NodeId="i=3" BrowseName="Byte">
-    <DisplayName>Byte</DisplayName>
-    <Description>Describes a value that is an integer between 0 and 255.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=28</Reference>
-    </References>
-  </UADataType>
-  <UADataType NodeId="i=5" BrowseName="UInt16">
-    <DisplayName>UInt16</DisplayName>
-    <Description>Describes a value that is an integer between 0 and 65535.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=28</Reference>
-    </References>
-  </UADataType>
-  <UADataType NodeId="i=7" BrowseName="UInt32">
-    <DisplayName>UInt32</DisplayName>
-    <Description>Describes a value that is an integer between 0 and 4,294,967,295.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=28</Reference>
-    </References>
-  </UADataType>
-  <UADataType NodeId="i=9" BrowseName="UInt64">
-    <DisplayName>UInt64</DisplayName>
-    <Description>Describes a value that is an integer between 0 and 18,446,744,073,709,551,615.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=28</Reference>
-    </References>
-  </UADataType>
-  <UADataType NodeId="i=12" BrowseName="String">
-    <DisplayName>String</DisplayName>
-    <Description>Describes a value that is a sequence of printable Unicode characters.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=24</Reference>
-    </References>
-  </UADataType>
-  <UADataType NodeId="i=13" BrowseName="DateTime">
-    <DisplayName>DateTime</DisplayName>
-    <Description>Describes a value that is a Gregorian calender date and time.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=24</Reference>
-    </References>
-  </UADataType>
-  <UADataType NodeId="i=14" BrowseName="Guid">
-    <DisplayName>Guid</DisplayName>
-    <Description>Describes a value that is a 128-bit globally unique identifier.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=24</Reference>
-    </References>
-  </UADataType>
-  <UADataType NodeId="i=15" BrowseName="ByteString">
-    <DisplayName>ByteString</DisplayName>
-    <Description>Describes a value that is a sequence of bytes.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=24</Reference>
-    </References>
-  </UADataType>
-  <UADataType NodeId="i=16" BrowseName="XmlElement">
-    <DisplayName>XmlElement</DisplayName>
-    <Description>Describes a value that is an XML element.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=24</Reference>
-    </References>
-  </UADataType>
-  <UADataType NodeId="i=17" BrowseName="NodeId">
-    <DisplayName>NodeId</DisplayName>
-    <Description>Describes a value that is an identifier for a node within a Server address space.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=24</Reference>
-    </References>
-  </UADataType>
-  <UADataType NodeId="i=18" BrowseName="ExpandedNodeId">
-    <DisplayName>ExpandedNodeId</DisplayName>
-    <Description>Describes a value that is an absolute identifier for a node.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=24</Reference>
-    </References>
-  </UADataType>
-  <UADataType NodeId="i=19" BrowseName="StatusCode">
-    <DisplayName>StatusCode</DisplayName>
-    <Description>Describes a value that is a code representing the outcome of an operation by a Server.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=24</Reference>
-    </References>
-  </UADataType>
-  <UADataType NodeId="i=20" BrowseName="QualifiedName">
-    <DisplayName>QualifiedName</DisplayName>
-    <Description>Describes a value that is a name qualified by a namespace.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=24</Reference>
-    </References>
-  </UADataType>
-  <UADataType NodeId="i=21" BrowseName="LocalizedText">
-    <DisplayName>LocalizedText</DisplayName>
-    <Description>Describes a value that is human readable Unicode text with a locale identifier.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=24</Reference>
-    </References>
-  </UADataType>
-  <UADataType NodeId="i=22" BrowseName="Structure" IsAbstract="true">
-    <DisplayName>Structure</DisplayName>
-    <Description>Describes a value that is any type of structure that can be described with a data encoding.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=24</Reference>
-    </References>
-  </UADataType>
-  <UADataType NodeId="i=862" BrowseName="ServerStatusDataType">
-    <DisplayName>ServerStatusDataType</DisplayName>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=22</Reference>
-    </References>
-    <Definition Name="ServerStatusDataType">
-      <Field Name="StartTime" DataType="i=294" />
-      <Field Name="CurrentTime" DataType="i=294" />
-      <Field Name="State" DataType="i=852" />
-      <Field Name="BuildInfo" DataType="i=338" />
-      <Field Name="SecondsTillShutdown" DataType="i=7" />
-      <Field Name="ShutdownReason" DataType="i=21" />
-    </Definition>
-  </UADataType>
-  <UADataType NodeId="i=338" BrowseName="BuildInfo">
-    <DisplayName>BuildInfo</DisplayName>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=22</Reference>
-    </References>
-    <Definition Name="BuildInfo">
-      <Field Name="ProductUri" DataType="i=12" />
-      <Field Name="ManufacturerName" DataType="i=12" />
-      <Field Name="ProductName" DataType="i=12" />
-      <Field Name="SoftwareVersion" DataType="i=12" />
-      <Field Name="BuildNumber" DataType="i=12" />
-      <Field Name="BuildDate" DataType="i=294" />
-    </Definition>
-  </UADataType>
-  <UADataType NodeId="i=23" BrowseName="DataValue">
-    <DisplayName>DataValue</DisplayName>
-    <Description>Describes a value that is a structure containing a value, a status code and timestamps.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=24</Reference>
-    </References>
-  </UADataType>
-  <UADataType NodeId="i=25" BrowseName="DiagnosticInfo">
-    <DisplayName>DiagnosticInfo</DisplayName>
-    <Description>Describes a value that is a structure containing diagnostics associated with a StatusCode.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=24</Reference>
-    </References>
-  </UADataType>
-  <UADataType NodeId="i=29" BrowseName="Enumeration" IsAbstract="true">
-    <DisplayName>Enumeration</DisplayName>
-    <Description>Describes a value that is an enumerated DataType.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=24</Reference>
-    </References>
-  </UADataType>
-  <UADataType NodeId="i=852" BrowseName="ServerState">
-    <DisplayName>ServerState</DisplayName>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=29</Reference>
-    </References>
-    <Definition Name="ServerState">
-      <Field Name="Running" Value="0" />
-      <Field Name="Failed" Value="1" />
-      <Field Name="NoConfiguration" Value="2" />
-      <Field Name="Suspended" Value="3" />
-      <Field Name="Shutdown" Value="4" />
-      <Field Name="Test" Value="5" />
-      <Field Name="CommunicationFault" Value="6" />
-      <Field Name="Unknown" Value="7" />
-    </Definition>
-  </UADataType>
-  <UAObject NodeId="i=89" BrowseName="VariableTypes" SymbolicName="VariableTypesFolder">
-    <DisplayName>VariableTypes</DisplayName>
-    <Description>The browse entry point when looking for variable types in the server address space.</Description>
-    <References>
-      <Reference ReferenceType="Organizes" IsForward="false">i=86</Reference>
-      <Reference ReferenceType="Organizes">i=62</Reference>
-      <Reference ReferenceType="HasTypeDefinition">i=61</Reference>
-    </References>
-  </UAObject>
-  <UAVariableType NodeId="i=62" BrowseName="BaseVariableType" IsAbstract="true" ValueRank="-2">
-    <DisplayName>BaseVariableType</DisplayName>
-    <Description>The abstract base type for all variable nodes.</Description>
-    <References />
-  </UAVariableType>
-  <UAVariableType NodeId="i=63" BrowseName="BaseDataVariableType" ValueRank="-2">
-    <DisplayName>BaseDataVariableType</DisplayName>
-    <Description>The type for variable that represents a process value.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=62</Reference>
-    </References>
-  </UAVariableType>
-  <UAVariableType NodeId="i=68" BrowseName="PropertyType" ValueRank="-2">
-    <DisplayName>PropertyType</DisplayName>
-    <Description>The type for variable that represents a property of another node.</Description>
-    <References>
-      <Reference ReferenceType="HasSubtype" IsForward="false">i=62</Reference>
-    </References>
-  </UAVariableType>
-  
-  
-  <UAObject NodeId="i=2253" BrowseName="Server" EventNotifier="1">
-    <DisplayName>Server</DisplayName>
-    <References>
-      <Reference ReferenceType="HasProperty">i=2254</Reference>
-      <Reference ReferenceType="HasProperty">i=2255</Reference>
-      <Reference ReferenceType="HasComponent">i=2256</Reference>
-      <Reference ReferenceType="HasComponent">i=2268</Reference>
-      <Reference ReferenceType="HasComponent">i=2274</Reference>
-      <Reference ReferenceType="Organizes" IsForward="false">i=85</Reference>
-      <Reference ReferenceType="HasTypeDefinition">i=2004</Reference>
-    </References>
-  </UAObject>
-  <UAVariable NodeId="i=2255" BrowseName="NamespaceArray" ParentNodeId="i=2253" DataType="String" ValueRank="1" MinimumSamplingInterval="1000">
-    <DisplayName>NamespaceArray</DisplayName>
-    <Description>The list of namespace URIs used by the server.</Description>
-    <References>
-      <Reference ReferenceType="HasTypeDefinition">i=68</Reference>
-      <Reference ReferenceType="HasProperty" IsForward="false">i=2253</Reference>
-    </References>
-  </UAVariable>
-  <UAVariable NodeId="i=2254" BrowseName="ServerArray" ParentNodeId="i=2253" DataType="String" ValueRank="1" MinimumSamplingInterval="1000">
-    <DisplayName>ServerArray</DisplayName>
-    <Description>The list of server URIs used by the server.</Description>
-    <References>
-      <Reference ReferenceType="HasTypeDefinition">i=68</Reference>
-      <Reference ReferenceType="HasProperty" IsForward="false">i=2253</Reference>
-    </References>
-  </UAVariable>
-  <UAObject NodeId="i=2268" BrowseName="ServerCapabilities" ParentNodeId="i=2253">
-    <DisplayName>ServerCapabilities</DisplayName>
-    <Description>Describes capabilities supported by the server.</Description>
-    <References>
-      <Reference ReferenceType="HasProperty">i=2271</Reference>
-      <Reference ReferenceType="HasProperty">i=2735</Reference>
-      <Reference ReferenceType="HasTypeDefinition">i=2013</Reference>
-      <Reference ReferenceType="HasComponent" IsForward="false">i=2253</Reference>
-    </References>
-  </UAObject>
-  <UAVariable NodeId="i=2271" BrowseName="LocaleIdArray" ParentNodeId="i=2268" DataType="i=295" ValueRank="1">
-    <DisplayName>LocaleIdArray</DisplayName>
-    <Description>A list of locales supported by the server.</Description>
-    <References>
-      <Reference ReferenceType="HasTypeDefinition">i=68</Reference>
-      <Reference ReferenceType="HasProperty" IsForward="false">i=2268</Reference>
-    </References>
-  </UAVariable>
-  <UAVariable NodeId="i=2735" BrowseName="MaxBrowseContinuationPoints" ParentNodeId="i=2268" DataType="UInt16">
-    <DisplayName>MaxBrowseContinuationPoints</DisplayName>
-    <Description>The maximum number of continuation points for Browse operations per session.</Description>
-    <References>
-      <Reference ReferenceType="HasTypeDefinition">i=68</Reference>
-      <Reference ReferenceType="HasProperty" IsForward="false">i=2268</Reference>
-    </References>
-  </UAVariable>
-  <UAObject NodeId="i=2274" BrowseName="ServerDiagnostics" ParentNodeId="i=2253">
-    <DisplayName>ServerDiagnostics</DisplayName>
-    <Description>Reports diagnostics about the server.</Description>
-    <References>
-      <Reference ReferenceType="HasProperty">i=2294</Reference>
-      <Reference ReferenceType="HasTypeDefinition">i=2020</Reference>
-      <Reference ReferenceType="HasComponent" IsForward="false">i=2253</Reference>
-    </References>
-  </UAObject>
-  <UAVariable NodeId="i=2294" BrowseName="EnabledFlag" ParentNodeId="i=2274" DataType="Boolean" AccessLevel="3" UserAccessLevel="3">
-    <DisplayName>EnabledFlag</DisplayName>
-    <Description>If TRUE the diagnostics collection is enabled.</Description>
-    <References>
-      <Reference ReferenceType="HasTypeDefinition">i=68</Reference>
-      <Reference ReferenceType="HasProperty" IsForward="false">i=2274</Reference>
-    </References>
-  </UAVariable>
-  <UAVariable NodeId="i=2256" BrowseName="ServerStatus" ParentNodeId="i=2253" DataType="i=862" MinimumSamplingInterval="1000">
-    <DisplayName>ServerStatus</DisplayName>
-    <Description>The current status of the server.</Description>
-    <References>
-      <Reference ReferenceType="HasComponent">i=2257</Reference>
-      <Reference ReferenceType="HasComponent">i=2258</Reference>
-      <Reference ReferenceType="HasComponent">i=2259</Reference>
-      <Reference ReferenceType="HasComponent">i=2260</Reference>
-      <Reference ReferenceType="HasComponent">i=2992</Reference>
-      <Reference ReferenceType="HasComponent">i=2993</Reference>
-      <Reference ReferenceType="HasTypeDefinition">i=2138</Reference>
-      <Reference ReferenceType="HasComponent" IsForward="false">i=2253</Reference>
-    </References>
-  </UAVariable>
-  <UAVariable NodeId="i=2257" BrowseName="StartTime" ParentNodeId="i=2256" DataType="i=294">
-    <DisplayName>StartTime</DisplayName>
-    <References>
-      <Reference ReferenceType="HasTypeDefinition">i=63</Reference>
-      <Reference ReferenceType="HasComponent" IsForward="false">i=2256</Reference>
-    </References>
-  </UAVariable>
-  <UAVariable NodeId="i=2258" BrowseName="CurrentTime" ParentNodeId="i=2256" DataType="i=294">
-    <DisplayName>CurrentTime</DisplayName>
-    <References>
-      <Reference ReferenceType="HasTypeDefinition">i=63</Reference>
-      <Reference ReferenceType="HasComponent" IsForward="false">i=2256</Reference>
-    </References>
-  </UAVariable>
-  <UAVariable NodeId="i=2259" BrowseName="State" ParentNodeId="i=2256" DataType="i=852">
-    <DisplayName>State</DisplayName>
-    <References>
-      <Reference ReferenceType="HasTypeDefinition">i=63</Reference>
-      <Reference ReferenceType="HasComponent" IsForward="false">i=2256</Reference>
-    </References>
-  </UAVariable>
-  <UAVariable NodeId="i=2260" BrowseName="BuildInfo" ParentNodeId="i=2256" DataType="i=338">
-    <DisplayName>BuildInfo</DisplayName>
-    <References>
-      <Reference ReferenceType="HasComponent">i=2262</Reference>
-      <Reference ReferenceType="HasComponent">i=2263</Reference>
-      <Reference ReferenceType="HasComponent">i=2261</Reference>
-      <Reference ReferenceType="HasComponent">i=2264</Reference>
-      <Reference ReferenceType="HasComponent">i=2265</Reference>
-      <Reference ReferenceType="HasComponent">i=2266</Reference>
-      <Reference ReferenceType="HasTypeDefinition">i=3051</Reference>
-      <Reference ReferenceType="HasComponent" IsForward="false">i=2256</Reference>
-    </References>
-  </UAVariable>
-  <UAVariable NodeId="i=2262" BrowseName="ProductUri" ParentNodeId="i=2260" DataType="String" MinimumSamplingInterval="1000">
-    <DisplayName>ProductUri</DisplayName>
-    <References>
-      <Reference ReferenceType="HasTypeDefinition">i=63</Reference>
-      <Reference ReferenceType="HasComponent" IsForward="false">i=2260</Reference>
-    </References>
-  </UAVariable>
-  <UAVariable NodeId="i=2263" BrowseName="ManufacturerName" ParentNodeId="i=2260" DataType="String" MinimumSamplingInterval="1000">
-    <DisplayName>ManufacturerName</DisplayName>
-    <References>
-      <Reference ReferenceType="HasTypeDefinition">i=63</Reference>
-      <Reference ReferenceType="HasComponent" IsForward="false">i=2260</Reference>
-    </References>
-  </UAVariable>
-  <UAVariable NodeId="i=2261" BrowseName="ProductName" ParentNodeId="i=2260" DataType="String" MinimumSamplingInterval="1000">
-    <DisplayName>ProductName</DisplayName>
-    <References>
-      <Reference ReferenceType="HasTypeDefinition">i=63</Reference>
-      <Reference ReferenceType="HasComponent" IsForward="false">i=2260</Reference>
-    </References>
-  </UAVariable>
-  <UAVariable NodeId="i=2264" BrowseName="SoftwareVersion" ParentNodeId="i=2260" DataType="String" MinimumSamplingInterval="1000">
-    <DisplayName>SoftwareVersion</DisplayName>
-    <References>
-      <Reference ReferenceType="HasTypeDefinition">i=63</Reference>
-      <Reference ReferenceType="HasComponent" IsForward="false">i=2260</Reference>
-    </References>
-  </UAVariable>
-  <UAVariable NodeId="i=2265" BrowseName="BuildNumber" ParentNodeId="i=2260" DataType="String" MinimumSamplingInterval="1000">
-    <DisplayName>BuildNumber</DisplayName>
-    <References>
-      <Reference ReferenceType="HasTypeDefinition">i=63</Reference>
-      <Reference ReferenceType="HasComponent" IsForward="false">i=2260</Reference>
-    </References>
-  </UAVariable>
-  <UAVariable NodeId="i=2266" BrowseName="BuildDate" ParentNodeId="i=2260" DataType="i=294" MinimumSamplingInterval="1000">
-    <DisplayName>BuildDate</DisplayName>
-    <References>
-      <Reference ReferenceType="HasTypeDefinition">i=63</Reference>
-      <Reference ReferenceType="HasComponent" IsForward="false">i=2260</Reference>
-    </References>
-  </UAVariable>
-  <UAVariable NodeId="i=2992" BrowseName="SecondsTillShutdown" ParentNodeId="i=2256" DataType="UInt32">
-    <DisplayName>SecondsTillShutdown</DisplayName>
-    <References>
-      <Reference ReferenceType="HasTypeDefinition">i=63</Reference>
-      <Reference ReferenceType="HasComponent" IsForward="false">i=2256</Reference>
-    </References>
-  </UAVariable>
-  <UAVariable NodeId="i=2993" BrowseName="ShutdownReason" ParentNodeId="i=2256" DataType="LocalizedText">
-    <DisplayName>ShutdownReason</DisplayName>
-    <References>
-      <Reference ReferenceType="HasTypeDefinition">i=63</Reference>
-      <Reference ReferenceType="HasComponent" IsForward="false">i=2256</Reference>
-    </References>
-  </UAVariable>
-</UANodeSet>

File diff suppressed because it is too large
+ 0 - 31499
tools/schema/namespace0/Opc.Ua.NodeSet2.xml


+ 0 - 3
tools/schema/namespace0/README.md

@@ -1,3 +0,0 @@
-This directory contains XML nodesets of namespace 0 for automatic generation.
-The generation option can be activated via CMake option ENABLE_GENERATE_NAMESPACE0.
-The nodesets can be selected via CMake variable GENERATE_NAMESPACE0_FILE.

+ 19 - 16
tools/travis/travis_linux_script.sh

@@ -72,7 +72,7 @@ else
     echo -e "\r\n== Documentation and certificate build =="  && echo -en 'travis_fold:start:script.build.doc\\r'
     mkdir -p build
     cd build
-    cmake -DCMAKE_BUILD_TYPE=Release -DUA_BUILD_EXAMPLES=ON -DUA_BUILD_SELFSIGNED_CERTIFICATE=ON ..
+    cmake -DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/$PYTHON -DCMAKE_BUILD_TYPE=Release -DUA_BUILD_EXAMPLES=ON -DUA_BUILD_SELFSIGNED_CERTIFICATE=ON ..
     make doc
     make doc_pdf
     make selfsigned
@@ -85,7 +85,7 @@ else
     echo -e "\r\n== Full Namespace 0 Generation ==" && echo -en 'travis_fold:start:script.build.ns0\\r'
     mkdir -p build
     cd build
-    cmake -DCMAKE_BUILD_TYPE=Debug -DUA_ENABLE_GENERATE_NAMESPACE0=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  ..
     make -j
     if [ $? -ne 0 ] ; then exit 1 ; fi
     cd .. && rm build -rf
@@ -137,7 +137,7 @@ else
 
     echo -e "\r\n== Compile release build for 64-bit linux =="  && echo -en 'travis_fold:start:script.build.linux_64\\r'
     mkdir -p build && cd build
-    cmake -DCMAKE_BUILD_TYPE=Release -DUA_ENABLE_AMALGAMATION=ON -DUA_BUILD_EXAMPLES=ON ..
+    cmake -DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/$PYTHON -DCMAKE_BUILD_TYPE=Release -DUA_ENABLE_AMALGAMATION=ON -DUA_BUILD_EXAMPLES=ON ..
     make -j
     if [ $? -ne 0 ] ; then exit 1 ; fi
     tar -pczf open62541-linux64.tar.gz ../../doc_latex/open62541.pdf ../LICENSE ../AUTHORS ../README.md ./bin/examples/server ./bin/examples/client ./bin/libopen62541.a open62541.h open62541.c
@@ -158,13 +158,13 @@ else
 
     echo "Compile as shared lib version" && echo -en 'travis_fold:start:script.build.shared_libs\\r'
     mkdir -p build && cd build
-    cmake -DBUILD_SHARED_LIBS=ON -DUA_ENABLE_AMALGAMATION=ON -DUA_BUILD_EXAMPLES=ON ..
+    cmake -DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/$PYTHON -DBUILD_SHARED_LIBS=ON -DUA_ENABLE_AMALGAMATION=ON -DUA_BUILD_EXAMPLES=ON ..
     make -j
     if [ $? -ne 0 ] ; then exit 1 ; fi
     cd .. && rm build -rf
     echo -en 'travis_fold:end:script.build.shared_libs\\r'echo -e "\r\n==Compile multithreaded version==" && echo -en 'travis_fold:start:script.build.multithread\\r'
     mkdir -p build && cd build
-    cmake -DUA_ENABLE_MULTITHREADING=ON -DUA_BUILD_EXAMPLES=ON ..
+    cmake -DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/$PYTHON -DUA_ENABLE_MULTITHREADING=ON -DUA_BUILD_EXAMPLES=ON ..
     make -j
     if [ $? -ne 0 ] ; then exit 1 ; fi
     cd .. && rm build -rf
@@ -172,14 +172,14 @@ else
 
     echo -e "\r\n== Compile without discovery version ==" && echo -en 'travis_fold:start:script.build.unit_test_valgrind\\r'
     mkdir -p build && cd build
-    cmake -DUA_ENABLE_DISCOVERY=OFF -DUA_ENABLE_DISCOVERY_MULTICAST=OFF -DUA_BUILD_EXAMPLES=ON ..
+    cmake -DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/$PYTHON -DUA_ENABLE_DISCOVERY=OFF -DUA_ENABLE_DISCOVERY_MULTICAST=OFF -DUA_BUILD_EXAMPLES=ON ..
     make -j
     if [ $? -ne 0 ] ; then exit 1 ; fi
     cd .. && rm build -rf
 
     echo -e "\r\n== Compile discovery without multicast version =="
     mkdir -p build && cd build
-    cmake -DUA_ENABLE_DISCOVERY=ON -DUA_ENABLE_DISCOVERY_MULTICAST=OFF -DUA_BUILD_EXAMPLES=ON ..
+    cmake -DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/$PYTHON -DUA_ENABLE_DISCOVERY=ON -DUA_ENABLE_DISCOVERY_MULTICAST=OFF -DUA_BUILD_EXAMPLES=ON ..
     make -j
     if [ $? -ne 0 ] ; then exit 1 ; fi
     cd .. && rm build -rf
@@ -187,28 +187,31 @@ else
 
     echo -e "\r\n== Compile multithreaded version with discovery =="
     mkdir -p build && cd build
-    cmake -DUA_ENABLE_MULTITHREADING=ON -DUA_ENABLE_DISCOVERY=ON -DUA_ENABLE_DISCOVERY_MULTICAST=ON -DUA_BUILD_EXAMPLES=ON ..
+    cmake -DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/$PYTHON -DUA_ENABLE_MULTITHREADING=ON -DUA_ENABLE_DISCOVERY=ON -DUA_ENABLE_DISCOVERY_MULTICAST=ON -DUA_BUILD_EXAMPLES=ON ..
     make -j
     if [ $? -ne 0 ] ; then exit 1 ; fi
     cd .. && rm build -rf
     echo -en 'travis_fold:end:script.build.multithread\\r'
 
-    echo -e "\r\n== Debug build and unit tests (64 bit, python 2) ==" && echo -en 'travis_fold:start:script.build.unit_test_valgrind_python2\\r'
+    echo -e "\r\n== Unit tests (full NS0) ==" && echo -en 'travis_fold:start:script.build.unit_test_ns0_full\\r'
     mkdir -p build && cd build
-    # Force to use python2 to test compilation with python2
-    cmake -DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/python2 -DCMAKE_BUILD_TYPE=Debug -DUA_BUILD_EXAMPLES=ON -DUA_ENABLE_DISCOVERY=ON -DUA_ENABLE_DISCOVERY_MULTICAST=ON -DUA_BUILD_UNIT_TESTS=ON -DUA_ENABLE_COVERAGE=ON -DUA_ENABLE_VALGRIND_UNIT_TESTS=ON ..
+    # Valgrind cannot handle the full NS0 because the generated file is too big. Thus run NS0 full without valgrind
+    cmake -DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/$PYTHON -DUA_ENABLE_FULL_NS0=ON \
+    -DCMAKE_BUILD_TYPE=Debug -DUA_BUILD_EXAMPLES=ON -DUA_ENABLE_DISCOVERY=ON -DUA_ENABLE_DISCOVERY_MULTICAST=ON \
+    -DUA_BUILD_UNIT_TESTS=ON -DUA_ENABLE_COVERAGE=OFF -DUA_ENABLE_VALGRIND_UNIT_TESTS=OFF ..
     make -j && make test ARGS="-V"
     if [ $? -ne 0 ] ; then exit 1 ; fi
     cd .. && rm build -rf
-    echo -en 'travis_fold:end:script.build.unit_test_valgrind_python2\\r'
+    echo -en 'travis_fold:end:script.build.unit_test_ns0\\r'
 
-    echo -e "\r\n== Debug build and unit tests (64 bit, python 3) ==" && echo -en 'travis_fold:start:script.build.unit_test_valgrind_python3\\r'
+    echo -e "\r\n== Unit tests (minimal NS0) ==" && echo -en 'travis_fold:start:script.build.unit_test_ns0_minimal\\r'
     mkdir -p build && cd build
-    # Force to use python3 to test compilation with python3
-    cmake -DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/python3 -DCMAKE_BUILD_TYPE=Debug -DUA_BUILD_EXAMPLES=ON -DUA_ENABLE_DISCOVERY=ON -DUA_ENABLE_DISCOVERY_MULTICAST=ON -DUA_BUILD_UNIT_TESTS=ON -DUA_ENABLE_COVERAGE=ON -DUA_ENABLE_VALGRIND_UNIT_TESTS=ON ..
+    cmake -DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/$PYTHON \
+    -DCMAKE_BUILD_TYPE=Debug -DUA_BUILD_EXAMPLES=ON -DUA_ENABLE_DISCOVERY=ON -DUA_ENABLE_DISCOVERY_MULTICAST=ON \
+    -DUA_BUILD_UNIT_TESTS=ON -DUA_ENABLE_COVERAGE=ON -DUA_ENABLE_VALGRIND_UNIT_TESTS=ON ..
     make -j && make test ARGS="-V"
     if [ $? -ne 0 ] ; then exit 1 ; fi
-    echo -en 'travis_fold:end:script.build.unit_test_valgrind_python3\\r'
+    echo -en 'travis_fold:end:script.build.unit_test_ns0_minimal\\r'
 
     # only run coveralls on main repo, otherwise it fails uploading the files
     echo -e "\r\n== -> Current repo: ${TRAVIS_REPO_SLUG} =="

+ 1 - 1
tools/travis/travis_osx_script.sh

@@ -18,7 +18,7 @@ echo -en 'travis_fold:end:script.build.doc\\r'
 echo "Full Namespace 0 Generation"  && echo -en 'travis_fold:start:script.build.ns0\\r'
 mkdir -p build
 cd build
-cmake -DCMAKE_BUILD_TYPE=Debug -DUA_ENABLE_GENERATE_NAMESPACE0=On -DUA_BUILD_EXAMPLES=ON  ..
+cmake -DCMAKE_BUILD_TYPE=Debug -DUA_ENABLE_FULL_NS0=ON -DUA_BUILD_EXAMPLES=ON  ..
 make -j
 cd .. && rm -rf build
 echo -en 'travis_fold:end:script.build.ns0\\r'