过程控制编程之简要探讨
过程控制编程之简要探讨
发布日期:2009-3-9 0:42:32 浏览次数:3209
抽像一点讲,过程控制渗透在生活的各个角落。特别是在数控行业,尤为明显。早期在PLC时代,几乎是一统天下。当大小不一的控制卡公司如雨后春笋般成长起来时,控制卡在过程控制逐步体现出强劲优势。 其编程手段往往邦定非常成熟的编程工具,如:CB,VC,Dephi,CB。故其推广非常迅速. 很快拥有大量客户群。
但是,作为编程者,碰到同一程序需要控制多个同样设备时(动作不一定同步),往往在Window多任务系统的影响下,很快想到多线程技术,其实多线程编程对系统依赖性较强,不同级别的处理,将导致程序的维护成本增加,并且还需要考虑线程之间的通讯,使编程起来较为麻烦。事实上,依靠过程编程的思想,会使程序获得更简单的做法。
纯粹从编程角度来看,过程控制编程应包含几个要素,下面以C++伪码方式来表示一个类,较易说明问题:
/**************************
一个动作封装类,包含一个
设备的所有执行动作
**************************/
class CAction
{
public:
CAction():m_nWorkStep(-1000){}
~CAction(){}
public:
enum{ None, Inital, Start, Stop, Pause, Continue };//部分控制标记
public:
int Control( int nStatus, void *pParam ); //要素2: 一定可以接受外部控制
int Run( void *pParam ); //要素3: 绝对有一个不断推动的执行过程(这是整个过程编程的心脏所在)
public:
int m_nWorkStep; //要素1: 必须有一个工作执行步
private:
void * m_pMemberData; //要素4: 可选,邦定被操作的数据
// ..其它辅助变量
};
要素5:可选,为了能与外界用户进行沟通,Control函数与Run函数,须选择一个参数,满足用户的输入输出。但由于用户可选项太多,故古人告诉我们,万事皆空即是成功,对于C++而言,没有比void *类型更有前途了(若想体会,需搭配类型转换更有潜力)。早在一年前,本人写过一个过程控制器编程,并有成功实例,但今次版本,更有精装之特点。
住下再参见一下Control函数,及Run函数的一个小样例,这将是程序员发挥强劲功能的场地所在。如下:
int CAction::Control( int nStatus, void *pParam )
{//此处的设计需要一点技巧,当然完全取决程序员的经验,和对控制的理解
switch( nStatus ){
case Inital:
m_pMemberData = pParam;//初始化邦定的用户数据
break;
case Start:
m_nWorkStep = 0; //一般令第0步作为开始较易理解
break;
case Stop:
m_nWorkStep = -1000;//停止
break;
case …//其它略去
default: break;
}
return m_nWorkStep;//返回值可自定
}
int CAction::Run( void *pParam )
{//以下为一个动作不断返回,直至接受到停止指令
switch( m_nWorkStep ){
case 0:
if( IsMove() ) break; //正在忙,不执行
Move(1600); //正向移动1600单位
m_nWorkStep++; //跳至下一步
break;
case 1:
if( IsMove() ) break; //正在忙,不执行
Move(-1600); //反向移动1600单位
m_nWorkStep=0; //再回至第0步
break;
case –1000: break;//停止
default: break;//此句最好有,以便于理解
}
return m_nWorkStep; //返回值可自定
}
对于Run函数须谨记一点:不要有长时间的循环操作,更不要有死循环,否则另外设备无法正确动作,因为另外的设备Run函数无法被激活执行,心脏无法跳动,只好等死吧!
当然,还须担心一点:多个设备同时执行时,其实时性会下降一点,若没有特别高的要求,在现今CPU狂奔的时代,以上程序框架足已满足90%的需求,还是有学习和理解的必要吧!
以上的框架出来了,那么如何操作使设备驱动起来呢,不管在DOS亦或在Window系统环境里,以下的思路都一样,参见伪代码如下:
const int nSize=10; //定义10台设备对象
CAction exeArray[nSize];
BOOL bRunning=FALSE;//控制整个驱动的标志
Void OnRunAction()//启动函数
{
//初始化一下
for( int i(0); i〈nSize; i++)
exeArray[i].Control( CAction::Inital, ( void *)userData );//userData由用户选择
bRunning = true;//外部控制变量
for( i=0; bRunning; i=(i+1)%nSize)//给予运行时间,即是驱动了
{
exeArray[i].Run(( void *)userData); //不断驱动Action设备(即激活心脏)
DoEvents();//做其它事情,如系统消息检测,以防止死机
}
}
DoEvents函数功能的详解可参见本人的〈〈DMC1000控制卡不能响应系统消息〉〉,上面有不同编程工具下的实现源代码。
void OnControlAction()
{
for( int i(0); i〈 nSize; i++)
exeArray[i].Control( Caction::(/*用户想要的动作*/), ( void *)userData);
}
void OnOver()
{
bRunning = FALSE;//结束驱动
}
注意:有很多程序员在不同编译器下,需要不同的OnRunAction函数版本,以上实现最为简单,但有一些较特殊。比如:在程序启动时,就驱动所有设备,这点本人在VC6.0环境有过经验,可以分享一下,其它环境下,还需同胞们一起去努力发现。
还有一点声明,多线程技术也不是一无是处,在花费时间较频繁的通讯编程方面,它的优势,还是有相当的诱惑力。