#define TAG "alarm_control" #include #include #include #include #include #include "cJSON.h" #include "lisa_log.h" #include "lisa_mem.h" #include "listen_system.h" #include "mcp.h" #include "alarm_next.h" #include "service_alarm.h" #define ALARM_CTRL_FAIL(_msg) \ do { \ snprintf(result_text, text_len, "%s", (_msg)); \ return -1; \ } while (0) typedef struct { uint64_t cloud_id; // cloud alarm id const char *action; // CREATE / DELETE / UPDATE / QUERY const char *alarm_type; // ONCE / DAILY / WEEKLY / WORKDAY / WEEKEND / MONTHLY / YEARLY const char *holiday_name; // 节日名称 const char *calendar_type; // GREGORIAN / LUNAR const char *time_str; // HH:mm:ss const char *date_str; // YYYY-MM-DD const char *lunar_date_str; // MM-DD (LUNAR) int day_of_week; // 1-7 int day_of_month; // 1-31 int month_of_year; // 1-12 const char *text; // 提醒文本 cJSON *search_keywords; // array of strings } alarm_control_params_t; /* ==================== 参数解析工具 ==================== */ static void alarm_control_params_init(alarm_control_params_t *params) { if (!params) { return; } memset(params, 0, sizeof(*params)); params->cloud_id = 0; params->day_of_week = -1; params->day_of_month = -1; params->month_of_year = -1; } static void alarm_control_params_parse(cJSON *args, alarm_control_params_t *params) { if (!args || !params) { return; } cJSON *pval = NULL; pval = mcp_tool_call_args_get(args, "id"); if (pval && cJSON_IsNumber(pval)) { params->cloud_id = (uint64_t)pval->valuedouble; } pval = mcp_tool_call_args_get(args, "action"); if (pval && cJSON_IsString(pval) && pval->valuestring[0] != '\0') { params->action = pval->valuestring; } pval = mcp_tool_call_args_get(args, "alarm_type"); if (pval && cJSON_IsString(pval) && pval->valuestring[0] != '\0') { params->alarm_type = pval->valuestring; } pval = mcp_tool_call_args_get(args, "holiday_name"); if (pval && cJSON_IsString(pval) && pval->valuestring[0] != '\0') { params->holiday_name = pval->valuestring; } pval = mcp_tool_call_args_get(args, "calendar_type"); if (pval && cJSON_IsString(pval) && pval->valuestring[0] != '\0') { params->calendar_type = pval->valuestring; } pval = mcp_tool_call_args_get(args, "time"); if (pval && cJSON_IsString(pval) && pval->valuestring[0] != '\0') { params->time_str = pval->valuestring; } pval = mcp_tool_call_args_get(args, "date"); if (pval && cJSON_IsString(pval) && pval->valuestring[0] != '\0') { params->date_str = pval->valuestring; } pval = mcp_tool_call_args_get(args, "lunar_date"); if (pval && cJSON_IsString(pval) && pval->valuestring[0] != '\0') { params->lunar_date_str = pval->valuestring; } pval = mcp_tool_call_args_get(args, "day_of_week"); if (pval && cJSON_IsNumber(pval)) { params->day_of_week = pval->valueint; } pval = mcp_tool_call_args_get(args, "day_of_month"); if (pval && cJSON_IsNumber(pval)) { params->day_of_month = pval->valueint; } pval = mcp_tool_call_args_get(args, "month_of_year"); if (pval && cJSON_IsNumber(pval)) { params->month_of_year = pval->valueint; } pval = mcp_tool_call_args_get(args, "text"); if (pval && cJSON_IsString(pval)) { params->text = pval->valuestring; } pval = mcp_tool_call_args_get(args, "search_keywords"); if (pval && cJSON_IsArray(pval)) { params->search_keywords = pval; } } static alarm_calendar_t parse_calendar_type(const char *calendar_type) { if (calendar_type && strcmp(calendar_type, "LUNAR") == 0) { return ALARM_CAL_LUNAR; } return ALARM_CAL_GREGORIAN; } static alarm_trigger_type_t parse_trigger_type(const char *alarm_type, const char *holiday_name) { if (holiday_name && holiday_name[0] != '\0') { return ALARM_TRIG_HOLIDAY; } if (!alarm_type) { return ALARM_TRIG_ONCE; } if (strcmp(alarm_type, "ONCE") == 0) { return ALARM_TRIG_ONCE; } if (strcmp(alarm_type, "DAILY") == 0) { return ALARM_TRIG_DAILY; } if (strcmp(alarm_type, "WEEKLY") == 0) { return ALARM_TRIG_WEEKLY; } if (strcmp(alarm_type, "WORKDAY") == 0) { return ALARM_TRIG_WORKDAY; } if (strcmp(alarm_type, "WEEKEND") == 0) { return ALARM_TRIG_WEEKEND; } if (strcmp(alarm_type, "MONTHLY") == 0) { return ALARM_TRIG_MONTHLY; } if (strcmp(alarm_type, "YEARLY") == 0) { return ALARM_TRIG_YEARLY; } return ALARM_TRIG_ONCE; } static bool parse_time_hms(const char *time_str, uint8_t *hour, uint8_t *minute, uint8_t *second) { int h = -1, m = -1, s = -1; if (!time_str || !hour || !minute || !second) { return false; } if (sscanf(time_str, "%d:%d:%d", &h, &m, &s) == 3) { // ok } else if (sscanf(time_str, "%d:%d", &h, &m) == 2) { s = 0; } else { return false; } if (h < 0 || h > 23 || m < 0 || m > 59 || s < 0 || s > 59) { return false; } *hour = (uint8_t)h; *minute = (uint8_t)m; *second = (uint8_t)s; return true; } static bool parse_date_ymd(const char *date_str, uint16_t *year, uint8_t *month, uint8_t *day) { int y = -1, m = -1, d = -1; if (!date_str || !year || !month || !day) { return false; } if (sscanf(date_str, "%d-%d-%d", &y, &m, &d) != 3) { return false; } if (y < 1970 || m < 1 || m > 12 || d < 1 || d > 31) { return false; } *year = (uint16_t)y; *month = (uint8_t)m; *day = (uint8_t)d; return true; } static bool parse_lunar_md(const char *lunar_date_str, uint8_t *month, uint8_t *day) { int m = -1, d = -1; if (!lunar_date_str || !month || !day) { return false; } if (sscanf(lunar_date_str, "%d-%d", &m, &d) != 2) { return false; } if (m < 1 || m > 12 || d < 1 || d > 31) { return false; } *month = (uint8_t)m; *day = (uint8_t)d; return true; } static void alarm_fill_keywords(const cJSON *arr, alarm_object_t *alarm) { if (!arr || !alarm) { return; } uint8_t count = 0; cJSON *item = NULL; cJSON_ArrayForEach(item, arr) { if (!cJSON_IsString(item) || !item->valuestring) { continue; } if (count >= ALARM_KEYWORD_MAX) { break; } strncpy(alarm->keywords[count], item->valuestring, ALARM_KEYWORD_LEN - 1); alarm->keywords[count][ALARM_KEYWORD_LEN - 1] = '\0'; count++; } alarm->keyword_count = count; } /* ==================== 闹钟日期处理工具 ==================== */ static void normalize_tm(struct tm *tmv); static int get_current_tm(struct tm *out_tm) { if (!out_tm) { return -1; } struct timeval tv; if (ls_sys_get_time(&tv) != 0) { if (gettimeofday(&tv, NULL) != 0) { return -1; } } long int ts = (long int)tv.tv_sec; if (!ls_sys_get_tmtime(&ts, out_tm)) { return -1; } // ls_sys_get_tmtime uses year base 1970 and does not set tm_wday. // Normalize via mktime with a 1900-based year offset, then convert back. normalize_tm(out_tm); return 0; } static void normalize_tm(struct tm *tmv) { if (!tmv) { return; } struct tm tmp = *tmv; tmp.tm_year += 70; // 1970-based -> 1900-based mktime(&tmp); tmv->tm_year = tmp.tm_year - 70; tmv->tm_mon = tmp.tm_mon; tmv->tm_mday = tmp.tm_mday; tmv->tm_hour = tmp.tm_hour; tmv->tm_min = tmp.tm_min; tmv->tm_sec = tmp.tm_sec; tmv->tm_wday = tmp.tm_wday; tmv->tm_yday = tmp.tm_yday; tmv->tm_isdst = tmp.tm_isdst; } static int days_in_month(int year, int month) { static const uint8_t days[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; if (month < 1 || month > 12) { return 30; } if (month == 2) { bool leap = ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)); return leap ? 29 : 28; } return days[month - 1]; } static void set_date_from_tm(alarm_object_t *alarm, const struct tm *tmv) { if (!alarm || !tmv) { return; } alarm->trigger.year = (uint16_t)(tmv->tm_year + 1970); alarm->trigger.month = (uint8_t)(tmv->tm_mon + 1); alarm->trigger.day = (uint8_t)tmv->tm_mday; } static int calc_tm_seconds(const struct tm *tmv) { if (!tmv) { return 0; } return tmv->tm_hour * 3600 + tmv->tm_min * 60 + tmv->tm_sec; } static int calc_alarm_seconds(const alarm_object_t *alarm) { if (!alarm) { return 0; } return alarm->trigger.hour * 3600 + alarm->trigger.minute * 60 + alarm->trigger.second; } static int set_nearest_date_from_workday_rule(alarm_object_t *alarm, struct tm *tmv, bool want_workday) { int now_sec = calc_tm_seconds(tmv); int alarm_sec = calc_alarm_seconds(alarm); LISA_LOGI(TAG, "workday calc start: want=%s now=%04d-%02d-%02d %02d:%02d:%02d alarm=%02u:%02u:%02u", want_workday ? "workday" : "weekend", tmv->tm_year + 1970, tmv->tm_mon + 1, tmv->tm_mday, tmv->tm_hour, tmv->tm_min, tmv->tm_sec, alarm->trigger.hour, alarm->trigger.minute, alarm->trigger.second); for (int i = 0; i < 370; i++) { // 查询日期是否工作日 char date_str[16] = {0}; snprintf(date_str, sizeof(date_str), "%02d-%02d", tmv->tm_mon + 1, tmv->tm_mday); bool is_work_day = false; if (listenai_date_is_workday(date_str, &is_work_day) != 0) { LISA_LOGE(TAG, "workday query failed, date=%s", date_str); return -6; } bool match = want_workday ? is_work_day : !is_work_day; LISA_LOGI(TAG, "workday calc: i=%d date=%04d-%02d-%02d is_workday=%d match=%d", i, tmv->tm_year + 1970, tmv->tm_mon + 1, tmv->tm_mday, is_work_day, match); if (match) { if (i > 0 || alarm_sec > now_sec) { set_date_from_tm(alarm, tmv); return 0; } } tmv->tm_mday += 1; normalize_tm(tmv); } LISA_LOGW(TAG, "workday rule not found within limit"); return -6; } // 获取未来最近的 周/月/年循环闹钟的日期 static int set_nearest_date_from_rule(alarm_object_t *alarm, alarm_trigger_type_t trigger_type, int day_of_week, int day_of_month, int month_of_year) { struct tm tmv; if (get_current_tm(&tmv) != 0) { return -1; } // Ensure tm_wday is normalized normalize_tm(&tmv); int now_sec = calc_tm_seconds(&tmv); int alarm_sec = calc_alarm_seconds(alarm); LISA_LOGI(TAG, "set_nearest_date_from_rule: now=%02d:%02d:%02d alarm=%02u:%02u:%02u", tmv.tm_hour, tmv.tm_min, tmv.tm_sec, alarm->trigger.hour, alarm->trigger.minute, alarm->trigger.second); switch (trigger_type) { case ALARM_TRIG_DAILY: { if (alarm_sec <= now_sec) { tmv.tm_mday += 1; normalize_tm(&tmv); } set_date_from_tm(alarm, &tmv); return 0; } case ALARM_TRIG_WEEKLY: { if (day_of_week < 1 || day_of_week > 7) { return -2; } int today_wd = tmv.tm_wday == 0 ? 7 : tmv.tm_wday; // 1=Mon ... 7=Sun int diff = (day_of_week - today_wd + 7) % 7; if (diff == 0) { if (alarm_sec <= now_sec) { diff = 7; } } LISA_LOGI(TAG, "weekly calc: today_wd=%d target=%d diff=%d ", today_wd, day_of_week, diff); tmv.tm_mday += diff; normalize_tm(&tmv); set_date_from_tm(alarm, &tmv); return 0; } case ALARM_TRIG_WORKDAY: { return set_nearest_date_from_workday_rule(alarm, &tmv, true); } case ALARM_TRIG_WEEKEND: { int today_wd = tmv.tm_wday == 0 ? 7 : tmv.tm_wday; // 1=Mon ... 7=Sun bool today_is_weekend = (today_wd == 6 || today_wd == 7); if (today_is_weekend && alarm_sec > now_sec) { set_date_from_tm(alarm, &tmv); return 0; } tmv.tm_mday += 1; normalize_tm(&tmv); for (int i = 0; i < 7; i++) { int wd = tmv.tm_wday == 0 ? 7 : tmv.tm_wday; if (wd == 6 || wd == 7) { set_date_from_tm(alarm, &tmv); return 0; } tmv.tm_mday += 1; normalize_tm(&tmv); } LISA_LOGW(TAG, "weekend rule not found within limit"); return -6; } case ALARM_TRIG_MONTHLY: { if (day_of_month < 1 || day_of_month > 31) { return -3; } LISA_LOGI(TAG, "monthly calc: today=%04d-%02d-%02d now=%02d:%02d:%02d target_day=%d ", tmv.tm_year + 1970, tmv.tm_mon + 1, tmv.tm_mday, tmv.tm_hour, tmv.tm_min, tmv.tm_sec); int max_day = days_in_month(tmv.tm_year + 1970, tmv.tm_mon + 1); int target_day = day_of_month > max_day ? max_day : day_of_month; if (tmv.tm_mday < target_day) { tmv.tm_mday = target_day; } else if (tmv.tm_mday == target_day) { if (alarm_sec <= now_sec) { tmv.tm_mon += 1; } max_day = days_in_month(tmv.tm_year + 1970, tmv.tm_mon + 1); target_day = day_of_month > max_day ? max_day : day_of_month; tmv.tm_mday = target_day; } else { tmv.tm_mon += 1; max_day = days_in_month(tmv.tm_year + 1970, tmv.tm_mon + 1); target_day = day_of_month > max_day ? max_day : day_of_month; tmv.tm_mday = target_day; } normalize_tm(&tmv); set_date_from_tm(alarm, &tmv); return 0; } case ALARM_TRIG_YEARLY: { if (month_of_year < 1 || month_of_year > 12 || day_of_month < 1 || day_of_month > 31) { return -4; } LISA_LOGI(TAG, "yearly calc: today=%04d-%02d-%02d now=%02d:%02d:%02d target=%02d-%02d ", tmv.tm_year + 1970, tmv.tm_mon + 1, tmv.tm_mday, tmv.tm_hour, tmv.tm_min, tmv.tm_sec, month_of_year, day_of_month); int max_day = days_in_month(tmv.tm_year + 1970, month_of_year); int target_day = day_of_month > max_day ? max_day : day_of_month; if ((tmv.tm_mon + 1 < month_of_year) || ((tmv.tm_mon + 1 == month_of_year) && (tmv.tm_mday < target_day))) { tmv.tm_mon = month_of_year - 1; tmv.tm_mday = target_day; } else if ((tmv.tm_mon + 1 == month_of_year) && (tmv.tm_mday == target_day)) { if (alarm_sec <= now_sec) { tmv.tm_year += 1; } tmv.tm_mon = month_of_year - 1; max_day = days_in_month(tmv.tm_year + 1970, month_of_year); target_day = day_of_month > max_day ? max_day : day_of_month; tmv.tm_mday = target_day; } else { tmv.tm_year += 1; tmv.tm_mon = month_of_year - 1; max_day = days_in_month(tmv.tm_year + 1970, month_of_year); target_day = day_of_month > max_day ? max_day : day_of_month; tmv.tm_mday = target_day; } normalize_tm(&tmv); set_date_from_tm(alarm, &tmv); return 0; } default: return -5; } } /* ==================== 闹钟对象构造工具 ==================== */ static int build_alarm_by_params(const alarm_control_params_t *params, alarm_object_t *alarm_obj, char *err_msg, size_t err_len) { if (!params || !alarm_obj || !err_msg || err_len == 0) { return -1; } memset(alarm_obj, 0, sizeof(*alarm_obj)); alarm_obj->alarm_id = 0; alarm_obj->cloud_id = params->cloud_id; alarm_obj->calendar = parse_calendar_type(params->calendar_type); alarm_obj->trigger.type = parse_trigger_type(params->alarm_type, params->holiday_name); // 检查闹钟时间(必选) if (!params->time_str || params->time_str[0] == '\0') { LISA_LOGE(TAG, "time missing"); snprintf(err_msg, err_len, "没有指定时间或时间格式错误"); return -1; } if (!parse_time_hms(params->time_str, &alarm_obj->trigger.hour, &alarm_obj->trigger.minute, &alarm_obj->trigger.second)) { LISA_LOGE(TAG, "invalid time format: %s", params->time_str); snprintf(err_msg, err_len, "没有指定时间或时间格式错误"); return -1; } // 检查闹钟日期 bool date_set = false; if (params->date_str) { uint16_t y = 0; uint8_t m = 0; uint8_t d = 0; if (parse_date_ymd(params->date_str, &y, &m, &d)) { alarm_obj->trigger.year = y; alarm_obj->trigger.month = m; alarm_obj->trigger.day = d; date_set = true; } } // 如果没有下发日期,构造闹钟日期 // 先按日历类型构造 if (!date_set) { if (alarm_obj->calendar == ALARM_CAL_LUNAR) { if (!params->lunar_date_str || params->lunar_date_str[0] == '\0') { LISA_LOGE(TAG, "lunar_date missing"); snprintf(err_msg, err_len, "缺少农历日期"); return -1; } char gregorian_date[16] = {0}; if (listenai_date_transition("lunar", params->lunar_date_str, gregorian_date, sizeof(gregorian_date)) != 0) { LISA_LOGE(TAG, "lunar transition failed"); snprintf(err_msg, err_len, "农历日期转换失败"); return -1; } uint16_t y = 0; uint8_t m = 0; uint8_t d = 0; if (!parse_date_ymd(gregorian_date, &y, &m, &d)) { LISA_LOGE(TAG, "invalid lunar transition date: %s", gregorian_date); snprintf(err_msg, err_len, "农历日期转换失败"); return -1; } alarm_obj->trigger.year = y; alarm_obj->trigger.month = m; alarm_obj->trigger.day = d; date_set = true; } } // 再按闹钟类型构造 if (!date_set) { if (alarm_obj->trigger.type == ALARM_TRIG_ONCE) { struct tm tmv; if (get_current_tm(&tmv) != 0) { LISA_LOGE(TAG, "get today date failed"); snprintf(err_msg, err_len, "无法获取当前日期"); return -1; } set_date_from_tm(alarm_obj, &tmv); date_set = true; } else if (alarm_obj->trigger.type == ALARM_TRIG_DAILY || alarm_obj->trigger.type == ALARM_TRIG_WEEKLY || alarm_obj->trigger.type == ALARM_TRIG_MONTHLY || alarm_obj->trigger.type == ALARM_TRIG_YEARLY || alarm_obj->trigger.type == ALARM_TRIG_WORKDAY|| alarm_obj->trigger.type == ALARM_TRIG_WEEKEND ) { int r = set_nearest_date_from_rule(alarm_obj, alarm_obj->trigger.type, params->day_of_week, params->day_of_month, params->month_of_year); if (r != 0) { LISA_LOGE(TAG, "missing or invalid rule params, type=%d", alarm_obj->trigger.type); snprintf(err_msg, err_len, "缺少必要的日期参数"); return -1; } }else if (alarm_obj->trigger.type == ALARM_TRIG_HOLIDAY) { if (!params->holiday_name || params->holiday_name[0] == '\0') { LISA_LOGE(TAG, "holiday_name missing"); snprintf(err_msg, err_len, "缺少节日名称"); return -1; } char gregorian_date[16] = {0}; if (listenai_date_transition("holiday", params->holiday_name, gregorian_date, sizeof(gregorian_date)) != 0) { LISA_LOGE(TAG, "holiday transition failed"); snprintf(err_msg, err_len, "节假日转换失败"); return -1; } uint16_t y = 0; uint8_t m = 0; uint8_t d = 0; if (!parse_date_ymd(gregorian_date, &y, &m, &d)) { LISA_LOGE(TAG, "invalid holiday transition date: %s", gregorian_date); snprintf(err_msg, err_len, "节假日转换失败"); return -1; } alarm_obj->trigger.year = y; alarm_obj->trigger.month = m; alarm_obj->trigger.day = d; date_set = true; } } // 保存时间戳 alarm_obj->alarm_id = alarm_obj_to_timestamp(alarm_obj); if (alarm_obj->alarm_id == 0) { LISA_LOGE(TAG, "invalid alarm datetime, cannot build alarm_id"); snprintf(err_msg, err_len, "闹钟时间无效"); return -1; } // 保存循环信息 if (params->day_of_week >= 1 && params->day_of_week <= 7) { alarm_obj->trigger.day_of_week = (uint8_t)params->day_of_week; } if (params->day_of_month >= 1 && params->day_of_month <= 31) { alarm_obj->trigger.day_of_month = (uint8_t)params->day_of_month; } if (params->month_of_year >= 1 && params->month_of_year <= 12) { alarm_obj->trigger.month = (uint8_t)params->month_of_year; } if (params->holiday_name && params->holiday_name[0] != '\0') { strncpy(alarm_obj->trigger.holiday_name, params->holiday_name, sizeof(alarm_obj->trigger.holiday_name) - 1); alarm_obj->trigger.holiday_name[sizeof(alarm_obj->trigger.holiday_name) - 1] = '\0'; } if (params->lunar_date_str && alarm_obj->calendar == ALARM_CAL_LUNAR) { uint8_t m = 0; uint8_t d = 0; if (parse_lunar_md(params->lunar_date_str, &m, &d)) { alarm_obj->trigger.lunar_month = m; alarm_obj->trigger.lunar_day = d; } else { LISA_LOGW(TAG, "invalid lunar_date format: %s", params->lunar_date_str); } } // 保存文本和关键词 if (params->text && params->text[0] != '\0') { strncpy(alarm_obj->text, params->text, sizeof(alarm_obj->text) - 1); alarm_obj->text[sizeof(alarm_obj->text) - 1] = '\0'; } if (params->search_keywords) { alarm_fill_keywords(params->search_keywords, alarm_obj); } return 0; } /* ==================== 闹钟对象查找工具 ==================== */ static int delete_alarm_by_cloud_id(uint64_t cloud_id, char *result_text, size_t text_len) { return service_alarm_delete_by_cloud_id(cloud_id, result_text, text_len); } /** * @brief 闹钟提醒相关功能的处理函数 * * 该工具用于处理闹钟提醒的创建、查询、删除操作。 * 首先构造 alarm_obj 闹钟对象, * 然后基于 action 调用不同的函数处理闹钟操作指令, * 最后基于处理结果构造 MCP 响应。 */ static int alarm_control_execute(const alarm_control_params_t *params, char *result_text, size_t text_len) { if (!params || !result_text || text_len == 0) { ALARM_CTRL_FAIL("缺少参数"); } if (!params->action || params->action[0] == '\0') { ALARM_CTRL_FAIL("缺少操作类型"); } if (params->cloud_id == 0) { ALARM_CTRL_FAIL("缺少闹钟id"); } result_text[0] = '\0'; // 执行闹钟指令 if (params->action && strcmp(params->action, "CREATE") == 0) { alarm_object_t alarm_obj; memset(&alarm_obj, 0, sizeof(alarm_obj)); if (build_alarm_by_params(params, &alarm_obj, result_text, text_len) != 0) { ALARM_CTRL_FAIL(result_text); } alarm_obj_print(&alarm_obj); if (service_alarm_create_obj(&alarm_obj, result_text, text_len) != 0) { ALARM_CTRL_FAIL(result_text); } } else if (params->action && strcmp(params->action, "DELETE") == 0) { if (service_alarm_delete_by_cloud_id(params->cloud_id, result_text, text_len) != 0) { ALARM_CTRL_FAIL(result_text); } } else if (params->action && strcmp(params->action, "UPDATE") == 0) { if (service_alarm_delete_by_cloud_id(params->cloud_id, result_text, text_len) != 0) { ALARM_CTRL_FAIL(result_text); } alarm_object_t new_alarm; memset(&new_alarm, 0, sizeof(new_alarm)); if (build_alarm_by_params(params, &new_alarm, result_text, text_len) != 0) { ALARM_CTRL_FAIL(result_text); } alarm_obj_print(&new_alarm); if (service_alarm_create_obj(&new_alarm, result_text, text_len) != 0) { ALARM_CTRL_FAIL(result_text); } } else if (params->action && strcmp(params->action, "QUERY") == 0) { const alarm_object_t *target = service_alarm_find_by_cloud_id(params->cloud_id); if (!target) { ALARM_CTRL_FAIL("未找到对应闹钟"); } snprintf(result_text, text_len, "查询成功, id=%llu, timestamp=%llu, text=%s", (unsigned long long)target->cloud_id, (unsigned long long)target->alarm_id, target->text); } else { ALARM_CTRL_FAIL("不支持的操作类型"); } return 0; } static cJSON *alarm_control_call(const char *id, const char *name, cJSON *args) { (void)id; alarm_control_params_t params; alarm_control_params_init(¶ms); alarm_control_params_parse(args, ¶ms); char result_text[512] = {0}; int exec_ret = alarm_control_execute(¶ms, result_text, sizeof(result_text)); if (result_text[0] == '\0') { snprintf(result_text, sizeof(result_text), "%s", (exec_ret == 0) ? "闹钟操作成功" : "闹钟操作失败"); } cJSON *result = mcp_tool_call_result_create(name); if (!result) { return NULL; } cJSON_AddStringToObject(result, "content", result_text); cJSON_AddBoolToObject(result, "isError", exec_ret != 0); return result; } static cJSON *alarm_control_list(const char *name) { cJSON *tool = cJSON_CreateObject(); if (!tool) { return NULL; } cJSON_AddStringToObject(tool, "name", name); cJSON_AddStringToObject(tool, "description", "智能闹钟与提醒管理系统。支持公历/农历、固定周期及动态节假日。核心准则:1.【禁止猜测时间】若用户仅提供‘下午’、‘早晨’、‘待会’、‘以后’等模糊时段,或未提及具体时间,严禁私自编造时间点,此时必须放弃调用函数,转而向用户询问具体几点几分。2. 识别到特定节日必须填充 holiday_name;3. 只要存在提醒内容(text),就必须同步提取搜索关键词(search_keywords)以供后续索引。"); cJSON *schema = cJSON_CreateObject(); if (!schema) { cJSON_Delete(tool); return NULL; } cJSON_AddStringToObject(schema, "type", "object"); cJSON *properties = cJSON_CreateObject(); if (!properties) { cJSON_Delete(schema); cJSON_Delete(tool); return NULL; } cJSON *action_prop = cJSON_CreateObject(); cJSON_AddStringToObject(action_prop, "type", "string"); cJSON *action_enum = cJSON_CreateArray(); cJSON_AddItemToArray(action_enum, cJSON_CreateString("CREATE")); cJSON_AddItemToArray(action_enum, cJSON_CreateString("DELETE")); cJSON_AddItemToArray(action_enum, cJSON_CreateString("UPDATE")); cJSON_AddItemToArray(action_enum, cJSON_CreateString("QUERY")); cJSON_AddItemToObject(action_prop, "enum", action_enum); cJSON_AddStringToObject(action_prop, "description", "操作类型:CREATE(新建), DELETE(删除), UPDATE(修改), QUERY(查询)。"); cJSON_AddItemToObject(properties, "action", action_prop); cJSON *alarm_type_prop = cJSON_CreateObject(); cJSON_AddStringToObject(alarm_type_prop, "type", "string"); cJSON *alarm_type_enum = cJSON_CreateArray(); cJSON_AddItemToArray(alarm_type_enum, cJSON_CreateString("ONCE")); cJSON_AddItemToArray(alarm_type_enum, cJSON_CreateString("DAILY")); cJSON_AddItemToArray(alarm_type_enum, cJSON_CreateString("WEEKLY")); cJSON_AddItemToArray(alarm_type_enum, cJSON_CreateString("WORKDAY")); cJSON_AddItemToArray(alarm_type_enum, cJSON_CreateString("WEEKEND")); cJSON_AddItemToArray(alarm_type_enum, cJSON_CreateString("MONTHLY")); cJSON_AddItemToArray(alarm_type_enum, cJSON_CreateString("YEARLY")); cJSON_AddItemToObject(alarm_type_prop, "enum", alarm_type_enum); cJSON_AddStringToObject(alarm_type_prop, "description", "循环策略。节日提醒固定使用 YEARLY。"); cJSON_AddItemToObject(properties, "alarm_type", alarm_type_prop); cJSON *holiday_name_prop = cJSON_CreateObject(); cJSON_AddStringToObject(holiday_name_prop, "type", "string"); cJSON_AddStringToObject(holiday_name_prop, "description", "节日名称(如'中秋节'、'母亲节')。若用户提及节日,此项必填。"); cJSON_AddItemToObject(properties, "holiday_name", holiday_name_prop); cJSON *calendar_type_prop = cJSON_CreateObject(); cJSON_AddStringToObject(calendar_type_prop, "type", "string"); cJSON *calendar_type_enum = cJSON_CreateArray(); cJSON_AddItemToArray(calendar_type_enum, cJSON_CreateString("GREGORIAN")); cJSON_AddItemToArray(calendar_type_enum, cJSON_CreateString("LUNAR")); cJSON_AddItemToObject(calendar_type_prop, "enum", calendar_type_enum); cJSON_AddStringToObject(calendar_type_prop, "default", "GREGORIAN"); cJSON_AddStringToObject(calendar_type_prop, "description", "历法:GREGORIAN(公历), LUNAR(农历)。"); cJSON_AddItemToObject(properties, "calendar_type", calendar_type_prop); cJSON *time_prop = cJSON_CreateObject(); cJSON_AddStringToObject(time_prop, "type", "string"); cJSON_AddStringToObject(time_prop, "description", "24小时制时间 (HH:mm:ss)。严禁推测,未提及则反问用户。"); cJSON_AddItemToObject(properties, "time", time_prop); cJSON *date_prop = cJSON_CreateObject(); cJSON_AddStringToObject(date_prop, "type", "string"); cJSON_AddStringToObject(date_prop, "description", "具体日期 (YYYY-MM-DD)。仅在 ONCE 模式下使用。"); cJSON_AddItemToObject(properties, "date", date_prop); cJSON *lunar_date_prop = cJSON_CreateObject(); cJSON_AddStringToObject(lunar_date_prop, "type", "string"); cJSON_AddStringToObject(lunar_date_prop, "description", "具体日期 (MM-DD)。仅在 calendar_type 为 LUNAR 模式下使用。需要输出农历对应的数字月日格式,如‘正月初三’输出‘01-03’。"); cJSON_AddItemToObject(properties, "lunar_date", lunar_date_prop); cJSON *day_of_week_prop = cJSON_CreateObject(); cJSON_AddStringToObject(day_of_week_prop, "type", "integer"); cJSON_AddNumberToObject(day_of_week_prop, "minimum", 1); cJSON_AddNumberToObject(day_of_week_prop, "maximum", 7); cJSON_AddStringToObject(day_of_week_prop, "description", "周几(1-7,1代表周一)。仅在 WEEKLY 模式有效。"); cJSON_AddItemToObject(properties, "day_of_week", day_of_week_prop); cJSON *day_of_month_prop = cJSON_CreateObject(); cJSON_AddStringToObject(day_of_month_prop, "type", "integer"); cJSON_AddNumberToObject(day_of_month_prop, "minimum", 1); cJSON_AddNumberToObject(day_of_month_prop, "maximum", 31); cJSON_AddStringToObject(day_of_month_prop, "description", "每月几号。仅在 MONTHLY 或无节日的 YEARLY 模式有效。"); cJSON_AddItemToObject(properties, "day_of_month", day_of_month_prop); cJSON *month_of_year_prop = cJSON_CreateObject(); cJSON_AddStringToObject(month_of_year_prop, "type", "integer"); cJSON_AddNumberToObject(month_of_year_prop, "minimum", 1); cJSON_AddNumberToObject(month_of_year_prop, "maximum", 12); cJSON_AddStringToObject(month_of_year_prop, "description", "每年几月。仅在无节日的 YEARLY 模式有效。"); cJSON_AddItemToObject(properties, "month_of_year", month_of_year_prop); cJSON *text_prop = cJSON_CreateObject(); cJSON_AddStringToObject(text_prop, "type", "string"); cJSON_AddStringToObject(text_prop, "description", "提醒的完整文本内容。例如:'给妈妈打电话庆祝生日'。"); cJSON_AddItemToObject(properties, "text", text_prop); cJSON *search_keywords_prop = cJSON_CreateObject(); cJSON_AddStringToObject(search_keywords_prop, "type", "array"); cJSON *keywords_items = cJSON_CreateObject(); cJSON_AddStringToObject(keywords_items, "type", "string"); cJSON_AddItemToObject(search_keywords_prop, "items", keywords_items); cJSON_AddStringToObject(search_keywords_prop, "description", "【强制要求】从 text 中提取的 1-3 个核心动词或名词。若 text 存在,此项禁止为空。例如 text 为'记得下午三点喝水',关键词为 ['喝水']。"); cJSON_AddItemToObject(properties, "search_keywords", search_keywords_prop); cJSON_AddItemToObject(schema, "properties", properties); cJSON *required = cJSON_CreateArray(); cJSON_AddItemToArray(required, cJSON_CreateString("action")); cJSON_AddItemToObject(schema, "required", required); cJSON_AddItemToObject(tool, "inputSchema", schema); return tool; } MCP_TOOL_DEFINE(ls.built_in.alarm_clock, alarm_control_list, alarm_control_call);