From ad90702ab6cdc4ccdf37bab3f00c7fea48b1b044 Mon Sep 17 00:00:00 2001 From: Emanuele Trabattoni Date: Sun, 27 Jul 2025 13:59:50 +0200 Subject: [PATCH] string conversion utility in rtc driver --- lib/RTC/PCF85063_Driver.cpp | 6 +++ lib/RTC/PCF85063_Driver.h | 1 + src/commands.cpp | 60 ++++++++++++++++++++++++++++-- src/cronjobs.cpp | 73 ++++++++++++++++++++----------------- src/cronjobs.h | 22 ++++++++--- src/main.cpp | 20 ++++------ 6 files changed, 128 insertions(+), 54 deletions(-) diff --git a/lib/RTC/PCF85063_Driver.cpp b/lib/RTC/PCF85063_Driver.cpp index 2a7a6ad..e8efc79 100644 --- a/lib/RTC/PCF85063_Driver.cpp +++ b/lib/RTC/PCF85063_Driver.cpp @@ -185,6 +185,12 @@ namespace drivers return buf.substr(0, std::min(buf.find('\n'),buf.find('\r'))); } + const std::string PCF85063::tm2str(const std::tm &datetime) + { + const std::string buf(std::asctime(&datetime)); + return buf.substr(0, std::min(buf.find('\n'),buf.find('\r'))); + } + const std::tm PCF85063::datetime2tm(const datetime_t& datetime) { tm dtime; dtime.tm_sec = datetime.second; diff --git a/lib/RTC/PCF85063_Driver.h b/lib/RTC/PCF85063_Driver.h index 97d8cc2..9276926 100644 --- a/lib/RTC/PCF85063_Driver.h +++ b/lib/RTC/PCF85063_Driver.h @@ -102,6 +102,7 @@ namespace drivers const std::string getTimeStr(); static const std::string datetime2str(const datetime_t &datetime); + static const std::string tm2str(const std::tm &datetime); static const std::tm datetime2tm(const datetime_t& datetime); static const PCF85063::datetime_t fromEpoch(const time_t currentTime); diff --git a/src/commands.cpp b/src/commands.cpp index 2bf8a5d..bf08de5 100644 --- a/src/commands.cpp +++ b/src/commands.cpp @@ -1,4 +1,5 @@ #include +#include namespace commands { @@ -53,19 +54,72 @@ namespace commands const ArduinoJson::JsonDocument Commands::setCronjob(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms) { ArduinoJson::JsonDocument response; - LOG_WARN("setCronjob not yet implemented"); + response["cmd"] = "setCronjob"; + + const auto &jobName = params["name"].as(); + const auto &timeStr = params["timeStr"].as(); + const auto &actionStr = params["action"].as(); + + ArduinoJson::JsonDocument action; + if (ArduinoJson::deserializeJson(action, actionStr) != ArduinoJson::DeserializationError::Ok) + { + LOG_ERROR("setCronJob unable to deserialize cron job [", actionStr.c_str(), "]"); + response["value"]["status"] = "invalid"; + return response; + } + + auto &cron = Cron::getInstance(dev); + if (!cron.addEvent(jobName, timeStr, action)) + { + LOG_ERROR("setCronJob unable to add job [", actionStr.c_str(), "]"); + response["value"]["status"] = "invalid"; + return response; + } + LOG_INFO("setCronJob added job [", actionStr.c_str(), "]"); + response["value"]["status"] = "valid"; return response; } const ArduinoJson::JsonDocument Commands::getCronjob(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms) { ArduinoJson::JsonDocument response; - LOG_WARN("getCronjob not yet implemented"); + response["cmd"] = "getCronjob"; + auto &cron = Cron::getInstance(dev); + auto eventName = params["name"].as(); + Cron::CronEvent event; + if (eventName.empty() || !cron.getEvent(eventName, event)) + { + LOG_ERROR("delCronjob failed to get job [", eventName.c_str(), "]"); + response["values"]["status"] = "invalid"; + return response; + } + + auto cmd = std::get<0>(event); + auto chronExpr = 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"]["action"] = action; + + LOG_INFO("getCronjob get job [", eventName.c_str(), "]"); return response; } const ArduinoJson::JsonDocument Commands::delCronjob(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms) { ArduinoJson::JsonDocument response; - LOG_WARN("delCronjob not yet implemented"); + response["cmd"] = "delCronjob"; + auto &cron = Cron::getInstance(dev); + auto eventName = params["name"].as(); + if (eventName.empty() || !cron.delEvent(eventName)) + { + LOG_ERROR("delCronjob failed to delete job [", eventName.c_str(), "]"); + response["values"]["status"] = "invalid"; + return response; + } + response["values"]["status"] = "valid"; return response; } // CRONJOBS // diff --git a/src/cronjobs.cpp b/src/cronjobs.cpp index 731823c..2dbb4f9 100644 --- a/src/cronjobs.cpp +++ b/src/cronjobs.cpp @@ -4,27 +4,19 @@ #define STACK_DEPTH 4096 #define PRIORITY 3 -Cron::Cron(devices_t &dev) : m_dev(dev) -{ -} - -Cron::~Cron() -{ -} - const bool Cron::loadEvents(fs::File &file) { return true; } -const bool Cron::addEvent(const std::string &name, const std::string &command, const std::string &expr) +const bool Cron::addEvent(const std::string &name, const std::string &expr, const ArduinoJson::JsonDocument action) { if (m_cronMap.contains(name)) { LOG_ERROR("Cron event [", name.c_str(), "] already scheduled"); return false; } - if (name.empty() || command.empty() || expr.empty()) + if (name.empty() || expr.empty() || action.isNull()) { LOG_ERROR("Cron event invalid parameters"); return false; @@ -33,20 +25,13 @@ const bool Cron::addEvent(const std::string &name, const std::string &command, c try { const auto eventExpr(cron::make_cron(expr)); - ArduinoJson::JsonDocument action; - if (ArduinoJson::deserializeJson(action, command) != ArduinoJson::DeserializationError::Ok) - { - LOG_ERROR("Cron unable to deserialize command [", command.c_str(), "]"); - return false; - } const auto cmd = action["cmd"].as(); - const auto params = action["params"].as(); + const auto params = action["params"]; if (!commands::commandMap.contains(cmd)) { - LOG_ERROR("Cron unknown command [", command.c_str(), "]"); + LOG_ERROR("Cron unknown command [", cmd.c_str(), "]"); return false; } - LOG_INFO("Cron added event [", name.c_str(), "]"); drivers::PCF85063::datetime_t now; if (!m_dev.rtc.readDatetime(now)) { @@ -54,7 +39,10 @@ const bool Cron::addEvent(const std::string &name, const std::string &command, c return false; } std::tm nowTm = drivers::PCF85063::datetime2tm(now); - m_cronMap[name] = std::make_tuple(cmd, eventExpr, cron::cron_next(eventExpr, nowTm), params); + 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(), "]"); + m_cronMap[name] = std::make_tuple(cmd, eventExpr, next, act); } catch (cron::bad_cronexpr const &ex) { @@ -64,6 +52,18 @@ const bool Cron::addEvent(const std::string &name, const std::string &command, c return true; } +const bool Cron::getEvent(const std::string &name, CronEvent &event) +{ + if (!m_cronMap.contains(name)) + { + LOG_ERROR("Cron event [", name.c_str(), "] does not exist"); + return false; + } + LOG_INFO("Cron get event [", name.c_str(), "]"); + event = m_cronMap.at(name); + return true; +} + const bool Cron::delEvent(const std::string &name) { if (!m_cronMap.contains(name)) @@ -76,23 +76,29 @@ const bool Cron::delEvent(const std::string &name) return true; } -void cronLoop(void* params) { - auto cron = (Cron*)(params); - while (true) { - cron->processEvents(); +void cronLoop(void *dev) +{ + auto &cron = Cron::getInstance(*(devices_t*)dev); + while (true) + { + cron.processEvents(); delay(1000); } } -void Cron::startCron() { - if (!m_cronTaskHandle) { +void Cron::startCron() +{ + if (!m_cronTaskHandle) + { LOG_INFO("Cron starting loop"); - xTaskCreate(cronLoop, "cronLoop", STACK_DEPTH, this, PRIORITY, &m_cronTaskHandle); + xTaskCreate(cronLoop, "cronLoop", STACK_DEPTH, (void *)&m_dev, PRIORITY, &m_cronTaskHandle); } } -void Cron::stopCron() { - if (m_cronTaskHandle) { +void Cron::stopCron() +{ + if (m_cronTaskHandle) + { LOG_WARN("Cron stopping loop"); vTaskDelete(m_cronTaskHandle); m_cronTaskHandle = NULL; @@ -126,14 +132,15 @@ const bool Cron::processEvents() const auto nextEventPoint = std::chrono::system_clock::from_time_t(std::mktime(&next)); LOG_DEBUG("Cron current time [", std::asctime(&nowTm), "]"); - LOG_DEBUG("Cron checking event [", eventName.c_str(), "] executionTime [", std::asctime(&next), "]"); + LOG_DEBUG("Cron checking event [", eventName.c_str(), "] executionTime [", drivers::PCF85063::tm2str(next).c_str(), "]"); if (nextEventPoint <= nowPoint) // execution time hs passed, run event { - LOG_INFO("Cron executing event [", eventName.c_str(), "]"); + 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 - next = cron::cron_next(cronexrp, nowTm); // update next execution time only if event was executed - } // otherwise time tracking is lost + } } return true; } \ No newline at end of file diff --git a/src/cronjobs.h b/src/cronjobs.h index a8292f5..6b287f1 100644 --- a/src/cronjobs.h +++ b/src/cronjobs.h @@ -14,13 +14,25 @@ class Cron { +public: // eventName cronExpression nextExec command parameters + using CronEvent = std::tuple; public: - Cron(devices_t &dev); - ~Cron(); + static Cron &getInstance(const devices_t &dev) + { + static Cron instance(dev); + return instance; + } +private: + Cron(const devices_t &dev) : m_dev(dev) {}; + Cron(const Cron &) = delete; + Cron &operator=(const Cron &) = delete; + +public: const bool loadEvents(fs::File &file); - const bool addEvent(const std::string &name, const std::string &command, const std::string &expr); + 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); void startCron(); @@ -28,7 +40,7 @@ public: const bool processEvents(); private: - devices_t &m_dev; + const devices_t &m_dev; TaskHandle_t m_cronTaskHandle; - std::map> m_cronMap; + std::map m_cronMap; }; \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 626467f..4e73dce 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -51,26 +51,20 @@ void loop() devices_t devices(rtc, tmp, seneca, buzzer, led, io); //////////////// DEVICES //////////////// - //////////////// NETWORK //////////////// + //////////////// MQTT //////////////// auto mqtt = MQTTwrapper(); - //////////////// NETWORK //////////////// - - + //////////////// MQTT //////////////// + //////////////// CRONJOB //////////////// - auto cron = Cron(devices); + auto &cron = Cron::getInstance(devices); ArduinoJson::JsonDocument job; job["cmd"] = "setIrrigation"; job["params"]["zone"] = "zone1"; - job["params"]["timeOn"] = 30; + job["params"]["timeOn"] = 300; job["params"]["timePause"] = 2; - { - std::string buf; - serializeJson(job,buf); - LOG_INFO("Example Cronjob -> ", buf.c_str()); - cron.addEvent("exampleCronEvent", buf, "0 */10 * * * *"); - cron.startCron(); - }; + cron.addEvent("exampleCronEvent", "0 */5 08,20 * * *", job); + cron.startCron(); //////////////// CRONJOB //////////////// //////////////// MQTT ////////////////