fix: harden get_wire_connections against edge cases and improve performance

- Add +1 safety margin to grid_radius to handle banker's rounding at cell
  boundaries in the spatial index
- Move symbol property guards inside per-symbol try/except to prevent
  AttributeError from aborting all pin processing
- Replace O(n) connected_points scan for pin matching with spatial index
  lookup (_frontier_has_neighbour), consistent with flood-fill approach
- Wrap float(x)/float(y) conversion with clear user-facing error message

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Eugene Mikhantyev
2026-03-14 14:20:56 +00:00
parent ddbfc62a49
commit b257bef89b

View File

@@ -2335,7 +2335,10 @@ class KiCADInterface:
if not all([schematic_path, x is not None, y is not None]):
return {"success": False, "message": "Missing required parameters: schematicPath, x, y"}
x, y = float(x), float(y)
try:
x, y = float(x), float(y)
except (TypeError, ValueError):
return {"success": False, "message": "Parameters x and y must be numeric"}
tolerance = 0.5
def points_coincide(p1, p2):
@@ -2379,7 +2382,7 @@ class KiCADInterface:
# Spatial index: grid-snapped dict for O(1) proximity lookup
import math
GRID = 0.05 # mm, matches KiCAD schematic grid
grid_radius = math.ceil(tolerance / GRID) # cells to check per axis
grid_radius = math.ceil(tolerance / GRID) + 1 # cells to check per axis (+1 safety margin for banker's rounding)
def _grid_key(x_coord, y_coord):
return (round(x_coord / GRID), round(y_coord / GRID))
@@ -2448,28 +2451,25 @@ class KiCADInterface:
processed_refs = set()
for symbol in schematic.symbol:
if not hasattr(symbol.property, "Reference"):
continue
ref = symbol.property.Reference.value
if ref.startswith("_TEMPLATE"):
continue
if ref in processed_refs:
continue
processed_refs.add(ref)
try:
if not hasattr(symbol, 'property') or not hasattr(symbol.property, "Reference"):
continue
ref = symbol.property.Reference.value
if ref.startswith("_TEMPLATE"):
continue
if ref in processed_refs:
continue
processed_refs.add(ref)
all_pins = locator.get_all_symbol_pins(Path(schematic_path), ref)
if not all_pins:
continue
for pin_num, pin_data in all_pins.items():
pin_loc = [pin_data[0], pin_data[1]]
for cp in connected_points:
if points_coincide(pin_loc, list(cp)):
key = (ref, pin_num)
if key not in seen:
seen.add(key)
pins.append({"component": ref, "pin": pin_num})
break
if _frontier_has_neighbour(pin_loc[0], pin_loc[1]):
key = (ref, pin_num)
if key not in seen:
seen.add(key)
pins.append({"component": ref, "pin": pin_num})
except Exception as e:
logger.warning(f"Error checking pins for {ref}: {e}")