From 44c436ce849f9155706109e767fe7b1666172f7e Mon Sep 17 00:00:00 2001 From: Trygve Laugstøl Date: Tue, 24 Jul 2018 21:01:46 +0200 Subject: o Starting on some BOM code. --- demo/doit/dodo.py | 9 ++++- src/ee/bom/__init__.py | 0 src/ee/bom/doit.py | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/ee/ds/__init__.py | 60 +++++++++++++++++++++++-------- src/ee/kicad/doit.py | 10 +++--- 5 files changed, 153 insertions(+), 21 deletions(-) create mode 100644 src/ee/bom/__init__.py create mode 100644 src/ee/bom/doit.py diff --git a/demo/doit/dodo.py b/demo/doit/dodo.py index 395cbee..1f5fec8 100644 --- a/demo/doit/dodo.py +++ b/demo/doit/dodo.py @@ -1,6 +1,9 @@ import ee.kicad.doit -from ee.kicad.doit import * +import ee.bom.doit +# noinspection PyUnresolvedReferences +from ee.bom.doit import * from ee.ds import DataSetManager +from ee.kicad.doit import * prj = "demo" sch = "{}.sch".format(prj) @@ -21,3 +24,7 @@ ee.kicad.doit.init( kicad_pcb=kicad_pcb, gerber_dir="gerber", ) + +ee.bom.doit.init( + data_set_manager=dsm, +) diff --git a/src/ee/bom/__init__.py b/src/ee/bom/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/ee/bom/doit.py b/src/ee/bom/doit.py new file mode 100644 index 0000000..d7407d8 --- /dev/null +++ b/src/ee/bom/doit.py @@ -0,0 +1,95 @@ +from typing import Mapping + +from configclass import Config + +from ee.ds import DataSetManager + +_dsm = None # type: DataSetManager + +_data_sets = {} + +_config_template = Config({ +}) + +_config = None # type: Mapping[str, str] + + +def init(data_set_manager: DataSetManager, **kwargs): + global _config + _config = _config_template.make(kwargs) + + global _dsm + _dsm = data_set_manager + + +class BomComponent(object): + def __init__(self, ref, mpn): + self.ref = ref + self.mpn = mpn + + def to_object(self, ds): + return ds.create_object("bom-component", self.ref).\ + set("ref", self.ref).\ + set("mpn", self.mpn) + + +class MpnBomComponent(object): + def __init__(self, ref, mpn): + self.ref = ref + self.mpn = mpn + self.count = 1 + + def to_object(self, ds): + return ds.create_object("mpn-bom-component", self.ref).\ + set("ref", self.ref).\ + set("mpn", self.mpn) + + +def task_bom(): + out_data_set = "bom" + in_data_sets = _data_sets[task_bom] + + def action(count=1): + with _dsm.create_ro(in_data_sets) as in_ds: + with _dsm.create_rw(out_data_set, clean=True) as output: + components = [o for o in in_ds.items() if o.object_type.name == "component"] + + bom_components = {} + mpn_bom_components = {} + + for c in components: + ref = c.get("ref") + mpn = c.get("mpn") + + if not ref: + raise Exception("Missing ref") + + if not mpn: + output.create_object("message", "bom-{}".format(ref)). \ + set("level", "error"). \ + set("message", "Missing required field 'mpn'") + continue + + if ref in bom_components: + raise Exception("Duplicate ref '{}'".format("ref")) + + bom_components[ref] = BomComponent(ref, mpn) + + mpn_bom_component = mpn_bom_components[ref] + if not mpn_bom_component: + mpn_bom_component = MpnBomComponent(ref, mpn) + mpn_bom_components[ref] = mpn_bom_component + + mpn_bom_component.count += 1 + + [c.to_object(output) for c in bom_components.values()] + [c.to_object(output) for c in mpn_bom_components.values()] + + return { + "file_dep": [_dsm.cookie_for_ds(ds) for ds in in_data_sets], + "actions": [action], + "targets": [_dsm.cookie_for_ds(out_data_set)], + } + + +_data_sets[task_bom] = ["components"] diff --git a/src/ee/ds/__init__.py b/src/ee/ds/__init__.py index aff5b64..5899d28 100644 --- a/src/ee/ds/__init__.py +++ b/src/ee/ds/__init__.py @@ -2,6 +2,7 @@ import configparser import csv import logging import os +import shutil from functools import total_ordering from pathlib import Path from typing import MutableMapping, Optional, List, Tuple, Union, Iterator @@ -64,12 +65,14 @@ class Object(object): def key(self): return self._key - def set(self, key: str, value: str): + def set(self, key: str, value: str) -> "Object": if self._ds._frozen: raise Exception("This data set is frozen") idx = self._ot.index_of(key, create=True) self._data.insert(idx, value) + return self + def _set_from_object(self, other: "Object"): for k in other._ot.fields: self.set(k, other.get(k)) @@ -89,7 +92,7 @@ class Object(object): class DataSet(object): - def __init__(self, name): + def __init__(self, name: Optional[str] = None): self._name = name self._object_types = {} # type: MutableMapping[str, ObjectType] self._objects_by_type = {} # type: MutableMapping[ObjectType, MutableMapping[str, Object]] @@ -97,6 +100,8 @@ class DataSet(object): @property def name(self): + if not self._name: + raise Exception("Unnamed data set") return self._name def __len__(self): @@ -210,11 +215,11 @@ class DataSetManager(object): except KeyError: return self._basedir / ds_name / "data-set.ini" - def create_rw(self, name, inputs: List[str] = None) -> "LazyDataSet": - return LazyDataSet(self, False, name, inputs if inputs else []) + def create_rw(self, name, clean: bool) -> "LazyRwDataSet": + return LazyRwDataSet(self, name, clean) - def create_ro(self, inputs: List[str]) -> "LazyDataSet": - return LazyDataSet(self, True, None, inputs) + def create_ro(self, inputs: List[str]) -> "LazyRoDataSet": + return LazyRoDataSet(self, inputs) def add_ds(self, ds_type: str, name: str, object_type: str, path: str = None): if ds_type == "csv": @@ -337,27 +342,52 @@ class DataSetManager(object): with open(path, "w") as f: ini.write(f) + def remove(self, name: str): + try: + object_type, path = self._csv[name] + os.remove(str(path)) + except KeyError: + shutil.rmtree(self._basedir / name) + -class LazyDataSet(object): - def __init__(self, dsm: DataSetManager, freeze: bool, name, inputs): +class LazyRoDataSet(object): + def __init__(self, dsm: DataSetManager, inputs): self._dsm = dsm - self._freeze = freeze - self._name = name self._inputs = inputs def __enter__(self) -> DataSet: # logger.info("enter: name={}, inputs={}".format(self._name, self._inputs)) - ds = DataSet(self._name) + ds = DataSet() for name in self._inputs: ds = ds.merge(self._dsm.load(name, freeze=True)) - if self._freeze: - ds.freeze() + ds.freeze() + + self._ds = ds + return ds + def __exit__(self, *args): + return False + + +class LazyRwDataSet(object): + def __init__(self, dsm: DataSetManager, name, clean): + self._dsm = dsm + self._name = name + self._clean = clean + + def __enter__(self) -> DataSet: + cookie = self._dsm.cookie_for_ds(self._name) + + if cookie.exists(): + if not self._clean: + raise IOError("DataSet already exists: {}, cookie={}".format(self._name, cookie)) + self._dsm.remove(self._name) + + ds = DataSet(self._name) self._ds = ds return ds def __exit__(self, *args): - if not self._freeze: - self._dsm.store(self._ds) + self._dsm.store(self._ds) return False diff --git a/src/ee/kicad/doit.py b/src/ee/kicad/doit.py index a3f8464..30ca40d 100644 --- a/src/ee/kicad/doit.py +++ b/src/ee/kicad/doit.py @@ -25,8 +25,8 @@ _dsm = None # type: DataSetManager _data_sets = {} -def change_data_sets_for_task(task, callable): - _data_sets[task] = callable(_data_sets[task]) +def change_data_sets_for_task(task, _callable): + _data_sets[task] = _callable(_data_sets[task]) def init(data_set_manager: DataSetManager, **kwargs): @@ -92,7 +92,7 @@ def task_kicad_sch_to_data_set(): def action(): from ee.kicad.model import ComponentField - with _dsm.create_rw(out_data_set) as ds: + with _dsm.create_rw(out_data_set, clean=True) as ds: schematics = ee.kicad.read_schematics(sch) for c in [c for c in schematics.components]: o = ds.create_object("kicad-schematic-component", c.timestamp) @@ -129,7 +129,7 @@ def task_kicad_pcb_to_data_set(): logger.debug("Parsing PCB {}".format(kicad_pcb)) - with _dsm.create_rw(out_data_set, inputs=in_data_sets) as ds: + with _dsm.create_rw(out_data_set, clean=True) as ds: # [ds.delete(o) for o in ds.items(object_type="kicad-pcb-component")] pcb = ee.kicad.pcb.parse(kicad_pcb) # type: KicadPcb @@ -179,7 +179,7 @@ def task_kicad_create_component_data_set(): return footprint - with _dsm.create_rw(out_data_set) as output: + with _dsm.create_rw(out_data_set, clean=True) as output: kicad_sch = [o for o in in_ds.items() if o.object_type.name == "kicad-schematic-component"] logger.info("processing {} kicad-sch".format(len(kicad_sch))) -- cgit v1.2.3