string conversion utility in rtc driver

This commit is contained in:
Emanuele Trabattoni
2025-07-27 13:59:50 +02:00
parent 448e1bad15
commit ad90702ab6
6 changed files with 128 additions and 54 deletions

View File

@@ -185,6 +185,12 @@ namespace drivers
return buf.substr(0, std::min(buf.find('\n'),buf.find('\r'))); 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) { const std::tm PCF85063::datetime2tm(const datetime_t& datetime) {
tm dtime; tm dtime;
dtime.tm_sec = datetime.second; dtime.tm_sec = datetime.second;

View File

@@ -102,6 +102,7 @@ namespace drivers
const std::string getTimeStr(); const std::string getTimeStr();
static const std::string datetime2str(const datetime_t &datetime); 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 std::tm datetime2tm(const datetime_t& datetime);
static const PCF85063::datetime_t fromEpoch(const time_t currentTime); static const PCF85063::datetime_t fromEpoch(const time_t currentTime);

View File

@@ -1,4 +1,5 @@
#include <commands.h> #include <commands.h>
#include <cronjobs.h>
namespace commands namespace commands
{ {
@@ -53,19 +54,72 @@ namespace commands
const ArduinoJson::JsonDocument Commands::setCronjob(const devices_t &dev, const ArduinoJson::JsonDocument &params) const ArduinoJson::JsonDocument Commands::setCronjob(const devices_t &dev, const ArduinoJson::JsonDocument &params)
{ {
ArduinoJson::JsonDocument response; ArduinoJson::JsonDocument response;
LOG_WARN("setCronjob not yet implemented"); response["cmd"] = "setCronjob";
const auto &jobName = params["name"].as<std::string>();
const auto &timeStr = params["timeStr"].as<std::string>();
const auto &actionStr = params["action"].as<std::string>();
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; return response;
} }
const ArduinoJson::JsonDocument Commands::getCronjob(const devices_t &dev, const ArduinoJson::JsonDocument &params) const ArduinoJson::JsonDocument Commands::getCronjob(const devices_t &dev, const ArduinoJson::JsonDocument &params)
{ {
ArduinoJson::JsonDocument response; ArduinoJson::JsonDocument response;
LOG_WARN("getCronjob not yet implemented"); response["cmd"] = "getCronjob";
auto &cron = Cron::getInstance(dev);
auto eventName = params["name"].as<std::string>();
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; return response;
} }
const ArduinoJson::JsonDocument Commands::delCronjob(const devices_t &dev, const ArduinoJson::JsonDocument &params) const ArduinoJson::JsonDocument Commands::delCronjob(const devices_t &dev, const ArduinoJson::JsonDocument &params)
{ {
ArduinoJson::JsonDocument response; ArduinoJson::JsonDocument response;
LOG_WARN("delCronjob not yet implemented"); response["cmd"] = "delCronjob";
auto &cron = Cron::getInstance(dev);
auto eventName = params["name"].as<std::string>();
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; return response;
} }
// CRONJOBS // // CRONJOBS //

View File

@@ -4,27 +4,19 @@
#define STACK_DEPTH 4096 #define STACK_DEPTH 4096
#define PRIORITY 3 #define PRIORITY 3
Cron::Cron(devices_t &dev) : m_dev(dev)
{
}
Cron::~Cron()
{
}
const bool Cron::loadEvents(fs::File &file) const bool Cron::loadEvents(fs::File &file)
{ {
return true; 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)) if (m_cronMap.contains(name))
{ {
LOG_ERROR("Cron event [", name.c_str(), "] already scheduled"); LOG_ERROR("Cron event [", name.c_str(), "] already scheduled");
return false; return false;
} }
if (name.empty() || command.empty() || expr.empty()) if (name.empty() || expr.empty() || action.isNull())
{ {
LOG_ERROR("Cron event invalid parameters"); LOG_ERROR("Cron event invalid parameters");
return false; return false;
@@ -33,20 +25,13 @@ const bool Cron::addEvent(const std::string &name, const std::string &command, c
try try
{ {
const auto eventExpr(cron::make_cron(expr)); 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<std::string>(); const auto cmd = action["cmd"].as<std::string>();
const auto params = action["params"].as<JsonObject>(); const auto params = action["params"];
if (!commands::commandMap.contains(cmd)) if (!commands::commandMap.contains(cmd))
{ {
LOG_ERROR("Cron unknown command [", command.c_str(), "]"); LOG_ERROR("Cron unknown command [", cmd.c_str(), "]");
return false; return false;
} }
LOG_INFO("Cron added event [", name.c_str(), "]");
drivers::PCF85063::datetime_t now; drivers::PCF85063::datetime_t now;
if (!m_dev.rtc.readDatetime(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; return false;
} }
std::tm nowTm = drivers::PCF85063::datetime2tm(now); 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) 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; 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) const bool Cron::delEvent(const std::string &name)
{ {
if (!m_cronMap.contains(name)) if (!m_cronMap.contains(name))
@@ -76,23 +76,29 @@ const bool Cron::delEvent(const std::string &name)
return true; return true;
} }
void cronLoop(void* params) { void cronLoop(void *dev)
auto cron = (Cron*)(params); {
while (true) { auto &cron = Cron::getInstance(*(devices_t*)dev);
cron->processEvents(); while (true)
{
cron.processEvents();
delay(1000); delay(1000);
} }
} }
void Cron::startCron() { void Cron::startCron()
if (!m_cronTaskHandle) { {
if (!m_cronTaskHandle)
{
LOG_INFO("Cron starting loop"); 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() { void Cron::stopCron()
if (m_cronTaskHandle) { {
if (m_cronTaskHandle)
{
LOG_WARN("Cron stopping loop"); LOG_WARN("Cron stopping loop");
vTaskDelete(m_cronTaskHandle); vTaskDelete(m_cronTaskHandle);
m_cronTaskHandle = NULL; m_cronTaskHandle = NULL;
@@ -126,14 +132,15 @@ const bool Cron::processEvents()
const auto nextEventPoint = std::chrono::system_clock::from_time_t(std::mktime(&next)); const auto nextEventPoint = std::chrono::system_clock::from_time_t(std::mktime(&next));
LOG_DEBUG("Cron current time [", std::asctime(&nowTm), "]"); 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 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 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; return true;
} }

View File

@@ -14,13 +14,25 @@
class Cron class Cron
{ {
public: // eventName cronExpression nextExec command parameters
using CronEvent = std::tuple<std::string, cron::cronexpr, std::tm, ArduinoJson::JsonDocument>;
public: public:
Cron(devices_t &dev); static Cron &getInstance(const devices_t &dev)
~Cron(); {
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 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); const bool delEvent(const std::string &name);
void startCron(); void startCron();
@@ -28,7 +40,7 @@ public:
const bool processEvents(); const bool processEvents();
private: private:
devices_t &m_dev; const devices_t &m_dev;
TaskHandle_t m_cronTaskHandle; TaskHandle_t m_cronTaskHandle;
std::map<std::string, std::tuple<std::string, cron::cronexpr, std::tm, ArduinoJson::JsonDocument>> m_cronMap; std::map<std::string, CronEvent> m_cronMap;
}; };

View File

@@ -51,26 +51,20 @@ void loop()
devices_t devices(rtc, tmp, seneca, buzzer, led, io); devices_t devices(rtc, tmp, seneca, buzzer, led, io);
//////////////// DEVICES //////////////// //////////////// DEVICES ////////////////
//////////////// NETWORK //////////////// //////////////// MQTT ////////////////
auto mqtt = MQTTwrapper(); auto mqtt = MQTTwrapper();
//////////////// NETWORK //////////////// //////////////// MQTT ////////////////
//////////////// CRONJOB //////////////// //////////////// CRONJOB ////////////////
auto cron = Cron(devices); auto &cron = Cron::getInstance(devices);
ArduinoJson::JsonDocument job; ArduinoJson::JsonDocument job;
job["cmd"] = "setIrrigation"; job["cmd"] = "setIrrigation";
job["params"]["zone"] = "zone1"; job["params"]["zone"] = "zone1";
job["params"]["timeOn"] = 30; job["params"]["timeOn"] = 300;
job["params"]["timePause"] = 2; job["params"]["timePause"] = 2;
{ cron.addEvent("exampleCronEvent", "0 */5 08,20 * * *", job);
std::string buf; cron.startCron();
serializeJson(job,buf);
LOG_INFO("Example Cronjob -> ", buf.c_str());
cron.addEvent("exampleCronEvent", buf, "0 */10 * * * *");
cron.startCron();
};
//////////////// CRONJOB //////////////// //////////////// CRONJOB ////////////////
//////////////// MQTT //////////////// //////////////// MQTT ////////////////