/******************************************************************************* * File Name : NTRIPClient.c * Description : * Created on : 2022-03-29 * Author : ¶Å¼ü *******************************************************************************/ /******************************************************************************* * Include Files * *******************************************************************************/ #include "NTRIPClient.h" #include "stdlib.h" #include "string.h" #include "HIDO_Util.h" #include "HIDO_Debug.h" #include "HIDO_ArraryQueue.h" #include "HIDO_Timer.h" #include "HIDO_Base64.h" #include "Socket.h" #include "Internet.h" /******************************************************************************* * Macro * *******************************************************************************/ #define RECV_BUFF_SIZE (4096) #define STRX(x) #x #define STR(x) STRX(x) #define __debug_info__ __FILE__ ":" STR(__LINE__) /******************************************************************************* * Type Definition * *******************************************************************************/ typedef enum { CLIENT_STATE_IDLE, CLIENT_STATE_CONNECT_SOUCE_LIST_SERVER, CLIENT_STATE_GET_SOUCE_LIST, CLIENT_STATE_GET_RTCM_DATA, CLIENT_STATE_CONNECT_RTCM_DATA_SERVER, CLIENT_STATE_GET_RTCM_DATA_SUCCESS, CLIENT_STATE_MAX, } E_ClientState; typedef enum { RECV_STATE_HEAD, RECV_STATE_BODY, } E_RecvState; typedef struct { HIDO_CHAR m_acName[32]; HIDO_CHAR m_acFormat[32]; HIDO_BOOL m_bNeedNMEA; } ST_MountpointAttrib; /******************************************************************************* * Local Variable * *******************************************************************************/ static E_ClientState l_eClientState = CLIENT_STATE_IDLE; static E_RecvState l_eRecvState = RECV_STATE_HEAD; static HIDO_INT32 l_i32SockID = 0; static HIDO_UINT32 l_u32TimerID = 0; static FN_NTRIPClientCallback l_fnClientCallback; static HIDO_VOID *l_pPrivateArg = HIDO_NULL; static HIDO_UINT32 l_u32Port = 80; static HIDO_CHAR l_acHost[64]; static HIDO_CHAR l_acUsernamePassword[64]; static HIDO_CHAR l_acScourceName[32]; static ST_MountpointAttrib l_stMountpointAttrib; static HIDO_CHAR l_acAuthorization[128]; static HIDO_CHAR l_acSendBuff[1024]; static HIDO_UINT32 l_u32SendBuffLen; static HIDO_CHAR l_acRecvBuff[RECV_BUFF_SIZE + 1]; static HIDO_UINT32 l_u32RecvLen = 0; static HIDO_UINT32 l_u32HTTPRecvTotalLen = 0; static HIDO_UINT32 l_u32HTTPRespCode = 0; static HIDO_UINT32 l_u32HTTPContentLength = 0; static HIDO_UINT8 *l_pu8HTTPRespBody = HIDO_NULL; static const HIDO_CHAR *l_apcStateName[CLIENT_STATE_MAX] = { "IDLE", "CONNECT_SOUCE_LIST_SERVER", "GET_SOUCE_LIST", "GET_RTCM_DATA", "CONNECT_RTCM_DATA_SERVER", "GET_RTCM_DATA_SUCCESS", } ; /******************************************************************************* * Local Function Declaration * *******************************************************************************/ /******************************************************************************* * Local Function * *******************************************************************************/ /******************************************************************************* * Function Name : ClientCallback * Description : * Input : * Output : * Return : * Author : ¶Å¼ü * Modified Date: : 2022-03-29 *******************************************************************************/ static HIDO_INT32 SetClientState(E_ClientState _eClientState) { if(_eClientState < CLIENT_STATE_MAX) { HIDO_Debug("ClientState from [%s] change to [%s]\r\n", l_apcStateName[l_eClientState], l_apcStateName[_eClientState]); l_eClientState = _eClientState; } return HIDO_OK; } /******************************************************************************* * Function Name : ClientCallback * Description : * Input : * Output : * Return : * Author : ¶Å¼ü * Modified Date: : 2022-03-29 *******************************************************************************/ static HIDO_INT32 ClientCallback(HIDO_UINT32 _u32Code, HIDO_UINT8 *_pu8Data, HIDO_UINT32 _u32Len) { if(l_fnClientCallback) { l_fnClientCallback(_u32Code, _pu8Data, _u32Len, l_pPrivateArg); } return HIDO_OK; } /******************************************************************************* * Function Name : TimeOutCallback * Description : * Input : * Output : * Return : * Author : ¶Å¼ü * Modified Date: : 2022-03-29 *******************************************************************************/ static HIDO_VOID HandleError(HIDO_CHAR *_pcDebugInfo) { HIDO_Debug("Error occurred @%s\r\n", _pcDebugInfo); if(l_eClientState != CLIENT_STATE_IDLE) { if(Socket_GetSocketState(l_i32SockID) != SOCKET_STATE_CLOSED) { Socket_Close(l_i32SockID); } else { l_eClientState = CLIENT_STATE_IDLE; } } HIDO_TimerCancel(l_u32TimerID); } /******************************************************************************* * Function Name : TimeOutCallback * Description : * Input : * Output : * Return : * Author : ¶Å¼ü * Modified Date: : 2022-03-29 *******************************************************************************/ static HIDO_VOID TimeOutCallback(HIDO_VOID *_pArg) { HIDO_Debug("NTRIP Client time out\r\n"); HandleError(__debug_info__); } /******************************************************************************* * Function Name : * Description : * Input : * Output : * Return : * Author : www.hido-studio.com * Modified Date: : 2022Äê3ÔÂ29ÈÕ *******************************************************************************/ static HIDO_INT32 GetMountpointFromSourceTable(ST_MountpointAttrib *_pstAttrib, HIDO_CHAR *_pcDefaultName, HIDO_CHAR *_pcSourceTable, HIDO_UINT32 _u32Len) { HIDO_CHAR *pcSplitStr[20]; HIDO_UINT32 u32Cnt = 0; HIDO_CHAR *pcTargetLine = HIDO_NULL; HIDO_CHAR *pcLine = _pcSourceTable; HIDO_UINT32 u32TmpLen = _u32Len; HIDO_CHAR *pcTmp = HIDO_NULL; if((HIDO_NULL == _pstAttrib) || (HIDO_NULL == _pcDefaultName) || (HIDO_NULL == _pcSourceTable)) { return HIDO_ERR; } while((pcTmp = HIDO_UtilStrnstr(pcLine, "\r\n", u32TmpLen)) != HIDO_NULL) { *pcTmp = '\0'; u32Cnt = HIDO_UtilStrSplit(pcLine, ';', pcSplitStr, HIDO_ARRARY_COUNT(pcSplitStr)); if(u32Cnt > 18) { if(strcmp(pcSplitStr[0], "STR") != 0) { continue; } if(HIDO_NULL == pcTargetLine) { pcTargetLine = pcLine; HIDO_UtilSnprintf(_pstAttrib->m_acName, sizeof(_pstAttrib->m_acName), pcSplitStr[1]); HIDO_UtilSnprintf(_pstAttrib->m_acFormat, sizeof(_pstAttrib->m_acFormat), pcSplitStr[2]); _pstAttrib->m_bNeedNMEA = atoi(pcSplitStr[11]) == 0 ? HIDO_FALSE : HIDO_TRUE; } if(strcmp(pcSplitStr[2], _pcDefaultName) == 0) { HIDO_UtilSnprintf(_pstAttrib->m_acName, sizeof(_pstAttrib->m_acName), pcSplitStr[1]); HIDO_UtilSnprintf(_pstAttrib->m_acFormat, sizeof(_pstAttrib->m_acFormat), pcSplitStr[3]); _pstAttrib->m_bNeedNMEA = atoi(pcSplitStr[11]) == 0 ? HIDO_FALSE : HIDO_TRUE; return HIDO_OK; } } u32TmpLen -= ((pcTmp + 2) - pcLine); pcLine = pcTmp + 2; } if(HIDO_NULL == pcTargetLine) { return HIDO_ERR; } return HIDO_OK; } /******************************************************************************* * Function Name : SendGetSourceTableRequest * Description : * Input : * Output : * Return : * Author : ¶Å¼ü * Modified Date: : 2022-03-29 *******************************************************************************/ static HIDO_INT32 SendGetSourceTableRequest(void) { l_u32SendBuffLen = HIDO_UtilSnprintf(l_acSendBuff, sizeof(l_acSendBuff), "GET / HTTP/1.0\r\n" \ "User-Agent: NTRIP LefebureNTRIPClient/20131124\r\n" \ "Accept: */*\r\n" \ "Connection: close\r\n" \ "Authorization: Basic %s\r\n" \ "\r\n", l_acAuthorization); return Socket_Send(l_i32SockID, (HIDO_UINT8 *)l_acSendBuff, l_u32SendBuffLen); } /******************************************************************************* * Function Name : SendGetRTCMDataRequest * Description : * Input : * Output : * Return : * Author : ¶Å¼ü * Modified Date: : 2022-03-29 *******************************************************************************/ static HIDO_INT32 SendGetRTCMDataRequest(void) { l_u32SendBuffLen = HIDO_UtilSnprintf(l_acSendBuff, sizeof(l_acSendBuff), "GET /%s HTTP/1.0\r\n" \ "User-Agent: NTRIP LefebureNTRIPClient/20131124\r\n" \ "Accept: */*\r\n" \ "Connection: close\r\n" \ "Authorization: Basic %s\r\n" \ "\r\n", l_stMountpointAttrib.m_acName, l_acAuthorization); return Socket_Send(l_i32SockID, (HIDO_UINT8 *)l_acSendBuff, l_u32SendBuffLen); } /******************************************************************************* * Function Name : HandleRecvData * Description : * Input : * Output : * Return : * Author : ¶Å¼ü * Modified Date: : 2022-03-29 *******************************************************************************/ static HIDO_INT32 HandleRecvData(HIDO_UINT8 *_pu8Data, HIDO_UINT32 _u32Len) { if (CLIENT_STATE_GET_SOUCE_LIST == l_eClientState) { if(200 == l_u32HTTPRespCode) { if(HIDO_NULL == _pu8Data) { HandleError(__debug_info__); return HIDO_ERR; } _pu8Data[_u32Len] = '\0'; if(GetMountpointFromSourceTable(&l_stMountpointAttrib, l_acScourceName, (HIDO_CHAR *)_pu8Data, _u32Len) != HIDO_OK) { HandleError(__debug_info__); return HIDO_ERR; } SetClientState(CLIENT_STATE_CONNECT_RTCM_DATA_SERVER); Socket_Connect(l_i32SockID, l_acHost, l_u32Port); } else { HandleError(__debug_info__); return HIDO_ERR; } } else if (CLIENT_STATE_GET_RTCM_DATA == l_eClientState) { if(200 == l_u32HTTPRespCode) { SetClientState(CLIENT_STATE_GET_RTCM_DATA_SUCCESS); l_u32RecvLen = 0; HIDO_TimerStart(l_u32TimerID, HIDO_TIMER_TYPE_ONCE, HIDO_TIMER_TICK_S(60), TimeOutCallback, HIDO_NULL); } else { HandleError(__debug_info__); return HIDO_ERR; } } else if (CLIENT_STATE_GET_RTCM_DATA_SUCCESS == l_eClientState) { if(HIDO_NULL == _pu8Data) { HandleError(__debug_info__); return HIDO_ERR; } HIDO_TimerStart(l_u32TimerID, HIDO_TIMER_TYPE_ONCE, HIDO_TIMER_TICK_S(60), TimeOutCallback, HIDO_NULL); ClientCallback(NTRIP_CODE_RTCM_DATA, _pu8Data, _u32Len); } return HIDO_OK; } /******************************************************************************* * Function Name : OnRecvData * Description : * Input : * Output : * Return : * Author : ¶Å¼ü * Modified Date: : 2022-03-29 *******************************************************************************/ static HIDO_INT32 OnRecvData(void) { HIDO_INT32 i32Ret = 0; HIDO_UINT32 u32RecvLen = 0; do { i32Ret = Socket_Recv(l_i32SockID, (HIDO_UINT8 *) (l_acRecvBuff + l_u32RecvLen), RECV_BUFF_SIZE - l_u32RecvLen, &u32RecvLen); if ((i32Ret != HIDO_OK) || (0 == u32RecvLen)) { break; } l_u32RecvLen += u32RecvLen; if(l_u32RecvLen >= RECV_BUFF_SIZE) { HandleError(__debug_info__); break; } if(CLIENT_STATE_GET_RTCM_DATA_SUCCESS == l_eClientState) { HandleRecvData((HIDO_UINT8 *)l_acRecvBuff, l_u32RecvLen); l_u32RecvLen = 0; continue; } else if(CLIENT_STATE_GET_RTCM_DATA == l_eClientState) { l_pu8HTTPRespBody = (HIDO_UINT8 *) HIDO_UtilStrnstr(l_acRecvBuff, "\r\n", u32RecvLen); if (l_pu8HTTPRespBody != HIDO_NULL) { if (HIDO_UtilParseFormat((HIDO_UINT8 *) l_acRecvBuff, u32RecvLen, "%* %d %*\r\n", &l_u32HTTPRespCode) != 3) { HandleError(__debug_info__); break; } HandleRecvData(HIDO_NULL, 0); } } else if(CLIENT_STATE_GET_SOUCE_LIST == l_eClientState) { if (RECV_STATE_HEAD == l_eRecvState) { l_pu8HTTPRespBody = (HIDO_UINT8 *) HIDO_UtilStrnstr(l_acRecvBuff, "\r\n\r\n", u32RecvLen); if (l_pu8HTTPRespBody != HIDO_NULL) { HIDO_CHAR *pcKeyName = HIDO_NULL; l_pu8HTTPRespBody += 4; if (HIDO_UtilParseFormat((HIDO_UINT8 *) l_acRecvBuff, u32RecvLen, "%* %d %*\r\n", &l_u32HTTPRespCode) != 3) { HandleError(__debug_info__); break; } pcKeyName = HIDO_UtilStrnstr(l_acRecvBuff, "Content-Length: ", u32RecvLen); if (pcKeyName != HIDO_NULL) { pcKeyName += strlen("Content-Length: "); while(*pcKeyName == ' ') { pcKeyName++; } if (HIDO_UtilParseFormat((HIDO_UINT8 *) pcKeyName, u32RecvLen - (pcKeyName - l_acRecvBuff), "%d\r\n", &l_u32HTTPContentLength) != 1) { HandleError(__debug_info__); break; } l_u32HTTPRecvTotalLen = l_pu8HTTPRespBody - (HIDO_UINT8 *) l_acRecvBuff + l_u32HTTPContentLength; l_eRecvState = RECV_STATE_BODY; if (l_u32RecvLen >= l_u32HTTPRecvTotalLen) { HandleRecvData(l_pu8HTTPRespBody, l_u32HTTPContentLength); break; } } else { HandleError(__debug_info__); break; } } } else { if (l_u32RecvLen >= l_u32HTTPRecvTotalLen) { HandleRecvData(l_pu8HTTPRespBody, l_u32HTTPContentLength); } } } } while (HIDO_TRUE); return HIDO_OK; } /******************************************************************************* * Function Name : NTRIPClient_SocketEventProc * Description : * Input : * Output : * Return : * Author : ¶Å¼ü * Modified Date: : 2020Äê11ÔÂ14ÈÕ *******************************************************************************/ static void SocketEventProc(HIDO_INT32 _i32SockID, E_SocketEvent _eEvent, HIDO_VOID *_pArg) { switch (_eEvent) { case SOCKET_EVENT_CONNECT_FAILED: { if((CLIENT_STATE_CONNECT_SOUCE_LIST_SERVER == l_eClientState) || (CLIENT_STATE_CONNECT_RTCM_DATA_SERVER == l_eClientState)) { SetClientState(CLIENT_STATE_IDLE); ClientCallback(NTRIP_CODE_CONNECT_FAILED, HIDO_NULL, 0); } break; } case SOCKET_EVENT_CONNECTED: { l_u32RecvLen = 0; if(CLIENT_STATE_CONNECT_SOUCE_LIST_SERVER == l_eClientState) { SendGetSourceTableRequest(); l_eRecvState = RECV_STATE_HEAD; SetClientState(CLIENT_STATE_GET_SOUCE_LIST); HIDO_TimerStart(l_u32TimerID, HIDO_TIMER_TYPE_ONCE, HIDO_TIMER_TICK_S(30), TimeOutCallback, HIDO_NULL); } else if(CLIENT_STATE_CONNECT_RTCM_DATA_SERVER == l_eClientState) { SendGetRTCMDataRequest(); l_eRecvState = RECV_STATE_HEAD; SetClientState(CLIENT_STATE_GET_RTCM_DATA); HIDO_TimerStart(l_u32TimerID, HIDO_TIMER_TYPE_ONCE, HIDO_TIMER_TICK_S(30), TimeOutCallback, HIDO_NULL); } break; } case SOCKET_EVENT_CLOSED: { if(CLIENT_STATE_CONNECT_RTCM_DATA_SERVER == l_eClientState) { break; } HIDO_TimerCancel(l_u32TimerID); if(l_eClientState != CLIENT_STATE_IDLE) { SetClientState(CLIENT_STATE_IDLE); ClientCallback(NTRIP_CODE_DISCONNECT, HIDO_NULL, 0); } break; } case SOCKET_EVENT_RECV_DATA: { OnRecvData(); break; } default: { break; } } } /******************************************************************************* * Global Function * *******************************************************************************/ /******************************************************************************* * Function Name : NTRIPClient_Connect * Description : NTRIP Connect * Input : _pcHost µØÖ· * : _u16Port ¶Ë¿Ú * : _fnResponseCallback HTTPÏìÓ¦»Øµ÷º¯Êý * : _pArg ²ÎÊý * Output : Mone * Return : HIDO_OK ³É¹¦,HIDO_ERR ʧ°Ü * Author : ¶Å¼ü * Modified Date: : 2021Äê5ÔÂ3ÈÕ *******************************************************************************/ HIDO_INT32 NTRIPClient_Connect(HIDO_CHAR *_pcHost, HIDO_UINT16 _u16Port, HIDO_CHAR *_pcUsername, HIDO_CHAR *_pcPassword, HIDO_CHAR *_pcSourceName, FN_NTRIPClientCallback _fnCallback, HIDO_VOID *_pArg) { HIDO_UINT32 u32Len = 0; if(l_eClientState != CLIENT_STATE_IDLE) { return HIDO_ERR; } if((HIDO_NULL == _pcHost) || (HIDO_NULL == _fnCallback) || (HIDO_NULL == _pcUsername) || (HIDO_NULL == _pcPassword)) { return HIDO_ERR; } HIDO_UtilSnprintf(l_acHost, sizeof(l_acHost), _pcHost); HIDO_UtilSnprintf(l_acScourceName, sizeof(l_acScourceName), _pcSourceName); u32Len = HIDO_UtilSnprintf(l_acUsernamePassword, sizeof(l_acUsernamePassword), "%s:%s", _pcUsername, _pcPassword); HIDO_Base64Encode((HIDO_UINT8 *)l_acUsernamePassword, u32Len, l_acAuthorization, sizeof(l_acAuthorization)); l_u32Port = _u16Port; l_fnClientCallback = _fnCallback; l_pPrivateArg = _pArg; SetClientState(CLIENT_STATE_CONNECT_SOUCE_LIST_SERVER); Socket_Connect(l_i32SockID, l_acHost, l_u32Port); return HIDO_OK; } /******************************************************************************* * Function Name : NTRIPClient_ReportGGA * Description : * Input : None * Output : None * Return : HIDO_OK ³É¹¦,HIDO_ERR ʧ°Ü * Author : ¶Å¼ü * Modified Date: : 2022-03-29 *******************************************************************************/ HIDO_INT32 NTRIPClient_ReportGGA(HIDO_UINT8 *_pu8Data, HIDO_UINT32 _u32Len) { if(CLIENT_STATE_GET_RTCM_DATA_SUCCESS == l_eClientState) { Socket_Send(l_i32SockID, _pu8Data, _u32Len); } return HIDO_OK; } /******************************************************************************* * Function Name : NTRIPClient_Poll * Description : * Input : None * Output : None * Return : HIDO_OK ³É¹¦,HIDO_ERR ʧ°Ü * Author : ¶Å¼ü * Modified Date: : 2022-03-29 *******************************************************************************/ HIDO_INT32 NTRIPClient_Poll(HIDO_VOID) { return HIDO_OK; } /******************************************************************************* * Function Name : NTRIPClient_Init * Description : * Input : None * Output : None * Return : HIDO_OK ³É¹¦,HIDO_ERR ʧ°Ü * Author : ¶Å¼ü * Modified Date: : 2022-03-29 *******************************************************************************/ HIDO_INT32 NTRIPClient_Init(HIDO_VOID) { l_eClientState = CLIENT_STATE_IDLE; if(Socket_Create(&l_i32SockID, SOCKET_TYPE_TCP, SocketEventProc, HIDO_NULL) != HIDO_OK) { return HIDO_ERR; } if(HIDO_TimerCreate(&l_u32TimerID) != HIDO_OK) { return HIDO_ERR; } return HIDO_OK; }