Added KiBoM and InteractiveHtmlBoM support

This commit is contained in:
Salvador E. Tropea 2020-03-19 16:26:45 -03:00
parent 85acaadf26
commit 438142dabd
3 changed files with 120 additions and 7 deletions

View File

@ -108,7 +108,7 @@ class CfgYamlReader(CfgReader):
if mapping['required'](cfg_options):
cfg_val = self._get_required(cfg_options, key)
elif key in cfg_options:
elif not(cfg_options is None) and key in cfg_options:
# not required but given anyway
cfg_val = cfg_options[key]
else:
@ -337,7 +337,7 @@ class CfgYamlReader(CfgReader):
},
{
'key': 'format',
'types': ['position'],
'types': ['position','kibom'],
'to': 'format',
'required': lambda opts: True,
},
@ -358,7 +358,19 @@ class CfgYamlReader(CfgReader):
'types': ['position'],
'to': 'only_smd',
'required': lambda opts: True,
}
},
{
'key': 'blacklist',
'types': ['ibom'],
'to': 'blacklist',
'required': lambda opts: False,
},
{
'key': 'name_format',
'types': ['ibom'],
'to': 'name_format',
'required': lambda opts: False,
},
]
po = PC.OutputOptions(otype)
@ -444,13 +456,16 @@ class CfgYamlReader(CfgReader):
raise YamlError("Output needs a type")
if otype not in ['gerber', 'ps', 'hpgl', 'dxf', 'pdf', 'svg',
'gerb_drill', 'excellon', 'position']:
'gerb_drill', 'excellon', 'position',
'kibom', 'ibom']:
raise YamlError("Unknown output type: {}".format(otype))
try:
options = o_obj['options']
except KeyError:
raise YamlError("Output need to have options specified")
if otype != 'ibom':
raise YamlError("Output need to have options specified")
options = None
logger.debug("Parsing output options for {} ({})".format(name, otype))

View File

@ -7,7 +7,7 @@ import os
from sys import exit
import operator
from shutil import which
from subprocess import call, run, PIPE
from subprocess import call, run, PIPE, check_output, CalledProcessError, STDOUT
import logging
from distutils.version import StrictVersion
import re
@ -102,6 +102,8 @@ class Plotter(object):
self._do_drill_plot(board, pc, op)
elif self._output_is_position(op):
self._do_position_plot(board, pc, op)
elif self._output_is_bom(op):
self._do_bom(board, pc, op, brd_file)
else:
raise PlotError("Don't know how to plot type {}"
.format(op.options.type))
@ -218,6 +220,12 @@ class Plotter(object):
def _output_is_position(self, output):
return output.options.type == PCfg.OutputOptions.POSITION
def _output_is_bom(self, output):
return output.options.type in [
PCfg.OutputOptions.KIBOM,
PCfg.OutputOptions.IBOM,
]
def _get_layer_plot_format(self, output):
"""
Gets the Pcbnew plot format for a given KiPlot output type
@ -528,6 +536,59 @@ class Plotter(object):
else:
raise PlotError("Format is invalid: {}".format(to.format))
def _do_bom(self, board, plot_ctrl, output, brd_file):
if output.options.type == 'kibom':
self._do_kibom(board, plot_ctrl, output, brd_file)
else:
self._do_ibom(board, plot_ctrl, output, brd_file)
def _do_kibom(self, board, plot_ctrl, output, brd_file):
if which('KiBOM_CLI.py') is None:
logger.error('No `KiBOM_CLI.py` command found.\n'
'Please install it, visit: https://github.com/INTI-CMNB/KiBoM')
exit(misc.MISSING_TOOL)
to = output.options.type_options
format = to.format.lower()
outdir = plot_ctrl.GetPlotOptions().GetOutputDirectory()
if not os.path.exists(outdir):
os.makedirs(outdir)
prj = os.path.splitext(os.path.relpath(brd_file))[0]
logger.debug('Doing BoM, format '+format+' prj: '+prj)
cmd = [ 'KiBOM_CLI.py', prj+'.xml', os.path.join(outdir, prj)+'.'+format ]
logger.debug('Running: '+str(cmd))
try:
check_output(cmd, stderr=STDOUT)
except CalledProcessError as e:
logger.error('Failed to create BoM, error %d', e.returncode)
exit(misc.BOM_ERROR)
def _do_ibom(self, board, plot_ctrl, output, brd_file):
if which('generate_interactive_bom.py') is None:
logger.error('No `generate_interactive_bom.py` command found.\n'
'Please install it, visit: https://github.com/INTI-CMNB/InteractiveHtmlBom')
exit(misc.MISSING_TOOL)
outdir = plot_ctrl.GetPlotOptions().GetOutputDirectory()
if not os.path.exists(outdir):
os.makedirs(outdir)
prj = os.path.splitext(os.path.relpath(brd_file))[0]
logger.debug('Doing Interactive BoM, prj: '+prj)
cmd = [ 'generate_interactive_bom.py', brd_file,
'--dest-dir', outdir,
'--no-browser', ]
to = output.options.type_options
if to.blacklist:
cmd.append('--blacklist')
cmd.append(to.blacklist)
if to.name_format:
cmd.append('--name-format')
cmd.append(to.name_format)
logger.debug('Running: '+str(cmd))
try:
check_output(cmd, stderr=STDOUT)
except CalledProcessError as e:
logger.error('Failed to create BoM, error %d', e.returncode)
exit(misc.BOM_ERROR)
def _configure_gerber_opts(self, po, output):
# true if gerber
@ -582,6 +643,14 @@ class Plotter(object):
assert(output.options.type == PCfg.OutputOptions.POSITION)
def _configure_kibom_opts(self, po, output):
assert(output.options.type == PCfg.OutputOptions.KIBOM)
def _configure_ibom_opts(self, po, output):
assert(output.options.type == PCfg.OutputOptions.IBOM)
def _configure_output_dir(self, plot_ctrl, output):
po = plot_ctrl.GetPlotOptions()
@ -637,6 +706,10 @@ class Plotter(object):
self._configure_hpgl_opts(po, output)
elif output.options.type == PCfg.OutputOptions.POSITION:
self._configure_position_opts(po, output)
elif output.options.type == PCfg.OutputOptions.KIBOM:
self._configure_kibom_opts(po, output)
elif output.options.type == PCfg.OutputOptions.IBOM:
self._configure_ibom_opts(po, output)
po.SetDrillMarksType(opts.drill_marks)

View File

@ -377,6 +377,25 @@ class PositionOptions(TypeOptions):
return errs
class KiBoMOptions(TypeOptions):
def __init__(self):
self.format = None
def validate(self):
errs = []
if self.format not in ["HTML", "CSV"]:
errs.append("Format must be either HTML or CSV")
return errs
class IBoMOptions(TypeOptions):
def __init__(self):
self.blacklist = None
self.name_format = None
class OutputOptions(object):
GERBER = 'gerber'
@ -389,6 +408,8 @@ class OutputOptions(object):
EXCELLON = 'excellon'
GERB_DRILL = 'gerb_drill'
POSITION = 'position'
KIBOM = 'kibom'
IBOM = 'ibom'
def __init__(self, otype):
self.type = otype
@ -411,12 +432,16 @@ class OutputOptions(object):
self.type_options = GerberDrillOptions()
elif otype == self.POSITION:
self.type_options = PositionOptions()
elif otype == self.KIBOM:
self.type_options = KiBoMOptions()
elif otype == self.IBOM:
self.type_options = IBoMOptions()
else:
self.type_options = None
def validate(self):
if self.type_options is None:
if self.type_options is None and self.type != self.IBOM:
return ["No type specific options found"]
return self.type_options.validate()