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-usb-top-records.mjs

import fs from "node:fs"
import path from "node:path"
import crypto from "node:crypto"
import assert from "node:assert/strict"
const baseline="../../checkpoints/nema17-before-usb-top-v052-UkdpHy/restore-verified"
const read=p=>JSON.parse(fs.readFileSync(p,"utf8")),sha=p=>crypto.createHash("sha256").update(fs.readFileSync(p)).digest("hex")
const before=read(baseline+"/dist/index/circuit.json"),after=read("dist/index/circuit.json"),moves=read("usb-top-placement.json")
const of=(c,t)=>c.filter(e=>e.type===t)
const canonical=e=>JSON.stringify(e,(_,v)=>typeof v==="number"?Math.round(v*1e7)/1e7:v&&typeof v==="object"&&!Array.isArray(v)?Object.fromEntries(Object.entries(v).sort(([a],[b])=>a.localeCompare(b))):v)
for(const t of new Set(before.filter(e=>(e.type.startsWith("source_")&&e.type!=="source_project_metadata")||e.type.startsWith("schematic_")).map(e=>e.type)))
  assert.deepEqual(of(after,t).map(canonical),of(before,t).map(canonical),"unchanged electrical/schematic "+t)
assert.deepEqual(of(after,"pcb_board"),of(before,"pcb_board"))
const names=new Map(of(after,"source_component").map(e=>[e.source_component_id,e.name]))
const movedIds=new Set(of(after,"pcb_component").filter(e=>moves[names.get(e.source_component_id)]).map(e=>e.pcb_component_id))
for(const t of ["pcb_component","pcb_port","pcb_smtpad","pcb_plated_hole","pcb_courtyard_rect","pcb_courtyard_outline","pcb_courtyard_circle",...new Set(before.filter(e=>e.type.startsWith("cad_")).map(e=>e.type))]){
  const keep=e=>!movedIds.has(e.pcb_component_id)
  assert.deepEqual(of(after,t).filter(keep).map(canonical),of(before,t).filter(keep).map(canonical),"unchanged unmoved geometry "+t)
}
const hole=e=>canonical(Object.fromEntries(Object.entries(e).filter(([k])=>!k.endsWith("_id"))))
assert.deepEqual(of(after,"pcb_hole").map(hole).sort(),of(before,"pcb_hole").map(hole).sort(),"all physical drilled holes remain unchanged")
assert.equal(after.filter(e=>e.type.endsWith("_error")).length,0)
const oldWarnings=before.filter(e=>e.type.endsWith("_warning"))
const warningKey=e=>e.type+":"+(e.source_trace_id??e.message?.replace(/#\d+/g,"#ID"))
for(const warning of after.filter(e=>e.type.endsWith("_warning"))){
  const old=oldWarnings.find(e=>warningKey(e)===warningKey(warning))
  assert.ok(old,"no new warning kind/source: "+warning.message)
  const width=warning.message?.match(/actual: ([0-9.]+)mm/),prior=old.message?.match(/actual: ([0-9.]+)mm/)
  if(width&&prior)assert.ok(Number(width[1])>=Number(prior[1])-1e-8,"no narrower warning width: "+warning.message)
}
for(const v of of(after,"pcb_via"))assert.ok(v.hole_diameter===.3&&v.outer_diameter===.45&&v.layers.length===4&&["top","inner1","inner2","bottom"].every(l=>v.layers.includes(l)))
let unchangedFiles=0
function unchanged(dir){for(const e of fs.readdirSync(baseline+"/"+dir,{withFileTypes:true})){
  if(["node_modules","dist","build","reports",".git"].includes(e.name))continue
  const p=path.join(dir,e.name);if(e.isDirectory())unchanged(p);else{assert.equal(sha(p),sha(baseline+"/"+p),p);unchangedFiles++}
}}
unchanged("modules");unchanged("firmware")
const result={checkedAt:new Date().toISOString(),circuitSha256:sha("dist/index/circuit.json"),baselineCircuitSha256:sha(baseline+"/dist/index/circuit.json"),
  electricalAndSchematicUnchanged:true,allUnmovedPartAndAssemblyGeometryUnchanged:true,drilledHolesAndBoardOutlineUnchanged:true,
  unchangedFirmwareAndModuleFiles:unchangedFiles,intendedPlacementChanges:moves,nativeErrors:0,nativeWarnings:after.filter(e=>e.type.endsWith("_warning")).length,
  baselineWarnings:before.filter(e=>e.type.endsWith("_warning")).length,noNewOrWorsenedWarnings:true,viaCount:of(after,"pcb_via").length,fabricationReady:false,pushed:false}
fs.writeFileSync("reports/usb-top-record-regression.json",JSON.stringify(result,null,2)+"\n")
console.log(JSON.stringify(result))