简介:面向PDMS二次开发人员的系统教程,聚焦PML脚本语言及DARS、.NET等外部接口开发,适用于化工、石油、制药等行业中需要扩展PDMS能力的工程设计人员。包内共59个文件、约237KB,以PML基础语法、Dars接口开发、PDMS12 dotNet接口开发等文档为主,含C++/C#工程源码(MFC窗口模式与控制台示例)、PML窗体与函数定义、可执行程序及UE语法高亮配置,便于对照实践。已有2743人学习下载。内容从PML变量、控制结构、函数与对象属性访问讲起,延伸到自动化布置、定制报告、界面扩展、数据校验与工作流自动化,并给出DARS调用和.NET集成的具体实现路径,能帮助读者从示例入手掌握编写宏、构建外部程序的关键方法,适合想系统学习并落地项目的初中级PDMS二次开发人员。
1. 做pdms二次开发,先别一头扎进C++和.NET:PML才是多数项目里最省力的杠杆
做pdms二次开发,很多人的第一反应是去翻C++或者.NET的接口文档,结果环境配置就劝退了一半。实际上,PDMS内置的PML语言才是多数设计场景下最值得先投入的方向:它在PDMS进程内直接运行,不用单独装SDK,也没有编译打包那一整套工程。我拆过一套以PML为主线的PDMS二次开发教程资源,学习路径基本是四段:PML语法基础、模型对象导航、Form表单交互、调试与交付。这套东西解决的问题很具体——设计模型里几百根管线的名称、描述、长度需要批量调整,手工改一天,脚本几分钟跑完。适合两类人:一类是被重复操作耗到想自动化的设计员,另一类是学过一点PML但始终写不出完整工具的入门工程师。这篇实操版复盘,把教程里最容易卡住的参数和坑都标了出来,所有脚本可以直接改后套用。
2. 先把PML的脾气摸清楚:解释型执行、加载方式与第一个可用脚本
2.1 为什么“解释型”这个属性决定了你的调试方式
PML全称Programmable Macro Language,是PDMS内置的宏语言,解释执行,不经过编译。这意味着两件事:第一,你可以在PDMS命令窗口里逐行输入命令,立刻看到返回结果;第二,错误通常要到运行到那一行才会暴露,不会像编译型语言那样在构建阶段给你提示。所以调试PML的常态是“脚本自己跑一段,然后停在某个属性上返回了空值”,看起来像数据不对,实际是前面某一行写错了对象路径。
PDMS里加载PML的方式主要有三种:直接在命令输入框敲入单行命令;通过“自定义”菜单加载宏文件;写在启动文件里让PDMS启动时自动执行。新手最容易忽略的是文件后缀名:函数模块保存为.pmlfnc,宏文件保存为.pmlmac,两者加载机制不同。函数文件一般要先加载才能调用,宏文件则可以直接执行。如果你把函数写进了.pmlmac,再按函数的调用方式去命令窗口敲,大概率会得到“未定义”的提示,这一点在学习教程时值得注意。
先看一段最基础的命令框验证代码:
!name = 'DEMO-PIPE' !msg = '当前宏演示对象: ' + !name !!msg变量名以半角感叹号开头,字符串必须用单引号,加号做拼接,!!的作用是把结果输出到命令回显区。这个回显是PML学习阶段的“眼睛”,也是之后所有调试的基础。你可以在PDMS任意一个命令输入框里逐行执行,或者保存为.pmlmac文件后用命令调用。把这三行执行通,说明你的PML运行环境基本可用。
2.2 object()是进入模型世界的唯一入口,但空对象是头号陷阱
PML里几乎所有针对模型数据的操作,都要从一个对象引用开始。最常用的写法是object '',意思是“把当前设计环境中选中的对象作为操作对象”。这个设计很朴素,却让很多初学脚本的人栽过跟头:界面上一旦没有选中任何东西,object ''返回的是一个空对象引用,而不是报错。你后续访问它的任何属性,都会返回空值或空串,脚本既不报错也不中断,看起来像“黑匣子”。
所以写任何PML脚本的第一步,不应该是取属性,而应该是判空:
!ce = object '' IF !ce.IsMember THEN !!ce.Name ELSE !!warn('没有选中任何模型元件,请先拾取一个管件或分支') ENDIFIsMember是PML中判断对象是否有效且属于模型成员的方法。如果整个库都比较庞大,取出的是一个Zone或Site,IsMember也会返回相应结果,但返回逻辑和具体元件不同。这里提醒两点:一是不要指望object ''在无选中时会抛异常,它只会安静地返回空;二是如果你的脚本是给设计员用的,务必在入口处加上类似上面的判空,否则后面所有批量逻辑都可能在空对象上跑完,界面却毫无动静,用户会以为脚本坏了。
2.3 循环与数组:一切批量操作的基本骨架
PML里的循环结构和常见语言类似,但要记住它的大写关键字风格。FOR ... ENDFOR、IF ... ENDIF,这些闭合标签是必须写全的。数组用ARRAY()创建,长度可以在运行时自动增长,元素通过append方法追加。
一个非常典型的入门循环,是统计当前对象下所有成员的数量:
!all = ARRAY() FOR !m IN object('').Members !all.append(!m.Name) ENDFOR !!all.length参数说明:Members属性返回当前对象的全部直接子节点,!all.length返回数组长度。这里有一个大多数教程不会特意提醒的点:PDMS数据库内部存储的成员顺序,和设计树界面显示的顺序并不一定一致。如果你的脚本逻辑依赖“处理顺序和界面看起来一样”,不要把希望寄托在默认顺序上,后面第5.2节有个更具体的坑,这里先建立一个意识:批量脚本必须先明确排序规则,或者对顺序不敏感。
2.4 用函数封装逻辑,避免全局名称污染
写了几段脚本之后,你会发现所有代码都推在宏文件里,可读性会变得很差,而且不同脚本之间变量作用域经常互相干扰。PML提供了函数文件机制,扩展名.pmlfnc,每个函数用Function开头,EndFunction结束。函数可以通过参数传入对象,也可以通过->后面的参数把结果带出来。
这是一个可运行的函数骨架:
Function CountPipeBranches(!zone -> !count) !count = 0 IF !zone.IsMember THEN FOR !m IN !zone.Members IF !m.SPEC = 'PIPE' THEN !count += 1 ENDIF ENDFOR ENDIF EndFunction调用方式是在命令框或宏里写!!CountPipeBranches(object '')。这里!zone是传入的对象参数,!count是返回参数,如果传入的不是有效成员,函数内部判空后返回0。需要特别说明的是!m.SPEC = 'PIPE'这一行:不同PDMS版本的属性字典和类型名称有差异,有些环境里管道元素类型对应的不是PIPE而是本地化描述,这是一处典型版本陷阱。我第一次按资深同事的脚本抄SPEC写法时,在自己的环境里跑出来统计值全是0,之后才认识到要先在目标环境里实际打印一次类型值。经验是:凡涉及类型关键字、属性关键字,先写一个十来行的属性转储脚本跑一遍目标模型,看看真实返回值,再进入正式逻辑。
函数文件多了之后,命名冲突会很常见。PML在加载多个函数文件时,如果存在同名函数,后加载的会把先加载的覆盖掉。为了避免这种问题,我给每个项目都加固定的函数前缀,比如某个项目的工具类函数一律写成proj_开头。这个习惯在团队开发中非常值得坚持。
3. 模型对象导航:成员、属性与批量更新,三类高频任务都在这章
3.1 先把PDMS的对象树在脑海里画出来
PML的对象导航,本质上就是在PDMS的模型树里移动和筛选。PDMS模型顶层的结构大致是:Database(数据库)下挂Site、Site下挂Zone,Zone里再放管线、设备、结构等具体对象。这些具体对象又包含成员,比如一条管线下面可以挂分支、管嘴、支撑,分支下面再挂管件和元件。
为什么要先理解这个层级?因为几乎所有批量脚本要做的第一步都是确定“我在哪个层级取数”。比如你取Zone.Members,拿到的不一定是管线,可能是设备也掺杂其中;而你取Pipe.Members,拿到的才更可能是分支。层级错了,后面所有循环条件都要跟着白跑。这个道理看起来简单,但在真实项目里,对象层级往往因为多人协同建模而不那么规整。有一次我在某项目的联合装置区域统计管道支吊架数量,直接遍历了Zone下的所有成员,结果把设备上的平台支腿也算成了支吊架,整个统计表后续返工,这就是对象层级没做条件过滤的血泪教训。
3.2 从当前选中对象向上回溯Zone,做一次分区统计
日常操作中最常见的导航逻辑之一,是从当前选中的元件回溯到所属Zone,然后对整个Zone做统计。这样设计员在画布里随手选一个分支,脚本就知道该处理哪个区域。
代码可以这样写:
!ce = object '' !zone = !ce.Zone !pipeCount = 0 FOR !item IN !zone.Members IF !item.TYPE = 'PIPE' THEN !pipeCount += 1 ENDIF ENDFOR !!pipeCount参数说明:!ce.Zone是PML提供的属性回调,可以从任意对象向上找到它所属的Zone;TYPE是对象内置类型关键字,PIPE表示管线对象。这里同样存在版本差异问题,建议你先做一轮打印验证。如果你的模型里管道类型存成了别的名称,把'PIPE'换成你那边打印出的实际类型关键字即可。
这个脚本还有一个变体,可以在真实项目里帮你快速看清模型组成结构。打印出某个Zone下所有成员的TYPE和Name,形成一份临时目录,方便你确认筛选条件写的是否准确。
3.3 批量更新分支长度和描述:把手工逐条操作改成循环
第2章打完基础,这一章开始就上更典型的实际操作。设计项目中经常发生管廊调整,一个区段几十条分支的长度需要统一改到某个安全值。如果人工作业,选中一个分支、改一个属性、再选中下一个,五十个对象要十几分钟。用PML写一个循环,时间开销是瞬时完成的。
!changed = 0 FOR !bm IN object('').Members IF !bm.IsMember AND !bm.HasAttribute('LENGTH') THEN IF !bm.LENGTH > 4500 THEN !bm.DESCRIPTION = 'LONG-自动标记' !bm.LENGTH = 4500 !changed += 1 ENDIF ENDIF ENDFOR !!changed逻辑说明:外层循环遍历当前对象的所有成员;HasAttribute('LENGTH')先判断该对象是否有长度属性,避免在管嘴、支撑等没有长度属性的成员上报错;然后判断如果长度大于4500毫米,就批量修改描述文字并截断长度。参数说明:4500是阈值,你可以按项目标准改成任意数值;DESCRIPTION是描述字段,在部分PDMS版本里也可能是其他关键字,脚本中要留意当前环境的属性字典。
这个脚本的隐患在于,再次运行时会重复修改已经改过的对象。如果同一个脚本要被执行两次,建议判断条件里加上“描述已经包含某个标记则跳过”:
IF LEFT(!bm.DESCRIPTION, 4) <> 'LONG' THEN !bm.DESCRIPTION = 'LONG-自动标记' !bm.LENGTH = 4500 !changed += 1 ENDIF这段修改体现了PDMS批量脚本的关键思路:幂等性。批量更新脚本最好能接受重复运行,不会把设计员已经调整过二次的内容覆盖掉,这一点是教程资源里很容易被忽略的价值点。
3.4 从CSV文件驱动的批量改名脚本
批量操作还会经常面对外部数据源,最常见的方式是CSV文件。PML本身提供了文件读取相关函数,常见做法是先用FOPEN打开文件句柄,再用循环逐行读取并切割字段,最后把新名称写回对象。这里给一个可以套用的骨架:
!fp = FOPEN('rename_list.csv', 'R') IF !fp <> 0 THEN FGETS !fp !line WHILE NOT FEOF(!fp) !parts = SPLIT(!line, ',') !oldName = !parts[1] !newName = !parts[2] !obj = object !oldName IF !obj.IsMember THEN !obj.Name = !newName ENDIF FGETS !fp !line ENDWHILE FCLOSE !fp ENDIF参数说明:FOPEN的第二个参数R表示只读;FGETS把一行内容读入变量;SPLIT按逗号切割,得到原对象名和新对象名;object !oldName用路径或名称定位对象。注意这里不同版本的文件函数可能名不同,有的版本用FREADLINE代替FGETS。建议在拿到教程资源后先跑一个最小读取样例,确认当前环境的函数名称。
这个脚本有一个必须注意的边界条件:CSV文件的编码。PDMS在不同区域版本里对中文编码支持不一样,曾经有某公司同事导出的CSV里包含中文字符,读进PML后全部乱码,也没报错,结果批量改名产生了大量含乱码的对象名,最后只能靠备份恢复。遇到中文CSV,建议先做编码转换,或者改用UTF-8格式配合当前版本对应的读取方式。
4. 表单(FORM)进阶:把脚本封装成设计员愿意天天用的面板工具
4.1 为什么要用Form,以及PML表单的基本结构
命令框里的脚本虽然强大,但让设计员每天打开命令框敲函数并不太现实。PML提供了表单(FORM)机制,可以在PDMS里绘制出标准的窗口界面,包含输入框、按钮、下拉列表,最终把之前写好的批量逻辑封装在一个可视化的面板里。
一个典型的表单文件,从结构上分为定义区和动作区。定义区写窗口布局,动作区绑定按钮事件或初始化事件。下面是一个用于批量更新分支数据的表单定义骨架:
FORM fmBatchUpdater TITLE "批量更新分支数据" ALT 'FMBATCHUPDATER' WIDTH 460 HEIGHT 320 BEGIN_FORM GROUP "目标范围" FIELD fldZone "区域路径" TYPE TEXTLONG FIELD fldBranchNo "分支号过滤" TYPE TEXTLONG GROUP "更新内容" FIELD fldLength "长度" TYPE NUMBER FIELD fldDesc "描述" TYPE TEXTLONG BUTTON btnRun "运行脚本" COMMAND "!!run_batch_update()" BUTTON btnClose "关闭窗口" COMMAND "!!close_form()" END_FORM逻辑说明:FORM声明表单名,ALT指定内部别名,BEGIN_FORM到END_FORM之间是控件定义区。FIELD控件用于输入,BUTTON控件绑定命令字符串,当按钮被点击时,PML会执行命令字符串里写的函数。参数说明:TEXTLONG表示长文本输入框,NUMBER表示数字输入框,WIDTH和HEIGHT控制窗口尺寸。
4.2 初始化回调:打开表单时自动带入当前选中对象
一个好的表单工具,打开时应该自动把当前画布中的选中信息带进来,减少设计员手动输入。这就需要在表单加载完成后执行一个初始化函数。常见做法是在表单定义里增加一个bev或form load相关的事件声明,然后在函数文件中写出对应的初始化逻辑。
Function fmBatchUpdater_init() !ce = object '' IF !ce.IsMember THEN f.fldZone.Value = !ce.Zone.Name ELSE f.fldZone.Value = '' ENDIF EndFunction逻辑说明:f前缀是PML表单字段的标准访问方式,f.fldZone.Value表示给表单里的区域路径输入框赋值。参数说明:如果当前没有选中对象,则把输入框置空,等待设计员手动填写。这里有一个值得注意的地方:不同PDMS版本触发初始化的动作名称略有不同,有的版本需要在FORM定义中显式声明动作绑定,有些则默认执行同名初始化函数。教程资源一般会给出对应版本的声明方式,如果发现窗口打开后没有预填数据,优先检查这一处绑定设置。
4.3 在按钮事件里调用批处理函数并回显结果
表单最大的价值,是把之前裸奔的脚本变成一个可以反复使用的工具。下面是按钮绑定的批处理函数,对应4.1节里COMMAND "!!run_batch_update()"中的调用:
Function run_batch_update() !branchFilter = f.fldBranchNo.Value !newLength = f.fldLength.Value !count = 0 FOR !bm IN object('').Members IF !bm.IsMember THEN IF !branchFilter <> '' THEN IF !bm.Name <> !branchFilter THEN CONTINUE ENDIF ENDIF !bm.LENGTH = !newLength !count += 1 ENDIF ENDFOR !!warn('更新完成,共处理 ' + !!count + ' 个对象') EndFunction逻辑说明:先从表单两个输入框里取过滤条件和目标长度,然后遍历当前对象的所有成员,如果分支号过滤项非空,则只处理名称匹配的分支,否则全部处理。最后用!!warn弹出一个结果窗口,显示一共处理了多少个对象。参数说明:CONTINUE用于跳过本次循环中不满足条件的对象,继续下一个。
结果回显是做表单工具时不应该省掉的环节。如果脚本是静默执行的,设计员不知道它是否把不相关的对象也改了。回显只是一个雏形,更进一步可以把处理前后的对象清单写入日志文件,这部分在第6章展开。
4.4 数值校验:防止设计员把明显不合法的参数提交给数据库
表单打开了输入框,就必然要面对人的误操作。如果设计员把长度填成-500或者99999999,脚本会非常老实地把所有分支都改成那个数值,这种错误尤其让人头大。所以按钮执行函数里要先做一轮数值合理性校验:
IF !newLength < 0 OR !newLength > 20000 THEN !!warn('长度超出合理范围,请检查输入') RETURN ENDIF逻辑说明:RETURN在PML函数里表示直接结束当前函数,后面的代码不会执行。参数说明:上限20000要根据项目实际情况调整,如果项目里有超过20米的分支,就把阈值相应放大。这一段直接放在run_batch_update函数体最前面即可。
4.5 把面板做成菜单项:设计员不用碰命令行
表单封装好之后,下一步是把它挂到PDMS的菜单栏上,让设计员通过点击菜单打开工具,而不是在命令框敲加载语句。常见做法是通过PDMS的自定义菜单系统完成,不同版本有不同的菜单配置入口,基本原理都是写一行类似!!form_load fmBatchUpdater的命令,把这个动作作为菜单按钮的点击事件。
团队里统一部署时,我通常会把这行加载语句写进项目启动宏文件里,这样PDMS启动即获得菜单入口。如果你只是自用,在命令框敲!!form_load fmBatchUpdater也可以完成加载。可这样一来,你觉得时间久了就会把Form加载方式淡忘,建议还是花十分钟把菜单入口做好。这方面学到的是:一个工具,只有当设计员不用记任何命令就能用起来时,它才真正有交付价值。
5. 常见问题排查:PML脚本开发中的四个经典疑难与处理记录
5.1 函数文件加载后提示“未定义”或调用无响应
现象:把函数文件放进PDMS加载路径后,在命令窗口执行!!myfunc(),系统提示找不到该函数,或者毫无反应。
原因:最常见的是文件扩展名不对。函数应保存为.pmlfnc,但很多人按习惯保存成了.pmlmac。PDMS对不同扩展名的处理方式不同,.pmlmac会被当作宏直接执行,里面如果只有函数定义而没有调用动作,执行结果就是“什么也没发生”,你再去调函数,自然无响应。另一个常见原因是函数文件加载顺序不对:你在宏文件里已经写了!!myfunc(),但包含myfunc定义的.pmlfnc文件还没有被加载。
解决:先确认文件扩展名,再手动加载函数文件,最后再执行宏。命令行里加载函数文件的常见写法类似!!'load_function_names',具体以当前版本的加载命令为准。建议团队内部统一一个加载文件,把项目所有.pmlfnc放在同一个加载清单里,按依赖顺序排列,避免加载顺序导致的不稳定。还有一个额外建议:函数名尽量带项目前缀,例如proj_batch_update,避免和PDMS自带的函数重名。
5.2 遍历Members返回的成员顺序和设计树界面对不上
现象:用FOR !m IN object('').Members遍历当前对象,脚本输出的顺序和PDMS设计树上看到的看起来不是同一个顺序,甚至统计出来的数量也和界面上数出来的不一致。
原因:PDMS数据库中Members返回的顺序是内部存储顺序,它不等同于界面导航树所展示的层级顺序。在多人协同设计环境下,不同设计员的视图过滤条件不同,也会让界面显示结果和脚本统计结果产生差异。
解决:如果脚本逻辑不依赖顺序,过滤条件里明确做好类型判断即可。如果必须按某种顺序处理,一般会先把成员名称放进数组,再用SORTUP排序,最后按排序后的数组取对象:
!list = ARRAY() FOR !m IN object('').Members !list.append(!m.Name) ENDFOR !sorted = SORTUP(!list) FOR !name IN !sorted !!object(!name).Name ENDFOR这一段操作目的很明确:先收集全部符合范围的对象名,排序后再遍历。排序关键字除了名称,也可以是长度、位号等数值字段,具体取决于你的业务目标。如果发现排序结果里的数字按字典序而非数值序排列,例如10排在9前面,就先对字段做一次数值格式化再排序。
5.3 表单加载正常,但字段数据为空,按钮怎么点都没反应
现象:表单能弹出来,文本框里却是空的,按钮点击以后界面没有变化,也没有任何报错。
原因:这一类问题多数出在字段名称不一致。例如表单定义里的字段名是fldBranchNo,但函数里访问时写成了fldbranchno,PML对部分环境的大小写并不宽容。还有一种是按钮事件里的命令字符串没有正确触发,比如命令前后缺少一对双引号,或者命令调用的函数名与实际函数名不一致。
解决:把字段名统一为fld前缀,所有访问保持一致。按钮事件排查时,第一步先在按钮命令里临时改成弹出一个提示框:
BUTTON btnRun "运行脚本" COMMAND "!!warn('button clicked')"如果点击按钮能弹出这个提示,说明事件绑定没有问题,问题出在后续函数内部。如果连这个提示都不弹,说明表单事件解析或函数加载方面有故障,回头检查加载顺序。
5.4 批处理脚本处理大量对象时越跑越慢,最后像“卡死”一样
现象:脚本在几十个对象的小范围上运行很流畅,一旦放到整个装置区段跑,前面处理很快,后面越来越慢,界面像卡死了一样。
原因:PML在逐条修改模型属性时,PDMS会同步触发模型数据库更新和界面刷新。对象数量大了以后,界面刷新消耗的时间远远超过脚本逻辑本身。如果脚本里还嵌套了多层循环,每次循环都对全库做一次查询,数据量会呈几何级增长。
解决:第一,调整批处理范围,尽量把对象集缩小到当前要修改的分支或某个特定类型;第二,在循环外部临时关闭无关的显示刷新机制,处理完成后再恢复。不少老工程师会用SET Q这一类命令来关闭数据库查询和界面提示,不同版本命令有所不同。具体语法需要以你环境的命令速查表为准,但思路是一致的:大规模批处理时要降低PDMS的交互刷新频率。第三,如果数据量真的很大,可以把脚本拆成分段批处理,每处理200个对象暂停一次并输出进度,避免一次长时间运行。
5.5 模型数据改错了想撤销,发现PDMS的撤销机制帮不上忙
现象:脚本跑完,设计员发现批量改值有误,按撤销快捷键发现只能退回很小一步,大量修改已经写入数据库。
原因:PDMS内置的撤销机制对脚本批量修改的支持有限,尤其当脚本通过成员遍历逐条写入属性时,撤销操作可能只记录最后一次修改。
解决:这是所有PML批量工具在交付前最值得认真对待的问题。通常的做法是,在运行任何写操作脚本前,先对当前模型作一次完整备份,或者用PDMS自带的备份能力把相关区域数据导出为一个中间文件,这样出错后有后悔药可以回滚。这个习惯我已经坚持了很久。现在每次给设计员交付批量工具,我都会在运行按钮里先执行一次安全备份,再进入正式逻辑。
6. 把脚本做成可交付的工具:样本验证、日志追踪与部署习惯
工具函数写完之后,距离真正交给设计员使用还有一步:验证。这一步在某些教程里常被压缩成一句“测试一下”,但实际工程里,没做过验证的工具上线后,出错的代价远比多花半小时测试要高。
通常我会准备一个固定的试算样本。这个样本里包含:几十根分支、不同类型的管嘴、一两个设备,以及少量边界数据。样本固定有两个好处:一是每次修改脚本后,跑一遍样本的输出结果可以和上次对比;二是当模型数据出问题时,可以快速确认脚本在新的数据集上是否符合预期。固定样本对同一个脚本多跑几次,如果结果一致,再拿真实项目里的一个小区段跑一遍,对比设计员手工统计的结果,两次对得上,脚本才具备交付资格。
这里有一个可以作为习惯的技巧:在批处理脚本里落一行日志。日志不需要复杂,记录处理时间、处理对象名、修改前后的关键数值就够用:
!logPath = 'C:\work\batch_run_' + string(DATE('yyyymmdd')) + '.log' !fp = FOPEN(!logPath, 'A') FWRITE(!fp, !time + ' 已处理 ' + !obj.Name + ',长度=' + string(!newLen) + '\n') FCLOSE !fp参数说明:FOPEN的A参数表示追加写入,不覆盖上次日志;string()把数值和日期转成字符串用于拼接;FCLOSE务必执行,否则文件句柄会一直占用。日志能让你在脚本跑完之后回溯每一笔操作,这在多人共用同一个设计环境时特别有用。有一次某成员不小心覆盖了我的函数定义文件,导致当天导出的数据整体错位,当时全靠日志定位到出错的时间点和对象,及时把影响范围控制住了。
部署方面,建议把函数文件、表单文件、启动宏文件放在同一个项目目录,并用一个加载宏统一管理。PDMS启动时加载这个加载宏,所有工具函数和菜单即可生效。每次更新脚本后,重跑一次固定样本,确认没有破坏旧功能,再把文件同步到团队成员的工作目录。
从那以后,我每次交付脚本都会强制走一遍最小样本回放,再补一条日志到本地文件。这个习惯救过我很多次。希望这篇文章能帮你在PDMS二次开发上少走几个弯路,也希望你第一次跑脚本前,就把备份功夫做在前面。
本文还有配套的精品资源,点击获取