From: 孙春阳 Date: Sun, 21 Sep 2025 13:59:21 +0000 (+0800) Subject: 优化ppp接收发送 测试程序可以发一包 X-Git-Tag: leiniao_debug_1.0.0_20250922_020716~1^2~5 X-Git-Url: http://git.snicker.com.cn/css/static/gitweb.css?a=commitdiff_plain;h=db9c18629cd17b1560b5bf588ade72dd769cff6a;p=mirrors%2FFD230.git 优化ppp接收发送 测试程序可以发一包 --- diff --git a/code/sdk/zhihui/ppp/example/ppp_at_cmd.c b/code/sdk/zhihui/ppp/example/ppp_at_cmd.c index 9e7fbf666..e796366c4 100644 --- a/code/sdk/zhihui/ppp/example/ppp_at_cmd.c +++ b/code/sdk/zhihui/ppp/example/ppp_at_cmd.c @@ -959,7 +959,7 @@ static errcode_t at_ppp_update_single_runtime(uint8_t *para, uint32_t paraLen) return ret; } - +extern void ppp_lwip_print_discard_stats(void); /* PPP Runtime状态显示指令 */ static errcode_t at_ppp_show_runtime(uint8_t *para, uint32_t paraLen) { @@ -1013,6 +1013,8 @@ static errcode_t at_ppp_show_runtime(uint8_t *para, uint32_t paraLen) ppp_print_info("===============================\n"); + ppp_lwip_print_discard_stats(); + return ERRCODE_SUCC; } diff --git a/code/sdk/zhihui/ppp/include/ppp_api.h b/code/sdk/zhihui/ppp/include/ppp_api.h index 951c5caff..86c69b1e9 100644 --- a/code/sdk/zhihui/ppp/include/ppp_api.h +++ b/code/sdk/zhihui/ppp/include/ppp_api.h @@ -266,6 +266,24 @@ bool ppp_lwip_is_receive_thread_running(void); */ errcode_t ppp_get_network_signal_info(void); +/* ========== PPP字节丢弃统计接口 ========== */ + +/** + * @brief 获取PPP字节丢弃统计信息 + * @param stats 统计信息结构指针 + */ +void ppp_lwip_get_discard_stats(ppp_discard_stats_t *stats); + +/** + * @brief 重置PPP字节丢弃统计信息 + */ +void ppp_lwip_reset_discard_stats(void); + +/** + * @brief 打印PPP字节丢弃统计信息 + */ +void ppp_lwip_print_discard_stats(void); + #ifdef __cplusplus } #endif diff --git a/code/sdk/zhihui/ppp/include/ppp_types.h b/code/sdk/zhihui/ppp/include/ppp_types.h index 4e43cb797..7d865e872 100644 --- a/code/sdk/zhihui/ppp/include/ppp_types.h +++ b/code/sdk/zhihui/ppp/include/ppp_types.h @@ -70,6 +70,21 @@ typedef struct { uint32_t last_activity; // 最后活动时间戳 } ppp_stats_t; +/* PPP字节丢弃统计结构 */ +typedef struct { + uint32_t total_bytes_received; // 总接收字节数 + uint32_t total_bytes_processed; // 总处理字节数 + uint32_t total_bytes_discarded; // 总丢弃字节数 + uint32_t invalid_frame_bytes; // 无效帧丢弃字节数 + uint32_t queue_full_bytes; // 队列满丢弃字节数 + uint32_t malloc_fail_bytes; // 内存分配失败丢弃字节数 + uint32_t oversized_frame_bytes; // 超大帧丢弃字节数 + uint32_t incomplete_frame_bytes; // 不完整帧丢弃字节数 + uint32_t total_frames_received; // 总接收帧数 + uint32_t total_frames_processed; // 总处理帧数 + uint32_t total_frames_discarded; // 总丢弃帧数 +} ppp_discard_stats_t; + /* PPP事件类型 */ typedef enum { PPP_EVENT_CONNECTED = 0, // 连接成功 diff --git a/code/sdk/zhihui/ppp/src/ppp_lwip.c b/code/sdk/zhihui/ppp/src/ppp_lwip.c index 27a82df3c..61885111b 100644 --- a/code/sdk/zhihui/ppp/src/ppp_lwip.c +++ b/code/sdk/zhihui/ppp/src/ppp_lwip.c @@ -481,53 +481,21 @@ ppp_error_t ppp_lwip_input(const uint8_t *data, uint32_t length) #ifndef LWIP_PPP_SUPPORT return PPP_ERR_INVALID_PARAM; #else - /* 参数和状态验证 */ - if (!g_lwip_ctx.initialized || !data || length == 0) { + if (!g_lwip_ctx.initialized || !g_lwip_ctx.ppp_pcb || !data || length == 0) { return PPP_ERR_INVALID_PARAM; } - - /* 记录数据活动 */ - ppp_lwip_record_data_activity(); - - /* 检查PPP控制块是否有效 */ - if (!g_lwip_ctx.ppp_pcb) { - PPP_DEBUG_ERROR("PPP control block is NULL"); - return PPP_ERR_INVALID_STATE; - } - /* 添加数据完整性检查 */ - if (length > 1500) { // PPP最大帧长度 - PPP_DEBUG_WARN("PPP frame too large: %u bytes, truncating", length); - length = 1500; // 截断而不是拒绝 - } - - /* 检查数据有效性 */ - if (length < 4) { // PPP最小帧长度(地址+控制+协议) - PPP_DEBUG_DEBUG("PPP frame too small: %u bytes, ignoring", length); - return PPP_ERR_OK; // 忽略而不是报错 - } - - PPP_DEBUG_PRINT("=== PPP Input Data ===\n"); - PPP_DEBUG_PRINT("Length: %d bytes\n", length); - // PPP_DEBUG_HEXDUMP("RX", data, length > 32 ? 32 : length); + ppp_lwip_record_data_activity(); #if !NO_SYS && !PPP_INPROC_IRQ_SAFE - /* 在多线程环境中使用线程安全的输入函数,添加异常保护 */ - err_t result; - - /* 使用try-catch风格的错误处理 */ - result = pppos_input_tcpip(g_lwip_ctx.ppp_pcb, (u8_t *)data, length); + err_t result = pppos_input_tcpip(g_lwip_ctx.ppp_pcb, (u8_t *)data, length); if (result != ERR_OK) { PPP_DEBUG_WARN("pppos_input_tcpip failed with error: %d (continuing)", result); - /* 不要因为单次输入失败就返回错误,这可能导致连接中断 */ - return PPP_ERR_OK; } #else - /* 在单线程或中断安全环境中使用直接输入 */ pppos_input(g_lwip_ctx.ppp_pcb, (u8_t *)data, length); #endif - PPP_DEBUG_PRINT("=== PPP Input Processed ===\n"); return PPP_ERR_OK; #endif } @@ -940,6 +908,16 @@ static u32_t ppp_lwip_output_callback(ppp_pcb *pcb, u8_t *data, u32_t len, void PPP_DEBUG_PRINT("=== PPP Protocol Output ===\n"); PPP_DEBUG_PRINT("PPP TX: %u bytes\n", len); + /* 打印发送数据的前几个字节 */ + PPP_DEBUG_PRINT("Raw Data: "); + for (uint32_t i = 0; i < len && i < 32; i++) { + PPP_DEBUG_PRINT("%02X ", data[i]); + } + if (len > 32) { + PPP_DEBUG_PRINT("... (total %u bytes)", len); + } + PPP_DEBUG_PRINT("\n"); + /* 解析PPP协议包 */ if (len >= 4) { /* 跳过PPP Flag (0x7E) 如果存在 */ diff --git a/code/sdk/zhihui/ppp/src/ppp_lwip_thread.c b/code/sdk/zhihui/ppp/src/ppp_lwip_thread.c index 2007edab8..57bcf18fc 100644 --- a/code/sdk/zhihui/ppp/src/ppp_lwip_thread.c +++ b/code/sdk/zhihui/ppp/src/ppp_lwip_thread.c @@ -1,6 +1,6 @@ /** * @file ppp_lwip_thread.c - * @brief PPP接收线程实现 - 参考lwIP slipif_init方式 + * @brief PPP接收线程实现 - 双线程架构:接收线程拼包 + 处理线程处理 */ #include "ppp_api.h" @@ -10,243 +10,513 @@ #include "ppp_lwip.h" #include "soc_osal.h" #include "schedule/osal_task.h" +#include "msgqueue/osal_msgqueue.h" #include #include -/* PPP接收线程相关 - 参考slipif_init方式 */ -static osal_task *g_ppp_rx_thread = NULL; +/* PPP帧队列相关 */ +#define PPP_FRAME_QUEUE_SIZE 32 // 队列大小 +#define PPP_MAX_FRAME_SIZE 1500 // PPP最大帧大小 + +/* PPP帧结构 */ +typedef struct { + uint8_t *data; // 帧数据 + uint32_t length; // 帧长度 +} ppp_frame_t; + +/* PPP字节丢弃统计结构已在ppp_types.h中定义 */ + +/* PPP线程相关 */ +static osal_task *g_ppp_rx_thread = NULL; // 接收线程 +static osal_task *g_ppp_process_thread = NULL; // 处理线程 static volatile bool g_ppp_rx_thread_running = false; +static volatile bool g_ppp_process_thread_running = false; static volatile bool g_ppp_rx_thread_completed = false; +static volatile bool g_ppp_process_thread_completed = false; + +/* PPP帧队列 */ +static unsigned long g_ppp_frame_queue = 0xFFFFFFFFu; + +/* PPP接收缓冲区 */ +static uint8_t g_ppp_rx_buffer[PPP_RX_BUFFER_SIZE * 2]; // 双倍缓冲区用于拼包 +static uint32_t g_buffer_pos = 0; // 当前缓冲区位置 + +/* PPP帧标志 */ +#define PPP_FLAG_CHAR 0x7E // PPP帧标志字符 +#define PPP_ESCAPE_CHAR 0x7D // PPP转义字符 +#define PPP_ESCAPE_MASK 0x20 // PPP转义掩码 + +/* PPP丢弃统计 */ +static ppp_discard_stats_t g_ppp_discard_stats = {0}; + +/* PPP帧拼包状态 */ +typedef struct { + uint8_t *buffer; // 拼包缓冲区 + uint32_t buffer_size; // 缓冲区大小 + uint32_t data_len; // 当前数据长度 + bool in_frame; // 是否在帧内 + bool escaped; // 是否转义状态 +} ppp_frame_assembler_t; + +static ppp_frame_assembler_t g_frame_assembler = {0}; /** - * @brief PPP数据接收线程 - 参考slipif_init实现 - * - * 这个实现参考了lwIP中slipif_init的线程创建方式: - * 1. 使用标准的线程函数签名 - * 2. 实现流数据处理和PPP帧重组 - * 3. 直接调用lwIP的输入函数 + * @brief 初始化PPP帧拼包器 */ -static void ppp_lwip_receive_thread(void *arg) +static ppp_error_t ppp_frame_assembler_init(void) { - (void)arg; + g_frame_assembler.buffer_size = PPP_MAX_FRAME_SIZE; + g_frame_assembler.buffer = (uint8_t *)malloc(g_frame_assembler.buffer_size); + if (!g_frame_assembler.buffer) { + PPP_DEBUG_ERROR("Failed to allocate frame assembler buffer"); + return PPP_ERR_MEMORY; + } - PPP_DEBUG_DEBUG("PPP lwIP receive thread started (slipif-style)"); + g_frame_assembler.data_len = 0; + g_frame_assembler.in_frame = false; + g_frame_assembler.escaped = false; - /* 动态分配接收缓冲区以避免栈溢出 */ - uint8_t *rx_buffer = (uint8_t *)malloc(PPP_RX_BUFFER_SIZE); - if (!rx_buffer) { - PPP_DEBUG_ERROR("Failed to allocate RX buffer for PPP thread"); - return; + PPP_DEBUG_INFO("PPP frame assembler initialized with %u bytes buffer", + g_frame_assembler.buffer_size); + return PPP_ERR_OK; +} + +/** + * @brief 清理PPP帧拼包器 + */ +static void ppp_frame_assembler_cleanup(void) +{ + if (g_frame_assembler.buffer) { + free(g_frame_assembler.buffer); + g_frame_assembler.buffer = NULL; + } + memset(&g_frame_assembler, 0, sizeof(g_frame_assembler)); +} + +/** + * @brief 重置帧拼包状态 + */ +static void ppp_frame_assembler_reset(void) +{ + g_frame_assembler.data_len = 0; + g_frame_assembler.in_frame = false; + g_frame_assembler.escaped = false; +} + +/** + * @brief 发送完整PPP帧到队列 + */ +static ppp_error_t ppp_send_frame_to_queue(const uint8_t *data, uint32_t length) +{ + if (!data || length == 0 || length > PPP_MAX_FRAME_SIZE) { + return PPP_ERR_INVALID_PARAM; } - /* 动态分配PPP流处理缓冲区 */ - uint8_t *stream_buffer = (uint8_t *)malloc(PPP_RX_BUFFER_SIZE * 2); - if (!stream_buffer) { - PPP_DEBUG_ERROR("Failed to allocate stream buffer for PPP thread"); - free(rx_buffer); - return; + // 分配帧结构 + ppp_frame_t *frame = (ppp_frame_t *)malloc(sizeof(ppp_frame_t)); + if (!frame) { + PPP_DEBUG_ERROR("Failed to allocate frame structure"); + + // 统计内存分配失败丢弃 + g_ppp_discard_stats.total_frames_discarded++; + g_ppp_discard_stats.malloc_fail_bytes += length; + g_ppp_discard_stats.total_bytes_discarded += length; + + return PPP_ERR_MEMORY; } - uint32_t bytes_read; - uint32_t total_packets = 0; - uint32_t total_bytes = 0; + // 分配帧数据 + frame->data = (uint8_t *)malloc(length); + if (!frame->data) { + PPP_DEBUG_ERROR("Failed to allocate frame data"); + + // 统计内存分配失败丢弃 + g_ppp_discard_stats.total_frames_discarded++; + g_ppp_discard_stats.malloc_fail_bytes += length; + g_ppp_discard_stats.total_bytes_discarded += length; + + free(frame); + return PPP_ERR_MEMORY; + } - /* PPP流处理状态 */ - uint32_t stream_pos = 0; /* 流缓冲区当前位置 */ - bool in_frame = false; /* 是否正在接收PPP帧 */ - uint32_t frame_start = 0; /* 当前帧开始位置 */ - uint32_t complete_frames = 0; /* 完整帧计数 */ + // 复制数据 + memcpy(frame->data, data, length); + frame->length = length; - PPP_DEBUG_DEBUG("PPP lwIP thread: allocated %d bytes RX buffer + %d bytes stream buffer", - PPP_RX_BUFFER_SIZE, PPP_RX_BUFFER_SIZE * 2); + // 发送到队列 + int ret = osal_msg_queue_write_copy(g_ppp_frame_queue, &frame, sizeof(ppp_frame_t *), 0); + if (ret != OSAL_SUCCESS) { + PPP_DEBUG_WARN("Failed to send frame to queue: %d, dropping frame", ret); + + // 统计队列满丢弃 + g_ppp_discard_stats.total_frames_discarded++; + g_ppp_discard_stats.queue_full_bytes += length; + g_ppp_discard_stats.total_bytes_discarded += length; + + free(frame->data); + free(frame); + return PPP_ERR_UNKNOWN; // 使用已定义的错误码 + } - PPP_DEBUG_CRITICAL("PPP receive thread started and entering main loop"); - PPP_DEBUG_DEBUG("Thread will read from serial in PPP data mode"); + PPP_DEBUG_DEBUG("Sent complete PPP frame to queue: %u bytes", length); - /* 主接收循环 - 参考slipif的实现方式 */ + // 更新帧统计 + g_ppp_discard_stats.total_frames_received++; + + return PPP_ERR_OK; +} + +/** + * @brief 处理接收到的字节,进行PPP帧拼包(保留完整帧格式) + */ +static ppp_error_t ppp_process_received_byte(uint8_t byte) +{ + // PPP帧标志字符 + if (byte == 0x7E) { + if (g_frame_assembler.in_frame && g_frame_assembler.data_len > 0) { + // 帧结束,添加结束标志并发送完整帧 + if (g_frame_assembler.data_len < g_frame_assembler.buffer_size) { + g_frame_assembler.buffer[g_frame_assembler.data_len++] = 0x7E; + + PPP_DEBUG_DEBUG("Complete PPP frame assembled: %u bytes (with 0x7E flags)", g_frame_assembler.data_len); + + // 发送完整帧(包含首尾0x7E标志) + ppp_send_frame_to_queue(g_frame_assembler.buffer, g_frame_assembler.data_len); + } else { + // 缓冲区满,丢弃帧 + PPP_DEBUG_WARN("Frame buffer full, dropping frame"); + g_ppp_discard_stats.total_frames_discarded++; + g_ppp_discard_stats.oversized_frame_bytes += g_frame_assembler.data_len; + g_ppp_discard_stats.total_bytes_discarded += g_frame_assembler.data_len; + } + } else if (g_frame_assembler.in_frame && g_frame_assembler.data_len == 0) { + // 连续的0x7E标志,这是正常的 + PPP_DEBUG_DEBUG("Consecutive 0x7E flag, continuing"); + } else { + // 不在帧内收到0x7E,开始新帧 + PPP_DEBUG_DEBUG("Starting new frame with 0x7E"); + } + + // 开始新帧,首先添加起始0x7E标志 + ppp_frame_assembler_reset(); + g_frame_assembler.in_frame = true; + g_frame_assembler.buffer[g_frame_assembler.data_len++] = 0x7E; + return PPP_ERR_OK; + } + + // 如果不在帧内,忽略数据 + if (!g_frame_assembler.in_frame) { + // 统计被忽略的数据 + g_ppp_discard_stats.total_bytes_discarded++; + g_ppp_discard_stats.incomplete_frame_bytes++; + PPP_DEBUG_DEBUG("Ignoring byte 0x%02X outside frame", byte); + return PPP_ERR_OK; + } + + // 检查缓冲区空间(为结束0x7E预留1字节) + if (g_frame_assembler.data_len >= (g_frame_assembler.buffer_size - 1)) { + PPP_DEBUG_WARN("Frame too large (%u bytes), resetting assembler", g_frame_assembler.data_len); + + // 统计超大帧丢弃 + g_ppp_discard_stats.total_frames_discarded++; + g_ppp_discard_stats.oversized_frame_bytes += g_frame_assembler.data_len; + g_ppp_discard_stats.total_bytes_discarded += g_frame_assembler.data_len; + + ppp_frame_assembler_reset(); + return PPP_ERR_UNKNOWN; + } + + // 直接添加字节到帧缓冲区(保留原始格式,包括转义字符) + g_frame_assembler.buffer[g_frame_assembler.data_len++] = byte; + + return PPP_ERR_OK; +} + +/** + * @brief PPP数据接收线程 - 只负责拼包 + */ +static void ppp_lwip_receive_thread(void *arg) +{ + (void)arg; + + PPP_DEBUG_INFO("PPP receive thread started (frame assembly mode)"); + + // 分配串口接收缓冲区 + uint8_t *rx_buffer = (uint8_t *)malloc(PPP_RX_BUFFER_SIZE); + if (!rx_buffer) { + PPP_DEBUG_ERROR("PPP RX buffer alloc failed"); + g_ppp_rx_thread_completed = true; + return; + } + + uint32_t bytes_read = 0; + uint32_t total_bytes = 0, total_frames = 0; + + PPP_DEBUG_DEBUG("PPP receive thread: allocated %d bytes RX buffer", PPP_RX_BUFFER_SIZE); + + // 主循环:从串口读取数据并拼包 while (g_ppp_rx_thread_running) { - // 从串口读取PPP数据 - 使用较短的超时时间提高响应性 + // 从串口读取数据 ppp_error_t ret = ppp_serial_read(rx_buffer, PPP_RX_BUFFER_SIZE, &bytes_read); - + if (ret == PPP_ERR_OK && bytes_read > 0) { - total_packets++; total_bytes += bytes_read; - PPP_DEBUG_DEBUG("PPP RX: Got %u bytes (total: %u packets, %u bytes)", - bytes_read, total_packets, total_bytes); - - /* 将接收到的数据添加到流缓冲区 - 加强边界检查 */ - if (stream_pos + bytes_read < PPP_RX_BUFFER_SIZE * 2) { - memcpy(stream_buffer + stream_pos, rx_buffer, bytes_read); - stream_pos += bytes_read; - } else { - PPP_DEBUG_WARN("PPP stream buffer overflow, resetting"); - stream_pos = 0; - frame_start = 0; - in_frame = false; - continue; // 跳过本次处理,避免缓冲区溢出 - } - - /* 处理PPP帧 - 查找0x7E标志并添加边界检查 */ - for (uint32_t i = frame_start; i < stream_pos && i < PPP_RX_BUFFER_SIZE * 2; i++) { - if (stream_buffer[i] == 0x7E) { - if (in_frame && i > frame_start) { - /* 找到帧结束,处理完整的PPP帧 */ - uint32_t frame_len = i - frame_start + 1; - - /* 添加帧长度合理性检查 */ - if (frame_len > 0 && frame_len <= 1500) { // PPP最大帧长度 - PPP_DEBUG_DEBUG("PPP complete frame found: %u bytes", frame_len); - - /* 确保指针和长度有效 */ - if (frame_start + frame_len <= PPP_RX_BUFFER_SIZE * 2 && - frame_start < PPP_RX_BUFFER_SIZE * 2) { - /* 将完整帧传递给lwIP - 让lwIP自己验证帧的有效性 */ - ppp_lwip_input(stream_buffer + frame_start, frame_len); - complete_frames++; - } else { - PPP_DEBUG_WARN("PPP frame pointer out of bounds: start=%u, len=%u", - frame_start, frame_len); - } - } else { - PPP_DEBUG_WARN("Invalid PPP frame length: %u (frame_start=%u, i=%u)", - frame_len, frame_start, i); - } - } - - /* 开始新帧 */ - frame_start = i; - in_frame = true; - } - } - - /* 如果缓冲区使用超过一半,移动未处理的数据到缓冲区开头 */ - if (frame_start > PPP_RX_BUFFER_SIZE) { - uint32_t remaining = stream_pos - frame_start; - if (remaining > 0) { - memmove(stream_buffer, stream_buffer + frame_start, remaining); + // 更新接收字节统计 + g_ppp_discard_stats.total_bytes_received += bytes_read; + + PPP_DEBUG_DEBUG("PPP RX: Got %u bytes (total: %u bytes)", bytes_read, total_bytes); + + // 逐字节处理,进行PPP帧拼包(保留完整帧格式) + for (uint32_t i = 0; i < bytes_read; i++) { + ppp_error_t process_ret = ppp_process_received_byte(rx_buffer[i]); + if (process_ret != PPP_ERR_OK) { + // 字节处理失败,统计丢弃 + g_ppp_discard_stats.total_bytes_discarded++; + g_ppp_discard_stats.invalid_frame_bytes++; } - stream_pos = remaining; - frame_start = 0; - /* 保持in_frame状态,不要重置为false */ - /* in_frame状态应该保持,因为我们可能正在处理一个跨缓冲区的帧 */ } } else if (ret != PPP_ERR_OK) { - /* 检查是否是因为线程停止导致的错误 */ + // 读取错误 if (!g_ppp_rx_thread_running) { - PPP_DEBUG_DEBUG("PPP thread stopping, ignoring read error"); + PPP_DEBUG_DEBUG("PPP receive thread stopping, ignoring read error"); break; } PPP_DEBUG_ERROR("PPP serial read error: %d", ret); } else { - /* ret == PPP_ERR_OK but bytes_read == 0 - no data available */ - static uint32_t no_data_count = 0; - no_data_count++; + // 没有数据,短暂休眠 + osal_msleep(1); + } + + // 定期打印统计信息 + if ((total_frames % 100) == 0 && total_frames > 0) { + PPP_DEBUG_DEBUG("PPP RX: %u frames assembled, %u total bytes", + total_frames, total_bytes); + } + } + + // 释放缓冲区 + free(rx_buffer); + + PPP_DEBUG_INFO("PPP receive thread completed"); + g_ppp_rx_thread_completed = true; +} + +/** + * @brief PPP帧处理线程 - 从队列获取完整帧并处理 + */ +static void ppp_lwip_process_thread(void *arg) +{ + (void)arg; + + PPP_DEBUG_INFO("PPP process thread started"); + + uint32_t processed_frames = 0; + uint32_t processed_bytes = 0; + + // 主循环:从队列获取完整PPP帧并处理 + while (g_ppp_process_thread_running) { + ppp_frame_t *frame = NULL; + uint32_t msg_size = sizeof(ppp_frame_t *); + + // 从队列获取帧(超时100ms) + int ret = osal_msg_queue_read_copy(g_ppp_frame_queue, &frame, &msg_size, OSAL_MSGQ_WAIT_FOREVER); + + if (ret == OSAL_SUCCESS && frame) { + // 处理完整PPP帧 + PPP_DEBUG_DEBUG("Processing PPP frame: %u bytes", frame->length); - /* 减少无数据时的日志输出频率,避免日志开销 */ - if (no_data_count % 5000 == 0) { /* 每5000次无数据时打印一次 */ - PPP_DEBUG_DEBUG("PPP RX: No data available (count: %u)", no_data_count); - } + // 调用lwIP处理 + ppp_lwip_input(frame->data, frame->length); + + // 更新统计 + processed_frames++; + processed_bytes += frame->length; + + // 更新处理统计 + g_ppp_discard_stats.total_frames_processed++; + g_ppp_discard_stats.total_bytes_processed += frame->length; - /* 适应性休眠策略,避免过度占用CPU */ - if (no_data_count % 5 == 0) { /* 每5次无数据后短暂休眠 */ - osal_msleep(2); /* 2ms休眠,让出CPU给tcpip_thread */ + // 释放帧内存 + free(frame->data); + free(frame); + + // 定期打印统计 + if ((processed_frames % 50) == 0) { + PPP_DEBUG_DEBUG("PPP Process: %u frames, %u bytes processed", + processed_frames, processed_bytes); + } + } else if (ret != OSAL_SUCCESS) { + // 超时或其他错误 + if (!g_ppp_process_thread_running) { + PPP_DEBUG_DEBUG("PPP process thread stopping"); + break; + } + // 超时是正常的,其他错误才需要警告 + if (ret != 0) { // 假设超时返回非0值 + PPP_DEBUG_WARN("Failed to read from frame queue: %d", ret); } } - - // 统计信息输出 - 每5000次循环输出一次,减少日志开销 - if ((total_packets % 5000) == 0 && total_packets > 0) { - PPP_DEBUG_DEBUG("PPP lwIP RX: %u fragments, %u complete frames, %u total bytes", - total_packets, complete_frames, total_bytes); - } - - // 适应性休眠以避免CPU占用过高,给tcpip_thread更多时间 - if (complete_frames > 0) { - osal_msleep(5); /* 有数据时短休眠 */ - } else { - osal_msleep(15); /* 无数据时长休眠 */ + // 超时是正常的,继续循环 + } + + // 清理队列中剩余的帧 + ppp_frame_t *frame = NULL; + uint32_t msg_size = sizeof(ppp_frame_t *); + while (osal_msg_queue_read_copy(g_ppp_frame_queue, &frame, &msg_size, 0) == OSAL_SUCCESS) { + if (frame) { + free(frame->data); + free(frame); } } - - /* 清理资源 */ - free(rx_buffer); - free(stream_buffer); - - PPP_DEBUG_INFO("PPP lwIP receive thread completed"); - g_ppp_rx_thread_completed = true; - - PPP_DEBUG_INFO("PPP lwIP receive thread exiting"); + + PPP_DEBUG_INFO("PPP process thread completed, processed %u frames, %u bytes", + processed_frames, processed_bytes); + g_ppp_process_thread_completed = true; } /** - * @brief 启动PPP接收线程 - 参考slipif_init方式 + * @brief 启动PPP接收和处理线程 */ ppp_error_t ppp_lwip_start_receive_thread(void) { - if (g_ppp_rx_thread != NULL) { - PPP_DEBUG_WARN("PPP lwIP receive thread already running"); + if (g_ppp_rx_thread != NULL || g_ppp_process_thread != NULL) { + PPP_DEBUG_WARN("PPP threads already running"); return PPP_ERR_INVALID_STATE; } - PPP_DEBUG_INFO("Starting PPP lwIP receive thread"); + PPP_DEBUG_INFO("Starting PPP receive and process threads"); + + // 初始化帧拼包器 + ppp_error_t ret = ppp_frame_assembler_init(); + if (ret != PPP_ERR_OK) { + PPP_DEBUG_ERROR("Failed to initialize frame assembler"); + return ret; + } + + // 创建帧队列 + int queue_ret = osal_msg_queue_create("ppp_frames", PPP_FRAME_QUEUE_SIZE, + &g_ppp_frame_queue, 0, sizeof(ppp_frame_t *)); + if (queue_ret != OSAL_SUCCESS) { + PPP_DEBUG_ERROR("Failed to create PPP frame queue: %d", queue_ret); + ppp_frame_assembler_cleanup(); + return PPP_ERR_MEMORY; + } - // 重置线程完成标志 + // 重置完成标志 g_ppp_rx_thread_completed = false; + g_ppp_process_thread_completed = false; - // 设置线程运行标志为true(在创建线程之前) - g_ppp_rx_thread_running = true; + // 启动处理线程 + g_ppp_process_thread_running = true; + g_ppp_process_thread = osal_kthread_create((osal_kthread_handler)ppp_lwip_process_thread, + NULL, "ppp_process", 4096); + if (!g_ppp_process_thread) { + PPP_DEBUG_ERROR("Failed to create PPP process thread"); + g_ppp_process_thread_running = false; + osal_msg_queue_delete(g_ppp_frame_queue); + g_ppp_frame_queue = 0; + ppp_frame_assembler_cleanup(); + return PPP_ERR_MEMORY; + } - // 创建线程 - 使用正确的OSAL API + // 启动接收线程 + g_ppp_rx_thread_running = true; g_ppp_rx_thread = osal_kthread_create((osal_kthread_handler)ppp_lwip_receive_thread, - NULL, "ppp_lwip_rx", 4096); - if (g_ppp_rx_thread == NULL) { - PPP_DEBUG_ERROR("Failed to create PPP lwIP receive thread"); - g_ppp_rx_thread_running = false; // 创建失败时重置标志 + NULL, "ppp_receive", 4096); + if (!g_ppp_rx_thread) { + PPP_DEBUG_ERROR("Failed to create PPP receive thread"); + g_ppp_rx_thread_running = false; + + // 清理处理线程 + g_ppp_process_thread_running = false; + osal_kthread_destroy(g_ppp_process_thread, 0); + g_ppp_process_thread = NULL; + + // 清理资源 + osal_msg_queue_delete(g_ppp_frame_queue); + g_ppp_frame_queue = 0; + ppp_frame_assembler_cleanup(); return PPP_ERR_MEMORY; } - PPP_DEBUG_INFO("PPP lwIP receive thread started successfully"); + PPP_DEBUG_INFO("PPP receive and process threads started successfully"); return PPP_ERR_OK; } /** - * @brief 停止PPP接收线程 + * @brief 停止PPP接收和处理线程 */ ppp_error_t ppp_lwip_stop_receive_thread(void) { - if (g_ppp_rx_thread == NULL) { + if (!g_ppp_rx_thread && !g_ppp_process_thread) { return PPP_ERR_OK; } - PPP_DEBUG_INFO("Stopping PPP lwIP receive thread"); - - // 停止线程 - g_ppp_rx_thread_running = false; + PPP_DEBUG_INFO("Stopping PPP receive and process threads"); - // 等待线程完成,使用completion flag进行同步 - int wait_count = 0; - const int max_wait = 100; // 最多等待1秒 + // 停止接收线程 + if (g_ppp_rx_thread) { + g_ppp_rx_thread_running = false; + + // 等待接收线程完成 + int wait_count = 0; + const int max_wait = 100; + + while (wait_count < max_wait && !g_ppp_rx_thread_completed) { + osal_msleep(10); + wait_count++; + } + + if (g_ppp_rx_thread_completed) { + PPP_DEBUG_DEBUG("PPP receive thread completed gracefully"); + } else { + PPP_DEBUG_WARN("PPP receive thread did not complete within timeout"); + } + + osal_kthread_destroy(g_ppp_rx_thread, 0); + g_ppp_rx_thread = NULL; + } - while (wait_count < max_wait && !g_ppp_rx_thread_completed) { - osal_msleep(10); - wait_count++; + // 停止处理线程 + if (g_ppp_process_thread) { + g_ppp_process_thread_running = false; + + // 等待处理线程完成 + int wait_count = 0; + const int max_wait = 100; + + while (wait_count < max_wait && !g_ppp_process_thread_completed) { + osal_msleep(10); + wait_count++; + } + + if (g_ppp_process_thread_completed) { + PPP_DEBUG_DEBUG("PPP process thread completed gracefully"); + } else { + PPP_DEBUG_WARN("PPP process thread did not complete within timeout"); + } + + osal_kthread_destroy(g_ppp_process_thread, 0); + g_ppp_process_thread = NULL; } - if (g_ppp_rx_thread_completed) { - PPP_DEBUG_DEBUG("PPP thread completed gracefully after %d ms", wait_count * 10); - } else { - PPP_DEBUG_WARN("PPP thread did not complete within timeout, forcing termination"); + // 清理队列 + if (g_ppp_frame_queue != 0) { + osal_msg_queue_delete(g_ppp_frame_queue); + g_ppp_frame_queue = 0; } - // 销毁线程 - osal_kthread_destroy(g_ppp_rx_thread, 0); - g_ppp_rx_thread = NULL; + // 清理帧拼包器 + ppp_frame_assembler_cleanup(); // 重置状态标志 g_ppp_rx_thread_running = false; + g_ppp_process_thread_running = false; g_ppp_rx_thread_completed = false; + g_ppp_process_thread_completed = false; - PPP_DEBUG_INFO("PPP lwIP receive thread stopped"); + PPP_DEBUG_INFO("PPP receive and process threads stopped"); return PPP_ERR_OK; } @@ -255,5 +525,60 @@ ppp_error_t ppp_lwip_stop_receive_thread(void) */ bool ppp_lwip_is_receive_thread_running(void) { - return g_ppp_rx_thread_running && (g_ppp_rx_thread != NULL); + return g_ppp_rx_thread_running && (g_ppp_rx_thread != NULL) && + g_ppp_process_thread_running && (g_ppp_process_thread != NULL); +} + +/** + * @brief 获取PPP字节丢弃统计信息 + */ +void ppp_lwip_get_discard_stats(ppp_discard_stats_t *stats) +{ + if (stats) { + memcpy(stats, &g_ppp_discard_stats, sizeof(ppp_discard_stats_t)); + } +} + +/** + * @brief 重置PPP字节丢弃统计信息 + */ +void ppp_lwip_reset_discard_stats(void) +{ + memset(&g_ppp_discard_stats, 0, sizeof(ppp_discard_stats_t)); + PPP_DEBUG_INFO("PPP discard statistics reset"); +} + +/** + * @brief 打印PPP字节丢弃统计信息 + */ +void ppp_lwip_print_discard_stats(void) +{ + PPP_DEBUG_INFO("=== PPP Discard Statistics ==="); + PPP_DEBUG_INFO("Total Bytes Received: %u", g_ppp_discard_stats.total_bytes_received); + PPP_DEBUG_INFO("Total Bytes Processed: %u", g_ppp_discard_stats.total_bytes_processed); + PPP_DEBUG_INFO("Total Bytes Discarded: %u", g_ppp_discard_stats.total_bytes_discarded); + PPP_DEBUG_INFO(" - Invalid Frame Bytes: %u", g_ppp_discard_stats.invalid_frame_bytes); + PPP_DEBUG_INFO(" - Queue Full Bytes: %u", g_ppp_discard_stats.queue_full_bytes); + PPP_DEBUG_INFO(" - Malloc Fail Bytes: %u", g_ppp_discard_stats.malloc_fail_bytes); + PPP_DEBUG_INFO(" - Oversized Frame Bytes: %u", g_ppp_discard_stats.oversized_frame_bytes); + PPP_DEBUG_INFO(" - Incomplete Frame Bytes: %u", g_ppp_discard_stats.incomplete_frame_bytes); + PPP_DEBUG_INFO("Total Frames Received: %u", g_ppp_discard_stats.total_frames_received); + PPP_DEBUG_INFO("Total Frames Processed: %u", g_ppp_discard_stats.total_frames_processed); + PPP_DEBUG_INFO("Total Frames Discarded: %u", g_ppp_discard_stats.total_frames_discarded); + + // 计算丢弃率 + if (g_ppp_discard_stats.total_bytes_received > 0) { + uint32_t discard_rate = (g_ppp_discard_stats.total_bytes_discarded * 100) / + g_ppp_discard_stats.total_bytes_received; + PPP_DEBUG_INFO("Byte Discard Rate: %u.%02u%%", + discard_rate / 100, discard_rate % 100); + } + + if (g_ppp_discard_stats.total_frames_received > 0) { + uint32_t frame_discard_rate = (g_ppp_discard_stats.total_frames_discarded * 100) / + g_ppp_discard_stats.total_frames_received; + PPP_DEBUG_INFO("Frame Discard Rate: %u.%02u%%", + frame_discard_rate / 100, frame_discard_rate % 100); + } + PPP_DEBUG_INFO("=============================="); } \ No newline at end of file diff --git a/code/sdk/zhihui/ppp/src/ppp_serial.c b/code/sdk/zhihui/ppp/src/ppp_serial.c index 4caa55a26..804d179f3 100644 --- a/code/sdk/zhihui/ppp/src/ppp_serial.c +++ b/code/sdk/zhihui/ppp/src/ppp_serial.c @@ -377,7 +377,7 @@ ppp_error_t ppp_serial_write(const uint8_t *data, uint32_t length) // PPP_DEBUG_HEXDUMP(data, length); PPP_DEBUG_DEBUG("Calling uapi_uart_write(bus=%u, data=%p, len=%u, timeout=1000)...", - g_serial_ctx.uart_bus, data, length); + g_serial_ctx.uart_bus, data, length, 1000); write_result = uapi_uart_write(g_serial_ctx.uart_bus, data, length, 1000); PPP_DEBUG_DEBUG("uapi_uart_write returned: %d (expected: %u)", write_result, length); @@ -398,15 +398,19 @@ ppp_error_t ppp_serial_write(const uint8_t *data, uint32_t length) /* PPP数据模式:直接使用UART发送(与AT模式一致) */ PPP_DEBUG_INFO("Sending %u bytes via UART (PPP data mode)", length); - // 打印PPP数据包内容用于调试 - // PPP_DEBUG_PRINT("PPP TX Data: "); - // for (uint32_t i = 0; i < length && i < 16; i++) { - // PPP_DEBUG_PRINT("%02X ", data[i]); - // } - // if (length > 16) { - // PPP_DEBUG_PRINT("... (total %u bytes)", length); - // } - // PPP_DEBUG_PRINT("\n"); + /* 检查UART状态 */ + PPP_DEBUG_DEBUG("UART Bus: %u, Mode: PPP_DATA, TX Bytes: %u, TX Errors: %u", + g_serial_ctx.uart_bus, g_serial_ctx.tx_bytes, g_serial_ctx.tx_errors); + + /* 打印要发送的数据 */ + PPP_DEBUG_PRINT("PPP TX Data: "); + for (uint32_t i = 0; i < length && i < 16; i++) { + PPP_DEBUG_PRINT("%02X ", data[i]); + } + if (length > 16) { + PPP_DEBUG_PRINT("... (total %u bytes)", length); + } + PPP_DEBUG_PRINT("\n"); PPP_DEBUG_DEBUG("Calling uapi_uart_write(bus=%u, data=%p, len=%u, timeout=%d)...", g_serial_ctx.uart_bus, data, length, PPP_OPTIMIZED_UART_WRITE_TIMEOUT); @@ -417,7 +421,9 @@ ppp_error_t ppp_serial_write(const uint8_t *data, uint32_t length) if (write_result != (int32_t)length) { PPP_DEBUG_ERROR("UART write failed in PPP data mode: returned %d, expected %u", write_result, length); - PPP_DEBUG_ERROR("Possible causes: UART not initialized, hardware error, or timeout"); + PPP_DEBUG_ERROR("UART状态: bus=%u, 总发送字节=%u, 总错误=%u", + g_serial_ctx.uart_bus, g_serial_ctx.tx_bytes, g_serial_ctx.tx_errors); + PPP_DEBUG_ERROR("可能原因: UART未初始化、硬件错误、超时或缓冲区满"); g_serial_ctx.tx_errors++; result = PPP_ERR_UART; } else { diff --git a/code/sdk/zhihui/tcptest/src/zhihui_tcp_test.c b/code/sdk/zhihui/tcptest/src/zhihui_tcp_test.c index 5e8c97770..0365bf091 100644 --- a/code/sdk/zhihui/tcptest/src/zhihui_tcp_test.c +++ b/code/sdk/zhihui/tcptest/src/zhihui_tcp_test.c @@ -235,11 +235,37 @@ errcode_t tcp_test_stop(void) g_tcp_test_manager.stats_timer_id = 0; } - // 等待所有连接线程结束 + // 等待所有连接线程结束 - 增加等待时间和检查机制 printf("等待所有连接关闭...\n"); - TCP_SLEEP_MS(2000); // 给线程时间退出 - // 强制关闭所有socket + // 等待线程自然退出,最多等待10秒 + int wait_count = 0; + bool all_threads_stopped = false; + + while (wait_count < 100 && !all_threads_stopped) { // 最多等待10秒 + all_threads_stopped = true; + + for (int i = 0; i < g_tcp_test_manager.config.connection_count; i++) { + tcp_connection_stats_t *conn = &g_tcp_test_manager.connections[i]; + if (conn->thread_running) { + all_threads_stopped = false; + break; + } + } + + if (!all_threads_stopped) { + TCP_SLEEP_MS(100); // 每次等待100ms + wait_count++; + } + } + + if (all_threads_stopped) { + printf("所有线程已正常退出\n"); + } else { + printf("警告:部分线程未能在超时时间内退出\n"); + } + + // 现在安全地关闭所有socket(线程应该已经退出) for (int i = 0; i < g_tcp_test_manager.config.connection_count; i++) { tcp_connection_stats_t *conn = &g_tcp_test_manager.connections[i]; if (conn->socket_fd >= 0) { @@ -366,16 +392,41 @@ static void* tcp_test_connection_worker(void *arg) } // 发送数据 + ZHIHUI_TCP_LOG_DEBUG("[连接 %03d] 准备发送 %d 字节数据", conn_id, data_size); + ZHIHUI_TCP_LOG_DEBUG("[连接 %03d] Socket FD: %d, 连接状态: %s", + conn_id, conn->socket_fd, conn->connected ? "已连接" : "未连接"); + + // 检查socket状态 + int socket_error = 0; + socklen_t error_len = sizeof(socket_error); + if (TCP_GETSOCKOPT(conn->socket_fd, SOL_SOCKET, SO_ERROR, &socket_error, &error_len) == 0) { + if (socket_error != 0) { + ZHIHUI_TCP_LOG_ERROR("[连接 %03d] Socket错误状态: %d (%s)", + conn_id, socket_error, TCP_STRERROR(socket_error)); + } else { + ZHIHUI_TCP_LOG_DEBUG("[连接 %03d] Socket状态正常", conn_id); + } + } + ssize_t bytes_sent = TCP_SEND(conn->socket_fd, send_buffer, data_size, 0); + ZHIHUI_TCP_LOG_DEBUG("[连接 %03d] TCP_SEND返回: %ld (期望: %d)", conn_id, bytes_sent, data_size); + if (bytes_sent != (ssize_t)data_size) { if (bytes_sent < 0) { - ZHIHUI_TCP_LOG_ERROR("[连接 %03d] 发送失败: %s", conn_id, TCP_STRERROR(TCP_GET_ERRNO())); + int err = TCP_GET_ERRNO(); + ZHIHUI_TCP_LOG_ERROR("[连接 %03d] 发送失败: %s (errno: %d)", + conn_id, TCP_STRERROR(err), err); + ZHIHUI_TCP_LOG_ERROR("[连接 %03d] 发送失败详情: socket_fd=%d, data_size=%d, flags=0", + conn_id, conn->socket_fd, data_size); conn->errors++; break; } else { ZHIHUI_TCP_LOG_WARN("[连接 %03d] 发送不完整 (%ld/%d 字节)", conn_id, bytes_sent, data_size); + ZHIHUI_TCP_LOG_WARN("[连接 %03d] 部分发送可能是正常的,继续执行", conn_id); } + } else { + ZHIHUI_TCP_LOG_DEBUG("[连接 %03d] 发送成功: %ld 字节", conn_id, bytes_sent); } conn->packets_sent++;