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

import fs from "node:fs"
import assert from "node:assert/strict"
import crypto from "node:crypto"
const baseline="../../checkpoints/nema17-pre-round5-20261019-ywUpyW/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 geometryTypes=new Set(["pcb_board","pcb_component","pcb_port","pcb_smtpad","pcb_plated_hole","pcb_hole","pcb_keepout","cad_component"])
const electricalTypes=new Set(["source_component","source_port","source_trace","source_net","source_component_internal_connection"])
const normalized=(c,accept)=>c.filter(accept).map(stable).sort()
assert.deepEqual(normalized(a,e=>geometryTypes.has(e.type)),normalized(b,e=>geometryTypes.has(e.type)),"outline, every component/terminal/pad/hole/keepout and CAD record unchanged")
assert.deepEqual(normalized(a,e=>electricalTypes.has(e.type)),normalized(b,e=>electricalTypes.has(e.type)),"all source parts, nets, values and connections unchanged")
assert.deepEqual(normalized(a,e=>e.type.startsWith("schematic_")),normalized(b,e=>e.type.startsWith("schematic_")),"every schematic sheet record unchanged")
assert.equal(sha("routing/expected-input.json"),sha(`${baseline}/routing/expected-input.json`),"routing input guard retained unchanged")
assert.equal(sha("routing/native-features.json"),sha(`${baseline}/routing/native-features.json`),"thermal arrays unchanged")
for(const file of ["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 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 xyz=v=>`${v.x.toFixed(6)},${v.y.toFixed(6)}`
const before=new Set(vias(a).map(xyz)),after=new Set(vias(b).map(xyz))
assert.deepEqual([...before].filter(x=>!after.has(x)).sort(),["-14.400000,11.300000","-8.550000,-15.050000","18.770000,0.250000","-11.700000,-0.600000"].sort())
assert.deepEqual([...after].filter(x=>!before.has(x)).sort(),["-14.190000,11.200000","-9.220000,-15.910000","18.770000,0.550000","-11.490000,-0.370000"].sort())
const solder=read("reports/solder-process-audit.json")
assert.equal(solder.circuitSha256,sha("dist/index/circuit.json"))
assert.equal(solder.ordinaryPadDrillIntersections.length,15)
assert.equal(solder.thermalPadDrillIntersections.length,16)
const prior=read(`${baseline}/reports/review-round4-20261019-uZfHI8/review-evidence.json`)
const key=e=>`${e.part}/${e.pin}/${e.layer}/${e.x}/${e.y}`
const original=new Set(prior.ordinaryPopulatedPadDrillIntersections.map(key))
assert.ok(solder.ordinaryPadDrillIntersections.every(e=>original.has(key(e))),"no new ordinary populated-pad drill intersections")
const removed=prior.ordinaryPopulatedPadDrillIntersections.filter(e=>!new Set(solder.ordinaryPadDrillIntersections.map(key)).has(key(e))).map(e=>e.part).sort()
assert.deepEqual(removed,["C_BUZZER","R_ISEN_B","C_PD_VDD","R_BUCK_BOTTOM"].sort())
const parts=new Map(b.filter(e=>e.type==="source_component").map(e=>[e.source_component_id,e.name]))
const motor=b.find(e=>e.type==="source_component"&&e.name==="J_MOTOR")
for(const n of [1,2,3,4]) {
  const port=b.find(e=>e.type==="source_port"&&e.source_component_id===motor.source_component_id&&e.pin_number===n)
  const pcb=b.find(e=>e.type==="pcb_port"&&e.source_port_id===port.source_port_id)
  const label=b.find(e=>e.type==="pcb_silkscreen_text"&&e.text===String(n))
  assert.equal(label.layer,"bottom");assert.ok(Math.abs(label.anchor_position.x-pcb.x)<.001)
  assert.ok(label.anchor_position.y-pcb.y>1.8,"number clears the actual motor solder land")
}
const legend=read("reports/round5-legend-audit.json")
assert.equal(legend.labels,9);assert.deepEqual(legend.findings,[])
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`),unchangedOriginalParts:parts.size,
  unchangedConnectivity:true,unchangedAllPlacementsAndCad:true,unchangedSchematicRecords:true,
  unchangedBoardAndMounts:true,unchangedFirmware:true,unchangedRoutingInputGuard:true,
  vias:335,throughViaHoleMm:.3,throughViaPadMm:.45,relocatedVias:4,addedOrdinaryPadDrillIntersections:0,
  ordinaryIntersectionsBefore:19,ordinaryIntersectionsAfter:15,thermalIntersectionsUnchanged:16,
  readablePrintedLabels:9,printedMotorPinOrderMatchesPhysicalPads:true,nativeErrors:0,
  nativeWarnings:b.filter(e=>e.type.endsWith("_warning")).length,
  designRevisionLocalOnly:true,fabricationReady:false}
fs.writeFileSync("reports/round5-regression.json",JSON.stringify(report,null,2)+"\n")
console.log(JSON.stringify(report))