串口通信在双机冗余系统中应用

(整期优先)网络出版时间:2019-01-09
/ 3

摘要:本文扼要分析了当今带专用检测转换电路的双机冗余系统存在的可靠性问题,提出了利用双机的串行口代替专用检测转换电路,通过串行通信相互检测主备机工作状态,由软件完成备机切换成主机的新型设计思想,为计算机在工业实时监控系统应用中的可靠性开辟新思路。

关键词:双机 冗余 软件 切换

 1、引言

冗余技术是计算机系统可靠性设计中常用的一种技术,是提高计算机系统可靠性的最有效方法,同时也是鉴别各类计算机控制系统好坏的标志之一。故世界各地厂商推出的新产品都或多或少带有冗余技术。如美国的Honeywell、德国的西门子和日本的三武等,它们都有一个共同的特点,就是自己设计了专用检测转换电路来实现主备切换。本文提出一种新的构思,取消专用检测转换电路,采用一种高效、实用的软件冗余技术来完成双机切换。

2、冗余系统介绍

双机冗余包括CPU、扩展电路、电源和外设双备份的全系统冗余,不但可简化设计方案,还可大大地提高应用系统的可靠性。这种冗余系统具有如图1所示的典型硬件结构。

1419239254.jpg

在图1所示的系统中,U1和U2单元的软硬件结构完全相同。如有必要,在设计各单元时,通过采用自诊断技术,软件陷井或Watch dog等系统自行恢复措施可使单元可靠性达到最大限度的提高。系统正常运行时,U1和U2中的一个单元处于正常工作状态(把该单元称为主机),完成应用功能,而另一个单元(备机)处于等待备用状态。当检测转换电路检测到主机不能正常工作时,自动启动备机进入正常运行状态,完成应用功能。此时,可对故障单元进行脱线维护,在排除其故障后,可使其联机进入等待备用状态。显然,这种冗余系统已大大提高了应用系统的可靠性,并基本保证了应用系统的不间断运行。但仔细分析,就会发现它存在以下不足之处:

① 系统存在可靠性瓶颈,当检测转换电路自身出现故障时,不能监视主备机状态,也无法完成主备机自动切换功能;

② 对于某些冗余系统,当备机需要实时保留主机之间的数据备份时,检测转换电路无法完成主备机之间的数据通信功能;

③ 由于需要设计检测转换电路,系统设计和实现复杂,引入了附加的不可靠因素。

3、改进设计方案

针对图1所示的双机冗余系统结构和缺点,笔者实现了一个简单高效、具有更高可靠性和主备机数据通信功能的改进设计方案。其基本设计思想是:不改变上述冗余系统的基本结构,但完全去掉专用的检测转换电路,利用主备机双方的串行口和软件相结合的方法,实现检测转换电路的功能和主备机之间的数据通信功能。在此改进的设计方案中,主备单元的硬件和软件结构完全相同,各单元的主备工作状态由上电顺序决定,先上电的一方自动进入主机工作状态,后上电者则进入备机状态。主机在其工作过程中除实现应用功能外,定期向备机发送反映其工作正常的状态数据,当需要备份的数据发生变化时,主机及时向备机发送已更新的数据。此外,主机也定期接收来自备机的状态数据,当发生接收超时时,主机认为备机已经发生故障,并通过本单元的显示装置向用户给出通知信号,以便及时对备机进行脱线维护。备机在其工作过程中不完成应用功能,但定期接收来自主机的状态数据,当发生接收超时,备机认为主机以经发生故障,自动切换进入主机工作状态,并通过本单元显示装置通知用户,以便对原主机单元进行脱线维护。此外,备机还自动接收来自主机的备份数据并进行存储备份。

4、软件模板

下面的软件模板给出了笔者已实现的双机冗余系统的软件框架,和应用系统实现密切相关的部分用自然语言简单描述,其余部分为Visual C++源代码,因此,这一软件模板很容易移植到相似结构的冗余系统中。

该软件模板以Visual C++进行程序设计,常量OK,REQ,ACK和NACK分别表示主备机工作状态正常,备份数据发送请求,肯定应答和否定应答信息的字符常量,MAIN,STANDBY分别是表示工作单元为主机或备机的常量标志。全局变量timeout用于设定以毫秒为单位的超时间隔,而Update,CpuStatus和Failure分别是表示备份数据是否更新,主备机状态和主备机是否发生故障的标志位变量。

定时器0产生每隔1ms的定时中断(采用QueryPerformanceCounter((LARGE_INTEGER *)&endtime )计数方式),每次中断将全局变量timeout的值减1,减到0时即关闭定时器0。所有需要软件超时机制或延时功能的程序模块都可在设定timeout的值后开启定时器0,并通过判断该变量的值是否为0而实现定时功能。

定时器1产生每隔55ms的定时中断(采用SetTime(55)函数)。每次中断,主备单元的中断服务程序都向对方单元发送表示自身工作状态正常的OK字符,同时也接收对方单元发送的OK字符,若连续三次都未能成功接收,则本单元认为对方单元出了故障,并将表示对方单元工作状态的全局变量Failure置为常量Yes,否则,置为No;若为备机,还将表示主备身份的标志变量CpuStatus的值置为Main,从而为备机程序切换和给出原主机故障提示设置判断标志。若备机在中断服务程序中接收到字符REQ,则置表示备份数据是否更新的标志变量Update为Yes,并立即退出中断服务程序,以便主程序及时接收备份数据帧。

主函数在完成应用系统的公共初使化,主备单元的定时器1在后台产生定时中断并通过串行通信进行相互检测,由于规定两个单元的上电时间间隔大于500ms,因此,利用中断服务程序的检测结果必能自动确定初使的主备身份。主函数延时500ms后,即根据当前的主备身份自动进入主(备)机的前台监控例程,主机的前台监控例程除完成应用功能外,当应用功能改变了需要备份的数据时,则调用send_frame函数关闭定时器1的中断并发送备份数据帧。当备机的前台监控例程判断出需要接收备份数据时,调用receive_frame函数关闭定时器1的中断并接收备份数据帧。当备机的前台监控例程监视到主机故障时,备机的前台监控循环自动切换进入主机的前台监控循环。主函数中的监控切换代码较难理解,应结合并发执行的定时器1的中断服务程序一起分析。

函数send_char和receive_char通过串行口直接发送和接收单个字符。函数send_frame和receive_frame分别发送和接收备份数据帧。VC++中用通讯控件开发串行通信程序,通讯控件的工作原理类似与中断方式,当有通讯事件发生时(如发送数据、接收数据等),就会触发OnComm事件,在该事件的处理函数中调用GetCommEvent()函数,通过返回值即可确定是那类事件,再作出相应的处理。通信成功时返回1,否则返回0。Delay为以毫秒数为调用参数的延时函数。

限于篇幅,本文仅给出完成检测切换功能的主函数和定时器1的中断服务函数,以及相关数据的代码,其它函数仅给出函数原型。代码如下:

#include "stdafx.h"

#include "Try.h"

#include "TryDoc.h"

#include "TryView.h"

#include <windows.h>

#include <stdlib.h>

#include <conio.h>

#include <stdio.h>

#include <string.h>

int timeout;

bool Update,Failure;

char CpuStatus[12],

void delay(unsigned int time);

void CTryDlg::Send_char(unsigned char ch)

{

if(!m_Comm.GetPortOpen())

{

m_Comm.SetPortOpen(TRUE);//打开串口

m_Comm.SetOutput(COleVariant(ch));//发送数据

}

}

void CTryDlg::receive_char(unsigned char * ch)

{

VARIANT m_input1;

if(m_Comm.GetInBufferCount())

{

m_input1=m_Comm.GetInput();//读取缓冲区内的数据

ch=m_input1.bstrVal;//将VARIANT型变量转换为CString型变量

return 1;

}

else return 0;

}

mian()

{和应用系统相关的公共初使化程序段;

Updata=false;Failure=true;

Delay(500);/* 等待与对方建立通信连接,通信由定时器1的中断例程完成 */

If(Failure)/* 在定时器1的中断例程判断出初使主备状态后 */

{CpuStatus=MAIN;和应用系统实现相关的主机初使化程序断;

while(1)/* 主机单元的前台例行监控循环 */

{和应用系统实现相关的主机应用功能例程;

if(应用例程修改了需要备份的数据) Update;

if((Update)&&(!Failure)&&send_frame()) Update=false;

if(Failure){给出备机故障通知信号;Update=true;}

}}

else

{CpuStatus=STANDBY;和应用系统实现相关的备机初使化程序段;

while(1)/* 备机单元的前台例行监控循环*/

{if(CpuStatus= =STANDBY)

while(CpuStatus= =STANDBY)/* 在备机状态中循环 */

{和应用系统实现相关的备机例程;

if((!Failure)&&(Update)&&receive_frame()) Update=false;

}

else

{Update=true;/* 以便在未来的备机联机后再次发送备份数据 */

while(1)/* 切换到主机的前台监控循环中 */

{和应用系统实现相关的主机应用功能例程;

if(应用例程修改了需要备份的数据) Update;

if((Update)&&(!Failure)&&send_frame()) Update=false;

if(Failure){给出备机故障通知信号;Update=YES;}

}}}}}

void CTryDlg::OnTimer(UINT nIDEvent) /* 定时器1的55ms中断服务程序 */

{int count=0;

if(receive_char(&ch)= =NULL)

{count++;

if(count= =3)/* 三次接收字符不成功*/

{count=0; Failure=true;/* 对方单元故障,如果本方为备机,准备切换 */

if(CpuStatus= =STANDBY) CpuStatus= MAIN;

}}

else

{count=0;Failure=false;

if((ch= =REQ)&&(CpuStatus= =STANDBY))/* 如果备机接收到主机的备份数据帧 */

{Update=true;return;} /* 发送请求字符,立即返回,接收此备份数据帧 */

}

send_char(OK);/* 发送本单元工作正常的状态数据字符 */

应用系统和55ms定时相关的例程;

}

5、结束语

以上改进的冗余系统设计具有结构简单,可靠性高,易于实现等显著特点。不足之处是通信协议太简单,因此,系统仅适用于通信质量比较可靠的应用场合,通过引入滑动窗口流协议或其它更为完善的通信协议,可大大提高通信可靠性,从而使系统具有更广泛的适应性。

[1] 俞金寿何衍庆.集散控制系统原理及应用.北京:化学工业出版社.1995.

[2] 史惠康.实用编程技术.中国水利电力出版社.1997.8.