cron job load and store events

This commit is contained in:
Emanuele Trabattoni
2025-07-27 15:49:40 +02:00
parent ad90702ab6
commit 1d1eb6fbfa
9 changed files with 163 additions and 50 deletions

View File

@@ -82,7 +82,7 @@
"cmd": "setCronJob", "cmd": "setCronJob",
"params": { "params": {
"name": "nomedeljob", "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" "action": "qua ci va un dizionario come se arrivasse da mqtt, cosi li interpreto alla stessa maniera"
} }
}, },

View File

@@ -5,39 +5,7 @@
#include <DebugLog.h> #include <DebugLog.h>
#include <Arduino.h> #include <Arduino.h>
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <FFat.h> #include <fsmount.h>
#include <mutex>
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");
}
};
class Config class Config
{ {

38
include/fsmount.h Normal file
View File

@@ -0,0 +1,38 @@
#pragma once
#define DEBUGLOG_DEFAULT_LOG_LEVEL_DEBUG
#include <DebugLog.h>
#include <Arduino.h>
#include <FFat.h>
#include <mutex>
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");
}
};

View File

@@ -51,13 +51,26 @@ namespace commands
// CRONJOBS // // CRONJOBS //
// CRONJOBS // // CRONJOBS //
const ArduinoJson::JsonDocument Commands::loadCronjob(const devices_t &dev, const ArduinoJson::JsonDocument &params){
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 &params) const ArduinoJson::JsonDocument Commands::setCronjob(const devices_t &dev, const ArduinoJson::JsonDocument &params)
{ {
ArduinoJson::JsonDocument response; ArduinoJson::JsonDocument response;
response["cmd"] = "setCronjob"; response["cmd"] = "setCronjob";
const auto &jobName = params["name"].as<std::string>(); const auto &jobName = params["name"].as<std::string>();
const auto &timeStr = params["timeStr"].as<std::string>(); const auto &timeStr = params["cronExpr"].as<std::string>();
const auto &actionStr = params["action"].as<std::string>(); const auto &actionStr = params["action"].as<std::string>();
ArduinoJson::JsonDocument action; ArduinoJson::JsonDocument action;
@@ -94,14 +107,14 @@ namespace commands
} }
auto cmd = std::get<0>(event); auto cmd = std::get<0>(event);
auto chronExpr = std::get<1>(event); auto cronExpr = std::get<1>(event);
auto cmdParams = std::get<3>(event); auto cmdParams = std::get<3>(event);
ArduinoJson::JsonDocument action; ArduinoJson::JsonDocument action;
action["cmd"] = cmd; action["cmd"] = cmd;
action["params"] = cmdParams; action["params"] = cmdParams;
response["values"]["name"] = eventName; response["values"]["name"] = eventName;
response["values"]["timeStr"] = cron::to_cronstr(chronExpr); response["values"]["cronExpr"] = cron::to_cronstr(cronExpr);
response["values"]["action"] = action; response["values"]["action"] = action;
LOG_INFO("getCronjob get job [", eventName.c_str(), "]"); LOG_INFO("getCronjob get job [", eventName.c_str(), "]");
@@ -122,6 +135,20 @@ namespace commands
response["values"]["status"] = "valid"; response["values"]["status"] = "valid";
return response; return response;
} }
const ArduinoJson::JsonDocument Commands::storeCronjob(const devices_t &dev, const ArduinoJson::JsonDocument &params)
{
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 //
// CRONJOBS // // CRONJOBS //

View File

@@ -69,9 +69,11 @@ namespace commands
static const ArduinoJson::JsonDocument getConfig(const devices_t &dev, const ArduinoJson::JsonDocument &params); static const ArduinoJson::JsonDocument getConfig(const devices_t &dev, const ArduinoJson::JsonDocument &params);
// CRONJOBS // // CRONJOBS //
static const ArduinoJson::JsonDocument loadCronjob(const devices_t &dev, const ArduinoJson::JsonDocument &params);
static const ArduinoJson::JsonDocument setCronjob(const devices_t &dev, const ArduinoJson::JsonDocument &params); static const ArduinoJson::JsonDocument setCronjob(const devices_t &dev, const ArduinoJson::JsonDocument &params);
static const ArduinoJson::JsonDocument getCronjob(const devices_t &dev, const ArduinoJson::JsonDocument &params); static const ArduinoJson::JsonDocument getCronjob(const devices_t &dev, const ArduinoJson::JsonDocument &params);
static const ArduinoJson::JsonDocument delCronjob(const devices_t &dev, const ArduinoJson::JsonDocument &params); static const ArduinoJson::JsonDocument delCronjob(const devices_t &dev, const ArduinoJson::JsonDocument &params);
static const ArduinoJson::JsonDocument storeCronjob(const devices_t &dev, const ArduinoJson::JsonDocument &params);
// SETTERS // // SETTERS //
static const ArduinoJson::JsonDocument setHPlimit(const devices_t &dev, const ArduinoJson::JsonDocument &params); static const ArduinoJson::JsonDocument setHPlimit(const devices_t &dev, const ArduinoJson::JsonDocument &params);
@@ -93,9 +95,11 @@ namespace commands
{"setConfig", Commands::setConfig}, {"setConfig", Commands::setConfig},
{"getConfig", Commands::getConfig}, {"getConfig", Commands::getConfig},
{"loadCronjob", Commands::loadCronjob},
{"setCronjob", Commands::setCronjob}, {"setCronjob", Commands::setCronjob},
{"getCronjob", Commands::getCronjob}, {"getCronjob", Commands::getCronjob},
{"delCronjob", Commands::delCronjob}, {"delCronjob", Commands::delCronjob},
{"storeCronjob", Commands::storeCronjob},
{"setHPlimit", Commands::setHPlimit}, {"setHPlimit", Commands::setHPlimit},
{"setHeating", Commands::setHeating}, {"setHeating", Commands::setHeating},

View File

@@ -1,16 +1,91 @@
#include <cronjobs.h> #include <cronjobs.h>
#include <chrono> #include <chrono>
#include <fsmount.h>
#define STACK_DEPTH 4096 #define STACK_DEPTH 4096
#define PRIORITY 3 #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<JsonArray>();
LOG_INFO("Cron loadEvents loaded [", cronjobList.size(), "] events");
for (const auto &job : cronjobList)
{
const auto &eventName = job["name"].as<std::string>();
const auto &cronExpr = job["cronExpr"].as<std::string>();
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<std::mutex> 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<JsonArray>();
for (const auto &job : m_cronMap) // convert cron events map to json file
{
const auto &eventName = job.first;
const auto &params = 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; return true;
} }
const bool Cron::addEvent(const std::string &name, const std::string &expr, const ArduinoJson::JsonDocument action) const bool Cron::addEvent(const std::string &name, const std::string &expr, const ArduinoJson::JsonDocument action)
{ {
std::lock_guard<std::mutex> lock(m_mutex);
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");
@@ -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) const bool Cron::getEvent(const std::string &name, CronEvent &event)
{ {
std::lock_guard<std::mutex> lock(m_mutex);
if (!m_cronMap.contains(name)) if (!m_cronMap.contains(name))
{ {
LOG_ERROR("Cron event [", name.c_str(), "] does not exist"); 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) const bool Cron::delEvent(const std::string &name)
{ {
std::lock_guard<std::mutex> lock(m_mutex);
if (!m_cronMap.contains(name)) if (!m_cronMap.contains(name))
{ {
LOG_WARN("Cron event [", name.c_str(), "] does not exist"); LOG_WARN("Cron event [", name.c_str(), "] does not exist");
@@ -107,6 +184,7 @@ void Cron::stopCron()
const bool Cron::processEvents() const bool Cron::processEvents()
{ {
std::lock_guard<std::mutex> lock(m_mutex);
LOG_DEBUG("Cron processEvents [", m_cronMap.size(), "]"); LOG_DEBUG("Cron processEvents [", m_cronMap.size(), "]");
drivers::PCF85063::datetime_t now; drivers::PCF85063::datetime_t now;
@@ -126,7 +204,7 @@ const bool Cron::processEvents()
auto &cmd = std::get<0>(eventAction); auto &cmd = std::get<0>(eventAction);
auto &cronexrp = std::get<1>(eventAction); auto &cronexrp = std::get<1>(eventAction);
auto &next = std::get<2>(eventAction); auto &next = std::get<2>(eventAction);
auto &params = 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 nowPoint = std::chrono::system_clock::from_time_t(std::mktime(&nowTm));
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));
@@ -139,7 +217,7 @@ const bool Cron::processEvents()
next = cron::cron_next(cronexrp, nowTm); // update next execution time only if event was executed next = cron::cron_next(cronexrp, nowTm); // update next execution time only if event was executed
// otherwise time tracking is lost // otherwise time tracking is lost
LOG_INFO("Cron running event [", eventName.c_str(), "] next execution time [", drivers::PCF85063::tm2str(next).c_str(), "]"); 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; return true;

View File

@@ -30,7 +30,8 @@ private:
Cron &operator=(const Cron &) = delete; Cron &operator=(const Cron &) = delete;
public: 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 addEvent(const std::string &name, const std::string &expr, const ArduinoJson::JsonDocument action);
const bool getEvent(const std::string &name, CronEvent &event); const bool getEvent(const std::string &name, CronEvent &event);
const bool delEvent(const std::string &name); const bool delEvent(const std::string &name);
@@ -43,4 +44,5 @@ private:
const devices_t &m_dev; const devices_t &m_dev;
TaskHandle_t m_cronTaskHandle; TaskHandle_t m_cronTaskHandle;
std::map<std::string, CronEvent> m_cronMap; std::map<std::string, CronEvent> m_cronMap;
std::mutex m_mutex;
}; };

View File

@@ -57,13 +57,7 @@ void loop()
//////////////// CRONJOB //////////////// //////////////// CRONJOB ////////////////
auto &cron = Cron::getInstance(devices); auto &cron = Cron::getInstance(devices);
ArduinoJson::JsonDocument job; cron.loadEvents();
job["cmd"] = "setIrrigation";
job["params"]["zone"] = "zone1";
job["params"]["timeOn"] = 300;
job["params"]["timePause"] = 2;
cron.addEvent("exampleCronEvent", "0 */5 08,20 * * *", job);
cron.startCron(); cron.startCron();
//////////////// CRONJOB //////////////// //////////////// CRONJOB ////////////////

View File

@@ -1,5 +1,7 @@
#pragma once #pragma once
#define DEBUGLOG_DEFAULT_LOG_LEVEL_DEBUG
#include <DebugLog.h> #include <DebugLog.h>
#include <Arduino.h> #include <Arduino.h>
#include <ArduinoJson.h> #include <ArduinoJson.h>