Chinaunix首页 | 论坛 | 博客
  • 博客访问: 868330
  • 博文数量: 190
  • 博客积分: 7021
  • 博客等级: 少将
  • 技术积分: 1752
  • 用 户 组: 普通用户
  • 注册时间: 2010-05-17 19:26
文章分类

全部博文(190)

文章存档

2014年(9)

2011年(32)

2010年(149)

我的朋友

分类: 嵌入式

2010-06-12 15:47:31

为了在BREW应用程序开发的过程中能够拥有更加清晰的程序流程,我们特意设计了这样的一个状态机的应用程序框架。

       我们的基本思路是这样的,划分应用程序状态的方法是每一个显示界面都作为一个状态,在进入状态的时候创建界面,同时状态机停止运转,此时等待用户在当前的界面进行操作,用户操作完成后关闭当前的界面,并启动状态机继续运行。由于BREW的每一个应用程序都是通过HandleEvent方法来获得事件的,在每一个界面等待用户输入的时候,每一个界面都对应一个界面的HandleEvent函数,用来捕获用户在这个界面的输入。这样,我们的应用程序将被分成状态和界面两种单元。

      状态机在代码上讲就是一个for(;;)循环,只有在创建一个用户界面之后才会暂时终止运行,可以实现这样功能的代码如下(可以在本书所附的示例代码Test5/FileExplorer应用目录下的AppStateMachine.c中找到这个函数):

 

/*====================================================================
函数 : CStateMachine_RunFSM
说明 : 有限状态机引擎。
参数 : pMe [in]:指向CStateMachine对象结构的指针
返回值 : 无。
备注 : 无
====================================================================*/

static void CStateMachine_RunFSM(CStateMachine *pMe)
{
    NextFSMAction nextFSMAction = NFSMACTION_WAIT;
    
    for(;;)
    {
        nextFSMAction = pMe->m_pfnState((IStateMachine *)pMe,
                                         pMe->m_pUserState);
        
        pMe->m_eWndRet = WNDRET_CREATE;
           pMe->m_wWndParam = 0;
           pMe->m_dwWndParam = 0;
           
        if (nextFSMAction == NFSMACTION_WAIT)
        {
            break;
        }
    }
} // End CStateMachine_RunFSM

 pMe->m_pfnState就是我们在应用程序中的状态函数,状态机根据这个函数的返回值nextFSMAction来判断是否暂停状态机的运行。从代码中可以看到当nextFSMActionNFSMACTION_WAIT的时候这个for循环将被break语句打破。

       我们定义的状态机结构体如下所示(下面的代码综合了AppStateMachine.h.c的内容):


// 状态处理函数返回给状态处理主函数的值类型

typedef enum _NextFSMAction{
    NFSMACTION_WAIT,
    NFSMACTION_CONTINUE
} NextFSMAction;
 
typedef NextFSMAction (*PFNSTATE)(IStateMachine *po, void *pUser);
 
typedef struct _CStateMachine{
    AEEVTBL(IStateMachine) *pvt;
    uint32 m_nRefs;
    
    // IShell interface

    IShell *m_pShell;
    
    PFNSTATE m_pfnState; // 状态处理函数

    void * m_pUserState; // 用户数据

    PFNAEEEVENT m_pfnEvent; // 事件处理函数

    void * m_pUserEvent; // 用户数据

       int m_eWndRet; // Windows返回值

       uint16 m_wWndParam; // Windows返回值word参数

       uint32 m_dwWndParam; // Windows返回值dword参数

       
       AEECLSID m_dwAppClsID; // 当前使用此接口的应用程序Class ID

       boolean m_bSuspending; // 当前状态机是否被挂起

}CStateMachine;

    应用程序中将通过一个指针变量来保存一个这样的结构体。我们将创建的用户界面叫做一个Window。状态机使用m_pfnState来获得状态,使用m_pfnEvent来处理用户Window的事件,在m_pfnState状态函数执行的时候创建Window

8.2.3实现状态机管理的接口

       为了能够管理状态机,我们必须提供一些管理状态机的接口:

       1、启动状态机,通过这个接口应用程序可以选择在何时启动这个状态机。

       2、终止状态机,通过这个接口应用程序可以选择在何时终止状态机的运行。

       3、挂起状态机,通过这个接口应用程序可以暂停状态机的运行,同时保存相关的状态数据,以便于恢复状态机的运行。

       4、恢复状态机,通过这个接口应用程序可以恢复一个被挂起的状态机继续运行。

       5、转移状态,通过这个接口应用程序可以转移到下一个状态去处理。

       6、创建一个窗口(Window),通过这个接口应用程序可以创建一个显示界面。

       7、关闭一个窗口(Window),通过这个接口应用程序可以关闭一个显示界面,同时设置相应的Window返回值给状态处理函数。

       8Window事件捕获函数,应用程序将通过这个接口将事件传递给Window的事件捕获函数。

       9、辅助函数,如获得当前的状态,以及获得当前窗口的返回值等操作。

       与上面描述功能基本相近的各个函数已经定义在了FileExplorer应用程序中的AppStateMachine.c的文件中。他们的形式如下:

 


/*====================================================================
函数 :IStateMachine_HandleEvent
描述 :状态机的事件捕获函数,在此函数中将事件传递给在当前状态中打开的窗口
参数 :po[in] 指向当前状态机结构体的指针
          eCode[in] 当前的事件代码
          wParam[in] 事件的16位参数
          dwParam[in] 事件的32位参数
返回值 :如果捕获事件则返回TRUE,否则返回FALSE
====================================================================*/

static boolean IStateMachine_HandleEvent(IStateMachine *po,
                                         AEEEvent eCode,
                                         uint16 wParam,
                                         uint32 dwParam)
{
    CStateMachine *pMe = (CStateMachine*)po;
    return pMe->m_pfnEvent?pMe->m_pfnEvent(pMe->m_pUserEvent,eCode,wParam,dwParam):FALSE;
}// End IStateMachine_HandleEvent

 
/*====================================================================
函数 :IStateMachine_Start
描述 :开始状态机的运行,状态机开始之后可以调用ISTATEMACHINE_Stop终止状态机
          的运行,或者ISTATEMACHINE_Stop挂起状态机
参数 :po[in] 指向当前状态机结构体的指针
          pfnState[in] 启动时的状态函数,同时也是状态ID
          pUser[in] 传递给状态函数的用户数据
返回值 :无
====================================================================*/

static void IStateMachine_Start(IStateMachine *po,
                                PFNSTATE pfnState,
                                void *pUser)
{
    CStateMachine *pMe = (CStateMachine*)po;
    pMe->m_pfnState = pfnState;
    pMe->m_pUserState = pUser;
    
    if(pMe->m_pfnState){
        CStateMachine_RunFSM(pMe);
    }
}// End IStateMachine_Start

 
/*====================================================================
函数 :IStateMachine_Stop
描述 :终止状态机的运行,终止之后只能使用ISTATEMACHINE_Start重新启动状态机
参数 :po[in] 指向当前状态机结构体的指针
返回值 :无
====================================================================*/

static void IStateMachine_Stop(IStateMachine *po)
{
    CStateMachine *pMe = (CStateMachine*)po;
    
    IStateMachine_Suspend(po);
    pMe->m_pfnState = NULL;
    pMe->m_pfnEvent = NULL;
}// End IStateMachine_Stop

 
/*====================================================================
函数 :IStateMachine_Suspend
描述 :挂起当前正在运行的状态机,挂起之后使用ISTATEMACHINE_Start重新启动
          状态机,或使用ISTATEMACHINE_Resume恢复当前的状态机运行
参数 :po[in] 指向当前状态机结构体的指针
返回值 :无
====================================================================*/

static void IStateMachine_Suspend(IStateMachine *po)
{
    CStateMachine *pMe = (CStateMachine*)po;
    if(pMe->m_pfnState){
        pMe->m_bSuspending = TRUE;
        IStateMachine_CloseWnd(po,0,0,0);
    }
}// End IStateMachine_Suspend

 
/*====================================================================
函数 :IStateMachine_Resume
描述 :恢复被挂起的状态机
参数 :po[in] 指向当前状态机结构体的指针
返回值 :无
====================================================================*/

static void IStateMachine_Resume(IStateMachine *po)
{
    CStateMachine *pMe = (CStateMachine*)po;
    
    if(pMe->m_pfnState){
        pMe->m_bSuspending = FALSE;
        CStateMachine_RunFSM(pMe);
    }
}// IStateMachine_Resume

 
/*====================================================================
函数 :IStateMachine_GetState
描述 :获取状态机当前的状态
参数 :po[in] 指向当前状态机结构体的指针
返回值 :无
====================================================================*/

static void* IStateMachine_GetState(IStateMachine *po)
{
    CStateMachine *pMe = (CStateMachine*)po;
       return pMe->m_pfnState;
}// IStateMachine_GetState

 
/*====================================================================
函数 :IStateMachine_MoveTo
描述 :转移当前的状态到下一个状态
参数 :po[in] 指向当前状态机结构体的指针
          pfnState[in] 状态处理函数,也是状态的ID
          pUser[in] 状态处理函数使用的用户数据
返回值 :无
====================================================================*/

static void IStateMachine_MoveTo(IStateMachine *po,
                                 PFNSTATE pfnState,
                                 void *pUser)
{
    CStateMachine *pMe = (CStateMachine*)po;
    pMe->m_pfnState = pfnState;
    pMe->m_pUserState = pUser;
}// End IStateMachine_MoveTo

 
/*====================================================================
函数 :IStateMachine_OpenWnd
描述 :打开一个窗口,用来显示用户界面和处理用户事件
参数 :po[in] 指向当前状态机结构体的指针
          HandleEvent[in] 窗口事件处理函数,也是窗口的ID
          pUser[in] 窗口事件处理函数使用的用户数据
返回值 :无
====================================================================*/

static void IStateMachine_OpenWnd(IStateMachine *po,
                                  PFNAEEEVENT HandleEvent,
                                  void *pUser)
{
    CStateMachine *pMe = (CStateMachine*)po;
    pMe->m_pfnEvent = HandleEvent;
    pMe->m_pUserEvent = pUser;
    (void)ISHELL_PostEvent( pMe->m_pShell,
                            pMe->m_dwAppClsID,
                            EVT_WND_OPEN,
                            0, 0);
}// End IStateMachine_OpenWnd

 
/*====================================================================
函数 :IStateMachine_CloseWnd
描述 :关闭当前的窗口
参数 :po[in] 指向当前状态机结构体的指针
          eWndRet[in] 窗口关闭时的返回值,状态处理函数通过这个值来决定下一步
                      怎么做
          wParam[in] 窗口返回值的16位参数
          dwParam[in] 窗口返回值的32位参数
返回值 :无
====================================================================*/

static void IStateMachine_CloseWnd(IStateMachine *po,
                                   int eWndRet,
                                   uint16 wParam,
                                   uint32 dwParam)
{
    CStateMachine *pMe = (CStateMachine*)po;
    
    if(pMe->m_pfnEvent){
        (void)ISHELL_HandleEvent(pMe->m_pShell, EVT_WND_CLOSE, 0, 0);
        pMe->m_pfnEvent = NULL;
        pMe->m_pUserEvent = NULL;
        pMe->m_eWndRet = eWndRet;
        pMe->m_wWndParam = wParam;
        pMe->m_dwWndParam = dwParam;
        
        // 状态机被挂起,等待状态机恢复或重新Start

        if(FALSE == pMe->m_bSuspending){
            CStateMachine_RunFSM(pMe);
        }
    }
}// End IStateMachine_CloseWnd

 
/*====================================================================
函数 :IStateMachine_GetWndRet
描述 :获得最近关闭窗口的返回值
参数 :po[in] 指向当前状态机结构体的指针
          *pwParam[in/out] 窗口返回值的16位参数
          *pdwParam[in/out] 窗口返回值的32位参数
返回值 :int 窗口的返回值
====================================================================*/

static int IStateMachine_GetWndRet(IStateMachine *po,
                                   uint16 *pwParam,
                                   uint32 *pdwParam)
{
    CStateMachine *pMe = (CStateMachine*)po;
       if(pwParam){
              *pwParam = pMe->m_wWndParam;
       }
 
       if(pdwParam){
              *pdwParam = pMe->m_dwWndParam;
       }
       return pMe->m_eWndRet;
}// End IStateMachine_GetWndRet

       如果我们完全使用状态机来构建一个应用程序的话,那么,我们可以在在应用程序的EVT_APP_START事件中直接调用IStateMachine_Start函数来启动状态机,在调用这个函数时需要指定初始状态;在EVT_APP_STOP事件中调用IStateMachine_Stop来终止状态机的运行;在EVT_APP_SUSPEND事件中调用IStateMachine_Suspend来挂起状态机;在EVT_APP_RESUME事件中调用IStateMachiine_Resume来恢复状态机的运行。在状态机运行的过程中,使用IStateMachine_MoveTo来转移到下一个状态,使用IStateMachine_GetState来获得当前的状态。

       在应用程序中,表示状态的是一个如下类型的函数:

typedef NextFSMAction (*PFNSTATE)(IStateMachine *po, void *pUser);

       其中,po就是传递了CStateMachine结构体的一个指针,pUser是用户在使用IStateMachine_MoveTo中指定的状态处理函数所需要传入的用户数据。在这个状态处理函数中通常判断当前的窗口(Window)返回值是什么来决定如何处理状态,这些返回值预定在下面的枚举变量中(AppStateMachine.h):


// 窗口返回值

enum{
    WNDRET_CREATE = 0,
    WNDRET_OK,
    WNDRET_CANCELED,
    WNDRET_YES,
    WNDRET_NO,
    WNDRET_USER1,
    WNDRET_USER2,
    WNDRET_USER3,
    WNDRET_USER4,
    WNDRET_USER5,
    WNDRET_MAX
};


       WNDRET_CREATE表示用户要在这个状态中创建一个窗口(Window),此时应该调用IStateMachine_OpenWnd接口打开一个窗口(Window)。调用此接口的时候,窗口(Window)的事件处理函数将接收到一个EVT_WND_OPEN事件。关闭窗口(Window)的动作是由窗口(Window)本身通过调用IStateMachine_Close来完成的,在这个接口执行的过程中将向窗口(Window)事件处理函数发送EVT_WND_CLOSE事件,同时重新启动状态机。启动状态机的时候将再次执行当前的状态处理函数,所不同的是此时执行状态处理函数的时候,窗口(Window)的返回值不同了(在关闭窗口的时候重新替换成了上面的一些定义)。此时根据窗口的返回值决定如何操作。

       到此为止您可能有这样的一个疑问,除了CStateMachine外,里面还有一个IStateMachine类型是做什么的呢?在这里呢,我先不说,具体的情况将会在第三篇扩展BREW接口中谈到。当然,有兴趣的读者可以自己试着将AppStateMachine.cAppStateMachine.h两个文件通读一遍,提前研究一下有好处。在这里唯一要说明的是,要想使用StateMachine的接口函数,需要调用AppStateMachine_New函数来获得IStateMachine的接口指针。

 

ps:以上内容来自 《深入BREW开发》 作者:焦玉海

阅读(1556) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~