| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798 |
- """Regenerate catalog.json from GxEPD2's example headers. Re-run whenever
- the vendored GxEPD2 version is bumped.
- """
- import json
- import pathlib
- import re
- HERE = pathlib.Path(__file__).parent
- SOURCES = [
- HERE / "GxEPD2_display_selection.h",
- HERE / "GxEPD2_display_selection_added.h",
- ]
- VENDOR_SRC = HERE.parent.parent / "third_party" / "GxEPD2" / "src"
- OUT = HERE / "catalog.json"
- def find_source(driver: str) -> str:
- """Path (relative to vendor/src) of the .cpp implementing `driver`."""
- matches = list(VENDOR_SRC.glob(f"**/{driver}.cpp"))
- if len(matches) != 1:
- raise ValueError(f"expected exactly one {driver}.cpp under {VENDOR_SRC}, found {matches}")
- return str(matches[0].relative_to(VENDOR_SRC))
- LINE_RE = re.compile(
- r"^//?\s*"
- r"(?P<wrapper>GxEPD2_(?:BW|BW_SHM|3C|4C|7C))\s*<\s*"
- r"(?P<driver>GxEPD2_[A-Za-z0-9_]+)\s*,\s*(?P=driver)\s*::\s*HEIGHT"
- r"(?:\s*/\s*(?P<divisor>\d+))?\s*>\s*"
- r"display\s*\(\s*(?P=driver)\s*\((?P<args>[^)]*)\)\s*\)\s*;"
- r"\s*(?://\s*(?P<comment>.*))?$"
- )
- def split_args(args: str):
- return [a.strip() for a in args.split(",") if a.strip()]
- def main():
- # (driver, wrapper) -> best entry seen.
- by_driver_wrapper = {}
- skipped_nonstandard = []
- for src in SOURCES:
- text = src.read_text()
- for line in text.splitlines():
- m = LINE_RE.match(line.strip())
- if not m:
- continue
- driver = m.group("driver")
- wrapper = m.group("wrapper")
- args = split_args(m.group("args"))
- comment = (m.group("comment") or "").strip()
- divisor = int(m.group("divisor") or 1)
- # 4 args: standard single-CS. 5: dual-CS "master+slave". Else skip.
- if len(args) not in (4, 5):
- skipped_nonstandard.append((driver, len(args), comment))
- continue
- key = (driver, wrapper)
- entry = {
- "driver": driver,
- "wrapper": wrapper,
- "comment": comment,
- "height_divisor": divisor,
- "ctor_args": len(args),
- }
- existing = by_driver_wrapper.get(key)
- if existing:
- # Keep the smallest height_divisor and the longest comment seen.
- entry["height_divisor"] = min(existing["height_divisor"], divisor)
- if len(comment) <= len(existing["comment"]):
- entry["comment"] = existing["comment"]
- by_driver_wrapper[key] = entry
- # Model id = driver class name, disambiguated with a wrapper suffix
- # when a driver has more than one wrapper.
- drivers_seen = {}
- for (driver, _wrapper) in by_driver_wrapper:
- drivers_seen.setdefault(driver, []).append(_wrapper)
- catalog = {}
- for (driver, wrapper), entry in by_driver_wrapper.items():
- wrapper_suffix = wrapper.removeprefix("GxEPD2_")
- model_id = driver if len(drivers_seen[driver]) == 1 else f"{driver}_{wrapper_suffix}"
- entry["source"] = find_source(driver)
- catalog[model_id] = entry
- OUT.write_text(json.dumps(catalog, indent=2, sort_keys=True) + "\n")
- dual_cs = sum(1 for e in catalog.values() if e["ctor_args"] == 5)
- print(f"wrote {len(catalog)} panel models ({dual_cs} dual-CS) to {OUT}")
- print(f"skipped {len(skipped_nonstandard)} non-standard-constructor drivers (need special-casing later):")
- for driver, nargs, comment in skipped_nonstandard:
- print(f" {driver} ({nargs} ctor args) // {comment}")
- if __name__ == "__main__":
- main()
|