1. 历时两天,完整且完美的设计了宠物类,使用到了多种设计模式,完成了低耦合,高内聚的完美代码,测试也完美通过。
2. 顺便完善了底层通信类的封装,基于websocket,基准测试通过,但存在一点很小的线程bug,似乎是来自于esp32 idf底层的问题,待解决
This commit is contained in:
@@ -26,8 +26,6 @@ WebSocketManager::WebSocketManager()
|
||||
|
||||
{
|
||||
|
||||
// 初始化统计信息
|
||||
stats = {0, 0, 0, 0};
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -4,8 +4,118 @@
|
||||
#include "CppHandle.h"
|
||||
|
||||
#include "OTAClass.h"
|
||||
#include "PetBaseClass.h"
|
||||
#include "PetDao.h"
|
||||
#include <iostream>
|
||||
|
||||
void testPetSystem() {
|
||||
std::cout << "Test point1" << std::endl;
|
||||
// 创建阶段策略
|
||||
auto stageStrategy = std::make_unique<PetStageStrategy>();
|
||||
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<PetActionStrategy>();
|
||||
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<PetBase>(info, std::move(stageStrategy), std::move(actionStrategy));
|
||||
std::cout << "Test point3" << std::endl;
|
||||
|
||||
// 创建音频观察者
|
||||
auto audioStrategy = std::make_shared<PetAudioStrategy>();
|
||||
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<PetRendererStrategy>(
|
||||
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<int>(pet->getCurrentStage()) << std::endl;
|
||||
std::cout << "Current action: " << static_cast<int>(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<int>(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();
|
||||
}
|
||||
|
||||
@@ -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 <string>
|
||||
@@ -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<WifiConnectors>(
|
||||
// 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<WifiConnectors>(
|
||||
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";
|
||||
|
||||
@@ -2,5 +2,214 @@
|
||||
// Created by misaki on 2025/9/2.
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "PetInterface.h"
|
||||
#include "PetObserver.h"
|
||||
// 宠物基类
|
||||
// 宠物基本信息
|
||||
struct PetBaseInfo {
|
||||
///<! 宠物名称
|
||||
std::string pet_name;
|
||||
///<! 宠物生命值(1~100)
|
||||
int pet_hp;
|
||||
///<! 宠物亲密度(1~150) 每过50为一个阶段
|
||||
int pet_density;
|
||||
///<! 宠物身份
|
||||
std::string pet_identity;
|
||||
};
|
||||
// 宠物阶段策略,继承自主题
|
||||
class PetBase : public PetSubject, public std::enable_shared_from_this<PetBase> {
|
||||
public:
|
||||
// 构造函数
|
||||
PetBase() = default;
|
||||
// 带参数的构造函数
|
||||
PetBase(PetBaseInfo info,
|
||||
std::shared_ptr<PetStageStrategy> stageStrategy,
|
||||
std::shared_ptr<PetActionStrategy> 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<PetObserver> observer) override {
|
||||
observers.push_back(observer);
|
||||
}
|
||||
/**
|
||||
* 移除观察者
|
||||
* @param observer 观察者
|
||||
*/
|
||||
void removeObserver(std::shared_ptr<PetObserver> 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<PetStageStrategy> getStageStrategy() const {
|
||||
return pet_stage_strategy;
|
||||
}
|
||||
/**
|
||||
* 设置阶段策略
|
||||
* @param strategy 阶段策略
|
||||
*/
|
||||
virtual void setStageStrategy(std::unique_ptr<PetStageStrategy> strategy) {
|
||||
pet_stage_strategy = std::move(strategy);
|
||||
}
|
||||
/**
|
||||
* 获取动作策略
|
||||
* @return 动作策略
|
||||
*/
|
||||
virtual std::shared_ptr<PetActionStrategy> getActionStrategy() const {
|
||||
return pet_action_strategy;
|
||||
}
|
||||
/**
|
||||
* 设置动作策略
|
||||
* @param strategy 动作策略
|
||||
*/
|
||||
virtual void setActionStrategy(std::unique_ptr<PetActionStrategy> 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:
|
||||
///<! 宠物信息
|
||||
PetBaseInfo pet_info;
|
||||
///<! 宠物阶段策略
|
||||
std::shared_ptr<PetStageStrategy> pet_stage_strategy;
|
||||
///<! 宠物动作策略
|
||||
std::shared_ptr<PetActionStrategy> pet_action_strategy;
|
||||
///<! 观察者列表
|
||||
std::list<std::shared_ptr<PetObserver>> observers;
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,306 @@
|
||||
//
|
||||
// Created by misaki on 2025/9/14.
|
||||
//
|
||||
|
||||
#include "PetDao.h"
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
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<PetBase>& 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<PetBase> 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<std::string> 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<PetBase>& 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<PetBase> 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<PetBase>(info, stageStrategy, actionStrategy);
|
||||
}
|
||||
|
||||
cJSON* PetDAO::stageStrategyToJson(const std::shared_ptr<PetStageStrategy>& 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<PetStageStrategy> PetDAO::stageStrategyFromJson(cJSON* json) {
|
||||
if (!json) return nullptr;
|
||||
|
||||
auto strategy = std::make_shared<PetStageStrategy>();
|
||||
|
||||
// 获取当前阶段
|
||||
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<PetActionStrategy>& 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<PetActionStrategy> PetDAO::actionStrategyFromJson(cJSON* json) {
|
||||
if (!json) return nullptr;
|
||||
|
||||
auto strategy = std::make_shared<PetActionStrategy>();
|
||||
|
||||
// 获取当前动作
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
//
|
||||
// Created by misaki on 2025/9/14.
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "PetBaseClass.h"
|
||||
#include "SDFileManager.h"
|
||||
#include "cJSON.h"
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
|
||||
// 辅助函数:枚举类型与字符串的转换
|
||||
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<PetBase>& pet, const std::string& filename);
|
||||
|
||||
// 从文件加载宠物数据
|
||||
std::shared_ptr<PetBase> loadPet(const std::string& filename);
|
||||
|
||||
// 获取所有保存的宠物文件列表
|
||||
std::vector<std::string> listPetFiles();
|
||||
|
||||
// 删除宠物文件
|
||||
bool deletePetFile(const std::string& filename);
|
||||
|
||||
private:
|
||||
// 将宠物数据转换为JSON对象
|
||||
cJSON* petToJson(const std::shared_ptr<PetBase>& pet);
|
||||
|
||||
// 从JSON对象创建宠物
|
||||
std::shared_ptr<PetBase> petFromJson(cJSON* json);
|
||||
|
||||
// 将阶段策略转换为JSON对象
|
||||
cJSON* stageStrategyToJson(const std::shared_ptr<PetStageStrategy>& strategy);
|
||||
|
||||
// 从JSON对象创建阶段策略
|
||||
std::shared_ptr<PetStageStrategy> stageStrategyFromJson(cJSON* json);
|
||||
|
||||
// 将动作策略转换为JSON对象
|
||||
cJSON* actionStrategyToJson(const std::shared_ptr<PetActionStrategy>& strategy);
|
||||
|
||||
// 从JSON对象创建动作策略
|
||||
std::shared_ptr<PetActionStrategy> actionStrategyFromJson(cJSON* json);
|
||||
|
||||
// 文件管理器实例
|
||||
SDFileManager* fileManager;
|
||||
|
||||
// 宠物数据存储目录
|
||||
static constexpr const char* PET_DATA_DIR = "/sdcard/pet_data";
|
||||
};
|
||||
@@ -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 <string>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
// 前向声明主题
|
||||
class PetSubject;
|
||||
|
||||
// 资源句柄,防止裸指针到处飞
|
||||
template<class T>
|
||||
using Handle = std::shared_ptr<T>; /// 宠物句柄
|
||||
// 某个宠物不同阶段的抽象接口 宠物的不同阶段对应于不同的模型文件路径
|
||||
// 如果需要添加新的阶段,要在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<PetStageType>& getStageOrder() {
|
||||
static const std::vector<PetStageType> stageOrder = []() {
|
||||
std::vector<PetStageType> 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<PetStageType, std::string>& getStageModelMap() const {
|
||||
return stage_model_map;
|
||||
}
|
||||
/**
|
||||
* 获取阶段音频映射表
|
||||
* @return 阶段到音频路径的映射表
|
||||
*/
|
||||
virtual const std::unordered_map<PetStageType, std::string>& 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<IPet>; /// 宠物句柄
|
||||
|
||||
// 事件类型
|
||||
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<PetStageType> getAllStages() const {
|
||||
std::list<PetStageType> 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:
|
||||
///<! 阶段到模型路径的映射表
|
||||
std::unordered_map<PetStageType, std::string> stage_model_map;
|
||||
///<! 阶段到音频路径的映射表
|
||||
std::unordered_map<PetStageType, std::string> stage_audio_map;
|
||||
///<! 当前阶段
|
||||
PetStageType current_stage;
|
||||
};
|
||||
|
||||
// 成长阶段
|
||||
enum class Stage {
|
||||
Baby, /// 幼年
|
||||
Teen, /// 青年
|
||||
Adult /// 成长
|
||||
// 宠物动作接口
|
||||
// 一般而言,对于所有的动物,其动作行为基本相同,不同的地方在与发生动作时,所触发的模型不同
|
||||
// 因此,宠物的动作设计,和阶段设计基本类似
|
||||
#define PET_ACTION_TYPES \
|
||||
X(PET_ACTION_SLEEP, "睡觉") \
|
||||
X(PET_ACTION_EAT, "喂食") \
|
||||
X(PET_ACTION_HAPPY, "开心") \
|
||||
X(PET_ACTION_ANGRY, "生气") \
|
||||
X(PET_ACTION_SAD, "沮丧") \
|
||||
X(PET_ACTION_EVOLVE, "进化") \
|
||||
X(PET_ACTION_TOUCH, "被触摸")
|
||||
enum class PetActionType {
|
||||
#define X(name, desc) name,
|
||||
PET_ACTION_TYPES
|
||||
#undef X
|
||||
};
|
||||
// 宠物动作策略类
|
||||
class PetActionStrategy {
|
||||
public:
|
||||
virtual ~PetActionStrategy() = default;
|
||||
// 定义动作顺序的静态常量
|
||||
static const std::vector<PetActionType>& getActionOrder() {
|
||||
static const std::vector<PetActionType> actionOrder = []() {
|
||||
std::vector<PetActionType> 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<PetActionType, std::string>& getActionModelMap() const {
|
||||
return action_model_map;
|
||||
}
|
||||
/**
|
||||
* 获取动作音频映射表
|
||||
* @return 动作到音频路径的映射表
|
||||
*/
|
||||
virtual const std::unordered_map<PetActionType, std::string>& 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<PetActionType> getAllActions() const {
|
||||
std::list<PetActionType> 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:
|
||||
///<! 动作到模型路径的映射表
|
||||
std::unordered_map<PetActionType, std::string> action_model_map;
|
||||
///<! 动作到音频路径的映射表
|
||||
std::unordered_map<PetActionType, std::string> action_audio_map;
|
||||
///<! 当前动作
|
||||
PetActionType current_action = PetActionType::PET_ACTION_SLEEP; // 默认动作
|
||||
};
|
||||
|
||||
// 数值封装
|
||||
struct Vitals {
|
||||
int hp = 100; // 0-100 /// 生命值
|
||||
int intimacy = 0; // 0-100 /// 亲密度
|
||||
// 观察者接口
|
||||
class PetObserver {
|
||||
public:
|
||||
virtual ~PetObserver() = default;
|
||||
// 宠物动作
|
||||
virtual void onPetAction(PetActionType action) = 0;
|
||||
// 宠物阶段
|
||||
virtual void onPetStageChange(PetStageType oldStage, PetStageType newStage) = 0;
|
||||
};
|
||||
|
||||
// 身份设定
|
||||
struct Persona {
|
||||
std::string systemPrompt; /// 系统提示
|
||||
std::string greet; /// 默认欢迎语
|
||||
// …可扩展
|
||||
// 主题接口
|
||||
class PetSubject {
|
||||
public:
|
||||
virtual ~PetSubject() = default;
|
||||
// 添加观察者
|
||||
virtual void addObserver(std::shared_ptr<PetObserver> observer) = 0;
|
||||
// 移除观察者
|
||||
virtual void removeObserver(std::shared_ptr<PetObserver> 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<Stage, std::string> modelPath; /// 阶段→模型
|
||||
std::unordered_map<Emotion, std::string> audioPath; /// 阶段→音频
|
||||
std::string version; /// 版本
|
||||
};
|
||||
@@ -0,0 +1,4 @@
|
||||
//
|
||||
// Created by misaki on 2025/9/14.
|
||||
//
|
||||
#include "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<PetAudioStrategy> {
|
||||
public:
|
||||
using AudioCallback = std::function<void(const std::string&)>;
|
||||
/**
|
||||
* 构造时候就将“事件→音频”两张表填好
|
||||
* @param actionAudios 动作→音频
|
||||
* @param stageAudios 阶段→音频
|
||||
*/
|
||||
explicit PetAudioStrategy(
|
||||
std::unordered_map<PetActionType, std::string> actionAudios = {},
|
||||
std::unordered_map<PetStageType, std::string> stageAudios = {})
|
||||
: action_audio(std::move(actionAudios)),
|
||||
stage_audio(std::move(stageAudios))
|
||||
{}
|
||||
/**
|
||||
* 构造函数,需要传入阶段策略和动作策略,从其中获取所需的类型→模型路径映射
|
||||
* @param stageStrategy 阶段策略
|
||||
* @param actionStrategy 动作策略
|
||||
*/
|
||||
explicit PetAudioStrategy(
|
||||
const std::shared_ptr<PetStageStrategy>& stageStrategy,
|
||||
const std::shared_ptr<PetActionStrategy>& 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<PetSubject> 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:
|
||||
///<! 宠物音频播放回调
|
||||
AudioCallback audio_callback;
|
||||
///<! 宠物主题
|
||||
std::weak_ptr<PetSubject> pet_subject;
|
||||
///<! 两张映射表,保证“同一事件只对应一种音频”
|
||||
std::unordered_map<PetActionType, std::string> action_audio;
|
||||
std::unordered_map<PetStageType, std::string> stage_audio;
|
||||
};
|
||||
|
||||
// 渲染器观察者类,继承自PetObserver
|
||||
class PetRendererStrategy : public PetObserver, public std::enable_shared_from_this<PetRendererStrategy> {
|
||||
public:
|
||||
using RenderCallback = std::function<void(const std::string&)>;
|
||||
/**
|
||||
* 构造函数,可以传入动作到模型路径和阶段到模型路径的映射表
|
||||
* @param actionModels 动作→模型路径映射
|
||||
* @param stageModels 阶段→模型路径映射
|
||||
*/
|
||||
explicit PetRendererStrategy(
|
||||
std::unordered_map<PetActionType, std::string> actionModels = {},
|
||||
std::unordered_map<PetStageType, std::string> stageModels = {})
|
||||
: action_models(std::move(actionModels)),
|
||||
stage_models(std::move(stageModels))
|
||||
{}
|
||||
/**
|
||||
* 构造函数,需要传入阶段策略和动作策略,从其中获取所需的类型→模型路径映射
|
||||
* @param stageStrategy 阶段策略
|
||||
* @param actionStrategy 动作策略
|
||||
*/
|
||||
explicit PetRendererStrategy(
|
||||
const std::shared_ptr<PetStageStrategy>& stageStrategy,
|
||||
const std::shared_ptr<PetActionStrategy>& 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<PetSubject> 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:
|
||||
///<! 渲染回调函数
|
||||
RenderCallback render_callback;
|
||||
///<! 宠物主题
|
||||
std::weak_ptr<PetSubject> pet_subject;
|
||||
///<! 动作到模型路径的映射表
|
||||
std::unordered_map<PetActionType, std::string> action_models;
|
||||
///<! 阶段到模型路径的映射表
|
||||
std::unordered_map<PetStageType, std::string> stage_models;
|
||||
};
|
||||
@@ -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);
|
||||
|
||||
@@ -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" # 工具类库
|
||||
|
||||
@@ -216,4 +216,11 @@
|
||||
|
||||
- [x] 3. 增加了一些CPU资源占用的日志打印函数,运行在主线程当中
|
||||
|
||||
- [x] 4. 完善了底层通信类的封装,基于websocket,尚未测试
|
||||
- [x] 4. 完善了底层通信类的封装,基于websocket,尚未测试
|
||||
|
||||
#### Day14 2025.9.15
|
||||
##### 主要目标:完成具体业务开发&各种优化
|
||||
实际完成任务:
|
||||
- [x] 1. 历时两天,完整且完美的设计了宠物类,使用到了多种设计模式,完成了低耦合,高内聚的完美代码,测试也完美通过。
|
||||
|
||||
- [x] 2. 顺便完善了底层通信类的封装,基于websocket,基准测试通过,但存在一点很小的线程bug,似乎是来自于esp32 idf底层的问题,待解决
|
||||
|
||||
Reference in New Issue
Block a user