You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
151 lines
5.6 KiB
Python
151 lines
5.6 KiB
Python
#!/usr/bin/env kipy
|
|
|
|
__copyright__ = 'Copyright 2020 ALT-TEKNIK LLC'
|
|
|
|
from argparse import ArgumentParser
|
|
from os import path, rename
|
|
from os.path import dirname, realpath
|
|
from pcbnew import B_Cu, B_Mask, Edge_Cuts, EXCELLON_WRITER, F_Cu, F_Mask, \
|
|
FILLED, FromMils, LoadBoard, PCB_PLOT_PARAMS, PLOT_CONTROLLER, \
|
|
PLOT_FORMAT_GERBER, PLOT_FORMAT_PDF, TEXTE_PCB
|
|
from uuid import uuid1
|
|
|
|
_BOARD_NAME = '2x10-dip-adapter'
|
|
_VERSION_TOKEN = 'X.Y.Z'
|
|
_GERBER_EXT_OVERRIDES = {Edge_Cuts: 'gko'}
|
|
|
|
class _Layer:
|
|
|
|
def __init__(self, num, descr, **kwargs):
|
|
self.__num = num
|
|
self.__descr = descr
|
|
if kwargs.get('is_pdf'):
|
|
self.__use_aux_origin = False
|
|
self.__fmt = PLOT_FORMAT_PDF
|
|
self.__fmt_descr = 'PDF'
|
|
else:
|
|
self.__use_aux_origin = True
|
|
self.__fmt = PLOT_FORMAT_GERBER
|
|
self.__fmt_descr = 'Gerber'
|
|
scale = kwargs.get('scale')
|
|
self.__scale = 1.0 if not scale else scale
|
|
suffix = kwargs.get('suffix')
|
|
self.__suffix = '' if suffix is None else suffix
|
|
|
|
def plot(self, board_file, output_dir, version=None):
|
|
# Append the suffix for Gerber layers with file extension overrides to
|
|
# ease renaming after plotting
|
|
suffix = self.__suffix
|
|
override_gerber_ext = (self.__fmt == PLOT_FORMAT_GERBER and
|
|
self.__num in _GERBER_EXT_OVERRIDES)
|
|
if override_gerber_ext:
|
|
uuid = str(uuid1())
|
|
suffix = '{}-{}'.format(suffix, uuid) if suffix else uuid
|
|
|
|
# Indicate status
|
|
print('Plotting {} {}...'.format(self.__descr, self.__fmt_descr))
|
|
|
|
# Load the board
|
|
board = LoadBoard(board_file)
|
|
|
|
# Replace version text
|
|
if version is not None:
|
|
for drawing in board.GetDrawings():
|
|
if (drawing.IsOnLayer(self.__num) and
|
|
isinstance(drawing, TEXTE_PCB)):
|
|
text = drawing.GetText()
|
|
if _VERSION_TOKEN in text:
|
|
drawing.SetText(text.replace(_VERSION_TOKEN, version))
|
|
print(' Replaced version text')
|
|
|
|
# Create a plot controller
|
|
controller = PLOT_CONTROLLER(board)
|
|
|
|
# Set the plot options
|
|
opts = controller.GetPlotOptions()
|
|
opts.SetOutputDirectory(output_dir)
|
|
opts.SetPlotFrameRef(False)
|
|
opts.SetPlotValue(False)
|
|
opts.SetPlotReference(True)
|
|
opts.SetPlotInvisibleText(False)
|
|
opts.SetExcludeEdgeLayer(True)
|
|
opts.SetSubtractMaskFromSilk(False)
|
|
opts.SetPlotViaOnMaskLayer(False)
|
|
opts.SetSkipPlotNPTH_Pads(True)
|
|
opts.SetUseAuxOrigin(self.__use_aux_origin)
|
|
opts.SetDrillMarksType(PCB_PLOT_PARAMS.NO_DRILL_SHAPE)
|
|
opts.SetAutoScale(False)
|
|
opts.SetScale(self.__scale)
|
|
opts.SetPlotMode(FILLED)
|
|
opts.SetLineWidth(FromMils(0.1))
|
|
opts.SetMirror(False)
|
|
opts.SetNegative(False)
|
|
if self.__fmt == PLOT_FORMAT_GERBER:
|
|
opts.SetUseGerberProtelExtensions(not override_gerber_ext)
|
|
opts.SetCreateGerberJobFile(False)
|
|
opts.SetGerberPrecision(6)
|
|
opts.SetUseGerberX2format(False)
|
|
opts.SetIncludeGerberNetlistInfo(False)
|
|
|
|
# Plot the layer
|
|
controller.SetColorMode(False)
|
|
controller.SetLayer(self.__num)
|
|
controller.OpenPlotfile(suffix, self.__fmt, self.__descr)
|
|
controller.PlotLayer()
|
|
controller.ClosePlot()
|
|
|
|
# Rename Gerber layers with file extension overrides
|
|
if override_gerber_ext:
|
|
src = '{}-{}.gbr'.format(_BOARD_NAME, suffix)
|
|
dest = _BOARD_NAME
|
|
if self.__suffix:
|
|
dest = '{}-{}'.format(dest, self.__suffix)
|
|
dest = '{}.{}'.format(dest, _GERBER_EXT_OVERRIDES[self.__num])
|
|
rename(path.join(output_dir, src), path.join(output_dir, dest))
|
|
|
|
_OUTPUT_DIR = 'plots'
|
|
_LAYERS = (
|
|
_Layer(F_Mask, 'F.Mask'),
|
|
_Layer(F_Cu, 'F.Cu'),
|
|
_Layer(B_Cu, 'B.Cu'),
|
|
_Layer(B_Mask, 'B.Mask'),
|
|
_Layer(Edge_Cuts, 'Edge.Cuts')
|
|
)
|
|
|
|
def main(version=None):
|
|
# Canonicalize the board filename and output directory
|
|
project_dir = realpath(dirname(__file__))
|
|
board_file = '{}.kicad_pcb'.format(_BOARD_NAME)
|
|
abs_board_file = path.join(project_dir, board_file)
|
|
output_dir = realpath(path.join(project_dir, _OUTPUT_DIR))
|
|
|
|
# Plot the layers
|
|
for layer in _LAYERS:
|
|
layer.plot(abs_board_file, output_dir, version)
|
|
|
|
# Write the drill file
|
|
print('Writing Excellon drill file...')
|
|
board = LoadBoard(abs_board_file)
|
|
writer = EXCELLON_WRITER(board)
|
|
writer.SetOptions(False, # aMirror
|
|
False, # aMinimalHeader
|
|
board.GetDesignSettings().m_AuxOrigin, # aOffset
|
|
True) # aMerge_PTH_NPTH
|
|
writer.SetRouteModeForOvalHoles(True)
|
|
writer.SetFormat(True, # aMetric
|
|
EXCELLON_WRITER.DECIMAL_FORMAT) # aZerosFmt
|
|
writer.CreateDrillandMapFilesSet(output_dir, # aPlotDirectory
|
|
True, # aGenDrill
|
|
False) # aGenMap
|
|
rename(path.join(output_dir, '{}.drl'.format(_BOARD_NAME)),
|
|
path.join(output_dir, '{}.xln'.format(_BOARD_NAME)))
|
|
|
|
# Indicate status
|
|
print('Plotted {}'.format(board_file))
|
|
|
|
if __name__ == '__main__':
|
|
parser = ArgumentParser(description='creates board plots')
|
|
parser.add_argument('version', nargs='?', help='the board version')
|
|
args = parser.parse_args()
|
|
main(args.version)
|