-
-
Save gcormier/61addd28226bfd4ed6f645f312c3f13a to your computer and use it in GitHub Desktop.
""" | |
@package | |
Output: CSV (comma-separated) | |
Grouped By: Value | |
Sorted By: Ref | |
Fields: Comment,Designator,Footprint,LCSC | |
Outputs the BOM for JLCPCB. Run this AFTER generating your POS file for SMT assembly. | |
Attempts to find and modify POS header to JLC format in the same output folder. | |
REQUIRES kicad_utils.py and kicad_netlist_reader.py in the same director as this script | |
https://gitlab.com/kicad/code/kicad/-/tree/master/eeschema/plugins/python_scripts | |
Command line: | |
python "pathToFile/bom_csv_grouped_by_value.py" "%I" "%O.csv" | |
Using a gerber output subfolder: | |
python "pathToFile/bom_csv_grouped_by_value.py" "%I" "%P/gerber/%B-bom.csv" | |
""" | |
from __future__ import print_function | |
# Import the KiCad python helper module and the csv formatter | |
import kicad_netlist_reader | |
import kicad_utils | |
import csv | |
import sys | |
import os | |
# A helper function to convert a UTF8/Unicode/locale string read in netlist | |
# for python2 or python3 (Windows/unix) | |
def fromNetlistText( aText ): | |
currpage = sys.stdout.encoding #the current code page. can be none | |
if currpage is None: | |
return aText | |
if currpage != 'utf-8': | |
try: | |
return aText.encode('utf-8').decode(currpage) | |
except UnicodeDecodeError: | |
return aText | |
else: | |
return aText | |
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.getValue() != other.getValue(): | |
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 = kicad_utils.open_file_write(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 = ['Comment', 'Designator', 'Footprint', 'LCSC'] | |
# 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( fromNetlistText( str(col) ) ) | |
acsvwriter.writerow( utf8row ) | |
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 ) ); | |
# 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 = "" | |
# 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 | |
# Fill in the component groups common data | |
# columns = ['Item', 'Qty', 'Reference(s)', 'Value', 'LibPart', 'Footprint', 'Datasheet'] + sorted(list(columnset)) | |
item += 1 | |
row.append( c.getValue() ) | |
row.append( refs ); | |
row.append( net.getGroupFootprint(group) ) | |
row.append( c.getField('LCSC') ) | |
# 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() | |
# Try to see if POS files exist. If so, modify them to the JLCPCB header | |
# Looks for a few permutations defined in fileOptions which depends if user selected a single POS | |
# or if they select seperate for layers | |
inputFile = os.path.split(sys.argv[1])[1] | |
projectName = os.path.splitext(inputFile)[0] | |
destination = sys.argv[2] | |
destinationPath = os.path.dirname(destination) | |
print("Project name detected as :", projectName) | |
fileOptions = [ '-all-pos', '-top-pos', '-bottom-pos'] | |
for possibility in fileOptions: | |
positionFile = destinationPath + "/" + projectName + possibility + ".csv" | |
print("\nChecking for existance of ", positionFile) | |
if os.path.isfile(positionFile): | |
print("Modifying header of ", positionFile) | |
with open(positionFile) as f: | |
lines = f.readlines() | |
lines[0] = "Designator,Val,Package,Mid X,Mid Y,Rotation,Layer\n" | |
with open(positionFile, "w") as f: | |
f.writelines(lines) | |
else: | |
print("Not found (" + positionFile + ")") | |
Any chance you can get together with this dev? because i like the idea of this just being a plugin instead of running command line scripts. https://github.com/bennymeg/JLC-Plugin-for-KiCad
lines 113 to 129 are useless, remove them
then fix line 138 " raw = [] "
What is the point of counting 'item' ? It serves nothing. (lines 136, 151)
Lines 156..158 also useless !
Line 186 brakes what JLC is expecting ??????!!!!!!!
Designator, ~~Val,Package, ~~ Mid X,Mid Y,Rotation,Layer
I got here from https://www.youtube.com/watch?v=v9FglN80EMM then https://jlcpcb.com/help/article/81-How-to-generate-the-BOM-and-Centroid-file-from-KiCAD then https://gist.github.com/arturo182/a8c4a4b96907cfccf616a1edb59d0389
What I did not get anywhere was an idea how to install and how to use this python file.
I am using Windows 10.
Thank you!