krishnax12/watchy-eink-smartwatch

Unfinished prototype (not production-ready): 4 native DRC errors, 2 display-flex intersections, and missing antenna radiator copper remain

Version
1.2.7
License
MIT
Stars
1

scripts/check-built.ts

import type { AnyCircuitElement } from "circuit-json";
import expectedNets from "../tests/fixtures/expected-nets.json";

const circuitPath = process.argv[2] ?? "dist/index/circuit.json";
const reportPath = process.argv[3] ?? "reports/routing-validation.json";
const circuit: AnyCircuitElement[] = await Bun.file(circuitPath).json();
const components = new Map(
	circuit
		.filter((element) => element.type === "source_component")
		.map((element) => [element.source_component_id, element.name]),
);
const ports = circuit.filter((element) => element.type === "source_port");
const expectedGroups = Object.values(expectedNets)
	.map((members) => [...members].sort().join("|"))
	.sort();
const actualGroups = new Map<string, string[]>();
const expectedPins = new Set(Object.values(expectedNets).flat());
for (const port of ports) {
	if (!port.source_component_id) continue;
	const pin = components.get(port.source_component_id) + "." + port.pin_number;
	if (!expectedPins.has(pin)) continue;
	const key = port.subcircuit_connectivity_map_key;
	if (!key) throw new Error("Missing connectivity key: " + pin);
	actualGroups.set(key, [...(actualGroups.get(key) ?? []), pin]);
}
const actualGroupsSorted = [...actualGroups.values()]
	.map((members) => members.sort().join("|"))
	.sort();
if (JSON.stringify(expectedGroups) !== JSON.stringify(actualGroupsSorted))
	throw new Error("Electrical connectivity changed");
const errors = circuit.filter((element) => element.type.endsWith("_error"));
const warnings = circuit.filter((element) => element.type.endsWith("_warning"));
const traces = circuit.filter((element) => element.type === "pcb_trace");
// The built-in DRC does not currently enforce every bus allowed-layer rule.
const topOnlyNetNames = new Set([
	"XTAL_40M_OUT",
	"XTAL_40M_FB",
	"XTAL_40M_IN",
	"XTAL_32K_P",
	"XTAL_32K_N",
	"RF_ANT",
	"RF_IN",
]);
const topOnlySourceTraces = new Map(
	circuit
		.filter((element) => element.type === "source_trace")
		.filter((trace) => trace.name && topOnlyNetNames.has(trace.name))
		.map((trace) => [trace.source_trace_id, trace.name]),
);
const violatedLayerRules = new Set<string>();
for (const trace of traces) {
	const netName = trace.source_trace_id
		? topOnlySourceTraces.get(trace.source_trace_id)
		: undefined;
	if (!netName) continue;
	if (
		trace.route.some(
			(point) =>
				point.route_type === "via" ||
				(point.route_type === "wire" && point.layer !== "top"),
		)
	)
		violatedLayerRules.add(netName);
}
const routingConstraintErrors = [...violatedLayerRules]
	.sort()
	.map(
		(netName) =>
			`${netName} violates the required top-only, no-via routing rule`,
	);
const vias = circuit.filter((element) => element.type === "pcb_via");
const report = {
	circuitPath,
	connectivityPreserved: true,
	nets: expectedGroups.length,
	connectedPins: expectedPins.size,
	traces: traces.length,
	vias: vias.length,
	routed: traces.length > 0,
	errors,
	routingConstraintErrors,
	warningCount: warnings.length,
};
await Bun.write(reportPath, JSON.stringify(report, null, 2) + "\n");
console.log(
	JSON.stringify(
		{
			...report,
			errors: errors.map((error) => ({
				type: error.type,
				message: "message" in error ? error.message : "Unknown error",
			})),
		},
		null,
		2,
	),
);
if (errors.length || routingConstraintErrors.length || traces.length === 0)
	process.exitCode = 1;