Skip to content
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

Support merging multiple schematics for fab output #632

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 55 additions & 5 deletions kikit/fab/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,59 @@
from kikit import eeschema_v6 # import getField, getUnit, getReference
from kikit import eeschema #import getField, getUnit, getReference

def filterComponents(components, refsToIgnore, ignoreField):
filtered = []
for c in components:
if getUnit(c) != 1:
continue
reference = getReference(c)
if reference.startswith("#PWR") or reference.startswith("#FL"):
continue
if reference in refsToIgnore:
continue
if getField(c, ignoreField) is not None and getField(c, ignoreField) != "":
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
filtered.append(c)
return filtered

# A user can still supply v5 schematics even when we run v6, therefore,
# we have to load the correct schematics and provide the right getters
def extractComponents(filename):
def extractComponents(filename, refsToIgnore, ignoreField):
if isinstance(filename, (list, tuple)):
mergedComponents = []
seenRefs = dict()

for f in filename:
components = extractComponents(f, refsToIgnore, ignoreField)
for c in components:
ref = getReference(c)

seen = seenRefs.get(ref)
if seen == f:
raise RuntimeError(
f"Duplicate reference designator: '{f}' defines reference '{ref}' more than once."
)
elif seen:
raise RuntimeError(
f"Duplicate reference designator: both '{f}' and '{seen}' define reference '{ref}'. "
+ "When using multiple schematics, component references must be unique across all schematics."
)

seenRefs[ref] = f
mergedComponents.append(c)

return mergedComponents

if filename.endswith(".kicad_sch"):
return eeschema_v6.extractComponents(filename)
return filterComponents(eeschema_v6.extractComponents(filename), refsToIgnore, ignoreField)
if filename.endswith(".sch"):
return eeschema.extractComponents(filename)
return filterComponents(eeschema.extractComponents(filename), refsToIgnore, ignoreField)
raise RuntimeError(f"Unknown schematic file type specified: {filename}")

def getUnit(component):
Expand Down Expand Up @@ -223,8 +269,12 @@ def isValidBoardPath(filename):
return os.path.splitext(filename)[1] in [".kicad_pcb"]

def ensureValidSch(filename):
if not isValidSchPath(filename):
raise RuntimeError(f"The path {filename} is not a valid KiCAD schema file")
if isinstance(filename, (list, tuple)):
for f in filename:
ensureValidSch(f)
else:
if not isValidSchPath(filename):
raise RuntimeError(f"The path {filename} is not a valid KiCAD schema file")

def ensureValidBoard(filename):
if not isValidBoardPath(filename):
Expand Down
23 changes: 4 additions & 19 deletions kikit/fab/jlcpcb.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,9 @@
from kikit.common import *
from kikit.export import gerberImpl

def collectBom(components, lscsFields, ignore):
def collectBom(components, lscsFields):
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)
Expand All @@ -37,7 +22,7 @@ def collectBom(components, lscsFields, ignore):
getField(c, "Footprint"),
orderCode
)
bom[cType] = bom.get(cType, []) + [reference]
bom[cType] = bom.get(cType, []) + [getReference(c)]
return bom

def bomToCsv(bomData, filename):
Expand Down Expand Up @@ -85,9 +70,9 @@ def exportJlcpcb(board, outputdir, assembly, schematic, ignore, field,
ensureValidSch(schematic)

correctionFields = [x.strip() for x in corrections.split(",")]
components = extractComponents(schematic)
components = extractComponents(schematic, refsToIgnore, "JLCPCB_IGNORE")
ordercodeFields = [x.strip() for x in field.split(",")]
bom = collectBom(components, ordercodeFields, refsToIgnore)
bom = collectBom(components, ordercodeFields)

posData = collectPosData(loadedBoard, correctionFields,
bom=components, posFilter=noFilter, correctionFile=correctionpatterns)
Expand Down
21 changes: 4 additions & 17 deletions kikit/fab/neodenyy1.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,27 +18,14 @@
re.compile(r'Crystal:Crystal_SMD_(.*?)_.*'): 'CRYSTAL_{}'
}

def collectBom(components, ignore):
def collectBom(components):
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 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
cType = (
getField(c, "Value"),
getField(c, "Footprint")
)
bom[cType] = bom.get(cType, []) + [reference]
bom[cType] = bom.get(cType, []) + [getReference(c)]
return bom

def transcodeFootprint(footprint):
Expand Down Expand Up @@ -146,8 +133,8 @@ def exportNeodenYY1(board, outputdir, schematic, ignore,
ensureValidSch(schematic)

correctionFields = [x.strip() for x in corrections.split(",")]
components = extractComponents(schematic)
bom = collectBom(components, refsToIgnore)
components = extractComponents(schematic, refsToIgnore, "NEODENYY1_IGNORE")
bom = collectBom(components)

posData = collectPosData(loadedBoard, correctionFields,
bom=components, posFilter=noFilter, correctionFile=correctionpatterns)
Expand Down
21 changes: 4 additions & 17 deletions kikit/fab/pcbway.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ def addVirtualToRefsToIgnore(refsToIgnore, board):
refsToIgnore.append(footprint.GetReference())

def collectBom(components, manufacturerFields, partNumberFields,
descriptionFields, notesFields, typeFields, footprintFields,
ignore):
descriptionFields, notesFields, typeFields, footprintFields):
bom = {}

# Use KiCad footprint as fallback for footprint
Expand All @@ -38,17 +37,6 @@ def collectBom(components, manufacturerFields, partNumberFields,
descriptionFields.append("Value")

for c in components:
if getUnit(c) != 1:
continue
reference = getReference(c)
if reference.startswith("#PWR") or reference.startswith("#FL") or reference in 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
manufacturer = None
for manufacturerName in manufacturerFields:
manufacturer = getField(c, manufacturerName)
Expand Down Expand Up @@ -88,7 +76,7 @@ def collectBom(components, manufacturerFields, partNumberFields,
notes,
solderType
)
bom[cType] = bom.get(cType, []) + [reference]
bom[cType] = bom.get(cType, []) + [getReference(c)]
return bom

def bomToCsv(bomData, filename, nBoards, types):
Expand Down Expand Up @@ -146,8 +134,7 @@ def exportPcbway(board, outputdir, assembly, schematic, ignore,

ensureValidSch(schematic)


components = extractComponents(schematic)
components = extractComponents(schematic, refsToIgnore, "PCBWAY_IGNORE")
correctionFields = [x.strip() for x in corrections.split(",")]
manufacturerFields = [x.strip() for x in manufacturer.split(",")]
partNumberFields = [x.strip() for x in partnumber.split(",")]
Expand All @@ -158,7 +145,7 @@ def exportPcbway(board, outputdir, assembly, schematic, ignore,
addVirtualToRefsToIgnore(refsToIgnore, loadedBoard)
bom = collectBom(components, manufacturerFields, partNumberFields,
descriptionFields, notesFields, typeFields,
footprintFields, refsToIgnore)
footprintFields)

missingFields = False
for type, references in bom.items():
Expand Down
9 changes: 6 additions & 3 deletions kikit/fab_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ def execute(fab, kwargs):
@click.command()
@fabCommand
@click.option("--assembly/--no-assembly", help="Generate files for SMT assembly (schematics is required)")
@click.option("--schematic", type=click.Path(dir_okay=False), help="Board schematics (required for assembly files)")
@click.option("--schematic", type=click.Path(dir_okay=False), multiple=True,
help="Board schematics (required for assembly files, can be specified more than once to merge components from multiple schematics)")
@click.option("--ignore", type=str, default="", help="Comma separated list of designators to exclude from SMT assembly")
@click.option("--field", type=str, default="LCSC",
help="Comma separated list of component fields field with LCSC order code. First existing field is used")
Expand All @@ -57,7 +58,8 @@ def jlcpcb(**kwargs):
@click.command()
@fabCommand
@click.option("--assembly/--no-assembly", help="Generate files for SMT assembly (schematics is required)")
@click.option("--schematic", type=click.Path(dir_okay=False), help="Board schematics (required for assembly files)")
@click.option("--schematic", type=click.Path(dir_okay=False), multiple=True,
help="Board schematics (required for assembly files, can be specified more than once to merge components from multiple schematics)")
@click.option("--ignore", type=str, default="", help="Comma separated list of designators to exclude from SMT assembly")
@click.option("--corrections", type=str, default="PCBWAY_CORRECTION",
help="Comma separated list of component fields with the correction value. First existing field is used")
Expand Down Expand Up @@ -100,7 +102,8 @@ def oshpark(**kwargs):

@click.command()
@fabCommand
@click.option("--schematic", type=click.Path(dir_okay=False), help="Board schematics (required for assembly files)")
@click.option("--schematic", type=click.Path(dir_okay=False), multiple=True,
help="Board schematics (required for assembly files, can be specified more than once to merge components from multiple schematics)")
@click.option("--ignore", type=str, default="", help="Comma separated list of designators to exclude from SMT assembly")
@click.option("--corrections", type=str, default="YY1_CORRECTION",
help="Comma separated list of component fields with the correction value. First existing field is used")
Expand Down