forked from imrehg/kicad-bom-seeedstudio
-
Notifications
You must be signed in to change notification settings - Fork 1
/
kicad_bom_seeedstudio.py
96 lines (84 loc) · 3.13 KB
/
kicad_bom_seeedstudio.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#!/usr/bin/env python3
import csv
import sys
import xml.etree.ElementTree as ET
### Natural key sorting for orders like : C1, C5, C10, C12 ... (instead of C1, C10, C12, C5...)
# http://stackoverflow.com/a/5967539
import re
def atoi(text):
return int(text) if text.isdigit() else text
def natural_keys(text):
'''
alist.sort(key=natural_keys) sorts in human order
http://nedbatchelder.com/blog/200712/human_sorting.html
(See Toothy's implementation in the comments)
'''
return [ atoi(c) for c in re.split('(\d+)', text) ]
###
def parse_kicad_xml(input_file):
"""Parse the KiCad XML file and look for the part designators
as done in the case of the official KiCad Open Parts Library:
* OPL parts are designated with "SKU" (preferred)
* other parts are designated with "MPN"
"""
components = {}
parts = {}
missing = []
tree = ET.parse(input_file)
root = tree.getroot()
for f in root.findall('./components/'):
name = f.attrib['ref']
info = {}
fields = f.find('fields')
opl, mpn = None, None
if fields is not None:
for x in fields:
if x.attrib['name'].upper() == 'SKU':
opl = x.text
elif x.attrib['name'].upper() == 'MPN':
mpn = x.text
if opl:
components[name] = opl
elif mpn:
components[name] = mpn
else:
missing += [name]
continue
if components[name] not in parts:
parts[components[name]] = []
parts[components[name]] += [name]
return components, missing
def write_bom_seeed(output_file_slug, components):
"""Write the BOM according to the Seeed Studio Fusion PCBA template available at:
https://statics3.seeedstudio.com/assets/file/fusion/bom_template_2016-08-18.csv
```
Part/Designator,Manufacture Part Number/Seeed SKU,Quantity
C1,RHA,1
"D1,D2",CC0603KRX7R9BB102,2
```
The output is a CSV file at the `output_file_slug`.csv location.
"""
parts = {}
for c in components:
if components[c] not in parts:
parts[components[c]] = []
parts[components[c]] += [c]
field_names = ['Part/Designator', 'Manufacture Part Number/Seeed SKU', 'Quantity']
with open("{}.csv".format(output_file_slug), 'w') as csvfile:
bomwriter = csv.DictWriter(csvfile, fieldnames=field_names, delimiter=',',
quotechar='"', quoting=csv.QUOTE_MINIMAL)
bomwriter.writeheader()
for p in sorted(parts.keys()):
pieces = sorted(parts[p], key=natural_keys)
designators = ",".join(pieces)
bomwriter.writerow({'Part/Designator': designators,
'Manufacture Part Number/Seeed SKU': p,
'Quantity': len(pieces)})
if __name__ == "__main__":
input_file = sys.argv[1]
output_file = sys.argv[2]
components, missing = parse_kicad_xml(input_file)
write_bom_seeed(output_file, components)
if len(missing) > 0:
print("** Warning **: there were parts with missing SKU/MFP")
print(missing)