您当前的位置:首页 > 发表论文

汽车维修店通告系统的设计全文

2015-10-09 09:28 来源:学术参考网 作者:未知


第1章  绪  论
1.1  概述
座驾,意即有人坐有人驾驶的代步工具。每天迎着晨曦,眺望满街的车水马龙,汽车像蝗虫一样塞满了街道,汽车已经主宰了交通工具市场。进入二十一世纪以后,面对着进入WTO的巨大契机,中国经济得到了翻天覆地的发展,以往作为体现富人身份的的汽车也开始走入了寻常人家。随着汽车工业的高速发展,各大汽车产商之间的竞争也进入了白热化的阶段,而作为消费者最为关心的售后服务的问题则成为了竞争的焦点,几乎每个商家都推出了属于各自品牌的汽车维修店。促使自己的汽车维修店为消费者所喜爱,最大限度地节约成本,提高生产力,成为了各大厂商追求的目标。
近些年来,汽车各种维修技术都得到了不断的提高,而作为汽车维修店的核心技术汽车维修店通告系统依然存在不合理性。由于客户本身性质的不同和维修时间长短的不一,传统的汽车维修店需要通过不同的通告方式来通知客户,这样的方式不但耗费人力物力,而且通知效率不高,容易出现通知错误的问题。随着汽车维修行业的竞争加大,整个行业急需一个可以通过统一的发布方式而不同的发布途径进行信息发布的平台,针对现有工作中存在的问题进行解决。根据对中国汽车维修店通告系统的现状进行分析,可以清晰地看到集信息整和,信息发布和运营服务为一体的统一平台必然是大势所趋。
传统的通告系统已不能适应当今社会的需要,所以为了满足客户的需求,解决在汽车维修店通告系统中出现的问题,更好地为企业决策提供依据,本文设计了基于Visual C++和Access数据库技术的智能通告系统。结合当前RS-232总线的串口传输,并提出了短信发送及LCD显示、电子邮件终端解决方案。并考虑到汽车维修店LCD显示器通常与主机距离较远,设计出RS-232/RS-485转换器,提高了传输距离,最终达到一个平台控制不同通告方式的目的[1]。克服了传统汽车维修店通告系统通告方式不健全,用户资料保存不易,通知效率不高等诸多问题。
1.2  本课题的研究现状及意义
我国汽车售后服务起步较晚,但发展迅速,其专业化、社会化程度正在迅速提高,目前我国已出现了很多汽车维修公司,竞争日益加剧。提高效率,降低成本,才能在市场竞争中生存下去。
公司如何在日益白热化的竞争中站稳脚跟,就是要实现“专业化、智能化、信息化”,如今大多数汽车维修店多向外国软件公司直接购买通告系统,但高额软件开发费用和语言障碍问题又让企业苦不堪言。开发出拥有自主产权的通告系统迫在眉睫,这不单单能节约企业的生产成本,更能以科学的售后服务理念和现代的信息技术手段实现商家思维的转变和服务机制的重组,使商家在竞争中拥有了属于自己的杀手锏。
本系统采用功能强大的数据库作为信息容器,实现数据采集,记录并跟踪用户的资料,帮助企业的管理人员更快更准确地定位客户所需要地服务。使用标准的工业通讯总线为传输基础,建立整个系统的通讯结构,通过通告方式的上端控制软件,实现不同接口之间的转换,并提出了具体的通告终端方式。最终提高企业的服务质量,赢得消费者的信任。
 
第2章  系统总体方案设计
2.1  系统目标设计
系统开发的总体任务是实现汽车维修店客户资料管理的系统化、信息发布的统一化,从而达到提高企业效率的目的。
2.2  开发设计思想
 
图2-1 程序工作流程图
如图2-1所示,系统流程具体步骤如下:
(1) 当用户汽车入店进行维修,记录客户的基本信息,其信息包括客户的姓名,地址,联系方式以及客户汽车的车牌号码等。以便可以随时调出记录查看。
(2) 在汽车完成维修之后,通过系统数据库中的信息找到指定的用户,并根据实际情况选择合适的通告方式通知客户,可以在各种通告方式中任选其一,也可以几种通告方式同时进行。
通过以上流程,实现汽车维修店通告系统信息的统一管理,统一发布。
2.3  系统功能与模块分析
 
图2-2 系统功能图
如图2-2,软件具有以下功能:
(1) 提高系统的安全性,实现密码登入,防止恶意修改记录。
(2) 软件通过ODBC类连接数据库,实现记录的增加、修改、删除、查询。提供模糊查找和精确查找两大扩展功能,方便用户更快更准确地定位用户。
(3) 实现对数据的文本保存,即可以在不登陆系统的情况下对记录进行查看。
(4) 可以根据实际情况对串口传输进行设定,例如,串口端口的选定、波特率、数据位数、奇偶校验方式、停止位长度、握手方式。
(5) 可以任意输入需要传输的文字,使整个通告信息更为灵活。
2.4  系统传输方案的选择与比较
数据传输的设计有较多的实现方案,下面介绍其中三种,并指出其优缺点。
(1) 以太网传输方案
优点:无传输距离限制,传输速度快,不存在系统接入的计算机数量限制问题。
缺点:企业投入开支较大,软件开发及维护相对困难,客户资料容易泄露。
(2) USB传输方案
优点:传输速度快,支持热拔插功能,使用方便。
缺点:在实际应用中,大多数USB产品也许串联3到4个设备就可能导致一些设备失效。无法达到理论的127个设备,并且USB碰到高电耗的设备,就会导致供电不足的情况。
(3) RS-232传输方案
优点:价格低廉,软件开发较为简单,硬件配置较低。
缺点:只能实现点对点数据传输,传输距离有限。
经过上述比较,三种方案各有优势,但考虑到该系统的设计目的在于最大限度的节约企业开支,所以最终选择了RS-232传输方案,并设计了RS-232/RS-485转换器,提高了系统接入计算机的数量和传输距离,克服了该方案的缺陷。
2.5  编程软件的选择
在软件开发工具上,Visual C++与Visual Basic都可供选择,两种语言都有优势和劣势,并且VB和VC++的功能上差别并不大。
VB的最大的优点就是简单易学,开发项目的时间比较短。VB语法简单,可视化程度高,概念少,一般开发的效率比较高。缺点是编译后的速度较VC++慢,需要庞大的运行时间库支持,软件发行比较困难(编译后的EXE程序如果在没有安装过VB的机器上运行需要拷贝数兆的文件,并且做一些设置工作);底层调用手段较少,不太适合开发硬件操作多、速度要求高的场合。
VB的缺点大多都是VC++的强项,但VC++比较难学,尽管也是Visual的,但很多地方需要手工编写代码,不如VB方便。
针对汽车维修店通告系统需要很强的移植性以及短暂的运行时间的特点,最终选择了Viual C++ 6.0作为其开发工具[2]。
因为Access与Visual C++同为Microsoft Jet数据库引擎,有着最好的兼容性。所以选用Access 2000作为后台数据库,通过VC++的数据库控件连接Access 2000实现各种操作功能。


 
第3章  开发工具的相关技术
3.1  Visual C++ 6.0概述
Visual C++作为一个功能非常强大的可视化应用程序开发工具,是计算机界公认的最优秀的应用开发工具之一。Microsoft的基本类库MFC使得开发Windows应用程序比以往任何时候都要容易。1998年8月,微软推出了VC++ 6.0的版本,进一步加强了部件开发的功能。
提到Visual C++就不得不提到其核心语言C++。C++语言是C语言的扩充。早在1980年,贝尔实验室的Bjarne Strotstrup博士及其同事开始对C语言进行改进和扩充,最初被称为“带类的C”,1983年才取名为C++。之后经过不断完善和发展,成为目前的C++语言。一方面,它将C语言作为它的子集,使它能够与C语言兼容。使许多C语言代码不经修改就可以为C++语言所用以及用C语言编写的众多库函数和和实用软件可以直接用于C++语言中;另一方面。C++语言最大的特点就是支持面向对象的程序设计。
Visual C++6.0不仅是一个C++编译器,而且是一个基于Windows操作系统的可视化集成开发环境。Visual C++6.0由许多组件组成,包括编辑器、调试器以及程序向导AppWizard、类向导Class Wizard等开发工具。 这些组件通过一个名为Developer Studio的组件集成为和谐的开发环境[3]。
3.2  Access 2000数据库概述
作为Microsoft的office套件产品之一,Access已经成为世界上最流行的桌面数据库系统。Access与许多优秀的关系数据库一样,可以让你很容易地连接相关的信息,并且对其他的数据库系统有所补充。它能操作其它来源的资料,包括许多流行的PC数据库程序和服务器、小型机及大型机上的许多SQL数据库。Access还完全支持Microsoft的OLE技术。
Access将所有有名字的东西都成为对象,在Access 2000中,最重要的对象有表,查询,窗体,报表,宏和模块。
3.3  SQL语言的介绍
SQL(Structure Query Language)语言是数据库的核心语言,是一个功能强大的数据库语言。SQL通常使用于数据库的通讯。ANSI(美国国家标准学会)声称,SQL是关系数据库管理系统的标准语言。SQL语句通常用于完成一些数据库的操作任务,比如在数据库中更新数据,或者从数据库中检索数据。使用SQL的常见关系数据库管理系统有:Oracle、Sybase、Microsoft SQL Server、Access、Ingres等等。SQL语言有着非常突出的优点,主要是:(1) 非过程化语言,(2) 统一的语言,(3) 是所有关系数据库的公共语言。
SQL为许多任务提供了命令,其中包括:(1) 查询数据,(2) 在表中插入、修改和删除记录,(3) 建立、修改和删除数据对象,(4) 控制对数据和数据对象的存取,(5) 保证数据库一致性和完整性。
传统的数据库管理系统为上述各类操作提供单独的语言,而SQL语言将全部任务统一在一种语言中。
 
第4章  串口传输方案设计
4.1  串行标准
串行通信接口标准经过使用和发展,目前已经有几种。其中包括RS-485、RS-422、RS-499、RS-232,其中RS-232标准是其他串行标准的基础。
4.1.1  RS-232简介
RS-323标准是美国EIA(电子工业联合会)与BELL等公司一起开发于1969年公布的通信协议。这个标准对串行通信接口的有关问题,如信号线功能、电器特性都作了明确规定。由于通行设备厂商都生产与RS-232制式兼容的通信设备,因此,它作为一种标准,目前已在微机通信接口中广泛采用。
RS是英文“推荐标准”的缩写,232为标识号。RS-232总线标准设有25条信号线,包括一个主通道和一个辅助通道。
RS-232采取全双工通信,仅需几条信号线就可实现,如一条发送线、一条接收线及一条地线。
RS-232标准规定的数据传输速率为每秒50、75、100、150、300、600、1200、2400、4800、9600、19200波特。
RS-232标准规定,驱动器允许有2500pF的电容负载,因此通信距离将受此电容限制,例如,采用150pF/m的通信电缆时,最大通信距离为15m;若每米电缆的电容量减小,通信距离增加。传输距离短的另一原因是RS-232属单端信号传送,存在共地噪声和不能抑制共模干扰等问题,因此一般用于20m以内的通信[4]。
Window95/98/NT/XP最多支持4个串口,COM1,COM2,COM3和COM4。
4.1.2  RS-485简介
在要求通信距离为几十米到上千米时,广泛采用RS-485串行总线标准。RS-485采用平衡发送和差分接收,因此具有抑制共模干扰的能力。加上总线收发器具有高灵敏度,能检测低至200mV的电压,故传输信号能在千米以外得到恢复。 RS-485采用半双工工作方式,任何时候只能有一点处于发送状态,因此,发送电路须由使能信号加以控制。RS-485用于多点互连时非常方便,可以省掉许多信号线。应用RS-485可以联网构成分布式系统,其允许最多并联32台驱动器和32台接收器。
RS-485与RS-232的比较及特点:
(1) RS-485的电气特性:逻辑“1”以两线间的电压差为+(2-6)V表示;逻辑“0”以两线间的电压差为-(2-6)V表示。接口信号电平比RS-232降低了,不易损坏接口电路的芯片,且该电平与TTL电平兼容,可方便与TTL电路连接
(2) RS-485的数据最高传输速率为10Mbps。
(3) RS-485接口是采用平衡驱动器和差分接收器的组合,抗共模干能力增强,即抗噪声干扰性好。
(4) RS-485接口的最大传输距离标准值为4000英尺,实际上可达3000米,另外RS-232接口在总线上只允许连接1个收发器,即单站能力。而RS-485接口在总线上是允许连接多达128个收发器。即具有多站能力,这样可以利用单一的RS-485接口方便地建立起设备网络[5]。
4.1.3  RS-232标准连接器 
本系统采用ATX结构的个人PC机作为操作平台,由于个人PC机很少配备RS-485连接器,而多采用RS-232连接器。因此本系统软件设计上都是以RS-232为基础,下面是对RS-232标准连接器的介绍。
RS-232并未定义连接器的物理特性,因此,出现了DB-25、DB-15和DB-9各种类型的连接器,其引脚的定义也各不相同。其中仅有IBM PC和IBM PC/XT机支持DB-25。如图4-1,在AT机出现及以后,均使用DB-9连接器作为主板上COM1和COM2两个串行接口的连接器。它只提供异步通信的9个信号。
 
图4-1 串口针脚定义
(1) DCD  载波检测        数据链路连接
(2) RXD  接收数据         DTE(数据终端设备) 接受串口数据
(3) TXD  发送数据         DTE准备就绪
(4) DTR  数据终端准备好   DTE准备就绪
(5) SG   信号地线         公共信号地
(6) DSR  数据准备好       DCE(数据通信设备)准备就绪,可以接受
(7) RTS  请求发送         DTE准备DCE请求发送
(8) CTS  清除发送         DCE已切换到接受模式
(9) RI    振铃指示         通知DTE有远程呼叫
4.2  串行传输参数
串口用于ASCII码字符的传输时通常使用3根线完成:地线,发送,接收。由于串口通信是异步的,端口能够在一根线上发送数据同时在另一根线上接收数据。其他线用于握手,但是不是必须的。串口通信最重要的参数是波特率、数据位、停止位和奇偶校验位。
(1) 波特率:这是一个衡量通信速度的参数。它表示每秒钟传送的bit的个数,波特率和距离成反比。
(2) 数据位:这是衡量通信中实际数据位的参数。当计算机发送一个信息包,实际的数据不会是8位的,标准的值是5、7和8位。标准的ASCII码是0~127(7位)。扩展的ASCII码是0~255(8位)。如果数据使用简单的文本(标准ASCII码),那么每个数据包使用7位数据。
(3) 停止位:用于表示单个包的最后一位。典型的值为1,1.5和2位。由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。适用于停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率同时也越慢。
(4) 奇偶校验位:在串口通信中一种简单的检错方式。有四种检错方式:偶、奇、高和低。当然没有校验位也是可以的。对于偶和奇校验的情况,串口会设置校验位(数据位后面的一位),用一个值确保传输的数据有偶个或者奇个逻辑高位。这样使得接收设备能够知道一个位的状态,有机会判断是否有噪声干扰了通信或者是否传输和接收数据是否不同步。
(5) 握手方式:对于数据传输,双方必须对数据定时采用使用相同的波特率。尽管这种方法对于大多数应用已经足够,但是对于接收方过载的情况这种使用受到限制。这时需要串口的握手功能。三种最常用的RS-232握手形式为软件握手、硬件握手和Xmodem。
4.3  串口程序的实现及相关代码
4.3.1  程序总体流程
在MFC环境下,编写32位串口应用程序一般采用两种方法,一种时利用ActiveX控件,即MSComm控件,使用这种方法程序实现非常简单,结构清晰,缺点是欠灵活,可定制性不强;另一种利用API通信函数,它的优点和缺点与使用ActiveX控件正好相反。本软件采用了ActiveX控件方法,总体程序流程如图4-2。
 
图4-2 串口传输程序流程图
4.3.2  MSComm控件及相关代码
MSComm控件从属于ActiveX技术,ActiveX技术是一种共享程序数据和功能的技术。ActiveX技术是Microsoft对OLE技术的更新和发展,Microsoft公司为了适应网络的高速发展把它的OLE技术和OCX技术融为一体并加以改进形成联合标准,ActiveX指的是一组包括控件、DLL和ActiveX文档的组件,通常以动态链接库的形式存在,其设计思想是将一个程序嵌入到另一个程序中。借助这种技术使得在一个程序中所创建的信息可以被集成到其它程序所产生的文档中。这样就可使其可以随意地应用到各种场合。
Microsoft Communications Control(以下简称MSComm)是Microsoft公司提供的简化Windows下串行通信编程的ActiveX控件,它为应用程序提供了通过串行接口收发数据的简便方法。MSComm控件通过串行端口传输和接收数据,为应用程序提供串行通讯功能。MSComm控件在串口编程时非常方便,不必去花时间去了解较为复杂的API函数。
在本系统中,添加MSCcomm控件到系统之中,而系统的其他成员类无需做任何改变,这就是ActiveX的优势。在主窗体类头文件中增加MSCcomm对象之后,就可以直接使用MSCcomm类中已经定义完整的函数对串口进行操作[6]。
MSComm 控件有很多重要的属性,其基本属性如表4-1中。
表4-1 MSComm控件的基本属性
CommPort 设置并返回通讯端口号。
Settings 以字符串的形式设置并返回波特率、奇偶校验、数据位、停止位
PortOpen 设置并返回通讯端口的状态。也可以打开和关闭端口。
Input 从接收缓冲区返回和删除字符。
Output 向传输缓冲区写一个字符串。

在系统通信设备配置上并没有使用现成的ActiveX控件,而是使用更为简单的CommConfigDialog函数来完成。
CommConfigDialog的定义:
BOOL  CommConfigDialog( 
LPTSTR lpszName,           //指定的串口;
HWND hWnd,               //窗口的句柄;
LPCOMMCONFIG lpCC);       //LPCOMMCONFIG结构;
CommConfigDialog是弹出系统内置串口设置对话框的API函数,在使用CommConfigDialog函数之后,弹出设置通信设备属性的通用对话框,该函数显示了如图4-3的对话框,可以从中设置波特率、数据位、奇偶校验方法、停止位和流控制[7]。
 
图4-3 串口参数设置图
表4-2 传输功能选择表
短信发送 COM1 使用MSCcomm控件
LCD COM2 使用MSCcomm控件
邮件发送 调用默认邮件客户端 调用ShellExecute函数
如表4-2,采用PC机的COM1,COM2分别作为短信发送器与LCD显示器的传输接口,而邮件发送则使用ShellExecute函数调用默认的邮件客户端,通过VC++提供的复选控件,可以对三种通告方式进行自由的选择。
ShellExecute函数不仅可以运行EXE文件,也可以运行已经关联的文件。
ShellExecute函数原型及参数含义如下:
Function ShellExecute(
hWnd: HWND;            //用于指定父窗口句柄
Operation,                //用于指定要进行的操作
FileName,               //用于指定要打开的文件名、要执行的程序文件名
Parameters,Directory: PChar  //若FileName参数是一个可执行程序,此参数指定命令行参数. 否则此参数应为nil或PChar(0)
ShowCmd: Integer);        //若FileName参数是一个可执行程序,则此参数指定程序窗口的初始显示方式,否则此参数应设置为0
若ShellExecute函数调用成功,则返回值为被执行程序的实例句柄。若返回值小于32,则表示出现错误。如果将FileName参数设置为“mailto:”协议格式,那么该函数将启动默认邮件客户程序,如Microsoft Outlook或Netscape Messanger。若用户机器中安装了多个邮件客户程序,则该函数将根据Windows 9x/NT注册表中mailto协议处理程序的设置确定启动哪个邮件客户程序。
下面给出了传输程序代码的部分清单:
void CSCommTestDlg::OnBut tonManualsend()
{UpdateData();                           //读取界面信息
if(m_email==TRUE)
{CString a ;a="mailto:";a+=m_memail;a+="?subject=客户通知";a+="&Body=";a+=m_strTXData;
ShellExecute(NULL,"open",a,NULL,NULL,SW_SHOW);  //启动邮件客户程序
if(m_ctrlComm.GetPortOpen())
m_ctrlComm.SetPortOpen(FALSE);
if(m_message==TRUE)
{m_ctrlComm.SetCommPort(1);}                       //选择COM1
if(m_screen==TRUE)
{m_ctrlComm.SetCommPort(2);}                      //选择COM2
if( !m_ctrlComm.GetPortOpen())
m_ctrlComm.SetPortOpen(TRUE);                    //打开指定串口
else
{AfxMessageBox("不能打开串口!");
return;}
m_ctrlComm.SetInputMode(1);             //表示以二进制方式检取数据
m_ctrlComm.SetRThreshold(1);             //参数1表示每当串口接收缓冲区中有多于或等于1个字符时将引发一个接收数据的OnComm事件
m_ctrlComm.SetInputLen(0);              //设置当前接收区数据长度为0
m_ctrlComm.GetInput();                  //先预读缓冲区以清除残留数据UpdateData(TRUE);
m_ctrlComm.SetOutput(COleVariant(m_strTXData));//发送数据
AfxMessageBox(_T("数据传送完毕!"));
if(m_ctrlComm.GetPortOpen())
m_ctrlComm.SetPortOpen(FALSE);
}

 
第5章  数据库模块的实现
汽车维修店通告系统在商家日复一日的商业活动中收集了大量的信息,系统除了记录客户详细情况,还要求能任意修改数据库,所以数据库软件必须将数据以一种组织良好的数据结构保存起来,并提供一系列有效的获取和更新数据的方法。但这仅仅是开发数据库管理系统的一部分,还需要将编写好的数据库连接到应用程序之中。
如图5-1,使用Accesss 2000数据库设计出Password和Person两张表,并通过ODBC类连接到应用程序。
 
图5-1 管理程序实现流程
5.1  数据库的建立
数据库分为关系数据库和面向对象数据库。两者之间主要的差别是它们在处理数据时有着概念性的不同。关系数据库的数据时围绕一些基本的数据库来组织的,并且不允许用户创建新的数据类型。面向对象数据库则在更高的级别上来处理数据类型,它允许创建任何可定义的数据类型,这些对象是使用面向对象编程语言创建的对象的镜像[8]。Access2000也是一种关系数据库,这是因为关系数据库发展比较早,也是当前应用最为广泛的数据库。
在本系统中,采用向导创建表的方法创建password和person两个表,前者如表5-1,用于系统密码登入。后者如表5-2,用于客户资料的查询。
表5-1 Password表
PASSWORD
记录登入密码
此表设定了PASSWORD的字段,但并不一定需要设定PASSWORD的值,因为它在应用程序中作为密码查询,如果PASSWORD没有值,则系统采用无密码登入。
表5-2 Person表
ID 记录客户的ID号,用于识别用户
NAME 记录客户姓名
SEX 记录客户的性别
RELATION 记录客户与车主的关系
TELEPHONE 记录客户的联系电话
HANDPHONE 记录客户的手机号码
ADDRESS 记录客户的住宅地址
WORKPLACE 记录客户的办公地址
EMAIL 记录客户的电子邮箱
CAR 记录客户的车牌号码
表5-2详细地记录了客户的资料,在创建表的同时并不需要给每一个字段赋值,原因在于可以通过应用程序连接数据库,从而修改每一个字段的值。

5.2  连接数据库
数据库的出现使本系统记录客户信息的工作变得相对容易很多,使用Visual C++可以有效地将数据库和面向对象得程序设计方法有机地结合起来,形成强大的Windows程序。
MFC提供了对数据库编程的强大支持。对于数据库的访问,MFC提供了两组类:ODBC(Open Datebase Connectivity)和DAO(Datebase Access Object)。两者使用的数据引擎并不相同,但是都可以满足用户编写独立于DBMS的应用程序的要求。其中ODBC发展较早,应用也更为广泛,本系统采用ODBC类进行软件开发[9]。
ODBC(Open Database Connectivity,开放数据库互连)是微软公司开放服务结构中有关数据库的一个组成部分,它建立了一组规范,并提供了一组对数据库访问的标准API。这些API利用SQL来完成其大部分任务。
不论是FoxPro、Access还是Oracle数据库,均可用ODBC API进行访问。由此可见,ODBC的最大优点是能以统一的方式处理所有的数据库。
一个完整的ODBC由下列几个部件组成:
(1)应用程序。
(2) ODBC管理器。该程序位于Windows系统的控制面板的32位ODBC内,其主要任务是管理安装的ODBC驱动程序和管理数据源。
(3) 驱动程序管理器(Driver Manager)。驱动程序管理器包含在ODBC32.DLL中,对用户是透明的。其任务是管理ODBC驱动程序,是ODBC中最重要的部件。
(4) ODBC API。
(5) ODBC 驱动程序。是一些DLL,提供了ODBC和数据库之间的接口。
(6) 数据源。数据源包含了数据库位置和数据库类型等信息,实际上是一种数据连接的抽象。
各部件之间的关系如图5-2:
 
图5-2 ODBC关系图
    在ODBC中,ODBC API不能直接访问数据库,必须通过驱动程序管理器与数据库交换信息。驱动程序管理器负责将应用程序对ODBC API的调用传递给正确的驱动程序,而驱动程序在执行完相应的操作后,将结果传给应用程序。
5.3  设置ODBC数据源
    应用程序要访问一个数据库,首先必须用ODBC管理器注册一个数据源,管理器根据数据源提供的数据库位置、数据库类型及ODBC驱动程序等信息,建立起ODBC与具体数据库的联系。这样,只要应用程序将数据源名提供给ODBC,ODBC就能建立起与相应数据库的连接[10]。
设置ODBC数据库具体如下
(1) 在本软件中用Access建立一个数据库,数据库名为addresslist。
(2) 在控制面板里找到“odbc数据源”图标,打开数据源管理器的交互界面,如图5-3
 
图5-3 数据源配置图一
(3) 如图5-4,根据要创建的数据源选择一个驱动程序,本文的数据库文件是用microsoft access创建的,所以要选择“microsoft access driver (*.mdb)”

 
图5-4 数据源配置图二
如图5-5,按“完成”按钮后,进入一个标题为“odbc microsoft access 安装”的界面,在其中设置“数据源名”为“addresslist”,选取数据库文件,然后确定即可。
 
图5-5 数据源配置图三
(4) 如图5-6,通过以上步骤完成了一个简单的odbc数据源的注册。数据源就显示在“ODBC数据源管理器中”了。
 
图5-6 数据 源配置图四

5.4  ODBC类
MFC的ODBC类对较复杂的ODBC API进行了封装,提供了简化的调用接口,从而大大方便了数据库应用程序的开发。程序员不必了解ODBC API和SQL的具体细节,利用ODBC类即可完成对数据库的大部分操作。
MFC的ODBC类主要包括:
CDatabase类:主要功能是建立与数据源的连接。
CRecordset类:该类代表从数据源选择的一组记录(记录集),程序可以选择数据源中的某个表作为一个记录集,也可以通过对表的查询得到记录集,还可以合并同一数据源中多个表的列到一个记录集中。通过该类可对记录集中的记录进行滚动、修改、增加和删除等操作。
CRecordView类:提供了一个表单视图与某个记录集直接相连,利用对话框数据交换机制在记录集与表单视图的控件之间传输数据。该类支持对记录的浏览和更新,在撤销时会自动关闭与之相联系的记录集。
CFieldExchange类:支持记录字段数据交换,即记录集字段数据成员与相应的数据库的表的字段之间的数据交换。该类的功能与CDataExchange类的对话框数据交换功能类似。
CDBException类:代表ODBC类产生的异常。
概括地讲,CDatabase针对某个数据库,它负责连接数据源;CRecordset针对数据源中的记录集,它负责对记录的操作;CRecordView负责界面,而CFieldExchange负责CRecordset与数据源的数据交换[11]。
Cdatabase类、CRecordset类和CRecordView类的具体用法如下:
5.4.1  Cdatabase类
要建立与数据源的连接,首先应构造一个CDatabase对象,然后再调用CDatabase的Open成员函数。Open函数负责建立连接,其声明为
virtual BOOL Open(
 LPCTSTR lpszDSN,
 BOOL bExclusive = FALSE,
BOOL bReadOnly = FALSE,
LPCTSTR lpszConnect = “ODBC;”,
 BOOL bUseCursorLib = TRUE );
throw( CDBException, CMemoryException );
参数lpszDSN指定了数据源名(构造数据源的方法将在后面介绍),在lpszConnect参数中也可包括数据源名,此时lpszDSN必需为NULL,若在函数中未提供数据源名且使lpszDSN为NULL,则会显示一个数据源对话框,用户可以在该对话框中选择一个数据源。参数bExclusive说明是否独占数据源,由于目前版本的类库还不支持独占方式,故该参数的值应该是FALSE,这说明数据源是被共享的。参数bReadOnly若为TRUE则对数据源的连接是只读的。参数lpszConnect指定了一个连接字符串,连接字符串中可以包括数据源名、用户帐号(ID)和口令等信息,字符串中的“ODBC”表示要连接到一个ODBC数据源上。参数bUseCursorLib若为TRUE,则会装载光标库,否则不装载,快照需要光标库,动态集不需要光标库。若连接成功,函数返回TRUE,若返回FALSE,则说明用户在数据源对话框中按了Cancel按钮。若函数内部出现错误,则框架会产生一个异常。
要从一个数据源中脱离,可调用函数Close。在脱离后,可以再次调用Open函数来建立一个新的连接。调用IsOpen可判断当前是否有一个连接,调用GetConnect可返回当前的连接字符串。CDatabase的析构函数会调用Close,所以只要删除了CDatabase对象就可以与数据源脱离。
5.4.2  CRecordSet类
CrecordSet类代表一个记录集。需要用ClassWizard创建一个CrecordSet的派生类。ClassWizard可以为派生的记录集类创建一批数据成员,这些数据成员与记录的各字段相对应,被称为字段数据或域数据成员。
要建立记录集,首先要构造一个CrecordSet派生类对象然后调用Open成员函数查询数据源中的记录并建立纪录集。在Open函数中,可能会调用GetDefaultConnect( )和GetDefaultSQL( )函数。函数的声明如下:
CRecordset( CDatabase* pDatabase = NULL);
参数pDatabase指向一个CDatabase对象,用来获取数据源。如果pDatabase为NULL,则会在Open函数中自动构建一个CDatabase对象。如果CDatabase对象还未与数据源连接,那么在Open函数中会建立连接,连接字符串由成员函数GetDefaultConnect提供。
virtual CString GetDefaultConnect( );
该函数返回缺省的连接字符串。Open函数在必要的时侯会调用该函数获取连接字符串以建立与数据源的连接。一般需要在CRecordset派生类中覆盖该函数并在新版的函数中提供连接字符串。
virtual BOOL Open(
UINT nOpenType =AFX_DB_USE_DEFAULT_TYPE,
LPCTSTR lpszSQL =NULL,
DWORD dwOptions =none );
throw( CDBException, CMemoryException );
该函数使用指定的SQL语句查询数据源中的记录并按指定的类型和选项建立记录集。参数nOpenType说明了记录集的类型,如果要求的类型驱动程序不支持,则函数将产生一个异常。参数lpszSQL是一个SQL的SELECT语句,或是一个表名。函数用lpszSQL来进行查询,如果该参数为NULL,则函数会调用GetDefaultSQL获取缺省的SQL语句。参数dwOptions可以是一些选项的组合,若创建成功则函数返回TRUE,若函数调用了CDatabase::Open且返回FALSE,则函数返回FALSE。
virtual CString GetDefaultSQL( );
Open函数在必要时会调用该函数返回缺省的SQL语句或表名以查询数据源中的记录。一般需要在CRecordset派生类中覆盖该函数并在新版的函数中提供SQL语句或表名。
建立记录集后,可以随时调用Requery成员函数来重新查询和建立记录集。Requery有两个重要用途:
(1) 使记录集能反映用户对数据源的改变。
(2) 照新的过滤或排序方法查询记录并重新建立记录集。
5.4.3  CRecordView类
CrecordView(记录视图)是CformView的派生类,它提供了一个表单视图来显示当前记录。可以通过表单试图显示当前记录,通过记录视图可以修改、添加和删除记录。一般需要创建一个CrecordView的派生类并在其对应的对话框模板中加入控件。
5.5  数据库管理系统模块及相关代码
系统主要模块组成如图5-7:
 
图5-7 系统模块图
从上图可以看出,通过建立相关类,可以在成员函数中直接调用已经编写好的类,而不必在主程序中进行重复编写,这就是面向对象设计程序的优势。
下面给出增加、编辑、删除、查询记录等模块的代码清单。


(1) 增加记录模块:
void CAddInfoDlg::OnOK()
{UpdateData(TRUE);                                  //得到输入的内容
CString strSQL,strchkSQL;
int i=1;
int flag=0;
if(!m_name.IsEmpty())                                 //如果姓名填写了
 {while(!flag)
  {CPersonSet m_recordset(&a mp;m_database);
     strSQL.Format("select * from person where ID=%d",i);
  m_recordset.Open(AFX_DB_USE_DEFAULT_TYPE,strSQL);
  if(m_recordset.GetRecordCount()==0)
  {strSQL.Format("insert into personvalues(%d,'%s','%s','%s','%s','%s','%s','%s','%s','%s')",i,m_name,m_sex,m_relation,m_hometelephone,m_handphone,m_address,m_workplace,m_email,m_oicq);
m_database.ExecuteSQL(strSQL);
m_database.Close();
CDialog::OnOK();
flag=1;}                           //将界面上的记录添加到数据库中
i++; }}
Else                                          //如果姓名项为空
{MessageBox("“姓名”项一定要填写!","提示",MB_OK|MB_ICONINFORMATION);
m_ctrlname.SetFocus();}}
(2) 删除记录模块:
void CMainDlg::OnMenuDel()
{int i=m_ctrlperson.GetSelectionMark(); //i是记录中所选的记录号,如果没有则返回
CString strSQL,msg,strname;
strname=m_ctrlperson.GetItemText(i,1);
msg.Format("第 %d 项,姓名为“%s”的记录将被删除!是否继续?",i+1,strname)
f(i==-1)
{MessageBox("请选择一条要删除的记录!","提示",MB_OK|MB_ICONINFORMATION);}
else
{if(MessageBox(msg,"提示",MB_YESNO|MB_ICONINFORMATION)==IDYES)
{CString strname=m_ctrlperson.GetItemText(i,1); //从表中删除对应的记录
strSQL.Format("delete from person where NAME='%s'",strname);
m_database.ExecuteSQL(strSQL);
m_database.Close();
RefreshData();}}}                                           //更新记录
(3) 修改记录模块:
void CModifyDlg::OnOK()
{UpdateData(TRUE);                                     //读取界面信息
CPersonSet m_recordset;
CString strSQL;
CMainDlg dlg;
if(!m_modname.IsEmpty())
{strSQL.Format("update person set NAME='%s',SEX='%s',RELATION='%s',TELEPHONE='%s',HANDPHONE='%s',ADDRESS='%s',WORKPLACE='%s',EMAIL='%s',OICQ='%s' where ID=%d"
,m_modname,m_modsex,m_modrelation,m_modtelephone,m_modhandphone,m_modaddress,m_modworkplace,m_modemail,m_modoicq,m_modid);   //格式化数据库信息
m_database.ExecuteSQL(strSQL);
m_database.Close();
CDialog::OnOK();}
else{
MessageBox("“姓名”项不可以为空!","提示",MB_OK|MB_ICONINFORMATION);}}
(4) 记录查询模块
void CMainDlg::SearchData()
{if(!m_database.IsOpen())                      //首先确保数据库打开
{m_database.Open(_T("addresslist"));} //对列表控件的内容更新,清空原来的内容
m_ctrlperson.DeleteAllItems();                //创建记录集
CPersonSet m_personset(&m_database);
m_personset.Open(AFX_DB_USE_DEFAULT_TYPE,m_searchSQL);
CDBVariant varValue;
char buf[20];                              //用来记录当前记录的序号
int i=0;
if(m_personset.GetRecordCount()!=0)  m_personset.MoveFirst();
while(!m_personset.IsEOF())
{int temp=0;                               //对整型数字的处理
m_personset.GetFieldValue(temp,varValue);
sprintf(buf,"%d",varValue.m_lVal);m_ctrlperson.InsertItem(i,buf); //对字符串显示m_personset.GetFieldValue(1,varValue);
m_ctrlperson.SetItemText(i,1,varValue.m_pstring->GetBuffer(1));
  m_personset.GetFieldValue(2,varValue);
  m_ctrlperson.SetItemText(i,2,varValue.m_pstring->GetBuffer(1));
  m_personset.GetFieldValue(3,varValue);
  m_ctrlperson.SetItemText(i,3,varValue.m_pstring->GetBuffer(1));
  m_personset.GetFieldValue(4,varValue);
  m_ctrlperson.SetItemText(i,4,varValue.m_pstring->GetBuffer(1));
  m_personset.GetFieldValue(5,varValue);
  m_ctrlperson.SetItemText(i,5,varValue.m_pstring->GetBuffer(1));
  m_personset.GetFieldValue(6,varValue);
  m_ctrlperson.SetItemText(i,6,varValue.m_pstring->GetBuffer(1));
  m_personset.GetFieldValue(7,varValue);
  m_ctrlperson.SetItemText(i,7,varValue.m_pstring->GetBuffer(1));
  m_personset.GetFieldValue(8,varValue);
  m_ctrlperson.SetItemText(i,8,varValue.m_pstring->GetBuffer(1));
  m_personset.GetFieldValue(9,varValue);
  m_ctrlperson.SetItemText(i,9,varValue.m_pstring->GetBuffer(1));
  m_personset.MoveNext();
  i++; }                      //在标题栏中显示共有记录条数
int counts=m_personset.GetRecordCount();CString str;
str.Format("4S店通告系统     符合条件的记录数: %d",counts); 
this->SetWindowText(str);}
由于本系统实现的功能较复杂,这里只给出了部分代码,具体的程序代码包含附录之中,这里就没有进行详细的介绍了。
 
第6章  系统硬件设计
如图6-1,汽车维修店通告系统的终端解决方案上采用了短信发送器以及LCD显示屏,并使用PC机的COM1与COM2分别作为终端的传输端口。由于大多数普通PC机只有常用的RS-232串行通信口,并不具备RS-485通信接口。为了实现RS-485协议的串行通信,必须在PC机侧配置设计RS-232/RS-485转换器。
 
图6-1 通告方式选择图
6.1  RS-232/RS-485转换器的设计
 
图6-2 RS-232/RS-485转换器内部框图
如图6-2所示,RS-232/RS-485转换器主要由电源部分、RS-485接口和RS-232接口三部分组成。电源部分采用外部直流电压通过变压稳压电路给两端供电,RS-232接口电路实现COMS电平和TTL电平之间的转换。RS-485接口电路则将TTL电平转化为差分电路,由专门的逻辑控制电路控制其发送及接受。由于整个传输通道的两端均有光电隔离,故无论是PC机还是单片机都不会因数据传输线上可能遭受到的高压静电等的干扰而出现“死机”现象[12]。
6.1.1  电源电路
SP232EEP和SP485EEP电平转换芯片的工作电压均为+5V,如图6-3,系统从RS-485接口Vpp管教直接取10-30V直流电压作为外部电源,通过LM2576T芯片构成的一个变压稳压电路,实现各个芯片的5V直流供电。为了更有效的保护设备和网络,达到抑制电磁干扰和消除接地环路的作用,采用两块B0505LS-1W电源芯片实现了输出输入电源隔离,使产品具有双重防静电防浪涌的功能。
 
图6-3 电源电路
6.1.2  RS-232接口电路
UART是通用异步串行通信接口的总称,UART允许在串行链路上进行全双工的通信,输出/输入的电平为TTL电平。一般来 说,全双工UART定义了一个串行发送引脚(TXD)和一个串行接收引脚(RXD),可以在同一时刻发送和接收数据。
使用UART时,数据位的宽度是由波特率而定,数据发送/接收时序参考图6-4。
 
图6-4 数据发送/接收时序图

由于RS-232是用正负电压来表示逻辑状态,如:逻辑1=-3~-15V,逻辑0=+3~+15V。而TTL以高低电平表示逻辑状态,所以如果要使用UART与RS-232接口的设备进行基本的通讯,就需要一个RS-232转换器接受COMS信号,并其转换成TTL电平,本系统选用常用的SP3232E芯片完成电平转换,电路如图6-5所示[13]。
 
图6-5 串口电路图
6.1.3  RS-485接口电路
在本系统设计中,选用了SP485EEP作为RS-232/RS-485的转换芯片,SP485EEP 是一种半双工差分收发器,包含一个驱动器和一个接收器,专用于TTL协议与RS-485协议间的转换,图6-6为SP485EEP的引脚图。表6-1为其电气特性[14]。
 
图6-6 引脚分布图
表6-1 SP485EEP电气特性
通信
方式 数据率
/Mbps 转换率
限制 低功耗
关机 接受器/
驱动器使能 静态电流/mA 收发器数量 管教数
半双工 2.5 NO NO YES 300 32 8

SP485EEP具有8个管脚,其功能如下:
(1) RO脚(接收器输出):若A比B大200mV,RO为高;若A比B小200mV,RO为低。(RS-485数据信号采用差分传输方式,也称作平衡传输,它使用一对双绞线,将其中一线定义为A,另一线定义为B。)
(2) RE脚(接收器输出使能端) :PRE为低时,RO有效;PRE为高时,RO成高阻状态。
(3) DE脚(驱动器输出使能端) :若DE为高,驱动输出A和B有效;若DE为低,它们成高阻状态,若驱动器输出有效,器件作为线驱动器用;若为高阻状态时,PRE为低,它们作线接收器用。
(4) DI脚(驱动器输入) :DI为低,将迫使输出Y为低,Z为高;若DI为高,将迫使输出Y为高,Z为低。
(5) GND脚:接地。
(6) B脚:反相接收器输入和反相驱动器输出。
(7) A脚:同相接收器输入和同相驱动器输出。
(8) Vcc :电源正极4.75~5.25V[15]。
由于RS-485采用半双工传递,系统必须对传输与接受线路进行必要的控制。,防止数据在传输中出现拥塞。如图6-7,使用CD4093四二输入斯密特触发器结合两块6N136光耦芯片可以实现自动控制传输方向,使传输数据能够顺利地从DN10连接器地A、B端进出[16]。
 
图6-7 RS-485接口电路
6.2  LCD显示屏
LCD(Liquid Crystal Display)称为液晶。液晶得名于其物理特性,它的分子晶体以液态存在而非固态。LCD显示屏的基本工作原理就是通过给不同的液晶单元供电,控制其光线的通过与否,从而达到显示目的。在本系统中,并没有专门设计制作LCD显示器,由于实际使用的大屏幕LCD显示器价格昂贵,如图6-8,系统使用了广州汉显有限公司HZ132-64B30点阵式液晶模块进行实验测试。
 
图6-8 HZ132-64B30液晶模块
6.2.1  产品规格
HZ132-64B30点阵式液晶模块加入了汉显驱动模组,编程时就可以避开液晶的驱动操作和汉字点阵字库的操作,而直接将汉字内码输入液晶模块,LCD显示器就能立即显示出汉字。主要功能如下:
(1) 132×64汉字显示液晶模块,自带一、二级汉字字库,一屏可显示50个汉字。
(2) 自带一、二级汉字12点阵全部汉字字库、中文符号和半角ASC字符。
(3) 全屏幕可以显示每行10个共5行标准宋体汉字。或者20×5个ASC字符,也可以混合显示。字间和行间均有一个点的间距。
(4) 提供多种文本显示命令。
(5) 具备RS-485接口,连线简单。
6.2.1  LCD指令
系统采用串口通信控制LCD显示器,所有的发往LCD命令格式都是以“ESC”键值0x1b开始,后面跟一个字节的命令码,其后再跟具体命令内容。可以通过指令发送完之后延时来保证指令执行完毕,但是这样程序的效率将会降低。发送模块命令格式:0x1b 命令代码 命令内容。
下面为部分操作指令:
表6-2 波特率命令接口
设置波特率
命令 内容
1 byte  1 byte
0x24 0xff=57600bps,0xfe=28800bps,0xfd=19200bps
0xfa=9600bps,0xf4=4800bps,0xe8=2400bps
0xd0=1200bps
如表6-2,设置新的波特率,开机默认波特率为9600bps。
表6-3 写液晶命令接口
在光标位置显示字符串
命令 内容
1 byte  1 byte N byte 1 byte
0x37 属性:
=0正常显示
=1反显  该字符串内容 0x00
如表6-3,这条指令也用来在光标位置显示一个汉字或一个ASC码。
表6-4 清屏指令
清屏命令
命令 内容
1 byte 无
0x32 无
如表6-4,清屏,上位机往模块发送数据串"0x1b,0x32"
6.3  短信发送器
目前可以选择的短信接入方式主要有如下几种:
(1) 专线接入运营商短信网关:企业通过自己的服务器直接接入移动运营商的网络,运营商对于设备和业务有一定的要求,开展的业务需要经过运营商的综合评测,该方式较适用于超大型企业。
(2) 虚拟运营商接入:企业只作为其一个客户,利用虚拟运营商提供的客户端软件或二次开发接口发送短消息。业务内容、服务的质量和信息安全不尽人意,资费较高。
(3) 手机或GSM MODEM无线接入:这种方法是应用程序通过串口利用手机或GSMMODEM收发短消息。成本低,操作简单。
本方案就是采用第三种方式实现的。
6.3.1  短信发送原理
本系统借助TC35i手机模块,SMS系统由以下几部分组成:短信息实体(Short Messaging Entity,SME),手机模块,短信业务中心(ShortMessage Service Center,SMSC),相应的GSM/SGSN网络系统。本系统的短信指令传递路线模拟如图6-9所示。
 
图6-9 短信指令传递路线
从图6-9可以看出,系统所发出短信息实体,通过相应的GSM网络,由短信中心转发到手机模块,连接串口进入主机,再经过SMS短信接口输入系统数据库,最后系统根据短信息指令进行响应。短信的发送流程与此相反。
本系统中的TC35i手机模块与主机的串口相连,主机通过向串口发送AT指令来发送或者接受短信。TC35i手机模块是采用德国西门子公司的产品,是一款双频900/1800MHZ高度集成的GSM模块,可以传送语音和数据,需要外接SIM卡以传送语音和数据,并支持MT、MO、CB和PDU短信息模式,通过RS-232串行口与主机相连进行指令和数据的双向传送。
短信息的收发模式为:Text模式和PDU模式。Text Mode是纯文本方式,一般用于发送英文信息;PDU Mode被所有手机支持,可以使用任何字符集,可发送中英文信息。在PDU模式中,可以采用3种编码方式来对发送的内容进行编码:7bit、 8bit 和UCS2编码。本系统可接收纯文本、中文以及中英文混合短信息,由于PDU模式具有通用性,故系统在收发短信时采用PDU模式。
6.3.2  AT指令
主机和GSM引擎之间采用AT指令实现通信,主机发出的AT指令用来建立通信链路,AT指令集的命令格式帧都以AT开头。
下面是几个常用的AT指令。
(1) 设置短消息中心:AT+CSCA="+8613XXXXXXXXX" (具体的号码由当地的运营商决定) 。
(2) 接收短消息:AT +CMTI:"SM", X(X表示接收短消息的SIM 卡存储号码),AT+CMGR=X(从X存储区读短消息),AT+CMGD=X(从X存储区删除短消息) 。
(3) 发送短消息:AT+CMGF=1(采用文本格式发送,如用PDU格式,则AT+CMGF=0),AT+CMGS="+8613xxxxxxxxx", 输入短消息。Crtl+Z 结束并发送。
通信链路建立以后,主机就可以发出信息单元帧了,其中标志帧开始的帧头为十六进制的10H和02H,标志信息结束的是10H和03H,最后二位是数据部分按16 位求和以后取模的结果,用做校验位。对于数据位,为了避免出现错误的结束标志,规定每出现一个10H码字以后,后面插入一个空字符00H。接收端如果收到信息以后,就要相应地去掉空字符。如果接收端收到的数据部分和BCS部分按照16位相加以后的结果不等于0,就表示传输出现错误,所收到的信息就要丢掉。
 
第7章  系统联机调试
7.1  软件调试
在实现各个功能模块后,将程序连接编译生成可执行文件,并运行系统进行总体测试。
系统启动后,如图7-1首先显示通告系统的登陆界面:
 
图7-1 系统登陆界面
此系统具备了延时20秒登陆的功能,如果用户在20秒内没有登陆系统,整个软件会自动关闭。用户在20秒内输入口令后。系统会查询数据库password表中的PASSWORD字段,如果两者相符,系统允许登陆,反之,系统在接受三次错误密码后,退出整个系统。
成功经过登陆校验,如图7-2为系统主窗体:
 
图7-2 系统主界面
在此窗口中显示了客户的各种资料,并将五个最常用的记录操作放置在界面醒目的位置,方便用户操作。
在菜单区设置了四个菜单。子菜单如下
表7-1 菜单选项
主菜单 子菜单
文件 以文件保存记录,退出系统
编辑 增加记录,修改记录,删除记录,查询记录,COM1配置,COM2配置
帐户 更改口令
帮助 帮助与索引,关于
系统功能界面如下:
 
图7-3 串口配置界面
 
图7-4 文本保存界面
 
图7-5 更改口令界面
 
图7-6 增加记录界面
 
图7-7 修改记录界面
 
图7-8 查询记录界面
 
图7-9 传输信息界面
    如图7-9,选择客户所需要的服务(短信发送,屏幕显示,E-MAIL)后,点击发送数据按钮,,即可进行RS-232数据传输,按照模块默认的串口配置情况,在串口调试助手(串口调试工具,互联网上一个免费共享软件)中选择相应的参数配置,即可在接受栏收到相应的数据,如图7-10所示。
 
图7-10 串口接受界面
7.2  硬件调试
在硬件调试之前,要对整个电路进行检查,用万用表测试硬件电路是否有短路或者虚焊的地方。在检查无误后,才可以进行具体的调试。
7.2.1  电源系统调试
电源部分分为5V稳压电路及电源隔离电路,主要有LM2576、B0505LS-1W这两个芯片及一些电感电容。通电后,首先检查芯片有无明显发热现象,如果有,则应立即断电,进一步检查电路。
如果芯片不发热,并且电源部分的发光二极管如期而亮,则说明这部分电路工作正常,然后用万用表测试LM2576、SB0505LS-1W这两个芯片的输出端的电压值均为5V左右。
经验证,电源电路工作正常,输出电压值分别符合要求,电源系统调试结束。

7.2.2  接口电路调试
RS-232接口这部分电路较简单,一般不会出现问题。RS-485接口部分的电路主要由SP485EEP的电平转换电路及与逻辑控制电路组成,首先对这部分电路上电,检查芯片是否发热。若没有发热,则说明这部分电路基本无误。如图7-11,通过9针串口线将转换器和PC机相连,再将另一个RS-232/RS-485转换器(成品)接入系统之中,即将传输总线重新转为RS-232总线,并传送数据回PC机,进一步,通过计算机上的串口调试助手(COM1发送数据,COM2接受数据)发送数据,若能显示收到预期的数据,则可进一步说明转换器已经能够正常工作。
经验证,此部分电路工作正常。
 
图7-11 串口接受界面

7.3  调试中的有关问题及分析
7.3.1  数据丢包问题
在调试过程中,出现丢包现象,即:模块不能及时接收串口上的数据,而导致源数据丢失现象。如串口设备发送的数据为1、2、3、4、5,经过串口传输后,在调试软件得到的数据仅为3、4、5。
导致上述现象出现的原因有以下几种:
(1) 模块未接收串口来的数据;
(2) 模块接收到了数据,但是出现误码;
(3) 串口发送任务未将所有的数据发送到终端。
经过多次实验,排除了(2)和(3)这两种原因。对于模块未接收串口来的数据,经改分析,得出以下答案:串口接收任务不能及时取得系统资源来完成数据的接收操作,而丢失数据。要解决此问题,只有将串口接收任务的优先级别设为最高,并且考虑在串口有数据的情况下,保证资源不被其它任务占用。
最后,本设计将串口收任务优先级设为0,并在串口接收任务的前后添加中断屏蔽,以保证串口收任务能随时接收串口数据,从而解决了数据丢包问题。
7.3.2  串口参数设置问题
在使用CommConfigDialog函数来提供通用通信设备配置时,修改了波特率,数据位,奇偶校验,停止位,数据流控制,但发现在实际传输中并没有改变。
导致上述现象出现的原因有以下几种:
(1) CommConfigDialog函数并没有修改DCB结构;
(2) 修改的DCB结构没有把结果设置到串口;
针对原因(1),在CommConfigDialog函数之后,编写了一个DCB查看程序,经过测试,发现DCB结构已经更改,由此排除原因(1)。
排除原因(1)后,经过分析,得出下面的结论:CommConfigDialog是弹出系统内置串口设置对话框的API函数,使用这个API时不用先打开端口,它并不针对一个已打开的端口,而是仅仅是把DCB的内容填写到对话框中,当按了OK后把输入的结果存回到DCB数据结构中。
据上述分析,解决问题的方法是:在串口打开后,还需要将设置结构出入串口结构后,出现的问题就解决了。
7.3.3  数据缓冲区溢出问题
在极限测试(串口调试软件长时间接收大量数据)情况下,会出现数据缓冲区溢出,这是由于本设计中为了不出现数据丢包现象,而将串口收任务占用资源的优先级别设为最 高,所以在串口长时间接收大量数据情况下,其它任务不能占用系统资源。
对于这种问题,只有扩展内存,增大数据缓冲区,延长此问题出现的时间,但是不能够从根本上解决。但是在实际的工业场合下,串口数据量及速率有一定的上限,根据具体情况,配置硬件相应的大小内存后,在实际应用中并不会出现此种问题。
7.4  系统的维护与可靠性
软件维护阶段覆盖了从软件交付使用到软件被淘汰为此的整个时期,它是在软件交付使用后,为了改正软件中隐藏的错误,或者为了使软件适应新的环境,或者为了扩充和完善软件的功能或性能而修改软件的过程。
由于本设计的调试过程都在实验室中进行,并未在实际应用中实测。所以对相关干扰并未考虑到,仅仅设计完成功能上的实现和调试。在投入到实际应用或将其产品化之前还需要进一步的完善和努力。
在实际的调试过程中会遇到很多困难和问题,这要根据具体情况加以解决,在此,只是把遇到的主要问题加以分析。

 
结    论
基于Visual C++与Access 2000的汽车维修店通告系统,可以对所有客户进行统一的管理,统一发布通告信息,并使现有的串口设备立即联入系统使用,不但解决了以往系统可靠性,兼容性差等问题,而且为整个汽车维修店提出了一种新的管理概念。
本文中设计的汽车维修店通告系统只是一个初步设计,在将来的实际应用中,还有三个主要问题需进一步解决:
(1) 更加方便快捷的管理软件
在系统的使用中,使用本设计中的主管理界面来显示客户的信息并不完整,需要进一步编写方便快捷的人机界面,能够在系统上对客户的更多细节进行更详细的管理。
(2) 支持更多的传输方式
对原有设备基于串口通讯的管理软件的支持,可以在信息传输上采取更多的传输方式,例如通过USB与TCP/IP协议的网络管理,不必重新设计管理软件,从而减少改造的任务量。
(3) 通信稳定性和可靠性
尽管系统提供了串口传输,但是客户并不能很清楚地了解传输进度以及传输中出现地问题和问题的原因,编写更详细地传输界面将有助改善于这一问题的重要手段。
上面只是介绍了在将来应用中会遇到的几个主要问题,而根据不同的使用环境以及不同的用户要求,会遇到更多的问题,这需要进一步去解决。

致  谢
本课题及学位论文是在我的导师刘婷婷与何桥的悉心关怀和精心指导下完成的。他们严肃的科学态度,严谨的治学精神,精益求精的工作作风,深深地感染和激励着我。从课题的开始到最终完成,我不仅学到了扎实、宽广的专业知识,也学到了许多做人的道理。在我的课题开展过程中倾注着导师辛勤的汗水和心血。导师的为人师表、渊博的知识、宽广的胸怀让我倍受教益,在此谨向导师们致以诚挚的谢意和崇高的敬意!
我能顺利地完成学业,与父母多年来一如既往的支持和关怀是分不开的,在此,向任劳任怨、含辛茹苦的父母致以衷心的感谢!
衷心感谢在我成长的路上指点和帮助我的前辈和朋友们!
衷心感谢在百忙之中评阅论文和参加答辩的各位专家、教授!

 
参考文献
[1] 苏岳龙,李贻斌.基于VC++6.0的高速串口通信数据采集系统[N].微计算机信息,2005-5.
[2] 张力.Visual C++高级编程[M].北京:人民邮电出版社,2004:403-431.
[3] 何健辉,董方鹏.实用Visual C++ 6.0教程[M].北京:清华大学出版社,2000:1-100.
[4] 段吉祥,李志华.一种RS-232多点通信系统的实现方法[N].工矿自动化,2007-7(B1).
[5] 李现勇.Visual C++串口通信技术与工程实践[M].人民邮电出版社,2004:200-211.
[6] 金法华.一种新的串口软件狗的设计与实现[N].淮阴师范学院学报,2005,(03).
[7] Jan Axelson.Serial.port complete[M].Lakeview Research,1999:2-19.
[8] 戴梅萼,史嘉权.微型计算机技术与应用[M].北京:清华大学出版社,1996.100-144.
[9] Robert Signore, The ODBC Solution:Open Database Connectivity in Distributed Environments [M].McGraw-Hill, Inc. 1995:2-60.
[10] Peter Harvey,Open Database Connectivity[M].Specialized Systems Consultants,Inc.1999:2-40.
[11] 官章全,刘加明.Visual C++ 6.0类库大全[M].北京:电子工业出版社,1999:505-581.
[12] 刘武光.RS-232至RS-485/422接口的智能转换器[J].电子技术应用,2002 (5):46-48.
[13] 张慰兮.微型计算机(MCS-51系列)原理、接口及应用[M].南京:南京大学出版社, 1999-1:2-55.
[14] 樊俊峰,尹斌.简易RS-232/RS-485智能转换器[J].通信技术,2002 (5):52-54.
[15] 陈新忠.基于RS48总线的单片机多机通信软件设计[J].现代电子技术, 2002,(03).
[16] Martin D.Seyer,Complete guide to RS-232 and parallel connections:a step-by-step approach to connecting computers,printers,terminals,and modems[M].Prentice-Hall,Inc.1988:55-89.
[17] Rick Lehrbaum,Focus on Embedded Systems[M] Specialized Systems Consultants.2001:1-20.
[18] 刘树中,王春平.单片机和液晶显示驱动器串行接口的实现[N].微计算机信息,2007-2.
[19] 李毅.一种微处理器和液晶显示驱动器之间串行接口实现的新方案[N].电子技术,2003,(12).
[20] 郑丰隆.新型二线AT24C系列串行E~2PROM及其在单片机系统中的应用[N].微电脑世界,1995,(01).
 
附录1  部分源代码
//功能:通告系统主界面
#include "stdafx.h"
#include "Address.h"
#include "MainDlg.h"
#include "PersonSet.h"
#include "ChangePswDlg.h"
#include "AddInfoDlg.h"
#include "Aboutdlg.h"
#include "ModifyDlg.h"
#include "SearchDlg.h"
#include "SCommTestDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
//名称:构造函数 功能:对象产生则调用此函数
CMainDlg::CMainDlg(CWnd* pParent /*=NULL*/): CDialog(CMainDlg::IDD, pParent)
{m_input = _T("");
m_field = _T("");}
//功能:实现数据交换
void CMainDlg::DoDataExchange(CDataExchange* pDX)
{CDialog::DoDataExchange(pDX);
 DDX_Control(pDX, IDC_FIELD_COMBO, m_ctrlfield);
 DDX_Control(pDX, IDC_LIST1, m_ctrlperson);
 DDX_Text(pDX, IDC_INPUT_EDIT, m_input);
 DDX_CBString(pDX, IDC_FIELD_COMBO, m_field);}
//功能:消息映射
BEGIN_MESSAGE_MAP(CMainDlg, CDi alog)
 ON_COMMAND(ID_MENU_MODPSW, OnMenuModpsw)
 ON_COMMAND(ID_MENU_ABOUT, OnMenuAbout)
 ON_BN_CLICKED(IDC_RADIO_ADD, OnRadioAdd)
 ON_COMMAND(ID_MENU_DEL, OnMenuDel)
 ON_BN_CLICKED(IDC_RADIO_DEL, OnRadioDel)
 ON_COMMAND(ID_MENU_ADD, OnMenuAdd)
 ON_COMMAND(ID_MENU_HELP, OnMenuHelp)
 ON_COMMAND(ID_MENU_EXIT, OnMenuExit)
 ON_BN_CLICKED(IDC_RADIO_MOD, OnRadioMod)
 ON_BN_CLICKED(IDC_RADIO_SEARCH, OnRadioSearch)
 ON_COMMAND(ID_MENU_MOD, OnMenuMod)
 ON_COMMAND(ID_MENU_SAVE, OnMenuSave)
 ON_COMMAND(ID_MENU_SEARCH, OnMenuSearch)
 ON_NOTIFY(NM_CLICK, IDC_LIST1, OnClickList1)
 ON_BN_CLICKED(IDC_RADIO1, OnRadio1)
 ON_BN_CLICKED(IDC_RADIO2, OnRadio2)
 ON_BN_CLICKED(IDC_RADIO3, OnRadio3)
 ON_COMMAND(IDC_sys1, Onsys1)
 ON_COMMAND(IDC_sys2, Onsys2)
END_MESSAGE_MAP(
//名称:OnInitDialog功能:初始化对话框图
BOOL CMainDlg::OnInitDialog()
{CDialog::OnInitDialog();
CDialog::CheckRadioButton(IDC_RADIO1,IDC_RADIO2,IDC_RADIO1);  
//默认为“精确查询”按钮被选中
m_ctrlfield.SetCurSel(0);                                 //默认“姓名”被选定
CenterWindow();                                       //让窗口出现时居中
m_hIcon=AfxGetApp()->LoadIcon(IDR_MAINFRAME);      //为标题栏加图标
this->SetIcon(m_hIcon,TRUE);                           //设定列的颜色
 m_ctrlperson.SetTextColor(RGB(100,0,100));
 m_ctrlperson.SetTextBkColor(RGB(240,247,233));
 m_ctrlperson.InsertColumn(0,"序号");                      //确定列名
 m_ctrlperson.InsertColumn(1,"姓名");
 m_ctrlperson.InsertColumn(2,"性别");
 m_ctrlperson.InsertColumn(3,"关系");
 m_ctrlperson.InsertColumn(4,"联系电话");
 m_ctrlperson.InsertColumn(5,"手机号码");
 m_ctrlperson.InsertColumn(6,"家庭住址");
 m_ctrlperson.InsertColumn(7,"工作单位");
 m_ctrlperson.InsertColumn(8,"E_mail地址");
 m_ctrlperson.InsertColumn(9,"车牌号码");
 m_ctrlperson.SetColumnWidth(0,40);                       //重新分配列宽
 m_ctrlperson.SetColumnWidth(1,60);
 m_ctrlperson.SetColumnWidth(2,40);
 m_ctrlperson.SetColumnWidth(3,80);
 m_ctrlperson.SetColumnWidth(4,100);
 m_ctrlperson.SetColumnWidth(5,100);
 m_ctrlperson.SetColumnWidth(6,150);
 m_ctrlperson.SetColumnWidth(7,150);
 m_ctrlperson.SetColumnWidth(8,150);
 m_ctrlperson.SetColumnWidth(9,80);
 m_ctrlperson.SetExtendedStyle(LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES);//|LVS_SHOWSELALWAYS);                               //扩展风格
m_query.Format("select * from person order by ID ASC");  //查找记录并按ID升序排列RefreshData();
m_addTip.Create(this);                            //为操作区增加工具条提示
CButton* m_radio_add=(CButton*)GetDlgItem(IDC_RADIO_ADD);
m_addTip.AddTool(m_radio_add,"单击此按钮,可以为通告系统增加记录。");
m_modTip.Create(this);
 CButton* m_radio_mod=(CButton*)GetDlgItem(IDC_RADIO_MOD);
 m_modTip.AddTool(m_radio_mod,"先选取列表框中要修改的记录,再单击此按钮就可以修改相应的记录。");
 m_delTip.Create(this);
 CButton* m_radio_del=(CButton*)GetDlgItem(IDC_RADIO_DEL);
 m_delTip.AddTool(m_radio_del,"先选取列表框中要删除的记录,再单击此按钮就可以彻底将此记录删除。");
m_searchTip.Create(this);
 CButton* m_radio_search=(CButton*)GetDlgItem(IDC_RADIO_SEARCH);
 m_searchTip.AddTool(m_radio_search,"单击此按钮可以查到你想要的记录。");
 CRect dlgrect;                                     //使对话框居中显示
 GetWindowRect(&dlgrect);
 CRect desktoprect;
 GetDesktopWindow()->GetWindowRect(&desktoprect);
 CRect rect1,rect2;
 GetDlgItem(IDC_SEARCH_STATIC)->GetWindowRect(&rect1);
 GetDlgItem(IDC_FLAG_STATIC)->GetWindowRect(&rect2);
m_nReduceHeight=rect1.Height()+(rect1.top-rect2.bottom)/2;   //收缩后窗体的高度
dlgrect.bottom-=(rect1.Height()-(rect1.top-rect2.bottom)/2);
 MoveWindow(&dlgrect);
 m_bflag=false;
 m_bsearchflag=true;
 return TRUE;  }
//名称:RefreshData功能:刷新数据显示
void CMainDlg::RefreshData()
{if(!m_database.IsOpen())                          //首先确保数据库打开
{m_database.Open(_T("addresslist"));}
m_ctrlperson.DeleteAllItems();          //对列表控件的内容更新,清空原来的内容
CPersonSet m_personset(&m_database);             //创建记录集
m_personset.Open(AFX_DB_USE_DEFAULT_TYPE,m_query);
CDBVariant varValue;
char buf[20];
int i=0;                                         //用来记录当前记录的序号
//如果表中有记录,打开后将游标定在第一位,使记录集中的第一条记录为当前记录
if(m_personset.GetRecordCount()!=0)  m_personset.MoveFirst();
while(!m_personset.IsEOF())
{int temp=0;
m_personset.GetFieldValue(temp,varValue);                 //对整型数字的处理
sprintf(buf,"%d",varValue.m_lVal);m_ctrlperson.InsertItem(i,buf);
    m_personset.GetFieldValue(1,varValue);
  m_ctrlperson.SetItemText(i,1,varValue.m_pstring->GetBuffer(1));
  m_personset.GetFieldValue(2,varValue);
  m_ctrlperson.SetItemText(i,2,varValue.m_pstring->GetBuffer(1));
  m_personset.GetFieldValue(3,varValue);
  m_ctrlperson.SetItemText(i,3,varValue.m_pstring->GetBuffer(1));
  m_personset.GetFieldValue(4,varValue);
  m_ctrlperson.SetItemText(i,4,varValue.m_pstring->GetBuffer(1));
  m_personset.GetFieldValue(5,varValue);
  m_ctrlperson.SetItemText(i,5,varValue.m_pstring->GetBuffer(1));
  m_personset.GetFieldValue(6,varValue);
  m_ctrlperson.SetItemText(i,6,varValue.m_pstring->GetBuffer(1));
  m_personset.GetFieldValue(7,varValue);
  m_ctrlperson.SetItemText(i,7,varValue.m_pstring->GetBuffer(1));
  m_personset.GetFieldValue(8,varValue);
  m_ctrlperson.SetItemText(i,8,varValue.m_pstring->GetBuffer(1));
  m_personset.GetFieldValue(9,va rValue);
  m_ctrlperson.SetItemText(i,9,varValue.m_pstring->GetBuffer(1));
  m_personset.MoveNext();i++; }
 int counts=m_personset.GetRecordCount();   //在标题栏中显示共有记录条数
 CString str;
 str.Format("4S店通告系统     目前共有记录数: %d",counts); 
 this->SetWindowText(str);}
//功能:弹出更改口令窗口
void CMainDlg::OnMenuModpsw()
{CChangePswDlg dlg;dlg.DoModal();}
//功能:弹出增加记录窗口
void CMainDlg::OnRadioAdd()
{m_database.Close();                       //本对话框对应的数据库连接关闭     CAddInfoDlg dlg;                           //新建一个CAddInfoDlg的对象
dlg.m_database.Open(_T("addresslist"))         //新对话框建立到数据库连接
dlg.DoModal();                             //弹出新对话框
RefreshData();}                 //新对话框关闭后,在原对话框中刷新数据显示
//功能:弹出删除记录窗口
void CMainDlg::OnMenuDel()
{int i=m_ctrlperson.GetSelectionMark(); //i是记录中所选的记录号,如果没有则返回
CString strSQL,msg,strname;
strname=m_ctrlperson.GetItemText(i,1);
msg.Format("第 %d 项,姓名为“%s”的记录将被删除!是否继续?",i+1,strname);
//如果用户没有选择记录,则提示选取一条记录
if(i==-1)
{MessageBox("请选择一条要删除的记录!","提示",MB_OK|MB_ICONINFORMATION);}
else
 {if(MessageBox(msg,"提示",MB_YESNO|MB_ICONINFORMATION)==IDYES)
{CString strname=m_ctrlperson.GetItemText(i,1);
trSQL.Format("delete from person where NAME='%s'",strname);
//从表中删除对应的记录
   m_database.ExecuteSQL(strSQL);
   m_database.Close();
   RefreshData();}}}
///功能:弹出删除记录窗口
void CMainDlg::OnRadioDel()
{int i=m_ctrlperson.GetSelectionMark(); //i是记录中所选的记录号,如果没有则返回
CString strSQL,msg,strname;
strname=m_ctrlperson.GetItemText(i,1);
msg.Format("第 %d 项,姓名为“%s”的记录将被删除!是否继续?",i+1,strname);
//如果用户没有选择记录,则提示选取一条记录
if(i==-1){MessageBox("请选择一条要删除的记录!","提示",MB_OK|MB_ICONINFORMATION);}
else
{if(MessageBox(msg,"提示",MB_YESNO|MB_ICONINFORMATION)==IDYES)
  {CString strname=m_ctrlperson.GetItemText(i,1);
  strSQL.Format("delete from person where NAME='%s'",strname);
   m_database.ExecuteSQL(strSQL);
   m_database.Close();
   RefreshData();}}}
//功能:弹出增加数据窗口
void CMainDlg::OnMenuAdd()
{
 m_database.Close();                  //本对话框对应的数据库连接关闭
 CAddInfoDlg dlg;                    //新建一个CAddInfoDlg的对象
 dlg.m_database.Open(_T("addresslist"));  //新对话框建立到数据库连接
 dlg.DoModal();                      //弹出新对话框
 RefreshData();              //新对话框关闭后,在原对话框中刷新数据显示
}
//功能:弹出修改记录窗口
void CMainDlg::OnRadioMod()
{m_database.Close();//本对话框断开与数据库的连接
 CModifyDlg dlg;
 dlg.m_database.Open(_T("addresslist"));
 int i=m_ctrlperson.GetSelectionMark();
 CString strSQL;
 int id=atoi(m_ctrlperson.GetItemText(i,0));
 CPersonSet m_recordset;
 CDBVariant varValue;
 if(i==-1)
 {MessageBox("请选择一条要修改的记录!","提示",MB_OK|MB_ICONINFORMATION);}
 else
 {int temp=0;
  strSQL.Format("select * from person where ID=%d",id);
  m_recordset.Open(AFX_DB_USE_DEFAULT_TYPE,strSQL);
  m_recordset.GetFieldValue(temp,varValue);
  dlg.m_modid=varValue.m_lVal;
  m_recordset.GetFieldValue(1,varValue);
  dlg.m_modname=varValue.m_pstring->GetBuffer(1);
  m_recordset.GetFieldValue(2,varValue);
  dlg.m_modsex=varValue.m_pstring->GetBuffer(1);
  m_recordset.GetFieldValue(3,varValue);
  dlg.m_modrelation=varValue.m_pstring->GetBuffer(1);
  m_recordset.GetFieldValue(4,varValue);
  dlg.m_modtelephone=varValue.m_pstring->GetBuffer(1);
  m_recordset.GetFieldValue(5,varValue);
  dlg.m_modhandphone=varValue.m_pstring->GetBuffer(1);
  m_recordset.GetFieldValue(6,varValue);
  dlg.m_modaddress=varValue.m_pstring->GetBuffer(1);
  m_recordset.GetFieldValue(7,varValue);
  dlg.m_modworkplace=varValue.m_pstring->GetBuffer(1);
  m_recordset.GetFieldValue(8,varValue);
  dlg.m_modemail=varValue.m_pstring->GetBuffer(1);
  m_recordset.GetFieldValue(9,varValue);
  dlg.m_modoicq=varValue.m_pstring->GetBuffer(1);
  dlg.DoModal();
  RefreshData();}}
//功能:展开查询记录窗口
void CMainDlg::OnRadioSearch()
{m_bflag=!m_bflag;
if(m_bflag==false)//没有展开对话框(退出查询)
{GetDlgItem(IDC_RADIO_SEARCH)->SetWindowText(_T("查询记录"));
SizeWindow(m_nReduceHeight,true);
RefreshData();}
else
 {GetDlgItem(IDC_RADIO_SEARCH)->SetWindowText(_T("退出查询"));
 //在“关键字”框中设置光标
 GetDlgItem(IDC_INPUT_EDIT)->SetFocus();
 SizeWindow(m_nReduceHeight,false);}}
//功能:弹出修改记录窗口
void CMainDlg::OnMenuMod()
{m_database.Close();//本对话框断开与数据库的连接
 CModifyDlg dlg;
 dlg.m_database.Open(_T("addresslist"));
 int i=m_ctrlperson.GetSelectionMark();
 CString strSQL;
 int id=atoi(m_ctrlperson.GetItemText(i,0));
 CPersonSet m_recordset;
 CDBVariant varValue;
 if(i==-1)
 {MessageBox("请选择一条要修改的记录!","提示",MB_OK|MB_ICONINFORMATION);}
 else
 {int temp=0;
  strSQL.Format("select * from person where ID=%d",id);
  m_recordset.Open(AFX_DB_USE_DEFAULT_TYPE,strSQL);
  m_recordset.GetFieldValue(temp,varValue);
  dlg.m_modid=varValue.m_lVal;
  m_recordset.GetFieldValue(1,varValue);
  dlg.m_modname=varValue.m_pstring->GetBuffer(1);
  m_recordset.GetFieldValue(2,varValue);
  dlg.m_modsex=varValue.m_pstring->GetBuffer(1);
  m_recordset.GetFieldValue(3,varValue );
  dlg.m_modtelephone=varValue.m_pstring->GetBuffer(1);
  m_recordset.GetFieldValue(4,varValue);
  dlg.m_modtelephone=varValue.m_pstring->GetBuffer(1);
  m_recordset.GetFieldValue(5,varValue);
  dlg.m_modhandphone=varValue.m_pstring->GetBuffer(1);
  m_recordset.GetFieldValue(6,varValue);
  dlg.m_modaddress=varValue.m_pstring->GetBuffer(1);
  m_recordset.GetFieldValue(7,varValue);
  dlg.m_modworkplace=varValue.m_pstring->GetBuffer(1);
  m_recordset.GetFieldValue(8,varValue);
  dlg.m_modemail=varValue.m_pstring->GetBuffer(1);
  m_recordset.GetFieldValue(9,varValue);
  dlg.m_modoicq=varValue.m_pstring->GetBuffer(1);
  dlg.DoModal();
  RefreshData();}}
//功能:展开查询窗口
void CMainDlg::OnMenuSearch()
{m_bflag=!m_bflag;
if(m_bflag==false)
{GetDlgItem(IDC_RADIO_SEARCH)->SetWindowText(_T("查询记录"));
SizeWindow(m_nReduceHeight,true);
RefreshData();}
else
 {GetDlgItem(IDC_RADIO_SEARCH)->SetWindowText(_T("退出查询"));
  GetDlgItem(IDC_INPUT_EDIT)->SetFocus();  //在“关键字”框中设置光标
  SizeWindow(m_nReduceHeight,false);}}
BOOL CMainDlg::PreTranslateMessage(MSG* pMsg)
{if(pMsg->message==WM_MOUSEMOVE)
 {m_addTip.RelayEvent(pMsg);
  m_delTip.RelayEvent(pMsg);
  m_modTip.RelayEvent(pMsg);
  m_searchTip.RelayEvent(pMsg);}
 return CDialog::PreTranslateMessage(pMsg);
}
//功能:展开对话框,出现查询对话框
void CMainDlg::SizeWindow(int ReduceHeight, bool bflag)
{CRect rect;
 GetWindowRect(&rect);
 if(bflag)
 {for(int i=0;i<ReduceHeight;i++)
 {rect.bottom--;
 MoveWindow(&rect);}}
 else
 {for(int i=0;i<ReduceHeight;i++)
 {rect.bottom++;
 MoveWindow(&rect);}}
 UpdateWindow();}
//名称:OnOk 功能:提交通讯录果询结果
void CMainDlg::OnOK()
{if(m_bsearchflag)//如果flag=true,则调用精确查询
 {ExactSearch();}
 else//否则调用模糊查询
 {BlurSearch();}}
//名称:ExactSearch 功能:实现精确查询
void CMainDlg::ExactSearch()
{if(!m_database.IsOpen())                //如果数据库没有打开,先将其打开
 {m_database.Open(_T("addresslist"));}
 UpdateData(TRUE);
 CPersonSet m_personset;//定义CPersonset类的对象
 CString m_tablefield;
 if(m_field.Compare("姓名")==0)
 {m_tablefield.Format("NAME");}
 if(m_field.Compare("所在城市")==0)
 {m_tablefield.Format("ADDRESS");}
 if(m_field.Compare("手机号码")==0)
 {m_tablefield.Format("HANDPHONE");}
 if(m_field.Compare("车牌号码")==0)
 {m_tablefield.Format("OICQ");}
 if(!m_input.IsEmpty())
 {m_searchSQL.Format("select * from person where %s='%s'",m_tablefield,m_input);
SearchData();}else
 {MessageBox("请填写查询关键字","提示",MB_OK|MB_ICONINFORMATION);
 GetDlgItem(IDC_INPUT_EDIT)->SetFocus();}}
//名称:BlurSearch功能:实现模糊查询
void CMainDlg::BlurSearch()
{if(!m_database.IsOpen())            //如果数据库没有打开,先将其打开
 {m_database.Open(_T("addresslist"));}
 UpdateData(TRUE);
 CPersonSet m_personset;            //定义CPersonset类的对象
 CString m_tablefield,m_str="%";
 m_str=m_str+m_input+"%";
 if(!m_input.IsEmpty())
 {if(m_field.Compare("姓名")==0)
  {m_tablefield.Format("NAME");}
     if(m_field.Compare("所在城市")==0)
  {m_tablefield.Format("ADDRESS");}
  if(m_field.Compare("手机号码")==0)
  {m_tablefield.Format("HANDPHONE");}
  if(m_field.Compare("车牌号码")==0)
  {m_tablefield.Format("OICQ");}
m_searchSQL.Format("select * from person where %s like '%s'",m_tablefield,m_str);
SearchData();}
else{MessageBox("请填写查询关键字","提示",MB_OK|MB_ICONINFORMATION);
GetDlgItem(IDC_INPUT_EDIT)->SetFocus();}}
//功能:精确查询按钮
void CMainDlg::OnRadio1()
{m_bsearchflag=true;}
//功能:模糊查询按钮
void CMainDlg::OnRadio2()
{m_bsearchflag=!m_bsearchflag;}
//名称:SearchData 功能:实现记录查询
void CMainDlg::SearchData()
{if(!m_database.IsOpen())                 //首先确保数据库打开
{m_database.Open(_T("addresslist"));}
m_ctrlperson.DeleteAllItems();          //对列表控件的内容更新,清空原来的内容
CPersonSet m_personset(&m_database);        //创建记录集
m_personset.Open(AFX_DB_USE_DEFAULT_TYPE,m_searchSQL);
CDBVariant varValue;char buf[20];
int i=0;                                      //用来记录当前记录的序号
if(m_personset.GetRecordCount()!=0)  m_personset.MoveFirst();
while(!m_personset.IsEOF())
{int temp=0;
  m_personset.GetFieldValue(temp,varValue);         //对整型数字的处理
  sprintf(buf,"%d",varValue.m_lVal);m_ctrlperson.InsertItem(i,buf);
  m_personset.GetFieldValue(1,varValue);
  m_ctrlperson.SetItemText(i,1,varValue.m_pstring->GetBuffer(1));
  m_personset.GetFieldValue(2,varValue);
  m_ctrlperson.SetItemText(i,2,varValue.m_pstring->GetBuffer(1));
  m_personset.GetFieldValue(3,varValue);
  m_ctrlperson.SetItemText(i,3,varValue.m_pstring->GetBuffer(1));
  m_personset.GetFieldValue(4,varValue);
  m_ctrlperson.SetItemText(i,4,varValue.m_pstring->GetBuffer(1));
  m_personset.GetFieldValue(5,varValue);
  m_ctrlperson.SetItemText(i,5,varValue.m_pstring->GetBuffer(1));
  m_personset.GetFieldValue(6,varValue);
  m_ctrlperson.SetItemText(i,6,varValue.m_pstring->GetBuffer(1));
  m_personset.GetFieldValue(7,varValue);
  m_ctrlperson.SetItemText(i,7,varValue.m_pstring->GetBuffer(1));
  m_personset.GetFieldValue(8,varValue);
  m_ctrlperson.SetItemText(i,8,varValue.m_pstring->GetBuffer(1));
  m_personset.GetFieldValue(9,varValue);
  m_ctrlperson.SetItemText(i,9,varValue.m_pstring ->GetBuffer(1));
  m_personset.MoveNext();
  i++; }
 int counts=m_personset.GetRecordCount();    //在标题栏中显示共有记录条数
CString str;
str.Format("4S店通告系统     符合条件的记录数: %d",counts); 
this->SetWindowText(str);}
void CMainDlg::OnRadio3()
{m_database.Close();                      //本对话框断开与数据库的连接
   CSCommTestDlg dlg;
 dlg.m_database.Open(_T("addresslist"));
 int i=m_ctrlperson.GetSelectionMark();
 CString strSQL;
 int id=atoi(m_ctrlperson.GetItemText(i,0));
 CPersonSet m_recordset;
 CDBVariant varValue;
 if(i==-1)
{MessageBox("请选择一条要修改的记录!","提示",MB_OK|MB_ICONINFORMATION);}
else{int temp=0;
 strSQL.Format("select * from person where ID=%d",id);
 m_recordset.Open(AFX_DB_USE_DEFAULT_TYPE,strSQL);
 m_recordset.GetFieldValue(8,varValue);
 dlg.m_memail=varValue.m_pstring->GetBuffer(1);
 dlg.DoModal();RefreshData();}}
void CMainDlg::Onsys1()
{COMMCONFIG   lp;
CommConfigDialog("COM1",   NULL,   &lp);}
void CMainDlg::Onsys2()
{COMMCONFIG   lp;
CommConfigDialog("COM2",   NULL,   &lp);}
 
附录2  系统电路图

附录3  系统实物图

相关文章
学术参考网 · 手机版
https://m.lw881.com/
首页