1. 完整封装并拓展了SD卡文件管理类,支持基本文件管理功能
2. 简单封装了LVGL渲染类,已经封装好了gif渲染功能 3. 修复了硬件厂商提供的驱动的Bug 4. 初步定义了宠物基类的抽象信息
This commit is contained in:
@@ -0,0 +1,505 @@
|
||||
//
|
||||
// Created by misaki on 2025/9/9.
|
||||
//
|
||||
|
||||
#include "SDFileManager.h"
|
||||
#include "SD_MMC.h"
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <iomanip>
|
||||
|
||||
SDFileManager* SDFileManager::SDFileInstance = nullptr;
|
||||
std::mutex SDFileManager::instance_mutex;
|
||||
|
||||
SDFileManager *SDFileManager::getInstance() {
|
||||
// 双检锁(DCLP),C++11 起 atomic+mutex 组合保证线程安全
|
||||
SDFileManager* tmp = SDFileInstance;
|
||||
if (tmp == nullptr) {
|
||||
std::lock_guard<std::mutex> lock(instance_mutex);
|
||||
tmp = SDFileInstance;
|
||||
if (tmp == nullptr) {
|
||||
tmp = new SDFileManager();
|
||||
SDFileInstance = tmp;
|
||||
}
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
SDFileManager::SDFileManager() : is_initialized(false), current_directory(MOUNT_POINT) {
|
||||
// 初始化SD卡
|
||||
init();
|
||||
// 获取Flash大小
|
||||
Flash_Searching();
|
||||
}
|
||||
|
||||
SDFileManager::~SDFileManager() {
|
||||
// 清理资源
|
||||
}
|
||||
|
||||
void SDFileManager::init() {
|
||||
SD_Init();
|
||||
is_initialized = true;
|
||||
}
|
||||
|
||||
bool SDFileManager::writeFileSync(const char* path, const char* data) {
|
||||
std::lock_guard<std::mutex> lock(file_operation_mutex);
|
||||
|
||||
if (!is_initialized) {
|
||||
ESP_LOGE("SDFileManager", "SD card not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
esp_err_t result = s_example_write_file(path, const_cast<char*>(data));
|
||||
return result == ESP_OK;
|
||||
}
|
||||
|
||||
std::string SDFileManager::readFileSync(const char* path) {
|
||||
std::lock_guard<std::mutex> lock(file_operation_mutex);
|
||||
|
||||
if (!is_initialized) {
|
||||
ESP_LOGE("SDFileManager", "SD card not initialized");
|
||||
return "";
|
||||
}
|
||||
|
||||
FILE* file = fopen(path, "r");
|
||||
if (file == nullptr) {
|
||||
ESP_LOGE("SDFileManager", "Failed to open file: %s", path);
|
||||
return "";
|
||||
}
|
||||
|
||||
// 获取文件大小
|
||||
fseek(file, 0, SEEK_END);
|
||||
long size = ftell(file);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
|
||||
// 读取文件内容
|
||||
std::string content;
|
||||
content.resize(size);
|
||||
size_t bytes_read = fread(&content[0], 1, size, file);
|
||||
fclose(file);
|
||||
|
||||
if (bytes_read != static_cast<size_t>(size)) {
|
||||
ESP_LOGE("SDFileManager", "Failed to read entire file: %s", path);
|
||||
return "";
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
std::vector<std::string> SDFileManager::listFilesSync(const char* directory, const char* extension) {
|
||||
std::lock_guard<std::mutex> lock(file_operation_mutex);
|
||||
|
||||
if (!is_initialized) {
|
||||
ESP_LOGE("SDFileManager", "SD card not initialized");
|
||||
return {};
|
||||
}
|
||||
|
||||
const int max_files = 50;
|
||||
char file_names[max_files][100];
|
||||
uint16_t file_count = Folder_retrieval(directory, extension, file_names, max_files);
|
||||
|
||||
std::vector<std::string> files;
|
||||
for (uint16_t i = 0; i < file_count; ++i) {
|
||||
files.emplace_back(file_names[i]);
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
FILE* SDFileManager::openFileSync(const char* path, const char* mode) {
|
||||
std::lock_guard<std::mutex> lock(file_operation_mutex);
|
||||
|
||||
if (!is_initialized) {
|
||||
ESP_LOGE("SDFileManager", "SD card not initialized");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return Open_File(path);
|
||||
}
|
||||
|
||||
bool SDFileManager::closeFileSync(FILE* file) {
|
||||
if (file != nullptr) {
|
||||
return fclose(file) == 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SDFileManager::asyncWriteFile(const char* path, const char* data, WriteCallback callback) {
|
||||
ThreadConfig config = getThreadConfig("write_file");
|
||||
|
||||
ThreadManager::createThread(config, [this, path = std::string(path),
|
||||
data = std::string(data), callback]() {
|
||||
bool success = this->writeFileSync(path.c_str(), data.c_str());
|
||||
if (callback) {
|
||||
callback(success, path.c_str());
|
||||
}
|
||||
}).detach(); // 分离线程,使其独立运行
|
||||
}
|
||||
|
||||
void SDFileManager::asyncReadFile(const char* path, ReadCallback callback) {
|
||||
if (!callback) {
|
||||
return; // 没有回调函数,异步读取无意义
|
||||
}
|
||||
|
||||
ThreadConfig config = getThreadConfig("read_file");
|
||||
|
||||
ThreadManager::createThread(config, [this, path = std::string(path), callback]() {
|
||||
std::string content = this->readFileSync(path.c_str());
|
||||
bool success = !content.empty();
|
||||
callback(success, path.c_str(), content);
|
||||
}).detach();
|
||||
}
|
||||
|
||||
void SDFileManager::asyncListFiles(const char* directory, const char* extension, ListCallback callback) {
|
||||
if (!callback) {
|
||||
return; // 没有回调函数,异步列出文件无意义
|
||||
}
|
||||
|
||||
ThreadConfig config = getThreadConfig("list_files");
|
||||
|
||||
ThreadManager::createThread(config, [this, directory = std::string(directory),
|
||||
extension = std::string(extension ? extension : ""),
|
||||
callback]() {
|
||||
std::vector<std::string> files = this->listFilesSync(
|
||||
directory.c_str(),
|
||||
extension.empty() ? nullptr : extension.c_str()
|
||||
);
|
||||
bool success = !files.empty();
|
||||
callback(success, files);
|
||||
}).detach();
|
||||
}
|
||||
|
||||
void SDFileManager::asyncOpenFile(const char* path, const char* mode, OpenCallback callback) {
|
||||
if (!callback) {
|
||||
return; // 没有回调函数,异步打开文件无意义
|
||||
}
|
||||
|
||||
ThreadConfig config = getThreadConfig("open_file");
|
||||
|
||||
ThreadManager::createThread(config, [this, path = std::string(path),
|
||||
mode = std::string(mode), callback]() {
|
||||
FILE* file = this->openFileSync(path.c_str(), mode.c_str());
|
||||
bool success = (file != nullptr);
|
||||
callback(success, file);
|
||||
}).detach();
|
||||
}
|
||||
|
||||
ThreadConfig SDFileManager::getThreadConfig(const char* operation) {
|
||||
ThreadConfig config;
|
||||
config.name = "sd_" + std::string(operation);
|
||||
config.core_id = -1; // 不绑定核心
|
||||
config.stack_size = 4096;
|
||||
config.priority = 5;
|
||||
config.inherit_cfg = false;
|
||||
return config;
|
||||
}
|
||||
|
||||
// 实现ls命令
|
||||
std::string SDFileManager::lsCommand(const char* path, bool recursive, bool showDetails) {
|
||||
std::lock_guard<std::mutex> lock(file_operation_mutex);
|
||||
|
||||
if (!is_initialized) {
|
||||
ESP_LOGE("SDFileManager", "SD card not initialized");
|
||||
return "Error: SD card not initialized";
|
||||
}
|
||||
|
||||
std::string fullPath = path;
|
||||
if (fullPath.empty() || fullPath == ".") {
|
||||
fullPath = current_directory;
|
||||
} else if (fullPath[0] != '/') {
|
||||
// 相对路径
|
||||
fullPath = current_directory + "/" + fullPath;
|
||||
}
|
||||
|
||||
DIR* dir = opendir(fullPath.c_str());
|
||||
if (dir == nullptr) {
|
||||
return "Error: Cannot open directory " + fullPath;
|
||||
}
|
||||
|
||||
std::stringstream result;
|
||||
struct dirent* entry;
|
||||
struct stat st;
|
||||
|
||||
result << "Directory listing for: " << fullPath << "\n";
|
||||
result << "----------------------------------------\n";
|
||||
|
||||
while ((entry = readdir(dir)) != nullptr) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string entryPath = fullPath + "/" + entry->d_name;
|
||||
if (stat(entryPath.c_str(), &st) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (showDetails) {
|
||||
result << getFileInfoString(entry->d_name, &st);
|
||||
} else {
|
||||
result << entry->d_name;
|
||||
}
|
||||
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
result << "/";
|
||||
}
|
||||
result << "\n";
|
||||
|
||||
// 递归列出子目录
|
||||
if (recursive && S_ISDIR(st.st_mode)) {
|
||||
std::string subDirResult = lsCommand(entryPath.c_str(), true, showDetails);
|
||||
result << subDirResult;
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
return result.str();
|
||||
}
|
||||
|
||||
// 实现cat命令
|
||||
std::string SDFileManager::catCommand(const char* path, bool lineNumbers) {
|
||||
std::lock_guard<std::mutex> lock(file_operation_mutex);
|
||||
|
||||
if (!is_initialized) {
|
||||
ESP_LOGE("SDFileManager", "SD card not initialized");
|
||||
return "Error: SD card not initialized";
|
||||
}
|
||||
|
||||
std::string fullPath = path;
|
||||
if (fullPath[0] != '/') {
|
||||
// 相对路径
|
||||
fullPath = current_directory + "/" + fullPath;
|
||||
}
|
||||
|
||||
FILE* file = fopen(fullPath.c_str(), "r");
|
||||
if (file == nullptr) {
|
||||
return "Error: Cannot open file " + fullPath;
|
||||
}
|
||||
|
||||
std::stringstream result;
|
||||
char buffer[256];
|
||||
int lineNumber = 1;
|
||||
|
||||
while (fgets(buffer, sizeof(buffer), file) != nullptr) {
|
||||
if (lineNumbers) {
|
||||
result << std::setw(4) << lineNumber << ": ";
|
||||
}
|
||||
result << buffer;
|
||||
lineNumber++;
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
return result.str();
|
||||
}
|
||||
|
||||
// 实现pwd命令
|
||||
std::string SDFileManager::pwdCommand() {
|
||||
return current_directory;
|
||||
}
|
||||
|
||||
// 实现cd命令
|
||||
bool SDFileManager::cdCommand(const char* path) {
|
||||
std::lock_guard<std::mutex> lock(file_operation_mutex);
|
||||
|
||||
if (!is_initialized) {
|
||||
ESP_LOGE("SDFileManager", "SD card not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string newPath;
|
||||
if (path[0] == '/') {
|
||||
// 绝对路径
|
||||
newPath = path;
|
||||
} else if (strcmp(path, "..") == 0) {
|
||||
// 上级目录
|
||||
size_t pos = current_directory.find_last_of('/');
|
||||
if (pos != std::string::npos && pos > 0) {
|
||||
newPath = current_directory.substr(0, pos);
|
||||
} else {
|
||||
newPath = "/";
|
||||
}
|
||||
} else {
|
||||
// 相对路径
|
||||
newPath = current_directory + "/" + path;
|
||||
}
|
||||
|
||||
// 检查路径是否存在且是目录
|
||||
struct stat st;
|
||||
if (stat(newPath.c_str(), &st) != 0 || !S_ISDIR(st.st_mode)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
current_directory = newPath;
|
||||
return true;
|
||||
}
|
||||
|
||||
// 实现mkdir命令
|
||||
bool SDFileManager::mkdirCommand(const char* path) {
|
||||
std::lock_guard<std::mutex> lock(file_operation_mutex);
|
||||
|
||||
if (!is_initialized) {
|
||||
ESP_LOGE("SDFileManager", "SD card not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string fullPath = path;
|
||||
if (fullPath[0] != '/') {
|
||||
// 相对路径
|
||||
fullPath = current_directory + "/" + fullPath;
|
||||
}
|
||||
|
||||
// 创建目录
|
||||
int result = mkdir(fullPath.c_str(), 0777);
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
// 实现rm命令
|
||||
bool SDFileManager::rmCommand(const char* path, bool recursive) {
|
||||
std::lock_guard<std::mutex> lock(file_operation_mutex);
|
||||
|
||||
if (!is_initialized) {
|
||||
ESP_LOGE("SDFileManager", "SD card not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string fullPath = path;
|
||||
if (fullPath[0] != '/') {
|
||||
// 相对路径
|
||||
fullPath = current_directory + "/" + fullPath;
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
if (stat(fullPath.c_str(), &st) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
if (recursive) {
|
||||
return removeDirectoryRecursive(fullPath.c_str());
|
||||
} else {
|
||||
// 非递归删除空目录
|
||||
return rmdir(fullPath.c_str()) == 0;
|
||||
}
|
||||
} else {
|
||||
// 删除文件
|
||||
return remove(fullPath.c_str()) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 实现stat命令
|
||||
std::string SDFileManager::statCommand(const char* path) {
|
||||
std::lock_guard<std::mutex> lock(file_operation_mutex);
|
||||
|
||||
if (!is_initialized) {
|
||||
ESP_LOGE("SDFileManager", "SD card not initialized");
|
||||
return "Error: SD card not initialized";
|
||||
}
|
||||
|
||||
std::string fullPath = path;
|
||||
if (fullPath[0] != '/') {
|
||||
// 相对路径
|
||||
fullPath = current_directory + "/" + fullPath;
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
if (stat(fullPath.c_str(), &st) != 0) {
|
||||
return "Error: Cannot get file status for " + fullPath;
|
||||
}
|
||||
|
||||
return getFileInfoString(fullPath.c_str(), &st);
|
||||
}
|
||||
|
||||
// 辅助方法:递归删除目录
|
||||
bool SDFileManager::removeDirectoryRecursive(const char* path) {
|
||||
DIR* dir = opendir(path);
|
||||
if (dir == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct dirent* entry;
|
||||
while ((entry = readdir(dir)) != nullptr) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string entryPath = std::string(path) + "/" + entry->d_name;
|
||||
struct stat st;
|
||||
if (stat(entryPath.c_str(), &st) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
if (!removeDirectoryRecursive(entryPath.c_str())) {
|
||||
closedir(dir);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (remove(entryPath.c_str()) != 0) {
|
||||
closedir(dir);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
return rmdir(path) == 0;
|
||||
}
|
||||
|
||||
// 辅助方法:获取文件信息字符串
|
||||
std::string SDFileManager::getFileInfoString(const char* path, const struct stat* st) {
|
||||
std::stringstream info;
|
||||
|
||||
// 文件类型和权限
|
||||
info << getFileTypeString(st->st_mode);
|
||||
info << ((st->st_mode & S_IRUSR) ? "r" : "-");
|
||||
info << ((st->st_mode & S_IWUSR) ? "w" : "-");
|
||||
info << ((st->st_mode & S_IXUSR) ? "x" : "-");
|
||||
info << ((st->st_mode & S_IRGRP) ? "r" : "-");
|
||||
info << ((st->st_mode & S_IWGRP) ? "w" : "-");
|
||||
info << ((st->st_mode & S_IXGRP) ? "x" : "-");
|
||||
info << ((st->st_mode & S_IROTH) ? "r" : "-");
|
||||
info << ((st->st_mode & S_IWOTH) ? "w" : "-");
|
||||
info << ((st->st_mode & S_IXOTH) ? "x" : "-");
|
||||
|
||||
// 文件大小
|
||||
info << " " << std::setw(10) << formatFileSize(st->st_size);
|
||||
|
||||
// 最后修改时间
|
||||
struct tm* timeinfo = localtime(&st->st_mtime);
|
||||
char timeBuf[20];
|
||||
strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M", timeinfo);
|
||||
info << " " << timeBuf;
|
||||
|
||||
// 文件名
|
||||
info << " " << path;
|
||||
|
||||
return info.str();
|
||||
}
|
||||
|
||||
// 辅助方法:获取文件类型字符串
|
||||
std::string SDFileManager::getFileTypeString(mode_t mode) {
|
||||
if (S_ISREG(mode)) return "-"; // 普通文件
|
||||
if (S_ISDIR(mode)) return "d"; // 目录
|
||||
if (S_ISCHR(mode)) return "c"; // 字符设备
|
||||
if (S_ISBLK(mode)) return "b"; // 块设备
|
||||
if (S_ISFIFO(mode)) return "p"; // 管道
|
||||
if (S_ISLNK(mode)) return "l"; // 符号链接
|
||||
if (S_ISSOCK(mode)) return "s"; // 套接字
|
||||
return "?"; // 未知类型
|
||||
}
|
||||
|
||||
// 辅助方法:格式化文件大小
|
||||
std::string SDFileManager::formatFileSize(size_t size) {
|
||||
std::stringstream ss;
|
||||
if (size < 1024) {
|
||||
ss << size << " B";
|
||||
} else if (size < 1024 * 1024) {
|
||||
ss << std::fixed << std::setprecision(1) << (size / 1024.0) << " KB";
|
||||
} else if (size < 1024 * 1024 * 1024) {
|
||||
ss << std::fixed << std::setprecision(1) << (size / (1024.0 * 1024.0)) << " MB";
|
||||
} else {
|
||||
ss << std::fixed << std::setprecision(1) << (size / (1024.0 * 1024.0 * 1024.0)) << " GB";
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user