#include "soc_osal.h"
#include <string.h>
#include <stdlib.h> // 添加malloc/free支持
+#include <time.h> // 添加时间处理支持
+#include <sys/time.h> // 添加settimeofday支持
#include "osal_debug.h"
#include "schedule/osal_task.h"
#include "securec.h"
extern void cat1_api_enable_cat1(bool isopen);
static void ppp_check_and_update_state(void);
+// 时区相关宏定义(参照 cat1_porting.c)
+#define MINUTES_PER_QUARTER 15 /* minutes per quarter */
+#define QUARTERS_PER_HOUR 4 /* quarters per hour */
+#define MAX_TIMEZONE_HOURS 24
+
errcode_t ppp_api_get_ppp_netif_name(char* netif_name, uint32_t buffer_size);
void ppp_check_at_communication_and_baudrate(void);
PPP_DEBUG_ERROR("Failed to update version");
ret = ERRCODE_FAIL;
}
+
+ // 更新时间
+ if (ppp_api_update_time() != ERRCODE_SUCC) {
+ PPP_DEBUG_ERROR("Failed to update time");
+ ret = ERRCODE_FAIL;
+ }
PPP_DEBUG_INFO("Runtime status update completed");
return ret;
}
}
+/**
+ * @brief 解析AT+CCLK响应并转换为时间戳和时区
+ * @param response AT命令响应
+ * @param timestamp 输出UTC时间戳(秒)
+ * @param timezone_quarters 输出时区(quarters,15分钟为单位)
+ * @return ERRCODE_SUCC-成功,其他-失败
+ *
+ * AT+CCLK响应格式示例:
+ * +CCLK: "25/10/20,02:59:43+32"
+ *
+ * 重要:CCLK返回的时间字段是UTC时间,不是本地时间!
+ * - 时间字段:02:59:43 是 UTC 时间
+ * - 时区字段:+32 表示本地时区为 UTC+8(32 quarters = 8 hours)
+ * - 本地时间:02:59:43 + 8小时 = 10:59:43
+ *
+ * 处理流程:
+ * 1. 解析CCLK响应得到UTC时间和时区偏移
+ * 2. 将UTC时间转换为时间戳
+ * 3. 返回UTC时间戳和时区信息
+ */
+static errcode_t ppp_parse_cclk_and_convert_to_timestamp(const char *response,
+ uint32_t *timestamp,
+ int *timezone_quarters)
+{
+ if (!response || !timestamp || !timezone_quarters) {
+ return ERRCODE_INVALID_PARAM;
+ }
+
+ // 查找 "+CCLK: "
+ const char *start = strstr(response, "+CCLK: ");
+ if (start == NULL) {
+ PPP_DEBUG_ERROR("CCLK response format error: no +CCLK: prefix");
+ return ERRCODE_FAIL;
+ }
+
+ start += 7; // 跳过"+CCLK: "
+
+ // 查找第一个引号
+ const char *quote_start = strchr(start, '"');
+ if (quote_start == NULL) {
+ PPP_DEBUG_ERROR("CCLK response format error: no opening quote");
+ return ERRCODE_FAIL;
+ }
+ quote_start++; // 跳过引号
+
+ // 查找第二个引号
+ const char *quote_end = strchr(quote_start, '"');
+ if (quote_end == NULL) {
+ PPP_DEBUG_ERROR("CCLK response format error: no closing quote");
+ return ERRCODE_FAIL;
+ }
+
+ // 提取并解析时间字符串
+ char time_str[32] = {0};
+ size_t len = quote_end - quote_start;
+ if (len >= sizeof(time_str)) {
+ len = sizeof(time_str) - 1;
+ }
+ strncpy_s(time_str, sizeof(time_str), quote_start, len);
+ time_str[len] = '\0';
+
+ PPP_DEBUG_INFO("Parsing CCLK time: %s", time_str);
+
+ // 解析CCLK格式: "YY/MM/DD,HH:MM:SS+TZ"
+ int year, month, day, hour, minute, second, tz_quarters;
+ int parsed = sscanf_s(time_str, "%d/%d/%d,%d:%d:%d%d",
+ &year, &month, &day, &hour, &minute, &second, &tz_quarters);
+
+ if (parsed != 7) {
+ PPP_DEBUG_ERROR("Failed to parse CCLK time format: %s", time_str);
+ return ERRCODE_FAIL;
+ }
+
+ // 转换2位年份为4位年份(假设20xx年)
+ if (year < 100) {
+ year += 2000;
+ }
+
+ // 验证年份合法性(参照 cat1_utils.c 中的检查)
+ if (year < 2023) {
+ PPP_DEBUG_ERROR("Invalid year: %d (must be >= 2023)", year);
+ return ERRCODE_FAIL;
+ }
+
+ // 构造 struct tm 结构(CCLK返回的是UTC时间)
+ struct tm time_info = {0};
+ time_info.tm_year = year - 1900; // tm_year 是从1900年开始的
+ time_info.tm_mon = month - 1; // tm_mon 是 0-11
+ time_info.tm_mday = day;
+ time_info.tm_hour = hour;
+ time_info.tm_min = minute;
+ time_info.tm_sec = second;
+ time_info.tm_isdst = -1; // 让系统自动判断夏令时
+
+ // 使用 mktime 转换为时间戳
+ // 注意:CCLK返回的时间字段是UTC时间,不是本地时间
+ time_t _timestamp = mktime(&time_info);
+ if (_timestamp == -1) {
+ PPP_DEBUG_ERROR("Failed to convert time to timestamp");
+ return ERRCODE_FAIL;
+ }
+
+ // CCLK返回的时间本身就是UTC时间,直接使用
+ // 时区字段表示本地时区相对于UTC的偏移
+ uint32_t utc_timestamp = (uint32_t)_timestamp;
+
+ *timestamp = utc_timestamp;
+ *timezone_quarters = tz_quarters;
+
+ // 计算本地时间用于显示验证(精确计算,包括非整数小时)
+ int timezone_hours = tz_quarters / QUARTERS_PER_HOUR; // 小时部分
+ int timezone_minutes = (tz_quarters % QUARTERS_PER_HOUR) * MINUTES_PER_QUARTER; // 分钟部分
+
+ // 计算本地时间:UTC + 时区偏移
+ int local_hour = hour;
+ int local_minute = minute + timezone_minutes;
+ int local_day = day;
+
+ // 处理分钟进位
+ if (local_minute >= 60) {
+ local_hour += local_minute / 60;
+ local_minute = local_minute % 60;
+ } else if (local_minute < 0) {
+ local_hour += (local_minute / 60) - 1;
+ local_minute = 60 + (local_minute % 60);
+ }
+
+ // 加上时区的小时部分
+ local_hour += timezone_hours;
+
+ // 处理小时进位
+ if (local_hour >= 24) {
+ local_day += local_hour / 24;
+ local_hour = local_hour % 24;
+ } else if (local_hour < 0) {
+ local_day += (local_hour / 24) - 1;
+ local_hour = 24 + (local_hour % 24);
+ }
+
+ PPP_DEBUG_WARN("Parsed UTC time: %04d-%02d-%02d %02d:%02d:%02d",
+ year, month, day, hour, minute, second);
+
+ // 显示时区信息(支持非整数小时)
+ if (timezone_minutes == 0) {
+ PPP_DEBUG_WARN("Timezone: %+d quarters (UTC%+d)",
+ tz_quarters, timezone_hours);
+ } else {
+ PPP_DEBUG_WARN("Timezone: %+d quarters (UTC%+d:%02d)",
+ tz_quarters, timezone_hours, timezone_minutes);
+ }
+
+ PPP_DEBUG_WARN("Local time will be: %04d-%02d-%02d %02d:%02d:%02d",
+ year, month, local_day, local_hour, local_minute, second);
+ PPP_DEBUG_WARN("UTC timestamp: %u", utc_timestamp);
+
+ return ERRCODE_SUCC;
+}
+
+/**
+ * @brief 设置系统时间(参照 cat1_porting.c 的 sync_time_utc 实现)
+ * @param utc_timestamp UTC时间戳(秒)
+ * @param timezone_quarters 时区(quarters,15分钟为单位)
+ * @return ERRCODE_SUCC-成功,其他-失败
+ *
+ * 时间设置逻辑说明:
+ * - 输入:UTC时间戳 + 时区偏移
+ * - 设置:本地时间 = UTC时间 + 时区偏移
+ *
+ * 示例(UTC+8):
+ * - UTC时间:02:59:43
+ * - 时区:+32 quarters = +8 hours
+ * - 本地时间:02:59:43 + 8小时 = 10:59:43
+ * - settimeofday 会将系统时间设置为 10:59:43
+ */
+static errcode_t ppp_set_system_time(uint32_t utc_timestamp, int timezone_quarters)
+{
+ // 参数验证(参照 cat1_porting.c)
+ if (timezone_quarters > (MAX_TIMEZONE_HOURS * QUARTERS_PER_HOUR) ||
+ timezone_quarters < -(MAX_TIMEZONE_HOURS * QUARTERS_PER_HOUR)) {
+ PPP_DEBUG_ERROR("Invalid timezone: %d quarters", timezone_quarters);
+ return ERRCODE_FAIL;
+ }
+
+ if (utc_timestamp == 0) {
+ PPP_DEBUG_ERROR("Invalid timestamp: 0");
+ return ERRCODE_FAIL;
+ }
+
+ // 准备时间结构(参照 cat1_porting.c 的实现)
+ struct timeval tv = {0};
+ struct timezone tzone = {0};
+
+ // 设置本地时间 = UTC时间 + 时区偏移
+ // settimeofday 的 tv_sec 应该设置为本地时间戳
+ tv.tv_sec = utc_timestamp + timezone_quarters * MINUTES_PER_QUARTER * 60;
+ tv.tv_usec = 0;
+
+ // 设置时区信息
+ tzone.tz_dsttime = 0; // 不使用夏令时
+ tzone.tz_minuteswest = timezone_quarters * MINUTES_PER_QUARTER;
+
+ PPP_DEBUG_INFO("Setting system time:");
+ PPP_DEBUG_INFO(" UTC timestamp: %u", utc_timestamp);
+ PPP_DEBUG_INFO(" Timezone: %+d minutes (%+d quarters)",
+ tzone.tz_minuteswest, timezone_quarters);
+ PPP_DEBUG_INFO(" Local timestamp: %ld", tv.tv_sec);
+
+ // 调用系统API设置时间
+ int ret = settimeofday(&tv, &tzone);
+ if (ret != 0) {
+ PPP_DEBUG_ERROR("settimeofday failed: %d", ret);
+ return ERRCODE_FAIL;
+ }
+
+ // 验证时间设置成功
+ time_t current_time = time(NULL);
+ struct tm timeinfo;
+ localtime_r(¤t_time, &timeinfo);
+
+ PPP_DEBUG_CRITICAL("System time set successfully: %04d-%02d-%02d %02d:%02d:%02d",
+ timeinfo.tm_year + 1900,
+ timeinfo.tm_mon + 1,
+ timeinfo.tm_mday,
+ timeinfo.tm_hour,
+ timeinfo.tm_min,
+ timeinfo.tm_sec);
+
+ return ERRCODE_SUCC;
+}
+
+/**
+ * @brief 通过AT+CCLK命令获取网络时间并更新系统时间
+ * @return ERRCODE_SUCC-成功,其他-失败
+ *
+ * 实现说明(参照 cat1_set_time_from_cat1):
+ * 1. 发送 AT+CCLK? 命令获取网络时间
+ * 2. 解析响应,获取UTC时间和时区偏移
+ * 3. 计算本地时间 = UTC时间 + 时区偏移
+ * 4. 使用 settimeofday 设置系统时间为本地时间
+ *
+ * AT+CCLK 返回格式说明:
+ * - 命令:AT+CCLK?
+ * - 响应:+CCLK: "25/10/20,02:59:43+32"
+ * - 时间字段(02:59:43)是 UTC 时间
+ * - 时区字段(+32)表示本地时区为 UTC+8
+ * - 最终显示时间:02:59:43 + 8小时 = 10:59:43
+ */
+errcode_t ppp_api_update_time(void)
+{
+ // 检查是否在AT模式
+ if (ppp_serial_get_mode() != PPP_SERIAL_MODE_AT || !get_at_communication_and_baudrate_flag()) {
+ PPP_DEBUG_WARN("Cannot update time: not in AT mode (current mode: %d)", ppp_serial_get_mode());
+ return ERRCODE_FAIL;
+ }
+
+ PPP_DEBUG_INFO("=== Starting network time synchronization ===");
+ PPP_DEBUG_INFO("Sending AT+CCLK? command...");
+
+ // 分配响应缓冲区
+ char *response = (char *)malloc(512);
+ if (response == NULL) {
+ PPP_DEBUG_ERROR("Failed to allocate memory for AT response");
+ return ERRCODE_FAIL;
+ }
+
+ // 发送AT+CCLK?命令
+ uint32_t response_len;
+ errcode_t ret = ppp_execute_at_command("AT+CCLK?", response, 512, &response_len, 5000);
+ if (ret != ERRCODE_SUCC) {
+ PPP_DEBUG_ERROR("Failed to send AT+CCLK command");
+ free(response);
+ return ret;
+ }
+
+ PPP_DEBUG_INFO("AT+CCLK response received (%u bytes)", response_len);
+
+ // 解析CCLK响应并转换为时间戳
+ uint32_t utc_timestamp = 0;
+ int timezone_quarters = 0;
+ ret = ppp_parse_cclk_and_convert_to_timestamp(response, &utc_timestamp, &timezone_quarters);
+ free(response);
+
+ if (ret != ERRCODE_SUCC) {
+ PPP_DEBUG_ERROR("Failed to parse CCLK response");
+ return ret;
+ }
+
+ // 设置系统时间
+ ret = ppp_set_system_time(utc_timestamp, timezone_quarters);
+ if (ret != ERRCODE_SUCC) {
+ PPP_DEBUG_ERROR("Failed to set system time");
+ return ret;
+ }
+
+ PPP_DEBUG_CRITICAL("=== Network time synchronized successfully ===");
+ return ERRCODE_SUCC;
+}
+
/**
* @brief 获取并显示网络信号信息
* @return 错误码