Support for extra data in the Value field
- Currently we just use the tolerance for the 3D resistors - Uses a port of the JavaScript Electro-Grammar
This commit is contained in:
parent
4de3152ac9
commit
158f267eb5
|
|
@ -18,6 +18,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
using all pages or individually.
|
||||
- Plot related outputs:
|
||||
- All outputs now support scaling.
|
||||
- BoM:
|
||||
- Support for extra information in the *Value* field.
|
||||
Currently just parsed, not rejected.
|
||||
|
||||
### Fixed
|
||||
- Makefile: don't skip all preflights on each run, just the ones we generate
|
||||
|
|
@ -33,6 +36,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- Diff:
|
||||
- Problems when using an output and no variant specified.
|
||||
|
||||
### Changed:
|
||||
- Some R, L and C values that were rejected are accepted now. You just get a
|
||||
warning about what part of the value was discarded.
|
||||
|
||||
|
||||
## [1.6.1] - 2023-03-16
|
||||
### Added
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ include kibot/resources/config_templates/panelize/*.yaml
|
|||
include kibot/resources/config_templates/*.yaml
|
||||
include kibot/resources/images/*.svg
|
||||
include kibot/resources/images/*.ico
|
||||
include kibot/resources/parsers/*.lark
|
||||
include kibot/resources/pcbdraw/styles/*.json
|
||||
include kibot/resources/pcbdraw/templates/*.handlebars
|
||||
include kibot/blender_scripts/*.py
|
||||
|
|
|
|||
2
Makefile
2
Makefile
|
|
@ -48,7 +48,7 @@ test: lint
|
|||
rm -f tests/.local
|
||||
$(PY_COV) erase
|
||||
# python3-pytest-xdist
|
||||
$(PYTEST) -m "not slow" -n 2 --test_dir=output
|
||||
$(PYTEST) -m "not slow" -n 4 --test_dir=output
|
||||
$(PYTEST) -m "slow" --test_dir=output
|
||||
$(PY_COV) combine
|
||||
$(PY_COV) report
|
||||
|
|
|
|||
|
|
@ -4,3 +4,4 @@ kibot/resources/kicad_colors/ /usr/share/kibot/
|
|||
kibot/resources/kicad_layouts/ /usr/share/kibot/
|
||||
kibot/resources/pcbdraw/ /usr/share/kibot/
|
||||
kibot/resources/report_templates/ /usr/share/kibot/
|
||||
kibot/resources/parsers/ /usr/share/kibot/
|
||||
|
|
|
|||
|
|
@ -218,6 +218,7 @@ This file comes from KiKit, but it has too much in common with `populate.py`.
|
|||
|
||||
## 2023-03-20 Various fixes and changes in resistor colors
|
||||
|
||||
```diff
|
||||
diff --git a/kibot/PcbDraw/plot.py b/kibot/PcbDraw/plot.py
|
||||
index 8ca660e6..9dc45ba9 100644
|
||||
--- a/kibot/PcbDraw/plot.py
|
||||
|
|
@ -288,10 +289,11 @@ index 8ca660e6..9dc45ba9 100644
|
|||
ref = footprint.GetReference().strip()
|
||||
center = footprint.GetPosition()
|
||||
orient = math.radians(footprint.GetOrientation().AsDegrees())
|
||||
|
||||
```
|
||||
|
||||
## 2023-03-27 Fixe for KiCad 7.0.1 polygons
|
||||
|
||||
```diff
|
||||
diff --git a/kibot/PcbDraw/plot.py b/kibot/PcbDraw/plot.py
|
||||
index 9dc45ba9..8df84469 100644
|
||||
--- a/kibot/PcbDraw/plot.py
|
||||
|
|
@ -320,4 +322,82 @@ index 9dc45ba9..8df84469 100644
|
|||
elif svg_element.tag == "circle":
|
||||
# Convert circle to path
|
||||
att = svg_element.attrib
|
||||
```
|
||||
|
||||
## 2023-03-30 Removed the tolerance look-up, now using electro_grammar
|
||||
|
||||
So now *unit.py* is in charge of returning the tolerance.
|
||||
Note that we still use a field, but in a very ridiculous way because we add it to the value, to then separate it.
|
||||
|
||||
```diff
|
||||
diff --git a/kibot/PcbDraw/plot.py b/kibot/PcbDraw/plot.py
|
||||
index 23b7d31f..65fbea66 100644
|
||||
--- a/kibot/PcbDraw/plot.py
|
||||
+++ b/kibot/PcbDraw/plot.py
|
||||
@@ -938,21 +938,20 @@ class PlotComponents(PlotInterface):
|
||||
return
|
||||
|
||||
def _get_resistance_from_value(self, value: str) -> Tuple[Decimal, str]:
|
||||
- res, tolerance = None, str(GS.global_default_resistor_tolerance)+"%"
|
||||
- value_l = value.split(" ", maxsplit=1)
|
||||
+ res, tolerance = None, None
|
||||
try:
|
||||
- res = read_resistance(value_l[0])
|
||||
+ res, tolerance = read_resistance(value)
|
||||
except ValueError:
|
||||
- raise UserWarning(f"Invalid resistor value {value_l[0]}")
|
||||
- if len(value_l) > 1:
|
||||
- t_string = value_l[1].strip().replace(" ", "")
|
||||
- if "%" in t_string:
|
||||
- s = self._plotter.get_style("tht-resistor-band-colors")
|
||||
- if not isinstance(s, dict):
|
||||
- raise RuntimeError(f"Invalid style specified, tht-resistor-band-colors should be dictionary, got {type(s)}")
|
||||
- if t_string.strip() not in s:
|
||||
- raise UserWarning(f"Invalid resistor tolerance {value_l[1]}")
|
||||
- tolerance = t_string
|
||||
+ raise UserWarning(f"Invalid resistor value {value}")
|
||||
+ if tolerance is None:
|
||||
+ tolerance = GS.global_default_resistor_tolerance
|
||||
+ tolerance = str(tolerance)+"%"
|
||||
+ s = self._plotter.get_style("tht-resistor-band-colors")
|
||||
+ if not isinstance(s, dict):
|
||||
+ raise RuntimeError(f"Invalid style specified, tht-resistor-band-colors should be dictionary, got {type(s)}")
|
||||
+ if tolerance not in s:
|
||||
+ raise UserWarning(f"Invalid resistor tolerance {tolerance}")
|
||||
+ tolerance = "5%"
|
||||
return res, tolerance
|
||||
|
||||
|
||||
@@ -1113,7 +1112,7 @@ class PcbPlotter():
|
||||
prop = footprint.GetProperties()
|
||||
tol = next(filter(lambda x: x, map(prop.get, GS.global_field_tolerance)), None)
|
||||
if tol:
|
||||
- value = value+' '+tol
|
||||
+ value = value+' '+tol.strip()
|
||||
ref = footprint.GetReference().strip()
|
||||
center = footprint.GetPosition()
|
||||
orient = math.radians(footprint.GetOrientation().AsDegrees())
|
||||
diff --git a/kibot/PcbDraw/unit.py b/kibot/PcbDraw/unit.py
|
||||
index 2fad683c..0c5dfcab 100644
|
||||
--- a/kibot/PcbDraw/unit.py
|
||||
+++ b/kibot/PcbDraw/unit.py
|
||||
@@ -1,10 +1,9 @@
|
||||
# Author: Salvador E. Tropea
|
||||
# License: MIT
|
||||
-from decimal import Decimal
|
||||
from ..bom.units import comp_match
|
||||
|
||||
|
||||
-def read_resistance(value: str) -> Decimal:
|
||||
+def read_resistance(value: str):
|
||||
"""
|
||||
Given a string, try to parse resistance and return it as Ohms (Decimal)
|
||||
|
||||
@@ -13,5 +12,4 @@ def read_resistance(value: str) -> Decimal:
|
||||
res = comp_match(value, 'R')
|
||||
if res is None:
|
||||
raise ValueError(f"Cannot parse '{value}' to resistance")
|
||||
- v, mul, uni = res
|
||||
- return Decimal(str(v))*Decimal(str(mul[0]))
|
||||
+ return res.get_decimal(), res.get_extra('tolerance')
|
||||
```
|
||||
|
|
|
|||
|
|
@ -938,21 +938,20 @@ class PlotComponents(PlotInterface):
|
|||
return
|
||||
|
||||
def _get_resistance_from_value(self, value: str) -> Tuple[Decimal, str]:
|
||||
res, tolerance = None, str(GS.global_default_resistor_tolerance)+"%"
|
||||
value_l = value.split(" ", maxsplit=1)
|
||||
res, tolerance = None, None
|
||||
try:
|
||||
res = read_resistance(value_l[0])
|
||||
res, tolerance = read_resistance(value)
|
||||
except ValueError:
|
||||
raise UserWarning(f"Invalid resistor value {value_l[0]}")
|
||||
if len(value_l) > 1:
|
||||
t_string = value_l[1].strip().replace(" ", "")
|
||||
if "%" in t_string:
|
||||
s = self._plotter.get_style("tht-resistor-band-colors")
|
||||
if not isinstance(s, dict):
|
||||
raise RuntimeError(f"Invalid style specified, tht-resistor-band-colors should be dictionary, got {type(s)}")
|
||||
if t_string.strip() not in s:
|
||||
raise UserWarning(f"Invalid resistor tolerance {value_l[1]}")
|
||||
tolerance = t_string
|
||||
raise UserWarning(f"Invalid resistor value {value}")
|
||||
if tolerance is None:
|
||||
tolerance = GS.global_default_resistor_tolerance
|
||||
tolerance = str(tolerance)+"%"
|
||||
s = self._plotter.get_style("tht-resistor-band-colors")
|
||||
if not isinstance(s, dict):
|
||||
raise RuntimeError(f"Invalid style specified, tht-resistor-band-colors should be dictionary, got {type(s)}")
|
||||
if tolerance not in s:
|
||||
raise UserWarning(f"Invalid resistor tolerance {tolerance}")
|
||||
tolerance = "5%"
|
||||
return res, tolerance
|
||||
|
||||
|
||||
|
|
@ -1113,7 +1112,7 @@ class PcbPlotter():
|
|||
prop = footprint.GetProperties()
|
||||
tol = next(filter(lambda x: x, map(prop.get, GS.global_field_tolerance)), None)
|
||||
if tol:
|
||||
value = value+' '+tol
|
||||
value = value+' '+tol.strip()
|
||||
ref = footprint.GetReference().strip()
|
||||
center = footprint.GetPosition()
|
||||
orient = math.radians(footprint.GetOrientation().AsDegrees())
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
# Author: Salvador E. Tropea
|
||||
# License: MIT
|
||||
from decimal import Decimal
|
||||
from ..bom.units import comp_match
|
||||
|
||||
|
||||
def read_resistance(value: str) -> Decimal:
|
||||
def read_resistance(value: str):
|
||||
"""
|
||||
Given a string, try to parse resistance and return it as Ohms (Decimal)
|
||||
|
||||
|
|
@ -13,5 +12,4 @@ def read_resistance(value: str) -> Decimal:
|
|||
res = comp_match(value, 'R')
|
||||
if res is None:
|
||||
raise ValueError(f"Cannot parse '{value}' to resistance")
|
||||
v, mul, uni = res
|
||||
return Decimal(str(v))*Decimal(str(mul[0]))
|
||||
return res.get_decimal(), res.get_extra('tolerance')
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020-2022 Salvador E. Tropea
|
||||
# Copyright (c) 2020-2022 Instituto Nacional de Tecnología Industrial
|
||||
# Copyright (c) 2020-2023 Salvador E. Tropea
|
||||
# Copyright (c) 2020-2023 Instituto Nacional de Tecnología Industrial
|
||||
# Copyright (c) 2016-2020 Oliver Henry Walters (@SchrodingersGat)
|
||||
# License: MIT
|
||||
# Project: KiBot (formerly KiPlot)
|
||||
|
|
@ -12,7 +12,7 @@ All the logic to convert a list of components into the rows and columns used to
|
|||
import locale
|
||||
from copy import deepcopy
|
||||
from math import ceil
|
||||
from .units import compare_values, comp_match, get_last_warning
|
||||
from .units import compare_values, comp_match
|
||||
from .bom_writer import write_bom
|
||||
from .columnlist import ColumnList
|
||||
from ..misc import DNF, W_FIELDCONF, W_MISSFPINFO
|
||||
|
|
@ -382,14 +382,7 @@ def get_value_sort(comp, fallback_ref=False):
|
|||
""" Try to better sort R, L and C components """
|
||||
res = comp.value_sort
|
||||
if res:
|
||||
value, (mult, mult_s), unit = res
|
||||
if comp.ref_prefix in "CL":
|
||||
# femto Farads
|
||||
value = "{0:15d}".format(int(value * 1e15 * mult + 0.1))
|
||||
else:
|
||||
# milli Ohms
|
||||
value = "{0:15d}".format(int(value * 1000 * mult + 0.1))
|
||||
return value
|
||||
return res.get_sortable()
|
||||
if fallback_ref:
|
||||
return comp.ref_prefix + "{0:15d}".format(_suffix_to_num(comp.ref_suffix))
|
||||
return comp.value
|
||||
|
|
@ -397,14 +390,11 @@ def get_value_sort(comp, fallback_ref=False):
|
|||
|
||||
def normalize_value(c, decimal_point):
|
||||
if c.value_sort is None:
|
||||
return c.value
|
||||
value, (mult, mult_s), unit = c.value_sort
|
||||
ivalue = int(value)
|
||||
if value == ivalue:
|
||||
value = ivalue
|
||||
elif decimal_point:
|
||||
value = str(value).replace('.', decimal_point)
|
||||
return '{} {}{}'.format(value, mult_s, unit)
|
||||
return c.value.strip()
|
||||
value = str(c.value_sort)
|
||||
if decimal_point:
|
||||
value = value.replace('.', decimal_point)
|
||||
return value
|
||||
|
||||
|
||||
def compute_multiple_stats(cfg, groups):
|
||||
|
|
@ -443,14 +433,6 @@ def group_components(cfg, components):
|
|||
# Cache the value used to sort
|
||||
if c.ref_prefix in RLC_PREFIX and c.value.lower() not in DNF:
|
||||
c.value_sort = comp_match(c.value, c.ref_prefix, c.ref)
|
||||
if c.value_sort is None and (' ' in c.value):
|
||||
# Try with the data before a space
|
||||
value = c.value.split(' ')[0]
|
||||
value_sort = comp_match(value, c.ref_prefix)
|
||||
if value_sort is not None:
|
||||
c.value_sort = value_sort
|
||||
extra = ', only for sorting purposes' if not cfg.normalize_values else ''
|
||||
logger.warning(get_last_warning() + "Using `{}` for {} instead{}".format(value, c.ref, extra))
|
||||
else:
|
||||
c.value_sort = None
|
||||
# Try to add the component to an existing group
|
||||
|
|
|
|||
|
|
@ -0,0 +1,218 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2023 Salvador E. Tropea
|
||||
# Copyright (c) 2023 Instituto Nacional de Tecnología Industrial
|
||||
# License: MIT
|
||||
# Project: KiBot (formerly KiPlot)
|
||||
|
||||
from decimal import Decimal
|
||||
from lark import Lark, Transformer
|
||||
import os
|
||||
from ..gs import GS
|
||||
from .. import log
|
||||
|
||||
logger = log.get_logger()
|
||||
# Metric to imperial package sizes
|
||||
TO_IMPERIAL = {'0402': '01005',
|
||||
'0603': '0201',
|
||||
'1005': '0402',
|
||||
'1608': '0603',
|
||||
'2012': '0805',
|
||||
'2520': '1008',
|
||||
'3216': '1206',
|
||||
'3225': '1210',
|
||||
'4516': '1806',
|
||||
'4532': '1812',
|
||||
'5025': '2010',
|
||||
'6332': '2512'}
|
||||
parser = None
|
||||
|
||||
|
||||
class ComponentTransformer(Transformer):
|
||||
""" Transforms a tree parsed by Lark to the electro-grammar dict """
|
||||
def __init__(self):
|
||||
self.parsed = {}
|
||||
# Extra information, not in the original lib and needed for internal purposes
|
||||
self.extra = {}
|
||||
|
||||
def value3(self, d, type):
|
||||
""" VALUE [METRIC_PREFIX [MANTISSA]] """
|
||||
v = Decimal(d[0])
|
||||
c = len(d)
|
||||
if c >= 3:
|
||||
# We have something like 2n2
|
||||
dec = d[2]
|
||||
c_dec = len(dec)
|
||||
v += Decimal(dec)/(Decimal(10)*c_dec)
|
||||
self.extra['val'] = v
|
||||
if c >= 2:
|
||||
# Metric prefix
|
||||
v *= d[1]
|
||||
self.extra['mult'] = d[1]
|
||||
else:
|
||||
self.extra['mult'] = Decimal(1)
|
||||
v = float(v)
|
||||
self.parsed[type] = v
|
||||
return v
|
||||
|
||||
def value2(self, d, type):
|
||||
""" VALUE [MANTISSA] """
|
||||
v = float(d[0])
|
||||
c = len(d)
|
||||
if c >= 2:
|
||||
# We have something like 3V3
|
||||
dec = d[1]
|
||||
c_dec = len(dec)
|
||||
v += float(dec)/(10.0*c_dec)
|
||||
self.parsed[type] = v
|
||||
return v
|
||||
|
||||
def value1(self, d, type):
|
||||
""" VALUE """
|
||||
v = float(d[0])
|
||||
iv = int(d[0])
|
||||
if iv == v:
|
||||
v = iv
|
||||
self.parsed[type] = v
|
||||
return v
|
||||
|
||||
def tolerance(self, d):
|
||||
return self.value1(d, 'tolerance')
|
||||
|
||||
def voltage_rating(self, d):
|
||||
return self.value2(d, 'voltage_rating')
|
||||
|
||||
def temp_coef(self, d):
|
||||
c_len = len(d)
|
||||
if c_len == 3:
|
||||
# Class 2: i.e. X7R
|
||||
v = d[0].value+d[1].value+d[2].value
|
||||
else:
|
||||
# Class 1: i.e. C0G
|
||||
v = d[0].type
|
||||
self.parsed['characteristic'] = v.upper()
|
||||
return v
|
||||
|
||||
def power_rating(self, d):
|
||||
if len(d) == 1:
|
||||
# 1 W
|
||||
v = float(d[0])
|
||||
elif d[0].type == 'INT':
|
||||
# 1/4 W
|
||||
v = float(d[0].value)/float(d[1].value)
|
||||
else:
|
||||
# 250 mW
|
||||
v = float(Decimal(d[0].value)*d[1])
|
||||
self.parsed['power_rating'] = v
|
||||
return v
|
||||
|
||||
def color(self, d):
|
||||
c = d[0].value.lower()
|
||||
self.parsed['color'] = c
|
||||
return c
|
||||
|
||||
def set_type(self, type, d):
|
||||
self.parsed['type'] = type
|
||||
return d
|
||||
|
||||
# Package size
|
||||
def imperial_size(self, d):
|
||||
s = d[0].value
|
||||
self.parsed['size'] = s
|
||||
return s
|
||||
|
||||
def unambigious_metric_size(self, d):
|
||||
s = TO_IMPERIAL[d[0].value]
|
||||
self.parsed['size'] = s
|
||||
return s
|
||||
|
||||
metric_size_base = unambigious_metric_size
|
||||
|
||||
# RLC
|
||||
def resistance(self, d):
|
||||
return self.value3(d, 'resistance')
|
||||
|
||||
resistance_no_r = resistance
|
||||
|
||||
def inductance(self, d):
|
||||
return self.value3(d, 'inductance')
|
||||
|
||||
inductance_no_henry = inductance
|
||||
|
||||
def capacitance(self, d):
|
||||
return self.value3(d, 'capacitance')
|
||||
|
||||
capacitance_no_farad = capacitance
|
||||
|
||||
# Known components
|
||||
def inductor(self, d):
|
||||
return self.set_type('inductor', d)
|
||||
|
||||
def capacitor(self, d):
|
||||
return self.set_type('capacitor', d)
|
||||
|
||||
def resistor(self, d):
|
||||
return self.set_type('resistor', d)
|
||||
|
||||
def led(self, d):
|
||||
return self.set_type('led', d)
|
||||
|
||||
# Metrix prefixes
|
||||
def giga(self, _):
|
||||
return Decimal('1e9')
|
||||
|
||||
def mega(self, _):
|
||||
return Decimal('1e6')
|
||||
|
||||
def kilo(self, _):
|
||||
return Decimal('1e3')
|
||||
|
||||
def unit(self, _):
|
||||
return Decimal(1)
|
||||
|
||||
def milli(self, _):
|
||||
return Decimal('1e-3')
|
||||
|
||||
def nano(self, _):
|
||||
return Decimal('1e-9')
|
||||
|
||||
def micro(self, _):
|
||||
return Decimal('1e-6')
|
||||
|
||||
def pico(self, _):
|
||||
return Decimal('1e-12')
|
||||
|
||||
def femto(self, _):
|
||||
return Decimal('1e-15')
|
||||
|
||||
def crap(self, v):
|
||||
if 'discarded' in self.extra:
|
||||
self.extra['discarded'].append(v[0].value)
|
||||
else:
|
||||
self.extra['discarded'] = [v[0].value]
|
||||
return None
|
||||
|
||||
|
||||
def initialize():
|
||||
global parser
|
||||
if parser is not None:
|
||||
return
|
||||
with open(os.path.join(GS.get_resource_path('parsers'), 'electro.lark'), 'rt') as f:
|
||||
g = f.read()
|
||||
parser = Lark(g, start='main') # , debug=DEBUG)
|
||||
|
||||
|
||||
def parse(text, with_extra=False):
|
||||
initialize()
|
||||
try:
|
||||
tree = parser.parse(text)
|
||||
except Exception as e:
|
||||
logger.debugl(2, str(e))
|
||||
return {}
|
||||
logger.debugl(3, tree.pretty())
|
||||
res_o = ComponentTransformer()
|
||||
res = res_o.transform(tree)
|
||||
logger.debugl(3, res)
|
||||
res = res_o.parsed
|
||||
if with_extra:
|
||||
res.update(res_o.extra)
|
||||
return res
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020 Salvador E. Tropea
|
||||
# Copyright (c) 2020 Instituto Nacional de Tecnología Industrial
|
||||
# Copyright (c) 2020-2023 Salvador E. Tropea
|
||||
# Copyright (c) 2020-2023 Instituto Nacional de Tecnología Industrial
|
||||
# Copyright (c) 2016-2020 Oliver Henry Walters (@SchrodingersGat)
|
||||
# License: MIT
|
||||
# Project: KiBot (formerly KiPlot)
|
||||
|
|
@ -13,10 +13,13 @@ e.g.
|
|||
0R1 = 0.1Ohm (Unit replaces decimal, different units)
|
||||
Oriented to normalize and sort R, L and C values.
|
||||
"""
|
||||
from decimal import Decimal
|
||||
import re
|
||||
import locale
|
||||
from math import log10
|
||||
from .. import log
|
||||
from ..misc import W_BADVAL1, W_BADVAL2, W_BADVAL3
|
||||
from ..misc import W_BADVAL1, W_BADVAL2, W_BADVAL3, W_BADVAL4
|
||||
from .electro_grammar import parse
|
||||
|
||||
logger = log.get_logger()
|
||||
|
||||
|
|
@ -30,25 +33,54 @@ PREFIX_GIGA = ["giga", "g"]
|
|||
|
||||
# All prefixes
|
||||
PREFIX_ALL = PREFIX_PICO + PREFIX_NANO + PREFIX_MICRO + PREFIX_MILLI + PREFIX_KILO + PREFIX_MEGA + PREFIX_GIGA
|
||||
MAX_POW_PREFIX = 9
|
||||
MIN_POW_PREFIX = -12
|
||||
PREFIXES = {-15: 'f', -12: 'p', -9: 'n', -6: u"µ", -3: 'm', 0: '', 3: 'k', 6: 'M', 9: 'G'}
|
||||
|
||||
# Common methods of expressing component units
|
||||
# Note: we match lowercase string, so both: Ω and Ω become the lowercase omega
|
||||
UNIT_R = ["r", "ohms", "ohm", u'\u03c9']
|
||||
UNIT_C = ["farad", "f"]
|
||||
UNIT_L = ["henry", "h"]
|
||||
OHMS = u"Ω"
|
||||
|
||||
UNIT_ALL = UNIT_R + UNIT_C + UNIT_L
|
||||
|
||||
GRAM_TYPES = {'inductor': 'L', 'capacitor': 'C', 'resistor': 'R', 'led': ''}
|
||||
# Compiled regex to match the values
|
||||
match = None
|
||||
# Current locale decimal point value
|
||||
decimal_point = None
|
||||
# Last warning
|
||||
last_warning = ''
|
||||
# Parser cache
|
||||
parser_cache = {}
|
||||
|
||||
|
||||
def get_last_warning():
|
||||
return last_warning
|
||||
class ParsedValue(object):
|
||||
def __init__(self, v, pow, unit, extra=None):
|
||||
# From a value that matched the regex
|
||||
ival = int(v)
|
||||
self.norm_val = int(v) if v == ival else v
|
||||
self.exp = pow
|
||||
self.unit = unit
|
||||
self.prefix = PREFIXES[pow]
|
||||
self.extra = extra
|
||||
|
||||
def __str__(self):
|
||||
return '{} {}{}'.format(self.norm_val, self.prefix, self.unit)
|
||||
|
||||
def get_sortable(self):
|
||||
mult = pow(10, self.exp)
|
||||
if self.unit in "FH":
|
||||
# femto Farads
|
||||
return "{0:15d}".format(int(self.norm_val * 1e15 * mult + 0.1))
|
||||
# milli Ohms
|
||||
return "{0:15d}".format(int(self.norm_val * 1000 * mult + 0.1))
|
||||
|
||||
def get_decimal(self):
|
||||
return Decimal(str(self.norm_val))*pow(10, Decimal(self.exp))
|
||||
|
||||
def get_extra(self, property):
|
||||
return self.extra.get(property) if self.extra else None
|
||||
|
||||
|
||||
def get_unit(unit, ref_prefix):
|
||||
|
|
@ -58,42 +90,54 @@ def get_unit(unit, ref_prefix):
|
|||
return "H"
|
||||
if ref_prefix == 'C':
|
||||
return "F"
|
||||
return u"Ω"
|
||||
return OHMS
|
||||
unit = unit.lower()
|
||||
if unit in UNIT_R:
|
||||
return u"Ω"
|
||||
return OHMS
|
||||
if unit in UNIT_C:
|
||||
return "F"
|
||||
if unit in UNIT_L:
|
||||
return "H"
|
||||
|
||||
|
||||
def get_prefix(prefix):
|
||||
def get_prefix_simple(prefix):
|
||||
""" Return the (numerical) value of a given prefix """
|
||||
if not prefix:
|
||||
return 1, ''
|
||||
return 0
|
||||
# 'M' is mega, 'm' is milli
|
||||
if prefix != 'M':
|
||||
prefix = prefix.lower()
|
||||
if prefix in PREFIX_PICO:
|
||||
return 1.0e-12, 'p'
|
||||
return -12
|
||||
if prefix in PREFIX_NANO:
|
||||
return 1.0e-9, 'n'
|
||||
return -9
|
||||
if prefix in PREFIX_MICRO:
|
||||
return 1.0e-6, u"µ"
|
||||
return -6
|
||||
if prefix in PREFIX_MILLI:
|
||||
return 1.0e-3, 'm'
|
||||
return -3
|
||||
if prefix in PREFIX_KILO:
|
||||
return 1.0e3, 'k'
|
||||
return 3
|
||||
if prefix in PREFIX_MEGA:
|
||||
return 1.0e6, 'M'
|
||||
return 6
|
||||
if prefix in PREFIX_GIGA:
|
||||
return 1.0e9, 'G'
|
||||
return 9
|
||||
# Unknown, we shouldn't get here because the regex matched
|
||||
# BUT: I found that sometimes unexpected things happen, like mu matching micro and then we reaching this code
|
||||
# Now is fixed, but I can't be sure some bizarre case is overlooked
|
||||
logger.error('Unknown prefix, please report')
|
||||
return 1, ''
|
||||
return 0
|
||||
|
||||
|
||||
def get_prefix(val, prefix):
|
||||
pow = get_prefix_simple(prefix)
|
||||
# Try to normalize it
|
||||
while val >= 1000.0 and pow < MAX_POW_PREFIX:
|
||||
val /= 1000.0
|
||||
pow += 3
|
||||
while val < 1.0 and pow > MIN_POW_PREFIX:
|
||||
val *= 1000.0
|
||||
pow -= 3
|
||||
return val, pow
|
||||
|
||||
|
||||
def group_string(group): # Return a reg-ex string for a list of values
|
||||
|
|
@ -104,14 +148,27 @@ def match_string():
|
|||
return r"(\d*\.?\d*)\s*(" + group_string(PREFIX_ALL) + ")*(" + group_string(UNIT_ALL) + r")*(\d*)$"
|
||||
|
||||
|
||||
def value_from_grammar(r):
|
||||
""" Convert a result parsed by the Lark grammar to a ParsedResult object """
|
||||
val = r.get('val')
|
||||
if not val:
|
||||
return None
|
||||
# Create an object with the result
|
||||
val, pow = get_prefix(float(val), PREFIXES[int(log10(r['mult']))])
|
||||
parsed = ParsedValue(val, pow, get_unit(GRAM_TYPES[r['type']], ''), r)
|
||||
return parsed
|
||||
|
||||
|
||||
def comp_match(component, ref_prefix, ref=None):
|
||||
"""
|
||||
Return a normalized value and units for a given component value string
|
||||
e.g. comp_match('10R2') returns (10, R)
|
||||
e.g. comp_match('3.3mOhm') returns (0.0033, R)
|
||||
Also tries to separate extra data, i.e. tolerance, using a complex parser
|
||||
"""
|
||||
global last_warning
|
||||
original = component
|
||||
global parser_cache
|
||||
parsed = parser_cache.get(original+ref_prefix)
|
||||
if parsed:
|
||||
return parsed
|
||||
# Remove useless spaces
|
||||
component = component.strip()
|
||||
# ~ is the same as empty for KiCad
|
||||
|
|
@ -128,6 +185,7 @@ def comp_match(component, ref_prefix, ref=None):
|
|||
if decimal_point:
|
||||
component = component.replace(decimal_point, ".")
|
||||
|
||||
with_commas = component
|
||||
# Remove any commas
|
||||
component = component.strip().replace(",", "")
|
||||
|
||||
|
|
@ -140,13 +198,22 @@ def comp_match(component, ref_prefix, ref=None):
|
|||
where = ' in {}'.format(ref) if ref is not None else ''
|
||||
result = match.match(component)
|
||||
if not result:
|
||||
last_warning = W_BADVAL1
|
||||
logger.warning(W_BADVAL1 + "Malformed value: `{}` (no match{})".format(original, where))
|
||||
return None
|
||||
# Failed with the regex, try with the parser
|
||||
result = parse(ref_prefix[0]+' '+with_commas, with_extra=True)
|
||||
if result:
|
||||
result = value_from_grammar(result)
|
||||
if result and result.get_extra('discarded'):
|
||||
discarded = " ".join(list(map(lambda x: '`'+x+'`', result.get_extra('discarded'))))
|
||||
logger.warning(W_BADVAL4 + "Malformed value: `{}` (discarded: {}{})".format(original, discarded, where))
|
||||
if not result:
|
||||
logger.warning(W_BADVAL1 + "Malformed value: `{}` (no match{})".format(original, where))
|
||||
return None
|
||||
# Cache the result
|
||||
parser_cache[original+ref_prefix] = result
|
||||
return result
|
||||
|
||||
value, prefix, units, post = result.groups()
|
||||
if value == '.':
|
||||
last_warning = W_BADVAL2
|
||||
logger.warning(W_BADVAL2 + "Malformed value: `{}` (reduced to decimal point{})".format(original, where))
|
||||
return None
|
||||
if value == '':
|
||||
|
|
@ -158,7 +225,6 @@ def comp_match(component, ref_prefix, ref=None):
|
|||
# We will also have a trailing number
|
||||
if post:
|
||||
if "." in value:
|
||||
last_warning = W_BADVAL3
|
||||
logger.warning(W_BADVAL3 + "Malformed value: `{}` (unit split, but contains decimal point{})".
|
||||
format(original, where))
|
||||
return None
|
||||
|
|
@ -168,35 +234,21 @@ def comp_match(component, ref_prefix, ref=None):
|
|||
else:
|
||||
val = float(value)
|
||||
|
||||
# Return all the data, let the caller join it
|
||||
return (val, get_prefix(prefix), get_unit(units, ref_prefix))
|
||||
# Create an object with the result
|
||||
val, pow = get_prefix(val, prefix)
|
||||
parsed = ParsedValue(val, pow, get_unit(units, ref_prefix))
|
||||
# Cache the result
|
||||
parser_cache[original+ref_prefix] = parsed
|
||||
return parsed
|
||||
|
||||
|
||||
def compare_values(c1, c2):
|
||||
""" Compare two values """
|
||||
|
||||
# These are the results from comp_match()
|
||||
r1 = c1.value_sort
|
||||
r2 = c2.value_sort
|
||||
|
||||
# If they can't be parsed use the value
|
||||
if not r1 or not r2:
|
||||
return False
|
||||
|
||||
# Join the data to compare
|
||||
(v1, (p1, ps1), u1) = r1
|
||||
(v2, (p2, ps2), u2) = r2
|
||||
|
||||
v1 = "{0:.15f}".format(v1 * 1.0 * p1)
|
||||
v2 = "{0:.15f}".format(v2 * 1.0 * p2)
|
||||
|
||||
if v1 == v2:
|
||||
# Values match
|
||||
if u1 == u2:
|
||||
return True # Units match
|
||||
# No longer possible because now we use the prefix to determine absent units
|
||||
# if not u1:
|
||||
# return True # No units for component 1
|
||||
# if not u2:
|
||||
# return True # No units for component 2
|
||||
|
||||
return False
|
||||
return c1.value.strip() == c2.value.strip()
|
||||
# Compare the normalized representation, i.e. 3300 == 3k3 == 3.3 k
|
||||
return str(r1) == str(r2)
|
||||
|
|
|
|||
|
|
@ -272,6 +272,7 @@ W_BADRES = '(W123) '
|
|||
W_RESVALISSUE = '(W124) '
|
||||
W_RES3DNAME = '(W125) '
|
||||
W_ESCINV = '(W126) '
|
||||
W_BADVAL4 = '(W127) '
|
||||
# Somehow arbitrary, the colors are real, but can be different
|
||||
PCB_MAT_COLORS = {'fr1': "937042", 'fr2': "949d70", 'fr3': "adacb4", 'fr4': "332B16", 'fr5': "6cc290"}
|
||||
PCB_FINISH_COLORS = {'hal': "8b898c", 'hasl': "8b898c", 'imag': "8b898c", 'enig': "cfb96e", 'enepig': "cfb96e",
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
# Copyright (c) 2020-2023 Instituto Nacional de Tecnología Industrial
|
||||
# License: GPL-3.0
|
||||
# Project: KiBot (formerly KiPlot)
|
||||
from decimal import Decimal
|
||||
from fnmatch import fnmatch
|
||||
import os
|
||||
import re
|
||||
|
|
@ -341,11 +342,23 @@ class Base3DOptions(VariantOptions):
|
|||
return name
|
||||
r_len = float(m.group(1))
|
||||
# THT Resistor that we want to add colors
|
||||
# Check the tolerance
|
||||
# Check the value
|
||||
res = comp_match(c.value, c.ref_prefix, c.ref)
|
||||
if res is None:
|
||||
return name
|
||||
val = res.get_decimal()
|
||||
if val < Decimal('0.01'):
|
||||
logger.warning(W_BADRES+'Resistor {} out of range, minimum value is 10 mOhms'.format(c.ref))
|
||||
return name
|
||||
val_str = "{0:.0f}".format(val*100)
|
||||
# Check the tolerance (from the schematic fields)
|
||||
tol = next(filter(lambda x: x, map(c.get_field_value, GS.global_field_tolerance)), None)
|
||||
if not tol:
|
||||
tol = GS.global_default_resistor_tolerance
|
||||
logger.warning(W_BADTOL+'Missing tolerance for {}, using {}%'.format(c.ref, tol))
|
||||
# Try using the parsed value (i.e. Value="12k 1%")
|
||||
tol = res.get_extra('tolerance')
|
||||
if not tol:
|
||||
tol = GS.global_default_resistor_tolerance
|
||||
logger.warning(W_BADTOL+'Missing tolerance for {}, using {}%'.format(c.ref, tol))
|
||||
else:
|
||||
tol = tol.strip()
|
||||
if tol[-1] == '%':
|
||||
|
|
@ -359,15 +372,6 @@ class Base3DOptions(VariantOptions):
|
|||
logger.warning(W_BADTOL+'Unknown tolerance for {}: `{}`'.format(c.ref, tol))
|
||||
return name
|
||||
tol_color = TOL_COLORS[tol]
|
||||
# Check the value
|
||||
res = comp_match(c.value, c.ref_prefix, c.ref)
|
||||
if res is None:
|
||||
return name
|
||||
val = res[0]*res[1][0]
|
||||
if val < 0.01:
|
||||
logger.warning(W_BADRES+'Resistor {} out of range, minimum value is 10 mOhms'.format(c.ref))
|
||||
return name
|
||||
val_str = "{0:.0f}".format(val*100)
|
||||
# Find how many bars we'll use
|
||||
if tol < 5:
|
||||
# Use 5 bars for 2 % tol or better
|
||||
|
|
|
|||
|
|
@ -0,0 +1,214 @@
|
|||
//*****************************************************************************
|
||||
//
|
||||
// Copyright (c) 2023 Salvador E. Tropea
|
||||
// Copyright (c) 2023 Instituto Nacional de Tecnologia Industrial
|
||||
// Copyright (c) 2017-2018 Kaspar Emanuel
|
||||
//
|
||||
// LICENSE: MIT
|
||||
//
|
||||
// Grammar to parse electronic components.
|
||||
// Can currently parse resistors, capacitors, inductors and LEDs.
|
||||
// Is strongly based on "electro-grammar" created by Kaspar Emanuel.
|
||||
// This description was coded to be used with Lark, a Python tool.
|
||||
// Is an ambiguous description, so it needs the Early algorithm.
|
||||
// Unlike the original version by Kaspar, implemented using Nearly, this
|
||||
// grammar handles the stuff to ignore in the grammar description, not retrying
|
||||
// in the parser. I think Lark can't retry. This makes things a little bit more
|
||||
// complicated, and harder to debug.
|
||||
//
|
||||
//*****************************************************************************
|
||||
%import common.INT
|
||||
%import common.NUMBER
|
||||
%import common.WS
|
||||
%import common.CNAME
|
||||
|
||||
?main: capacitor | inductor | resistor | led
|
||||
|
||||
// All we ignore
|
||||
crap: CNAME | NUMBER
|
||||
// Whitespace and separators
|
||||
_WS: WS
|
||||
_SEP: _WS | /[,;]/
|
||||
|
||||
//************************************
|
||||
//************ Capacitors ************
|
||||
//************************************
|
||||
|
||||
// When we know this is a capacitor we don't need the units
|
||||
capacitor: _c_specs capacitance _c_specs
|
||||
| _CAP _c_specs (capacitance_no_farad | capacitance)? (_c_specs | c_spec)
|
||||
|
||||
_c_specs: ((c_spec _SEP)+ | (_SEP c_spec)+ | _SEP)*
|
||||
?c_spec: tolerance | temp_coef | voltage_rating | package_size | crap
|
||||
|
||||
// Give priority to things like "25 V" to avoid separating the "V" as crap
|
||||
voltage_rating.6: NUMBER _WS? _VOLT INT?
|
||||
|
||||
// See https://en.wikipedia.org/wiki/Ceramic_capacitor#Class_1_ceramic_capacitor
|
||||
// https://en.wikipedia.org/wiki/Ceramic_capacitor#Class_2_ceramic_capacitor
|
||||
temp_coef: _class1 | _class2
|
||||
_class1: P100 | C0G | N33 | N75 | N150 | N220 | N330 | N470 | N750 | N1000 | N1500
|
||||
_class2: /[XYZ]/i "4".."9" /[PRSTUV]/i
|
||||
|
||||
tolerance: (_PLUSMINUS _WS?)? NUMBER _WS? "%"
|
||||
|
||||
capacitance.10: (_capacitance_no_farad | NUMBER) _WS? _FARAD
|
||||
capacitance_no_farad.10: _capacitance_no_farad
|
||||
_capacitance_no_farad: INT _c_metric_prefix INT?
|
||||
| NUMBER _WS? _c_metric_prefix
|
||||
_c_metric_prefix: milli | micro | nano | pico
|
||||
|
||||
//***********************************
|
||||
//************ Inductors ************
|
||||
//***********************************
|
||||
|
||||
// When we know this is an inductor we don't need the units
|
||||
inductor: _l_specs inductance _l_specs
|
||||
| _IND _l_specs (inductance_no_henry | inductance)? (_l_specs | l_spec)
|
||||
|
||||
_l_specs: ((l_spec _SEP)+ | (_SEP l_spec)+ | _SEP)*
|
||||
?l_spec: tolerance | voltage_rating | package_size | crap
|
||||
|
||||
inductance: _inductance_no_henry _WS? _HENRY
|
||||
inductance_no_henry: _inductance_no_henry
|
||||
_inductance_no_henry: NUMBER _WS? _l_metric_prefix? INT?
|
||||
_l_metric_prefix: milli | micro | nano | pico
|
||||
|
||||
//***********************************
|
||||
//************ Resistors ************
|
||||
//***********************************
|
||||
|
||||
// When we know this is a resistor we don't need the units
|
||||
resistor: _r_specs resistance _r_specs
|
||||
| _RES _r_specs (resistance_no_r | resistance)? (_r_specs | r_spec)
|
||||
|
||||
_r_specs: ((r_spec _SEP)+ | (_SEP r_spec)+ | _SEP)*
|
||||
?r_spec: tolerance | power_rating | package_size | crap
|
||||
|
||||
power_rating: _power_rating_decimal | _power_rating_fraction
|
||||
_power_rating_fraction: INT "/" INT _WS? _WATTS
|
||||
_power_rating_decimal: NUMBER _WS? _power_metric_prefix? _WS? _WATTS
|
||||
_power_metric_prefix: giga | mega | kilo | milli | micro | nano | pico | femto
|
||||
|
||||
resistance.9: NUMBER _WS? (_r_metric_prefix INT? (_WS? _OHM)? | _OHM)
|
||||
// Just a number, no R, K, ohm etc.
|
||||
resistance_no_r.9: NUMBER
|
||||
_r_metric_prefix: giga | mega | kilo | unit | milli | micro
|
||||
|
||||
//******************************
|
||||
//************ LEDs ************
|
||||
//******************************
|
||||
|
||||
led: _led_specs _LED (_SEP _led_specs _led_spec?)?
|
||||
|
||||
_led_specs: ((_led_spec _SEP)+ | (_SEP _led_spec)+ | _SEP)*
|
||||
_led_spec: package_size | color | crap
|
||||
|
||||
!color: "red"i
|
||||
| "green"i
|
||||
| "blue"i
|
||||
| "yellow"i
|
||||
| "orange"i
|
||||
| "white"i
|
||||
| "amber"i
|
||||
| "cyan"i
|
||||
| "purple"i
|
||||
| "yellow" WS "green"
|
||||
|
||||
//******************************
|
||||
//************ Size ************
|
||||
//******************************
|
||||
|
||||
// Sizes looks like numbers and resistors doesn't need units, things like 2512 are hard to differentiate from a
|
||||
// resistor value. So we use a high priority here
|
||||
?package_size.11: imperial_size | metric_size
|
||||
!imperial_size.11: IS01005 | IS0201 | IS0402 | IS0603 | IS0805 | IS1008 | IS1206 | IS1210 | IS1806 | IS2010 | IS2512
|
||||
?metric_size.11: metric_size_base _WS _METRIC
|
||||
| _METRIC _WS metric_size_base
|
||||
| unambigious_metric_size
|
||||
// Metric sizes, with names to avoid anonymous
|
||||
!unambigious_metric_size.11: MS1005 | MS1608 | MS2012 | MS2520 | MS3216 | MS3225 | MS4516 | MS5025 | MS6332
|
||||
!metric_size_base.11: unambigious_metric_size | MS0402 | MS0603
|
||||
|
||||
//******************************
|
||||
//****** Metric prefixes *******
|
||||
//******************************
|
||||
|
||||
// !exa: "E" | "exa"i
|
||||
// !peta: "P" | "peta"i
|
||||
// !tera: "T" | "tera"i
|
||||
!giga: "G" | "gig"i "a"i?
|
||||
!mega: "M" | "meg"i "a"i?
|
||||
!kilo: "K"i "ilo"i?
|
||||
!unit: "R"i
|
||||
// !hecto: "h" | "hecto"i
|
||||
// !deci: "d" | "deci"i
|
||||
// !centi: "c" | "centi"i
|
||||
!milli: "m" | "milli"i
|
||||
!micro: "U"i
|
||||
| "\u03BC"
|
||||
| "\u00B5"
|
||||
| "𝛍"
|
||||
| "𝜇"
|
||||
| "𝝁"
|
||||
| "𝝻"
|
||||
| "𝞵"
|
||||
| /micro/i
|
||||
!nano: "N"i | "nan"i "o"i?
|
||||
!pico: "P"i "ico"i?
|
||||
!femto: "f" | "femto"i
|
||||
// !atto: "a" | "atto"i
|
||||
|
||||
//******************************
|
||||
//****** Named terminals *******
|
||||
//******************************
|
||||
// Components
|
||||
_CAP: ("CAPACITOR"i | "CAPA"i | "C"i "AP"i?) _SEP
|
||||
_RES: ("RESISTOR"i | "RES"i | "R"i) _SEP
|
||||
_IND: ("IND"i "UCTOR"i? | "L"i) _SEP
|
||||
_LED: "LED"i
|
||||
// Units
|
||||
_FARAD: "F"i "arad"i?
|
||||
_OHM: "ohm"i "s"i? | "Ω" | "Ω"
|
||||
_HENRY: "h"i "enry"i?
|
||||
_VOLT: "Volt"i "s"i? | "V"i
|
||||
_WATTS: "W"i "atts"i?
|
||||
// Used for percent
|
||||
_PLUSMINUS: "+/-" | "±" | "+-"
|
||||
// Size
|
||||
IS01005: "01005"
|
||||
IS0201: "0201"
|
||||
IS0402: "0402"
|
||||
IS0603: "0603"
|
||||
IS0805: "0805"
|
||||
IS1008: "1008"
|
||||
IS1206: "1206"
|
||||
IS1210: "1210"
|
||||
IS1806: "1806"
|
||||
IS2010: "2010"
|
||||
IS2512: "2512"
|
||||
_METRIC: "METRIC"i
|
||||
MS1005: "1005"
|
||||
MS1608: "1608"
|
||||
MS2012: "2012"
|
||||
MS2520: "2520"
|
||||
MS3216: "3216"
|
||||
MS3225: "3225"
|
||||
MS4516: "4516"
|
||||
MS5025: "5025"
|
||||
MS6332: "6332"
|
||||
MS0402: "0402"
|
||||
MS0603: "0603"
|
||||
// Capacitor temp. coef. classes
|
||||
P100: "P100"i "/" "M7G"i | "M7G"i "/" "P100"i | "P100"i | "M7G"i
|
||||
N33: "N33"i "/" "H2G"i | "H2G"i "/" "N33"i | "N33"i | "H2G"i
|
||||
N75: "N75"i "/" "L2G"i | "L2G"i "/" "N75"i | "N75"i | "L2G"i
|
||||
N150: "N150"i "/" "P2H"i | "P2H"i "/" "N150"i | "N150"i | "P2H"i
|
||||
N220: "N220"i "/" "R2H"i | "R2H"i "/" "N220"i | "N220"i | "R2H"i
|
||||
N330: "N330"i "/" "S2H"i | "S2H"i "/" "N330"i | "N330"i | "S2H"i
|
||||
N470: "N470"i "/" "T2H"i | "T2H"i "/" "N470"i | "N470"i | "T2H"i
|
||||
N750: "N750"i "/" "U2J"i | "U2J"i "/" "N750"i | "N750"i | "U2J"i
|
||||
N1000: "N1000"i "/" "Q3K"i | "Q3K"i "/" "N1000"i | "N1000"i | "Q3K"i
|
||||
N1500: "N1500"i "/" "P3K"i | "P3K"i "/" "N1500"i | "N1500"i | "P3K"i
|
||||
C0G: /C[O0]G/i "/" /NP[O0]/i | /NP[O0]/i "/" /C[O0]G/i | /C[O0]G/i | /NP[O0]/i
|
||||
|
||||
|
|
@ -39,6 +39,7 @@ exclude = experiments/kicad/v6/
|
|||
experiments/JLC/
|
||||
experiments/resistor_colors/
|
||||
experiments/EasyEDA/
|
||||
experiments/grammar
|
||||
kibot/mcpyrate/
|
||||
kibot/PcbDraw/
|
||||
kibot/PyPDF2/
|
||||
|
|
|
|||
|
|
@ -326,7 +326,7 @@
|
|||
(effects (font (size 1 1) (thickness 0.15)))
|
||||
(tstamp 8c217b04-361d-48af-b961-1d540df74a9f)
|
||||
)
|
||||
(fp_text value "0.01" (at 12.7 3.37) (layer "F.Fab")
|
||||
(fp_text value "0.01 5%" (at 12.7 3.37) (layer "F.Fab")
|
||||
(effects (font (size 1 1) (thickness 0.15)))
|
||||
(tstamp d228c21f-e790-45ba-aad5-43b7d6020d9a)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -684,7 +684,7 @@
|
|||
(in_bom yes) (on_board yes) (fields_autoplaced)
|
||||
(uuid bb38c897-d2f8-4276-a663-bb1f35f5db41)
|
||||
(property "Reference" "R26" (id 0) (at 92.71 101.9642 90))
|
||||
(property "Value" "0.01" (id 1) (at 92.71 104.5011 90))
|
||||
(property "Value" "0.01 5%" (id 1) (at 92.71 104.5011 90))
|
||||
(property "Footprint" "Resistor_THT:R_Axial_DIN0414_L11.9mm_D4.5mm_P25.40mm_Horizontal" (id 2) (at 92.71 108.458 90)
|
||||
(effects (font (size 1.27 1.27)) hide)
|
||||
)
|
||||
|
|
@ -1001,7 +1001,7 @@
|
|||
(reference "R25") (unit 1) (value "33k") (footprint "Resistor_THT:R_Axial_DIN0414_L11.9mm_D4.5mm_P20.32mm_Horizontal")
|
||||
)
|
||||
(path "/bb38c897-d2f8-4276-a663-bb1f35f5db41"
|
||||
(reference "R26") (unit 1) (value "0.01") (footprint "Resistor_THT:R_Axial_DIN0414_L11.9mm_D4.5mm_P25.40mm_Horizontal")
|
||||
(reference "R26") (unit 1) (value "0.01 5%") (footprint "Resistor_THT:R_Axial_DIN0414_L11.9mm_D4.5mm_P25.40mm_Horizontal")
|
||||
)
|
||||
(path "/9cfcad2d-3334-4d64-b2a9-fb405ef03985"
|
||||
(reference "R27") (unit 1) (value "0.12") (footprint "Resistor_THT:R_Axial_DIN0414_L11.9mm_D4.5mm_P5.08mm_Vertical")
|
||||
|
|
|
|||
|
|
@ -367,7 +367,7 @@
|
|||
(effects (font (size 1 1) (thickness 0.15)))
|
||||
(tstamp 8c217b04-361d-48af-b961-1d540df74a9f)
|
||||
)
|
||||
(fp_text value "0.01" (at 12.7 3.37) (layer "F.Fab")
|
||||
(fp_text value "0.01 5%" (at 12.7 3.37) (layer "F.Fab")
|
||||
(effects (font (size 1 1) (thickness 0.15)))
|
||||
(tstamp d228c21f-e790-45ba-aad5-43b7d6020d9a)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1082,7 +1082,7 @@
|
|||
(property "Reference" "R26" (at 92.71 101.9642 90)
|
||||
(effects (font (size 1.27 1.27)))
|
||||
)
|
||||
(property "Value" "0.01" (at 92.71 104.5011 90)
|
||||
(property "Value" "0.01 5%" (at 92.71 104.5011 90)
|
||||
(effects (font (size 1.27 1.27)))
|
||||
)
|
||||
(property "Footprint" "Resistor_THT:R_Axial_DIN0414_L11.9mm_D4.5mm_P25.40mm_Horizontal" (at 92.71 108.458 90)
|
||||
|
|
|
|||
|
|
@ -486,13 +486,13 @@ def int_bom_sort(test_dir, locale, dp):
|
|||
ref_column = header.index(REF_COLUMN_NAME)
|
||||
exp = ['C5', 'C6', 'C7', 'C8', 'C9', 'C10', 'C1', 'C2', 'C3', 'C4', 'C11', 'C12',
|
||||
'L2', 'L1', 'L3',
|
||||
'R5', 'R16', 'R12', 'R4', 'R9', 'R10', 'R3']
|
||||
'R5', 'R16', 'R12', 'R4', 'R13', 'R9', 'R10', 'R3']
|
||||
if dp == ',':
|
||||
exp += ['R2', 'R1', 'R8']
|
||||
else:
|
||||
# 8,2 k is interpreted as 82 k
|
||||
exp += ['R1', 'R2', 'R8']
|
||||
exp += ['R7', 'R11', 'R14', 'R13', 'R15']
|
||||
exp += ['R7', 'R11', 'R14', 'R15']
|
||||
check_kibom_test_netlist(rows, ref_column, 23, None, exp)
|
||||
# Check the sorting
|
||||
assert get_column(rows, ref_column) == exp
|
||||
|
|
|
|||
|
|
@ -16,7 +16,8 @@ from kibot.dep_downloader import search_as_plugin
|
|||
from kibot.registrable import RegOutput, RegFilter
|
||||
from kibot.misc import (WRONG_INSTALL, BOM_ERROR, DRC_ERROR, ERC_ERROR, PDF_PCB_PRINT, KICAD2STEP_ERR)
|
||||
from kibot.bom.columnlist import ColumnList
|
||||
from kibot.bom.units import get_prefix
|
||||
from kibot.bom.units import get_prefix, comp_match
|
||||
from kibot.bom.electro_grammar import parse
|
||||
from kibot.__main__ import detect_kicad
|
||||
from kibot.kicad.config import KiConf
|
||||
from kibot.globals import Globals
|
||||
|
|
@ -291,7 +292,7 @@ def test_step_fail(test_dir, caplog, monkeypatch):
|
|||
|
||||
def test_unknown_prefix(caplog):
|
||||
with context.cover_it(cov):
|
||||
get_prefix('y')
|
||||
get_prefix(1, 'y')
|
||||
assert 'Unknown prefix, please report' in caplog.text
|
||||
|
||||
|
||||
|
|
@ -346,21 +347,166 @@ def test_makefile_kibot_sys(test_dir):
|
|||
ctx.clean_up()
|
||||
|
||||
|
||||
def test_units_1():
|
||||
with context.cover_it(cov):
|
||||
assert str(comp_match("1", 'R')) == "1 Ω"
|
||||
assert str(comp_match("1000", 'R')) == "1 kΩ"
|
||||
assert str(comp_match("1000000", 'R')) == "1 MΩ"
|
||||
assert str(comp_match("1000000000", 'R')) == "1 GΩ"
|
||||
assert str(comp_match("3.3 pF", 'C')) == "3.3 pF"
|
||||
assert str(comp_match("0.0033 nF", 'C')) == "3.3 pF"
|
||||
assert str(comp_match("3p3", 'C')) == "3.3 pF"
|
||||
a = comp_match("3k3 1% 0805", 'R')
|
||||
assert str(a) == "3.3 kΩ"
|
||||
assert a.extra['tolerance'] == 1
|
||||
assert a.extra['size'] == '0805'
|
||||
a = comp_match("0.01 1%", 'R')
|
||||
assert str(a) == "10 mΩ"
|
||||
assert a.extra['tolerance'] == 1
|
||||
|
||||
|
||||
def test_read_resistance():
|
||||
assert read_resistance("4k7") == D("4700")
|
||||
assert read_resistance("4k7") == D("4700")
|
||||
assert read_resistance("4.7R") == D("4.7")
|
||||
assert read_resistance("4R7") == D("4.7")
|
||||
assert read_resistance("0R47") == D("0.47")
|
||||
assert read_resistance("4700k") == D("4700000")
|
||||
assert read_resistance("470m") == D("0.47")
|
||||
assert read_resistance("470M") == D("470000000")
|
||||
assert read_resistance("4M7") == D("4700000")
|
||||
assert read_resistance("470") == D("470")
|
||||
assert read_resistance("470Ω") == D("470")
|
||||
assert read_resistance("470 Ω") == D("470")
|
||||
assert read_resistance("470Ohm") == D("470")
|
||||
assert read_resistance("470 Ohms") == D("470")
|
||||
assert read_resistance("R47") == D("0.47")
|
||||
assert read_resistance("1G") == D("1000000000")
|
||||
assert read_resistance("4k7000") == D("4700")
|
||||
with context.cover_it(cov):
|
||||
assert read_resistance("4k7")[0] == D("4700")
|
||||
assert read_resistance("4k7")[0] == D("4700")
|
||||
assert read_resistance("4.7R")[0] == D("4.7")
|
||||
assert read_resistance("4R7")[0] == D("4.7")
|
||||
assert read_resistance("0R47")[0] == D("0.47")
|
||||
assert read_resistance("4700k")[0] == D("4700000")
|
||||
assert read_resistance("470m")[0] == D("0.47")
|
||||
assert read_resistance("470M")[0] == D("470000000")
|
||||
assert read_resistance("4M7")[0] == D("4700000")
|
||||
assert read_resistance("470")[0] == D("470")
|
||||
assert read_resistance("470Ω")[0] == D("470")
|
||||
assert read_resistance("470 Ω")[0] == D("470")
|
||||
assert read_resistance("470Ohm")[0] == D("470")
|
||||
assert read_resistance("470 Ohms")[0] == D("470")
|
||||
assert read_resistance("R47")[0] == D("0.47")
|
||||
assert read_resistance("1G")[0] == D("1000000000")
|
||||
assert read_resistance("4k7000")[0] == D("4700")
|
||||
|
||||
|
||||
def test_electro_grammar_1():
|
||||
with context.cover_it(cov):
|
||||
C2UF_0603_30P = {'type': 'capacitor', 'capacitance': 2e-6, 'size': '0603', 'tolerance': 30}
|
||||
C2UF_0603 = {'type': 'capacitor', 'capacitance': 2e-6, 'size': '0603'}
|
||||
C10UF_0402 = {'type': 'capacitor', 'capacitance': 10e-6, 'size': '0402'}
|
||||
C100NF_0603 = {'type': 'capacitor', 'capacitance': 100e-9, 'size': '0603'}
|
||||
C100NF_0603_X7R = {'type': 'capacitor', 'capacitance': 100e-9, 'size': '0603', 'characteristic': 'X7R'}
|
||||
C100NF_0603_Z5U = {'type': 'capacitor', 'capacitance': 100e-9, 'size': '0603', 'characteristic': 'Z5U'}
|
||||
C100NF_0603_Y5V = {'type': 'capacitor', 'capacitance': 100e-9, 'size': '0603', 'characteristic': 'Y5V'}
|
||||
C100NF_0603_C0G = {'type': 'capacitor', 'capacitance': 100e-9, 'size': '0603', 'characteristic': 'C0G'}
|
||||
C100NF_0603_25V = {'type': 'capacitor', 'capacitance': 100e-9, 'size': '0603', 'voltage_rating': 25}
|
||||
C100NF_0603_6V3 = {'type': 'capacitor', 'capacitance': 100e-9, 'size': '0603', 'voltage_rating': 6.3}
|
||||
C100UF_0603 = {'type': 'capacitor', 'capacitance': 100e-6, 'size': '0603'}
|
||||
C100UF_0603_X7R = {'type': 'capacitor', 'capacitance': 100e-6, 'size': '0603', 'characteristic': 'X7R'}
|
||||
C1N5_0603_X7R = {'type': 'capacitor', 'capacitance': 1.5e-9, 'size': '0603', 'characteristic': 'X7R'}
|
||||
C1F_0603_25V = {'type': 'capacitor', 'capacitance': 1, 'size': '0603', 'voltage_rating': 25}
|
||||
C_01005 = {'type': 'capacitor', 'size': '01005'}
|
||||
C_0201 = {'type': 'capacitor', 'size': '0201'}
|
||||
C_0402 = {'type': 'capacitor', 'size': '0402'}
|
||||
C_0603 = {'type': 'capacitor', 'size': '0603'}
|
||||
C_0805 = {'type': 'capacitor', 'size': '0805'}
|
||||
C_1206 = {'type': 'capacitor', 'size': '1206'}
|
||||
C_TESTS = ((('this is total rubbish', ''), {}),
|
||||
(('2uF 0603',), C2UF_0603),
|
||||
(('2uF 0603 30%', '2uF 0603 +/-30%', '2uF 0603 ±30%', '2uF 0603 +-30%'), C2UF_0603_30P),
|
||||
(('10uF 0402',
|
||||
'10 micro Farad 0402',
|
||||
'10 \u03BC''F 0402',
|
||||
'10 \u00B5''F 0402',
|
||||
'10𝛍F 0402',
|
||||
'10𝜇F 0402',
|
||||
'10𝝁 F 0402',
|
||||
'10 𝝻F 0402',
|
||||
'10𝞵F 0402'), C10UF_0402),
|
||||
(('100nF 0603 kajdlkja alkdjlkajd',
|
||||
'adjalkjd 100nF akjdlkjda 0603 kajdlkja alkdjlkajd',
|
||||
'capacitor 100nF 0603, warehouse 5',
|
||||
'adjalkjd 0603 akjdlkjda 100nF kajdlkja alkdjlkajd',
|
||||
'C 100n 0603',
|
||||
'Capacitor 100n 0603',
|
||||
'cap 100n 0603'), C100NF_0603),
|
||||
(('1n5F 0603 X7R',), C1N5_0603_X7R),
|
||||
(('100NF 0603 X7R', '100nF 0603 X7R', '100nF 0603 x7r'), C100NF_0603_X7R),
|
||||
(('100UF 0603 X7R',), C100UF_0603_X7R),
|
||||
(('100nF 0603 Z5U',), C100NF_0603_Z5U),
|
||||
(('100nF 0603 Y5V',), C100NF_0603_Y5V),
|
||||
(('100nF 0603 C0G',
|
||||
'100nF 0603 NP0',
|
||||
'100nF 0603 np0',
|
||||
'100nF 0603 c0g',
|
||||
'100nF 0603 cog',
|
||||
'100nF 0603 npO',
|
||||
'100nF 0603 COG',
|
||||
'100nF 0603 C0G/NP0'), C100NF_0603_C0G),
|
||||
(('1F 0603 25V', '1f 0603 25V', '1 Farad 0603 25V'), C1F_0603_25V),
|
||||
(('100nF 0603 25V', '100nF 0603 25 v'), C100NF_0603_25V),
|
||||
(('100nF 0603 6v3', '100nF 0603 6V3', '100nF 0603 6.3V', '100nF 0603 6.3v'), C100NF_0603_6V3),
|
||||
(('0603 0.0001F', '0603 0.0001 F', '0603 0.1mF'), C100UF_0603),
|
||||
(('capacitor 01005',), C_01005),
|
||||
(('capacitor 0201',), C_0201),
|
||||
(('capacitor 0402',), C_0402),
|
||||
(('capacitor 0603',), C_0603),
|
||||
(('capacitor 0805',), C_0805),
|
||||
(('capacitor 1206',), C_1206))
|
||||
R1K_0603 = {'type': 'resistor', 'size': '0603', 'resistance': 1000}
|
||||
R1K_0805_5P = {'type': 'resistor', 'size': '0805', 'resistance': 1000, 'tolerance': 5}
|
||||
R1K_0805_5P_100MW = {'type': 'resistor', 'size': '0805', 'resistance': 1000, 'tolerance': 5, 'power_rating': 0.1}
|
||||
R1K_0201_500MW = {'type': 'resistor', 'size': '0201', 'resistance': 1000, 'power_rating': 0.5}
|
||||
R0_0201_125MW = {'type': 'resistor', 'size': '0201', 'resistance': 0, 'power_rating': 0.125}
|
||||
R1M_0603 = {'type': 'resistor', 'size': '0603', 'resistance': 1e6}
|
||||
R1M = {'type': 'resistor', 'resistance': 1e6}
|
||||
R1M1_0603 = {'type': 'resistor', 'size': '0603', 'resistance': 1.1e6}
|
||||
R100 = {'type': 'resistor', 'resistance': 100}
|
||||
R10K_0805 = {'type': 'resistor', 'size': '0805', 'resistance': 10000}
|
||||
R1 = {'type': 'resistor', 'resistance': 1}
|
||||
R1_0402 = {'type': 'resistor', 'resistance': 1, 'size': '0402'}
|
||||
R1_0805 = {'type': 'resistor', 'resistance': 1, 'size': '0805'}
|
||||
R1K5_0402 = {'type': 'resistor', 'resistance': 1500, 'size': '0402'}
|
||||
R2_7_0402 = {'type': 'resistor', 'resistance': 2.7, 'size': '0402'}
|
||||
R1MILI = {'type': 'resistor', 'resistance': 0.001}
|
||||
R100U = {'type': 'resistor', 'resistance': 0.0001}
|
||||
R_01005 = {'type': 'resistor', 'size': '01005'}
|
||||
R_0201 = {'type': 'resistor', 'size': '0201'}
|
||||
R_0402 = {'type': 'resistor', 'size': '0402'}
|
||||
R_0603 = {'type': 'resistor', 'size': '0603'}
|
||||
R_0805 = {'type': 'resistor', 'size': '0805'}
|
||||
R_1206 = {'type': 'resistor', 'size': '1206'}
|
||||
R_TESTS = ((('R 0.01 1%',), {'type': 'resistor', 'resistance': 0.01, 'tolerance': 1}),
|
||||
(('1k 0603', '1k ohm 0603', '1K ohms 0603'), R1K_0603),
|
||||
(('resistor 100', '100R', '100 R'), R100),
|
||||
(('r 10000 0805',), R10K_0805),
|
||||
(('res or whatever 1',), R1),
|
||||
(('1 ohm 0402',), R1_0402),
|
||||
(('1Ω 0805', '1Ω 0805'), R1_0805),
|
||||
(('1MEG 0603', '1M 0603'), R1M_0603),
|
||||
(('1M1 ohms 0603',), R1M1_0603),
|
||||
(('1k5 0402', '1.5k 0402'), R1K5_0402),
|
||||
(('2r7 0402', '2R7 0402'), R2_7_0402),
|
||||
(('1 mOhm',), R1MILI),
|
||||
(('1 MOhm',), R1M),
|
||||
(('100 uΩ',), R100U),
|
||||
(('1k 0805 5%',), R1K_0805_5P),
|
||||
(('1k 0805 5% 100mW',), R1K_0805_5P_100MW),
|
||||
(('0 ohm 0201 0.125W', '0 ohm 0201 1/8W'), R0_0201_125MW),
|
||||
(('resistor 1k 0201 1/2 watts',), R1K_0201_500MW),
|
||||
(('resistor 01005',), R_01005),
|
||||
(('resistor 0201',), R_0201),
|
||||
(('resistor 0402',), R_0402),
|
||||
(('resistor 0603',), R_0603),
|
||||
(('resistor 0805',), R_0805),
|
||||
(('resistor 1206',), R_1206))
|
||||
LED_TEST = ((('led red 0603',), {'type': 'led', 'size': '0603', 'color': 'red'}),
|
||||
(('SMD LED GREEN 0805', 'GREEN 0805 LED'), {'type': 'led', 'size': '0805', 'color': 'green'}))
|
||||
L_TEST = ((('L 100 0805', 'IND 100 0805', 'Inductor 100 0805'), {'type': 'inductor', 'inductance': 100,
|
||||
'size': '0805'}),
|
||||
(('3n3 H', '3n3H', '3.3 nH', '3300pH', '3.3 nano Henry',
|
||||
'This is a 3.3 nH inductor'), {'type': 'inductor', 'inductance': 3.3e-9}))
|
||||
TESTS = C_TESTS+R_TESTS+L_TEST+LED_TEST
|
||||
for test in TESTS:
|
||||
ref = test[1]
|
||||
for c in test[0]:
|
||||
res = parse(c)
|
||||
assert res == ref, "For `{}` got:\n{}\nExpected:\n{}".format(c, res, ref)
|
||||
logging.debug(c+" Ok")
|
||||
|
|
|
|||
Loading…
Reference in New Issue