123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182 |
- #
- # Example python script to generate a BOM from a KiCad generic netlist
- #
- # Example: Sorted and Grouped CSV BOM
- #
- """
- @package
- Generate a csv BOM list.
- Components are sorted by ref and grouped by value
- Fields are (if exist)
- Item, Qty, Reference(s), Value, LibPart, Footprint, Datasheet
- Command line:
- python "pathToFile/bom_csv_grouped_by_value.py" "%I" "%O.csv"
- """
- from __future__ import print_function
- # Import the KiCad python helper module and the csv formatter
- import kicad_netlist_reader
- import csv
- import sys
- def myEqu(self, other):
- """myEqu is a more advanced equivalence function for components which is
- used by component grouping. Normal operation is to group components based
- on their value and footprint.
- In this example of a custom equivalency operator we compare the
- value, the part name and the footprint.
- """
- result = True
- if self.getField("Hersteller-Nr.") != other.getField("Hersteller-Nr."):
- result = False
- elif self.getField("Mouser-Nr.") != other.getField("Mouser-Nr."):
- result = False
- elif self.getField("Datasheet") != other.getField("Datasheet"):
- result = False
- elif self.getPartName() != other.getPartName():
- result = False
- elif self.getFootprint() != other.getFootprint():
- result = False
- return result
- # Override the component equivalence operator - it is important to do this
- # before loading the netlist, otherwise all components will have the original
- # equivalency operator.
- kicad_netlist_reader.comp.__eq__ = myEqu
- if len(sys.argv) != 3:
- print("Usage ", __file__, "<generic_netlist.xml> <output.csv>", file=sys.stderr)
- sys.exit(1)
- # Generate an instance of a generic netlist, and load the netlist tree from
- # the command line option. If the file doesn't exist, execution will stop
- net = kicad_netlist_reader.netlist(sys.argv[1])
- # Open a file to write to, if the file cannot be opened output to stdout
- # instead
- try:
- f = open(sys.argv[2], 'w')
- except IOError:
- e = "Can't open output file for writing: " + sys.argv[2]
- print( __file__, ":", e, sys.stderr )
- f = sys.stdout
- # subset the components to those wanted in the BOM, controlled
- # by <configure> block in kicad_netlist_reader.py
- components = net.getInterestingComponents()
- compfields = net.gatherComponentFieldUnion(components)
- partfields = net.gatherLibPartFieldUnion()
- # remove Reference, Value, Datasheet, and Footprint, they will come from 'columns' below
- partfields -= set( ['Reference', 'Value', 'Datasheet', 'Footprint'] )
- columnset = compfields | partfields # union
- # prepend an initial 'hard coded' list and put the enchillada into list 'columns'
- columns = ['Item', 'Qty', 'Reference(s)', 'Value', 'LibPart', 'Footprint', 'Datasheet'] + sorted(list(columnset))
- # Create a new csv writer object to use as the output formatter
- out = csv.writer( f, lineterminator='\n', delimiter=',', quotechar='\"', quoting=csv.QUOTE_ALL )
- # override csv.writer's writerow() to support encoding conversion (initial encoding is utf8):
- def writerow( acsvwriter, columns ):
- utf8row = []
- for col in columns:
- utf8row.append( str(col) ) # currently, no change
- acsvwriter.writerow( utf8row )
- # Output a set of rows as a header providing general information
- writerow( out, ['Source:', net.getSource()] )
- writerow( out, ['Date:', net.getDate()] )
- writerow( out, ['Tool:', net.getTool()] )
- writerow( out, ['Generator:', sys.argv[0]] )
- writerow( out, ['Component Count:', len(components)] )
- writerow( out, [] )
- writerow( out, ['Individual Components:'] )
- writerow( out, [] ) # blank line
- writerow( out, columns )
- # Output all the interesting components individually first:
- row = []
- for c in components:
- del row[:]
- row.append('') # item is blank in individual table
- row.append('') # Qty is always 1, why print it
- row.append( c.getRef() ) # Reference
- row.append( c.getValue() ) # Value
- row.append( c.getLibName() + ":" + c.getPartName() ) # LibPart
- #row.append( c.getDescription() )
- row.append( c.getFootprint() )
- row.append( c.getDatasheet() )
- # from column 7 upwards, use the fieldnames to grab the data
- for field in columns[7:]:
- row.append( c.getField( field ) );
- writerow( out, row )
- writerow( out, [] ) # blank line
- writerow( out, [] ) # blank line
- writerow( out, [] ) # blank line
- writerow( out, ['Collated Components:'] )
- writerow( out, [] ) # blank line
- writerow( out, columns ) # reuse same columns
- # Get all of the components in groups of matching parts + values
- # (see kicad_netlist_reader.py)
- grouped = net.groupComponents(components)
- # Output component information organized by group, aka as collated:
- item = 0
- for group in grouped:
- del row[:]
- refs = ""
- vals = ""
- gval = []
- # Add the reference of every component in the group and keep a reference
- # to the component so that the other data can be filled in once per group
- for component in group:
- if len(refs) > 0:
- refs += ", "
- refs += component.getRef()
- c = component
-
- for component in group:
- if component.getValue() not in gval:
- gval.append(component.getValue())
-
- for component in gval:
- if len(vals) > 0:
- vals += ", "
- vals += component
- # Fill in the component groups common data
- # columns = ['Item', 'Qty', 'Reference(s)', 'Value', 'LibPart', 'Footprint', 'Datasheet'] + sorted(list(columnset))
- item += 1
- row.append( item )
- row.append( len(group) )
- row.append( refs )
- row.append( vals )
- row.append( c.getLibName() + ":" + c.getPartName() )
- row.append( net.getGroupFootprint(group) )
- row.append( net.getGroupDatasheet(group) )
- # from column 7 upwards, use the fieldnames to grab the data
- for field in columns[7:]:
- row.append( net.getGroupField(group, field) );
- writerow( out, row )
- f.close()
|