#include "mylog/mylog.h" #include "FreeRTOS.h" #include "task.h" #include #include #include #include "usart.h" #include "app_rtc/app_rtc.h" #include "app_init/app_init.h" #include "app_init/app_test.h" #include "app_init/task_lock.h" #include "publicdata/public_define.h" #include "publicdata/publicdata.h" #include "wdt_task/wdt_task.h" #if FATFS_EN == 1 #include "app_fatfs/fatfs_init.h" #endif #if SD_CARD_EN #include "sdmmc/app_sdmmc.h" #endif /* 暂时屏蔽 mylog UDP(保留代码,不删除) */ #ifndef MYLOG_UDP_EN #define MYLOG_UDP_EN 1 #endif #if (MYLOG_UDP_EN) #include "lwip/sockets.h" #include "lwip/inet.h" #endif /* mylog UDP 目标地址配置(模块内定义) */ #define MYLOG_UDP_DST_IP0 192 #define MYLOG_UDP_DST_IP1 168 #define MYLOG_UDP_DST_IP2 1 #define MYLOG_UDP_DST_IP3 238 #define MYLOG_UDP_DST_PORT 18487 /* UDP日志发送的目标端口(上位机监听端口) */ #define MYLOG_UDP_SRC_PORT 8088 /* 本机UDP发送socket绑定的源端口(抓包显示的本地端口) */ #define MYLOG_UDP_RX_PORT 8088 /* 本机UDP接收socket监听端口(用于接收并回显测试数据) */ /* 日志头格式选择: * 1: [task] file:line func - msg * 0: [task][sec=<当前秒>] - msg */ #ifndef MYLOG_PRINT_WITH_FILE_LINE_FUNC #define MYLOG_PRINT_WITH_FILE_LINE_FUNC (0) #endif /* 本任务ID定义:用于统一索引任务表、日志与锁资源。 */ #define MYLOG_TASK_ID (TASK_ID_MyLog) /* 获取 MyLog 任务睡眠时间(ms)。 */ #define MYLOG_SLEEP_TIME_MS() (MY_GET_SLEEP_TIME(MYLOG_TASK_ID)) /* MyLog 任务锁操作宏:简化锁接口调用。 */ #define MYLOG_LOCK_INIT() (u8_task_lock_ctrl(MYLOG_TASK_ID, TASK_LOCK_OP_INIT)) #define MYLOG_LOCK_TAKE() (u8_task_lock_ctrl(MYLOG_TASK_ID, TASK_LOCK_OP_TAKE)) #define MYLOG_LOCK_GIVE() (u8_task_lock_ctrl(MYLOG_TASK_ID, TASK_LOCK_OP_GIVE)) /* MyLog 任务喂狗宏:标记本任务周期正常运行。 */ #define MYLOG_FEED_DOG() (v_wdt_setFlag(MYLOG_TASK_ID)) /* MyLog 自身打印接口:对齐参考工程写法。 */ #define MYLOG_LOG(fmt, ...) MYLOG_MSG(MYLOG_TASK_ID, (fmt), ##__VA_ARGS__) /* 任务日志开关表:默认仅 MyLog 任务允许打印,其它任务默认关闭。 */ /* 索引与 TASK_ID 一一对应,值 1 表示允许该任务打印,0 表示禁止。 */ uint8_t u8_task_log_flag[MY_TASK_NUM] = { [TASK_ID_EthIf] = 0U, [TASK_ID_TCPIP] = 0U, [TASK_ID_BmsComm] = 0U, [TASK_ID_DataColl] = 0U, [TASK_ID_FlowCtrl] = 1U, [TASK_ID_FaultCheck] = 1U, [TASK_ID_MduComm] = 0U, [TASK_ID_SubComm] = 0U, [TASK_ID_PlatComm] = 1U, #if (MY_SHELL_EN) [TASK_ID_MyShell] = 0U, #endif [TASK_ID_UI] = 0U, [TASK_ID_WATCHDOG] = 0U, [TASK_ID_MeterColl] = 0U, [TASK_ID_Meterfee] = 0U, [TASK_ID_MyLog] = 1U, #if (TCP_DEBUG_EN) [TASK_ID_TcpSer] = 1U, #endif [TASK_ID_EthLink] = 0U }; /* UDP 套接字句柄:<0 表示未创建。 */ #if (MYLOG_UDP_EN) static int g_mylog_udp_sock = -1; /* UDP 接收套接字句柄:<0 表示未创建。 */ static int g_mylog_udp_rx_sock = -1; /* UDP 目的地址结构体。 */ static struct sockaddr_in g_mylog_udp_addr; #endif /** * @brief 初始化 mylog 的 UDP 发送通道。 * * @details * - 首次调用时创建 UDP socket; * - 配置固定的目的 IP 和端口; * - 若 socket 创建失败,则保持未初始化状态,后续发送时可重试。 */ static void v_mylog_udp_init(void) { #if (MYLOG_UDP_EN) struct sockaddr_in local_addr; if (g_mylog_udp_sock >= 0) { return; } g_mylog_udp_sock = lwip_socket(AF_INET, SOCK_DGRAM, 0); if (g_mylog_udp_sock < 0) { return; } memset(&local_addr, 0, sizeof(local_addr)); local_addr.sin_family = AF_INET; local_addr.sin_port = PP_HTONS(MYLOG_UDP_SRC_PORT); local_addr.sin_addr.s_addr = PP_HTONL(INADDR_ANY); if (lwip_bind(g_mylog_udp_sock, (const struct sockaddr *)&local_addr, sizeof(local_addr)) != 0) { lwip_close(g_mylog_udp_sock); g_mylog_udp_sock = -1; return; } memset(&g_mylog_udp_addr, 0, sizeof(g_mylog_udp_addr)); g_mylog_udp_addr.sin_family = AF_INET; g_mylog_udp_addr.sin_port = PP_HTONS(MYLOG_UDP_DST_PORT); g_mylog_udp_addr.sin_addr.s_addr = PP_HTONL(LWIP_MAKEU32(MYLOG_UDP_DST_IP0, MYLOG_UDP_DST_IP1, MYLOG_UDP_DST_IP2, MYLOG_UDP_DST_IP3)); #else /* UDP disabled */ #endif } /** * @brief 初始化 mylog 的 UDP 接收通道。 * * @details * - 创建 UDP socket 并绑定到 `MYLOG_UDP_RX_PORT`; * - 设置为非阻塞模式,避免阻塞任务主循环。 */ static void v_mylog_udp_rx_init(void) { #if (MYLOG_UDP_EN) if (g_mylog_udp_rx_sock >= 0) { return; } #if (MYLOG_UDP_RX_PORT == MYLOG_UDP_SRC_PORT) if (g_mylog_udp_sock < 0) { v_mylog_udp_init(); } g_mylog_udp_rx_sock = g_mylog_udp_sock; return; #endif #if (MYLOG_UDP_RX_PORT != MYLOG_UDP_SRC_PORT) struct sockaddr_in local_addr; g_mylog_udp_rx_sock = lwip_socket(AF_INET, SOCK_DGRAM, 0); if (g_mylog_udp_rx_sock < 0) { return; } memset(&local_addr, 0, sizeof(local_addr)); local_addr.sin_family = AF_INET; local_addr.sin_port = PP_HTONS(MYLOG_UDP_RX_PORT); local_addr.sin_addr.s_addr = PP_HTONL(INADDR_ANY); if (lwip_bind(g_mylog_udp_rx_sock, (const struct sockaddr *)&local_addr, sizeof(local_addr)) != 0) { lwip_close(g_mylog_udp_rx_sock); g_mylog_udp_rx_sock = -1; return; } #endif #else /* UDP disabled */ #endif } /** * @brief 发送 UDP 日志数据。 * * @param data 待发送数据指针。 * @param len 待发送数据长度(字节)。 * * @details * - 入参非法时直接返回; * - socket 未初始化时先尝试初始化; * - 发送失败不阻塞业务流程(静默处理)。 */ void v_mylog_udp_send_data(const uint8_t *data, uint16_t len) { #if (MYLOG_UDP_EN) if (data == NULL || len == 0U) { return; } if (g_mylog_udp_sock < 0) { v_mylog_udp_init(); } if (g_mylog_udp_sock >= 0) { (void)lwip_sendto(g_mylog_udp_sock, data, len, 0, (const struct sockaddr *)&g_mylog_udp_addr, sizeof(g_mylog_udp_addr)); } #else (void)data; (void)len; #endif } /** * @brief 非阻塞接收 UDP 数据并原样回显给发送端。 */ static void v_mylog_udp_recv_echo(void) { #if (MYLOG_UDP_EN) uint8_t rx_buf[256]; int recv_len; socklen_t peer_len; struct sockaddr_in peer_addr; if (g_mylog_udp_rx_sock < 0) { v_mylog_udp_rx_init(); } if (g_mylog_udp_rx_sock < 0) { return; } peer_len = (socklen_t)sizeof(peer_addr); recv_len = lwip_recvfrom(g_mylog_udp_rx_sock, rx_buf, sizeof(rx_buf), MSG_DONTWAIT, (struct sockaddr *)&peer_addr, &peer_len); if (recv_len > 0) { (void)lwip_sendto(g_mylog_udp_rx_sock, rx_buf, (size_t)recv_len, 0, (const struct sockaddr *)&peer_addr, peer_len); } #else /* UDP disabled */ #endif } /** * @brief 统一格式化并输出日志(UDP + 串口)。 * * @param id 任务ID(用于显示任务名)。 * @param file 文件名(建议传 BASENAME(__FILE__))。 * @param line 行号。 * @param func 函数名。 * @param fmt 可变参数格式字符串。 * * @details * - 通过 MYLOG_TASK_ID 对应锁串行化输出,避免多任务并发打乱日志; * - 输出格式:`[task] file:line func - message`; * - 同时发 UDP 和串口 printf。 */ void v_format_logMsg(int id, const char *file, int line, const char *func, const char *fmt, ...) { va_list args; char log_buf[256]; int written = 0; if (fmt == NULL) { return; } if (MYLOG_LOCK_TAKE() != 1U) { return; } #if (MYLOG_PRINT_WITH_FILE_LINE_FUNC) written += snprintf(log_buf + written, (int)sizeof(log_buf) - written, "[%s] %s:%d %s - ", MY_GET_TASK_NAME(id), (file != NULL) ? file : "unknown", line, (func != NULL) ? func : "unknown"); #else Comm_Time now_time; (void)memset(&now_time, 0, sizeof(now_time)); GetCurrentTime(&now_time); written += snprintf(log_buf + written, (int)sizeof(log_buf) - written, "[%s][%02u%02u%02u] - ", MY_GET_TASK_NAME(id), (unsigned int)now_time.ucHour, (unsigned int)now_time.ucMin, (unsigned int)now_time.ucSec); #endif if (written < (int)sizeof(log_buf)) { va_start(args, fmt); written += vsnprintf(log_buf + written, (int)sizeof(log_buf) - written, fmt, args); va_end(args); } if (written < 0) { (void)MYLOG_LOCK_GIVE(); return; } if (written >= (int)sizeof(log_buf)) { written = (int)sizeof(log_buf) - 1; } // v_mylog_udp_send_data((const uint8_t *)log_buf, (uint16_t)written); printf("%s\r\n", log_buf); // 同时写入文件系统日志 if( // id == TASK_ID_BMS || // id == TASK_ID_FlowCtrl || // id == TASK_ID_FaultCheck || id == TASK_ID_PlatComm ) { #define LOG_BUFFER_SIZE 512 char logBuffer[LOG_BUFFER_SIZE]; memset(logBuffer , 0 , LOG_BUFFER_SIZE); written = 0; // written += snprintf(logBuffer + written, LOG_BUFFER_SIZE - written, // "[%s]", // 任务名称 // MY_GET_TASK_NAME(id)); // 处理内容 va_start(args, fmt); written += vsnprintf(logBuffer + written, LOG_BUFFER_SIZE - written, fmt, args); va_end(args); /* LOG_INFO("%s", logBuffer); -- FS log disabled */ } (void)MYLOG_LOCK_GIVE(); } /** * ************************************************************************ * @brief - RTC时间更新, * (由于RCT无外部时钟,内部时钟精度较低,定时20分钟使用rx8025对RTC进行校时处理) * * * * @author YZG * @date 2025-11-04 * ************************************************************************ */ #if SYSTEM_TYPE == SYSTEM_TYPE_CCU601E_D void v_current_time_updata() { static uint32_t current_time_last = 0; Comm_Time comm_time; memset(&comm_time,0,sizeof(Comm_Time)); if(u32_safe_seconds_since(current_time_last) > NUM_60 *20) //运行10分钟 { GetCurrentTime(&comm_time); MYLOG_LOG("RTC time:%d-%d-%d %d:%d:%d \n",comm_time.iYear,comm_time.ucMonth,comm_time.ucDay, comm_time.ucHour,comm_time.ucMin,comm_time.ucSec); v_get_rx8025_set_rtc(); GetCurrentTime(&comm_time); MYLOG_LOG("rx8025 time:%d-%d-%d %d:%d:%d \n",comm_time.iYear,comm_time.ucMonth,comm_time.ucDay, comm_time.ucHour,comm_time.ucMin,comm_time.ucSec); current_time_last = get_current_seconds(); } } #endif /** * @brief MyLog 任务主函数。 * * @param argument 任务参数,当前未使用。 * * @details * - 初始化 MyLog 对应锁; * - 按任务周期执行; * - 在 `TASK_RUN_PRINT_TIME` 周期点输出“任务运行中”日志,便于在线确认任务活性。 */ void v_mylog_task(void *argument) { uint16_t u16_sleep_time; uint32_t last_send_time; uint32_t current_time; (void)argument; /* 初始化本任务专属互斥锁,保护日志格式化与输出过程。 */ (void)MYLOG_LOCK_INIT(); /* 读取任务表中的 MyLog 周期配置,后续循环统一使用该周期。 */ u16_sleep_time = MYLOG_SLEEP_TIME_MS(); /* 记录周期打印基准时间(秒),用于 TASK_RUN_PRINT_TIME 节拍判断。 */ last_send_time = get_current_seconds(); for (;;) { /* 每个循环先喂狗,标记 MyLog 任务仍在正常执行。 */ MYLOG_FEED_DOG(); /* 使用全局 TASK_RUN_PRINT_TIME 判断是否到达周期打印时刻。 */ current_time = get_current_seconds(); if ((TASK_RUN_PRINT_TIME != 0U) && (u32_safe_seconds_since(last_send_time) >= TASK_RUN_PRINT_TIME)) { MYLOG_LOG("mylog is runing, sleep time is %ums", u16_sleep_time); last_send_time = current_time; } /* 轮询 UDP 接收并回显收到的数据(可通过 MYLOG_UDP_EN 屏蔽)。 */ v_mylog_udp_recv_echo(); #if FATFS_EN == 1 fatfs_action(); //文件系统周期执行函数 #endif #if SD_CARD_EN app_sdmmc_periodic(); #endif #if SYSTEM_TYPE == SYSTEM_TYPE_CCU601E_D v_current_time_updata(); //crt时间周期更新 #endif /* 按任务配置周期休眠,避免忙轮询占用 CPU。 */ mSleep(u16_sleep_time); } }