diff --git a/docs/commands.json b/docs/commands.json index 9eebd0e..b4a9036 100644 --- a/docs/commands.json +++ b/docs/commands.json @@ -82,7 +82,7 @@ "cmd": "setCronJob", "params": { "name": "nomedeljob", - "timeStr": "* * * 10,45 5 *", + "cronExpr": "* * * 10,45 5 *", "action": "qua ci va un dizionario come se arrivasse da mqtt, cosi li interpreto alla stessa maniera" } }, diff --git a/include/config.h b/include/config.h index 7c140f3..1bb2f3f 100644 --- a/include/config.h +++ b/include/config.h @@ -5,39 +5,7 @@ #include #include #include -#include - -#include - -class FSmount -{ -public: - FSmount() - { - if (!FFat.begin(false)) - { - LOG_ERROR("Unable to mount filesystem without formatting"); - if (!FFat.begin(true)) - { - LOG_ERROR("Formatted and mounted filesystem"); - } - } - - LOG_INFO("Local Filesystem Mounted Correctly"); - const auto totalBytes = FFat.totalBytes(); - const auto freeBytes = FFat.freeBytes(); - const auto usedBytes = FFat.usedBytes(); - const auto mountPoint = FFat.mountpoint(); - LOG_INFO("Local filesystem, total", totalBytes / 1024, "KB - used", usedBytes / 1024, "KB - free", freeBytes / 1024, "KB"); - LOG_INFO("Local filesystem, mountpoint", mountPoint); - } - - ~FSmount() - { - FFat.end(); // unmout filesystem to avoid corruption - LOG_INFO("Local Filesystem Unmounted Correctly"); - } -}; +#include class Config { diff --git a/include/fsmount.h b/include/fsmount.h new file mode 100644 index 0000000..10a6310 --- /dev/null +++ b/include/fsmount.h @@ -0,0 +1,38 @@ +#pragma once + +#define DEBUGLOG_DEFAULT_LOG_LEVEL_DEBUG + +#include +#include +#include +#include + +class FSmount +{ +public: + FSmount() + { + if (!FFat.begin(false)) + { + LOG_ERROR("Unable to mount filesystem without formatting"); + if (!FFat.begin(true)) + { + LOG_ERROR("Formatted and mounted filesystem"); + } + } + + LOG_INFO("Local Filesystem Mounted Correctly"); + const auto totalBytes = FFat.totalBytes(); + const auto freeBytes = FFat.freeBytes(); + const auto usedBytes = FFat.usedBytes(); + const auto mountPoint = FFat.mountpoint(); + LOG_INFO("Local filesystem, total", totalBytes / 1024, "KB - used", usedBytes / 1024, "KB - free", freeBytes / 1024, "KB"); + LOG_INFO("Local filesystem, mountpoint", mountPoint); + } + + ~FSmount() + { + FFat.end(); // unmout filesystem to avoid corruption + LOG_INFO("Local Filesystem Unmounted Correctly"); + } +}; diff --git a/src/commands.cpp b/src/commands.cpp index bf08de5..56bbed1 100644 --- a/src/commands.cpp +++ b/src/commands.cpp @@ -51,13 +51,26 @@ namespace commands // CRONJOBS // // CRONJOBS // + const ArduinoJson::JsonDocument Commands::loadCronjob(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms){ + ArduinoJson::JsonDocument response; + response["cmd"] = "loadCronjob"; + auto& cron = Cron::getInstance(dev); + if(!cron.loadEvents()){ + LOG_ERROR("loadCronJob failed to load events from flash"); + response["values"]["status"] = "invalid"; + return response; + } + response["values"]["status"] = "valid"; + return response; + } + const ArduinoJson::JsonDocument Commands::setCronjob(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms) { ArduinoJson::JsonDocument response; response["cmd"] = "setCronjob"; const auto &jobName = params["name"].as(); - const auto &timeStr = params["timeStr"].as(); + const auto &timeStr = params["cronExpr"].as(); const auto &actionStr = params["action"].as(); ArduinoJson::JsonDocument action; @@ -94,14 +107,14 @@ namespace commands } auto cmd = std::get<0>(event); - auto chronExpr = std::get<1>(event); + auto cronExpr = std::get<1>(event); auto cmdParams = std::get<3>(event); ArduinoJson::JsonDocument action; action["cmd"] = cmd; action["params"] = cmdParams; response["values"]["name"] = eventName; - response["values"]["timeStr"] = cron::to_cronstr(chronExpr); + response["values"]["cronExpr"] = cron::to_cronstr(cronExpr); response["values"]["action"] = action; LOG_INFO("getCronjob get job [", eventName.c_str(), "]"); @@ -122,6 +135,20 @@ namespace commands response["values"]["status"] = "valid"; return response; } + + const ArduinoJson::JsonDocument Commands::storeCronjob(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms) + { + ArduinoJson::JsonDocument response; + response["cmd"] = "storeCronjob"; + auto& cron = Cron::getInstance(dev); + if(!cron.storeEvents()){ + LOG_ERROR("storeCronJob failed to store events in flash"); + response["values"]["status"] = "invalid"; + return response; + } + response["values"]["status"] = "valid"; + return response; + } // CRONJOBS // // CRONJOBS // diff --git a/src/commands.h b/src/commands.h index 4ed206a..cf617f2 100644 --- a/src/commands.h +++ b/src/commands.h @@ -69,9 +69,11 @@ namespace commands static const ArduinoJson::JsonDocument getConfig(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms); // CRONJOBS // + static const ArduinoJson::JsonDocument loadCronjob(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms); static const ArduinoJson::JsonDocument setCronjob(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms); static const ArduinoJson::JsonDocument getCronjob(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms); static const ArduinoJson::JsonDocument delCronjob(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms); + static const ArduinoJson::JsonDocument storeCronjob(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms); // SETTERS // static const ArduinoJson::JsonDocument setHPlimit(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms); @@ -93,9 +95,11 @@ namespace commands {"setConfig", Commands::setConfig}, {"getConfig", Commands::getConfig}, + {"loadCronjob", Commands::loadCronjob}, {"setCronjob", Commands::setCronjob}, {"getCronjob", Commands::getCronjob}, {"delCronjob", Commands::delCronjob}, + {"storeCronjob", Commands::storeCronjob}, {"setHPlimit", Commands::setHPlimit}, {"setHeating", Commands::setHeating}, diff --git a/src/cronjobs.cpp b/src/cronjobs.cpp index 2dbb4f9..8c69fe9 100644 --- a/src/cronjobs.cpp +++ b/src/cronjobs.cpp @@ -1,16 +1,91 @@ #include #include +#include #define STACK_DEPTH 4096 #define PRIORITY 3 -const bool Cron::loadEvents(fs::File &file) +const bool Cron::loadEvents() { + FSmount fs; + File cronFile = FFat.open("/cronjobs.json", FILE_READ, false); + if (!cronFile) + { + LOG_ERROR("Cron failed to open cronjobs.json"); + return false; + } + ArduinoJson::JsonDocument cronFileContent; + if (ArduinoJson::deserializeJson(cronFileContent, cronFile) != ArduinoJson::DeserializationError::Ok) + { + LOG_ERROR("Cron unable to deserialize cronjobs.json"); + return false; + } + + std::string buf; + ArduinoJson::serializeJsonPretty(cronFileContent, buf); + LOG_INFO("Cron loadEvents loaded cronjobs.json\n", buf.c_str()); + + ArduinoJson::JsonArray cronjobList = cronFileContent.as(); + LOG_INFO("Cron loadEvents loaded [", cronjobList.size(), "] events"); + for (const auto &job : cronjobList) + { + const auto &eventName = job["name"].as(); + const auto &cronExpr = job["cronExpr"].as(); + ArduinoJson::JsonDocument action(job["action"]); + if (!addEvent(eventName, cronExpr, action)) + LOG_ERROR("Cron failed to load event [", eventName.c_str(), "]"); + else + LOG_INFO("Cron loaded event [", eventName.c_str(), "]"); + delay(10); + } + cronFile.close(); + return true; +} + +const bool Cron::storeEvents() +{ + FSmount fs; + std::lock_guard lock(m_mutex); + File cronFile = FFat.open("/cronjobs.json", FILE_WRITE, true); + if (!cronFile) + { + LOG_ERROR("Cron failed to open cronjobs.json"); + return false; + } + + ArduinoJson::JsonDocument cronFileContent; + ArduinoJson::JsonArray cronFileArray = cronFileContent.to(); + + for (const auto &job : m_cronMap) // convert cron events map to json file + { + const auto &eventName = job.first; + const auto ¶ms = job.second; + + const auto &cmd = std::get<0>(params); + const auto &cronExpr = std::get<1>(params); + const auto &cmdParams = std::get<3>(params); + + ArduinoJson::JsonDocument thisJob; + thisJob["name"] = eventName; + thisJob["cronExpr"] = cron::to_cronstr(cronExpr); + thisJob["action"]["cmd"] = cmd; + thisJob["action"]["params"] = cmdParams; + + cronFileArray.add(thisJob); + } + + std::string buf; + ArduinoJson::serializeJsonPretty(cronFileContent, buf); + LOG_INFO("Cron storeEvents generated cronjobs.json\n", buf.c_str()); + + ArduinoJson::serializeJson(cronFileContent, cronFile); + cronFile.close(); return true; } const bool Cron::addEvent(const std::string &name, const std::string &expr, const ArduinoJson::JsonDocument action) { + std::lock_guard lock(m_mutex); if (m_cronMap.contains(name)) { LOG_ERROR("Cron event [", name.c_str(), "] already scheduled"); @@ -41,7 +116,7 @@ const bool Cron::addEvent(const std::string &name, const std::string &expr, cons std::tm nowTm = drivers::PCF85063::datetime2tm(now); auto next = cron::cron_next(eventExpr, nowTm); JsonDocument act(params); - LOG_INFO("Cron adding event [", name.c_str(), "] next execution [", drivers::PCF85063::tm2str(next).c_str(), "]"); + LOG_INFO("Cron adding event [", name.c_str(), "] next execution [", drivers::PCF85063::tm2str(next).c_str(), "]"); m_cronMap[name] = std::make_tuple(cmd, eventExpr, next, act); } catch (cron::bad_cronexpr const &ex) @@ -54,6 +129,7 @@ const bool Cron::addEvent(const std::string &name, const std::string &expr, cons const bool Cron::getEvent(const std::string &name, CronEvent &event) { + std::lock_guard lock(m_mutex); if (!m_cronMap.contains(name)) { LOG_ERROR("Cron event [", name.c_str(), "] does not exist"); @@ -66,6 +142,7 @@ const bool Cron::getEvent(const std::string &name, CronEvent &event) const bool Cron::delEvent(const std::string &name) { + std::lock_guard lock(m_mutex); if (!m_cronMap.contains(name)) { LOG_WARN("Cron event [", name.c_str(), "] does not exist"); @@ -78,7 +155,7 @@ const bool Cron::delEvent(const std::string &name) void cronLoop(void *dev) { - auto &cron = Cron::getInstance(*(devices_t*)dev); + auto &cron = Cron::getInstance(*(devices_t *)dev); while (true) { cron.processEvents(); @@ -107,6 +184,7 @@ void Cron::stopCron() const bool Cron::processEvents() { + std::lock_guard lock(m_mutex); LOG_DEBUG("Cron processEvents [", m_cronMap.size(), "]"); drivers::PCF85063::datetime_t now; @@ -126,7 +204,7 @@ const bool Cron::processEvents() auto &cmd = std::get<0>(eventAction); auto &cronexrp = std::get<1>(eventAction); auto &next = std::get<2>(eventAction); - auto ¶ms = std::get<3>(eventAction); + auto &cmdParams = std::get<3>(eventAction); const auto nowPoint = std::chrono::system_clock::from_time_t(std::mktime(&nowTm)); const auto nextEventPoint = std::chrono::system_clock::from_time_t(std::mktime(&next)); @@ -139,7 +217,7 @@ const bool Cron::processEvents() next = cron::cron_next(cronexrp, nowTm); // update next execution time only if event was executed // otherwise time tracking is lost LOG_INFO("Cron running event [", eventName.c_str(), "] next execution time [", drivers::PCF85063::tm2str(next).c_str(), "]"); - commands::commandMap.at(cmd)(m_dev, params); // here the magic happens + commands::commandMap.at(cmd)(m_dev, cmdParams); // here the magic happens } } return true; diff --git a/src/cronjobs.h b/src/cronjobs.h index 6b287f1..714529c 100644 --- a/src/cronjobs.h +++ b/src/cronjobs.h @@ -30,7 +30,8 @@ private: Cron &operator=(const Cron &) = delete; public: - const bool loadEvents(fs::File &file); + const bool loadEvents(); + const bool storeEvents(); const bool addEvent(const std::string &name, const std::string &expr, const ArduinoJson::JsonDocument action); const bool getEvent(const std::string &name, CronEvent &event); const bool delEvent(const std::string &name); @@ -43,4 +44,5 @@ private: const devices_t &m_dev; TaskHandle_t m_cronTaskHandle; std::map m_cronMap; + std::mutex m_mutex; }; \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 4e73dce..87444a1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -57,13 +57,7 @@ void loop() //////////////// CRONJOB //////////////// auto &cron = Cron::getInstance(devices); - ArduinoJson::JsonDocument job; - job["cmd"] = "setIrrigation"; - job["params"]["zone"] = "zone1"; - job["params"]["timeOn"] = 300; - job["params"]["timePause"] = 2; - - cron.addEvent("exampleCronEvent", "0 */5 08,20 * * *", job); + cron.loadEvents(); cron.startCron(); //////////////// CRONJOB //////////////// diff --git a/src/mqtt.h b/src/mqtt.h index 5c4fbb9..ad2df4f 100644 --- a/src/mqtt.h +++ b/src/mqtt.h @@ -1,5 +1,7 @@ #pragma once +#define DEBUGLOG_DEFAULT_LOG_LEVEL_DEBUG + #include #include #include