|
|
@ -7,6 +7,7 @@ |
|
|
|
#include <chrono> |
|
|
|
#include <chrono> |
|
|
|
#include <thread> |
|
|
|
#include <thread> |
|
|
|
#include <functional> |
|
|
|
#include <functional> |
|
|
|
|
|
|
|
#include <cmath> |
|
|
|
#include <croncpp.h> |
|
|
|
#include <croncpp.h> |
|
|
|
#include "easylogging++.h" |
|
|
|
#include "easylogging++.h" |
|
|
|
|
|
|
|
|
|
|
@ -24,8 +25,9 @@ private: |
|
|
|
TaskAction action; |
|
|
|
TaskAction action; |
|
|
|
callable action_fn; |
|
|
|
callable action_fn; |
|
|
|
int pin; |
|
|
|
int pin; |
|
|
|
|
|
|
|
std::string schedule; |
|
|
|
public: |
|
|
|
public: |
|
|
|
Task(cron::cronexpr cronexpr, std::string command, callable action_fn); |
|
|
|
Task(std::string schedule, callable action_fn); |
|
|
|
Task(Task &task); |
|
|
|
Task(Task &task); |
|
|
|
void doAction(); |
|
|
|
void doAction(); |
|
|
|
std::time_t next(); |
|
|
|
std::time_t next(); |
|
|
@ -36,51 +38,52 @@ public: |
|
|
|
class TaskManager { |
|
|
|
class TaskManager { |
|
|
|
private: |
|
|
|
private: |
|
|
|
std::map<std::time_t, Task> tasks; |
|
|
|
std::map<std::time_t, Task> tasks; |
|
|
|
|
|
|
|
std::string human_readable(std::time_t value); |
|
|
|
public: |
|
|
|
public: |
|
|
|
TaskManager(std::istream &stream, callable action_fn); |
|
|
|
TaskManager(std::istream &stream, callable action_fn); |
|
|
|
|
|
|
|
|
|
|
|
int doWork(); |
|
|
|
int doWork(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
friend std::ostream& operator<< (std::ostream &out, const TaskManager &tasksMgr); |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
inline std::ostream& operator<< (std::ostream &out, const TaskManager &tasksMgr) { |
|
|
|
|
|
|
|
out << "Schedule table:" << std::endl; |
|
|
|
|
|
|
|
for (const auto& [key, value] : tasksMgr.tasks) { |
|
|
|
|
|
|
|
std::tm * ptm = std::localtime(&key); |
|
|
|
|
|
|
|
char buffer[32]; |
|
|
|
|
|
|
|
// Format: Mo, 15.06.2009 20:20:00
|
|
|
|
|
|
|
|
std::strftime(buffer, 32, "%a, %d.%m.%Y %H:%M:%S", ptm);
|
|
|
|
|
|
|
|
out << "Next time value: " << buffer << " " << value << std::endl; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return out; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
inline TaskManager::TaskManager(std::istream &stream, callable action_fn) { |
|
|
|
inline TaskManager::TaskManager(std::istream &stream, callable action_fn) { |
|
|
|
std::string line; |
|
|
|
std::string line; |
|
|
|
while (std::getline(stream, line).good()) { |
|
|
|
while (std::getline(stream, line).good()) { |
|
|
|
auto fields = cron::utils::split(line, ' '); |
|
|
|
Task task{line, action_fn}; |
|
|
|
fields.erase( |
|
|
|
|
|
|
|
std::remove_if(std::begin(fields), std::end(fields), |
|
|
|
|
|
|
|
[](CRONCPP_STRING_VIEW s) {return s.empty(); }), |
|
|
|
|
|
|
|
std::end(fields)); |
|
|
|
|
|
|
|
if (fields.size() != 7) |
|
|
|
|
|
|
|
throw cron::bad_cronexpr("cron expression must have 7 fields"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::stringstream schedule; |
|
|
|
|
|
|
|
for (int i = 0; i < 6; ++i) { |
|
|
|
|
|
|
|
schedule << fields[i] << ' '; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
auto cron = cron::make_cron(schedule.str()); |
|
|
|
|
|
|
|
std::time_t now = std::time(0); |
|
|
|
|
|
|
|
std::time_t next = cron::cron_next(cron, now);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tasks.emplace(std::piecewise_construct, |
|
|
|
tasks.emplace(std::piecewise_construct, |
|
|
|
std::forward_as_tuple(next), |
|
|
|
std::forward_as_tuple(task.next()), |
|
|
|
std::forward_as_tuple(cron, fields[6], action_fn)); |
|
|
|
std::forward_as_tuple(task)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
LOG(INFO) << "Schedule table:"; |
|
|
|
LOG(INFO) << *this; |
|
|
|
for (const auto& [key, value] : tasks) { |
|
|
|
|
|
|
|
LOG(INFO) << key << ' ' << value; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
inline int TaskManager::doWork() { |
|
|
|
inline int TaskManager::doWork() { |
|
|
|
auto it = tasks.begin(); |
|
|
|
auto it = tasks.begin(); |
|
|
|
if(it == tasks.end()) return 0; |
|
|
|
if(it == tasks.end()) { |
|
|
|
|
|
|
|
LOG(WARNING) << "Table of tasks is empty!!! Nothing to do!!"; |
|
|
|
|
|
|
|
return 0; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
std::time_t now = std::time(0); |
|
|
|
std::time_t now = std::time(0); |
|
|
|
std::time_t stop = it->first; |
|
|
|
std::time_t stop = it->first; |
|
|
|
|
|
|
|
|
|
|
|
LOG(DEBUG) << "Will sleep till next action " << stop - now << 's'; |
|
|
|
LOG(INFO) << "Will sleep " << human_readable(stop - now) << " till next action "; |
|
|
|
|
|
|
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::seconds{stop - now}); |
|
|
|
std::this_thread::sleep_for(std::chrono::seconds{stop - now}); |
|
|
|
|
|
|
|
|
|
|
@ -90,11 +93,30 @@ inline int TaskManager::doWork() { |
|
|
|
std::forward_as_tuple(it->second)); |
|
|
|
std::forward_as_tuple(it->second)); |
|
|
|
tasks.erase(it); |
|
|
|
tasks.erase(it); |
|
|
|
|
|
|
|
|
|
|
|
return 0; |
|
|
|
LOG(INFO) << *this; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return 1; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
inline std::string TaskManager::human_readable(std::time_t value) { |
|
|
|
|
|
|
|
std::stringstream result; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int secs = value % 60; |
|
|
|
|
|
|
|
int mins = std::floor((float)value / 60); |
|
|
|
|
|
|
|
int hours = std::floor((float)mins / 60); |
|
|
|
|
|
|
|
if(mins > 59) { |
|
|
|
|
|
|
|
mins = mins % 60; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
result << std::setfill('0') << std::setw(2) << hours << ':'
|
|
|
|
|
|
|
|
<< std::setfill('0') << std::setw(2) << mins << ':'
|
|
|
|
|
|
|
|
<< std::setfill('0') << std::setw(2) << secs; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return result.str(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
inline std::ostream& operator<< (std::ostream &out, const Task &task) { |
|
|
|
inline std::ostream& operator<< (std::ostream &out, const Task &task) { |
|
|
|
out << "Action: "; |
|
|
|
out << "Schedule: "<< task.schedule << " Action: "; |
|
|
|
switch (task.action) |
|
|
|
switch (task.action) |
|
|
|
{ |
|
|
|
{ |
|
|
|
case TASK_ACTION_HIGH: |
|
|
|
case TASK_ACTION_HIGH: |
|
|
@ -113,12 +135,30 @@ inline std::ostream& operator<< (std::ostream &out, const Task &task) { |
|
|
|
return out; |
|
|
|
return out; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
inline Task::Task(cron::cronexpr cronexpr, std::string command, callable action_fn): |
|
|
|
inline Task::Task(std::string schedule, callable action_fn): |
|
|
|
cronexpr{cronexpr}, |
|
|
|
cronexpr{}, |
|
|
|
action{TASK_ACTION_NONE}, |
|
|
|
action{TASK_ACTION_NONE}, |
|
|
|
pin{0}, |
|
|
|
pin{0}, |
|
|
|
action_fn{action_fn} |
|
|
|
action_fn{action_fn}, |
|
|
|
|
|
|
|
schedule{schedule} |
|
|
|
{ |
|
|
|
{ |
|
|
|
|
|
|
|
auto fields = cron::utils::split(schedule, ' '); |
|
|
|
|
|
|
|
fields.erase( |
|
|
|
|
|
|
|
std::remove_if(std::begin(fields), std::end(fields), |
|
|
|
|
|
|
|
[](CRONCPP_STRING_VIEW s) {return s.empty(); }), |
|
|
|
|
|
|
|
std::end(fields)); |
|
|
|
|
|
|
|
if (fields.size() != 7) |
|
|
|
|
|
|
|
throw cron::bad_cronexpr("cron expression must have 7 fields"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::stringstream line; |
|
|
|
|
|
|
|
for (int i = 0; i < 6; ++i) { |
|
|
|
|
|
|
|
line << fields[i] << ' '; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this->schedule = line.str(); |
|
|
|
|
|
|
|
cronexpr = cron::make_cron(this->schedule); |
|
|
|
|
|
|
|
std::string command{fields[6]}; |
|
|
|
|
|
|
|
|
|
|
|
if(command.rfind("HIGH", 0) == 0) { |
|
|
|
if(command.rfind("HIGH", 0) == 0) { |
|
|
|
action = TASK_ACTION_HIGH; |
|
|
|
action = TASK_ACTION_HIGH; |
|
|
|
} else if((command.rfind("LOW", 0) == 0)) { |
|
|
|
} else if((command.rfind("LOW", 0) == 0)) { |
|
|
@ -140,7 +180,8 @@ inline Task::Task(Task &task): |
|
|
|
cronexpr{task.cronexpr}, |
|
|
|
cronexpr{task.cronexpr}, |
|
|
|
action{task.action}, |
|
|
|
action{task.action}, |
|
|
|
action_fn{task.action_fn}, |
|
|
|
action_fn{task.action_fn}, |
|
|
|
pin{task.pin} |
|
|
|
pin{task.pin}, |
|
|
|
|
|
|
|
schedule{task.schedule} |
|
|
|
{} |
|
|
|
{} |
|
|
|
|
|
|
|
|
|
|
|
inline void Task::doAction() { |
|
|
|
inline void Task::doAction() { |
|
|
|