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-round10-regression.mjs
import fs from "node:fs"
import crypto from "node:crypto"
import assert from "node:assert/strict"
const base="../../checkpoints/nema17-round9-validated-local-20261020-HTyidb/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 sha=hash("dist/index/circuit.json"),of=(c,t)=>c.filter(e=>e.type===t)
const invariantTypes=["source_component","source_port","source_trace","source_net","source_component_internal_connection",
"pcb_board","pcb_component","pcb_port","pcb_smtpad","pcb_hole","pcb_plated_hole","pcb_keepout",
"pcb_courtyard_rect","pcb_courtyard_outline","pcb_courtyard_circle","pcb_silkscreen_text","pcb_silkscreen_circle",
...new Set(before.filter(e=>e.type.startsWith("schematic_")||e.type.startsWith("cad_")).map(e=>e.type))]
for(const t of invariantTypes)assert.deepEqual(of(after,t),of(before,t),`unchanged ${t}`)
assert.deepEqual(after.filter(e=>e.type.endsWith("_error")),[])
const warningKey=e=>`${e.type}:${(e.message??"").replace(/#\d+\b/g,"#ID").replaceAll("0.12000000000000001","0.12")}`
const warnings=c=>c.filter(e=>e.type.endsWith("_warning")).map(warningKey).sort()
assert.deepEqual(warnings(after),warnings(before),"no new warnings, removal or suppression")
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))))
const coords=c=>new Set(of(c,"pcb_via").map(e=>`${e.x.toFixed(6)},${e.y.toFixed(6)}`))
const a=coords(after),b=coords(before)
const added=[...a].filter(p=>!b.has(p)),removed=[...b].filter(p=>!a.has(p))
assert.deepEqual(added,["4.200000,-16.500000"]);assert.deepEqual(removed,["3.750000,-15.900000"])
const solder=read("reports/solder-process-audit.json"),oldSolder=read(`${base}/reports/solder-process-audit.json`)
assert.equal(solder.circuitSha256,sha);assert.deepEqual(solder.ordinaryPadDrillIntersections,[])
const key=e=>[e.part,e.pin,e.layer,e.x,e.y].join(":")
assert.deepEqual(solder.thermalPadDrillIntersections.map(key).sort(),oldSolder.thermalPadDrillIntersections.map(key).sort())
const proof=read("reports/round10-width-proof.json")
assert.equal(proof.circuitSha256,sha);assert.equal(proof.vmBridgeMinimumMm,.8)
assert.equal(proof.reducedHalfMmVmSegmentsRemaining,0);assert.equal(proof.phaseBReturnMinimumMm,.3)
assert.equal(proof.ethernetWarningPhysicalRegression,false)
for(const p of ["reports/connectivity-audit.json","reports/coil-all-copper-preflight-meta.json",
"reports/drill-clearance-regression.json","reports/additional-dfm-review.json","reports/bom-validation.json",
"reports/jlcpcb-stock-audit.json","reports/round10-release-gates.json"])assert.equal(read(p).circuitSha256,sha)
const continuity=read("reports/connectivity-audit.json"),copper=read("reports/coil-all-copper-preflight-meta.json")
assert.equal(continuity.sourceFailures,0);assert.equal(continuity.disconnectedTerminalIslands,0);assert.equal(continuity.emittedCopperOnly,true)
assert.equal(copper.failureCount,0);assert.equal(copper.physicalCopperSha256,hash("routing/physical-copper.json"))
assert.equal(read("reports/drill-clearance-regression.json").currentFindings,0)
assert.deepEqual(read("reports/additional-dfm-review.json").findings,[])
assert.deepEqual(read("reports/bom-validation.json").findings,[])
assert.deepEqual(read("reports/round5-legend-audit.json").findings,[])
const moduleReport=read("reports/module-test-regression.json")
assert.equal(moduleReport.currentPassed,32);assert.equal(moduleReport.currentFailed,0);assert.equal(moduleReport.addedFailures,0)
let firmware=0
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}`));firmware++}}}
visit("firmware")
const config=read("tsconfig.json"),oldConfig=read(`${base}/tsconfig.json`)
assert.deepEqual(config.compilerOptions,oldConfig.compilerOptions,"compiler strictness/options retained")
assert.deepEqual(config.include,oldConfig.include,"all original design includes retained")
assert.deepEqual(config.exclude,oldConfig.exclude.map(p=>p==="reports/published-comparison"?"reports":p))
const compiled=fs.readFileSync("reports/round10-typecheck-files.log","utf8").split("\n")
for(const p of ["/index.circuit.tsx","/board-layout.ts","/modules/f1c100s/src/index.tsx"])
assert.ok(compiled.some(f=>f.endsWith(p)),`design code still typechecked: ${p}`)
assert.ok(!compiled.some(f=>f.includes("/reports/")),"downloaded diagnostic snapshots are not design inputs")
const log=f=>fs.readFileSync(`reports/round10-${f}.log`,"utf8")
assert.match(log("build"),/Circuits\s+1 passed/);assert.match(log("shorts"),/No shorts detected/)
assert.match(log("board-tests"),/6 pass[\s\S]*0 fail/);assert.match(log("module-tests"),/32 pass[\s\S]*0 fail/)
assert.match(log("motion-tests"),/PASS: signed coil conversion/);assert.match(log("host-tests"),/Ran 6 tests[\s\S]*OK/)
assert.match(log("verify"),/all JLCPCB parts cover board quantity/)
const stock=read("reports/jlcpcb-stock-audit.json")
assert.deepEqual(stock.parts.filter(p=>p.status!=="in-stock").map(p=>p.code).sort(),["C32843","C85202"])
const files=["tsconfig.json","reports/round10-typecheck-files.log","scripts/check-round10-widths.py","reports/round10-width-proof.json","reports/solder-process-audit.json",
"reports/connectivity-audit.json","reports/coil-all-copper-preflight-meta.json","reports/coil-all-copper-preflight.json",
"reports/drill-clearance-regression.json","reports/additional-dfm-review.json","reports/jlcpcb-stock-audit.json",
"reports/bom-validation.json","reports/module-test-regression.json","reports/round10-release-gates.json",
"reports/round5-legend-audit.json","reports/round9-pin-glyph-bounds.json","reports/round10-pcb-top.png","reports/round10-pcb-bottom.png",
...["build","check","style","typecheck","continuity","copper","solder","shorts","drill","dfm","kelvin","mechanical",
"glyphs","legend","width-proof","board-tests","module-tests","module-regression","motion-tests","host-tests","stock","bom","verify","gate-tests"].map(f=>`reports/round10-${f}.log`)]
const report={checkedAt:new Date().toISOString(),circuitSha256:sha,baselineSha256:hash(`${base}/dist/index/circuit.json`),
replaySha256:hash("routing/complete-routes.json"),realWidthRegressionFixed:true,
changes:{vmBridge:"0.80 mm outer copper restored between exact original crossbar endpoints; 9.214 mm planar, one full-stack via switches between top and bottom",
phaseBReturn:"rerouted on outer copper, minimum 0.30 mm",componentPlacementsChanged:0,viaAdded:added,obsoleteVmViaRemoved:removed},
diagnosticTypecheckFix:"Exclude reports rather than only reports/published-comparison; strict compiler options, source includes and all actual design inputs retained.",
correctedDiagnosis:{ethernetMainActualMm:proof.ethernetMainMinimumMm,ethernetMainRequestedMm:proof.ethernetMainRequestedMm,
capacitorBranchActualMm:proof.ethernetCapBranchMinimumMm,capacitorBranchRequestedMm:proof.ethernetCapBranchRequestedMm,
warning:"Native warning associates the separately constrained 0.15 mm C_ETH_7 branch with the 0.25 mm main source. Both real paths meet their own constraints; no warning is hidden.",
otherNewWarningsVsPublishedLatest:"Two Q_USB_ATTACH modeling/refdes metadata warnings, not reduced electrical functionality."},
invariants:{allNetlistValuesPartsAndFootprintsUnchanged:true,allPlacementsSchematicAndCadUnchanged:true,
outlineMm:42.3,mounts:4,mountPatternMm:31,layers:4,thicknessMm:1.6,actualThroughVias:vias.length,viaHoleMm:.3,viaPadMm:.45,
ordinaryPadViaConflicts:0,thermalPadRecords:16,thermalStitchesRemoved:0,firmwareFilesUnchanged:firmware},
checks:{nativeErrors:0,nativeWarnings:warnings(after).length,warningsUnchangedFromPreviousCheckpoint:true,
allLayerShorts:"pass",emittedContinuity:"pass",copper:"pass",drill:"pass, no findings/regressions",kelvin:"four pass",
mechanicalAndLegend:"pass",schematicPlacement:"CLI pass; all schematic records unchanged",typecheck:"pass",
modules:{passed:32,failed:0},boardTests:{passed:6,failed:0},firmwareAndHost:"pass",
fullVerification:{passed:false,blockedFor:"unchanged W5500/BSS84 orderable-stock shortages"},assemblyExport:"blocked; negative gate tests pass"},
stock:{checkedAt:stock.checkedAt,parts:stock.populatedComponentCount,codes:stock.uniquePartCount,issues:stock.parts.filter(p=>p.status!=="in-stock")},
fabricationReady:false,allHardwareRisksQualified:false,localChangesNotPushed:true,
remaining:"153 native warnings remain reported. Thermal/double-sided assembly, actual rear motor fastening, controlled USB/Ethernet stackup/return paths and hardware flashing/load/thermal/current calibration remain unqualified. No stock/assembly gate waived.",
evidence:Object.fromEntries(files.map(p=>[p,hash(p)]))}
fs.writeFileSync("reports/round10-fix-status.json",JSON.stringify(report,null,2)+"\n")
console.log(JSON.stringify({circuitSha256:sha,realWidthRegressionFixed:true,vmWidthMm:.8,phaseBReturnMm:.3,
ethernetPhysicalRegression:false,nativeErrors:0,nativeWarnings:warnings(after).length,ordinaryPadViaConflicts:0,
moduleTests:report.checks.modules,fabricationReady:false,localChangesNotPushed:true}))