#include "ErrorRecoveryModule.h" #include "grove/JsonDataNode.h" #include #include #include #include namespace grove { void ErrorRecoveryModule::setConfiguration(const IDataNode& configNode, IIO* io, ITaskScheduler* scheduler) { // Logger logger = spdlog::get("ErrorRecoveryModule"); if (!logger) { logger = spdlog::stdout_color_mt("ErrorRecoveryModule"); } logger->set_level(spdlog::level::debug); // Clone config const auto* jsonConfigNode = dynamic_cast(&configNode); if (jsonConfigNode) { config = std::make_unique("config", jsonConfigNode->getJsonData()); } else { config = std::make_unique("config"); } // Lire configuration crashAtFrame = configNode.getInt("crashAtFrame", -1); crashType = configNode.getInt("crashType", 0); enableAutoRecovery = configNode.getBool("enableAutoRecovery", true); versionTag = configNode.getString("versionTag", "v1.0"); logger->info("Initializing ErrorRecoveryModule"); logger->info(" Version: {}", versionTag); logger->info(" Crash at frame: {}", crashAtFrame); logger->info(" Crash type: {}", crashType); logger->info(" Auto-recovery enabled: {}", enableAutoRecovery); frameCount = 0; crashCount = 0; recoveryCount = 0; hasCrashed = false; } const IDataNode& ErrorRecoveryModule::getConfiguration() { return *config; } void ErrorRecoveryModule::process(const IDataNode& input) { isProcessing = true; frameCount++; // Si crash planifié à cette frame précise if (crashAtFrame > 0 && frameCount == crashAtFrame) { triggerConfiguredCrash(); } isProcessing = false; } void ErrorRecoveryModule::triggerConfiguredCrash() { crashCount++; hasCrashed = true; logger->warn("💥 CRASH TRIGGERED at frame {}", frameCount); logger->warn(" Crash type: {}", crashType); switch (crashType) { case 0: logger->error("Throwing runtime_error"); throw std::runtime_error("CRASH: Controlled runtime error at frame " + std::to_string(frameCount)); case 1: logger->error("Throwing logic_error"); throw std::logic_error("CRASH: Logic error at frame " + std::to_string(frameCount)); case 2: logger->error("Throwing out_of_range"); throw std::out_of_range("CRASH: Out of range at frame " + std::to_string(frameCount)); case 3: logger->error("Throwing domain_error"); throw std::domain_error("CRASH: Domain error at frame " + std::to_string(frameCount)); default: logger->error("Unknown crash type, defaulting to runtime_error"); throw std::runtime_error("CRASH: Unknown crash type at frame " + std::to_string(frameCount)); } } std::unique_ptr ErrorRecoveryModule::getHealthStatus() { nlohmann::json healthJson; healthJson["status"] = hasCrashed ? "crashed" : "healthy"; healthJson["frameCount"] = frameCount; healthJson["crashCount"] = crashCount; healthJson["recoveryCount"] = recoveryCount; healthJson["versionTag"] = versionTag; return std::make_unique("health", healthJson); } void ErrorRecoveryModule::shutdown() { logger->info("Shutting down ErrorRecoveryModule"); logger->info(" Version: {}", versionTag); logger->info(" Total frames: {}", frameCount); logger->info(" Crashes: {}", crashCount); logger->info(" Recoveries: {}", recoveryCount); } std::string ErrorRecoveryModule::getType() const { return "error-recovery"; } std::unique_ptr ErrorRecoveryModule::getState() { nlohmann::json json; json["frameCount"] = frameCount; json["crashCount"] = crashCount; json["recoveryCount"] = recoveryCount; json["hasCrashed"] = hasCrashed; json["versionTag"] = versionTag; json["crashAtFrame"] = crashAtFrame; return std::make_unique("state", json); } void ErrorRecoveryModule::setState(const IDataNode& state) { const auto* jsonNode = dynamic_cast(&state); if (!jsonNode) { if (logger) { logger->error("setState: Invalid state (not JsonDataNode)"); } return; } const auto& json = jsonNode->getJsonData(); // Ensure logger is initialized (needed after hot-reload) if (!logger) { logger = spdlog::get("ErrorRecoveryModule"); if (!logger) { logger = spdlog::stdout_color_mt("ErrorRecoveryModule"); } } // Ensure config is initialized (needed after hot-reload) if (!config) { config = std::make_unique("config"); } // AUTO-RECOVERY: Si le module avait crashé et que auto-recovery est activé bool hadCrashed = json.value("hasCrashed", false); if (hadCrashed && enableAutoRecovery) { logger->warn("🔄 AUTO-RECOVERY TRIGGERED"); logger->warn(" Module had crashed before reload"); logger->warn(" Applying recovery strategy..."); // Récupérer l'état mais reset le flag de crash frameCount = json.value("frameCount", 0); crashCount = json.value("crashCount", 0); recoveryCount = json.value("recoveryCount", 0) + 1; // Incrémenter recovery count hasCrashed = false; // RECOVERY: On n'est plus en état crashé // Désactiver le crash planifié pour éviter de re-crasher crashAtFrame = -1; versionTag = json.value("versionTag", "v1.0"); logger->info("✅ RECOVERY SUCCESSFUL"); logger->info(" Frame count preserved: {}", frameCount); logger->info(" Recovery count: {}", recoveryCount); logger->info(" Crash trigger disabled"); return; } // État normal (pas de crash) frameCount = json.value("frameCount", 0); crashCount = json.value("crashCount", 0); recoveryCount = json.value("recoveryCount", 0); hasCrashed = json.value("hasCrashed", false); versionTag = json.value("versionTag", "v1.0"); crashAtFrame = json.value("crashAtFrame", -1); logger->info("State restored: frame {}, crashes {}, recoveries {}, version {}", frameCount, crashCount, recoveryCount, versionTag); } } // namespace grove // Export symbols extern "C" { grove::IModule* createModule() { return new grove::ErrorRecoveryModule(); } void destroyModule(grove::IModule* module) { delete module; } }