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的 模式,多此一举
本文来自博客园,作者:xjxcxjx,转载请注明原文链接:https://www.cnblogs.com/xjxcxjx/p/17072706.html,谢绝CSDN转载!