From 1699cca793c6a8ebc00942557b6764fff6739044 Mon Sep 17 00:00:00 2001 From: Trygve Laugstøl Date: Thu, 23 May 2019 21:08:36 +0200 Subject: part-find-requirements: wip --- src/ee/__init__.py | 26 +++++++++++-- src/ee/kicad/functions.py | 25 ++++++++++--- src/ee/part/common_fact_types.py | 1 + src/ee/part/fact_keys.py | 1 + src/ee/part/requirement.py | 30 +++++++++++---- src/ee/tools/part_find_requirements.py | 29 ++++++++------- src/ee/tools/templates/build.ninja.j2 | 8 ++-- test/test_EeVal.py | 68 +++++++++++++++++++--------------- 8 files changed, 125 insertions(+), 63 deletions(-) diff --git a/src/ee/__init__.py b/src/ee/__init__.py index 2ba113e..06c5aab 100644 --- a/src/ee/__init__.py +++ b/src/ee/__init__.py @@ -1,5 +1,6 @@ import re from functools import total_ordering +from typing import Optional import math @@ -52,8 +53,10 @@ class EeVal(object): 'P': 15, 'E': 18, } + keys = "".join(exponents.keys()) + units = "|".join(units) r = re.compile( - "([0-9]+\\.[0-9]+|\\.?[0-9]+|[0-9]+\\.?) *([" + "".join(exponents.keys()) + "]?) *(" + "|".join(units) + "?)") + "([0-9]+(?:\\.[0-9]*)?(?:e-?[0-9]+)|[0-9]+\\.[0-9]+|\\.?[0-9]+|[0-9]+\\.?) *([" + keys + "]?) *(" + units + "?)") def __init__(self, s=None, value=None, exp=None, unit=None): # This is where I regret having a string in the API at once. @@ -66,7 +69,14 @@ class EeVal(object): (self._value, self._exp, self._unit) = (value, exp, unit) @staticmethod - def parse(s) -> "EeVal": + def try_parse(s, expected_unit=None) -> Optional["EeVal"]: + try: + return EeVal.parse(s, expected_unit) + except EeException: + return None + + @staticmethod + def parse(s, expected_unit=None) -> "EeVal": m = EeVal.r.match(s) if not m: raise EeException("Could not parse value: " + str(s)) @@ -79,10 +89,18 @@ class EeVal(object): e = math.ceil(math.log10(value)) exp = exp + e value = value * math.pow(10, -e) - return EeVal(None, value=float(value), exp=exp, unit=unit if len(unit) > 0 else None) + + unit = unit if len(unit) > 0 else None + if expected_unit is not None: + if unit is None: + unit = expected_unit + elif unit != expected_unit: + raise EeException("Bad unit when parsing, expected {}, got {}".format(expected_unit, unit)) + + return EeVal(None, value=float(value), exp=exp, unit=unit) @property - def value(self): + def value(self) -> float: return self.__float__() @property diff --git a/src/ee/kicad/functions.py b/src/ee/kicad/functions.py index a2f9f80..b8ad7f5 100644 --- a/src/ee/kicad/functions.py +++ b/src/ee/kicad/functions.py @@ -1,8 +1,8 @@ import re - import ee.kicad.model import ee.kicad.sch_fact_types as kicad_ft +from ee import EeVal from ee.kicad import sch_fact_types from ee.part import Part from ee.part import common_fact_types @@ -80,11 +80,26 @@ def fix_value_strategy(**kwargs): symbol_name = part.facts.get_value(sch_fact_types.symbol_name) - if ref_type in ("D", "R", "L", "C") and v == symbol_name: - part.remove_fact(uris.make_fact_key("value")) + # if ref_type in ("D", "R", "L", "C") and v == symbol_name: + # part.remove_fact(uris.make_fact_key("value")) + + if ref_type == "R": + ee_value = EeVal.try_parse(v) + if ee_value: + part.facts.add(common_fact_types.resistance, str(ee_value.value)) + + if ref_type == "C": + ee_value = EeVal.try_parse(v) + if ee_value: + part.facts.add(common_fact_types.capacitance, str(ee_value.value)) + + if ref_type == "L": + ee_value = EeVal.try_parse(v) + if ee_value: + part.facts.add(common_fact_types.inductance, str(ee_value.value)) - if ref_type == "Q" and v == symbol_name and re.match("^Q_[NP]MOS_[DSG]{3}$", symbol_name): - part.remove_fact(uris.make_fact_key("value")) + # if ref_type == "Q" and v == symbol_name and re.match("^Q_[NP]MOS_[DSG]{3}$", symbol_name): + # part.remove_fact(uris.make_fact_key("value")) return part diff --git a/src/ee/part/common_fact_types.py b/src/ee/part/common_fact_types.py index 10ba1eb..7474437 100644 --- a/src/ee/part/common_fact_types.py +++ b/src/ee/part/common_fact_types.py @@ -5,5 +5,6 @@ footprint = FactType(fact_keys.footprint, "Footprint") resistance = EeValueFactType(fact_keys.resistance, "Resistance", ee.resistance_type) capacitance = EeValueFactType(fact_keys.capacitance, "Capacitance", ee.capacitance_type) +inductance = EeValueFactType(fact_keys.inductance, "Inductance", ee.inductance_type) ee_component_type = EeValueFactType(fact_keys.ee_component_type, "EE component type", ee.capacitance_type) diff --git a/src/ee/part/fact_keys.py b/src/ee/part/fact_keys.py index 9617b9c..668a197 100644 --- a/src/ee/part/fact_keys.py +++ b/src/ee/part/fact_keys.py @@ -1,5 +1,6 @@ ee_component_type = "http://purl.org/ee/fact-type/ee-component-type" capacitance = "http://purl.org/ee/fact-type/capacitance" +inductance = "http://purl.org/ee/fact-type/inductance" max_voltage = "http://purl.org/ee/fact-type/voltage" # https://en.wikipedia.org/wiki/Ceramic_capacitor#Class_2_ceramic_capacitors rs_198_class_2 = "http://purl.org/ee/fact-type/rs-198 class 2" diff --git a/src/ee/part/requirement.py b/src/ee/part/requirement.py index a9381df..297727b 100644 --- a/src/ee/part/requirement.py +++ b/src/ee/part/requirement.py @@ -1,6 +1,20 @@ from typing import List -from ee.part import Part, fact_keys, FactType, common_fact_types +from ee import EeVal +from ee.part import Part, FactType, common_fact_types, EeValueFactType + + +def to_str(part, fact_type, value, op): + v = None + if isinstance(fact_type, EeValueFactType): + eeft: EeValueFactType=fact_type + ev = EeVal.try_parse(value, eeft.ee_type.symbol) + v = str(ev) + + if not v: + v = value + + return "{}.{} {} {}".format(part.printable_reference, fact_type.label or fact_type.uri, op, v) class Requirement(object): @@ -15,7 +29,7 @@ class EqualRequirement(Requirement): self.value = value def __str__(self): - return "{}.{} == {}".format(self.part.printable_reference, self.fact_type, self.value) + return to_str(self.part, self.fact_type, self.value, "==") class MinRequirement(Requirement): @@ -25,7 +39,7 @@ class MinRequirement(Requirement): self.value = value def __str__(self): - return "{}.{} == {}".format(self.part.printable_reference, self.fact_type, self.value) + return to_str(self.part, self.fact_type, self.value, ">") class MaxRequirement(Requirement): @@ -35,7 +49,7 @@ class MaxRequirement(Requirement): self.value = value def __str__(self): - return "{}.{} == {}".format(self.part.printable_reference, self.fact_type, self.value) + return to_str(self.part, self.fact_type, self.value, "<") class PartAnalysis(object): @@ -47,12 +61,12 @@ class PartAnalysis(object): def analyze_requirements(part: Part) -> PartAnalysis: rs = [] - resistance = part.find_fact(common_fact_types.resistance.uri) + resistance = part.facts.get_value(common_fact_types.resistance) if resistance: - rs.append(EqualRequirement(part, common_fact_types.resistance, resistance.valueProp)) + rs.append(EqualRequirement(part, common_fact_types.resistance, resistance)) - capacitance = part.find_fact(common_fact_types.capacitance.uri) + capacitance = part.facts.get_value(common_fact_types.capacitance) if capacitance: - rs.append(EqualRequirement(part, common_fact_types.capacitance, capacitance.valueProp)) + rs.append(EqualRequirement(part, common_fact_types.capacitance, capacitance)) return PartAnalysis(part, rs) diff --git a/src/ee/tools/part_find_requirements.py b/src/ee/tools/part_find_requirements.py index 032db02..a13c3f2 100644 --- a/src/ee/tools/part_find_requirements.py +++ b/src/ee/tools/part_find_requirements.py @@ -1,26 +1,27 @@ import argparse from pathlib import Path -from ee.part import requirement, Part, PartDb, load_db, save_db +from ee.part import requirement, Part, load_db -def work(in_path: Path, out_path: Path): +def work(in_path: Path, out_path: Path, report_path: Path): in_parts = load_db(in_path) - with out_path.open("w") as f: - print("", file=f) - for xml in in_parts.iterparts(): - part = Part(xml) + requirements = [] + for xml in in_parts.iterparts(): + part = Part(xml) - analysis = requirement.analyze_requirements(part) + analysis = requirement.analyze_requirements(part) + requirements.append(analysis) - print("Part: {}. Found {} requirements".format(analysis.part.printable_reference, - len(analysis.requirements)), file=f) + with report_path.open("w") as f: + for a in requirements: + print("{}\n{}\n".format(a.part.printable_reference, "=" * len(a.part.printable_reference)), file=f) - for r in analysis.requirements: - print(" {}".format(r), file=f) + for r in a.requirements: + print("* {}".format(str(r)), file=f) - print("", file=f) + print("", file=f) parser = argparse.ArgumentParser() @@ -34,6 +35,8 @@ parser.add_argument("--out", required=True, metavar="REQUIREMENTS") +parser.add_argument("--report") + args = parser.parse_args() -work(Path(args.in_path), Path(args.out)) +work(Path(args.in_path), Path(args.out), Path(args.report)) diff --git a/src/ee/tools/templates/build.ninja.j2 b/src/ee/tools/templates/build.ninja.j2 index 0706b11..6ff4e0a 100644 --- a/src/ee/tools/templates/build.ninja.j2 +++ b/src/ee/tools/templates/build.ninja.j2 @@ -32,7 +32,7 @@ rule part-apply-function rule part-find-requirements description = part-find-requirements - command = $ee part-find-requirements --in $in --out $out + command = $ee part-find-requirements --in $in --out $out $report rule part-validate-parts command = $ee part-validate-parts --in $in --out $out @@ -88,7 +88,9 @@ build ee/sch.xml: part-apply-function ee/kicad-sch.xml build $report_dir/part-validate-parts.rst: part-validate-parts ee/sch.xml {%- set reports=reports+["$report_dir/part-validate-parts.rst"] %} -build ee/requirements.xml: part-find-requirements ee/sch.xml +build ee/requirements.xml | $report_dir/requirements.rst: part-find-requirements ee/sch.xml + report = --report $report_dir/requirements.rst +{%- set reports=reports+["$report_dir/requirements.rst"] %} {% for s in distributors %} {%- set cfg = project.cfg["supplier:" + s] if "supplier:" + s in project.cfg else None %} @@ -97,7 +99,7 @@ build ee/{{ s }}/pn-part-search-list.xml: pn-part-search-list ee/sch.xml supplier = {{ s }} build ee/{{ s }}/downloaded.xml | ee/{{ s }}/downloaded.rst: {{ s }}-search-parts ee/{{ s }}/pn-part-search-list.xml -{%- set reports=reports+["ee/" + s + "/downloaded.rst"] %} +{%- set reports=reports+["$report_dir/" + s + "/downloaded.rst"] %} build ee/{{ s }}/parts.xml: part-apply-function ee/{{ s }}/downloaded.xml execution = {{ s }} diff --git a/test/test_EeVal.py b/test/test_EeVal.py index 0c21133..6d4032f 100644 --- a/test/test_EeVal.py +++ b/test/test_EeVal.py @@ -1,37 +1,43 @@ import pytest from ee import EeVal + @pytest.mark.parametrize("s, num_value, str_value, unit", [ - ("0", 0, "0", None), - ("0 k", 0, "0", None), - ("1", 1, "1", None), - ("10", 10, "10", None), - ("10k", 10 * 1000, "10 k", None), - ("10 k", 10 * 1000, "10 k", None), - ("10 k", 10 * 1000, "10 k", None), - ("1000 m", 1, "1", None), - ("100 m", 0.1, "100 m", None), - ("100 n", 0.0000001, "100 n", None), - ("1 m", 0.001, "1 m", None), - ("1 u", 0.000001, "1 u", None), - ("0.1 u", 0.0000001, "100 n", None), - (".1 u", 0.0000001, "100 n", None), - ("4.7 u", 0.0000047, "4.7 u", None), - ("1 µ", 0.000001, "1 u", None), - ("1 n", 0.000000001, "1 n", None), - ("10 n", 0.00000001, "10 n", None), - ("1p", 1e-12, "1 p", None), - ("0.8p", 8e-13, "0.8 p", None), - ("0.008p", 8e-15, "0.008 p", None), - ]) + # ("0", 0, "0", None), + # ("0 k", 0, "0", None), + # ("1", 1, "1", None), + # ("1e-3", 0.001, "1 m", None), + # ("1e3", 1000, "1 k", None), + # ("4.7e3", 4700, "4.7 k", None), + ("4.e3", 4000, "4 k", None), + # ("10", 10, "10", None), + # ("10k", 10 * 1000, "10 k", None), + # ("10 k", 10 * 1000, "10 k", None), + # ("10 k", 10 * 1000, "10 k", None), + # ("1000 m", 1, "1", None), + # ("100 m", 0.1, "100 m", None), + # ("100 n", 0.0000001, "100 n", None), + # ("1 m", 0.001, "1 m", None), + # ("1 u", 0.000001, "1 u", None), + # ("0.1 u", 0.0000001, "100 n", None), + # (".1 u", 0.0000001, "100 n", None), + # ("4.7 u", 0.0000047, "4.7 u", None), + # ("1 µ", 0.000001, "1 u", None), + # ("1 n", 0.000000001, "1 n", None), + # ("10 n", 0.00000001, "10 n", None), + # ("1p", 1e-12, "1 p", None), + # ("0.8p", 8e-13, "0.8 p", None), + # ("0.008p", 8e-15, "0.008 p", None), +]) def test_basic(s, num_value, str_value, unit): num_value = float(num_value) v = EeVal(s) - assert float(v) == v.value + assert v.value == float(v) epsilon = num_value * 0.01 - assert (num_value + epsilon) >= float(v) and (num_value - epsilon) <= float(v) - assert str_value == str(v) - assert unit == v.unit + assert (num_value + epsilon) >= float(v) >= (num_value - epsilon) + assert str(v) == str_value + assert v.unit == unit + @pytest.mark.parametrize("s, str_value, unit", [ ("10nF", "10 nF", "F"), @@ -39,26 +45,28 @@ def test_basic(s, num_value, str_value, unit): ("10n F", "10 nF", "F"), ("10 n F", "10 nF", "F"), ("4.7 n F", "4.7 nF", "F"), - ]) +]) def test_units(s, str_value, unit): v = EeVal(s) assert unit == v.unit assert float(v) == v.value assert str_value == str(v) + def test_ordering(): p100 = EeVal('100 p') n100 = EeVal('100 n') - n100F = EeVal('100 n F') + n100f = EeVal('100 n F') u1 = EeVal('1u') assert n100 > p100 assert p100 < n100 - assert n100F < u1 + assert n100f < u1 assert [p100, n100, u1] == sorted([p100, u1, n100]) + def test_hash(): p100 = EeVal('100 p') n100 = EeVal('100 n') u1 = EeVal('1u') - assert 3 == len(set([p100, p100, n100, u1])) + assert 3 == len({p100, p100, n100, u1}) -- cgit v1.2.3