diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..2152fce --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,39 @@ +Rules: +- 涉及 QET <-> FreeCAD 协同开发前,先阅读 `D:\project\LightWork3D\FreeCAD\docs\数据库设计.md` +- 第一版 2D/3D 协同只允许使用这两张绑定表: + - `project_2d3d_symbol_binding` + - `project_2d3d_terminal_binding` +- 第一版禁止依赖这些 Legacy 表: + - `project_3d_scene_instance` + - `project_3d_space_object` + - `project_2d3d_link` + - `start_end_terminal_matches` +- 第一版禁止假定 3D 位姿保存在数据库中;3D 位姿和装配状态以 FreeCAD 文档为准 +- `device_attribute` 只作为设备类型主数据表使用,不承担 2D/3D 实例绑定职责 +- 3D 模型资源绑定优先使用 `device_3d_asset`,`device_attribute.parts_3d` 仅允许作为兼容/回退字段 +- `project_2d3d_symbol_binding` 第一版只允许依赖: + - `project_uuid` + - `element_uuid` + - `instance_id` +- `project_2d3d_terminal_binding` 第一版只允许依赖: + - `project_uuid` + - `terminal_uuid` + - `instance_id` +- 第一版禁止继续依赖这些字段: + - `source_diagram_uuid` + - `scene_diagram_uuid` + - `device_id` + - `display_tag` + - `asset_uri` + - `host_binding_mode` + - `host_object_id` + - `host_object_type` + - `extra_json` + - `binding_key` + - `symbol_terminal` + - `terminal_key` + - `connection_point_key` + - `wire_label` + - `net_id` + - `conductor_uuid` +- 第一版 3D 端子绑定唯一依据是 `terminal_uuid` diff --git a/docs/FreeCAD Windows 编译运行指南.md b/docs/FreeCAD Windows 编译运行指南.md new file mode 100644 index 0000000..d85137e --- /dev/null +++ b/docs/FreeCAD Windows 编译运行指南.md @@ -0,0 +1,522 @@ +# FreeCAD Windows 编译运行傻瓜指南 + +这份文档面向第一次在 Windows 上编译 FreeCAD 的同学。 + +目标是: + +- 不改源码 +- 用 Visual Studio 2022 成功编译 +- 能在本机直接运行 FreeCAD +- 能在 VS 里按 `F5` 调试 + +本文档基于下面这套已经验证成功的组合: + +- 源码分支:`releases/FreeCAD-1-1` +- 源码版本:`1.1.1-28-g94f4cb77f6` +- 编译器:Visual Studio 2022 +- 构建配置:`RelWithDebInfo | x64` +- LibPack:`LibPack-1.1.0-v3.1.1.3-Release` + +## 1. 先理解三个目录 + +编译 FreeCAD 时,最好把“源码目录”“构建目录”“运行目录”分开。 + +建议按下面这样放: + +```text +D:\project\LightWork3D\FreeCAD 源码目录 +E:\fc\LibPack-1.1.0-v3.1.1.3-Release 依赖目录 +E:\fc\build-relwithdebinfo-libpack3113 构建目录 +E:\fc\run-FreeCAD-1.1.1 安装后的运行目录 +``` + +不要把所有东西都堆在一个目录里。 + +## 2. 需要安装的软件 + +至少准备好这些: + +1. `Visual Studio 2022` +2. `Desktop development with C++` 工作负载 +3. `CMake` +4. `Git` + +建议: + +- 用 64 位系统 +- `E:` 盘最好预留 100GB 以上 +- `C:` 和 `D:` 至少各留 30GB 可用空间 + +## 3. 下载源码 + +把 FreeCAD 源码放到: + +```text +D:\project\LightWork3D\FreeCAD +``` + +如果你已经有源码,确认当前分支是: + +```powershell +git -C D:\project\LightWork3D\FreeCAD rev-parse --abbrev-ref HEAD +``` + +应该看到: + +```text +releases/FreeCAD-1-1 +``` + +## 4. 下载完全匹配的 LibPack + +这一步非常重要。 + +对于这份源码,不要随便换别的 LibPack。已经验证成功的是: + +[LibPack-1.1.0-v3.1.1.3-Release.7z](https://github.com/FreeCAD/FreeCAD-LibPack/releases/download/3.1.1.3/LibPack-1.1.0-v3.1.1.3-Release.7z) + +下载后解压到: + +```text +E:\fc\LibPack-1.1.0-v3.1.1.3-Release +``` + +不要用 `3.1.1.2`。 +这份源码配 `3.1.1.2` 时,已经实测会在链接阶段报 `boost::program_options ... contains` 相关错误。 + +## 5. 清理磁盘空间 + +如果你之前编过很多次,建议先清理旧构建目录。 + +重点清理这些“可再生目录”: + +```text +旧 build 目录 +旧 LibPack 目录 +下载缓存 +C:\Users\<你的用户名>\AppData\Local\Temp +C:\Windows\Temp +``` + +如果你在编译中看到下面这些错误: + +```text +No space left on device +磁盘空间不足 +无法创建目录 +``` + +那不是源码问题,就是磁盘满了。 + +## 6. 新建构建目录 + +在 `E:` 盘新建目录: + +```text +E:\fc\build-relwithdebinfo-libpack3113 +``` + +## 7. 用 CMake 生成 VS 工程 + +打开 PowerShell,执行: + +```powershell +$cmake = 'C:\CMake\bin\cmake.exe' +$src = 'D:\project\LightWork3D\FreeCAD' +$build = 'E:\fc\build-relwithdebinfo-libpack3113' +$libpack = 'E:\fc\LibPack-1.1.0-v3.1.1.3-Release' + +$env:Path = @( + 'C:\CMake\bin', + 'D:\VisualStudio\MSBuild\Current\Bin', + 'D:\VisualStudio\Common7\IDE', + 'C:\Git\cmd', + 'C:\Windows\System32', + 'C:\Windows', + 'C:\Windows\System32\Wbem', + 'C:\Windows\System32\WindowsPowerShell\v1.0', + "$libpack\bin", + "$libpack\lib" +) -join ';' + +& $cmake ` + -S $src ` + -B $build ` + -G 'Visual Studio 17 2022' ` + -A x64 ` + -D FREECAD_LIBPACK_USE=ON ` + -D FREECAD_LIBPACK_DIR=$libpack ` + -D CMAKE_CONFIGURATION_TYPES=RelWithDebInfo +``` + +成功后会生成: + +```text +E:\fc\build-relwithdebinfo-libpack3113\FreeCAD.sln +``` + +## 8. 编译前先记住一个坑 + +这套环境里,`TechDraw` 模块会触发 MSVC 的编译器堆空间不足: + +```text +error C1060: 编译器的堆空间不足 +``` + +这不是源码坏了。 +解决办法是不改源码,只在编译时强制: + +```text +/MP1 +``` + +也就是让 `cl.exe` 单进程编译重模块。 + +## 9. 编译整个工程 + +继续在 PowerShell 里执行: + +```powershell +$cmake = 'C:\CMake\bin\cmake.exe' +$build = 'E:\fc\build-relwithdebinfo-libpack3113' +$libpack = 'E:\fc\LibPack-1.1.0-v3.1.1.3-Release' + +$env:Path = @( + 'C:\CMake\bin', + 'D:\VisualStudio\MSBuild\Current\Bin', + 'D:\VisualStudio\Common7\IDE', + 'C:\Git\cmd', + 'C:\Windows\System32', + 'C:\Windows', + 'C:\Windows\System32\Wbem', + 'C:\Windows\System32\WindowsPowerShell\v1.0', + "$libpack\bin", + "$libpack\lib" +) -join ';' + +$env:_CL_ = '/MP1' + +& $cmake --build $build --config RelWithDebInfo --target ALL_BUILD --parallel 1 +``` + +说明: + +- `--parallel 1` 是 MSBuild 外层单并发 +- `_CL_=/MP1` 是 C/C++ 编译器内层单并发 + +这两个都保留,最稳。 + +## 10. 不要直接运行 build 目录里的 FreeCAD.exe + +很多人编译成功后,第一反应是去点: + +```text +E:\fc\build-relwithdebinfo-libpack3113\bin\RelWithDebInfo\FreeCAD.exe +``` + +这通常会报各种缺 DLL,比如: + +- `xerces-c_3_2.dll` +- `icuuc74.dll` +- `boost_program_options-vc143-mt-x64-1_87.dll` +- `pyside6.abi3.dll` +- `shiboken6.abi3.dll` + +原因不是没编好,而是它只是构建产物,不是整理好的运行目录。 + +## 11. 生成真正可运行的安装目录 + +编译完成后执行: + +```powershell +$cmake = 'C:\CMake\bin\cmake.exe' +$build = 'E:\fc\build-relwithdebinfo-libpack3113' +$prefix = 'E:\fc\run-FreeCAD-1.1.1' + +& $cmake --install $build --config RelWithDebInfo --prefix $prefix +``` + +执行完以后,真正建议运行的是: + +```text +E:\fc\run-FreeCAD-1.1.1\bin\FreeCAD.exe +``` + +## 12. 推荐运行方式 + +推荐做法不是双击某个 `bat`,而是: + +1. 先执行 `INSTALL` +2. 然后在 Visual Studio 里直接按 `F5` + +这样有几个好处: + +- 断点能直接生效 +- 环境变量配置固定在 VS 里,不容易忘 +- 每次改完代码后可以继续用同一套调试入口 + +运行时真正使用的程序是: + +```text +E:\fc\run-FreeCAD-1.1.1\bin\FreeCAD.exe +``` + +## 13. 在 VS 中打开哪个目录 + +你在 Visual Studio 里要打开的是: + +```text +E:\fc\build-relwithdebinfo-libpack3113 +``` + +准确地说,是打开这个文件: + +[E:\fc\build-relwithdebinfo-libpack3113\FreeCAD.sln](E:\fc\build-relwithdebinfo-libpack3113\FreeCAD.sln) + +不要打开: + +- `D:\project\LightWork3D\FreeCAD` 作为“编译入口” +- `E:\fc\run-FreeCAD-1.1.1` 作为“解决方案目录” + +这两个都不是 VS 编译入口。 + +## 14. 在 VS 中怎样直接 F5 运行 + +### 14.1 启动工程 + +打开解决方案后: + +1. 配置切到 `RelWithDebInfo | x64` +2. 右键 `FreeCADMain` +3. 选择“设为启动项目” + +### 14.2 调试配置 + +打开: + +`FreeCADMain -> 属性 -> 配置属性 -> 调试` + +填写: + +`命令` + +```text +E:\fc\run-FreeCAD-1.1.1\bin\FreeCAD.exe +``` + +`工作目录` + +```text +E:\fc\run-FreeCAD-1.1.1\bin +``` + +`环境` + +```text +FREECAD_LIBPACK_BIN=E:\fc\LibPack-1.1.0-v3.1.1.3-Release\bin +PATH=E:\fc\LibPack-1.1.0-v3.1.1.3-Release\bin;E:\fc\LibPack-1.1.0-v3.1.1.3-Release\lib;E:\fc\LibPack-1.1.0-v3.1.1.3-Release\bin\Lib\site-packages\PySide6;E:\fc\LibPack-1.1.0-v3.1.1.3-Release\bin\Lib\site-packages\shiboken6;E:\fc\run-FreeCAD-1.1.1\bin;%PATH% +QT_PLUGIN_PATH=E:\fc\LibPack-1.1.0-v3.1.1.3-Release\plugins +QML2_IMPORT_PATH=E:\fc\LibPack-1.1.0-v3.1.1.3-Release\qml +``` + +配完以后,直接按 `F5`。 + +注意: + +- `FREECAD_LIBPACK_BIN` 这一行不要漏 +- 它不是可有可无的变量 +- 在 Windows + Python 3.12 下,很多模块会靠它触发 `os.add_dll_directory(...)` +- 如果漏掉它,程序本体可能能启动,但打开带 `Part`、`Measure`、`TechDraw`、`PartDesignGui` 的工程时会报 `DLL load failed while importing ...` + +### 14.3 如果打开自带工程时报模块导入失败 + +如果你打开 FreeCAD 自带工程后,消息面板里出现类似这些错误: + +```text +DLL load failed while importing Part +DLL load failed while importing Measure +DLL load failed while importing TechDraw +DLL load failed while importing PartDesignGui +Cannot create object 'Page' +Cannot create object 'Template' +Cannot create object 'Hatch' +``` + +先不要怀疑源码,也不要先改代码。 + +先检查下面 4 件事: + +1. 你运行的是不是 `E:\fc\run-FreeCAD-1.1.1\bin\FreeCAD.exe` +2. 你是不是已经执行过 `INSTALL` +3. VS 调试环境里有没有 `FREECAD_LIBPACK_BIN=E:\fc\LibPack-1.1.0-v3.1.1.3-Release\bin` +4. `PATH` 里有没有 LibPack 的 `bin/lib`、PySide6、shiboken6、运行目录的 `bin` + +这类报错在当前这套环境下,绝大多数都属于“VS 调试环境没配完整”,不是源码本身坏了。 + +## 15. 每次改完代码后该怎么做 + +推荐顺序: + +1. 在 VS 里编译你改动的项目 +2. 再编一次 `ALL_BUILD` +3. 再执行一次 `INSTALL` +4. 在 VS 里按 `F5` + +如果你不执行 `INSTALL`,运行目录里的文件可能不是最新的。 + +## 16. 常见错误和对应处理 + +### 错误 1:`python312_d.lib` 找不到 + +原因: + +- 你在编 `Debug` +- 但 LibPack 是 release 版 + +解决: + +- 不要用 `Debug` +- 用 `RelWithDebInfo` 或 `Release` + +### 错误 2:`boost::program_options ... contains` 链接失败 + +原因: + +- LibPack 版本不匹配 + +解决: + +- 改用 `LibPack-1.1.0-v3.1.1.3-Release` + +### 错误 3:`No space left on device` / `磁盘空间不足` + +原因: + +- 盘满了 + +解决: + +- 清理旧 build +- 清理旧 LibPack +- 清理 Temp +- 把构建目录移到 `E:` + +### 错误 4:`C1060 编译器的堆空间不足` + +原因: + +- `TechDraw` 太重 +- 编译器内部并发太高 + +解决: + +```powershell +$env:_CL_='/MP1' +cmake --build E:\fc\build-relwithdebinfo-libpack3113 --config RelWithDebInfo --target ALL_BUILD --parallel 1 +``` + +### 错误 5:直接双击 exe 提示缺 DLL + +原因: + +- 你点的是构建产物目录里的裸 `exe` + +解决: + +- 不要直接点 `build\bin\RelWithDebInfo\FreeCAD.exe` +- 先执行 `INSTALL` +- 然后在 VS 中把启动命令设为 `E:\fc\run-FreeCAD-1.1.1\bin\FreeCAD.exe` +- 并在 VS 调试配置里补好 `PATH`、`QT_PLUGIN_PATH`、`QML2_IMPORT_PATH` + +### 错误 6:打开自带工程时报 `DLL load failed while importing Part/Measure/TechDraw/PartDesignGui` + +原因: + +- 运行时不是源码坏了 +- 而是 VS 的调试环境缺少关键变量 +- 最常见的是漏掉 `FREECAD_LIBPACK_BIN` + +解决: + +- 确认已经执行过 `INSTALL` +- 在 VS 的调试环境中加入: + +```text +FREECAD_LIBPACK_BIN=E:\fc\LibPack-1.1.0-v3.1.1.3-Release\bin +``` + +- 同时保留: + +```text +PATH=E:\fc\LibPack-1.1.0-v3.1.1.3-Release\bin;E:\fc\LibPack-1.1.0-v3.1.1.3-Release\lib;E:\fc\LibPack-1.1.0-v3.1.1.3-Release\bin\Lib\site-packages\PySide6;E:\fc\LibPack-1.1.0-v3.1.1.3-Release\bin\Lib\site-packages\shiboken6;E:\fc\run-FreeCAD-1.1.1\bin;%PATH% +QT_PLUGIN_PATH=E:\fc\LibPack-1.1.0-v3.1.1.3-Release\plugins +QML2_IMPORT_PATH=E:\fc\LibPack-1.1.0-v3.1.1.3-Release\qml +``` + +说明: + +- 这类问题通常可以通过环境配置解决 +- 不需要先修改任何源码 + +## 17. 一套最终可复用的命令 + +### 生成工程 + +```powershell +$cmake = 'C:\CMake\bin\cmake.exe' +$src = 'D:\project\LightWork3D\FreeCAD' +$build = 'E:\fc\build-relwithdebinfo-libpack3113' +$libpack = 'E:\fc\LibPack-1.1.0-v3.1.1.3-Release' + +$env:Path = @( + 'C:\CMake\bin', + 'D:\VisualStudio\MSBuild\Current\Bin', + 'D:\VisualStudio\Common7\IDE', + 'C:\Git\cmd', + 'C:\Windows\System32', + 'C:\Windows', + 'C:\Windows\System32\Wbem', + 'C:\Windows\System32\WindowsPowerShell\v1.0', + "$libpack\bin", + "$libpack\lib" +) -join ';' + +& $cmake -S $src -B $build -G 'Visual Studio 17 2022' -A x64 -D FREECAD_LIBPACK_USE=ON -D FREECAD_LIBPACK_DIR=$libpack -D CMAKE_CONFIGURATION_TYPES=RelWithDebInfo +``` + +### 编译 + +```powershell +$env:_CL_='/MP1' +cmake --build E:\fc\build-relwithdebinfo-libpack3113 --config RelWithDebInfo --target ALL_BUILD --parallel 1 +``` + +### 安装到运行目录 + +```powershell +cmake --install E:\fc\build-relwithdebinfo-libpack3113 --config RelWithDebInfo --prefix E:\fc\run-FreeCAD-1.1.1 +``` + +### 运行 + +打开: + +[E:\fc\build-relwithdebinfo-libpack3113\FreeCAD.sln](E:\fc\build-relwithdebinfo-libpack3113\FreeCAD.sln) + +确认: + +- 启动项目是 `FreeCADMain` +- 配置是 `RelWithDebInfo | x64` +- 已执行过 `INSTALL` +- 调试命令指向 `E:\fc\run-FreeCAD-1.1.1\bin\FreeCAD.exe` + +然后按: + +```text +F5 +``` + +--- + +如果你严格按这份文档走,基于当前这份源码和这套 LibPack,已经实测可以成功编译并运行。 +如果后面换了源码分支,第一件事先确认:**LibPack 版本是不是还匹配。** diff --git a/FREECAD_SECONDARY_DEVELOPMENT_CN.md b/docs/FreeCAD 二次开发说明.md similarity index 55% rename from FREECAD_SECONDARY_DEVELOPMENT_CN.md rename to docs/FreeCAD 二次开发说明.md index f14bf3d..0108a5e 100644 --- a/FREECAD_SECONDARY_DEVELOPMENT_CN.md +++ b/docs/FreeCAD 二次开发说明.md @@ -701,3 +701,494 @@ LCS 不负责: 一句话总结: > 3D 端子不应只是几何点,而应是“有位置的 2D 电气语义映射节点”。 + +## 24. 阶段 A:统一标识和数据口径 + +阶段 A 的目标不是先把 2D/3D 联动界面做出来,而是先把“双方到底在说谁”这件事说清楚。 + +如果这一阶段没有先定死,后面最容易出现的问题是: + +- 2D 里的设备实例和 3D 里的设备实例靠显示名硬猜 +- 端子编号改一次,绑定关系全部漂移 +- 同一个对象在数据库、3D 文档、界面显示里分别有三套名字 +- 回写数据时不知道应该更新哪一条记录 + +所以阶段 A 的交付物应该是: + +- 一份统一字段清单 +- 一份主键和唯一性约定 +- 一份“谁是主数据源”的边界说明 +- 一份最小同步记录样例 + +### 24.1 阶段 A 的范围 + +阶段 A 先不做下面这些事: + +- 不做 FreeCAD 端子对象代码 +- 不做自动装配 +- 不做自动路由 +- 不做 2D/3D 双向实时联动 + +阶段 A 只回答四个问题: + +1. 设备怎么唯一识别 +2. 端子怎么唯一识别 +3. 2D 和 3D 之间靠哪些字段绑定 +4. 哪些字段由 2D 负责,哪些字段由 3D 负责 + +### 24.2 总体原则 + +建议在阶段 A 固定下面四条原则: + +1. 电气语义真相源在 2D +2. 空间位姿真相源在 3D +3. 绑定靠稳定 ID,不靠显示名称 +4. 一个字段只定义一个主责任方,避免双写冲突 + +也就是说: + +- 2D 负责定义“这是什么设备、这是什么端子、它属于哪个网络” +- 3D 负责定义“这个设备装在哪里、这个端子在空间里具体在哪” + +### 24.3 阶段 A 推荐统一字段 + +建议第一版统一下面这组字段名。后续无论走 SQLite、JSON、还是接口对象,都尽量保持同名。 + +#### 24.3.1 项目级字段 + +- `project_uuid` +- `project_name` +- `source_diagram_uuid` +- `scene_diagram_uuid` + +含义建议: + +- `project_uuid`:整个项目唯一标识 +- `project_name`:项目显示名称,不参与绑定主键 +- `source_diagram_uuid`:2D 原理图/来源图纸唯一标识 +- `scene_diagram_uuid`:3D 场景或 3D 视图唯一标识 + +#### 24.3.2 设备级字段 + +- `element_uuid` +- `instance_id` +- `device_id` +- `display_tag` +- `asset_uri` +- `layout_2d_diagram` +- `host_object_id` +- `host_object_type` + +含义建议: + +- `element_uuid`:2D 元件实例唯一标识 +- `instance_id`:3D 设备实例唯一标识 +- `device_id`:设备类型或库元件标识 +- `display_tag`:界面显示标签,例如 `QF1` +- `asset_uri`:3D 资产路径或 3D 资源定位符 +- `layout_2d_diagram`:2D 布局图标识 +- `host_object_id`:3D 中的宿主对象标识,例如某导轨、安装板、柜体分区 +- `host_object_type`:宿主对象类型,例如 `Rail`、`Panel`、`Cabinet` + +#### 24.3.3 端子级字段 + +- `terminal_uuid` +- `terminal_key` +- `symbol_terminal` +- `connection_point_key` +- `terminal_direction` +- `terminal_type` +- `wire_label` +- `net_id` + +含义建议: + +- `terminal_uuid`:2D 端子实例唯一标识 +- `terminal_key`:设备内部端子业务键,例如 `QF1:13` +- `symbol_terminal`:图纸显示端子号,例如 `13` +- `connection_point_key`:3D 连接点键,例如设备模型中的 `cp_13` +- `terminal_direction`:端子方向/进出方向语义 +- `terminal_type`:端子类型语义 +- `wire_label`:当前线号/导线标签 +- `net_id`:网络唯一标识 + +### 24.4 推荐主键和唯一性约定 + +建议在阶段 A 先把“哪个字段必须稳定、哪个字段只是显示用途”彻底分开。 + +#### 24.4.1 只做显示用途,不可作为绑定主键 + +下面这些字段不建议直接作为绑定主键: + +- `project_name` +- `display_tag` +- `symbol_terminal` +- 任何中文名称 +- 任何模型树显示名称 + +原因很简单: + +- 这些字段可能会被人工修改 +- 它们有显示意义,但不一定有全局唯一性 +- 一旦改名,绑定关系会断 + +#### 24.4.2 推荐的主键约定 + +建议按下面规则理解: + +- 设备绑定主键:`project_uuid + element_uuid` +- 3D 设备实例主键:`project_uuid + instance_id` +- 端子绑定主键:`project_uuid + terminal_uuid` +- 3D 连接点主键:`project_uuid + instance_id + connection_point_key` + +如果需要业务可读键,可以再保留: + +- `terminal_key = :` + +但这个可读键建议作为辅助字段,不代替 `terminal_uuid`。 + +### 24.5 一对一 / 一对多关系约定 + +阶段 A 建议先把关系收紧,不要一开始就支持太多复杂映射。 + +第一版建议: + +- 一个 `element_uuid` 对应一个 3D `instance_id` +- 一个 `terminal_uuid` 对应一个 3D 端子对象 +- 一个 3D 端子对象对应一个 `connection_point_key` + +这意味着第一版先按最稳的模型来: + +- 一个 2D 设备实例,只落成一个 3D 设备实例 +- 一个 2D 端子,只绑定一个 3D 连接点 + +后续如果要支持: + +- 一符号多模型 +- 一端子多连接点 +- 跳线桥接 + +再在阶段 B/C 之后扩展,不建议在阶段 A 就放开。 + +### 24.6 2D 与 3D 的职责边界 + +为了避免双向覆盖,建议把字段责任按下面方式固定。 + +#### 24.6.1 由 2D 负责的字段 + +- `project_uuid` +- `source_diagram_uuid` +- `element_uuid` +- `device_id` +- `display_tag` +- `terminal_uuid` +- `terminal_key` +- `symbol_terminal` +- `wire_label` +- `net_id` +- 端子类型、端子方向、电气属性 + +也就是说,2D 负责: + +- 设备是谁 +- 端子是谁 +- 设备和端子在电气上是什么意思 +- 它们属于哪个网络 + +#### 24.6.2 由 3D 负责的字段 + +- `instance_id` 对应的空间位姿 +- 宿主对象落点 +- 3D 端子具体几何位置 +- `connection_point_key` 对应的实际连接点落位 +- 柜内装配关系 + +也就是说,3D 负责: + +- 它在柜体中装在哪里 +- 它挂在哪条导轨、哪块安装板上 +- 端子在三维空间里具体位于哪里 + +#### 24.6.3 需要双方共同遵守但不能双写冲突的字段 + +- `asset_uri` +- `instance_id` +- `connection_point_key` +- `host_object_id` +- `host_object_type` + +建议策略: + +- 第一版由 2D 提供 `asset_uri` +- 第一版由 3D 回写 `instance_id / connection_point_key / host_object_*` +- 如果 2D 后续能预先规划 3D 实例 ID,也要保证生成规则稳定且不会和 3D 重复造号 + +### 24.7 阶段 A 的推荐记录样例 + +下面给出两个“建议形态”的样例,目的是帮助阶段 A 对齐字段语义,不代表必须先改成 JSON 存储。 + +#### 24.7.1 设备级绑定样例 + +```json +{ + "project_uuid": "proj-001", + "source_diagram_uuid": "sch-001", + "element_uuid": "elem-a1b2c3", + "instance_id": "inst-qf1", + "device_id": "MCCB_1P_OF", + "display_tag": "QF1", + "asset_uri": "file:///assets/MCCB_1P_OF.step", + "layout_2d_diagram": "layout-main", + "host_object_id": "rail-01", + "host_object_type": "Rail" +} +``` + +#### 24.7.2 端子级绑定样例 + +```json +{ + "project_uuid": "proj-001", + "source_diagram_uuid": "sch-001", + "element_uuid": "elem-a1b2c3", + "terminal_uuid": "term-13", + "instance_id": "inst-qf1", + "terminal_key": "QF1:13", + "symbol_terminal": "13", + "connection_point_key": "cp_13", + "terminal_direction": "bidirectional", + "terminal_type": "power", + "wire_label": "W-001", + "net_id": "net-l1" +} +``` + +### 24.8 阶段 A 与现有 2D 代码的对应关系 + +当前 2D 代码里,阶段 A 所需的大部分基础其实已经存在。 + +#### 24.8.1 设备 3D 资源字段 + +已有: + +- `parts_3d` +- `layout_2d_diagram` + +参考: + +- [deviceeditor.cpp](D:/project/MingTuCAD/qelectrotech/sources/DeviceManager/deviceeditor.cpp:1484) +- [DeviceMeta.h](D:/project/MingTuCAD/qelectrotech/sources/DeviceManager/DeviceMeta.h:32) + +#### 24.8.2 端子属性字段 + +已有: + +- `tag` +- `direction` +- `type_id` +- `max_wire` +- `mnemonic` +- `usage` +- `max_sec` +- `min_sec` + +参考: + +- [deviceterminaleditor.cpp](D:/project/MingTuCAD/qelectrotech/sources/InsertFunction/deviceterminaleditor.cpp:94) +- [deviceterminaleditor.cpp](D:/project/MingTuCAD/qelectrotech/sources/InsertFunction/deviceterminaleditor.cpp:214) + +#### 24.8.3 2D/3D 绑定表 + +已有: + +- `project_3d_scene_instance` +- `project_3d_space_object` +- `project_2d3d_link` +- `project_2d3d_symbol_binding` +- `project_2d3d_terminal_binding` +- `start_end_terminal_matches` + +参考: + +- [projectlocaldatabase.cpp](D:/project/MingTuCAD/qelectrotech/sources/dataBase/projectlocaldatabase.cpp:364) +- [projectlocaldatabase.cpp](D:/project/MingTuCAD/qelectrotech/sources/dataBase/projectlocaldatabase.cpp:411) +- [projectlocaldatabase.cpp](D:/project/MingTuCAD/qelectrotech/sources/dataBase/projectlocaldatabase.cpp:425) +- [projectlocaldatabase.cpp](D:/project/MingTuCAD/qelectrotech/sources/dataBase/projectlocaldatabase.cpp:446) +- [projectlocaldatabase.cpp](D:/project/MingTuCAD/qelectrotech/sources/dataBase/projectlocaldatabase.cpp:469) + +所以阶段 A 的重点并不是“重新设计一套完全新的数据结构”,而是: + +- 统一字段命名 +- 统一主键规则 +- 明确哪些现有字段可以直接沿用 +- 明确哪些字段需要补充或规范化 + +### 24.9 阶段 A 的文档交付清单 + +建议阶段 A 至少完成下面这些文档性交付物: + +1. 《字段对照表》 +2. 《主键与唯一性约定》 +3. 《2D/3D 职责边界说明》 +4. 《设备级绑定样例》 +5. 《端子级绑定样例》 + +只有这几样都齐了,后面的代码实现才不容易边写边改字段语义。 + +### 24.10 阶段 A 的验收标准 + +阶段 A 完成的判断标准建议定成下面这些: + +1. 团队内部能明确回答: + - 设备靠哪个字段唯一识别 + - 端子靠哪个字段唯一识别 + - 哪些字段只是显示用途 +2. 能给出一条设备绑定记录样例 +3. 能给出一条端子绑定记录样例 +4. 能明确指出每个关键字段由 2D 还是 3D 负责 +5. 不需要看代码猜字段含义,单看文档就能开始阶段 B + +## 25. 2D -> 3D 字段对照表 + +下面这张表的目标不是一次覆盖所有未来字段,而是先把阶段 A 真正要对齐的核心字段固定下来。 + +建议使用原则: + +- 左侧 `2D 字段` 表示 QElectroTech 侧已有字段或推荐输出字段 +- 中间 `3D 字段` 表示 FreeCAD 侧对象属性、绑定对象属性或同步结构中的字段名 +- `责任方` 表示该字段的主维护方 +- `备注` 用于说明是否直接映射、是否仅显示、是否后续回写 + +### 25.1 项目级字段对照 + +| 2D 字段 | 3D 字段 | 责任方 | 备注 | +| --- | --- | --- | --- | +| `project_uuid` | `ProjectUuid` | 2D | 项目全局主键,3D 只读并保存,不自行生成 | +| `project_name` | `ProjectName` | 2D | 仅显示用途,不作为绑定主键 | +| `source_diagram_uuid` | `SourceDiagramUuid` | 2D | 2D 来源图纸主键,3D 端用于追溯来源 | +| `scene_diagram_uuid` | `SceneDiagramUuid` | 3D | 3D 场景主键,若 2D 侧已有对应字段可同步保存 | + +### 25.2 设备级字段对照 + +| 2D 字段 | 3D 字段 | 责任方 | 备注 | +| --- | --- | --- | --- | +| `element_uuid` | `ElementUuid` | 2D | 2D 设备实例主键,设备绑定首要识别字段 | +| `instance_id` | `InstanceId` | 3D | 3D 设备实例主键,建议由 3D 创建后回写 2D | +| `device_id` | `DeviceId` | 2D | 设备类型标识,可映射到元件库类型 | +| `display_tag` | `DisplayTag` | 2D | 显示名,例如 `QF1`,不可替代主键 | +| `parts_3d` / `asset_uri` | `AssetUri` | 2D | 3D 资产路径来源字段,第一版由 2D 提供 | +| `layout_2d_diagram` | `Layout2DDiagram` | 2D | 2D 布局来源标识,3D 仅保存引用 | +| `host_object_id` | `HostObjectId` | 3D | 设备在 3D 中挂载到哪个宿主对象,例如导轨或安装板 | +| `host_object_type` | `HostObjectType` | 3D | 宿主对象类型,例如 `Rail`、`Panel`、`Cabinet` | +| `pos_x / pos_y / pos_z`(若 2D 侧后续引入) | `Placement.Base` | 3D | 第一版建议位姿由 3D 主导,2D 可只保存回写结果 | +| `rot_x / rot_y / rot_z`(若 2D 侧后续引入) | `Placement.Rotation` | 3D | 同上,作为 3D 位姿结果回写,不建议 2D 首发控制 | + +### 25.3 端子级字段对照 + +| 2D 字段 | 3D 字段 | 责任方 | 备注 | +| --- | --- | --- | --- | +| `terminal_uuid` | `TerminalUuid` | 2D | 2D 端子实例主键,端子绑定核心标识 | +| `terminal_key` | `TerminalKey` | 2D | 业务可读键,例如 `QF1:13`,建议稳定生成 | +| `symbol_terminal` | `SymbolTerminal` | 2D | 图纸显示端子号,例如 `13`,仅显示和辅助检索 | +| `tag` | `TerminalTag` | 2D | 若 2D 端已有端子标签字段,可单独映射保留 | +| `direction` | `TerminalDirection` | 2D | 端子方向语义,3D 用于规则判断,不直接决定整体路由 | +| `type_id` | `TerminalTypeId` | 2D | 端子类型枚举主键,便于后续规则扩展 | +| `mnemonic` | `Mnemonic` | 2D | 端子助记符,偏显示和检索用途 | +| `usage` | `Usage` | 2D | 端子用途说明,例如控制、电源、保护 | +| `max_wire` | `MaxWire` | 2D | 最大接线数,后续可用于 3D 规则检查 | +| `max_sec` | `MaxSection` | 2D | 最大线径/截面积,第一版可先只保存不执行校验 | +| `min_sec` | `MinSection` | 2D | 最小线径/截面积,第一版可先只保存不执行校验 | +| `connection_point_key` | `ConnectionPointKey` | 3D | 3D 连接点键,建议由 3D 端确定后回写 2D | +| `wire_label` | `WireLabel` | 2D | 线号/导线标签,3D 只读并附着到端子或导线对象 | +| `net_id` | `NetId` | 2D | 网络主键,3D 连线对象应直接读取并保存 | +| `can_wire`(若 2D 侧后续显式输出) | `CanWire` | 2D/3D 共同遵守 | 第一版默认可由 3D 端按端子对象类型推导,也可由 2D 显式下发 | + +### 25.4 3D 端子与连接点扩展字段 + +这部分字段不一定在 2D 现有数据库里原生存在,但建议在 3D 侧先固定命名,便于后续回写和扩展。 + +| 2D 字段 | 3D 字段 | 责任方 | 备注 | +| --- | --- | --- | --- | +| - | `OwnerDevice` | 3D | FreeCAD 端子对象指向所属设备对象的链接属性 | +| - | `LcsName` | 3D | 端子几何基准的 LCS 名称,便于界面调试和对象检索 | +| - | `Status` | 3D | 3D 端子当前状态,例如 `Unbound`、`Bound`、`Connected` | +| `extra_json`(若已有或后续扩展) | `ExtraJson` | 双方约定 | 放临时扩展字段,但不建议承载主键语义 | + +### 25.5 表级映射建议 + +为了让字段对照真正能落到代码里,建议阶段 A 同时固定“字段主要从哪张表来”。 + +| 2D 表/来源 | 对应 3D 对象/结构 | 责任方 | 备注 | +| --- | --- | --- | --- | +| `project_2d3d_symbol_binding` | 3D 设备实例绑定记录 | 2D 写入,3D 读取/回写部分字段 | 设备级绑定主表 | +| `project_2d3d_terminal_binding` | 3D 端子对象绑定记录 | 2D 写入,3D 回写 `connection_point_key` 等 | 端子级绑定主表 | +| `project_3d_scene_instance` | 3D 场景实例位姿记录 | 3D | 位姿与宿主落位建议由 3D 主导 | +| `device_circuit_terminals` | 3D 端子语义属性来源 | 2D | 类型、方向、线径、接线数等语义来源 | +| `parts_3d` / `layout_2d_diagram` | 3D 资产入口与布局引用 | 2D | 第一版不建议 3D 自行猜资产路径 | + +### 25.6 推荐命名规则 + +为了减少后续字段漂移,建议阶段 A 先固定这些命名规则: + +- 数据库存储字段保持 `snake_case` +- FreeCAD 对象属性使用 `PascalCase` +- 界面显示名允许中文,但不参与绑定 +- `Uuid` 后缀只用于真正全局唯一标识 +- `Key` 后缀用于业务键或连接点键 +- `Id` 后缀用于类型标识、实例标识或宿主标识 + +示例: + +- 数据库字段:`terminal_uuid` +- FreeCAD 属性:`TerminalUuid` +- 界面显示:`端子 13` + +### 25.7 第一版建议严格区分的字段 + +下面这些字段看起来相近,但建议在阶段 A 就明确区分,避免后面混用: + +#### 25.7.1 `terminal_uuid` 与 `terminal_key` + +- `terminal_uuid`:机器主键,稳定唯一 +- `terminal_key`:业务可读键,便于调试和跨界面识别 + +不建议只保留 `terminal_key`,因为显示编号或设备标签变化时它可能变化。 + +#### 25.7.2 `element_uuid` 与 `instance_id` + +- `element_uuid`:2D 设备实例身份 +- `instance_id`:3D 设备实例身份 + +这两个字段不要互相替代。第一版即使它们暂时值相同,也建议语义上保持区分。 + +#### 25.7.3 `symbol_terminal` 与 `connection_point_key` + +- `symbol_terminal`:2D 图纸上的端子显示号 +- `connection_point_key`:3D 模型内部连接点键 + +一个显示端子号理论上可以映射到一个具体连接点,但两者不是同一个概念。 + +### 25.8 字段对照表的使用方式 + +这张表建议在阶段 B/C 里直接作为开发输入使用: + +1. 2D 侧先检查哪些字段已经稳定存在 +2. 对不存在但必要的字段做补充或规范化 +3. 3D 侧严格按对照表创建对象属性 +4. 接口层不要临时发明新名字,优先复用这里的统一命名 + +也就是说,后续代码实现时应该尽量做到: + +> 看字段对照表就能知道字段从哪来、落到哪去、该由谁维护。 + +### 24.11 阶段 A 结束后再进入代码 + +阶段 A 结束后,下一步再做代码时,建议顺序是: + +1. 先在 2D 侧把设备级绑定输出稳定下来 +2. 再在 3D 侧按 `instance_id` 落设备实例 +3. 设备级稳定后,再进入端子级绑定 + +也就是说,阶段 A 的价值就在于: + +> 先把绑定语言说清楚,再开始真正的数据读写和对象创建。 diff --git a/docs/启动 FreeCAD-E.bat b/docs/启动 FreeCAD-E.bat new file mode 100644 index 0000000..b26ea75 --- /dev/null +++ b/docs/启动 FreeCAD-E.bat @@ -0,0 +1,10 @@ +@echo off +setlocal +set "FC_LIBPACK=E:\fc\LibPack-1.1.0-v3.1.1.3-Release" +set "FC_RUN=E:\fc\run-FreeCAD-1.1.1" +set "FC_PYSIDE=%FC_LIBPACK%\bin\Lib\site-packages\PySide6" +set "FC_SHIBOKEN=%FC_LIBPACK%\bin\Lib\site-packages\shiboken6" +set "QT_PLUGIN_PATH=%FC_LIBPACK%\plugins" +set "QML2_IMPORT_PATH=%FC_LIBPACK%\qml" +set "PATH=%FC_LIBPACK%\bin;%FC_LIBPACK%\lib;%FC_PYSIDE%;%FC_SHIBOKEN%;%FC_RUN%\bin;%PATH%" +start "" /D "%FC_RUN%\bin" "%FC_RUN%\bin\FreeCAD.exe" diff --git a/docs/数据库设计.md b/docs/数据库设计.md new file mode 100644 index 0000000..9c45b58 --- /dev/null +++ b/docs/数据库设计.md @@ -0,0 +1,481 @@ +# 2D / 3D 协同数据库设计(第一版最小集) + +本文档只保留当前阶段真正必需的字段。 + +目标不是一次把未来所有能力都放进表里,而是先支持下面这条最小闭环: + +1. 2D 里有设备实例 +2. 2D 里有端子实例 +3. FreeCAD 里创建对应的 3D 设备实例 +4. FreeCAD 里创建对应的 3D 端子对象 +5. 2D 与 3D 可以靠稳定主键互相找到对方 + +当前设计严格遵守两条原则: + +- 电气语义以 2D 为准 +- 空间位姿以 3D 为准 + +另外再补一条当前版本明确采用的约定: + +- 3D 设备资源不在绑定表中重复保存,统一通过 `element_uuid -> device_id -> parts_3d` 回查 + +因此,第一版不重复存这些信息: + +- `display_tag` +- `terminal_key` +- `connection_point_key` +- `host_binding_mode` +- `host_object_id` +- `host_object_type` +- `extra_json` +- `scene_diagram_uuid` +- `source_diagram_uuid` + +原因很简单: + +- 这些字段当前要么能通过 2D 主键回查 +- 要么属于未来扩展 +- 要么当前语义还不够稳定 + +## 1. 统一主键约定 + +第一版只认下面这几个核心标识: + +- `project_uuid` +- `element_uuid` +- `terminal_uuid` +- `instance_id` + +含义: + +- `project_uuid`:项目唯一标识 +- `element_uuid`:2D 设备实例唯一标识 +- `terminal_uuid`:2D 端子实例唯一标识 +- `instance_id`:3D 设备实例唯一标识,由 FreeCAD 生成 + +## 2. 需要保留的最小表 + +第一版只建议保留 2 张表: + +1. `project_2d3d_symbol_binding` +2. `project_2d3d_terminal_binding` + +--- + +## 3. 设备绑定表 + +表名: + +`project_2d3d_symbol_binding` + +作用: + +> 记录“一个 2D 设备实例,对应哪个 3D 设备实例,以及它使用哪个 3D 资源” + +但“它使用哪个 3D 资源”这一点,第一版不在本表中直接存储,而是通过 2D 侧已有关系回查: + +`element_uuid -> device_id -> parts_3d` + +### 3.1 保留字段 + +| 字段名 | 中文 | 责任方 | 说明 | +| --- | --- | --- | --- | +| `project_uuid` | 项目UUID | 2D | 项目范围主键 | +| `element_uuid` | 设备实例UUID | 2D | 2D 设备实例唯一标识 | +| `instance_id` | 3D实例ID | 3D | FreeCAD 生成的 3D 设备实例唯一标识 | + +### 3.2 推荐约束 + +- 主唯一键:`(project_uuid, element_uuid)` +- 3D 实例唯一键:`(project_uuid, instance_id)` + +### 3.3 推荐 SQLite 结构 + +```sql +CREATE TABLE IF NOT EXISTS project_2d3d_symbol_binding ( + project_uuid TEXT NOT NULL, + element_uuid TEXT NOT NULL, + instance_id TEXT NOT NULL, + PRIMARY KEY (project_uuid, element_uuid) +); + +CREATE UNIQUE INDEX IF NOT EXISTS idx_symbol_binding_instance +ON project_2d3d_symbol_binding(project_uuid, instance_id); +``` + +### 3.4 为什么只留这 3 个字段 + +因为第一版要解决的问题只有: + +- 2D 设备是谁:`element_uuid` +- 3D 设备是谁:`instance_id` +- 属于哪个项目:`project_uuid` + +而 3D 模型资源本身可以通过: + +- `element_uuid` +- 回查到对应设备实例 +- 再回查 `device_id` +- 最后拿到 `parts_3d` + +所以第一版不重复存 `asset_uri`。 + +--- + +## 4. 端子绑定表 + +表名: + +`project_2d3d_terminal_binding` + +作用: + +> 记录“一个 2D 端子实例,对应哪个 3D 设备实例上的端子对象” + +### 4.1 保留字段 + +| 字段名 | 中文 | 责任方 | 说明 | +| --- | --- | --- | --- | +| `project_uuid` | 项目UUID | 2D | 项目范围主键 | +| `terminal_uuid` | 端子UUID | 2D | 2D 端子实例唯一标识 | +| `instance_id` | 3D实例ID | 3D | 该端子属于哪个 3D 设备实例 | + +### 4.2 推荐约束 + +- 主唯一键:`(project_uuid, terminal_uuid)` +- 查询索引:`(project_uuid, instance_id)` + +### 4.3 推荐 SQLite 结构 + +```sql +CREATE TABLE IF NOT EXISTS project_2d3d_terminal_binding ( + project_uuid TEXT NOT NULL, + terminal_uuid TEXT NOT NULL, + instance_id TEXT NOT NULL, + PRIMARY KEY (project_uuid, terminal_uuid) +); + +CREATE INDEX IF NOT EXISTS idx_terminal_binding_instance +ON project_2d3d_terminal_binding(project_uuid, instance_id); +``` + +### 4.4 为什么不保留 `terminal_key` 和 `connection_point_key` + +当前第一版设计里: + +- 3D 端子本身就是 2D 端子的空间映射 +- 所有端子语义都可以通过 `terminal_uuid` 从 2D 回查 + +所以目前不需要额外再保存: + +- `terminal_key` +- `symbol_terminal` +- `connection_point_key` +- `wire_label` +- `net_id` + +这些信息如果后续 3D 侧需要显示或校验,可以通过 `terminal_uuid` 回查 2D。 + +--- + +## 5. 现阶段明确删掉的字段 + +下面这些字段不是说永远没用,而是 **第一版先删掉,不进入最小表结构**。 + +### 5.1 从设备绑定表中删掉 + +- `source_diagram_uuid` +- `scene_diagram_uuid` +- `device_id` +- `display_tag` +- `asset_uri` +- `host_binding_mode` +- `host_object_id` +- `host_object_type` +- `extra_json` + +### 5.2 从端子绑定表中删掉 + +- `element_uuid` +- `binding_key` +- `symbol_terminal` +- `terminal_key` +- `connection_point_key` +- `wire_label` +- `net_id` +- `conductor_uuid` +- `extra_json` + +### 5.3 从 3D 实例表中删掉 + +- `project_3d_scene_instance` 整张表 +- 所有位姿字段:`tx / ty / tz / rx / ry / rz` +- 所有缩放字段:`sx / sy / sz` +- 所有 3D 场景附加字段:`diagram_uuid / asset_id / extra_json` + +--- + +## 6. 第一版数据流 + +### 6.1 2D -> 3D + +2D 提供: + +- `project_uuid` +- `element_uuid` +- `terminal_uuid` + +3D 在导入和实例化时生成: + +- `instance_id` + +然后写入: + +- `project_2d3d_symbol_binding` +- `project_2d3d_terminal_binding` + +### 6.2 3D -> 2D + +3D 第一版只回写: + +- `instance_id` + +当前不回写位姿。 + +原因是: + +- 3D 场景真相源已经切换为 FreeCAD +- 2D 不再承担 3D 场景保存职责 +- 避免 FreeCAD 文档和数据库各存一份位姿造成冲突 + +--- + +## 7. 这版设计的核心思想 + +第一版故意把设计收得很紧,只保留: + +- 一个 2D 设备实例如何找到 3D 设备实例 +- 一个 2D 端子实例如何找到 3D 端子对象 + +也就是说,当前数据库设计只服务于: + +> “2D 语义 + 3D 空间映射”这个最小闭环 + +而且这里的“空间映射”只表示: + +- 2D 对象知道自己对应哪个 3D 实例 + +不表示数据库要保存完整 3D 位姿。 + +--- + +## 8. 后续扩展时再加的字段 + +只有当下面这些需求真的出现时,再补字段: + +- 多 3D 场景:补 `scene_diagram_uuid` +- 设备挂载规则:补 `host_object_id / host_object_type` +- 3D 资源缓存:补 `asset_uri` +- 多连接点/复杂端子:补 `connection_point_key` +- 3D 端子独立语义缓存:补 `terminal_key / symbol_terminal` +- 导线级 3D 校验:补 `wire_label / net_id / conductor_uuid` +- 2D 侧需要读取 3D 位姿:再恢复 3D 场景实例表或等价位姿存储 +- 临时扩展:最后才考虑 `extra_json` + +--- + +## 9. FreeCAD 与数据库的职责边界 + +继续收缩后的职责建议如下: + +### 9.1 数据库负责 + +- 2D 设备实例和 3D 设备实例的绑定 +- 2D 端子实例和 3D 端子对象的绑定 + +### 9.2 FreeCAD 负责 + +- 3D 设备实例实际创建 +- 设备位姿 +- 导轨/安装板/柜体装配关系 +- 端子在空间中的实际几何位置 +- 3D 接线几何 + +也就是说,第一版开始就建议明确: + +> 3D 位姿和装配关系只保存在 FreeCAD 文档里,不在数据库中重复保存。 + +--- + +## 10. 当前推荐结论 + +第一版数据库设计建议就收成这三张表: + +1. `project_2d3d_symbol_binding` +2. `project_2d3d_terminal_binding` + +并且只保留最小核心字段: + +- 设备绑定只保留:`project_uuid / element_uuid / instance_id` +- 端子绑定只保留:`project_uuid / terminal_uuid / instance_id` + +一句话总结: + +> 第一版先让 2D 能找到 3D;至于 3D 怎么摆、摆在哪、怎么装配,全部交给 FreeCAD 自己管理。 + +--- + +## 11. 第一版交换形式 + +第一版 2D / 3D 协同,交换形式先统一为: + +- **JSON 文件交换** + +当前不建议第一版直接做: + +- 实时数据库双向同步 +- 进程间 RPC +- HTTP API +- FreeCAD 保存后直接写回 QET 运行库 + +原因: + +- JSON 最容易调试 +- 字段改动时最容易观察 +- 不会把 QET 和 FreeCAD 的运行时强耦合在一起 +- 更适合当前仍在收缩字段和表结构的阶段 + +--- + +## 12. 第一版工具入口 + +QET 侧建议保留并改造一个工具项: + +- `3D视图` + +这个工具项的职责不再是走 QET 旧的 3D 模块逻辑,而是: + +1. 从 QET 当前项目和当前 2D 图纸中整理 2D -> 3D 最小绑定数据 +2. 导出 `2d_to_3d.json` +3. 启动 FreeCAD,或打开已有的 FreeCAD 工程文件 + +也就是说: + +> 第一版 `3D视图` 本质上是“导出 2D 快照并切换到 FreeCAD”的入口。 + +--- + +## 13. 第一版交换目录建议 + +建议在项目目录下固定一套交换目录: + +```text +/.qet_freecad/ + 2d_to_3d.json + 3d_to_2d.json + scene.FCStd + logs/ +``` + +含义: + +- `2d_to_3d.json`:QET 导出给 FreeCAD 的输入 +- `3d_to_2d.json`:FreeCAD 回写给 QET 的输出 +- `scene.FCStd`:该项目对应的 FreeCAD 3D 工程 +- `logs/`:调试与排障日志 + +--- + +## 14. 第一版时机约定 + +### 14.1 QET -> FreeCAD + +触发时机: + +- 用户点击 QET 的 `3D视图` + +动作: + +1. 更新并确认当前 2D 设备实例 / 端子实例绑定 +2. 生成 `2d_to_3d.json` +3. 打开 FreeCAD +4. 打开或创建 `scene.FCStd` + +### 14.2 FreeCAD -> QET + +第一版不建议: + +- FreeCAD 保存时直接写数据库 + +第一版建议: + +- FreeCAD 保存时输出 `3d_to_2d.json` + +然后由 QET 在后续时机主动读取: + +- 手动刷新 +- 再次点击 `3D视图` +- 或后续增加单独的“从 3D 刷新”命令 + +这样做的目的,是先把同步时机收成“文件交换”,避免两边运行时直接打架。 + +--- + +## 15. 第一版最小交换内容 + +### 15.1 `2d_to_3d.json` + +第一版只要求包含最小绑定信息: + +- 设备绑定: + - `project_uuid` + - `element_uuid` + - `instance_id` +- 端子绑定: + - `project_uuid` + - `terminal_uuid` + - `instance_id` + +说明: + +- `instance_id` 在第一版中由 FreeCAD 侧生成更合理 +- 如果首次进入 3D 时尚未生成 `instance_id`,可以先导出为空,再由 FreeCAD 创建后回写 + +### 15.2 `3d_to_2d.json` + +第一版只建议回写: + +- `project_uuid` +- `element_uuid` +- `instance_id` +- `terminal_uuid` + +当前不要求回写: + +- 3D 位姿 +- 装配结构 +- 导线几何路径 + +这些仍以 FreeCAD 文档为准。 + +--- + +## 16. 第一版推荐交互流程 + +建议按下面这条闭环实现: + +1. 用户在 QET 中点击 `3D视图` +2. QET 生成 `2d_to_3d.json` +3. QET 打开 FreeCAD,并打开 `scene.FCStd` +4. FreeCAD 读取 `2d_to_3d.json` +5. FreeCAD 创建或更新: + - 3D 设备实例 + - 3D 端子对象 +6. 用户在 FreeCAD 中完成装配、摆放、接线 +7. 用户保存 FreeCAD 工程 +8. FreeCAD 生成 `3d_to_2d.json` +9. QET 在后续时机读取 `3d_to_2d.json` + +一句话总结: + +> 第一版先做“QET 导出 JSON + 打开 FreeCAD;FreeCAD 保存时回写 JSON”的文件交换闭环,不做强耦合实时同步。