-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat local freerouting autorouter (#30)
- Loading branch information
Showing
13 changed files
with
358 additions
and
156 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import { routeUsingLocalFreerouting } from "freerouting" | ||
import { readFile, writeFile, mkdir, unlink } from "fs/promises" | ||
import { join, dirname } from "path/posix" | ||
import { convertAndSaveCircuitToDsn } from "../utils/circuit-json-to dsn-file-converter" | ||
import { | ||
parseDsnToDsnJson, | ||
convertDsnSessionToCircuitJson, | ||
type DsnPcb, | ||
type DsnSession, | ||
} from "dsn-converter" | ||
import { glob } from "glob" | ||
import Debug from "debug" | ||
import type { AnyCircuitElement } from "circuit-json" | ||
|
||
const debug = Debug("autorouting:cli/run/local-freerouting") | ||
|
||
export async function processCircuitFileLocally(inputPath: string) { | ||
debug(`Processing circuit file locally: ${inputPath}`) | ||
|
||
const sampleDir = dirname(inputPath) | ||
let dsnPath: string | ||
let circuitWithoutTraces: AnyCircuitElement[] | undefined | ||
|
||
// Check if input is DSN or JSON | ||
if (inputPath.toLowerCase().endsWith(".dsn")) { | ||
dsnPath = inputPath | ||
// For DSN files, we don't need to do initial conversion | ||
} else if (inputPath.toLowerCase().endsWith(".json")) { | ||
// Assume JSON file | ||
// Read and parse the input circuit | ||
const circuitJson = await readFile(inputPath, "utf8") | ||
const circuit: AnyCircuitElement[] = JSON.parse(circuitJson) | ||
|
||
// Filter out any existing pcb_traces from the original circuit | ||
circuitWithoutTraces = circuit.filter((item) => item.type !== "pcb_trace") | ||
|
||
// Convert circuit to DSN and save to temp file | ||
dsnPath = await convertAndSaveCircuitToDsn(circuitWithoutTraces) | ||
} else { | ||
throw new Error(`Unsupported file format for input file: ${inputPath}`) | ||
} | ||
|
||
try { | ||
// Run local freerouting | ||
debug(`Running freerouting on DSN file: ${dsnPath}`) | ||
const routedDsn = await routeUsingLocalFreerouting({ inputPath: dsnPath }) | ||
|
||
// Read the original DSN file to get the PCB data | ||
const originalDsnContent = await readFile(dsnPath, "utf8") | ||
const pcbJson = parseDsnToDsnJson(originalDsnContent) as DsnPcb | ||
|
||
// Parse the routed DSN to get the session data | ||
const sessionJson = parseDsnToDsnJson(routedDsn) as DsnSession | ||
|
||
// Convert the routed DSN back to circuit JSON | ||
const routedTraces = convertDsnSessionToCircuitJson(pcbJson, sessionJson) | ||
|
||
// Combine original circuit (without traces) with new traces | ||
const outputCircuit = circuitWithoutTraces | ||
? [...circuitWithoutTraces, ...routedTraces] | ||
: routedTraces | ||
|
||
// Write the routed circuit JSON to the outputs directory | ||
const outputsDir = join(sampleDir, "outputs") | ||
await mkdir(outputsDir, { recursive: true }) | ||
const outputPath = join(outputsDir, "freerouting_routed_circuit.json") | ||
await writeFile(outputPath, JSON.stringify(outputCircuit, null, 2)) | ||
|
||
debug(`Wrote routed circuit to: ${outputPath}`) | ||
|
||
// Clean up temporary DSN file | ||
if (!inputPath.toLowerCase().endsWith(".dsn")) { | ||
await unlink(dsnPath) | ||
debug(`Deleted temporary DSN file: ${dsnPath}`) | ||
} | ||
|
||
return outputPath | ||
} catch (error) { | ||
debug(`Error processing file ${inputPath}:`, error) | ||
throw error | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { convertCircuitJsonToDsnString } from "dsn-converter" | ||
import type { AnyCircuitElement } from "circuit-json" | ||
import { writeFile } from "fs/promises" | ||
import { temporaryFile as tempyFile } from "tempy" | ||
|
||
export async function convertAndSaveCircuitToDsn( | ||
circuit: AnyCircuitElement[], | ||
): Promise<string> { | ||
// Convert circuit JSON to DSN format | ||
const dsnContent = convertCircuitJsonToDsnString(circuit) | ||
|
||
// Create temporary file with .dsn extension | ||
const tempPath = tempyFile({ extension: "dsn" }) | ||
|
||
// Write DSN file to temp location | ||
await writeFile(tempPath, dsnContent) | ||
|
||
return tempPath | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import { expect, test } from "bun:test" | ||
import { temporaryDirectory } from "tempy" | ||
import { runAutorouter } from "@/cli/run/run-autorouter" | ||
import { existsSync, readFileSync } from "fs" | ||
import { join } from "path/posix" | ||
import { writeFile, mkdir } from "fs/promises" | ||
import type { AnyCircuitElement } from "circuit-json" | ||
import unroutedCircuit from "tests/fixtures/unrouted_circuit.json" | ||
|
||
const createMockFiles = async (dir: string) => { | ||
// Create unrouted circuit JSON | ||
const mockCircuit = unroutedCircuit | ||
await writeFile( | ||
join(dir, "unrouted_circuit.json"), | ||
JSON.stringify(mockCircuit, null, 2), | ||
) | ||
} | ||
|
||
test("should run local autorouter on a single circuit file", async () => { | ||
// Create a temporary directory with a sample folder structure | ||
const tempDir = temporaryDirectory() | ||
const sampleDir = join(tempDir, "sample1") | ||
await mkdir(sampleDir, { recursive: true }) | ||
await createMockFiles(sampleDir) | ||
const inputPath = join(sampleDir, "unrouted_circuit.json") | ||
console.log("inputPath", inputPath) | ||
// Run command | ||
await runAutorouter({ | ||
inputPath, | ||
autorouter: "freerouting", | ||
serverUrl: "http://localhost:3000", | ||
isLocal: true, | ||
}) | ||
|
||
// Check routed file was created in sample's outputs directory | ||
const outputPath = join( | ||
sampleDir, | ||
"outputs", | ||
"freerouting_routed_circuit.json", | ||
) | ||
expect(existsSync(outputPath)).toBe(true) | ||
|
||
// Verify output content | ||
const outputJson: AnyCircuitElement[] = JSON.parse( | ||
readFileSync(outputPath, "utf8"), | ||
) | ||
expect(Array.isArray(outputJson)).toBe(true) | ||
expect(outputJson.some((item) => item.type !== "pcb_trace")).toBe(true) | ||
expect(outputJson.some((item) => item.type === "pcb_trace")).toBe(true) | ||
}, 30000) // Increase timeout to 30 seconds |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import { expect, test } from "bun:test" | ||
import { temporaryDirectory } from "tempy" | ||
import { runAutorouter } from "@/cli/run/run-autorouter" | ||
import { existsSync, readFileSync } from "fs" | ||
import { join } from "path/posix" | ||
import { writeFile, mkdir } from "fs/promises" | ||
import type { AnyCircuitElement } from "circuit-json" | ||
import unroutedCircuit from "tests/fixtures/unrouted_circuit.json" | ||
|
||
const createMockCircuitFile = async (dir: string) => { | ||
const mockCircuit = unroutedCircuit | ||
await writeFile( | ||
join(dir, "unrouted_circuit.json"), | ||
JSON.stringify(mockCircuit, null, 2), | ||
) | ||
} | ||
|
||
test("should run local autorouter on a dataset directory", async () => { | ||
// Create a temporary dataset directory structure | ||
const datasetDir = temporaryDirectory() | ||
|
||
// Create multiple sample folders | ||
const sampleDirs = ["sample1", "sample2"] // Reduced to 2 samples to speed up test | ||
for (const sampleDir of sampleDirs) { | ||
const fullSamplePath = join(datasetDir, sampleDir) | ||
await mkdir(fullSamplePath, { recursive: true }) | ||
await createMockCircuitFile(fullSamplePath) | ||
} | ||
|
||
console.log("Running local autorouter with dataset directory:", datasetDir) | ||
// Run command | ||
await runAutorouter({ | ||
inputPath: datasetDir, | ||
autorouter: "freerouting", | ||
isDataset: true, | ||
serverUrl: "http://localhost:3000", | ||
isLocal: true, | ||
}) | ||
|
||
// Check each sample folder | ||
for (const sampleDir of sampleDirs) { | ||
const outputPath = join( | ||
datasetDir, | ||
sampleDir, | ||
"outputs", | ||
"freerouting_routed_circuit.json", | ||
) | ||
expect(existsSync(outputPath)).toBe(true) | ||
|
||
const outputJson: AnyCircuitElement[] = JSON.parse( | ||
readFileSync(outputPath, "utf8"), | ||
) | ||
expect(Array.isArray(outputJson)).toBe(true) | ||
expect(outputJson.some((item) => item.type !== "pcb_trace")).toBe(true) | ||
expect(outputJson.some((item) => item.type === "pcb_trace")).toBe(true) | ||
} | ||
}, 30000) // Increased timeout to 30 seconds |
Oops, something went wrong.