1. NX二次开发与Parasolid格式转换概述
在工业设计领域,NX(原Unigraphics)作为主流的三维CAD/CAM/CAE软件,其二次开发能力为工程师提供了强大的定制化工具。本次我们将探讨如何通过Python脚本实现NX装配图到Parasolid(.x_t)格式的转换——这是工程设计流程中一个极具实用价值的操作。
Parasolid作为几何建模内核,被广泛应用于CAD数据交换。其.x_t格式是一种文本格式的Parasolid文件,相比二进制.x_b格式更易于版本控制和跨平台处理。在实际工程协作中,不同部门或供应商可能使用不同的CAD系统,而Parasolid格式因其良好的兼容性成为中间交换的理想选择。
提示:Parasolid文件不包含特征历史信息,仅保存BREP(边界表示)几何数据,这既是优势(文件精简)也是局限(无法参数化编辑)
Python在NX二次开发中的应用越来越普遍,相比传统的Journal脚本或C++/C#方案,Python具有以下优势:
- 语法简洁,开发效率高
- 丰富的第三方库支持
- 跨平台兼容性好
- 便于与非CAD系统集成
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 开发环境准备与基础配置
2.1 NX Open Python环境搭建
要开始NX二次开发,首先需要配置正确的Python环境。NX通常内置了Python解释器,但版本可能较旧。建议使用与NX兼容的外部Python环境:
-
确认NX版本对应的Python版本:
- NX 1847系列:Python 3.6.x
- NX 1872系列:Python 3.7.x
- NX 1980系列:Python 3.8.x
- 最新NX版本:通常支持Python 3.9+
-
安装NX Open Python包:
python复制# 在NX安装目录下通常已有这些包
import NXOpen
import NXOpen.UF
import NXOpen.Utilities
- 配置开发环境:
- 推荐使用VS Code + Python插件
- 设置PYTHONPATH包含NX的python库路径
- 安装pylint等静态检查工具
2.2 基本程序结构
每个NX Open Python程序都应包含以下基本结构:
python复制import NXOpen
import NXOpen.UF
def main():
theSession = NXOpen.Session.GetSession()
theUfSession = NXOpen.UF.UFSession.GetUFSession()
workPart = theSession.Parts.Work
# 你的代码逻辑
theSession.UpdateManager.DoUpdate(NXOpen.Update.Options.WithinModeling)
if __name__ == '__main__':
main()
3. 装配图转Parasolid的核心实现
3.1 获取当前装配结构
转换前需要正确获取装配结构。NX中的装配体通过组件(Component)组织:
python复制def get_assembly_structure(workPart):
rootComponent = workPart.ComponentAssembly.RootComponent
components = []
def traverse(component):
components.append(component)
for child in component.GetChildren():
traverse(child)
traverse(rootComponent)
return components
3.2 设置导出参数
Parasolid导出需要配置多个关键参数:
python复制def setup_export_options():
builder = theSession.PartExportManager.CreateParasolidExportBuilder()
# 设置导出版本(建议使用较新版本保证兼容性)
builder.FileVersion = NXOpen.ParasolidExportBuilder.FileVersionType.Version330
# 导出选项
builder.ExportFrom = NXOpen.ParasolidExportBuilder.ExportFromType.DisplayPart
builder.ObjectTypes = NXOpen.ParasolidExportBuilder.ObjectTypesType.Solids
builder.OutputFileType = NXOpen.ParasolidExportBuilder.OutputFileType.Text
# 高级选项
builder.AdvancedOptions.Tolerance = 0.001 # 导出公差
builder.AdvancedOptions.WriteFreeSurfaces = True
return builder
3.3 执行导出操作
核心导出代码实现:
python复制def export_to_parasolid(file_path):
try:
builder = setup_export_options()
builder.FileName = file_path
# 验证并执行导出
result = builder.Validate()
if result == NXOpen.ParasolidExportBuilder.ValidateResult.Ok:
builder.Commit()
print(f"成功导出到 {file_path}")
else:
print(f"导出验证失败: {result}")
except Exception as e:
print(f"导出过程中出错: {str(e)}")
finally:
if 'builder' in locals():
builder.Destroy()
4. 实战技巧与常见问题解决
4.1 性能优化策略
处理大型装配体时,可采用以下优化方法:
- 内存管理:
python复制# 定期清理临时对象
theSession.UpdateManager.ClearErrorList()
theSession.DeleteUndoMark(None, "Export Mark")
- 分批处理:
python复制# 对大型装配分批次导出
batch_size = 50
for i in range(0, len(components), batch_size):
batch = components[i:i+batch_size]
export_batch(batch)
- 多线程处理(需谨慎):
python复制from concurrent.futures import ThreadPoolExecutor
def export_worker(component):
# 设置每个组件的工作部件
theSession.Parts.SetWork(component.Prototype)
export_to_parasolid(f"output_{component.Name}.x_t")
with ThreadPoolExecutor(max_workers=4) as executor:
executor.map(export_worker, components)
4.2 常见错误排查
- 许可证问题:
- 症状:导出时报"Parasolid license not available"
- 解决方案:检查NX许可证是否包含Parasolid导出权限
- 几何错误:
- 症状:导出文件损坏或无法导入
- 解决方案:
python复制# 导出前修复几何 theUfSession.Modl.AskHealGeometry(workPart.Tag, 0.01, 1)
- 路径问题:
- 症状:文件保存失败
- 解决方案:
python复制import os if not os.path.exists(os.path.dirname(file_path)): os.makedirs(os.path.dirname(file_path))
4.3 高级应用:自定义导出内容
通过UFUN API可以实现更精细的控制:
python复制def selective_export(component_names, file_path):
ufs = NXOpen.UF.UFSession.GetUFSession()
tags = []
for name in component_names:
obj, tag = ufs.Assem.AskComponentByName(workPart.Tag, name)
if tag != 0:
tags.append(tag)
if tags:
ufs.Ps.ExportPsText(file_path, tags, len(tags), 330, 0)
5. 工程实践中的扩展应用
5.1 批量转换工具开发
基于上述核心代码,可以构建完整的批量转换工具:
python复制import os
import time
from datetime import datetime
def batch_convert(input_dir, output_dir):
start_time = time.time()
log_file = os.path.join(output_dir, "conversion_log.txt")
with open(log_file, 'w') as f:
f.write(f"转换开始于: {datetime.now()}\n")
for root, _, files in os.walk(input_dir):
for file in files:
if file.lower().endswith(('.prt', '.asm')):
try:
part_path = os.path.join(root, file)
theSession.Parts.Open(part_path, None, None)
output_file = os.path.join(
output_dir,
os.path.splitext(file)[0] + '.x_t'
)
export_to_parasolid(output_file)
f.write(f"成功: {file} -> {output_file}\n")
except Exception as e:
f.write(f"失败: {file} - {str(e)}\n")
elapsed = time.time() - start_time
f.write(f"转换完成于: {datetime.now()}, 耗时: {elapsed:.2f}秒\n")
5.2 与PDM系统集成
在企业环境中,通常需要与Teamcenter等PDM系统集成:
python复制from tc import SoaClient
def get_pdm_items(query):
client = SoaClient('http://pdm-server:8080/tc')
response = client.service.executeQuery(query)
return response.objects
def convert_pdm_items(query, output_dir):
items = get_pdm_items(query)
for item in items:
try:
# 从PDM检出文件
checkout_path = checkout_from_pdm(item.uid)
# 转换
output_file = os.path.join(
output_dir,
f"{item.uid}.x_t"
)
export_to_parasolid(output_file)
# 上传结果
upload_to_pdm(output_file, item.uid)
except Exception as e:
log_error(item.uid, str(e))
5.3 质量检查与报告生成
转换后可添加自动质量检查:
python复制def check_parasolid_file(file_path):
"""检查导出的Parasolid文件质量"""
stats = {
'body_count': 0,
'face_count': 0,
'error_count': 0
}
ufs = NXOpen.UF.UFSession.GetUFSession()
model, stat = ufs.Ps.AskModelFromFile(file_path)
if stat == 0:
stats['body_count'] = ufs.Ps.AskBodyCount(model)
stats['face_count'] = ufs.Ps.AskFaceCount(model)
stats['error_count'] = ufs.Ps.AskErrorCount(model)
ufs.Ps.FreeModel(model)
return stats
def generate_report(stats_dict, output_file):
"""生成HTML格式的质量报告"""
html = """<html><head><title>转换质量报告</title>
<style>table {border-collapse: collapse;}
th, td {border: 1px solid black; padding: 5px;}</style>
</head><body><table>
<tr><th>文件名</th><th>实体数</th><th>面数</th><th>错误数</th></tr>"""
for file, stats in stats_dict.items():
row = f"<tr><td>{file}</td><td>{stats['body_count']}</td>"
row += f"<td>{stats['face_count']}</td><td>{stats['error_count']}</td></tr>"
html += row
html += "</table></body></html>"
with open(output_file, 'w') as f:
f.write(html)
6. 版本兼容性与长期维护
6.1 处理不同NX版本的差异
不同NX版本的API可能有细微变化,建议采用兼容性写法:
python复制def get_export_builder():
try:
# NX 12及以后版本
return theSession.PartExportManager.CreateParasolidExportBuilder()
except AttributeError:
# 旧版本兼容
return theSession.Parts.ExportManager.CreateParasolidExporter()
6.2 脚本的模块化设计
为提高代码可维护性,建议采用模块化结构:
code复制nx_parasolid_export/
├── __init__.py
├── core.py # 核心导出功能
├── utils.py # 辅助函数
├── batch.py # 批量处理
└── tests/ # 单元测试
├── test_core.py
└── test_batch.py
6.3 单元测试示例
使用unittest框架测试核心功能:
python复制import unittest
from unittest.mock import MagicMock
from nx_parasolid_export.core import export_to_parasolid
class TestExport(unittest.TestCase):
def setUp(self):
self.mock_session = MagicMock()
self.mock_part = MagicMock()
def test_successful_export(self):
# 模拟成功的导出场景
builder = MagicMock()
builder.Validate.return_value = MagicMock(Ok=True)
self.mock_session.PartExportManager.CreateParasolidExportBuilder.return_value = builder
result = export_to_parasolid("test.x_t", self.mock_session)
self.assertTrue(result)
def test_failed_validation(self):
# 模拟验证失败场景
builder = MagicMock()
builder.Validate.return_value = MagicMock(Ok=False)
self.mock_session.PartExportManager.CreateParasolidExportBuilder.return_value = builder
with self.assertRaises(RuntimeError):
export_to_parasolid("test.x_t", self.mock_session)
if __name__ == '__main__':
unittest.main()
在实际项目中,我发现将导出逻辑封装成独立类能更好地管理状态和配置。以下是一个改进版的面向对象实现:
python复制class ParasolidExporter:
def __init__(self, session=None, version=330, tolerance=0.001):
self.session = session or NXOpen.Session.GetSession()
self.version = version
self.tolerance = tolerance
self._setup_ufun()
def _setup_ufun(self):
try:
self.uf_session = NXOpen.UF.UFSession.GetUFSession()
except:
self.uf_session = None
def export(self, file_path, objects=None):
"""主导出方法"""
if objects is None:
return self._export_whole_part(file_path)
else:
return self._export_selected(file_path, objects)
def _export_whole_part(self, file_path):
builder = self._create_builder()
builder.FileName = file_path
try:
if builder.Validate() == NXOpen.ParasolidExportBuilder.ValidateResult.Ok:
builder.Commit()
return True
return False
finally:
builder.Destroy()
def _export_selected(self, file_path, objects):
if not self.uf_session:
raise RuntimeError("UFUN API不可用")
tags = [obj.Tag for obj in objects]
return self.uf_session.Ps.ExportPsText(
file_path, tags, len(tags),
self.version, 0
) == 0
def _create_builder(self):
builder = self.session.PartExportManager.CreateParasolidExportBuilder()
builder.FileVersion = self._get_version_enum()
builder.ExportFrom = NXOpen.ParasolidExportBuilder.ExportFromType.DisplayPart
builder.ObjectTypes = NXOpen.ParasolidExportBuilder.ObjectTypesType.Solids
builder.OutputFileType = NXOpen.ParasolidExportBuilder.OutputFileType.Text
builder.AdvancedOptions.Tolerance = self.tolerance
return builder
def _get_version_enum(self):
versions = {
300: NXOpen.ParasolidExportBuilder.FileVersionType.Version300,
330: NXOpen.ParasolidExportBuilder.FileVersionType.Version330
}
return versions.get(self.version, NXOpen.ParasolidExportBuilder.FileVersionType.Version330)
这种封装方式使得导出器可以保持配置状态,支持多种导出方式,并且更容易扩展新功能。在实际项目中,我还添加了日志记录、性能监控等额外功能,这对长期维护特别重要。
