[iBoM] Added support for variants that change component fields

Closes #242
This commit is contained in:
Salvador E. Tropea 2022-07-30 10:39:48 -03:00
parent 6bd8ff9c6b
commit 3b6c9e7d8b
3 changed files with 70 additions and 11 deletions

View File

@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Preflights can be imported (#181)
- `--dont-stop` command line option, to try to continue even on errors (#209)
- PDF/SVG PCB Print: option to print all pages/single page (#236)
- iBoM: Support for variants that change component fields (#242)
### Fixed
- OAR computation (Report) (#225)

View File

@ -423,6 +423,39 @@ class VariantOptions(BaseOptions):
GS.board.GetTitleBlock().SetTitle(self.old_title)
self.old_title = None
def sch_fields_to_pcb(self, comps, board):
""" Change the module/footprint data according to the filtered fields.
iBoM can parse it. """
comps_hash = self.get_refs_hash()
self.sch_fields_to_pcb_bkp = {}
for m in GS.get_modules_board(board):
ref = m.GetReference()
comp = comps_hash.get(ref, None)
if comp is not None:
properties = {f.name: f.value for f in comp.fields}
old_value = m.GetValue()
m.SetValue(properties['Value'])
if GS.ki6():
old_properties = m.GetProperties()
old_fp = m.GetFPIDAsString()
m.SetProperties(properties)
m.SetFPIDAsString(properties['Footprint'])
self.sch_fields_to_pcb_bkp[ref] = (old_value, old_properties, old_fp)
else:
self.sch_fields_to_pcb_bkp[ref] = old_value
def restore_sch_fields_to_pcb(self, board):
for m in GS.get_modules_board(board):
ref = m.GetReference()
data = self.sch_fields_to_pcb_bkp.get(ref, None)
if data is not None:
if GS.ki6():
m.SetValue(data[0])
m.SetProperties(data[1])
m.SetFPIDAsString(data[2])
else:
m.SetValue(data)
def save_tmp_board(self, dir=None):
""" Save the PCB to a temporal file.
Advantage: all relative paths inside the file remains valid

View File

@ -20,7 +20,8 @@ Dependencies:
"""
import os
from subprocess import (check_output, STDOUT, CalledProcessError)
from shutil import which
from shutil import which, rmtree
from tempfile import mkdtemp
from .misc import BOM_ERROR, W_EXTNAME, W_NONETLIST
from .gs import GS
from .out_base import VariantOptions
@ -164,19 +165,35 @@ class IBoMOptions(VariantOptions):
cur = os.path.join(output_dir, 'ibom.html')
else:
output_dir = name
cmd = [tool, GS.pcb_file, '--dest-dir', output_dir, '--no-browser', ]
# Solve the variants stuff
ori_extra_data_file = self.extra_data_file
net_dir = None
pcb_name = GS.pcb_file
if self._comps:
# Write a custom netlist to a temporal dir
net_dir = mkdtemp(prefix='tmp-kibot-ibom-')
netlist = os.path.join(net_dir, GS.pcb_basename+'.xml')
self.extra_data_file = netlist
logger.debug('Creating variant netlist `{}`'.format(netlist))
with open(netlist, 'wb') as f:
GS.sch.save_netlist(f, self._comps)
# Write a board with the filtered values applied
self.sch_fields_to_pcb(self._comps, GS.board)
pcb_name = self.save_tmp_board(dir=net_dir)
else:
# Check if the user wants extra_fields but there is no data about them (#68)
if self.need_extra_fields() and not os.path.isfile(self.extra_data_file):
logger.warning(W_NONETLIST+'iBoM needs information about user defined fields and no netlist provided')
if self._extra_data_file_guess:
logger.warning(W_NONETLIST+'Create a BoM in XML format or use the `update_xml` preflight')
# If the name of the netlist is just a guess remove it from the options
self.extra_data_file = ''
else:
logger.warning(W_NONETLIST+"The file name used in `extra_data_file` doesn't exist")
cmd = [tool, pcb_name, '--dest-dir', output_dir, '--no-browser', ]
if not which(tool) and not os.access(tool, os.X_OK):
# Plugin could be installed without execute flags
cmd.insert(0, 'python3')
# Check if the user wants extra_fields but there is no data about them (#68)
if self.need_extra_fields() and not os.path.isfile(self.extra_data_file):
logger.warning(W_NONETLIST+'iBoM needs information about user defined fields and no netlist provided')
if self._extra_data_file_guess:
logger.warning(W_NONETLIST+'Create a BoM in XML format or use the `update_xml` preflight')
# If the name of the netlist is just a guess remove it from the options
self.extra_data_file = ''
else:
logger.warning(W_NONETLIST+"The file name used in `extra_data_file` doesn't exist")
# Apply variants/filters
to_remove = ','.join(self.get_not_fitted_refs())
if self.blacklist and to_remove:
@ -204,6 +221,14 @@ class IBoMOptions(VariantOptions):
if "'PCB_SHAPE' object has no attribute 'GetAngle'" in e.output.decode():
logger.error("Update Interactive HTML BoM your version doesn't support KiCad 6 files")
exit(BOM_ERROR)
finally:
if net_dir:
logger.debug('Removing temporal variant dir `{}`'.format(net_dir))
rmtree(net_dir)
# Restore the PCB properties and values
self.restore_sch_fields_to_pcb(GS.board)
# Restore the real name selected
self.extra_data_file = ori_extra_data_file
logger.debug('Output from command:\n'+cmd_output_dec+'\n')
if output:
logger.debug('Renaming output file: {} -> {}'.format(cur, output))