Changed how variant field renamer is implemented.

- Now is a separated filter type (`var_rename`).
- Now all variants support a `pre_transform` filter list.
- An internal `_var_rename` filter is defined (default).
This commit is contained in:
Salvador E. Tropea 2020-12-23 10:19:23 -03:00
parent 986f0c7157
commit 204ad189de
14 changed files with 158 additions and 43 deletions

View File

@ -271,7 +271,7 @@ Currently the only type available is `generic`.
#### Supported filters:
- generic: Generic filter
This filter is based on regular exressions.
This filter is based on regular expressions.
It also provides some shortcuts for common situations.
Note that matches aren't case sensitive and spaces at the beggining and the end are removed.
* Valid keys:
@ -312,6 +312,12 @@ Currently the only type available is `generic`.
Use `dnf_list` for ['dnf', 'dnl', 'dnp', 'do not fit', 'do not load', 'do not place', 'no stuff', 'nofit', 'noload', 'noplace', 'nostuff', 'not fitted', 'not loaded', 'not placed'].
Use `dnc_list` for ['dnc', 'do not change', 'fixed', 'no change'].
- `name`: [string=''] Used to identify this particular filter definition.
- var_rename: Var_Rename
This filter implements the VARIANT:FIELD=VALUE renamer to get FIELD=VALUE when VARIANT is in use.
* Valid keys:
- `comment`: [string=''] A comment for documentation purposes.
- `name`: [string=''] Used to identify this particular filter definition.
- `separator`: [string=':'] Separator used between the variant and the field name.

View File

@ -4,7 +4,7 @@
# License: GPL-3.0
# Project: KiBot (formerly KiPlot)
from .registrable import RegFilter, Registrable, RegOutput
from .misc import IFILL_MECHANICAL
from .misc import IFILT_MECHANICAL, IFILT_VAR_RENAME
from .error import KiPlotConfigurationError
from .bom.columnlist import ColumnList
from .macros import macros, document # noqa: F401
@ -65,11 +65,11 @@ class NotFilter(Registrable):
return not self._filter.filter(comp)
def reset_filters(comps):
for c in comps:
c.included = True
c.fitted = True
c.fixed = False
def apply_pre_transform(comps, filter):
if filter:
logger.debug('Applying transform filter `{}`'.format(filter.name))
for c in comps:
filter.filter(c)
def apply_exclude_filter(comps, filter):
@ -125,6 +125,14 @@ class BaseFilter(RegFilter):
logger.debug('Creating internal filter: '+str(o_tree))
return o_tree
@staticmethod
def _create_var_rename(name):
o_tree = {'name': name}
o_tree['type'] = 'var_rename'
o_tree['comment'] = 'Internal default variant field renamer filter'
logger.debug('Creating internal filter: '+str(o_tree))
return o_tree
@staticmethod
def _create_kibom_dnx(name):
type = name[7:10]
@ -146,10 +154,12 @@ class BaseFilter(RegFilter):
@staticmethod
def _create_internal_filter(name):
if name == IFILL_MECHANICAL:
if name == IFILT_MECHANICAL:
tree = BaseFilter._create_mechanical(name)
elif name.startswith('_kibom_dn') and len(name) >= 10:
tree = BaseFilter._create_kibom_dnx(name)
elif name == IFILT_VAR_RENAME:
tree = BaseFilter._create_var_rename(name)
else:
return None
filter = RegFilter.get_class_for(tree['type'])()

View File

@ -28,7 +28,7 @@ class DNFList(Optionable):
@filter_class
class Generic(BaseFilter): # noqa: F821
""" Generic filter
This filter is based on regular exressions.
This filter is based on regular expressions.
It also provides some shortcuts for common situations.
Note that matches aren't case sensitive and spaces at the beggining and the end are removed """
def __init__(self):

50
kibot/fil_var_rename.py Normal file
View File

@ -0,0 +1,50 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2020 Salvador E. Tropea
# Copyright (c) 2020 Instituto Nacional de Tecnología Industrial
# License: GPL-3.0
# Project: KiBot (formerly KiPlot)
"""
Implements the VARIANT:FIELD=VALUE renamer to get FIELD=VALUE when VARIANT is in use.
"""
# from re import compile, IGNORECASE
# from .optionable import Optionable
# from .bom.columnlist import ColumnList
from .gs import GS
# from .misc import DNF, DNC
from .macros import macros, document, filter_class # noqa: F401
# from .out_base import BoMRegex
from . import log
logger = log.get_logger(__name__)
@filter_class
class Var_Rename(BaseFilter): # noqa: F821
""" Var_Rename
This filter implements the VARIANT:FIELD=VALUE renamer to get FIELD=VALUE when VARIANT is in use """
def __init__(self):
super().__init__()
with document:
self.separator = ':'
""" Separator used between the variant and the field name """
def config(self):
super().config()
if not self.separator:
self.separator = ':'
def filter(self, comp):
""" Look for fields containing VARIANT:FIELD used to change fields according to the variant """
if not GS.variant:
# No variant in use, nothing to do
return
for variant in GS.variant:
for name, value in comp.get_user_fields():
res = name.strip().split(self.separator)
if len(res) == 2:
f_variant = res[0].lower()
f_field = res[1].lower()
if f_variant == variant:
if GS.debug_level > 2:
logger.debug('ref: {} value: {} -> {}'.format(comp.ref, comp.get_field_value(f_field), value))
comp.set_field(f_field, value)

View File

@ -55,6 +55,8 @@ class GS(object):
pcb_date = None
pcb_rev = None
pcb_comp = None
# Current variant/s
variant = None
# Global defaults
# This is used as default value for classes supporting "output" option
def_global_output = '%f-%i%v.%x'

View File

@ -72,7 +72,8 @@ AUTO_SCALE = 0
KICAD_VERSION_5_99 = 5099000
# Internal filter names
IFILL_MECHANICAL = '_mechanical'
IFILT_MECHANICAL = '_mechanical'
IFILT_VAR_RENAME = '_var_rename'
# KiCad 5 GUI values for the attribute
UI_THT = 0 # 1 for KiCad 6
UI_SMD = 1 # 2 for KiCad 6

View File

@ -3,6 +3,7 @@
# Copyright (c) 2020 Instituto Nacional de Tecnología Industrial
# License: GPL-3.0
# Project: KiBot (formerly KiPlot)
from copy import deepcopy
from .gs import GS
from .kiplot import load_sch
from .misc import Rect, KICAD_VERSION_5_99, W_WRONGPASTE
@ -13,7 +14,7 @@ else:
from pcbnew import EDGE_MODULE, wxPoint, LSET
from .registrable import RegOutput
from .optionable import Optionable, BaseOptions
from .fil_base import BaseFilter, apply_fitted_filter, reset_filters
from .fil_base import BaseFilter, apply_fitted_filter
from .macros import macros, document # noqa: F401
from . import log
@ -259,9 +260,9 @@ class VariantOptions(BaseOptions):
return
load_sch()
# Get the components list from the schematic
comps = GS.sch.get_components()
# Note: we work with a copy to avoid changes by filters affecting other outputs
comps = deepcopy(GS.sch.get_components())
# Apply the filter
reset_filters(comps)
apply_fitted_filter(comps, self.dnf_filter)
# Apply the variant
if self.variant:

View File

@ -8,6 +8,7 @@ Internal BoM (Bill of Materials) output for KiBot.
This is somehow compatible with KiBoM.
"""
import os
from copy import deepcopy
from .gs import GS
from .optionable import Optionable, BaseOptions
from .registrable import RegOutput
@ -17,7 +18,7 @@ from .macros import macros, document, output_class # noqa: F401
from .bom.columnlist import ColumnList, BoMError
from .bom.bom import do_bom
from .var_kibom import KiBoM
from .fil_base import BaseFilter, apply_exclude_filter, apply_fitted_filter, apply_fixed_filter, reset_filters
from .fil_base import BaseFilter, apply_exclude_filter, apply_fitted_filter, apply_fixed_filter
from . import log
# To debug the `with document` we can use:
# from .mcpyrate.debug import macros, step_expansion
@ -373,8 +374,9 @@ class BoMOptions(BaseOptions):
# Get the components list from the schematic
comps = GS.sch.get_components()
get_board_comps_data(comps)
# We work with a copy to avoid changes by filters affecting other outputs
comps = deepcopy(comps)
# Apply all the filters
reset_filters(comps)
apply_exclude_filter(comps, self.exclude_filter)
apply_fitted_filter(comps, self.dnf_filter)
apply_fixed_filter(comps, self.dnc_filter)

View File

@ -5,7 +5,7 @@
# Project: KiBot (formerly KiPlot)
from .registrable import RegVariant
from .optionable import Optionable
from .fil_base import apply_exclude_filter, apply_fitted_filter, apply_fixed_filter
from .fil_base import apply_exclude_filter, apply_fitted_filter, apply_fixed_filter, BaseFilter, apply_pre_transform
from .macros import macros, document # noqa: F401
@ -23,6 +23,9 @@ class BaseVariant(RegVariant):
self.file_id = ''
""" Text to use as the """
# * Filters
self.pre_transform = Optionable
""" [string|list(string)=''] Name of the filter to transform fields before applying other filters.
Use '_var_rename' to transform VARIANT:FIELD fields """
self.exclude_filter = Optionable
""" [string|list(string)=''] Name of the filter to exclude components from BoM processing.
Use '_mechanical' for the default KiBoM behavior """
@ -33,8 +36,13 @@ class BaseVariant(RegVariant):
""" [string|list(string)=''] Name of the filter to mark components as 'Do Not Change'.
Use '_kibom_dnc' for the default KiBoM behavior """
def config(self):
super().config()
self.pre_transform = BaseFilter.solve_filter(self.pre_transform, 'pre_transform')
def filter(self, comps):
# Apply all the filters
apply_pre_transform(comps, self.pre_transform)
apply_exclude_filter(comps, self.exclude_filter)
apply_fitted_filter(comps, self.dnf_filter)
apply_fixed_filter(comps, self.dnc_filter)

View File

@ -8,7 +8,7 @@ Implements the IBoM variants mechanism.
"""
from .optionable import Optionable
from .gs import GS
from .misc import IFILL_MECHANICAL
from .misc import IFILT_MECHANICAL
from .fil_base import BaseFilter
from .macros import macros, document, variant_class # noqa: F401
from . import log
@ -48,7 +48,7 @@ class IBoM(BaseVariant): # noqa: F821
def config(self):
super().config()
self.exclude_filter = BaseFilter.solve_filter(self.exclude_filter, 'exclude_filter', IFILL_MECHANICAL)
self.exclude_filter = BaseFilter.solve_filter(self.exclude_filter, 'exclude_filter', IFILT_MECHANICAL)
self.dnf_filter = BaseFilter.solve_filter(self.dnf_filter, 'dnf_filter')
self.dnc_filter = BaseFilter.solve_filter(self.dnc_filter, 'dnc_filter')
self.variants_blacklist = self._force_list(self.variants_blacklist)
@ -67,6 +67,7 @@ class IBoM(BaseVariant): # noqa: F821
return False
def filter(self, comps):
GS.variant = self.variants_whitelist
super().filter(comps)
logger.debug("Applying IBoM style variants `{}`".format(self.name))
# Make black/white lists case insensitive

View File

@ -8,7 +8,7 @@ Implements the KiBoM variants mechanism.
"""
from .optionable import Optionable
from .gs import GS
from .misc import IFILL_MECHANICAL
from .misc import IFILT_MECHANICAL
from .fil_base import BaseFilter
from .macros import macros, document, variant_class # noqa: F401
from . import log
@ -32,10 +32,6 @@ class KiBoM(BaseVariant): # noqa: F821
""" Name of the field used to clasify components """
self.variant = Optionable
""" [string|list(string)=''] Board variant(s) """
self.field_changer = False
""" Enable the VARIANT.FIELD to FIELD rename mechanism """
self.field_variant_separator = '.'
""" Separator used for the VARIANT.FIELD rename mechanism """
def set_def_filters(self, exclude_filter, dnf_filter, dnc_filter):
""" Filters delegated to the variant """
@ -61,7 +57,7 @@ class KiBoM(BaseVariant): # noqa: F821
# 3) KiBoM default behavior
# exclude_filter
if not self._def_exclude_filter:
self._def_exclude_filter = IFILL_MECHANICAL
self._def_exclude_filter = IFILT_MECHANICAL
self.exclude_filter = BaseFilter.solve_filter(self.exclude_filter, 'exclude_filter', self._def_exclude_filter)
# dnf_filter
if not self._def_dnf_filter:
@ -97,21 +93,8 @@ class KiBoM(BaseVariant): # noqa: F821
# No match
return not exclusive
def _rename_var_fields(self, comp):
""" Look for fields containing VARIANT:FIELD used to change fields according to the variant """
for variant in self.variant:
for name, value in comp.get_user_fields():
res = name.strip().split(self.field_variant_separator)
if len(res) == 2:
f_variant = res[0].lower()
f_field = res[1].lower()
if f_variant == variant:
if GS.debug_level > 2:
logger.debug('ref: {} value: {} -> {}'.format(comp.ref, comp.get_field_value(f_field), value))
comp.set_field(f_field, value)
def filter(self, comps):
GS.variant = self.variant
super().filter(comps)
logger.debug("Applying KiBoM style variants `{}`".format(self.name))
for c in comps:
@ -121,8 +104,6 @@ class KiBoM(BaseVariant): # noqa: F821
value = c.value.lower()
config = c.get_field_value(self.config_field).lower()
c.fitted = self._variant_comp_is_fitted(value, config)
if c.fitted and self.field_changer:
self._rename_var_fields(c)
elif not c.fitted and GS.debug_level > 2:
if not c.fitted and GS.debug_level > 2:
logger.debug('ref: {} value: {} config: {} variant: {} -> False'.
format(c.ref, value, config, self.variant))

View File

@ -1226,6 +1226,18 @@ def test_int_bom_variant_t2b():
ctx.clean_up()
def test_int_bom_variant_t2c():
prj = 'kibom-variant_2'
ctx = context.TestContextSCH('test_int_bom_variant_t2c', prj, 'int_bom_var_t2c_csv', BOM_DIR)
ctx.run()
rows, header, info = ctx.load_csv(prj+'-bom_(test).csv')
ref_column = header.index(REF_COLUMN_NAME)
val_column = header.index(VALUE_COLUMN_NAME)
check_kibom_test_netlist(rows, ref_column, 2, ['R2'], ['R1', 'C1', 'C2'])
check_value(rows, ref_column, 'R1', val_column, '3k3')
ctx.clean_up()
def test_int_bom_variant_t2s():
prj = 'kibom-variant_2'
ctx = context.TestContextSCH('test_int_bom_variant_t2s', prj, 'int_bom_var_t2s_csv', BOM_DIR)

View File

@ -2,6 +2,11 @@
kibot:
version: 1
filters:
- name: 'Variant rename'
type: var_rename
separator: ':'
variants:
- name: 'production'
comment: 'Production variant'
@ -14,8 +19,7 @@ variants:
type: kibom
file_id: '_(test)'
variant: test
field_changer: true
field_variant_separator: ':'
pre_transform: 'Variant rename'
outputs:
- name: 'bom_internal'

View File

@ -0,0 +1,37 @@
# Example KiBot config file
kibot:
version: 1
variants:
- name: 'production'
comment: 'Production variant'
type: kibom
file_id: '_(production)'
variant: production
- name: 'test'
comment: 'Test variant'
type: kibom
file_id: '_(test)'
variant: test
pre_transform: '_var_rename'
outputs:
- name: 'bom_internal'
comment: "Bill of Materials in CSV format"
type: bom
dir: BoM
- name: 'bom_internal_production'
comment: "Bill of Materials in CSV format for production"
type: bom
dir: BoM
options:
variant: production
- name: 'bom_internal_test'
comment: "Bill of Materials in CSV format for test"
type: bom
dir: BoM
options:
variant: test