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`)