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:
parent
0f7a55dc8b
commit
4a3e7faace
|
|
@ -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`.
|
||||
|
|
|
|||
11
README.md
11
README.md
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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'])()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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__)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
../5_1_6/light_control_bom_jlc.csv
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
../5_1_6/light_control_cpl_jlc.csv
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
../5_1_6/light_control_bom_jlc.csv
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
../5_1_6/light_control_cpl_jlc.csv
|
||||
|
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
Loading…
Reference in New Issue