#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; } bool FileManager::writeJsonFile(const String& filePath, JsonDocument& doc) { if (!initializeSD()) { return false; } File file = SD.open(filePath.c_str(), FILE_WRITE); if (!file) { LOG_ERROR("Failed to open file for writing: %s", filePath.c_str()); return false; } if (serializeJson(doc, file) == 0) { LOG_ERROR("Failed to write JSON to file: %s", filePath.c_str()); file.close(); return false; } file.close(); LOG_DEBUG("JSON file written successfully: %s", filePath.c_str()); return true; } bool FileManager::readJsonFile(const String& filePath, JsonDocument& doc) { if (!initializeSD()) { return false; } File file = SD.open(filePath.c_str(), FILE_READ); if (!file) { LOG_ERROR("Failed to open file for reading: %s", filePath.c_str()); return false; } DeserializationError error = deserializeJson(doc, file); file.close(); if (error) { LOG_ERROR("Failed to parse JSON from file: %s, error: %s", filePath.c_str(), error.c_str()); return false; } LOG_DEBUG("JSON file read successfully: %s", filePath.c_str()); return true; } // ════════════════════════════════════════════════════════════════════════════ // 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; }