Changed job strategy to find all projects and run kibot on them.
Also added: - script to produce neoden yy1 compatible pnp files - panelisation capability which runs for all compabatible .json files.
This commit is contained in:
170
.gitlab-ci.yml
170
.gitlab-ci.yml
@@ -8,60 +8,100 @@ workflow:
|
||||
stages:
|
||||
- run_erc
|
||||
- run_drc
|
||||
- gen_mech
|
||||
- mech_out
|
||||
- gen_fab
|
||||
- fab_out
|
||||
|
||||
image:
|
||||
name: ghcr.io/inti-cmnb/kicad6_auto:1.3.0
|
||||
name: ghcr.io/inti-cmnb/kicad7_auto:latest
|
||||
|
||||
.main_rules:
|
||||
rules:
|
||||
- if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "main"'
|
||||
- if: $CI_COMMIT_BRANCH == "main"
|
||||
- if: $GITLAB_CI == 'false'
|
||||
|
||||
.dev_rules:
|
||||
rules:
|
||||
- if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "dev"'
|
||||
- if: $CI_COMMIT_BRANCH == "dev"
|
||||
|
||||
|
||||
.commands:
|
||||
get_files:
|
||||
- |
|
||||
if find **/*$SEARCH ; then
|
||||
FILES=$(find **/*$SEARCH)
|
||||
else
|
||||
FILES=''
|
||||
fi
|
||||
get_dirs:
|
||||
- |
|
||||
if find **/*$SEARCH ; then
|
||||
FILES=$(find **/*$SEARCH)
|
||||
DIRS=$(dirname $FILES)
|
||||
else
|
||||
FILES=''
|
||||
DIRS=''
|
||||
fi
|
||||
- ''
|
||||
sch_from_pro:
|
||||
- 'SCHEMS=$(for f in $FILES ; do echo "${f%.*}.kicad_sch"; done)'
|
||||
- 'SCHEMS=$(for f in $SCHEMS ; do echo "${f##**/}"; done)'
|
||||
get_names:
|
||||
- 'NAMES=$(for f in $FILES ; do echo "${**/f%$SEARCH}"; done)'
|
||||
dir_arr:
|
||||
- !reference [.commands, get_dirs]
|
||||
- END=$(wc -w <<< $DIRS)
|
||||
- dir_arr=($DIRS)
|
||||
kibot:
|
||||
- 'SEARCH=".kicad_pro"'
|
||||
- !reference [.commands, dir_arr]
|
||||
- !reference [.commands, sch_from_pro]
|
||||
- sch_arr=($SCHEMS)
|
||||
- |
|
||||
for i in $(seq 1 $END)
|
||||
do
|
||||
echo ${dir_arr[i-1]}
|
||||
cd $CI_PROJECT_DIR/${dir_arr[i-1]}
|
||||
kibot -e ${sch_arr[i-1]} -c $CI_PROJECT_DIR/default.kibot.yaml -d $CI_PROJECT_DIR/Fabrication/${dir_arr[i-1]} -s $SUFFIX
|
||||
done
|
||||
|
||||
panel:
|
||||
- 'SEARCH="boards.json"'
|
||||
- !reference [.commands, get_dirs]
|
||||
- |
|
||||
for d in $DIRS
|
||||
do
|
||||
echo $d
|
||||
cd $CI_PROJECT_DIR/$d
|
||||
JSON=$(find panel_*.json )
|
||||
NAME=$(echo "${JSON%.json}")
|
||||
mkdir panel
|
||||
kikit -p $JSON boards.json panel/$NAME.kicad_pcb
|
||||
touch panel/$NAME.kicad_sch
|
||||
done
|
||||
|
||||
neo:
|
||||
- 'SEARCH=".kicad_pro"'
|
||||
- !reference [.commands, get_dirs]
|
||||
- |
|
||||
for d in $DIRS
|
||||
do
|
||||
python3 scripts/neo.py $d
|
||||
done
|
||||
|
||||
.template:
|
||||
rules:
|
||||
- !reference [.main_rules, rules]
|
||||
variables:
|
||||
COMMAND: kibot -e "$SCHEM" -c "$CI_PROJECT_DIR/default.kibot.yaml" -d "$CI_PROJECT_DIR/Fabrication/$DIR" -s
|
||||
vars:
|
||||
SUFFIX: ""
|
||||
script:
|
||||
- cd "$DIR"
|
||||
- echo $COMMAND $SUFFIX | bash
|
||||
- !reference [.commands, kibot]
|
||||
dependencies: []
|
||||
parallel:
|
||||
matrix:
|
||||
|
||||
.tempout:
|
||||
extends: .template
|
||||
artifacts:
|
||||
when: always
|
||||
expire_in: 2 mins
|
||||
paths:
|
||||
- Fabrication/
|
||||
|
||||
.temprc:
|
||||
extends: .template
|
||||
rules:
|
||||
- !reference [.main_rules, rules]
|
||||
- !reference [.dev_rules, rules]
|
||||
|
||||
.tempcoll:
|
||||
rules:
|
||||
- !reference [.main_rules, rules]
|
||||
script:
|
||||
- ls Fabrication/
|
||||
artifacts:
|
||||
when: always
|
||||
paths:
|
||||
- Fabrication/
|
||||
|
||||
erc:
|
||||
extends: .temprc
|
||||
@@ -75,46 +115,38 @@ drc:
|
||||
variables:
|
||||
SUFFIX: update_xml,run_erc -i
|
||||
|
||||
cad_outputs:
|
||||
extends: .tempout
|
||||
outputs_dev:
|
||||
rules:
|
||||
- !reference [.dev_rules, rules]
|
||||
stage: gen_fab
|
||||
artifacts:
|
||||
when: always
|
||||
paths:
|
||||
- Fabrication/
|
||||
variables:
|
||||
SUFF_SCH: run_drc,run_erc print_sch step
|
||||
SUFF_PCB: all print_front
|
||||
script:
|
||||
- SUFFIX=$SUFF_SCH
|
||||
- !reference [.commands, kibot]
|
||||
- SUFFIX=$SUFF_PCB
|
||||
- !reference [.commands, kibot]
|
||||
|
||||
outputs_all:
|
||||
extends: outputs_dev
|
||||
rules:
|
||||
- !reference [.main_rules, rules]
|
||||
- !reference [.dev_rules, rules]
|
||||
stage: gen_mech
|
||||
variables:
|
||||
SUFFIX: run_drc step
|
||||
|
||||
mech_outputs:
|
||||
extends: .tempcoll
|
||||
rules:
|
||||
- !reference [.main_rules, rules]
|
||||
- !reference [.dev_rules, rules]
|
||||
stage: mech_out
|
||||
dependencies:
|
||||
- cad_outputs
|
||||
|
||||
sch_outputs:
|
||||
extends: .tempout
|
||||
stage: gen_fab
|
||||
variables:
|
||||
SUFFIX: run_drc,run_erc print_sch
|
||||
|
||||
pcb_outputs:
|
||||
extends: .tempout
|
||||
stage: gen_fab
|
||||
variables:
|
||||
SUFFIX: all print_front JLCPCB_fab assembly step
|
||||
|
||||
bom:
|
||||
extends: .tempout
|
||||
stage: gen_fab
|
||||
variables:
|
||||
SUFFIX: all bom
|
||||
|
||||
fab_outputs:
|
||||
extends: .tempcoll
|
||||
stage: fab_out
|
||||
dependencies:
|
||||
- sch_outputs
|
||||
- pcb_outputs
|
||||
- bom
|
||||
SUFF_SCH: run_drc,run_erc print_sch step bom
|
||||
SUFF_PCB: all print_front gerbers drill position
|
||||
script:
|
||||
- SUFFIX=$SUFF_SCH
|
||||
- !reference [.commands, kibot]
|
||||
- cd $CI_PROJECT_DIR
|
||||
- !reference [.commands, panel]
|
||||
- SUFFIX=$SUFF_PCB
|
||||
- cd $CI_PROJECT_DIR
|
||||
- !reference [.commands, kibot]
|
||||
- cd $CI_PROJECT_DIR
|
||||
- ls Fabrication/interface
|
||||
- !reference [.commands, neo]
|
||||
|
||||
@@ -60,6 +60,16 @@ outputs:
|
||||
download: true
|
||||
kicad_3d_url: https://gitlab.com/kicad/libraries/kicad-packages3D/-/raw/master/
|
||||
|
||||
- name: 'position'
|
||||
comment: "Pick and place"
|
||||
type: position
|
||||
options:
|
||||
output: '%f_cpl.%x'
|
||||
format: CSV
|
||||
units: millimeters
|
||||
separate_files_for_front_and_back: false
|
||||
only_smd: true
|
||||
|
||||
- name: 'assembly'
|
||||
comment: "Pick and place file, JLC style"
|
||||
type: position
|
||||
|
||||
156
scripts/neo.py
Normal file
156
scripts/neo.py
Normal file
@@ -0,0 +1,156 @@
|
||||
import csv
|
||||
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 = "-all-pos"
|
||||
|
||||
|
||||
pcb_file = glob("*.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)
|
||||
|
||||
|
||||
csv_files = glob("*.csv")
|
||||
input = next(filter(lambda f: KI_POS_SUFFIX in f, csv_files))
|
||||
output = input.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 = 0
|
||||
# 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]
|
||||
if rotation > 180.0:
|
||||
rotation = -1 * (360.0 - rotation)
|
||||
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(origin[0] - fid_pos[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)
|
||||
Reference in New Issue
Block a user