imrishabh18/rp2040-motor-controller
A library of PCB component definitions covering MOSFETs, motor drivers, USB-PD control, sensors, current monitors, transistors, diodes, resistors, speakers, connectors, and mirrored bottom-side logos, with pin mappings, footprints, and 3D models.
- Version
- 1.0.55
- License
- unset
- Stars
- 5
scripts/validate-top-assembly.ts
import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
import { createHash } from "node:crypto";
const input = process.argv[2] ?? "dist/index/circuit.json";
const raw = readFileSync(input, "utf8");
const json: any[] = JSON.parse(raw);
const sources = new Map(json.filter(e => e.type === "source_component").map(e => [e.source_component_id, e]));
const parts = json.filter(e => e.type === "pcb_component" && sources.has(e.source_component_id));
const name = (part: any) => sources.get(part.source_component_id).name;
const bottom = parts.filter(e => e.layer === "bottom");
if (bottom.length !== 1 || name(bottom[0]) !== "U_ENCODER" || !bottom[0].do_not_place) {
throw new Error("Only the DNP encoder may remain on the bottom");
}
const testPoints = [...sources.values()].filter(e => e.ftype === "simple_test_point" || e.name.startsWith("TP_"));
if (testPoints.length) throw new Error("Dedicated test points remain");
const populated = parts.filter(e => !e.do_not_place && !/^MH[1-4]$/.test(name(e)));
if (populated.some(e => e.layer !== "top")) throw new Error("Factory assembly requires a bottom component");
for (const designator of ["U_PD_MON", "C_PD_MON", "C_ENCODER", "C_ENCODER_BULK"]) {
const part = parts.find(e => name(e) === designator);
if (!part || part.layer !== "top") throw new Error(`${designator} must be on top`);
if (Boolean(part.do_not_place) !== designator.startsWith("C_ENCODER")) {
throw new Error(`Unexpected population state for ${designator}`);
}
}
// Compare actual electrical edges using physical component pins, independently
// of regenerated source IDs and cached connectivity labels. Exclude only the
// seven deliberately removed test-point components.
function electricalPartition(circuit: any[]) {
const names = new Map(circuit.filter(e => e.type === "source_component").map(e => [e.source_component_id, e.name]));
const parent = new Map<string, string>();
const root = (id: string): string => {
if (!parent.has(id)) parent.set(id, id);
if (parent.get(id) !== id) parent.set(id, root(parent.get(id)!));
return parent.get(id)!;
};
for (const e of circuit) {
const ids: string[] = e.type === "source_trace" ? [...e.connected_source_port_ids, ...e.connected_source_net_ids]
: e.type === "source_component_internal_connection" ? e.source_port_ids : [];
for (const id of ids.slice(1)) parent.set(root(id), root(ids[0]!));
}
const groups = new Map<string, string[]>();
for (const e of circuit.filter(e => e.type === "source_port")) {
const component = names.get(e.source_component_id);
if (!component || component.startsWith("TP_")) continue;
const group = root(e.source_port_id);
groups.set(group, [...groups.get(group) ?? [], `${component}.${e.pin_number ?? e.name}`]);
}
return [...groups.values()].map(group => [...new Set(group)].sort().join("|")).sort();
}
let preservesFunctionalNetlist: boolean | null = null;
if (process.argv[3]) {
const baseline = JSON.parse(readFileSync(process.argv[3], "utf8"));
preservesFunctionalNetlist = JSON.stringify(electricalPartition(baseline)) === JSON.stringify(electricalPartition(json));
if (!preservesFunctionalNetlist) throw new Error("Functional pin connectivity changed beyond test-point removal");
}
const report = {
circuitJsonSha256: createHash("sha256").update(raw).digest("hex"),
factoryAssemblySide: "top", populatedTopComponents: populated.length, populatedBottomComponents: 0,
bottomComponents: bottom.map(e => ({ designator: name(e), doNotPlace: e.do_not_place, center: e.center })),
doNotPlaceComponents: parts.filter(e => e.do_not_place).map(name).sort(),
dedicatedTestPoints: testPoints.length, preservesFunctionalNetlist,
removedTestPoints: ["TP_SWCLK", "TP_GND", "TP_SWDIO", "TP_3V3", "TP_PD", "TP_VMOTOR", "TP_PD_GND"],
programming: "USB BOOT/RUN retained; external SWD probe pads removed",
};
mkdirSync("reports", { recursive: true });
writeFileSync("reports/single-sided-assembly.json", JSON.stringify(report, null, 2) + "\n");
console.log(JSON.stringify(report, null, 2));
