A5的消息桥

A5里有个叫CMsgBridge的消息桥,在软件的结构设计和模块设计方面很有研究价值

  1 /*!----------------------------------------------------
  2 \brief        MsgBridge客户类型
  3         ----------------------------------------------------------*/
  4 typedef enum
  5 {
  6     MSB_TYPE_NULL,/*!< 空类型 */
  7     MSB_TYPE_HOST,/*!< 主类 */
  8     MSB_TYPE_SLAVE,/*!< 从类 */
  9 }MSB_ELEMENT_TYPE;
 10 
 11 typedef DWORD (* MSB_INPUT_API)(BYTE,DWORD,DWORD,PBYTE);
 12 /*!< Host的消息输入接口格式 */
 13 typedef DWORD (* MSB_INPUT_API_WPARS)(BYTE,DWORD,DWORD,PBYTE,LPVOID);
 14 /*!< 带有参数的Host 的消息输入接口格式 */
 15 typedef void *(* MSB_OUTPUT_API)(void);
 16 /*!< Host 提供给Slave的消息读取接口格式 */
 17 typedef void *(* MSB_OUTPUT_API_WPARS)(LPVOID);
 18 /*!< 带有参数的Host 提供给Slave的消息读取接口格式 */
 19 
 20 typedef struct
 21 {
 22     OS_MUT sCrtiticalSec;
 23     BYTE byInUse;
 24 }MSB_SYNC_ELE;
 25 
 26 /*!----------------------------------------------------
 27 \brief        MSB_PIPES_INFO
 28         ----------------------------------------------------------*/
 29 typedef struct
 30 {
 31     BYTE byNum;/*!< 客户主类计数 */
 32     class CMsgBridge *p_cEle[MSB_MAX_HOST_NUM];/*!< 客户类数组 */
 33 }MSB_PIPES_INFO;
 34 
 35 //class MSGBRIDGE_API CMsgBridge
 36 class CMsgBridge
 37 { 
 39     protected:
 41         MSB_ELEMENT_TYPE m_ePipType;
 42         
 43         static MSB_SYNC_ELE m_SCriticals[MSB_MAX_HOST_NUM];
 44         MSB_SYNC_ELE *m_pSCritical;
 45         static BOOLEAN m_boCriticalsIni;
 46         
 47         static MSB_PIPES_INFO m_sPipes;
 48         
 49         static BOOLEAN m_boInit;
 50         
 51         MSB_INPUT_API m_fpMsgIn;
 52         /*!< 输入接口 */
 53         MSB_OUTPUT_API m_fpMsgOut;
 54         /*!< 输出接口 */
 55         MSB_INPUT_API_WPARS m_fpMsgIn_wPars;
 56         /*!< 带有参数的输入接口 */
 57         MSB_OUTPUT_API_WPARS m_fpMsgOut_wPars;
 58         /*!< 带有参数的输出接口 */
 59         BYTE m_strName[3];
 60         LPVOID m_lpPars;
 61         
 62         BOOLEAN m_boHostNoInput;
 63         BOOLEAN m_boHostNoOutput;
 64         
 65         BOOLEAN Create_Msg_Pipe(PBYTE p_byName,MSB_INPUT_API fp_MsgIn,MSB_OUTPUT_API fp_MsgOut,LPVOID pPars);
 66         
 67         BOOLEAN Search_Host(void);
 68         /*!< 搜索主类 */
 69         BOOLEAN Creat_Client(PBYTE p_byName);
 70         /*!< 创建客户类 */
 71         
 72         BOOLEAN ReleaseSync(void);
 73         void GetSync(CMsgBridge *pCSyncMSB);
 74         void Lock(void);
 75         void SlaveGetSync(CMsgBridge *pCHost);
 76         void UnLock(void);
 77         
 78     public:
 79 
 80         ~CMsgBridge();
 81         /*------------------------------------
 82             客户类:
 83             ------------------------------------*/  
 85         /*!---------------------------------------------------------------------
 87         \brief        客户类构造函数        
 89         \param    [in]    p_byName 3字符客户类名称 
 91         \param    [out]    p_boRet
 97         \remarks        客户类使用此构造函数        
101                 ---------------------------------------------------------------------*/
102         CMsgBridge(PBYTE p_byName,BOOLEAN *p_boRet);
103         /*!---------------------------------------------------------------------     
105         \brief        消息发送接口          
107         \param    [in]    BYTE 消息id      
109         \param    [in]    DWORD 参数1       
111         \param    [in]    DWORD 参数2       
113         \param    [in]    DWORD 参数指针3     
125                 ---------------------------------------------------------------------*/
126         DWORD Send_Msg(BYTE byCmd,DWORD dwDat1 = 0,DWORD dwDat2 = 0,PBYTE p_byDat3 = 0);
127         /*!---------------------------------------------------------------------       
129         \brief        获取主类输出的消息
143         ---------------------------------------------------------------------*/
144         void *Check_MsgOut(void);
145         /*------------------------------------
146             主类:
147             ------------------------------------*/
148         /*!---------------------------------------------------------------------      
150         \brief        Host类: 只带有输入接口        
152         \param    [in]    主类名称 
154         \param    [in]    输入接口
156         \param    [out]    BOOLEAN 
158         \param    [out]    LPVOID 参数
168                 ---------------------------------------------------------------------*/
169         CMsgBridge(PBYTE p_byName,MSB_INPUT_API fp_MsgIn,BOOLEAN *p_boRet,LPVOID pPars = NULL);
170         /*!---------------------------------------------------------------------     
172         \brief        Host类: 只带有输出接口            
174         \param    [in]    主类名称      
176         \param    [in]    输出接口      
178         \param    [out]    BOOLEAN     
180         \param    [out]    LPVOID 参数
190                 ---------------------------------------------------------------------*/
191         CMsgBridge(PBYTE p_byName,MSB_OUTPUT_API fp_MsgOut,BOOLEAN *p_boRet,LPVOID pPars = NULL);
192         /*!---------------------------------------------------------------------
194         \brief        Host类:带有输入和输出接口 
196         \param    [in]    主类名称 
198         \param    [in]    输入接口     
200         \param    [in]    输出接口         
202         \param    [out]    BOOLEAN         
204         \param    [out]    LPVOID 参数
214                 ---------------------------------------------------------------------*/
215         CMsgBridge(PBYTE p_byName,MSB_INPUT_API fp_MsgIn,MSB_OUTPUT_API fp_MsgOut,BOOLEAN *p_boRet,LPVOID pPars = NULL);
216         
217     public:
218     /* 严禁外部使用 */
219         static BOOLEAN InitialSync(void);
220 //        static BOOLEAN ReleaseAllSync(void);
221         void InitializeCriticalSection(OS_MUT &sCrtiticalSec);
222 };

在CMsB_private.h里对CLASS里的4个static变量进行定义:

 

使用的时候要分别创建HOST和SLAVE,以A5里的SPI的消息桥为例:

// 先创建其HOST对象

void HAL_Spi_Initial(void)
{
    g_SpiSRV_psSpiMsg = new CMsgBridge((PBYTE)SPI_SRV_MSG,(MSB_INPUT_API)&HAL_SPI_InputMsg,(MSB_OUTPUT_API)&SPISrv_ReadOutputMsg,(BOOL *)&boRet, NULL);
}
 
// 再创建SPI消息桥的SLAVE对象
BOOL APP_Init(BOOLEAN ifService)
{
    BOOL boRet;
  
    g_App_psAppVars.pcSpiMsg = new CMsgBridge((PBYTE)SPI_SRV_MSG,&boRet);
}

在创建HOST对象的时候,在它的 MSB_OUTPUT_API 和MSB_INPUT_API里使用的是已经定义好的全局fifo,用来存储消息,大小自定义。

HOST的构造函数最终调用的是下面这个函数

 1 BOOLEAN CMsgBridge::Create_Msg_Pipe(PBYTE p_byName,MSB_INPUT_API fp_MsgIn,MSB_OUTPUT_API fp_MsgOut,LPVOID pPars)
 2 {
 3     DWORD i;    
 4     BOOLEAN ret = FALSE;    
 5     if(m_boInit == FALSE) 
 6     {
 7         m_sPipes.byNum = 0;        
 8         ::memset(&m_sPipes.p_cEle, 0, sizeof(CMsgBridge *) * MSB_MAX_HOST_NUM);        
 9         InitialSync();        
10         m_boInit = TRUE;
11     }
12     if(m_sPipes.byNum < (MSB_MAX_HOST_NUM - 1))
13     {
14         if(((p_byName[0] >= 'a') && (p_byName[0] <= 'z'))        
15          || ((p_byName[0] >= 'A') && (p_byName[0] <= 'Z')))
16         {
17             for(i = 0 ; i < MSB_MAX_HOST_NUM ; i++)
18             {
19                 if(!m_sPipes.p_cEle[i])
20                 {
21                     m_sPipes.byNum++;                    
22                     m_sPipes.p_cEle[i] = this;            
23                     m_ePipType = MSB_TYPE_HOST;                    
24                     m_strName[0] = p_byName[0];                    
25                     m_strName[1] = p_byName[1];                    
26                     m_strName[2] = p_byName[2];                    
27                     if(pPars != NULL)
28                     {
29                         m_fpMsgIn_wPars = (MSB_INPUT_API_WPARS)fp_MsgIn;                        
30                         m_fpMsgOut_wPars = (MSB_OUTPUT_API_WPARS)fp_MsgOut;                        
31                         m_fpMsgIn = 0;                        
32                         m_fpMsgOut = 0;
33                     }
34                     else
35                     {
36                         m_fpMsgIn = fp_MsgIn;                        
37                         m_fpMsgOut = fp_MsgOut;                        
38                         m_fpMsgIn_wPars = 0;                        
39                         m_fpMsgOut_wPars = 0;
40                     }
41                     m_lpPars = pPars;                    
42                     m_boHostNoInput = (fp_MsgIn)? FALSE : TRUE;                    
43                     m_boHostNoOutput = (fp_MsgOut)? FALSE : TRUE;                    
44                     GetSync(0);                    
45                     ret = TRUE;                    
46                     break;
47                 }
48             }
49         }
50     }
51     return ret;
52 }

SLAVE的构造函数最终调用的是下面这个函数

 1 BOOLEAN CMsgBridge::Creat_Client(PBYTE p_byName)
 2 {
 3     BOOLEAN ret = FALSE;    
 4     if(((p_byName[0] >= 'a') && (p_byName[0] <= 'z')) || ((p_byName[0] >= 'A') && (p_byName[0] <= 'Z')))
 5     {
 6         m_ePipType = MSB_TYPE_SLAVE;            
 7         m_strName[0] = p_byName[0];        
 8         m_strName[1] = p_byName[1];            
 9         m_strName[2] = p_byName[2];        
10         m_boHostNoInput = FALSE;            
11         m_boHostNoOutput = FALSE;        
12         if(Search_Host() == FALSE)
13         {
14             m_fpMsgIn = 0;            
15             m_fpMsgIn_wPars = 0;            
16             m_fpMsgOut = 0;            
17             m_fpMsgOut_wPars = 0;
18         }
19         ret = TRUE;
20     }
21     return ret;
22 }

 

然后我们查看两个对象的句柄会发现,虽然slave仅指定了消息桥的名字,但是其内部的输入接口m_fpMsgIn 和输出接口m_fpMsgOut用的都是HOST创建时传入的。

 这是因为在创建SLAVE类时会调用Search_Host(),其会遍历已创建的HOST类,找到名字相同的HOST,然后与HOST同步。

BOOLEAN CMsgBridge::Search_Host(void)
{
    BOOLEAN ret = FALSE;    
    DWORD i;    
    CMsgBridge *p_cMsgEle;    
    for(i = 0 ; i < m_sPipes.byNum ; i++)
    {
        if((p_cMsgEle = m_sPipes.p_cEle[i]) != 0)
        {
            if((p_cMsgEle->m_strName[0] == m_strName[0])            
                && (p_cMsgEle->m_strName[1] == m_strName[1])                
                && (p_cMsgEle->m_strName[2] == m_strName[2]))
            {
                m_fpMsgIn = p_cMsgEle->m_fpMsgIn;                
                m_fpMsgIn_wPars = p_cMsgEle->m_fpMsgIn_wPars;                
                m_boHostNoInput = p_cMsgEle->m_boHostNoInput;                
                SlaveGetSync(p_cMsgEle);                
                m_fpMsgOut = p_cMsgEle->m_fpMsgOut;
                m_fpMsgOut_wPars = p_cMsgEle->m_fpMsgOut_wPars;
                m_boHostNoOutput = p_cMsgEle->m_boHostNoOutput;                
                m_lpPars = p_cMsgEle->m_lpPars;                
                ret = TRUE;                
                break;
            }
        }
    }
    return ret;
}

这样设计就像服务器与客户端的通信,但是它们共用一个共享内存,其实它们的读写函数接口也是同一个,但是展现给使用者的却是不同的接口封装。

但HOST的写并不是用的它创建时传入的HAL_SPI_InputMsg(),而且查看其实现也能看到其内部并没有写的功能

 1 DWORD HAL_SPI_InputMsg(SPI_MSG_OUT eCmd, DWORD dwDat1, DWORD dwDat2, BYTE *pbyDat3)
 2 {
 3     DWORD dwRet = 0;
 5     switch(eCmd)
 6     {
 7         default:break;
 8     }
 9     return dwRet;
10 }

HOST在写入的时候其实是用的下面这个,这样其实并不规范,应该将这个函数的实现放在上面那个函数里。

 1 /**********************************************
 2 参数:
 3 1\指令
 4 2、数据
 5 3、Index
 6 4、预留
 7 **********************************************/
 8 void SPISrv_OutputMsg(SPI_MSG_OUT eMsg, INT32 dwDat1, BYTE dwDat2, DWORD dwDat3)
 9 {
10     SPI_FUNC_MSG *psOutput;    
11     psOutput = &g_SPIFunc_sMsg[g_SPIFunc_dwMsgWr];    
12     psOutput->eMsg = eMsg;    
13     psOutput->dwDat1 = dwDat1;    
14     psOutput->dwDat2 = dwDat2;    
15     psOutput->dwDat3 = dwDat3;    
16     g_SPIFunc_dwMsgWr++;    
17     if(g_SPIFunc_dwMsgWr >= SPI_MSG_OUTPUT_MAX_NUM)
18     {
19         g_SPIFunc_dwMsgWr = 0;
20     }
21 }

正确的用法应该是在底层或者发送方使用HOST对象 g_SpiSRV_psSpiMsg->Send_Msg()来进行写入, 在上层使用g_App_psAppVars.psAdcMsg->Check_MsgOut()进行读出。这样使用两个对象分别在两个layer工作,达到了分层设计,偶何低的效果。

通过消息桥学习了这种设计模式,但是总感觉是大炮打蚊子。

如果用C来实现则更简单,就是普通的邮箱模式。

而且只创建一个HOST对象,只要保证读写函数的安全,一样能达到上述效果。这种创建一个HOST和一个SLAVE的 模式,多此一举

posted @ 2023-01-29 16:58  xjxcxjx  阅读(24)  评论(0编辑  收藏  举报