2021-06-22 10:51:43 -06:00
|
|
|
import os
|
2022-10-01 14:57:23 -06:00
|
|
|
import zipfile
|
2021-06-22 10:51:43 -06:00
|
|
|
from datetime import datetime
|
|
|
|
from pathlib import Path
|
2022-10-01 14:57:23 -06:00
|
|
|
|
|
|
|
import pcbnew
|
2021-06-22 10:51:43 -06:00
|
|
|
|
|
|
|
__all__ = ["FabOutputs"]
|
|
|
|
|
|
|
|
|
|
|
|
class FabOutputs(pcbnew.ActionPlugin):
|
|
|
|
def defaults(self):
|
|
|
|
self.name = "Generate Outputs"
|
|
|
|
self.category = "Output Generation"
|
|
|
|
self.description = "Generate Gerbers, BOM, Drawings, etc and add to pdf/zip"
|
|
|
|
|
|
|
|
self.show_toolbar_button = True
|
2021-07-01 23:47:02 -06:00
|
|
|
self.icon_file_name = os.path.join(os.path.dirname(__file__), "icon.png")
|
2021-06-22 10:51:43 -06:00
|
|
|
|
|
|
|
def Run(self):
|
|
|
|
# ================
|
|
|
|
# General Data
|
|
|
|
# ================
|
|
|
|
|
|
|
|
now = datetime.now()
|
|
|
|
pcb = pcbnew.GetBoard()
|
|
|
|
path_cwd = Path.cwd()
|
|
|
|
path_pcb = Path(pcb.GetFileName())
|
|
|
|
dir_pcb = path_pcb.parent
|
|
|
|
dir_fab = dir_pcb / "FabricationOutputs"
|
|
|
|
dir_asy = dir_pcb / "AssemblyOutputs"
|
|
|
|
|
|
|
|
project_name = path_pcb.stem
|
|
|
|
part_number = pcb.GetTitleBlock().GetTitle()
|
|
|
|
rev = str.upper(pcb.GetTitleBlock().GetRevision())
|
|
|
|
|
|
|
|
suffix = ""
|
2021-06-29 02:33:05 -06:00
|
|
|
if rev != "":
|
|
|
|
suffix += f"_REV{rev}"
|
2021-06-22 10:51:43 -06:00
|
|
|
suffix += f"_{now.strftime('%Y%m%d_%H%M%S')}"
|
|
|
|
|
|
|
|
layer_count = pcb.GetDesignSettings().GetCopperLayerCount()
|
|
|
|
|
|
|
|
layer_info = [
|
|
|
|
("Front Paste", "gtp", pcbnew.F_Paste),
|
|
|
|
("Front Silkscreen", "gto", pcbnew.F_SilkS),
|
|
|
|
("Front Mask", "gts", pcbnew.F_Mask),
|
|
|
|
("Front Copper", "gtl", pcbnew.F_Cu),
|
2022-10-01 14:57:23 -06:00
|
|
|
*[
|
|
|
|
(f"Inner Layer {layer} Copper", f"g{layer}", layer)
|
|
|
|
for layer in range(1, layer_count - 1)
|
|
|
|
],
|
|
|
|
("Back Copper", "gbl", pcbnew.B_Cu),
|
|
|
|
("Back Mask", "gbs", pcbnew.B_Mask),
|
|
|
|
("Back SilkScreen", "gbo", pcbnew.B_SilkS),
|
|
|
|
("Back Paste", "gbp", pcbnew.B_Paste),
|
|
|
|
("Edges Cuts", "gm1", pcbnew.Edge_Cuts),
|
|
|
|
("Drill", "drl", None),
|
2021-06-22 10:51:43 -06:00
|
|
|
]
|
|
|
|
|
|
|
|
stackup = [
|
|
|
|
# [pcbnew layer, file extension, thickness, comment]
|
2022-10-01 14:57:23 -06:00
|
|
|
[pcbnew.F_Paste, "gtp", None, "SN63/PB37"],
|
|
|
|
[pcbnew.F_SilkS, "gto", None, "White"],
|
|
|
|
[pcbnew.F_Mask, "gts", 1, "Explicit mask material"],
|
2021-06-22 10:51:43 -06:00
|
|
|
[None, None, None, "ENIG"],
|
2022-10-01 14:57:23 -06:00
|
|
|
[pcbnew.F_Cu, "gtl", 2.1, "copper roughness"],
|
2021-06-22 10:51:43 -06:00
|
|
|
[None, None, 10, "Dielectric stuff"],
|
2022-10-01 14:57:23 -06:00
|
|
|
[1, "g1", 0.7, "Copper roughness"],
|
2021-06-22 10:51:43 -06:00
|
|
|
[None, None, 24, "Dielectric stuff"],
|
|
|
|
[None, None, 12, "Dielectric stuff"],
|
2022-10-01 14:57:23 -06:00
|
|
|
[2, "g2", 0.7, "Copper roughness"],
|
2021-06-22 10:51:43 -06:00
|
|
|
[None, None, 10, "Dielectric stuff"],
|
2022-10-01 14:57:23 -06:00
|
|
|
[pcbnew.B_Cu, "gbl", 2.1, "copper roughness"],
|
2021-06-22 10:51:43 -06:00
|
|
|
[None, None, None, "ENIG"],
|
2022-10-01 14:57:23 -06:00
|
|
|
[pcbnew.B_Mask, "gbs", 1, "Explicit mask material"],
|
|
|
|
[pcbnew.B_SilkS, "gbo", None, "White"],
|
|
|
|
[pcbnew.B_Paste, "gbp", None, "SN63/PB37"],
|
2021-06-22 10:51:43 -06:00
|
|
|
]
|
|
|
|
|
|
|
|
board_features = {
|
|
|
|
"core cap": True,
|
|
|
|
"castellated": False,
|
|
|
|
"plated board edge": False,
|
|
|
|
"copper finish": "ENIG",
|
|
|
|
"hard gold": False,
|
|
|
|
"bevelled edge": False,
|
2022-10-01 14:57:23 -06:00
|
|
|
"soldermask defined": None, # TODO: how do I want to determine this?
|
2021-06-22 10:51:43 -06:00
|
|
|
}
|
|
|
|
|
2021-07-01 23:47:02 -06:00
|
|
|
dir_fab.mkdir(parents=True, exist_ok=True)
|
|
|
|
dir_asy.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
2021-06-29 02:43:41 -06:00
|
|
|
files_fab = []
|
|
|
|
files_asy = []
|
2021-06-22 10:51:43 -06:00
|
|
|
|
2021-06-29 02:43:41 -06:00
|
|
|
# ================
|
|
|
|
# Gerbers
|
|
|
|
# ================
|
2021-07-01 23:47:02 -06:00
|
|
|
#### SETTINGS
|
|
|
|
tent_vias = True
|
|
|
|
trim_silkscreen = False
|
2021-06-22 10:51:43 -06:00
|
|
|
|
2021-06-29 02:43:41 -06:00
|
|
|
plot_controller = pcbnew.PLOT_CONTROLLER(pcb)
|
|
|
|
plot_options = plot_controller.GetPlotOptions()
|
2022-10-01 14:57:23 -06:00
|
|
|
|
2021-06-29 02:43:41 -06:00
|
|
|
# Set General Options:
|
2021-07-01 23:47:02 -06:00
|
|
|
# plot_options.Format()
|
2021-06-29 02:43:41 -06:00
|
|
|
plot_options.SetOutputDirectory(dir_fab)
|
|
|
|
plot_options.SetPlotFrameRef(False)
|
|
|
|
plot_options.SetPlotValue(False)
|
|
|
|
plot_options.SetPlotReference(True)
|
|
|
|
plot_options.SetPlotInvisibleText(False)
|
2021-07-01 23:47:02 -06:00
|
|
|
plot_options.SetPlotViaOnMaskLayer(not tent_vias)
|
2021-06-29 02:43:41 -06:00
|
|
|
plot_options.SetExcludeEdgeLayer(True)
|
|
|
|
plot_options.SetUseAuxOrigin(False)
|
|
|
|
plot_options.SetMirror(False)
|
|
|
|
plot_options.SetNegative(False)
|
2021-07-01 23:47:02 -06:00
|
|
|
plot_options.SetScale(1)
|
|
|
|
# plot_options.SetAutoScale(True)
|
2022-10-01 14:57:23 -06:00
|
|
|
# plot_options.SetPlotMode(PLOT_MODE)
|
|
|
|
# plot_options.SetLineWidth(pcbnew.FromMM(PLOT_LINE_WIDTH))
|
2021-06-29 02:43:41 -06:00
|
|
|
plot_options.SetUseGerberAttributes(True)
|
|
|
|
plot_options.SetUseGerberProtelExtensions(False)
|
|
|
|
plot_options.SetCreateGerberJobFile(False)
|
|
|
|
plot_options.SetIncludeGerberNetlistInfo(False)
|
2021-07-01 23:47:02 -06:00
|
|
|
plot_options.SetUseGerberX2format(True)
|
|
|
|
# plot_options.SetDrillMarksType()
|
|
|
|
plot_options.SetSubtractMaskFromSilk(trim_silkscreen)
|
2021-06-22 10:51:43 -06:00
|
|
|
|
2021-06-29 02:43:41 -06:00
|
|
|
plot_plan = [
|
|
|
|
# ( layer ID, file extension, description)
|
2022-10-01 14:57:23 -06:00
|
|
|
(pcbnew.F_Paste, "gtp", "Front Paste"),
|
|
|
|
(pcbnew.F_SilkS, "gto", "Front SilkScreen"),
|
|
|
|
(pcbnew.F_Mask, "gts", "Front Mask"),
|
|
|
|
(pcbnew.F_Cu, "gtl", "Front Copper"),
|
|
|
|
*[
|
|
|
|
(layer, f"g{layer}", f"Inner Layer {layer} Copper")
|
|
|
|
for layer in range(1, layer_count - 1)
|
|
|
|
],
|
|
|
|
(pcbnew.B_Cu, "gbl", "Back Copper"),
|
|
|
|
(pcbnew.B_Mask, "gbs", "Back Mask"),
|
|
|
|
(pcbnew.B_SilkS, "gbo", "Back SilkScreen"),
|
|
|
|
(pcbnew.B_Paste, "gbp", "Back Paste"),
|
|
|
|
(pcbnew.Edge_Cuts, "gm1", "Edges Cuts"),
|
2021-06-29 02:43:41 -06:00
|
|
|
]
|
|
|
|
|
|
|
|
for layer_info in plot_plan:
|
|
|
|
plot_controller.SetLayer(layer_info[0])
|
2022-10-01 14:57:23 -06:00
|
|
|
plot_controller.OpenPlotfile("", pcbnew.PLOT_FORMAT_GERBER, layer_info[2])
|
2021-06-29 02:43:41 -06:00
|
|
|
plot_controller.PlotLayer()
|
|
|
|
|
|
|
|
fname = f"{project_name}{suffix}.{layer_info[1]}"
|
|
|
|
os.rename(dir_fab / f"{project_name}.gbr", dir_fab / fname)
|
|
|
|
files_fab.append(fname)
|
2022-10-01 14:57:23 -06:00
|
|
|
|
2021-06-29 02:43:41 -06:00
|
|
|
plot_controller.ClosePlot()
|
|
|
|
|
|
|
|
# ================
|
|
|
|
# Drill Files
|
|
|
|
# ================
|
|
|
|
|
|
|
|
METRIC = True
|
|
|
|
ZERO_FORMAT = pcbnew.GENDRILL_WRITER_BASE.DECIMAL_FORMAT
|
|
|
|
INTEGER_DIGITS = 3
|
|
|
|
MANTISSA_DIGITS = 3
|
|
|
|
MIRROR_Y_AXIS = False
|
|
|
|
HEADER = True
|
2022-10-01 14:57:23 -06:00
|
|
|
OFFSET = pcbnew.wxPoint(0, 0)
|
2021-06-29 02:43:41 -06:00
|
|
|
MERGE_PTH_NPTH = True
|
|
|
|
DRILL_FILE = True
|
|
|
|
MAP_FILE = False
|
|
|
|
REPORTER = None
|
|
|
|
|
|
|
|
drill_writer = pcbnew.EXCELLON_WRITER(pcb)
|
|
|
|
drill_writer.SetFormat(METRIC, ZERO_FORMAT, INTEGER_DIGITS, MANTISSA_DIGITS)
|
|
|
|
drill_writer.SetOptions(MIRROR_Y_AXIS, HEADER, OFFSET, MERGE_PTH_NPTH)
|
2022-10-01 14:57:23 -06:00
|
|
|
drill_writer.CreateDrillandMapFilesSet(
|
|
|
|
str(dir_fab), DRILL_FILE, MAP_FILE, REPORTER
|
|
|
|
)
|
2021-06-29 02:43:41 -06:00
|
|
|
|
|
|
|
fname = f"{project_name}{suffix}.drl"
|
|
|
|
os.rename(dir_fab / f"{project_name}.drl", dir_fab / fname)
|
|
|
|
files_fab.append(fname)
|
|
|
|
|
2021-06-22 10:51:43 -06:00
|
|
|
# ================
|
2021-07-01 23:47:02 -06:00
|
|
|
# Drawing
|
|
|
|
# ================
|
|
|
|
# TODO
|
|
|
|
|
|
|
|
# ================
|
|
|
|
# BOM
|
|
|
|
# ================
|
|
|
|
# TODO
|
2021-06-22 10:51:43 -06:00
|
|
|
|
2021-07-01 23:47:02 -06:00
|
|
|
# ================
|
|
|
|
# Pick and Place
|
|
|
|
# ================
|
2021-06-22 10:51:43 -06:00
|
|
|
# TODO
|
2021-06-29 02:43:41 -06:00
|
|
|
|
|
|
|
# ================
|
2021-07-01 23:47:02 -06:00
|
|
|
# Fab Notes
|
2021-06-29 02:43:41 -06:00
|
|
|
# ================
|
|
|
|
|
2021-07-01 23:47:02 -06:00
|
|
|
# fname = f"README_FABRICATION{suffix}.TXT"
|
|
|
|
# with open(dir_fab / fname, "w") as f:
|
|
|
|
# f.write(f"{project_name}-REV{rev}\n")
|
|
|
|
# f.write(f"Layer Order\n")
|
|
|
|
# # for layer in plot_plan:
|
|
|
|
# files_fab.append(fname)
|
2022-10-01 14:57:23 -06:00
|
|
|
|
2021-06-22 10:51:43 -06:00
|
|
|
# ================
|
2021-07-01 23:47:02 -06:00
|
|
|
# Assembly Notes
|
2021-06-22 10:51:43 -06:00
|
|
|
# ================
|
|
|
|
|
2021-07-01 23:47:02 -06:00
|
|
|
# fname = f"README_ASSEMBLY{suffix}.TXT"
|
|
|
|
# with open(dir_asy / fname, "w") as f:
|
|
|
|
# f.write(f"{project_name}-REV{rev}\n")
|
|
|
|
# files_asy.append(fname)
|
2021-06-29 02:43:41 -06:00
|
|
|
|
|
|
|
# ================
|
|
|
|
# Zip
|
|
|
|
# ================
|
|
|
|
|
2022-10-01 14:57:23 -06:00
|
|
|
with zipfile.ZipFile(
|
|
|
|
dir_fab / f"{project_name}{suffix}_fabrication.zip", "w"
|
|
|
|
) as z:
|
2021-06-29 02:43:41 -06:00
|
|
|
for fname in files_fab:
|
|
|
|
z.write(dir_fab / fname, arcname=fname)
|
|
|
|
|
2022-10-01 14:57:23 -06:00
|
|
|
with zipfile.ZipFile(
|
|
|
|
dir_asy / f"{project_name}{suffix}_assembly.zip", "w"
|
|
|
|
) as z:
|
2021-06-29 02:43:41 -06:00
|
|
|
for fname in files_fab:
|
|
|
|
z.write(dir_fab / fname, arcname=Path("fabrication") / fname)
|
|
|
|
for fname in files_asy:
|
|
|
|
z.write(dir_asy / fname, arcname=fname)
|
2022-10-01 14:57:23 -06:00
|
|
|
|
2021-06-29 02:43:41 -06:00
|
|
|
# dir_archive = dir_pcb / "Archive"
|
|
|
|
# with zipfile.ZipFile(dir_archive / f"{project_name}{suffix}_archive.zip", "w") as z:
|
|
|
|
# for fname in files_fab:
|
|
|
|
# z.write(dir_fab / fname, arcname=Path("fabrication") / fname)
|
|
|
|
# for fname in files_asy:
|
|
|
|
# z.write(dir_asy / fname, arcname=Path("assembly") / fname)
|
2022-10-01 14:57:23 -06:00
|
|
|
# # TODO: archive project here
|