PML开发介绍与总结1pml语言简介(2)1.1pml变量(2)1.2变量类型(2)1.3自定义变量类型(3)1.4定义变量(3)1.5函数(Function)和方法(Method)(4)1.6使用ANY类型的变量作为函数参数(5)1.7变量的方法(6)1.8自定义变量类型的方法(7)1.9方法的重载(8)1.10Unset和Undefined(9)1.11删除变量(10)2Pml的一些特点(10)3Pml表达式(11)4pml流程控制(11)4.1选择结构(12)4.2do循环(13)4.3GoLabel跳转语句(14)5pml数组(15)5.1定义数组(15)5.2数组的赋值(15)5.3多维数组(15)5.4数组的方法(16)5.5利用数组进行字符串处理(17)5.6数组排序(18)5.7Dovalues循环和Doindices循环访问数组变量(19)6pml宏文件概念及其运行(19)6.1概念(19)6.2带参数的宏文件(20)语言简介介1pml语言简pml是用于pdms二次开发的面向对象的编程语言,它的面向对象属性是与其他脚本语言的最大区别,也是现代高级语言的基本特征。
pml提供了编辑窗体和菜单的功能,且语法简单方便,使得pml能实现高级语言的大部分功能,用户可以开发自己的菜单窗体,完成需求定制。
pml提供了大量的变量类型(objecttype)及与变量类型相对应的方法(method)。
pml提供的方法实用,具有针对性。
比如数组(Arrayobject)可以存放不同的数据类型,可以进行排序、清空没有赋值的元素、查找、删除等等方法,这是大多数高级语言没有提供的功能。
1.1pml变量对于pml语言,每个变量即是一个object,对应一个变量类型objecttype。
不同类型的变量只能保存对应类型的数据。
pml变量的特点:变量名称唯一,且不能重复。
每个objecttype即是一个类(class),有属于类的通用的方法和属性。
因此每个pml的变量(object)都可以调用所属类的方法。
每个变量对应一个变量类型,在定义变量前,对应的变量类型必须存在。
1.2变量类型Pml的变量类型比较多,可以分为三类,built-in、system-defined、user-defined。
Built-in变量类型包括以下几种:字符串实数布尔数组system-defined变量类型包括Direction,Position,Orientation等多种类型,详细信息可以参考手册。
user-defined变量类型是指用户自定义的类型。
1.3自定义变量类型Pml提供的变量类型可以满足大部分情况的使用,如果有需要,用户也可以定义自己的变量类型。
下面是一个自定义变量类型的示例。
defineobjectFACTORYmember.NameisSTRINGmember.WorkersisREALmember.OutputisREALendobject自定义变量类型的名称是FACTORY,该变量类型有3个成员(member),分别是Name,Workers和Output。
任意一个FACTORY类型的pml变量都默认有这3个成员。
比如设变量!MyFactory是FACTORY类型,则可以通过!访问或赋值变量的Name成员,如下面的表达式分别读取和设置了变量!MyFactory的Name属性,!FactoryName=!!=‘APPLE’Pml对成员的类型没有限制,自定义变量类型的变量也可以作为成员,如下例,成员Site的类型是FACTORY。
defineobjectPRODUCTmember.CodeisSTRINGmember.TotalisREALmember.SiteisFACTORYendobject为了方便区分,变量类型一般用大写表示,变量一般首字母大写。
自定义变量类型一般以变量类型的名称为文件名(字母必须是小写),以.pmlobj为后缀保存到PMLLIB文件夹下面。
程序会自动加载定义文件。
1.4定义变量Pml的变量分为全局变量(global)和局部变量(local)两种,全局变量在整个程序运行期间一直存在,而局部变量只在所在的宏文件或函数内有效。
比如一个宏文件包含下面的语句,!Y=3!!X=5$p$!Y$!!X运行后得到结果35运行完成后在pdms中分别查询变量!Y和!!X,!Y是Undefined,而!!X是实数类型变量,值是5。
因为!Y是局部变量,宏文件运行完成后自动释放。
Pml变量名的最大允许长度是16个字符(包含前面的符号!),变量名必须以字母开头,可以是字母和数字的组合。
确定变量名时要简单易懂、避免重复,可以添加自定义的前缀进行区分。
在编写程序时,为了避免重复和减少资源占用,应该尽量减少使用全局变量,使用!!CD为前缀的变量是系统预留的全局变量,在定义变量时要避免重复。
定义一个built-in类型变量(REAL,STRING,BOOL,ARRAY)可以通过直接赋值确定其变量类型,比如!MyVariable=2,则变量!MyVariable会自动根据所赋值的类型确定为real类型,不用另外指定变量类型。
当定义一个值为空的变量时,可以采用如下的方式!MyVariable2=STRING(),指定!MyVariable的类型为字符串,而值为空(UNSET)。
对应其他类型变量的定义需采用下面的形式,!MyVariable3=objectDIRECTION()和方法((Method)1.5函数函数((Function)和方法函数一般定义在一个单独的文件中,文件名必须是小写的函数名称,后缀为.pmlfnc。
函数定义文件一般存放在PMLLIB文件夹中。
每个函数定义文件只能包含一个函数,且文件的第一行必须是definefunction开头。
所有的用户自定义函数都是global类型变量。
函数和方法类似,都可以带有参数和指定返回值。
参数和返回值的类型必须在函数和方法定义时指定,当在主程序中调用函数或方法时必须把对应类型的值传递给函数或方法,经过处理后把返回值传递给主程序。
下面的语句定义了一个函数!!AreaDefineFunction!!Area(!LengthisREAL,!WidthisREAL)isREAL!Area=!Length*!WidthReturn!AreaEndFunction!!Area的功能是根据主程序传递过来的参数!Length和!Width计算矩形面积并将面积返回主程序。
参数!Length、!Width和返回值都是REAL类型,返回值用关键词Return指定,最后以endfunction结束函数定义。
一般在表达式中调用有返回值的函数,如:!MyLength=15!MyWidth=10!MyArea=!!Area(!MyLength,!MyWidth)则得到!MyArea=150,注意参数的顺序不能颠倒,否则可能会导致错误。
上面的语句也可以简化为!MyArea=!!Area(15,10)函数也可以用参数的方式返回运行结果(没有返回值的函数称为PMLProcedure,用Call关键词调用),即在调用函数前定义一个变量,并把变量包含在函数的参数列表里。
如下面的例子,DefineFunction!!LengthAndTrim(!NameisSTRING,!LengthisREAL)!Name=!Name.Trim()!Length=!Name.Length()EndFunction函数的功能是计算字符串变量!Name的长度(去掉前后空格),REAL类型参数!Length用于存放并返回计算结果。
对于特殊的情况,当函数或Procedure没有参数时,函数名后面的括号也必须保留,如DefineFunction!!LengthAndTrim()。
对有返回值的函数也可以采用Call关键词调用,但会由于无法保存而导致返回值丢失。
1.6使用ANY类型的变量作为函数参数某些特殊情况下,可以指定函数的参数或返回值的类型为ANY,即可以接受任何类型的变量。
如下面的例子定义参数!Argument为ANY类型。
DefineFunction!!Print(!ArgumentisANY)$P$!Argument在使用ANY类型的变量前需要确定变量的具体类型,可以使用变量的pmlobjectType()方法如下。
每种变量类型包含的方法可以参考手册。
下面以STRING类型为例说明如何使用变量的方法。
方法定义时以!this代表当前object,并用其调用object的方法(method)和成员变量(member)。
defineobjectLIFEmember.AnswerisREALendobjectdefinemethod.Life()!This.Answer=42Endmethod和变量类型同名且没有参数的方法Life()称为构造方法,在定义该类型的变量时程序会自动调用该方法用于变量的初始化。
变量的方法可以带有返回值,与函数的功能类似,如下面的方法,调用时会返回成员Answer的值。
definemethod.Answer()ISREALreturn!This.Answerendmethod也可以用方法对类成员进行赋值definemethod.Answer(!ValueIsREAL)!This.Answer=!Valueendmethod上面两个方法的调用过程如下,!Marvin=objectLIFE()定义!Marvin为LIFE类型变量!Number=!Marvin.Answer()给!Number赋值为!This.Answer!Marvin.Answer(40)!This.Answer的值设为40!Number=!Marvin.Answer()!Number赋值为40创建新的自定义变量类型后,需要输入pmlreloadobjectobjectname加载定义文件后,才能正常使用自定义的变量类型。
1.9方法的重载重载是指同一个变量类型的两个或多个方法具有相同的方法名称和不同的参数列表,在进行调用时,程序会根据传递的参数类型和数量自动匹配对应的方法。
如下面两个方法,definemethod.mymethod(!ValueIsREAL)$p‘Itisarealnumber’Endmethoddefinemethod.mymethod(!ValueIsSTRING)$p‘Itisastring’Endmethod调用时,若输出的参数时REAL类型,则打印Itisarealnumber,若参数是STRING类型,则打印Itisastring。
当添加新的方法后,需要输入pmlreloadobjectobjectname进行重新加载,以便程序加载最新的定义。
1.10Unset和Undefined变量类型都有一个string()方法,即把当前变量类型转为一个字符串。
!X=2.5!S=!X.String()变量!S是字符串类型,值为’2.5’对于空值的情况,调用String()方法后,得到值如下!X=REAL()!S=!X.String()!S为空!X=BOOLEAN()!S=!X.String()!S为空!X=STRING()!S=!X.String()!S为‘Unset’!X=ARRAY()!S=!X.String()!S为‘ARRAY’UNSET是指已经定义的变量,但还没有给变量赋值。
程序编写时经常需要预先对变量进行判断是否为UNSET,可以用函数和自带的方法两种方式进行判断。
FunctionsMethodsif(Unset(!X))thenif(!X.Unset())thenif(Set(!X))thenif(!X.Set())thenUndefined是指还没有定义的变量,即系统中不存在。
可以用下面的函数进行判断if(Undefined(!!Y))thenif(Defined(!!Y))then1.11删除变量删除一个已定义的变量可以调用delete()方法,!X.delete()即删除了变量!X2Pml的一些特点宏、函数和方法都是pml命令的集合,但在写程序时应尽量的使用函数和方法,因为函数和方法可以方便的包含任意类型的参数,且可以有返回值,使得程序的整体性和可读性比较好,同时符合面向对象编程语言的特点。
Pml文件的注释语句分三种。
用符号”--”开头的是注释行;写在pml语句末尾且同行的注释用$*开头;多行注释用$(开头,用$)结尾。
Return命令用于立即停止当前的pml文件,返回上一层的文件继续执行。
Pml文件一般以Return语句结尾。
Pml语言不区分大小写,IF和if具有相同的作用。
但对于字符串中的字符,需要对大小写进行区分。
Pml命令可以进行简写,如POSITION可以简写为POS。
但为了程序的可读性和避免混淆,一般不建议这样做。
特殊字符$。
$与其他字符的组合表示特别的含义,如$p表示打印。
当pml语句中需要$字符时,需要输入$$。
另外,$也是续行的标识符。
Pml中的字符串表示方法。
Pml文件的类型。
.pmlobj变量类型定义文件.pmlfnc函数定义文件.pmlfrm窗体定义文件PDMS程序启动时,会根据依次加载文件pml.index(在PMLLIB文件夹下)中指定路径下的文件。
如果在程序运行过程中,在已有的路径下添加了新的文件,则需要输入命令pmlrehash加载新文件。
如果其他用户已经更新了pml.index,则只需要输入pmlindex。
Pmlrehashall命令用于重新扫描PMLLIB路径下的所有pml文件,并同时更新文件pml.index。
一个表达式里的变量类型比较匹配,否则会报错。
表达式书写时必须在运算符的前后各留一个空格。
一般情况下,在括号的前后不用留空格,但如果与括号相邻的是一个字符串,则需要与括号之间留一个空格,避免混淆。
运算符的优先级规定如下,由上至下依次降低。
括号用于调整表达式的优先级。
BRACKETS()FUNCTIONS*/+-NENEQGTLTGEGEQLELEQNOTANDOR连接运算符&。
&可以把任意类型的两个变量首先转换为字符串类型,然后把两个变量值前后连接在一起。
可以在表达式中包含对函数的调用,也不用在变量前加$符号。
4pml流程控制pml流程控制语句包括下面几种,选择语句(if)循环语句(do)跳转语句(golable)错误处理(handle)4.1选择结构Pml中if结构和其他编程语言一样,通过判断表达式的值确定程序的执行路径,if结构可以有多个分支,每个分支对应一个判断条件。
典型结构如下,If(BoolA)then(BoolA为true时执行)Elseif(BoolB)then(BoolB为true时执行)Elseif(BoolC)then(BoolC为true时执行)Else(上面条件都不满足时执行)Endif括号内的bool表达式可以是一个bool变量,也可以是一个值为bool变量的表达式。
If结构中各个分支只能执行一个,即程序在执行完任意一个分支后立即退出选择结构,继续下面的语句。
If结构不支持简写为一行的格式,if()thenendif,类似的格式是错误的。
If结构可以嵌套使用,下面的例子是合法的,If(!numGT1)thenIf(!numGT5)then$p‘good’Else$p‘passed’EndifEndif布尔表达式布尔表达式的运算符包括EQ、GT、LT等,布尔表达式的运算结果必须是布尔变量(true或false)。
为了向上兼容,pml支持用字符$和字符串格式的‘TRUE’‘FALSE’组合表达布尔变量,如variable=‘TRUE’,if($variable)是合法的。
另外,字符串类型的自带方法.boolean()可以把字符格式的’TRUE’‘FALSE’转为bool类型。
如,!mystr=‘true’则!mystr.boolean()表示一个bool类型变量。
.boolean()方法可以把所有的非0实数转为TRUE,把0转为FALSE。
4.2do循环典型的do循环结构如下,Do!xFromstartvaluetoendvalueByincrement
对于简化形式的循环结构,!x、startvalue、endvalue和increment都可以省略,上面的例子可以简化为Do
循环控制语句:Break、Breakif、Skip、SkipifBreak和Breakif语句可以用于退出循环的操作,如下面的例子,do!Numberif(!NumberGT100)thenbreakendif!Result=!Result+!NumberEnddo当!number值大于100时,会执行break语句退出循环。
其中的if结构也可以替换为breakif(!NumberGT100),效果相同。
Skip和skipif语句是在满足一定条件时忽略掉(不执行)skip后面的语句而直接执行下一次循环。
如下例,可以用skip语句忽略掉所有的奇数,只对偶数进行操作。
do!X!Number=!Sample[!X]if((INT(!Number/2)NE(!Number/2))thenskipendif!Result=!Result+!Numberenddodo!X!Number=!Sample[!X]skipif(INT(!Number/2)NE(!Number/2))!Result=!Result+!NumberEnddoDo循环的嵌套do!XFrom1to10!Z=‘’do!YFrom!Xto10!Z=!Z+‘’+!Y.string()Enddo$p$!ZEnddo循环的结果应该打印出如下的数字序列123456789102345678910345678910456789105678910678910789108910910104.3GoLabel跳转语句Golabel/labelname语句用于跳转到/labelname下一行继续执行。
见下例do!Ado!Bto3!C=!A*!Bgolabel/finishedif(!CGT100)!Total=!Total+!Cenddoenddolabel/finished$PTotalis$!Total在变量!C大于100时程序跳转到label/finished的下一行继续执行,即跳出了循环体。
但是使用golabel语句不能用循环外面跳转到循环内部,否则程序报错。
5pml数组5.1定义数组和其他编程语言的概念一样,pml中数组是可以包含多个元素的object。
数组定义的方式有两种,一种是提前定义,即在使用前首先对数组进行定义,如!X=ARRAY()定义了一个数组!X。
需要注意的是,pml中数组不需要指定类型和数组大小,即可以在同一个数组中存放不同类型的元素也可以任意增加数组元素的数量。
另外一种数组定义方法是在给数组中元素赋值时同时完成数组的定义,即不提前进行定义。
如表达式!NewArray[1]=!NewValue,如果数组!NewArray没有提前定义,则该表达式会自动完成数组的定义,同时给其中第一个元素赋值。
数组在书写时注意数组名和后面的中括号之间不能有空格,pml的变量名不区分大小写,可以用自己喜欢的方式书写,但尽量前后保持统一,方便阅读和检查。
数组名称不能与其他变量名或数组名相同,否则会报错。
5.2数组的赋值Pml数组赋值时没有顺序限制,也可以不连续赋值,即在赋值的两个元素之间可以有任意多个没有赋值的元素。
如对于数组!X,可以只对!X[1]和!X[10]进行赋值,中间没有赋值的元素认为是不存在的,可以用UNDEFINED方法进行判断。
数组的索引值可以是表达式形式,但表达式的结果必须是一个real类型的数组(pml中没有整型变量)。
Pml数组的索引可以从0开始,但一般从1开始,避免一些混淆。
Pml数组的方法完整列表可以参考用户手册。
数组方法可以分为两种,即不改变原数组的方法和改变原数组的方法。
例如获取数组的元素数量!Nelements=!MyArray.Size(),执行后数组的大小会存到变量!Nelements中,而数组!MyArray没有任何变化。
但对于数组的clear()方法,则是进行清除数组所有元素的操作,改变了原数组。
常用的数组方法:RemoveFrom(),清除指定范围内的元素并赋值给新数组,如下面的例子。
追加的元素是在已定义的索引值最高的元素后添加,如果原数组为空,则Append()方法会将追加的值赋给第一个元素即!MyArray[1]=‘newvalue’。
需要注意,不是赋值给!MyArray[0]。
Pml的字符串(string)变量提供了split()方法,用于按指定的字符对字符串进行拆分。
如下面的例子。
5.6数组排序数组提供了sort()方法进行元素升序排序,对于字符类型元素按照ASCII,对于数值类型元素按大小进行排序。
Sort()方法没有返回值,结果是对原数组的修改。
对于需要降序的情况,可以用Invert()方法对排序后的数组进一步操作,得到降序数组。
另外一种排序的方法是SortedIndices(),即原数组中元素在排序后数组中的索引值,并保存到一个新数组(real类型)中。
如!newposition=!MyArray.SortedIndices()。
!newposition是新生成的real数组,依次存放数组!MyArray中的元素排序后的位置。
此时!MyArray数组没有改变,需要用ReIndex()方法把得到索引数组应用到数组!MyArray中完成排序操作。
下面的例子可以通过循环变量访问数组!MyArray中的元素并打印出来。
Do!dovariableValues!MyArray$p$!dovariableenddoDoindices循环是依次访问数组中每个元素的索引值,Do!dovariableIndices!MyArray!value=!MyArray[!dovariable]$p$!valueenddo6pml宏文件概念及其运行6.1概念宏文件是由多个pml命令行组成的文本文件,一般文件的后缀设为.mac(也可以是其他后缀比如.txt)。
对于一般赋值语句或定义语句,从宏文件中运行和手动输入到commandwindow中运行的效果一样的。
但是对于流程控制结构,手动输入会报错,只能以文件的形式运行。
比如下面的do循环,Do!xfrom1to5by1$p$!xEnddo逐行手动输入运行时会报错,保存到宏文件中运行时则会正确运行,一次打印出数据12345宏文件的运行方法是在命令窗口输入$mfilepath,其中filepath是宏文件所在的完整路径,且须包含完整的宏文件名,如$mC:\Windows\myfirstmacro.mac如果没有输入文件所在的路径,程序会在当前运行目录(pdms安装路径)下查找文件,如果找不到指定文件就会报错。
6.2带参数的宏文件使用参数化的宏可以方便的用一个宏处理一类相同的问题,只需要在调用宏时按需要赋值给参数就可以得到对应的结果。
比如下面的例子,!result=$1+$2+$3$p$!result宏的运算是求三个参数的和,调用时输入$mfilepath123,则输出结果6。
需要注意,作为参数的字符串中间不能有空格,否则程序会把空格作为分隔符对字符串进行拆分,得到错误的结果。
如果在调用参数化宏文件时没有指定参数值,则宏中的参数会被忽略掉,由此可能会导致程序报错。
宏文件中的参数可以通过在宏中设置默认值的方式避免出现参数没有被赋值的情况发生,具体语法是在宏文件的开头定义参数的默认值(也可以在宏文件其他任意位置,但默认值只对定义默认值所在行后面的表达式有效)。
$D3=‘defaultvalue’注意,参数的默认值只在参数没有被赋值时有效。
另外,$,也可以用于在调用宏文件时忽略指定位置的参数,如$M/demo.mac$,$,arg2$,arg3$.忽略了第一个参数。
也可以用$<$>忽略指定的参数,如$M/demo.macarg1$<$>arg3忽略了第二个参数。
如果忽略参数表中的最后一个参数,可以直接忽略不输入,不用特殊符号表示,如$M/demo.macarg1arg2另外注意在用变量作为参数调用参数化宏时,必须把变量转为字符串类型,比如,!X=12!Y=15$mfilepath$!X$!Y。