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

tests/rishabh-review.test.ts

import { test, expect } from "bun:test"
import fs from "node:fs"
import path from "node:path"
import crypto from "node:crypto"
import {originalSourceContract,stablePhysicalContract,stripGrouping} from "./software-current-contract"
const baseline = "../../checkpoints/nema17-before-rishabh-review-v053-DuFOGi/restore-verified"
const read = (p: string) => JSON.parse(fs.readFileSync(p, "utf8"))
const before: any[] = read(baseline + "/dist/index/circuit.json")
const after: any[] = read("dist/index/circuit.json")
const of = (c: any[], t: string) => c.filter(e => e.type === t)
const component = (c: any[], name: string) => c.find(e => e.type === "source_component" && e.name === name)
const placed = (c: any[], name: string) => c.find(e => e.type === "pcb_component" && e.source_component_id === component(c, name).source_component_id)
const sha = (p: string) => crypto.createHash("sha256").update(fs.readFileSync(p)).digest("hex")

test("all original parts/values/connections remain, apart from the explicit DAC reference feed", () => {
  originalSourceContract(before,after)
  const fuse = component(after, "F_VM")
  const originalIds=new Set(of(before,"source_port").map(e=>e.source_port_id))
  const ports = structuredClone(of(after, "source_port").filter(e=>originalIds.has(e.source_port_id)))
  const output = ports.find(e => e.source_component_id === fuse.source_component_id && e.pin_number === 2)
  expect(output.provides_power).toBe(true)
  delete output.provides_power // Intentional electrical annotation: this is the fused power feed.
  expect(ports.map(stripGrouping)).toEqual(of(before, "source_port").map(stripGrouping))
})

test("bulk capacitor is assembled on top with unchanged value, rating and polarity", () => {
  const cap = component(after, "C_VM_BULK"), pcb = placed(after, "C_VM_BULK")
  expect(cap.manufacturer_part_number).toBe("CA55-D050M226TE100")
  expect(cap.supplier_part_numbers.jlcpcb).toEqual(["C41417029"])
  expect(cap.capacitance).toBeCloseTo(22e-6, 10)
  expect(cap.max_voltage_rating).toBe(50)
  expect(cap.are_pins_interchangeable).toBe(false)
  expect(pcb.layer).toBe("top")
  expect(pcb.center).toEqual({x:18.4,y:-7.9})
  expect(pcb.rotation).toBe(90)
  expect(pcb.do_not_place).toBe(false)
  const cad = after.find(e => e.type === "cad_component" && e.pcb_component_id === pcb.pcb_component_id)
  expect(cad.position.z).toBeGreaterThan(0)
  expect(cad.rotation).toEqual({x:0,y:0,z:90})
  expect(cad.model_obj_url).toBe(before.find(e => e.type === "cad_component" && e.pcb_component_id === pcb.pcb_component_id).model_obj_url)
  const tvs = placed(after,"D_VM_TVS")
  expect(tvs.layer).toBe("bottom")
  expect(after.find(e=>e.type==="cad_component"&&e.pcb_component_id===tvs.pcb_component_id).position.z).toBeLessThan(0)
})

test("the eight reviewed bulk-capacitor moves and the new reference-resistor move are the ONLY original placement changes", () => {
  const moves = read("review-placement.json")
  const moved = new Set(of(after, "pcb_component").filter(e => moves[after.find(s => s.source_component_id === e.source_component_id && s.type === "source_component").name]).map(e => e.pcb_component_id))
  expect(Object.keys(moves).sort()).toEqual(["C_VM_BULK","D_VM_TVS","D_VM_BLOCK","R_CURRENT_A_FILTER","C_CURRENT_A_FILTER","R_TEMP_ALERT","R_PD_12V","R_PD_DEBUG"].sort())
  expect(moved.size).toBe(8)
  for(const [name,move] of Object.entries(moves) as [string,any][]) {
    const p=placed(after,name)
    expect(p.center.x).toBeCloseTo(move.x,9)
    expect(p.center.y).toBeCloseTo(move.y,9)
    expect(p.rotation).toBe(move.rotation)
    expect(p.layer).toBe(move.layer)
    expect(of(after,"pcb_smtpad").filter(e=>e.pcb_component_id===p.pcb_component_id).every(e=>e.layer===move.layer)).toBe(true)
  }
  stablePhysicalContract({before,after,allowedMoves:new Set([...Object.keys(moves),"R_REF_TOP","C_U_2V5_IN","C_ETH_1"])})
})

test("board outline, screw holes, USB positions and complete motor assembly are preserved", () => {
  expect(of(after, "pcb_board").map(stripGrouping)).toEqual(of(before, "pcb_board").map(stripGrouping))
  expect(of(after, "pcb_hole").map(stripGrouping)).toEqual(of(before, "pcb_hole").map(stripGrouping))
  for (const type of new Set(before.filter(e => e.type.startsWith("cad_") && e.type !== "cad_component").map(e => e.type)))
    expect(of(after, type).map(stripGrouping)).toEqual(of(before, type).map(stripGrouping))
  for (const name of ["J_USB_DATA", "J_USB_PWR", "J_ETH", "J_MOTOR", "U_MOTOR", "MH1", "MH2", "MH3", "MH4"])
    expect(stripGrouping(placed(after, name))).toEqual(stripGrouping(placed(before, name)))
})

test("all thirteen sheets have explanatory sections and zero current style / page-boundary issues", () => {
  const sheets = of(after, "schematic_sheet")
  expect(sheets.length).toBe(13)
  expect(sheets.find(s => s.sheet_index === 0)?.name).toBe("overview")
  for (const s of sheets) {
    expect(of(after, "schematic_rect").filter(e => e.schematic_sheet_id === s.schematic_sheet_id && !e.schematic_component_id)).toEqual([])
    expect(of(after, "schematic_line").filter(e => e.schematic_sheet_id === s.schematic_sheet_id).length).toBeGreaterThanOrEqual(2)
    expect(of(after, "schematic_text").filter(e => e.schematic_sheet_id === s.schematic_sheet_id).length).toBeGreaterThan(6)
  }
  expect(of(after, "schematic_element_outside_sheet_warning")).toEqual([])
  const report = read("reports/review-style/analysis.json")
  expect(report.circuitSha256).toBe(sha("dist/index/circuit.json"))
  expect(report.analyzerVersion).toBe("0.0.82")
  expect(report.issueCount).toBe(0)
})

test("all vias remain full-stack 0.30 mm holes / 0.45 mm pads", () => {
  for (const v of of(after, "pcb_via")) {
    expect(v.hole_diameter).toBe(.3)
    expect(v.outer_diameter).toBe(.45)
    expect(v.layers).toEqual(["top", "inner1", "inner2", "bottom"])
  }
})

test("all unrelated firmware and Linux module sources remain byte-identical", () => {
  const declared=new Set(["firmware/PROTOCOL.md","firmware/motor-control.py","firmware/test_motor_control.py",
    "firmware/stm32/Makefile","firmware/stm32/main.c","firmware/stm32/motion.c","firmware/stm32/motion.h","firmware/stm32/test_motion.c"])
  let count = 0
  function visit(dir: string) {
    for (const e of fs.readdirSync(path.join(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()) visit(p)
      else { if(!declared.has(p))expect(sha(p)).toBe(sha(path.join(baseline, p)));count++ }
    }
  }
  visit("modules"); visit("firmware")
  expect(count).toBe(56)
})