Files
CCU621M/app/mylog/mylog.c
T

441 lines
13 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "mylog/mylog.h"
#include "FreeRTOS.h"
#include "task.h"
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#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);
}
}