topologyTool.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. import os.path
  2. import pickle
  3. from optparse import OptionParser
  4. from lxml import etree
  5. """
  6. This script is meant to serve as "sanity" checker for the MagicDraw generated topology XML files
  7. """
  8. def setup_opt_parse():
  9. usage = "usage: %prog [options] [topology_filename]"
  10. parser = OptionParser(usage)
  11. parser.add_option(
  12. "-d",
  13. "--xml_diff",
  14. dest="xml_diff_flag",
  15. help="Performs a diff on the specified XML files.",
  16. action="store_true",
  17. default=False,
  18. )
  19. parser.add_option(
  20. "-c",
  21. "--command_index_print",
  22. dest="command_index_print_flag",
  23. help="Prints a table of the command indexes.",
  24. action="store_true",
  25. default=False,
  26. )
  27. return parser
  28. def validate_xml(xml_list):
  29. """
  30. Iterates through XML list, shows an error if an XML file is not an XML file or does not exist.
  31. A list of valid XML files is returned (Not valid files are thrown out)
  32. """
  33. out_list = []
  34. for xml_path in xml_list:
  35. if os.path.isfile(xml_path):
  36. out_list.append(xml_path)
  37. else:
  38. print(
  39. "WARNING: XML {} is not a valid file. Rejecting file.".format(xml_path)
  40. )
  41. return out_list
  42. def recursive_xml_parse(tree_obj):
  43. """
  44. returns a list of items
  45. [tagName , [(argKey:argVal)] , [" " or [tagName , [] , []] ] ]
  46. """
  47. out_obj = [tree_obj.tag, [], []]
  48. for att in tree_obj.attrib:
  49. out_obj[1].append((att, tree_obj.attrib[att]))
  50. internal_text = tree_obj.text
  51. if internal_text is not None:
  52. internal_text = internal_text.strip()
  53. if internal_text != "":
  54. out_obj[2].append(internal_text)
  55. for internal_item in tree_obj:
  56. out_obj[2].append(recursive_xml_parse(internal_item))
  57. return out_obj
  58. def tag_object_to_string(tag_obj):
  59. out = "<{}{}>".format(
  60. tag_obj[0], "".join(" " + x[0] + "=" + '"' + x[1] + '"' for x in tag_obj[1])
  61. )
  62. final_line_break = ""
  63. for internal_item in tag_obj[2]:
  64. if type(internal_item) == str:
  65. out += internal_item
  66. else:
  67. out += "\n\t" + tag_object_to_string(internal_item).replace("\n", "\n\t")
  68. final_line_break = "\n"
  69. out += final_line_break + "</" + tag_obj[0] + ">"
  70. return out
  71. def diff_files(xml_list):
  72. """
  73. Finds the difference between topology XML files, ignoring ordering and names in "connection" tags
  74. Iterate through root tag elements
  75. Create a dictionary with file_dict[tag] = [list of tag objects]
  76. """
  77. if len(xml_list) < 2:
  78. print("Less than two XML files were specified. Exiting.")
  79. return
  80. master_tag_dict = {}
  81. for xml_path in xml_list:
  82. # Create etree object
  83. fd = open(xml_path, "r")
  84. xml_parser = etree.XMLParser(remove_comments=True)
  85. element_tree = etree.parse(fd, parser=xml_parser)
  86. fd.close()
  87. # Internal Parsing
  88. xml_dict = recursive_xml_parse(element_tree.getroot())
  89. for tag_obj in xml_dict[2]:
  90. if tag_obj[0] == "connection":
  91. tag_obj[1] = []
  92. pickled_obj = pickle.dumps(tag_obj)
  93. if pickled_obj not in master_tag_dict:
  94. # del master_tag_dict[pickled_obj]
  95. master_tag_dict[pickled_obj] = []
  96. master_tag_dict[pickled_obj].append(xml_path)
  97. # Separate by XML path
  98. tag_to_object = {} # tag_to_object[xml_path] = [obj]
  99. for pickled_obj in master_tag_dict:
  100. if len(master_tag_dict[pickled_obj]) == 1:
  101. if master_tag_dict[pickled_obj][0] not in tag_to_object:
  102. tag_to_object[master_tag_dict[pickled_obj][0]] = []
  103. tag_to_object[master_tag_dict[pickled_obj][0]].append(pickled_obj)
  104. for xml_path in tag_to_object:
  105. print(xml_path + "\n")
  106. # sort pickled obj lists
  107. tag_to_object[xml_path].sort()
  108. for pickled_obj in tag_to_object[xml_path]:
  109. tag_obj = pickle.loads(pickled_obj)
  110. print(tag_object_to_string(tag_obj))
  111. print("\n")
  112. print("\n")
  113. def command_index_print(xml_list):
  114. for xml_path in xml_list:
  115. # Create etree object
  116. fd = open(xml_path, "r")
  117. xml_parser = etree.XMLParser(remove_comments=True)
  118. element_tree = etree.parse(fd, parser=xml_parser)
  119. fd.close()
  120. # Internal Parsing
  121. xml_dict = recursive_xml_parse(element_tree.getroot())
  122. connection_information = [] # [{target/source: {attribKey:attribVal}}]
  123. # Gather connection data
  124. for tag_obj in xml_dict[2]:
  125. if tag_obj[0] == "connection":
  126. in_dict = {"SOURCE": None, "TARGET": None}
  127. for conn_obj in tag_obj[2]:
  128. if type(conn_obj) != str:
  129. attrib_dict = {}
  130. for attrib in conn_obj[1]:
  131. attrib_dict[attrib[0].upper()] = attrib[1].upper()
  132. in_dict[conn_obj[0].upper()] = attrib_dict
  133. connection_information.append(in_dict)
  134. component_to_cmd_info = (
  135. {}
  136. ) # component_to_cmd_info[componentName] = {cmdIndex:index , cmdRegIndex:index}
  137. # Create dict mapping components to their mult values on the cmdDisp
  138. for conn_info in connection_information:
  139. source_dict = conn_info["SOURCE"]
  140. target_dict = conn_info["TARGET"]
  141. source_comp_name = source_dict["COMPONENT"]
  142. target_comp_name = target_dict["COMPONENT"]
  143. if source_comp_name not in component_to_cmd_info:
  144. component_to_cmd_info[source_comp_name] = {
  145. "cmdIndex": None,
  146. "cmdRegIndex": None,
  147. }
  148. if target_comp_name not in component_to_cmd_info:
  149. component_to_cmd_info[target_comp_name] = {
  150. "cmdIndex": None,
  151. "cmdRegIndex": None,
  152. }
  153. if source_dict["TYPE"] == "CMD":
  154. component_to_cmd_info[target_comp_name]["cmdIndex"] = source_dict["NUM"]
  155. if target_dict["TYPE"] == "CMDREG":
  156. component_to_cmd_info[source_comp_name]["cmdRegIndex"] = target_dict[
  157. "NUM"
  158. ]
  159. # sort by num
  160. sorted_list = []
  161. for comp_info in component_to_cmd_info:
  162. sorted_list.append([comp_info, component_to_cmd_info[comp_info]])
  163. sorted_list.sort(
  164. key=lambda x: int(float(x[1]["cmdIndex"]))
  165. if x[1]["cmdIndex"] is not None
  166. else -1
  167. )
  168. # Print table
  169. print(xml_path + "\n")
  170. header_list = [
  171. "\t\tComponent Name\t\t",
  172. "Command Index",
  173. "Command Registration Index",
  174. ]
  175. len_list = [len(x) for x in header_list]
  176. print("| " + " | ".join(x for x in header_list) + " |")
  177. for comp_info in sorted_list:
  178. print_list = [
  179. comp_info[0],
  180. comp_info[1]["cmdIndex"],
  181. comp_info[1]["cmdRegIndex"],
  182. ]
  183. i = 0
  184. row_string = ""
  185. while i != len(header_list):
  186. if i != 0:
  187. row_string += " | "
  188. format_string = "{0:^" + str(len_list[i]) + "}"
  189. row_string += format_string.format(print_list[i])
  190. i += 1
  191. print("| " + row_string + " |")
  192. print("\n\n")
  193. def main():
  194. parser = setup_opt_parse()
  195. (opt, args) = parser.parse_args()
  196. xml_list = validate_xml(args)
  197. if opt.command_index_print_flag:
  198. command_index_print(xml_list)
  199. if opt.xml_diff_flag:
  200. diff_files(xml_list)
  201. if __name__ == "__main__":
  202. main()