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/software-current-contract.ts

import { expect } from "bun:test"

// Render-order grouping/connectivity identifiers are metadata, not copper or
// electrical connections. Appending a group changes the board's group number.
export const stripGrouping = (record: any) => JSON.parse(JSON.stringify(record,
  (key, value) => ["subcircuit_id","subcircuit_connectivity_map_key","source_group_id","pcb_group_id"].includes(key) ? undefined : value))
export const additionNames = new Set(["U_CURRENT_DAC","C_CURRENT_DAC"])
export function originalSourceContract(before: any[], after: any[]) {
  const fuseId=before.find(e=>e.type==="source_component"&&e.name==="F_VM")?.source_component_id
  const byId = new Map(after.map(e => [e.type+":"+e[e.type+"_id"],e]))
  for (const e of before.filter(e => ["source_component","source_port","source_trace","source_net","source_component_internal_connection"].includes(e.type))) {
    if (e.type === "source_trace" && e.name === "DRV_REF_FEED") continue
    const actual = byId.get(e.type+":"+e[e.type+"_id"])
    expect(actual).toBeDefined()
    const wanted = stripGrouping(e), value = stripGrouping(actual)
    // This annotation was deliberately added by the already-published bulk
    // capacitor revision. The 0.4.53 historical test predates the annotation.
    if (e.type === "source_port" && e.source_component_id === fuseId && e.pin_number === 2 && value.provides_power && !wanted.provides_power)
      delete value.provides_power
    expect(value).toEqual(wanted)
  }
  const oldNames = before.filter(e=>e.type==="source_component").map(e=>e.name)
  expect(after.filter(e=>e.type==="source_component"&&!oldNames.includes(e.name)).map(e=>e.name).sort())
    .toEqual([...additionNames].sort())
}
export function stablePhysicalContract({before,after,allowedMoves=new Set(["R_REF_TOP","C_U_2V5_IN","C_ETH_1"])}:
  {before:any[];after:any[];allowedMoves?:Set<string>}) {
  const sourceName = (c: any[]) => new Map(c.filter(e=>e.type==="source_component").map(e=>[e.source_component_id,e.name]))
  const oldNames=sourceName(before),newNames=sourceName(after)
  const pcbNames = new Map(after.filter(e=>e.type==="pcb_component").map(e=>[e.pcb_component_id,newNames.get(e.source_component_id)]))
  const padOwners=new Map(before.filter(e=>e.type==="pcb_smtpad").map(e=>[e.pcb_smtpad_id,e.pcb_component_id]))
  const types=new Set(["pcb_board","pcb_component","pcb_port","pcb_smtpad","pcb_plated_hole","pcb_hole","pcb_courtyard_rect","pcb_courtyard_outline","pcb_solder_paste","cad_component"])
  const byId=new Map(after.map(e=>[e.type+":"+e[e.type+"_id"],e]))
  for(const e of before.filter(e=>types.has(e.type))) {
    const name=oldNames.get(e.source_component_id)??pcbNames.get(e.pcb_component_id??padOwners.get(e.pcb_smtpad_id))
    if(allowedMoves.has(name))continue
    if(["pcb_courtyard_rect","pcb_courtyard_outline"].includes(e.type)) {
      // Manual courtyards are emitted after imported footprints, so adding
      // a new footprint renumbers those database IDs. Preserve the exact
      // owner, geometry, face and multiplicity, not the transient ID.
      const normalize=(record:any)=>JSON.stringify(stripGrouping({...record,[e.type+"_id"]:undefined}))
      expect(after.filter(z=>z.type===e.type&&z.pcb_component_id===e.pcb_component_id).map(normalize).sort())
        .toEqual(before.filter(z=>z.type===e.type&&z.pcb_component_id===e.pcb_component_id).map(normalize).sort())
      continue
    }
    expect(stripGrouping(byId.get(e.type+":"+e[e.type+"_id"]))).toEqual(stripGrouping(e))
  }
}