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

import fs from "node:fs"
import crypto from "node:crypto"
import assert from "node:assert/strict"
const base="../../checkpoints/nema17-round7-validated-usb-20261020-ed31KT/restore-verified"
const read=p=>JSON.parse(fs.readFileSync(p,"utf8"))
const hash=p=>crypto.createHash("sha256").update(fs.readFileSync(p)).digest("hex")
const before=read(`${base}/dist/index/circuit.json`),after=read("dist/index/circuit.json")
const of=(c,t)=>c.filter(e=>e.type===t)
const replacements=new Set(["C_CURRENT_A_SUPPLY","C_CURRENT_B_SUPPLY"])
function parts(c){return of(c,"source_component").map(e=>replacements.has(e.name)?(({manufacturer_part_number,supplier_part_numbers,max_voltage_rating,...p})=>p)(e):e)}
assert.deepEqual(parts(after),parts(before),"no component removal/addition or unintended BOM/value change")
for(const name of replacements){const p=of(after,"source_component").find(e=>e.name===name);assert.equal(p.manufacturer_part_number,"TCC0201X7R104K100ZT");assert.deepEqual(p.supplier_part_numbers.jlcpcb,["C7393837"]);assert.equal(p.max_voltage_rating,10)}
for(const t of ["source_port","source_trace","source_net","source_component_internal_connection"])
  assert.deepEqual(of(after,t),of(before,t),`all original electrical connectivity and constraints unchanged: ${t}`)
const withoutThickness=({thickness,...p})=>p
assert.deepEqual(of(after,"pcb_board").map(withoutThickness),of(before,"pcb_board").map(withoutThickness),"same outline, layers and routing rules")
assert.equal(of(after,"pcb_board")[0].thickness,1.6)
for(const t of ["pcb_hole","pcb_plated_hole"])assert.deepEqual(of(after,t),of(before,t),`same ${t}`)
const names=c=>new Map(of(c,"source_component").map(e=>[e.source_component_id,e.name]))
const bn=names(before),an=names(after),old=new Map(of(before,"pcb_component").map(p=>[bn.get(p.source_component_id),p]))
const moves=read("round8-placement.json")
for(const p of of(after,"pcb_component")){
  const name=an.get(p.source_component_id),q=old.get(name),move=moves[name]
  if(move){assert.ok(Math.hypot(p.center.x-move.x,p.center.y-move.y)<1e-6);assert.equal(p.rotation,move.rotation);assert.equal(p.layer,move.layer)}
  else for(const key of ["center","rotation","width","height","layer","do_not_place"])assert.deepEqual(p[key],q[key],`${name} ${key} retained`)
}
for(const t of ["schematic_component","schematic_port","schematic_trace","schematic_sheet","pcb_silkscreen_text"])
  assert.deepEqual(of(after,t),of(before,t),`unchanged ${t}`)
const vias=of(after,"pcb_via")
assert.ok(vias.every(v=>v.hole_diameter===.3&&v.outer_diameter===.45&&v.layers.length===4&&["top","inner1","inner2","bottom"].every(l=>v.layers.includes(l))),"every manufactured via remains 0.30/0.45 full stack, irrespective of reusable-module fixtures")
const gnd=of(after,"source_net").find(n=>n.name==="BOARD_GND").source_net_id
const pours=of(after,"pcb_copper_pour")
assert.ok(pours.every(p=>p.source_net_id===gnd))
assert.deepEqual([...new Set(pours.map(p=>p.layer))].sort(),["bottom","inner1","inner2","top"])
const solder=read("reports/solder-process-audit.json"),prior=read(`${base}/reports/solder-process-audit.json`)
assert.equal(solder.circuitSha256,hash("dist/index/circuit.json"))
const key=e=>[e.part,e.pin,e.layer,e.x,e.y].join(":")
const original=new Set(prior.ordinaryPadDrillIntersections.map(key)),remaining=new Set(solder.ordinaryPadDrillIntersections.map(key))
assert.ok(solder.ordinaryPadDrillIntersections.every(e=>original.has(key(e))),"no newly drilled ordinary solder land")
const cleared=prior.ordinaryPadDrillIntersections.filter(e=>!remaining.has(key(e))).map(e=>e.part).sort()
assert.deepEqual(cleared,["C_B_AVCC","C_B_VDD_CORE","C_ETH_XI","C_ETH_TCT","C_STATUS_BUFFER","C_U_3V3_IN","U_STATUS_BUFFER"].sort())
assert.equal(solder.ordinaryPadDrillIntersections.length,6)
const expectedThermal=prior.thermalPadDrillIntersections.map(e=>e.part==="U1"&&e.x===.4&&e.y===6.75?{...e,x:1.78}:e)
assert.deepEqual(solder.thermalPadDrillIntersections.map(key).sort(),expectedThermal.map(key).sort(),"same thermal drill count; one SoC stitch moves within its own thermal land")
const modules=read("reports/module-test-regression.json")
assert.equal(modules.currentPassed,32);assert.equal(modules.currentFailed,0);assert.equal(modules.addedFailures,0)
const firmware=[]
function visit(dir){for(const e of fs.readdirSync(`${base}/${dir}`,{withFileTypes:true})){const p=`${dir}/${e.name}`;if(e.isDirectory())visit(p);else if(/\.(c|h|py|md)$/.test(p)||e.name==="Makefile"){assert.equal(hash(p),hash(`${base}/${p}`),`unchanged firmware ${p}`);firmware.push(p)}}}
visit("firmware")
assert.equal(after.filter(e=>e.type.endsWith("_error")).length,0)
const report={circuitSha256:hash("dist/index/circuit.json"),baselineSha256:hash(`${base}/dist/index/circuit.json`),
  allElectricalConnectionsUnchanged:true,allOtherBomValuesUnchanged:true,approvedBomReplacement:{parts:[...replacements],old:"C18164586 / 16 V",new:"C7393837 / 10 V",same:"100 nF, ±10%, X7R, 0201",supplyVoltage:3.3},
  movedComponents:Object.keys(moves),outlineAndMountingHolesUnchanged:true,layers:4,thicknessMm:1.6,componentCadSurfacesMm:[-.8,.8],
  throughVias:vias.length,viaHoleMm:.3,viaPadMm:.45,groundFillLayers:["top","inner1","inner2","bottom"],
  ordinaryIntersectionsBefore:prior.ordinaryPadDrillIntersections.length,ordinaryIntersectionsAfter:solder.ordinaryPadDrillIntersections.length,addedOrdinaryIntersections:0,clearedParts:cleared,
  thermalIntersectionRecords:16,thermalStitchesRemoved:0,firmwareFilesUnchanged:firmware.length,
  moduleTests:{passed:32,failed:0,originalTestImplementationsUnchanged:true},nativeErrors:0,nativeWarnings:after.filter(e=>e.type.endsWith("_warning")).length,
  fabricationReady:false,localChangesNotPushed:true}
fs.writeFileSync("reports/round8-regression.json",JSON.stringify(report,null,2)+"\n")
console.log(JSON.stringify(report))