Added support for KiCost's subparts
This commit is contained in:
parent
91dc9c5488
commit
40bd7c24f2
|
|
@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
### Added
|
||||
- `erc_warnings` pre-flight option to consider ERC warnings as errors.
|
||||
- Pattern expansion in the `dir` option for outputs (#58)
|
||||
- New filter type `suparts`. Adds support for KiCost's subparts feature.
|
||||
|
||||
### Changed
|
||||
- Errors and warnings from KiAuto now are printed as errors and warnings.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,259 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2021 Salvador E. Tropea
|
||||
# Copyright (c) 2021 Instituto Nacional de Tecnología Industrial
|
||||
# License: GPL-3.0
|
||||
# Project: KiBot (formerly KiPlot)
|
||||
"""
|
||||
Implements the KiCost subparts mechanism.
|
||||
The 'manf#' field can contain more than one value separated by ;
|
||||
The result is REF#subpart
|
||||
"""
|
||||
import re
|
||||
from copy import deepcopy
|
||||
from .gs import GS
|
||||
from .optionable import Optionable
|
||||
from .misc import W_NUMSUBPARTS, W_PARTMULT
|
||||
from .macros import macros, document, filter_class # noqa: F401
|
||||
from . import log
|
||||
|
||||
|
||||
logger = log.get_logger(__name__)
|
||||
DISTRIBUTORS = ['digikey#', 'farnell#', 'mouser#', 'newark#', 'rs#', 'arrow#', 'tme#', 'lcsc#']
|
||||
|
||||
|
||||
class DistributorsList(Optionable):
|
||||
_default = DISTRIBUTORS
|
||||
|
||||
|
||||
@filter_class
|
||||
class Subparts(BaseFilter): # noqa: F821
|
||||
""" Subparts
|
||||
This filter implements the KiCost subparts mechanism """
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._is_transform = True
|
||||
with document:
|
||||
self.check_multiplier = Optionable
|
||||
""" [list(string)] List of fields to include for multiplier computation.
|
||||
If empty all fields in `split_fields` and `manf_pn_field` are used """
|
||||
self.manf_field = 'manf'
|
||||
""" Field for the manufacturer name """
|
||||
self.manf_pn_field = 'manf#'
|
||||
""" Field for the manufacturer part number """
|
||||
self.modify_value = True
|
||||
""" Add '- p N/M' to the value """
|
||||
self.modify_first_value = True
|
||||
""" Modify even the value for the first component in the list (KiCost behavior) """
|
||||
self.multiplier = True
|
||||
""" Enables the subpart multiplier mechanism """
|
||||
self.mult_separators = ':'
|
||||
""" Separators used for the multiplier. Each character in this string is a valid separator """
|
||||
self.ref_sep = '#'
|
||||
""" Separator used in the reference (i.e. R10#1) """
|
||||
self.separators = ';,'
|
||||
""" Separators used between subparts. Each character in this string is a valid separator """
|
||||
self.split_fields = DistributorsList
|
||||
""" [list(string)] List of fields to split, usually the distributors part numbers """
|
||||
self.split_fields_expand = False
|
||||
""" When `true` the fields in `split_fields` are added to the internal names """
|
||||
self.use_ref_sep_for_first = True
|
||||
""" Force the reference separator use even for the first component in the list (KiCost behavior) """
|
||||
self.value_alt_field = 'value_subparts'
|
||||
""" Field containing replacements for the `Value` field. So we get real values for splitted parts """
|
||||
|
||||
def config(self, parent):
|
||||
super().config(parent)
|
||||
if not self.separators:
|
||||
self.separators = ';,'
|
||||
if not self.mult_separators:
|
||||
self.mult_separators = ':'
|
||||
if not self.ref_sep:
|
||||
self.ref_sep = '#'
|
||||
if isinstance(self.split_fields, type):
|
||||
self.split_fields = DISTRIBUTORS
|
||||
else:
|
||||
if self.split_fields_expand:
|
||||
self.split_fields.extend(DISTRIBUTORS)
|
||||
# (?<!\\) is used to skip \;
|
||||
self._part_sep = re.compile(r'(?<!\\)\s*['+self.separators+r']\s*')
|
||||
self._qty_sep = re.compile(r'(?<!\\)\s*['+self.mult_separators+r']\s*')
|
||||
# TODO: The spaces here seems a bug
|
||||
self._esc = re.compile(r'\\\s*(['+self.separators+self.mult_separators+r'])\s*')
|
||||
self._num_format = re.compile(r"^\s*[\-\+]?\s*[0-9]*\s*[\.\/]*\s*?[0-9]*\s*$")
|
||||
self._remove_sep = re.compile(r'[\.\/]')
|
||||
# The list of all fields that controls the process
|
||||
self._fields = self.split_fields
|
||||
if self.manf_pn_field:
|
||||
self._fields.append(self.manf_pn_field)
|
||||
# List of fields that needs qty computation
|
||||
if isinstance(self.check_multiplier, type) or self.check_multiplier is None:
|
||||
self.check_multiplier = self._fields
|
||||
self.check_multiplier = set(self.check_multiplier)
|
||||
|
||||
def subpart_list(self, value):
|
||||
""" Split a field containing self.separators into a list """
|
||||
return self._part_sep.split(value.strip())
|
||||
|
||||
def manf_code_qtypart(self, value):
|
||||
# Remove any escape backslashes preceding PART_SEPRTR.
|
||||
value = self._esc.sub(r'\1', value)
|
||||
strings = self._qty_sep.split(value)
|
||||
if len(strings) == 2:
|
||||
# Search for numbers, matching with simple, frac and decimal ones.
|
||||
string0_test = re.match(self._num_format, strings[0])
|
||||
string1_test = re.match(self._num_format, strings[1])
|
||||
if string0_test and not(string1_test):
|
||||
qty = strings[0].strip()
|
||||
part = strings[1].strip()
|
||||
elif not(string0_test) and string1_test:
|
||||
qty = strings[1].strip()
|
||||
part = strings[0].strip()
|
||||
elif string0_test and string1_test:
|
||||
# May be just a numeric manufacturer/distributor part number,
|
||||
# in this case, the quantity is the shortest string not
|
||||
# considering "." and "/" marks.
|
||||
if len(self._remove_sep.sub('', strings[0])) < len(self._remove_sep.sub('', strings[1])):
|
||||
qty = strings[0].strip()
|
||||
part = strings[1].strip()
|
||||
else:
|
||||
qty = strings[1].strip()
|
||||
part = strings[0].strip()
|
||||
else:
|
||||
qty = '1'
|
||||
part = strings[0].strip() + strings[1].strip()
|
||||
if qty == '':
|
||||
qty = '1'
|
||||
else:
|
||||
qty = '1'
|
||||
part = ''.join(strings)
|
||||
if GS.debug_level > 2 and qty != '1':
|
||||
logger.debug('Subparts filter: `{}` -> part `{}` qty `{}`'.format(value, part, qty))
|
||||
return qty, part
|
||||
|
||||
@staticmethod
|
||||
def qty_to_float(qty):
|
||||
try:
|
||||
if '/' in qty:
|
||||
vals = qty.split('/')
|
||||
return float(vals[0])/float(vals[1])
|
||||
return float(qty)
|
||||
except ValueError:
|
||||
logger.error('Internal error qty_to_float("{}"), please report'.format(qty))
|
||||
|
||||
def do_split(self, comp, max_num_subparts, splitted_fields):
|
||||
""" Split `comp` according to the detected subparts """
|
||||
# Split it
|
||||
multi_part = max_num_subparts > 1
|
||||
if multi_part and GS.debug_level > 1:
|
||||
logger.debug("Splitting {} in {} subparts".format(comp.ref, max_num_subparts))
|
||||
splitted = []
|
||||
# Compute the total for the modified value
|
||||
total_parts = max_num_subparts if self.modify_first_value else max_num_subparts-1
|
||||
# Check if we have replacements for the `Value` field
|
||||
alt_values = []
|
||||
alt_v = comp.get_field_value(self.value_alt_field)
|
||||
if alt_v:
|
||||
alt_values = self.subpart_list(alt_v)
|
||||
alt_values_len = len(alt_values)
|
||||
for i in range(max_num_subparts):
|
||||
new_comp = deepcopy(comp)
|
||||
if multi_part:
|
||||
# Adjust the reference name
|
||||
if self.use_ref_sep_for_first:
|
||||
new_comp.ref = new_comp.ref+self.ref_sep+str(i+1)
|
||||
elif i > 0:
|
||||
# I like it better. The first is usually the real component, the rest are accesories.
|
||||
new_comp.ref = new_comp.ref+self.ref_sep+str(i)
|
||||
# Adjust the suffix to be "sort friendly"
|
||||
# Currently useless, but could help in the future
|
||||
new_comp.ref_suffix = str(int(new_comp.ref_suffix)*100+i)
|
||||
# Adjust the value field
|
||||
if i < alt_values_len:
|
||||
# We have a replacement
|
||||
new_comp.set_field('value', alt_values[i])
|
||||
elif self.modify_value:
|
||||
idx = i
|
||||
if self.modify_first_value:
|
||||
idx += 1
|
||||
if self.modify_first_value or i:
|
||||
new_comp.set_field('value', new_comp.value+' - p{}/{}'.format(idx, total_parts))
|
||||
# Adjust the related fields
|
||||
prev_qty = None
|
||||
prev_field = None
|
||||
max_qty = 0
|
||||
if not self.check_multiplier.intersection(splitted_fields):
|
||||
# No field to check for qty here, default to 1
|
||||
max_qty = 1
|
||||
for field, values in splitted_fields.items():
|
||||
check_multiplier = field in self.check_multiplier
|
||||
value = ''
|
||||
qty = '1'
|
||||
if len(values) > i:
|
||||
value = values[i]
|
||||
if check_multiplier:
|
||||
# Analyze the multiplier
|
||||
qty, value = self.manf_code_qtypart(value)
|
||||
if prev_qty is not None and qty != prev_qty and field != self.manf_field:
|
||||
logger.warning(W_PARTMULT+'Different part multiplier on {r}, '
|
||||
'field {c} has {cn} and {lc} has {lcn}.'
|
||||
.format(r=new_comp.ref, c=prev_field, cn=prev_qty, lc=field, lcn=qty))
|
||||
prev_qty = qty
|
||||
prev_field = field
|
||||
new_comp.set_field(field, value)
|
||||
if check_multiplier:
|
||||
new_comp.set_field(field+'_qty', qty)
|
||||
max_qty = max(max_qty, self.qty_to_float(qty))
|
||||
new_comp.qty = max_qty
|
||||
splitted.append(new_comp)
|
||||
if not multi_part and int(max_qty) == 1:
|
||||
# No real split and no multiplier
|
||||
return
|
||||
if GS.debug_level > 2:
|
||||
logger.debug('Old component: '+comp.ref+' '+str([str(f) for f in comp.fields]))
|
||||
logger.debug('Fields to split: '+str(splitted_fields))
|
||||
logger.debug('New components:')
|
||||
for c in splitted:
|
||||
logger.debug(' '+c.ref+' '+str([str(f) for f in c.fields]))
|
||||
return splitted
|
||||
|
||||
def filter(self, comp):
|
||||
""" Look for fields containing `part1; mult:part2; etc.` """
|
||||
# Analyze how to split this component
|
||||
max_num_subparts = 0
|
||||
splitted_fields = {}
|
||||
field_max = None
|
||||
for field in self._fields:
|
||||
value = comp.get_field_value(field)
|
||||
if not value:
|
||||
# Skip it if not used
|
||||
continue
|
||||
subparts = self.subpart_list(value)
|
||||
splitted_fields[field] = subparts
|
||||
num_subparts = len(subparts)
|
||||
if num_subparts > max_num_subparts:
|
||||
field_max = field
|
||||
max_num_subparts = num_subparts
|
||||
# Print a warning if this field has a different ammount
|
||||
if num_subparts != max_num_subparts:
|
||||
logger.warning(W_NUMSUBPARTS+'Different subparts ammount on {r}, field {c} has {cn} and {lc} has {lcn}.'
|
||||
.format(r=comp.ref, c=field_max, cn=max_num_subparts, lc=field, lcn=num_subparts))
|
||||
if len(splitted_fields) == 0:
|
||||
# Nothing to split
|
||||
return
|
||||
# Split the manufacturer name
|
||||
# It can contain just one name, so we exclude it from the above warning
|
||||
if self.manf_field:
|
||||
value = comp.get_field_value(self.manf_field)
|
||||
if value:
|
||||
manfs = self.subpart_list(value)
|
||||
if len(manfs) == 1:
|
||||
# If just one `manf` apply it to all
|
||||
manfs = [manfs[0]]*max_num_subparts
|
||||
else:
|
||||
# Expand the "repeat" indicator
|
||||
for i in range(len(manfs)-1):
|
||||
if manfs[i+1] == '~':
|
||||
manfs[i+1] = manfs[i]
|
||||
splitted_fields[self.manf_field] = manfs
|
||||
# Now do the work
|
||||
return self.do_split(comp, max_num_subparts, splitted_fields)
|
||||
|
|
@ -746,6 +746,9 @@ class SchematicField(object):
|
|||
f.write(' "{}"'.format(self.name))
|
||||
f.write('\n')
|
||||
|
||||
def __str__(self):
|
||||
return self.name+'='+self.value
|
||||
|
||||
|
||||
class SchematicAltRef():
|
||||
def __init__(self):
|
||||
|
|
@ -842,8 +845,9 @@ class SchematicComponent(object):
|
|||
|
||||
def set_field(self, field, value):
|
||||
""" Change the value for an existing field """
|
||||
if field in self.dfields:
|
||||
target = self.dfields[field]
|
||||
field_lc = field.lower()
|
||||
if field_lc in self.dfields:
|
||||
target = self.dfields[field_lc]
|
||||
target.value = value
|
||||
# Adjust special fields
|
||||
if target.number < 4:
|
||||
|
|
|
|||
|
|
@ -176,6 +176,8 @@ W_MUSTBEINT = '(W055) '
|
|||
W_NOOUTPUTS = '(W056) '
|
||||
W_NOTASCII = '(W057) '
|
||||
W_KIAUTO = '(W058) '
|
||||
W_NUMSUBPARTS = '(W059) '
|
||||
W_PARTMULT = '(W060) '
|
||||
|
||||
|
||||
class Rect(object):
|
||||
|
|
|
|||
|
|
@ -474,7 +474,7 @@ class BoMOptions(BaseOptions):
|
|||
apply_fitted_filter(comps, self.dnf_filter)
|
||||
apply_fixed_filter(comps, self.dnc_filter)
|
||||
# Apply the variant
|
||||
self.variant.filter(comps)
|
||||
comps = self.variant.filter(comps)
|
||||
# We add the main project to the aggregate list so do_bom sees a complete list
|
||||
base_sch = Aggregate()
|
||||
base_sch.file = GS.sch_file
|
||||
|
|
|
|||
|
|
@ -0,0 +1,164 @@
|
|||
EESchema Schematic File Version 4
|
||||
EELAYER 30 0
|
||||
EELAYER END
|
||||
$Descr A4 11693 8268
|
||||
encoding utf-8
|
||||
Sheet 1 1
|
||||
Title "KiCost subparts test"
|
||||
Date "2021-03-16"
|
||||
Rev "r1"
|
||||
Comp "KiBot"
|
||||
Comment1 ""
|
||||
Comment2 ""
|
||||
Comment3 ""
|
||||
Comment4 ""
|
||||
$EndDescr
|
||||
$Comp
|
||||
L Mechanical:Heatsink HS1
|
||||
U 1 1 6063AB02
|
||||
P 3750 1325
|
||||
F 0 "HS1" H 3891 1491 50 0000 L CNN
|
||||
F 1 "322400B00000G" H 3891 1400 50 0000 L CNN
|
||||
F 2 "" H 3762 1325 50 0001 C CNN
|
||||
F 3 "~" H 3762 1325 50 0001 C CNN
|
||||
F 4 "Aavid" H 3750 1325 50 0001 C CNN "manf"
|
||||
F 5 "322400B00000G" H 3891 1309 50 0000 L CNN "manf#"
|
||||
F 6 "HS100-ND" H 3750 1325 50 0001 C CNN "digikey#"
|
||||
F 7 "HEAT SINK TO-18 1W BLK" H 3750 1325 50 0001 C CNN "Description"
|
||||
1 3750 1325
|
||||
1 0 0 -1
|
||||
$EndComp
|
||||
$Comp
|
||||
L Device:Q_NPN_BCE Q2
|
||||
U 1 1 6063D25D
|
||||
P 3750 1550
|
||||
F 0 "Q2" V 3675 1350 50 0000 C CNN
|
||||
F 1 "2N2222A" V 3575 1250 50 0000 C CNN
|
||||
F 2 "Package_TO_SOT_THT:TO-18-3" H 3950 1650 50 0001 C CNN
|
||||
F 3 "~" H 3750 1550 50 0001 C CNN
|
||||
F 4 "Central Semiconductor Corp" V 3750 1550 50 0001 C CNN "manf"
|
||||
F 5 "2N2222A PBFREE" V 3500 1100 50 0000 C CNN "manf#"
|
||||
F 6 "TRANS NPN 40V 0.8A TO-18" V 3750 1550 50 0001 C CNN "Description"
|
||||
F 7 "1514-2N2222APBFREE-ND" V 3750 1550 50 0001 C CNN "digikey#"
|
||||
1 3750 1550
|
||||
0 -1 -1 0
|
||||
$EndComp
|
||||
Text Notes 3600 1100 0 50 ~ 0
|
||||
A Transistor and its heatsink\nOnly one goes to the PCB
|
||||
Text Notes 575 825 0 200 ~ 40
|
||||
KiCost subparts
|
||||
$Comp
|
||||
L Device:Q_NPN_BCE Q1
|
||||
U 1 1 60640F08
|
||||
P 1075 1300
|
||||
F 0 "Q1" V 1000 1100 50 0000 C CNN
|
||||
F 1 "2N2222A" V 900 1000 50 0000 C CNN
|
||||
F 2 "Package_TO_SOT_THT:TO-18-3" H 1275 1400 50 0001 C CNN
|
||||
F 3 "~" H 1075 1300 50 0001 C CNN
|
||||
F 4 "Central Semiconductor Corp; Aavid" V 1075 1300 50 0001 C CNN "manf"
|
||||
F 5 "2N2222A PBFREE; 322400B00000G" V 825 500 50 0000 C CNN "manf#"
|
||||
F 6 "TRANS NPN 40V 0.8A TO-18 + Heatsink" V 1075 1300 50 0001 C CNN "Description"
|
||||
F 7 "1514-2N2222APBFREE-ND; HS100-ND" V 1075 1300 50 0001 C CNN "digikey#"
|
||||
F 8 "2N2222A;322400B00000G" V 1150 2600 50 0001 C CNN "Value_Subparts"
|
||||
1 1075 1300
|
||||
0 -1 -1 0
|
||||
$EndComp
|
||||
Text Notes 2650 1550 0 50 ~ 0
|
||||
Equivalent to ->
|
||||
$Comp
|
||||
L Connector_Generic:Conn_01x06 J1
|
||||
U 1 1 60649B1F
|
||||
P 1150 2600
|
||||
F 0 "J1" V 1114 2212 50 0000 R CNN
|
||||
F 1 "Conn_01x06" V 1023 2212 50 0000 R CNN
|
||||
F 2 "Connector_PinHeader_2.54mm:PinHeader_1x06_P2.54mm_Vertical" H 1150 2600 50 0001 C CNN
|
||||
F 3 "~" H 1150 2600 50 0001 C CNN
|
||||
F 4 "Molex; Molex; Molex; LEM; LEM" V 1150 2600 50 0001 C CNN "manf"
|
||||
F 5 "0022232061;0022012067; 6: 08-50-0114; LA 55-P; lv 25-P" V 1150 2600 50 0001 C CNN "manf#"
|
||||
F 6 "WM4204-ND; WM2015-ND; WM1114-ND; 398-1010-ND; 398-1019-ND" V 1150 2600 50 0001 C CNN "digikey#"
|
||||
F 7 "CONN HEADER VERT 6POS 2.54MM; CONN HOUSING 6POS .100 W/RAMP; CONN 22-30AWG CRIMP TIN; SENSOR CURRENT HALL 50A AC/DC; TRANSDUCR VOLTAG CLOSE LOOP 10MA" V 1150 2600 50 0001 C CNN "Description"
|
||||
F 8 "Male_01x06;Female_01x06;Crimp_Pin;LA 55-P;LV 25-P" V 1150 2600 50 0001 C CNN "Value_Subparts"
|
||||
1 1150 2600
|
||||
0 -1 -1 0
|
||||
$EndComp
|
||||
Text Notes 900 2400 0 50 ~ 0
|
||||
A male header\nBut we want to include the female, the pins and the two\nsensors wired to the female.
|
||||
$Comp
|
||||
L Device:R R1
|
||||
U 1 1 6064C12F
|
||||
P 3750 2650
|
||||
F 0 "R1" H 3820 2696 50 0000 L CNN
|
||||
F 1 "0" H 3820 2605 50 0000 L CNN
|
||||
F 2 "" V 3680 2650 50 0001 C CNN
|
||||
F 3 "~" H 3750 2650 50 0001 C CNN
|
||||
F 4 "Bourns" H 3750 2650 50 0001 C CNN "manf"
|
||||
F 5 "CR0603-J/-000ELF:5" H 3750 2650 50 0001 C CNN "manf#"
|
||||
F 6 "CR0603-J/-000ELFCT-ND: 5" H 3750 2650 50 0001 C CNN "digikey#"
|
||||
F 7 "RES SMD 0 OHM JUMPER 1/8W 0603" H 3750 2650 50 0001 C CNN "Description"
|
||||
1 3750 2650
|
||||
1 0 0 -1
|
||||
$EndComp
|
||||
Text Notes 3675 2475 0 50 ~ 0
|
||||
A simple resistor, \nbut multiplied by 5\nWe add: R4 no mult \n and R5 no manf#\nTotal: 7
|
||||
$Comp
|
||||
L Device:R R2
|
||||
U 1 1 6064CA86
|
||||
P 3750 3400
|
||||
F 0 "R2" H 3820 3446 50 0000 L CNN
|
||||
F 1 "100" H 3820 3355 50 0000 L CNN
|
||||
F 2 "" V 3680 3400 50 0001 C CNN
|
||||
F 3 "~" H 3750 3400 50 0001 C CNN
|
||||
F 4 "Bourns" H 3750 3400 50 0001 C CNN "manf"
|
||||
F 5 "CR0603-JW-101ELF : 4.5" H 3750 3400 50 0001 C CNN "manf#"
|
||||
F 6 "CR0603-JW-101ELFCT-ND :4.5" H 3750 3400 50 0001 C CNN "digikey#"
|
||||
F 7 "RES SMD 100 OHM 5% 1/10W 0603" H 3750 3400 50 0001 C CNN "Description"
|
||||
1 3750 3400
|
||||
1 0 0 -1
|
||||
$EndComp
|
||||
Text Notes 3675 3225 0 50 ~ 0
|
||||
A simple resistor, \nbut multiplied by 4.5
|
||||
$Comp
|
||||
L Device:R R3
|
||||
U 1 1 6064D8D1
|
||||
P 3750 4150
|
||||
F 0 "R3" H 3820 4196 50 0000 L CNN
|
||||
F 1 "100k" H 3820 4105 50 0000 L CNN
|
||||
F 2 "" V 3680 4150 50 0001 C CNN
|
||||
F 3 "~" H 3750 4150 50 0001 C CNN
|
||||
F 4 "Bourns" H 3750 4150 50 0001 C CNN "manf"
|
||||
F 5 "4/5 : CR0603-JW-104ELF " H 3750 4150 50 0001 C CNN "manf#"
|
||||
F 6 " 4/5 : CR0603-JW-104ELFCT-ND" H 3750 4150 50 0001 C CNN "digikey#"
|
||||
F 7 "RES SMD 100K OHM 5% 1/10W 0603" H 3750 4150 50 0001 C CNN "Description"
|
||||
1 3750 4150
|
||||
1 0 0 -1
|
||||
$EndComp
|
||||
Text Notes 3675 3975 0 50 ~ 0
|
||||
A simple resistor, \nbut multiplied by 4/5
|
||||
$Comp
|
||||
L Device:R R4
|
||||
U 1 1 6064EF62
|
||||
P 4250 2650
|
||||
F 0 "R4" H 4320 2696 50 0000 L CNN
|
||||
F 1 "0" H 4320 2605 50 0000 L CNN
|
||||
F 2 "" V 4180 2650 50 0001 C CNN
|
||||
F 3 "~" H 4250 2650 50 0001 C CNN
|
||||
F 4 "Bourns" H 4250 2650 50 0001 C CNN "manf"
|
||||
F 5 "CR0603-J/-000ELF" H 4250 2650 50 0001 C CNN "manf#"
|
||||
F 6 "CR0603-J/-000ELFCT-ND" H 4250 2650 50 0001 C CNN "digikey#"
|
||||
F 7 "RES SMD 0 OHM JUMPER 1/8W 0603" H 4250 2650 50 0001 C CNN "Description"
|
||||
1 4250 2650
|
||||
1 0 0 -1
|
||||
$EndComp
|
||||
$Comp
|
||||
L Device:R R5
|
||||
U 1 1 6064F812
|
||||
P 4500 2650
|
||||
F 0 "R5" H 4570 2696 50 0000 L CNN
|
||||
F 1 "0" H 4570 2605 50 0000 L CNN
|
||||
F 2 "" V 4430 2650 50 0001 C CNN
|
||||
F 3 "~" H 4500 2650 50 0001 C CNN
|
||||
F 4 "RES SMD 0 OHM JUMPER 1/8W 0603" H 4500 2650 50 0001 C CNN "Description"
|
||||
1 4500 2650
|
||||
1 0 0 -1
|
||||
$EndComp
|
||||
$EndSCHEMATC
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
Row,References,Value,Description,manf,manf#,digikey#,Quantity Per PCB,Build Quantity
|
||||
1,HS1 Q1#2,322400B00000G,HEAT SINK TO-18 1W BLK,Aavid,322400B00000G,HS100-ND,2,200
|
||||
2,J1#3,Crimp_Pin,CONN 22-30AWG CRIMP TIN,Molex,08-50-0114,WM1114-ND,6,600
|
||||
3,J1#2,Female_01x06,CONN HOUSING 6POS .100 W/RAMP,Molex,0022012067,WM2015-ND,1,100
|
||||
4,J1#4,LA 55-P,SENSOR CURRENT HALL 50A AC/DC,LEM,LA 55-P,398-1010-ND,1,100
|
||||
5,J1#5,LV 25-P,TRANSDUCR VOLTAG CLOSE LOOP 10MA,LEM,lv 25-P,398-1019-ND,1,100
|
||||
6,J1#1,Male_01x06,CONN HEADER VERT 6POS 2.54MM,Molex,0022232061,WM4204-ND,1,100
|
||||
7,Q2 Q1#1,2N2222A,TRANS NPN 40V 0.8A TO-18 TRANS NPN 40V 0.8A TO-18 + Heatsink,Central Semiconductor Corp,2N2222A PBFREE,1514-2N2222APBFREE-ND,2,200
|
||||
8,R1 R4 R5,0,RES SMD 0 OHM JUMPER 1/8W 0603,Bourns,CR0603-J/-000ELF,CR0603-J/-000ELFCT-ND,7,700
|
||||
9,R2,100,RES SMD 100 OHM 5% 1/10W 0603,Bourns,CR0603-JW-101ELF,CR0603-JW-101ELFCT-ND,5,450
|
||||
10,R3,100k,RES SMD 100K OHM 5% 1/10W 0603,Bourns,CR0603-JW-104ELF,CR0603-JW-104ELFCT-ND,1,80
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Statistics:
|
||||
Component Groups:,10
|
||||
Component Count:,27
|
||||
Fitted Components:,27
|
||||
Number of PCBs:,100
|
||||
Total Components:,2630
|
||||
|
Can't render this file because it has a wrong number of fields in line 17.
|
|
|
@ -0,0 +1 @@
|
|||
../5_1_6/subparts-bom.csv
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
../5_1_6/subparts-bom.csv
|
||||
|
|
|
@ -1506,3 +1506,13 @@ def test_int_bom_merge_xml_1(test_dir):
|
|||
src_column = header.index(SOURCE_BOM_COLUMN_NAME.replace(' ', '_'))
|
||||
check_source(rows, 'A:R1', ref_column, src_column, MERGED_R1_SRC)
|
||||
ctx.clean_up()
|
||||
|
||||
|
||||
def test_int_bom_subparts_1(test_dir):
|
||||
prj = 'subparts'
|
||||
ctx = context.TestContextSCH(test_dir, 'test_int_bom_subparts_1', prj, 'int_bom_subparts_1', '')
|
||||
ctx.run(extra_debug=True)
|
||||
output = prj+'-bom.csv'
|
||||
ctx.expect_out_file(output)
|
||||
ctx.compare_txt(output)
|
||||
ctx.clean_up()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,64 @@
|
|||
# Example KiBot config file
|
||||
kibot:
|
||||
version: 1
|
||||
|
||||
filters:
|
||||
- name: 'Subparts splitter'
|
||||
type: subparts
|
||||
# We want to also split the `Description` field
|
||||
split_fields: ['Description']
|
||||
split_fields_expand: true
|
||||
# We only use the multiplier in `manf#`
|
||||
check_multiplier: ['manf#', 'digikey#']
|
||||
|
||||
variants:
|
||||
- name: place_holder
|
||||
comment: 'Just a place holder for the subparts splitter'
|
||||
type: kibom
|
||||
pre_transform: 'Subparts splitter'
|
||||
|
||||
outputs:
|
||||
- name: 'bom_internal_subparts'
|
||||
comment: "Bill of Materials in CSV format, subparts splitted"
|
||||
type: bom
|
||||
dir: .
|
||||
options: &bom_options
|
||||
variant: place_holder
|
||||
number: 100
|
||||
group_fields: ['manf#']
|
||||
group_fields_fallbacks: ['value']
|
||||
# int_qtys: false
|
||||
columns:
|
||||
- Row
|
||||
- References
|
||||
- Value
|
||||
- Description
|
||||
- manf
|
||||
- manf#
|
||||
- digikey#
|
||||
- 'Quantity Per PCB'
|
||||
- 'Build Quantity'
|
||||
csv:
|
||||
hide_pcb_info: true
|
||||
|
||||
- name: 'bom_html'
|
||||
comment: "Bill of Materials in HTML format"
|
||||
type: bom
|
||||
dir: .
|
||||
options:
|
||||
<<: *bom_options
|
||||
html:
|
||||
digikey_link: 'digikey#'
|
||||
highlight_empty: false
|
||||
|
||||
|
||||
- name: 'bom_xlsx'
|
||||
comment: "Bill of Materials in XLSX format"
|
||||
type: bom
|
||||
dir: .
|
||||
options:
|
||||
<<: *bom_options
|
||||
xlsx:
|
||||
digikey_link: 'digikey#'
|
||||
highlight_empty: false
|
||||
|
||||
Loading…
Reference in New Issue