SAM 插件编译、部署与故障排查 / Building, Deploying, and Troubleshooting SAM Plugins
本篇集中说明 MSVC/CMake、GUI DLL、PYD、自动复制和运行故障。架构总览见 09-sam-secondary-development-guide.md。
1. 本机路径
| 用途 | 路径 |
|---|---|
| SAM 安装目录 | D:\Program Files\SAM |
| SAM 主程序目录 | D:\Program Files\SAM\Release |
| Toolset DLL 目录 | D:\Program Files\SAM\Release\FilePlugin |
| PYD 部署目录 | D:\Program Files\SAM\Release |
| SDK | D:\wx702\OpenOLTranSim-main\SAMSDK |
| GUI 头文件 | D:\wx702\OpenOLTranSim-main\SAMSDK\include\guiProject |
2. 两个 Target、两个责任
text
YourToolsetGui target
-> SAM.Pre.YourToolset.dll
-> Qt/SAM GUI 插件
-> 部署到 Release\FilePlugin
YourKernel target
-> YourModule.pyd
-> Python 2.7 C++ 扩展
-> 部署到 Release二者可以链接同一个内部算法库,但不要把 Qt 插件入口和 Python 模块入口混在一个 Target 中。
3. CMake 基本形式
3.1 GUI DLL
cmake
add_library(YourToolsetGui SHARED
YourPlugin.cpp
YourToolsetGui.cpp
YourDialog.cpp
YourForm.cpp
)
target_include_directories(YourToolsetGui PRIVATE
"D:/wx702/OpenOLTranSim-main/SAMSDK/include"
"D:/wx702/OpenOLTranSim-main/SAMSDK/include/guiProject"
)
set_target_properties(YourToolsetGui PROPERTIES
OUTPUT_NAME "SAM.Pre.YourToolset"
)实际链接库应根据使用的头文件及 SDK 示例确定,不能只靠“头文件能找到”判断链接完整。
3.2 PYD
cmake
add_library(YourModule MODULE
YourModule.cpp
YourModelFragment.cpp
YourPartFragment.cpp
)
set_target_properties(YourModule PROPERTIES
PREFIX ""
SUFFIX ".pyd"
)PYD 必须链接 SAM 对应库、Python 2.7 库和实际使用的 C++ 依赖。
4. 自动复制
4.1 GUI DLL
cmake
add_custom_command(TARGET YourToolsetGui POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"$<TARGET_FILE:YourToolsetGui>"
"D:/Program Files/SAM/Release/FilePlugin/$<TARGET_FILE_NAME:YourToolsetGui>"
)4.2 PYD
cmake
add_custom_command(TARGET YourModule POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"$<TARGET_FILE:YourModule>"
"D:/Program Files/SAM/Release/$<TARGET_FILE_NAME:YourModule>"
)自动复制只解决部署,不会让 DLL 自动调用 PYD。
5. MSVC 与 ABI
必须同时满足:
- x64 与 SAM 一致;
- MSVC 工具集兼容;
- Qt 版本和 ABI 兼容;
- Python 版本为 SAM 使用的 Python 2.7;
- Release Runtime 与 SAM 一致;
/MD、迭代器调试等级等运行库设置兼容;- C++ 对象不能在不兼容 Runtime 之间错误释放。
常见错误组合:
- Debug 插件依赖 Debug Qt/Python,SAM 只有 Release DLL;
- PYD 使用 Python 3 头文件和库;
- 编译为 Win32;
- 编译器版本差异导致 Qt 或 STL ABI 不一致;
- 二级依赖缺失,文件存在但加载失败。
6. CLion 图形化编译
- 用 CLion 打开包含顶层
CMakeLists.txt的项目目录。 - 进入
Settings | Build, Execution, Deployment | Toolchains。 - 选择 Visual Studio Toolchain,Architecture 设为
amd64/x86_64。 - 在
CMakeProfile 中选择 Release 或 RelWithDebInfo。 - 重新加载 CMake。
- 右上角 Target 下拉框分别选择 GUI DLL 和 PYD Target。
- 点击锤子 Build,或使用
Build | Build Project。 - 检查 Build 输出最后的 POST_BUILD copy 是否成功。
- 完全关闭并重新启动 SAM,让插件重新扫描。
如果 SAM 正在运行,Windows 可能锁定 DLL/PYD,使复制失败或留下旧版本。
7. GUI 插件没有出现
按顺序检查:
- 文件是否在
Release\FilePlugin; - 文件名和输出目录是否正确;
- 是否实现
SAMToolsetGuiInterface; - 是否使用 SDK 中实际 IID;
- 是否有
Q_PLUGIN_METADATA、Q_INTERFACES; registerToolset()是否创建并注册对象;- DLL 是否缺少二级依赖;
- Qt、MSVC、x64 ABI 是否一致;
- 是否被当前 Module 的可见性规则隐藏。
8. 菜单出现但点击无反应
检查:
QAction::triggered是否连接;- 接收对象是否已经销毁;
- 新旧 Qt 信号槽签名是否匹配;
- Action 是否被禁用;
- Form 是否注册到正确 owner;
- 槽函数是否抛出异常或提前返回。
9. PYD 无法导入
在 SAM 命令窗口输入:
python
import YourModule
print(YourModule)不要输入 >>>,不要添加前导空格。
检查:
YourModule.pyd是否在 Python 可搜索路径;- 文件名与模块导出名是否一致;
- Python 2.7、x64 和 Release ABI;
- 是否缺少依赖 DLL;
- 初始化函数是否正确注册;
- 是否仍在使用旧文件。
10. 导入成功但功能不存在
说明加载器已经找到 PYD。继续检查:
pyoModule的方法表;DefineConstants()、DefineType();iniPythonModuleRegistrar;- Model/Part Fragment 的注册层级;
- Python 调用的大小写和方法名;
- 初始化代码是否因异常提前结束。
11. 模型为空
先检查:
python
print(mdb.models.keys())
print(mdb.models['Model-1'].parts.keys())如果 parts 为 [],说明还没有 Part。此时读取节点、单元、面积或输出点坐标都不会得到业务结果。
12. 计算成功但没有图形
依次判断:
- 只计算了数值,还是创建了真实 Part/Feature/Mesh?
- 修改的是临时副本还是模型中实际对象?
- 是否调用再生和有效性更新?
- 是否通知
ptoKPart::UpdateScene()或等价机制? - 当前 Scene 是否 Update/Refresh?
- DisplayRep 中是否生成面而不仅是红色轮廓线?
- 面连接、法向、颜色和背面剔除是否正确?
13. Apply 点击后没有输出
检查链路:
text
Apply 按钮
-> SAMDataDialog::onCmdApply
-> SAMGuiMode/SAMForm::onCmdCommit
-> 参数校验
-> issueCommands 或自定义计算
-> Message Area/Dialog 输出还要检查当前 Model、Part、节点和单元是否存在。
14. 推荐的最小验证顺序
- DLL 能被扫描并显示一个菜单;
- 菜单点击能弹出纯 Qt 警告框;
- Dialog 的 OK/Apply 能进入槽函数;
- GUI 能发送简单 Python
print命令; - PYD 可以单独
import; - PYD 的纯数学函数可以调用;
- PYD 能读取当前 Model/Part;
- PYD 能读取节点和单元;
- 最后再尝试修改模型和刷新渲染。
分层验证可以快速确定问题发生在插件扫描、Qt 事件、命令桥、Python 导入、模型访问还是渲染阶段。