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-active-imports.ts

import { readFile, writeFile } from "node:fs/promises"
import { createHash } from "node:crypto"
import { any_circuit_element } from "circuit-json"
import { z } from "zod"
const auditConfiguration = z
  .object({
    revision: z.string(),
    manifestPath: z.string(),
    reportPath: z.string(),
  })
  .parse(
    process.argv[2]
      ? JSON.parse(await readFile(process.argv[2], "utf8"))
      : {
          revision: "A19",
          manifestPath: "evidence/active-supplier-inspection-manifest-A19.json",
          reportPath: "evidence/active-import-audit-A19.json",
        },
  )
const manifest = z
  .array(
    z.object({
      ref: z.string(),
      code: z.string(),
      import_path: z.string(),
      pins: z.record(z.string(), z.string()),
      nc: z.array(z.string()),
    }),
  )
  .parse(JSON.parse(await readFile(auditConfiguration.manifestPath, "utf8")))
const issues: string[] = []
const parts = []
let totalPcbTraces = 0
for (const part of manifest) {
  const path = `dist/tests/supplier-audit/${part.code}/circuit.json`
  const raw: unknown = JSON.parse(await readFile(path, "utf8"))
  const parsed = z.array(any_circuit_element).safeParse(raw)
  const sourceSha256 = createHash("sha256")
    .update(await readFile(part.import_path))
    .digest("hex")
  if (!parsed.success) {
    parts.push({
      code: part.code,
      ref: part.ref,
      sourceSha256,
      path,
      schemaIssues: parsed.error.issues,
      status: "failed",
    })
    issues.push(`${part.code}: generated circuit-json schema failure`)
    continue
  }
  const elements = parsed.data
  const localIssues: string[] = []
  const source = elements.find(
    (e) => e.type === "source_component" && e.name === part.ref,
  )
  const ports = []
  if (elements.filter((e) => e.type === "source_component").length !== 1)
    localIssues.push("Probe does not contain exactly one supplier component")
  const sheet = elements.find(
    (e) => e.type === "schematic_sheet" && e.name === "probe",
  )
  if (sheet?.type !== "schematic_sheet" || sheet.sheet_size !== "a4")
    localIssues.push("Required native A4 sheet absent")
  if (source?.type !== "source_component")
    localIssues.push("source component absent")
  else {
    if (!source.supplier_part_numbers?.jlcpcb?.includes(part.code))
      localIssues.push("supplier identity mismatch")
    const sourcePortCount = elements.filter(
      (e) =>
        e.type === "source_port" &&
        e.source_component_id === source.source_component_id,
    ).length
    if (sourcePortCount !== Object.keys(part.pins).length + part.nc.length)
      localIssues.push(
        "Extra or missing electrical source pins outside the reviewed manifest",
      )
    for (const alias of [...Object.keys(part.pins), ...part.nc]) {
      const port = elements.find(
        (e) =>
          e.type === "source_port" &&
          e.source_component_id === source.source_component_id &&
          (e.name === alias || e.port_hints?.includes(alias)),
      )
      if (port?.type !== "source_port") {
        localIssues.push(`${alias}: source port absent`)
        continue
      }
      const pcbPort = elements.find(
        (e) =>
          e.type === "pcb_port" && e.source_port_id === port.source_port_id,
      )
      if (pcbPort?.type !== "pcb_port") {
        localIssues.push(`${alias}: PCB port absent`)
        continue
      }
      const pads = elements.filter(
        (e) =>
          (e.type === "pcb_smtpad" || e.type === "pcb_plated_hole") &&
          e.pcb_port_id === pcbPort.pcb_port_id,
      )
      if (!pads.length)
        localIssues.push(`${alias}: electrical footprint pad absent`)
      if (
        part.pins[alias] &&
        !elements.some(
          (e) =>
            e.type === "schematic_port" &&
            e.source_port_id === port.source_port_id,
        )
      )
        localIssues.push(`${alias}: schematic port absent`)
      ports.push({
        alias,
        pinNumber: port.pin_number,
        pcbPortId: pcbPort.pcb_port_id,
        pads,
      })
    }
  }
  const pcbTraceCount = elements.filter((e) => e.type === "pcb_trace").length
  totalPcbTraces += pcbTraceCount
  if (pcbTraceCount) localIssues.push("Unexpected routed copper")
  const diagnostics = elements.filter(
    (e) => e.type.includes("warning") || e.type.includes("error"),
  )
  if (diagnostics.some((e) => e.type.includes("error")))
    localIssues.push("Native generated error diagnostics remain")
  parts.push({
    code: part.code,
    ref: part.ref,
    sourceSha256,
    path,
    ports,
    pcbTraceCount,
    diagnostics,
    issues: localIssues,
    status: localIssues.length
      ? "failed"
      : "electrical/schema pass; metadata and mechanical approval separate",
  })
  issues.push(...localIssues.map((i) => `${part.code} ${part.ref}: ${i}`))
}
const report = {
  revision: auditConfiguration.revision,
  supplierCount: parts.length,
  totalPcbTraces,
  issues,
  parts,
}
await writeFile(
  auditConfiguration.reportPath,
  JSON.stringify(report, null, 2) + "\n",
)
console.log(
  JSON.stringify(
    { supplierCount: parts.length, totalPcbTraces, issues },
    null,
    2,
  ),
)
if (issues.length) process.exitCode = 1