Added KiBoM and InteractiveHtmlBoM support
This commit is contained in:
parent
85acaadf26
commit
438142dabd
|
|
@ -108,7 +108,7 @@ class CfgYamlReader(CfgReader):
|
||||||
if mapping['required'](cfg_options):
|
if mapping['required'](cfg_options):
|
||||||
|
|
||||||
cfg_val = self._get_required(cfg_options, key)
|
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
|
# not required but given anyway
|
||||||
cfg_val = cfg_options[key]
|
cfg_val = cfg_options[key]
|
||||||
else:
|
else:
|
||||||
|
|
@ -337,7 +337,7 @@ class CfgYamlReader(CfgReader):
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'key': 'format',
|
'key': 'format',
|
||||||
'types': ['position'],
|
'types': ['position','kibom'],
|
||||||
'to': 'format',
|
'to': 'format',
|
||||||
'required': lambda opts: True,
|
'required': lambda opts: True,
|
||||||
},
|
},
|
||||||
|
|
@ -358,7 +358,19 @@ class CfgYamlReader(CfgReader):
|
||||||
'types': ['position'],
|
'types': ['position'],
|
||||||
'to': 'only_smd',
|
'to': 'only_smd',
|
||||||
'required': lambda opts: True,
|
'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)
|
po = PC.OutputOptions(otype)
|
||||||
|
|
@ -444,13 +456,16 @@ class CfgYamlReader(CfgReader):
|
||||||
raise YamlError("Output needs a type")
|
raise YamlError("Output needs a type")
|
||||||
|
|
||||||
if otype not in ['gerber', 'ps', 'hpgl', 'dxf', 'pdf', 'svg',
|
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))
|
raise YamlError("Unknown output type: {}".format(otype))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
options = o_obj['options']
|
options = o_obj['options']
|
||||||
except KeyError:
|
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))
|
logger.debug("Parsing output options for {} ({})".format(name, otype))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import os
|
||||||
from sys import exit
|
from sys import exit
|
||||||
import operator
|
import operator
|
||||||
from shutil import which
|
from shutil import which
|
||||||
from subprocess import call, run, PIPE
|
from subprocess import call, run, PIPE, check_output, CalledProcessError, STDOUT
|
||||||
import logging
|
import logging
|
||||||
from distutils.version import StrictVersion
|
from distutils.version import StrictVersion
|
||||||
import re
|
import re
|
||||||
|
|
@ -102,6 +102,8 @@ class Plotter(object):
|
||||||
self._do_drill_plot(board, pc, op)
|
self._do_drill_plot(board, pc, op)
|
||||||
elif self._output_is_position(op):
|
elif self._output_is_position(op):
|
||||||
self._do_position_plot(board, pc, op)
|
self._do_position_plot(board, pc, op)
|
||||||
|
elif self._output_is_bom(op):
|
||||||
|
self._do_bom(board, pc, op, brd_file)
|
||||||
else:
|
else:
|
||||||
raise PlotError("Don't know how to plot type {}"
|
raise PlotError("Don't know how to plot type {}"
|
||||||
.format(op.options.type))
|
.format(op.options.type))
|
||||||
|
|
@ -218,6 +220,12 @@ class Plotter(object):
|
||||||
def _output_is_position(self, output):
|
def _output_is_position(self, output):
|
||||||
return output.options.type == PCfg.OutputOptions.POSITION
|
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):
|
def _get_layer_plot_format(self, output):
|
||||||
"""
|
"""
|
||||||
Gets the Pcbnew plot format for a given KiPlot output type
|
Gets the Pcbnew plot format for a given KiPlot output type
|
||||||
|
|
@ -528,6 +536,59 @@ class Plotter(object):
|
||||||
else:
|
else:
|
||||||
raise PlotError("Format is invalid: {}".format(to.format))
|
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):
|
def _configure_gerber_opts(self, po, output):
|
||||||
|
|
||||||
# true if gerber
|
# true if gerber
|
||||||
|
|
@ -582,6 +643,14 @@ class Plotter(object):
|
||||||
|
|
||||||
assert(output.options.type == PCfg.OutputOptions.POSITION)
|
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):
|
def _configure_output_dir(self, plot_ctrl, output):
|
||||||
|
|
||||||
po = plot_ctrl.GetPlotOptions()
|
po = plot_ctrl.GetPlotOptions()
|
||||||
|
|
@ -637,6 +706,10 @@ class Plotter(object):
|
||||||
self._configure_hpgl_opts(po, output)
|
self._configure_hpgl_opts(po, output)
|
||||||
elif output.options.type == PCfg.OutputOptions.POSITION:
|
elif output.options.type == PCfg.OutputOptions.POSITION:
|
||||||
self._configure_position_opts(po, output)
|
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)
|
po.SetDrillMarksType(opts.drill_marks)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -377,6 +377,25 @@ class PositionOptions(TypeOptions):
|
||||||
return errs
|
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):
|
class OutputOptions(object):
|
||||||
|
|
||||||
GERBER = 'gerber'
|
GERBER = 'gerber'
|
||||||
|
|
@ -389,6 +408,8 @@ class OutputOptions(object):
|
||||||
EXCELLON = 'excellon'
|
EXCELLON = 'excellon'
|
||||||
GERB_DRILL = 'gerb_drill'
|
GERB_DRILL = 'gerb_drill'
|
||||||
POSITION = 'position'
|
POSITION = 'position'
|
||||||
|
KIBOM = 'kibom'
|
||||||
|
IBOM = 'ibom'
|
||||||
|
|
||||||
def __init__(self, otype):
|
def __init__(self, otype):
|
||||||
self.type = otype
|
self.type = otype
|
||||||
|
|
@ -411,12 +432,16 @@ class OutputOptions(object):
|
||||||
self.type_options = GerberDrillOptions()
|
self.type_options = GerberDrillOptions()
|
||||||
elif otype == self.POSITION:
|
elif otype == self.POSITION:
|
||||||
self.type_options = PositionOptions()
|
self.type_options = PositionOptions()
|
||||||
|
elif otype == self.KIBOM:
|
||||||
|
self.type_options = KiBoMOptions()
|
||||||
|
elif otype == self.IBOM:
|
||||||
|
self.type_options = IBoMOptions()
|
||||||
else:
|
else:
|
||||||
self.type_options = None
|
self.type_options = None
|
||||||
|
|
||||||
def validate(self):
|
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 ["No type specific options found"]
|
||||||
|
|
||||||
return self.type_options.validate()
|
return self.type_options.validate()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue