Browse Source

readable timestaps in the log

master
Sergey Markov 3 years ago
parent
commit
fe22e92ff1
  1. 28
      cmd_line_args.cpp
  2. 5
      cmd_line_args.h
  3. 26
      main.cpp
  4. 103
      task_manager.hpp

28
cmd_line_args.cpp

@ -6,10 +6,11 @@
static struct option options[] = static struct option options[] =
{ {
{"help", no_argument, NULL, '?'}, {"help", no_argument, NULL, '?'},
{"version", no_argument, NULL, 'v'}, {"version", no_argument, NULL, 'v'},
{"config-file", optional_argument, NULL, 'c'}, {"config-file", optional_argument, NULL, 'c'},
{NULL, 0, NULL, 0 } {"initial-pin-state", optional_argument, NULL, 's'},
{NULL, 0, NULL, 0 }
}; };
static constexpr auto optc = "?v:c:"; static constexpr auto optc = "?v:c:";
@ -18,9 +19,10 @@ static constexpr auto help_format =
"{0} v{1}\n\n" "{0} v{1}\n\n"
"Usage:\n" "Usage:\n"
"Availabe options are:\n" "Availabe options are:\n"
"\t--help Show help\n" "\t--help Show help\n"
"\t--config-file Config file path\n" "\t--config-file Config file path\n"
"\t--version Version of the {0}\n" "\t--initial-pin-state Initial state of control pin\n"
"\t--version Version of the {0}\n"
"\n" "\n"
"Usage example:\n" "Usage example:\n"
"{0} --config-file=scheduler.conf\n" "{0} --config-file=scheduler.conf\n"
@ -31,18 +33,21 @@ static constexpr auto version_format =
void usage() { void usage() {
std::cout << fmt::format(help_format, APP_NAME, APP_VERSION) << std::endl; std::cout << fmt::format(help_format, APP_NAME, APP_VERSION) << std::endl;
exit(0);
} }
void version() { void version() {
std::cout << fmt::format(version_format, APP_NAME, APP_VERSION, APP_GCC) << std::endl; std::cout << fmt::format(version_format, APP_NAME, APP_VERSION, APP_GCC) << std::endl;
exit(0);
} }
parsed_opts parse_opts(int argc, char** argv) { app_options parse_opts(int argc, char** argv) {
if(argc < 1){ if(argc < 1){
exit(1); exit(1);
} }
std::string config_file {""}; std::string config_file {""};
int initial_pin_state = 0;
for(;;) { for(;;) {
int opt_idx, c; int opt_idx, c;
@ -59,10 +64,13 @@ parsed_opts parse_opts(int argc, char** argv) {
case 'c': case 'c':
config_file = optarg; config_file = optarg;
break; break;
case 's':
initial_pin_state = std::stoi(optarg);
break;
default: default:
exit(1); exit(1);
} }
} }
return {config_file}; return {config_file, initial_pin_state};
} }

5
cmd_line_args.h

@ -2,9 +2,10 @@
#define CMD_LINE_ARGS_H #define CMD_LINE_ARGS_H
#include <string> #include <string>
struct parsed_opts { struct app_options {
std::string config_file; std::string config_file;
int initial_pin_state;
}; };
parsed_opts parse_opts(int argc, char** argv); app_options parse_opts(int argc, char** argv);
#endif // CMD_LINE_ARGS_H #endif // CMD_LINE_ARGS_H

26
main.cpp

@ -5,7 +5,10 @@
#include "task_manager.hpp" #include "task_manager.hpp"
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include <chrono>
#include <thread>
using namespace std::chrono;
INITIALIZE_EASYLOGGINGPP INITIALIZE_EASYLOGGINGPP
@ -19,25 +22,36 @@ static std::stringstream schedule = std::stringstream(
"* * * * * SUN LOW(0)\n" "* * * * * SUN LOW(0)\n"
); );
inline std::ostream& operator<< (std::ostream &out, const app_options &opts) {
return out << "Options: { "
<< "config-file: " << opts.config_file << ", "
<< "initial-pin-state: " << opts.initial_pin_state << " }";
}
int main(int argc, char** argv) { int main(int argc, char** argv) {
el::Loggers::reconfigureAllLoggers(el::ConfigurationType::Filename, APP_NAME ".log");
el::Loggers::reconfigureAllLoggers(el::ConfigurationType::ToStandardOutput, "true"); el::Loggers::reconfigureAllLoggers(el::ConfigurationType::ToStandardOutput, "true");
el::Loggers::addFlag(el::LoggingFlag::ColoredTerminalOutput); el::Loggers::addFlag(el::LoggingFlag::ColoredTerminalOutput);
parsed_opts opts = parse_opts(argc, argv); app_options opts = parse_opts(argc, argv);
if(opts.config_file.empty()) { if(opts.config_file.empty()) {
LOG(ERROR) << "Config file is not set!"; LOG(WARNING) << "Config file is not set! So we use hardcoded configuration:" << std::endl
return 0; << schedule.str();
} }
LOG(INFO) << opts.config_file; LOG(INFO) << opts;
TaskManager manager{schedule, digitalWrite}; TaskManager manager{schedule, digitalWrite};
wiringPiSetup (); wiringPiSetup ();
pinMode (PIN, OUTPUT); pinMode (PIN, OUTPUT);
digitalWrite(PIN, opts.initial_pin_state);
while(true) { while(true) {
manager.doWork(); if(manager.doWork() == 0) {
std::this_thread::sleep_for(1s);
}
} }
return 0; return 0;

103
task_manager.hpp

@ -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() {

Loading…
Cancel
Save