Files
kicad-ci/.scripts/neo.py

168 lines
5.4 KiB
Python

import csv
import os
import sys
from copy import deepcopy
from glob import glob
from kikit.panelize import Origin,getOriginCoord
from kikit.common import findBoardBoundingBox
from pcbnewTransition.pcbnew import LoadBoard
YY1_SEP = ["","","","","","","","","","","","","",""]
YY1_HEADER = ["NEODEN","YY1","P&P FILE","","","","","","","","","","",""]
YY1_PANEL = ["PanelizedPCB","UnitLength",0,"UnitWidth",0,"Rows",1,"Columns",1,]
YY1_NOZZLE_DEF = ["NozzleChange","OFF","BeforeComponent",1,"Head1","Drop","Station1","PickUp","Station1",]
YY1_COMP_HEADER = ["Designator","Comment","Footprint","Mid X(mm)","Mid Y(mm) ","Rotation","Head ","FeederNo","Mount Speed(%)","Pick Height(mm)","Place Height(mm)","Mode", "Skip"]
YY1_EXTRA_IND = YY1_COMP_HEADER.index("Head ")
YY1_FEEDER_IND = YY1_COMP_HEADER.index("FeederNo") - YY1_EXTRA_IND
YY1_DEF_EXTRA = [0, 0, 50, 0.0, 0.0, 1, 0]
KI_POS_SUFFIX = "_cpl"
pcb_dir = sys.argv[1]
pcb_file = glob(pcb_dir + "/*.kicad_pcb")
board = LoadBoard(pcb_file[0])
sourceArea = findBoardBoundingBox(board)
bottomOrig = [(a - b)/10**6 for a,b in zip(*[list(getOriginCoord(o, sourceArea)) for o in [Origin.BottomRight, Origin.BottomLeft]])]
print("bottom Orig: ", bottomOrig)
output_dir = deepcopy(pcb_dir)
csv_files = glob(output_dir + "/*.csv")
print("CSV Files: ", csv_files)
if len(csv_files) < 1:
output_dir = os.path.join(os.environ.get('CI_PROJECT_DIR', ''), '') + "Fabrication/" + output_dir
csv_files = glob(output_dir + "/*.csv")
input = next(filter(lambda f: KI_POS_SUFFIX in f, csv_files))
output = output_dir + "/" + input.split("/")[-1].split(KI_POS_SUFFIX)[0] + "-neo-pos.csv"
try:
feed_opt = next(filter(lambda f: "feed" in f, csv_files))
except:
feed_opt = None
rows_in = []
with open(input, newline='') as csvfile:
spamreader = csv.reader(csvfile, delimiter=',', quotechar='|')
for row in spamreader:
# Round floats to 2 places
for n, r in enumerate(row):
r = r.strip('"')
row[n] = r
try:
r = round(float(r),2)
row[n] = "{:.2f}".format(r)
except:
continue
rows_in.append(row)
in_header = rows_in[0]
posx = in_header.index("PosX")
posy = in_header.index("PosY")
desig = in_header.index("Ref")
comm = in_header.index("Val")
foot = in_header.index("Package")
rot = in_header.index("Rot")
out_inds = [desig, comm, foot, posx, posy, rot]
def pos_diff(pos1, pos2):
return sum([(pos1[i] - pos2[i])**2 for i in range(2)])**1/2
# Fiducial
fids = [x for x in filter(lambda x: "Fiducial" in x, rows_in)]
for f in fids:
rows_in.remove(f)
# Components
comps = rows_in[1:]
comp_feed = {}
feeder = 1
# Load in optional file
if feed_opt is not None:
# Read in feeder options for components
with open(feed_opt, newline='') as csvfile:
spamreader = csv.reader(csvfile, delimiter=',', quotechar='|')
for row in spamreader:
comp_feed[row[0]] = row[1:]
f = int(row[YY1_FEEDER_IND])
if f > feeder:
feeder = f
def comp_name(row):
return row[foot] + row[comm]
# Add all component types
for comp in comps:
name = comp_name(comp)
if name not in comp_feed:
entry = deepcopy(YY1_DEF_EXTRA)
entry[YY1_FEEDER_IND] = feeder
comp_feed[name] = entry
feeder += 1
def comp_format(filt, offset):
filt_rows = filter(filt, rows_in[1:])
out = []
for r in filt_rows:
t = []
for i in out_inds:
t.append(r[i])
t.extend(comp_feed[comp_name(r)])
# Cannot go below 0, so just use abs to take care of sign
for n,p in enumerate([out_inds.index(n) for n in [posx, posy]]):
t[p] = "{:.2f}".format(abs(float(t[p]) - offset[n]))
rotation = float(t[rot])
rotation = rotation + offset[2]
# Wrap the phase [-180, 180]
rotation = (rotation + 180.0) % (2 * 180.0) - 180.0
t[rot] = "{:.2f}".format(rotation)
out.append(t)
return out
# Top and bottom
origins = {"top": [0.0,0.0], "bottom": list(bottomOrig)}
print("origins:", origins)
for tag, origin in origins.items():
rotation = 180.0 * (tag != "top")
print("rot: ", rotation)
# Find fiducial furthest away from origin
fid_pos = deepcopy(origin)
fid_dist = 0.0
for f in fids:
pos = [float(f[posx]), float(f[posy])]
d = pos_diff(pos, origin)
if d > fid_dist :
fid_pos = pos
fid_dist = d
print("Fid pos: ", fid_pos)
# component formatting
comp = comp_format(lambda x: tag in x, origin + [rotation])
# Format output
rows_out = [YY1_HEADER, YY1_SEP, YY1_PANEL, YY1_SEP]
# y is still same dir, but x gets flipped, so the x calc is opposite sign
rows_out.append(["Fiducial","1-X", "{:.2f}".format(abs(fid_pos[0] - origin[0])) ,"1-Y","{:.2f}".format(fid_pos[1] - origin[1]) ,"OverallOffsetX",0,"OverallOffsetY",0,])
rows_out.append(YY1_SEP)
for _ in range(4):
rows_out.append(YY1_NOZZLE_DEF)
rows_out.append(YY1_SEP)
rows_out.append(YY1_COMP_HEADER)
rows_out.extend(comp)
# Write output
with open(output.split(".csv")[0] + "_" + tag + ".csv", 'w', newline='') as csvfile:
spamwriter = csv.writer(csvfile, delimiter=',',
quotechar='|', quoting=csv.QUOTE_MINIMAL)
for row in rows_out:
spamwriter.writerow(row)