AnasSarkiz/usb-c-pd-brushed-motor
USB-C PD-powered motor controller with protected input, selectable 5/9/12 V buck supply, MCU qualification, PWM H-bridge drive, overvoltage sensing, regenerative energy-dump resistors, and safety interlocks.
- Version
- 0.0.1-a48-cloud-pipeline9-unrouted-c41d79a-305b1e7a
- License
- unset
- Stars
- 0
scripts/audit-supplier-pnp.ts
import { createHash } from "node:crypto"
import { readFile, writeFile } from "node:fs/promises"
import { any_circuit_element } from "circuit-json"
import { z } from "zod"
import {
convertCircuitJsonToPickAndPlaceCsv,
convertCircuitJsonToPickAndPlaceRows,
type PickAndPlaceRotationWarning,
} from "circuit-json-to-pnp-csv"
const artifact = await readFile("dist/index/circuit.json", "utf8")
const elements = z.array(any_circuit_element).parse(JSON.parse(artifact))
const expectedReferences = elements
.filter((element) => element.type === "source_component")
.map((element) => element.name)
.sort()
if (expectedReferences.length !== 140)
throw new Error("Supplier orientation audit requires the full140-part board")
const manualReferences = new Set([
"C18",
"RV1",
"SW1",
"J2",
"TP1",
"TP2",
"TP3",
"TP4",
"TP5",
"TP6",
"TP7",
])
const sources = elements.filter(
(element) => element.type === "source_component",
)
// This converter input selects the documented SMT assembly operation only.
// The full native artifact and every supplier definition remain unchanged.
const automaticElements = elements.filter(
(element) =>
element.type !== "pcb_component" ||
!manualReferences.has(
sources.find(
(source) => source.source_component_id === element.source_component_id,
)?.name ?? "",
),
)
const automaticRows = convertCircuitJsonToPickAndPlaceRows(automaticElements, {
supplier: "jlcpcb",
requireSupplierRotation: true,
})
if (automaticRows.length !== 129)
throw new Error("The documented SMT operation must contain129 parts")
const warnings: PickAndPlaceRotationWarning[] = []
const options = {
supplier: "jlcpcb",
onRotationWarning: (warning: PickAndPlaceRotationWarning) => {
warnings.push(warning)
console.warn(warning.message)
},
} as const
const rows = convertCircuitJsonToPickAndPlaceRows(elements, options)
for (const warning of warnings) {
if (
!/^TP[1-7]$/.test(warning.designator) ||
warning.reason !== "missing_pin1_location"
)
throw new Error(`Unreviewed supplier orientation: ${warning.message}`)
}
for (const warning of warnings) {
const source = sources.find((source) => source.name === warning.designator)
if (
!source ||
source.manufacturer_part_number !== "5015" ||
JSON.stringify(source.supplier_part_numbers?.jlcpcb) !==
JSON.stringify(["C2906768"])
)
throw new Error(
"Accepted manual orientation must refer to the exact Keystone5015 import",
)
const ports = elements
.filter((element) => element.type === "source_port")
.filter((port) => port.source_component_id === source.source_component_id)
if (ports.length !== 1 || ports[0].pin_number !== 1)
throw new Error(
"A manual contact orientation advisory requires exactly one electrical pin",
)
}
const actualAutomaticRows = rows.filter(
(row) => !manualReferences.has(row.designator),
)
if (JSON.stringify(actualAutomaticRows) !== JSON.stringify(automaticRows))
throw new Error(
"Full native export differs from verified automatic assembly rows",
)
const references = rows.map((row) => row.designator).sort()
if (JSON.stringify(references) !== JSON.stringify(expectedReferences))
throw new Error("Native pick-and-place reference set differs from the board")
for (const row of rows) {
if (
!Number.isFinite(row.mid_x) ||
!Number.isFinite(row.mid_y) ||
!Number.isFinite(row.rotation) ||
row.rotation < 0 ||
row.rotation >= 360
)
throw new Error(`${row.designator}: invalid native placement coordinates`)
}
const csv = convertCircuitJsonToPickAndPlaceCsv(elements, options)
await writeFile("evidence/supplier-pnp-preview-A22.csv", csv)
await writeFile(
"evidence/supplier-pnp-audit-A22.json",
JSON.stringify(
{
artifactSha256: createHash("sha256").update(artifact).digest("hex"),
csvSha256: createHash("sha256").update(csv).digest("hex"),
referenceCount: rows.length,
supplier: options.supplier,
automaticReferenceCount: automaticRows.length,
automaticSupplierRotationsVerified: true,
acceptedManualRotationWarnings: warnings,
manualOrientationReview:
"TP1..TP7 are single-electrical-pin, nonpolar contacts, omitted from automatic assembly; board rotation is the documented manual-fit intent; mechanical fit requires the prototype test plan",
rows,
status:
"Orientation and placement data only; copper/fabrication approval requires all routed checks",
},
null,
2,
) + "\n",
)
console.log(
`All${automaticRows.length} automatic supplier rotations verified; seven manual contact advisories retained`,
)