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-native-placement.ts

import { createHash } from "node:crypto"
import { readFile, writeFile } from "node:fs/promises"
import { gzipSync } from "node:zlib"
import { any_circuit_element } from "circuit-json"
import { z } from "zod"

const probes = [
  "coordinate-serialization-probe",
  "native-manual-placement-probe",
  "native-numeric-placement-probe",
]
const reports = []
for (const probe of probes) {
  const sourcePath = `tests/${probe}.circuit.tsx`
  const outputPath = `dist/tests/${probe}/circuit.json`
  const serialized = await readFile(outputPath, "utf8")
  await writeFile(`evidence/${probe}-A12.circuit.json`, serialized)
  const raw: unknown = JSON.parse(serialized)
  const parsed = z.array(any_circuit_element).safeParse(raw)
  const issues: string[] = []
  const components = []
  if (parsed.success) {
    const elements = parsed.data
    for (const element of elements) {
      if (element.type.endsWith("_error") || element.type.endsWith("_warning"))
        issues.push(`Unresolved ${element.type}`)
      if (element.type === "pcb_trace") issues.push("Unexpected routed copper")
      if (element.type !== "pcb_component") continue
      const source = elements.find(
        (candidate) =>
          candidate.type === "source_component" &&
          candidate.source_component_id === element.source_component_id,
      )
      if (source?.type !== "source_component") {
        issues.push("PCB component has no source")
        continue
      }
      if (!source.supplier_part_numbers?.jlcpcb?.includes("C23162"))
        issues.push(`${source.name}: unexpected supplier identity`)
      const expected =
        source.name === "R1"
          ? { x: -4, y: 3, rotation: 0 }
          : { x: 4, y: -3, rotation: 90 }
      if (
        probe === "native-numeric-placement-probe" &&
        (element.center.x !== expected.x ||
          element.center.y !== expected.y ||
          element.rotation !== expected.rotation)
      )
        issues.push(`${source.name}: component transform does not match input`)
      const pads = elements.filter(
        (candidate) =>
          candidate.type === "pcb_smtpad" &&
          candidate.pcb_component_id === element.pcb_component_id,
      )
      const ports = elements.filter(
        (candidate) =>
          candidate.type === "pcb_port" &&
          candidate.pcb_component_id === element.pcb_component_id,
      )
      if (pads.length !== 2 || ports.length !== 2)
        issues.push(`${source.name}: expected two electrical pads and ports`)
      for (const pad of pads) {
        if (pad.type !== "pcb_smtpad") continue
        if (pad.shape !== "rect") {
          issues.push(`${source.name}: unexpected pad shape`)
          continue
        }
        if (Math.hypot(pad.x - element.center.x, pad.y - element.center.y) > 1)
          issues.push(
            `${source.name}: pad lost its component-relative position`,
          )
        const expectedWidth = element.rotation === 90 ? 0.8640064 : 0.8064754
        const expectedHeight = element.rotation === 90 ? 0.8064754 : 0.8640064
        if (
          Math.abs(pad.width - expectedWidth) > 1e-8 ||
          Math.abs(pad.height - expectedHeight) > 1e-8
        )
          issues.push(`${source.name}: pad rotation or dimension changed`)
      }
      for (const port of ports) {
        if (port.type !== "pcb_port") continue
        const pad = pads.find(
          (candidate) =>
            candidate.type === "pcb_smtpad" &&
            candidate.pcb_port_id === port.pcb_port_id,
        )
        if (
          pad?.type !== "pcb_smtpad" ||
          pad.shape !== "rect" ||
          Math.hypot(pad.x - port.x, pad.y - port.y) > 1e-8
        )
          issues.push(`${source.name}: electrical port and pad disagree`)
      }
      components.push({ name: source.name, pcb: element, pads, ports })
    }
    if (probe === "native-numeric-placement-probe" && components.length !== 2)
      issues.push("Expected exactly two supplier components")
  }
  reports.push({
    probe,
    sourcePath,
    sourceSha256: createHash("sha256")
      .update(await readFile(sourcePath))
      .digest("hex"),
    outputSha256: createHash("sha256").update(serialized).digest("hex"),
    status:
      parsed.success && issues.length === 0 ? "passed" : "failed: retained",
    schemaIssues: parsed.success ? [] : parsed.error.issues,
    issues,
    components,
  })
}
const report = {
  revision: "A12",
  productPlacementStarted: false,
  officialDependenciesUnchanged: true,
  reports,
}
// Preserve complete union-schema diagnostics in a lossless archive rather
// than repeating hundreds of thousands of union branches in review JSON.
const serializedReport = JSON.stringify(report, null, 2) + "\n"
const completeReportPath = "evidence/native-placement-audit-A12-full.json.gz"
await writeFile(completeReportPath, gzipSync(serializedReport))
await writeFile(
  "evidence/native-placement-audit-A12.json",
  JSON.stringify(
    {
      revision: report.revision,
      productPlacementStarted: false,
      officialDependenciesUnchanged: true,
      completeReportPath,
      completeReportSha256: createHash("sha256")
        .update(serializedReport)
        .digest("hex"),
      reports: reports.map(({ schemaIssues, ...probeReport }) => ({
        ...probeReport,
        schemaIssueCount: schemaIssues.length,
        schemaDiagnostics: "Complete unfiltered issues in lossless archive",
      })),
    },
    null,
    2,
  ) + "\n",
)
console.log(
  JSON.stringify(
    reports.map(({ probe, status, schemaIssues, issues }) => ({
      probe,
      status,
      schemaIssueCount: schemaIssues.length,
      issues,
    })),
    null,
    2,
  ),
)
if (reports.some(({ status }) => status !== "passed")) process.exitCode = 1