VFP 环 境 下 实 时 数 据 采 集 的 实 现

东 南 大 学 热 能 所(210096)---- 周 卫 平


---- VFP 作 为 新 一 代 数 据 库 管 理 系 统, 在 数 据 的 存 取、 编 辑、 显 示、 处 理 等 方 面 具 有 强 大 和 丰 富 的 工 具, 在 国 内 外 得 到 了 广 泛 应 用。 在 许 多 工 业 应 用 中, 往 往 要 对 现 场 模 拟 数 据 进 行A/D 转 换 且 实 时 处 理, 由 于VFP 不 能 对 端 口 进 行 直 接 访 问, 因 而 无 法 实 现 数 据 采 集。 幸 好VFP 提 供 了DDE 功 能。DDE 是Windows 应 用 程 序 之 间 进 行 动 态 信 息 传 递 和 共 享 的 一 个 消 息 协 议, 利 用DDE 可 实 现Windows 的 服 务 程 序 和 客 户 程 序 的 相 互 独 立 运 行, 具 有 很 好 的 实 时 性。 在 实 践 中, 我 们 利 用 高 级 语 言BorLand C++ for Windows 实 现96 路A/D 数 据 采 集, 并 以 此 为 服 务 程 序; 在VFP 环 境 下 建 立 客 户 程 序, 将 服 务 程 序 传 递 过 来 的 数 据 及 时 进 行 运 算 处 理 后, 存 入 数 据 库, 并 作 为 各 种 表 单、 报 表、 曲 线 的 数 据 源。 该 方 法 在VB 或ACCESS 97 环 境 下 同 样 适 用, 只 要 将 客 户 程 序 稍 加 修 改 即 可。 下 面 就 如 何 实 现VFP 环 境 下 的 实 时 数 据 采 集 作 一 介 绍。

1. 建 立 数 据 采 集 服 务 程 序

---- 服 务 程 序 包 括 二 个 基 本 部 分: 其 一 是WinMain 入 口 函 数, 它 主 要 完 成 一 此 初 始 化 任 务, 建 立 一 个 名 为hwnd 的 原 始 数 据 显 示 窗 口, 服 务 程 序 名 为PFBCdas。 其 二 是 应 用 程 序 功 能 函 数MainWndProc。 下 面 是 程 序 清 单( 因 篇 幅 关 系, 在 此 省 去WinMain 函 数)。
#define AD_Interval 1000    //采样周期为 1000ms
#define  base   0x280          // A/D采样板基地址
/*  base+k (k=1,2,…14)为A/D板各寄存器地址    */
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
int     i, j, k ,  AD_Status, idTimer;
int   AD_Data[8][16];         // A/D数据存储单元 
int     DdeAdviseStatus=0;
char    buf[64], DDEbuf[2048];       //数据暂存缓冲器 
char    szAppName[]="PFBCdas";    //服务程序名
char    szDdeTopic[]="AD001";     //主题名 
char    szDdeItem[]="DATA";      // 数据项名
long FAR PASCAL _export MainWndProc(HWND, UINT, UINT, LONG) ;
long FAR PASCAL _export MainWndProc (HWND hwnd, UINT message, UINT 
                                  wParam, LONG lParam)    
//应用程序功能函数   
     {
     ATOM        aAppName,aTopic;
     ATOM        aItem;
     GLOBALHANDLE        hDdeData;
     DDEDATA FAR *lpDdeData;
     static HWND hwndClient;
     switch (message)
        {       
     case WM_CREATE:
        idTimer=SetTimer(hwnd,NULL,AD_Interval,NULL); 
                         //打开定时器
        outportb(base+11,0x10);  //选择软件触发方式
        outportb(base+9,1);      //选择输入信号放大倍数为10
        outportb(base+14,0);
        outportb(base+13,0);     //清A/D完成位
        outportb(base+10,0);    //选择通道0
        return 0;
   case WM_TIMER:
        for(i=0;i<6;i++)
          for(j=0;j<16;j++)       //采样点为6*16=96个
          {
           outportb(base+13,j);                  
           outportb(base+10,i);   
           outportb(base+12,0);     //启动A/D转换
           do AD_Status=inportb(base+5);
           while ((AD_Status&0x10)==0x10); 
//判转换是否结束?                                                                 
AD_Data[i][j]=(inportb(base+5)&0x0f)*256+inportb(base+4); 
                /*  读A/D转换后的数字值  */
            }
           _strdate(buf);                 //取采样日期
           strcpy(DDEbuf,buf);
           strcat(DDEbuf,",");
           _strtime(buf);                //取采样时间
           strcat(DDEbuf,buf);
           strcat(DDEbuf,",");
           for(i=0;i<6;i++)
            for(j=0;j<16;j++)
            {
             char  temp[32];
             strcat(itoa(AD_Data[i][j],buf,10),",");
              /*各采样数据之间以逗号分隔*/
             strcat(DDEbuf,buf);
             }
           if(DdeAdviseStatus!=0)      //如果建立了DDE服务
           {
           aItem = GlobalAddAtom (szDdeItem) ;
                       //添加DDE数据项为全局原子
           hDdeData = GlobalAlloc (GHND | GMEM_DDESHARE,
                         sizeof (DDEDATA) + strlen (DDEbuf)) ;
               /*给采样数据分配全局内存块*/
           lpDdeData = (DDEDATA FAR *) GlobalLock (hDdeData) ;
           lpDdeData->fResponse = 0 ;
           lpDdeData->fRelease  = 1 ;
           lpDdeData->fAckReq   = 0 ;
           lpDdeData->cfFormat  = CF_TEXT ;
                          //采样数据为文本格式
           lstrcpy ((LPSTR) lpDdeData->Value,DDEbuf) ;
               /*将采样数据从缓冲器放至DDE内存*/
           GlobalUnlock (hDdeData) ;
           if(!PostMessage(hwndClient,WM_DDE_DATA,hwnd,
              MAKELONG(hDdeData,aItem)))  //发送DDE数据
           {
            GlobalFree(hDdeData); 
                  //若发送失败,则释放资源
            GlobalDeleteAtom(aItem);        
             }
             }
            return 0;
   case WM_DDE_INITIATE:        //DDE初始化
          hwndClient=wParam;
          hdc=GetDC(hwnd);
          aAppName=GlobalAddAtom(szAppName);
          aTopic=GlobalAddAtom(szDdeTopic);
          if ((LOWORD (lParam) == NULL || LOWORD (lParam) == aAppName)
            && (HIWORD (lParam) == NULL || HIWORD (lParam) == aTopic))
          {           // 发送服务程序名和主题名
        SendMessage(hwndClient,WM_DDE_ACK,hwnd,
                       MAKELONG(aAppName,aTopic));
               } 
          else
          {
           GlobalDeleteAtom (aAppName) ;
           GlobalDeleteAtom (aTopic) ;
              }
           return 0 ;
   case WM_DDE_TERMINATE:     //关闭DDE服务
        hwndClient=wParam;      
        PostMessage (hwndClient, WM_DDE_TERMINATE, hwnd, 0L) ;
        return 0 ;
   case WM_DDE_ADVISE:        //建立DDE服务
        DdeAdviseStatus=1;           //已建立DDE服务标志
        hwndClient=wParam;
        aItem=HIWORD(lParam);
        if(!PostMessage(hwndClient,  WM_DDE_ACK,  hwnd,
          MAKELONG(0x8000,aItem)))          //发送DDE应答
        {
         GlobalDeleteAtom(aItem);
                }
         return 0;
   case WM_DESTROY:             //程序失败处理
        PostQuitMessage (0) ;
        return 0 ;
           }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
     }

2. 建 立VFP 环 境 下 的 客 户 端 程 序

---- 首 先 建 立 和 服 务 程 序 之 间 的 通 道, 然 后 将 服 务 程 序 传 递 来 的 数 据 放 至 字 符 串DATA 内, 再 通 过 自 定 义 函 数recdata 将 采 样 数 据 存 入 数 据 库 中 待 用。 下 面 是 客 户 程 序 清 单。
PROCEDURE   getdata
PUBLIC  mchannel
mchannel = DDEInitiate('PFBCdas','AD001') 
&&建立客户程序和服务程序间的通道 
IF   mchannel !=-1
  =DDEadvise(mchannel,'DATA','recdata',2)
                &&建立DDE热连接,数据项为DATA
ENDIF
RETURN

PROCEDURE  recdata      &&用户自定义函数
PARAMETERS  channel,action,item,data,format,status
PRIVATE   newdata
newdata=""
IF  action='ADVISE'        &&若连接成功
   newdata=DATA            &&将采样数据传递至newdata
   do process with newdata      &&调用数据处理程序 
ELSE
   IF  action='TERMINATE'                       
        = DDETerminate(mchannel)   &&关闭通道
   ENDIF
ENDIF
RETURN

PROCEDURE    process
PARAMETER    newdata
PRIVATE  i,loc1,loc2
DIMENSION  gdata(98)
 * 从字符串 newdata中取采样值
gdata(97)=CTOD(left(newdata,8))        &&取日期值
gdata(98)=SUBSTR(newdata,10,8)         &&取时间值
* 从字符串newdata的相邻逗号间取采样值,
                    并将采样值转化为毫伏值
FOR  i=3  TO  98
   loc1=ATC(",", newdata, (i-1))
   loc2=ATC(",", newdata, (i))
   gdata(i-2)=VAL(SUBSTR(newdata,(loc1+1),(loc2-loc1)))
                   *0.024414062
ENDFOR
IF  USED("onedata")
        SELECT  onedata
ELSE
        SELECT  0
USE  onedata  ALIAS  onedata
                    &&打开数据库onedata
ENDIF
APPEND  FROM  ARRAY  gdata
               &&将数组gdata中的转换数据添加到数据库中 
RETURN  
---- 联 系 地 址: 南 京 市 东 南 大 学 热 能 工 程 研 究 所
---- 邮 编:210096
---- 联 系 人: 周 卫 平
---- 电 话:(025)3794191
---- EMAIL 地 址:qhye@seu.edu.cn