Prechádzať zdrojové kódy

NodesetCompiler: Support multiple backends on command line and fix graphviz

Stefan Profanter 5 rokov pred
rodič
commit
9ace34e2af

+ 19 - 9
tools/nodeset_compiler/backend_graphviz.py

@@ -121,19 +121,22 @@ def addReferenceToGraph(nodeset, nodeFrom, nodeTo, reference, graph):
     add_edges(graph, [((getNodeString(nodeFrom), getNodeString(nodeTo)), {'label': getReferenceString(nodeset, reference)})])
 
 
-def addNodeToGraph(nodeset, node, graph, alreadyAdded=[], relevantReferences=[], isRoot=False, depth = 0):
-    if node.id in alreadyAdded:
+def addNodeToGraph(nodeset, node, graph, alreadyAdded=set(), relevantReferences=set(), ignoreNodes=set(), isRoot=False, depth = 0):
+    if node.id in alreadyAdded or node.id in ignoreNodes:
         return
-    alreadyAdded.append(node.id)
+    alreadyAdded.add(node.id)
     add_nodes(graph, [(getNodeString(node), getNodeStyle(node))])
     for ref in node.references:
         if ref.referenceType in relevantReferences and ref.isForward:
             targetNode = nodeset.nodes[ref.target]
-            addNodeToGraph(nodeset, targetNode, graph, alreadyAdded, depth=depth+1, relevantReferences=relevantReferences)
+            if targetNode.id in ignoreNodes:
+                continue
+            addNodeToGraph(nodeset, targetNode, graph, alreadyAdded, depth=depth+1, relevantReferences=relevantReferences,
+                           ignoreNodes = ignoreNodes)
             addReferenceToGraph(nodeset, node, targetNode, ref, graph)
 
 
-def printDependencyGraph(nodeset, filename="dependencies", rootNode=None, excludeNodeIds=[]):
+def generateGraphvizCode(nodeset, filename="dependencies", rootNode=None, excludeNodeIds=[]):
     if rootNode == None or not isinstance(rootNode, Node) or not rootNode in nodeset.nodes:
         root = nodeset.getRoot()
     else:
@@ -142,9 +145,16 @@ def printDependencyGraph(nodeset, filename="dependencies", rootNode=None, exclud
     if root == None:
         return
 
-    g = gv.Digraph(name="NodeSet Dependency", format='pdf')
-
-    alreadyAdded = []
-    addNodeToGraph(nodeset, root, g, alreadyAdded, isRoot=True, relevantReferences=nodeset.getRelevantOrderingReferences())
+    g = gv.dot.Digraph(name="NodeSet Dependency", format='pdf', )
+
+    alreadyAdded = set()
+    ignoreNodes = set()
+    # Ignore some nodes since almost all nodes will point to that which messes up the graph
+    ignoreNodes.add(NodeId("i=68")) # PropertyType
+    ignoreNodes.add(NodeId("i=63")) # BaseDataVariableType
+    ignoreNodes.add(NodeId("i=61")) # FolderType
+    addNodeToGraph(nodeset, root, g, alreadyAdded, isRoot=True,
+                   relevantReferences=nodeset.getRelevantOrderingReferences(),
+                   ignoreNodes=ignoreNodes)
 
     g.render(filename)

+ 0 - 7
tools/nodeset_compiler/backend_open62541.py

@@ -37,13 +37,6 @@ from backend_open62541_nodes import generateNodeCode_begin, generateNodeCode_fin
 
 # Kahn's algorithm: https://algocoding.wordpress.com/2015/04/05/topological-sorting-python/
 def sortNodes(nodeset):
-
-    # Ensure that every reference has an inverse reference in the target
-    for u in nodeset.nodes.values():
-        for ref in u.references:
-            back = Reference(ref.target, ref.referenceType, ref.source, not ref.isForward)
-            nodeset.nodes[ref.target].references.add(back) # ref set does not make a duplicate entry
-
     # reverse hastypedefinition references to treat only forward references
     hasTypeDef = NodeId("ns=0;i=40")
     for u in nodeset.nodes.values():

+ 21 - 6
tools/nodeset_compiler/nodeset.py

@@ -40,10 +40,17 @@ hassubtype = NodeId("ns=0;i=45")
 def getSubTypesOf(nodeset, node, skipNodes=[]):
     if node in skipNodes:
         return []
-    re = [node]
+    re = set()
+    re.add(node)
     for ref in node.references:
-        if ref.referenceType == hassubtype and ref.isForward:
-            re = re + getSubTypesOf(nodeset, nodeset.nodes[ref.target], skipNodes=skipNodes)
+        if (ref.referenceType == hassubtype):
+            skipAll = set()
+            skipAll.update(skipNodes)
+            skipAll.update(re)
+            if (ref.source == node.id and ref.isForward):
+                re.update(getSubTypesOf(nodeset, nodeset.nodes[ref.target], skipNodes=skipAll))
+            elif (ref.target == node.id and not ref.isForward):
+                re.update(getSubTypesOf(nodeset, nodeset.nodes[ref.source], skipNodes=skipAll))
     return re
 
 def extractNamespaces(xmlfile):
@@ -327,7 +334,15 @@ class NodeSet(object):
         return None
 
     def getRelevantOrderingReferences(self):
-        relevant_types = getSubTypesOf(self, self.getNodeByBrowseName("HierarchicalReferences"), [])
-        relevant_types += getSubTypesOf(self, self.getNodeByBrowseName("HasEncoding"), [])
-        relevant_types += getSubTypesOf(self, self.getNodeByBrowseName("HasTypeDefinition"), [])
+        relevant_types = set()
+        relevant_types.update(getSubTypesOf(self, self.getNodeByBrowseName("HierarchicalReferences"), []))
+        relevant_types.update(getSubTypesOf(self, self.getNodeByBrowseName("HasEncoding"), []))
+        relevant_types.update(getSubTypesOf(self, self.getNodeByBrowseName("HasTypeDefinition"), []))
         return list(map(lambda x: x.id, relevant_types))
+
+    def addInverseReferences(self):
+        # Ensure that every reference has an inverse reference in the target
+        for u in self.nodes.values():
+            for ref in u.references:
+                back = Reference(ref.target, ref.referenceType, ref.source, not ref.isForward)
+                self.nodes[ref.target].references.add(back) # ref set does not make a duplicate entry

+ 22 - 5
tools/nodeset_compiler/nodeset_compiler.py

@@ -25,7 +25,6 @@
 import logging
 import argparse
 from nodeset import *
-from backend_open62541 import generateOpen62541Code
 
 parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter)
 parser.add_argument('-e', '--existing',
@@ -92,6 +91,13 @@ parser.add_argument('-v', '--verbose', action='count',
                     default=1,
                     help='Make the script more verbose. Can be applied up to 4 times')
 
+parser.add_argument('--backend',
+                    default='open62541',
+                    const='open62541',
+                    nargs='?',
+                    choices=['open62541', 'graphviz'],
+                    help='Backend for the output files (default: %(default)s)')
+
 args = parser.parse_args()
 
 # Set up logging
@@ -178,9 +184,20 @@ ns.buildEncodingRules()
 # buidEncodingRules.
 ns.allocateVariables()
 
-#printDependencyGraph(ns)
+ns.addInverseReferences()
+
+logger.info("Generating Code for Backend: {}".format(args.backend))
+
+if args.backend == "open62541":
+    # Create the C code with the open62541 backend of the compiler
+    from backend_open62541 import generateOpen62541Code
+    generateOpen62541Code(ns, args.outputFile, args.generate_ns0, args.internal_headers, args.typesArray, args.encode_binary_size)
+elif args.backend == "graphviz":
+    from backend_graphviz import generateGraphvizCode
+    generateGraphvizCode(ns, filename=args.outputFile)
+else:
+    logger.error("Unsupported backend: {}".format(args.backend))
+    exit(1)
+
 
-# Create the C code with the open62541 backend of the compiler
-logger.info("Generating Code")
-generateOpen62541Code(ns, args.outputFile, args.generate_ns0, args.internal_headers, args.typesArray, args.encode_binary_size)
 logger.info("NodeSet generation code successfully printed")