Files
xiaozhi-esp32/main/schedule_manager.cc
2025-09-15 22:04:01 +08:00

409 lines
14 KiB
C++

#include "schedule_manager.h"
#include <esp_log.h>
#include <esp_timer.h>
#include <cstring>
#include <algorithm>
#include <sstream>
const char* ScheduleManager::TAG = "ScheduleManager";
ScheduleManager::ScheduleManager() {
ESP_LOGI(TAG, "ScheduleManager initialized");
}
ScheduleManager::~ScheduleManager() {
ESP_LOGI(TAG, "ScheduleManager destroyed");
}
std::string ScheduleManager::CreateEvent(const std::string& title,
const std::string& description,
time_t start_time,
time_t end_time,
const std::string& category,
bool is_all_day,
int reminder_minutes) {
std::lock_guard<std::mutex> lock(events_mutex_);
if (title.empty()) {
ESP_LOGE(TAG, "Event title cannot be empty");
return "";
}
if (!IsEventTimeValid(start_time, end_time)) {
ESP_LOGE(TAG, "Invalid event time");
return "";
}
ScheduleEvent event;
event.id = GenerateEventId();
event.title = title;
event.description = description;
event.start_time = start_time;
event.end_time = end_time;
event.is_all_day = is_all_day;
event.reminder_minutes = reminder_minutes;
event.created_time = time(nullptr);
event.updated_time = event.created_time;
// 智能分类
if (category.empty()) {
event.category = CategorizeEvent(title, description);
} else {
event.category = category;
}
events_[event.id] = event;
ESP_LOGI(TAG, "Created event: %s (ID: %s)", title.c_str(), event.id.c_str());
return event.id;
}
bool ScheduleManager::UpdateEvent(const std::string& event_id,
const std::string& title,
const std::string& description,
time_t start_time,
time_t end_time,
const std::string& category,
bool is_all_day,
int reminder_minutes) {
std::lock_guard<std::mutex> lock(events_mutex_);
auto it = events_.find(event_id);
if (it == events_.end()) {
ESP_LOGE(TAG, "Event not found: %s", event_id.c_str());
return false;
}
ScheduleEvent& event = it->second;
if (!title.empty()) {
event.title = title;
}
if (!description.empty()) {
event.description = description;
}
if (start_time > 0) {
event.start_time = start_time;
}
if (end_time > 0) {
event.end_time = end_time;
}
if (!category.empty()) {
event.category = category;
}
if (reminder_minutes >= 0) {
event.reminder_minutes = reminder_minutes;
}
event.is_all_day = is_all_day;
event.updated_time = time(nullptr);
ESP_LOGI(TAG, "Updated event: %s", event_id.c_str());
return true;
}
bool ScheduleManager::DeleteEvent(const std::string& event_id) {
std::lock_guard<std::mutex> lock(events_mutex_);
auto it = events_.find(event_id);
if (it == events_.end()) {
ESP_LOGE(TAG, "Event not found: %s", event_id.c_str());
return false;
}
events_.erase(it);
ESP_LOGI(TAG, "Deleted event: %s", event_id.c_str());
return true;
}
ScheduleEvent* ScheduleManager::GetEvent(const std::string& event_id) {
std::lock_guard<std::mutex> lock(events_mutex_);
auto it = events_.find(event_id);
if (it == events_.end()) {
return nullptr;
}
return &it->second;
}
std::vector<ScheduleEvent> ScheduleManager::GetEventsByDate(time_t date) {
std::lock_guard<std::mutex> lock(events_mutex_);
std::vector<ScheduleEvent> result;
struct tm date_tm = *localtime(&date);
for (const auto& pair : events_) {
const ScheduleEvent& event = pair.second;
struct tm event_tm = *localtime(&event.start_time);
if (event_tm.tm_year == date_tm.tm_year &&
event_tm.tm_mon == date_tm.tm_mon &&
event_tm.tm_mday == date_tm.tm_mday) {
result.push_back(event);
}
}
// 按开始时间排序
std::sort(result.begin(), result.end(),
[](const ScheduleEvent& a, const ScheduleEvent& b) {
return a.start_time < b.start_time;
});
return result;
}
std::vector<ScheduleEvent> ScheduleManager::GetEventsByCategory(const std::string& category) {
std::lock_guard<std::mutex> lock(events_mutex_);
std::vector<ScheduleEvent> result;
for (const auto& pair : events_) {
const ScheduleEvent& event = pair.second;
if (event.category == category) {
result.push_back(event);
}
}
return result;
}
std::vector<ScheduleEvent> ScheduleManager::GetUpcomingEvents(int days) {
std::lock_guard<std::mutex> lock(events_mutex_);
std::vector<ScheduleEvent> result;
time_t now = time(nullptr);
time_t future_time = now + (days * 24 * 60 * 60);
for (const auto& pair : events_) {
const ScheduleEvent& event = pair.second;
if (event.start_time >= now && event.start_time <= future_time && !event.is_completed) {
result.push_back(event);
}
}
// 按开始时间排序
std::sort(result.begin(), result.end(),
[](const ScheduleEvent& a, const ScheduleEvent& b) {
return a.start_time < b.start_time;
});
return result;
}
std::vector<ScheduleEvent> ScheduleManager::GetEventsByKeyword(const std::string& keyword) {
std::lock_guard<std::mutex> lock(events_mutex_);
std::vector<ScheduleEvent> result;
std::string lower_keyword = keyword;
std::transform(lower_keyword.begin(), lower_keyword.end(), lower_keyword.begin(), ::tolower);
for (const auto& pair : events_) {
const ScheduleEvent& event = pair.second;
std::string lower_title = event.title;
std::string lower_description = event.description;
std::transform(lower_title.begin(), lower_title.end(), lower_title.begin(), ::tolower);
std::transform(lower_description.begin(), lower_description.end(), lower_description.begin(), ::tolower);
if (lower_title.find(lower_keyword) != std::string::npos ||
lower_description.find(lower_keyword) != std::string::npos) {
result.push_back(event);
}
}
return result;
}
std::string ScheduleManager::CategorizeEvent(const std::string& title, const std::string& description) {
std::string text = title + " " + description;
std::transform(text.begin(), text.end(), text.begin(), ::tolower);
// 工作相关关键词
if (text.find("会议") != std::string::npos || text.find("工作") != std::string::npos ||
text.find("项目") != std::string::npos || text.find("报告") != std::string::npos ||
text.find("deadline") != std::string::npos || text.find("meeting") != std::string::npos) {
return CategoryToString(EventCategory::WORK);
}
// 学习相关关键词
if (text.find("学习") != std::string::npos || text.find("课程") != std::string::npos ||
text.find("考试") != std::string::npos || text.find("作业") != std::string::npos ||
text.find("study") != std::string::npos || text.find("exam") != std::string::npos) {
return CategoryToString(EventCategory::STUDY);
}
// 健康相关关键词
if (text.find("运动") != std::string::npos || text.find("健身") != std::string::npos ||
text.find("医院") != std::string::npos || text.find("体检") != std::string::npos ||
text.find("exercise") != std::string::npos || text.find("doctor") != std::string::npos) {
return CategoryToString(EventCategory::HEALTH);
}
// 娱乐相关关键词
if (text.find("电影") != std::string::npos || text.find("游戏") != std::string::npos ||
text.find("聚会") != std::string::npos || text.find("娱乐") != std::string::npos ||
text.find("movie") != std::string::npos || text.find("party") != std::string::npos) {
return CategoryToString(EventCategory::ENTERTAINMENT);
}
// 旅行相关关键词
if (text.find("旅行") != std::string::npos || text.find("旅游") != std::string::npos ||
text.find("出差") != std::string::npos || text.find("travel") != std::string::npos ||
text.find("trip") != std::string::npos) {
return CategoryToString(EventCategory::TRAVEL);
}
// 家庭相关关键词
if (text.find("家庭") != std::string::npos || text.find("家人") != std::string::npos ||
text.find("孩子") != std::string::npos || text.find("family") != std::string::npos ||
text.find("child") != std::string::npos) {
return CategoryToString(EventCategory::FAMILY);
}
return CategoryToString(EventCategory::OTHER);
}
void ScheduleManager::SetReminderCallback(ReminderCallback callback) {
reminder_callback_ = callback;
}
void ScheduleManager::CheckReminders() {
std::lock_guard<std::mutex> lock(events_mutex_);
time_t now = time(nullptr);
for (const auto& pair : events_) {
const ScheduleEvent& event = pair.second;
if (event.is_completed || event.reminder_minutes <= 0) {
continue;
}
time_t reminder_time = event.start_time - (event.reminder_minutes * 60);
if (now >= reminder_time && now < event.start_time) {
ESP_LOGI(TAG, "Reminder triggered for event: %s", event.title.c_str());
if (reminder_callback_) {
reminder_callback_(event);
}
}
}
}
int ScheduleManager::GetEventCount() {
std::lock_guard<std::mutex> lock(events_mutex_);
return events_.size();
}
int ScheduleManager::GetEventCountByCategory(const std::string& category) {
std::lock_guard<std::mutex> lock(events_mutex_);
int count = 0;
for (const auto& pair : events_) {
if (pair.second.category == category) {
count++;
}
}
return count;
}
std::map<std::string, int> ScheduleManager::GetCategoryStatistics() {
std::lock_guard<std::mutex> lock(events_mutex_);
std::map<std::string, int> stats;
for (const auto& pair : events_) {
stats[pair.second.category]++;
}
return stats;
}
bool ScheduleManager::SaveToStorage() {
// TODO: 实现数据持久化到NVS或SPIFFS
ESP_LOGW(TAG, "SaveToStorage not implemented yet");
return true;
}
bool ScheduleManager::LoadFromStorage() {
// TODO: 实现从NVS或SPIFFS加载数据
ESP_LOGW(TAG, "LoadFromStorage not implemented yet");
return true;
}
std::string ScheduleManager::ExportToJson() {
std::lock_guard<std::mutex> lock(events_mutex_);
std::stringstream json;
json << "{\"events\":[";
bool first = true;
for (const auto& pair : events_) {
if (!first) json << ",";
first = false;
const ScheduleEvent& event = pair.second;
json << "{"
<< "\"id\":\"" << event.id << "\","
<< "\"title\":\"" << event.title << "\","
<< "\"description\":\"" << event.description << "\","
<< "\"category\":\"" << event.category << "\","
<< "\"start_time\":" << event.start_time << ","
<< "\"end_time\":" << event.end_time << ","
<< "\"is_all_day\":" << (event.is_all_day ? "true" : "false") << ","
<< "\"reminder_minutes\":" << event.reminder_minutes << ","
<< "\"is_completed\":" << (event.is_completed ? "true" : "false") << ","
<< "\"created_time\":" << event.created_time << ","
<< "\"updated_time\":" << event.updated_time
<< "}";
}
json << "]}";
return json.str();
}
bool ScheduleManager::ImportFromJson(const std::string& json_data) {
// TODO: 实现JSON导入功能
ESP_LOGW(TAG, "ImportFromJson not implemented yet");
return false;
}
std::string ScheduleManager::GenerateEventId() {
static int counter = 0;
return "event_" + std::to_string(++counter) + "_" + std::to_string(time(nullptr));
}
std::string ScheduleManager::CategoryToString(EventCategory category) {
switch (category) {
case EventCategory::WORK: return "工作";
case EventCategory::LIFE: return "生活";
case EventCategory::STUDY: return "学习";
case EventCategory::HEALTH: return "健康";
case EventCategory::ENTERTAINMENT: return "娱乐";
case EventCategory::TRAVEL: return "旅行";
case EventCategory::FAMILY: return "家庭";
case EventCategory::OTHER: return "其他";
default: return "其他";
}
}
EventCategory ScheduleManager::StringToCategory(const std::string& category_str) {
if (category_str == "工作") return EventCategory::WORK;
if (category_str == "生活") return EventCategory::LIFE;
if (category_str == "学习") return EventCategory::STUDY;
if (category_str == "健康") return EventCategory::HEALTH;
if (category_str == "娱乐") return EventCategory::ENTERTAINMENT;
if (category_str == "旅行") return EventCategory::TRAVEL;
if (category_str == "家庭") return EventCategory::FAMILY;
return EventCategory::OTHER;
}
bool ScheduleManager::IsEventTimeValid(time_t start_time, time_t end_time) {
if (start_time <= 0) return false;
if (end_time > 0 && end_time <= start_time) return false;
return true;
}
void ScheduleManager::UpdateEventTimestamp(ScheduleEvent& event) {
event.updated_time = time(nullptr);
}