Added a new filter used to rotate footprints.

Used to generate position files for some manufacturers like JLC.
Also in this patch:
- Now position files are naturally sorted (R10 after R9, not after R1)
- Position files in CSV format quotes only the columns that could
  contain an space. Just like KiCad does.
- Fixed: Generic filter `include_only` option worked only when debug
  enabled.
This commit is contained in:
Salvador E. Tropea 2020-12-28 18:26:27 -03:00
parent 0f7a55dc8b
commit 4a3e7faace
23 changed files with 7411 additions and 28 deletions

View File

@ -13,13 +13,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- A hint for pip installations without using `--no-compile`.
- Support to field overwrite according to variant.
- Support to generate negative X positions for the bottom layer.
### Changed
- Now position files are naturally sorted (R10 after R9, not after R1)
- Position files in CSV format quotes only the columns that could contain an
space. Just like KiCad does.
### Fixed
- Now we support missing field names in schematic library entries.
- Generic filter `include_only` option worked only when debug enabled.
## [0.8.1] - 2020-12-09
### Added
- Internal BoM HTML: highlight cell when hover.
- Internal BoM HTML: allow to jump to REF of row number using anchors.
### Fixed
- Internal BoM separator wasn't applied when using `use_alt`
- Problems loading plug-ins when using `pip`.

View File

@ -312,6 +312,17 @@ 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.
- rot_footprint: Rot_Footprint
This filter can rotate footprints, used for the positions file generation.
Some manufacturers use a different rotation than KiCad..
* Valid keys:
- `comment`: [string=''] A comment for documentation purposes.
- `extend`: [boolean=true] Extends the internal list of rotations with the one provided.
Otherwise just use the provided list.
- `name`: [string=''] Used to identify this particular filter definition.
- `negative_bottom`: [boolean=true] Rotation for bottom components is computed substracting.
- `rotations`: [list(list(string))] A list of pairs regular expression/rotation.
Components matching the regular expression will be rotated the indicated angle.
- var_rename: Var_Rename
This filter implements the VARIANT:FIELD=VALUE renamer to get FIELD=VALUE when VARIANT is in use.
* Valid keys:

View File

@ -4,7 +4,7 @@
# License: GPL-3.0
# Project: KiBot (formerly KiPlot)
from .registrable import RegFilter, Registrable, RegOutput
from .misc import IFILT_MECHANICAL, IFILT_VAR_RENAME
from .misc import IFILT_MECHANICAL, IFILT_VAR_RENAME, IFILT_ROT_FOOTPRINT
from .error import KiPlotConfigurationError
from .bom.columnlist import ColumnList
from .macros import macros, document # noqa: F401
@ -142,6 +142,14 @@ class BaseFilter(RegFilter):
logger.debug('Creating internal filter: '+str(o_tree))
return o_tree
@staticmethod
def _create_rot_footprint(name):
o_tree = {'name': name}
o_tree['type'] = 'rot_footprint'
o_tree['comment'] = 'Internal default footprint rotator'
logger.debug('Creating internal filter: '+str(o_tree))
return o_tree
@staticmethod
def _create_kibom_dnx(name):
type = name[7:10]
@ -169,6 +177,8 @@ class BaseFilter(RegFilter):
tree = BaseFilter._create_kibom_dnx(name)
elif name == IFILT_VAR_RENAME:
tree = BaseFilter._create_var_rename(name)
elif name == IFILT_ROT_FOOTPRINT:
tree = BaseFilter._create_rot_footprint(name)
else:
return None
filter = RegFilter.get_class_for(tree['type'])()

101
kibot/fil_rot_footprint.py Normal file
View File

@ -0,0 +1,101 @@
# -*- 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 a filter to rotate footprints.
This is inspired in JLCKicadTools by Matthew Lai.
"""
from re import compile
from .gs import GS
from .optionable import Optionable
from .error import KiPlotConfigurationError
from .macros import macros, document, filter_class # noqa: F401
from . import log
logger = log.get_logger(__name__)
# Known rotations for JLC
DEFAULT_ROTATIONS = [["^R_Array_Convex_", 90.0],
["^R_Array_Concave_", 90.0],
["^SOT-223", 180.0],
["^SOT-23", 180.0],
["^TSOT-23", 180.0],
["^SOT-353", 180.0],
["^QFN-", 270.0],
["^LQFP-", 270.0],
["^TQFP-", 270.0],
["^SOP-(?!18_)", 270.0],
["^TSSOP-", 270.0],
["^DFN-", 270.0],
["^SOIC-", 270.0],
# ["^SOP-18_", 0],
["^VSSOP-10_", 270.0],
["^CP_EIA-3216-18_", 180.0],
["^CP_EIA-3528-15_AVX-H", 180.0],
["^CP_EIA-3528-21_Kemet-B", 180.0],
["^CP_Elec_8x10.5", 180.0],
["^CP_Elec_6.3x7.7", 180.0],
["^CP_Elec_8x6.7", 180.0],
["^CP_Elec_8x10", 180.0],
["^(.*?_|V)?QFN-(16|20|24|28|40)(-|_|$)", 270.0],
["^Bosch_LGA-8_2x2.5mm_P0.65mm_ClockwisePinNumbering", 90.0],
["^PowerPAK_SO-8_Single", 270.0],
["^HTSSOP-28-1EP_4.4x9.7mm*", 270.0],
]
@filter_class
class Rot_Footprint(BaseFilter): # noqa: F821
""" Rot_Footprint
This filter can rotate footprints, used for the positions file generation.
Some manufacturers use a different rotation than KiCad. """
def __init__(self):
super().__init__()
with document:
self.extend = True
""" Extends the internal list of rotations with the one provided.
Otherwise just use the provided list """
self.negative_bottom = True
""" Rotation for bottom components is computed substracting """
self.rotations = Optionable
""" [list(list(string))] A list of pairs regular expression/rotation.
Components matching the regular expression will be rotated the indicated angle """
def config(self):
super().config()
self._rot = []
if isinstance(self.rotations, list):
for r in self.rotations:
if len(r) != 2:
raise KiPlotConfigurationError("Each regex/angle pair must contain exactly two values, not {} ({})".
format(len(r), r))
regex = compile(r[0])
try:
angle = float(r[1])
except ValueError:
raise KiPlotConfigurationError("The second value in the regex/angle pairs must be a number, not {}".
format(r[1]))
self._rot.append([regex, angle])
if self.extend:
for regex_str, angle in DEFAULT_ROTATIONS:
self._rot.append([compile(regex_str), angle])
if not self._rot:
raise KiPlotConfigurationError("No rotations provided")
def filter(self, comp):
""" Apply the rotation """
for regex, angle in self._rot:
if regex.search(comp.footprint):
old_angle = comp.footprint_rot
if self.negative_bottom and comp.bottom:
comp.footprint_rot -= angle
else:
comp.footprint_rot += angle
comp.footprint_rot = comp.footprint_rot % 360
if GS.debug_level > 2:
logger.debug('Rotating ref: {} {}: {} -> {}'.
format(comp.ref, comp.footprint, old_angle, comp.footprint_rot))
return

View File

@ -6,13 +6,8 @@
"""
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__)

View File

@ -811,6 +811,8 @@ class SchematicComponent(object):
self.fitted = True
self.included = True
self.fixed = False
self.bottom = False
self.footprint_rot = 0.0
# KiCad 5 PCB flags (mutually exclusive)
self.smd = False
self.virtual = False

View File

@ -200,6 +200,8 @@ def get_board_comps_data(comps):
logger.warning(W_PCBNOSCH + '`{}` component in board, but not in schematic'.format(ref))
continue
c = comps_hash[ref]
c.bottom = m.IsFlipped()
c.footprint_rot = m.GetOrientationDegrees()
attrs = m.GetAttributes()
if GS.kicad_version_n < KICAD_VERSION_5_99:
# KiCad 5

View File

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

View File

@ -4,7 +4,7 @@
# License: GPL-3.0
# Project: KiBot (formerly KiPlot)
from .gs import GS
from .kiplot import load_sch
from .kiplot import load_sch, get_board_comps_data
from .misc import Rect, KICAD_VERSION_5_99, W_WRONGPASTE
if GS.kicad_version_n >= KICAD_VERSION_5_99:
# New name, no alias ...
@ -260,6 +260,7 @@ class VariantOptions(BaseOptions):
load_sch()
# Get the components list from the schematic
comps = GS.sch.get_components()
get_board_comps_data(comps)
# Apply the filter
reset_filters(comps)
apply_fitted_filter(comps, self.dnf_filter)

View File

@ -5,7 +5,7 @@
# License: GPL-3.0
# Project: KiBot (formerly KiPlot)
# Adapted from: https://github.com/johnbeard/kiplot/pull/10
import operator
from re import compile
from datetime import datetime
from pcbnew import IU_PER_MM, IU_PER_MILS
from collections import OrderedDict
@ -18,6 +18,17 @@ from .macros import macros, document, output_class # noqa: F401
from . import log
logger = log.get_logger(__name__)
ref_re = compile(r'([^\d]+)([\?\d]+)')
def _ref_key(ref_str):
""" Splits a reference intro prefix and suffix.
Helps to sort references in a natural way. """
m = ref_re.match(ref_str)
if not m:
return [ref_str]
pre, suf = m.groups()
return [pre, 0 if suf == '?' else int(suf)]
class PosColumns(Optionable):
@ -155,7 +166,7 @@ class PositionOptions(VariantOptions):
fle = topf
else:
fle = botf
fle.write(",".join('"{}"'.format(e) for e in m))
fle.write(",".join('{}'.format(e) for e in m))
fle.write("\n")
if topf is not None:
@ -181,6 +192,12 @@ class PositionOptions(VariantOptions):
def is_not_virtual_6(m):
return not (m.GetAttributes() & MOD_EXCLUDE_FROM_POS_FILES)
@staticmethod
def get_attr_tests():
if GS.kicad_version_n < KICAD_VERSION_5_99:
return PositionOptions.is_pure_smd_5, PositionOptions.is_not_virtual_5
return PositionOptions.is_pure_smd_6, PositionOptions.is_not_virtual_6
def run(self, output_dir, board):
super().run(output_dir, board)
columns = self.columns.values()
@ -193,19 +210,26 @@ class PositionOptions(VariantOptions):
# Format all strings
comps_hash = self.get_refs_hash()
modules = []
if GS.kicad_version_n < KICAD_VERSION_5_99:
is_pure_smd = self.is_pure_smd_5
is_not_virtual = self.is_not_virtual_5
else:
is_pure_smd = self.is_pure_smd_6
is_not_virtual = self.is_not_virtual_6
for m in sorted(board.GetModules(), key=operator.methodcaller('GetReference')):
is_pure_smd, is_not_virtual = self.get_attr_tests()
quote_char = '"' if self.format == 'CSV' else ''
for m in sorted(board.GetModules(), key=lambda c: _ref_key(c.GetReference())):
ref = m.GetReference()
value = None
# Apply any filter or variant data
if comps_hash:
c = comps_hash.get(ref, None)
if c and (not c.fitted or not c.included):
if c:
if not c.fitted or not c.included:
continue
value = c.value
footprint = c.footprint
is_bottom = c.bottom
rotation = c.footprint_rot
if value is None:
value = m.GetValue()
footprint = str(m.GetFPID().GetLibItemName()) # pcbnew.UTF8 type
is_bottom = m.IsFlipped()
rotation = m.GetOrientationDegrees()
# If passed check the position options
if (self.only_smd and is_pure_smd(m)) or (not self.only_smd and is_not_virtual(m)):
center = m.GetCenter()
@ -213,22 +237,22 @@ class PositionOptions(VariantOptions):
row = []
for k in self.columns:
if k == 'Ref':
row.append(ref)
row.append(quote_char+ref+quote_char)
elif k == 'Val':
row.append(m.GetValue())
row.append(quote_char+value+quote_char)
elif k == 'Package':
row.append(str(m.GetFPID().GetLibItemName())) # pcbnew.UTF8 type
row.append(quote_char+footprint+quote_char)
elif k == 'PosX':
pos_x = center.x * conv
if self.bottom_negative_x and m.IsFlipped():
if self.bottom_negative_x and is_bottom:
pos_x = -pos_x
row.append("{:.4f}".format(pos_x))
elif k == 'PosY':
row.append("{:.4f}".format(-center.y * conv))
elif k == 'Rot':
row.append("{:.4f}".format(m.GetOrientationDegrees()))
row.append("{:.4f}".format(rotation))
elif k == 'Side':
row.append("bottom" if m.IsFlipped() else "top")
row.append("bottom" if is_bottom else "top")
modules.append(row)
# Find max width for all columns
maxlengths = []

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,21 @@
"Comment","Designator","Footprint","LCSC Part #"
"0.1uF","C2,C8","R_0402_1005Metric","C1525"
"1uF","C1","R_0402_1005Metric","C52923"
"1uF","C4","R_0603_1608Metric","C15849"
"4.7uF","C3","R_0603_1608Metric","C19666"
"4.7uF","C6","R_0402_1005Metric","C23733"
"10uF","C7","R_0402_1005Metric","C15525"
"22uF","C5","R_0603_1608Metric","C59461"
"D_Schottky","D1,D2,D3,D4,D5,D6,D7,D8,D9,D10","D_SOD-123","C8598"
"Green","D11,D12","LED_0603_1608Metric","C72043"
"Red","D13,D14","LED_0603_1608Metric","C2286"
"Q_NMOS_GDS","Q4,Q5,Q6,Q7,Q8,Q9,Q10","SOT-23","C20917"
"Q_NMOS_GSD","Q3","SOT-23","C20917"
"Q_NPN_BEC","Q1,Q2","SOT-23","C2150"
"47R","R12,R13","R_0402_1005Metric","C25118"
"100R","R4,R5,R6,R7,R8,R9,R10,R11","R_0402_1005Metric","C25076"
"150R","R14,R15","R_0402_1005Metric","C25082"
"10kR","R1,R2,R3,R16,R17,R18,R19,R20,R21,R22,R23,R24","R_0402_1005Metric","C25744"
"SW_Push","SW1","TS-1187A","C318888"
"AZ1117-3.3","U1","SOT-223-3_TabPin2","C92102"
"CP2104","U2","QFN-24-1EP_4x4mm_P0.5mm_EP2.6x2.6mm","C47742"
1 Comment Designator Footprint LCSC Part #
2 0.1uF C2,C8 R_0402_1005Metric C1525
3 1uF C1 R_0402_1005Metric C52923
4 1uF C4 R_0603_1608Metric C15849
5 4.7uF C3 R_0603_1608Metric C19666
6 4.7uF C6 R_0402_1005Metric C23733
7 10uF C7 R_0402_1005Metric C15525
8 22uF C5 R_0603_1608Metric C59461
9 D_Schottky D1,D2,D3,D4,D5,D6,D7,D8,D9,D10 D_SOD-123 C8598
10 Green D11,D12 LED_0603_1608Metric C72043
11 Red D13,D14 LED_0603_1608Metric C2286
12 Q_NMOS_GDS Q4,Q5,Q6,Q7,Q8,Q9,Q10 SOT-23 C20917
13 Q_NMOS_GSD Q3 SOT-23 C20917
14 Q_NPN_BEC Q1,Q2 SOT-23 C2150
15 47R R12,R13 R_0402_1005Metric C25118
16 100R R4,R5,R6,R7,R8,R9,R10,R11 R_0402_1005Metric C25076
17 150R R14,R15 R_0402_1005Metric C25082
18 10kR R1,R2,R3,R16,R17,R18,R19,R20,R21,R22,R23,R24 R_0402_1005Metric C25744
19 SW_Push SW1 TS-1187A C318888
20 AZ1117-3.3 U1 SOT-223-3_TabPin2 C92102
21 CP2104 U2 QFN-24-1EP_4x4mm_P0.5mm_EP2.6x2.6mm C47742

View File

@ -0,0 +1,62 @@
Designator,Val,Package,Mid X,Mid Y,Rotation,Layer
"C1","1uF","R_0402_1005Metric",111.2000,-69.8500,270.0000,top
"C2","0.1uF","R_0402_1005Metric",112.2000,-69.8500,270.0000,top
"C3","4.7uF","R_0603_1608Metric",117.6020,-66.2685,270.0000,top
"C4","1uF","R_0603_1608Metric",118.6000,-81.3000,270.0000,top
"C5","22uF","R_0603_1608Metric",117.6000,-71.3000,180.0000,top
"C6","4.7uF","R_0402_1005Metric",111.5060,-61.9760,270.0000,top
"C7","10uF","R_0402_1005Metric",121.6660,-62.7380,90.0000,top
"C8","0.1uF","R_0402_1005Metric",122.9360,-62.7380,90.0000,top
"D1","D_Schottky","D_SOD-123",115.3000,-81.0000,180.0000,top
"D2","D_Schottky","D_SOD-123",109.4000,-68.8000,90.0000,top
"D3","D_Schottky","D_SOD-123",118.8000,-88.8840,270.0000,top
"D4","D_Schottky","D_SOD-123",131.5000,-89.9000,270.0000,top
"D5","D_Schottky","D_SOD-123",145.2880,-87.8840,270.0000,top
"D6","D_Schottky","D_SOD-123",149.3000,-79.4000,0.0000,top
"D7","D_Schottky","D_SOD-123",125.2000,-88.8840,270.0000,top
"D8","D_Schottky","D_SOD-123",138.9000,-90.0000,270.0000,top
"D9","D_Schottky","D_SOD-123",149.2000,-85.5000,0.0000,top
"D10","D_Schottky","D_SOD-123",149.2000,-73.3000,0.0000,top
"D11","Green","LED_0603_1608Metric",149.9000,-63.8000,0.0000,top
"D12","Green","LED_0603_1608Metric",153.2000,-63.8000,0.0000,top
"D13","Red","LED_0603_1608Metric",156.5000,-63.8000,0.0000,top
"D14","Red","LED_0603_1608Metric",159.7000,-63.8000,0.0000,top
"J2","USB_B_Micro","USB_Micro-B_Molex-105017-0001",105.4100,-66.0400,270.0000,top
"Q1","Q_NPN_BEC","SOT-23",121.1580,-66.0400,180.0000,top
"Q2","Q_NPN_BEC","SOT-23",121.1580,-69.8500,180.0000,top
"Q3","Q_NMOS_GSD","SOT-23",121.8000,-89.4080,90.0000,top
"Q4","Q_NMOS_GDS","SOT-23",134.6000,-90.5000,90.0000,top
"Q5","Q_NMOS_GDS","SOT-23",148.5900,-89.9160,90.0000,top
"Q6","Q_NMOS_GDS","SOT-23",149.8000,-76.4000,180.0000,top
"Q7","Q_NMOS_GDS","SOT-23",128.2000,-90.6780,90.0000,top
"Q8","Q_NMOS_GDS","SOT-23",141.9000,-89.6620,90.0000,top
"Q9","Q_NMOS_GDS","SOT-23",149.8000,-82.5000,180.0000,top
"Q10","Q_NMOS_GDS","SOT-23",149.8000,-70.3000,180.0000,top
"R1","10kR","R_0402_1005Metric",117.6020,-62.2300,180.0000,top
"R2","10kR","R_0402_1005Metric",117.6020,-63.5000,180.0000,top
"R3","10kR","R_0402_1005Metric",110.2360,-61.9760,270.0000,top
"R4","100R","R_0402_1005Metric",122.6820,-86.6140,90.0000,top
"R5","100R","R_0402_1005Metric",134.2000,-88.2000,0.0000,top
"R6","100R","R_0402_1005Metric",145.3000,-84.9000,180.0000,top
"R7","100R","R_0402_1005Metric",147.4000,-77.1150,90.0000,top
"R8","100R","R_0402_1005Metric",129.2860,-88.2650,180.0000,top
"R9","100R","R_0402_1005Metric",141.1380,-86.8520,0.0000,top
"R10","100R","R_0402_1005Metric",147.4470,-82.5500,270.0000,top
"R11","100R","R_0402_1005Metric",147.3200,-70.9000,270.0000,top
"R12","47R","R_0402_1005Metric",150.2000,-65.3000,0.0000,top
"R13","47R","R_0402_1005Metric",153.0000,-65.3000,0.0000,top
"R14","150R","R_0402_1005Metric",155.9000,-65.3000,0.0000,top
"R15","150R","R_0402_1005Metric",159.0000,-65.3000,0.0000,top
"R16","10kR","R_0402_1005Metric",126.2000,-83.6000,180.0000,top
"R17","10kR","R_0402_1005Metric",121.1580,-86.3600,0.0000,top
"R18","10kR","R_0402_1005Metric",136.2710,-88.1380,180.0000,top
"R19","10kR","R_0402_1005Metric",144.3990,-83.4390,0.0000,top
"R20","10kR","R_0402_1005Metric",145.9230,-77.4700,90.0000,top
"R21","10kR","R_0402_1005Metric",127.3810,-88.2650,0.0000,top
"R22","10kR","R_0402_1005Metric",142.7480,-86.8680,270.0000,top
"R23","10kR","R_0402_1005Metric",144.3990,-82.0420,0.0000,top
"R24","10kR","R_0402_1005Metric",147.3200,-68.8570,270.0000,top
"SW1","SW_Push","TS-1187A",114.0000,-58.1660,0.0000,top
"U1","AZ1117-3.3","SOT-223-3_TabPin2",117.3000,-75.8000,180.0000,top
"U2","CP2104","QFN-24-1EP_4x4mm_P0.5mm_EP2.6x2.6mm",113.9040,-66.0400,270.0000,top
"U3","ESP32-WROOM-32","ESP32-WROOM-32",134.3660,-70.3580,0.0000,top
1 Designator Val Package Mid X Mid Y Rotation Layer
2 C1 1uF R_0402_1005Metric 111.2000 -69.8500 270.0000 top
3 C2 0.1uF R_0402_1005Metric 112.2000 -69.8500 270.0000 top
4 C3 4.7uF R_0603_1608Metric 117.6020 -66.2685 270.0000 top
5 C4 1uF R_0603_1608Metric 118.6000 -81.3000 270.0000 top
6 C5 22uF R_0603_1608Metric 117.6000 -71.3000 180.0000 top
7 C6 4.7uF R_0402_1005Metric 111.5060 -61.9760 270.0000 top
8 C7 10uF R_0402_1005Metric 121.6660 -62.7380 90.0000 top
9 C8 0.1uF R_0402_1005Metric 122.9360 -62.7380 90.0000 top
10 D1 D_Schottky D_SOD-123 115.3000 -81.0000 180.0000 top
11 D2 D_Schottky D_SOD-123 109.4000 -68.8000 90.0000 top
12 D3 D_Schottky D_SOD-123 118.8000 -88.8840 270.0000 top
13 D4 D_Schottky D_SOD-123 131.5000 -89.9000 270.0000 top
14 D5 D_Schottky D_SOD-123 145.2880 -87.8840 270.0000 top
15 D6 D_Schottky D_SOD-123 149.3000 -79.4000 0.0000 top
16 D7 D_Schottky D_SOD-123 125.2000 -88.8840 270.0000 top
17 D8 D_Schottky D_SOD-123 138.9000 -90.0000 270.0000 top
18 D9 D_Schottky D_SOD-123 149.2000 -85.5000 0.0000 top
19 D10 D_Schottky D_SOD-123 149.2000 -73.3000 0.0000 top
20 D11 Green LED_0603_1608Metric 149.9000 -63.8000 0.0000 top
21 D12 Green LED_0603_1608Metric 153.2000 -63.8000 0.0000 top
22 D13 Red LED_0603_1608Metric 156.5000 -63.8000 0.0000 top
23 D14 Red LED_0603_1608Metric 159.7000 -63.8000 0.0000 top
24 J2 USB_B_Micro USB_Micro-B_Molex-105017-0001 105.4100 -66.0400 270.0000 top
25 Q1 Q_NPN_BEC SOT-23 121.1580 -66.0400 180.0000 top
26 Q2 Q_NPN_BEC SOT-23 121.1580 -69.8500 180.0000 top
27 Q3 Q_NMOS_GSD SOT-23 121.8000 -89.4080 90.0000 top
28 Q4 Q_NMOS_GDS SOT-23 134.6000 -90.5000 90.0000 top
29 Q5 Q_NMOS_GDS SOT-23 148.5900 -89.9160 90.0000 top
30 Q6 Q_NMOS_GDS SOT-23 149.8000 -76.4000 180.0000 top
31 Q7 Q_NMOS_GDS SOT-23 128.2000 -90.6780 90.0000 top
32 Q8 Q_NMOS_GDS SOT-23 141.9000 -89.6620 90.0000 top
33 Q9 Q_NMOS_GDS SOT-23 149.8000 -82.5000 180.0000 top
34 Q10 Q_NMOS_GDS SOT-23 149.8000 -70.3000 180.0000 top
35 R1 10kR R_0402_1005Metric 117.6020 -62.2300 180.0000 top
36 R2 10kR R_0402_1005Metric 117.6020 -63.5000 180.0000 top
37 R3 10kR R_0402_1005Metric 110.2360 -61.9760 270.0000 top
38 R4 100R R_0402_1005Metric 122.6820 -86.6140 90.0000 top
39 R5 100R R_0402_1005Metric 134.2000 -88.2000 0.0000 top
40 R6 100R R_0402_1005Metric 145.3000 -84.9000 180.0000 top
41 R7 100R R_0402_1005Metric 147.4000 -77.1150 90.0000 top
42 R8 100R R_0402_1005Metric 129.2860 -88.2650 180.0000 top
43 R9 100R R_0402_1005Metric 141.1380 -86.8520 0.0000 top
44 R10 100R R_0402_1005Metric 147.4470 -82.5500 270.0000 top
45 R11 100R R_0402_1005Metric 147.3200 -70.9000 270.0000 top
46 R12 47R R_0402_1005Metric 150.2000 -65.3000 0.0000 top
47 R13 47R R_0402_1005Metric 153.0000 -65.3000 0.0000 top
48 R14 150R R_0402_1005Metric 155.9000 -65.3000 0.0000 top
49 R15 150R R_0402_1005Metric 159.0000 -65.3000 0.0000 top
50 R16 10kR R_0402_1005Metric 126.2000 -83.6000 180.0000 top
51 R17 10kR R_0402_1005Metric 121.1580 -86.3600 0.0000 top
52 R18 10kR R_0402_1005Metric 136.2710 -88.1380 180.0000 top
53 R19 10kR R_0402_1005Metric 144.3990 -83.4390 0.0000 top
54 R20 10kR R_0402_1005Metric 145.9230 -77.4700 90.0000 top
55 R21 10kR R_0402_1005Metric 127.3810 -88.2650 0.0000 top
56 R22 10kR R_0402_1005Metric 142.7480 -86.8680 270.0000 top
57 R23 10kR R_0402_1005Metric 144.3990 -82.0420 0.0000 top
58 R24 10kR R_0402_1005Metric 147.3200 -68.8570 270.0000 top
59 SW1 SW_Push TS-1187A 114.0000 -58.1660 0.0000 top
60 U1 AZ1117-3.3 SOT-223-3_TabPin2 117.3000 -75.8000 180.0000 top
61 U2 CP2104 QFN-24-1EP_4x4mm_P0.5mm_EP2.6x2.6mm 113.9040 -66.0400 270.0000 top
62 U3 ESP32-WROOM-32 ESP32-WROOM-32 134.3660 -70.3580 0.0000 top

View File

@ -0,0 +1 @@
../5_1_6/light_control_bom_jlc.csv
1 ../5_1_6/light_control_bom_jlc.csv

View File

@ -0,0 +1 @@
../5_1_6/light_control_cpl_jlc.csv
1 ../5_1_6/light_control_cpl_jlc.csv

View File

@ -0,0 +1 @@
../5_1_6/light_control_bom_jlc.csv
1 ../5_1_6/light_control_bom_jlc.csv

View File

@ -0,0 +1 @@
../5_1_6/light_control_cpl_jlc.csv
1 ../5_1_6/light_control_cpl_jlc.csv

View File

@ -30,7 +30,7 @@ from utils import context
POS_DIR = 'positiondir'
positions = {'R1': (105, 35, 'top'), 'R2': (110, 35, 'bottom'), 'R3': (110, 45, 'top')}
CSV_EXPR = r'^"%s",[^,]+,[^,]+,"([-\d\.]+)","([-\d\.]+)","([-\d\.]+)","(\S+)"$'
CSV_EXPR = r'^"%s",[^,]+,[^,]+,([-\d\.]+),([-\d\.]+),([-\d\.]+),(\S+)$'
ASCII_EXPR = r'^%s\s+\S+\s+\S+\s+([-\d\.]+)\s+([-\d\.]+)\s+([-\d\.]+)\s+(\S+)\s*$'
@ -194,3 +194,25 @@ def test_position_variant_t2i():
rows, header, info = ctx.load_csv(prj+'-both_pos_(test).csv')
check_comps(rows, ['C1', 'C2', 'R1', 'R3'])
ctx.clean_up(keep_project=True)
def test_position_rot_1():
prj = 'light_control'
ctx = context.TestContext('test_position_rot_1', prj, 'simple_position_rot_1', POS_DIR)
ctx.run()
output = prj+'_cpl_jlc.csv'
ctx.expect_out_file(output)
ctx.compare_txt(output)
ctx.compare_txt(prj+'_bom_jlc.csv')
ctx.clean_up()
def test_position_rot_2():
prj = 'light_control'
ctx = context.TestContext('test_position_rot_2', prj, 'simple_position_rot_2', POS_DIR)
ctx.run()
output = prj+'_cpl_jlc.csv'
ctx.expect_out_file(output)
ctx.compare_txt(output)
ctx.compare_txt(prj+'_bom_jlc.csv')
ctx.clean_up()

View File

@ -441,7 +441,7 @@ class TestContext(object):
self.get_out_path(text)+' > '+self.get_out_path(diff)]
logging.debug('Comparing texts with: '+usable_cmd(cmd))
res = subprocess.call(cmd)
assert res == 0
assert res == 0, res
def filter_txt(self, file, pattern, repl):
fname = self.get_out_path(file)

View File

@ -0,0 +1,66 @@
kibot:
version: 1
filters:
- name: fix_rotation
comment: 'Adjust rotation for JLC'
type: rot_footprint
- name: only_jlc_parts
comment: 'Only parts with JLC code'
type: generic
include_only:
- column: Field4
regex: '^C\d+'
variants:
- name: default
comment: 'Just a place holder for the rotation filter'
type: kibom
variant: default
pre_transform: fix_rotation
outputs:
- name: 'position'
comment: "Pick and place file, JLC style"
type: position
options:
variant: default
output: '%f_cpl_jlc.%x'
format: CSV
units: millimeters
separate_files_for_front_and_back: false
only_smd: true
columns:
- id: Ref
name: Designator
- Val
- Package
- id: PosX
name: "Mid X"
- id: PosY
name: "Mid Y"
- id: Rot
name: Rotation
- id: Side
name: Layer
- name: 'bom'
comment: "BoM for JLC"
type: bom
options:
output: '%f_%i_jlc.%x'
exclude_filter: 'only_jlc_parts'
ref_separator: ','
columns:
- field: Value
name: Comment
- field: References
name: Designator
- Footprint
- field: Field4
name: 'LCSC Part #'
csv:
hide_pcb_info: true
hide_stats_info: true
quote_all: true

View File

@ -0,0 +1,62 @@
kibot:
version: 1
filters:
- name: only_jlc_parts
comment: 'Only parts with JLC code'
type: generic
include_only:
- column: Field4
regex: '^C\d+'
variants:
- name: default
comment: 'Just a place holder for the rotation filter'
type: kibom
variant: default
pre_transform: _rot_footprint
outputs:
- name: 'position'
comment: "Pick and place file, JLC style"
type: position
options:
variant: default
output: '%f_cpl_jlc.%x'
format: CSV
units: millimeters
separate_files_for_front_and_back: false
only_smd: true
columns:
- id: Ref
name: Designator
- Val
- Package
- id: PosX
name: "Mid X"
- id: PosY
name: "Mid Y"
- id: Rot
name: Rotation
- id: Side
name: Layer
- name: 'bom'
comment: "BoM for JLC"
type: bom
options:
output: '%f_%i_jlc.%x'
exclude_filter: 'only_jlc_parts'
ref_separator: ','
columns:
- field: Value
name: Comment
- field: References
name: Designator
- Footprint
- field: Field4
name: 'LCSC Part #'
csv:
hide_pcb_info: true
hide_stats_info: true
quote_all: true