Complete Rebuild, with Subsystems for each component. RTOS Tasks. (help by Claude)

This commit is contained in:
2025-10-01 12:42:00 +03:00
parent 104c1d04d4
commit f696984cd1
57 changed files with 11757 additions and 2290 deletions

View File

@@ -0,0 +1,241 @@
#include "FileManager.hpp"
FileManager::FileManager(ConfigManager* config) : configManager(config) {
// Constructor - store reference to ConfigManager
}
bool FileManager::initializeSD() {
uint8_t sdPin = configManager->getHardwareConfig().sdChipSelect;
if (!SD.begin(sdPin)) {
LOG_ERROR("SD Card initialization failed!");
return false;
}
return true;
}
bool FileManager::addMelody(JsonVariant doc) {
LOG_INFO("Adding melody from JSON data...");
// Extract URL and filename from JSON
if (!doc.containsKey("download_url") || !doc.containsKey("melodys_uid")) {
LOG_ERROR("Missing required parameters: download_url or melodys_uid");
return false;
}
const char* url = doc["download_url"];
const char* filename = doc["melodys_uid"];
// Download the melody file to /melodies directory
if (downloadFile(url, "/melodies", filename)) {
LOG_INFO("Melody download successful: %s", filename);
return true;
}
LOG_ERROR("Melody download failed: %s", filename);
return false;
}
bool FileManager::ensureDirectoryExists(const String& dirPath) {
if (!initializeSD()) {
return false;
}
// Ensure the directory ends with '/'
String normalizedPath = dirPath;
if (!normalizedPath.endsWith("/")) {
normalizedPath += "/";
}
// Create directory if it doesn't exist
return SD.mkdir(normalizedPath.c_str());
}
bool FileManager::downloadFile(const String& url, const String& directory, const String& filename) {
LOG_INFO("Starting download from: %s", url.c_str());
HTTPClient http;
http.begin(url);
int httpCode = http.GET();
if (httpCode != HTTP_CODE_OK) {
LOG_ERROR("HTTP GET failed, error: %s", http.errorToString(httpCode).c_str());
http.end();
return false;
}
if (!initializeSD()) {
http.end();
return false;
}
// Ensure directory exists
if (!ensureDirectoryExists(directory)) {
LOG_ERROR("Failed to create directory: %s", directory.c_str());
http.end();
return false;
}
// Build full file path
String dirPath = directory;
if (!dirPath.endsWith("/")) dirPath += "/";
String fullPath = dirPath + filename;
File file = SD.open(fullPath.c_str(), FILE_WRITE);
if (!file) {
LOG_ERROR("Failed to open file for writing: %s", fullPath.c_str());
http.end();
return false;
}
WiFiClient* stream = http.getStreamPtr();
uint8_t buffer[1024];
int bytesRead;
while (http.connected() && (bytesRead = stream->readBytes(buffer, sizeof(buffer))) > 0) {
file.write(buffer, bytesRead);
}
file.close();
http.end();
LOG_INFO("Download complete, file saved to: %s", fullPath.c_str());
return true;
}
String FileManager::listFilesAsJson(const char* dirPath) {
if (!initializeSD()) {
LOG_ERROR("SD initialization failed");
return "{}";
}
File dir = SD.open(dirPath);
if (!dir || !dir.isDirectory()) {
LOG_ERROR("Directory not found: %s", dirPath);
return "{}";
}
DynamicJsonDocument doc(1024);
JsonArray fileList = doc.createNestedArray("files");
File file = dir.openNextFile();
while (file) {
if (!file.isDirectory()) {
fileList.add(file.name());
}
file = dir.openNextFile();
}
String json;
serializeJson(doc, json);
return json;
}
bool FileManager::fileExists(const String& filePath) {
if (!initializeSD()) {
return false;
}
File file = SD.open(filePath.c_str());
if (file) {
file.close();
return true;
}
return false;
}
bool FileManager::deleteFile(const String& filePath) {
if (!initializeSD()) {
return false;
}
if (SD.remove(filePath.c_str())) {
LOG_INFO("File deleted: %s", filePath.c_str());
return true;
} else {
LOG_ERROR("Failed to delete file: %s", filePath.c_str());
return false;
}
}
bool FileManager::createDirectory(const String& dirPath) {
return ensureDirectoryExists(dirPath);
}
size_t FileManager::getFileSize(const String& filePath) {
if (!initializeSD()) {
return 0;
}
File file = SD.open(filePath.c_str());
if (!file) {
return 0;
}
size_t size = file.size();
file.close();
return size;
}
// ════════════════════════════════════════════════════════════════════════════
// HEALTH CHECK IMPLEMENTATION
// ════════════════════════════════════════════════════════════════════════════
bool FileManager::isHealthy() const {
// Check if ConfigManager is available
if (!configManager) {
LOG_DEBUG("FileManager: Unhealthy - ConfigManager not available");
return false;
}
// Check if SD card can be initialized
uint8_t sdPin = configManager->getHardwareConfig().sdChipSelect;
if (!SD.begin(sdPin)) {
LOG_DEBUG("FileManager: Unhealthy - SD Card initialization failed");
return false;
}
// Check if we can read from SD card (test with root directory)
File root = SD.open("/");
if (!root) {
LOG_DEBUG("FileManager: Unhealthy - Cannot access SD root directory");
return false;
}
if (!root.isDirectory()) {
LOG_DEBUG("FileManager: Unhealthy - SD root is not a directory");
root.close();
return false;
}
root.close();
// Check if we can write to SD card (create/delete a test file)
String testFile = "/health_test.tmp";
File file = SD.open(testFile.c_str(), FILE_WRITE);
if (!file) {
LOG_DEBUG("FileManager: Unhealthy - Cannot write to SD card");
return false;
}
file.print("health_check");
file.close();
// Verify we can read the test file
file = SD.open(testFile.c_str(), FILE_READ);
if (!file) {
LOG_DEBUG("FileManager: Unhealthy - Cannot read test file from SD card");
return false;
}
String content = file.readString();
file.close();
// Clean up test file
SD.remove(testFile.c_str());
if (content != "health_check") {
LOG_DEBUG("FileManager: Unhealthy - SD card read/write test failed");
return false;
}
return true;
}