mohan-bee/rc-link-breakaway-panel

A four-layer RC link panel combining an STM32/E28 2.4‑GHz transmitter with USB-RP2040 socket and an STM32/E28 receiver providing six buffered PWM servo outputs, regulated 3.3‑V power, headers, controls, and protection/decoupling.

Version
0.1.5
License
unset
Stars
0

circuits/panel-routing.ts

import { EventEmitter } from "node:events";
import type { SimpleRouteJson, SimplifiedPcbTrace } from "@tscircuit/capacity-autorouter";

// Rebase by physical port identity; IDs remain opaque.
export function panelRouting(savedTraces: SimplifiedPcbTrace[], positions: Record<string, { x: number; y: number }>, offset: { x: number; y: number }, prefix: string) {
  return async (input: SimpleRouteJson) => {
    const ports = new Map<string, string>();
    const connections = new Map<string, string>();
    for (const connection of input.connections) {
      for (const point of connection.pointsToConnect) {
        if (!point.pcb_port_id) continue;
        const matches = Object.entries(positions).filter(([, p]) => Math.hypot(p.x + offset.x - point.x, p.y + offset.y - point.y) < 1e-6);
        if (matches.length !== 1) throw new Error(`Reviewed routing is stale at ${point.pcb_port_id}`);
        ports.set(matches[0][0], point.pcb_port_id);
        connections.set(matches[0][0], connection.name);
      }
    }
    const names = new Map<string, string>();
    for (const trace of savedTraces) for (const port of trace.connectsTo ?? []) {
      const name = connections.get(port);
      if (!name) continue;
      const previous = names.get(trace.connection_name);
      if (previous && previous !== name) throw new Error("Reviewed net split during panelization");
      names.set(trace.connection_name, name);
    }
    const portId = (id: string | undefined) => {
      if (!id) return undefined;
      const mapped = ports.get(id);
      if (!mapped) throw new Error(`Unmapped reviewed port ${id}`);
      return mapped;
    };
    const traces = savedTraces.map((trace, i) => {
      const connection_name = names.get(trace.connection_name);
      if (!connection_name) throw new Error(`Unmapped reviewed connection ${trace.connection_name}`);
      return {
        type: "pcb_trace" as const,
        pcb_trace_id: `${prefix}_reviewed_${i}`,
        connection_name,
        connectsTo: trace.connectsTo?.map(portId).filter((id): id is string => !!id),
        route: trace.route.map(p => {
          if (p.route_type !== "wire" && p.route_type !== "via") throw new Error("Unsupported reviewed route point");
          return p.route_type === "wire" ? {
          ...p, x: p.x + offset.x, y: p.y + offset.y,
          start_pcb_port_id: portId(p.start_pcb_port_id), end_pcb_port_id: portId(p.end_pcb_port_id),
        } : { ...p, x: p.x + offset.x, y: p.y + offset.y };
        }),
      };
    });
    const router = new EventEmitter();
    return Object.assign(router, {
      start() { queueMicrotask(() => router.emit("complete", { traces: [...traces, ...(input.traces ?? [])] })); },
      stop() { router.removeAllListeners(); },
    });
  };
}