805 lines
25 KiB
C++
805 lines
25 KiB
C++
#include "appinfo.h"
|
|
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <re2/re2.h>
|
|
|
|
AppInfo::~AppInfo() {
|
|
std::cout << "Module: " << getModuleName() << " finished ops." << std::endl;
|
|
}
|
|
|
|
void AppInfo::setProgramName(std::string name)
|
|
{
|
|
this->program_name = name;
|
|
}
|
|
|
|
void AppInfo::setModuleName(std::string name)
|
|
{
|
|
this->module_name = name;
|
|
}
|
|
|
|
void AppInfo::setArgs(std::deque<std::string> args)
|
|
{
|
|
this->args = args;
|
|
}
|
|
|
|
void AppInfo::setWorkingDir(std::string working_dir)
|
|
{
|
|
this->working_dir = working_dir;
|
|
}
|
|
|
|
void AppInfo::setEnv(string_map env)
|
|
{
|
|
this->env = env;
|
|
}
|
|
|
|
void AppInfo::setAfter(std::unordered_map<std::string, std::string> after)
|
|
{
|
|
this->after = after;
|
|
}
|
|
|
|
void AppInfo::setCreateLog(bool create_log)
|
|
{
|
|
this->create_log = create_log;
|
|
}
|
|
|
|
void AppInfo::setRunMode(std::string mode)
|
|
{
|
|
if(mode == "script") {
|
|
this->mode = RunMode::script;
|
|
}
|
|
else if(mode == "daemon") {
|
|
this->mode = RunMode::daemon;
|
|
}
|
|
}
|
|
|
|
void AppInfo::setRunAs(std::string run_as)
|
|
{
|
|
this->run_as = run_as;
|
|
}
|
|
|
|
void AppInfo::setScheduled(bool val)
|
|
{
|
|
this->scheduled = val;
|
|
}
|
|
|
|
void AppInfo::setFinished()
|
|
{
|
|
this->state = State::exited;
|
|
}
|
|
|
|
void AppInfo::setRunning()
|
|
{
|
|
this->state = State::running;
|
|
}
|
|
|
|
void AppInfo::addToPreviousExecutionList(pid_t new_process)
|
|
{
|
|
this->previous_execution_list.push_back(new_process);
|
|
}
|
|
|
|
void AppInfo::insertParent(std::shared_ptr<AppInfo> parent, State state)
|
|
{
|
|
this->parents[parent] = state;
|
|
}
|
|
|
|
bool AppInfo::isParentDependencyCleared(std::osyncstream &out) const
|
|
{
|
|
for(auto &[key, value]: parents) {
|
|
if(key->getState() != value) {
|
|
out << key->getModuleName() << " not at state: " << stateToString(value) <<
|
|
" but is at:" << stateToString(key->getState()) << std::endl;
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
std::string AppInfo::getModuleName() const
|
|
{
|
|
return module_name;
|
|
}
|
|
|
|
std::string AppInfo::getProgramName() const
|
|
{
|
|
return program_name;
|
|
}
|
|
|
|
std::string AppInfo::getRunAs() const
|
|
{
|
|
return run_as;
|
|
}
|
|
|
|
std::string AppInfo::getWorkingDir() const
|
|
{
|
|
return working_dir;
|
|
}
|
|
|
|
AppInfo::string_map AppInfo::getEnv() const
|
|
{
|
|
return env;
|
|
}
|
|
|
|
std::string AppInfo::getAfterString() const
|
|
{
|
|
std::stringstream ss;
|
|
for(auto &[key,value]: after) {
|
|
ss << key << "[" << value << "], ";
|
|
}
|
|
std::string after_str = ss.str();
|
|
if(after_str.size() >= 2)
|
|
after_str = after_str.substr(0, after_str.size() - 2);
|
|
return after_str;
|
|
}
|
|
|
|
std::string AppInfo::getAfterKeys() const
|
|
{
|
|
std::stringstream ss;
|
|
for(auto &[key,value]: after) {
|
|
ss << key << ", ";
|
|
}
|
|
std::string after_str = ss.str();
|
|
if(after_str.size() >= 2)
|
|
after_str = after_str.substr(0, after_str.size() - 2);
|
|
return after_str;
|
|
}
|
|
|
|
std::string AppInfo::getStringRunMode() const
|
|
{
|
|
return runmodeToString();
|
|
}
|
|
|
|
AppInfo::RunMode AppInfo::getRunMode() const
|
|
{
|
|
return this->mode;
|
|
}
|
|
|
|
AppInfo::State AppInfo::getState() const
|
|
{
|
|
return this->state;
|
|
}
|
|
|
|
void AppInfo::copyFromData(AppInfo::AppData &other)
|
|
{
|
|
this->module_name = other.module_name;
|
|
this->program_name = other.program_name;
|
|
this->create_log = other.create_log;
|
|
this->working_dir = other.working_dir;
|
|
for(auto &i: other.args) {
|
|
this->args.push_back(i);
|
|
}
|
|
this->after = other.after;
|
|
this->env = other.env;
|
|
this->run_at = other.run_at;
|
|
this->run_every = other.run_every;
|
|
this->run_on_start = other.run_on_start;
|
|
setRunMode(other.run_mode);
|
|
setRunAs(other.run_as);
|
|
}
|
|
|
|
std::unordered_map<std::shared_ptr<AppInfo>, AppInfo::State> AppInfo::getParents() const
|
|
{
|
|
return this->parents;
|
|
}
|
|
|
|
std::unordered_map<std::string, std::string> AppInfo::getAfter() const
|
|
{
|
|
return after;
|
|
}
|
|
|
|
bool AppInfo::getCreateLog()
|
|
{
|
|
return create_log;
|
|
}
|
|
|
|
bool AppInfo::getScheduled()
|
|
{
|
|
return this->scheduled;
|
|
}
|
|
|
|
bool AppInfo::getRunOnStart()
|
|
{
|
|
return this->run_on_start;
|
|
}
|
|
|
|
std::vector<pid_t> AppInfo::getPreviousExecutionList() const
|
|
{
|
|
return this->previous_execution_list;
|
|
}
|
|
|
|
std::chrono::system_clock::time_point AppInfo::getNextTimeToRun() const
|
|
{
|
|
return this->next_time_to_run;
|
|
}
|
|
|
|
std::deque<std::string> AppInfo::getArgs() const
|
|
{
|
|
return args;
|
|
}
|
|
|
|
AppInfo::AppInfo() {
|
|
|
|
}
|
|
|
|
bool AppInfo::operator==(const AppInfo &other)
|
|
{
|
|
if(this->module_name == other.module_name && this->program_name == other.program_name
|
|
&& this->args == other.args
|
|
&& this->working_dir == other.working_dir
|
|
&& this->after == other.after
|
|
&& this->create_log == other.create_log
|
|
)
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
void AppInfo::printInfo(std::ostream &out)
|
|
{
|
|
out << "************************************App************************************" << std::endl;
|
|
out << "Module: " << module_name << std::endl;
|
|
out << "App Name: " << program_name << std::endl;
|
|
out << "Arg List: " << std::endl;
|
|
int index = 0;
|
|
for(auto &i: args) {
|
|
out << "Arg[" << index++ << "]: " << i << std::endl;
|
|
}
|
|
out << "Run Mode: " << runmodeToString() << std::endl;
|
|
out << "Working Directory: " << working_dir << std::endl;
|
|
std::stringstream ss;
|
|
for(auto &[key,value]: after) {
|
|
ss << key << "[" << value << "], ";
|
|
}
|
|
std::string after = ss.str();
|
|
if(!after.empty() && after.size() >= 2)
|
|
after = after.substr(0,after.size() - 2);
|
|
out << "After: " << after << std::endl;
|
|
std::stringstream ss2, ss3;
|
|
for(auto &[key, value]: parents) {
|
|
if(!key) {
|
|
ss2 << "Invalid Parent" << std::endl;
|
|
}
|
|
else if(value == State::running) {
|
|
ss2 << key->getModuleName() << " At: " << key << " with state: running." << std::endl;
|
|
}
|
|
else if(value == State::exited) {
|
|
ss2 << key->getModuleName() << " At: " << key << " with state: exited." << std::endl;
|
|
}
|
|
else {
|
|
ss2 << key->getModuleName() << " At: " << key << " with state: unknown." << std::endl;
|
|
}
|
|
}
|
|
if(parents.size() != 0) {
|
|
out << "Parents: " << parents.size() << " modules." << std::endl;
|
|
out << ss2.str();
|
|
}
|
|
else {
|
|
out << "Parents: None." << std::endl;
|
|
}
|
|
for(auto &[key,value]: env) {
|
|
ss3 << key << "=" << value << ", ";
|
|
}
|
|
std::string env = ss3.str();
|
|
if(!env.empty() && env.size() >= 2)
|
|
env = env.substr(0,env.size() - 2);
|
|
out << "Environment: " << env << std::endl;
|
|
out << "Create Log: " << create_log << std::endl;
|
|
out << "Run as: " << run_as << std::endl;
|
|
out << "Run on start: " << run_on_start << std::endl;
|
|
const auto* zone = std::chrono::current_zone();
|
|
auto local_now = zone->to_local(this->next_time_to_run);
|
|
auto local_now_string = std::format("{:%F %T}", local_now);
|
|
if((run_at.size() != 0 || run_every.size() != 0)) {
|
|
out << "Next run scheduled at: " << local_now_string << std::endl;
|
|
} else {
|
|
out << "Normal scheduled run." << std::endl;
|
|
}
|
|
out << "************************************End************************************" << std::endl;
|
|
}
|
|
|
|
bool AppInfo::checkValidity()
|
|
{
|
|
// The json is wrong is run on start is set when run_every and run_at are not set
|
|
if(this->run_every.size() == 0 && this->run_at.size() == 0 && this->run_on_start)
|
|
return false;
|
|
bool got_run_every = false;
|
|
bool got_run_at = false;
|
|
// The json is wrong if the format of run_every is wrong
|
|
std::chrono::system_clock::time_point every_time_point, at_time_point;
|
|
if(this->run_every.size() != 0) {
|
|
auto next_time = parseEveryTimeFormat(run_every);
|
|
if(next_time.has_value()) {
|
|
every_time_point = next_time.value();
|
|
got_run_every = true;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
// The json is wrong if the format of run_at is wrong
|
|
if(this->run_at.size() != 0) {
|
|
auto next_time = parseAtTimeFormat(run_at);
|
|
if(next_time.has_value()) {
|
|
at_time_point = next_time.value();
|
|
got_run_at = true;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
// Update next time to run
|
|
if(got_run_every && !got_run_at) {
|
|
scheduled = true;
|
|
next_time_to_run = every_time_point;
|
|
}
|
|
if(!got_run_every && got_run_at) {
|
|
scheduled = true;
|
|
next_time_to_run = at_time_point;
|
|
}
|
|
if(got_run_every && got_run_at) {
|
|
scheduled = true;
|
|
next_time_to_run = std::min(every_time_point, at_time_point);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void AppInfo::updateNextTimeToRun()
|
|
{
|
|
next_time_to_run_parse_failed = false;
|
|
bool got_run_every = false;
|
|
bool got_run_at = false;
|
|
scheduled = false;
|
|
std::chrono::system_clock::time_point every_time_point, at_time_point;
|
|
if(this->run_every.size() != 0) {
|
|
auto next_time = parseEveryTimeFormat(run_every);
|
|
if(next_time.has_value()) {
|
|
every_time_point = next_time.value();
|
|
got_run_every = true;
|
|
}
|
|
else {
|
|
next_time_to_run_parse_failed = true;
|
|
}
|
|
}
|
|
if(this->run_at.size() != 0) {
|
|
auto next_time = parseAtTimeFormat(run_at);
|
|
if(next_time.has_value()) {
|
|
at_time_point = next_time.value();
|
|
got_run_at = true;
|
|
}
|
|
else {
|
|
next_time_to_run_parse_failed = true;
|
|
}
|
|
}
|
|
if(next_time_to_run_parse_failed)
|
|
return;
|
|
if(got_run_every && !got_run_at)
|
|
next_time_to_run = every_time_point;
|
|
if(!got_run_every && got_run_at)
|
|
next_time_to_run = at_time_point;
|
|
if(got_run_every && got_run_at)
|
|
next_time_to_run = std::min(every_time_point, at_time_point);
|
|
scheduled = true;
|
|
}
|
|
|
|
std::optional<std::chrono::system_clock::time_point> AppInfo::parseAtTimeFormat(std::string run_every)
|
|
{
|
|
/* format should be like:
|
|
* "[time] Daily": for running daily at that time
|
|
* "[day] [time] Weekly": for running every week on those days at that time.
|
|
* "[day_month] [time] Monthly": for running every month on those days at that time.
|
|
* [time] format is 24Hrs with [HH:MM:SS]
|
|
* [day] format is any of [Mon, Tue, Wed, Thu, Fri, Sat, Sun]
|
|
* [day_month] format is 1-31.
|
|
* For Weekly, [day] can be a list of days seperated by commas.
|
|
* For Monthly, [day_month] can be a list of days seperated by commas.
|
|
* For Any, [time] can be a list of timestamps seperated by commas.
|
|
*/
|
|
|
|
auto timeParseDaily = [this](std::string temp_run_every, std::chrono::system_clock::time_point now) -> std::optional<std::chrono::system_clock::time_point> {
|
|
using timepoint = std::chrono::system_clock::time_point;
|
|
std::vector<timepoint> possible_time_points;
|
|
std::vector<std::string> time_list = tokenize(temp_run_every, ",");
|
|
const auto* zone = std::chrono::current_zone();
|
|
auto local_now = zone->to_local(now);
|
|
std::string date_part = std::format("{:%F}", local_now);
|
|
|
|
for(auto &time: time_list) {
|
|
std::string string_time_next = date_part + " " + time;
|
|
std::chrono::local_seconds local_next;
|
|
std::istringstream is{string_time_next};
|
|
is >> std::chrono::parse("%F %T", local_next);
|
|
if(is.fail()) {
|
|
return std::nullopt;
|
|
}
|
|
if(local_now >= local_next) {
|
|
local_next = local_next + std::chrono::days(1);
|
|
}
|
|
timepoint next_time_point = zone->to_sys(local_next, std::chrono::choose::earliest);
|
|
possible_time_points.push_back(next_time_point);
|
|
}
|
|
if(possible_time_points.size() == 0) {
|
|
return std::nullopt;
|
|
}
|
|
return *std::ranges::min_element(possible_time_points);
|
|
};
|
|
|
|
auto timeParseWeekly = [this](std::string temp_run_every, std::string week_part, std::chrono::system_clock::time_point now) -> std::optional<std::chrono::system_clock::time_point> {
|
|
using timepoint = std::chrono::system_clock::time_point;
|
|
std::vector<timepoint> possible_time_points;
|
|
std::vector<std::string> time_list = tokenize(temp_run_every, ",");
|
|
std::vector<std::string> week_list = tokenize(week_part, ",");
|
|
const auto* zone = std::chrono::current_zone();
|
|
auto local_now = zone->to_local(now);
|
|
int weekday_of_local_now = integer(std::format("{:%w}", local_now));
|
|
auto getDayOfWeekInteger = [this](std::string day) -> std::optional<int> {
|
|
if(tolower(day) == "sun") {
|
|
return 0;
|
|
}
|
|
else if(tolower(day) == "mon") {
|
|
return 1;
|
|
}
|
|
else if(tolower(day) == "tue") {
|
|
return 2;
|
|
}
|
|
else if(tolower(day) == "wed") {
|
|
return 3;
|
|
}
|
|
else if(tolower(day) == "thu") {
|
|
return 4;
|
|
}
|
|
else if(tolower(day) == "fri") {
|
|
return 5;
|
|
}
|
|
else if(tolower(day) == "sat") {
|
|
return 6;
|
|
}
|
|
else {
|
|
return std::nullopt;
|
|
}
|
|
};
|
|
for(auto &day_of_week: week_list) {
|
|
auto temp_local_now = local_now;
|
|
auto day_of_week_integer_ret = getDayOfWeekInteger(day_of_week);
|
|
if(!day_of_week_integer_ret.has_value()) {
|
|
return std::nullopt;
|
|
}
|
|
int day_of_week_integer = day_of_week_integer_ret.value();
|
|
if(day_of_week_integer != weekday_of_local_now) {
|
|
temp_local_now = temp_local_now + std::chrono::days( (day_of_week_integer - weekday_of_local_now + 7) % 7 );
|
|
}
|
|
std::string date_part = std::format("{:%F}", temp_local_now);
|
|
for(auto &time: time_list) {
|
|
std::string string_time_next = date_part + " " + time;
|
|
std::chrono::local_seconds local_next;
|
|
std::istringstream is{string_time_next};
|
|
is >> std::chrono::parse("%F %T", local_next);
|
|
if(is.fail()) {
|
|
return std::nullopt;
|
|
}
|
|
if(temp_local_now >= local_next && day_of_week_integer == weekday_of_local_now) {
|
|
local_next = local_next + std::chrono::days(7);
|
|
}
|
|
timepoint next_time_point = zone->to_sys(local_next, std::chrono::choose::earliest);
|
|
possible_time_points.push_back(next_time_point);
|
|
}
|
|
}
|
|
if(possible_time_points.size() == 0) {
|
|
return std::nullopt;
|
|
}
|
|
return *std::ranges::min_element(possible_time_points);
|
|
};
|
|
|
|
auto timeParseMonthly = [this](std::string temp_run_every, std::string month_part, std::chrono::system_clock::time_point now) -> std::optional<std::chrono::system_clock::time_point> {
|
|
using timepoint = std::chrono::system_clock::time_point;
|
|
std::vector<timepoint> possible_time_points;
|
|
std::vector<std::string> time_list = tokenize(temp_run_every, ",");
|
|
std::vector<std::string> month_days_list = tokenize(month_part, ",");
|
|
|
|
const auto* zone = std::chrono::current_zone();
|
|
auto local_now = zone->to_local(now);
|
|
std::chrono::year_month_day current_ymd{std::chrono::floor<std::chrono::days>(local_now)};
|
|
for(auto &day_of_month: month_days_list) {
|
|
int day_int = integer(day_of_month);
|
|
if (day_int < 1 || day_int > 31) {
|
|
return std::nullopt;
|
|
}
|
|
std::chrono::day target_day{static_cast<unsigned>(day_int)};
|
|
std::chrono::year_month_day ymd = current_ymd.year() / current_ymd.month() / target_day;
|
|
if (!ymd.ok()) {
|
|
ymd = ymd.year() / ymd.month() / std::chrono::last;
|
|
}
|
|
for(auto &time: time_list) {
|
|
std::chrono::seconds time_of_day;
|
|
std::istringstream is{time};
|
|
is >> std::chrono::parse("%T", time_of_day);
|
|
if (is.fail()) {
|
|
return std::nullopt;
|
|
}
|
|
auto local_next = std::chrono::local_days{ymd} + time_of_day;
|
|
if (local_next <= local_now) {
|
|
auto next_ym = current_ymd.year() / current_ymd.month() + std::chrono::months(1);
|
|
std::chrono::year_month_day ymd_next = next_ym / target_day;
|
|
if (!ymd_next.ok()) {
|
|
ymd_next = next_ym / std::chrono::last;
|
|
}
|
|
local_next = std::chrono::local_days{ymd_next} + time_of_day;
|
|
}
|
|
timepoint next_time_point = zone->to_sys(local_next, std::chrono::choose::earliest);
|
|
possible_time_points.push_back(next_time_point);
|
|
}
|
|
}
|
|
if(possible_time_points.size() == 0) {
|
|
return std::nullopt;
|
|
}
|
|
return *std::ranges::min_element(possible_time_points);
|
|
};
|
|
|
|
using timepoint = std::chrono::system_clock::time_point;
|
|
std::chrono::time_point now = std::chrono::system_clock::now();
|
|
if(run_every.contains("Daily")) {
|
|
std::string temp_run_every = run_every.erase(run_every.find("Daily"));
|
|
temp_run_every = stringStrip(temp_run_every);
|
|
auto options = timeParseDaily(temp_run_every, now);
|
|
if(!options.has_value()) {
|
|
return std::nullopt;
|
|
}
|
|
return options.value();
|
|
}
|
|
else if(run_every.contains("Weekly")) {
|
|
std::string temp_run_weekly = run_every.erase(run_every.find("Weekly"));
|
|
temp_run_weekly = stringStrip(temp_run_weekly);
|
|
std::string week_part;
|
|
std::string time_part;
|
|
if(!RE2::FullMatch(temp_run_weekly,R"(([a-zA-Z ,]+)([0-9: ,]+))", &week_part, &time_part)) {
|
|
return std::nullopt;
|
|
}
|
|
week_part = stringStrip(week_part);
|
|
time_part = stringStrip(time_part);
|
|
auto options = timeParseWeekly(time_part, week_part, now);
|
|
if(!options.has_value()) {
|
|
return std::nullopt;
|
|
}
|
|
return options.value();
|
|
}
|
|
else if(run_every.contains("Monthly")) {
|
|
std::string temp_run_monthly = run_every.erase(run_every.find("Monthly"));
|
|
temp_run_monthly = stringStrip(temp_run_monthly);
|
|
std::string_view view_for_temp_run_monthy(temp_run_monthly);
|
|
std::string temp_time_part;
|
|
std::string time_part = "";
|
|
re2::RE2 r(R"((\d\d:\d\d:\d\d[, ]?)+)");
|
|
while (RE2::FindAndConsume(&view_for_temp_run_monthy, r, &temp_time_part)) {
|
|
time_part += temp_time_part;
|
|
}
|
|
RE2::GlobalReplace(&temp_run_monthly,R"((\d\d:\d\d:\d\d[, ]?)+)", "");
|
|
std::string month_part = stringStrip(temp_run_monthly); time_part = stringStrip(time_part);
|
|
auto options = timeParseMonthly(time_part, month_part, now);
|
|
if(!options.has_value()) {
|
|
return std::nullopt;
|
|
}
|
|
return options.value();
|
|
}
|
|
else {
|
|
return std::nullopt;
|
|
}
|
|
}
|
|
|
|
std::optional<std::chrono::system_clock::time_point> AppInfo::parseEveryTimeFormat(std::string run_every)
|
|
{
|
|
/* format should be like:
|
|
* [digits][unit]
|
|
* valid units are:
|
|
* s or S: seconds
|
|
* m: minutes
|
|
* h or H: hours
|
|
* d or D: days
|
|
* w or W: weeks
|
|
* M: months
|
|
* Example: 15s
|
|
* Example: 2h
|
|
*/
|
|
int duration = 0;
|
|
std::string unit;
|
|
if(!RE2::FullMatch(run_every,R"((\d+)([sSmMhHdDwW]))", &duration, &unit)) {
|
|
return std::nullopt;
|
|
}
|
|
if(duration <= 0) {
|
|
return std::nullopt;
|
|
}
|
|
if(unit.size() <= 0) {
|
|
return std::nullopt;
|
|
}
|
|
char ch = unit[0];
|
|
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
|
|
const auto* zone = std::chrono::current_zone();
|
|
auto local_now = zone->to_local(now);
|
|
|
|
switch(ch) {
|
|
case 's':
|
|
case 'S': {
|
|
return zone->to_sys(local_now + std::chrono::seconds(duration), std::chrono::choose::earliest);
|
|
}
|
|
case 'm': {
|
|
return zone->to_sys(local_now + std::chrono::minutes(duration), std::chrono::choose::earliest);
|
|
}
|
|
case 'h':
|
|
case 'H': {
|
|
return zone->to_sys(local_now + std::chrono::hours(duration), std::chrono::choose::earliest);
|
|
}
|
|
case 'd':
|
|
case 'D': {
|
|
return zone->to_sys(local_now + std::chrono::days(duration), std::chrono::choose::earliest);
|
|
}
|
|
case 'w':
|
|
case 'W': {
|
|
return zone->to_sys(local_now + std::chrono::weeks(duration), std::chrono::choose::earliest);
|
|
}
|
|
case 'M': {
|
|
auto today = std::chrono::floor<std::chrono::days>(local_now);
|
|
auto time_of_day = local_now - today;
|
|
std::chrono::year_month_day ymd{today};
|
|
ymd += std::chrono::months(duration);
|
|
if (!ymd.ok()) {
|
|
ymd = ymd.year() / ymd.month() / std::chrono::last;
|
|
}
|
|
auto next_local = std::chrono::local_days{ymd} + time_of_day;
|
|
return zone->to_sys(next_local, std::chrono::choose::earliest);
|
|
}
|
|
default: {
|
|
return std::nullopt;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string AppInfo::runmodeToString() const
|
|
{
|
|
switch(this->mode) {
|
|
case RunMode::script:
|
|
return "script";
|
|
case RunMode::daemon:
|
|
return "daemon";
|
|
default:
|
|
return "unknown";
|
|
}
|
|
}
|
|
|
|
std::vector<std::string> AppInfo::tokenize(std::string main_string, std::string sub_string)
|
|
{
|
|
std::vector<std::string> tokens;
|
|
size_t pos = 0;
|
|
while ((pos = main_string.find(sub_string)) != std::string::npos) {
|
|
std::string token = stringStrip(main_string.substr(0, pos));
|
|
if (!token.empty()) {
|
|
tokens.push_back(token);
|
|
}
|
|
main_string.erase(0, pos + sub_string.length());
|
|
}
|
|
std::string last_token = stringStrip(main_string);
|
|
if (!last_token.empty()) {
|
|
tokens.push_back(last_token);
|
|
}
|
|
return tokens;
|
|
}
|
|
|
|
std::string AppInfo::stringStrip(const std::string str)
|
|
{
|
|
std::string ch;
|
|
bool front_found = false;
|
|
for(int i=0; i<str.size(); i++) {
|
|
if(!front_found) {
|
|
if(str[i] == ' ' || str[i] == '\t' || str[i] == '\n' || str[i] == '\v' || str[i] == '\r')
|
|
continue;
|
|
else {
|
|
front_found = true;
|
|
ch += str[i];
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
ch += str[i];
|
|
}
|
|
auto pos = ch.find_last_not_of(" \t\n\v\r");
|
|
if(pos == std::string::npos)
|
|
return ch;
|
|
ch.erase(pos+1);
|
|
return ch;
|
|
}
|
|
|
|
int AppInfo::integer(const std::string str)
|
|
{
|
|
std::stringstream ss;
|
|
int i = -1;
|
|
ss << str;
|
|
if (!(ss >> i) || !(ss >> std::ws).eof())
|
|
return -1;
|
|
return i;
|
|
}
|
|
|
|
std::string AppInfo::tolower(const std::string str) const
|
|
{
|
|
std::string s;
|
|
std::transform(str.begin(), str.end(), std::back_inserter(s), [](unsigned char c){
|
|
return static_cast<char>(std::tolower(c));
|
|
});
|
|
return s;
|
|
}
|
|
|
|
std::string AppInfo::stateToString() const
|
|
{
|
|
return stateToString(this->state);
|
|
}
|
|
|
|
std::string AppInfo::stateToString(State s) const
|
|
{
|
|
switch(s) {
|
|
case State::initial:
|
|
return "initial";
|
|
case State::running:
|
|
return "running";
|
|
case State::exited:
|
|
return "exited";
|
|
default:
|
|
return "unknown";
|
|
}
|
|
}
|
|
|
|
void AppInfo::AppData::printInfo(std::ostream &out)
|
|
{
|
|
out << "************************************App************************************" << std::endl;
|
|
out << "Module: " << AppData::module_name << std::endl;
|
|
out << "App Name: " << AppData::program_name << std::endl;
|
|
out << "Arg List: " << std::endl;
|
|
int index = 0;
|
|
for(auto &i: AppData::args) {
|
|
out << "Arg[" << index++ << "]: " << i << std::endl;
|
|
}
|
|
out << "Run Mode: " << run_mode << std::endl;
|
|
out << "Working Directory: " << AppData::working_dir << std::endl;
|
|
std::stringstream ss, ss2;
|
|
for(auto &[key,value]: AppData::after) {
|
|
ss << key << "[" << value << "], ";
|
|
}
|
|
std::string after = ss.str();
|
|
if(!after.empty() && after.size() >= 2)
|
|
after = after.substr(0,after.size() - 2);
|
|
out << "After: " << after << std::endl;
|
|
ss.clear();
|
|
ss.flush();
|
|
for(auto &[key,value]: AppData::env) {
|
|
ss2 << key << "=" << value << ", ";
|
|
}
|
|
std::string env = ss2.str();
|
|
if(!env.empty() && env.size() >= 2)
|
|
env = env.substr(0,env.size() - 2);
|
|
out << "Environment: " << env << std::endl;
|
|
out << "Create Log: " << AppData::create_log << std::endl;
|
|
out << "Run as: " << run_as << std::endl;
|
|
out << "Run on start: " << run_on_start << std::endl;
|
|
out << "Run At: " << run_at << std::endl;
|
|
out << "Run Every: " << run_every << std::endl;
|
|
out << "************************************End************************************" << std::endl;
|
|
}
|
|
|
|
std::string AppInfo::AppData::getAfterString() const
|
|
{
|
|
std::stringstream ss;
|
|
for(auto &[key,value]: after) {
|
|
ss << key << "[" << value << "], ";
|
|
}
|
|
std::string after_str = ss.str();
|
|
if(after_str.size() >= 2)
|
|
after_str = after_str.substr(0, after_str.size() - 2);
|
|
return after_str;
|
|
}
|
|
|
|
std::string AppInfo::AppData::getAfterKeys() const
|
|
{
|
|
std::stringstream ss;
|
|
for(auto &[key,value]: after) {
|
|
ss << key << ", ";
|
|
}
|
|
std::string after_str = ss.str();
|
|
if(after_str.size() >= 2)
|
|
after_str = after_str.substr(0, after_str.size() - 2);
|
|
return after_str;
|
|
}
|