diff --git a/Bionic_Core/CommClass/CommClass.cpp b/Bionic_Core/CommClass/CommClass.cpp index e18f849..e8034ed 100644 --- a/Bionic_Core/CommClass/CommClass.cpp +++ b/Bionic_Core/CommClass/CommClass.cpp @@ -26,8 +26,6 @@ WebSocketManager::WebSocketManager() { - // 初始化统计信息 - stats = {0, 0, 0, 0}; } diff --git a/Bionic_Core/CppHandle/CppHandle.cpp b/Bionic_Core/CppHandle/CppHandle.cpp index dd265d1..c427637 100644 --- a/Bionic_Core/CppHandle/CppHandle.cpp +++ b/Bionic_Core/CppHandle/CppHandle.cpp @@ -4,8 +4,118 @@ #include "CppHandle.h" #include "OTAClass.h" +#include "PetBaseClass.h" +#include "PetDao.h" +#include + +void testPetSystem() { + std::cout << "Test point1" << std::endl; + // 创建阶段策略 + auto stageStrategy = std::make_unique(); + stageStrategy->addStage(PetStageType::PET_STAGE_YOUNG, "models/young.obj"); + stageStrategy->addStage(PetStageType::PET_STAGE_ADULT, "models/adult.obj"); + stageStrategy->addStage(PetStageType::PET_STAGE_OLD, "models/old.obj"); + stageStrategy->addStageAudio(PetStageType::PET_STAGE_YOUNG, "audio/young.mp3"); + stageStrategy->addStageAudio(PetStageType::PET_STAGE_ADULT, "audio/adult.mp3"); + stageStrategy->addStageAudio(PetStageType::PET_STAGE_OLD, "audio/old.mp3"); + + // 创建动作策略 + auto actionStrategy = std::make_unique(); + actionStrategy->addAction(PetActionType::PET_ACTION_EAT, "models/eat.obj"); + actionStrategy->addAction(PetActionType::PET_ACTION_HAPPY, "models/happy.obj"); + actionStrategy->addAction(PetActionType::PET_ACTION_SLEEP, "models/sleep.obj"); + actionStrategy->addAction(PetActionType::PET_ACTION_ANGRY, "models/angry.obj"); + actionStrategy->addAction(PetActionType::PET_ACTION_SAD, "models/sad.obj"); + actionStrategy->addAction(PetActionType::PET_ACTION_EVOLVE, "models/evolve.obj"); + actionStrategy->addAction(PetActionType::PET_ACTION_TOUCH, "models/touch.obj"); + + actionStrategy->addActionAudio(PetActionType::PET_ACTION_EAT, "audio/eat.mp3"); + actionStrategy->addActionAudio(PetActionType::PET_ACTION_HAPPY, "audio/happy.mp3"); + actionStrategy->addActionAudio(PetActionType::PET_ACTION_SLEEP, "audio/sleep.mp3"); + std::cout << "Test point2" << std::endl; + + // 创建宠物信息 + PetBaseInfo info; + info.pet_name = "芝士雪豹"; + info.pet_hp = 100; + info.pet_density = 50; + info.pet_identity = "我是顶真"; + + // 创建宠物 + auto pet = std::make_shared(info, std::move(stageStrategy), std::move(actionStrategy)); + std::cout << "Test point3" << std::endl; + + // 创建音频观察者 + auto audioStrategy = std::make_shared(); + audioStrategy->setAudioCallback([](const std::string& audioPath) { + std::cout << "Playing audio: " << audioPath << std::endl; + }); + audioStrategy->subscribe(pet); + std::cout << "Test point4" << std::endl; + + // 创建渲染器观察者 + auto rendererStrategy = std::make_shared( + pet->getStageStrategy(), + pet->getActionStrategy() + ); + rendererStrategy->setRenderCallback([](const std::string& modelPath) { + std::cout << "Rendering model: " << modelPath << std::endl; + }); + rendererStrategy->subscribe(pet); + std::cout << "Test point5" << std::endl; + + // 执行一些动作 + std::cout << "=== Testing basic actions ===" << std::endl; + pet->feed(); + pet->play(); + pet->touch(); + std::cout << "Test point6" << std::endl; + + // 检查当前状态 + std::cout << "Current HP: " << pet->getPetInfo().pet_hp << std::endl; + std::cout << "Current density: " << pet->getPetInfo().pet_density << std::endl; + std::cout << "Current stage: " << static_cast(pet->getCurrentStage()) << std::endl; + std::cout << "Current action: " << static_cast(pet->getCurrentAction()) << std::endl; + std::cout << "Test point7" << std::endl; + + // 测试进化 + std::cout << "\n=== Testing evolution ===" << std::endl; + // 直接修改亲密度来测试进化 + PetBaseInfo newInfo = pet->getPetInfo(); + newInfo.pet_density = 100; // 达到进化条件 + pet->setPetInfo(newInfo); + std::cout << "Test point8" << std::endl; + + if (pet->checkEvolution()) { + std::cout << "Evolution successful!" << std::endl; + } else { + std::cout << "Evolution failed!" << std::endl; + } + + // 进一步增加亲密度到150,尝试再次进化 + newInfo.pet_density = 150; + pet->setPetInfo(newInfo); + if (pet->checkEvolution()) { + std::cout << "Second evolution successful!" << std::endl; + } else { + std::cout << "Second evolution failed!" << std::endl; + } + + std::cout << "Final stage: " << static_cast(pet->getCurrentStage()) << std::endl; + + PetDAO petDAO(SDFileManager::getInstance()); + petDAO.savePet(pet, "my_pet.json"); + // 列出所有宠物文件 + auto petFiles = petDAO.listPetFiles(); + for (const auto& file : petFiles) { + std::cout << "Pet file: " << file << std::endl; + } + std::cout << SDFileManager::getInstance()->catCommand("/sdcard/pet_data/my_pet.json") << std::endl; +} void Cpp_Hand() { + testPetSystem(); + OTAClass oc; oc.Init(); } diff --git a/Bionic_Core/OTAClass/OTAClass.cpp b/Bionic_Core/OTAClass/OTAClass.cpp index add5e50..66b2362 100644 --- a/Bionic_Core/OTAClass/OTAClass.cpp +++ b/Bionic_Core/OTAClass/OTAClass.cpp @@ -20,17 +20,6 @@ const auto sleep_time = seconds{ 5 }; -esp_pthread_cfg_t create_config(const char *name, int core_id, int stack, int prio) -{ - - auto cfg = esp_pthread_get_default_config(); - cfg.thread_name = name; - cfg.pin_to_core = core_id; - cfg.stack_size = stack; - cfg.prio = prio; - return cfg; -} - #include "ThreadManager.h" #include "WifiConnectors.h" #include @@ -38,6 +27,124 @@ esp_pthread_cfg_t create_config(const char *name, int core_id, int stack, int pr #include "LVGLRender.h" #include "SDFileManager.h" #include "AudioOutput.h" +#include "CommClass.h" +// JSON数据回调函数 +void onJsonData(cJSON* json) { + // 打印接收到的JSON数据 + char* jsonStr = cJSON_Print(json); + ESP_LOGI("JSON_CALLBACK", "收到JSON数据: %s", jsonStr); + free(jsonStr); + + // 解析消息类型并处理 + cJSON* type = cJSON_GetObjectItem(json, "type"); + if (type && cJSON_IsString(type)) { + if (strcmp(type->valuestring, "greeting") == 0) { + ESP_LOGI("JSON_CALLBACK", "收到服务器问候消息"); + } else if (strcmp(type->valuestring, "heartbeat") == 0) { + ESP_LOGI("JSON_CALLBACK", "收到心跳响应"); + } else if (strcmp(type->valuestring, "response") == 0) { + ESP_LOGI("JSON_CALLBACK", "收到服务器响应"); + } else if (strcmp(type->valuestring, "echo") == 0) { + ESP_LOGI("JSON_CALLBACK", "收到回显消息"); + } else if (strcmp(type->valuestring, "broadcast") == 0) { + ESP_LOGI("JSON_CALLBACK", "收到广播消息"); + } + } + + // 记得删除cJSON对象 + cJSON_Delete(json); +} + +// WebSocket事件回调函数 +void onWebSocketEvent(WebSocketEvent event, const std::string& message) { + switch (event) { + case WebSocketEvent::CONNECTED: + ESP_LOGI("EVENT_CALLBACK", "WebSocket已连接: %s", message.c_str()); + break; + case WebSocketEvent::DISCONNECTED: + ESP_LOGI("EVENT_CALLBACK", "WebSocket已断开: %s", message.c_str()); + break; + case WebSocketEvent::DATA_RECEIVED: + ESP_LOGI("EVENT_CALLBACK", "收到原始数据: %s", message.c_str()); + break; + case WebSocketEvent::ERROR: + ESP_LOGE("EVENT_CALLBACK", "WebSocket错误: %s", message.c_str()); + break; + } +} + +// 发送状态信息函数 +void sendStatus() { + cJSON* status = cJSON_CreateObject(); + cJSON_AddStringToObject(status, "type", "status"); + + cJSON* data = cJSON_CreateObject(); + cJSON_AddNumberToObject(data, "free_heap", esp_get_free_heap_size()); + cJSON_AddNumberToObject(data, "uptime", xTaskGetTickCount() * portTICK_PERIOD_MS / 1000); + + cJSON_AddItemToObject(status, "data", data); + + if (WebSocketManager::getInstance()->sendJson(status)) { + ESP_LOGI("SEND", "已发送状态信息"); + } else { + ESP_LOGE("SEND", "发送状态信息失败"); + } +} + +// 发送问候消息函数 +void sendGreeting() { + cJSON* greeting = cJSON_CreateObject(); + cJSON_AddStringToObject(greeting, "type", "greeting"); + cJSON_AddStringToObject(greeting, "message", "Hello from ESP32-S3"); + cJSON_AddNumberToObject(greeting, "timestamp", xTaskGetTickCount() * portTICK_PERIOD_MS / 1000); + + if (WebSocketManager::getInstance()->sendJson(greeting)) { + ESP_LOGI("SEND", "已发送问候消息"); + } else { + ESP_LOGE("SEND", "发送问候消息失败"); + } +} + +void websocket_task() { + TickType_t lastStatusTime = 0; + TickType_t lastHeartbeatTime = 0; + TickType_t lastGreetingTime = 0; + + while (true) { + TickType_t currentTime = xTaskGetTickCount(); + + // 检查连接状态 + if (!WebSocketManager::getInstance()->isConnected()) { + ESP_LOGI("APP_TASK", "WebSocket未连接,尝试重新连接..."); + + // 确保WiFi已连接 + if (!WifiConnectors::getInstance()->isWifiConnect()) { + ESP_LOGI("APP_TASK", "WiFi未连接,等待WiFi连接..."); + vTaskDelay(5000 / portTICK_PERIOD_MS); + continue; + } + + if (WebSocketManager::getInstance()->connect()) { + ESP_LOGI("APP_TASK", "重新连接成功"); + } else { + ESP_LOGI("APP_TASK", "重新连接失败"); + } + vTaskDelay(5000 / portTICK_PERIOD_MS); + continue; + } + // 每10秒发送状态信息 + if (currentTime - lastStatusTime > (10000 / portTICK_PERIOD_MS)) { + sendStatus(); + lastStatusTime = currentTime; + } + // 每60秒发送问候 + if (currentTime - lastGreetingTime > (60000 / portTICK_PERIOD_MS)) { + sendGreeting(); + lastGreetingTime = currentTime; + } + vTaskDelay(100 / portTICK_PERIOD_MS); + } +} void OTAClass::Init() { ESP_LOGI("OTA", "Init"); @@ -65,27 +172,67 @@ void OTAClass::Init() { // 同步播放 AudioOutput::getInstance()->playSync("/sdcard/music", "Old_Memory.mp3"); - // 等待5秒 - // vTaskDelay(pdMS_TO_TICKS(10000)); - // 暂停 - // AudioOutput::getInstance()->pause(); + // // 配置Wifi连接线程参数 + // ThreadConfig wifi_config; + // wifi_config.name = "WifiConnector"; // 线程名称 + // wifi_config.core_id = 1; // 绑定到核心1(避免与主线程冲突) + // wifi_config.stack_size = 4096; // 设置稍大的栈空间(Wifi连接可能需要) + // wifi_config.priority = 6; // 设置较高优先级(确保连接及时) + // // 使用单例方式创建线程,调用connectWifi成员函数 + // std::thread wifi_thread = ThreadManager::createSingletonThread( + // wifi_config, + // &WifiConnectors::connectWifi, + // "Misaki-2.4G", // SSID + // "88888888", // 密码 + // 5 // 最大重试次数 + // ); + // wifi_thread.detach(); + + WifiConnectors::getInstance()->connectWifi("Misaki-2.4G", "88888888", 5); - // 配置Wifi连接线程参数 - ThreadConfig wifi_config; - wifi_config.name = "WifiConnector"; // 线程名称 - wifi_config.core_id = 1; // 绑定到核心1(避免与主线程冲突) - wifi_config.stack_size = 4096; // 设置稍大的栈空间(Wifi连接可能需要) - wifi_config.priority = 6; // 设置较高优先级(确保连接及时) - // 使用单例方式创建线程,调用connectWifi成员函数 - std::thread wifi_thread = ThreadManager::createSingletonThread( - wifi_config, - &WifiConnectors::connectWifi, - "Misaki-2.4G", // SSID - "88888888", // 密码 - 5 // 最大重试次数 - ); + // 等待WiFi连接成功后再连接WebSocket + ESP_LOGI("APP_TASK", "等待WiFi连接..."); + while (!WifiConnectors::getInstance()->isWifiConnect()) { + ESP_LOGI("APP_TASK", "WiFi未连接,等待中..."); + vTaskDelay(1000 / portTICK_PERIOD_MS); + } + + // 配置WebSocket + WebSocketConfig config; + config.uri = "ws://" + std::string("192.168.1.11") + ":" + std::to_string(8080) + "/ws"; + config.auto_reconnect = true; // 自动重连 + config.reconnect_interval = 5000; // 重连间隔(毫秒) + config.heartbeat_interval = 30000; // 心跳间隔(毫秒) + config.max_reconnect_attempts = 10; // 最大重连尝试次数 + // TODO: 此处通信类存在线程重复创建bug,似乎是来自esp-idf的bug,待查证 + // 初始化WebSocket管理器 + // if (!WebSocketManager::getInstance()->initialize(config)) { + // ESP_LOGE("APP_TASK", "WebSocket管理器初始化失败"); + // vTaskDelete(NULL); + // return; + // } + + // 设置回调函数 + // WebSocketManager::getInstance()->setJsonCallback(onJsonData); + // WebSocketManager::getInstance()->setEventCallback(onWebSocketEvent); + + // 连接WebSocket服务器 + // ESP_LOGI("APP_TASK", "正在连接WebSocket服务器: %s", config.uri.c_str()); + // if (!WebSocketManager::getInstance()->connect()) { + // ESP_LOGE("APP_TASK", "WebSocket连接失败"); + // } + + // 创建WebSocket任务 + // ThreadConfig websocket_config; + // websocket_config.core_id = 0; + // websocket_config.inherit_cfg = true; + // websocket_config.name = "websocket_task"; + // websocket_config.priority = 5; + // websocket_config.stack_size = 4096; + // std::thread websocket_thread = ThreadManager::createThread(websocket_config, websocket_task); + // websocket_thread.detach(); // ThreadConfig ota_config; // ota_config.name = "OTA"; diff --git a/Bionic_Core/PetBaseClass/PetBaseClass.h b/Bionic_Core/PetBaseClass/PetBaseClass.h index 651ed7a..19a2479 100644 --- a/Bionic_Core/PetBaseClass/PetBaseClass.h +++ b/Bionic_Core/PetBaseClass/PetBaseClass.h @@ -2,5 +2,214 @@ // Created by misaki on 2025/9/2. // #pragma once - #include "PetInterface.h" +#include "PetObserver.h" +// 宠物基类 +// 宠物基本信息 +struct PetBaseInfo { + /// { +public: + // 构造函数 + PetBase() = default; + // 带参数的构造函数 + PetBase(PetBaseInfo info, + std::shared_ptr stageStrategy, + std::shared_ptr actionStrategy) + : pet_info(std::move(info)), + pet_stage_strategy(std::move(stageStrategy)), + pet_action_strategy(std::move(actionStrategy)) {} + virtual ~PetBase() = default; + // 实现PetSubject接口 + /** + * 添加观察者 + * @param observer 观察者 + */ + void addObserver(std::shared_ptr observer) override { + observers.push_back(observer); + } + /** + * 移除观察者 + * @param observer 观察者 + */ + void removeObserver(std::shared_ptr observer) override { + observers.remove(observer); + } + /** + * 通知动作 + * @param action 动作 + */ + void notifyAction(PetActionType action) override { + for (auto& observer : observers) { + observer->onPetAction(action); + } + } + /** + * 通知阶段 + * @param oldStage 旧阶段 + * @param newStage 新阶段 + */ + void notifyStageChange(PetStageType oldStage, PetStageType newStage) override { + for (auto& observer : observers) { + observer->onPetStageChange(oldStage, newStage); + } + } + // 宠物行为方法 + // 喂食 + virtual void feed() { + // 喂食会增加生命值和亲密度 + pet_info.pet_hp = std::min(100, pet_info.pet_hp + 10); + pet_info.pet_density = std::min(150, pet_info.pet_density + 15); + // 执行喂食动作并通知观察者 + if (pet_action_strategy->performAction(PetActionType::PET_ACTION_EAT)) { + notifyAction(PetActionType::PET_ACTION_EAT); + } + // 检查是否可以进化 + checkEvolution(); + } + // 玩耍 + virtual void play() { + // 玩耍会增加亲密度 + pet_info.pet_density = std::min(150, pet_info.pet_density + 10); + // 执行开心动作并通知观察者 + if (pet_action_strategy->performAction(PetActionType::PET_ACTION_HAPPY)) { + notifyAction(PetActionType::PET_ACTION_HAPPY); + } + // 检查是否可以进化 + checkEvolution(); + } + // 责骂 + virtual void scold() { + // 责骂会减少亲密度 + pet_info.pet_density = std::max(0, pet_info.pet_density - 10); + // 执行生气动作并通知观察者 + if (pet_action_strategy->performAction(PetActionType::PET_ACTION_ANGRY)) { + notifyAction(PetActionType::PET_ACTION_ANGRY); + } + } + // 忽视 + virtual void neglect() { + // 忽视会减少生命值和亲密度 + pet_info.pet_hp = std::max(0, pet_info.pet_hp - 5); + pet_info.pet_density = std::max(0, pet_info.pet_density - 8); + // 执行沮丧动作并通知观察者 + if (pet_action_strategy->performAction(PetActionType::PET_ACTION_SAD)) { + notifyAction(PetActionType::PET_ACTION_SAD); + } + } + // 触摸 + virtual void touch() { + // 触摸会增加亲密度 + pet_info.pet_density = std::min(150, pet_info.pet_density + 5); + // 执行被触摸动作并通知观察者 + if (pet_action_strategy->performAction(PetActionType::PET_ACTION_TOUCH)) { + notifyAction(PetActionType::PET_ACTION_TOUCH); + } + // 检查是否可以进化 + checkEvolution(); + } + // 进化检查 + virtual bool checkEvolution() { + // 获取当前阶段 + auto currentStage = pet_stage_strategy->getCurrentStageType(); + // 根据亲密度决定是否可以进化 + if (pet_info.pet_density >= 100 && currentStage == PetStageType::PET_STAGE_YOUNG) { + // 从幼年进化到青年 + return evolveToStage(PetStageType::PET_STAGE_ADULT); + } else if (pet_info.pet_density >= 150 && currentStage == PetStageType::PET_STAGE_ADULT) { + // 从青年进化到成年 + return evolveToStage(PetStageType::PET_STAGE_OLD); + } + return false; + } + // 进化到指定阶段 + virtual bool evolveToStage(PetStageType newStage) { + auto oldStage = pet_stage_strategy->getCurrentStageType(); // 获取当前阶段 + if (pet_stage_strategy->switchToStage(newStage)) { + // 执行进化动作并通知观察者 + if (pet_action_strategy->performAction(PetActionType::PET_ACTION_EVOLVE)) { + notifyAction(PetActionType::PET_ACTION_EVOLVE); + } + + // 通知阶段变化 + notifyStageChange(oldStage, newStage); + return true; + } + return false; + } + /** + * 获取宠物信息 + * @return 宠物信息 + */ + virtual const PetBaseInfo& getPetInfo() const { + return pet_info; + } + /** + * 设置宠物信息 + * @param info 宠物信息 + */ + virtual void setPetInfo(const PetBaseInfo& info) { + pet_info = info; + } + /** + * 获取阶段策略 + * @return 阶段策略 + */ + virtual std::shared_ptr getStageStrategy() const { + return pet_stage_strategy; + } + /** + * 设置阶段策略 + * @param strategy 阶段策略 + */ + virtual void setStageStrategy(std::unique_ptr strategy) { + pet_stage_strategy = std::move(strategy); + } + /** + * 获取动作策略 + * @return 动作策略 + */ + virtual std::shared_ptr getActionStrategy() const { + return pet_action_strategy; + } + /** + * 设置动作策略 + * @param strategy 动作策略 + */ + virtual void setActionStrategy(std::unique_ptr strategy) { + pet_action_strategy = std::move(strategy); + } + /** + * 获取当前阶段 + * @return 当前阶段 + */ + virtual PetStageType getCurrentStage() const { + return pet_stage_strategy->getCurrentStageType(); + } + /** + * 获取当前动作 + * @return 当前动作 + */ + virtual PetActionType getCurrentAction() const { + return pet_action_strategy->getCurrentActionType(); + } +public: + /// pet_stage_strategy; + /// pet_action_strategy; + ///> observers; +}; + diff --git a/Bionic_Core/PetBaseClass/PetDao.cpp b/Bionic_Core/PetBaseClass/PetDao.cpp new file mode 100644 index 0000000..b7caad8 --- /dev/null +++ b/Bionic_Core/PetBaseClass/PetDao.cpp @@ -0,0 +1,306 @@ +// +// Created by misaki on 2025/9/14. +// + +#include "PetDao.h" +#include +#include + +using namespace PetEnumConverter; + +// PetEnumConverter 实现 +std::string PetEnumConverter::stageTypeToString(PetStageType stage) { + switch (stage) { +#define X(name, desc) case PetStageType::name: return #name; + PET_STAGE_TYPES +#undef X + default: return "UNKNOWN"; + } +} + +PetStageType PetEnumConverter::stringToStageType(const std::string& str) { +#define X(name, desc) if (str == #name) return PetStageType::name; + PET_STAGE_TYPES +#undef X + return PetStageType::PET_STAGE_YOUNG; // 默认值 +} + +std::string PetEnumConverter::actionTypeToString(PetActionType action) { + switch (action) { +#define X(name, desc) case PetActionType::name: return #name; + PET_ACTION_TYPES +#undef X + default: return "UNKNOWN"; + } +} + +PetActionType PetEnumConverter::stringToActionType(const std::string& str) { +#define X(name, desc) if (str == #name) return PetActionType::name; + PET_ACTION_TYPES +#undef X + return PetActionType::PET_ACTION_SLEEP; // 默认值 +} + +// PetDAO 实现 +PetDAO::PetDAO(SDFileManager* fileManager) : fileManager(fileManager) { + // 确保宠物数据目录存在 + fileManager->mkdirCommand( PET_DATA_DIR); +} + +bool PetDAO::savePet(const std::shared_ptr& pet, const std::string& filename) { + // 转换为JSON + cJSON* json = petToJson(pet); + if (!json) { + return false; + } + + // 转换为字符串 + char* jsonStr = cJSON_PrintUnformatted(json); + std::string fullPath = std::string(PET_DATA_DIR) + "/" + filename; + + // 保存到文件 + bool success = fileManager->writeFileSync(fullPath.c_str(), jsonStr); + + // 清理资源 + free(jsonStr); + cJSON_Delete(json); + + return success; +} + +std::shared_ptr PetDAO::loadPet(const std::string& filename) { + std::string fullPath = std::string(PET_DATA_DIR) + "/" + filename; + + // 读取文件内容 + std::string content = fileManager->readFileSync(fullPath.c_str()); + if (content.empty()) { + return nullptr; + } + + // 解析JSON + cJSON* json = cJSON_Parse(content.c_str()); + if (!json) { + return nullptr; + } + + // 创建宠物对象 + auto pet = petFromJson(json); + + // 清理资源 + cJSON_Delete(json); + + return pet; +} + +std::vector PetDAO::listPetFiles() { + return fileManager->listFilesSync(PET_DATA_DIR, ".json"); +} + +bool PetDAO::deletePetFile(const std::string& filename) { + std::string fullPath = std::string(PET_DATA_DIR) + "/" + filename; + return fileManager->rmCommand(fullPath.c_str(), false); +} + +cJSON* PetDAO::petToJson(const std::shared_ptr& pet) { + cJSON* json = cJSON_CreateObject(); + if (!json) return nullptr; + + // 添加基本信息 + cJSON_AddStringToObject(json, "name", pet->pet_info.pet_name.c_str()); + cJSON_AddNumberToObject(json, "hp", pet->pet_info.pet_hp); + cJSON_AddNumberToObject(json, "density", pet->pet_info.pet_density); + cJSON_AddStringToObject(json, "identity", pet->pet_info.pet_identity.c_str()); + + // 添加阶段策略 + cJSON* stageJson = stageStrategyToJson(pet->pet_stage_strategy); + if (stageJson) { + cJSON_AddItemToObject(json, "stage_strategy", stageJson); + } + + // 添加动作策略 + cJSON* actionJson = actionStrategyToJson(pet->pet_action_strategy); + if (actionJson) { + cJSON_AddItemToObject(json, "action_strategy", actionJson); + } + + return json; +} + +std::shared_ptr PetDAO::petFromJson(cJSON* json) { + if (!json) return nullptr; + + // 创建宠物基本信息 + PetBaseInfo info; + cJSON* nameItem = cJSON_GetObjectItemCaseSensitive(json, "name"); + if (cJSON_IsString(nameItem)) { + info.pet_name = nameItem->valuestring; + } + + cJSON* hpItem = cJSON_GetObjectItemCaseSensitive(json, "hp"); + if (cJSON_IsNumber(hpItem)) { + info.pet_hp = hpItem->valueint; + } + + cJSON* densityItem = cJSON_GetObjectItemCaseSensitive(json, "density"); + if (cJSON_IsNumber(densityItem)) { + info.pet_density = densityItem->valueint; + } + + cJSON* identityItem = cJSON_GetObjectItemCaseSensitive(json, "identity"); + if (cJSON_IsString(identityItem)) { + info.pet_identity = identityItem->valuestring; + } + + // 创建阶段策略 + cJSON* stageJson = cJSON_GetObjectItemCaseSensitive(json, "stage_strategy"); + auto stageStrategy = stageStrategyFromJson(stageJson); + + // 创建动作策略 + cJSON* actionJson = cJSON_GetObjectItemCaseSensitive(json, "action_strategy"); + auto actionStrategy = actionStrategyFromJson(actionJson); + + // 创建宠物对象 + return std::make_shared(info, stageStrategy, actionStrategy); +} + +cJSON* PetDAO::stageStrategyToJson(const std::shared_ptr& strategy) { + if (!strategy) return nullptr; + + cJSON* json = cJSON_CreateObject(); + if (!json) return nullptr; + + // 添加当前阶段 + cJSON_AddStringToObject(json, "current_stage", + stageTypeToString(strategy->getCurrentStageType()).c_str()); + + // 添加阶段模型映射 + cJSON* modelMap = cJSON_CreateObject(); + for (const auto& pair : strategy->getStageModelMap()) { + cJSON_AddStringToObject(modelMap, + stageTypeToString(pair.first).c_str(), + pair.second.c_str()); + } + cJSON_AddItemToObject(json, "stage_model_map", modelMap); + + // 添加阶段音频映射 + cJSON* audioMap = cJSON_CreateObject(); + for (const auto& pair : strategy->getStageAudioMap()) { + cJSON_AddStringToObject(audioMap, + stageTypeToString(pair.first).c_str(), + pair.second.c_str()); + } + cJSON_AddItemToObject(json, "stage_audio_map", audioMap); + + return json; +} + +std::shared_ptr PetDAO::stageStrategyFromJson(cJSON* json) { + if (!json) return nullptr; + + auto strategy = std::make_shared(); + + // 获取当前阶段 + cJSON* currentStageItem = cJSON_GetObjectItemCaseSensitive(json, "current_stage"); + if (cJSON_IsString(currentStageItem)) { + strategy->current_stage = stringToStageType(currentStageItem->valuestring); + } + + // 获取阶段模型映射 + cJSON* modelMapItem = cJSON_GetObjectItemCaseSensitive(json, "stage_model_map"); + if (cJSON_IsObject(modelMapItem)) { + cJSON* child = modelMapItem->child; + while (child) { + if (cJSON_IsString(child)) { + PetStageType stage = stringToStageType(child->string); + strategy->stage_model_map[stage] = child->valuestring; + } + child = child->next; + } + } + + // 获取阶段音频映射 + cJSON* audioMapItem = cJSON_GetObjectItemCaseSensitive(json, "stage_audio_map"); + if (cJSON_IsObject(audioMapItem)) { + cJSON* child = audioMapItem->child; + while (child) { + if (cJSON_IsString(child)) { + PetStageType stage = stringToStageType(child->string); + strategy->stage_audio_map[stage] = child->valuestring; + } + child = child->next; + } + } + + return strategy; +} + +cJSON* PetDAO::actionStrategyToJson(const std::shared_ptr& strategy) { + if (!strategy) return nullptr; + + cJSON* json = cJSON_CreateObject(); + if (!json) return nullptr; + + // 添加当前动作 + cJSON_AddStringToObject(json, "current_action", + actionTypeToString(strategy->getCurrentActionType()).c_str()); + + // 添加动作模型映射 + cJSON* modelMap = cJSON_CreateObject(); + for (const auto& pair : strategy->getActionModelMap()) { + cJSON_AddStringToObject(modelMap, + actionTypeToString(pair.first).c_str(), + pair.second.c_str()); + } + cJSON_AddItemToObject(json, "action_model_map", modelMap); + + // 添加动作音频映射 + cJSON* audioMap = cJSON_CreateObject(); + for (const auto& pair : strategy->getActionAudioMap()) { + cJSON_AddStringToObject(audioMap, + actionTypeToString(pair.first).c_str(), + pair.second.c_str()); + } + cJSON_AddItemToObject(json, "action_audio_map", audioMap); + + return json; +} + +std::shared_ptr PetDAO::actionStrategyFromJson(cJSON* json) { + if (!json) return nullptr; + + auto strategy = std::make_shared(); + + // 获取当前动作 + cJSON* currentActionItem = cJSON_GetObjectItemCaseSensitive(json, "current_action"); + if (cJSON_IsString(currentActionItem)) { + strategy->current_action = stringToActionType(currentActionItem->valuestring); + } + + // 获取动作模型映射 + cJSON* modelMapItem = cJSON_GetObjectItemCaseSensitive(json, "action_model_map"); + if (cJSON_IsObject(modelMapItem)) { + cJSON* child = modelMapItem->child; + while (child) { + if (cJSON_IsString(child)) { + PetActionType action = stringToActionType(child->string); + strategy->action_model_map[action] = child->valuestring; + } + child = child->next; + } + } + + // 获取动作音频映射 + cJSON* audioMapItem = cJSON_GetObjectItemCaseSensitive(json, "action_audio_map"); + if (cJSON_IsObject(audioMapItem)) { + cJSON* child = audioMapItem->child; + while (child) { + if (cJSON_IsString(child)) { + PetActionType action = stringToActionType(child->string); + strategy->action_audio_map[action] = child->valuestring; + } + child = child->next; + } + } + + return strategy; +} \ No newline at end of file diff --git a/Bionic_Core/PetBaseClass/PetDao.h b/Bionic_Core/PetBaseClass/PetDao.h new file mode 100644 index 0000000..6710585 --- /dev/null +++ b/Bionic_Core/PetBaseClass/PetDao.h @@ -0,0 +1,65 @@ +// +// Created by misaki on 2025/9/14. +// +#pragma once + +#include "PetBaseClass.h" +#include "SDFileManager.h" +#include "cJSON.h" +#include +#include + +// 辅助函数:枚举类型与字符串的转换 +namespace PetEnumConverter { + // PetStageType 转换 + std::string stageTypeToString(PetStageType stage); + PetStageType stringToStageType(const std::string& str); + + // PetActionType 转换 + std::string actionTypeToString(PetActionType action); + PetActionType stringToActionType(const std::string& str); +} + +// PetDAO 类 - 负责宠物的数据持久化 +class PetDAO { +public: + // 构造函数,需要SDFileManager实例 + explicit PetDAO(SDFileManager* fileManager); + + // 保存宠物数据到文件 + bool savePet(const std::shared_ptr& pet, const std::string& filename); + + // 从文件加载宠物数据 + std::shared_ptr loadPet(const std::string& filename); + + // 获取所有保存的宠物文件列表 + std::vector listPetFiles(); + + // 删除宠物文件 + bool deletePetFile(const std::string& filename); + +private: + // 将宠物数据转换为JSON对象 + cJSON* petToJson(const std::shared_ptr& pet); + + // 从JSON对象创建宠物 + std::shared_ptr petFromJson(cJSON* json); + + // 将阶段策略转换为JSON对象 + cJSON* stageStrategyToJson(const std::shared_ptr& strategy); + + // 从JSON对象创建阶段策略 + std::shared_ptr stageStrategyFromJson(cJSON* json); + + // 将动作策略转换为JSON对象 + cJSON* actionStrategyToJson(const std::shared_ptr& strategy); + + // 从JSON对象创建动作策略 + std::shared_ptr actionStrategyFromJson(cJSON* json); + + // 文件管理器实例 + SDFileManager* fileManager; + + // 宠物数据存储目录 + static constexpr const char* PET_DATA_DIR = "/sdcard/pet_data"; +}; \ No newline at end of file diff --git a/Bionic_Core/PetBaseClass/PetInterface.h b/Bionic_Core/PetBaseClass/PetInterface.h index dfc40a2..24100be 100644 --- a/Bionic_Core/PetBaseClass/PetInterface.h +++ b/Bionic_Core/PetBaseClass/PetInterface.h @@ -1,54 +1,389 @@ // // Created by misaki on 2025/9/8. // +/** + * 需要实现的最终效果 + 1. 可以选择多种宠物,如雪豹,卡皮巴拉,守宫,每种宠物有独特音频和动作,通过联网更新切换。 + 2. 每种宠物有可以编辑的身份设定,类似小智AI的身份 + 3. 宠物三个阶段,幼年,青年,成年,阶段切换载入新模型即可。 + 4. 语音互动可以实现喂食,开心,生气,沮丧几种动作 + 5. 宠物具有生命值hp和亲密度,当发生喂食等动作会对生命值和亲密度产生影响,亲密度达到一定程度宠物可以进化到新的阶段。 + 6. 宠物的各种信息是可以保存和读取的。 + 分析一下具体设计思路: + 1. 首先抽象出所有的宠物的共同点为一个基类,包括:名称,生命值,亲密度,身份设定 + 对于阶段,动作,不同的宠物之间存在不同的实现差异,可以为其抽象出不同的接口,组合到抽象出来的宠物基类当中 + 这样当创建新的宠物时,只需要实现对应的接口即可(在此使用策略模式),而在创建不同的宠物的过程中,又使用到了抽象工厂模式 + 对于音频,由于阶段变化或是产生动作,都会触发响应的音频,因此音频需要作为一个观察者,当宠物产生阶段变化或是产生动作,就会触发相应的音频播放 + 2. 接着要考虑到与宠物相关的一些事件,例如外部通知网络更新,语音输入互动,喂食这些事件,需要及时做出响应,在此使用依赖注入来解决这一类事件处理问题 + 向外部类当中组合一个依赖注入类,将宠物类当中的回调函数注入到其中,当发生某些事件时,就会触发相应的回调函数,并执行相应的操作 + 这样在增加新的事件时,就不需要频繁修改外部事件类,只需要新增新的依赖注入项(如果C++17支持反射的话还有更好的实现方法) + 3. 再者是对于宠物的信息保存与读取,在此处增加一个DAO层,专门用于数据持久化,信息以json的方式存储与读取 + 既然存在了宠物的数据信息,那么就需要在DAO层之上增加一个可以从json数据中得到新的宠物对象的方法或者是类,以此组合出所需的宠物对象 + 4. 还需要考虑到一些极端事件的情况,例如设备断电,用户强制关机,网络中断等,最好的处理方式就是及时做好数据持久化和数据备份 + 设置bak备份防止在写文件的时候断电导致写文件错误 + */ #pragma once #include #include -#include +#include +#include +#include +#include +#include +// 前向声明主题 +class PetSubject; -// 资源句柄,防止裸指针到处飞 -template -using Handle = std::shared_ptr; /// 宠物句柄 +// 某个宠物不同阶段的抽象接口 宠物的不同阶段对应于不同的模型文件路径 +// 如果需要添加新的阶段,要在PetStageType当中增加新的阶段,并将新的类继承自PetStage类 +// 由于使用的是C++17,并不支持反射,只能通过宏定义统一管理,以此实现松耦合和开闭原则 +#define PET_STAGE_TYPES \ + X(PET_STAGE_YOUNG, "幼年") \ + X(PET_STAGE_ADULT, "青年") \ + X(PET_STAGE_OLD, "成年") +enum class PetStageType { +#define X(name, desc) name, + PET_STAGE_TYPES +#undef X +}; +// 某个宠物所有阶段的接口集合,该接口集合会被组合到抽象出来的宠物基类当中,该接口还需要抽象出宠物不同阶段可能具备的行为 +class PetStageStrategy { +public: + virtual ~PetStageStrategy() = default; + // 定义阶段顺序的静态常量 + static const std::vector& getStageOrder() { + static const std::vector stageOrder = []() { + std::vector order; +#define X(name, desc) order.push_back(PetStageType::name); + PET_STAGE_TYPES +#undef X + return order; + }(); + return stageOrder; + } + /** + * 添加阶段及其模型路径 + * @param stage 阶段类型 + * @param model_path 模型路径 + */ + virtual void addStage(PetStageType stage, const std::string& model_path) { + stage_model_map[stage] = model_path; + // 如果这是第一个阶段,设置为当前阶段 + if (stage_model_map.size() == 1) { + current_stage = stage; + } + } + /** + * 添加阶段音频映射 + * @param stage 阶段类型 + * @param audio_path 音频文件路径 + */ + virtual void addStageAudio(PetStageType stage, const std::string& audio_path) { + stage_audio_map[stage] = audio_path; + } + /** + * 获取阶段模型映射表 + * @return 阶段到模型路径的映射表 + */ + virtual const std::unordered_map& getStageModelMap() const { + return stage_model_map; + } + /** + * 获取阶段音频映射表 + * @return 阶段到音频路径的映射表 + */ + virtual const std::unordered_map& getStageAudioMap() const { + return stage_audio_map; + } + /** + * 获取特定阶段的模型路径 + * @param stage 阶段类型 + * @return 模型路径,如果不存在则返回空字符串 + */ + virtual std::string getStageModelPath(PetStageType stage) const { + auto it = stage_model_map.find(stage); + if (it != stage_model_map.end()) { + return it->second; + } + return ""; + } + /** + * 获取特定阶段的音频路径 + * @param stage 阶段类型 + * @return 音频路径,如果不存在则返回空字符串 + */ + virtual std::string getStageAudioPath(PetStageType stage) const { + auto it = stage_audio_map.find(stage); + if (it != stage_audio_map.end()) { + return it->second; + } + return ""; + } + /** + * 切换到指定阶段 + * @param newStage 要切换到的阶段类型 + * @return 是否成功切换 + */ + virtual bool switchToStage(PetStageType newStage) { + if (stage_model_map.find(newStage) != stage_model_map.end()) { + current_stage = newStage; + return true; + } + return false; + } + /** + * 进化到下一阶段(基于当前阶段) + * @return 是否成功进化 + */ + virtual bool evolveToNextStage() { + const auto& stageOrder = getStageOrder(); + auto it = std::find(stageOrder.begin(), stageOrder.end(), current_stage); -// 前向声明 -class IPet; /// 宠物接口 -using IPetPtr = Handle; /// 宠物句柄 - -// 事件类型 -enum class Emotion { - Feed, /// 喂食 - Happy, /// 高兴 - Angry, /// 生气 - Upset /// 沮丧 + if (it == stageOrder.end() || it == stageOrder.end() - 1) { + return false; + } + // 查找下一个可用阶段 + for (auto iter = it + 1; iter != stageOrder.end(); ++iter) { + if (stage_model_map.find(*iter) != stage_model_map.end()) { + current_stage = *iter; + return true; + } + } + return false; + } + /** + * 获取当前阶段 + * @return 当前阶段类型 + */ + virtual PetStageType getCurrentStageType() const { + return current_stage; + } + /** + * 获取当前阶段的模型路径 + * @return 当前阶段模型路径 + */ + virtual std::string getCurrentStageModelPath() const { + return getStageModelPath(current_stage); + } + /** + * 获取所有可用阶段 + * @return 所有阶段的列表 + */ + virtual std::list getAllStages() const { + std::list stages; + for (const auto& stage : stage_model_map) { + stages.push_back(stage.first); + } + return stages; + } + /** + * 移除阶段 + * @param stage 要移除的阶段类型 + */ + virtual void removeStage(PetStageType stage) { + if (current_stage == stage && stage_model_map.size() > 1) { + // 找到另一个阶段作为当前阶段 + for (const auto& s : stage_model_map) { + if (s.first != stage) { + current_stage = s.first; + break; + } + } + } + stage_model_map.erase(stage); + stage_audio_map.erase(stage); + } +public: + /// stage_model_map; + /// stage_audio_map; + ///& getActionOrder() { + static const std::vector actionOrder = []() { + std::vector order; +#define X(name, desc) order.push_back(PetActionType::name); + PET_ACTION_TYPES +#undef X + return order; + }(); + return actionOrder; + } + /** + * 添加动作及其模型路径 + * @param action 动作类型 + * @param model_path 模型路径 + */ + virtual void addAction(PetActionType action, const std::string& model_path) { + action_model_map[action] = model_path; + } + /** + * 添加动作音频映射 + * @param action 动作类型 + * @param audio_path 音频文件路径 + */ + virtual void addActionAudio(PetActionType action, const std::string& audio_path) { + action_audio_map[action] = audio_path; + } + /** + * 获取动作模型映射表 + * @return 动作到模型路径的映射表 + */ + virtual const std::unordered_map& getActionModelMap() const { + return action_model_map; + } + /** + * 获取动作音频映射表 + * @return 动作到音频路径的映射表 + */ + virtual const std::unordered_map& getActionAudioMap() const { + return action_audio_map; + } + /** + * 获取特定动作的模型路径 + * @param action 动作类型 + * @return 模型路径,如果不存在则返回空字符串 + */ + virtual std::string getActionModelPath(PetActionType action) const { + auto it = action_model_map.find(action); + if (it != action_model_map.end()) { + return it->second; + } + return ""; + } + /** + * 获取特定动作的音频路径 + * @param action 动作类型 + * @return 音频路径,如果不存在则返回空字符串 + */ + virtual std::string getActionAudioPath(PetActionType action) const { + auto it = action_audio_map.find(action); + if (it != action_audio_map.end()) { + return it->second; + } + return ""; + } + /** + * 执行指定动作 + * @param action 要执行的动作类型 + * @return 是否成功执行 + */ + virtual bool performAction(PetActionType action) { + if (action_model_map.find(action) != action_model_map.end()) { + current_action = action; + return true; + } + return false; + } + /** + * 获取当前动作 + * @return 当前动作类型 + */ + virtual PetActionType getCurrentActionType() const { + return current_action; + } + /** + * 获取当前动作的模型路径 + * @return 当前动作模型路径 + */ + virtual std::string getCurrentActionModelPath() const { + return getActionModelPath(current_action); + } + /** + * 获取所有可用动作 + * @return 所有动作的列表 + */ + virtual std::list getAllActions() const { + std::list actions; + for (const auto& action : action_model_map) { + actions.push_back(action.first); + } + return actions; + } + /** + * 获取动作的描述信息 + * @param action 动作类型 + * @return 动作描述 + */ + virtual std::string getActionDescription(PetActionType action) const { + switch (action) { +#define X(name, desc) case PetActionType::name: return desc; + PET_ACTION_TYPES +#undef X + default: return "未知动作"; + } + } + /** + * 获取当前动作的描述信息 + * @return 当前动作描述 + */ + virtual std::string getCurrentActionDescription() const { + return getActionDescription(current_action); + } + /** + * 移除动作 + * @param action 要移除的动作类型 + */ + virtual void removeAction(PetActionType action) { + action_model_map.erase(action); + action_audio_map.erase(action); + if (current_action == action) { + current_action = PetActionType::PET_ACTION_SLEEP; // 默认动作 + } + } +public: + /// action_model_map; + /// action_audio_map; + /// observer) = 0; + // 移除观察者 + virtual void removeObserver(std::shared_ptr observer) = 0; + // 通知动作 + virtual void notifyAction(PetActionType action) = 0; + // 通知阶段 + virtual void notifyStageChange(PetStageType oldStage, PetStageType newStage) = 0; }; - -// 动物元数据(只读) -struct AnimalManifest { - std::string id; // "snow_leopard" /// ID - std::string displayName; /// 显示名称 - Persona persona; /// 身份设定 - std::unordered_map modelPath; /// 阶段→模型 - std::unordered_map audioPath; /// 阶段→音频 - std::string version; /// 版本 -}; \ No newline at end of file diff --git a/Bionic_Core/PetBaseClass/PetObserver.cpp b/Bionic_Core/PetBaseClass/PetObserver.cpp new file mode 100644 index 0000000..c89a540 --- /dev/null +++ b/Bionic_Core/PetBaseClass/PetObserver.cpp @@ -0,0 +1,4 @@ +// +// Created by misaki on 2025/9/14. +// +#include "PetObserver.h" \ No newline at end of file diff --git a/Bionic_Core/PetBaseClass/PetObserver.h b/Bionic_Core/PetBaseClass/PetObserver.h new file mode 100644 index 0000000..06557f3 --- /dev/null +++ b/Bionic_Core/PetBaseClass/PetObserver.h @@ -0,0 +1,209 @@ +// +// Created by misaki on 2025/9/14. +// +#pragma once +#include "PetInterface.h" +// 宠物音频播放观察者类,继承自宠物观察者 +class PetAudioStrategy : public PetObserver, public std::enable_shared_from_this { +public: + using AudioCallback = std::function; + /** + * 构造时候就将“事件→音频”两张表填好 + * @param actionAudios 动作→音频 + * @param stageAudios 阶段→音频 + */ + explicit PetAudioStrategy( + std::unordered_map actionAudios = {}, + std::unordered_map stageAudios = {}) + : action_audio(std::move(actionAudios)), + stage_audio(std::move(stageAudios)) + {} + /** + * 构造函数,需要传入阶段策略和动作策略,从其中获取所需的类型→模型路径映射 + * @param stageStrategy 阶段策略 + * @param actionStrategy 动作策略 + */ + explicit PetAudioStrategy( + const std::shared_ptr& stageStrategy, + const std::shared_ptr& actionStrategy) { + stage_audio = stageStrategy->getStageAudioMap(); // 获取阶段→音频map + action_audio = actionStrategy->getActionAudioMap(); // 获取动作→音频map + } + + ~PetAudioStrategy() override { + // 自动取消注册 + if (auto subject = pet_subject.lock()) { + subject->removeObserver(shared_from_this()); + } + } + /** + * 设置音频播放回调函数,注入真正的“播放函数”,由外部(主程序 / 平台层)提供 + * @param callback 音频播放回调,接受音频文件路径 + */ + virtual void setAudioCallback(AudioCallback callback) { + audio_callback = std::move(callback); + } + /** + * 注册到 Subject(一般由 Pet 基类实现) + * @param subject 主题 + */ + void subscribe(std::shared_ptr subject) + { + if (auto old = pet_subject.lock()) { + old->removeObserver(shared_from_this()); + } + pet_subject = subject; + if (subject) { + subject->addObserver(shared_from_this()); + } + } + /** + * 宠物动作时触发[Observer 接口实现] + * @param action 动作 + */ + void onPetAction(const PetActionType action) override + { + if (!audio_callback) return; + auto it = action_audio.find(action); + if (it != action_audio.end()) { + audio_callback(it->second); // 播放对应音频 + } + } + /** + * 宠物阶段时触发[Observer 接口实现] + * @param oldStage 旧阶段 + * @param newStage 新阶段 + */ + void onPetStageChange(PetStageType oldStage, const PetStageType newStage) override + { + if (!audio_callback) return; + auto it = stage_audio.find(newStage); // 注意:播“新阶段”的音频 + if (it != stage_audio.end()) { + audio_callback(it->second); + } + } +private: + /// pet_subject; + /// action_audio; + std::unordered_map stage_audio; +}; + +// 渲染器观察者类,继承自PetObserver +class PetRendererStrategy : public PetObserver, public std::enable_shared_from_this { +public: + using RenderCallback = std::function; + /** + * 构造函数,可以传入动作到模型路径和阶段到模型路径的映射表 + * @param actionModels 动作→模型路径映射 + * @param stageModels 阶段→模型路径映射 + */ + explicit PetRendererStrategy( + std::unordered_map actionModels = {}, + std::unordered_map stageModels = {}) + : action_models(std::move(actionModels)), + stage_models(std::move(stageModels)) + {} + /** + * 构造函数,需要传入阶段策略和动作策略,从其中获取所需的类型→模型路径映射 + * @param stageStrategy 阶段策略 + * @param actionStrategy 动作策略 + */ + explicit PetRendererStrategy( + const std::shared_ptr& stageStrategy, + const std::shared_ptr& actionStrategy) { + stage_models = stageStrategy->getStageModelMap(); + action_models = actionStrategy->getActionModelMap(); + } + ~PetRendererStrategy() override { + // 自动取消注册 + if (auto subject = pet_subject.lock()) { + subject->removeObserver(shared_from_this()); + } + } + /** + * 设置渲染回调函数,注入真正的"渲染函数",由外部(主程序/平台层)提供 + * @param callback 渲染回调,接受模型文件路径 + */ + virtual void setRenderCallback(RenderCallback callback) { + render_callback = std::move(callback); + } + /** + * 注册到Subject(一般由Pet基类实现) + * @param subject 主题 + */ + void subscribe(std::shared_ptr subject) { + if (auto old = pet_subject.lock()) { + old->removeObserver(shared_from_this()); + } + pet_subject = subject; + if (subject) { + subject->addObserver(shared_from_this()); + } + } + /** + * 宠物动作时触发[Observer接口实现] + * @param action 动作类型 + */ + void onPetAction(PetActionType action) override { + if (!render_callback) return; + auto it = action_models.find(action); + if (it != action_models.end()) { + render_callback(it->second); // 渲染对应动作模型 + } + } + /** + * 宠物阶段变化时触发[Observer接口实现] + * @param oldStage 旧阶段 + * @param newStage 新阶段 + */ + void onPetStageChange(PetStageType oldStage, PetStageType newStage) override { + if (!render_callback) return; + auto it = stage_models.find(newStage); // 注意:渲染"新阶段"的模型 + if (it != stage_models.end()) { + render_callback(it->second); + } + } + /** + * 添加动作到模型的映射 + * @param action 动作类型 + * @param modelPath 模型路径 + */ + virtual void addActionModel(PetActionType action, const std::string& modelPath) { + action_models[action] = modelPath; + } + /** + * 添加阶段到模型的映射 + * @param stage 阶段类型 + * @param modelPath 模型路径 + */ + virtual void addStageModel(PetStageType stage, const std::string& modelPath) { + stage_models[stage] = modelPath; + } + /** + * 移除动作模型映射 + * @param action 动作类型 + */ + virtual void removeActionModel(PetActionType action) { + action_models.erase(action); + } + /** + * 移除阶段模型映射 + * @param stage 阶段类型 + */ + virtual void removeStageModel(PetStageType stage) { + stage_models.erase(stage); + } +private: + /// pet_subject; + /// action_models; + /// stage_models; +}; \ No newline at end of file diff --git a/Bionic_Core/ToolsClass/ThreadManager/ThreadManager.h b/Bionic_Core/ToolsClass/ThreadManager/ThreadManager.h index 56f1564..adfbc8d 100644 --- a/Bionic_Core/ToolsClass/ThreadManager/ThreadManager.h +++ b/Bionic_Core/ToolsClass/ThreadManager/ThreadManager.h @@ -135,7 +135,7 @@ public: /** * @brief 打印系统内存信息 */ - static void print_sys_memory(void) + static void print_sys_memory() { size_t internal = heap_caps_get_free_size(MALLOC_CAP_INTERNAL); size_t spiram = heap_caps_get_free_size(MALLOC_CAP_SPIRAM); @@ -144,9 +144,9 @@ public: - static void stats_task(void) + static void stats_task() { - char stats_buf[2*1024]; + char stats_buf[2 * 1024]; // 存储任务列表和绑核信息,占用 2KB 栈空间,调用时需注意 /* 任务列表 + 绑核信息 */ printf("\n-------- vTaskList --------\n"); vTaskList(stats_buf); diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 1affd53..d142e96 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -30,6 +30,8 @@ idf_component_register(SRCS "Bionic_sphere.c" # 业务代码(使用Cpp编写) "../Bionic_Core/PetBaseClass/PetBaseClass.cpp" # 宠物基类库 "../Bionic_Core/PetBaseClass/PetInterface.cpp" # 宠物接口层 + "../Bionic_Core/PetBaseClass/PetObserver.cpp" # 宠物观察者库 + "../Bionic_Core/PetBaseClass/PetDao.cpp" # 宠物数据访问层 "../Bionic_Core/OTAClass/OTAClass.cpp" # OTA类库 "../Bionic_Core/CommClass/CommClass.cpp" # 通信类库 "../Bionic_Core/ToolsClass/ToolsClass.cpp" # 工具类库 diff --git a/项目开发日志.md b/项目开发日志.md index c033bb8..c9bf6d5 100644 --- a/项目开发日志.md +++ b/项目开发日志.md @@ -216,4 +216,11 @@ - [x] 3. 增加了一些CPU资源占用的日志打印函数,运行在主线程当中 -- [x] 4. 完善了底层通信类的封装,基于websocket,尚未测试 \ No newline at end of file +- [x] 4. 完善了底层通信类的封装,基于websocket,尚未测试 + +#### Day14 2025.9.15 +##### 主要目标:完成具体业务开发&各种优化 +实际完成任务: +- [x] 1. 历时两天,完整且完美的设计了宠物类,使用到了多种设计模式,完成了低耦合,高内聚的完美代码,测试也完美通过。 + +- [x] 2. 顺便完善了底层通信类的封装,基于websocket,基准测试通过,但存在一点很小的线程bug,似乎是来自于esp32 idf底层的问题,待解决