feat: add get_net_at_point tool for coordinate-based net lookup
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -331,3 +331,53 @@ def get_pin_net(schematic: Any, schematic_path: str, x_mm: float, y_mm: float) -
|
||||
pins = _find_pins_on_net(net_points, schematic_path, schematic)
|
||||
|
||||
return {"net": net, "pins": pins, "wires": wires_out, "query_point": {"x": x_mm, "y": y_mm}}
|
||||
|
||||
|
||||
def get_net_at_point(
|
||||
schematic: Any, schematic_path: str, x_mm: float, y_mm: float
|
||||
) -> Dict[str, Any]:
|
||||
"""Return the net name at the given coordinate, or null if none found.
|
||||
|
||||
Checks net label positions first (exact IU match within tolerance), then
|
||||
wire endpoints. Returns a dict with keys:
|
||||
- "net_name": str or None
|
||||
- "position": {"x": float, "y": float}
|
||||
- "source": "net_label" | "wire_endpoint" | None
|
||||
"""
|
||||
query_iu = _to_iu(x_mm, y_mm)
|
||||
position = {"x": x_mm, "y": y_mm}
|
||||
|
||||
# Build label map from schematic
|
||||
point_to_label, _ = _parse_virtual_connections(schematic, schematic_path)
|
||||
|
||||
# Check if query point is exactly on a net label / power symbol position
|
||||
label_name = point_to_label.get(query_iu)
|
||||
if label_name is not None:
|
||||
return {"net_name": label_name, "position": position, "source": "net_label"}
|
||||
|
||||
# Check if query point is on a wire endpoint
|
||||
all_wires = _parse_wires(schematic) if hasattr(schematic, "wire") else []
|
||||
if all_wires:
|
||||
_, iu_to_wires = _build_adjacency(all_wires)
|
||||
if query_iu in iu_to_wires:
|
||||
# Found a wire endpoint — trace the net to get the name
|
||||
adjacency, _ = _build_adjacency(all_wires)
|
||||
visited, net_points = _find_connected_wires(
|
||||
x_mm,
|
||||
y_mm,
|
||||
all_wires,
|
||||
iu_to_wires,
|
||||
adjacency,
|
||||
point_to_label=point_to_label,
|
||||
label_to_points=None,
|
||||
)
|
||||
if visited is not None:
|
||||
net: Optional[str] = None
|
||||
if net_points:
|
||||
for pt in net_points:
|
||||
net = point_to_label.get(pt)
|
||||
if net is not None:
|
||||
break
|
||||
return {"net_name": net, "position": position, "source": "wire_endpoint"}
|
||||
|
||||
return {"net_name": None, "position": position, "source": None}
|
||||
|
||||
@@ -382,6 +382,7 @@ class KiCADInterface:
|
||||
"get_schematic_pin_locations": self._handle_get_schematic_pin_locations,
|
||||
"get_net_connections": self._handle_get_net_connections,
|
||||
"get_wire_connections": self._handle_get_wire_connections,
|
||||
"get_net_at_point": self._handle_get_net_at_point,
|
||||
"get_pin_net": self._handle_get_pin_net,
|
||||
"run_erc": self._handle_run_erc,
|
||||
"generate_netlist": self._handle_generate_netlist,
|
||||
@@ -2504,6 +2505,40 @@ class KiCADInterface:
|
||||
logger.error(traceback.format_exc())
|
||||
return {"success": False, "message": str(e)}
|
||||
|
||||
def _handle_get_net_at_point(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""Return the net name at a given (x, y) coordinate, or null if none found."""
|
||||
logger.info("Getting net at point")
|
||||
try:
|
||||
from commands.wire_connectivity import get_net_at_point
|
||||
|
||||
schematic_path = params.get("schematicPath")
|
||||
if not schematic_path:
|
||||
return {"success": False, "message": "Missing required parameter: schematicPath"}
|
||||
|
||||
x = params.get("x")
|
||||
y = params.get("y")
|
||||
if x is None or y is None:
|
||||
return {"success": False, "message": "Missing required parameters: x and y"}
|
||||
|
||||
try:
|
||||
x, y = float(x), float(y)
|
||||
except (TypeError, ValueError):
|
||||
return {"success": False, "message": "Parameters x and y must be numeric"}
|
||||
|
||||
schematic = SchematicManager.load_schematic(schematic_path)
|
||||
if not schematic:
|
||||
return {"success": False, "message": "Failed to load schematic"}
|
||||
|
||||
result = get_net_at_point(schematic, schematic_path, x, y)
|
||||
return {"success": True, **result}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting net at point: {str(e)}")
|
||||
import traceback
|
||||
|
||||
logger.error(traceback.format_exc())
|
||||
return {"success": False, "message": str(e)}
|
||||
|
||||
def _handle_get_pin_net(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""Find net name and all connected pins for a reference+pin or coordinate query"""
|
||||
logger.info("Getting pin net")
|
||||
|
||||
@@ -1523,6 +1523,35 @@ SCHEMATIC_TOOLS = [
|
||||
"required": ["schematicPath", "x", "y"],
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "get_net_at_point",
|
||||
"title": "Get Net At Point",
|
||||
"description": (
|
||||
"Returns the net name at a given (x, y) coordinate in a schematic, "
|
||||
"or null if no net label or wire endpoint is present at that position. "
|
||||
"Checks net label positions first, then wire endpoints. "
|
||||
"Useful for quickly identifying what net occupies a specific coordinate "
|
||||
"without traversing the full wire graph."
|
||||
),
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"schematicPath": {
|
||||
"type": "string",
|
||||
"description": "Path to the schematic file (.kicad_sch)",
|
||||
},
|
||||
"x": {
|
||||
"type": "number",
|
||||
"description": "X coordinate in mm",
|
||||
},
|
||||
"y": {
|
||||
"type": "number",
|
||||
"description": "Y coordinate in mm",
|
||||
},
|
||||
},
|
||||
"required": ["schematicPath", "x", "y"],
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "get_pin_net",
|
||||
"title": "Get Pin Net",
|
||||
|
||||
Reference in New Issue
Block a user