feat: add connect_passthrough tool for FFC/ribbon cable pin-to-pin wiring

This commit is contained in:
Tom
2026-03-06 12:25:25 +01:00
parent 4c12096886
commit 11d7e49e74
4 changed files with 165 additions and 1 deletions

View File

@@ -291,6 +291,77 @@ class ConnectionManager:
logger.error(traceback.format_exc())
return False
@staticmethod
def connect_passthrough(
schematic_path: Path,
source_ref: str,
target_ref: str,
net_prefix: str = "PIN",
pin_offset: int = 0,
):
"""
Connect all pins of source_ref to matching pins of target_ref via shared net labels.
Useful for passthrough adapters: J1 pin N <-> J2 pin N on net {net_prefix}_{N}.
Args:
schematic_path: Path to .kicad_sch file
source_ref: Reference of the first connector (e.g., "J1")
target_ref: Reference of the second connector (e.g., "J2")
net_prefix: Prefix for generated net names (default: "PIN" -> PIN_1, PIN_2, ...)
pin_offset: Add this value to the pin number when building the net name (default 0)
Returns:
dict with 'connected' list and 'failed' list
"""
if not WIRE_MANAGER_AVAILABLE:
logger.error("WireManager/PinLocator not available")
return {"connected": [], "failed": ["WireManager unavailable"]}
locator = ConnectionManager.get_pin_locator()
if not locator:
return {"connected": [], "failed": ["PinLocator unavailable"]}
# Get all pins of source and target
src_pins = locator.get_all_symbol_pins(schematic_path, source_ref) or {}
tgt_pins = locator.get_all_symbol_pins(schematic_path, target_ref) or {}
if not src_pins:
return {"connected": [], "failed": [f"No pins found on {source_ref}"]}
if not tgt_pins:
return {"connected": [], "failed": [f"No pins found on {target_ref}"]}
connected = []
failed = []
for pin_num in sorted(src_pins.keys(), key=lambda x: int(x) if x.isdigit() else 0):
try:
net_name = f"{net_prefix}_{int(pin_num) + pin_offset}" if pin_num.isdigit() else f"{net_prefix}_{pin_num}"
ok_src = ConnectionManager.connect_to_net(
schematic_path, source_ref, pin_num, net_name
)
if not ok_src:
failed.append(f"{source_ref}/{pin_num}")
continue
if pin_num in tgt_pins:
ok_tgt = ConnectionManager.connect_to_net(
schematic_path, target_ref, pin_num, net_name
)
if not ok_tgt:
failed.append(f"{target_ref}/{pin_num}")
continue
else:
failed.append(f"{target_ref}/{pin_num} (pin not found)")
continue
connected.append(f"{source_ref}/{pin_num} <-> {target_ref}/{pin_num} [{net_name}]")
except Exception as e:
failed.append(f"{source_ref}/{pin_num}: {e}")
logger.info(f"connect_passthrough: {len(connected)} connected, {len(failed)} failed")
return {"connected": connected, "failed": failed}
@staticmethod
def get_net_connections(
schematic: Schematic, net_name: str, schematic_path: Optional[Path] = None

View File

@@ -375,6 +375,7 @@ class KiCADInterface:
"add_schematic_connection": self._handle_add_schematic_connection,
"add_schematic_net_label": self._handle_add_schematic_net_label,
"connect_to_net": self._handle_connect_to_net,
"connect_passthrough": self._handle_connect_passthrough,
"get_schematic_pin_locations": self._handle_get_schematic_pin_locations,
"get_net_connections": self._handle_get_net_connections,
"generate_netlist": self._handle_generate_netlist,
@@ -1272,6 +1273,39 @@ class KiCADInterface:
"errorDetails": traceback.format_exc(),
}
def _handle_connect_passthrough(self, params):
"""Connect all pins of source connector to matching pins of target connector"""
logger.info("Connecting passthrough between two connectors")
try:
from pathlib import Path
schematic_path = params.get("schematicPath")
source_ref = params.get("sourceRef")
target_ref = params.get("targetRef")
net_prefix = params.get("netPrefix", "PIN")
pin_offset = int(params.get("pinOffset", 0))
if not all([schematic_path, source_ref, target_ref]):
return {"success": False, "message": "Missing required parameters: schematicPath, sourceRef, targetRef"}
result = ConnectionManager.connect_passthrough(
Path(schematic_path), source_ref, target_ref, net_prefix, pin_offset
)
n_ok = len(result["connected"])
n_fail = len(result["failed"])
return {
"success": n_fail == 0,
"message": f"Passthrough complete: {n_ok} connected, {n_fail} failed",
"connected": result["connected"],
"failed": result["failed"],
}
except Exception as e:
logger.error(f"Error in connect_passthrough: {str(e)}")
import traceback
logger.error(traceback.format_exc())
return {"success": False, "message": str(e)}
def _handle_get_schematic_pin_locations(self, params):
"""Return exact pin endpoint coordinates for a schematic component"""
logger.info("Getting schematic pin locations")

View File

@@ -1483,7 +1483,38 @@ SCHEMATIC_TOOLS = [
}
},
{
"name": "generate_netlist",
"name": "connect_passthrough",
"title": "Connect Passthrough (Pin-to-Pin)",
"description": "Connects all pins of a source connector to the matching pins of a target connector using shared net labels. Ideal for passthrough adapters where J1 pin N connects directly to J2 pin N. Each pair gets a net label '{netPrefix}_{pinNumber}'. Use this instead of calling connect_to_net 15 times for FFC/ribbon cable passthroughs.",
"inputSchema": {
"type": "object",
"properties": {
"schematicPath": {
"type": "string",
"description": "Path to the schematic file"
},
"sourceRef": {
"type": "string",
"description": "Reference of the source connector (e.g., J1)"
},
"targetRef": {
"type": "string",
"description": "Reference of the target connector (e.g., J2)"
},
"netPrefix": {
"type": "string",
"description": "Prefix for generated net names, e.g. 'CSI' produces CSI_1, CSI_2, ... (default: PIN)"
},
"pinOffset": {
"type": "integer",
"description": "Add this value to the pin number when building net names (default: 0)"
}
},
"required": ["schematicPath", "sourceRef", "targetRef"]
}
},
{
"name": "generate_netlist",,
"title": "Generate Netlist",
"description": "Generates a netlist from the schematic showing all components and their net connections.",
"inputSchema": {