/*******************************************************************************
|
* File Name : MQTTApp.c
|
* Description : MQTT应用层 - 负责数据填充、JSON序列化/反序列化
|
*******************************************************************************/
|
|
/*******************************************************************************
|
* Include Files *
|
*******************************************************************************/
|
#include "MQTTApp.h"
|
#include "MQTTClient.h"
|
#include "global_param.h"
|
#include "HIDO_Util.h"
|
#include "HIDO_Debug.h"
|
#include "HIDO_Timer.h"
|
#include "HIDO_Json.h"
|
#include "GPS.h"
|
#include "string.h"
|
#include "stdlib.h"
|
|
/*******************************************************************************
|
* Macro *
|
*******************************************************************************/
|
#define JSON_PACK(_buf, _len, _size, ...) \
|
(_len) += HIDO_UtilSnprintf((HIDO_CHAR *)(_buf) + (_len), (_size) - (_len), __VA_ARGS__)
|
|
/*******************************************************************************
|
* Type Definition *
|
*******************************************************************************/
|
|
/*******************************************************************************
|
* Local Variable *
|
*******************************************************************************/
|
static ST_MQTTAppConfig l_stAppConfig;
|
static HIDO_UINT8 l_au8JsonBuffer[2048]; // JSON缓冲区
|
|
/*******************************************************************************
|
* Local Function Declaration *
|
*******************************************************************************/
|
|
/*******************************************************************************
|
* Local Function *
|
*******************************************************************************/
|
|
/*******************************************************************************
|
* Function Name : MQTTApp_GenerateMsgID
|
* Description : 生成消息ID
|
* Input : _pcBuffer 缓冲区
|
* _u32Size 缓冲区大小
|
* _pcPrefix 前缀
|
* Output : None
|
* Return : HIDO_OK/HIDO_ERR
|
*******************************************************************************/
|
static HIDO_INT32 MQTTApp_GenerateMsgID(HIDO_CHAR *_pcBuffer, HIDO_UINT32 _u32Size, const HIDO_CHAR *_pcPrefix)
|
{
|
HIDO_UINT32 u32Timestamp = HIDO_TimerGetTick();
|
return HIDO_UtilSnprintf(_pcBuffer, _u32Size, "%s_%u", _pcPrefix, u32Timestamp);
|
}
|
|
/*******************************************************************************
|
* Function Name : MQTTApp_PackGpsJson
|
* Description : 封装GPS数据为JSON
|
* Input : _pstGpsData GPS数据
|
* Output : _pu8JsonBuf JSON缓冲区
|
* _pu32JsonLen JSON长度
|
* Return : HIDO_OK/HIDO_ERR
|
*******************************************************************************/
|
static HIDO_INT32 MQTTApp_PackGpsJson(ST_MQTTAppGpsData *_pstGpsData,
|
HIDO_UINT8 *_pu8JsonBuf,
|
HIDO_UINT32 *_pu32JsonLen)
|
{
|
HIDO_UINT32 u32Len = 0;
|
HIDO_CHAR acMsgID[64];
|
|
if (HIDO_NULL == _pstGpsData || HIDO_NULL == _pu8JsonBuf || HIDO_NULL == _pu32JsonLen)
|
{
|
return HIDO_ERR;
|
}
|
|
// 生成消息ID
|
MQTTApp_GenerateMsgID(acMsgID, sizeof(acMsgID), "hxzkgps");
|
|
// 构建JSON
|
JSON_PACK(_pu8JsonBuf, u32Len, sizeof(l_au8JsonBuffer), "{");
|
JSON_PACK(_pu8JsonBuf, u32Len, sizeof(l_au8JsonBuffer), "\"msg_id\":\"%s\"", acMsgID);
|
JSON_PACK(_pu8JsonBuf, u32Len, sizeof(l_au8JsonBuffer), ",\"timestamp\":%llu", _pstGpsData->timestamp);
|
JSON_PACK(_pu8JsonBuf, u32Len, sizeof(l_au8JsonBuffer), ",\"device_id\":\"%u\"", g_com_map[DEV_ID]);
|
JSON_PACK(_pu8JsonBuf, u32Len, sizeof(l_au8JsonBuffer), ",\"data_type\":\"gps\"");
|
JSON_PACK(_pu8JsonBuf, u32Len, sizeof(l_au8JsonBuffer), ",\"gps_raw\":\"%s\"", _pstGpsData->gps_raw);
|
|
// IMU数据
|
JSON_PACK(_pu8JsonBuf, u32Len, sizeof(l_au8JsonBuffer), ",\"imu_data\":{");
|
JSON_PACK(_pu8JsonBuf, u32Len, sizeof(l_au8JsonBuffer), "\"roll\":%.2f", _pstGpsData->imu_data.roll);
|
JSON_PACK(_pu8JsonBuf, u32Len, sizeof(l_au8JsonBuffer), ",\"pitch\":%.2f", _pstGpsData->imu_data.pitch);
|
JSON_PACK(_pu8JsonBuf, u32Len, sizeof(l_au8JsonBuffer), ",\"yaw\":%.2f", _pstGpsData->imu_data.yaw);
|
JSON_PACK(_pu8JsonBuf, u32Len, sizeof(l_au8JsonBuffer), "}");
|
|
// 状态数据
|
JSON_PACK(_pu8JsonBuf, u32Len, sizeof(l_au8JsonBuffer), ",\"status\":{");
|
JSON_PACK(_pu8JsonBuf, u32Len, sizeof(l_au8JsonBuffer), "\"battery_level\":%u", _pstGpsData->status.battery_level);
|
JSON_PACK(_pu8JsonBuf, u32Len, sizeof(l_au8JsonBuffer), ",\"battery_voltage\":%.2f", _pstGpsData->status.battery_voltage);
|
JSON_PACK(_pu8JsonBuf, u32Len, sizeof(l_au8JsonBuffer), ",\"operation_mode\":\"%s\"", _pstGpsData->status.operation_mode);
|
JSON_PACK(_pu8JsonBuf, u32Len, sizeof(l_au8JsonBuffer), ",\"motor_status\":\"%s\"", _pstGpsData->status.motor_status);
|
JSON_PACK(_pu8JsonBuf, u32Len, sizeof(l_au8JsonBuffer), ",\"blade_status\":\"%s\"", _pstGpsData->status.blade_status);
|
JSON_PACK(_pu8JsonBuf, u32Len, sizeof(l_au8JsonBuffer), ",\"blade_height\":%u", _pstGpsData->status.blade_height);
|
JSON_PACK(_pu8JsonBuf, u32Len, sizeof(l_au8JsonBuffer), ",\"self_check_status\":%u", _pstGpsData->status.self_check_status);
|
JSON_PACK(_pu8JsonBuf, u32Len, sizeof(l_au8JsonBuffer), ",\"error_code\":%u", _pstGpsData->status.error_code);
|
JSON_PACK(_pu8JsonBuf, u32Len, sizeof(l_au8JsonBuffer), ",\"error_message\":\"%s\"", _pstGpsData->status.error_message);
|
JSON_PACK(_pu8JsonBuf, u32Len, sizeof(l_au8JsonBuffer), "}");
|
|
JSON_PACK(_pu8JsonBuf, u32Len, sizeof(l_au8JsonBuffer), "}");
|
|
*_pu32JsonLen = u32Len;
|
|
return HIDO_OK;
|
}
|
|
/*******************************************************************************
|
* Function Name : MQTTApp_GetJsonStringValue
|
* Description : 从JSON中提取字符串值(简单实现,带边界检查)
|
* Input : _pcJson JSON字符串
|
* _u32JsonLen JSON字符串长度
|
* _pcKey 键名
|
* Output : _pcValue 值缓冲区
|
* _u32ValueSize 值缓冲区大小
|
* Return : HIDO_OK/HIDO_ERR
|
*******************************************************************************/
|
static HIDO_INT32 MQTTApp_GetJsonStringValue(HIDO_CHAR *_pcJson,
|
HIDO_UINT32 _u32JsonLen,
|
const HIDO_CHAR *_pcKey,
|
HIDO_CHAR *_pcValue,
|
HIDO_UINT32 _u32ValueSize)
|
{
|
HIDO_CHAR *pcKeyPos = HIDO_NULL;
|
HIDO_CHAR *pcValueStart = HIDO_NULL;
|
HIDO_CHAR *pcValueEnd = HIDO_NULL;
|
HIDO_CHAR *pcJsonEnd = HIDO_NULL;
|
HIDO_CHAR acSearchKey[64];
|
HIDO_UINT32 u32Len = 0;
|
|
if (HIDO_NULL == _pcJson || HIDO_NULL == _pcKey || HIDO_NULL == _pcValue || _u32JsonLen == 0)
|
{
|
return HIDO_ERR;
|
}
|
|
// 计算JSON字符串的结束位置
|
pcJsonEnd = _pcJson + _u32JsonLen;
|
|
// 构造搜索字符串 "key":"
|
HIDO_UtilSnprintf(acSearchKey, sizeof(acSearchKey), "\"%s\":", _pcKey);
|
|
// 查找键
|
pcKeyPos = strstr(_pcJson, acSearchKey);
|
if (HIDO_NULL == pcKeyPos || pcKeyPos >= pcJsonEnd)
|
{
|
return HIDO_ERR;
|
}
|
|
// 跳过键和冒号,查找值的起始引号(限制搜索范围)
|
pcValueStart = pcKeyPos + strlen(acSearchKey);
|
if (pcValueStart >= pcJsonEnd)
|
{
|
return HIDO_ERR;
|
}
|
|
// 在剩余范围内查找起始引号
|
while (pcValueStart < pcJsonEnd && *pcValueStart != '\"')
|
{
|
pcValueStart++;
|
}
|
if (pcValueStart >= pcJsonEnd || *pcValueStart != '\"')
|
{
|
return HIDO_ERR;
|
}
|
pcValueStart++; // 跳过起始引号
|
|
// 查找值的结束引号(限制搜索范围)
|
pcValueEnd = pcValueStart;
|
while (pcValueEnd < pcJsonEnd && *pcValueEnd != '\"')
|
{
|
pcValueEnd++;
|
}
|
if (pcValueEnd >= pcJsonEnd || *pcValueEnd != '\"')
|
{
|
return HIDO_ERR;
|
}
|
|
// 计算值长度
|
u32Len = pcValueEnd - pcValueStart;
|
if (u32Len >= _u32ValueSize)
|
{
|
u32Len = _u32ValueSize - 1;
|
}
|
|
// 复制值
|
memcpy(_pcValue, pcValueStart, u32Len);
|
_pcValue[u32Len] = '\0';
|
|
return HIDO_OK;
|
}
|
|
/*******************************************************************************
|
* Function Name : MQTTApp_ParseControlJson
|
* Description : 解析控制指令JSON
|
* Input : _pu8JsonBuf JSON缓冲区
|
* _u32JsonLen JSON长度
|
* Output : _pstCtrlData 控制数据
|
* Return : HIDO_OK/HIDO_ERR
|
*******************************************************************************/
|
static HIDO_INT32 MQTTApp_ParseControlJson(HIDO_UINT8 *_pu8JsonBuf,
|
HIDO_UINT32 _u32JsonLen,
|
ST_MQTTAppControlData *_pstCtrlData)
|
{
|
if (HIDO_NULL == _pu8JsonBuf || HIDO_NULL == _pstCtrlData)
|
{
|
return HIDO_ERR;
|
}
|
|
memset(_pstCtrlData, 0, sizeof(ST_MQTTAppControlData));
|
|
// 解析 command
|
MQTTApp_GetJsonStringValue((HIDO_CHAR *)_pu8JsonBuf, _u32JsonLen, "command",
|
_pstCtrlData->command, sizeof(_pstCtrlData->command));
|
|
// 解析 request_id
|
MQTTApp_GetJsonStringValue((HIDO_CHAR *)_pu8JsonBuf, _u32JsonLen, "request_id",
|
_pstCtrlData->request_id, sizeof(_pstCtrlData->request_id));
|
|
// 解析 parameters (根据具体协议扩展)
|
// TODO: 根据实际需求解析参数
|
|
return HIDO_OK;
|
}
|
|
/*******************************************************************************
|
* Function Name : MQTTApp_ParsePathJson
|
* Description : 解析路径数据JSON(使用HIDO_Json库,安全无越界风险)
|
* Input : _pu8JsonBuf JSON缓冲区
|
* _u32JsonLen JSON长度
|
* Output : _pstPathData 路径数据
|
* Return : HIDO_OK/HIDO_ERR
|
*******************************************************************************/
|
static HIDO_INT32 MQTTApp_ParsePathJson(HIDO_UINT8 *_pu8JsonBuf,
|
HIDO_UINT32 _u32JsonLen,
|
ST_MQTTAppPathData *_pstPathData)
|
{
|
HIDO_JsonNodeStruct *pstJsonRoot = HIDO_NULL;
|
HIDO_JsonNodeStruct *pstBasestationNode = HIDO_NULL;
|
HIDO_JsonNodeStruct *pstPathDataNode = HIDO_NULL;
|
HIDO_JsonArraryNodeStruct *pstArrayNode = HIDO_NULL;
|
HIDO_CHAR *pcValue = HIDO_NULL;
|
HIDO_INT32 i32Value = 0;
|
HIDO_UINT32 i = 0;
|
|
if (HIDO_NULL == _pu8JsonBuf || HIDO_NULL == _pstPathData)
|
{
|
HIDO_DebugEx("[MQTTApp] ERROR: Invalid parameters\r\n");
|
return HIDO_ERR;
|
}
|
|
memset(_pstPathData, 0, sizeof(ST_MQTTAppPathData));
|
|
// 解析JSON
|
pstJsonRoot = HIDO_JsonJsonParse((HIDO_CHAR *)_pu8JsonBuf);
|
if (pstJsonRoot == HIDO_NULL)
|
{
|
HIDO_DebugEx("[MQTTApp] ERROR: JSON parse failed\r\n");
|
return HIDO_ERR;
|
}
|
|
// 解析顶层字段
|
if (HIDO_JsonGetString(pstJsonRoot, "msg_id", &pcValue) == HIDO_OK)
|
{
|
HIDO_UtilSnprintf(_pstPathData->msg_id, sizeof(_pstPathData->msg_id), "%s", pcValue);
|
}
|
|
if (HIDO_JsonGetString(pstJsonRoot, "timestamp", &pcValue) == HIDO_OK)
|
{
|
_pstPathData->timestamp = (HIDO_UINT64)atoll(pcValue);
|
}
|
|
if (HIDO_JsonGetString(pstJsonRoot, "user_id", &pcValue) == HIDO_OK)
|
{
|
HIDO_UtilSnprintf(_pstPathData->user_id, sizeof(_pstPathData->user_id), "%s", pcValue);
|
}
|
|
if (HIDO_JsonGetString(pstJsonRoot, "device_id", &pcValue) == HIDO_OK)
|
{
|
HIDO_UtilSnprintf(_pstPathData->device_id, sizeof(_pstPathData->device_id), "%s", pcValue);
|
}
|
|
if (HIDO_JsonGetString(pstJsonRoot, "command", &pcValue) == HIDO_OK)
|
{
|
HIDO_UtilSnprintf(_pstPathData->command, sizeof(_pstPathData->command), "%s", pcValue);
|
}
|
|
// 解析basestation_data对象
|
pstBasestationNode = HIDO_JsonGetValue(pstJsonRoot, "basestation_data");
|
if (pstBasestationNode != HIDO_NULL && pstBasestationNode->m_eType == HIDO_JSON_TYPE_JSON)
|
{
|
HIDO_JsonNodeStruct *pstBasestationJsonNode = HIDO_JsonJsonParse(pstBasestationNode->m_stValue.m_pcPtr);
|
if (pstBasestationJsonNode != HIDO_NULL)
|
{
|
if (HIDO_JsonGetString(pstBasestationJsonNode, "latitude_dm", &pcValue) == HIDO_OK)
|
{
|
HIDO_UtilSnprintf(_pstPathData->basestation.latitude_dm,
|
sizeof(_pstPathData->basestation.latitude_dm), "%s", pcValue);
|
}
|
if (HIDO_JsonGetString(pstBasestationJsonNode, "latitude_hemisphere", &pcValue) == HIDO_OK)
|
{
|
HIDO_UtilSnprintf(_pstPathData->basestation.latitude_hemisphere,
|
sizeof(_pstPathData->basestation.latitude_hemisphere), "%s", pcValue);
|
}
|
if (HIDO_JsonGetString(pstBasestationJsonNode, "longitude_dm", &pcValue) == HIDO_OK)
|
{
|
HIDO_UtilSnprintf(_pstPathData->basestation.longitude_dm,
|
sizeof(_pstPathData->basestation.longitude_dm), "%s", pcValue);
|
}
|
if (HIDO_JsonGetString(pstBasestationJsonNode, "longitude_hemisphere", &pcValue) == HIDO_OK)
|
{
|
HIDO_UtilSnprintf(_pstPathData->basestation.longitude_hemisphere,
|
sizeof(_pstPathData->basestation.longitude_hemisphere), "%s", pcValue);
|
}
|
if (HIDO_JsonGetString(pstBasestationJsonNode, "altitude", &pcValue) == HIDO_OK)
|
{
|
_pstPathData->basestation.altitude = (HIDO_FLOAT)atof(pcValue);
|
}
|
HIDO_JsonNodeFree(pstBasestationJsonNode);
|
}
|
}
|
|
// 解析path_data对象
|
pstPathDataNode = HIDO_JsonGetValue(pstJsonRoot, "path_data");
|
if (pstPathDataNode != HIDO_NULL && pstPathDataNode->m_eType == HIDO_JSON_TYPE_JSON)
|
{
|
HIDO_JsonNodeStruct *pstPathJsonNode = HIDO_JsonJsonParse(pstPathDataNode->m_stValue.m_pcPtr);
|
if (pstPathJsonNode != HIDO_NULL)
|
{
|
// 解析path_data中的字段
|
if (HIDO_JsonGetString(pstPathJsonNode, "path_id", &pcValue) == HIDO_OK)
|
{
|
HIDO_UtilSnprintf(_pstPathData->path_id, sizeof(_pstPathData->path_id), "%s", pcValue);
|
}
|
if (HIDO_JsonGetString(pstPathJsonNode, "coordinate_system", &pcValue) == HIDO_OK)
|
{
|
HIDO_UtilSnprintf(_pstPathData->coordinate_system,
|
sizeof(_pstPathData->coordinate_system), "%s", pcValue);
|
}
|
if (HIDO_JsonGetString(pstPathJsonNode, "mowing_pattern", &pcValue) == HIDO_OK)
|
{
|
HIDO_UtilSnprintf(_pstPathData->mowing_pattern,
|
sizeof(_pstPathData->mowing_pattern), "%s", pcValue);
|
}
|
if (HIDO_JsonGetInteger(pstPathJsonNode, "boundary_point_count", &i32Value) == HIDO_OK)
|
{
|
_pstPathData->boundary_point_count = (HIDO_UINT32)i32Value;
|
if (_pstPathData->boundary_point_count > MQTT_APP_MAX_PATH_POINTS1)
|
{
|
HIDO_DebugEx("[MQTTApp] WARNING: boundary_point_count=%u exceeds max=%u\r\n",
|
_pstPathData->boundary_point_count, MQTT_APP_MAX_PATH_POINTS1);
|
_pstPathData->boundary_point_count = MQTT_APP_MAX_PATH_POINTS1;
|
}
|
}
|
if (HIDO_JsonGetInteger(pstPathJsonNode, "navigation_point_count", &i32Value) == HIDO_OK)
|
{
|
_pstPathData->navigation_point_count = (HIDO_UINT32)i32Value;
|
if (_pstPathData->navigation_point_count > MQTT_APP_MAX_PATH_POINTS2)
|
{
|
HIDO_DebugEx("[MQTTApp] WARNING: navigation_point_count=%u exceeds max=%u\r\n",
|
_pstPathData->navigation_point_count, MQTT_APP_MAX_PATH_POINTS2);
|
_pstPathData->navigation_point_count = MQTT_APP_MAX_PATH_POINTS2;
|
}
|
}
|
|
// 解析boundary_points数组
|
if (HIDO_JsonGetJsonArrary(pstPathJsonNode, "boundary_points", &pstArrayNode) == HIDO_OK)
|
{
|
HIDO_JsonArraryNodeStruct *pstCurrent = pstArrayNode;
|
i = 0;
|
while (pstCurrent != HIDO_NULL && i < MQTT_APP_MAX_PATH_POINTS1)
|
{
|
if (pstCurrent->m_pstJson != HIDO_NULL)
|
{
|
HIDO_FLOAT fX = 0.0f, fY = 0.0f;
|
if (HIDO_JsonGetFloat(pstCurrent->m_pstJson, "x", &fX) == HIDO_OK &&
|
HIDO_JsonGetFloat(pstCurrent->m_pstJson, "y", &fY) == HIDO_OK)
|
{
|
_pstPathData->boundary_points[i].x = fX;
|
_pstPathData->boundary_points[i].y = fY;
|
i++;
|
}
|
}
|
pstCurrent = pstCurrent->m_pstNext;
|
}
|
// 如果JSON数组中的实际点数与boundary_point_count不一致,以实际解析到的为准
|
if (i != _pstPathData->boundary_point_count)
|
{
|
HIDO_DebugEx("[MQTTApp] WARNING: boundary_point_count mismatch: declared=%u, actual=%u\r\n",
|
_pstPathData->boundary_point_count, i);
|
_pstPathData->boundary_point_count = i;
|
}
|
HIDO_JsonArrayNodeFree(pstArrayNode);
|
}
|
|
// 解析navigation_points数组
|
if (HIDO_JsonGetJsonArrary(pstPathJsonNode, "navigation_points", &pstArrayNode) == HIDO_OK)
|
{
|
HIDO_JsonArraryNodeStruct *pstCurrent = pstArrayNode;
|
i = 0;
|
while (pstCurrent != HIDO_NULL && i < MQTT_APP_MAX_PATH_POINTS2)
|
{
|
if (pstCurrent->m_pstJson != HIDO_NULL)
|
{
|
HIDO_FLOAT fX = 0.0f, fY = 0.0f;
|
if (HIDO_JsonGetFloat(pstCurrent->m_pstJson, "x", &fX) == HIDO_OK &&
|
HIDO_JsonGetFloat(pstCurrent->m_pstJson, "y", &fY) == HIDO_OK)
|
{
|
_pstPathData->navigation_points[i].x = fX;
|
_pstPathData->navigation_points[i].y = fY;
|
i++;
|
}
|
}
|
pstCurrent = pstCurrent->m_pstNext;
|
}
|
// 如果JSON数组中的实际点数与navigation_point_count不一致,以实际解析到的为准
|
if (i != _pstPathData->navigation_point_count)
|
{
|
HIDO_DebugEx("[MQTTApp] WARNING: navigation_point_count mismatch: declared=%u, actual=%u\r\n",
|
_pstPathData->navigation_point_count, i);
|
_pstPathData->navigation_point_count = i;
|
}
|
HIDO_JsonArrayNodeFree(pstArrayNode);
|
}
|
|
HIDO_JsonNodeFree(pstPathJsonNode);
|
}
|
}
|
|
// 释放根节点
|
HIDO_JsonNodeFree(pstJsonRoot);
|
|
HIDO_DebugEx("[MQTTApp] Path parsed successfully: path_id=%s, boundary=%u, navigation=%u\r\n",
|
_pstPathData->path_id,
|
_pstPathData->boundary_point_count,
|
_pstPathData->navigation_point_count);
|
|
return HIDO_OK;
|
}
|
|
/*******************************************************************************
|
* Global Function *
|
*******************************************************************************/
|
|
/*******************************************************************************
|
* Function Name : MQTTApp_Init
|
* Description : MQTT应用层初始化
|
* Input : _pstConfig 配置参数
|
* Output : None
|
* Return : HIDO_OK/HIDO_ERR
|
*******************************************************************************/
|
HIDO_INT32 MQTTApp_Init(ST_MQTTAppConfig *_pstConfig)
|
{
|
if (HIDO_NULL == _pstConfig)
|
{
|
return HIDO_ERR;
|
}
|
|
memcpy(&l_stAppConfig, _pstConfig, sizeof(ST_MQTTAppConfig));
|
|
HIDO_DebugEx("[MQTTApp] Init OK\r\n");
|
|
return HIDO_OK;
|
}
|
|
/*******************************************************************************
|
* Function Name : MQTTApp_ReportGps
|
* Description : 上报GPS数据
|
* Input : _pstGpsData GPS数据
|
* Output : None
|
* Return : HIDO_OK/HIDO_ERR
|
*******************************************************************************/
|
HIDO_INT32 MQTTApp_ReportGps(ST_MQTTAppGpsData *_pstGpsData)
|
{
|
HIDO_UINT32 u32JsonLen = 0;
|
ST_ReportGps stReportGps;
|
|
if (HIDO_NULL == _pstGpsData)
|
{
|
return HIDO_ERR;
|
}
|
|
// 封装JSON
|
if (MQTTApp_PackGpsJson(_pstGpsData, l_au8JsonBuffer, &u32JsonLen) != HIDO_OK)
|
{
|
HIDO_DebugEx("[MQTTApp] Pack GPS JSON failed\r\n");
|
return HIDO_ERR;
|
}
|
|
// 填充上报结构体
|
memset(&stReportGps, 0, sizeof(stReportGps));
|
MQTTApp_GenerateMsgID(stReportGps.msg_id, sizeof(stReportGps.msg_id), "hxzkgps");
|
stReportGps.timestamp = _pstGpsData->timestamp;
|
HIDO_UtilSnprintf(stReportGps.device_id, sizeof(stReportGps.device_id), "%u", g_com_map[DEV_ID]);
|
HIDO_UtilSnprintf(stReportGps.data_type, sizeof(stReportGps.data_type), "gps");
|
HIDO_UtilSnprintf(stReportGps.gps_raw, sizeof(stReportGps.gps_raw), "%s", _pstGpsData->gps_raw);
|
memcpy(&stReportGps.imu_data, &_pstGpsData->imu_data, sizeof(ST_IMUData));
|
memcpy(&stReportGps.status, &_pstGpsData->status, sizeof(ST_StatusData));
|
|
// 调用MQTT客户端发送
|
return MQTTClient_GpsReport(&stReportGps);
|
}
|
|
/*******************************************************************************
|
* Function Name : MQTTApp_ParseMessage
|
* Description : 解析下发消息
|
* Input : _pcTopic 主题
|
* _pu8Payload 消息负载
|
* _u32PayloadLen 负载长度
|
* Output : None
|
* Return : HIDO_OK/HIDO_ERR
|
*******************************************************************************/
|
HIDO_INT32 MQTTApp_ParseMessage(HIDO_CHAR *_pcTopic,
|
HIDO_UINT8 *_pu8Payload,
|
HIDO_UINT32 _u32PayloadLen)
|
{
|
if (HIDO_NULL == _pcTopic || HIDO_NULL == _pu8Payload)
|
{
|
return HIDO_ERR;
|
}
|
|
// 入口防护:检查payload长度
|
if (_u32PayloadLen == 0 || _u32PayloadLen >= sizeof(l_au8JsonBuffer))
|
{
|
HIDO_DebugEx("[MQTTApp] ERROR: Payload length invalid: %u (max: %u)\r\n",
|
_u32PayloadLen, sizeof(l_au8JsonBuffer) - 1);
|
return HIDO_ERR;
|
}
|
|
// 安全复制payload到本地缓冲区,并确保'\0'结尾
|
memcpy(l_au8JsonBuffer, _pu8Payload, _u32PayloadLen);
|
l_au8JsonBuffer[_u32PayloadLen] = '\0';
|
|
HIDO_DebugEx("[MQTTApp] Recv Topic: %s, Len: %u\r\n", _pcTopic, _u32PayloadLen);
|
|
// 判断主题类型
|
if (strstr(_pcTopic, "/control") != HIDO_NULL)
|
{
|
// 控制指令
|
ST_MQTTAppControlData stCtrlData;
|
if (MQTTApp_ParseControlJson(l_au8JsonBuffer, _u32PayloadLen, &stCtrlData) == HIDO_OK)
|
{
|
if (l_stAppConfig.fnControlCallback != HIDO_NULL)
|
{
|
l_stAppConfig.fnControlCallback(&stCtrlData, l_stAppConfig.pControlArg);
|
}
|
}
|
}
|
else if (strstr(_pcTopic, "/path") != HIDO_NULL)
|
{
|
// 路径数据
|
static ST_MQTTAppPathData stPathData;
|
if (MQTTApp_ParsePathJson(l_au8JsonBuffer, _u32PayloadLen, &stPathData) == HIDO_OK)
|
{
|
if (l_stAppConfig.fnPathCallback != HIDO_NULL)
|
{
|
l_stAppConfig.fnPathCallback(&stPathData, l_stAppConfig.pPathArg);
|
}
|
}
|
}
|
else if (strstr(_pcTopic, "/basestation") != HIDO_NULL)
|
{
|
// 基站配置
|
// TODO: 实现基站配置解析
|
}
|
|
return HIDO_OK;
|
}
|
|
/*******************************************************************************
|
* Function Name : MQTTApp_FillGpsData
|
* Description : 填充GPS数据(供应用层调用)
|
* Input : _pcGpsRaw GPS原始数据
|
* _fRoll 横滚角
|
* _fPitch 俯仰角
|
* _fYaw 偏航角
|
* _u8BattLevel 电池电量
|
* _fBattVolt 电池电压
|
* Output : _pstGpsData GPS数据结构
|
* Return : HIDO_OK/HIDO_ERR
|
*******************************************************************************/
|
HIDO_INT32 MQTTApp_FillGpsData(ST_MQTTAppGpsData *_pstGpsData,
|
const HIDO_CHAR *_pcGpsRaw,
|
HIDO_FLOAT _fRoll,
|
HIDO_FLOAT _fPitch,
|
HIDO_FLOAT _fYaw,
|
HIDO_UINT8 _u8BattLevel,
|
HIDO_FLOAT _fBattVolt)
|
{
|
if (HIDO_NULL == _pstGpsData || HIDO_NULL == _pcGpsRaw)
|
{
|
return HIDO_ERR;
|
}
|
|
memset(_pstGpsData, 0, sizeof(ST_MQTTAppGpsData));
|
|
// 时间戳
|
_pstGpsData->timestamp = HIDO_TimerGetTick();
|
|
// GPS原始数据
|
HIDO_UtilSnprintf(_pstGpsData->gps_raw, sizeof(_pstGpsData->gps_raw), "%s", _pcGpsRaw);
|
|
// IMU数据
|
_pstGpsData->imu_data.roll = _fRoll;
|
_pstGpsData->imu_data.pitch = _fPitch;
|
_pstGpsData->imu_data.yaw = _fYaw;
|
|
// 状态数据
|
_pstGpsData->status.battery_level = _u8BattLevel;
|
_pstGpsData->status.battery_voltage = _fBattVolt;
|
HIDO_UtilSnprintf(_pstGpsData->status.operation_mode, sizeof(_pstGpsData->status.operation_mode), "auto");
|
HIDO_UtilSnprintf(_pstGpsData->status.motor_status, sizeof(_pstGpsData->status.motor_status), "running");
|
HIDO_UtilSnprintf(_pstGpsData->status.blade_status, sizeof(_pstGpsData->status.blade_status), "rotating");
|
_pstGpsData->status.blade_height = 10;
|
_pstGpsData->status.self_check_status = 1;
|
_pstGpsData->status.error_code = 0;
|
HIDO_UtilSnprintf(_pstGpsData->status.error_message, sizeof(_pstGpsData->status.error_message), "");
|
|
return HIDO_OK;
|
}
|
|
/*******************************************************************************
|
* Function Name : MQTTApp_ReportGpsFromDevice
|
* Description : 从FML GPS模块获取数据并上报(使用GGA格式)
|
* Input : None
|
* Output : None
|
* Return : HIDO_OK/HIDO_ERR
|
*******************************************************************************/
|
HIDO_INT32 MQTTApp_ReportGpsFromDevice(HIDO_VOID)
|
{
|
ST_GPRMI stGPRMI;
|
ST_GPGIG stGPGIG;
|
ST_MQTTAppGpsData stGpsData;
|
HIDO_CHAR acGgaBuffer[256];
|
HIDO_UINT32 ggaLen;
|
|
/* 获取GPRMI数据(fmin) */
|
if (GPS_GetGPRMI(&stGPRMI) != HIDO_OK)
|
{
|
HIDO_DebugEx("[MQTTApp] GPS GPRMI data not ready\r\n");
|
return HIDO_ERR;
|
}
|
|
/* 获取GPGIG数据(fmig) */
|
if (GPS_GetGPGIG(&stGPGIG) != HIDO_OK)
|
{
|
HIDO_DebugEx("[MQTTApp] GPS GPGIG data not ready\r\n");
|
return HIDO_ERR;
|
}
|
|
/* 生成GGA报文(经纬度优先使用fmig数据) */
|
ggaLen = GPS_FormatGGA(&stGPRMI, &stGPGIG, acGgaBuffer, sizeof(acGgaBuffer));
|
|
if (ggaLen == 0U)
|
{
|
HIDO_DebugEx("[MQTTApp] GPS_FormatGGA failed\r\n");
|
return HIDO_ERR;
|
}
|
|
/* 填充MQTT上报数据 */
|
memset(&stGpsData, 0, sizeof(ST_MQTTAppGpsData));
|
|
/* 时间戳 */
|
stGpsData.timestamp = HIDO_TimerGetTick();
|
|
/* GPS原始数据(GGA报文) */
|
HIDO_UtilSnprintf(stGpsData.gps_raw, sizeof(stGpsData.gps_raw), "%s", acGgaBuffer);
|
|
/* IMU数据(从fmin中获取) */
|
stGpsData.imu_data.roll = stGPRMI.m_fRollAngle;
|
stGpsData.imu_data.pitch = stGPRMI.m_fPitchAngle;
|
stGpsData.imu_data.yaw = stGPRMI.m_fHeadingAngle;
|
|
/* 状态数据(TODO: 需要从实际系统获取) */
|
stGpsData.status.battery_level = 85; /* TODO: 从电源管理模块获取 */
|
stGpsData.status.battery_voltage = 24.5f; /* TODO: 从电源管理模块获取 */
|
HIDO_UtilSnprintf(stGpsData.status.operation_mode, sizeof(stGpsData.status.operation_mode), "auto");
|
HIDO_UtilSnprintf(stGpsData.status.motor_status, sizeof(stGpsData.status.motor_status), "running");
|
HIDO_UtilSnprintf(stGpsData.status.blade_status, sizeof(stGpsData.status.blade_status), "rotating");
|
stGpsData.status.blade_height = 10;
|
stGpsData.status.self_check_status = 1;
|
stGpsData.status.error_code = 0;
|
HIDO_UtilSnprintf(stGpsData.status.error_message, sizeof(stGpsData.status.error_message), "");
|
|
/* 上报数据 */
|
return MQTTApp_ReportGps(&stGpsData);
|
}
|
|
/*******************************************************************************
|
* Function Name : MQTTApp_ReportGpsVirtual
|
* Description : 虚拟GPS数据上报(用于测试)
|
* Input : None
|
* Output : None
|
* Return : HIDO_OK/HIDO_ERR
|
*******************************************************************************/
|
HIDO_INT32 MQTTApp_ReportGpsVirtual(HIDO_VOID)
|
{
|
ST_MQTTAppGpsData stGpsData;
|
static HIDO_UINT32 s_ulSeq = 0; /* 序列号,用于模拟移动 */
|
|
/* 填充MQTT上报数据 */
|
memset(&stGpsData, 0, sizeof(ST_MQTTAppGpsData));
|
|
/* 时间戳 */
|
stGpsData.timestamp = HIDO_TimerGetTick();
|
|
/* 模拟GPS原始数据(GGA报文)
|
* 模拟位置:北京市某地点 (39.9N, 116.4E)
|
* 每次调用经度会轻微变化以模拟移动
|
*/
|
{
|
HIDO_FLOAT fLat = 39.9042f + (s_ulSeq % 100) * 0.00001f; /* 39.90420 ~ 39.90519 */
|
HIDO_FLOAT fLon = 116.4074f + (s_ulSeq % 100) * 0.00001f; /* 116.40740 ~ 116.40839 */
|
HIDO_UINT32 ulLatDeg = (HIDO_UINT32)fLat;
|
HIDO_UINT32 ulLonDeg = (HIDO_UINT32)fLon;
|
HIDO_FLOAT fLatMin = (fLat - ulLatDeg) * 60.0f;
|
HIDO_FLOAT fLonMin = (fLon - ulLonDeg) * 60.0f;
|
HIDO_UINT32 ulHour = (HIDO_TimerGetTick() / 3600000) % 24;
|
HIDO_UINT32 ulMin = (HIDO_TimerGetTick() / 60000) % 60;
|
HIDO_UINT32 ulSec = (HIDO_TimerGetTick() / 1000) % 60;
|
|
/* 格式: $GPGGA,hhmmss.ss,ddmm.mmmm,N,dddmm.mmmm,E,1,08,1.0,45.0,M,0.0,M,,*checksum */
|
HIDO_UtilSnprintf(stGpsData.gps_raw, sizeof(stGpsData.gps_raw),
|
"$GPGGA,%02u%02u%02u.00,%02u%07.4f,N,%03u%07.4f,E,1,08,1.0,45.0,M,0.0,M,,*47",
|
ulHour, ulMin, ulSec,
|
ulLatDeg, fLatMin,
|
ulLonDeg, fLonMin);
|
}
|
|
/* 模拟IMU数据(轻微摆动) */
|
stGpsData.imu_data.roll = (s_ulSeq % 20 - 10) * 0.5f; /* -5.0 ~ +5.0 度 */
|
stGpsData.imu_data.pitch = (s_ulSeq % 30 - 15) * 0.3f; /* -4.5 ~ +4.5 度 */
|
stGpsData.imu_data.yaw = (s_ulSeq % 360) * 1.0f; /* 0 ~ 359 度 */
|
|
/* 模拟状态数据 */
|
stGpsData.status.battery_level = 85 - (s_ulSeq / 100) % 20; /* 85% ~ 65% 循环变化 */
|
stGpsData.status.battery_voltage = 24.5f - (s_ulSeq / 100) % 2 * 0.5f; /* 24.5V ~ 23.5V */
|
HIDO_UtilSnprintf(stGpsData.status.operation_mode, sizeof(stGpsData.status.operation_mode), "auto");
|
HIDO_UtilSnprintf(stGpsData.status.motor_status, sizeof(stGpsData.status.motor_status),
|
(s_ulSeq % 2 == 0) ? "running" : "idle");
|
HIDO_UtilSnprintf(stGpsData.status.blade_status, sizeof(stGpsData.status.blade_status),
|
(s_ulSeq % 3 == 0) ? "rotating" : "stopped");
|
stGpsData.status.blade_height = 10 + (s_ulSeq % 5); /* 10 ~ 14 mm */
|
stGpsData.status.self_check_status = 1;
|
stGpsData.status.error_code = 0;
|
HIDO_UtilSnprintf(stGpsData.status.error_message, sizeof(stGpsData.status.error_message), "Test Mode");
|
|
/* 更新序列号 */
|
s_ulSeq++;
|
|
/* 上报数据 */
|
return MQTTApp_ReportGps(&stGpsData);
|
}
|