fix: net-class API compatibility for KiCad 10 and trackWidth alias (#144)
`create_netclass` previously called legacy NETCLASSES.Find/.Add APIs that were removed in KiCad 10. NETCLASS getters like GetMicroViaDiameter that also no longer exist crashed any subsequent edit. The schema accepted `traceWidth` but the handler only read `trackWidth`, so requests using the documented field silently produced no-op netclasses. - Add a KiCad-version-defensive shim around netclass creation that prefers the new netclasses_map dict-style API and falls back to legacy. - Introduce _safe_get/_safe_set helpers so missing getters/setters on KiCad 10 NETCLASS objects fail gracefully instead of raising. - Accept both traceWidth and trackWidth in the request payload. Net classes still need to be written into .kicad_pro directly because KiCad 10 stores them in net_settings.classes and the MCP only writes to .kicad_pcb; that's a separate fix.
This commit is contained in:
@@ -48,11 +48,20 @@ class RoutingCommands:
|
|||||||
net = pcbnew.NETINFO_ITEM(self.board, name)
|
net = pcbnew.NETINFO_ITEM(self.board, name)
|
||||||
self.board.Add(net)
|
self.board.Add(net)
|
||||||
|
|
||||||
# Set net class if provided
|
# Set net class if provided — defensive against KiCad 6/7 vs KiCad 9/10 API.
|
||||||
if net_class:
|
if net_class:
|
||||||
net_classes = self.board.GetNetClasses()
|
net_classes = self.board.GetNetClasses()
|
||||||
if net_classes.Find(net_class):
|
resolved = None
|
||||||
net.SetClass(net_classes.Find(net_class))
|
if hasattr(net_classes, "Find"):
|
||||||
|
resolved = net_classes.Find(net_class)
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
if net_class in net_classes:
|
||||||
|
resolved = net_classes[net_class]
|
||||||
|
except Exception:
|
||||||
|
resolved = None
|
||||||
|
if resolved is not None:
|
||||||
|
net.SetClass(resolved)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"success": True,
|
"success": True,
|
||||||
@@ -1028,7 +1037,8 @@ class RoutingCommands:
|
|||||||
|
|
||||||
name = params.get("name")
|
name = params.get("name")
|
||||||
clearance = params.get("clearance")
|
clearance = params.get("clearance")
|
||||||
track_width = params.get("trackWidth")
|
# Schema exposes "traceWidth"; older callers may send "trackWidth". Accept both.
|
||||||
|
track_width = params.get("traceWidth", params.get("trackWidth"))
|
||||||
via_diameter = params.get("viaDiameter")
|
via_diameter = params.get("viaDiameter")
|
||||||
via_drill = params.get("viaDrill")
|
via_drill = params.get("viaDrill")
|
||||||
uvia_diameter = params.get("uviaDiameter")
|
uvia_diameter = params.get("uviaDiameter")
|
||||||
@@ -1044,34 +1054,52 @@ class RoutingCommands:
|
|||||||
"errorDetails": "name parameter is required",
|
"errorDetails": "name parameter is required",
|
||||||
}
|
}
|
||||||
|
|
||||||
# Get net classes
|
# Get net classes — KiCad 6/7 returns NETCLASSES with .Find/.Add;
|
||||||
|
# KiCad 9/10 returns a netclasses_map (SWIG-wrapped std::map) that is dict-like.
|
||||||
net_classes = self.board.GetNetClasses()
|
net_classes = self.board.GetNetClasses()
|
||||||
|
|
||||||
# Create new net class if it doesn't exist
|
existing = None
|
||||||
if not net_classes.Find(name):
|
if hasattr(net_classes, "Find"):
|
||||||
|
existing = net_classes.Find(name)
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
if name in net_classes:
|
||||||
|
existing = net_classes[name]
|
||||||
|
except Exception:
|
||||||
|
existing = None
|
||||||
|
|
||||||
|
if existing is None:
|
||||||
netclass = pcbnew.NETCLASS(name)
|
netclass = pcbnew.NETCLASS(name)
|
||||||
|
if hasattr(net_classes, "Add"):
|
||||||
net_classes.Add(netclass)
|
net_classes.Add(netclass)
|
||||||
else:
|
else:
|
||||||
netclass = net_classes.Find(name)
|
net_classes[name] = netclass
|
||||||
|
else:
|
||||||
|
netclass = existing
|
||||||
|
|
||||||
# Set properties
|
# Set properties
|
||||||
scale = 1000000 # mm to nm
|
scale = 1000000 # mm to nm
|
||||||
if clearance is not None:
|
|
||||||
netclass.SetClearance(int(clearance * scale))
|
# Defensive setters — KiCad 10's NETCLASS dropped some legacy mutators.
|
||||||
if track_width is not None:
|
def _safe_set(method_name, value):
|
||||||
netclass.SetTrackWidth(int(track_width * scale))
|
if value is None:
|
||||||
if via_diameter is not None:
|
return
|
||||||
netclass.SetViaDiameter(int(via_diameter * scale))
|
method = getattr(netclass, method_name, None)
|
||||||
if via_drill is not None:
|
if method is None:
|
||||||
netclass.SetViaDrill(int(via_drill * scale))
|
return
|
||||||
if uvia_diameter is not None:
|
try:
|
||||||
netclass.SetMicroViaDiameter(int(uvia_diameter * scale))
|
method(int(value * scale))
|
||||||
if uvia_drill is not None:
|
except Exception:
|
||||||
netclass.SetMicroViaDrill(int(uvia_drill * scale))
|
pass
|
||||||
if diff_pair_width is not None:
|
|
||||||
netclass.SetDiffPairWidth(int(diff_pair_width * scale))
|
_safe_set("SetClearance", clearance)
|
||||||
if diff_pair_gap is not None:
|
_safe_set("SetTrackWidth", track_width)
|
||||||
netclass.SetDiffPairGap(int(diff_pair_gap * scale))
|
_safe_set("SetViaDiameter", via_diameter)
|
||||||
|
_safe_set("SetViaDrill", via_drill)
|
||||||
|
_safe_set("SetMicroViaDiameter", uvia_diameter)
|
||||||
|
_safe_set("SetMicroViaDrill", uvia_drill)
|
||||||
|
_safe_set("SetDiffPairWidth", diff_pair_width)
|
||||||
|
_safe_set("SetDiffPairGap", diff_pair_gap)
|
||||||
|
|
||||||
# Add nets to net class
|
# Add nets to net class
|
||||||
netinfo = self.board.GetNetInfo()
|
netinfo = self.board.GetNetInfo()
|
||||||
@@ -1081,19 +1109,29 @@ class RoutingCommands:
|
|||||||
net = nets_map[net_name]
|
net = nets_map[net_name]
|
||||||
net.SetClass(netclass)
|
net.SetClass(netclass)
|
||||||
|
|
||||||
|
# Defensive accessors — KiCad 10's NETCLASS dropped some legacy getters.
|
||||||
|
def _safe_get(method_name):
|
||||||
|
method = getattr(netclass, method_name, None)
|
||||||
|
if method is None:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
return method() / scale
|
||||||
|
except Exception:
|
||||||
|
return None
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"success": True,
|
"success": True,
|
||||||
"message": f"Created net class: {name}",
|
"message": f"Created net class: {name}",
|
||||||
"netClass": {
|
"netClass": {
|
||||||
"name": name,
|
"name": name,
|
||||||
"clearance": netclass.GetClearance() / scale,
|
"clearance": _safe_get("GetClearance"),
|
||||||
"trackWidth": netclass.GetTrackWidth() / scale,
|
"trackWidth": _safe_get("GetTrackWidth"),
|
||||||
"viaDiameter": netclass.GetViaDiameter() / scale,
|
"viaDiameter": _safe_get("GetViaDiameter"),
|
||||||
"viaDrill": netclass.GetViaDrill() / scale,
|
"viaDrill": _safe_get("GetViaDrill"),
|
||||||
"uviaDiameter": netclass.GetMicroViaDiameter() / scale,
|
"uviaDiameter": _safe_get("GetMicroViaDiameter"),
|
||||||
"uviaDrill": netclass.GetMicroViaDrill() / scale,
|
"uviaDrill": _safe_get("GetMicroViaDrill"),
|
||||||
"diffPairWidth": netclass.GetDiffPairWidth() / scale,
|
"diffPairWidth": _safe_get("GetDiffPairWidth"),
|
||||||
"diffPairGap": netclass.GetDiffPairGap() / scale,
|
"diffPairGap": _safe_get("GetDiffPairGap"),
|
||||||
"nets": nets,
|
"nets": nets,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user