yaml2dot.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. #!/usr/bin/env python
  2. #-*- coding: utf-8 -*-
  3. import sys
  4. import os
  5. import argparse
  6. import subprocess
  7. import yaml
  8. import pygraphviz
  9. from datetime import datetime
  10. def get_rospack():
  11. cwd = os.getcwd()
  12. lines = subprocess.check_output(['rospack', 'list']).strip().split("\n")
  13. pkgs = {}
  14. for line in lines:
  15. n = line.split(' ')
  16. f = n[1] + os.sep + 'interface.yaml'
  17. if cwd in n[1] and os.path.exists(f):
  18. pkgs[f] = n[0] # key:path value:name
  19. return pkgs
  20. def get_cluster(name, clusters):
  21. for n in clusters:
  22. if name in clusters[n]:
  23. return n
  24. return ""
  25. def check_topics(topics, pkg, data):
  26. for t in data:
  27. if t in topics:
  28. if pkg not in topics[t]:
  29. topics[t].append(pkg)
  30. else:
  31. topics[t] = [pkg]
  32. def get_topic_info(conf):
  33. ret = [{}, {}] # publish, subscribe
  34. for pname in conf:
  35. pkg = conf[pname]
  36. if not 'nodes' in pkg:
  37. continue
  38. name = pkg['name']
  39. for node in pkg['nodes']:
  40. if 'publish' in node:
  41. check_topics(ret[0], name, node['publish'])
  42. if 'subscribe' in node:
  43. check_topics(ret[1], name, node['subscribe'])
  44. return ret
  45. def is_inner_topic(topic, topics):
  46. return topic in topics[0] and len(topics[0][topic]) == 1 and \
  47. topic in topics[1] and len(topics[1][topic]) == 1
  48. def is_one_leaf(topic, topics):
  49. len0 = len(topics[0][topic]) if topic in topics[0] else 0
  50. len1 = len(topics[1][topic]) if topic in topics[1] else 0
  51. return len0 + len1 == 1
  52. if __name__ == "__main__":
  53. # parse args
  54. parser = argparse.ArgumentParser(description='Convert YAML into DOT')
  55. parser.add_argument('input_file', nargs='*', type=str,
  56. help='input yaml files')
  57. parser.add_argument('-f', '--format', default='pdf', type=str,
  58. help='output format')
  59. ofile = os.path.expanduser(
  60. datetime.now().strftime('~/.autoware/autoware-graph-%Y%m%d.pdf'))
  61. parser.add_argument('-o', '--output', default=ofile,
  62. type=argparse.FileType('w'), help='output file')
  63. parser.add_argument('-d', '--detail', const=True, default=False, nargs='?',
  64. help='show edges between nodes')
  65. args = parser.parse_args()
  66. # initialize input files
  67. if len(args.input_file) == 0:
  68. pkgs = get_rospack()
  69. fargs = pkgs.keys()
  70. else:
  71. pkgs = {}
  72. fargs = args.input_file
  73. # initialize variables
  74. graph0 = pygraphviz.AGraph(directed=True, rankdir='LR', compound=True)
  75. graph_nr = 0
  76. node_nr = 0
  77. # read yaml files
  78. conf = {}
  79. for rfile in fargs:
  80. cname = "cluster%d" % graph_nr
  81. pkg = pkgs[rfile] if rfile in pkgs else cname
  82. nodes = yaml.load(file(rfile))
  83. conf[pkg] = {}
  84. conf[pkg]['file'] = rfile
  85. conf[pkg]['name'] = cname
  86. conf[pkg]['nodes'] = nodes
  87. graph_nr += 1
  88. topics = get_topic_info(conf)
  89. # create graph
  90. for pname in conf:
  91. pkg = conf[pname]
  92. rfile = pkg['file']
  93. cname = pkg['name']
  94. graph = graph0.add_subgraph(name=cname)
  95. if rfile in pkgs:
  96. graph.graph_attr['label'] = '(' + pkgs[rfile] + ')'
  97. graph.graph_attr['color'] = 'grey'
  98. n0 = None
  99. for node in pkg['nodes']:
  100. if n0 is None:
  101. n0 = node['name']
  102. graph.add_node(node['name'], shape='ellipse')
  103. node_nr += 1
  104. if 'publish' in node:
  105. for pub in node['publish']:
  106. if is_inner_topic(pub, topics):
  107. if not graph.has_node(pub):
  108. graph.add_node(pub, shape='box')
  109. graph.add_edge(node['name'], pub)
  110. elif args.detail or not is_one_leaf(pub, topics):
  111. if not graph0.has_node(pub):
  112. graph0.add_node(pub, shape='box')
  113. if args.detail:
  114. graph0.add_edge(node['name'], pub)
  115. else:
  116. if not graph0.has_edge(n0, pub):
  117. graph0.add_edge(n0, pub, ltail=cname)
  118. if 'subscribe' in node:
  119. for sub in node['subscribe']:
  120. if is_inner_topic(sub, topics):
  121. if not graph.has_node(sub):
  122. graph.add_node(sub, shape='box')
  123. graph.add_edge(sub, node['name'])
  124. elif args.detail or not is_one_leaf(sub, topics):
  125. if not graph0.has_node(sub):
  126. graph0.add_node(sub, shape='box')
  127. if args.detail:
  128. graph0.add_edge(sub, node['name'])
  129. else:
  130. if not graph0.has_edge(sub, n0):
  131. graph0.add_edge(sub, n0, lhead=cname)
  132. # output
  133. print "# output", args.output
  134. print "# format", args.format
  135. graph0.draw(path=args.output, format=args.format, prog='dot')
  136. #print "## ", reduce(lambda a,b: a + "\n### " + b, sorted(graph.nodes()))
  137. print "# total file#=%d node#=%d topic#=%d" % \
  138. (graph_nr, node_nr, len(set(reduce(lambda m,n:m.keys()+n.keys(), topics))))