Added full KiBoM configuration from the YAML config.
This commit is contained in:
parent
97e95ff7c5
commit
165d9aa15d
|
|
@ -66,6 +66,8 @@ def check_version(command, version):
|
|||
logger.debug('Running: '+str(cmd))
|
||||
result = run(cmd, stdout=PIPE, stderr=PIPE, universal_newlines=True)
|
||||
z = re.match(command + r' (\d+\.\d+\.\d+)', result.stdout)
|
||||
if not z:
|
||||
z = re.search(r'Version: (\d+\.\d+\.\d+)', result.stdout)
|
||||
if not z:
|
||||
logger.error('Unable to determine ' + command + ' version:\n' +
|
||||
result.stdout)
|
||||
|
|
|
|||
|
|
@ -1,15 +1,291 @@
|
|||
import os
|
||||
from glob import (glob)
|
||||
from re import search
|
||||
from tempfile import NamedTemporaryFile
|
||||
from subprocess import (check_output, STDOUT, CalledProcessError)
|
||||
from .misc import (CMD_KIBOM, URL_KIBOM, BOM_ERROR)
|
||||
from .kiplot import (check_script)
|
||||
from .gs import (GS)
|
||||
from .optionable import BaseOptions
|
||||
from .optionable import Optionable, BaseOptions
|
||||
from .error import KiPlotConfigurationError
|
||||
from kiplot.macros import macros, document, output_class # noqa: F401
|
||||
from . import log
|
||||
|
||||
logger = log.get_logger(__name__)
|
||||
|
||||
CONFIG_FILENAME = 'config.kibom.ini'
|
||||
|
||||
|
||||
class KiBoMRegex(Optionable):
|
||||
""" Implements the pair column/regex """
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._unkown_is_error = True
|
||||
with document:
|
||||
self.column = ''
|
||||
""" Name of the column to apply the regular expression """
|
||||
self.regex = ''
|
||||
""" Regular expression to match """
|
||||
self.field = None
|
||||
""" {column} """
|
||||
self.regexp = None
|
||||
""" {regex} """ # pragma: no cover
|
||||
|
||||
def __str__(self):
|
||||
return self.column+'\t'+self.regex
|
||||
|
||||
|
||||
class KiBoMColumns(Optionable):
|
||||
""" Information for the BoM columns """
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._unkown_is_error = True
|
||||
with document:
|
||||
self.field = ''
|
||||
""" Name of the field to use for this column """
|
||||
self.name = ''
|
||||
""" Name to display in the header. The field is used when empty """
|
||||
self.join = Optionable
|
||||
""" [list(string)|string] List of fields to join to this column """ # pragma: no cover
|
||||
|
||||
def config(self):
|
||||
super().config()
|
||||
if not self.field:
|
||||
raise KiPlotConfigurationError("Missing or empty `field` in columns list ({})".format(str(self._tree)))
|
||||
if isinstance(self.join, type):
|
||||
self.join = None
|
||||
elif isinstance(self.join, list):
|
||||
self.join = '\t'.join(self.join)
|
||||
|
||||
|
||||
class KiBoMConfig(Optionable):
|
||||
""" Implements the .ini options """
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
with document:
|
||||
self.ignore_dnf = True
|
||||
""" Exclude DNF (Do Not Fit) components """
|
||||
self.html_generate_dnf = True
|
||||
""" Generate a separated section for DNF (Do Not Fit) components (HTML only) """
|
||||
self.use_alt = False
|
||||
""" Print grouped references in the alternate compressed style eg: R1-R7,R18 """
|
||||
self.number_rows = True
|
||||
""" First column is the row number """
|
||||
self.group_connectors = True
|
||||
""" Connectors with the same footprints will be grouped together, independent of the name of the connector """
|
||||
self.test_regex = True
|
||||
""" Each component group will be tested against a number of regular-expressions (see ``). """
|
||||
self.merge_blank_fields = True
|
||||
""" Component groups with blank fields will be merged into the most compatible group, where possible """
|
||||
self.fit_field = 'Config'
|
||||
""" Field name used to determine if a particular part is to be fitted (also DNC and variants) """
|
||||
self.datasheet_as_link = ''
|
||||
""" Column with links to the datasheet (HTML only) """
|
||||
self.hide_headers = False
|
||||
""" Hide column headers """
|
||||
self.hide_pcb_info = False
|
||||
""" Hide project information """
|
||||
self.digikey_link = Optionable
|
||||
""" [string|list(string)] Column/s containing Digi-Key part numbers, will be linked to web page (HTML only) """
|
||||
self.group_fields = Optionable
|
||||
""" [list(string)] List of fields used for sorting individual components into groups.
|
||||
Components which match (comparing *all* fields) will be grouped together.
|
||||
Field names are case-insensitive.
|
||||
If empty: ['Part', 'Part Lib', 'Value', 'Footprint', 'Footprint Lib'] is used """
|
||||
self.component_aliases = Optionable
|
||||
""" [list(list(string))] A series of values which are considered to be equivalent for the part name.
|
||||
Each entry is a list of equivalen names. Example: ['c', 'c_small', 'cap' ]
|
||||
will ensure the equivalent capacitor symbols can be grouped together.
|
||||
If empty the following aliases are used:
|
||||
- ['r', 'r_small', 'res', 'resistor']
|
||||
- ['l', 'l_small', 'inductor']
|
||||
- ['c', 'c_small', 'cap', 'capacitor']
|
||||
- ['sw', 'switch']
|
||||
- ['zener', 'zenersmall']
|
||||
- ['d', 'diode', 'd_small'] """
|
||||
self.include_only = KiBoMRegex
|
||||
""" [list(dict)] A series of regular expressions used to select included parts.
|
||||
If there are any regex defined here, only components that match against ANY of them will be included.
|
||||
Column names are case-insensitive.
|
||||
If empty all the components are included """
|
||||
self.exclude_any = KiBoMRegex
|
||||
""" [list(dict)] A series of regular expressions used to exclude parts.
|
||||
If a component matches ANY of these, it will be excluded.
|
||||
Column names are case-insensitive.
|
||||
If empty the following list is used:
|
||||
- column: References
|
||||
..regex: '^TP[0-9]*'
|
||||
- column: References
|
||||
..regex: '^FID'
|
||||
- column: Part
|
||||
..regex: 'mount.*hole'
|
||||
- column: Part
|
||||
..regex: 'solder.*bridge'
|
||||
- column: Part
|
||||
..regex: 'test.*point'
|
||||
- column: Footprint
|
||||
..regex 'test.*point'
|
||||
- column: Footprint
|
||||
..regex: 'mount.*hole'
|
||||
- column: Footprint
|
||||
..regex: 'fiducial' """
|
||||
self.columns = KiBoMColumns
|
||||
""" [list(dict)|list(string)] List of columns to display.
|
||||
Can be just the name of the field """ # pragma: no cover
|
||||
|
||||
@staticmethod
|
||||
def _create_minimal_ini():
|
||||
""" KiBoM config to get only the headers """
|
||||
with NamedTemporaryFile(mode='w', delete=False) as f:
|
||||
f.write('[BOM_OPTIONS]\n')
|
||||
f.write('output_file_name = %O\n')
|
||||
f.write('hide_pcb_info = 1\n')
|
||||
f.write('\n[IGNORE_COLUMNS]\n')
|
||||
f.write('\n[REGEX_EXCLUDE]\n')
|
||||
f.write('Part\t.*\n')
|
||||
f.close()
|
||||
return f.name
|
||||
|
||||
@staticmethod
|
||||
def _get_columns():
|
||||
""" Create a list of valid columns """
|
||||
check_script(CMD_KIBOM, URL_KIBOM, '1.8.0')
|
||||
config = None
|
||||
csv = None
|
||||
columns = None
|
||||
try:
|
||||
xml = os.path.splitext(os.path.abspath(GS.sch_file))[0]+'.xml'
|
||||
config = os.path.abspath(KiBoMConfig._create_minimal_ini())
|
||||
with NamedTemporaryFile(mode='w', suffix='.csv', delete=False) as f:
|
||||
csv = f.name
|
||||
cmd = [CMD_KIBOM, '--cfg', config, '-d', os.path.dirname(csv), '-s', ',', xml, csv]
|
||||
logger.debug('Running: '+str(cmd))
|
||||
cmd_output = check_output(cmd, stderr=STDOUT)
|
||||
with open(csv, 'rt') as f:
|
||||
columns = f.readline().rstrip().split(',')
|
||||
except CalledProcessError as e:
|
||||
logger.error('Failed to get the column names for `{}`, error {}'.format(xml, e.returncode))
|
||||
if e.output:
|
||||
logger.debug('Output from command: '+e.output.decode())
|
||||
exit(BOM_ERROR)
|
||||
finally:
|
||||
if config:
|
||||
os.remove(config)
|
||||
if csv:
|
||||
os.remove(csv)
|
||||
logger.debug('Output from command:\n'+cmd_output.decode())
|
||||
return columns
|
||||
|
||||
def config(self):
|
||||
super().config()
|
||||
# digikey_link
|
||||
if isinstance(self.digikey_link, type):
|
||||
self.digikey_link = None
|
||||
elif isinstance(self.digikey_link, list):
|
||||
self.digikey_link = '\t'.join(self.digikey_link)
|
||||
# group_fields
|
||||
if isinstance(self.group_fields, type):
|
||||
self.group_fields = None
|
||||
# component_aliases
|
||||
if isinstance(self.component_aliases, type):
|
||||
self.component_aliases = None
|
||||
else:
|
||||
self.component_aliases = ['\t'.join(a) for a in self.component_aliases]
|
||||
# include_only
|
||||
if isinstance(self.include_only, type):
|
||||
self.include_only = None
|
||||
else:
|
||||
self.include_only = [str(r) for r in self.include_only]
|
||||
# exclude_any
|
||||
if isinstance(self.exclude_any, type):
|
||||
self.exclude_any = None
|
||||
else:
|
||||
self.exclude_any = [str(r) for r in self.exclude_any]
|
||||
# columns
|
||||
if isinstance(self.columns, type):
|
||||
self.columns = None
|
||||
self.col_rename = None
|
||||
self.join = None
|
||||
self.ignore = None
|
||||
else:
|
||||
# This is tricky
|
||||
# Lower case available columns
|
||||
valid_columns = self._get_columns()
|
||||
valid_columns_l = {c.lower(): c for c in valid_columns}
|
||||
logger.debug("Valid columns: "+str(valid_columns))
|
||||
# Create the different lists
|
||||
columns = []
|
||||
columns_l = {}
|
||||
self.col_rename = []
|
||||
self.join = []
|
||||
for col in self.columns:
|
||||
if isinstance(col, str):
|
||||
# Just a string, add to the list of used
|
||||
new_col = col
|
||||
else:
|
||||
# A complete entry
|
||||
new_col = col.field
|
||||
# A column rename
|
||||
if col.name:
|
||||
self.col_rename.append(col.field+'\t'+col.name)
|
||||
# Attach other columns
|
||||
if col.join:
|
||||
self.join.append(col.field+'\t'+col.join)
|
||||
# Check this is a valid column
|
||||
if new_col.lower() not in valid_columns_l:
|
||||
raise KiPlotConfigurationError('Invalid column name `{}`'.format(new_col))
|
||||
columns.append(new_col)
|
||||
columns_l[new_col.lower()] = new_col
|
||||
# Create a list of the columns we don't want
|
||||
self.ignore = [c for c in valid_columns_l.keys() if c not in columns_l]
|
||||
# And this is the ordered list with the case style defined by the user
|
||||
self.columns = columns
|
||||
|
||||
def write_bool(self, attr):
|
||||
""" Write a .INI bool option """
|
||||
self.f.write('{} = {}\n'.format(attr, '1' if getattr(self, attr) else '0'))
|
||||
|
||||
def write_str(self, attr):
|
||||
""" Write a .INI string option """
|
||||
val = getattr(self, attr)
|
||||
if val:
|
||||
self.f.write('{} = {}\n'.format(attr, val))
|
||||
|
||||
def write_vector(self, vector, section):
|
||||
""" Write a .INI section filled with a vector of strings """
|
||||
if vector:
|
||||
self.f.write('\n[{}]\n'.format(section))
|
||||
for v in vector:
|
||||
self.f.write(v+'\n')
|
||||
|
||||
def save(self, filename):
|
||||
""" Create an INI file for KiBoM """
|
||||
logger.debug("Saving KiBoM config to `{}`".format(filename))
|
||||
with open(filename, 'wt') as f:
|
||||
self.f = f
|
||||
f.write('[BOM_OPTIONS]\n')
|
||||
self.write_bool('ignore_dnf')
|
||||
self.write_bool('html_generate_dnf')
|
||||
self.write_bool('use_alt')
|
||||
self.write_bool('number_rows')
|
||||
self.write_bool('group_connectors')
|
||||
self.write_bool('test_regex')
|
||||
self.write_bool('merge_blank_fields')
|
||||
self.write_str('fit_field')
|
||||
self.write_str('datasheet_as_link')
|
||||
self.write_bool('hide_headers')
|
||||
self.write_bool('hide_pcb_info')
|
||||
self.write_str('digikey_link')
|
||||
# Ask to keep the output name
|
||||
f.write('output_file_name = %O\n')
|
||||
self.write_vector(self.group_fields, 'GROUP_FIELDS')
|
||||
self.write_vector(self.include_only, 'REGEX_INCLUDE')
|
||||
self.write_vector(self.exclude_any, 'REGEX_EXCLUDE')
|
||||
self.write_vector(self.columns, 'COLUMN_ORDER')
|
||||
self.write_vector(self.ignore, 'IGNORE_COLUMNS')
|
||||
self.write_vector(self.col_rename, 'COLUMN_RENAME')
|
||||
self.write_vector(self.join, 'JOIN')
|
||||
self.write_vector(self.component_aliases, 'COMPONENT_ALIASES')
|
||||
|
||||
|
||||
class KiBoMOptions(BaseOptions):
|
||||
def __init__(self):
|
||||
|
|
@ -22,28 +298,51 @@ class KiBoMOptions(BaseOptions):
|
|||
are output to the BoM. To specify multiple variants,
|
||||
with a BOM file exported for each variant, separate
|
||||
variants with the ';' (semicolon) character """
|
||||
self.conf = 'bom.ini'
|
||||
""" BoM configuration file, relative to PCB """
|
||||
self.conf = KiBoMConfig
|
||||
""" [string|dict] BoM configuration file, relative to PCB.
|
||||
You can also define the configuration here, will be stored in `config.kibom.ini` """
|
||||
self.separator = ','
|
||||
""" CSV Separator """
|
||||
self.output = '%f-%i.%x'
|
||||
""" filename for the output (%i=bom)"""
|
||||
self.format = 'HTML'
|
||||
""" [HTML,CSV] format for the BoM """ # pragma: no cover
|
||||
""" [HTML,CSV,XML,XLSX] format for the BoM """ # pragma: no cover
|
||||
|
||||
def config(self):
|
||||
super().config()
|
||||
if isinstance(self.conf, type):
|
||||
self.conf = 'bom.ini'
|
||||
elif isinstance(self.conf, str):
|
||||
if not self.conf:
|
||||
self.conf = 'bom.ini'
|
||||
else:
|
||||
# A configuration
|
||||
conf = os.path.abspath(os.path.join(GS.out_dir, CONFIG_FILENAME))
|
||||
self.conf.save(conf)
|
||||
self.conf = conf
|
||||
|
||||
def run(self, output_dir, board):
|
||||
check_script(CMD_KIBOM, URL_KIBOM)
|
||||
check_script(CMD_KIBOM, URL_KIBOM, '1.8.0')
|
||||
format = self.format.lower()
|
||||
prj = os.path.splitext(os.path.abspath(GS.pcb_file))[0]
|
||||
config = os.path.join(os.path.dirname(os.path.abspath(GS.pcb_file)), self.conf)
|
||||
logger.debug('Doing BoM, format {} prj: {} config: {}'.format(format, prj, config))
|
||||
prj = os.path.splitext(os.path.abspath(GS.sch_file))[0]
|
||||
config = os.path.join(os.path.dirname(os.path.abspath(GS.sch_file)), self.conf)
|
||||
if self.output:
|
||||
force_output = True
|
||||
output = self.expand_filename_sch(output_dir, self.output, 'bom', format)
|
||||
else:
|
||||
force_output = False
|
||||
output = os.path.basename(prj)+'.'+format
|
||||
logger.debug('Doing BoM, format {} prj: {} config: {} output: {}'.format(format, prj, config, output))
|
||||
cmd = [CMD_KIBOM,
|
||||
'-n', str(self.number),
|
||||
'--cfg', config,
|
||||
'-s', self.separator]
|
||||
'-s', self.separator,
|
||||
'-d', output_dir]
|
||||
if GS.debug_enabled:
|
||||
cmd.append('-v')
|
||||
if self.variant:
|
||||
cmd.extend(['-r', self.variant])
|
||||
cmd.extend([prj+'.xml', os.path.join(output_dir, os.path.basename(prj))+'.'+format])
|
||||
cmd.extend([prj+'.xml', output])
|
||||
logger.debug('Running: '+str(cmd))
|
||||
try:
|
||||
cmd_output = check_output(cmd, stderr=STDOUT)
|
||||
|
|
@ -52,10 +351,14 @@ class KiBoMOptions(BaseOptions):
|
|||
if e.output:
|
||||
logger.debug('Output from command: '+e.output.decode())
|
||||
exit(BOM_ERROR)
|
||||
prj = os.path.basename(prj)
|
||||
for f in glob(os.path.join(output_dir, prj)+'*.tmp'):
|
||||
# I'm not sure when these files are left, but they are annoying
|
||||
os.remove(f) # pragma: no cover
|
||||
if force_output:
|
||||
# When we create the .ini we can control the name.
|
||||
# But when the user does it we can trust the settings.
|
||||
m = search(r'Saving BOM File: (.*)', cmd_output.decode())
|
||||
if m and m.group(1) != output:
|
||||
cur = m.group(1)
|
||||
logger.debug('Renaming output file: {} -> {}'.format(cur, output))
|
||||
os.rename(cur, output)
|
||||
logger.debug('Output from command:\n'+cmd_output.decode())
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import os
|
||||
from subprocess import (call)
|
||||
from .pre_base import BasePreFlight
|
||||
from .error import (KiPlotConfigurationError)
|
||||
|
|
|
|||
|
|
@ -32,9 +32,9 @@ def test_bom_ok():
|
|||
ctx.run(no_board_file=True, extra=['-e', os.path.join(ctx.get_board_dir(), 'bom.sch')])
|
||||
# Check all outputs are there
|
||||
# Default format is PRJ_bom_REVISION
|
||||
name = os.path.join(BOM_DIR, prj+'_bom_')
|
||||
csv = name+'.csv'
|
||||
html = name+'_(pp).html'
|
||||
name = os.path.join(BOM_DIR, prj)
|
||||
csv = name+'-bom.csv'
|
||||
html = name+'_bom__(pp).html'
|
||||
ctx.expect_out_file(csv)
|
||||
ctx.expect_out_file(html)
|
||||
ctx.search_in_file(csv, ['R,R1,100', 'R,R2,200', 'C,C1,1uF'])
|
||||
|
|
@ -46,3 +46,50 @@ def test_bom_fail():
|
|||
ctx = context.TestContext('BoM_fail', 'bom_no_xml', 'bom', BOM_DIR)
|
||||
ctx.run(BOM_ERROR)
|
||||
ctx.clean_up()
|
||||
|
||||
|
||||
def test_bom_cfg_1():
|
||||
prj = 'bom'
|
||||
ctx = context.TestContext('BoMConfig1', prj, 'bom_cfg', BOM_DIR)
|
||||
ctx.run(no_board_file=True, extra=['-e', os.path.join(ctx.get_board_dir(), 'bom.sch')])
|
||||
name = os.path.join(BOM_DIR, prj)
|
||||
csv = name+'-bom.csv'
|
||||
ctx.expect_out_file(csv)
|
||||
ctx.search_in_file(csv, ['R,R1,100 ~', 'R,R2,200 ~', 'C,C1,1uF ~'])
|
||||
ctx.clean_up()
|
||||
|
||||
|
||||
def test_bom_cfg_2():
|
||||
prj = 'bom'
|
||||
ctx = context.TestContext('BoMConfig2', prj, 'bom_cfg2', BOM_DIR)
|
||||
ctx.run(no_board_file=True, extra=['-e', os.path.join(ctx.get_board_dir(), 'bom.sch')])
|
||||
name = os.path.join(BOM_DIR, prj)
|
||||
csv = name+'-bom.csv'
|
||||
ctx.expect_out_file(csv)
|
||||
ctx.search_in_file(csv, ['R,100,R1', 'R,200,R2'])
|
||||
ctx.search_not_in_file(csv, ['C,1uF,C1'])
|
||||
ctx.clean_up()
|
||||
|
||||
|
||||
def test_bom_cfg_3():
|
||||
""" Without any column """
|
||||
prj = 'bom'
|
||||
ctx = context.TestContext('BoMConfig3', prj, 'bom_cfg3', BOM_DIR)
|
||||
ctx.run(no_board_file=True, extra=['-e', os.path.join(ctx.get_board_dir(), 'bom.sch')])
|
||||
name = os.path.join(BOM_DIR, prj)
|
||||
csv = name+'-bom.csv'
|
||||
ctx.expect_out_file(csv)
|
||||
ctx.search_in_file(csv, ['R,R1,100', 'R,R2,200', 'C,C1,1uF'])
|
||||
ctx.clean_up()
|
||||
|
||||
|
||||
def test_bom_cfg_4():
|
||||
""" Without join """
|
||||
prj = 'bom'
|
||||
ctx = context.TestContext('BoMConfig4', prj, 'bom_cfg4', BOM_DIR)
|
||||
ctx.run(no_board_file=True, extra=['-e', os.path.join(ctx.get_board_dir(), 'bom.sch')])
|
||||
name = os.path.join(BOM_DIR, prj)
|
||||
csv = name+'-bom.csv'
|
||||
ctx.expect_out_file(csv)
|
||||
ctx.search_in_file(csv, ['R,100,R1', 'R,200,R2', 'C,1uF,C1'])
|
||||
ctx.clean_up()
|
||||
|
|
|
|||
|
|
@ -38,6 +38,9 @@ Tests various errors in the config file
|
|||
- Unknown section
|
||||
- HPGL wrong pen_number
|
||||
- KiBoM wrong format
|
||||
- Invalid column name
|
||||
- Failed to get columns
|
||||
- Column without field
|
||||
- PcbDraw
|
||||
- Wrong color
|
||||
|
||||
|
|
@ -53,7 +56,7 @@ sys.path.insert(0, os.path.dirname(prev_dir))
|
|||
# Utils import
|
||||
from utils import context
|
||||
sys.path.insert(0, os.path.dirname(prev_dir))
|
||||
from kiplot.misc import (EXIT_BAD_CONFIG, PLOT_ERROR)
|
||||
from kiplot.misc import (EXIT_BAD_CONFIG, PLOT_ERROR, BOM_ERROR)
|
||||
|
||||
|
||||
PRJ = 'fail-project'
|
||||
|
|
@ -399,11 +402,32 @@ def test_error_hpgl_pen_num():
|
|||
|
||||
def test_error_bom_wrong_format():
|
||||
ctx = context.TestContext('BoMWrongFormat', PRJ, 'error_bom_wrong_format', '')
|
||||
ctx.run(EXIT_BAD_CONFIG)
|
||||
ctx.run(EXIT_BAD_CONFIG, no_board_file=True, extra=['-e', os.path.join(ctx.get_board_dir(), 'bom.sch')])
|
||||
assert ctx.search_err("Option .?format.? must be any of")
|
||||
ctx.clean_up()
|
||||
|
||||
|
||||
def test_error_bom_column():
|
||||
ctx = context.TestContext('BoMColumn', PRJ, 'error_bom_column', '')
|
||||
ctx.run(EXIT_BAD_CONFIG, no_board_file=True, extra=['-e', os.path.join(ctx.get_board_dir(), 'bom.sch')])
|
||||
assert ctx.search_err("Invalid column name .?Impossible.?")
|
||||
ctx.clean_up()
|
||||
|
||||
|
||||
def test_error_bom_no_columns():
|
||||
ctx = context.TestContext('BoMNoColumns', PRJ, 'error_bom_column', '')
|
||||
ctx.run(BOM_ERROR, no_board_file=True, extra=['-e', os.path.join(ctx.get_board_dir(), 'bom_no_xml.sch')])
|
||||
assert ctx.search_err("Failed to get the column names")
|
||||
ctx.clean_up()
|
||||
|
||||
|
||||
def test_error_bom_no_field():
|
||||
ctx = context.TestContext('BoMNoField', PRJ, 'error_bom_no_field', '')
|
||||
ctx.run(EXIT_BAD_CONFIG, no_board_file=True, extra=['-e', os.path.join(ctx.get_board_dir(), 'fail-erc.sch')])
|
||||
assert ctx.search_err("Missing or empty .?field.?")
|
||||
ctx.clean_up()
|
||||
|
||||
|
||||
def test_error_wrong_boolean():
|
||||
ctx = context.TestContext('WrongBoolean', PRJ, 'error_wrong_boolean', '')
|
||||
ctx.run(EXIT_BAD_CONFIG)
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ outputs:
|
|||
options:
|
||||
format: HTML # HTML or CSV
|
||||
variant: pp
|
||||
output: '' # Keep KiBoM name
|
||||
conf: ''
|
||||
|
||||
- name: 'bom_csv'
|
||||
comment: "Bill of Materials in CSV format"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
# Example KiPlot config file
|
||||
kiplot:
|
||||
version: 1
|
||||
|
||||
outputs:
|
||||
- name: 'bom_csv'
|
||||
comment: "Bill of Materials in CSV format"
|
||||
type: kibom
|
||||
dir: BoM
|
||||
options:
|
||||
format: CSV # HTML or CSV
|
||||
conf:
|
||||
ignore_dnf: true
|
||||
columns:
|
||||
- Part
|
||||
- References
|
||||
- field: Value
|
||||
join:
|
||||
- Footprint
|
||||
- Datasheet
|
||||
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
# Example KiPlot config file
|
||||
kiplot:
|
||||
version: 1
|
||||
|
||||
outputs:
|
||||
- name: 'bom_csv'
|
||||
comment: "Bill of Materials in CSV format"
|
||||
type: kibom
|
||||
dir: BoM
|
||||
options:
|
||||
format: CSV # HTML or CSV
|
||||
conf:
|
||||
ignore_dnf: true
|
||||
digikey_link:
|
||||
- digikey#
|
||||
- digikey_alt#
|
||||
component_aliases:
|
||||
- ['r', 'r_small', 'res', 'resistor']
|
||||
- - 'l'
|
||||
- 'l_small'
|
||||
- 'inductor'
|
||||
include_only:
|
||||
- field: References
|
||||
regex: R\d
|
||||
exclude_any:
|
||||
- field: References
|
||||
regex: C\d
|
||||
columns:
|
||||
- Part
|
||||
- field: Value
|
||||
name: Valor
|
||||
join: Footprint
|
||||
- References
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
# Example KiPlot config file
|
||||
kiplot:
|
||||
version: 1
|
||||
|
||||
outputs:
|
||||
- name: 'bom_csv'
|
||||
comment: "Bill of Materials in CSV format"
|
||||
type: kibom
|
||||
dir: BoM
|
||||
options:
|
||||
format: CSV # HTML or CSV
|
||||
conf:
|
||||
ignore_dnf: true
|
||||
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
# Example KiPlot config file
|
||||
kiplot:
|
||||
version: 1
|
||||
|
||||
outputs:
|
||||
- name: 'bom_csv'
|
||||
comment: "Bill of Materials in CSV format"
|
||||
type: kibom
|
||||
dir: BoM
|
||||
options:
|
||||
format: CSV # HTML or CSV
|
||||
conf:
|
||||
ignore_dnf: true
|
||||
columns:
|
||||
- Part
|
||||
- field: Value
|
||||
name: Valor
|
||||
- References
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
# Example KiPlot config file
|
||||
kiplot:
|
||||
version: 1
|
||||
|
||||
outputs:
|
||||
- name: 'bom_csv'
|
||||
comment: "Bill of Materials in CSV format"
|
||||
type: kibom
|
||||
dir: BoM
|
||||
options:
|
||||
format: CSV # HTML or CSV
|
||||
conf:
|
||||
ignore_dnf: true
|
||||
digikey_link:
|
||||
- digikey#
|
||||
- digikey_alt#
|
||||
component_aliases:
|
||||
- ['r', 'r_small', 'res', 'resistor']
|
||||
- - 'l'
|
||||
- 'l_small'
|
||||
- 'inductor'
|
||||
include_only:
|
||||
- field: References
|
||||
regex: R\d
|
||||
exclude_any:
|
||||
- field: References
|
||||
regex: C\d
|
||||
columns:
|
||||
- Part
|
||||
- field: Impossible
|
||||
name: Valor
|
||||
join: Footprint
|
||||
- References
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
# Example KiPlot config file
|
||||
kiplot:
|
||||
version: 1
|
||||
|
||||
outputs:
|
||||
- name: 'bom_csv'
|
||||
comment: "Bill of Materials in CSV format"
|
||||
type: kibom
|
||||
dir: BoM
|
||||
options:
|
||||
format: CSV # HTML or CSV
|
||||
conf:
|
||||
ignore_dnf: true
|
||||
digikey_link:
|
||||
- digikey#
|
||||
- digikey_alt#
|
||||
component_aliases:
|
||||
- ['r', 'r_small', 'res', 'resistor']
|
||||
- - 'l'
|
||||
- 'l_small'
|
||||
- 'inductor'
|
||||
include_only:
|
||||
- field: References
|
||||
regex: R\d
|
||||
exclude_any:
|
||||
- field: References
|
||||
regex: C\d
|
||||
columns:
|
||||
- Part
|
||||
- name: Valor
|
||||
join: Footprint
|
||||
- References
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
# Example KiPlot config file
|
||||
kiplot:
|
||||
version: 1
|
||||
|
||||
outputs:
|
||||
- name: 'bom_html'
|
||||
comment: "Bill of Materials in HTML format"
|
||||
type: kibom
|
||||
dir: BoM
|
||||
options:
|
||||
format: CSV # HTML or CSV
|
||||
variant: pp
|
||||
conf:
|
||||
ignore_dnf: False
|
||||
html_generate_dnf: False
|
||||
use_alt: True
|
||||
number_rows: False
|
||||
group_connectors: False
|
||||
test_regex: False
|
||||
merge_blank_fields: False
|
||||
fit_field: 'Configure'
|
||||
datasheet_as_link: 'manf#'
|
||||
hide_headers: False
|
||||
hide_pcb_info: True
|
||||
digikey_link:
|
||||
- digikey#
|
||||
- digikey_alt#
|
||||
group_fields:
|
||||
- Part
|
||||
- Part Lib
|
||||
component_aliases:
|
||||
- ['r', 'r_small', 'res', 'resistor']
|
||||
- ['l', 'l_small', 'inductor']
|
||||
- - c
|
||||
- c_small
|
||||
- cap
|
||||
- capacitor
|
||||
include_only:
|
||||
- column: 'References'
|
||||
regex: 'C.*'
|
||||
exclude_any:
|
||||
- column: References
|
||||
regex: '^TP[0-9]*'
|
||||
- column: References
|
||||
regex: '^FID'
|
||||
- column: Part
|
||||
regex: 'mount.*hole'
|
||||
- column: Part
|
||||
regex: 'solder.*bridge'
|
||||
- column: Part
|
||||
regex: 'test.*point'
|
||||
- column: Footprint
|
||||
regex: 'test.*point'
|
||||
- column: Footprint
|
||||
regex: 'mount.*hole'
|
||||
- column: Footprint
|
||||
regex: 'fiducial'
|
||||
columns:
|
||||
- References
|
||||
- field: Value
|
||||
name: Valor
|
||||
- Part
|
||||
- field: Description
|
||||
join:
|
||||
- Footprint
|
||||
- Footprint Lib
|
||||
|
||||
Loading…
Reference in New Issue