From 06945aa58d82157ecf5737bdc1cb0845d7c6767e Mon Sep 17 00:00:00 2001 From: kkkjtr Date: Mon, 18 Aug 2025 14:16:14 +0800 Subject: [PATCH] =?UTF-8?q?=E6=99=AF=E7=82=B9=E5=8F=8A=E7=94=B5=E5=AD=90?= =?UTF-8?q?=E5=9B=B4=E6=A0=8F=E8=AF=B7=E6=B1=82=E9=80=BB=E8=BE=91=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E6=B5=8B=E8=AF=95=E5=AE=8C=E6=88=90=EF=BC=8C=E5=8F=8C?= =?UTF-8?q?=E5=B7=A5=E9=80=9A=E4=BF=A1=E6=B5=8B=E8=AF=95=E5=AE=8C=E6=88=90?= =?UTF-8?q?=EF=BC=8COTA=E5=BE=85=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- custom/GPS/src/gps_config.c | 2 +- custom/attr_broadcast/inc/attr_broadcast.h | 12 + custom/attr_broadcast/src/attr_broadcast.c | 101 +++++++- custom/custom_main/src/custom_main.c | 2 +- custom/jt808/inc/jt808_protocol.h | 4 +- custom/jt808/inc/jt808_set_TermParam.h | 81 ++++++- custom/jt808/src/jt808_msg_parse.c | 179 +++++++++++++-- custom/jt808/src/jt808_msg_pkg.c | 46 ++++ custom/jt808/src/jt808_pkg_transmit.c | 4 +- custom/jt808/src/jt808_set_TermParam.c | 255 +++++++++++++++++++-- 10 files changed, 631 insertions(+), 55 deletions(-) diff --git a/custom/GPS/src/gps_config.c b/custom/GPS/src/gps_config.c index 6f6f7e6..760264e 100644 --- a/custom/GPS/src/gps_config.c +++ b/custom/GPS/src/gps_config.c @@ -14,7 +14,7 @@ #include "gps_config.h" #include "local_tts.h" -#if 1 +#if 0 #include "app_uart.h" #define DEBUG(fmt, args...) app_printf("[GPS]" fmt, ##args) #else diff --git a/custom/attr_broadcast/inc/attr_broadcast.h b/custom/attr_broadcast/inc/attr_broadcast.h index a104a14..d54d711 100644 --- a/custom/attr_broadcast/inc/attr_broadcast.h +++ b/custom/attr_broadcast/inc/attr_broadcast.h @@ -3,7 +3,13 @@ #include "cm_os.h" #include "nmea/nmea.h" +// 版本类型定义 +#define VERSION_TYPE_ATTRACTION 0 +#define VERSION_TYPE_FENCE 1 +// 操作类型定义 +#define VERSION_OP_GET 0 +#define VERSION_OP_SET 1 // 景点信息 typedef struct { @@ -22,6 +28,12 @@ typedef struct { extern const char *park_desc[]; +// 统一的版本号管理接口 +// op: 操作类型 (VERSION_OP_GET 或 VERSION_OP_SET) 0:获取版本号 1:写入版本号 +// type: 版本类型 (VERSION_TYPE_ATTRACTION 或 VERSION_TYPE_FENCE) 0:景区版本号 1:电子围栏版本号 +// version: 用于设置或返回版本号的指针 +int attr_broadcast_manage_version(uint8_t op, uint8_t type, uint32_t* version); + //多文字tts,景区播报专用 void safe_tts_play(const char* segments[], int count); diff --git a/custom/attr_broadcast/src/attr_broadcast.c b/custom/attr_broadcast/src/attr_broadcast.c index 2e85bc4..04b9b4a 100644 --- a/custom/attr_broadcast/src/attr_broadcast.c +++ b/custom/attr_broadcast/src/attr_broadcast.c @@ -18,6 +18,9 @@ #include "attr_broadcast.h" #include "gps_config.h" #include "local_tts.h" +#include + + #if 0 #include "app_uart.h" @@ -33,7 +36,7 @@ #define TTS_PRIORITY 5 // TTS播报优先级 #define MAX_TTS_SEGMENT_LEN 70 // TTS每段最大长度(字符) #define ATTRACTIONS_FILE "attr.txt" // 区域ID列表文件 - +#define DATA_VERSION_FILE "version.txt" // 本地数据版本文件 static nmeaPARSER parser; @@ -69,9 +72,6 @@ typedef struct { } BroadcastState; -typedef int BOOL; -#define true 1 -#define false 0 const char *park_desc[] = { "尊敬的游客欢迎来到顾村公园游玩", @@ -90,12 +90,89 @@ const char *park_desc[] = { static osMutexId_t attractions_mutex = NULL; static AttractionNode* attractions_head = NULL; +static osMutexId_t version_mutex = NULL; static BroadcastState broadcast_state = {0}; static int tts_speed = 5; static int tts_volume = 10; static osThreadId_t Attr_Broadcast_ThreadId = NULL; //串口数据接收、解析任务Handle +// 统一的版本号管理接口 +// op: 操作类型 (VERSION_OP_GET 或 VERSION_OP_SET) +// type: 版本类型 (VERSION_TYPE_ATTRACTION 或 VERSION_TYPE_FENCE) +// version: 用于设置或返回版本号的指针 +int attr_broadcast_manage_version(uint8_t op, uint8_t type, uint32_t* version) { + if (version_mutex == NULL || version == NULL) { + return -1; + } + + osMutexAcquire(version_mutex, osWaitForever); + + // 读取当前版本文件内容 + uint32_t attr_version = 0, fence_version = 0; + + // 检查文件是否存在 + if (cm_fs_exist(DATA_VERSION_FILE)) { + int32_t fd = cm_fs_open(DATA_VERSION_FILE, CM_FS_RB); + if (fd >= 0) { + uint8_t buf[8]; + if (cm_fs_read(fd, buf, sizeof(buf)) == sizeof(buf)) { + attr_version = *(uint32_t*)&buf[0]; + fence_version = *(uint32_t*)&buf[4]; + } + cm_fs_close(fd); + } + } + + int result = 0; + + // 根据操作类型处理 + switch (op) { + case VERSION_OP_GET: // 获取版本号 + if (type == VERSION_TYPE_ATTRACTION) { + *version = attr_version; + } else if (type == VERSION_TYPE_FENCE) { + *version = fence_version; + } else { + result = -2; // 无效的类型 + } + break; + + case VERSION_OP_SET: // 设置版本号 + if (type == VERSION_TYPE_ATTRACTION) { + attr_version = *version; + } else if (type == VERSION_TYPE_FENCE) { + fence_version = *version; + } else { + result = -2; // 无效的类型 + } + + // 写入更新后的版本号 + if (result == 0) { + uint8_t buf[8]; + memcpy(&buf[0], &attr_version, sizeof(attr_version)); + memcpy(&buf[4], &fence_version, sizeof(fence_version)); + + int32_t fd = cm_fs_open(DATA_VERSION_FILE, CM_FS_WB); + if (fd < 0) { + result = -3; // 文件打开失败 + } else { + if (cm_fs_write(fd, buf, sizeof(buf)) != sizeof(buf)) { + result = -4; // 写入失败 + } + cm_fs_close(fd); + } + } + break; + + default: + result = -5; // 无效的操作 + break; + } + + osMutexRelease(version_mutex); + return result; +} // 智能分段函数 SegmentedText smart_segment_text(const char* text, int max_segment_len) { @@ -590,7 +667,7 @@ int attr_broadcast_load_attractions(void) { } /******************** 关键修改:ID去重检查 ********************/ // 检查链表中是否已存在相同ID的景点 - BOOL is_duplicate = false; + bool is_duplicate = false; AttractionNode* current_node = attractions_head; while (current_node) { if (current_node->region_id == new_node->region_id) { @@ -734,7 +811,7 @@ static void play_attraction_info(AttractionNode* attraction, double distance) { static void attr_broadcast_task(void* arg) { (void)arg; // 避免未使用参数警告 - BOOL should_play; //是否播放最后判断量 + bool should_play; //是否播放最后判断量 DEBUG("task begin\r\n"); @@ -799,7 +876,9 @@ void attr_broadcast_init(void) { if (attractions_mutex == NULL) { attractions_mutex = osMutexNew(NULL); } - + if (version_mutex == NULL) { + version_mutex = osMutexNew(NULL); + } if(1 == cm_fs_exist(ATTRACTIONS_FILE)) { @@ -822,6 +901,14 @@ void attr_broadcast_init(void) { DEBUG("No saved attractions\n"); } + // 如果版本文件不存在则创建 + if (!cm_fs_exist(DATA_VERSION_FILE)) { + uint32_t init_version = 0; + // 初始化两个版本号为0 + attr_broadcast_manage_version(VERSION_OP_SET, VERSION_TYPE_ATTRACTION, &init_version); + attr_broadcast_manage_version(VERSION_OP_SET, VERSION_TYPE_FENCE, &init_version); + } + // 初始化状态 memset(&broadcast_state, 0, sizeof(broadcast_state)); broadcast_state.distance_threshold = 20.0; // 默认阈值50米 diff --git a/custom/custom_main/src/custom_main.c b/custom/custom_main/src/custom_main.c index b2e9d95..dd6418c 100644 --- a/custom/custom_main/src/custom_main.c +++ b/custom/custom_main/src/custom_main.c @@ -155,7 +155,7 @@ void my_appimg_enter(char *param){ } local_tts_init(); local_tts_mute(0);// 取消静音 - local_tts_volume(100); // 设置音量为55 + local_tts_volume(40); // 设置音量为55 // local_tts_set(7, 7, CM_LOCAL_TTS_DIGIT_AUTO); local_tts_text_play("已开机",0,0); diff --git a/custom/jt808/inc/jt808_protocol.h b/custom/jt808/inc/jt808_protocol.h index 5071553..35d2a60 100644 --- a/custom/jt808/inc/jt808_protocol.h +++ b/custom/jt808/inc/jt808_protocol.h @@ -34,7 +34,9 @@ typedef enum { ID_Delete_Polygon_area = 0x8605, // 删除多边形区域 ID_Data_Down = 0x8900, // 数据透传下行 ID_Data_Up = 0x0900, // 数据透传上行 - //ID_Req_Fence_update = + ID_Req_data_update = 0x0B03, //请求电子围栏/景点更新 + ID_data_update_reply = 0x8B03, //电子围栏/景点数量下发 + ID_data_update_complete = 0x8B02, //电子围栏/景点更新完成 }MessageID_t; #pragma pack(1) diff --git a/custom/jt808/inc/jt808_set_TermParam.h b/custom/jt808/inc/jt808_set_TermParam.h index a7365d7..1d9b1e4 100644 --- a/custom/jt808/inc/jt808_set_TermParam.h +++ b/custom/jt808/inc/jt808_set_TermParam.h @@ -104,6 +104,37 @@ typedef struct {// 终端参数项 #pragma pack() +// 更新状态枚举 +typedef enum { + UPDATE_IDLE, // 空闲状态,无更新进行 + UPDATE_REQUEST_SENT, // 更新请求已发送,等待响应 + UPDATE_RECEIVING_DATA, // 正在接收数据 + UPDATE_COMPLETE, // 最后一包数据接收完成 + UPDATE_COMPLETED, // 更新完成 + UPDATE_FAILED // 更新失败 +} UpdateState_t; + +// 更新类型 +typedef enum { + UPDATE_TYPE_NONE = 0, // 无更新类型 + UPDATE_TYPE_FENCE = 1, // 电子围栏更新 + UPDATE_TYPE_ATTR = 2 // 景点更新 +} UpdateType_t; + +// 全局状态结构体 +typedef struct { + UpdateState_t state; // 当前更新状态 + UpdateType_t type; // 当前更新类型 + uint32_t start_time; // 更新开始时间(系统滴答计数) + uint8_t retry_count; // 重试次数 + uint16_t expected_packets;// 预期数据包数量 + uint16_t received_packets;// 已接收数据包数量 +} UpdateStatus_t; + +// 声明全局变量(在.c文件中定义) +extern UpdateStatus_t update_status; +extern osMutexId_t update_mutex; + extern Term_Param_item_t jt808_term_param_item; extern osThreadFunc_t Autoreport_param_ThreadId; @@ -112,7 +143,6 @@ extern uint8_t Rsp_locked_condition; // 锁车状态, BYTE extern osMutexId_t Polygon_fence_mutex; // 多边形围栏互斥锁 - // 控制车辆状态 void jt808_Set_CarStatus(uint8_t status); @@ -142,7 +172,56 @@ void jt808_Autoreport_param_stop(void); // 初始化终端参数 void jt808_set_term_param_init(void); +// 函数声明 +/** + * @brief 重置更新状态到初始值 + */ +void update_manager_reset(void); + +/** + * @brief 启动更新流程 + * @param type 更新类型 (UPDATE_TYPE_FIRMWARE/UPDATE_TYPE_CONFIG) + */ +void update_manager_start(UpdateType_t type); + +/** + * @brief 标记数据接收开始 + * @param packet_count 预期接收的总数据包数量 + */ +void update_manager_begin_receiving(uint16_t packet_count); + +/** + * @brief 标记单个数据包接收完成 + * @note 当接收包数达到预期值时自动标记为完成状态 + */ +void update_manager_packet_received(void); + +/** + * @brief 强制标记更新完成 + */ +void update_manager_complete(void); + +/** + * @brief 标记更新失败 + */ +void update_manager_fail(void); + +/** + * @brief 检查更新是否正在进行 + * @return true 表示更新活跃中,false 表示空闲/完成/失败 + */ +_Bool update_manager_is_active(void); + +/** + * @brief 检查更新状态是否为"最后一包数据接收完成" + * @return true 表示更新处于"最后一包数据接收完成"状态,false 表示其他状态 + */ +_Bool update_manager_is_complete(void); +/** + * @brief 标记无需更新 + */ +void update_manager_no_update(void); #endif // JT808_SET_TERM_PARAM_H_ \ No newline at end of file diff --git a/custom/jt808/src/jt808_msg_parse.c b/custom/jt808/src/jt808_msg_parse.c index cbd05b1..a1ee383 100644 --- a/custom/jt808/src/jt808_msg_parse.c +++ b/custom/jt808/src/jt808_msg_parse.c @@ -5,11 +5,16 @@ #include "attr_broadcast.h" #include "cm_fota.h" #include "cm_fs.h" -#include +//#include PrsResult_t PrsResult; +typedef int bool; +#define true 1 +#define false 0 + + static void __cm_fota_cb(cm_fota_error_e error) { JT808_DEBUG("[FOTA] error code is %d\r\n", error); @@ -37,6 +42,7 @@ static int jt808_BodyParse(void *Prsmsg_body, PrsResult_t *Result){ Result->Rsp_flow_num = Swap16(resp_body.msg_flow_num); // 应答流水号 Result->Rsp_msg_id = Swap16(resp_body.msg_id_ack); // 应答ID Result->Rsp_result = resp_body.result; // 结果 + break; } case ID_FillPktReq:{// 补传分包请求 @@ -147,7 +153,7 @@ static int jt808_BodyParse(void *Prsmsg_body, PrsResult_t *Result){ } } } - + // 设置应答并发送通用应答 Result->Rsp_flow_num = Result->msg_head.msg_flow_num; Result->Rsp_msg_id = Result->msg_head.msg_id; @@ -168,31 +174,29 @@ static int jt808_BodyParse(void *Prsmsg_body, PrsResult_t *Result){ ret = cm_fota_set_url(Result->term_param_item->big_upgrade_info.file_path); //设置url路径 if(0 != ret) - { + { JT808_DEBUG("url error\r\n"); - } - else - { - + } + else { JT808_DEBUG("url right\r\n"); cm_fs_system_info_t fs_info = {0, 0}; cm_fs_getinfo(&fs_info); JT808_DEBUG("free size =%u",fs_info.free_size); - /* 文件系统剩余空间为0时不建议执行升级,关键文件系统操作可能会失败 */ + //文件系统剩余空间为0时不建议执行升级,关键文件系统操作可能会失败 if (0 == fs_info.free_size) { JT808_DEBUG("insufficient space left in the file system\r\n"); break; } #define MIN_FOTA_SPACE (4 * 1024) // 512KB - if(fs_info.free_size < MIN_FOTA_SPACE) { - JT808_DEBUG("Insufficient space: %u < %u\n", - fs_info.free_size, MIN_FOTA_SPACE); - break; - } + if(fs_info.free_size < MIN_FOTA_SPACE) + { + JT808_DEBUG("Insufficient space: %u < %u\n", fs_info.free_size, MIN_FOTA_SPACE); + break; + } cm_fota_info_t info = {0}; cm_fota_read_config(&info); if (CM_FOTA_TYPE_HTTP_HTTPS == info.fixed_info.fota_mode) @@ -204,14 +208,14 @@ static int jt808_BodyParse(void *Prsmsg_body, PrsResult_t *Result){ JT808_DEBUG("HTTP server error\r\n"); break; } - + osDelay(200/5); cm_fota_exec_upgrade(); - - } - + + } + break; - } + } case ID_GetLocInfo:{// 位置信息查询 break; @@ -267,10 +271,13 @@ static int jt808_BodyParse(void *Prsmsg_body, PrsResult_t *Result){ break; } case ID_Set_Circle_Area:{// 设置圆形区域(0x8600) + // 检查是否在更新流程中 + bool in_update = update_manager_is_active() && update_status.state == UPDATE_RECEIVING_DATA; + + int ret = 0; uint8_t *p = (uint8_t *)Prsmsg_body; - - + // 解析固定字段 uint32_t Area_ID = Swap32(*(uint32_t *)p); uint16_t Area_att = Swap16(*(uint16_t *)(p + 4)); @@ -310,7 +317,18 @@ static int jt808_BodyParse(void *Prsmsg_body, PrsResult_t *Result){ // 释放名称内存 if (scenic_name) jt808_free(scenic_name); - + + // 如果在更新流程中,标记数据包接收 + if (in_update) { + update_manager_packet_received(); + JT808_DEBUG("Received attr packet %d/%d\n", update_status.received_packets, update_status.expected_packets); + } + else + { + JT808_DEBUG("Direct attr packet received, please check \r\n"); + + } + // 设置响应结果 Result->Rsp_result = (ret == 0) ? Msg_ok : Msg_err; Result->Rsp_flow_num = Result->msg_head.msg_flow_num; @@ -350,6 +368,10 @@ static int jt808_BodyParse(void *Prsmsg_body, PrsResult_t *Result){ break; } case ID_Set_Polygon_area:{// 设置多边形区域 + // 检查是否在更新流程中 + bool in_update = update_manager_is_active() && update_status.state == UPDATE_RECEIVING_DATA; + + int ret = 0; uint32_t Area_ID; // 区域ID uint16_t Area_att; // 区域属性 @@ -364,6 +386,16 @@ static int jt808_BodyParse(void *Prsmsg_body, PrsResult_t *Result){ (AreaPoint_t *)(((uint8_t *)Prsmsg_body) + 23)); // 区域点坐标 if(ret == 0){ Result->Rsp_result = Msg_ok; + // 如果在更新流程中,标记数据包接收 + if (in_update) { + update_manager_packet_received(); + JT808_DEBUG("Received fence packet %d/%d\n", update_status.received_packets, update_status.expected_packets); + } + else + { + JT808_DEBUG("Direct fence packet received, please check\r\n"); + + } }else{ Result->Rsp_result = Msg_err; } @@ -423,6 +455,111 @@ static int jt808_BodyParse(void *Prsmsg_body, PrsResult_t *Result){ // JT808_DEBUG("ID_Data_Down\r\n"); break; } + case ID_data_update_reply:{// 电子围栏/景点数量下发 + if (update_manager_is_active()) { + // 检查消息体长度至少为5字节 (2+1+2) + if (Result->msg_head.msgbody_attr.msgbodylen < 5) { + JT808_DEBUG("Invalid update reply length: %d\n", Result->msg_head.msgbody_attr.msgbodylen); + Result->Rsp_result = Msg_invalid; + update_manager_fail(); + } else { + uint8_t *p_body = (uint8_t *)Prsmsg_body; + + // 手动组合应答流水号(2字节大端) + uint16_t reply_flow_num = (p_body[0] << 8) | p_body[1]; + Result->Rsp_flow_num = reply_flow_num; + + + // 解析类型 (1字节) + uint8_t update_type = p_body[2]; + + // 手动组合围栏/景点数量(2字节大端) + uint16_t packet_count = (p_body[3] << 8) | p_body[4]; + + JT808_DEBUG("Update reply: type=%d, count=%d\n", update_type, packet_count); + + // 验证类型是否匹配当前更新 + if ((update_type == 1 && update_status.type == UPDATE_TYPE_FENCE) || + (update_type == 2 && update_status.type == UPDATE_TYPE_ATTR)) { + + if (packet_count == 0) { + // 无数据需要更新 + update_manager_no_update(); + JT808_DEBUG("No data to update\n"); + Result->Rsp_result = Msg_ok; + } else { + // 开始接收数据 + update_manager_begin_receiving(packet_count); + JT808_DEBUG("Expecting %d data packets\n", packet_count); + Result->Rsp_result = Msg_ok; + } + } else { + // 类型不匹配 + JT808_DEBUG("Type mismatch: msg_type=%d, current_type=%d\n",update_type, update_status.type); + Result->Rsp_result = Msg_invalid; + update_manager_fail(); + } + } + } else { + Result->Rsp_result = Msg_err; // 更新管理器未激活 + JT808_DEBUG("Received update reply without active update\n"); + } + + // 设置应答 + Result->Rsp_msg_id = Result->msg_head.msg_id; + jt808_pkg_send(ID_Term_GenResp, 0); + break; + } + case ID_data_update_complete:{// 电子围栏/景点更新完成 + if (update_manager_is_complete()) { + // 检查消息体长度至少为5字节 (1字节类型+4字节版本号) + if (Result->msg_head.msgbody_attr.msgbodylen < 5) { + JT808_DEBUG("Invalid update complete length: %d\n", Result->msg_head.msgbody_attr.msgbodylen); + Result->Rsp_result = Msg_invalid; + update_manager_fail(); + } else { + uint8_t *p_body = (uint8_t *)Prsmsg_body; + + // 解析类型 (1字节) + uint8_t update_type = p_body[0]; + + // 解析版本号 (4字节大端) - 使用Swap32转换 + uint32_t new_version = Swap32(*(uint32_t*)(p_body+1)); + + JT808_DEBUG("Update complete: type=%d, version=%u\n", update_type, new_version); + + // 验证类型是否匹配当前更新 + if ((update_type == 1 && update_status.type == UPDATE_TYPE_FENCE) || + (update_type == 2 && update_status.type == UPDATE_TYPE_ATTR)) { + + // 更新本地版本号 + attr_broadcast_manage_version( + VERSION_OP_SET, + (update_type == 1) ? VERSION_TYPE_FENCE : VERSION_TYPE_ATTRACTION, + &new_version + ); + + update_manager_complete(); + Result->Rsp_result = Msg_ok; + } else { + JT808_DEBUG("Type mismatch: msg_type=%d, current_type=%d\n", + update_type, update_status.type); + Result->Rsp_result = Msg_invalid; + update_manager_fail(); + } + } + } else { + Result->Rsp_result = Msg_err; // 更新管理器未激活 + JT808_DEBUG("Received update complete without active update\n"); + } + + // 设置应答 + Result->Rsp_flow_num = Result->msg_head.msg_flow_num; + Result->Rsp_msg_id = Result->msg_head.msg_id; + jt808_pkg_send(ID_Term_GenResp, 0); + break; + + } default:{ return -2; // 没有对应的消息体解析函数 break; diff --git a/custom/jt808/src/jt808_msg_pkg.c b/custom/jt808/src/jt808_msg_pkg.c index 90c991f..7429108 100644 --- a/custom/jt808/src/jt808_msg_pkg.c +++ b/custom/jt808/src/jt808_msg_pkg.c @@ -1,5 +1,8 @@ #include "jt808_msg_pkg.h" #include "jt808_msg_parse.h" +#include "attr_broadcast.h" + + // 协议消息体打包(会自动为p_msg_body分配合适长度的内存,注意释放) static int jt808_BodyPackage(JT808_2013_MsgFrame_t *p_MsgFrame, MessageID_t Msg_ID){ @@ -193,6 +196,49 @@ static int jt808_BodyPackage(JT808_2013_MsgFrame_t *p_MsgFrame, MessageID_t Msg_ // JT808_DEBUG("ID_Data_Up\r\n"); break; } + case ID_Req_data_update:{// //请求电子围栏/景点更新 + // 获取当前更新类型 + UpdateType_t current_type; + osMutexAcquire(update_mutex, osWaitForever); + current_type = update_status.type; + osMutexRelease(update_mutex); + + + uint32_t cur_ver = 0; + uint8_t update_type = (current_type == UPDATE_TYPE_FENCE) ? 1 : 2; + + // 获取对应类型的版本号 + uint8_t res = attr_broadcast_manage_version(VERSION_OP_GET, (current_type == UPDATE_TYPE_FENCE) ? VERSION_TYPE_FENCE : VERSION_TYPE_ATTRACTION, &cur_ver); + + if (res != 0) { + JT808_DEBUG("Failed to get version for update request\n"); + break; + } + + + // 根据协议构造消息体 (类型1字节 + 版本4字节) + uint8_t *update_req_buf = (uint8_t *)jt808_malloc(5); + if (update_req_buf == NULL) { + JT808_DEBUG("Malloc failed for update request\n"); + break; + } + + // 填充消息体 + update_req_buf[0] = update_type; // 更新类型 (1或2) + + // 填充对应类型的版本号 (大端格式) + uint32_t version_to_send = cur_ver; + version_to_send = Swap32(version_to_send); // 转换为大端字节序 + memcpy(update_req_buf + 1, &version_to_send, sizeof(uint32_t)); + + // 设置消息体长度 + p_MsgFrame->msg_head.msgbody_attr.msgbodylen = 5; + p_MsgFrame->p_msg_body = (void *)update_req_buf; + + JT808_DEBUG("Sending update request: type=%d, version=%u\n", update_type, cur_ver); + break; + } + default:{ return -2; break; diff --git a/custom/jt808/src/jt808_pkg_transmit.c b/custom/jt808/src/jt808_pkg_transmit.c index 79c38d0..b91946e 100644 --- a/custom/jt808/src/jt808_pkg_transmit.c +++ b/custom/jt808/src/jt808_pkg_transmit.c @@ -39,8 +39,6 @@ static void jt808_timeout_monitor_task(void *arg) { } - - // 触发消息发送修改版 int jt808_pkg_send(MessageID_t Msg_ID, uint32_t timeout){ pkg_msg_t send_pkg_msg={0}; @@ -63,7 +61,7 @@ void jt808_pkg_handle(uint8_t *receive_buf, uint16_t receive_len) { // 1. 解析协议数据 if (0 == jt808_msg_parse(receive_buf, receive_len, &PrsResult)) { // 2. 提取接收到的消息ID和流水号 这地方根据消息id分两种情况 一种是平台应答 另一种是平台指令 平台应答需要判断结构体里的id和流水号 - if (PrsResult.msg_head.msg_id == ID_Plat_GenResp ||PrsResult.msg_head.msg_id == ID_Term_RegResp) + if (PrsResult.msg_head.msg_id == ID_Plat_GenResp || PrsResult.msg_head.msg_id == ID_Term_RegResp || PrsResult.msg_head.msg_id == ID_data_update_reply) { uint16_t recv_msg_id = PrsResult.Rsp_msg_id; uint16_t recv_flow_num = PrsResult.Rsp_flow_num; diff --git a/custom/jt808/src/jt808_set_TermParam.c b/custom/jt808/src/jt808_set_TermParam.c index 802cc62..a8c09fa 100644 --- a/custom/jt808/src/jt808_set_TermParam.c +++ b/custom/jt808/src/jt808_set_TermParam.c @@ -9,7 +9,9 @@ #include "cm_fs.h" #include "cm_sim.h" #include "cm_pm.h" - +#include "cm_fota.h" +#include "attr_broadcast.h" +#include #include "local_tts.h" #include "control_out.h" @@ -19,33 +21,153 @@ #define LOCAL_FENCE_COUNT (sizeof(local_fences) / sizeof(LocalFenceConfig_t)) #define __DMB() __asm__ volatile("dmb sy" ::: "memory") // GCC + + Term_Param_item_t jt808_term_param_item; // 终端参数项 osMutexId_t term_param_mutex = NULL; // 终端参数项互斥锁 osMutexId_t Polygon_fence_mutex = NULL; // 多边形围栏互斥锁 +osMutexId_t update_mutex = NULL; //更新数据状态互斥锁 -// 安全获取RadarEN值 -uint8_t jt808_get_radar_en(void) { - // 内存屏障确保读取最新值 - __DMB(); - uint8_t value = jt808_term_param_item.set_term_param.RadarEN; - __DMB(); - return value; +// 定义全局变量 +UpdateStatus_t update_status = { + .state = UPDATE_IDLE, + .type = UPDATE_TYPE_NONE, + .start_time = 0, + .retry_count = 0, + .expected_packets = 0, + .received_packets = 0 +}; + + +static void __cm_fota_cb(cm_fota_error_e error) +{ + JT808_DEBUG("[FOTA] error code is %d\r\n", error); } -// 安全设置RadarEN值 -void jt808_set_radar_en(uint8_t value) { - // 验证值范围 - if (value > 2) { - JT808_DEBUG("Invalid RadarEN value: %d", value); - return; + +// 重置更新状态 +void update_manager_reset(void) { + osMutexAcquire(update_mutex, osWaitForever); + update_status.state = UPDATE_IDLE; + update_status.type = UPDATE_TYPE_NONE; + update_status.start_time = 0; + update_status.retry_count = 0; + update_status.expected_packets = 0; + update_status.received_packets = 0; + osMutexRelease(update_mutex); +} + +// 开始更新流程 +void update_manager_start(UpdateType_t type) { + osMutexAcquire(update_mutex, osWaitForever); + update_status.state = UPDATE_REQUEST_SENT; + update_status.type = type; + update_status.start_time = osKernelGetTickCount(); + update_status.retry_count = 0; + osMutexRelease(update_mutex); + + JT808_DEBUG("Update started: type=%d\n", type); +} + +// 标记数据接收开始 +void update_manager_begin_receiving(uint16_t packet_count) { + osMutexAcquire(update_mutex, osWaitForever); + update_status.state = UPDATE_RECEIVING_DATA; + update_status.expected_packets = packet_count; + update_status.received_packets = 0; + osMutexRelease(update_mutex); + + JT808_DEBUG("Receiving %d data packets\n", packet_count); +} + +// 标记数据包接收 +void update_manager_packet_received(void) { + osMutexAcquire(update_mutex, osWaitForever); + if (update_status.state == UPDATE_RECEIVING_DATA) { + update_status.received_packets++; + + // 检查是否所有数据包都已接收 + if (update_status.received_packets >= update_status.expected_packets) { + update_status.state = UPDATE_COMPLETE; + } } + osMutexRelease(update_mutex); +} + +// 标记更新完成 +void update_manager_complete(void) { + osMutexAcquire(update_mutex, osWaitForever); + update_status.state = UPDATE_COMPLETED; + osMutexRelease(update_mutex); - // 内存屏障 - __DMB(); - jt808_term_param_item.set_term_param.RadarEN = value; - __DMB(); + JT808_DEBUG("Update completed: type=%d\n", update_status.type); +} + +// 标记无需更新 +void update_manager_no_update(void) { + osMutexAcquire(update_mutex, osWaitForever); + update_status.state = UPDATE_COMPLETE; + osMutexRelease(update_mutex); - JT808_DEBUG("Set RadarEN to %d", value); + JT808_DEBUG("Update completed: type=%d\n", update_status.type); +} + +// 标记更新失败 +void update_manager_fail(void) { + osMutexAcquire(update_mutex, osWaitForever); + update_status.state = UPDATE_FAILED; + osMutexRelease(update_mutex); + + JT808_DEBUG("Update failed: type=%d\n", update_status.type); +} + +// 检查更新是否正在进行 +bool update_manager_is_active(void) { + bool is_active = false; + osMutexAcquire(update_mutex, osWaitForever); + is_active = (update_status.state != UPDATE_IDLE && + update_status.state != UPDATE_COMPLETED && + update_status.state != UPDATE_FAILED); + osMutexRelease(update_mutex); + return is_active; +} + +// 在 jt808_set_TermParam.c 文件中添加函数实现 +bool update_manager_is_complete(void) { + bool is_complete = false; + osMutexAcquire(update_mutex, osWaitForever); + is_complete = (update_status.state == UPDATE_COMPLETE); + osMutexRelease(update_mutex); + return is_complete; +} + + +// 超时检查(在自动上报任务中调用) +void update_manager_check_timeout(void) { + osMutexAcquire(update_mutex, osWaitForever); + + if (update_status.state == UPDATE_REQUEST_SENT || + update_status.state == UPDATE_RECEIVING_DATA) { + + uint32_t current_time = osKernelGetTickCount(); + uint32_t elapsed = current_time - update_status.start_time; + + // 30秒超时 + if (elapsed > 30000) { + if (update_status.retry_count < 3) { + JT808_DEBUG("Update timeout, retrying (%d/3)\n", update_status.retry_count + 1); + update_status.retry_count++; + update_status.start_time = current_time; + + // 这里可以触发重发机制 + // resend_update_request(update_status.type); + } else { + JT808_DEBUG("Update failed after 3 retries\n"); + update_status.state = UPDATE_FAILED; + } + } + } + osMutexRelease(update_mutex); } // 控制车辆状态 @@ -630,6 +752,16 @@ void Autoreport_param_Task(void *arg){ #endif while(1){ + + + // 检查超时 + update_manager_check_timeout(); + + + + if (!update_manager_is_active()) + { + // 正常的自动上报逻辑 osMutexAcquire(gps_data.mutex, osWaitForever); memcpy(&gps_data_msg, &gps_data, sizeof(gps_data_t)); osMutexRelease(gps_data.mutex); @@ -668,6 +800,88 @@ void Autoreport_param_Task(void *arg){ } } + + } + + + // 在15秒时发送电子围栏更新请求 + if (count_Sec == 15) { + if (!update_manager_is_active()) { + uint32_t fence_ver = 0; + int res = attr_broadcast_manage_version(VERSION_OP_GET, VERSION_TYPE_FENCE, &fence_ver); + + if (res == 0) { + update_manager_start(UPDATE_TYPE_FENCE); + jt808_pkg_send(ID_Req_data_update, 200); // 发送围栏更新请求 + JT808_DEBUG("Sending fence update request, version=%u\n", fence_ver); + } else { + JT808_DEBUG("Failed to get fence version: %d\n", res); + } + } else { + JT808_DEBUG("Skipping fence request: update already active\n"); + } + } + + // 在45秒时发送景点更新请求 + if (count_Sec == 45) { + if (!update_manager_is_active()) { + uint32_t attr_ver = 0; + int res = attr_broadcast_manage_version(VERSION_OP_GET, VERSION_TYPE_ATTRACTION, &attr_ver); + + if (res == 0) { + update_manager_start(UPDATE_TYPE_ATTR); + jt808_pkg_send(ID_Req_data_update, 200); // 发送景点更新请求 + JT808_DEBUG("Sending attraction update request, version=%u\n", attr_ver); + } else { + JT808_DEBUG("Failed to get attraction version: %d\n", res); + } + } else { + JT808_DEBUG("Skipping attraction request: update already active\n"); + } + } +/* + if(count_Sec == 80) + { + int ret = 0; + + cm_fota_set_ota_plan(CM_FOTA_ASR_PLAN_MINI_INTEGRATE); + cm_fota_res_callback_register((cm_fota_result_callback)__cm_fota_cb); + ret = cm_fota_set_url("https://youyuanmao.oss-cn-shanghai.aliyuncs.com/system_patch.bin"); //仅作为示例,url不可使用 + + cm_fs_system_info_t fs_info = {0, 0}; + cm_fs_getinfo(&fs_info); + + //文件系统剩余空间为0时不建议执行升级,关键文件系统操作可能会失败 + if (0 == fs_info.free_size) + { + JT808_DEBUG("insufficient space left in the file system"); + return; + } + + cm_fota_info_t info = {0}; + cm_fota_read_config(&info); + + if (CM_FOTA_TYPE_HTTP_HTTPS == info.fixed_info.fota_mode) + { + JT808_DEBUG("HTTP server [%s]", info.fixed_info.url); + } + else if (CM_FOTA_TYPE_FTP == info.fixed_info.fota_mode) + { + JT808_DEBUG("FTP server [%s] [%s] [%s]", info.fixed_info.url, info.fixed_info.username, info.fixed_info.passwd); + } + else + { + JT808_DEBUG("HTTP server error"); + return; + } + + osDelay(200); + + cm_fota_exec_upgrade(); + + } + +*/ count_Sec++; osDelay(1000/5); // 1S } @@ -701,6 +915,7 @@ void jt808_set_term_param_init(void){ term_param_mutex = osMutexNew(NULL); // 创建互斥锁 Polygon_fence_mutex = osMutexNew(NULL); // 创建互斥锁 + update_mutex = osMutexNew(NULL); // 创建互斥锁 memset(&jt808_term_param_item,0,sizeof(Term_Param_item_t)); // 加载围栏区域信息 @@ -742,7 +957,7 @@ void jt808_set_term_param_init(void){ }else{ jt808_term_param_item.set_term_param.HeartBeatInterval = 30; // 心跳包间隔(秒) #if 1 - char ServerAddr[] ="106.15.224.140"; // 三一服务器地址 + char ServerAddr[] ="106.15.224.140"; // 三一服务器地址 47.99.118.34 uint32_t ServerPort = 5000; // 服务器端口 #else char ServerAddr[] ="106.14.30.23"; // 生产服务器地址