# Freerouting SpringBoot Starter(SpringBoot Freerouting 加载器) **Repository Path**: conmi/freerouting-spring-boot-starter ## Basic Information - **Project Name**: Freerouting SpringBoot Starter(SpringBoot Freerouting 加载器) - **Description**: SpringBoot Freerouting 加载器,参考源码,使用GPLv3许可证 - **Primary Language**: Java - **License**: GPL-3.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-03 - **Last Updated**: 2026-09-04 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # freerouting-spring-boot-starter > **代码移植**:本 starter(含 JDK 21 兼容改造、引擎内嵌方案与封装设计)由 AI 模型 **deepseek-v4-flash** 移植适配, > 引擎本体来自 [Freerouting](https://github.com/freerouting/freerouting)(GPLv3)。使用即代表接受 GPLv3 义务,仅限 GPLv3 兼容项目使用。 将 [Freerouting](https://github.com/freerouting/freerouting)(PCB 自动布线器,GPLv3)封装为 Spring Boot Starter。 消费方应用只需引入本依赖并注入相关 Service,即可在 Spring Boot 4.x / Java 21+ 中使用 freerouting 的全部非界面能力(纯编程式,不内嵌任何对外 Web 服务器): - **同步路由**:`FreeroutingService.route()` 阻塞直到完成(**DSN / KiCad design JSON 输入 → SES 输出**) - **异步任务**:`FreeroutingJobService.submitJob()` 提交后立即返回,由 freerouting 原生调度器后台并行执行(最多 5 个),支持 cancel / list / queue-position / await - **规则与 DRC**:`FreeroutingJobOptions` 携带 `.rules` 规则文件与 DRC 配置;`runDrc()` 返回 DRC 报告,`getJobResultJson()` 输出 KiCad design JSON ## 特性 - 自动配置(`AutoConfiguration.imports`):引入依赖即可获得 `FreeroutingService`、`FreeroutingJobService` Bean - 惰性初始化:首次使用前自动完成 freerouting 静态环境初始化(Log4j2 配置、user-data 目录、`freerouting.json`、settingsMergerProtype),幂等且线程安全 - 同步 + 异步双执行模式:`route()` 阻塞式;`submitJob()`/`awaitJob()` 异步 - 全量可配置:`freerouting.*` 属性映射 `RouterSettings` 与引擎全局设置 - 日志友好:排除 freerouting 传递的 `slf4j-nop`,日志经 log4j-to-slf4j 桥接汇入应用日志(Spring Boot 默认 logback) - 无端口、无服务器:所有能力均由 Spring Boot 程序直接调用,不启动 Jetty / MCP 等任何对外服务 ## 前置条件 | 项目 | 版本 | 说明 | |------|------|------| | JDK | 21+ | 本地源码已适配 Java 21 基线(上游官方包要求 Java 25) | | Spring Boot | 4.x | 本 starter 基于 4.1.1 构建(JDK 17+ 均可) | | freerouting | 2.3.1-SNAPSHOT | 需先发布到本地 Maven 仓库(见下) | ### 1. 发布 freerouting 到本地 Maven 仓库 freerouting 是 Gradle 单模块项目,用 JDK 21 构建并安装到本地仓库(本地源码已将工具链/目标降至 21, 并把 Java 23+ 的 `_` 未命名变量与 `IO.println` 改写为 Java 21 兼容语法): ```bash export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64 export PATH=$JAVA_HOME/bin:$PATH cd /root/freerouting ./gradlew publishToMavenLocal ``` 或在项目根目录直接运行一键脚本(会先发布 freerouting 再构建本 starter): ```bash ./install.sh ``` ## 使用方式 ### 1. 引入依赖(只需这一个) ```xml io.gitee.conmi freerouting-spring-boot-starter 1.1.0 ``` > 消费方项目需 JDK 21+;引擎已内嵌进本 jar,**无需**再引入 `app.freerouting:freerouting` 或其他引擎依赖。 > 已发布到 Maven Central(`io.gitee.conmi:freerouting-spring-boot-starter:1.1.0`),可直接从中央仓库拉取; > 若使用本地仓库构建,请先将本地 `~/.m2/repository` 中的 > `io.gitee.conmi:freerouting-spring-boot-starter:1.1.0` 发布到内部仓库。 ### 2. 配置(可选) `application.yml`: ```yaml freerouting: enabled: true # 是否启用自动配置(默认 true) user-data-path: /data/freerouting # freerouting.json 与日志目录(默认 OS user-data) console-logging-enabled: true # 控制台日志(默认 true) console-logging-level: INFO file-logging-enabled: false # 文件日志(默认关闭) file-logging-level: DEBUG file-logging-location: /data/freerouting/freerouting.log analytics-enabled: false # 遥测上报(默认关闭) # ---- 路由参数(不设置则用 freerouting 默认值)---- max-passes: 0 # 最大布线趟数(0/空 = 不限) job-timeout-string: 12:00:00 # 任务超时 HH:MM:SS run-router: true # 是否运行布线器 run-optimizer: true # 是否运行后置优化器 fanout-enabled: true # 是否启用 SMD 扇出预布线 max-threads: 8 # 最大工作线程数 ``` 关闭自动配置:`freerouting.enabled: false`(可自行 new `FreeroutingService`)。 ### 3. 注入并路由 ```java import com.freerouting.service.FreeroutingResult; import com.freerouting.service.FreeroutingService; import org.springframework.stereotype.Service; @Service public class RoutingService { private final FreeroutingService freeroutingService; public RoutingService(FreeroutingService freeroutingService) { this.freeroutingService = freeroutingService; } public byte[] route(byte[] input) { FreeroutingResult result = freeroutingService.route(input); // DSN 或 KiCad design JSON → SES if (!result.hasOutput()) { throw new IllegalStateException("Routing produced no output, state=" + result.state()); } return result.outputBytes(); // SES 会话文件字节 } } ``` `FreeroutingResult` 字段: - `state`:`RoutingJobState`(如 `COMPLETED`) - `designName`:设计名(通常为输入 DSN / KiCad JSON 文件名) - `outputBytes`:SES 会话文件字节内容 ### 4. 调用级参数覆盖 `route(byte[] input, RouterSettings overrides)` 支持按调用叠加 `RouterSettings` (仅复制非 null 字段,优先级高于全局属性): ```java RouterSettings overrides = new RouterSettings(); overrides.maxPasses = 1; overrides.optimizer.enabled = false; FreeroutingResult result = freeroutingService.route(dsnBytes, overrides); ``` ### 5. 异步任务(FreeroutingJobService) `submitJob()` 提交后立即返回,由 freerouting 原生 `RoutingJobScheduler` 后台执行(最多 5 个并行), 支持 DSN 与 KiCad design JSON 输入,输出为 SES。任务生命周期: `QUEUED → READY_TO_START → RUNNING → COMPLETED / TERMINATED / TIMED_OUT / CANCELLED`。 ```java import com.freerouting.service.FreeroutingJobService; import com.freerouting.service.FreeroutingResult; import app.freerouting.core.RoutingJob; import java.time.Duration; import java.util.UUID; @org.springframework.stereotype.Service public class AsyncRoutingService { private final FreeroutingJobService jobService; public AsyncRoutingService(FreeroutingJobService jobService) { this.jobService = jobService; } public byte[] routeAsync(byte[] dsnBytes) throws Exception { // 1. 提交(也可传入 RouterSettings overrides 作为本次调用的最高优先级参数) RoutingJob job = jobService.submitJob(dsnBytes, "board.dsn"); // 2. 轮询/等待直到终态(默认超时 10 分钟,可用 Duration 覆盖) FreeroutingResult result = jobService.awaitJob(job.id, Duration.ofMinutes(5)); // 3. 取 SES 输出;另可 getJobStatus / getJobResult / cancelJob / listJobs / getQueuePosition return result.outputBytes(); } } ``` **携带规则文件 / DRC / 参数覆盖提交**(`FreeroutingJobOptions`): ```java import com.freerouting.service.FreeroutingJobOptions; FreeroutingJobOptions options = new FreeroutingJobOptions(); options.overrides = overrides; // 路由参数覆盖(最高优先级) options.rules = Files.readAllBytes(path); // 设计规则 .rules 文件字节 options.drcEnabled = true; // 启用 DRC 检查配置(含 warnings/errors 开关) RoutingJob job = jobService.submitJob(dsnBytes, "board.dsn", options); FreeroutingResult result = jobService.awaitJob(job.id, Duration.ofMinutes(5)); // KiCad 设计 JSON 输出(对齐 REST /output/json,从内存板实时生成) String json = jobService.getJobResultJson(job.id); // DRC 报告 JSON(对齐 REST /drc,使用提交时的 drcSettings 配置) String drcReport = jobService.runDrc(job.id); ``` ## 常见问题 - **Unsupported input format**:输入必须是 Specctra DSN 或 KiCad design JSON 文件字节。 - **提交的异步任务一直停留在 QUEUED**:调度器只处理 `READY_TO_START` 状态的任务;`submitJob` 已自动 完成 QUEUED → READY_TO_START 转换(等价于 REST 的 `PUT /v1/jobs/{id}/start`),一般不会出现该现象。 - **日志里没有 freerouting 输出**:Spring Boot 默认 log4j-to-slf4j 桥接,freerouting 内部 log4j2 日志会自动汇入应用日志(logback),无需额外配置;文件日志需 `freerouting.file-logging-enabled: true`。 - **首次路由较慢**:首次调用会初始化引擎(加载/新建 `freerouting.json` 等),之后复用。 ## 测试 ```bash export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64 export PATH=$JAVA_HOME/bin:$PATH mvn clean test ``` JDK 21 与 25 下均可运行(产物基线 21)。包含 10 个测试: - `FreeroutingAutoConfigurationTest`:自动配置注册/禁用、`freerouting.*` 属性绑定 - `FreeroutingServiceIntegrationTest`:真实 DSN 与 KiCad design JSON 同步路由(`minimal_board.dsn` / `minimal_board.json` → SES)与非法输入拒绝 - `FreeroutingJobServiceIntegrationTest`:异步提交 → `awaitJob` 完成(COMPLETED + SES 输出)、非法输入拒绝、未知任务状态、options(rules+DRC)提交 → KiCad JSON 输出 + DRC 报告 ## 目录结构 ``` src/main/java/com/freerouting/ ├── autoconfigure/ │ ├── FreeroutingAutoConfiguration.java # @AutoConfiguration,注册 Service/JobService 两 Bean │ └── FreeroutingProperties.java # freerouting.* 配置属性 └── service/ ├── FreeroutingService.java # 同步服务:route(byte[]) → FreeroutingResult ├── FreeroutingJobService.java # 异步服务:submit/status/result/json/drc/cancel/list/await ├── FreeroutingJobOptions.java # 异步提交可选参数:overrides / rules / drc ├── FreeroutingResult.java # 路由结果(state / designName / outputBytes) └── FreeroutingException.java # 路由异常 src/main/resources/META-INF/spring/ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports ``` ## 许可 - 本 Starter 代码:遵循原始项目使用的 GPLv3 协议(与 freerouting 一致) - Freerouting 本体:GPLv3,见 [freerouting](https://github.com/freerouting/freerouting)