# DSH FreeRouting

[English](README.md) | [简体中文](README.zh-CN.md) | [文档目录](docs/README.md)

通过 DSH 对当前打开的 KiCad PCB 自动布线。计算中的临时走线显示在 KiCad 编辑器，任务卡片显示进度、删除选择和最终结果。

当前为内测版，仅支持 Windows x64、兼容的 KiCad 10.0.7+（10.0 系列）及其 Python 3.11。DSH 开发接口基线为 0.2.1-rc.2，其他版本需验证兼容性。板子必须至少保存过一次；之后允许未保存的编辑。

## 当前功能与限制

- 使用编辑器当前内容，保留既有走线和过孔；最终新增对象在一个 KiCad 事务中添加。
- 按配置显示计算中的临时走线，最终添加前清理本轮预览。临时走线的记录几何被修改时，卡片询问删除或保留。
- 任务卡提供「停止布线」，请求取消后等待计算与板子操作安全收尾，再展示实际结果。
- 发起布线的 DSH 会话持续运行到任务与收尾结束；支持原生停止入口，完成后在原工具位置显示最终结果，不再另发后台完成通知。底部按钮仍遵循 DSH 原生草稿规则。
- 可重新填充已有铜区，不创建新区域；失败时保留已确认添加的走线。
- 使用 KiCad 自带的 bin/freerouting.exe 和随包 Python 依赖，不要求用户另装 Java 或系统 Python。
- 不自动保存，不自动修复或重新布线，不执行 DRC、最终连通性检查或生成检查报告。
- 不比较 PCB 全文或板内容指纹。布线期间不要编辑、保存或撤销板子；任意元件、板框和铜对象编辑可能无法检测。

「已添加」只说明确认的新增走线和过孔，不保证全部接通、差分耦合、等长、阻抗或晶振电路性能。完成后由用户检查并决定是否保存。详见[能力边界](docs/freerouting-capability-boundaries.zh-CN.md)和[当前行为](docs/routing-current-behavior.zh-CN.md)。

## 安装与连接

独立 DSH 使用其插件命令安装已发布版本或本地 tgz：

```powershell
dsh plugin --profile web add -w --ignore-scripts @huaqiu/dsh-tool-freerouting
# 本地构建：将包名换成 dist/huaqiu-dsh-tool-freerouting-<version>.tgz 的完整路径。
```

安装后在空闲时重新启动 DSH，同一环境只加载一份插件。安装包已有宿主入口、浏览器入口和 cordis.patch.yml，不需要另装技能。KiCad 内置 DSH 的本地开发链接见下文，不能用独立 DSH 的 profile 安装步骤替代。

在 PCB 编辑器偏好设置中启用 KiCad API。KiCad 内置 DSH 可从自身安装目录选择 Python；独立 DSH 需在活动 profile 的 cordis.patch.yml 中配置实际路径：

```yaml
- id: huaqiu-dsh-tool-freerouting
  config:
    kicadPythonPath: C:/Program Files/KiCad/10.0/bin/python.exe
```

连接优先使用显式 kicadApiSocket，其次是启动环境中的 KICAD_API_SOCKET；均未提供时才发现可连接实例。多个实例要求明确选择，填写目标编辑器的完整 ipc:// 地址。明确地址连接失败不会改连另一块板。

## 配置与工具

| 配置               | 默认值  | 作用                                                   |
| ------------------ | ------- | ------------------------------------------------------ |
| realtime           | true    | 在 KiCad 显示临时走线                                  |
| drawtimeSec        | 5       | 每隔几秒尝试发布新预览；实际间隔取决于计算和原生操作   |
| requestTimeoutMs   | 120000  | 原生请求与引擎请求的等待期限；已开始的板子写入另行保护 |
| freeroutingBaseUrl | 空      | 自动分配本机独立端口；非空为固定服务地址               |
| resultTtlMs        | 3600000 | 终态后的结果保留时间，默认一小时                       |

完整配置、范围和默认值见 [config.ts](src/config.ts)。占用与待检查状态保存在 ipcStateDirectory；保持实际使用目录稳定，不通过删除该目录处理故障。插件固定只允许一个活动布线任务，没有等待队列，也没有 maxPendingRuns 配置。

| 工具                      | 当前行为                                                                         |
| ------------------------- | -------------------------------------------------------------------------------- |
| pcb_route                 | 整板自动布线；fast 默认 false，skip_net_classes 默认空数组，fill_zones 默认 true |
| pcb_cancel_route          | 请求取消，并在后续结果中说明原因和停止确认情况                                   |
| pcb_confirm_board_checked | 人工检查后明确确认板子状态；新请求通常可在联合核验后处理旧记录                   |
| pcb_repair_route          | 保留入口，返回自动重新布线已停用                                                 |

fast=true 关闭优化器，不切换算法。skip_net_classes 使用 FreeRouting 内部精确网络类名，原样传给主自动布线阶段的 autorouter.ignore_net_classes；默认类通常为 default，扇出和优化仍可能处理排除类。任意网名筛选、skip_nets 和 pcb_route_nets 不受支持。

整轮布线不限总时长，持续运行到完成、取消或明确故障；单次通信仍有超时。

自动启动使用所选 KiCad 的引擎，可通过 DSH_FREEROUTING_EXE 明确指定。自动端口只在本次引擎存活期间固定；显式服务地址保持固定。旧计算未确认结束时不开始下一次计算。

## 使用与故障处理

1. 备份工程，打开目标 PCB 并确认 API 可连接。
2. 发起请求，例如「使用 pcb_route 整板布线，保留已有走线和过孔，skip_net_classes=[]、fast=false、fill_zones=false，不保存文件」。
3. 观察 KiCad 和任务卡片。小板可能在首批预览前结束，不保证每隔固定秒数一定新增画面内容。
4. 按最终提示检查实际新增走线、过孔和铺铜，再手动保存。

临时走线被修改时，可选择删除仍可识别的本轮对象或保留。五分钟未回答默认保留；确认删除后也结束本轮供检查，不继续添加最终结果。自行新增、分割或失去身份的线段可能留下。

最终新增通常可用一次撤销移除；更早的撤销步骤可能恢复旧预览。铺铜是独立步骤，不包含在该撤销承诺中。

关闭目标 PCB 编辑器会请求取消计算并停止插件拥有的计算程序，即使工程管理器仍开着。插件约每 2 秒通过绑定的 KiCad 连接检查目标 PCB；通信超时或忙碌不视为关闭。关闭网页标签不取消后台任务。已开始的板子写入继续等待提交或撤销结果。超时不等于操作未发生；实际回复迟到时仍处理，无法确认时保留保护状态。

已确认的新增数量不会被下载保存或清理失败覆盖。错误会说明问题、板子影响和下一步；权限不足与文件占用无法区分时会明确说明。网页状态读取或删除选择请求超时只结束该请求，可手动重读；不会重新布线或重新添加走线。

再次布线会先检查旧任务的 PCB 是否还打开。确认关闭或旧任务已取消后，先等待旧任务收尾；正常进行中的任务不会被新请求取消。没有其他活动任务时，插件会回收自己启动但仍有旧计算或无法响应的引擎，确认退出后才准备新计算；外部配置的服务不会被强制结束。目标板旧记录仍须核验宿主和写入进程，只有旧写入已退出且所有权可确认时才清理。清状态不会删除板上铜，也不会证明未知操作已撤销。任务及下载结果不跨 DSH 重启恢复。

关闭检测是定期查询。如果在两次查询之间关闭又重新打开同一路径 PCB，现有接口可能无法识别这次关闭；不保证捕获每一次瞬间关闭事件。

### Windows：PCB 已打开，但仍提示无法读取板子

如果 API 已启用，能读取 KiCad 版本，却在读取当前板子时出现以下错误，请检查当前工程与 PCB 的文件名及实际运行的 KiCad 进程：

```text
no handler available for request of type kiapi.common.commands.GetOpenDocuments
```

KiCad 允许打开不同名的 PCB，但通过文件同名来确定工程对应的 PCB。[官方手册](https://docs.kicad.org/10.0/en/kicad/kicad.html#_using_the_kicad_project_manager)规定，一个工程对应一块 PCB，工程文件、顶层原理图和 PCB 应当同名。例如 `project.kicad_pro`、`project.kicad_sch` 和 `project.kicad_pcb`。管理器通过替换工程文件的扩展名寻找工程 PCB，见 [PcbFileName 实现](https://raw.githubusercontent.com/KiCad/kicad-source-mirror/10.0.7/kicad/kicad_manager_frame.cpp)。这种约定使管理器可以直接推导 PCB 路径，无需另设文件对应关系。

工程树同时显示工程文件夹内的其他文件。因此，文件显示在工程树中、可以双击打开，并不代表它被认定为当前工程的 PCB。即使只启动了一个工程管理器，KiCad 也可能在打开不同名 PCB 时自动启动另一个进程。

在 Windows 的 KiCad `10.0.7-6-g78d68fa7a5` 上，使用同一 PCB 副本完成了以下对照测试：

| 打开方式                                                    | 实际进程与 IPC 结果                                                                           |
| ----------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
| 在 `project.kicad_pro` 管理器中打开 `other-board.kicad_pcb` | KiCad 将其作为工程外的板子，另起 `pcbnew.exe`；连接地址属于 `kicad.exe`，读取板子返回上述错误 |
| 在已有的 `other-board.kicad_pro` 管理器中打开同一 PCB       | PCB 由 `kicad.exe` 承载，没有额外的 `pcbnew.exe`；IPC 返回正确板名和副本路径                  |

不同名 PCB 另起编辑器进程属于 KiCad 的设计；随后 IPC 无法读取板子属于异常。这个触发方式可以解释「其他工程正常，只有这个工程无法连接」，不代表 PCB 文件损坏。IPC 是插件与 KiCad 通信的接口。官方已报告 Windows 多进程连接地址问题，见 [#20880](https://gitlab.com/kicad/code/kicad/-/work_items/20880)、[#23994](https://gitlab.com/kicad/code/kicad/-/work_items/23994)，修复提案见 [!2532](https://gitlab.com/kicad/code/kicad/-/merge_requests/2532)。这些记录可作为排查线索；上述对照测试尚未证明本次故障与官方问题完全同一根因。

已验证的绕过步骤：

1. 先处理 PCB 编辑器中尚未保存的修改，再关闭这个单独打开的编辑器。
2. 在工程管理器中打开与目标 PCB **同名、已经存在的** `.kicad_pro`，例如 `other-board.kicad_pro`。
3. 从该工程管理器内打开 `other-board.kicad_pcb`，然后重新连接 DSH。
4. 确认连接到的板子路径正确，再开始布线。

这只是避开已知触发方式，不是修复 KiCad。不同 `.kicad_pro` 可能保存不同的设计规则，不要直接覆盖、合并工程配置或随意重命名板子。如果没有同名工程文件，先在工程副本中核对工程与 PCB 的对应关系及设计规则。其他连接错误仍应按实际提示排查，不能仅凭“无法连接”就认定是这个问题。

## 独立仓库开发

在本项目根目录使用 Node 22.19+（22.x）或 24+，以及 package.json 声明的 pnpm。不要使用旧仓库的 node_modules、移动过的 venv，或向 KiCad 自带 Python 安装开发工具。

```powershell
pnpm install --frozen-lockfile
$env:DSH_IPC_TEST_PYTHON = 'C:/Program Files/KiCad/10.0/bin/python.exe'
$env:PYTHONUTF8 = '1'
& $env:DSH_IPC_TEST_PYTHON -m venv .build-cache/python-types
& ./.build-cache/python-types/Scripts/python.exe -m pip install -r requirements-dev.txt
$env:DSH_RUFF_BIN = "$PWD/.build-cache/python-types/Scripts/ruff.exe"
pnpm exec playwright install chromium
pnpm run package:plugin
```

package:plugin 准备固定 Python 依赖、检查、构建、打包并验证隔离全新安装，产物位于 dist/。初次准备可能需要下载锁定的 wheel；运行时包包含 Python 依赖。SDK wheel、来源和 SHA-256 锁位于 packaging/wheels/ 及依赖锁文件中。引擎源码、Java 构建和引擎自身回归不在本仓库维护。

prepack 检查本项目 LICENSE 并构建，不从父仓库复制许可证。测试、内部文档和构建缓存不进入运行时安装包。事务或进程生命周期修改需额外运行隔离真实测试，步骤见 [Python 维护](docs/python-helpers.md)与[内测说明](docs/internal-test-guide.md)。

## KiCad 内置 DSH 的开发链接

先完成 FreeRouting 依赖准备及相关插件各自的构建。相邻 .dsh-dev-packages 集合的 sources.json 必须列出完整插件集和源码绝对路径，不能依次用不完整集合覆盖链接。

```powershell
pnpm run prepare:dependencies
pnpm run check:dev-links
pnpm run link-plugins:kicad
pnpm run plugin-status:kicad
# 其他安装位置可追加 --runtime 和 --collection。
```

脚本调用运行时提供的官方 hq-edge-plugins 工具，只为 FreeRouting 和 pcb-autoplacement 生成发布文件副本，其余插件保持来源链接。副本位于 .build-cache/dev-plugin-packages/，保留随包依赖、排除开发文件。准备失败时保留旧副本；状态命令只读。构建本仓库不会代替其他插件构建。

脚本不修改 profile 或自动重启宿主。等布线、写入和清理结束后，再通过 KiCad 的 Reset DSH 加载。首次配置与打包不要求拥有旧 dsh-pcb-eda 仓库。公开来源元数据保留历史出处，不据此推断当前 Git remote 或活动安装版本。

## 许可证与反馈

插件自有代码采用本项目 LICENSE 中的 MIT 许可证。FreeRouting 引擎由 KiCad 分发，保留 GPL-3.0；插件仅保留 bundled/freerouting.LICENSE 参考声明，对应源码由引擎发行方提供。第三方说明见 [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md)。

使用时遇到 Bug，请到 [Huaqiu-Electronics/dsh-tool-freerouting 的 Issues](https://github.com/Huaqiu-Electronics/dsh-tool-freerouting/issues) 提交问题。请说明复现步骤、预期结果和实际结果，并提供插件、DSH、KiCad 版本、失败阶段、最终提示和脱敏日志。可提供已保留的下载文件或可公开的工程副本；不要附上令牌、账号配置或保密 PCB 数据。
