简介:这份资源面向化工、石油、制药等行业的三维工厂设计从业者,以及希望基于PDMS进行功能扩展的工程师与开发者,重点讲解如何利用内置脚本语言PML实现二次开发。内容涵盖PML语法基础,包括变量与数据类型、条件与循环控制结构、函数与子程序、对象属性访问及事件驱动机制,并结合自动化设计、定制化报告、用户界面扩展、数据验证与工作流自动化等实例,帮助读者掌握PDMS对象模型与内部工作原理。资源包共59个文件,约237KB,以h头文件、cs与cpp源码、doc与ppt文档、pmlfrm与pmlobj脚本文件为主,另含sln与vcproj工程文件、exe可执行程序及uew语法高亮配置,覆盖从语法讲解到接口开发的完整脉络。目前已有2755人学习下载,适合需要系统入门PML并动手实践PDMS二次开发的读者参考。
1. PDMS二次开发教程:从PML脚本到批量建模的落地路径
如果你正在做海洋工程、石化或电力项目的三维设计,大概率绕不开PDMS这套工厂设计系统。它自带的功能已经覆盖了管道、结构、设备布置,但真正到了项目后期,你会发现大量重复操作根本没法靠鼠标点完——比如批量修改管径、按规则生成支吊架编号、把几百根管道的属性导出成表格。这时候就得靠二次开发。这份教程的核心就是PML(Programmable Macro Language),PDMS内置的脚本语言,不需要额外装编译器,直接在命令行里就能跑。它适合两类人:一是被重复劳动逼疯的配管工程师,二是想用脚本把设计规则固化下来的项目管理员。学完你能做到的事很具体:写一个脚本,十分钟干完以前两天的活。
2. PML基础:变量、循环与对象访问
2.1 为什么选PML而不是外部接口
PDMS对外提供了多种二次开发方式,常见的有PML、.NET API和DABACON。.NET API功能强,但需要Visual Studio环境,部署时要考虑版本兼容和许可证问题,一个项目组里能维护的人不多。PML的优势在于零依赖——它就在PDMS里面,打开命令行就能写,写完存成.mac文件,别人拿到就能用。代价是PML的语法比较老派,没有现代语言的语法糖,调试基本靠$P打印变量。但对于大多数批量操作场景,PML的投入产出比最高。我一般建议新手先从PML入手,把业务逻辑跑通,确实遇到性能瓶颈再考虑外部接口。
2.2 变量与数据类型
PML的变量用!声明,比如!pipeName = 'PIPE-001'。它不区分字符串和数字的声明方式,但运算时会自动判断。数组用!arr[1]这种形式,注意PML数组默认从1开始,不是0。这点跟大多数编程语言不一样,写循环的时候容易翻车。
-- 定义变量并输出 !name = 'PIPE-001' !diameter = 150 $P Name: $!name, Diameter: $!diameter -- 数组示例,注意下标从1开始 !arr[1] = 'A' !arr[2] = 'B' !arr[3] = 'C' DO !i FROM 1 TO 3 $P Element $!i: $!arr[$!i] ENDDO$P是PML的打印命令,相当于其他语言的print。DO...ENDDO是循环结构,!i是循环变量。这段代码会依次输出数组的三个元素。注意$!arr[$!i]的写法,取数组元素时变量名前面要加$!,下标变量也要加!。
2.3 访问PDMS数据库对象
PML最核心的能力是访问PDMS的数据库对象。每个对象都有类型和属性,比如一个管道对象类型是PIPE,它有BORE(管径)、SPEC(等级)等属性。访问方式是通过!!CE(Current Element)或者用导航命令定位。
-- 获取当前选中的元素 !ce = !!CE $P Current element: $!ce -- 假设当前是一个PIPE对象,读取它的管径 !bore = !ce.BORE $P Bore: $!bore -- 遍历当前SITE下的所有PIPE DO !pipe IN SITE $P Pipe: $!pipe.Name, Bore: $!pipe.BORE ENDDO!!CE是PML里最常用的全局变量,代表当前光标选中的对象。!ce.BORE这种点号访问属性的写法很直观。DO...IN是遍历集合的语法,SITE是对象类型名。实际项目中,你通常需要先导航到某个层级,比如$!ce = CE或者用NAVIGATE命令。
2.4 条件判断与错误处理
PML的IF语句跟其他语言类似,但要注意它没有elif,只能用嵌套或者多个IF。错误处理用$!ERROR或者HANDLE块。
-- 判断管径并分类 IF (!bore GT 200) THEN $P Large bore pipe ELSE IF (!bore GT 80) THEN $P Medium bore pipe ELSE $P Small bore pipe ENDIF ENDIF -- 错误处理示例 HANDLE !result = !ce.BORE $P Bore value: $!result ELSE $P Error: cannot read BORE attribute ENDHANDLEGT是大于,LT是小于,EQ是等于。PML不支持>这种符号,必须用字母缩写。HANDLE...ELSE...ENDHANDLE是错误捕获结构,当!ce.BORE读取失败时(比如当前对象不是管道),会跳到ELSE分支。这个结构在批量处理时特别有用,避免一个对象报错导致整个脚本中断。
3. 实战:用PML批量处理管道属性
3.1 批量修改管径的完整脚本
假设你接到一个任务:把某个SITE下所有BORE小于50的管道统一改成50。手动改的话,几百根管道要点到手酸。用PML脚本,几秒钟搞定。
-- 批量修改管径脚本 -- 功能:遍历当前SITE下所有PIPE,将BORE小于50的改为50 !count = 0 DO !pipe IN SITE HANDLE !currentBore = !pipe.BORE IF (!currentBore LT 50) THEN !pipe.BORE = 50 !count = !count + 1 $P Modified: $!pipe.Name, old bore: $!currentBore ENDIF ELSE $P Skip: $!pipe.Name, cannot read BORE ENDHANDLE ENDDO $P Total modified: $!count这段脚本的逻辑很直接:遍历SITE下所有PIPE对象,读取BORE属性,如果小于50就改成50,同时计数。!pipe.BORE = 50是写属性操作,PML允许直接赋值。HANDLE块确保即使某个对象读取失败也不会中断整个循环。最后输出修改总数,方便核对。
参数说明:SITE是遍历范围,你可以改成ZONE或者更具体的层级。LT 50是判断条件,50是目标管径值。实际使用时,建议先在小范围测试,确认无误再全量跑。
3.2 导出管道属性到CSV
项目交付时经常需要把管道属性导出成表格,给采购或者施工方用。PML可以写文件,格式自己定。
-- 导出管道属性到CSV文件 -- 输出:名称,管径,等级,起点坐标X,起点坐标Y,起点坐标Z !file = 'D:/export/pipes.csv' !fp = !file.OPEN('WRITE') !fp.WRITELN('Name,Bore,Spec,X,Y,Z') DO !pipe IN SITE HANDLE !name = !pipe.Name !bore = !pipe.BORE !spec = !pipe.SPEC.Name !pos = !pipe.Position !x = !pos.East !y = !pos.North !z = !pos.Up !line = '$!name,$!bore,$!spec,$!x,$!y,$!z' !fp.WRITELN(!line) ELSE $P Skip: cannot export $!pipe.Name ENDHANDLE ENDDO !fp.CLOSE() $P Export completed: $!file!file.OPEN('WRITE')打开文件用于写入,如果文件不存在会自动创建。WRITELN写一行并换行。!pipe.Position返回一个位置对象,.East、.North、.Up分别对应X、Y、Z坐标。注意路径用正斜杠/,PML在Windows下也认这个写法。导出的CSV可以直接用Excel打开,方便后续处理。
3.3 按规则生成支吊架编号
支吊架编号通常有固定规则,比如“项目代码-区域-序号”。用PML可以自动生成,避免人工编号冲突。
-- 生成支吊架编号 -- 规则:SUP-区域代码-四位序号 !areaCode = 'A01' !seq = 1 DO !support IN SITE IF (!support.Type EQ 'SUPPORT') THEN !seqStr = !seq.PAD(4, '0') !newName = 'SUP-$!areaCode-$!seqStr' !support.Name = !newName $P Renamed: $!newName !seq = !seq + 1 ENDIF ENDDO $P Total supports renamed: $!seq - 1PAD(4, '0')是补零操作,把数字1变成字符串'0001'。!support.Type EQ 'SUPPORT'判断对象类型。这个脚本会遍历SITE下所有对象,只处理类型为SUPPORT的。编号从SUP-A01-0001开始递增。实际项目中,区域代码可以从当前ZONE名称提取,序号也可以从已有编号最大值继续,避免重复。
4. 避坑与常见问题排查
4.1 数组下标从1开始导致越界
现象:循环遍历数组时,最后一个元素总是取不到,或者报错“index out of range”。原因:PML数组默认下标从1开始,但很多人习惯性写DO !i FROM 0 TO !arr.SIZE - 1。解决:要么改成FROM 1 TO !arr.SIZE,要么在声明时用!arr[0]占位。我一般直接在循环里用FROM 1,省得记混。
4.2 属性读取失败导致脚本中断
现象:脚本跑到某个对象时突然停止,命令行报错。原因:不是所有对象都有你访问的属性,比如一个SITE对象没有BORE属性。解决:用HANDLE...ELSE...ENDHANDLE包住属性访问,或者先用IF (!obj.Type EQ 'PIPE')判断类型。批量处理时,永远不要假设所有对象类型一致。
4.3 文件路径斜杠方向错误
现象:OPEN('WRITE')返回失败,文件没生成。原因:Windows路径用了反斜杠\,PML把它当转义字符。解决:统一用正斜杠/,比如'D:/export/pipes.csv'。如果非要用反斜杠,得写双反斜杠\\。这个坑我踩过不止一次,现在写路径一律用/。
4.4 循环内修改集合导致遍历异常
现象:在DO...IN循环里删除或新建对象,结果遍历跳过某些元素或者死循环。原因:PML的集合遍历是动态的,修改集合会影响迭代器。解决:先把要处理的对象存到数组里,遍历数组而不是直接遍历集合。或者用COLLECTION命令先固化集合再操作。
4.5 变量作用域混淆
现象:在DO循环里定义的变量,循环外访问不到,或者值不对。原因:PML的变量作用域是全局的,但循环变量!i在循环结束后会保留最后的值。解决:不要在循环外依赖循环变量的值,需要的话在循环内用另一个变量保存。另外,!开头的变量都是全局的,命名时注意别冲突。
5. 进阶:用PML调用外部程序与批量出图
5.1 调用外部命令
PML可以通过SYSTEM命令调用外部程序,比如用Python处理数据或者用批处理脚本做文件操作。
-- 调用外部Python脚本处理导出的CSV !csvFile = 'D:/export/pipes.csv' !pyScript = 'D:/scripts/process.py' !cmd = 'python $!pyScript $!csvFile' SYSTEM $!cmd $P External script executedSYSTEM命令会同步执行外部程序,等待它返回。注意路径里如果有空格,要用引号包起来。这个用法适合把PML的数据导出能力和Python的数据处理能力结合起来,比如用pandas做统计分析,再导回PDMS。
5.2 批量生成图纸
PDMS的出图功能也可以通过PML控制。下面是一个批量生成管道轴测图的示例框架。
-- 批量生成轴测图 !drawingDir = 'D:/drawings/' DO !pipe IN SITE IF (!pipe.Type EQ 'PIPE') THEN !dwgName = !pipe.Name !dwgFile = '$!drawingDir$!dwgName.dwg' -- 设置出图参数 !pipe.DRAWING.NAME = !dwgName !pipe.DRAWING.FILE = !dwgFile -- 触发出图命令 $P Generating drawing for $!pipe.Name -- 实际出图命令根据PDMS版本不同,常见的是DRAFT或ISO ENDIF ENDDO不同PDMS版本的出图命令有差异,常见的是DRAFT模块下的ISO命令。实际使用时需要查对应版本的文档。这个框架的价值在于批量逻辑——把出图参数设置和触发命令放在循环里,一次性处理所有管道。
5.3 验证脚本是否生效
写完脚本别急着全量跑。我一般分三步验证:第一步,在测试项目里跑,用$P输出关键变量,确认逻辑正确;第二步,小范围跑,比如只处理一个ZONE,检查结果是否符合预期;第三步,全量跑之前先备份数据库,PDMS的备份命令是BACKUP。跑完之后用$P输出统计信息,比如修改了多少个对象,导出了多少行数据,跟预期对一下。
从那以后我每次写PML脚本,都强制走一遍“测试项目→小范围→全量”的流程,再也没出现过跑完发现改错数据的情况。希望帮到你。
本文还有配套的精品资源,点击获取