fix: schematic pin connection reliability
- add_schematic_net_label: warn in description that coords must be exact pin endpoints; recommend connect_to_net instead - connect_to_net: stub wire direction now follows pin angle (was hardcoded +X) - pin_locator.py: add get_pin_angle() and _get_lib_id() helpers - new tool: get_schematic_pin_locations(schematicPath, reference) → returns exact x/y of every pin endpoint, so Claude can place labels correctly
This commit is contained in:
@@ -256,7 +256,16 @@ class ConnectionManager:
|
||||
return False
|
||||
|
||||
# Add a small wire stub from the pin (2.54mm = 0.1 inch, standard grid spacing)
|
||||
stub_end = [pin_loc[0] + 2.54, pin_loc[1]]
|
||||
# Stub direction follows the pin's outward angle from the PinLocator
|
||||
pin_angle_deg = getattr(locator, '_last_pin_angle', 0)
|
||||
try:
|
||||
pin_angle_deg = locator.get_pin_angle(schematic_path, component_ref, pin_name) or 0
|
||||
except Exception:
|
||||
pin_angle_deg = 0
|
||||
import math as _math
|
||||
angle_rad = _math.radians(pin_angle_deg)
|
||||
stub_end = [round(pin_loc[0] + 2.54 * _math.cos(angle_rad), 4),
|
||||
round(pin_loc[1] - 2.54 * _math.sin(angle_rad), 4)]
|
||||
|
||||
# Create wire stub using WireManager
|
||||
wire_success = WireManager.add_wire(schematic_path, pin_loc, stub_end)
|
||||
|
||||
@@ -179,6 +179,63 @@ class PinLocator:
|
||||
|
||||
return (rotated_x, rotated_y)
|
||||
|
||||
def _get_lib_id(self, schematic_path: Path, symbol_reference: str) -> Optional[str]:
|
||||
"""Helper: return the lib_id string for a placed symbol"""
|
||||
try:
|
||||
sch_key = str(schematic_path)
|
||||
if sch_key not in self._schematic_cache:
|
||||
self._schematic_cache[sch_key] = Schematic(sch_key)
|
||||
sch = self._schematic_cache[sch_key]
|
||||
for symbol in sch.symbol:
|
||||
if symbol.property.Reference.value == symbol_reference:
|
||||
return symbol.lib_id.value if hasattr(symbol, "lib_id") else None
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
|
||||
def get_pin_angle(
|
||||
self, schematic_path: Path, symbol_reference: str, pin_number: str
|
||||
) -> Optional[float]:
|
||||
"""
|
||||
Get the outward angle of a pin endpoint in degrees (0=right, 90=up, 180=left, 270=down).
|
||||
This is the direction a wire stub must extend to stay connected to the pin.
|
||||
|
||||
Returns angle in degrees, or None if pin not found.
|
||||
"""
|
||||
try:
|
||||
sch_key = str(schematic_path)
|
||||
if sch_key not in self._schematic_cache:
|
||||
self._schematic_cache[sch_key] = Schematic(sch_key)
|
||||
sch = self._schematic_cache[sch_key]
|
||||
|
||||
target_symbol = None
|
||||
for symbol in sch.symbol:
|
||||
if symbol.property.Reference.value == symbol_reference:
|
||||
target_symbol = symbol
|
||||
break
|
||||
|
||||
if not target_symbol:
|
||||
return None
|
||||
|
||||
symbol_at = target_symbol.at.value
|
||||
symbol_rotation = float(symbol_at[2]) if len(symbol_at) > 2 else 0.0
|
||||
|
||||
lib_id = target_symbol.lib_id.value if hasattr(target_symbol, "lib_id") else None
|
||||
if not lib_id:
|
||||
return None
|
||||
|
||||
pins = self.get_symbol_pins(schematic_path, lib_id)
|
||||
if pin_number not in pins:
|
||||
return None
|
||||
|
||||
# Pin definition angle + symbol rotation = absolute outward direction
|
||||
pin_def_angle = pins[pin_number].get("angle", 0)
|
||||
absolute_angle = (pin_def_angle + symbol_rotation) % 360
|
||||
return absolute_angle
|
||||
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def get_pin_location(
|
||||
self, schematic_path: Path, symbol_reference: str, pin_number: str
|
||||
) -> Optional[List[float]]:
|
||||
|
||||
@@ -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,
|
||||
"get_schematic_pin_locations": self._handle_get_schematic_pin_locations,
|
||||
"get_net_connections": self._handle_get_net_connections,
|
||||
"generate_netlist": self._handle_generate_netlist,
|
||||
"list_schematic_libraries": self._handle_list_schematic_libraries,
|
||||
@@ -1271,6 +1272,47 @@ class KiCADInterface:
|
||||
"errorDetails": traceback.format_exc(),
|
||||
}
|
||||
|
||||
def _handle_get_schematic_pin_locations(self, params):
|
||||
"""Return exact pin endpoint coordinates for a schematic component"""
|
||||
logger.info("Getting schematic pin locations")
|
||||
try:
|
||||
from pathlib import Path
|
||||
from commands.pin_locator import PinLocator
|
||||
|
||||
schematic_path = params.get("schematicPath")
|
||||
reference = params.get("reference")
|
||||
|
||||
if not all([schematic_path, reference]):
|
||||
return {"success": False, "message": "Missing required parameters: schematicPath, reference"}
|
||||
|
||||
locator = PinLocator()
|
||||
all_pins = locator.get_all_symbol_pins(Path(schematic_path), reference)
|
||||
|
||||
if not all_pins:
|
||||
return {"success": False, "message": f"No pins found for {reference} — check reference and schematic path"}
|
||||
|
||||
# Enrich with pin names and angles from the symbol definition
|
||||
pins_def = locator.get_symbol_pins(
|
||||
Path(schematic_path),
|
||||
locator._get_lib_id(Path(schematic_path), reference),
|
||||
) if hasattr(locator, "_get_lib_id") else {}
|
||||
|
||||
result = {}
|
||||
for pin_num, coords in all_pins.items():
|
||||
entry = {"x": coords[0], "y": coords[1]}
|
||||
if pin_num in pins_def:
|
||||
entry["name"] = pins_def[pin_num].get("name", pin_num)
|
||||
entry["angle"] = locator.get_pin_angle(Path(schematic_path), reference, pin_num) or 0
|
||||
result[pin_num] = entry
|
||||
|
||||
return {"success": True, "reference": reference, "pins": result}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting pin locations: {e}")
|
||||
import traceback
|
||||
logger.error(traceback.format_exc())
|
||||
return {"success": False, "message": str(e)}
|
||||
|
||||
def _handle_get_net_connections(self, params):
|
||||
"""Get all connections for a named net"""
|
||||
logger.info("Getting net connections")
|
||||
|
||||
@@ -1388,7 +1388,7 @@ SCHEMATIC_TOOLS = [
|
||||
{
|
||||
"name": "add_schematic_net_label",
|
||||
"title": "Add Net Label",
|
||||
"description": "Adds a net label to assign a name to a wire/net on the schematic.",
|
||||
"description": "Adds a net label at exact coordinates on a schematic wire or pin endpoint. WARNING: x/y must match an existing wire endpoint or pin endpoint exactly — placing the label even 0.01mm away from a pin will result in an unconnected pin ERC error. To connect a component pin to a net by reference and pin number (recommended), use connect_to_net instead.",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -1463,6 +1463,25 @@ SCHEMATIC_TOOLS = [
|
||||
"required": ["schematicPath", "netName"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "get_schematic_pin_locations",
|
||||
"title": "Get Schematic Pin Locations",
|
||||
"description": "Returns the exact absolute coordinates of all pins on a schematic component. Use this BEFORE placing net labels with add_schematic_net_label to get the correct x/y position for each pin endpoint.",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"schematicPath": {
|
||||
"type": "string",
|
||||
"description": "Path to the schematic file"
|
||||
},
|
||||
"reference": {
|
||||
"type": "string",
|
||||
"description": "Component reference designator (e.g., U1, R1, J2)"
|
||||
}
|
||||
},
|
||||
"required": ["schematicPath", "reference"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "generate_netlist",
|
||||
"title": "Generate Netlist",
|
||||
|
||||
Reference in New Issue
Block a user