app
Adom Desktop - KiCad Bridge
Public Made by Adomby adom
Reference implementation of the KiCad bridge — multi-instance Python server, forward path via kicad-cli, reverse path via in-process plugin. Most complex of the three bundled bridges.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
"""Subprocess wrappers for KiCad IPC Rust binaries."""
import os
import re
import subprocess
from pathlib import Path
# Paths to IPC binaries (built from Rust examples)
_EXAMPLES_DIR = (
Path(__file__).resolve().parents[3]
/ "desktop"
/ "src-tauri"
/ "target"
/ "release"
/ "examples"
)
_TEST_BINARY = _EXAMPLES_DIR / "test_kicad_ipc.exe"
_LAYER_BINARY = _EXAMPLES_DIR / "ipc_set_layer.exe"
def ensure_dummy_socket():
"""Create the dummy socket file for the Windows NNG workaround."""
temp = os.environ.get("TEMP", os.environ.get("TMP", ""))
if temp:
sock_dir = Path(temp) / "kicad"
sock_dir.mkdir(exist_ok=True)
sock_file = sock_dir / "api.sock"
if not sock_file.exists():
sock_file.touch()
def has_test_binary() -> bool:
return _TEST_BINARY.exists()
def has_layer_binary() -> bool:
return _LAYER_BINARY.exists()
def run_ipc_test() -> dict:
"""Run the full IPC test binary and parse sectioned output.
Returns dict keyed by test number (int) with the text for that section.
"""
if not has_test_binary():
return {}
ensure_dummy_socket()
try:
result = subprocess.run(
[str(_TEST_BINARY)],
capture_output=True,
text=True,
timeout=30,
)
return _parse_test_output(result.stdout)
except Exception as e:
return {0: f"IPC test failed: {e}"}
def set_active_layer(layer_id: int) -> dict:
"""Set KiCad's active layer via IPC."""
if not has_layer_binary():
return {"success": False, "output": "ipc_set_layer.exe not found"}
ensure_dummy_socket()
try:
result = subprocess.run(
[str(_LAYER_BINARY), str(layer_id)],
capture_output=True,
text=True,
timeout=10,
)
output = result.stdout.strip() or result.stderr.strip()
return {"success": result.returncode == 0, "output": output}
except Exception as e:
return {"success": False, "output": str(e)}
def _parse_test_output(stdout: str) -> dict:
"""Parse test_kicad_ipc.exe output into sections by [N] headers."""
sections = {}
current_key = None
current_lines = []
for line in stdout.splitlines():
match = re.match(r"\[(\d+)\]", line)
if match:
if current_key is not None:
sections[current_key] = "\n".join(current_lines)
current_key = int(match.group(1))
current_lines = [line]
elif current_key is not None:
current_lines.append(line)
if current_key is not None:
sections[current_key] = "\n".join(current_lines)
return sections