hellen-one/bin/convert_kicad_module_footpr...

214 lines
8.1 KiB
Python

#!/usr/bin/env python
############################################################################################
# Hellen-One: A script to convert a Kicad module footprint (*.kicad_mod) to PCAD .LIA.
# Needed by AD projects to use Kicad-created modules.
# (c) andreika <prometheus.pcb@gmail.com>
############################################################################################
import os, sys, errno
import csv, re
import glob, shutil
#if len(sys.argv) < 4:
# print "Error! Please specify the module project folder, module name and rev."
# sys.exit(1)
#project_base = sys.argv[1]
name = "wbo" # sys.argv[2]
#rev = sys.argv[3]
#base_path = project_base
#src_path = base_path
#dst_path = "modules"
mod_name = "MOD_HELLEN_" + name.upper()
pat_pad = re.compile(r'^\s*\(pad \"([^\"]+)\"\s+(thru_hole|smd)\s+(circle|oval|rect|roundrect)\s+\(at\s+([0-9\.\-]+)\s+([0-9\.\-]+)(\s+([0-9\.\-]+))?\)(\s+\(locked\))?\s+\(size\s+([0-9\.\-]+)\s+([0-9\.\-]+)\)(\s+\(drill\s+([0-9\.\-]+)\))?\s+\(layers\s+\"?(.*?)\.Cu')
pat_line_rect = re.compile(r'^\s*\(fp_(line|rect)\s+\(start\s+([0-9\.\-]+)\s+([0-9\.\-]+)\)\s+\(end\s+([0-9\.\-]+)\s+([0-9\.\-]+)\)\s+\(layer\s+\"([^\"]+)\"\)\s+\(width\s+([0-9\.\-]+)\)(\s+\(fill\s+([a-z]+)\))?')
pat_zone = re.compile(r'^\s*\(zone\s+\(net 0\)\s+\(net_name\s+\"\"\)\s+\(layers\s+\"?(.*?)\.Cu\)')
pat_xy = re.compile(r'^\s*\(xy\s+([0-9\.\-]+)\s+([0-9\.\-]+)\)')
shapeLut = {"circle": "Oval", "roundrect": "RndRect", "oval": "Oval", "rect": "Rect" }
# used by add_zone_line()
class Zone:
in_zone = False
zone_first = None
zone_prev = None
layer = ""
lines = None
def add_line(self, zone_cur):
if self.zone_prev:
print ("* adding line (" + self.zone_prev[0] + "," + self.zone_prev[1] + " - " + zone_cur[0] + "," + zone_cur[1] + ")")
if (self.layer == 'F' or self.layer == '*'):
self.lines.append([ self.zone_prev[0], self.zone_prev[1], zone_cur[0], zone_cur[1], "F.Zone", "0.1" ])
if (self.layer == 'B' or self.layer == '*'):
self.lines.append([ self.zone_prev[0], self.zone_prev[1], zone_cur[0], zone_cur[1], "B.Zone", "0.1" ])
else:
self.zone_first = zone_cur
self.zone_prev = zone_cur
def open(self, layer, lines):
self.in_zone = True
self.layer = layer
self.lines = lines
def close(self):
if self.zone_first:
self.add_line(self.zone_first)
def process_pcb(src_name, dst_name):
with open(src_name, 'rt') as src_f, open(dst_name, 'w') as dst_f:
dst_f.write("ACCEL_ASCII \"" + name + ".LIA\"\n\n")
dst_f.write("(asciiHeader\n (asciiVersion 3 0)\n)\n\n")
pad_lib = []
pads = []
lines = []
pad_names = {}
zone = Zone()
for line in src_f:
zon = pat_zone.match(line)
if zon:
zone.open(zon.group(1), lines)
print ("* Zone detected! Layer=" + zone.layer)
pad = pat_pad.match(line)
if pad:
pad_name = pad.group(1)
pad_type = pad.group(2)
pad_shape = pad.group(3)
pad_x = pad.group(4)
pad_y = str(-float(pad.group(5))) # Kicad->AD: reverse Y-coord
pad_rot = pad.group(7) or "0"
pad_w = pad.group(9)
pad_h = pad.group(10)
pad_hole = pad.group(12) or "0"
pad_layers = pad.group(13)
print ("* pad detected!")
print (" name=" + pad_name)
print (" type=" + pad_type)
print (" shape=" + pad_shape)
print (" x=" + pad_x)
print (" y=" + pad_y)
print (" rot=" + pad_rot)
print (" w=" + pad_w)
print (" h=" + pad_h)
print (" hole=" + pad_hole)
print (" layers=" + pad_layers)
pad_lib_entry = [ pad_type, pad_shape, pad_w, pad_h, pad_hole, pad_layers ]
if pad_lib_entry not in pad_lib:
pad_entry_idx = len(pad_lib)
pad_lib.append(pad_lib_entry)
print ("* adding pad type #" + str(pad_entry_idx))
else:
pad_entry_idx = pad_lib.index(pad_lib_entry)
if (pad_name in pad_names):
pad_unique_name = pad_name + "_" + str(pad_names[pad_name])
pad_names[pad_name] += 1
else:
pad_unique_name = pad_name
pad_names[pad_name] = 1
pad_entry = [ pad_entry_idx, pad_name, pad_x, pad_y, pad_rot, pad_unique_name ]
pads.append(pad_entry)
lin = pat_line_rect.match(line)
if lin:
line_type = lin.group(1)
line_x1 = lin.group(2)
line_y1 = str(-float(lin.group(3))) # Kicad->AD: reverse Y-coord
line_x2 = lin.group(4)
line_y2 = str(-float(lin.group(5))) # Kicad->AD: reverse Y-coord
line_layer = lin.group(6)
line_width = lin.group(7)
line_fill = lin.group(9)
print ("* " + line_type + " detected!")
print (" (" + line_x1 + "," + line_y1 + " - " + line_x2 + "," + line_y2 + ")")
print (" * layer=" + line_layer)
print (" * width=" + line_width)
print (" * fill=" + (line_fill or "?"))
if (line_type == "rect"):
if (line_fill != "none"):
print ("Error! Filled rects currently not supported!")
sys.exit(1)
lines.append([ line_x1, line_y1, line_x2, line_y1, line_layer, line_width ])
lines.append([ line_x2, line_y1, line_x2, line_y2, line_layer, line_width ])
lines.append([ line_x2, line_y2, line_x1, line_y2, line_layer, line_width ])
lines.append([ line_x1, line_y2, line_x1, line_y1, line_layer, line_width ])
else:
lines.append([ line_x1, line_y1, line_x2, line_y2, line_layer, line_width ])
# parse zones and add them as lines on special layers (P-CAD doesn't support zones natively)
if zone.in_zone:
xy = pat_xy.match(line)
if xy:
zone_point_x = xy.group(1)
zone_point_y = str(-float(xy.group(2))) # Kicad->AD: reverse Y-coord
zone.add_line([zone_point_x, zone_point_y])
# todo: add correct exit-zone conditions
zone.close()
# add pad entries
dst_f.write("(library \"Library_1\"\n")
pi = 0
for pe in pad_lib:
dst_f.write(" (padStyleDef \"PAD" + str(pi) + "\"\n")
dst_f.write(" (holeDiam " + pe[4] + "mm)\n")
dst_f.write(" (startRange 1)\n (endRange 2)\n")
if pe[0] == "smd":
if pe[5] == "B":
layers = ["layerNumRef 2"]
else:
layers = ["layerNumRef 1"]
else:
layers = ["layerNumRef 1", "layerNumRef 2", "layerType Signal"]
for l in layers:
dst_f.write(" (padShape (" + l + ") (padShapeType " + shapeLut[pe[1]] + ") (shapeWidth " + pe[2] + "mm) (shapeHeight " + pe[3] + "mm) )\n")
dst_f.write(" )\n")
pi += 1
# add pads
dst_f.write(" (patternDefExtended \"" + mod_name + "_1\"\n")
dst_f.write(" (originalName \"" + mod_name + "\")\n")
dst_f.write(" (patternGraphicsNameRef \"Primary\")\n\n (patternGraphicsDef\n")
dst_f.write(" (patternGraphicsNameDef \"Primary\")\n")
dst_f.write(" (multiLayer\n")
pi = 1
for p in pads:
dst_f.write(" (pad (padNum " + str(pi) + ") (padStyleRef \"PAD" + str(p[0]) + "\") (pt " + p[2] + "mm " + p[3] + "mm) (rotation " + p[4] + ") (defaultPinDes \"" + p[1] + "\"))\n")
pi += 1
dst_f.write(" )\n")
line_layers = [ [ "F.SilkS", 6 ], [ "B.SilkS", 7 ], [ "F.Zone", 12 ], [ "B.Zone", 13 ] ]
# output lines
for ll in line_layers:
dst_f.write(" (layerContents (layerNumRef " + str(ll[1]) + ")\n")
for l in lines:
if (l[4] == ll[0]):
dst_f.write(" (line (pt " + l[0] + "mm " + l[1] + "mm) (pt " + l[2] + "mm " + l[3] + "mm) (width " + l[5] + "mm) )\n")
dst_f.write(" )\n")
dst_f.write(" )\n )\n")
dst_f.write(" (compDef \"" + mod_name + "_1\"\n (originalName \"" + mod_name + "\")\n")
dst_f.write(" (compHeader\n (sourceLibrary \"\")\n (numPins " + str(len(pads)) + ")\n (numParts 1)\n (refDesPrefix \"\")\n")
dst_f.write(" )\n")
for p in pads:
dst_f.write(" (compPin \"" + p[5] + "\" (partNum 1) (symPinNum 1) (gateEq 0) (pinEq 0) )\n")
dst_f.write(" (attachedPattern (patternNum 1) (patternName \"" + mod_name + "\")\n")
dst_f.write(" (numPads " + str(len(pads)) + ")\n")
dst_f.write(" (padPinMap\n")
pi = 1
for p in pads:
dst_f.write(" (padNum " + str(pi) + ") (compPinRef \"" + p[5] + "\")\n")
pi += 1
dst_f.write(" )\n )\n )\n)\n")
#################################################
print ("Processing the pcb file...")
#process_pcb(src_path + "/hellen1-" + name + ".kicad_pcb", dst_path + "/" + name + "/" + rev + "/" + name + ".kicad_mod")
process_pcb("wbo.kicad_mod", "wbo.LIA")
print ("Done!")