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-round6-regression.mjs

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

const baseline="../../checkpoints/nema17-pre-round6-20261019-17Ug6H/restore-verified"
const read=p=>JSON.parse(fs.readFileSync(p,"utf8"))
const sha=p=>crypto.createHash("sha256").update(fs.readFileSync(p)).digest("hex")
const a=read(`${baseline}/dist/index/circuit.json`),b=read("dist/index/circuit.json")
const stable=x=>JSON.stringify(x,(_,v)=>typeof v==="number"?Math.round(v*1e8)/1e8:
  v&&typeof v==="object"&&!Array.isArray(v)?Object.fromEntries(Object.entries(v).sort(([a],[b])=>a.localeCompare(b))):v)
const unchangedTypes=new Set(["pcb_board","pcb_component","pcb_port","pcb_smtpad","pcb_plated_hole",
  "pcb_hole","pcb_keepout","cad_component","source_component","source_port","source_trace",
  "source_net","source_component_internal_connection"])
const records=c=>c.filter(e=>unchangedTypes.has(e.type)||e.type.startsWith("schematic_")||e.type.startsWith("pcb_silkscreen_")).map(stable).sort()
assert.deepEqual(records(a),records(b),"all electrical parts/connections, placements, outline, pads/holes, schematic sheets, legend and CAD unchanged")
for(const file of ["routing/expected-input.json","routing/native-features.json","routing-boundary.ts",
  "firmware/stm32/main.c","firmware/stm32/motion.c","firmware/stm32/motion.h","firmware/stm32/current.c",
  "firmware/motor-control.py","firmware/carrier.dtsi"])
  assert.equal(sha(file),sha(`${baseline}/${file}`),`${file} unchanged`)
const beforeRoutes=read(`${baseline}/routing/complete-routes.json`),afterRoutes=read("routing/complete-routes.json")
assert.equal(afterRoutes.length,beforeRoutes.length)
const old=new Map(beforeRoutes.map(e=>[e.pcb_trace_id,e]))
const changed=afterRoutes.filter(e=>stable(e)!==stable(old.get(e.pcb_trace_id)))
assert.deepEqual(changed.map(e=>[e.pcb_trace_id,e.source_trace_id]).sort(),[
  ["whole_board_1505","source_trace_157"],["whole_board_1506","source_trace_157"],
  ["whole_board_1712","source_trace_353"],
].sort(),"only the two targeted fanouts change; all other routed paths remain byte-equivalent")
const vias=c=>c.filter(e=>e.type==="pcb_via")
assert.equal(vias(b).length,335)
assert.ok(vias(b).every(v=>v.hole_diameter===.3&&v.outer_diameter===.45&&v.layers?.length===4))
const coordinate=v=>`${v.x.toFixed(6)},${v.y.toFixed(6)}`
const before=new Set(vias(a).map(coordinate)),after=new Set(vias(b).map(coordinate))
assert.deepEqual([...before].filter(x=>!after.has(x)).sort(),["-10.978800,-20.650000","1.750000,-0.200000"].sort())
assert.deepEqual([...after].filter(x=>!before.has(x)).sort(),["-10.118800,-20.710000","2.710000,-0.860000"].sort())
const solder=read("reports/solder-process-audit.json"),prior=read(`${baseline}/reports/solder-process-audit.json`)
assert.equal(solder.circuitSha256,sha("dist/index/circuit.json"))
assert.equal(solder.ordinaryPadDrillIntersections.length,13)
assert.equal(solder.thermalPadDrillIntersections.length,16)
const key=e=>`${e.part}/${e.pin}/${e.layer}/${e.x}/${e.y}`
const priorSet=new Set(prior.ordinaryPadDrillIntersections.map(key))
assert.ok(solder.ordinaryPadDrillIntersections.every(e=>priorSet.has(key(e))),"no new ordinary solder-land/drill intersections")
const currentSet=new Set(solder.ordinaryPadDrillIntersections.map(key))
assert.deepEqual(prior.ordinaryPadDrillIntersections.filter(e=>!currentSet.has(key(e))).map(e=>e.part).sort(),["R_INLINE_A","U_ETH"].sort())
const clock=b.find(e=>e.type==="source_trace"&&e.source_trace_id==="source_trace_157")
assert.equal(clock.max_length,15);assert.equal(clock.max_via_count,2)
assert.equal(b.filter(e=>e.type.endsWith("_error")).length,0)
const report={checkedAt:new Date().toISOString(),circuitSha256:sha("dist/index/circuit.json"),
  baselineCircuitSha256:sha(`${baseline}/dist/index/circuit.json`),unchangedAllPartsAndNets:true,
  unchangedPlacementsAndCad:true,unchangedSchematicAndLegend:true,unchangedBoardAndMounts:true,
  unchangedFirmware:true,unchangedRoutingGuard:true,changedPaths:changed.map(e=>e.pcb_trace_id),
  relocatedVias:2,throughVias:335,viaHoleMm:.3,viaPadMm:.45,clockLengthLimitMm:15,clockViaLimit:2,
  ordinaryIntersectionsBefore:15,ordinaryIntersectionsAfter:13,addedOrdinaryIntersections:0,
  thermalIntersectionsUnchanged:16,nativeErrors:0,nativeWarnings:b.filter(e=>e.type.endsWith("_warning")).length,
  localChangesNotPushed:true,fabricationReady:false}
fs.writeFileSync("reports/round6-regression.json",JSON.stringify(report,null,2)+"\n")
console.log(JSON.stringify(report))