import logging import os.path from configclass import Config from ee.fact import DataSetManager logger = logging.getLogger(__name__) class KicadDoitTasks(object): config = Config({ "sch": None, "kicad_pcb": None, "gerber_dir": None, "gerber_zip": None, "data_set_dir": None, }) def __init__(self, *args, **kwargs): self.config = self.config.make(kwargs) formatter = logging.Formatter("%(levelname)s: %(message)s") ch = logging.StreamHandler() ch.setFormatter(formatter) logger.addHandler(ch) logger.setLevel(logging.DEBUG) def tasks(self, *args, **kwargs): kicad_pcb = self.config["kicad_pcb"] sch = self.config["sch"] tasks = [] dsm = DataSetManager(self.config["data_set_dir"]) gerber_dir = self.config["gerber_dir"] gerber_zip = self.config["gerber_zip"] if kicad_pcb and gerber_dir: tasks.append(task_kicad_gerber(kicad_pcb, gerber_dir, gerber_zip)) sch_ds = task_kicad_sch_to_data_set(dsm, sch) \ if sch else None pcb_ds = task_kicad_pcb_to_data_set(dsm, kicad_pcb) \ if kicad_pcb else None component_ds = task_kicad_create_component_data_set(dsm) \ if sch_ds else None tasks = [component_ds, sch_ds, pcb_ds,] return tasks def task_kicad_gerber(kicad_pcb: str, gerber_dir: str, gerber_zip: str, name="kicad-gerber"): import ee.kicad gerber_zip = gerber_zip or "{}.zip".format(gerber_dir) # logger.info("gerber_zip={}".format(gerber_zip)) eg = next((p for p in (os.path.join(p, "export_gerber.py") for p in ee.kicad.__path__) if os.path.isfile(p)), None) if not eg: raise Exception("Could not find export_gerber.py") # TODO: replace with python mkdir = "mkdir -p {}".format(gerber_dir) export_gerber = " ".join([ eg, "--pcb", kicad_pcb, "--output-directory", gerber_dir, "--protel-extensions", ]) def make_zip(): import zipfile from pathlib import Path with zipfile.ZipFile(gerber_zip, "w") as z: for p in Path(gerber_dir).iterdir(): if not p.is_file(): continue z.write(p, arcname=p.relative_to(gerber_dir)) return { "name": name, "targets": [gerber_zip], "actions": [mkdir, export_gerber, make_zip], "file_dep": [kicad_pcb], } def task_kicad_sch_to_data_set(dsm: DataSetManager, sch, name="kicad-sch-to-data-set"): out_data_set = "kicad-sch" in_data_sets = [] def action(): import ee.kicad from ee.kicad.model import ComponentField with dsm.create_rw(out_data_set, inputs=in_data_sets) as ds: schematics = ee.kicad.read_schematics(sch) for c in [c for c in schematics.components]: o = ds.get_object("kicad-schematic-component", c.timestamp) o.set("ref", c.ref) o.set("ref-type", c.ref_type) if c.has_ref_num: o.set("ref-num", str(c.ref_num)) o.set("value", c.value) if c.footprint: o.set("footprint", c.footprint) for f in c.fields: if f.value and f.name not in ComponentField.names: o.set(f.name, str(f.value)) return { "name": name, "file_dep": [sch] + [dsm.metafile_for_ds(ds) for ds in in_data_sets], "actions": [action], "targets": [dsm.metafile_for_ds(out_data_set)], } def task_kicad_pcb_to_data_set(dsm: DataSetManager, pcb_path, name="kicad-pcb-to-data-set"): out_data_set = "kicad-pcb" in_data_sets = [] def action(): import ee.kicad.pcb from ee.kicad.pcb import FpText logger.debug("Parsing PCB {}".format(pcb_path)) with dsm.create_rw(out_data_set, inputs=in_data_sets) as ds: # [ds.delete(o) for o in ds.items(object_type="kicad-pcb-component")] pcb: ee.kicad.pcb.KicadPcb = ee.kicad.pcb.parse(pcb_path) for _m in pcb.modules: m: ee.kicad.pcb.Module = _m o = ds.get_object("kicad-pcb-component", m.tstamp) ref_text: FpText = next((t for t in m.fp_texts if t.kind == "reference"), None) o.set("reference", ref_text.value) x, y, rot = m.at o.set("at-x", x) o.set("at-y", y) o.set("at-rot", rot) o.set("layer", m.layer) return { "name": name, "file_dep": [pcb_path] + [dsm.metafile_for_ds(ds) for ds in in_data_sets], "actions": [action], "targets": [dsm.metafile_for_ds(out_data_set)], } def task_kicad_create_component_data_set(dsm: DataSetManager, name="kicad-create-component-data-set"): out_data_set = "components" in_data_sets = ["kicad-sch", "kicad-pcb"] def action(targets, *args, **kwargs): logger.info("targets={}, args={}, kwargs={}".format(targets, args, kwargs)) with dsm.create_rw(out_data_set, inputs=in_data_sets) as ds: items = ds.items() logger.info("Got {} objects".format(len(items))) for o in items: logger.info("processing {}".format(o.key)) return { "name": name, "file_dep": [dsm.metafile_for_ds(ds) for ds in in_data_sets], "actions": [action], "targets": [dsm.metafile_for_ds(out_data_set)], }