|
|
|
#ifndef TASK_MANAGER_HPP
|
|
|
|
#define TASK_MANAGER_HPP
|
|
|
|
|
|
|
|
#include <iostream>
|
|
|
|
#include <sstream>
|
|
|
|
#include <string>
|
|
|
|
#include <chrono>
|
|
|
|
#include <thread>
|
|
|
|
#include <functional>
|
|
|
|
#include <cmath>
|
|
|
|
#include <croncpp.h>
|
|
|
|
#include "easylogging++.h"
|
|
|
|
|
|
|
|
using callable = std::function<void (int pin, int value)>;
|
|
|
|
|
|
|
|
enum TaskAction {
|
|
|
|
TASK_ACTION_LOW = 0,
|
|
|
|
TASK_ACTION_HIGH = 1,
|
|
|
|
TASK_ACTION_NONE
|
|
|
|
};
|
|
|
|
|
|
|
|
class Task {
|
|
|
|
private:
|
|
|
|
cron::cronexpr cronexpr;
|
|
|
|
TaskAction action;
|
|
|
|
callable action_fn;
|
|
|
|
int pin;
|
|
|
|
std::string schedule;
|
|
|
|
public:
|
|
|
|
Task(std::string schedule, callable action_fn);
|
|
|
|
Task(Task &task);
|
|
|
|
void doAction();
|
|
|
|
std::time_t next();
|
|
|
|
|
|
|
|
friend std::ostream& operator<< (std::ostream &out, const Task &task);
|
|
|
|
};
|
|
|
|
|
|
|
|
class TaskManager {
|
|
|
|
private:
|
|
|
|
std::map<std::time_t, Task> tasks;
|
|
|
|
std::string human_readable(std::time_t value);
|
|
|
|
public:
|
|
|
|
TaskManager(std::istream &stream, callable action_fn);
|
|
|
|
|
|
|
|
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) {
|
|
|
|
std::string line;
|
|
|
|
while (std::getline(stream, line).good()) {
|
|
|
|
Task task{line, action_fn};
|
|
|
|
|
|
|
|
tasks.emplace(std::piecewise_construct,
|
|
|
|
std::forward_as_tuple(task.next()),
|
|
|
|
std::forward_as_tuple(task));
|
|
|
|
}
|
|
|
|
|
|
|
|
LOG(INFO) << *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline int TaskManager::doWork() {
|
|
|
|
auto it = tasks.begin();
|
|
|
|
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 stop = it->first;
|
|
|
|
|
|
|
|
LOG(INFO) << "Will sleep " << human_readable(stop - now) << " till next action ";
|
|
|
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::seconds{stop - now});
|
|
|
|
|
|
|
|
it->second.doAction();
|
|
|
|
tasks.emplace(std::piecewise_construct,
|
|
|
|
std::forward_as_tuple(it->second.next()),
|
|
|
|
std::forward_as_tuple(it->second));
|
|
|
|
tasks.erase(it);
|
|
|
|
|
|
|
|
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) {
|
|
|
|
out << "Schedule: "<< task.schedule << " Action: ";
|
|
|
|
switch (task.action)
|
|
|
|
{
|
|
|
|
case TASK_ACTION_HIGH:
|
|
|
|
out << "HIGH";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TASK_ACTION_LOW:
|
|
|
|
out << "LOW";
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
out << " Pin:" << task.pin;
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline Task::Task(std::string schedule, callable action_fn):
|
|
|
|
cronexpr{},
|
|
|
|
action{TASK_ACTION_NONE},
|
|
|
|
pin{0},
|
|
|
|
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) {
|
|
|
|
action = TASK_ACTION_HIGH;
|
|
|
|
} else if((command.rfind("LOW", 0) == 0)) {
|
|
|
|
action = TASK_ACTION_LOW;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto start = command.find('(');
|
|
|
|
auto stop = command.find(')');
|
|
|
|
if(start == std::string::npos && stop == std::string::npos) {
|
|
|
|
throw cron::bad_cronexpr("pin is not set in command");
|
|
|
|
}
|
|
|
|
|
|
|
|
LOG(DEBUG) << "pin to parse " << command.substr(start + 1, stop - start - 1);
|
|
|
|
|
|
|
|
pin = std::stoi(command.substr(start + 1, stop - start - 1));
|
|
|
|
}
|
|
|
|
|
|
|
|
inline Task::Task(Task &task):
|
|
|
|
cronexpr{task.cronexpr},
|
|
|
|
action{task.action},
|
|
|
|
action_fn{task.action_fn},
|
|
|
|
pin{task.pin},
|
|
|
|
schedule{task.schedule}
|
|
|
|
{}
|
|
|
|
|
|
|
|
inline void Task::doAction() {
|
|
|
|
if(action == TASK_ACTION_NONE) return;
|
|
|
|
|
|
|
|
action_fn(pin, action);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline std::time_t Task::next() {
|
|
|
|
std::time_t now = std::time(0);
|
|
|
|
return cron::cron_next(cronexpr, now);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif //TASK_MANAGER_HPP
|