You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

140 lines
4.6 KiB

  1. #
  2. # Example python script to generate a BOM from a KiCad generic netlist
  3. #
  4. # Example: Sorted and Grouped HTML BOM with more advanced grouping
  5. #
  6. """
  7. @package
  8. Generate a HTML BOM list.
  9. Components are sorted and grouped by ref
  10. Fields are (if exist)
  11. Ref, Quantity, Value, Symbol, footprint, Description, Vendor
  12. """
  13. from __future__ import print_function
  14. # Import the KiCad python helper module and the csv formatter
  15. import kicad_netlist_reader
  16. import sys
  17. # Start with a basic html template
  18. html = """
  19. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  20. "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  21. <html xmlns="http://www.w3.org/1999/xhtml">
  22. <head>
  23. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  24. <title>KiCad BOM grouped by value and footprint </title>
  25. </head>
  26. <body>
  27. <h1><!--SOURCE--></h1>
  28. <p><!--DATE--></p>
  29. <p><!--TOOL--></p>
  30. <p><!--COMPCOUNT--></p>
  31. <table>
  32. <!--TABLEROW-->
  33. </table>
  34. </body>
  35. </html>
  36. """
  37. def myEqu(self, other):
  38. """myEqu is a more advanced equivalence function for components which is
  39. used by component grouping. Normal operation is to group components based
  40. on their Value, Library source, and Library part.
  41. In this example of a more advanced equivalency operator we also compare the
  42. custom fields Voltage, Tolerance and Manufacturer as well as the assigned
  43. footprint. If these fields are not used in some parts they will simply be
  44. ignored (they will match as both will be empty strings).
  45. """
  46. result = True
  47. if self.getValue() != other.getValue():
  48. result = False
  49. elif self.getLibName() != other.getLibName():
  50. result = False
  51. elif self.getPartName() != other.getPartName():
  52. result = False
  53. elif self.getFootprint() != other.getFootprint():
  54. result = False
  55. # elif self.getField("Tolerance") != other.getField("Tolerance"):
  56. # result = False
  57. # elif self.getField("Manufacturer") != other.getField("Manufacturer"):
  58. # result = False
  59. # elif self.getField("Voltage") != other.getField("Voltage"):
  60. # result = False
  61. return result
  62. # Override the component equivalence operator - it is important to do this
  63. # before loading the netlist, otherwise all components will have the original
  64. # equivalency operator.
  65. kicad_netlist_reader.comp.__equ__ = myEqu
  66. # Generate an instance of a generic netlist, and load the netlist tree from
  67. # video.xml. If the file doesn't exist, execution will stop
  68. net = kicad_netlist_reader.netlist(sys.argv[1])
  69. # Open a file to write too, if the file cannot be opened output to stdout
  70. # instead
  71. try:
  72. f = open(sys.argv[2], 'w')
  73. except IOError:
  74. e = "Can't open output file for writing: " + sys.argv[2]
  75. print( __file__, ":", e, sys.stderr )
  76. f = sys.stdout
  77. components = net.getInterestingComponents()
  78. # Output a set of rows for a header providing general information
  79. html = html.replace('<!--SOURCE-->', net.getSource())
  80. html = html.replace('<!--DATE-->', net.getDate())
  81. html = html.replace('<!--TOOL-->', net.getTool())
  82. html = html.replace('<!--COMPCOUNT-->', "<b>Component Count:</b>" + \
  83. str(len(components)))
  84. row = "<tr><th style='width:640px'>Ref</th>" + "<th>Qnty</th>"
  85. row += "<th>Value</th>"
  86. row += "<th>Symbol</th>"
  87. row += "<th>Footprint</th>"
  88. row += "<th>Description</th>"
  89. row += "<th>PartNumber</th>"
  90. row += "<th>Vendor</th></tr>"
  91. html = html.replace('<!--TABLEROW-->', row + "<!--TABLEROW-->")
  92. # Get all of the components in groups of matching parts + values
  93. # (see kicad_netlist_reader.py)
  94. grouped = net.groupComponents(components)
  95. # Output all of the component information
  96. for group in grouped:
  97. refs = ""
  98. # Add the reference of every component in the group and keep a reference
  99. # to the component so that the other data can be filled in once per group
  100. for component in group:
  101. if len(refs) > 0:
  102. refs += ", "
  103. refs += component.getRef()
  104. c = component
  105. row = "<tr>"
  106. row += "<td>" + refs +"</td>"
  107. row += "<td align=center>" + str(len(group)) + "</td>"
  108. row += "<td align=center>" + c.getValue() + "</td>"
  109. # row += "<td align=center>" + c.getLibName() + ":" + c.getPartName() + "</td>"
  110. row += "<td align=center>" + c.getPartName() + "</td>"
  111. row += "<td align=center>" + c.getFootprint() + "</td>"
  112. row += "<td align=center>" + c.getDescription() + "</td>"
  113. row += "<td align=center>" + c.getField("PartNumber") + "</td>"
  114. row += "<td align=center>" + c.getField("Vendor") + "</td>"
  115. row += "</tr>"
  116. html = html.replace('<!--TABLEROW-->', row + "<!--TABLEROW-->")
  117. # Print the formatted html to output file
  118. print(html, file=f)