ShiboSoftwareDev/f1c100s-linux-gameboy
Restores PCB assembly metadata by assigning part numbers, managing 3D CAD models and orientations, removing test-point bodies, and validating top-side SMT solder-paste features.
- Version
- 1.0.8
- License
- unset
- Stars
- 0
scripts/export-assembly.mjs
import fs from "node:fs"
const circuit = JSON.parse(fs.readFileSync("dist/index/circuit.json", "utf8"))
const sources = circuit.filter((item) => item.type === "source_component" && item.ftype !== "simple_test_point")
const pcbs = new Map(circuit.filter((item) => item.type === "pcb_component").map((item) => [item.source_component_id, item]))
const csv = (rows) => rows.map((row) => row.map((cell) => `"${String(cell ?? "").replaceAll('"', '""')}"`).join(",")).join("\n") + "\n"
const value = (part) => part.display_capacitance ?? part.display_resistance ?? part.display_inductance ?? part.manufacturer_part_number
const footprint = (part, pcb) => `${part.ftype} ${pcb.width.toFixed(2)}x${pcb.height.toFixed(2)}mm`
const jlc = (part) => part.supplier_part_numbers.jlcpcb[0]
const groups = new Map()
for (const part of sources) {
const pcb = pcbs.get(part.source_component_id)
if (!pcb || pcb.do_not_place) continue
const key = `${jlc(part)}|${part.manufacturer_part_number}|${footprint(part, pcb)}`
const group = groups.get(key) ?? { part, pcb, designators: [] }
group.designators.push(part.name)
groups.set(key, group)
}
const grouped = [...groups.values()].sort((a, b) => jlc(a.part).localeCompare(jlc(b.part), undefined, { numeric: true }))
const bomRows = [["Quantity", "Designators", "Value", "Manufacturer Part Number", "LCSC Part #", "Footprint", "Assembly Side"]]
const jlcBomRows = [["Comment", "Designator", "Footprint", "LCSC Part #"]]
for (const { part, pcb, designators } of grouped) {
const refs = designators.sort().join(",")
bomRows.push([designators.length, refs, value(part), part.manufacturer_part_number, jlc(part), footprint(part, pcb), "Top"])
jlcBomRows.push([part.manufacturer_part_number, refs, footprint(part, pcb), jlc(part)])
}
const placed = sources.map((part) => ({ part, pcb: pcbs.get(part.source_component_id) }))
.filter(({ pcb }) => pcb && !pcb.do_not_place)
.sort((a, b) => a.part.name.localeCompare(b.part.name, undefined, { numeric: true }))
const pnpRows = [["Designator", "Mid X", "Mid Y", "Layer", "Rotation", "Manufacturer Part Number", "LCSC Part #"]]
const jlcCplRows = [["Designator", "Mid X", "Mid Y", "Layer", "Rotation"]]
for (const { part, pcb } of placed) {
const row = [part.name, `${pcb.center.x.toFixed(4)}mm`, `${pcb.center.y.toFixed(4)}mm`, pcb.layer === "bottom" ? "Bottom" : "Top", pcb.rotation ?? 0]
pnpRows.push([...row, part.manufacturer_part_number, jlc(part)])
jlcCplRows.push(row)
}
fs.mkdirSync("fabrication", { recursive: true })
fs.writeFileSync("fabrication/bom.csv", csv(bomRows))
fs.writeFileSync("fabrication/jlcpcb-bom.csv", csv(jlcBomRows))
fs.writeFileSync("fabrication/pick-and-place.csv", csv(pnpRows))
fs.writeFileSync("fabrication/jlcpcb-cpl.csv", csv(jlcCplRows))
fs.copyFileSync("dist/index/circuit.json", "fabrication/circuit.json")
fs.copyFileSync("verification.json", "fabrication/verification.json")
console.log(`Exported ${placed.length} placements in ${grouped.length} BOM lines`)