shibosoftwaredev/f1c100s-linux-nema17-stepper-controller

A four-layer NEMA 17 motor-controller PCB combining an Allwinner F1C100S Linux computer, USB‑PD/buck power system, W5500 Ethernet, STM32 real-time motion control, DRV8825 stepper driver, protection/temperature/current telemetry, USB interfaces, and local LED/buzzer diagnostics.

Version
0.4.56
License
unset
Stars
0

scripts/check-fab-stencil-regression.mjs

import fs from "node:fs"
import assert from "node:assert/strict"
import crypto from "node:crypto"
import path from "node:path"

const baseline = process.argv[2]
assert.ok(baseline, "Pass the preserved working v0.4.51 source directory")
const read = p => JSON.parse(fs.readFileSync(p,"utf8"))
const sha = p => crypto.createHash("sha256").update(fs.readFileSync(p)).digest("hex")
const before = read(`${baseline}/dist/index/circuit.json`), after = read("dist/index/circuit.json")
const of = (c,type) => c.filter(e => e.type === type)
assert.deepEqual(after.filter(e => !["pcb_solder_paste","source_project_metadata"].includes(e.type)),
  before.filter(e => !["pcb_solder_paste","source_project_metadata"].includes(e.type)),
  "all electrical, PCB, drill, mounting, CAD, schematic, constraint and warning records unchanged")
for (const file of ["routing/complete-routes.json","routing/expected-input.json","routing/physical-copper.json",
  "routing/native-features.json","routing/trace-constraints.json","board-layout.ts"])
  assert.equal(sha(file),sha(`${baseline}/${file}`),`unchanged ${file}`)
const old = new Map(of(before,"pcb_solder_paste").map(e => [e.pcb_solder_paste_id,e]))
const current = new Map(of(after,"pcb_solder_paste").map(e => [e.pcb_solder_paste_id,e]))
const added = [...current.values()].filter(e => !old.has(e.pcb_solder_paste_id))
const removed = [...old.values()].filter(e => !current.has(e.pcb_solder_paste_id))
assert.equal(added.length,96)
assert.equal(removed.length,4)
for (const [id,paste] of current) if (old.has(id)) assert.deepEqual(paste,old.get(id),"existing fitted-land apertures unchanged")
const src = new Map(of(before,"source_component").map(e => [e.source_component_id,e]))
const pcb = new Map(of(before,"pcb_component").map(e => [e.pcb_component_id,e]))
assert.deepEqual(removed.map(e => src.get(pcb.get(e.pcb_component_id).source_component_id).name).sort(),
  ["TP_MOTION_SWDIO","TP_MOTION_SWCLK","TP_MOTION_NRST","TP_MOTION_GND"].sort())
assert.ok(removed.every(e => src.get(pcb.get(e.pcb_component_id).source_component_id).ftype === "simple_test_point"))
let unchangedFirmwareAndModuleFiles = 0
function walk(dir) {
  for (const entry of fs.readdirSync(`${baseline}/${dir}`,{withFileTypes:true})) {
    if (["node_modules","build","dist","reports",".git"].includes(entry.name)) continue
    const p = path.join(dir,entry.name)
    if (entry.isDirectory()) walk(p)
    else { assert.equal(sha(p),sha(`${baseline}/${p}`),`unchanged ${p}`); unchangedFirmwareAndModuleFiles++ }
  }
}
walk("modules"); walk("firmware")
const circuitSha256 = sha("dist/index/circuit.json")
for (const f of ["stencil-artwork-audit.json","solder-process-audit.json","connectivity-audit.json",
  "coil-all-copper-preflight-meta.json","drill-clearance-regression.json","additional-dfm-review.json",
  "jlcpcb-stock-audit.json","bom-validation.json","assembly-motor-reference.json"]) {
  assert.equal(read(`reports/${f}`).circuitSha256,circuitSha256,`hash-bound ${f}`)
}
const artwork = read("reports/stencil-artwork-audit.json")
assert.deepEqual(artwork.failures,[])
assert.equal(artwork.populatedExposedSmtLands,674)
assert.equal(artwork.restoredRoundedLeadApertures,96)
assert.ok(artwork.roundedGerberChecks.every(p => p.boundsMatch && p.gerberObjects>0))
const negative = read("reports/stencil-baseline-negative.json")
assert.equal(negative.missingLandCount,96,"old board fails the new completeness gate")
assert.equal(negative.failures.filter(e=>e.rule==="paste-on-excluded-land").length,4)
const stock = read("reports/jlcpcb-stock-audit.json")
assert.ok(stock.parts.every(p=>p.status==="in-stock"))
const verification = read("verification.json")
assert.equal(verification.circuitSha256,circuitSha256)
assert.equal(verification.fabricationReady,false,"no fabrication approval invented")
assert.equal(read("reports/solder-process-audit.json").approvedForAssembly,false)
const log = name => fs.readFileSync(`reports/fab-stencil-${name}.log`,"utf8")
assert.match(log("build"),/Circuits\s+1 passed/)
assert.match(log("shorts"),/No shorts detected/)
assert.doesNotMatch(log("style"),/<SchematicPlacementIssues>/)
assert.doesNotMatch(log("typecheck"),/error TS\d+|Error:/)
assert.match(log("tests"),/43 pass[\s\S]*0 fail/)
assert.equal(read("reports/connectivity-audit.json").sourceFailures,0)
assert.equal(read("reports/connectivity-audit.json").disconnectedTerminalIslands,0)
assert.equal(read("reports/coil-all-copper-preflight-meta.json").failureCount,0)
assert.equal(read("reports/drill-clearance-regression.json").currentFindings,0)
assert.deepEqual(read("reports/additional-dfm-review.json").findings,[])
const report = {checkedAt:new Date().toISOString(),circuitSha256,baselineCircuitSha256:sha(`${baseline}/dist/index/circuit.json`),
  noRegressionsInReviewedScope:true,allNonPasteDesignRecordsUnchanged:true,firmwareAndModuleFilesUnchanged:unchangedFirmwareAndModuleFiles,
  restoredLeadApertures:96,removedBareTestPointApertures:4,populatedSmtLandsWithArtwork:674,missingSmtApertures:0,
  nativeErrors:of(after,"pcb_trace_error").length,nativeWarnings:after.filter(e=>e.type.endsWith("_warning")).length,
  testsPassed:43,testsFailed:0,stock:{parts:stock.uniquePartCount,populated:stock.populatedComponentCount,boards:stock.boardQuantity,issues:0,reserved:false},
  fabricationReady:false,pushed:false,
  remaining:["buck feedback layout and USB/Ethernet stackup/return-path qualification", "thermal-pad stencil/reflow and filled/capped-via CAM acceptance",
    "actual 17HS13-0404S1 rear fastening and adapted cable qualification", "first-article electrical, current, thermal and firmware/Linux bring-up"],
  note:"No failed buck-placement/routing experiment was installed. The only generated design change is paste artwork; paid four-layer via treatment is specified, not approved or ordered."}
fs.writeFileSync("reports/fab-stencil-regression.json",JSON.stringify(report,null,2)+"\n")
console.log(JSON.stringify(report))