-
-
Notifications
You must be signed in to change notification settings - Fork 239
Add Gatema PCB fab option #877
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| # Fabrication: Gatema PCB | ||
|
|
||
| The basic usage of this exporter is: | ||
| ``` | ||
| kikit fab gatema [OPTIONS] BOARD OUTPUTDIR | ||
| ``` | ||
|
|
||
| When you run this command, you will find file `gerbers.zip` in `OUTPUTDIR`. This | ||
| file can be directly uploaded to Gatema PCB site. KiKit automatically detects the | ||
| number of layers. If you would like to include the project name in the archive | ||
| name, you can supply `--autoname` | ||
|
|
||
| If you want to name your files differently, you can specify `--nametemplate`. | ||
| This option takes a string that should contain `{}`. This string will be | ||
| replaced by `gerber`, `pos` or `bom` in the out file names. The extension is | ||
| appended automatically. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,132 @@ | ||
| import click | ||
| from pcbnewTransition import pcbnew | ||
| import csv | ||
| import os | ||
| import sys | ||
| import shutil | ||
| from pathlib import Path | ||
| from kikit.fab.common import * | ||
| from kikit.common import * | ||
| from kikit.export import gerberImpl | ||
| from kikit.export import exportSettingsGatema | ||
|
|
||
| #def collectBom(components, lscsFields, ignore): | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please, no commented-out code. |
||
| # bom = {} | ||
| # for c in components: | ||
| # if getUnit(c) != 1: | ||
| # continue | ||
| # reference = getReference(c) | ||
| # if reference.startswith("#PWR") or reference.startswith("#FL"): | ||
| # continue | ||
| # if reference in ignore: | ||
| # continue | ||
| # if getField(c, "JLCPCB_IGNORE") is not None and getField(c, "JLCPCB_IGNORE") != "": | ||
| # continue | ||
| # if hasattr(c, "in_bom") and not c.in_bom: | ||
| # continue | ||
| # if hasattr(c, "on_board") and not c.on_board: | ||
| # continue | ||
| # if hasattr(c, "dnp") and c.dnp: | ||
| # continue | ||
| # orderCode = None | ||
| # for fieldName in lscsFields: | ||
| # orderCode = getField(c, fieldName) | ||
| # if orderCode is not None and orderCode.strip() != "": | ||
| # break | ||
| # cType = ( | ||
| # getField(c, "Value"), | ||
| # getField(c, "Footprint"), | ||
| # orderCode | ||
| # ) | ||
| # bom[cType] = bom.get(cType, []) + [reference] | ||
| # return bom | ||
|
|
||
| #def bomToCsv(bomData, filename): | ||
| # with open(filename, "w", newline="", encoding="utf-8") as csvfile: | ||
| # writer = csv.writer(csvfile) | ||
| # writer.writerow(["Comment", "Designator", "Footprint", "LCSC"]) | ||
| # for cType, references in bomData.items(): | ||
| # # JLCPCB allows at most 200 components per line so we have to split | ||
| # # the BOM into multiple lines. Let's make the chunks by 100 just to | ||
| # # be sure. | ||
| # CHUNK_SIZE = 100 | ||
| # sortedReferences = sorted(references, key=naturalComponentKey) | ||
| # for i in range(0, len(references), CHUNK_SIZE): | ||
| # refChunk = sortedReferences[i:i+CHUNK_SIZE] | ||
| # value, footprint, lcsc = cType | ||
| # writer.writerow([value, ",".join(refChunk), footprint, lcsc]) | ||
|
|
||
| def exportGatema(board, outputdir, ignore, nametemplate, drc, | ||
| autoname): | ||
| """ | ||
| Prepare fabrication files for Gatema PCB | ||
| """ | ||
| ensureValidBoard(board) | ||
| loadedBoard = pcbnew.LoadBoard(board) | ||
|
|
||
| if drc: | ||
| ensurePassingDrc(loadedBoard) | ||
|
|
||
| refsToIgnore = parseReferences(ignore) | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When you are not doing assembly, I think this is not needed. |
||
| removeComponents(loadedBoard, refsToIgnore) | ||
| Path(outputdir).mkdir(parents=True, exist_ok=True) | ||
|
|
||
| gerberdir = os.path.join(outputdir, "gerber") | ||
| shutil.rmtree(gerberdir, ignore_errors=True) | ||
| gerberImpl(board, gerberdir, settings=exportSettingsGatema) | ||
| ext_list = [("-CuTop.gtl", ".top"), ("-CuBottom.gbl", ".bot"), ("-MaskTop.gts", ".smt"), ("-MaskBottom.gbs", ".smb"), ("-NPTH.drl", ".mill"), ("-PTH.drl", ".pth"), ("-SilkTop.gto", ".plt"), ("-SilkBottom.gbo", ".plb"), ("-EdgeCuts.gm1", ".dim"), ("-PasteTop.gtp", ".pastetop"), ("-PasteBottom.gbp", ".pastebot")] | ||
| for f in os.listdir(gerberdir): | ||
| unneded = True | ||
| # mayby drrilling should be using the map - not tested | ||
| for old, new in ext_list: | ||
| if f.endswith(old): | ||
| unneded = False | ||
| newname = f.replace(old, new) | ||
| os.rename(os.path.join(gerberdir, f), os.path.join(gerberdir, newname)) | ||
| break | ||
| if unneded: | ||
| os.remove(os.path.join(gerberdir, f)) # remove unneeded files | ||
|
|
||
|
|
||
| if autoname: | ||
| boardName = os.path.basename(board.replace(".kicad_pcb", "")) | ||
| archiveName = expandNameTemplate(nametemplate, boardName + "-gerbers", loadedBoard) | ||
| else: | ||
| archiveName = expandNameTemplate(nametemplate, "gerbers", loadedBoard) | ||
| shutil.make_archive(os.path.join(outputdir, archiveName), "zip", outputdir, "gerber") | ||
|
|
||
| # if not assembly: | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No commented-out code, please |
||
| # return | ||
| # if schematic is None: | ||
| # raise RuntimeError("When outputing assembly data, schematic is required") | ||
| # | ||
| # ensureValidSch(schematic) | ||
| # | ||
| # correctionFields = [x.strip() for x in corrections.split(",")] | ||
| # components = extractComponents(schematic) | ||
| # ordercodeFields = [x.strip() for x in field.split(",")] | ||
| # bom = collectBom(components, ordercodeFields, refsToIgnore) | ||
| # | ||
| # bom_refs = set(x for xs in bom.values() for x in xs) | ||
| # bom_components = [c for c in components if getReference(c) in bom_refs] | ||
| # | ||
| # posData = collectPosData(loadedBoard, correctionFields, | ||
| # bom=bom_components, posFilter=noFilter, correctionFile=correctionpatterns, | ||
| # orientationHandling=FootprintOrientationHandling.MirrorBottom) | ||
| # boardReferences = set([x[0] for x in posData]) | ||
| # bom = {key: [v for v in val if v in boardReferences] for key, val in bom.items()} | ||
| # bom = {key: val for key, val in bom.items() if len(val) > 0} | ||
| # | ||
| # | ||
| # missingFields = False | ||
| # for type, references in bom.items(): | ||
| # _, _, lcsc = type | ||
| # if not lcsc: | ||
| # missingFields = True | ||
| # for r in references: | ||
| # print(f"WARNING: Component {r} is missing ordercode") | ||
| # if missingFields and missingerror: | ||
| # sys.exit("There are components with missing ordercode, aborting") | ||
| # | ||
| # posDataToFile(posData, os.path.join(outputdir, expandNameTemplate(nametemplate, "pos", loadedBoard) + ".csv")) | ||
| # bomToCsv(bom, os.path.join(outputdir, expandNameTemplate(nametemplate, "bom", loadedBoard) + ".csv")) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -41,6 +41,20 @@ def jlcpcb(**kwargs): | |
| app = fakeKiCADGui() | ||
| return execute_with_debug(jlcpcb.exportJlcpcb, kwargs) | ||
|
|
||
| @click.command() | ||
| @fabCommand | ||
| @click.option("--ignore", type=str, default="", help="Comma separated list of designators to exclude from SMT assembly") | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As Gatema doesn't do assembly, why one would need an option to ignore components? |
||
| @click.option("--autoname/--no-autoname", is_flag=True, help="Automatically name the output files based on the board name") | ||
| def gatema(**kwargs): | ||
| """ | ||
| Prepare fabrication files for Gatema PCB | ||
| """ | ||
| from kikit.fab import gatema | ||
| from kikit.common import fakeKiCADGui | ||
| app = fakeKiCADGui() | ||
| return execute_with_debug(gatema.exportGatema, kwargs) | ||
|
|
||
|
|
||
| @click.command() | ||
| @fabCommand | ||
| @click.option("--assembly/--no-assembly", help="Generate files for SMT assembly (schematics is required)") | ||
|
|
@@ -120,6 +134,7 @@ def fab(): | |
| pass | ||
|
|
||
| fab.add_command(jlcpcb) | ||
| fab.add_command(gatema) | ||
| fab.add_command(pcbway) | ||
| fab.add_command(oshpark) | ||
| fab.add_command(neodenyy1) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I see. You renamed the file
kikit.typingtokikit.kityping- why? This breaks other places where the file is imported.