mirror of
https://github.com/open62541/open62541.git
synced 2025-06-03 04:00:21 +00:00

* drop python<=3.7 support Filter all python code over `pyupgrade --py38-plus`. Signed-off-by: Tomasz Kłoczko <kloczek@github.com> * filter all code over `ruff` as well Signed-off-by: Tomasz Kłoczko <kloczek@github.com> --------- Signed-off-by: Tomasz Kłoczko <kloczek@github.com>
161 lines
6.5 KiB
Python
161 lines
6.5 KiB
Python
from nodeset import *
|
|
import graphviz as gv
|
|
import codecs
|
|
|
|
def nodePrintDot(node):
|
|
cleanname = "node_" + str(node.id).replace(";", "").replace("=", "")
|
|
dot = cleanname + " [label = \"{" + str(node.id) + "|" + str(node.browseName) + \
|
|
"}\", shape=\"record\"]"
|
|
for r in node.references:
|
|
if isinstance(r.target, Node):
|
|
tgtname = "node_" + str(r.target.id).replace(";", "").replace("=", "")
|
|
dot = dot + "\n"
|
|
if r.isForward == True:
|
|
dot = dot + cleanname + " -> " + tgtname + " [label=\"" + \
|
|
str(r.referenceType.browseName) + "\"]\n"
|
|
else:
|
|
if len(r.referenceType.inverseName) == 0:
|
|
logger.warn("Inverse name of reference is null " + str(r.referenceType.id))
|
|
dot = dot + cleanname + " -> " + tgtname + \
|
|
" [label=\"" + str(r.referenceType.inverseName) + "\"]\n"
|
|
return dot
|
|
|
|
def printDotGraphWalk(nodeset, depth=1, filename="out.dot", rootNode=None,
|
|
followInverse=False, excludeNodeIds=[]):
|
|
""" Outputs a graphiz/dot description the nodes centered around rootNode.
|
|
|
|
References beginning from rootNode will be followed for depth steps. If
|
|
"followInverse = True" is passed, then inverse (not Forward) references
|
|
will also be followed.
|
|
|
|
Nodes can be excluded from the graph by passing a list of NodeIds as
|
|
string representation using excludeNodeIds (ex ["i=53", "ns=2;i=453"]).
|
|
|
|
Output is written into filename to be parsed by dot/neato/srfp...
|
|
"""
|
|
iter = depth
|
|
processed = []
|
|
if rootNode is None or not isinstance(rootNode, Node) or rootNode not in nodeset.nodes:
|
|
root = nodeset.getRoot()
|
|
else:
|
|
root = rootNode
|
|
|
|
file = codecs.open(filename, 'w+', encoding='utf-8')
|
|
|
|
if root is None:
|
|
return
|
|
|
|
file.write("digraph ns {\n")
|
|
file.write(nodePrintDot(root))
|
|
refs = []
|
|
if followInverse == True:
|
|
refs = root.references # + root.getInverseReferences()
|
|
else:
|
|
for ref in root.references:
|
|
if ref.isForward:
|
|
refs.append(ref)
|
|
while iter > 0:
|
|
tmp = []
|
|
for ref in refs:
|
|
if isinstance(ref.target, NodeId):
|
|
tgt = nodeset.nodes[ref.target]
|
|
if str(tgt.id) not in excludeNodeIds:
|
|
if tgt not in processed:
|
|
file.write(nodePrintDot(tgt))
|
|
processed.append(tgt)
|
|
if ref.isForward is False and followInverse is True:
|
|
for inverseRef in tgt.inverseReferences:
|
|
refs.append(inverseRef)
|
|
tmp = tmp + tgt.references # + tgt.getInverseReferences()
|
|
elif ref.isForward:
|
|
for targetRef in tgt.references:
|
|
refs.append(targetRef)
|
|
refs = tmp
|
|
iter = iter - 1
|
|
|
|
file.write("}\n")
|
|
file.close()
|
|
|
|
def getNodeString(node):
|
|
return node.browseName.name + " (" + str(node.id) + ")"
|
|
|
|
def getReferenceString(nodeset, ref):
|
|
refNode = nodeset.nodes[ref.referenceType]
|
|
return refNode.browseName.name
|
|
|
|
def getNodeStyle(node):
|
|
if isinstance(node, ReferenceTypeNode):
|
|
return {'shape': 'box', 'style': 'filled', 'fillcolor': '1', 'colorscheme': "pastel19"}
|
|
if isinstance(node, VariableTypeNode):
|
|
return {'shape': 'box', 'style': 'filled', 'fillcolor': '2', 'colorscheme': "pastel19"}
|
|
if isinstance(node, ObjectTypeNode):
|
|
return {'shape': 'box', 'style': 'filled', 'fillcolor': '3', 'colorscheme': "pastel19"}
|
|
if isinstance(node, DataTypeNode):
|
|
return {'shape': 'box', 'style': 'filled', 'fillcolor': '4', 'colorscheme': "pastel19"}
|
|
if isinstance(node, VariableNode):
|
|
return {'shape': 'ellipse', 'style': 'rounded,filled', 'fillcolor': '5', 'colorscheme': "pastel19"}
|
|
if isinstance(node, ObjectNode):
|
|
return {'shape': 'box', 'style': 'rounded,filled', 'fillcolor': '6', 'colorscheme': "pastel19"}
|
|
if isinstance(node, MethodNode):
|
|
return {'shape': 'box', 'style': 'rounded,filled', 'fillcolor': '7', 'colorscheme': "pastel19"}
|
|
if isinstance(node, ViewNode):
|
|
return {'shape': 'box', 'style': 'rounded,filled', 'fillcolor': '8', 'colorscheme': "pastel19"}
|
|
|
|
def add_edges(graph, edges):
|
|
for e in edges:
|
|
if isinstance(e[0], tuple):
|
|
graph.edge(*e[0], **e[1])
|
|
else:
|
|
graph.edge(*e)
|
|
return graph
|
|
|
|
def add_nodes(graph, nodes):
|
|
for n in nodes:
|
|
if isinstance(n, tuple):
|
|
graph.node(n[0], **n[1])
|
|
else:
|
|
graph.node(n)
|
|
return graph
|
|
|
|
def addReferenceToGraph(nodeset, nodeFrom, nodeTo, reference, graph):
|
|
add_edges(graph, [((getNodeString(nodeFrom), getNodeString(nodeTo)), {'label': getReferenceString(nodeset, reference)})])
|
|
|
|
|
|
def addNodeToGraph(nodeset, node, graph, alreadyAdded=set(), relevantReferences=set(), ignoreNodes=set(), isRoot=False, depth = 0):
|
|
if node.id in alreadyAdded or node.id in ignoreNodes:
|
|
return
|
|
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]
|
|
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 generateGraphvizCode(nodeset, filename="dependencies", rootNode=None, excludeNodeIds=[]):
|
|
if rootNode is None or not isinstance(rootNode, Node) or rootNode not in nodeset.nodes:
|
|
root = nodeset.getRoot()
|
|
else:
|
|
root = rootNode
|
|
|
|
if root is None:
|
|
return
|
|
|
|
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)
|