"""CLI: convert EPRO2 docs to EasyEDA Std-format JSON files. Mirrors the layout of Std project sources: one ``.json`` per document, flat in ``--out``. Use this for downstream consumers that already speak Std (Wokwi-based pipelines, dataStr parsers, etc.) — the KiCad writer at ``tools.epro2.kicad`` is the alternate target for downstream that wants kicad_sch / kicad_pcb instead. Usage: uv run python -m tools.epro2.std --all-pcb --out uv run python -m tools.epro2.std --all-sch --out uv run python -m tools.epro2.std --all --out """ from __future__ import annotations import argparse import json import sys from pathlib import Path from ..project_relations import ProjectRelations from ..replay import Project, replay_project from .pcb_writer import write_pcb_std from .sch_writer import write_sch_std def _convert_pcbs(proj: Project, out_dir: Path, pr: ProjectRelations) -> int: pcb_uuids = [u for u, d in proj.documents.items() if d.doc_type == "PCB"] if not pcb_uuids: return 0 print(f"PCB: converting {len(pcb_uuids)} doc(s) → {out_dir}") for u in pcb_uuids: try: payload = write_pcb_std(proj.documents[u], project_relations=pr) except Exception as e: # noqa: BLE001 print(f" FAIL {u[:12]}: {e}", file=sys.stderr) continue # Stamp puuid so downstream can wire docs back to a project payload["result"]["puuid"] = proj.project_uuid or "" (out_dir / f"{u}.json").write_text( json.dumps(payload, ensure_ascii=False, separators=(",", ":")), encoding="utf-8", ) s = getattr(write_pcb_std, "last_stats", None) if s: print( f" {u[:12]}.json: tracks={s.tracks} vias={s.vias} " f"copperareas={s.copperareas} libs={s.libs} pads={s.pads} " f"libs_unresolved={s.libs_unresolved}" ) return len(pcb_uuids) def _convert_schs(proj: Project, out_dir: Path, pr: ProjectRelations) -> int: sch_uuids = [u for u, d in proj.documents.items() if d.doc_type == "SCH_PAGE"] if not sch_uuids: return 0 print(f"SCH: converting {len(sch_uuids)} doc(s) → {out_dir}") for u in sch_uuids: try: payload = write_sch_std(proj.documents[u], project_relations=pr) except Exception as e: # noqa: BLE001 print(f" FAIL {u[:12]}: {e}", file=sys.stderr) continue payload["result"]["puuid"] = proj.project_uuid or "" (out_dir / f"{u}.json").write_text( json.dumps(payload, ensure_ascii=False, separators=(",", ":")), encoding="utf-8", ) s = getattr(write_sch_std, "last_stats", None) if s: print( f" {u[:12]}.json: wires={s.wires} libs={s.libs} " f"netflags={s.netflags} texts={s.texts} libs_unresolved={s.libs_unresolved}" ) return len(sch_uuids) def main(argv: list[str] | None = None) -> int: ap = argparse.ArgumentParser(description="EPRO2 → EasyEDA Std JSON exporter") ap.add_argument("project_dir", type=Path) g = ap.add_mutually_exclusive_group(required=True) g.add_argument("--all-pcb", action="store_true", help="convert every PCB doc to Std JSON") g.add_argument("--all-sch", action="store_true", help="convert every SCH_PAGE doc to Std JSON") g.add_argument("--all", action="store_true", help="convert both PCB and SCH_PAGE docs") ap.add_argument("--out", type=Path, default=Path("data/processed/std_json")) args = ap.parse_args(argv) proj = replay_project(args.project_dir) args.out.mkdir(parents=True, exist_ok=True) pr = ProjectRelations.build(proj) n = 0 if args.all_pcb or args.all: n += _convert_pcbs(proj, args.out, pr) if args.all_sch or args.all: n += _convert_schs(proj, args.out, pr) if n == 0: print("nothing to convert (no PCB / SCH_PAGE docs found)", file=sys.stderr) return 1 return 0 if __name__ == "__main__": raise SystemExit(main())