Browse Source

Basis for server bootstrapping

Julius Pfrommer 7 years ago
parent
commit
0e9b6caeb2
29 changed files with 6418 additions and 6600 deletions
  1. 20 25
      CMakeLists.txt
  2. 185 6
      src/server/ua_server.c
  3. 4 0
      src/server/ua_server_internal.h
  4. 8 5
      src/server/ua_services_nodemanagement.c
  5. 0 0
      tools/nodeset_compiler/NodeID_AssumeExternal.txt
  6. 0 0
      tools/pyUANamespace/NodeID_Blacklist.txt
  7. 0 0
      tools/nodeset_compiler/NodeID_Blacklist_FullNS0.txt
  8. 0 0
      tools/nodeset_compiler/README.md
  9. 73 0
      tools/nodeset_compiler/backend_graphviz.py
  10. 163 0
      tools/nodeset_compiler/backend_open62541.py
  11. 128 0
      tools/nodeset_compiler/backend_open62541_datatypes.py
  12. 199 0
      tools/nodeset_compiler/backend_open62541_nodes.py
  13. 0 0
      tools/nodeset_compiler/constants.py
  14. 522 0
      tools/nodeset_compiler/datatypes.py
  15. 342 0
      tools/nodeset_compiler/nodes.py
  16. 218 0
      tools/nodeset_compiler/nodeset.py
  17. 125 0
      tools/nodeset_compiler/nodeset_compiler.py
  18. 3 7
      tools/pyUANamespace/ua_nodeset_testing.py
  19. 0 1514
      tools/pyUANamespace/NodeID_NameSpace0_All.txt
  20. 0 179
      tools/pyUANamespace/generate_open62541CCode.py
  21. 0 361
      tools/pyUANamespace/open62541_XMLPreprocessor.py
  22. 0 205
      tools/pyUANamespace/open62541_backend.py
  23. 0 716
      tools/pyUANamespace/open62541_backend_nodes.py
  24. 0 317
      tools/pyUANamespace/open62541_backend_types.py
  25. 0 1012
      tools/pyUANamespace/ua_builtin_types.py
  26. 0 1384
      tools/pyUANamespace/ua_node_types.py
  27. 0 542
      tools/pyUANamespace/ua_nodeset.py
  28. 5 0
      tools/schema/datatypes_minimal.txt
  29. 4423 327
      tools/schema/namespace0/Opc.Ua.NodeSet2.Minimal.xml

+ 20 - 25
CMakeLists.txt

@@ -90,8 +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_NAMESPACE0_XML ${CMAKE_SOURCE_DIR}/tools/schema/namespace0/Opc.Ua.NodeSet2.Minimal.xml CACHE FILEPATH "Namespace Zero definition XML file")
 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)
 
@@ -104,6 +103,7 @@ mark_as_advanced(UA_ENABLE_NONSTANDARD_UDP)
 # Build Targets
 option(UA_BUILD_EXAMPLES "Build example servers and clients" OFF)
 option(UA_BUILD_UNIT_TESTS "Build the unit tests" OFF)
+option(UA_BUILD_DOCUMENTATION "Generate doxygen/sphinx documentation" OFF)
 option(UA_BUILD_FUZZING "Build the fuzzing executables" OFF)
 mark_as_advanced(UA_BUILD_FUZZING)
 if (UA_BUILD_FUZZING)
@@ -288,6 +288,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 +305,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 +358,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()
@@ -476,25 +471,25 @@ add_dependencies(open62541-amalgamation-source open62541-generator-types
                  open62541-generator-transport open62541-generator-statuscode)
 
 # 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
+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/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)
+                   COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset_compiler.py
+                           --generate-ns0
+                           ${UA_NAMESPACE0_XML}
+                           ${PROJECT_BINARY_DIR}/src_generated/ua_namespace0
+                   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)
 # 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_namespaceinit_generated.c
+        ${PROJECT_BINARY_DIR}/src_generated/ua_namespaceinit_generated.h)
 
 #####################
 # Build the Library #

+ 185 - 6
src/server/ua_server.c

@@ -151,6 +151,184 @@ UA_Server_cleanup(UA_Server *server, void *_) {
 #endif
 }
 
+static void initNamespace0(UA_Server *server) {
+    /* Load nodes and references generated from the XML ns0 definition */
+    server->bootstrapNS0 = true;
+    ua_namespace0(server);
+    server->bootstrapNS0 = false;
+
+    /* NamespaceArray */
+    UA_DataSource namespaceDataSource = {.handle = server, .read = readNamespaces, .write = NULL};
+    UA_Server_setVariableNode_dataSource(server,
+                                         UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_NAMESPACEARRAY), namespaceDataSource);
+
+    /* ServerArray */
+    writeNs0VariableArray(server, UA_NS0ID_SERVER_SERVERARRAY,
+                          &server->config.applicationDescription.applicationUri,
+                          1, &UA_TYPES[UA_TYPES_STRING]);
+
+    /* LocaleIdArray */
+    UA_String locale_en = UA_STRING("en");
+    writeNs0VariableArray(server, UA_NS0ID_SERVER_SERVERCAPABILITIES_LOCALEIDARRAY,
+                          &locale_en, 1, &UA_TYPES[UA_TYPES_STRING]);
+
+    /* MaxBrowseContinuationPoints */
+    UA_UInt16 maxBrowseContinuationPoints = MAXCONTINUATIONPOINTS;
+    writeNs0Variable(server, UA_NS0ID_SERVER_SERVERCAPABILITIES_MAXBROWSECONTINUATIONPOINTS,
+                     &maxBrowseContinuationPoints, &UA_TYPES[UA_TYPES_UINT16]);
+
+    /* ServerProfileArray */
+    UA_String profileArray[4];
+    UA_UInt16 profileArraySize = 0;
+#define ADDPROFILEARRAY(x) profileArray[profileArraySize++] = UA_STRING_ALLOC(x)
+    ADDPROFILEARRAY("http://opcfoundation.org/UA-Profile/Server/NanoEmbeddedDevice");
+#ifdef UA_ENABLE_NODEMANAGEMENT
+    ADDPROFILEARRAY("http://opcfoundation.org/UA-Profile/Server/NodeManagement");
+#endif
+#ifdef UA_ENABLE_METHODCALLS
+    ADDPROFILEARRAY("http://opcfoundation.org/UA-Profile/Server/Methods");
+#endif
+#ifdef UA_ENABLE_SUBSCRIPTIONS
+    ADDPROFILEARRAY("http://opcfoundation.org/UA-Profile/Server/EmbeddedDataChangeSubscription");
+#endif
+    writeNs0VariableArray(server, UA_NS0ID_SERVER_SERVERCAPABILITIES_SERVERPROFILEARRAY,
+                          profileArray, profileArraySize, &UA_TYPES[UA_TYPES_STRING]);
+
+    /* MaxQueryContinuationPoints */
+    UA_UInt16 maxQueryContinuationPoints = 0;
+    writeNs0Variable(server, UA_NS0ID_SERVER_SERVERCAPABILITIES_MAXQUERYCONTINUATIONPOINTS,
+                     &maxQueryContinuationPoints, &UA_TYPES[UA_TYPES_UINT16]);
+
+    /* MaxHistoryContinuationPoints */
+    UA_UInt16 maxHistoryContinuationPoints = 0;
+    writeNs0Variable(server, UA_NS0ID_SERVER_SERVERCAPABILITIES_MAXHISTORYCONTINUATIONPOINTS,
+                     &maxHistoryContinuationPoints, &UA_TYPES[UA_TYPES_UINT16]);
+
+    /* MinSupportedSampleRate */
+    writeNs0Variable(server, UA_NS0ID_SERVER_SERVERCAPABILITIES_MINSUPPORTEDSAMPLERATE,
+                     &server->config.samplingIntervalLimits.min, &UA_TYPES[UA_TYPES_UINT16]);
+
+    /* ServerDiagnostics - ServerDiagnosticsSummary */
+    UA_ServerDiagnosticsSummaryDataType serverDiagnosticsSummary;
+    UA_ServerDiagnosticsSummaryDataType_init(&serverDiagnosticsSummary);
+    writeNs0Variable(server, UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY,
+                     &serverDiagnosticsSummary, &UA_TYPES[UA_TYPES_SERVERDIAGNOSTICSSUMMARYDATATYPE]);
+
+    /* ServerDiagnostics - EnabledFlag */
+    UA_Boolean enabledFlag = false;
+    writeNs0Variable(server, UA_NS0ID_SERVER_SERVERDIAGNOSTICS_ENABLEDFLAG,
+                     &enabledFlag, &UA_TYPES[UA_TYPES_BOOLEAN]);
+
+    /* ServerStatus */
+    UA_DataSource serverStatus = {.handle = server, .read = readStatus, .write = NULL};
+    UA_Server_setVariableNode_dataSource(server,
+                                         UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS), serverStatus);
+
+    /* StartTime */
+    writeNs0Variable(server, UA_NS0ID_SERVER_SERVERSTATUS_STARTTIME,
+                     &server->startTime, &UA_TYPES[UA_TYPES_DATETIME]);
+
+    /* CurrentTime */
+    UA_DataSource currentTime = {.handle = server, .read = readCurrentTime, .write = NULL};
+    UA_Server_setVariableNode_dataSource(server,
+                                         UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS), currentTime);
+
+    /* State */
+    UA_ServerState state = UA_SERVERSTATE_RUNNING;
+    writeNs0Variable(server, UA_NS0ID_SERVER_SERVERSTATUS_STATE,
+                     &state, &UA_TYPES[UA_TYPES_SERVERSTATE]);
+
+    /* BuildInfo */
+    writeNs0Variable(server, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO,
+                     &server->config.buildInfo, &UA_TYPES[UA_TYPES_BUILDINFO]);
+
+    /* BuildInfo - ProductUri */
+    writeNs0Variable(server, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_PRODUCTURI,
+                     &server->config.buildInfo.productUri, &UA_TYPES[UA_TYPES_STRING]);
+
+    /* BuildInfo - ManufacturerName */
+    writeNs0Variable(server, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_MANUFACTURERNAME,
+                     &server->config.buildInfo.manufacturerName, &UA_TYPES[UA_TYPES_STRING]);
+
+    /* BuildInfo - ProductName */
+    writeNs0Variable(server, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_PRODUCTNAME,
+                     &server->config.buildInfo.productName, &UA_TYPES[UA_TYPES_STRING]);
+
+    /* BuildInfo - SoftwareVersion */
+    writeNs0Variable(server, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_SOFTWAREVERSION,
+                     &server->config.buildInfo.softwareVersion, &UA_TYPES[UA_TYPES_STRING]);
+
+    /* BuildInfo - BuildNumber */
+    writeNs0Variable(server, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_BUILDNUMBER,
+                     &server->config.buildInfo.buildNumber, &UA_TYPES[UA_TYPES_STRING]);
+
+    /* BuildInfo - BuildDate */
+    writeNs0Variable(server, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_BUILDDATE,
+                     &server->config.buildInfo.buildDate, &UA_TYPES[UA_TYPES_DATETIME]);
+
+    /* SecondsTillShutdown */
+    UA_UInt32 secondsTillShutdown = 0;
+    writeNs0Variable(server, UA_NS0ID_SERVER_SERVERSTATUS_SECONDSTILLSHUTDOWN,
+                     &secondsTillShutdown, &UA_TYPES[UA_TYPES_UINT32]);
+
+    /* ShutDownReason */
+    UA_LocalizedText shutdownReason;
+    UA_LocalizedText_init(&shutdownReason);
+    writeNs0Variable(server, UA_NS0ID_SERVER_SERVERSTATUS_SHUTDOWNREASON,
+                     &shutdownReason, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
+
+    /* ServiceLevel */
+    UA_DataSource serviceLevel = {.handle = server, .read = readServiceLevel, .write = NULL};
+    UA_Server_setVariableNode_dataSource(server,
+                                         UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVICELEVEL), serviceLevel);
+
+    /* Auditing */
+    UA_DataSource auditing = {.handle = server, .read = readAuditing, .write = NULL};
+    UA_Server_setVariableNode_dataSource(server,
+                                         UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_AUDITING), auditing);
+
+    /* Redundancy Support */
+    /* TODO: Use enum */
+    UA_Int32 redundancySupport = 0;
+    writeNs0Variable(server, UA_NS0ID_SERVER_SERVERREDUNDANCY_REDUNDANCYSUPPORT,
+                     &redundancySupport, &UA_TYPES[UA_TYPES_INT32]);
+
+#if defined(UA_ENABLE_METHODCALLS) && defined(UA_ENABLE_SUBSCRIPTIONS)
+    UA_Argument inputArguments;
+    UA_Argument_init(&inputArguments);
+    inputArguments.dataType = UA_TYPES[UA_TYPES_UINT32].typeId;
+    inputArguments.name = UA_STRING("SubscriptionId");
+    inputArguments.valueRank = -1; /* scalar argument */
+
+    UA_Argument outputArguments[2];
+    UA_Argument_init(&outputArguments[0]);
+    outputArguments[0].dataType = UA_TYPES[UA_TYPES_UINT32].typeId;
+    outputArguments[0].name = UA_STRING("ServerHandles");
+    outputArguments[0].valueRank = 1;
+
+    UA_Argument_init(&outputArguments[1]);
+    outputArguments[1].dataType = UA_TYPES[UA_TYPES_UINT32].typeId;
+    outputArguments[1].name = UA_STRING("ClientHandles");
+    outputArguments[1].valueRank = 1;
+
+    UA_MethodAttributes addmethodattributes;
+    UA_MethodAttributes_init(&addmethodattributes);
+    addmethodattributes.displayName = UA_LOCALIZEDTEXT("", "GetMonitoredItems");
+    addmethodattributes.executable = true;
+    addmethodattributes.userExecutable = true;
+
+    UA_Server_addMethodNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_GETMONITOREDITEMS),
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER),
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
+                            UA_QUALIFIEDNAME(0, "GetMonitoredItems"), addmethodattributes,                   readMonitoredItems
+    , /* callback of the method node */
+                            NULL, /* handle passed with the callback */
+                            1, &inputArguments,
+                            2, outputArguments,
+                            NULL);
+#endif
+}
+
 /********************/
 /* Server Lifecycle */
 /********************/
@@ -199,6 +377,11 @@ UA_Server_new(const UA_ServerConfig *config) {
     /* Initialized SecureChannel and Session managers */
     UA_SecureChannelManager_init(&server->secureChannelManager, server);
     UA_SessionManager_init(&server->sessionManager, server);
+#ifdef UA_ENABLE_MULTITHREADING
+    rcu_init();
+    cds_wfcq_init(&server->dispatchQueue_head, &server->dispatchQueue_tail);
+    cds_lfs_init(&server->mainLoopJobs);
+#endif
 
     /* Add a regular callback for cleanup and maintenance */
     UA_Server_addRepeatedCallback(server, (UA_ServerCallback)UA_Server_cleanup, NULL,
@@ -232,12 +415,8 @@ 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*/
+    initNamespace0(server);
 
     return server;
 }

+ 4 - 0
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;
 };

+ 8 - 5
src/server/ua_services_nodemanagement.c

@@ -55,9 +55,9 @@ checkParentReference(UA_Server *server, UA_Session *session, UA_NodeClass nodeCl
     /* See if the parent exists */
     const UA_Node *parent = UA_Nodestore_get(server, parentNodeId);
     if(!parent) {
-        UA_LOG_INFO_SESSION(server->config.logger, session,
-                            "AddNodes: Parent node not found");
-        return UA_STATUSCODE_BADPARENTNODEIDINVALID;
+        UA_LOG_INFO_SESSION(server->config.logger, session, "AddNodes: Parent node not found");
+
+        returnUA_STATUSCODE_BADPARENTNODEIDINVALID;
     }
 
     UA_NodeClass parentNodeClass = parent->nodeClass;
@@ -69,7 +69,8 @@ checkParentReference(UA_Server *server, UA_Session *session, UA_NodeClass nodeCl
     if(!referenceType) {
         UA_LOG_INFO_SESSION(server->config.logger, session,
                             "AddNodes: Reference type to the parent not found");
-        return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
+
+        returnUA_STATUSCODE_BADREFERENCETYPEIDINVALID;
     }
 
     UA_NodeClass referenceTypeNodeClass = referenceType->nodeClass;
@@ -122,7 +123,9 @@ checkParentReference(UA_Server *server, UA_Session *session, UA_NodeClass nodeCl
         return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
     }
 
-    return UA_STATUSCODE_GOOD;
+
+
+    returnUA_STATUSCODE_GOOD;
 }
 
 static UA_StatusCode

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


+ 0 - 0
tools/pyUANamespace/NodeID_Blacklist.txt


tools/pyUANamespace/NodeID_Blacklist_FullNS0.txt → tools/nodeset_compiler/NodeID_Blacklist_FullNS0.txt


tools/pyUANamespace/README.md → tools/nodeset_compiler/README.md


+ 73 - 0
tools/nodeset_compiler/backend_graphviz.py

@@ -0,0 +1,73 @@
+from nodeset import *
+
+def NodePrintDot(self):
+    cleanname = "node_" + str(self.id).replace(";","").replace("=","")
+    dot = cleanname + " [label = \"{" + str(self.id) + "|" + str(self.browseName) + \
+                                               "}\", shape=\"record\"]"
+    for r in self.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(root.NodePrintDot())
+    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, Node):
+          tgt = ref.target
+          if not str(tgt.id) in excludeNodeIds:
+            if not tgt in processed:
+              file.write(tgt.NodePrintDot())
+              processed.append(tgt)
+              if ref.isForward == False and followInverse == True:
+                tmp = tmp + tgt.references; # + tgt.getInverseReferences()
+              elif ref.isForward == True :
+                tmp = tmp + tgt.references;
+      refs = tmp
+      iter = iter - 1
+
+    file.write("}\n")
+    file.close()

+ 163 - 0
tools/nodeset_compiler/backend_open62541.py

@@ -0,0 +1,163 @@
+#!/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 string
+from collections import deque
+from os.path import basename
+import logging; 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
+def selectPrintRefs(nodeset, L, node):
+    printRefs = []
+    for ref in node.references:
+        if ref.hidden:
+            continue
+        targetnode = nodeset.nodes[ref.target]
+        if not targetnode in L:
+            continue
+        printRefs.append(ref)
+    for ref in node.inverseReferences:
+        if ref.hidden:
+            continue
+        targetnode = nodeset.nodes[ref.target]
+        if not targetnode in L:
+            continue
+        printRefs.append(ref)
+    return printRefs
+
+def reorderNodesMinDependencies(nodeset):
+    #Kahn's algorithm
+    #https://algocoding.wordpress.com/2015/04/05/topological-sorting-python/
+    
+    relevant_types = getSubTypesOf(nodeset,
+                                   nodeset.getNodeByBrowseName("HierarchicalReferences"))
+    relevant_types = map(lambda x: x.id, relevant_types)
+
+    # determine in-degree
+    in_degree = { u.id : 0 for u in nodeset.nodes.values() }
+    for u in nodeset.nodes.values(): # of each node
+        for ref in u.references:
+            if(ref.referenceType in relevant_types and ref.isForward):
+                in_degree[ref.target] += 1
+    
+    # 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)
+      L.append(u)          # and 'remove' it from graph
+      for ref in u.references:
+        if(ref.referenceType in relevant_types and ref.isForward):
+         in_degree[ref.target] -= 1
+         if in_degree[ref.target] == 0:
+           Q.append(nodeset.nodes[ref.target])
+    if len(L) != len(nodeset.nodes.values()):
+      raise Exception("Node graph is circular on the specified references")
+    return L
+
+###################
+# Generate C Code #
+###################
+
+def generateOpen62541Code(nodeset, outfilename, supressGenerationOfAttribute = [], generate_ns0 = False):
+    outfilebase = basename(outfilename)
+    # Printing functions
+    outfileh = open(outfilename + ".h", r"w+")
+    outfilec = open(outfilename + ".c", r"w+")
+    def writeh(line):
+        print(unicode(line).encode('utf8'), end='\n', file=outfileh)
+    def writec(line):
+        print(unicode(line).encode('utf8'), end='\n', file=outfilec)
+
+    # 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_
+
+#ifdef UA_NO_AMALGAMATION
+#include "ua_types.h"
+#include "ua_job.h"
+#include "ua_server.h"
+#else
+#include "open62541.h"
+#define NULL ((void *)0)
+#endif
+    
+extern void %s(UA_Server *server);
+
+#endif /* %s_H_ */""" % \
+           (outfilebase.upper(), outfilebase.upper(), \
+            outfilebase, outfilebase.upper()))
+
+    writec("""/* WARNING: This is a generated file.
+ * Any manual changes will be overwritten. */
+
+#include "%s.h"
+
+void %s(UA_Server *server) {""" % (outfilebase, outfilebase))
+
+    parentrefs = getSubTypesOf(nodeset, nodeset.getNodeByBrowseName("HierarchicalReferences"))
+    parentrefs = 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) + " */")
+        writec(generateNodeCode(node, supressGenerationOfAttribute, generate_ns0, parentrefs))
+
+      # Print inverse references leading to this node
+      for ref in node.printRefs:
+          writec(generateReferenceCode(ref))
+
+    # Finalize the generated source
+    writec("} // closing nodeset()")
+    outfileh.close()
+    outfilec.close()

+ 128 - 0
tools/nodeset_compiler/backend_open62541_datatypes.py

@@ -0,0 +1,128 @@
+from datatypes import *
+
+def generateBooleanCode(value):
+  if value:
+    return "true"
+  return "false"
+
+def generateStringCode(value):
+  return "UA_STRING(\"" + value + "\")"
+
+def generateXmlElementCode(value):
+  return "UA_XMLELEMENT(\"" + value + "\")"
+
+def generateByteStringCode(value):
+  return "UA_BYTESTRING(\"" + value + "\")"
+
+def generateLocalizedTextCode(value):
+  return "UA_LOCALIZEDTEXT(\"" + value.locale + "\", \"" + value.text + "\")"
+
+def generateQualifiedNameCode(value):
+  return "UA_QUALIFIEDNAME(ns" + str(value.ns) + ", \"" + value.name + "\")"
+
+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)
+  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)
+    raise Exception(str(value) + " no NodeID generation for bytestring and guid..")
+
+def generateVariantCode(self):
+    code = []
+    valueName = self.parent.getCodePrintableID() + "_variant_DataContents"
+
+    # self.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(self.value) == 0):
+      return code
+    if not isinstance(self.value[0], opcua_value_t):
+      return code
+
+    if self.parent.valueRank() != -1 and \
+       (self.parent.valueRank() >=0 or (len(self.value) > 1 and \
+                                        (self.parent.valueRank() != -2 or self.parent.valueRank() != -3))):
+      # User the following strategy for all directly mappable values a la 'UA_Type MyInt = (UA_Type) 23;'
+      if self.value[0].__binTypeId__ == BUILTINTYPE_TYPEID_GUID:
+        logger.warn("Don't know how to print array of GUID in node " + str(self.parent.id()))
+      elif self.value[0].__binTypeId__ == BUILTINTYPE_TYPEID_DATETIME:
+        logger.warn("Don't know how to print array of DateTime in node " + str(self.parent.id()))
+      elif self.value[0].__binTypeId__ == BUILTINTYPE_TYPEID_DIAGNOSTICINFO:
+        logger.warn("Don't know how to print array of DiagnosticInfo in node " + str(self.parent.id()))
+      elif self.value[0].__binTypeId__ == BUILTINTYPE_TYPEID_STATUSCODE:
+        logger.warn("Don't know how to print array of StatusCode in node " + str(self.parent.id()))
+      else:
+        if self.value[0].__binTypeId__ == BUILTINTYPE_TYPEID_EXTENSIONOBJECT:
+          for v in self.value:
+            logger.debug("Building extObj array index " + str(self.value.index(v)))
+            code.extend(v.printOpen62541CCode_SubType_build(arrayIndex=self.value.index(v)))
+        #code.append("attr.value.type = &UA_TYPES[UA_TYPES_" + self.value[0].stringRepresentation.upper() + "];")
+        code.append("UA_" + self.value[0].stringRepresentation + " " + valueName + \
+                    "[" + str(len(self.value)) + "];")
+        if self.value[0].__binTypeId__ == BUILTINTYPE_TYPEID_EXTENSIONOBJECT:
+          for v in self.value:
+            logger.debug("Printing extObj array index " + str(self.value.index(v)))
+            code.append(valueName + "[" + str(self.value.index(v)) + "] = " + \
+                        v.printOpen62541CCode_SubType(asIndirect=False) + ";")
+            code.append("UA_free(" + v.printOpen62541CCode_SubType() + ");")
+        else:
+          for v in self.value:
+            code.append(valueName + "[" + str(self.value.index(v)) + "] = " + \
+                        v.printOpen62541CCode_SubType() + ";")
+        code.append("UA_Variant_setArray( &attr.value, &" + valueName +
+                    ", (UA_Int32) " + str(len(self.value)) + ", &UA_TYPES[UA_TYPES_" + \
+                    self.value[0].stringRepresentation.upper() + "]);")
+    else:
+      # User the following strategy for all directly mappable values a la 'UA_Type MyInt = (UA_Type) 23;'
+      if self.value[0].__binTypeId__ == BUILTINTYPE_TYPEID_GUID:
+        logger.warn("Don't know how to print scalar GUID in node " + str(self.parent.id()))
+      elif self.value[0].__binTypeId__ == BUILTINTYPE_TYPEID_DATETIME:
+        logger.warn("Don't know how to print scalar DateTime in node " + str(self.parent.id()))
+      elif self.value[0].__binTypeId__ == BUILTINTYPE_TYPEID_DIAGNOSTICINFO:
+        logger.warn("Don't know how to print scalar DiagnosticInfo in node " + str(self.parent.id()))
+      elif self.value[0].__binTypeId__ == BUILTINTYPE_TYPEID_STATUSCODE:
+        logger.warn("Don't know how to print scalar StatusCode in node " + str(self.parent.id()))
+      else:
+        # The following strategy applies to all other types, in particular strings and numerics.
+        if self.value[0].__binTypeId__ == BUILTINTYPE_TYPEID_EXTENSIONOBJECT:
+          code.extend(self.value[0].printOpen62541CCode_SubType_build())
+        #code.append("attr.value.type = &UA_TYPES[UA_TYPES_" + self.value[0].stringRepresentation.upper() + "];")
+        if self.value[0].__binTypeId__ == BUILTINTYPE_TYPEID_EXTENSIONOBJECT:
+          code.append("UA_" + self.value[0].stringRepresentation + " *" + valueName + " = " + \
+                      self.value[0].printOpen62541CCode_SubType() + ";")
+          code.append("UA_Variant_setScalar( &attr.value, " + valueName + ", &UA_TYPES[UA_TYPES_" + \
+                      self.value[0].stringRepresentation.upper() + "]);")
+
+          #FIXME: There is no membership definition for extensionObjects generated in this function.
+          #code.append("UA_" + self.value[0].stringRepresentation + "_deleteMembers(" + valueName + ");")
+        else:
+          if bootstrapping == True:
+              code.append("UA_Variant* " + self.parent.getCodePrintableID() + "_variant = UA_Variant_new();" )
+          code.append("UA_" + self.value[0].stringRepresentation + " *" + valueName + " =  UA_" + \
+                      self.value[0].stringRepresentation + "_new();")
+          code.append("*" + valueName + " = " + self.value[0].printOpen62541CCode_SubType() + ";")
+          if bootstrapping == False:
+            code.append("UA_Variant_setScalar( &attr.value, " + valueName + ", &UA_TYPES[UA_TYPES_" + \
+                        self.value[0].stringRepresentation.upper() + "]);")
+          else:
+            code.append("UA_Variant_setScalar( "+self.parent.getCodePrintableID()+"_variant, " + \
+                        valueName + ", &UA_TYPES[UA_TYPES_" + self.value[0].stringRepresentation.upper() + "]);")
+          #code.append("UA_" + self.value[0].stringRepresentation + "_deleteMembers(" + valueName + ");")
+    return code

+ 199 - 0
tools/nodeset_compiler/backend_open62541_nodes.py

@@ -0,0 +1,199 @@
+#!/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 *
+
+###########################################
+# Extract References with Special Meaning #
+###########################################
+
+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)
+            node.printRefs.remove(ref)
+            return (ref.target, ref.referenceType)
+    raise Exception("No node parent known for " + str(node))
+
+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 generateReferenceCode(reference):
+    if reference.isForward:
+        return "UA_Server_addReference(server, %s, %s, %s, true);" % \
+            (generateNodeIdCode(reference.source), \
+             generateNodeIdCode(reference.referenceType), \
+             generateExpandedNodeIdCode(reference.target))
+    else:
+      return "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;")
+    code.append("UA_ReferenceTypeAttributes_init(&attr);")
+    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_ALLOC(\"en_US\", \"%s\");" % \
+                    node.inverseName)
+    return code;
+
+def generateObjectNodeCode(node):
+    code = []
+    code.append("UA_ObjectAttributes attr;")
+    code.append("UA_ObjectAttributes_init(&attr);")
+    if node.eventNotifier:
+        code.append("attr.eventNotifier = true;")
+    return code;
+
+def generateVariableNodeCode(node):
+    code = []
+    code.append("UA_VariableAttributes attr;")
+    code.append("UA_VariableAttributes_init(&attr);")
+    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)
+    # # The variant is guaranteed to exist by SubtypeEarly()
+    # code.append(getCodePrintableNodeID(node) + ".value.variant.value = *" + \
+    #             getCodePrintableNodeID(node) + "_variant;")
+    # code.append(getCodePrintableNodeID(node) + ".valueSource = UA_VALUESOURCE_VARIANT;")
+    return code
+
+def generateVariableTypeNodeCode(node):
+    code = []
+    code.append("UA_VariableTypeAttributes attr;")
+    code.append("UA_VariableTypeAttributes_init(&attr);")
+    if node.historizing:
+        code.append("attr.historizing = true;")
+    code.append("attr.valueRank = (UA_Int32)%s;" %str(node.valueRank))
+    # # The variant is guaranteed to exist by SubtypeEarly()
+    # code.append(getCodePrintableNodeID(node) + ".value.variant.value = *" + \
+    #             getCodePrintableNodeID(node) + "_variant;")
+    # code.append(getCodePrintableNodeID(node) + ".valueSource = UA_VALUESOURCE_VARIANT;")
+    return code
+
+def generateMethodNodeCode(node):
+    code = []
+    code.append("UA_MethodAttributes attr;")
+    code.append("UA_MethodAttributes_init(&attr);")
+    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;")
+    code.append("UA_ObjectTypeAttributes_init(&attr);")
+    if node.isAbstract:
+      code.append("attr.isAbstract = true;")
+    return code
+
+def generateDataTypeNodeCode(node):
+    code = []
+    code.append("UA_DataTypeAttributes attr;")
+    code.append("UA_DataTypeAttributes_init(&attr);")
+    if node.isAbstract:
+      code.append("attr.isAbstract = true;")
+    return code
+
+def generateViewNodeCode(node):
+    code = []
+    code.append("UA_ViewAttributes attr;")
+    code.append("UA_ViewAttributes_init(&attr);")
+    if node.containsNoLoops:
+      code.append("attr.containsNoLoops = true;")
+    code.append("attr.eventNotifier = (UA_Byte)%s;" % str(node.eventNotifier))
+    return code
+
+def generateNodeCode(node, supressGenerationOfAttribute, generate_ns0, parentrefs):
+    code = []
+    code.append("{")
+
+    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):
+        code.extend(generateVariableNodeCode(node))
+    elif isinstance(node, VariableTypeNode):
+        code.extend(generateVariableTypeNodeCode(node))
+    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) + ";")
+    code.append("attr.description = " + generateLocalizedTextCode(node.description) + ";")
+    code.append("attr.writeMask = %d;" % node.writeMask)
+    code.append("attr.userWriteMask = %d;" % node.userWriteMask)
+    
+    if not generate_ns0:
+        (parentNode, parentRef) = extractNodeParent(node, parentrefs)
+    else:
+        (parentNode, parentRef) = (NodeId(), NodeId())
+
+    code.append("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, VariableNode) and not isinstance(node, VariableTypeNode)) or isinstance(node, ObjectNode):
+        code.append("UA_NODEID_NUMERIC(0,0),") # parent
+    code.append("attr,")
+    if isinstance(node, MethodNode):
+        code.append("NULL, NULL, 0, NULL, 0, NULL, NULL);")
+    else:
+        code.append("NULL, NULL);")
+    code.append("}\n")
+    return "\n".join(code)

tools/pyUANamespace/ua_constants.py → tools/nodeset_compiler/constants.py


+ 522 - 0
tools/nodeset_compiler/datatypes.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.
+###
+
+import sys
+from time import strftime, strptime
+import logging; logger = logging.getLogger(__name__)
+import xml.dom.minidom as dom
+
+from constants import *
+
+if sys.version_info[0] >= 3:
+  # strings are already parsed to unicode
+  def unicode(s):
+    return s
+
+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 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):
+    self.checkXML(xmlvalue)
+    if not "value" in xmlvalue.tagName.lower():
+      logger.error("Expected <Value> , but found " + xmlvalue.tagName + \
+                   " 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.tagName:
+    #   self.value = []
+    #   for el in xmlvalue.childNodes:
+    #     if not el.nodeType == el.ELEMENT_NODE:
+    #       continue
+    #     self.value.append(self.__parseXMLSingleValue(el))
+    # else:
+    #   self.value = [self.__parseXMLSingleValue(xmlvalue)]
+
+    logger.debug( "Parsed Value: " + str(self.value))
+
+  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 Byte(Value):
+  def __init__(self, xmlelement = None):
+    Value.__init__(self)
+    self.numericRepresentation = BUILTINTYPE_TYPEID_BYTE
+    if xmlelement:
+      self.parseXML(xmlelement)
+
+  def parseXML(self, xmlvalue):
+    # Expect <Byte>value</Byte> or
+    #        <Aliasname>value</Aliasname>
+    self.checkXML(xmlvalue)
+    if xmlvalue.firstChild == None:
+      self.value = 0 # Catch XML <Byte /> by setting the value to a default
+    else:
+      self.value = int(unicode(xmlvalue.firstChild.data))
+
+class SByte(Value):
+  def __init__(self, xmlelement = None):
+    Value.__init__(self)
+    self.numericRepresentation = BUILTINTYPE_TYPEID_SBYTE
+    if xmlelement:
+      self.parseXML(xmlelement)
+
+  def parseXML(self, xmlvalue):
+    # Expect <SByte>value</SByte> or
+    #        <Aliasname>value</Aliasname>
+    self.checkXML(xmlvalue)
+    if xmlvalue.firstChild == None:
+      self.value = 0 # Catch XML <SByte /> by setting the value to a default
+    else:
+      self.value = int(unicode(xmlvalue.firstChild.data))
+
+class Int16(Value):
+  def __init__(self, xmlelement = None):
+    Value.__init__(self)
+    self.numericRepresentation = BUILTINTYPE_TYPEID_INT16
+    if xmlelement:
+      self.parseXML(xmlelement)
+
+  def parseXML(self, xmlvalue):
+    # Expect <Int16>value</Int16> 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 UInt16(Value):
+  def __init__(self, xmlelement = None):
+    Value.__init__(self)
+    self.numericRepresentation = BUILTINTYPE_TYPEID_UINT16
+    if xmlelement:
+      self.parseXML(xmlelement)
+
+  def parseXML(self, xmlvalue):
+    # Expect <UInt16>value</UInt16> or
+    #        <Aliasname>value</Aliasname>
+    self.checkXML(xmlvalue)
+    if xmlvalue.firstChild == None:
+      self.value = 0 # Catch XML <UInt16 /> by setting the value to a default
+    else:
+      self.value = int(unicode(xmlvalue.firstChild.data))
+
+class Int32(Value):
+  def __init__(self, xmlelement = None):
+    Value.__init__(self)
+    self.numericRepresentation = BUILTINTYPE_TYPEID_INT32
+    if xmlelement:
+      self.parseXML(xmlelement)
+
+  def parseXML(self, xmlvalue):
+    # Expect <Int32>value</Int32> or
+    #        <Aliasname>value</Aliasname>
+    self.checkXML(xmlvalue)
+    if xmlvalue.firstChild == None:
+      self.value = 0 # Catch XML <Int32 /> by setting the value to a default
+    else:
+      self.value = int(unicode(xmlvalue.firstChild.data))
+
+class UInt32(Value):
+  def __init__(self, xmlelement = None):
+    Value.__init__(self)
+    self.numericRepresentation = BUILTINTYPE_TYPEID_UINT32
+    if xmlelement:
+      self.parseXML(xmlelement)
+
+  def parseXML(self, xmlvalue):
+    # Expect <UInt32>value</UInt32> or
+    #        <Aliasname>value</Aliasname>
+    self.checkXML(xmlvalue)
+    if xmlvalue.firstChild == None:
+      self.value = 0 # Catch XML <UInt32 /> by setting the value to a default
+    else:
+      self.value = int(unicode(xmlvalue.firstChild.data))
+
+class Int64(Value):
+  def __init__(self, xmlelement = None):
+    Value.__init__(self)
+    self.numericRepresentation = BUILTINTYPE_TYPEID_INT64
+    if xmlelement:
+      self.parseXML(xmlelement)
+
+  def parseXML(self, xmlvalue):
+    # Expect <Int64>value</Int64> or
+    #        <Aliasname>value</Aliasname>
+    self.checkXML(xmlvalue)
+    if xmlvalue.firstChild == None:
+      self.value = 0 # Catch XML <Int64 /> by setting the value to a default
+    else:
+      self.value = int(unicode(xmlvalue.firstChild.data))
+
+class UInt64(Value):
+  def __init__(self, xmlelement = None):
+    Value.__init__(self)
+    self.numericRepresentation = BUILTINTYPE_TYPEID_UINT64
+    if xmlelement:
+      self.parseXML(xmlelement)
+
+  def parseXML(self, xmlvalue):
+    # Expect <UInt16>value</UInt16> or
+    #        <Aliasname>value</Aliasname>
+    self.checkXML(xmlvalue)
+    if xmlvalue.firstChild == None:
+      self.value = 0 # Catch XML <UInt64 /> by setting the value to a default
+    else:
+      self.value = int(unicode(xmlvalue.firstChild.data))
+
+class Float(Value):
+  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(Value):
+  def __init__(self, xmlelement = None):
+    Value.__init__(self)
+    self.numericRepresentation = BUILTINTYPE_TYPEID_DOUBLE
+    if xmlelement:
+      self.parseXML(xmlelement)
+
+  def parseXML(self, xmlvalue):
+    # Expect <Double>value</Double> or
+    #        <Aliasname>value</Aliasname>
+    self.checkXML(xmlvalue)
+    if xmlvalue.firstChild == None:
+      self.value = 0.0 # Catch XML <Double /> by setting the value to a default
+    else:
+      self.value = float(unicode(xmlvalue.firstChild.data))
+
+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>
+    self.checkXML(xmlvalue)
+    if xmlvalue.firstChild == None:
+      self.value = "" # Catch XML <String /> by setting the value to a default
+    else:
+      self.value = str(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
+
+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 = 'en_US'
+    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
+    tmp = xmlvalue.getElementsByTagName("Text")
+    if len(tmp) > 0 and tmp[0].firstChild != None:
+        self.text = tmp[0].firstChild.data
+
+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
+
+    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 __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:
+        logger.error("Timestring format is illegible. Expected 2001-01-30T21:22:23, 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
+
+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

+ 342 - 0
tools/nodeset_compiler/nodes.py

@@ -0,0 +1,342 @@
+#!/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 sets import Set
+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):
+    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
+
+  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.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)
+
+    for x in xmlelement.childNodes:
+      if x.nodeType != x.ELEMENT_NODE:
+        continue
+      if x.firstChild:
+        if x.tagName == "BrowseName":
+          self.browseName = QualifiedName(x.firstChild.data)
+        elif x.tagName == "DisplayName":
+          self.displayName = LocalizedText(x.firstChild.data)
+        elif x.tagName == "Description":
+          self.description = LocalizedText(x.firstChild.data)
+        elif x.tagName == "WriteMask":
+          self.writeMask = int(unicode(x.firstChild.data))
+        elif x.tagName == "UserWriteMask":
+          self.userWriteMask = int(unicode(x.firstChild.data))
+        if x.tagName == "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]
+    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.tagName == "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           = -1
+    self.arrayDimensions     = []
+    self.accessLevel         = 0
+    self.userAccessLevel     = 0
+    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.tagName == "Value":
+          self.__xmlValueDef__ = x
+      elif x.tagName == "DataType":
+          self.dataType = NodeId(str(x))
+      elif x.tagName == "ValueRank":
+          self.valueRank = int(unicode(x.firstChild.data))
+      elif x.tagName == "ArrayDimensions":
+          self.arrayDimensions = int(unicode(x.firstChild.data))
+      elif x.tagName == "AccessLevel":
+          self.accessLevel = int(unicode(x.firstChild.data))
+      elif x.tagName == "UserAccessLevel":
+          self.userAccessLevel = int(unicode(x.firstChild.data))
+      elif x.tagName == "MinimumSamplingInterval":
+          self.minimumSamplingInterval = float(unicode(x.firstChild.data))
+      elif x.tagName == "Historizing":
+          self.historizing = "false" not in x.lower()
+
+class VariableTypeNode(VariableNode):
+  def __init__(self, xmlelement = None):
+    VariableNode.__init__(self)
+    self.nodeClass = NODE_CLASS_VARIABLETYPE
+    if xmlelement:
+      self.parseXML(xmlelement)
+
+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):
+  def __init__(self, xmlelement = None):
+    Node.__init__(self)
+    self.nodeClass = NODE_CLASS_DATATYPE
+    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 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()

+ 218 - 0
tools/nodeset_compiler/nodeset.py

@@ -0,0 +1,218 @@
+#!/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 datatypes import *
+from nodes import *
+from constants import *
+
+####################
+# Helper Functions #
+####################
+
+hassubtype = NodeId("ns=0;i=45")
+def getSubTypesOf(nodeset, node):
+  re = [node]
+  for ref in node.references: 
+    if ref.referenceType == hassubtype and ref.isForward:
+      re = re + getSubTypesOf(nodeset, nodeset.nodes[ref.target])
+  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.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 getRoot(self):
+    return self.getNodeByBrowseName("Root")
+
+  def createNode(self, xmlelement, nsMapping, hidden=False):
+    ndtype = xmlelement.tagName.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 addNodeSet(self, xmlfile, hidden = False):
+    # Extract NodeSet DOM
+    nodesets = dom.parse(xmlfile).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
+    aliases = None
+    for nd in nodeset.childNodes:
+      if nd.nodeType != nd.ELEMENT_NODE:
+        continue
+      ndtype = nd.tagName.lower()
+      if 'aliases' in ndtype:
+        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(aliases)
+      node.replaceNamespaces(nsMapping)
+      
+      # 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)
+            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)
+            newsource.references.add(newref)

+ 125 - 0
tools/nodeset_compiler/nodeset_compiler.py

@@ -0,0 +1,125 @@
+#!/usr/bin/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.
+###
+
+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('r'),
+                    dest="existing",
+                    action='append',
+                    default=[],
+                    help='NodeSet XML files with nodes that are already present on the server.')
+
+parser.add_argument('infiles',
+                    metavar="<nodeSetXML>",
+                    action='append',
+                    type=argparse.FileType('r'),
+                    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('-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('-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()
+
+# 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()
+for xmlfile in args.existing:
+  logger.info("Preprocessing (existing) " + str(xmlfile.name))
+  ns.addNodeSet(xmlfile, True)
+for xmlfile in args.infiles:
+  logger.info("Preprocessing " + str(xmlfile.name))
+  ns.addNodeSet(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 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()
+
+# Remove nodes that are not printable or contain parsing errors, such as
+# unresolvable or no references or invalid NodeIDs
+ns.sanitize()
+
+# Create the C code with the open62541 backend of the compiler
+logger.info("Generating Code")
+generateOpen62541Code(ns, args.outputFile, args.suppressedAttributes, args.generate_ns0)
+logger.info("NodeSet generation code successfully printed")

+ 3 - 7
tools/pyUANamespace/ua_nodeset_testing.py

@@ -1,4 +1,4 @@
-from ua_nodeset import *
+from nodeset import *
 
 class testing:
   def __init__(self):
@@ -29,7 +29,8 @@ class testing:
     ns = [self.ns.getRoot()]
 
     i = 0
-    #print "Starting depth search on " + str(len(allnodes)) + " nodes starting with from " + str(ns)
+    #print "Starting depth search on " + str(len(allnodes)) + " nodes starting
+    #with from " + str(ns)
     while (len(ns) < len(allnodes)):
       i = i + 1;
       tmp = [];
@@ -68,10 +69,5 @@ class testing_open62541_header:
     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 nodeset.
-#
-# import ua_types.py as ua; ns=ua.testing().nodeset
 if __name__ == '__main__':
   tst = testing_open62541_header()

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


+ 0 - 179
tools/pyUANamespace/generate_open62541CCode.py

@@ -1,179 +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
-import logging
-import argparse
-from os.path import basename
-from ua_nodeset import *
-from open62541_XMLPreprocessor import open62541_XMLPreprocessor
-from open62541_backend import generateCCode
-
-parser = argparse.ArgumentParser(
-  description="""Parse OPC UA NodeSetXML 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 nodeset. 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 nodeset.""",
-    formatter_class=argparse.RawDescriptionHelpFormatter)
-parser.add_argument('infiles',
-                    metavar="<nodeSetXML>",
-                    nargs='+',
-                    type=argparse.FileType('r'),
-                    help='NodeSet 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 nodeset 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('--high-level-api',
-                    action='store_true', default=False,
-                    dest='high_level_api',
-                    help="Use only high level API which makes it possible to add nodes in userspace")
-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)
-
-# Open the output file
-outfileh = open(args.outputFile+".h", r"w+")
-outfilec = open(args.outputFile+".c", r"w+")
-
-# Create a new nodeset. The nodeset name is not significant.
-ns = NodeSet("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 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()
-
-# Link the references in the nodeset
-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, NodeSet does currently not contain a node with id " + str(id))
-    else:
-      ignoreNodes.append(ns.getNodeByIDString(id))
-  ignore.close()
-
-# Create the C code with the open62541 backend of the compiler
-logger.info("Generating Code")
-(header, code) = generateCCode( ns,ignoreNodes, args.suppressedAttributes,
-                                        outfilename=basename(args.outputFile))
-for line in header:
-  print(line, end='\n', file=outfileh)
-for line in code:
-  print(line, end='\n', file=outfilec)
-
-outfilec.close()
-outfileh.close()
-
-logger.info("NodeSet generation code successfully printed")

+ 0 - 361
tools/pyUANamespace/open62541_XMLPreprocessor.py

@@ -1,361 +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
-logger = logging.getLogger(__name__)
-
-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
-
-
-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):
-    """ 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.
-    """
-    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 will gather information about the nodes and references contained in a XML File
-        to facilitate later preprocessing stages that adresss XML dependency issues
-    """
-    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):
-    """ 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
-    return namespaceIdGuessed
-
-  def getReferencedNamespaceUri(self, nsId):
-    """ Returns an URL that hopefully corresponds to the nsId that was used to reference this model """
-    # 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):
-    """Iterates over all references in this document, find references to
-       currentNsId and changes them to newNsId. NodeIds themselves are not
-       altered."""
-    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):
-    """Iterates over all nodes in this document, find those in namespace
-       currentNsId and changes them to newNsId."""
-
-    #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:
-  def __init__(self):
-      self.preProcDocuments = []
-
-  def addDocument(self, documentPath):
-    self.preProcDocuments.append(preProcessDocument(documentPath))
-
-  def getPreProcessedFiles(self):
-    files = []
-    for doc in self.preProcDocuments:
-      if (doc.parseOK):
-        files.append(doc.getTargetXMLName())
-    return files
-
-  def testModelCongruencyAgainstReferences(self, doc, refs):
-    """ Counts how many of the nodes referenced 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]):
-          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)
-        if c>0:
-          matches.append((c, tDoc))
-      best = (0, None)
-      for m in matches:
-        if m[0] > best[0]:
-          best = m
-      if best[1] != None:
-        logger.warn("Best match (%d) for what %s refers to as ns=%s was %s", best[1], os.path.basename(doc.originXML), d, 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):
-    # Gather statistics about the namespaces:
-    for doc in self.preProcDocuments:
-      doc.analyze()
-
-    # Preprocess step: Remove XML specific Naming scheme ("uax:")
-    # FIXME: Not implemented
-
-    # 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()

+ 0 - 205
tools/pyUANamespace/open62541_backend.py

@@ -1,205 +0,0 @@
-#!/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 string
-import logging; logger = logging.getLogger(__name__)
-
-from ua_constants import *
-from open62541_backend_nodes import Node_printOpen62541CCode, getCreateStandaloneReference
-
-####################
-# Helper Functions #
-####################
-
-def substitutePunctuationCharacters(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
-
-defined_typealiases = []
-def getNodeIdDefineString(node):
-    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 = substitutePunctuationCharacters(nodename)
-    if symbolic_name != nodename :
-        logger.warn("Substituted characters in browsename for nodeid " + \
-                    str(node.id().i) + " while generating C-Code ")
-    if symbolic_name in defined_typealiases:
-      logger.warn("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)
-    return "#define UA_NS%sID_%s %s" % (node.id().ns, symbolic_name.upper(), node.id().i)
-
-###################
-# Generate C Code #
-###################
-
-def generateCCode(nodeset, printedExternally=[], supressGenerationOfAttribute=[],
-                  outfilename="", high_level_api=False):
-    unPrintedNodes = []
-    unPrintedRefs  = []
-    code = []
-    header = []
-
-    # Reorder our nodes to produce a bare minimum of bootstrapping dependencies
-    logger.debug("Reordering nodes for minimal dependencies during printing.")
-    nodeset.reorderNodesMinDependencies(printedExternally)
-
-    # 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 nodeset.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("%d nodes and %d references need to get printed.", len(unPrintedNodes), len(unPrintedRefs))
-
-    # Print the preamble of the generated code
-    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('#ifdef UA_NO_AMALGAMATION')
-    header.append('#include "ua_types.h"')
-    if high_level_api:
-        header.append('#include "ua_job.h"')
-        header.append('#include "ua_server.h"')
-    if not high_level_api:
-        header.append('#include "server/ua_server_internal.h"')
-        header.append('#include "server/ua_nodes.h"')
-        header.append('#include "ua_util.h"')
-        header.append('#include "ua_types_encoding_binary.h"')
-        header.append('#include "ua_types_generated_encoding_binary.h"')
-        header.append('#include "ua_transport_generated_encoding_binary.h"')
-    header.append('#else')
-    header.append('#include "open62541.h"')
-    header.append('#define NULL ((void *)0)')
-    header.append('#endif')
-    code.append('#include "'+outfilename+'.h"')
-    code.append("UA_INLINE void "+outfilename+"(UA_Server *server) {")
-
-    # Before printing nodes, we need to request additional namespace arrays from the server
-    for nsid in nodeset.namespaceIdentifiers:
-      if nsid == 0 or nsid==1:
-        continue
-      else:
-        name =  nodeset.namespaceIdentifiers[nsid]
-        name = name.replace("\"","\\\"")
-        code.append("UA_Server_addNamespace(server, \"" + name + "\");")
-
-    # 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 nodeset.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.append(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("%d reference types are used in the namespace, which will now get bootstrapped.", len(refsUsed))
-    for r in refsUsed:
-      code.extend(Node_printOpen62541CCode(r, unPrintedNodes, unPrintedRefs, supressGenerationOfAttribute))
-
-    logger.debug("%d Nodes, %d References need to get printed.", len(unPrintedNodes), len(unPrintedRefs))
-
-    if not high_level_api:
-        # 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 nodeset.nodes:
-          code.extend(Node_printOpen62541CCode(n, unPrintedNodes, unPrintedRefs, supressGenerationOfAttribute))
-
-        if len(unPrintedNodes) != 0:
-          logger.warn("%d nodes could not be translated to code.", len(unPrintedNodes))
-        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.extend(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")
-    else:  # Using only High Level API
-        already_printed = list(printedExternally)
-        while unPrintedNodes:
-            node_found = False
-            for node in unPrintedNodes:
-                for ref in node.getReferences():
-                    if ref.referenceType() in already_printed and ref.target() in already_printed:
-                        node_found = True
-                        code.extend(Node_printOpen62541CCode_HL_API(node, ref, supressGenerationOfAttribute))
-                        unPrintedRefs.remove(ref)
-                        unPrintedNodes.remove(node)
-                        already_printed.append(node)
-                        break
-            if not node_found:
-                logger.critical("no complete code generation with high level API possible; not all nodes will be created")
-                code.append("CRITICAL: no complete code generation with high level API possible; not all nodes will be created")
-                break
-        code.append("// creating references")
-        for r in unPrintedRefs:
-            code.extend(getCreateStandaloneReference(r.parent(), r))
-
-    # finalizing source and header
-    header.append("extern void "+outfilename+"(UA_Server *server);\n")
-    header.append("#endif /* "+outfilename.upper()+"_H_ */")
-    code.append("} // closing nodeset()")
-    return (header,code)

+ 0 - 716
tools/pyUANamespace/open62541_backend_nodes.py

@@ -1,716 +0,0 @@
-#!/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 ua_node_types import *
-
-####################
-# Helper Functions #
-####################
-
-def getCreateNodeIDMacro(node):
-    if node.id().i != None:
-      return "UA_NODEID_NUMERIC(%s, %s)" % (node.id().ns, node.id().i)
-    elif node.id().s != None:
-      return "UA_NODEID_STRING(%s, %s)" % (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 getCreateExpandedNodeIDMacro(node):
-    if node.id().i != None:
-      return "UA_EXPANDEDNODEID_NUMERIC(%s, %s)" % (str(node.id().ns),str(node.id().i))
-    elif node.id().s != None:
-      return "UA_EXPANDEDNODEID_STRING(%s, %s)" % (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(sourcenode, reference):
-    code = []
-    if reference.isForward():
-      code.append("UA_Server_addReference(server, %s, %s, %s, true);" % \
-                  (getCreateNodeIDMacro(sourcenode), getCreateNodeIDMacro(reference.referenceType()), \
-                   getCreateExpandedNodeIDMacro(reference.target())))
-    else:
-      code.append("UA_Server_addReference(server, %s, %s, %s, false);" % \
-                  (getCreateNodeIDMacro(sourcenode), getCreateNodeIDMacro(reference.referenceType()), \
-                   getCreateExpandedNodeIDMacro(reference.target())))
-    return code
-
-
-#################
-# Subtype Early #
-#################
-
-def Node_printOpen62541CCode_SubtypeEarly(node, bootstrapping = True):
-    """ Initiate code segments for the nodes instantiotion that preceed
-        the actual UA_Server_addNode or UA_NodeStore_insert calls.
-    """
-    code = []
-    if isinstance(node, opcua_node_variable_t) or isinstance(node, opcua_node_variableType_t):
-        # If we have an encodable value, try to encode that
-        if node.dataType() != None and isinstance(node.dataType().target(), opcua_node_dataType_t):
-          # Delegate the encoding of the datavalue to the helper if we have
-          # determined a valid encoding
-          if node.dataType().target().isEncodable():
-            if node.value() != None:
-              code.extend(node.value().printOpen62541CCode(bootstrapping))
-              return code
-        if(bootstrapping):
-          code.append("UA_Variant *" + node.getCodePrintableID() + "_variant = UA_alloca(sizeof(UA_Variant));")
-          code.append("UA_Variant_init(" + node.getCodePrintableID() + "_variant);")
-
-    return code
-
-###########
-# Subtype #
-###########
-
-def ReferenceTypeNode_printOpen62541CCode_Subtype(node, unPrintedReferences=[], bootstrapping = True):
-    code = []
-
-    # Detect if this is bootstrapping or if we are attempting to use userspace...
-    if bootstrapping == False:
-        typeDefs = node.getNamespace().getSubTypesOf() # defaults to TypeDefinition
-        myTypeRef = None
-        for ref in node.getReferences():
-          if ref.referenceType() in typeDefs:
-            myTypeRef = ref
-            break
-        if myTypeRef==None:
-          for ref in node.getReferences():
-            if ref.referenceType().browseName() == "HasSubtype" and ref.isForward() == False:
-              myTypeRef = ref
-              break
-        if myTypeRef==None:
-          logger.warn(str(self) + " failed to locate a type definition, assuming BaseDataType.")
-          code.append("       // No valid typeDefinition found; assuming BaseDataType")
-          code.append("       UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BASEDATATYPE),")
-        else:
-          code.append("       " + getCreateExpandedNodeIDMacro(myTypeRef.target()) + ",")
-          while myTypeRef in unPrintedReferences:
-            unPrintedReferences.remove(myTypeRef)
-
-        code.append("       UA_LOCALIZEDTEXT(\"\",\"" + str(node.inverseName()) + "\"),");
-        code.append("       // FIXME: Missing, isAbstract")
-        code.append("       // FIXME: Missing, symmetric")
-        return code
-
-    if node.isAbstract():
-        code.append(node.getCodePrintableID() + "->isAbstract = true;")
-    if node.symmetric():
-        code.append(node.getCodePrintableID() + "->symmetric  = true;")
-    if node.__reference_inverseName__ != "":
-        code.append(node.getCodePrintableID() + "->inverseName  = UA_LOCALIZEDTEXT_ALLOC(\"en_US\", \"" + \
-                    node.__reference_inverseName__ + "\");")
-    return code;
-
-def ObjectNode_printOpen62541CCode_Subtype(node, unPrintedReferences=[], bootstrapping = True):
-    code = []
-
-    # Detect if this is bootstrapping or if we are attempting to use userspace...
-    if bootstrapping == False:
-      typeDefs = self.getNamespace().getSubTypesOf() # defaults to TypeDefinition
-      myTypeRef = None
-      for ref in self.getReferences():
-        if ref.referenceType() in typeDefs:
-          myTypeRef = ref
-          break
-      if myTypeRef==None:
-        for ref in self.getReferences():
-          if ref.referenceType().browseName() == "HasSubtype" and ref.isForward() == False:
-            myTypeRef = ref
-            break
-      if myTypeRef==None:
-        logger.warn(str(self) + " failed to locate a type definition, assuming BaseObjectType.")
-        code.append("       // No valid typeDefinition found; assuming BaseObjectType")
-        code.append("       UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE),")
-      else:
-        code.append("       " + getCreateExpandedNodeIDMacro(myTypeRef.target()) + ",")
-        while myTypeRef in unPrintedReferences:
-          unPrintedReferences.remove(myTypeRef)
-
-      #FIXME: No event notifier in UA_Server_addNode call!
-      return code
-
-    # We are being bootstrapped! Add the raw attributes to the node.
-    code.append(node.getCodePrintableID() + "->eventNotifier = (UA_Byte) " + str(node.eventNotifier()) + ";")
-    return code
-
-
-def ObjectTypeNode_printOpen62541CCode_Subtype(node, unPrintedReferences=[], bootstrapping = True):
-    code = []
-
-    # Detect if this is bootstrapping or if we are attempting to use userspace...
-    if bootstrapping == False:
-      typeDefs = node.getNamespace().getSubTypesOf() # defaults to TypeDefinition
-      myTypeRef = None
-      for ref in node.getReferences():
-        if ref.referenceType() in typeDefs:
-          myTypeRef = ref
-          break
-      if myTypeRef==None:
-        for ref in node.getReferences():
-          if ref.referenceType().browseName() == "HasSubtype" and ref.isForward() == False:
-            myTypeRef = ref
-            break
-      if myTypeRef==None:
-        logger.warn(str(node) + " failed to locate a type definition, assuming BaseObjectType.")
-        code.append("       // No valid typeDefinition found; assuming BaseObjectType")
-        code.append("       UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE),")
-      else:
-        code.append("       " + getCreateExpandedNodeIDMacro(myTypeRef.target()) + ",")
-        while myTypeRef in unPrintedReferences:
-          code.append("       // removed " + str(myTypeRef))
-          unPrintedReferences.remove(myTypeRef)
-
-      if (node.isAbstract()):
-        code.append("       true,")
-      else:
-        code.append("       false,")
-
-    # Fallback mode for bootstrapping
-    if (node.isAbstract()):
-      code.append(node.getCodePrintableID() + "->isAbstract = true;")
-
-    return code
-
-def VariableNode_printOpen62541CCode_Subtype(node, unPrintedReferences=[], bootstrapping = True):
-    code = []
-
-    # Detect if this is bootstrapping or if we are attempting to use userspace...
-    if bootstrapping == False:
-      code.append("       " + node.getCodePrintableID() + "_variant, ")
-      code.append("       // FIXME: missing minimumSamplingInterval")
-      code.append("       // FIXME: missing accessLevel")
-      code.append("       // FIXME: missing userAccessLevel")
-      code.append("       // FIXME: missing valueRank")
-      return code
-
-    if node.historizing():
-      code.append(node.getCodePrintableID() + "->historizing = true;")
-
-    code.append(node.getCodePrintableID() + "->minimumSamplingInterval = (UA_Double) " + \
-                str(node.minimumSamplingInterval()) + ";")
-    code.append(node.getCodePrintableID() + "->userAccessLevel = (UA_Int32) " + str(node.userAccessLevel()) + ";")
-    code.append(node.getCodePrintableID() + "->accessLevel = (UA_Int32) " + str(node.accessLevel()) + ";")
-    code.append(node.getCodePrintableID() + "->valueRank = (UA_Int32) " + str(node.valueRank()) + ";")
-    # The variant is guaranteed to exist by SubtypeEarly()
-    code.append(node.getCodePrintableID() + "->value.variant.value = *" + node.getCodePrintableID() + "_variant;")
-    code.append(node.getCodePrintableID() + "->valueSource = UA_VALUESOURCE_VARIANT;")
-    return code
-
-def VariableTypeNode_printOpen62541CCode_Subtype(node, unPrintedReferences=[], bootstrapping = True):
-    code = []
-    if bootstrapping == False:
-      code.append("       " + node.getCodePrintableID() + "_variant, ")
-      code.append("       " + str(node.valueRank()) + ",")
-      if node.isAbstract():
-        code.append("       true,")
-      else:
-        code.append("       false,")
-      return code
-
-    if (node.isAbstract()):
-      code.append(node.getCodePrintableID() + "->isAbstract = true;")
-    else:
-      code.append(node.getCodePrintableID() + "->isAbstract = false;")
-
-    # The variant is guaranteed to exist by SubtypeEarly()
-    code.append(node.getCodePrintableID() + "->value.variant.value = *" + node.getCodePrintableID() + "_variant;")
-    code.append(node.getCodePrintableID() + "->valueSource = UA_VALUESOURCE_VARIANT;")
-    return code
-
-def MethodNode_printOpen62541CCode_Subtype(node, unPrintedReferences=[], bootstrapping = True):
-    code = []
-
-    # Detect if this is bootstrapping or if we are attempting to use userspace...
-    if bootstrapping == False:
-      code.append("       // Note: in/outputArguments are added by attaching the variable nodes,")
-      code.append("       //       not by including the in the addMethodNode() call.")
-      code.append("       NULL,")
-      code.append("       NULL,")
-      code.append("       0, NULL,")
-      code.append("       0, NULL,")
-      code.append("       // FIXME: Missing executable")
-      code.append("       // FIXME: Missing userExecutable")
-      return code
-
-    # UA_False is default for booleans on _init()
-    if node.executable():
-      code.append(node.getCodePrintableID() + "->executable = true;")
-    if node.userExecutable():
-      code.append(node.getCodePrintableID() + "->userExecutable = true;")
-    return code
-
-def DataTypeNode_printOpen62541CCode_Subtype(node, unPrintedReferences=[], bootstrapping = True):
-    code = []
-
-    # Detect if this is bootstrapping or if we are attempting to use userspace...
-    if bootstrapping == False:
-      typeDefs = node.getNamespace().getSubTypesOf() # defaults to TypeDefinition
-      myTypeRef = None
-      for ref in node.getReferences():
-        if ref.referenceType() in typeDefs:
-          myTypeRef = ref
-          break
-      if myTypeRef==None:
-        for ref in node.getReferences():
-          if ref.referenceType().browseName() == "HasSubtype" and ref.isForward() == False:
-            myTypeRef = ref
-            break
-      if myTypeRef==None:
-        logger.warn(str(node) + " failed to locate a type definition, assuming BaseDataType.")
-        code.append("       // No valid typeDefinition found; assuming BaseDataType")
-        code.append("       UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BASEDATATYPE),")
-      else:
-        code.append("       " + getCreateExpandedNodeIDMacro(myTypeRef.target()) + ",")
-        while myTypeRef in unPrintedReferences:
-          unPrintedReferences.remove(myTypeRef)
-
-      if (node.isAbstract()):
-        code.append("       true,")
-      else:
-        code.append("       false,")
-      return code
-
-    if (node.isAbstract()):
-      code.append(node.getCodePrintableID() + "->isAbstract = true;")
-    else:
-      code.append(node.getCodePrintableID() + "->isAbstract = false;")
-    return code
-
-def ViewNode_printOpen62541CCode_Subtype(node, unPrintedReferences=[], bootstrapping = True):
-    code = []
-
-    # Detect if this is bootstrapping or if we are attempting to use userspace...
-    if bootstrapping == False:
-      typeDefs = node.getNamespace().getSubTypesOf() # defaults to TypeDefinition
-      myTypeRef = None
-      for ref in node.getReferences():
-        if ref.referenceType() in typeDefs:
-          myTypeRef = ref
-          break
-      if myTypeRef==None:
-        for ref in node.getReferences():
-          if ref.referenceType().browseName() == "HasSubtype" and ref.isForward() == False:
-            myTypeRef = ref
-            break
-      if myTypeRef==None:
-        logger.warn(str(node) + " failed to locate a type definition, assuming BaseViewType.")
-        code.append("       // No valid typeDefinition found; assuming BaseViewType")
-        code.append("       UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BASEViewTYPE),")
-      else:
-        code.append("       " + getCreateExpandedNodeIDMacro(myTypeRef.target()) + ",")
-        while myTypeRef in unPrintedReferences:
-          unPrintedReferences.remove(myTypeRef)
-
-      code.append("       // FIXME: Missing eventNotifier")
-      code.append("       // FIXME: Missing containsNoLoops")
-      return code
-
-    if node.containsNoLoops():
-      code.append(node.getCodePrintableID() + "->containsNoLoops = true;")
-    else:
-      code.append(node.getCodePrintableID() + "->containsNoLoops = false;")
-    code.append(node.getCodePrintableID() + "->eventNotifier = (UA_Byte) " + str(node.eventNotifier()) + ";")
-    return code
-
-def Node_printOpen62541CCode_Subtype(node, unPrintedReferences=[], bootstrapping = True):
-    """ Appends node type specific information to the nodes  UA_Server_addNode
-        or UA_NodeStore_insert calls.
-    """
-
-    if isinstance(node, opcua_node_referenceType_t):
-      return ReferenceTypeNode_printOpen62541CCode_Subtype(node, unPrintedReferences, bootstrapping)
-    elif isinstance(node, opcua_node_object_t):
-      return ObjectNode_printOpen62541CCode_Subtype(node, unPrintedReferences, bootstrapping)
-    elif isinstance(node, opcua_node_objectType_t):
-      return ObjectTypeNode_printOpen62541CCode_Subtype(node, unPrintedReferences, bootstrapping)
-    elif isinstance(node, opcua_node_variable_t):
-      return VariableNode_printOpen62541CCode_Subtype(node, unPrintedReferences, bootstrapping)
-    elif isinstance(node, opcua_node_variableType_t):
-      return VariableTypeNode_printOpen62541CCode_Subtype(node, unPrintedReferences, bootstrapping)
-    elif isinstance(node, opcua_node_method_t):
-      return MethodNode_printOpen62541CCode_Subtype(node, unPrintedReferences, bootstrapping)
-    elif isinstance(node, opcua_node_dataType_t):
-      return DataTypeNode_printOpen62541CCode_Subtype(node, unPrintedReferences, bootstrapping)
-    elif isinstance(node, opcua_node_view_t):
-      return ViewNode_printOpen62541CCode_Subtype(node, unPrintedReferences, bootstrapping)
-    
-    raise Exception('Unknown node type', node)
-
-###############
-# Entry Point #
-###############
-
-def getCreateNodeNoBootstrap(node, parentNode, parentReference, unprintedNodes=[]):
-    code = []
-    code.append("// Node: %s, %s" % (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 *) 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(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 *) 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(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;" % nodetype)
-    code.append("UA_%sAttributes_init(&attr);" %  nodetype);
-    code.append("attr.displayName = UA_LOCALIZEDTEXT(\"\", \"%s\");" % node.displayName().replace("\"", "\\\""))
-    code.append("attr.description = UA_LOCALIZEDTEXT(\"\", \"%s\");" % node.description().replace("\"", "\\\""))
-
-    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.extend(Node_printOpen62541CCode_SubtypeEarly(node, 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 = %s;" % str(getCreateNodeIDMacro(node)))
-    if nodetype in ["Object", "Variable"]:
-      #due to the current API we cannot set types here since the API will
-      #generate nodes with random IDs
-      code.append("UA_NodeId typeDefinition = UA_NODEID_NULL;")
-    code.append("UA_NodeId parentNodeId = %s;" % str(getCreateNodeIDMacro(parentNode)))
-    code.append("UA_NodeId parentReferenceNodeId = %s;" % \
-                str(getCreateNodeIDMacro(parentReference.referenceType())))
-    extrNs = node.browseName().split(":")
-    if len(extrNs) > 1:
-      code.append("UA_QualifiedName nodeName = UA_QUALIFIEDNAME(%s, \"%s\");" % (str(extrNs[0]), extrNs[1]))
-    else:
-      code.append("UA_QualifiedName nodeName = UA_QUALIFIEDNAME(0, \"%s\");" % 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"]:
-      code.append("       , typeDefinition")
-    if nodetype != "Method":
-      code.append("       , attr, NULL, NULL);")
-    else:
-      code.append("       , attr, (UA_MethodCallback) NULL, NULL, %s, inputArguments,  %s, outputArguments, NULL);" % (str(len(inArgVal)), str(len(outArgVal))))
-
-    #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
-
-##################
-# Node Bootstrap #
-##################
-
-def getCreateNodeBootstrap(node, supressGenerationOfAttribute=[]):
-    nodetype = ""
-    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:
-      raise Exception('Undefined NodeClass')
-
-    code.append("UA_" + nodetype + "Node *" + node.getCodePrintableID() + \
-                " = UA_NodeStore_new" + nodetype + "Node();")
-    if not "browsename" in supressGenerationOfAttribute:
-      extrNs = node.browseName().split(":")
-      if len(extrNs) > 1:
-        code.append(node.getCodePrintableID() + "->browseName = UA_QUALIFIEDNAME_ALLOC(" + \
-                    str(extrNs[0]) + ", \"" + extrNs[1] + "\");")
-      else:
-        code.append(node.getCodePrintableID() + "->browseName = UA_QUALIFIEDNAME_ALLOC(0, \"" + \
-                    node.browseName() + "\");")
-    if not "displayname" in supressGenerationOfAttribute:
-      code.append(node.getCodePrintableID() + "->displayName = UA_LOCALIZEDTEXT_ALLOC(\"en_US\", \"" + \
-                  node.displayName() + "\");")
-    if not "description" in supressGenerationOfAttribute:
-      code.append(node.getCodePrintableID() + "->description = UA_LOCALIZEDTEXT_ALLOC(\"en_US\", \"" + \
-                  node.description() + "\");")
-
-    if not "writemask" in supressGenerationOfAttribute:
-        if node.__node_writeMask__ != 0:
-          code.append(node.getCodePrintableID() + "->writeMask = (UA_Int32) " + \
-                      str(node.__node_writeMask__) + ";")
-    if not "userwritemask" in supressGenerationOfAttribute:
-        if node.__node_userWriteMask__ != 0:
-          code.append(node.getCodePrintableID() + "->userWriteMask = (UA_Int32) " + \
-                      str(node.__node_userWriteMask__) + ";")
-    if not "nodeid" in supressGenerationOfAttribute:
-      if node.id().ns != 0:
-        code.append(node.getCodePrintableID() + "->nodeId.namespaceIndex = " + str(node.id().ns) + ";")
-      if node.id().i != None:
-        code.append(node.getCodePrintableID() + "->nodeId.identifier.numeric = " + str(node.id().i) + ";")
-      elif node.id().b != None:
-        code.append(node.getCodePrintableID() + "->nodeId.identifierType = UA_NODEIDTYPE_BYTESTRING;")
-        logger.error("ByteString IDs for nodes has not been implemented yet.")
-        return []
-      elif node.id().g != None:
-        #<jpfr> the string is sth like { .length = 111, .data = <ptr> }
-        #<jpfr> there you _may_ alloc the <ptr> on the heap
-        #<jpfr> for the guid, just set it to {.data1 = 111, .data2 = 2222, ....
-        code.append(node.getCodePrintableID() + "->nodeId.identifierType = UA_NODEIDTYPE_GUID;")
-        logger.error("GUIDs for nodes has not been implemented yet.")
-        return []
-      elif node.id().s != None:
-        code.append(node.getCodePrintableID() + "->nodeId.identifierType = UA_NODEIDTYPE_STRING;")
-        code.append(node.getCodePrintableID() + "->nodeId.identifier.numeric = UA_STRING_ALLOC(\"" + str(node.id().i) + "\");")
-      else:
-        logger.error("Node ID is not numeric, bytestring, guid or string. I do not know how to create c code for that...")
-        return []
-    return code
-
-def Node_printOpen62541CCode(node, unPrintedNodes=[], unPrintedReferences=[],
-                             supressGenerationOfAttribute=[]):
-    """ Returns a list of strings containing the C-code necessary to intialize
-        this node for the open62541 OPC-UA Stack.
-
-        Note that this function will fail if the nodeid is non-numeric, as
-        there is no UA_EXPANDEDNNODEID_[STRING|GUID|BYTESTRING] macro.
-    """
-    code = []
-    code.append("")
-    code.append("do {")
-
-    # Just to be sure...
-    if not (node in unPrintedNodes):
-      logger.warn(str(node) + " attempted to reprint already printed node " + str(node)+ ".")
-      return []
-
-    # If we are being passed a parent node by the namespace, use that for registering ourselves in the namespace
-    # Note: getFirstParentNode will return [parentNode, referenceToChild]
-    (parentNode, parentRef) = node.getFirstParentNode()
-    if not (parentNode in unPrintedNodes) and (parentNode != None) and (parentRef.referenceType() != None):
-      code.append("// Referencing node found and declared as parent: " + str(parentNode .id()) + "/" +
-                  str(parentNode .__node_browseName__) + " using " + str(parentRef.referenceType().id()) +
-                  "/" + str(parentRef.referenceType().__node_browseName__))
-      code.extend(getCreateNodeNoBootstrap(node, parentNode, parentRef, unPrintedNodes))
-      # Parent to child reference is added by the server, do not reprint that reference
-      if parentRef in unPrintedReferences:
-        unPrintedReferences.remove(parentRef)
-      # the UA_Server_addNode function will use addReference which creates a
-      # bidirectional reference; remove any inverse references to our parent to
-      # avoid duplicate refs
-      for ref in node.getReferences():
-        if ref.target() == parentNode and ref.referenceType() == parentRef.referenceType() and \
-           ref.isForward() == False:
-          while ref in unPrintedReferences:
-            unPrintedReferences.remove(ref)
-    # Otherwise use the "Bootstrapping" method and we will get registered with other nodes later.
-    else:
-      code.extend(Node_printOpen62541CCode_SubtypeEarly(node, bootstrapping = True))
-      code.extend(getCreateNodeBootstrap(node, supressGenerationOfAttribute))
-      code.extend(Node_printOpen62541CCode_Subtype(node, unPrintedReferences = unPrintedReferences,
-                                                   bootstrapping = True))
-      code.append("// Parent node does not exist yet. This node will be bootstrapped and linked later.")
-      code.append("UA_RCU_LOCK();")
-      code.append("UA_NodeStore_insert(server->nodestore, (UA_Node*) " + node.getCodePrintableID() + ");")
-      code.append("UA_RCU_UNLOCK();")
-
-    # Try to print all references to nodes that already exist
-    # Note: we know the reference types exist, because the namespace class made sure they were
-    #       the first ones being printed
-    tmprefs = []
-    for r in node.getReferences():
-      #logger.debug("Checking if reference from " + str(r.parent()) + "can be created...")
-      if not (r.target() in unPrintedNodes):
-        if r in unPrintedReferences:
-          if (len(tmprefs) == 0):
-            code.append("// This node has the following references that can be created:")
-          code.extend(getCreateStandaloneReference(node, r))
-          tmprefs.append(r)
-    # Remove printed refs from list
-    for r in tmprefs:
-      unPrintedReferences.remove(r)
-
-    # Again, but this time check if other nodes deffered their node creation
-    # because this node did not exist...
-    tmprefs = []
-    for r in unPrintedReferences:
-      #logger.debug("Checking if another reference " + str(r.target()) + "can be created...")
-      if (r.target() == node) 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 this node has resolved the following open references:")
-          code.extend(getCreateStandaloneReference(r.parent(), r))
-          tmprefs.append(r)
-    # Remove printed refs from list
-    for r in tmprefs:
-      unPrintedReferences.remove(r)
-
-    # Again, just to be sure...
-    if node in unPrintedNodes:
-      # This is necessery to make printing work at all!
-      unPrintedNodes.remove(node)
-
-    code.append("} while(0);")
-    return code
-
-def Node_printOpen62541CCode_HL_API(node, reference, supressGenerationOfAttribute=[]):
-    """ Returns a list of strings containing the C-code necessary to intialize
-        this node for the open62541 OPC-UA Stack using only the high level API
-
-        Note that this function will fail if the nodeid is non-numeric, as
-        there is no UA_EXPANDEDNNODEID_[STRING|GUID|BYTESTRING] macro.
-    """
-    code = []
-    code.append("")
-    code.append("do {")
-    # If we are being passed a parent node by the namespace, use that for registering ourselves in the namespace
-    # Note: getFirstParentNode will return [parentNode, referenceToChild]
-    parentNode = reference.target()
-    parentRefType = reference.referenceType()
-    code.append("// Referencing node found and declared as parent: " + str(parentNode .id()) + "/" +
-                  str(parentNode .__node_browseName__) + " using " + str(parentRefType.id()) +
-                  "/" + str(parentRefType.__node_browseName__))
-    code.extend(getCreateNodeNoBootstrap(node, parentNode, reference))
-    code.append("} while(0);")
-    return code

+ 0 - 317
tools/pyUANamespace/open62541_backend_types.py

@@ -1,317 +0,0 @@
-import * from ua_builtin_types
-import open62541Backend from open62541_MacroHelper
-
-class opcua_value_t_open62541(opcua_value_t):
-  def printOpen62541CCode(self, bootstrapping = True):
-    codegen = open62541Backend()
-    code = []
-    valueName = self.parent.getCodePrintableID() + "_variant_DataContents"
-
-    # self.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(self.value) == 0):
-      return code
-    if not isinstance(self.value[0], opcua_value_t):
-      return code
-
-    if self.parent.valueRank() != -1 and \
-       (self.parent.valueRank() >=0 or (len(self.value) > 1 and \
-                                        (self.parent.valueRank() != -2 or self.parent.valueRank() != -3))):
-      # User the following strategy for all directly mappable values a la 'UA_Type MyInt = (UA_Type) 23;'
-      if self.value[0].__binTypeId__ == BUILTINTYPE_TYPEID_GUID:
-        logger.warn("Don't know how to print array of GUID in node " + str(self.parent.id()))
-      elif self.value[0].__binTypeId__ == BUILTINTYPE_TYPEID_DATETIME:
-        logger.warn("Don't know how to print array of DateTime in node " + str(self.parent.id()))
-      elif self.value[0].__binTypeId__ == BUILTINTYPE_TYPEID_DIAGNOSTICINFO:
-        logger.warn("Don't know how to print array of DiagnosticInfo in node " + str(self.parent.id()))
-      elif self.value[0].__binTypeId__ == BUILTINTYPE_TYPEID_STATUSCODE:
-        logger.warn("Don't know how to print array of StatusCode in node " + str(self.parent.id()))
-      else:
-        if self.value[0].__binTypeId__ == BUILTINTYPE_TYPEID_EXTENSIONOBJECT:
-          for v in self.value:
-            logger.debug("Building extObj array index " + str(self.value.index(v)))
-            code.extend(v.printOpen62541CCode_SubType_build(arrayIndex=self.value.index(v)))
-        #code.append("attr.value.type = &UA_TYPES[UA_TYPES_" + self.value[0].stringRepresentation.upper() + "];")
-        code.append("UA_" + self.value[0].stringRepresentation + " " + valueName + \
-                    "[" + str(len(self.value)) + "];")
-        if self.value[0].__binTypeId__ == BUILTINTYPE_TYPEID_EXTENSIONOBJECT:
-          for v in self.value:
-            logger.debug("Printing extObj array index " + str(self.value.index(v)))
-            code.append(valueName + "[" + str(self.value.index(v)) + "] = " + \
-                        v.printOpen62541CCode_SubType(asIndirect=False) + ";")
-            code.append("UA_free(" + v.printOpen62541CCode_SubType() + ");")
-        else:
-          for v in self.value:
-            code.append(valueName + "[" + str(self.value.index(v)) + "] = " + \
-                        v.printOpen62541CCode_SubType() + ";")
-        code.append("UA_Variant_setArray( &attr.value, &" + valueName +
-                    ", (UA_Int32) " + str(len(self.value)) + ", &UA_TYPES[UA_TYPES_" + \
-                    self.value[0].stringRepresentation.upper() + "]);")
-    else:
-      # User the following strategy for all directly mappable values a la 'UA_Type MyInt = (UA_Type) 23;'
-      if self.value[0].__binTypeId__ == BUILTINTYPE_TYPEID_GUID:
-        logger.warn("Don't know how to print scalar GUID in node " + str(self.parent.id()))
-      elif self.value[0].__binTypeId__ == BUILTINTYPE_TYPEID_DATETIME:
-        logger.warn("Don't know how to print scalar DateTime in node " + str(self.parent.id()))
-      elif self.value[0].__binTypeId__ == BUILTINTYPE_TYPEID_DIAGNOSTICINFO:
-        logger.warn("Don't know how to print scalar DiagnosticInfo in node " + str(self.parent.id()))
-      elif self.value[0].__binTypeId__ == BUILTINTYPE_TYPEID_STATUSCODE:
-        logger.warn("Don't know how to print scalar StatusCode in node " + str(self.parent.id()))
-      else:
-        # The following strategy applies to all other types, in particular strings and numerics.
-        if self.value[0].__binTypeId__ == BUILTINTYPE_TYPEID_EXTENSIONOBJECT:
-          code.extend(self.value[0].printOpen62541CCode_SubType_build())
-        #code.append("attr.value.type = &UA_TYPES[UA_TYPES_" + self.value[0].stringRepresentation.upper() + "];")
-        if self.value[0].__binTypeId__ == BUILTINTYPE_TYPEID_EXTENSIONOBJECT:
-          code.append("UA_" + self.value[0].stringRepresentation + " *" + valueName + " = " + \
-                      self.value[0].printOpen62541CCode_SubType() + ";")
-          code.append("UA_Variant_setScalar( &attr.value, " + valueName + ", &UA_TYPES[UA_TYPES_" + \
-                      self.value[0].stringRepresentation.upper() + "]);")
-
-          #FIXME: There is no membership definition for extensionObjects generated in this function.
-          #code.append("UA_" + self.value[0].stringRepresentation + "_deleteMembers(" + valueName + ");")
-        else:
-          if bootstrapping == True:
-              code.append("UA_Variant* " + self.parent.getCodePrintableID() + "_variant = UA_Variant_new();" )
-          code.append("UA_" + self.value[0].stringRepresentation + " *" + valueName + " =  UA_" + \
-                      self.value[0].stringRepresentation + "_new();")
-          code.append("*" + valueName + " = " + self.value[0].printOpen62541CCode_SubType() + ";")
-          if bootstrapping == False:
-            code.append("UA_Variant_setScalar( &attr.value, " + valueName + ", &UA_TYPES[UA_TYPES_" + \
-                        self.value[0].stringRepresentation.upper() + "]);")
-          else:
-            code.append("UA_Variant_setScalar( "+self.parent.getCodePrintableID()+"_variant, " + \
-                        valueName + ", &UA_TYPES[UA_TYPES_" + self.value[0].stringRepresentation.upper() + "]);")
-          #code.append("UA_" + self.value[0].stringRepresentation + "_deleteMembers(" + valueName + ");")
-    return code
-
-class opcua_BuiltinType_boolean_t_open62541(opcua_BuiltinType_boolean_t):
-  def printOpen62541CCode_SubType(self, asIndirect=True):
-    return "(UA_" + self.stringRepresentation + ") " + str(self.value)
-
-class opcua_BuiltinType_byte_t_open62541(opcua_BuiltinType_byte_t):
-  def printOpen62541CCode_SubType(self, asIndirect=True):
-    return "(UA_" + self.stringRepresentation + ") " + str(self.value)
-
-class opcua_BuiltinType_sbyte_t_open62541(opcua_BuiltinType_sbyte_t):
-  def printOpen62541CCode_SubType(self, asIndirect=True):
-    return "(UA_" + self.stringRepresentation + ") " + str(self.value)
-
-class opcua_BuiltinType_int16_t_open62541(opcua_BuiltinType_int16_t):
-  def printOpen62541CCode_SubType(self, asIndirect=True):
-    return "(UA_" + self.stringRepresentation + ") " + str(self.value)
-
-class opcua_BuiltinType_uint16_t_open62541(opcua_BuiltinType_uint16_t):
-  def printOpen62541CCode_SubType(self, asIndirect=True):
-    return "(UA_" + self.stringRepresentation + ") " + str(self.value)
-
-class opcua_BuiltinType_int32_t_open62541(opcua_BuiltinType_int32_t):
-  def printOpen62541CCode_SubType(self, asIndirect=True):
-    return "(UA_" + self.stringRepresentation + ") " + str(self.value)
-
-class opcua_BuiltinType_uint32_t_open62541(opcua_BuiltinType_uint32_t):
-  def printOpen62541CCode_SubType(self, asIndirect=True):
-    return "(UA_" + self.stringRepresentation + ") " + str(self.value)
-
-class opcua_BuiltinType_int64_t_open62541(opcua_BuiltinType_int64_t):
-  def printOpen62541CCode_SubType(self, asIndirect=True):
-    return "(UA_" + self.stringRepresentation + ") " + str(self.value)
-
-class opcua_BuiltinType_uint64_t_open62541(opcua_BuiltinType_uint64_t):
-  def printOpen62541CCode_SubType(self, asIndirect=True):
-    return "(UA_" + self.stringRepresentation + ") " + str(self.value)
-
-class opcua_BuiltinType_float_t_open62541(opcua_BuiltinType_float_t):
-  def printOpen62541CCode_SubType(self, asIndirect=True):
-    return "(UA_" + self.stringRepresentation + ") " + str(self.value)
-
-class opcua_BuiltinType_double_t_open62541(opcua_BuiltinType_double_t):
-  def printOpen62541CCode_SubType(self, asIndirect=True):
-    return "(UA_" + self.stringRepresentation + ") " + str(self.value)
-
-class opcua_BuiltinType_string_t_open62541(opcua_BuiltinType_string_t):
-  def printOpen62541CCode_SubType(self, asIndirect=True):
-      code = "UA_STRING_ALLOC(\"" + self.value.encode('utf-8') + "\")"
-      return code
-
-class opcua_BuiltinType_xmlelement_t_open62541(opcua_BuiltinType_xmlelement_t):
-  def printOpen62541CCode_SubType(self, asIndirect=True):
-      code = "UA_XMLELEMENT_ALLOC(\"" + self.value.encode('utf-8') + "\")"
-      return code
-
-class opcua_BuiltinType_bytestring_t_open62541(opcua_BuiltinType_bytestring_t):
-  def printOpen62541CCode_SubType(self, asIndirect=True):
-      bs = ""
-      for line in self.value:
-        bs = bs + str(line).replace("\n","");
-      outs = bs
-      logger.debug("Encoded Bytestring: " + outs)
-#      bs = bs.decode('base64')
-#      outs = ""
-#      for s in bs:
-#        outs = outs + hex(ord(s)).upper().replace("0X", "\\x")
-      code = "UA_STRING_ALLOC(\"" + outs + "\")"
-      return code
-
-class opcua_BuiltinType_localizedtext_open62541(opcua_BuiltinType_localizedtext_t):
-  def printOpen62541CCode_SubType(self, asIndirect=True):
-      if asIndirect==True:
-        return "UA_LOCALIZEDTEXT_ALLOC(\"" + str(self.value[0]) + "\", \"" + \
-            str(self.value[1].encode('utf-8')) + "\")"
-      else:
-        return "UA_LOCALIZEDTEXT(\"" + str(self.value[0]) + "\", \"" + \
-            str(self.value[1].encode('utf-8')) + "\")"
-
-class opcua_BuiltinType_qualifiedname_t_open62541(opcua_BuiltinType_qualifiedname_t):
-  def printOpen62541CCode_SubType(self, asIndirect=True):
-      code = "UA_QUALIFIEDNAME_ALLOC(" + str(self.value[0]) + ", \"" + self.value[1].encode('utf-8') + "\")"
-      return code
-
-class opcua_BuiltinType_nodeid_t_open62541(opcua_BuiltinType_nodeid_t):
-  def printOpen62541CCode_SubType(self, asIndirect=True):
-    if self.value == None:
-      return "UA_NODEID_NUMERIC(0,0)"
-    nodeId = self.value.id()
-    if nodeId.i != None:
-      return "UA_NODEID_NUMERIC(" + str(nodeId.ns) + ", " + str(nodeId.i) + ")"
-    elif nodeId.s != None:
-      return "UA_NODEID_STRING("  + str(nodeId.ns) + ", " + str(nodeId.s) + ")"
-    elif nodeId.b != None:
-      logger.debug("NodeID Generation macro for bytestrings has not been implemented.")
-      return "UA_NODEID_NUMERIC(0,0)"
-    elif nodeId.g != None:
-      logger.debug("NodeID Generation macro for guids has not been implemented.")
-      return "UA_NODEID_NUMERIC(0,0)"
-    return "UA_NODEID_NUMERIC(0,0)"
-
-class opcua_BuiltinType_expandednodeid_t_open62541(opcua_BuiltinType_expandednodeid_t):
-  def printOpen62541CCode_SubType(self, asIndirect=True):
-    #FIXME! This one is definetely broken!
-    code = ""
-    return code
-
-class opcua_BuiltinType_extensionObject_open62541(opcua_BuiltinType_extensionObject_t):
-  def printOpen62541CCode_SubType(self, asIndirect=True):
-    if asIndirect == False:
-      return "*" + str(self.getCodeInstanceName())
-    return str(self.getCodeInstanceName())
-
-  def printOpen62541CCode_SubType_build(self, recursionDepth=0, arrayIndex=0):
-    code = [""]
-    codegen = open62541_MacroHelper();
-
-    logger.debug("Building extensionObject for " + str(self.parent.id()))
-    logger.debug("Value    " + str(self.value))
-    logger.debug("Encoding " + str(self.getEncodingRule()))
-
-    self.setCodeInstanceName(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 self.value:
-      if isinstance(subv, list):
-        logger.debug("ExtensionObject contains an ExtensionObject, which is currently not encodable!",
-                     LOG_LEVEL_ERR)
-
-    code.append("struct {")
-    for field in self.getEncodingRule():
-      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("} " + self.getCodeInstanceName() + "_struct;")
-
-    # Assign data to the struct contents
-    # Track the encoding rule definition to detect arrays and/or ExtensionObjects
-    encFieldIdx = 0
-    for subv in self.value:
-      encField = self.getEncodingRule()[encFieldIdx]
-      encFieldIdx = encFieldIdx + 1;
-      logger.debug("Encoding of field " + subv.alias() + " is " + str(subv.getEncodingRule()) + \
-                   "defined by " + str(encField))
-      # Check if this is an array
-      if encField[2] == 0:
-        code.append(self.getCodeInstanceName()+"_struct."+subv.alias() + " = " + \
-                    subv.printOpen62541CCode_SubType(asIndirect=False) + ";")
-      else:
-        if isinstance(subv, list):
-          # this is an array
-          code.append(self.getCodeInstanceName()+"_struct."+subv.alias() + "Size = " + str(len(subv)) + ";")
-          code.append(self.getCodeInstanceName()+"_struct."+subv.alias()+" = (UA_" + \
-                      subv.stringRepresentation + " *) UA_malloc(sizeof(UA_" + subv.stringRepresentation + \
-                      ")*"+ str(len(subv))+");")
-          logger.debug("Encoding included array of " + str(len(subv)) + " values.")
-          for subvidx in range(0,len(subv)):
-            subvv = subv[subvidx]
-            logger.debug("  " + str(subvix) + " " + str(subvv))
-            code.append(self.getCodeInstanceName()+"_struct."+subv.alias() + "[" + str(subvidx) + \
-                        "] = " + subvv.printOpen62541CCode_SubType(asIndirect=True) + ";")
-          code.append("}")
-        else:
-          code.append(self.getCodeInstanceName()+"_struct."+subv.alias() + "Size = 1;")
-          code.append(self.getCodeInstanceName()+"_struct."+subv.alias()+" = (UA_" + \
-                      subv.stringRepresentation + " *) UA_malloc(sizeof(UA_" + subv.stringRepresentation + "));")
-          code.append(self.getCodeInstanceName()+"_struct."+subv.alias() + "[0]  = " + \
-                      subv.printOpen62541CCode_SubType(asIndirect=True) + ";")
-
-
-    # Allocate some memory
-    code.append("UA_ExtensionObject *" + self.getCodeInstanceName() + " =  UA_ExtensionObject_new();")
-    code.append(self.getCodeInstanceName() + "->encoding = UA_EXTENSIONOBJECT_ENCODED_BYTESTRING;")
-    code.append(self.getCodeInstanceName() + "->content.encoded.typeId = UA_NODEID_NUMERIC(" + \
-                str(self.parent.dataType().target().id().ns) + ", " + \
-                str(self.parent.dataType().target().id().i) + "+ UA_ENCODINGOFFSET_BINARY);")
-    code.append("if(UA_ByteString_allocBuffer(&" + self.getCodeInstanceName() + \
-                "->content.encoded.body, 65000) != UA_STATUSCODE_GOOD) {}" )
-
-    # Encode each value as a bytestring seperately.
-    code.append("size_t " + self.getCodeInstanceName() + "_encOffset = 0;" )
-    encFieldIdx = 0;
-    for subv in self.value:
-      encField = self.getEncodingRule()[encFieldIdx]
-      encFieldIdx = encFieldIdx + 1;
-      if encField[2] == 0:
-        code.append("UA_" + subv.stringRepresentation + "_encodeBinary(&" + \
-                    self.getCodeInstanceName()+"_struct."+subv.alias() + ", &" + \
-                    self.getCodeInstanceName() + "->content.encoded.body, &" + \
-                    self.getCodeInstanceName() + "_encOffset);" )
-      else:
-        if isinstance(subv, list):
-          for subvidx in range(0,len(subv)):
-            code.append("UA_" + subv.stringRepresentation + "_encodeBinary(&" + \
-                        self.getCodeInstanceName()+"_struct."+subv.alias() + \
-                        "[" + str(subvidx) + "], &" + self.getCodeInstanceName() + \
-                        "->content.encoded.body, &" + self.getCodeInstanceName() + "_encOffset);" )
-        else:
-          code.append("UA_" + subv.stringRepresentation + "_encodeBinary(&" + \
-                      self.getCodeInstanceName()+"_struct."+subv.alias() + "[0], &" + \
-                      self.getCodeInstanceName() + "->content.encoded.body, &" + \
-                      self.getCodeInstanceName() + "_encOffset);" )
-
-    # Reallocate the memory by swapping the 65k Bytestring for a new one
-    code.append(self.getCodeInstanceName() + "->content.encoded.body.length = " + \
-                self.getCodeInstanceName() + "_encOffset;");
-    code.append("UA_Byte *" + self.getCodeInstanceName() + "_newBody = (UA_Byte *) UA_malloc(" + \
-                self.getCodeInstanceName() + "_encOffset );" )
-    code.append("memcpy(" + self.getCodeInstanceName() + "_newBody, " + self.getCodeInstanceName() + \
-                "->content.encoded.body.data, " + self.getCodeInstanceName() + "_encOffset);" )
-    code.append("UA_Byte *" + self.getCodeInstanceName() + "_oldBody = " + \
-                self.getCodeInstanceName() + "->content.encoded.body.data;");
-    code.append(self.getCodeInstanceName() + "->content.encoded.body.data = " + \
-                self.getCodeInstanceName() + "_newBody;")
-    code.append("UA_free(" + self.getCodeInstanceName() + "_oldBody);")
-    code.append("")
-    return code

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


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


+ 0 - 542
tools/pyUANamespace/ua_nodeset.py

@@ -1,542 +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 struct import pack as structpack
-from collections import deque
-from time import struct_time, strftime, strptime, mktime
-import logging; logger = logging.getLogger(__name__)
-
-from ua_builtin_types import *;
-from ua_node_types import *;
-from ua_constants import *;
-
-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
-
-class NodeSet():
-  """ 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.
-    """
-    return next((n for n in self.nodes if idstring==str(n.browseName())), None)
-
-  def getNodeByIDString(self, idstring):
-    """ Returns the first node in the nodelist whose id string representation
-        matches idstring.
-    """
-    return next((n for n in self.nodes if idstring==str(n.id())), None)
-
-  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 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 getSubTypesOf2(self, node):
-    re = [node]
-    for ref in node.getReferences():
-      if isinstance(ref.target(), opcua_node_t):
-        if ref.referenceType().displayName() == "HasSubtype" and ref.isForward():
-          re = re + self.getSubTypesOf2(ref.target())
-    return re
-
-  def reorderNodesMinDependencies(self, printedExternally):
-    #Kahn's algorithm
-    #https://algocoding.wordpress.com/2015/04/05/topological-sorting-python/
-    
-    relevant_types = ["HierarchicalReferences", "HasComponent"]
-    
-    temp = []
-    for t in relevant_types:
-        temp = temp + self.getSubTypesOf2(self.getNodeByBrowseName(t))
-    relevant_types = temp
-
-    in_degree = { u : 0 for u in self.nodes }     # determine in-degree
-    for u in self.nodes: # of each node
-      if u not in printedExternally:
-        for ref in u.getReferences():
-         if isinstance(ref.target(), opcua_node_t):
-           if(ref.referenceType() in relevant_types and ref.isForward()):
-             in_degree[ref.target()] += 1
-
-    Q = deque()                 # collect nodes with zero in-degree
-    for u in in_degree:
-      if in_degree[u] == 0:
-        Q.appendleft(u)
-
-    L = []     # list for order of nodes
-
-    while Q:
-      u = Q.pop()          # choose node of zero in-degree
-      L.append(u)          # and 'remove' it from graph
-      for ref in u.getReferences():
-       if isinstance(ref.target(), opcua_node_t):
-         if(ref.referenceType() in relevant_types and ref.isForward()):
-           in_degree[ref.target()] -= 1
-           if in_degree[ref.target()] == 0:
-             Q.appendleft(ref.target())
-    if len(L) == len(self.nodes):
-        self.nodes = L
-    else:                    # if there is a cycle,
-        logger.error("Node graph is circular on the specified references")
-        self.nodes = L + [x for x in self.nodes if x not in L]
-    return

+ 5 - 0
tools/schema/datatypes_minimal.txt

@@ -178,4 +178,9 @@ SimpleAttributeOperand
 EventNotificationList
 EventFieldList
 StatusChangeNotification
+ServerDiagnosticsSummaryDataType
+SubscriptionDiagnosticsDataType
+SessionDiagnosticsDataType
+ServiceCounterDataType
+SessionSecurityDiagnosticsDataType
 Duration

File diff suppressed because it is too large
+ 4423 - 327
tools/schema/namespace0/Opc.Ua.NodeSet2.Minimal.xml