Fix solver correctness and input-validation issues from code review
- Refuse the uniform contact model when the fills form multiple disconnected copper groups that each touch both terminals: the prescribed injection split is ill-posed and the grounded system was singular, silently returning garbage (e.g. negative gigaohms). connected_restrict now reports the component count; a power-balance backstop (SolverError) catches any other inconsistent solve. - Connect via/pad barrels to the nearest fill copper within the pad footprint (+1 cell) instead of only the exact center cell, so thermal-relief spokes still stitch layers; barrels that reach fill on fewer than two layers are warned about. ViaLink gains pad_nm (extracted from the padstack, JSON-roundtripped). - Validate dialog input on OK (layers, current > 0, cell > 0, parseable frequency, extra Cu >= 0) with an inline error instead of silently substituting defaults; parse_frequency raises on garbage; pipeline rejects i_test <= 0; choose_cell_size rejects non-positive overrides. - Warn when a contact part is dropped by the connectivity restriction; floor instead of truncate in cell_of; correct the uniform-model summary line; drop an unused variable; refresh plugin.json wording. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
"""M1 smoke probe: verify the IPC API surface against a live KiCad.
|
||||
|
||||
Run from the dev venv while KiCad is open with the board loaded:
|
||||
.venv\\Scripts\\python.exe smoke\\smoke_probe.py
|
||||
|
||||
Deliberately uses kipy directly (not board_io) so it works even if
|
||||
board_io has a bug. Prints: version, board path, selection contents,
|
||||
shapes summary, zones + filled polygon stats, stackup table.
|
||||
"""
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
from kipy import KiCad
|
||||
from kipy.board_types import BoardRectangle, BoardShape
|
||||
from kipy.proto.board.board_pb2 import BoardStackupLayerType
|
||||
from kipy.proto.board.board_types_pb2 import ZoneType
|
||||
from kipy.util.board_layer import canonical_name
|
||||
|
||||
|
||||
def mm(nm):
|
||||
return nm / 1e6
|
||||
|
||||
|
||||
def main():
|
||||
print("== connect ==")
|
||||
kicad = KiCad()
|
||||
kicad.ping()
|
||||
print("version:", kicad.get_version())
|
||||
try:
|
||||
print("check_version:", kicad.check_version())
|
||||
except Exception as e:
|
||||
print("check_version raised:", e)
|
||||
|
||||
board = kicad.get_board()
|
||||
print("board.name:", board.name)
|
||||
print("board_filename:", getattr(board.document, "board_filename", "?"))
|
||||
|
||||
print("\n== selection ==")
|
||||
sel = list(board.get_selection())
|
||||
print(f"{len(sel)} item(s) selected")
|
||||
for item in sel:
|
||||
line = f" {type(item).__name__}"
|
||||
if isinstance(item, BoardRectangle):
|
||||
tl, br = item.top_left, item.bottom_right
|
||||
line += (f" layer={canonical_name(item.layer)}"
|
||||
f" tl=({mm(tl.x):.2f}, {mm(tl.y):.2f})mm"
|
||||
f" br=({mm(br.x):.2f}, {mm(br.y):.2f})mm")
|
||||
elif isinstance(item, BoardShape):
|
||||
line += f" layer={canonical_name(item.layer)}"
|
||||
print(line)
|
||||
|
||||
print("\n== shapes (board-wide rectangles) ==")
|
||||
shapes = list(board.get_shapes())
|
||||
rect_shapes = [s for s in shapes if isinstance(s, BoardRectangle)]
|
||||
print(f"{len(shapes)} shapes total, {len(rect_shapes)} rectangles")
|
||||
for s in rect_shapes[:20]:
|
||||
tl, br = s.top_left, s.bottom_right
|
||||
print(f" rect on {canonical_name(s.layer)}: "
|
||||
f"({mm(tl.x):.2f}, {mm(tl.y):.2f}) - ({mm(br.x):.2f}, {mm(br.y):.2f}) mm")
|
||||
|
||||
print("\n== zones ==")
|
||||
for zone in board.get_zones():
|
||||
ztype = ZoneType.Name(zone.type)
|
||||
net = zone.net.name if zone.net is not None else "<none>"
|
||||
layers = [canonical_name(l) for l in zone.layers]
|
||||
print(f" zone '{zone.name}' type={ztype} net={net} "
|
||||
f"layers={layers} filled={zone.filled}")
|
||||
try:
|
||||
for layer, polys in zone.filled_polygons.items():
|
||||
narcs = 0
|
||||
nnodes = 0
|
||||
nholes = 0
|
||||
for p in polys:
|
||||
nnodes += len(p.outline.nodes)
|
||||
nholes += len(p.holes)
|
||||
narcs += sum(1 for n in p.outline.nodes if n.has_arc)
|
||||
print(f" fill on {canonical_name(layer)}: {len(polys)} "
|
||||
f"poly(s), {nnodes} outline nodes, {nholes} holes, "
|
||||
f"{narcs} arc nodes")
|
||||
except Exception:
|
||||
print(" filled_polygons FAILED:")
|
||||
traceback.print_exc()
|
||||
|
||||
print("\n== stackup ==")
|
||||
try:
|
||||
for sl in board.get_stackup().layers:
|
||||
tname = BoardStackupLayerType.Name(sl.type)
|
||||
lname = canonical_name(sl.layer) if sl.type == \
|
||||
BoardStackupLayerType.BSLT_COPPER else "-"
|
||||
print(f" {tname:18s} layer={lname:8s} thickness={sl.thickness} nm"
|
||||
f" enabled={sl.enabled}")
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
|
||||
print("\nsmoke probe DONE")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user