程控交换机自动记费系统的开发及应用
摘要:介绍程控交换机与计算机通信的实现方法及其在实践中的应用,简要说明
Windows95的通信原理,并解释Windows95通信及相关API函数在程控交换机自动记费软
件编程中的应用
关键词:通信;原理;API函数;编程
0 前言
目前,我国的电力通信随着电力事业的发展而在迅速发展,许多电力企业均购置
了光进的程控交换机。通信手段的现代化不仅促进了电力事业的发展,而且还为电力
企业的职工安装私人电话提供了便利条件。为此,程控交换机自动记费系统的开发与
应用将显得越来越重要。同时,该系统的开发与应用也必将进一步促进和提高对程控
交换机的管理水平。
程控交换机自动记费系统的开发是建立在串行通信的基础上的,而串行通信是计
算机与计算机、计算机与程控交换机之间有效的通信手段。由于它高效可靠、价格便
宜并且遵循统一的标准,因而得到厂广泛的应用。随着计算机技术的飞速发展,操作
系统也日益复杂和庞大,同时能提供给程序员的编程手段也越来越先进和复杂。本又
试从理论和应用两个方面探讨基于32位操作系统Windows95&NT的串行通信编程方法。
1编程系统
Windows应用程序是一一个以事件驱动、信息传递以Windows对象为组织单位的系
统,在C++中可看作是一个基于对类实例(对象)组织设计编程的系统。本系统采用BC
++5.02版本提供owl5.0进行编程。owl5.0是Borland公司在原来owl2.5版本的基础经过
进一步扩充,并重新加以包装集成的一套软件应用框架。
owl是应用程序框架,主要有如下优点:
(1)程序码少。由于Obiect Windows的各个类是经过专家将Windows API加以组
合包装,而具有OOP(面向对象程序设计)的特性,更便于应用继承的特性,使其程序
码减少。
(2)运行速度快。程序码少,相对地也就执行快。
(3)标准的应用框架。在Object Windows中,各种类的分类及从属关系,不是随
意拼凑而成的,而是按一定的标准加以包装的。
(4)提供了更丰富及更有效率的功能。其功能包括工具条、消息栏、状态栏、工
具籍及更有效的MDI等。
(5)具有C++的Windows API特性。
2 应用程序的构成
Windows应用程序的任务就是构造所需的窗口井且提供给Windows。进行管理,
Windows又为每个窗口调用其窗口函数、处理输入输出以从其它事件,而这一切都是通
过消息来驱动的结果,因而程序设计的主体是在于消息的处理过程的设计。
在Windows的环境下,应用程序的组织实际就是对窗口及其窗口函数的组织,主要
实现构造相应窗口及编制相应窗口函数,在OWL中这些都是通过相应的类来实现的。
本系统有三个主要的类:一是JFApp;二是JFMainWindows;三是JFDecwindows。
这三个类分别继承了基类TApplication,TWindow及TDecoreted Window。这三个类构
成了本系统的基本应用框架。通常,采用TFrameWindows类作为框架窗口的某类,这里
我们采用装饰窗口类TDecoreted Windows作为框架窗口的基类,是为了在框架窗口中
构造工具条、状态条及信息条。客户窗口JFWindows继承Windows类,该窗口包含在框
架窗口内,随框架窗口的变化而变化,窗口函数的设计及构造在客户窗口JFWindwos内
加以实现,也可在TApp应用中买现。由于本系统采用单文档界面,所以,关闭客户窗
口在客观上也就关闭了该系统。
在JFApp类中,定义一个指向TDecoreted Windows类的protected类型的指针变量
decwndw,主要在建立主窗口时使用。还有一个public类型的成员函数为InitMain
Window(),主要功能足建立一个主窗口,其标题栏文字为“MD4110程控交换机自动
记费系统”,其实InitMain Window()是TApplication类下的prottected成员函数,
在此我们将该函数进行重新定义,利用SetMainWindow()建立一个主窗口,SetMain
Window 函数必须接受一指向TDecoratedWindow的指针变量,它是属于TAp-plication
下的Public成员函数。
OwlMain()函数为OWL的主程序,也是程序的入口点此函数执行TApplication类
下的run()函数,run()函数的主要功能如下:
(1)首先调用TApplication下的InitApplication()函数,替第一个实例做初
始化工作。
(2)继而调用 InitInstance()函数,执行所有实例的初始化工作。
(3)如果(1)(2)项的初始化工作皆成功地完成,run()函数继而调用消恩
循环函数MessageLoop(),并执行此应用程序。
其中第(2)项Initlnstance()函数又会调用Init-Main Window()函数(此
函数旨在建立 TDecoratedWindow对象),然后利用TWindow::Create()及TWi-ndow
::Show()来建立和显示主窗口,假使主窗口无法建立,则弹出TXInvalidate Window
()函数作异常处理。当消息循环有异常处理时,run()函数则会撷取此异常处理,
其执行过程如图1所示。
3 串口通信的实现
在主控程序的设计中,关键问题是如何高效地实现交换机与计算机之间通信联系,
因此通信函数的编制及实现是该系统能正常运行的关键。
通常,在DOS系统下的串行通信程序编制往往要对UART(电行通信芯片)进行操作,
通过调用INT14 BIOS中断的方式编程。而Windows系统是一个具有设备无关性的操作系
统,它不鼓励对系统硬件的直接操作,而是由系统自动进行处理。在Window3.1以后,
系统提供户中断方式驱动的通信设备驱动程序Comm.drv和一组专用API通信函数,使我
们可以高效直观地完成串行通信任务。
3.1硬件配置
要实现交换机与计算机之间的串行通信,就必须首先制定出一个硬件的通信协议,
通常是指何种连接方式。根据RS—232C的标准,RS~232上的线2被称为数据发送端
(TXD),线3为数据接收端(RXD),为实现数据的正确传送,终端设备连在线2上的
输出设备称为DTE设备,而调制解调器则必须接在线2接收数据,这样的设备被称为DCE
(DateCommunic-ation Equipmnet)。通常串口之间的连接方式采用下面两种,即调
制解调器连接和零调制解调器的连接。调制解调器连接通常在两台远程PC机之间和远
程终端与市机之间进行通信时使用;而零调制解调器的连接上要在15m以内(RS-232
标准)的采用交叉电缆的通信连接方式时,用于两个DTE设备或两个DCE设备的直接连
接。本系统采用交义电缆的通信连接方式,计算机的端口数据线连接均采用标准的D
TE设备信号方式,其连接如图2所示。
3.2 Windows消息驱动机制
由于Windows系统是一个基于消息驱动的操作系统,因此在Windows环境下进行串
口编程之前,有必要对设备消息处理作一个分析。Windows许多消息均来源于设备。来
自设备的消息首先由Windows的设备驱动程序进行处理,这些设备驱动程序将硬件的事
件转换成消息,并将转换的消息放入Windows系统队列中,而Windows有两种类型的消
息队列:即系统队列和应用程序队列。系统队列只有一个,硬件驱动程序把转换的消
息加入到系统队列,每个运行的Windows程序又有唯一的应用程序队列,W-indows把系
统队列中的消息传送给适当的应用程序队列后,Windows应用程序的消息环从应用程序
队列中检索出相应的输人消息,并将其发送到适当的窗口或窗口函数进行处理。
3.3Windows通信函数
在Windows 3.1中串行通信有一条专用的消息WM-COMMNOTIFY,当有一个串口事件
(接收或发送字符)发生时,通信设备驱动程序Comm.drv将WM-COMMNOTIFY消息发送
给Windows,该消息所携带的参数指出了串口事件的属性(给出相应的串口设备号),
同时标记目前的通信状态。Windows将这个消息放到其对应的应用程序队列中,主控程
序的入口函数OwlMain)得到这个信息后,经过一个消息环的处理,发送给相应的窗口
函数。在Windows 95&NT中,WM-COMMNOTIFY消息已被取消,取代的是操作系统为每个
通信设备开辟的可由用户自定义大小的读/写缓冲区,数据进、出通信口均由操作系统
完成,应用程序只要读/写缓冲区就可以了,这样通信数据丢失的可能性就大大降低。
在Windows95&NT中,对通信口的操作就像对文件的操作一样,串行通信的函数已作了
改进及标准化,其串行设备的打开和关闭与文件的打开与关闭使用相同的函数,同时,
应用程序亦可以通过创建线程来监视各通信设备的状态。
现将常用Windows的32位通信函数介绍如下,供参考:
(1)打开通信端口函数:
HANDLE CrearFile(szDevice,fdwAccess,fdwshareMode,lpsa,fdwCreate,
fdwAttrsAndFlags,hTemplateFile):
SZDevice为通信串口的逻辑名,如COM1或COM2。
fdWAccess指定了端口的访问的类型,如读、写或读写等,本系统可设fdwAccess
=GENERIC.READ。
fdwShareMode指端口的共享属性,文件可供许多应用程序共享,但通信口不行,
必须置0。
Insa引用安全性属性结构,该参数置为NULI,将为通信口分配缺省的安全性属性。
fdwCreate指定如果CreateFile正在被已有的文件调用时应该怎样做。由于串口
总是存在的,fdwCreate就必须置为 OPEN EXISSING。告诉Windows不要创建新端口,
而是打开已存在的端口。
fdwAttsAndFlags描述端口的各种属性。对于串口唯一有意义的设置是FILE FLAG
OVERLAPPED。
TemplateFile是指向模板文件的句柄,当端口处于打开状态时,它必须置为NULL。
(2)关闭通信端口函数:
CloseHandle(hComm),
hComm是CreateFile返回的句柄。
(3)配置端口数函数
ROOL GetcommState(hComm,&dcb);
ROOL SetCommState(hCom.m,&dcb);
这两个函数完成对打开端口参数的获得和配置,dcb是设备控制块DCB的结构,
包含各种选定的参数,用户可以对其进行合理的配置。设置端口参数时,通常是先打
开端口,用GetCommState()函数读取端口参数,在dcb内对参数做合理的必要的修
改,并通过调用SetCommstate()函数来完成这种改变。此外还可以用一个函数Bui
-ldCommDCB()来设定一个带有有限个参数的DCB,它改变的参数与MSDOS中的mode命
令的参数相同。
(4)串行端口的读操作函数
BOOL ReadFile(hComm,inBuffer,nBytes,&nBytesRead,&overlapped):
hComm为打开串行设备的句柄;
inBuffer是数据将要返回的缓冲区,该缓冲区要足够大;
nBytes是要读取的字节数;
nBytesRead是实际要读取的字节数;
overlapped是同步或异步方式。
(5)串行端口的写操作函数
BOOL WriteFile(hComm,outBuffer,nBytes,&nTOWrite,&overlapped):
hComm为打开串行设备的句柄;
outBuffer是数据将要写入的缓冲区,要足够大。
nBytes是要写的字节数;
nToWrte是实际要写的字节数;
overlapped是同步或异步方式。
(6)事件屏蔽设备函数
BOOL,setCommMask(hComm,dwMask):
hComm为打开单行设备的句柄;
dwMask是要等待的一个或多个事件的掩码,表示你要使Windows系统通知你哪类
事件。几个常用的事件为:EV-RXCHAR表示接收到一个字符;EV-RXFLAG表示在DCB
(设备控制块)中定义为EvtChar的字符到达;EV-TXEMPTY表示所有发送队列中的
字符已发送,本系统采用EV-RXFLAG作为标记。
(7)等待事件出现函数
BOOL WaitCommEvent(hComm,&dwEvent,&overlapped):
hComm为打开串行设备的句柄;
dwEvent为函数返回的事件;
overlapped是同步或异步方式。
(8)清除事件屏蔽函数
BOOL,CLearCommError(hComm,&dwErrorMask,&comstat);
该函数有双重目的,正如其名字所表示的那样,一是清除错误条件,二是确定
端口状态,我们感兴趣的是后者。
hComm为打开串行设备的句柄;
dwErrorMask为该函数可能返回的错误掩码;
comstat指向一个COMSTAT结构,此结构保存有关端口状态的有用信息。
其过程是函数从hComm指定的通信设备中清除错误,并获得事件掩码,获得事
件掩码后清除掩码。
(9)清除函数
BOOL PurgeComm(hComm,action):
hComm为打开串行设备的句柄;
action为该函数执行的动作,在本系统中置为PURGE-RXCLEAR。
4 软件编制
记费系统的关键问题在于通信程序是如何处理的,一般来说,有两种方式可采
用,一是“查询”方式,二是“事件”方式。由于活单输出的随机性,本系统采用
后者。即创建一个线程函数以监视通信设备的状态,当通信设备处于信号状态或超
时时,就会给应用程序发出消息,应用程度可利用该消息读取通信设备数据。使用
线程函数对通信设备监视的优点是
5应用实例:
本实例以MD110程控交换机为例。MD110程控交换机具有呼叫信息记录的功能,
称为CIL/SMDR。可以记录任何类型的呼叫信息。每次呼叫的内 容以记录的形式
存储在文件中,通过呼叫信息串行接1收板SIU传送到外部设备(如打印机、计算
机等)进行处理,对于所要记录的内容和输出格式可通过维护终端进行灵活的编
程选择。
(1)呼叫记录信息内容
在每个呼叫信息记录中均包含如下内容:
1)日期;
2)时间;;
3)时长;
4)主、被叫号码;
5)呼叫类型;
6)有权码、由号码等。
(2)呼叫信息记录的准则
即定义哪此呼叫应输出对外部设备上,可在一个或多个文件上定义一个或多个
呼叫准则,该呼叫输出文件可连接到一个成多个外部设备上。可以定 义的呼叫内
容是:
1)输出所有呼叫;
2)按主叫号码输出呼叫作息;
3)按被叫号码输出呼叫信息;
4)输出呼叫时长超过一个规定时间的呼叫;等等。
本实例采用主叫号码输出信息。
3)呼叫信息记录的格式
MD110程控交换机有几种输出格式,可通过人机命令选择其中一种。输出格式
表明呼叫数据应采用那种格式输出,每个输出格式呵被分配给一个成多个输出文件。
1)FP15格式;
2)MDFP15格式;
3)ASB501标准格式;
4)ASBU MDFP15格式。
其中 1),2),4)格式是经过简化后的格式,输出的信息流较短。
本实例采用3)的标准格式输出。具体内容可参看MD110交换机的随机技术手册。
(4)呼叫信息记录串行接口板SIU的电气特性。
SIU板的电气特性符合CCITT的V.24/.28标准,除电气特性外,还可通过人机
命令设置如下接口参数:
1)传输速率;
2)字长;
3)停止位
4)校验位等。
本系统的接口参数必须与上述参数相符合才能正确地完成呼叫信息的接收和处
理。
(5)软件接口的编程
确立传输协议,传输数据采用ASCII模式,传输的数据包是以换行符LF为结尾的
字符流,且长度为一固定值MAXLENGTH。程控交换机是MD110,数据接收为方是AST
adva-ntage!2000计算机。通信参数为:波特率9600,数据位8位,停止位1位,奇偶
无效验,本实例均没有考虑有关函数的错误信息。本实例少作修改即话用于HARRIS
20-20等程
控交换机的记费需要、我局采用本系统已安全稳定运行两年多。
#define EVENTCHAR 0X0a
unisgned long far_stdcall Comm Watch();
HANDLE handle_com,com_thread;
HWND post_hwnd;
static char aPacket[MAXLENGHT};
OVERLAPPED over;
DCB dcb;
COMSTAT comstat;
DWORD dw ThreadID;
int com_mask.com_buff,com_state;
bool fReadStat;
void ProcessApacket(void);//数据包处理函数
void JFMain Window::SetupWindow()
{
TWindow::Setup Window();
handle_com=CreateFile("COM2",GENRIC_READ,0,NULL,OPEN_EXISTING;
FILE_FLAG_OVERLAPPED,NULL);
//打开串口:
com_state=GetCommState(handle_com,
&dcb);//获得当前串口参数;
dcb, BaudRate=9600;
dcb.ByteSize=8;
dcb.Parity=NOPARITY;
dcb.StopBits=1;
dcb.EvtChat=EVENTCHAR;
com_state=SetCommState(hadle_com,
&dcb);//修改后的串口参数再设置;
com_buff=SetuppComm(handle_com,8092,
NULL);//设置接收缓冲区的大小;
if(!SetCommMask(handle_com,EVRXFLAG))//设置事件屏蔽字;
rerurn;
com_thread=Create Thread(NULL,0.
(LPTHREAD_START_ROUTINE)
CommWatch,
NULL,0,&dwThreadID);//创建线程;
post_hwnd=HWindow;
{
unsigned long far_stdcall CommWatch()
{
DWORD dwLen,number;
DWORD dwEvtMask;
if(!SetCommMask(hadle_cim,EV
RXFLAG))
returm false;
while(ture)
{
dwEvtMask-0;
if(WatiCommEvent(handle_com,
&dwEvtMask,&over)//等待EVRXFLAG事件发生;
{
ClearCommError(handle_com,&errorflag,&comstat),//清除端口错误,并确
定端口状态;
dwLen-comstat,cbInQue;
if((dwEvtMask&EV-RXFLAG-ev-RXFLAG&&dwLEN)
{
if(!ReadFile(handle_com,aPacket,dwLen,&number.&over))//从缓冲区中读
取数据;
GetLastError();//检查错误;
else ProcessApacket();//处理数据包
aPacket;
}
}
else GetLastError();
}
PurgeComm(handle_com,PURGE-RXCLEAR);//程序结束前刷新输入缓冲区;
}
|