论文部分内容阅读
摘要:研究了软件PLC中梯形图的逻辑化表达以及编译为指令表的实现方法,对梯形图元素之间的串并联关系进行了深入探讨,在此基础上给出了基于C 的梯形图逻辑表达模型,实现了梯形图到指令表的转换。该实现在可扩展性以及支持复杂梯形图转换方面具有一定优势。
关键词:软件PLC;梯形图;逻辑表达模型
中图分类号:TP273文献标识码:A文章编号:1009-3044(2008)33-1399-03
On Implementing Ladder-diagram Compilation with C in Soft-PLC
YUAN Chun-hua, SHI Jing, BO Zhan-chun
(Dept. of Automobile and Digital Control, Shanghai Xinqiao Vocational and Technical College, Shanghai 201806, China)
Abstract: The logic expression of the ladder diagram (LD) and the realization method of compiling the LD to the instruction list (IL) in soft PLC were studied. The series-parallel connection between the LD elements was thoroughly analyzed. Based on this work, the logic expression model of the LD based on C was given and the conversion from LD to IL implemented. The implementation has certain advantages in its expansibility and in supporting complicated LD conversion.
Key words: soft PLC; ladder diagram; logic expression model
1 引言
工业自动化控制领域的国际标准IEC61131的推出和实施,使得遵循此国际标准,充分利用工控机或嵌入式计算机的软硬件资源,用软件来实现和扩展传统硬件PLC的功能得以实现。
软件PLC以通用操作系统和PC为软硬件平台,用软件实现传统硬件PLC的控制软功能。软件PLC的开发中,编程语言模块的开发是其中的一个关键环节。IEC61131-3标准中提出了5种编程标准语言,分别是:梯形图、功能块图、顺序功能图、指令表和结构化文本。每一种语言都有其优点和适用的人群,梯形图直观明了,为大多数的工程人员所喜用,是编写PLC的首选语言。由梯形图编制的PLC程序一般需要首先经过编译并转换为指令表程序(或进一步编译成特定的字节码)后,才可以加载到相应的软PLC执行系统上运行。
本文分析了梯形图元素间的不同串并联关系,以C 为开发语言实现了梯形图程序到指令表程序的编译转换。
2 梯形图的逻辑表达
实现梯形图的软件化编辑、存储以及编译、执行,首先必须对梯形图模型进行分析,并在此基础上结合软件程序设计的基本原理,构造符合梯形图表达及运行特点的软件表达。基于面向对象编程中继承、数据封装及多态的概念,首先对梯形图中的图形化元素进行数据抽象和行为抽象,然后在此基础上建立起适合梯形图表达的C 类层次图。
为表达梯形图中的不同元素之间的各种连接关系,我们引入“根(Root)”、“集合点(Assembly Point)”以及“分支(Branch)”的概念:从根可以引出多条分支,每条分支由若干元素串联而成,而这些分支或者连接到某一集合点,或者连接到输出节点;汇集在某一集合点的若干条分支之间构成了一种并联关系(如图1所示)。
根和集合点是梯形图中的虚拟元素,它们不代表任何实际的元件或者软触点,但这两类虚拟元素的引入为描述梯形图中元素以及分支之间的复杂连接关系提供了极大的方便。根元素中记录了从该根节点引出的所有分支信息;而集合点元素中则记录了有哪些分支连接到该集合点。分支则是梯形图中的另一个虚拟元素:具有直接串联关系的若干元素构成一个分支。根以及集合点都可以成为分支上的元素;一条分支或者结束于某个根节点或输出节点,或者连接到某个集合点后结束。
梯形图中的元件(如计数器、定时器等)触点、功能块单元以及输出单元都可以看作是梯形图中基本元素。采用面向对象方法对梯形图结构进行抽象,我们定义基类LDElement表征不同元素的最基本属性和方法,同时针对不同种类的元素定义相应的派生类。其相应的C 描述示意如下:
class LDElement{
virtual void compile(LDElement*) = 0; //编译该元素
LDRoot* m_pRoot; //该元素的直接Root节点
LDBranch* m_pParent; //该元素的直接父节点
};
class LDBranch : public LDElement{
virtual void compile(LDElement*);
std::list m_Elements; //分支上的元素
};
class LDRoot : public LDElement{
virtual void compile(LDElement*);
std::list m_Branches; //源自该Root的分支
};
class LDAssemblyPoint : public LDElement{
virtual void compile(LDElement*);
std::list m_Branches; //连接到本集合点的所有分支
};
在设计中充分利用C 标准模版库提供的各种标准容器类(如链表容器类std::list等)可以方便实现梯形图元素节点的动态添加和删除,极大地简化了系统开发。
图1 根、集合点示意图
利用C 面向对象技术,通过将梯形图中的不同类型元素进行抽象,同时引入“根”以及“集合点”两类虚拟梯形图元素,可以使我们能够以一种一致的方式表达梯形图的复杂逻辑关系。在这种逻辑表达下,梯形图的左母线可以看作是整个梯形图的Root节点,而一个完整的梯形图则可以用一个Root节点来表达。事实上,在我们的实现中,梯形图类LadderDiagram直接继承自LDRoot类(如图2所示)。
下面的伪代码展示了遍历整个梯形图基本步骤:
for each(根节点中的分支 LDBranch* pB)
for each(分支中的元素 LDElement* pE)
pE->Visit(); // 访问元素
3 梯形图到指令表的编译转换
梯形图的基本执行策略是自上而下、从左至右逐行扫描,循环执行。为完成梯形图到指令表的转换,最重要的就是正确识别不同元素及分支之间的连接嵌套关系。根、集合点以及分支这三类梯形图组件为存储以及正确识别元素、分支之间连接关系提供了保障。同时,所有梯形图元素都记录了以下两项基本信息:其所属的直接根节点以及分支节点。对于分支,除了记录其所属根节点外,还额外记录了其连接到的集合点信息。
3.1 元素及分支之间的连接关系分析
3.1.1 串联关系
串联关系是梯形图中最基本,也是最容易识别的连接关系:具有串联关系的元素总是属于同一个分支,根以及集合点都可以成为分支上的元素;一条分支或者结束于某个根节点或者结束于某个输出节点,或者连接到某个集合点后结束。
3.1.2 并联关系
梯形图中允许两种不同的并联关系:一种称之为“简单并联关系”,即具有并联关系的若干分支源自相同的直接根节点,同时它们又汇集在同一个集合点。如图3所示,分支{X1}和{X2}之间形成简单并联关系,二者皆源自根R1并在AP1汇合。
另一种称为“嵌套并联关系”,即源自相同根节点的若干分支分别连接到不同的集合点(称之为“同根嵌套”,如图3中{X2}与{X4,X5}两分支之间的嵌套关系)或者源自不同根节点的若干分支汇集在同一集合点(称之为“异根嵌套”,如图3中{X4,X5}与{X6,X7}两分支之间的嵌套关系)。在编译梯形图的过程中,正确识别不同类型的分支并联关系是决定是否需要以及在什么时候插入ORB/ANB等块操作指令的基本依据。
简单并联关系的判断比较简单:由于分支元素中存储了根节点和集合点信息,通过检查两个分支是否连接到同一个集合点并具有相同的根节点可以很容易得到这一信息。而同根嵌套实际上完全可以看作是简单并联关系的泛化,例如图3中分支{X1}和{X2}的并联结果同X3相与之后形成一个新的分支,而这个分支与分支{X4,X5}之间实际上形成了一种简单并联关系。异根嵌套关系则可以通过比较两个连接到同一集合点的分支是否具有相同的直接根节点来完成。
图3 梯形图分支间并连关系增意图
3.1.3 多输出根节点
如果源自某根节点的多个分支最终连接到不同的输出元素,则我们称这个节点为“多输出根节点”。在编译梯形图的过程中,正确识别多输出根节点是决定是否需要使用堆栈指令的基本依据。简单起见,我们在梯形图类中跟踪记录所有的输出元素信息,并通过向上逐级回溯其根节点的方法来判定给定根元素是否为多输出根节点。
3.2 编译梯形图
如前所述,梯形图类直接继承自LDRoot根元素类,该根元素下的各个分支元素逐级展开,从而完整记录了梯形图元素之间的各种连接关系。简单来讲,编译梯形图的过程就是逐级遍历编译根节点下的所有分支元素,各分支元素再逐个遍历编译其包含的元素的过程。
如前所示,抽象基类LDElement定义了统一编译接口compile(LDElement* pPrev),继承自LDElement的各个子类通过该接口方法实现自己的编译过程。
3.2.1 非虚拟元素的编译
编译触点(LDContact)、输出(LDOutput)等非虚拟元素比较简单,一般只要根据元素具体类型给出相应的操作指令即可。编译过程中,需要检查前一个元素(由compile接口参数pPrev给出)是否为NULL以判断与前一元素的串并联关系,并依此对最终操作指令作出选择。
3.2.2 虚拟元素的编译
本文所指的虚拟元素主要包括分支、根和集合点三类。
1) 分支的编译
视具体情况,分支的编译可能由其直接根元素驱动,也可能由其连接的集合点驱动。如果是集合点驱动的(即在编译集合点元素时逆向回溯过程中启动的编译过程),则编译前需要首先检查该分支是否与前一个分支具有相同的直接根元素。如果不同,则表明这两个分支之间是一种异根嵌套的并联关系,则需要先插入一条ANB指令;如果相同,则表明这两个分支之间是一种简单并联关系,可以直接开始遍历并编译该分支上的元素。当该分支上的所有元素都编译完成后,还需要检查是否需要插入ORB指令,这取决于该分支是否为源自根节点的第一个分支以及该分支上是否有多个元素。这一编译过程的伪代码示意如下:
void LDBranch::compile(LDElement* pPrev)
{
// 判断该分支的编译是否为集合点驱动的
if (pPrev != NULL){ // 是集合点驱动的
// 判断是否存在异根嵌套关系
if (pPrev->getRoot() != getRoot()){
addInstruction(INST_ANB); // 异根嵌套关系,需插入一条 ANB 指令
}
}
// 开始依次编译分支上的各个元素
for each LDElement* pE on this branch
pE->compile(pPrev);
if (该分支连接到某个集合点
关键词:软件PLC;梯形图;逻辑表达模型
中图分类号:TP273文献标识码:A文章编号:1009-3044(2008)33-1399-03
On Implementing Ladder-diagram Compilation with C in Soft-PLC
YUAN Chun-hua, SHI Jing, BO Zhan-chun
(Dept. of Automobile and Digital Control, Shanghai Xinqiao Vocational and Technical College, Shanghai 201806, China)
Abstract: The logic expression of the ladder diagram (LD) and the realization method of compiling the LD to the instruction list (IL) in soft PLC were studied. The series-parallel connection between the LD elements was thoroughly analyzed. Based on this work, the logic expression model of the LD based on C was given and the conversion from LD to IL implemented. The implementation has certain advantages in its expansibility and in supporting complicated LD conversion.
Key words: soft PLC; ladder diagram; logic expression model
1 引言
工业自动化控制领域的国际标准IEC61131的推出和实施,使得遵循此国际标准,充分利用工控机或嵌入式计算机的软硬件资源,用软件来实现和扩展传统硬件PLC的功能得以实现。
软件PLC以通用操作系统和PC为软硬件平台,用软件实现传统硬件PLC的控制软功能。软件PLC的开发中,编程语言模块的开发是其中的一个关键环节。IEC61131-3标准中提出了5种编程标准语言,分别是:梯形图、功能块图、顺序功能图、指令表和结构化文本。每一种语言都有其优点和适用的人群,梯形图直观明了,为大多数的工程人员所喜用,是编写PLC的首选语言。由梯形图编制的PLC程序一般需要首先经过编译并转换为指令表程序(或进一步编译成特定的字节码)后,才可以加载到相应的软PLC执行系统上运行。
本文分析了梯形图元素间的不同串并联关系,以C 为开发语言实现了梯形图程序到指令表程序的编译转换。
2 梯形图的逻辑表达
实现梯形图的软件化编辑、存储以及编译、执行,首先必须对梯形图模型进行分析,并在此基础上结合软件程序设计的基本原理,构造符合梯形图表达及运行特点的软件表达。基于面向对象编程中继承、数据封装及多态的概念,首先对梯形图中的图形化元素进行数据抽象和行为抽象,然后在此基础上建立起适合梯形图表达的C 类层次图。
为表达梯形图中的不同元素之间的各种连接关系,我们引入“根(Root)”、“集合点(Assembly Point)”以及“分支(Branch)”的概念:从根可以引出多条分支,每条分支由若干元素串联而成,而这些分支或者连接到某一集合点,或者连接到输出节点;汇集在某一集合点的若干条分支之间构成了一种并联关系(如图1所示)。
根和集合点是梯形图中的虚拟元素,它们不代表任何实际的元件或者软触点,但这两类虚拟元素的引入为描述梯形图中元素以及分支之间的复杂连接关系提供了极大的方便。根元素中记录了从该根节点引出的所有分支信息;而集合点元素中则记录了有哪些分支连接到该集合点。分支则是梯形图中的另一个虚拟元素:具有直接串联关系的若干元素构成一个分支。根以及集合点都可以成为分支上的元素;一条分支或者结束于某个根节点或输出节点,或者连接到某个集合点后结束。
梯形图中的元件(如计数器、定时器等)触点、功能块单元以及输出单元都可以看作是梯形图中基本元素。采用面向对象方法对梯形图结构进行抽象,我们定义基类LDElement表征不同元素的最基本属性和方法,同时针对不同种类的元素定义相应的派生类。其相应的C 描述示意如下:
class LDElement{
virtual void compile(LDElement*) = 0; //编译该元素
LDRoot* m_pRoot; //该元素的直接Root节点
LDBranch* m_pParent; //该元素的直接父节点
};
class LDBranch : public LDElement{
virtual void compile(LDElement*);
std::list
};
class LDRoot : public LDElement{
virtual void compile(LDElement*);
std::list
};
class LDAssemblyPoint : public LDElement{
virtual void compile(LDElement*);
std::list
};
在设计中充分利用C 标准模版库提供的各种标准容器类(如链表容器类std::list等)可以方便实现梯形图元素节点的动态添加和删除,极大地简化了系统开发。
图1 根、集合点示意图
利用C 面向对象技术,通过将梯形图中的不同类型元素进行抽象,同时引入“根”以及“集合点”两类虚拟梯形图元素,可以使我们能够以一种一致的方式表达梯形图的复杂逻辑关系。在这种逻辑表达下,梯形图的左母线可以看作是整个梯形图的Root节点,而一个完整的梯形图则可以用一个Root节点来表达。事实上,在我们的实现中,梯形图类LadderDiagram直接继承自LDRoot类(如图2所示)。
下面的伪代码展示了遍历整个梯形图基本步骤:
for each(根节点中的分支 LDBranch* pB)
for each(分支中的元素 LDElement* pE)
pE->Visit(); // 访问元素
3 梯形图到指令表的编译转换
梯形图的基本执行策略是自上而下、从左至右逐行扫描,循环执行。为完成梯形图到指令表的转换,最重要的就是正确识别不同元素及分支之间的连接嵌套关系。根、集合点以及分支这三类梯形图组件为存储以及正确识别元素、分支之间连接关系提供了保障。同时,所有梯形图元素都记录了以下两项基本信息:其所属的直接根节点以及分支节点。对于分支,除了记录其所属根节点外,还额外记录了其连接到的集合点信息。
3.1 元素及分支之间的连接关系分析
3.1.1 串联关系
串联关系是梯形图中最基本,也是最容易识别的连接关系:具有串联关系的元素总是属于同一个分支,根以及集合点都可以成为分支上的元素;一条分支或者结束于某个根节点或者结束于某个输出节点,或者连接到某个集合点后结束。
3.1.2 并联关系
梯形图中允许两种不同的并联关系:一种称之为“简单并联关系”,即具有并联关系的若干分支源自相同的直接根节点,同时它们又汇集在同一个集合点。如图3所示,分支{X1}和{X2}之间形成简单并联关系,二者皆源自根R1并在AP1汇合。
另一种称为“嵌套并联关系”,即源自相同根节点的若干分支分别连接到不同的集合点(称之为“同根嵌套”,如图3中{X2}与{X4,X5}两分支之间的嵌套关系)或者源自不同根节点的若干分支汇集在同一集合点(称之为“异根嵌套”,如图3中{X4,X5}与{X6,X7}两分支之间的嵌套关系)。在编译梯形图的过程中,正确识别不同类型的分支并联关系是决定是否需要以及在什么时候插入ORB/ANB等块操作指令的基本依据。
简单并联关系的判断比较简单:由于分支元素中存储了根节点和集合点信息,通过检查两个分支是否连接到同一个集合点并具有相同的根节点可以很容易得到这一信息。而同根嵌套实际上完全可以看作是简单并联关系的泛化,例如图3中分支{X1}和{X2}的并联结果同X3相与之后形成一个新的分支,而这个分支与分支{X4,X5}之间实际上形成了一种简单并联关系。异根嵌套关系则可以通过比较两个连接到同一集合点的分支是否具有相同的直接根节点来完成。
图3 梯形图分支间并连关系增意图
3.1.3 多输出根节点
如果源自某根节点的多个分支最终连接到不同的输出元素,则我们称这个节点为“多输出根节点”。在编译梯形图的过程中,正确识别多输出根节点是决定是否需要使用堆栈指令的基本依据。简单起见,我们在梯形图类中跟踪记录所有的输出元素信息,并通过向上逐级回溯其根节点的方法来判定给定根元素是否为多输出根节点。
3.2 编译梯形图
如前所述,梯形图类直接继承自LDRoot根元素类,该根元素下的各个分支元素逐级展开,从而完整记录了梯形图元素之间的各种连接关系。简单来讲,编译梯形图的过程就是逐级遍历编译根节点下的所有分支元素,各分支元素再逐个遍历编译其包含的元素的过程。
如前所示,抽象基类LDElement定义了统一编译接口compile(LDElement* pPrev),继承自LDElement的各个子类通过该接口方法实现自己的编译过程。
3.2.1 非虚拟元素的编译
编译触点(LDContact)、输出(LDOutput)等非虚拟元素比较简单,一般只要根据元素具体类型给出相应的操作指令即可。编译过程中,需要检查前一个元素(由compile接口参数pPrev给出)是否为NULL以判断与前一元素的串并联关系,并依此对最终操作指令作出选择。
3.2.2 虚拟元素的编译
本文所指的虚拟元素主要包括分支、根和集合点三类。
1) 分支的编译
视具体情况,分支的编译可能由其直接根元素驱动,也可能由其连接的集合点驱动。如果是集合点驱动的(即在编译集合点元素时逆向回溯过程中启动的编译过程),则编译前需要首先检查该分支是否与前一个分支具有相同的直接根元素。如果不同,则表明这两个分支之间是一种异根嵌套的并联关系,则需要先插入一条ANB指令;如果相同,则表明这两个分支之间是一种简单并联关系,可以直接开始遍历并编译该分支上的元素。当该分支上的所有元素都编译完成后,还需要检查是否需要插入ORB指令,这取决于该分支是否为源自根节点的第一个分支以及该分支上是否有多个元素。这一编译过程的伪代码示意如下:
void LDBranch::compile(LDElement* pPrev)
{
// 判断该分支的编译是否为集合点驱动的
if (pPrev != NULL){ // 是集合点驱动的
// 判断是否存在异根嵌套关系
if (pPrev->getRoot() != getRoot()){
addInstruction(INST_ANB); // 异根嵌套关系,需插入一条 ANB 指令
}
}
// 开始依次编译分支上的各个元素
for each LDElement* pE on this branch
pE->compile(pPrev);
if (该分支连接到某个集合点