一种可处理函数顶点的梯形图转换成指令序列的方法与流程

文档序号:15557473发布日期:2018-09-29 01:27阅读:263来源:国知局

本发明涉及梯形图转换成指令序列的方法,尤其涉及一种可处理函数顶点的梯形图转换成指令序列的方法,属于工业控制技术领域。



背景技术:

梯形图(ladderdiagrame简称ld)广泛应用于工业控制领域中,对梯形图的处理主要集中在如何将其翻译成指令列表,以便可编程控制器(plc)可以执行这些指令来完成由梯形图表达的控制任务。梯形图本质上是一种通过运算单元之间的连接结构来描述的运算表达式,表达式只包含两种运算,分别与串联结构和并联结构相对应。将梯形图转换成指令表的方法已经不少,但都或多或少地存在问题。给出了将梯形图翻译成iec61131-3指令表的方法,在所转换的梯形图中可以有function顶点,以便完成特定的功能调用。但该方法对function顶点功能的处理并不完善,没有考虑function顶点必须提前计算其输入的问题。同时算法没有给出算法执行的时间。

梯形图到指令表的转换本质上都是图到二叉表达式树的转换。二叉表达式树,简称bet,是二叉树的一种,经过对树的中序遍历之后所得的遍历序列是一个表达式。树的任何一颗子树称成为二叉表达式子树,记作sub-bet。



技术实现要素:

本发明提出了一种把梯形图转换成二叉表达式树的方法:ld-bet方法,该方法不仅克服了以往方法的不足,更主要的是不以转换成指令表为目标,而是以二叉表达式树数据结构为目标。对树的中序遍历的打印输出稍作改变就可以得到不同规范的指令序列,即可以把梯形图翻译成各种指令序列。同时,还可以在内存中直接保存二叉表达式树,省去表达式的翻译过程,在得到二叉树的后续遍历的序列之后,可以进行基于栈的表达式计算,从而直接获得计算结果。该算法的另外一个特点是可以对后面二次用到的计算结果进行自动缓存,而不必对所有的并联合并的结果都进行缓存,从而节省了存储空间。

本发明公开了一种可处理函数顶点的梯形图转换成指令序列的方法,该方法中:图g=<v,e>由一组有限的顶点v和边e的集合组成。如果g是一个图,则v=g.v表示图中的顶点集,e=g.e表示图中的边集;任何一个梯形图都表示成一个有向无环图即dag。此外还必须满足一些约束条件,把能够满足这些约束条件的dag图称为ldgraph。

ld-bet方法中用到2个对象,一个为用于描述bet树的结点,另外一个为用于描述ldgraph的顶点。结点对象包含vertex,left,right,π属性,结点集合记为n。构成bet树的所有结点组成了一个结点集合nbet,而且vertex属性表示结点所包含的顶点,即表示该结点与图中哪个顶点相对应。left表示左指针,right表示右指针,π表示父指针,所有指针指向bet树中的其他结点。结点对象没有专门的名字属性,结点名字用包含其中的vertex对象的名字属性表示。结点对象通过node(vertex)方法创建,通过add-node方法使三个结点都变成属于nbet的结点。顶点对象包含name,adj,adj',buffer,buffer',root,π,d,color,conntiontype,functiontype属性。顶点名称由name属性表示,adj表示顶点的邻接表;图用邻接表的形式描述,对于顶点v其邻接表v.adj代表一个集合{adj1,adj2,……,adjn},其中adji∈v,i=1,2,……,n。该集合表示从v到集合v.adj的每一个元素之间都有一条有向边;顶点1的邻接表vertex1.adj中的各元素为{vertex2,vertex3,vertex6,vertex7,vertex10,vertex20,vertex21}。adj'是一个链表,用于收集射入并联型顶点的串联分支,串联分支的并联合并要使用它。对于顶点v其链表v.adj'代表一个有序序列<adj'1,adj'2,……,adj'n>,其中adj'i∈nbet,i=1,2,……,n。有序序列表示从v到序列的每一个元素之间都有一条射入v的串联分支,其排列次序代表了射入的先后顺序。buffer,buffer'用于缓存生成bet子树时的中间结果。root表示与该顶点相关的bet子树的根。π表示顶点的父顶点。d用于表示顶点到初始顶点的距离。color用来标识顶点的访问状态,以便判断顶点在遍历过程中是否已经被访问过。顶点对象通过vertex(name)方法创建,参数表示顶点的name属性。

按照连接关系分类把顶点分成串联型顶点和并联型顶点。串联型顶点:图中入度和出度都不大于1的顶点,用vs表示。并联型顶点:图中入度大于1或出度大于1的顶点,用vp表示;vs∪vp=g.v。连接关系分类由顶点属性connectiontype表示,对应的取值分别为serial,parallel,分别表示串联型顶点和并联型顶点。

按照功能分类把顶点分成初始顶点、终止顶点、relay顶点、coil顶点、function顶点、辅助function顶点、辅助relay顶点和辅助coil顶点;该分类由顶点属性functiontype表示,对应的取值分别为gs,gt,relay,coil,function,vsf,relay,vcoil,对应的连接关系分别为:并联,并联,串联,串联,串联,串联,串联,并联。

附图说明

图1为一个由ladderdiagram转化而成的有向图。

图2为串联型分支的结构示意图。

图3为添加顶点的结构示意图。

图4为一个串联bet树形成过程的例子示意图。。

图5为顶点25的adj′属性在经过1-13行处理后的结果图。

图6为图5的合并结果图。

图7为图6的处理过程和结果图。

图8为顶点11成为顶点x1和顶点14的共同尾顶点。

图9为成为左子树计算结果的输出缓存过程图。

图10为所对应的bet树形成的过程示意图。

图11为顶点25的adj'属性中的数据结构图

图12为图3所对应的bet树形成过程图。

图13为指令表示意图。

具体实施方式

下面结合附图和实施例对本发明进行详细的描述。

初始顶点:图中唯一的一个与任何顶点都连通且入度为0的顶点,用gs表示,一般画在图的最左端。图1中的白色顶点是串联型顶点,灰色顶点为并联型顶点。y0和y1是coil类型串联型顶点。

终止顶点:图中唯一的一个从任何顶点出发都可以到达且出度为0的顶点,用gt表示,一般画在图的最右端。在ldgraph中为便于对coil顶点统一处理引入辅助顶点gt,所有coil类型的顶点都射入其中,具体示例如图3(1)所示。终止顶点和辅助function顶点都是人为后加入到图g中的。初始顶点和终止顶点强制规定为并联型顶点,vcoil类型的顶点用于辅助function顶点的处理,而其他并联型顶点一般不用设置functiontype属性,其用途将随具体应用而定。除辅助顶点外,functiontype属性为已知属性,即随图g的给定而确定。

串联型分支:图中2个并联型顶点之间的一条path,该path所经过的顶点均为串联型顶点,串联分支的两端是并联型顶点,这两个顶点是串联分支中仅有的两个并联型顶点。如果忽略串联型顶点,每条串联分支为一条边,实际上由串联分支和并联型顶点组成了一幅图,可用g'=<vp,ep>表示这种图。每条串联分支的串联分支距离为1,不特别说明本方法中距离都指的是在g'中v∈vp到gs的距离。

串联分支的尾顶点:串联分支上的第一个顶点,该顶点是一并联型顶点。串联分支的尾顶点简称尾顶点,表示为vt,显然有vt∈vp。

串联分支的首顶点:串联分支上最后一个顶点,该顶点是一并联型顶点。串联分支的首顶点简称首顶点。尾顶点沿串联分支可以到达首顶点,首顶点表示为vh,显然有vh∈vp。

最小尾顶点与最小串联分支:在所有射入并联型顶点的串联分支中,必存在一条串联分支其尾顶点与其他尾顶点相比距离初始顶点最近,该尾顶点称为最小尾顶点,表示为vtn。最小尾顶点所在的串联分支称为最小串联分支。也即在两个并联型顶点之间最小串联分支是跨度最大的串联分支;最小串联分支并不唯一,图1中,<1,2,25>和<1,3,4,5,25>都是顶点25的最小串联分支,顶点1是顶点25的最小尾顶点,而顶点16,13,22,1都是顶点25的尾顶点。

ldgraph定义:除gs和gt外,dag图中的所有并联型顶点都满足,当沿着每条射入其尾顶点的边反向行走时,都必然经过其最小尾顶点。对于给定的图g,其反向图记为gt。如果在gt中存在v1,v2两个顶点,从顶点v1出发通往gs路径中经过了v2,则称v2覆盖了v1。dag图不会出现相互覆盖的现象。在ldgraph中最小尾顶点完全覆盖了其他尾顶点。只有满足ldgraph定义的图,每一并联型顶点的adj'列表才会包含可以进行并联型合并的所有信息。图1是ldgraph,而图2不是ldgraph图,因为对于顶点9,如果顶点3作为最小尾顶点它没有覆盖尾顶点6,其经过顶点4的串联分支没有经过顶点3。如果顶点6作为最小尾顶点也不能覆盖顶点3,本发明提出的算法只用于处理ldgraph。

图2:非ldgraph,如果顶点3作为最小尾顶点,则沿着射入顶点9的尾顶点6的反向边行走时,经过顶点4的串联分支并没有经过顶点9的最小尾顶点3。同样,如果顶点6作为最小尾顶点也不能覆盖顶点3。

在ldgraph中可以有function顶点,比如将图1中的顶点17和顶点23变成function类型的顶点。这类顶点和relay类型顶点完全不同,输入和输出的逻辑运算关系是断开的,可以认为输入和输出没有关系,因为function顶点所调用过程的输出可以是boolean值的任何值。为了对function顶点进行处理需要在ldgraph图中加入辅助function顶点和辅助relay顶点。添加的规则是将function顶点所在的串联分支上的尾顶点变成vcoil类型的顶点使其成为辅助coil顶点,同时在该尾顶点的所有输出边上添加辅助relay顶点用于表示辅助coil顶点输出结果,其名称与该尾顶点的名称相同,但为了便于说明,用“尾顶点名称”加“’”表示,其本质和relay顶点没有区别。然后在该尾顶点与顶点gt之间添加只含一个辅助function顶点的串联分支,辅助function顶点名称和尾顶点同名,但为了便于说明,用“~”加“尾顶点名称”表示。加入该串联分支的作用是使vcoil顶点的计算提前进行,以保证在用到辅助relay顶点时这些顶点已经拥有了计算结果。但这种串联分支的引入会破坏尾顶点覆盖原则,但由于这一情况只会在处理顶点gt时遇到,所以可以通过专门的处理过程处理这一问题(见build-subtree3过程)。如果图1中的顶点17和顶点23变成function类型的顶点,辅助顶点的添加示例如图3中的(2)所示。图中细虚线圆表示辅助function顶点,粗虚线圆表示辅助relay顶点,顶点16和22则变成辅助coil顶点。

尾结点与最小尾结点:图ldgraph中每个顶点在bet树中都可以找到对应的结点,所以顶点与结点存在对应关系。树中包含最小尾顶点的结点称为最小尾结点,表示为ntn。而树中包含其他尾顶点的结点称为尾结点。最小串联分支经转换所对应的bet子树称为最末bet子树,其树根表示为rtn。为了便于论述有时用顶点符号表示包含该顶点的结点,例如gt结点表示包含gt顶点的结点。

图3中的(1)为图1添加终止顶点gt,(2)图1中的顶点17和顶点23变成function类型的顶点后,需要添加2个辅助function顶点~16,~22,两顶点的functiontype属性设置为vsf,3个辅助relay顶点16',16',22'。并联型顶点16和22的functiontype属性设置为vcoil。

支经转换所对应的bet子树称为最末bet子树,其树根表示为rtn。为了便于论述有时用顶点符号表示包含该结点,gt结点表示包含gt顶点的结点。

运算结点与串联结点:bet树中的结点如果表示运算,如and,or,st,empty等,则称为运算结点,在图中分别用*,+,st,~表示。运算结点所包含的顶点称为运算顶点,表示为vop,其name属性表示运算符,其功能属性被设为operator,这些顶点都不属于图g,即如果结点的vertex属性为一串联型顶点则该结点是串联结点。

串联型顶点的合并与并联型顶点的合并:串联分支的上每个串联型顶点借助and结点和add-node过程所进行的合并称为成为串联型合并;对射入并联型顶点每条串联型分支借助or结点和add-node过程所进行的合并称为成为并联型合并。无论哪种合并,合并的结果都会生成一颗bet子树。

ld-bet算法由六部分组成。第一部分读入图g。第二部分设置图中每个顶点的连接属性connectiontype。第三部分为图添加辅助顶点。第四部分对串联分支上的顶点进行合并,第五部分对射入并联型顶点的串联分支进行合并。第六部分展开bet树中每个并联型顶点,形成一棵不含并联型顶点的bet树。每一部分又由一个或多个过程组成。每一步完成后都要将图g的顶点颜色恢复成白色。

ld-bet

1readthegraphg

2set-connectiontype

3add-auxiliary-vertex

4dfs

5bfs

6build-tree

第一部分包括梯形图转换成ldgraph的算法,格式验证和判别是否是ldgraph,但这些算法均不在本发明讨论之列,本发明的讨论从获得了合格的ldgraph图g开始。下面分别对2-6部分进行详细地说明。

set-connectiontype(g)

sub-set-connectiontype(u)

set-connectiontype过程采用深度优先搜索的方法,为每个顶点设置连接属性,如果是单输入单输出则顶点的连接属性设为串联;如果多输入或多输出则连接属性设为并联。1-3行是将图g中的所有顶点都涂成白色,并且其连接属性设为串联。第5行开始顶点gs强制设置为并联型顶点。7-8行判断每个顶点的出度是否大于1,如果是则该顶点是并联型顶点;在第13行中当前顶点u指向了一个灰色顶点,而灰色顶点的入度必大于1,所以该灰色顶点设置成并联型顶点。第9行对顶点u的所有邻接顶点进行遍历,检测这些顶点的颜色属性,其中表示u的邻接顶点们。第11行设置了顶点π属性,用于记录顶点的父顶点,这为当前图g的反向图(转置图)gt的形成创造了条件。整个过程依次访问图g中所有顶点一次,如果邻接顶点被涂成灰色则表明该顶点已经访问过,如果是白色表明未被访问过,这时可以通过递归调用sub-set-connectiontype过程继续访问白色顶点。在本方法所有算法中顶点的颜色属性都用来记录某一对象的访问状态。由图的深度优先搜索运行时间可得本过程的运行时间为o(v+e),写在o中的v和e分别表示图g中顶点的数和边数。

add-auxiliary-vertex(g)

add-auxiliary-vertex过程首先创建一个包含0个元素的辅助coil顶点集合vcoil。2-9行中找出图g中的所有coil顶点和function顶点,第4行为所有的coil顶点添加一条射入终止顶点gt的一条边。6-9行找到function顶点所在串联分支的尾顶点,并将其并入到vcoil集中。利用颜色属性的变化避免了重复并入。10-20行对所有辅助coil顶点依次处理。第13行创建辅助relay顶点,14-16行将辅助relay顶点加入到与尾顶点紧邻的串联分支中,17行改变尾顶点的连接属性,使其成为vcoil。第18行创建辅助function顶点,19-20行将辅助function顶点加入到vcoil顶点与终止顶点相连的串联分支中。第6行简化了find尾顶点的一些细节,通过π属性function顶点可以反向找到尾顶点,停止反向搜索的条件有两个:碰到尾顶点或碰到function顶点。后一个条件确保了串联分支上有多个function顶点时只需处理一次尾顶点。因此1-9行的运行时间为o(v+e),总的运行时间也是o(v+e)。

add-node(op,leftchild,rightchild)

ldgraph到二叉表达式树(binaryexpressiontree)的转换中,需要用到add-node过程,该过程会形成有三个结点的二叉树,树根np,左孩子leftchild,右孩子rightchild。如果左孩子或右孩子有一个不存在,则返回另外一个孩子。如果左右孩子都存在则返回树根,树根是一个运算结点,这点从第1行和第14行可以看出。在第2行中运算顶点的功能属性被设为operator。8-13行处理了几种特殊的孩子,在各自的情况下运算顶点被赋予了不同的名字,这些特殊名字的作用会在后面的论述中加以说明。17,18行使每个二叉结点能方便地找到自己的父亲。本过程的运行时间为o(1)。

dfs(g)

dfs过程按照深度优先搜索的顺序,合并同一串联分支上的所有串联型顶点。7行利用node过程将当前顶点包装成结点,通过add-node过程形成一棵新子树,使原有bet树的树根的右子树与新包装的结点分别成为该子树树根的左右孩子,然后再将其嫁接到原有的树根上,使其成为原子树树根的右孩子。6行使当前顶点的root指向其父顶点的root,所以整个串联分支每个顶点的root实际上是该分支首个串联型顶点的root,都表示所形成的bet子树的树根。沿整个串联分支搜索时bet在不断地成长,新创建的结点以右孩子的身份不断地添加到bet中,该树不断地长高,直到碰到并联型顶点。串联分支上第一个串联型顶点的处理在第8行进行,所生成的bet的左孩子所包装的顶点为与该顶点相邻的一个并联型顶点。从第7行可以看出,在串联合并的过程中左孩子的具有不变性,所以由第8行并入bet树的并联型顶点就是整个串联分支的尾顶点。整个串联分支所对应的bet树称为“串联bet树”。9-11行进行深度优先搜索,这点与set-connectiontype过程类似。图4给出了一个串联bet树形成过程的例子。

此外,还需要为并联型顶点设置拓扑距离,以便对并联型顶点进行拓扑排序。后面在处理终止顶点gt时,需要知道哪些顶点必须先于其他顶点进行处理。第1行为距离设置初值。每当遇到并联型顶点时执行13和14行。距离越小越靠前处理。由图的深度优先搜索运行时间可得本过程的运行时间为o(v+e)。

图4中,1-5顶点表示一条串联分支,顶点1和顶点5为并联型顶点,顶点2-4为串联型顶点。(1),(2),(3)按照算法的运行顺序描述了这一串联分支上顶点的合并过程。浅灰的圆圈表示bst树中的结点,"*"表示串联运算符and。串联分支上的所有串联型顶点的root属性最终都指向了所生成的bst的树根。

bfs(g)

skip-serial-vertices(u)

bfs过程按照广度优先搜索的顺序,处理图g中所有并联型顶点。1-6行加上11-13行是典型的广度优先搜索算法,对于整个图g的搜索始于初始顶点gs。enqueue和dequeue分别表示出队和入队方法。1-13行对并联型顶点进行初步合并使射入并联型顶点的串联分支形成or运算,每一条串联分支的都会通过or结点并入所形成的bet子树,所有射入并联型顶点vh的串联分支所对应的bet子树会被保存在vh.adj'中,该属性数据结构为列表,与邻接表的结构类似,但需要强调列表中各元素的次序。第8,9行用来找到串联分支上首顶点vh。skip-serial-vertices过程用于沿串联分支进行搜索,直到遇到该分支上的首顶点并将其返回。在搜索的过程中还顺带设定了所遇并联型顶点的父顶点,对于串联型顶点,第2行属于重复设置,因为前面的过程已经对串联型顶点的π属性进行了设置,但对于首顶点来说却非常重要,因为可能有多条串联分支射入首顶点,所以有必要为其指定当前与哪条串联分支相连,这一信息可通过返回顶点的π属性获得,为了便于说明,称π属性所表示的顶点为首父顶点,显然首父顶点是一个串联型顶点。bfs-parallel-vertices过程中,vh.π表示当前所处理的串联分支上的首父顶点,其root属性中保存着该串联分支所对应的bet子树根,其左孩子包含着该串联分支的尾顶点。∪表示两个列表的有序并,后面的列表元素会依次插入到前面列表的末尾或将后面的一个集合元素插入到前面列表的末尾。第10行将首父顶点中的串联分支所对应的bet子树的树根有序合并到顶点vh的adj'中。vh.adj'中最终保有所有射入顶点vh的串联分支所对应的bet子树的树根。图1中,顶点25的adj'属性在经过1-13行处理后其结果如图5所示。

图5:对于图1中的顶点25,经过dfs-serial-vertices处理之后,顶点2,5,23,15,18,19的root属性中已经保存了各自串联bet子树的树根,再经过bfs过程的1-13行的处理,其结果如图所示。adj'列表中的各元素依次表示为adj'1,adj'2……adj'n,于是adj'1,adj'2,adj'3,adj'4,adj'5,adj'6对应着2.root,5.root,23.root,15.root,18.root,19.root。

串联分支的尾顶点可以互不相同,也可以相同,如果串联分支上的尾顶点相同则串联分支可以用并联运算符or进行合并,对于图1中的顶点25,在其adj'列表中2.root和5.root可以合并,18.root和19.root可以合并。14-28行描述了合并的具体实现过程。14,15行找到图g中的所有并联型顶点,然后18行重新设置并联型顶点父顶点。由于广度优先搜索的缘故adj'中的第一棵子树的树根adj'1的左孩子所包含的顶点是最小串联分支的尾顶点,将该顶点设为当前并联顶点u的父顶点,因此并联型顶点的父顶点也是最小尾顶点。例如图5中对于顶点25其父顶点为1号顶点,而不是其他并联型顶点。19-25行对并联型顶点的adj'列表进行遍历,该列表保存着全部射入并联型顶点的串联分支的串联bet树的树根,在树根的左孩子中包含着串联分支的尾顶点。如果两条串联分支的尾顶点一样则这两条串联分支可以进行or运算的合并。21行判断两个尾顶点是否相等,如果相等则执行22行,其作用是将两棵串联bet树通过or运算结点合并成一颗bet树,两棵原有树的右孩子分别成为新合成树的右孩子的左右孩子,新合成树的左孩子可以是两棵原有树中任何一棵树的左孩子,因为它们都包含着相同的尾顶点。如果两个串联分支的尾顶点不同,则执行23行,它将bet树的树根直接加入临时列表adjt中。由于采用两两合并的策略,24行和25行交换了合并和被合并角色,这为进一步的合并动作做好了准备。26行将最后一个合并结果保存到临时列表adjt中。第27行将临时列表adjt中的所有元素颠倒次序,使得离初始顶点越近的尾顶点(包含在bet子树树根的左孩子中)越排在列表的后面,这里的距离测量单位为串联分支数,由一条串联分支连接的两个并联型顶点其距离是1。显然最小尾结点排在列表的最后。第28行用合并的成果重新设置adj'属性。图5的合并结果如6图所示。

图6:"+"表示并联运算符or。(1)2.root,5.root借助or运算结点进行合并的结果。(2)23.root保持不变。(3)15.root保持不变。(4)18.root,19.root借助or运算结点进行合并的结果。

下面证明对于任意一个并联型顶点vh,其vh.adj'n.left.vertex一定是最小尾顶点,其中n表示adj'列表中最后一个元素的index,并且串联分支借助or运算结点进行合并时尾顶点是有序的,即不同的尾顶点所代表的串联分支不会交替出现,也就是说相同的尾顶点之间不存在其他尾顶点,所以不会出现交叉合并的情况。例如对于图1中的顶点25,其合并顺序一定不会出现2.root,23.root,5.root或18.root,15.root,19.root这种排列情况,即2.root,5.root一定为一组,18.root,19.root一定为一组,它们之间不会有其他顶点,但每组各树之间顺序可以是任意顺序。

证:由于第9行的作用,bfs1~13行实际上是在图g'上进行广度优先搜索。6~13行是对尾顶点vt的邻接顶点vh进行遍历,显然vt与vh的距离为1,对于某一首顶点vh,如果射入其中的串联分支的尾顶点相同,均为vt,则意味着vh与vt之间有重边。对于有重边射入的头顶点vh会连续在其adj'列表中记录通过重边关联的尾顶点的信息,所以重边保证了相同的尾顶点在首顶点的adj'列表中必相邻。而在图g'中重边可以通过14~26行进行合并。另外,由于广度优先搜索本质上是按照距离初始顶点gs的最短路径进行,所以顶点的adj'列表中元素排列是有序的,28行执行完之后列表中各元素的次序就是尾顶点据gs由远到近的次序,距离越远在adj'列表中越向前排,经27行的处理,距离gs越远在adj'列表中越向后排,而这正是我们所希望,因为后面的算法要求最小尾顶点最后处理。1~13行本质上是图g的广度优先搜索,其运行时间为o(v+e),14~28行的运行时间为o(v+ep),因此总的运行时间为o(v+e)。

build-subtree(u)

build-subtree过程的主要作用是将属于并联型顶点u并保存在adj'中的串联分支初步的合并成果进一步合并,使射入并联型顶点u的串联分支都形成并联运算,其结果保存在u.root中。单个并联型顶点的处理分成三种情况分别进行。5行处理一般情况,np中保存着所形成的bet子树的树根。3行处理顶点是终止顶点gt的情况。2行处理的情况是已经存在处理好的bet子树,但需要二次使用以前的结果。在build-subtree过程调用前所有的并联型顶点的root属性都为空,如果不为空说明该顶点已被处理。三种情况的处理通过分别调用build-subtree-3,build-subtree-2,build-subtree-1子过程完成。

build-subtree-3(u)

在build-subtree-3过程中,rtn表示射入u的最末bet树的树根,vtn表示表示u的最小尾顶点。3-6行对u.adj'列表中的各串联分支的尾顶点初始化。所有的尾顶点被涂成灰色,顶点的颜色表示处理是否继续,如果遇到灰色顶点就会停止处理。buffer和buffer'属性被清空。这两个属性都用于缓存处理并联型顶点时临时生成的bet子树,但buffer'对应于vcoil顶点的处理结果,而buffer对应于其它并联型顶点的处理结果。7-28行利用u.adj'列表中的所有子树构建成一棵bet树。构建过程按照图g't有向边进行,这些边的方向与图g'中边的方向相反。所以这时的构建路径属于逆向构建路径,这与前面过程构建bet子树的路径不同。8,9行将树根的右孩子表示成np,它实际上是不含尾顶点的串联分支bet子树的树根。每条串联分支的尾顶点表示成vt,这时的串联分支是经bfs过程合并后的串联分支,所以u.adj'列表中不会出现相同的尾顶点。当前尾顶点vt通过10行涂成白色,以便可以开始处理该顶点,因为所有顶点初始都是灰色。第11行是将当前尾顶点所缓存的bet子树与np进行并联合并。因为对于被当前尾顶点覆盖的尾顶点的处理结果会以bet子树的形式保存在buffer中,在对当前尾顶点做进一步处理前首先要将以前的合并结果合并进来。第11行的作用还可以理解为是一种按照顶点出度而进行的并联型合并。12和15-17行是当前顶点vt只要没有遇到处理过的顶点就不断沿着g't的边搜索,一旦遇到并联型顶点就进行串联型合并,同时对所处理并联型顶点加入标记,表示该顶点已经处理过。每个并联型顶点只被处理一次,也意味着由最小尾顶点覆盖而产生的串联型合并只做一次。第16行通过对当前顶点vt的最末bet树树根的名字修改表明顶点vt已经被处理过,后面第26行通过最末bet树树根的名子就可以检测出最小尾顶点是否需要串联合并到bet子树中。顶点u所对应的最终bet树都由u.adj'列表中的bet子树构成,但不用担心bet树由于修改结点名子而遭到破坏,因为我们修改的是bet子树的树根名称,而完整的可计算的bet子树是其右孩子,这一点从第8行可以看出。13-14行,如果所遇到的顶点已经处理过则表明违反覆盖原则的情况出现,需要立即终止搜索,并通过涂黑该顶点表明上述情况的出现。18-22行处理尾顶点为黑色的情况。如果当前尾顶点functiontype属性是vcoil,这时需要将运行12-17行所生成的bet子树,其子树的树根保存在np中,并联合并到最小尾顶点buffer'属性所缓存的bet子树中。最终所有射入顶点vt的串联分支,如果其尾顶点是vcoil顶点,都将并联合并到vt.buffer'所缓存的bet子树中。并联型顶点的functiontype属性如果不是vcoil,则该尾结点首先要串联合并到临时bet子树np中,然后再将合并结果并联合并到最小尾顶点buffer属性所缓存的bet子树中。最终所有射入顶点vt的串联分支,如果其尾顶点是非vcoil黑色顶点,都将合并到vt.buffer所缓存的bet子树中。23-24行处理最小尾顶点的情况,由于11行首先执行,因此buffer属性所缓存的bet子树不用重复处理。25行处理非最小尾顶点在搜索时遇到最小尾顶点的情况,这是多数情况。这时当前顶点vt就是最小尾顶点vtn,临时bet树被并联合并到buffer属性所缓存的bet子树中。当u.adj'列表中所有尾顶点处理完之后,处理结果保存在最小尾顶点的buffer和buffer'属性中。对于buffer中的子树,这时需要判断是否要将最小尾顶点串联合并到bet树中,一般情况下是肯定的回答。但如果是最小尾顶点已经经其他路径处理了或最小尾顶点是vcoil型顶点,则无需再对最小尾顶点进行串联型合并。26-27行完成上述过程。由于我们希望最小尾结点离树根最近,它将借助and'运算结点添加到buffer缓存的bet子树中。最终最小尾结点成为该bet子树树根的右孩子。and'运算结点实际上就是and运算结点,其额外的作用是对该结点进行标注,以表明这种结点的右孩子是一个最小尾结点。需要注意的是bet子树中,最小尾结点的位置与其他尾结点的位置不同,前者是右孩子而后者都是左孩子,这一结构特点会用在随后的过程中。28行最后把buffer和buffer'中的两棵bet子树进行并联型合并,然后将其返回。用不同的缓存属性对bet子树进行缓存的目的是要保证buffer'中的子树被优先处理,因为bet树总是按照先左后右的次序进行处理和访问,所以27行的合并参数的次序满足了buffer'中的子树被优先处理的要求。图6的处理过程和结果如图7所示,其中结点1是最小尾结点。

每个并联型顶点只被处理一次,即对射入去其中的所有串联分支只形成一颗bet子树,但在图8中,顶点x1或顶点14都需要顶点11的结果。如果顶点11不是最小尾顶点,则build-subtree-3过程中被作为黑色尾顶点处理。如果顶点11是最小尾顶点由build-subtree-1过程处理。

build-subtree-1(u)

图7:adj'1,adj'2,adj'3,adj'4分别对应着图6中的18.root,15.root,23.root,2.root。rtn为2.root,vtn为顶点1。(1)vt为顶点16,15-17、12和25行执行完后,vt为顶点13,vt.buffer中的结果如图示。(2)vt为顶点13,11行执行完后np中的结果如图示。(3)vt为顶点13,15行执行完后np中的结果如图示,这时vt为顶点13。(4)vt为顶点13,16、17、12行然后再15行执行完后np中的结果如图示。接下来再次执行16、17、12行,此时vt==vtn,于是执行25行,np的结果保存在vtn.buffer中。(5)vt为顶点22,11和15-17行执行完后np的结果同23.root,25行执行完后的结果如图示,其结果同样保存在vtn.buffer中。(6)vt为顶点1,11行执行完后np的结果如图示,该结果在24行执行后保存在vtn.buffer中。(7)vt为顶点1,27行执行后的结果如图示。

如果root属性为空则该顶点被首次访问,如果root中已经存在bet树(一般bet的树根为运算符结点)但根结点所对应的顶点名称不是并联型顶点名称则表示第2次访问,不符合上述2种情况的表示2次以上对该顶点的访问。3种情况所对应的处理是,生成bet子树,为bet子树添加缓存顶点,直接返回缓存顶点。第一个处理由build-subtree-3过程完成,而后两个处理由build-subtree-1过程完成。在build-subtree-1过程中,第2行首先找到当前顶点所保存的bet子树的根结点的父亲π,它为把子树重新添加回来做好了准备。如果所处理的并联型顶点u被处理过,则需要借助st结点缓存u.root的结果,最终用并联型顶点所对应的并联型结点替代root属性中的bet子树。第5行中的np是一棵bet子树的树根,缓存的地址由np的右孩子给出,即u的name属性给出。这是可行的因为在对图g前期处理中所有的顶点都被赋予了不同的名字,且并联型顶点名字被赋予了不同的编号。为提高空间效率,并联型顶点的名字所表示缓存地址最终可能需要进一步优化,以使这些地址排列在一起,组成一个连续的空间,但这部分内容不在本方法讨论之列。np左孩子的计算结果将通过st结点保存到右孩子中,当再次需要原来左孩子的结果时,直接给出右孩子即可。6-8行是把5行所新形成的bst树重新加到原来的bst树中,如果原来的子树u.root是右孩子现在还是右孩子,否则就是左孩子。当第2次遇到处理过的并联型顶点时bst树已经比首次处理相同顶点时长大了许多,这时u.root所指向的子树只是其中的一个分支,为了对分支原有的结果进行缓存,5-8行实际做的是将u.root所指向的子树剪下来,通过st结点嫁接上以当前顶点名称为缓存地址的缓存顶点,再接回到原来的bet树中。第9行使u.root指向了一个新结点,该结点所包含顶点的名称透露了缓存地址的信息。缓存结点的应用可以理解为是对bst的优化,因为当多次使用同一棵子树的结果时可以只进行一次真正的表达式的计算,其计算结果在以后可以多次直接使用,不必重复处理。这种优化主要由上述过程完成。第3、4行处理的是如果π本身是一个st结点,则不用为子树添加缓存,因为缓存已经存在了,这时只需要返回缓存地址即可,该地址是当前结点右孩子所包含顶点的名字。不难看出,整个过程的运行时间为o(1)。

build-subtree中当u.functiontype为terminal时需要进行结点的位置调整,但这时不必维护结点父指针,因为这些结点都是gt结点的孩子,不可能再被其他结点引用。

build-subtree-2(u)

build-subtree-2过程用于处理terminal顶点gt,首先第1行对顶点gt中的所有尾顶点按照尾顶点的d属性进行排序,尾顶点通过gt.adj'列表中各子树树根的左孩子获得,sort实际上是对这些尾顶点进行拓扑排序,这样可以确保排在前面的顶点比排在后面的顶点先处理,以便后面的顶点可以利用前面顶点的生成结果,因为gt中的尾顶点可能并不满足顶点覆盖的关系。2-10行对gt.adj'列表中的所有子树进行遍历,如果子树的根结点不是st结点,则需要执行4-7行的"leftrotating"操作,以便把coil类型的顶点从串联分支中分离出来,使其成为左子树计算结果的输出缓存,其过程如图9所示。需要注意的是对coil类型顶点进行合并时所用的运算结点为st,从add-node过程可以看出这一点。

图8:顶点11成为顶点x1和顶点14的共同尾顶点

build-subtree中当u.functiontype为terminal时需要进行结点的位置调整,但这时不必维护结点父指针,因为这些结点都是gt结点的孩子,不可能再被其他结点引用。

build-subtree-2(u)

build-subtree-2过程用于处理terminal顶点gt,首先第1行对顶点gt中的所有尾顶点按照尾顶点的d属性进行排序,尾顶点通过gt.adj'列表中各子树树根的左孩子获得,sort实际上是对这些尾顶点进行拓扑排序,这样可以确保排在前面的顶点比排在后面的顶点先处理,以便后面的顶点可以利用前面顶点的生成结果,因为gt中的尾顶点可能并不满足顶点覆盖的关系。2-10行对gt.adj'列表中的所有子树进行遍历,如果子树的根结点不是st结点,则需要执行4-7行的"leftrotating"操作,以便把coil类型的顶点从串联分支中分离出来,使其成为左子树计算结果的输出缓存,其过程如图9所示。需要注意的是对coil类型顶点进行合并时所用的运算结点为st,从add-node过程可以看出这一点。

图9:yn是一个coil类型的顶点,(2)是(1)leftrotating的结果。

由于在串联分支中coil类型的顶点总是排在最后,所有在bet子树中其对应的结点一定是st结点的右孩子,图9中的a是最小尾顶点,a和b应该进行串联合并后再将结果送入coil类型顶点。8-10行将coil类型顶点和vcoil类型顶点分别进行合并,由于每条串联分支都是独立的输出分支,所以合并借助empty结点进行,该结点表明所合并两分支相互之间没有关系,彼此独立。11行对np子树和np'子树进行合并,np'子树为左孩子表明该子树先于np子树处理。由于“leftrotating”操作和add-node过程的运行时间为o(1),所以2-10行的运行时间为o(vp),第1行的运行时间由对|vp|个顶点进行排序的算法决定,不妨设排序算法的运行时间为o(vplgvp),所以build-sub-tree2过程的运行时间由第1行的排序算法决定,为o(vplgvp)。

build-tree(g)

1np=build-subtree(gt)

2walk-parallel-node(np)

3returnnp

walk-parallel-node(n)

除终止顶点外,图g中所有的并联型顶点都是尾顶点,它们都与各自的bet子树相对应,子树的树根保存在其root属性中。在最终所形成的bet树中每个尾顶点都有相应的由其root属性所指向的结点。build-tree过程是建立终止顶点所对应的bet树,该树也就是最终所需要的bet树,但此时树中还包含有许多尾结点,这些结点需要借助walk-parallel-node过程,递归地构建出与这些结点相对应的bet子树,直到bet树中不存在尾结点。walk-parallel-node过程对bet子树中的所有结点进行先序遍历,该过程的输入参数n为bet的结点,当遍历到某一尾结点时,即如果3行或8行的条件满足时,则需要借助build-subtree过程构建以尾结点所包含的尾顶点为输入参数的bet子树,所以bet树每当遇到尾结点时就会生长。整个过程采用的策略是先构建再遍历,所以在进行进一步遍历时先要判断其左右孩子是否是尾结点,如果是则构建bet子树,然后再沿新子树的树根进行遍历。尾结点的父结点都是运算结点,所以需要第2行的判断。3-7行处理左孩子的情况,8-12行处理右孩子的情况。5,6行与10,11行为新子树的树根添加父结点的信息。7,12,16,17行完成典型的先序遍历功能。13-15行对最小尾结点进行处理,由于最小尾结点是其父结点的右孩子,所以遍历新子树时被最后遍历到,这正好符合尾结点的处理顺序,即最小尾结点必须最后处理,因为它离终止结点最近,但这与执行顺序不同。最终的bet树将按照先左后右的次序执行,所以必须找到最小尾结点的父结点然后左右孩子对调。运算结点and'的使用可以方便我们实现上述任务,15行最后把and'还原成and。当build-tree过程执行完后会返回完整的bet树的树根。如果build-tree(g)过程的输入参数g是图1,则该图所对应的bet树的形成过程如图10所示。

图10为图1所对应的bet树形成的过程。图10中的(10)为原有并联型结点被以灰色结点为树根的子树所代替。

图10中(1)是build-tree过程中第1行执行的结果,然后按照先左后右先序遍历的次序对bet树(1)进行遍历,首先会遇到并联型顶点25,然后调用build-subtree构建该顶点的bet子树,其结果如图7中的(7)所示。接着再对这颗新子树进行遍历,如果遇到并联型顶点就调用build-subtree构建该顶点的bet子树,这一过程递归进行直到bet树中没有并联型顶点为止。图10中的序号表示并联型顶点处理的顺序,浅色顶点被其下方的bet子树替代。由于顶点25被访问2次所以需要添加st运算结点,粗线表示的and运算符是经过leftrotating处理的结果。而顶点25最后所形成的bet树从图7中的(7)所示bet树,变为图10中的(9)所示bet树。图10中的(7)所示bet树多了一个运算符结点,这是初始顶点造成的,多余的结点会通过delete-redundance-node过程将其转换成空结点。

如果图1中的顶点17,23变为function顶点,如图3所示,则其所对应的bet树的形成过程如图12所示。其中(1)是执行build-subtree-2第1行后终止顶点gt的属性adj'的数据结构。(2)是上述过程执行完后所形成的bet树,该树的树根最终在build-tree过程中赋值给np,然后对bet树按照先左后右先序遍历的次序进行遍历。每当遇到并联型顶点就调用build-subtree过程,以便构建该顶点的bet子树,这一过程递归进行直到bet树中没有并联型顶点为止。图12中的序号表示并联型顶点处理的顺序,浅色顶点被其下方的bet子树替代。由于顶点22,16,25被访问多次所以需要添加st运算结点,粗线结点表示包含function顶点的结点,虚线结点表示包含辅助relay顶点的结点。(11)表示最终所形成的bet树。下面说明(9)的bet树的形成过程。

图11:顶点25的adj'属性中的数据结构。其中,粗线结点表示包含function顶点的结点,虚线结点表示包含辅助relay顶点的结点。“+”结点表示对相同尾结点的并联合并。

对于顶点25,经过bfs的处理,其属性adj'的数据结构如图11所示。然后调用build-subtree-3构造顶点25的bet树。最小尾顶点vtn为顶点1,rtn为2.root。开始时尾顶点vt为顶点16,当13行进行判断时条件为真,因为vcoil顶点先于顶点25被处理。接下来运行20行,18.root的右子树保存到顶点1的buffer'属性中。然后执行第7行,尾顶点变为顶点13,通过执行12行和15-17行,尾顶点不断向最小尾顶点移动,并将沿途所遇的并联型顶点串联合并到由np所代表的临时bet子树中。最终将所形成的bet树保存到尾顶点vt的buffer中,这时的尾顶点vt为最小尾顶点1。接下来执行第7行,尾顶点变为顶点23,其处理过程同处理顶点16的过程类似,只是执行20行时,由于此时最小尾顶点的buffer'不空,其结果还需要与顶点16的结果进行并联合并,形成图12(9)所表示的左子树。然后执行第7行,尾顶点变为最小尾顶点1,执行11行后所形成bet树如图12(9)所表示的右孩子的左子树。由于第26的条件为真所以最小尾顶点通过第27行串联合并到bet树中,形成如图12(9)所表示的右子树。第28行将buffer'和buffer的结果进行并联型合并,形成如图12(9)所表示的bet子树。

由于在ldggraph图中串联型顶点只有一条射出边和一条射入边,而并联型顶点之间不能直接相连,所以有|vp|<|vs+2|,于是o(vp)=o(vs),o(e)=o(vs),最后可以得到:o(v)=o(v+e)=o(vs)。

即便我们增加了一些辅助顶点和辅助边(1)式仍然成立,因为辅助relay顶点的数量不会超过边数,辅助function顶点的数量不会超过|vs|。如果不考虑过程build-subtree-2中的排序过程,对于build-tree过程,图g的每个顶点都被遍历到,串联型顶点只被访问一次。作为尾顶点的并联型顶点可以被访问多次,但访问的次数受到其关联边的约束,所以并联型顶点的总访问次数为o(e),同时考虑到构造每个并联型顶点的bet树只进行一次,而构造的主要过程是用串联型结点为叶结点的子树替换并联型顶点,所以对并联型顶点总的处理时间为o(vp+e),于是总处理时间为o(v+e),又根据(1)式,有build-tree过程的处理时间为o(vs),即该过程的处理时间只与串联型顶点个数线性相关。如果考虑过程build-subtree-2中的排序过程,则build-tree过程的运行时间由对gt尾顶点的排序时间决定,排序的运行时间为o(vslgvs),因为o(vp)=o(vs)。

delete-redundance-node(n)

delete-redundance-node过程对bet树进行先序遍历,当遇到冗余结点时通过第3行和第5行将其设置为空结点,并且要保证置空后其左孩子不空,以方便bet树转换成指令表的处理。除叶结点外,当出现左孩子或右孩子为空结点时该结点就被认定为冗余结点。图10(10)和图12(11)中的bet树经过delete-redundance-node过程的处理,结点50的父结点都变成空结点。

至此,由ldgraph转换成bet树的处理全部完成。在得到bet树后可以方便地将其转换成各种指令表,下面给出将bet树转换成符合iec61131-3规范的指令表的方法。

output(n)

output过程对bet树进行中序遍历,遍历过程中根据所遇到结点的性质输出相应的指令码。对右子树进行转换时,如果不是孤立结点需要为指令添加括号。7-9行对所遇结点进行判断,如果是非孤立右子树,并且树根是运算结点and或or,则需要为该子树添加右括号,而左括号已经通过print-instructions过程进行了添加。

print-instructions(n)

在print-instructions过程中,2-14行对右子树树根进行指令码转换,16-28行对左子树树根进行指令码转换。两种转换方法十分类似,只是对右子树树根是非孤立结点并且是运算结点and或or的转换需要添加左括号。print语句如果以“,”结束表示在打印输出完成后加空格但不换行,没有任何结束符号表示打印输出完成后加换行。如何是function类型的顶点则执行7-9行或21-23行,en和eno表示两个缓存地址,分别用于缓存function顶点的输入和输出值。如果主寄存器用a表示则sten表示将a的值放入en,当通过cal指令进行功能调用时,cal指令调用的子过程会首先从en中获取输入,然后执行子过程,最终把运行的结果保存在eno中。ldeno表示将eno的值放入a,于是通过en,en两个缓存器,逻辑表达式与子过程建立了联系。如果是st类型的结点则输出顶点名称,这时顶点的名称就是“st”。如果是空结点则不进行任何打印。如果是relay类型的结点则直接输出顶点的名字。

如图13为指令表,指令表中如果在一行中只出现了一个字符串,如指令表中的粗体显示的情况,则还需要在这种指令字符串前添加“ld"字符串,这一处理比较简单,其具体过程不再赘述。虽然output过程针对的是具体的指令格式,但其方法不失一般性,只需对print-instructions过程中相应的指令格式作修改就可以满足不同指令表的输出要求。

结论:

顶点覆盖概念的引入使我们能够更精确地定义ldgraph图,从而可以更为准确地界定本方法算法的适用范围。ldgraph图可以通过ld-bet算法的6个过程转换成bet树,ldgraph图中的顶点按照功能和连接关系进行分类。对于function顶点需要为其添加辅助顶点,以处理function顶点的非连续性,终止顶点gt的引入使coil类型顶点的处理变得更为方便。在顶点分类的基础上,通过基于深度优先搜索的算法对串联分支上的串联型顶点进行串联合并;通过基于广度优先搜索的算法对射入并联型顶点的尾顶点进行并联合并。每个并联型顶点可能会有多个尾顶点但只有一个最小尾顶点,当所有尾顶点的合并都到达最小尾顶点时,并行顶点的并联合并才算完成。并联合并始于终止顶点gt,合并结果是一棵含有并联型结点的bet树,对该树进行遍历并对所遇到的并联型结点调用并联合并的过程,最终生成一棵不含并联型结点的bet树。生成bet树算法的运行时间为o(vs),把对输出顶点的排序运行时间考虑进去的话运行时间为o(vslgvs)。

bet树形成后,对其进行简单的遍历就可以生成il,本方法虽然给出了生成符合iec61131-3规范的指令表的例子,但依据类似的方法可以生成符合任何规范的il。引入一个栈结构结合对生成的bet树的一个后序遍历,可以直接计算出bet树的逻辑运算结果,从而在实际应用中可以跳过il表的生成过程进行直接计算。

图12:图3所对应的bet树形成的过程。(1)中顶点前的~符号是为便于指明该顶点是一个辅助function顶点,实际顶点的名称是去掉波浪号的名称,所以(1)以后的图中辅助顶点的辅助前缀都被去掉。(9)和(11)中的虚线结点表示其包含的顶点是辅助relay顶点,与图3不同但与实际情况相符,顶点名称没有加辅助后缀“‘”。粗线结点表示其包含的顶点是function结点。(11)中原有并联型结点被以灰色结点为树根的子树所代替。

当前第1页1 2 
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1