net.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. #
  2. # Example python script to generate a BOM from a KiCad generic netlist
  3. #
  4. # Example: Sorted and Grouped CSV BOM
  5. #
  6. """
  7. @package
  8. Generate a csv BOM list.
  9. Components are sorted by ref and grouped by value
  10. Fields are (if exist)
  11. Item, Qty, Reference(s), Value, LibPart, Footprint, Datasheet
  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 csv
  17. import sys
  18. def myEqu(self, other):
  19. """myEqu is a more advanced equivalence function for components which is
  20. used by component grouping. Normal operation is to group components based
  21. on their value and footprint.
  22. In this example of a custom equivalency operator we compare the
  23. value, the part name and the footprint.
  24. """
  25. result = True
  26. if self.getValue() != other.getValue():
  27. result = False
  28. elif self.getPartName() != other.getPartName():
  29. result = False
  30. elif self.getFootprint() != other.getFootprint():
  31. result = False
  32. return result
  33. # Override the component equivalence operator - it is important to do this
  34. # before loading the netlist, otherwise all components will have the original
  35. # equivalency operator.
  36. kicad_netlist_reader.comp.__eq__ = myEqu
  37. if len(sys.argv) != 3:
  38. print("Usage ", __file__, "<generic_netlist.xml> <output.csv>", file=sys.stderr)
  39. sys.exit(1)
  40. # Generate an instance of a generic netlist, and load the netlist tree from
  41. # the command line option. If the file doesn't exist, execution will stop
  42. net = kicad_netlist_reader.netlist(sys.argv[1])
  43. # Open a file to write to, if the file cannot be opened output to stdout
  44. # instead
  45. try:
  46. f = open(sys.argv[2], 'w')
  47. except IOError:
  48. e = "Can't open output file for writing: " + sys.argv[2]
  49. print( __file__, ":", e, sys.stderr )
  50. f = sys.stdout
  51. # subset the components to those wanted in the BOM, controlled
  52. # by <configure> block in kicad_netlist_reader.py
  53. components = net.getInterestingComponents()
  54. compfields = net.gatherComponentFieldUnion(components)
  55. partfields = net.gatherLibPartFieldUnion()
  56. # remove Reference, Value, Datasheet, and Footprint, they will come from 'columns' below
  57. partfields -= set( ['Reference', 'Value', 'Datasheet', 'Footprint'] )
  58. columnset = compfields | partfields # union
  59. # prepend an initial 'hard coded' list and put the enchillada into list 'columns'
  60. columnsIndividuals = ['Reference(s)', 'Value', 'Footprint'] + sorted(list(columnset))
  61. columnsCollected = ['Item', 'Qty', 'Reference(s)', 'Value', 'Footprint'] + sorted(list(columnset))
  62. # Create a new csv writer object to use as the output formatter
  63. out = csv.writer( f, lineterminator='\n', delimiter=',', quotechar='\"', quoting=csv.QUOTE_ALL )
  64. # override csv.writer's writerow() to support encoding conversion (initial encoding is utf8):
  65. def writerow( acsvwriter, columns ):
  66. utf8row = []
  67. for col in columns:
  68. utf8row.append( str(col) ) # currently, no change
  69. acsvwriter.writerow( utf8row )
  70. # Output a set of rows as a header providing general information
  71. #~ writerow( out, ['Source:', net.getSource()] )
  72. #~ writerow( out, ['Date:', net.getDate()] )
  73. #~ writerow( out, ['Tool:', net.getTool()] )
  74. #~ writerow( out, ['Generator:', sys.argv[0]] )
  75. writerow( out, ['Component Count:', len(components)] )
  76. writerow( out, [] )
  77. writerow( out, ['Individual Components:'] )
  78. writerow( out, [] ) # blank line
  79. writerow( out, columnsIndividuals )
  80. # Output all the interesting components individually first:
  81. row = []
  82. for c in components:
  83. del row[:]
  84. #~ row.append('') # item is blank in individual table
  85. #~ row.append('') # Qty is always 1, why print it
  86. row.append( c.getRef() ) # Reference
  87. row.append( c.getValue() ) # Value
  88. #~ row.append( c.getLibName() + ":" + c.getPartName() ) # LibPart
  89. #row.append( c.getDescription() )
  90. row.append( c.getFootprint() )
  91. #~ row.append( c.getDatasheet() )
  92. # from column 7 upwards, use the fieldnames to grab the data
  93. for field in columnsIndividuals[3:]:
  94. val = c.getField( field )
  95. if val == "Value":
  96. row.append('');
  97. else :
  98. row.append(val);
  99. writerow( out, row )
  100. writerow( out, [] ) # blank line
  101. writerow( out, [] ) # blank line
  102. writerow( out, [] ) # blank line
  103. writerow( out, ['Collated Components:'] )
  104. writerow( out, [] ) # blank line
  105. writerow( out, columnsCollected ) # reuse same columns
  106. # Get all of the components in groups of matching parts + values
  107. # (see kicad_netlist_reader.py)
  108. grouped = net.groupComponents(components)
  109. # Output component information organized by group, aka as collated:
  110. item = 0
  111. for group in grouped:
  112. del row[:]
  113. refs = ""
  114. # Add the reference of every component in the group and keep a reference
  115. # to the component so that the other data can be filled in once per group
  116. for component in group:
  117. if len(refs) > 0:
  118. refs += ", "
  119. refs += component.getRef()
  120. c = component
  121. # Fill in the component groups common data
  122. # columns = ['Item', 'Qty', 'Reference(s)', 'Value', 'LibPart', 'Footprint', 'Datasheet'] + sorted(list(columnset))
  123. item += 1
  124. row.append( item )
  125. row.append( len(group) )
  126. row.append( refs );
  127. row.append( c.getValue() )
  128. #~ row.append( c.getLibName() + ":" + c.getPartName() )
  129. row.append( net.getGroupFootprint(group) )
  130. #~ row.append( net.getGroupDatasheet(group) )
  131. # from column 7 upwards, use the fieldnames to grab the data
  132. for field in columnsCollected[5:]:
  133. val = net.getGroupField(group, field)
  134. if val == "Value":
  135. row.append('');
  136. else :
  137. row.append(val);
  138. writerow( out, row )
  139. f.close()