Added more coherence to the variants/filters.
- Now they behave more coherently across outputs. - Component status is explicitly reset, so having a variant + filter behaves in a more consistent way. Even when this isn't the recommended use. - Virtual components are handled by the exclude filter. - Moved code to modify the PCB to VariantOptions. - Added a real virtual component to the tests, not a ridiculous case.
This commit is contained in:
parent
23e46df1c5
commit
b03b56ae5d
|
|
@ -301,7 +301,7 @@ def group_components(cfg, components):
|
|||
groups = []
|
||||
# Iterate through each component, and test whether a group for these already exists
|
||||
for c in components:
|
||||
if not c.in_bom: # Skip components marked as excluded from BoM
|
||||
if not c.included: # Skip components marked as excluded from BoM
|
||||
continue
|
||||
# Cache the value used to sort
|
||||
if c.ref_prefix in RLC_PREFIX and c.value.lower() not in DNF:
|
||||
|
|
|
|||
|
|
@ -65,34 +65,35 @@ class NotFilter(Registrable):
|
|||
return not self._filter.filter(comp)
|
||||
|
||||
|
||||
def apply_in_bom_filter(comps, filter, reset=True):
|
||||
def reset_filters(comps):
|
||||
for c in comps:
|
||||
c.included = True
|
||||
c.fitted = True
|
||||
c.fixed = False
|
||||
|
||||
|
||||
def apply_exclude_filter(comps, filter):
|
||||
if filter:
|
||||
logger.debug('Applying filter `{}` to in_bom'.format(filter.name))
|
||||
logger.debug('Applying filter `{}` to exclude'.format(filter.name))
|
||||
for c in comps:
|
||||
c.in_bom = filter.filter(c)
|
||||
elif reset: # Reset the field
|
||||
for c in comps:
|
||||
c.in_bom = True
|
||||
if c.included:
|
||||
c.included = filter.filter(c)
|
||||
|
||||
|
||||
def apply_fitted_filter(comps, filter, reset=True):
|
||||
def apply_fitted_filter(comps, filter):
|
||||
if filter:
|
||||
logger.debug('Applying filter `{}` to fitted'.format(filter.name))
|
||||
for c in comps:
|
||||
c.fitted = filter.filter(c)
|
||||
elif reset: # Reset the field
|
||||
for c in comps:
|
||||
c.fitted = True
|
||||
if c.fitted:
|
||||
c.fitted = filter.filter(c)
|
||||
|
||||
|
||||
def apply_fixed_filter(comps, filter, reset=True):
|
||||
def apply_fixed_filter(comps, filter):
|
||||
if filter:
|
||||
logger.debug('Applying filter `{}` to fixed'.format(filter.name))
|
||||
for c in comps:
|
||||
c.fixed = filter.filter(c)
|
||||
elif reset: # Reset the field
|
||||
for c in comps:
|
||||
c.fixed = False
|
||||
if not c.fixed:
|
||||
c.fixed = filter.filter(c)
|
||||
|
||||
|
||||
class BaseFilter(RegFilter):
|
||||
|
|
@ -120,6 +121,7 @@ class BaseFilter(RegFilter):
|
|||
o_tree['comment'] = 'Internal default mechanical filter'
|
||||
o_tree['exclude_all_hash_ref'] = True
|
||||
o_tree['exclude_any'] = DEFAULT_EXCLUDE
|
||||
o_tree['exclude_virtual'] = True
|
||||
logger.debug('Creating internal filter: '+str(o_tree))
|
||||
return o_tree
|
||||
|
||||
|
|
|
|||
|
|
@ -783,7 +783,8 @@ class SchematicComponent(object):
|
|||
- Solded: only if True
|
||||
- BoM normal: only if True
|
||||
- BoM DNF: only if False
|
||||
- in_bom: equivalent to 'Exclude from BoM' but inverted
|
||||
- included: related to 'Exclude from BoM' but inverted,
|
||||
but also applied to other situations.
|
||||
- Solded: doesn't affected
|
||||
- BoM normal: only if True
|
||||
- BoM DNF: only if True (and fitted is False)
|
||||
|
|
@ -801,7 +802,7 @@ class SchematicComponent(object):
|
|||
self.desc = ''
|
||||
# Will be computed
|
||||
self.fitted = True
|
||||
self.in_bom = True
|
||||
self.included = True
|
||||
self.fixed = False
|
||||
# KiCad 5 PCB flags (mutually exclusive)
|
||||
self.smd = False
|
||||
|
|
@ -996,7 +997,7 @@ class SchematicComponent(object):
|
|||
|
||||
def write(self, f):
|
||||
# Fake lib to reflect fitted status
|
||||
lib = 'y' if self.fitted else 'n'
|
||||
lib = 'y' if self.fitted or not self.included else 'n'
|
||||
# Fake name using cache style
|
||||
name = '{}:{}_{}'.format(lib, self.lib, self.name)
|
||||
f.write('$Comp\n')
|
||||
|
|
|
|||
|
|
@ -6,12 +6,11 @@
|
|||
# Project: KiBot (formerly KiPlot)
|
||||
# Adapted from: https://github.com/johnbeard/kiplot
|
||||
import os
|
||||
from pcbnew import GERBER_JOBFILE_WRITER, PLOT_CONTROLLER, IsCopperLayer, LSET, wxPoint, EDGE_MODULE
|
||||
from pcbnew import GERBER_JOBFILE_WRITER, PLOT_CONTROLLER, IsCopperLayer
|
||||
from .out_base import (BaseOutput)
|
||||
from .error import (PlotError, KiPlotConfigurationError)
|
||||
from .layer import Layer
|
||||
from .gs import GS
|
||||
from .misc import UI_VIRTUAL, Rect
|
||||
from .out_base import VariantOptions
|
||||
from .macros import macros, document # noqa: F401
|
||||
from . import log
|
||||
|
|
@ -56,113 +55,17 @@ class AnyLayerOptions(VariantOptions):
|
|||
# We'll come back to this on a per-layer basis
|
||||
po.SetSkipPlotNPTH_Pads(False)
|
||||
|
||||
@staticmethod
|
||||
def cross_module(m, rect, layer):
|
||||
seg1 = EDGE_MODULE(m)
|
||||
m.Add(seg1)
|
||||
seg1.SetWidth(120000)
|
||||
seg1.SetStart(wxPoint(rect.x1, rect.y1))
|
||||
seg1.SetEnd(wxPoint(rect.x2, rect.y2))
|
||||
seg1.SetLayer(layer)
|
||||
seg2 = EDGE_MODULE(m)
|
||||
m.Add(seg2)
|
||||
seg2.SetWidth(120000)
|
||||
seg2.SetStart(wxPoint(rect.x1, rect.y2))
|
||||
seg2.SetEnd(wxPoint(rect.x2, rect.y1))
|
||||
seg2.SetLayer(layer)
|
||||
return [seg1, seg2]
|
||||
|
||||
def filter_components(self, board):
|
||||
""" Apply the variants and filters """
|
||||
if not self._comps:
|
||||
return None
|
||||
comps_hash = self.get_refs_hash()
|
||||
# Remove from solder past layers the filtered components
|
||||
exclude = LSET()
|
||||
exclude.addLayer(board.GetLayerID('F.Paste'))
|
||||
exclude.addLayer(board.GetLayerID('B.Paste'))
|
||||
old_layers = []
|
||||
fadhes = board.GetLayerID('F.Adhes')
|
||||
badhes = board.GetLayerID('B.Adhes')
|
||||
old_fadhes = []
|
||||
old_badhes = []
|
||||
ffab = board.GetLayerID('F.Fab')
|
||||
bfab = board.GetLayerID('B.Fab')
|
||||
extra_ffab_lines = []
|
||||
extra_bfab_lines = []
|
||||
for m in board.GetModules():
|
||||
ref = m.GetReference()
|
||||
# Rectangle containing the drawings, no text
|
||||
frect = Rect()
|
||||
brect = Rect()
|
||||
c = comps_hash.get(ref, None)
|
||||
if (c and not c.fitted) or m.GetAttributes() == UI_VIRTUAL:
|
||||
# Remove all pads from *.Paste
|
||||
old_c_layers = []
|
||||
for p in m.Pads():
|
||||
pad_layers = p.GetLayerSet()
|
||||
old_c_layers.append(pad_layers.FmtHex())
|
||||
pad_layers.removeLayerSet(exclude)
|
||||
p.SetLayerSet(pad_layers)
|
||||
old_layers.append(old_c_layers)
|
||||
# Remove any graphical item in the *.Adhes layers
|
||||
# Also: meassure the *.Fab drawings size
|
||||
for gi in m.GraphicalItems():
|
||||
l_gi = gi.GetLayer()
|
||||
if l_gi == fadhes:
|
||||
gi.SetLayer(-1)
|
||||
old_fadhes.append(gi)
|
||||
if l_gi == badhes:
|
||||
gi.SetLayer(-1)
|
||||
old_badhes.append(gi)
|
||||
if gi.GetClass() == 'MGRAPHIC':
|
||||
if l_gi == ffab:
|
||||
frect.Union(gi.GetBoundingBox().getWxRect())
|
||||
if l_gi == bfab:
|
||||
brect.Union(gi.GetBoundingBox().getWxRect())
|
||||
# Cross the graphics in *.Fab
|
||||
if frect.x1 is not None:
|
||||
extra_ffab_lines.append(self.cross_module(m, frect, ffab))
|
||||
else:
|
||||
extra_ffab_lines.append(None)
|
||||
if brect.x1 is not None:
|
||||
extra_bfab_lines.append(self.cross_module(m, brect, bfab))
|
||||
else:
|
||||
extra_bfab_lines.append(None)
|
||||
# Store the data to undo the above actions
|
||||
self.comps_hash = comps_hash
|
||||
self.old_layers = old_layers
|
||||
self.old_fadhes = old_fadhes
|
||||
self.old_badhes = old_badhes
|
||||
self.extra_ffab_lines = extra_ffab_lines
|
||||
self.extra_bfab_lines = extra_bfab_lines
|
||||
return exclude
|
||||
self.comps_hash = self.get_refs_hash()
|
||||
self.cross_modules(board, self.comps_hash)
|
||||
return self.remove_paste_and_glue(board, self.comps_hash)
|
||||
|
||||
def unfilter_components(self, board):
|
||||
for m in board.GetModules():
|
||||
ref = m.GetReference()
|
||||
c = self.comps_hash.get(ref, None)
|
||||
if (c and not c.fitted) or m.GetAttributes() == UI_VIRTUAL:
|
||||
restore = self.old_layers.pop(0)
|
||||
for p in m.Pads():
|
||||
pad_layers = p.GetLayerSet()
|
||||
res = restore.pop(0)
|
||||
pad_layers.ParseHex(res, len(res))
|
||||
p.SetLayerSet(pad_layers)
|
||||
restore = self.extra_ffab_lines.pop(0)
|
||||
if restore:
|
||||
for line in restore:
|
||||
m.Remove(line)
|
||||
restore = self.extra_bfab_lines.pop(0)
|
||||
if restore:
|
||||
for line in restore:
|
||||
m.Remove(line)
|
||||
fadhes = board.GetLayerID('F.Adhes')
|
||||
for gi in self.old_fadhes:
|
||||
gi.SetLayer(fadhes)
|
||||
badhes = board.GetLayerID('B.Adhes')
|
||||
for gi in self.old_badhes:
|
||||
gi.SetLayer(badhes)
|
||||
self.uncross_modules(board, self.comps_hash)
|
||||
self.restore_paste_and_glue(board, self.comps_hash)
|
||||
|
||||
def run(self, output_dir, board, layers):
|
||||
super().run(output_dir, board)
|
||||
|
|
|
|||
|
|
@ -3,11 +3,13 @@
|
|||
# Copyright (c) 2020 Instituto Nacional de Tecnología Industrial
|
||||
# License: GPL-3.0
|
||||
# Project: KiBot (formerly KiPlot)
|
||||
from pcbnew import EDGE_MODULE, wxPoint, LSET
|
||||
from .gs import GS
|
||||
from .misc import Rect
|
||||
from .kiplot import load_sch
|
||||
from .registrable import RegOutput
|
||||
from .optionable import Optionable, BaseOptions
|
||||
from .fil_base import BaseFilter, apply_fitted_filter
|
||||
from .fil_base import BaseFilter, apply_fitted_filter, reset_filters
|
||||
from .macros import macros, document # noqa: F401
|
||||
from . import log
|
||||
|
||||
|
|
@ -92,14 +94,143 @@ class VariantOptions(BaseOptions):
|
|||
return {c.ref: c for c in self._comps}
|
||||
|
||||
def get_fitted_refs(self):
|
||||
""" List of fitted and included components """
|
||||
if not self._comps:
|
||||
return []
|
||||
return [c.ref for c in self._comps if c.fitted]
|
||||
return [c.ref for c in self._comps if c.fitted and c.included]
|
||||
|
||||
def get_not_fitted_refs(self):
|
||||
""" List of 'not fitted' components, also includes 'not included' """
|
||||
if not self._comps:
|
||||
return []
|
||||
return [c.ref for c in self._comps if not c.fitted]
|
||||
return [c.ref for c in self._comps if not c.fitted or not c.included]
|
||||
|
||||
@staticmethod
|
||||
def cross_module(m, rect, layer):
|
||||
""" Draw a cross over a module.
|
||||
The rect is a Rect object with the size.
|
||||
The layer is which layer id will be used. """
|
||||
seg1 = EDGE_MODULE(m)
|
||||
seg1.SetWidth(120000)
|
||||
seg1.SetStart(wxPoint(rect.x1, rect.y1))
|
||||
seg1.SetEnd(wxPoint(rect.x2, rect.y2))
|
||||
seg1.SetLayer(layer)
|
||||
seg1.SetLocalCoord() # Update the local coordinates
|
||||
m.Add(seg1)
|
||||
seg2 = EDGE_MODULE(m)
|
||||
seg2.SetWidth(120000)
|
||||
seg2.SetStart(wxPoint(rect.x1, rect.y2))
|
||||
seg2.SetEnd(wxPoint(rect.x2, rect.y1))
|
||||
seg2.SetLayer(layer)
|
||||
seg2.SetLocalCoord() # Update the local coordinates
|
||||
m.Add(seg2)
|
||||
return [seg1, seg2]
|
||||
|
||||
def cross_modules(self, board, comps_hash):
|
||||
""" Draw a cross in all 'not fitted' modules using *.Fab layer """
|
||||
# Cross the affected components
|
||||
ffab = board.GetLayerID('F.Fab')
|
||||
bfab = board.GetLayerID('B.Fab')
|
||||
extra_ffab_lines = []
|
||||
extra_bfab_lines = []
|
||||
for m in board.GetModules():
|
||||
ref = m.GetReference()
|
||||
# Rectangle containing the drawings, no text
|
||||
frect = Rect()
|
||||
brect = Rect()
|
||||
c = comps_hash.get(ref, None)
|
||||
if c and c.included and not c.fitted:
|
||||
# Meassure the component BBox (only graphics)
|
||||
for gi in m.GraphicalItems():
|
||||
if gi.GetClass() == 'MGRAPHIC':
|
||||
l_gi = gi.GetLayer()
|
||||
if l_gi == ffab:
|
||||
frect.Union(gi.GetBoundingBox().getWxRect())
|
||||
if l_gi == bfab:
|
||||
brect.Union(gi.GetBoundingBox().getWxRect())
|
||||
# Cross the graphics in *.Fab
|
||||
if frect.x1 is not None:
|
||||
extra_ffab_lines.append(self.cross_module(m, frect, ffab))
|
||||
else:
|
||||
extra_ffab_lines.append(None)
|
||||
if brect.x1 is not None:
|
||||
extra_bfab_lines.append(self.cross_module(m, brect, bfab))
|
||||
else:
|
||||
extra_bfab_lines.append(None)
|
||||
# Remmember the data used to undo it
|
||||
self.extra_ffab_lines = extra_ffab_lines
|
||||
self.extra_bfab_lines = extra_bfab_lines
|
||||
|
||||
def uncross_modules(self, board, comps_hash):
|
||||
""" Undo the crosses in *.Fab layer """
|
||||
# Undo the drawings
|
||||
for m in board.GetModules():
|
||||
ref = m.GetReference()
|
||||
c = comps_hash.get(ref, None)
|
||||
if c and c.included and not c.fitted:
|
||||
restore = self.extra_ffab_lines.pop(0)
|
||||
if restore:
|
||||
for line in restore:
|
||||
m.Remove(line)
|
||||
restore = self.extra_bfab_lines.pop(0)
|
||||
if restore:
|
||||
for line in restore:
|
||||
m.Remove(line)
|
||||
|
||||
def remove_paste_and_glue(self, board, comps_hash):
|
||||
""" Remove from solder paste layers the filtered components. """
|
||||
exclude = LSET()
|
||||
exclude.addLayer(board.GetLayerID('F.Paste'))
|
||||
exclude.addLayer(board.GetLayerID('B.Paste'))
|
||||
old_layers = []
|
||||
fadhes = board.GetLayerID('F.Adhes')
|
||||
badhes = board.GetLayerID('B.Adhes')
|
||||
old_fadhes = []
|
||||
old_badhes = []
|
||||
for m in board.GetModules():
|
||||
ref = m.GetReference()
|
||||
c = comps_hash.get(ref, None)
|
||||
if c and c.included and not c.fitted:
|
||||
# Remove all pads from *.Paste
|
||||
old_c_layers = []
|
||||
for p in m.Pads():
|
||||
pad_layers = p.GetLayerSet()
|
||||
old_c_layers.append(pad_layers.FmtHex())
|
||||
pad_layers.removeLayerSet(exclude)
|
||||
p.SetLayerSet(pad_layers)
|
||||
old_layers.append(old_c_layers)
|
||||
# Remove any graphical item in the *.Adhes layers
|
||||
for gi in m.GraphicalItems():
|
||||
l_gi = gi.GetLayer()
|
||||
if l_gi == fadhes:
|
||||
gi.SetLayer(-1)
|
||||
old_fadhes.append(gi)
|
||||
if l_gi == badhes:
|
||||
gi.SetLayer(-1)
|
||||
old_badhes.append(gi)
|
||||
# Store the data to undo the above actions
|
||||
self.old_layers = old_layers
|
||||
self.old_fadhes = old_fadhes
|
||||
self.old_badhes = old_badhes
|
||||
self.fadhes = fadhes
|
||||
self.badhes = badhes
|
||||
return exclude
|
||||
|
||||
def restore_paste_and_glue(self, board, comps_hash):
|
||||
for m in board.GetModules():
|
||||
ref = m.GetReference()
|
||||
c = comps_hash.get(ref, None)
|
||||
if c and c.included and not c.fitted:
|
||||
restore = self.old_layers.pop(0)
|
||||
for p in m.Pads():
|
||||
pad_layers = p.GetLayerSet()
|
||||
res = restore.pop(0)
|
||||
pad_layers.ParseHex(res, len(res))
|
||||
p.SetLayerSet(pad_layers)
|
||||
for gi in self.old_fadhes:
|
||||
gi.SetLayer(self.fadhes)
|
||||
for gi in self.old_badhes:
|
||||
gi.SetLayer(self.badhes)
|
||||
|
||||
def run(self, output_dir, board):
|
||||
""" Makes the list of components available """
|
||||
|
|
@ -109,6 +240,7 @@ class VariantOptions(BaseOptions):
|
|||
# Get the components list from the schematic
|
||||
comps = GS.sch.get_components()
|
||||
# Apply the filter
|
||||
reset_filters(comps)
|
||||
apply_fitted_filter(comps, self.dnf_filter)
|
||||
# Apply the variant
|
||||
if self.variant:
|
||||
|
|
|
|||
|
|
@ -17,7 +17,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_in_bom_filter, apply_fitted_filter, apply_fixed_filter
|
||||
from .fil_base import BaseFilter, apply_exclude_filter, apply_fitted_filter, apply_fixed_filter, reset_filters
|
||||
from . import log
|
||||
|
||||
logger = log.get_logger(__name__)
|
||||
|
|
@ -369,7 +369,8 @@ class BoMOptions(BaseOptions):
|
|||
comps = GS.sch.get_components()
|
||||
get_board_comps_data(comps)
|
||||
# Apply all the filters
|
||||
apply_in_bom_filter(comps, self.exclude_filter)
|
||||
reset_filters(comps)
|
||||
apply_exclude_filter(comps, self.exclude_filter)
|
||||
apply_fitted_filter(comps, self.dnf_filter)
|
||||
apply_fixed_filter(comps, self.dnc_filter)
|
||||
# Apply the variant
|
||||
|
|
|
|||
|
|
@ -5,12 +5,11 @@
|
|||
# Project: KiBot (formerly KiPlot)
|
||||
import os
|
||||
from tempfile import NamedTemporaryFile
|
||||
from pcbnew import EDGE_MODULE, wxPoint
|
||||
from .pre_base import BasePreFlight
|
||||
from .error import (KiPlotConfigurationError)
|
||||
from .gs import (GS)
|
||||
from .kiplot import check_script, exec_with_retry
|
||||
from .misc import (CMD_PCBNEW_PRINT_LAYERS, URL_PCBNEW_PRINT_LAYERS, PDF_PCB_PRINT, Rect, UI_VIRTUAL)
|
||||
from .misc import (CMD_PCBNEW_PRINT_LAYERS, URL_PCBNEW_PRINT_LAYERS, PDF_PCB_PRINT)
|
||||
from .out_base import VariantOptions
|
||||
from .macros import macros, document, output_class # noqa: F401
|
||||
from .layer import Layer
|
||||
|
|
@ -28,75 +27,17 @@ class PDF_Pcb_PrintOptions(VariantOptions):
|
|||
""" {output} """
|
||||
super().__init__()
|
||||
|
||||
@staticmethod
|
||||
def cross_module(m, rect, layer):
|
||||
seg1 = EDGE_MODULE(m)
|
||||
seg1.SetWidth(120000)
|
||||
seg1.SetStart(wxPoint(rect.x1, rect.y1))
|
||||
seg1.SetEnd(wxPoint(rect.x2, rect.y2))
|
||||
seg1.SetLayer(layer)
|
||||
seg1.SetLocalCoord() # Update the local coordinates
|
||||
m.Add(seg1)
|
||||
seg2 = EDGE_MODULE(m)
|
||||
seg2.SetWidth(120000)
|
||||
seg2.SetStart(wxPoint(rect.x1, rect.y2))
|
||||
seg2.SetEnd(wxPoint(rect.x2, rect.y1))
|
||||
seg2.SetLayer(layer)
|
||||
seg2.SetLocalCoord() # Update the local coordinates
|
||||
m.Add(seg2)
|
||||
return [seg1, seg2]
|
||||
|
||||
def filter_components(self, board):
|
||||
if not self._comps:
|
||||
return GS.pcb_file
|
||||
comps_hash = self.get_refs_hash()
|
||||
# Cross the affected components
|
||||
ffab = board.GetLayerID('F.Fab')
|
||||
bfab = board.GetLayerID('B.Fab')
|
||||
extra_ffab_lines = []
|
||||
extra_bfab_lines = []
|
||||
for m in board.GetModules():
|
||||
ref = m.GetReference()
|
||||
# Rectangle containing the drawings, no text
|
||||
frect = Rect()
|
||||
brect = Rect()
|
||||
c = comps_hash.get(ref, None)
|
||||
if (c and not c.fitted) and m.GetAttributes() != UI_VIRTUAL:
|
||||
# Meassure the component BBox (only graphics)
|
||||
for gi in m.GraphicalItems():
|
||||
if gi.GetClass() == 'MGRAPHIC':
|
||||
l_gi = gi.GetLayer()
|
||||
if l_gi == ffab:
|
||||
frect.Union(gi.GetBoundingBox().getWxRect())
|
||||
if l_gi == bfab:
|
||||
brect.Union(gi.GetBoundingBox().getWxRect())
|
||||
# Cross the graphics in *.Fab
|
||||
if frect.x1 is not None:
|
||||
extra_ffab_lines.append(self.cross_module(m, frect, ffab))
|
||||
else:
|
||||
extra_ffab_lines.append(None)
|
||||
if brect.x1 is not None:
|
||||
extra_bfab_lines.append(self.cross_module(m, brect, bfab))
|
||||
else:
|
||||
extra_bfab_lines.append(None)
|
||||
self.cross_modules(board, comps_hash)
|
||||
# Save the PCB to a temporal file
|
||||
with NamedTemporaryFile(mode='w', suffix='.kicad_pcb', delete=False) as f:
|
||||
fname = f.name
|
||||
logger.debug('Storing filtered PCB to `{}`'.format(fname))
|
||||
GS.board.Save(fname)
|
||||
# Undo the drawings
|
||||
for m in GS.board.GetModules():
|
||||
ref = m.GetReference()
|
||||
c = comps_hash.get(ref, None)
|
||||
if (c and not c.fitted) and m.GetAttributes() != UI_VIRTUAL:
|
||||
restore = extra_ffab_lines.pop(0)
|
||||
if restore:
|
||||
for line in restore:
|
||||
m.Remove(line)
|
||||
restore = extra_bfab_lines.pop(0)
|
||||
if restore:
|
||||
for line in restore:
|
||||
m.Remove(line)
|
||||
self.uncross_modules(board, comps_hash)
|
||||
return fname
|
||||
|
||||
def run(self, output_dir, board, layers):
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@ class PositionOptions(VariantOptions):
|
|||
# Apply any filter or variant data
|
||||
if comps_hash:
|
||||
c = comps_hash.get(ref, None)
|
||||
if c and not c.fitted:
|
||||
if c and (not c.fitted or not c.included):
|
||||
continue
|
||||
# If passed check the position options
|
||||
if (self.only_smd and m.GetAttributes() == UI_SMD) or \
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ class STEPOptions(VariantOptions):
|
|||
for m in GS.board.GetModules():
|
||||
ref = m.GetReference()
|
||||
c = comps_hash.get(ref, None)
|
||||
if c and not c.fitted:
|
||||
if c and c.included and not c.fitted:
|
||||
models = m.Models()
|
||||
rem_m_models = []
|
||||
while not models.empty():
|
||||
|
|
@ -68,7 +68,7 @@ class STEPOptions(VariantOptions):
|
|||
for m in GS.board.GetModules():
|
||||
ref = m.GetReference()
|
||||
c = comps_hash.get(ref, None)
|
||||
if c and not c.fitted:
|
||||
if c and c.included and not c.fitted:
|
||||
models = m.Models()
|
||||
restore = rem_models.pop(0)
|
||||
for model in restore:
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
# Project: KiBot (formerly KiPlot)
|
||||
from .registrable import RegVariant
|
||||
from .optionable import Optionable
|
||||
from .fil_base import apply_in_bom_filter, apply_fitted_filter, apply_fixed_filter
|
||||
from .fil_base import apply_exclude_filter, apply_fitted_filter, apply_fixed_filter
|
||||
from .macros import macros, document # noqa: F401
|
||||
|
||||
|
||||
|
|
@ -33,8 +33,8 @@ 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 filter(self, comps, reset):
|
||||
def filter(self, comps):
|
||||
# Apply all the filters
|
||||
apply_in_bom_filter(comps, self.exclude_filter, reset)
|
||||
apply_fitted_filter(comps, self.dnf_filter, reset)
|
||||
apply_fixed_filter(comps, self.dnc_filter, reset)
|
||||
apply_exclude_filter(comps, self.exclude_filter)
|
||||
apply_fitted_filter(comps, self.dnf_filter)
|
||||
apply_fixed_filter(comps, self.dnc_filter)
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ Implements the IBoM variants mechanism.
|
|||
"""
|
||||
from .optionable import Optionable
|
||||
from .gs import GS
|
||||
from .misc import IFILL_MECHANICAL
|
||||
from .fil_base import BaseFilter
|
||||
from .macros import macros, document, variant_class # noqa: F401
|
||||
from . import log
|
||||
|
|
@ -47,7 +48,7 @@ class IBoM(BaseVariant): # noqa: F821
|
|||
|
||||
def config(self):
|
||||
super().config()
|
||||
self.exclude_filter = BaseFilter.solve_filter(self.exclude_filter, 'exclude_filter')
|
||||
self.exclude_filter = BaseFilter.solve_filter(self.exclude_filter, 'exclude_filter', IFILL_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)
|
||||
|
|
@ -65,15 +66,16 @@ class IBoM(BaseVariant): # noqa: F821
|
|||
return True
|
||||
return False
|
||||
|
||||
def filter(self, comps, reset=False):
|
||||
super().filter(comps, reset)
|
||||
def filter(self, comps):
|
||||
super().filter(comps)
|
||||
logger.debug("Applying IBoM style variants `{}`".format(self.name))
|
||||
# Make black/white lists case insensitive
|
||||
self.variants_whitelist = [v.lower() for v in self.variants_whitelist]
|
||||
self.variants_blacklist = [v.lower() for v in self.variants_blacklist]
|
||||
# Apply to all the components
|
||||
for c in comps:
|
||||
if not (c.fitted and c.in_bom):
|
||||
logger.debug("{} {} {}".format(c.ref, c.fitted, c.included))
|
||||
if not (c.fitted and c.included):
|
||||
# Don't check if we already discarded it
|
||||
continue
|
||||
c.fitted = not self.skip_component(c)
|
||||
|
|
|
|||
|
|
@ -91,11 +91,11 @@ class KiBoM(BaseVariant): # noqa: F821
|
|||
# No match
|
||||
return not exclusive
|
||||
|
||||
def filter(self, comps, reset=False):
|
||||
super().filter(comps, reset)
|
||||
def filter(self, comps):
|
||||
super().filter(comps)
|
||||
logger.debug("Applying KiBoM style variants `{}`".format(self.name))
|
||||
for c in comps:
|
||||
if not (c.fitted and c.in_bom):
|
||||
if not (c.fitted and c.included):
|
||||
# Don't check if we already discarded it
|
||||
continue
|
||||
value = c.value.lower()
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
(drawings 5)
|
||||
(tracks 0)
|
||||
(zones 0)
|
||||
(modules 5)
|
||||
(modules 6)
|
||||
(nets 9)
|
||||
)
|
||||
|
||||
|
|
@ -116,6 +116,27 @@
|
|||
(add_net "Net-(R2-Pad2)")
|
||||
)
|
||||
|
||||
(module Fiducial:Fiducial_0.5mm_Mask1mm (layer F.Cu) (tedit 5C18CB26) (tstamp 5F57EE4F)
|
||||
(at 139.6746 82.9564)
|
||||
(descr "Circular Fiducial, 0.5mm bare copper, 1mm soldermask opening (Level C)")
|
||||
(tags fiducial)
|
||||
(path /5F57EDDB)
|
||||
(attr virtual)
|
||||
(fp_text reference FID1 (at 0 -1.5) (layer F.SilkS)
|
||||
(effects (font (size 1 1) (thickness 0.15)))
|
||||
)
|
||||
(fp_text value Fiducial (at 0 1.5) (layer F.Fab)
|
||||
(effects (font (size 1 1) (thickness 0.15)))
|
||||
)
|
||||
(fp_circle (center 0 0) (end 0.5 0) (layer F.Fab) (width 0.1))
|
||||
(fp_circle (center 0 0) (end 0.75 0) (layer F.CrtYd) (width 0.05))
|
||||
(fp_text user %R (at 0 0) (layer F.Fab)
|
||||
(effects (font (size 0.2 0.2) (thickness 0.04)))
|
||||
)
|
||||
(pad "" smd circle (at 0 0) (size 0.5 0.5) (layers F.Cu F.Mask)
|
||||
(solder_mask_margin 0.25) (clearance 0.25))
|
||||
)
|
||||
|
||||
(module Resistor_SMD:R_0805_2012Metric (layer F.Cu) (tedit 5B36C52B) (tstamp 5F503C4A)
|
||||
(at 148.555 89.154)
|
||||
(descr "Resistor SMD 0805 (2012 Metric), square (rectangular) end terminal, IPC_7351 nominal, (Body size source: https://docs.google.com/spreadsheets/d/1BsfQQcO9C6DZCsRaXUlFlo91Tg2WpOkGARC1WS5S8t0/edit?usp=sharing), generated with kicad-footprint-generator")
|
||||
|
|
@ -142,8 +163,10 @@
|
|||
(fp_text user %R (at 0 0) (layer F.Fab)
|
||||
(effects (font (size 0.5 0.5) (thickness 0.08)))
|
||||
)
|
||||
(pad 1 smd roundrect (at -0.9375 0) (size 0.975 1.4) (layers F.Cu F.Paste F.Mask) (roundrect_rratio 0.25))
|
||||
(pad 2 smd roundrect (at 0.9375 0) (size 0.975 1.4) (layers F.Cu F.Paste F.Mask) (roundrect_rratio 0.25))
|
||||
(pad 1 smd roundrect (at -0.9375 0) (size 0.975 1.4) (layers F.Cu F.Paste F.Mask) (roundrect_rratio 0.25)
|
||||
(net 6 "Net-(R1-Pad1)"))
|
||||
(pad 2 smd roundrect (at 0.9375 0) (size 0.975 1.4) (layers F.Cu F.Paste F.Mask) (roundrect_rratio 0.25)
|
||||
(net 5 "Net-(R1-Pad2)"))
|
||||
(model ${KISYS3DMOD}/Resistor_SMD.3dshapes/R_0805_2012Metric.wrl
|
||||
(at (xyz 0 0 0))
|
||||
(scale (xyz 1 1 1))
|
||||
|
|
@ -265,7 +288,7 @@
|
|||
(descr "Capacitor SMD 0805 (2012 Metric), square (rectangular) end terminal, IPC_7351 nominal, (Body size source: https://docs.google.com/spreadsheets/d/1BsfQQcO9C6DZCsRaXUlFlo91Tg2WpOkGARC1WS5S8t0/edit?usp=sharing), generated with kicad-footprint-generator")
|
||||
(tags capacitor)
|
||||
(path /5F43BEC2)
|
||||
(attr virtual)
|
||||
(attr smd)
|
||||
(fp_text reference C1 (at 0 -1.65) (layer F.SilkS)
|
||||
(effects (font (size 1 1) (thickness 0.15)))
|
||||
)
|
||||
|
|
@ -305,9 +328,9 @@
|
|||
(gr_text "Bogus component.\nNot in schematic." (at 161.163 89.281) (layer Cmts.User)
|
||||
(effects (font (size 1.5 1.5) (thickness 0.3)))
|
||||
)
|
||||
(gr_line (start 133.35 83.82) (end 133.35 93.98) (layer Edge.Cuts) (width 0.1) (tstamp 5F496ACC))
|
||||
(gr_line (start 146.05 83.82) (end 133.35 83.82) (layer Edge.Cuts) (width 0.1))
|
||||
(gr_line (start 146.05 93.98) (end 146.05 83.82) (layer Edge.Cuts) (width 0.1))
|
||||
(gr_line (start 133.35 80.4) (end 133.35 93.98) (layer Edge.Cuts) (width 0.1) (tstamp 5F496ACC))
|
||||
(gr_line (start 146.05 80.4) (end 133.35 80.4) (layer Edge.Cuts) (width 0.1) (tstamp 5F57EEAF))
|
||||
(gr_line (start 146.05 93.98) (end 146.05 80.4) (layer Edge.Cuts) (width 0.1))
|
||||
(gr_line (start 133.35 93.98) (end 146.05 93.98) (layer Edge.Cuts) (width 0.1))
|
||||
|
||||
)
|
||||
|
|
|
|||
|
|
@ -65,4 +65,15 @@ F 4 "T1" H 2500 1700 50 0001 C CNN "Config"
|
|||
$EndComp
|
||||
Text Notes 5950 3200 0 118 ~ 0
|
||||
The test tests the following \nvariants matrix:\n production test default\nC1 X\nC2 X X\nR1 X X X\nR2 X X\n\nproduction: blacklist T2\ntest: blacklist T1\ndefault: whitelist T1,default \n blacklist T2,T3
|
||||
$Comp
|
||||
L Mechanical:Fiducial FID1
|
||||
U 1 1 5F57EDDB
|
||||
P 1750 2250
|
||||
F 0 "FID1" H 1835 2296 50 0000 L CNN
|
||||
F 1 "Fiducial" H 1835 2205 50 0000 L CNN
|
||||
F 2 "Fiducial:Fiducial_0.5mm_Mask1mm" H 1750 2250 50 0001 C CNN
|
||||
F 3 "~" H 1750 2250 50 0001 C CNN
|
||||
1 1750 2250
|
||||
1 0 0 -1
|
||||
$EndComp
|
||||
$EndSCHEMATC
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 5.8 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 5.7 KiB |
|
|
@ -104,7 +104,6 @@ def test_gerber_variant_1():
|
|||
prj = 'kibom-variant_3'
|
||||
ctx = context.TestContext('test_gerber_variant_1', prj, 'gerber_variant_1', GERBER_DIR)
|
||||
ctx.run()
|
||||
# C1 is virtual, not included for all cases
|
||||
# R3 is a component added to the PCB, included in all cases
|
||||
# variant: default directory: gerber components: R1, R2 and R3
|
||||
check_layers_exist(ctx, 'gerber', prj, ALL_LAYERS, '')
|
||||
|
|
@ -114,5 +113,5 @@ def test_gerber_variant_1():
|
|||
check_components(ctx, 'production', prj, ['F_Paste', 'F_Adhes'], '_(production)', ['C1'], ['R1', 'R2', 'R3', 'C2'])
|
||||
# variant: test directory: test components: R1, R3 and C2
|
||||
check_layers_exist(ctx, 'test', prj, ALL_LAYERS, '_(test)')
|
||||
check_components(ctx, 'test', prj, ['F_Paste', 'F_Adhes'], '_(test)', ['C1', 'R2'], ['R1', 'R3', 'C2'])
|
||||
check_components(ctx, 'test', prj, ['F_Paste', 'F_Adhes'], '_(test)', ['R2'], ['C1', 'R1', 'R3', 'C2'])
|
||||
ctx.clean_up()
|
||||
|
|
|
|||
|
|
@ -1222,9 +1222,9 @@ def test_int_bom_variant_t2s():
|
|||
ctx.clean_up()
|
||||
|
||||
|
||||
def test_int_bom_variant_t2i():
|
||||
def test_int_bom_variant_t2if():
|
||||
prj = 'kibom-variant_3'
|
||||
ctx = context.TestContextSCH('test_int_bom_variant_t2i', prj, 'int_bom_var_t2i_csv', BOM_DIR)
|
||||
ctx = context.TestContextSCH('test_int_bom_variant_t2if', prj, 'int_bom_var_t2i_csv', BOM_DIR)
|
||||
ctx.run()
|
||||
rows, header, info = ctx.load_csv(prj+'-bom.csv')
|
||||
ref_column = header.index(REF_COLUMN_NAME)
|
||||
|
|
@ -1291,11 +1291,11 @@ def test_int_bom_fil_2():
|
|||
ctx.run()
|
||||
rows, header, info = ctx.load_csv('smd.csv')
|
||||
ref_column = header.index(REF_COLUMN_NAME)
|
||||
check_kibom_test_netlist(rows, ref_column, 2, None, ['R2', 'C1-C2'])
|
||||
check_kibom_test_netlist(rows, ref_column, 3, None, ['R2', 'C2', 'FID1'])
|
||||
rows, header, info = ctx.load_csv('tht.csv')
|
||||
check_kibom_test_netlist(rows, ref_column, 2, None, ['R1', 'C1'])
|
||||
check_kibom_test_netlist(rows, ref_column, 3, None, ['R1', 'C1', 'FID1'])
|
||||
rows, header, info = ctx.load_csv('virtual.csv')
|
||||
check_kibom_test_netlist(rows, ref_column, 2, None, ['R1-R2', 'C2'])
|
||||
check_kibom_test_netlist(rows, ref_column, 2, None, ['R1-R2', 'C1-C2'])
|
||||
ctx.search_err(r".?R3.? component in board, but not in schematic")
|
||||
ctx.clean_up()
|
||||
|
||||
|
|
|
|||
|
|
@ -108,6 +108,7 @@ def test_pcbdraw_variant_1():
|
|||
# We use 30% because different versions of the tools are generating large differences
|
||||
# in the borders. With 30% these differences are removed and we still detect is a
|
||||
# components was removed.
|
||||
# Expected: R1 and R2 populated
|
||||
ctx.compare_image(fname, fuzz='30%')
|
||||
ctx.clean_up()
|
||||
|
||||
|
|
@ -119,6 +120,7 @@ def test_pcbdraw_variant_2():
|
|||
# Check all outputs are there
|
||||
fname = prj+'-top-C1.png'
|
||||
ctx.expect_out_file(fname)
|
||||
# Expected: R1 and R2 populated + C1 manually added
|
||||
ctx.compare_image(fname, fuzz='30%')
|
||||
ctx.clean_up()
|
||||
|
||||
|
|
|
|||
|
|
@ -167,5 +167,5 @@ def test_position_variant_t2i():
|
|||
rows, header, info = ctx.load_csv(prj+'-both_pos_(production).csv')
|
||||
check_comps(rows, ['C2', 'R1', 'R2', 'R3'])
|
||||
rows, header, info = ctx.load_csv(prj+'-both_pos_(test).csv')
|
||||
check_comps(rows, ['C2', 'R1', 'R3'])
|
||||
check_comps(rows, ['C1', 'C2', 'R1', 'R3'])
|
||||
ctx.clean_up()
|
||||
|
|
|
|||
|
|
@ -337,7 +337,7 @@ class TestContext(object):
|
|||
image,
|
||||
reference,
|
||||
# Avoid the part where KiCad version is printed
|
||||
'-crop', '100%x88%+0+0', '+repage',
|
||||
'-crop', '100%x87%+0+0', '+repage',
|
||||
'-colorspace', 'RGB',
|
||||
self.get_out_path(diff)]
|
||||
logging.debug('Comparing images with: '+str(cmd))
|
||||
|
|
|
|||
Loading…
Reference in New Issue