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

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

const base="../../checkpoints/nema17-pre-round7-20261019-HTEis5/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 parts=c=>new Map(of(c,"source_component").map(e=>[e.name,e]))
const old=parts(before),current=parts(after)
const added=[...current.keys()].filter(n=>!old.has(n)).sort()
assert.deepEqual(added,["C_USB_GATE","Q_USB_ATTACH","R_USB_BASE","R_USB_BASE_PD","R_USB_OE_PU","U_USB_GATE"].sort())
for(const [name,part] of old){
  const now=current.get(name);assert.ok(now,`retained part ${name}`)
  for(const key of ["resistance","capacitance","inductance","max_voltage_rating","do_not_place"])
    assert.deepEqual(now[key],part[key],`unchanged ${name} ${key}`)
  if(name!=="U_USB_ESD")for(const key of ["manufacturer_part_number","supplier_part_numbers"])
    assert.deepEqual(now[key],part[key],`unchanged ${name} ${key}`)
}
function partition(c){
  const names=new Map(of(c,"source_component").map(e=>[e.source_component_id,e.name]))
  const parents=new Map(),root=id=>{if(!parents.has(id))parents.set(id,id);if(parents.get(id)!==id)parents.set(id,root(parents.get(id)));return parents.get(id)}
  for(const e of c){const ids=e.type==="source_trace"?[...e.connected_source_port_ids??[],...e.connected_source_net_ids??[]]:e.type==="source_component_internal_connection"?e.source_port_ids:[];for(const id of ids.slice(1))parents.set(root(id),root(ids[0]))}
  return new Map(of(c,"source_port").map(p=>[`${names.get(p.source_component_id)}.${p.pin_number??p.name}`,root(p.source_port_id)]))
}
const changedPins=new Set(["J_USB_DATA.15","J_USB_DATA.16","U_USB_ESD.4","U_USB_ESD.5","U_USB_ESD.6","C_ESD.1"])
const bp=partition(before),ap=partition(after)
for(const name of bp.keys())assert.ok(ap.has(name),`retained terminal ${name}`)
const unaffected=[...bp.keys()].filter(n=>!changedPins.has(n))
function groups(pp){const g=new Map();for(const n of unaffected){const root=pp.get(n);if(!g.has(root))g.set(root,[]);g.get(root).push(n)}return [...g.values()].map(a=>a.sort().join("|")).sort()}
assert.deepEqual(groups(ap),groups(bp),"every original net partition outside the six approved USB terminals is unchanged")
const host=["J_USB_DATA.15","J_USB_DATA.16","U_USB_ESD.5","C_ESD.1","R_USB_BASE.1"]
assert.equal(new Set(host.map(n=>ap.get(n))).size,1)
assert.deepEqual([...ap].filter(([,r])=>r===ap.get(host[0])).map(([n])=>n).sort(),host.sort(),"host VBUS has exactly the approved loads, no system rail")
const placed=c=>new Map(of(c,"pcb_component").map(e=>[of(c,"source_component").find(s=>s.source_component_id===e.source_component_id).name,e]))
const bpc=placed(before),apc=placed(after),moves=read("usb-attach-placement.json")
for(const [n,p] of bpc){
  const q=apc.get(n),expected=moves[n]
  if(expected){assert.ok(Math.hypot(q.center.x-expected.x,q.center.y-expected.y)<1e-6,`${n} explicit placement`);assert.equal(q.rotation,expected.rotation);assert.equal(q.layer,expected.layer)}
  else for(const k of ["center","rotation","layer"])assert.deepEqual(q[k],p[k],`${n} unchanged ${k}`)
}
const physicalRecords=(c,t)=>of(c,t).map(({subcircuit_id,pcb_group_id,...e})=>e)
for(const t of ["pcb_board","pcb_hole"])assert.deepEqual(physicalRecords(after,t),physicalRecords(before,t),`unchanged ${t} geometry and rules`)
const firmware=[]
function visit(dir){for(const entry of fs.readdirSync(`${base}/${dir}`,{withFileTypes:true})){const p=path.join(dir,entry.name);if(entry.isDirectory())visit(p);else if(/\.(c|h|py|md)$/.test(p)||entry.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 solder=read("reports/solder-process-audit.json"),prior=read(`${base}/reports/solder-process-audit.json`)
const key=e=>[e.part,e.pin,e.layer,e.x,e.y].join(":")
const priorOrdinary=new Set(prior.ordinaryPadDrillIntersections.map(key))
assert.ok(solder.ordinaryPadDrillIntersections.every(e=>priorOrdinary.has(key(e))),"no added ordinary solder-land/drill intersections")
assert.deepEqual(solder.thermalPadDrillIntersections.map(key).sort(),prior.thermalPadDrillIntersections.map(key).sort(),"thermal arrays unchanged")
const report={circuitSha256:hash("dist/index/circuit.json"),baselineSha256:hash(`${base}/dist/index/circuit.json`),addedParts:added,
  approvedChangedOriginalPins:[...changedPins],unaffectedOriginalPorts:unaffected.length,allOtherNetPartitionsUnchanged:true,
  originalPlacementsChanged:[...bpc.keys()].filter(n=>moves[n]),firmwareFilesUnchanged:firmware.length,
  outlineAndMountsUnchanged:true,nativeErrors:0,nativeWarnings:after.filter(e=>e.type.endsWith("_warning")).length,
  ordinaryPadDrillIntersections:solder.ordinaryPadDrillIntersections.length,addedOrdinaryIntersections:0,
  fabricationReady:false,localChangesNotPushed:true}
fs.writeFileSync("reports/round7-regression.json",JSON.stringify(report,null,2)+"\n")
console.log(JSON.stringify(report))