com(componentobjectmodel)組件技術(shù)是構(gòu)造二進(jìn)制兼容軟件的規(guī)范,通過(guò)它可以建立能夠相互傳輸數(shù)據(jù)的組件,其服務(wù)器-客戶機(jī)結(jié)構(gòu)非常適合工控軟件應(yīng)用程序的開(kāi)發(fā)。由于工控軟件不僅包括pc機(jī)上的hmi(人-機(jī)界面)程序,還包括與各種基于isa或pci總線的數(shù)據(jù)采集卡進(jìn)行數(shù)據(jù)交換的程序,這部分程序?qū)﹂_(kāi)人員的硬件水平要求較高,而且開(kāi)發(fā)難度較大,與hmi程序是相互獨(dú)立的,所以可以把工控軟件分成兩部分,即把hmi程序作為客戶機(jī)端程序,把與硬件進(jìn)行數(shù)據(jù)交換的程序作為服務(wù)器端程序。基于這種思想,本文將服務(wù)器-客戶機(jī)結(jié)構(gòu)應(yīng)用到現(xiàn)場(chǎng)總線控制系統(tǒng)的組態(tài)軟件中,著重介紹客戶機(jī)和服務(wù)器的功能及實(shí)現(xiàn)。首先介紹現(xiàn)場(chǎng)總線控制系統(tǒng)的組成。
1系統(tǒng)組成
現(xiàn)場(chǎng)總線控制系統(tǒng)主要由pc機(jī)、isa或pci總線智能適配器、智能測(cè)控模塊、組態(tài)軟件、hmi軟件、com服務(wù)器、用戶軟件等構(gòu)成。
現(xiàn)場(chǎng)總線系統(tǒng)中所有信息的傳遞都是雙向的,com服務(wù)器介于智能適配器和上位機(jī)軟件之間,負(fù)責(zé)完成數(shù)據(jù)的傳輸。上位機(jī)軟件相當(dāng)于客戶機(jī)端應(yīng)用軟件,它使用com服務(wù)器提供的接口來(lái)操作適配器,對(duì)適配器進(jìn)行初始化及向特定單元寫(xiě)入和讀出數(shù)據(jù)。
由于在windows保護(hù)模式下不能直接訪問(wèn)存儲(chǔ)器,所以需要編寫(xiě)vxd驅(qū)動(dòng)程序,將物理地址轉(zhuǎn)換成線性地址,然后com就可以象使用dll一樣調(diào)用vxd的函數(shù),完成對(duì)isa或pci總線智能適配器的操作。
從測(cè)控模塊到上位機(jī)軟件自下而下的數(shù)據(jù)傳輸完成了用戶對(duì)測(cè)控模塊的監(jiān)測(cè);而上層軟件通過(guò)com將數(shù)據(jù)送往適配器,再由適配器送往測(cè)控模塊,實(shí)現(xiàn)了用戶對(duì)測(cè)控模塊工作參數(shù)的設(shè)置及工作狀態(tài)的管理。圖1給出了系統(tǒng)軟件結(jié)構(gòu)框圖。
2組態(tài)軟件的功能
現(xiàn)場(chǎng)總線控制系統(tǒng)組態(tài)軟件是一套基于windows98和windows2000平臺(tái)(或更高版本)、用于快速構(gòu)造和生成上位機(jī)監(jiān)控系統(tǒng)的組成軟件,它提供了從數(shù)據(jù)采集到數(shù)據(jù)處理、遠(yuǎn)程控制、報(bào)警處理、報(bào)表輸出等實(shí)際工程問(wèn)題的完整解決方案。它使用com服務(wù)器提供的接口與適配器進(jìn)行數(shù)據(jù)交換,是com客戶機(jī)端的程序。
3com組件技術(shù)
組件是完成一定功能的軟件塊,可以被其它程序使用,而且容易替換。為了使每個(gè)人編寫(xiě)的組件具有可移植性,必須建立一個(gè)標(biāo)準(zhǔn),保證其兼容性和可互換性。com正是這樣一種標(biāo)準(zhǔn),遵循com規(guī)則就可以建立能夠相互交換數(shù)據(jù)的組件。
在現(xiàn)場(chǎng)總線控制系統(tǒng)中,com組件服務(wù)器負(fù)責(zé)組態(tài)軟件等上位機(jī)軟件與智能適配器之間的數(shù)據(jù)傳輸,因?yàn)檫m配器通過(guò)can現(xiàn)場(chǎng)總線與測(cè)控模塊連接,所以對(duì)適配器的操作就是對(duì)模塊的監(jiān)測(cè)與控制。
com服務(wù)器提供的接口中有適配器初始化、模塊檢查、向模塊發(fā)送數(shù)據(jù)及讀取模塊數(shù)據(jù)等函數(shù)。下面著重介紹數(shù)據(jù)發(fā)送接收模式及如何編寫(xiě)這4個(gè)有代表性的函數(shù)。
3.1適配器初始化函數(shù)
只有適配器初始化成功后,才能進(jìn)行其它操作。由于在windows保護(hù)模式下不能直接訪問(wèn)適配器,com程序需要調(diào)用vxd程序?qū)⒋鎯?chǔ)對(duì)應(yīng)的物理地址轉(zhuǎn)換成線性地址指針lpbaseaddress,這樣對(duì)適配器的操作就轉(zhuǎn)換成對(duì)以該指針為首地址的數(shù)組的操作。向這個(gè)數(shù)組的0x3f0、0x3f1和0x3f8單元分別寫(xiě)入上閏機(jī)節(jié)點(diǎn)號(hào)以及適配器與模塊間的通信波特率和適配器程序規(guī)定的命令字0xc6(表示適配器初始化),等待幾十ms后,如果適配器接收到上面的數(shù)據(jù)并做出適當(dāng)?shù)姆磻?yīng),它會(huì)將0x3f8單元清零,這就表示初始化適配器成功;如果該單元不為零,則初始化失敗。
3.2數(shù)據(jù)傳輸格式
適配器初始化成功后,就可以同它交換數(shù)據(jù)了。下而簡(jiǎn)單說(shuō)明一下發(fā)送數(shù)據(jù)和接收數(shù)據(jù)的格式。
適配器初始化得到的線性地址指針lpbaseaddress的1~5單元分別存放上位機(jī)節(jié)點(diǎn)號(hào)、模塊節(jié)點(diǎn)號(hào)、保留字、發(fā)送或接收字節(jié)長(zhǎng)度及模塊操作的命令字。lpbaseaddress[6]~lpbaseaddress[256]存放所要發(fā)送的數(shù)據(jù);從lpbaseaddress[0x106]單元開(kāi)始存放接收到的數(shù)據(jù),lpbaseaddress[0x3f8]存放操作適配器的命令字,適配器根據(jù)這個(gè)單元內(nèi)容進(jìn)行處理,如果是0xc6,則初始化適配器和模塊上的can控制器;如果是0xc7,則將數(shù)組里的數(shù)送給模塊上的e2prom,模塊收到數(shù)據(jù)后根據(jù)lpbaseaddress[5]的命令字進(jìn)行相應(yīng)處理;如果是0xb0,則按照接收到的數(shù)據(jù)配置模塊工作狀態(tài);如果是0xa5,則將此時(shí)的測(cè)量值送到適配器上,由com程序讀出。
3.3模塊檢查函數(shù)
適配器初始化成功后,還要檢查適配器與下面的測(cè)控模塊是否連接好,或者是否存在組態(tài)軟件要組態(tài)的模塊,也就是要進(jìn)行模塊檢查操作。模塊檢查的命令字是0xad,向數(shù)組的1~5單元分別寫(xiě)入上位機(jī)節(jié)點(diǎn)號(hào)、模塊節(jié)點(diǎn)號(hào)、保留字、發(fā)送數(shù)據(jù)長(zhǎng)度和模塊檢查命令字0xad,向0x3f8單元寫(xiě)入0xc7(表示向適配器寫(xiě)入數(shù)據(jù)),等待幾十ms后,如果0x3f8單元清零而且0x100單元被置為0xaa,表示該模塊存在而且可以通信;否則,表明該模塊不存在或者硬件上有問(wèn)題。
3.4寫(xiě)適配器數(shù)據(jù)函數(shù)
在確定了網(wǎng)絡(luò)中存在哪些可通信的模塊之后,就可以向它們發(fā)送數(shù)據(jù)并進(jìn)行配置。為了實(shí)現(xiàn)向適配器發(fā)送數(shù)據(jù),總共編寫(xiě)了4個(gè)函數(shù)、senddata([in]bytesendbuf[256])、sendfinish([in]boolbfinish)、finishquery([out]bool*bfinish)和receiveresult([out]bool*bsendfinish)。senddata負(fù)責(zé)把一個(gè)模塊所需要發(fā)送的數(shù)據(jù)以數(shù)組的形式放到服務(wù)器的一個(gè)二維數(shù)組(room[64][256])里,每個(gè)模塊的數(shù)據(jù)作為一行。由于向適配器發(fā)送數(shù)據(jù)后,要等待一段時(shí)間判斷模塊是否接收成功,所以sendfinish中開(kāi)啟輔助線程來(lái)發(fā)送數(shù)據(jù)并等待結(jié)果,這相可不占用com主程序的時(shí)間,使客戶調(diào)用接口函數(shù)后能立即返回,執(zhí)行其它操作。finishquery查詢數(shù)據(jù)發(fā)送是否結(jié)束。receiveresult彈出一個(gè)非模式對(duì)話框,顯示哪些模塊接收到數(shù)據(jù),哪些沒(méi)有。
3.5讀適配器數(shù)據(jù)函數(shù)
除了向適配器發(fā)送數(shù)據(jù),還可以從適配器上讀取模塊傳上來(lái)的數(shù)據(jù)。讀取數(shù)據(jù)的命令字是0xa5。實(shí)現(xiàn)該任務(wù)的函數(shù)是getpv([in]bytebdesnode,[out]floatvalue[8]),第一個(gè)參數(shù)是模塊節(jié)點(diǎn)號(hào),第二個(gè)參數(shù)是返回的測(cè)量值數(shù)組。
這里,com是用atl編寫(xiě)的本地服務(wù)器,com對(duì)象的線程是套間線程。接口定義了6個(gè)函數(shù),com程序流程圖如圖2所示。#p#分頁(yè)標(biāo)題#e#
com對(duì)象接口的函數(shù)聲明以及適配器初始化的程序如下:
com接口定義:
interfaceincardwork:idispatch
{
[id(1),helpstring(適配器初始化函數(shù),返回值為是否成功)]
hresultncardinit([in]byte
bsrcnode,[in]bytebintradd,[in]bytebrate,[in]longbsegmantadd,[out]bool*flag);
[id(2),helpstring(將客戶端傳送的數(shù)組賦值給room[][])]
hresultsenddata[in]bytesendbuf[256]);
[id(3),helpstring(啟動(dòng)多線程)]
hresultsendfinish([in]boolbfinish);
[id(4),helpstring(此函數(shù)返回值表示數(shù)據(jù)是否已向下位機(jī)發(fā)送完畢,同時(shí)可顯示哪些模塊未被配置,通常在此函數(shù)前先用finishquery([out]bool*bfinish)查詢發(fā)送是否完畢)]
hresultreceiveresult([out]bool*bsendfinish);
[id(5)],helpstring(此函數(shù)返回值表示數(shù)據(jù)是否已向下位機(jī)發(fā)送完畢,“真”表示發(fā)送完畢)]
hresultfinishquery([out]bool*bfinish);
[id(6),helpstring(網(wǎng)絡(luò)檢查,用來(lái)在發(fā)送數(shù)據(jù)前檢測(cè)是否有該節(jié)點(diǎn)存在)]
hresultnetcheck[in]bytesour,[in]bytedes,[in]bytetype,[out]bool*flag);
[id(7),helpstring(讀取模塊的測(cè)量值)]
hresultgetpv([iv]bytebdesnode,[out]floatvalue[256]);
}
適配器初始化函數(shù):
#include<conio.h>
#includewinioctl.h
//包含其它頭文件
……
stdmethodimpcncardwork::ncardinit(bytebsrcnode,bytebintradd,bytebrate,longbsegmentadd,bool*flag)
{
ncardctrlcardctrl;//ncardctrl類的函數(shù)調(diào)用vxd函數(shù)
exbsrcnode=bsrcnode;//給上位機(jī)節(jié)點(diǎn)賦值
exbrate=brate;//下位機(jī)與適配器的通信波特率
booltransfersign;//初始化是否成功標(biāo)志
dworddwsegmentaddress=bsegmentadd;//適配器段地址
handlehdevice=null;//指向線性指針對(duì)句柄
lpbaseaddress=(pbyte)cardctrl.maplinearaddress(dwsegmentaddress,0x400,hdevice);
//調(diào)用vxd函數(shù),獲得指向isa總線物理地址的線性地址指針
cardctrl,unmaplinearaddress(lpbaseaddress,hdevice);
//關(guān)閉vxd
//調(diào)用適配器初始化函數(shù)
_outp(0x310,0x01);//打開(kāi)郵箱鎖
lpbaseaddress[0x3f0]=bsrcnodenumber;//上位機(jī)節(jié)點(diǎn)號(hào)
lpbaseaddress[0x3f1]=brate;//波特率
lpbaseaddress[0x3f8]=0xc6;//適配器初始化命令字
drvdelay(20,false);//延時(shí)20ms
…………//初始化后其它操作
_outp(0x310,00);//關(guān)閉郵箱鎖
returns_ok;
}
4虛擬設(shè)備驅(qū)動(dòng)程序
vxd是虛擬設(shè)備驅(qū)動(dòng)程序(virtualdevicedriver)的縮寫(xiě),中間的x表示某一設(shè)備。它能夠無(wú)限制地訪問(wèn)所有硬件設(shè)備、自由地檢測(cè)操作系統(tǒng)的數(shù)據(jù)結(jié)構(gòu)(如描述符和頁(yè)表)以及訪問(wèn)任何內(nèi)存位置。
本文中,vxd將isa總線對(duì)應(yīng)的物理地址轉(zhuǎn)換成段線性地址,供應(yīng)用程序使用。vxd的開(kāi)發(fā)工具是vtoolsd,轉(zhuǎn)換時(shí)用的函數(shù)為mapphystolinear。以下是部分程序代碼:
//定義結(jié)構(gòu)體
typedefstruct_mapdevrequest
{
pvoidmdr_physicaladdress;dwordmdr_sizeinbytes;
pvoidmdr_linearaddress;wordmdr_status;
}mapdevrequest,*pmapdevrequest;
#include<vtoolscp.h>
//包含其它頭文件
…………
paramspdiocparams
{
pmapdevrequestprea;//自己定義的結(jié)構(gòu)體
switch(pdiocparams->dioc_ioctlcode)
{
casedioc_open:
casedioc_closehandle:break;
casemdr_service_map:
preq=*(pmapdevrequest*)pdiocparams->dioc_inbuf;
preq->mdr_linearaddress=mapphystolinear
(preq->mdr_physicaladdress,preq->mdr_sizeinbytes,0);
if(preq->mdr_linearaddress==null)
preq->mdr_status=mdr_status_error;
else
preq->mdr_status=mdr_status_success;
break;
casemdr_service_unmap:break;
default:
returnerror_invalid_function;
}
returndevioctl_noerror;
}
在現(xiàn)場(chǎng)總線控制系統(tǒng)中使用com組件技術(shù),不僅可以使數(shù)據(jù)傳輸部分的功能獨(dú)立于客戶端程序,減小開(kāi)發(fā)難度,而且使其可以被任何支持二進(jìn)制代碼的程序如excel電子表格等直接調(diào)用。當(dāng)系統(tǒng)中采用服務(wù)器和客戶端方式時(shí),代碼更加易于維護(hù)。即使要升級(jí)服務(wù)器端程序,只要接口不變,其客戶端程序也完全不需要修改,大量后續(xù)工作被減輕。象服務(wù)器端一樣,客店端也只需關(guān)心服務(wù)器的接口,而不必考慮其如何實(shí)現(xiàn)數(shù)據(jù)交換。也就是說(shuō),com服務(wù)器或客戶機(jī)中的一端功能發(fā)生改變,只要其接口保持不變,另一端不需修改就可以工作。本文所介紹的技術(shù)已在勝利油田某注水站等實(shí)際工程項(xiàng)目中得到成功的應(yīng)用。