From 2ab2c119e3e4c83d490a971f2b2048b9d3f54485 Mon Sep 17 00:00:00 2001 From: carykees98 Date: Wed, 20 Nov 2024 16:11:22 -0500 Subject: [PATCH 01/49] i may have forgotten to commit along the way --- mirror-sync-scheduler/CMakeLists.txt | 4 +- mirror-sync-scheduler/TODO.md | 27 ++ mirror-sync-scheduler/src/main.cpp | 209 ++++------ mirror-sync-scheduler/src/queue.cpp | 521 +++++++++++++++---------- mirror-sync-scheduler/src/queue.h | 79 ---- mirror-sync-scheduler/src/queue.hpp | 153 ++++++++ mirror-sync-scheduler/src/schedule.cpp | 273 +++++++------ mirror-sync-scheduler/src/schedule.h | 67 ---- mirror-sync-scheduler/src/schedule.hpp | 134 +++++++ mirror-sync-scheduler/src/utils.hpp | 106 +++++ 10 files changed, 983 insertions(+), 590 deletions(-) create mode 100644 mirror-sync-scheduler/TODO.md delete mode 100644 mirror-sync-scheduler/src/queue.h create mode 100644 mirror-sync-scheduler/src/queue.hpp delete mode 100644 mirror-sync-scheduler/src/schedule.h create mode 100644 mirror-sync-scheduler/src/schedule.hpp create mode 100644 mirror-sync-scheduler/src/utils.hpp diff --git a/mirror-sync-scheduler/CMakeLists.txt b/mirror-sync-scheduler/CMakeLists.txt index 10b7c04..217ee05 100644 --- a/mirror-sync-scheduler/CMakeLists.txt +++ b/mirror-sync-scheduler/CMakeLists.txt @@ -7,6 +7,8 @@ add_executable(${PROJECT_NAME} src/queue.cpp ) +set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 23) + include(FetchContent) FetchContent_Declare(json @@ -20,4 +22,4 @@ add_subdirectory(lib/mirror-logging) target_link_libraries(${PROJECT_NAME} PUBLIC mirror-logging PRIVATE nlohmann_json::nlohmann_json -) \ No newline at end of file +) diff --git a/mirror-sync-scheduler/TODO.md b/mirror-sync-scheduler/TODO.md new file mode 100644 index 0000000..88f45b6 --- /dev/null +++ b/mirror-sync-scheduler/TODO.md @@ -0,0 +1,27 @@ +# Todos + + - Update private class members to use `m_` prefix. + - Convert public members to private members where possible. + - Other todos listed in code (`rg -i TODO`). + - Restructure classes to not use singleton design pattern. + - Move doxygen to the function declarations instead of the definitions + - Convert line-comment doxygen to comment blocks + - Start using const reference where reasonable instead of passing by value. + - There are a lot of loose threads being managed in so many different + contexts. I think there should be a thread pool to manage all threads + other than the main thread + - Use bounds checked access where possible (replace `[]` with `.at()`). + - Extract LCM determination to a function (Use `std::lcm()`?) + - There are a lot of pointers in use that are probably not needed. They + should either be replaced entirely by things that are not pointers or they + should be replaced with smart pointers (`std::shared_ptr`, + `std::unique_ptr`, and `std::weak_ptr`) + - Where possible replace use of `int` with more descriptive types + (`std::size_t`, `std::uint64_t`, `std::byte`, ...) + - Wrap everything in `namespace mirror::sync_scheduler::` + - Revisit `Job` and `Task` structs + - Shrink `main()` massively. + - Take everything that isn't `main()` out of `main.cpp` + - There are global variables that could probably not exist. + - Make things const where possible + - Make things constexpr where possible. diff --git a/mirror-sync-scheduler/src/main.cpp b/mirror-sync-scheduler/src/main.cpp index 97889b2..02a896f 100644 --- a/mirror-sync-scheduler/src/main.cpp +++ b/mirror-sync-scheduler/src/main.cpp @@ -1,174 +1,131 @@ +#include +#include #include -#include #include -#include #include -#include -#include +#include + #include #include +#include #include -using json = nlohmann::json; -#include -#include "schedule.h" -#include "queue.h" +#include "queue.hpp" +#include "schedule.hpp" +#include "utils.hpp" -//used to stop the program cleanly with ctrl c +// used to stop the program cleanly with ctrl c static bool keepRunning = true; -//used by the SIGINT signal handler to end the program -void intHandler(int i){ +// used by the SIGINT signal handler to end the program +auto intHandler(int i) -> void +{ keepRunning = false; } -//read json in from a file -//@param filename std::string json file to read -//@return json object -json readJSONFromFile(std::string filename){ - std::ifstream f(filename); - json config = json::parse(f); - f.close(); - return config; -} - -//thread for handling manual sync through std input -void cin_thread(){ - //create a pointer to the queue - Queue* queue = Queue::getInstance(); - //create a pointer to the schedule - Schedule* schedule = Schedule::getInstance(); - - while(true){ - std::string x; - std::cin >> x; - queue->manual_sync(x); - } -} - -//thread that sends a message to the log server every 29 minutes to keep the socket from closing -void keep_alive_thread(){ - mirror::Logger* logger = mirror::Logger::getInstance(); - - while(true){ - //sleep for 29 minutes - std::this_thread::sleep_for(std::chrono::minutes(29)); - //send keepalive message - logger->info("keep alive."); - } -} - -//thread that updates the schedule and syncCommandMap whenever there is any change made to mirrors.json -void update_schedule_thread(){ - //create a pointer to the schedule - Schedule* schedule = Schedule::getInstance(); - //create a pointer to the queue - Queue* queue = Queue::getInstance(); - - //struct to store file information about mirrors.json - struct stat mirrorsFile; - //initialize the struct - stat("configs/mirrors.json", &mirrorsFile); - - while(true){ - //sleep for 7 seconds because its the smallest number that doesnt divide 60 - std::this_thread::sleep_for(std::chrono::seconds(7)); - - //calculate old and new modification times - int oldModTime = mirrorsFile.st_mtime; - stat("configs/mirrors.json", &mirrorsFile); - - if(oldModTime != mirrorsFile.st_mtime){ - //retrieve the mirror data from mirrors.json - json config = readJSONFromFile("configs/mirrors.json"); - - //build the schedule based on the mirrors.json config - schedule->build(config["mirrors"]); - //create a new sync command map - queue->createSyncCommandMap(config["mirrors"]); - //set reloaded flag for main thread - schedule->reloaded = true; - std::cout << "Reloaded mirrors.json" << std::endl; - } - } -} - -int main(){ - //ctrl c signal handler +auto main() -> int +{ + // ctrl c signal handler signal(SIGINT, intHandler); - //read env data in from env.json - json env = readJSONFromFile("configs/sync-scheduler-env.json"); + // read env data in from env.json + nlohmann::json env = readJSONFromFile("configs/sync-scheduler-env.json"); - //read mirror data in from mirrors.json - json config = readJSONFromFile("configs/mirrors.json"); + // read mirror data in from mirrors.json + nlohmann::json config = readJSONFromFile("configs/mirrors.json"); - //initialize and configure connection to log server + // initialize and configure connection to log server mirror::Logger* logger = mirror::Logger::getInstance(); - logger->configure(env["logServerPort"], "Sync Scheduler", env["logServerHost"]); + logger->configure( + env.at("logServerPort"), + "Sync Scheduler", + env.at("logServerHost") + ); - //create and build new schedule + // create and build new schedule Schedule* schedule = Schedule::getInstance(); - //build the schedule based on the mirrors.json config - schedule->build(config["mirrors"]); + // build the schedule based on the mirrors.json config + schedule->build(config.at("mirrors")); - //create a pointer to the job queue class + // create a pointer to the job queue class Queue* queue = Queue::getInstance(); - //set queue dryrun - queue->setDryrun(env["dryrun"]); - //generate the sync command maps - queue->createSyncCommandMap(config["mirrors"]); - //start the queue (parameter is number of queue threads) - queue->startQueue(env["queueThreads"]); - - //keep alive thread + // set queue dryrun + queue->setDryrun(env.at("dryrun")); + // generate the sync command maps + queue->createSyncCommandMap(config.at("mirrors")); + // start the queue (parameter is number of queue threads) + queue->startQueue(env.at("queueThreads")); + + // keep alive thread std::thread kt(keep_alive_thread); - //cin thread for manual sync + // cin thread for manual sync std::thread ct(cin_thread); - //update schedule thread + // update schedule thread std::thread ust(update_schedule_thread); std::vector* name; - int seconds_to_sleep; - while(true){ - //get the name of the next job and how long we have to sleep till the next job from the schedule + int seconds_to_sleep; + while (true) + { + // get the name of the next job and how long we have to sleep till the + // next job from the schedule name = schedule->nextJob(seconds_to_sleep); - //print the next jobs and the time to sleep - for(int i = 0; i < name->size(); i++){ - std::cout << (*name)[i] << " " << std::endl; - } - std::cout << seconds_to_sleep << std::endl; + /* + // print the next jobs and the time to sleep + for (int idx = 0; idx < name->size(); idx++) + { + std::cout << name->at(idx) << " " << std::endl; + } + std::cout << seconds_to_sleep << std::endl; + */ - //reset reloaded flag + // reset reloaded flag schedule->reloaded = false; - //sleep for "seconds_to_sleep" seconds checking periodically for mirrors.json reloads or exit code - int secondsPassed = 0; - int interval = 1; //interval between checks for reload and exit code - while(secondsPassed <= seconds_to_sleep){ + // sleep for "seconds_to_sleep" seconds checking periodically for + // mirrors.json reloads or exit code + std::chrono::seconds secondsPassed(0); + + // interval between checks for reload and exit code + std::chrono::seconds interval(1); + while (secondsPassed <= seconds_to_sleep) + { std::this_thread::sleep_for(std::chrono::seconds(interval)); secondsPassed += interval; - if(schedule->reloaded == true) break; - if(keepRunning == false) break; + if (schedule->reloaded) + { + break; + } + if (!keepRunning) + { + break; + } + } + if (!keepRunning) + { + break; + } + if (schedule->reloaded) + { + continue; } - if(keepRunning == false) break; - if(schedule->reloaded == true) continue; - //add job names to job queue + // add job names to job queue queue->push_back_list(name); } - //program cleanup + // program cleanup logger->info("Shutting down gracefully..."); - //make sure there is enough time for the logger to send a message before freeing. + // make sure there is enough time for the logger to send a message before + // freeing. std::this_thread::sleep_for(std::chrono::seconds(1)); logger->close(); delete schedule; delete queue; - + return 0; -} \ No newline at end of file +} diff --git a/mirror-sync-scheduler/src/queue.cpp b/mirror-sync-scheduler/src/queue.cpp index 633a20d..214abab 100644 --- a/mirror-sync-scheduler/src/queue.cpp +++ b/mirror-sync-scheduler/src/queue.cpp @@ -1,290 +1,397 @@ +#include "queue.hpp" + +#include +#include +#include +#include +#include +#include #include -#include +#include #include -#include #include -#include -#include -#include #include -#include +#include -#include -using json = nlohmann::json; #include +#include -#include "queue.h" - -//private constructor for Queue class -Queue::Queue(): queueRunning(false){} +// private constructor for Queue class +Queue::Queue() + : m_QueueRunning(false) +{ +} -//create an instance of Queue the first time its ran on the heap -//every other time its ran it returns that same instance -Queue* Queue::getInstance(){ - //a static variable is not updated when getInstance is called a second time +// create an instance of Queue the first time its ran on the heap +// every other time its ran it returns that same instance +auto Queue::getInstance() -> Queue* +{ + // a static variable is not updated when getInstance is called a second time static Queue* queue = new Queue; return queue; } -//set the dryrun variable -void Queue::setDryrun(bool dr){ - dryrun = dr; +// set the dryrun variable +auto Queue::setDryrun(const bool dryrun) -> void +{ + m_DoingDryrun = dryrun; } -//used to add a list of jobs to the queue -void Queue::push_back_list(std::vector* name){ - tLock.lock(); - //store old queue size for duplicate check later. - int oldQueueSize = queue_.size(); - - //add name list to the queue - for(int i = 0; i < name->size(); i++){ - auto search = syncCommands.find((*name)[i]); - if(search != syncCommands.end()){ //check to make sure that given string is in syncCommands - queue_.push_back((*name)[i]); +// used to add a list of jobs to the queue +auto Queue::push_back_list(const std::vector& name) -> void +{ + m_ThreadLock.lock(); + // store old queue size for duplicate check later. + std::size_t oldQueueSize = m_Queue.size(); + + // add name list to the queue + for (std::size_t idx = 0; idx < name.size(); idx++) + { + auto search = m_SyncCommands.find(name.at(idx)); + if (search != m_SyncCommands.end()) + { + // check to make sure that given string is in syncCommands + m_Queue.emplace_back(name.at(idx)); } - else{ - logger->warn((*name)[i] + " is not valid"); + else + { + m_Logger->warn(name.at(idx) + " is not valid"); } } - //duplicate check (only runs when queue wasnt empty to start with, so should run rarely) - for(int i = 0; i < oldQueueSize; i++){ - for(std::deque::iterator it = queue_.begin() + oldQueueSize; it != queue_.end();){ - if(queue_[i] == *it){ - queue_.erase(it); - logger->warn("erasing duplicate from queue: " + queue_[i]); + // duplicate check (only runs when queue wasnt empty to start with, so + // should run rarely) + for (std::size_t idx = 0; idx < oldQueueSize; idx++) + { + for (auto iter = std::begin(m_Queue) + oldQueueSize; + iter != std::end(m_Queue); + iter++) + { + if (m_Queue.at(idx) == *iter) + { + m_Queue.erase(iter); + m_Logger->warn( + "erasing duplicate from queue: " + m_Queue.at(idx) + ); + break; } - else{ - it++; - } } } - std::cout << queue_.size() << std::endl; - tLock.unlock(); + m_ThreadLock.unlock(); } -//manualy sync a project in a detached thread -void Queue::manual_sync(std::string name){ - //create a thread using a lambda function - std::thread t([=]{ - //lock thread - tLock.lock(); - - //check to make sure that given string is in syncCommands - auto search = syncCommands.find(name); - if(search == syncCommands.end()){ - logger->warn(name + " is not a valid project (manual sync failed)"); - tLock.unlock(); - return; - } - - //make sure that the job is a not already syncing - if(std::find(currentJobs.begin(), currentJobs.end(), name) == currentJobs.end()){ - //add job to current jobs - currentJobs.push_back(name); +// manually sync a project in a detached thread +auto Queue::manual_sync(const std::string& name) -> void +{ + // create a thread using a lambda function + std::thread t( + [=] + { + // lock thread + m_ThreadLock.lock(); + + // check to make sure that given string is in syncCommands + if (m_SyncCommands.find(name) == m_SyncCommands.end()) + { + m_Logger->warn(std::format( + "Sync requested for non-existent project `%s`", + name + )); + m_ThreadLock.unlock(); + return; + } - //unlock before running the job - tLock.unlock(); - //run the job - syncProject(name); - //lock after running the job - tLock.lock(); + // make sure that the job is a not already syncing + if (std::find( + std::begin(m_CurrentJobs), + std::end(m_CurrentJobs), + name + ) + == m_CurrentJobs.end()) + { + // add job to current jobs + m_CurrentJobs.push_back(name); + + // unlock before running the job + m_ThreadLock.unlock(); + // run the job + syncProject(name); + // lock after running the job + m_ThreadLock.lock(); + + // erase the job from the currientJobs vector + m_CurrentJobs.erase( + std::find(m_CurrentJobs.begin(), m_CurrentJobs.end(), name) + ); + } + else + { + m_Logger->warn( + name + " is already syncing (manual sync failed)" + ); + } - //erase the job from the currientJobs vector - currentJobs.erase(std::find(currentJobs.begin(), currentJobs.end(), name)); - } - else{ - logger->warn(name + " is already syncing (manual sync failed)"); + // unlock thread + m_ThreadLock.unlock(); } + ); - //unlock thread - tLock.unlock(); - }); - - //detach the thread + // detach the thread + // TODO: Replace with thread pool + // * We should stop detaching threads, I think it is better to hold onto + // the thread handle t.detach(); } -//creates maxThreads jobQueueThreads that sync projects from the queue -void Queue::startQueue(std::size_t maxThreads){ - if(queueRunning == true){ - logger->warn("startQueue tried to start a second time"); +// TODO: Make better thread pool +// creates maxThreads jobQueueThreads that sync projects from the queue +auto Queue::startQueue(const std::size_t maxThreads) -> void +{ + if (m_QueueRunning) + { + m_Logger->warn("startQueue tried to start a second time"); return; } - //create a pool of threads to run syncs in - for(std::size_t i = 0; i < maxThreads; i++){ + // create a pool of threads to run syncs in + for (std::size_t idx = 0; idx < maxThreads; idx++) + { std::thread t(&Queue::jobQueueThread, this); + // * We should stop detaching threads, I think it is better to hold onto + // the thread handle t.detach(); } - queueRunning = true; + + m_QueueRunning = true; } -//syncs jobs from the queue in parallel with other jobQueueThreads -void Queue::jobQueueThread(){ - while(true){ - //lock thread - tLock.lock(); - - //check if the queue is empty - if(!queue_.empty()){ - //select and remove the first element from queue - std::string jobName = queue_.front(); - queue_.pop_front(); - std::cout << queue_.size() << std::endl; - - //check to make sure that jobName is not in currentJobs already - if(std::find(currentJobs.begin(), currentJobs.end(), jobName) == currentJobs.end()){ - //add job to current jobs - currentJobs.push_back(jobName); - - //unlock before running the job - tLock.unlock(); - //run the job within our threadpool - syncProject(jobName); - //lock after running the job - tLock.lock(); - - //erase the job from the currientJobs vector - currentJobs.erase(std::find(currentJobs.begin(), currentJobs.end(), jobName)); - } +// syncs jobs from the queue in parallel with other jobQueueThreads +// ! Doesn't handle dispatching to a thread, this function is already running +// multiple times in parallel. +auto Queue::jobQueueThread() -> void +{ + while (true) + { + m_ThreadLock.lock(); + + // If queue is empty, continue to next iteration of loop + if (m_Queue.empty()) + { + m_ThreadLock.unlock(); + std::this_thread::sleep_for(std::chrono::seconds(5)); + continue; } - //unlock thread - tLock.unlock(); + // select and remove the first element from queue + std::string jobName = m_Queue.front(); + m_Queue.pop_front(); + + // check to make sure that jobName is not in currentJobs already + if (std::find(m_CurrentJobs.begin(), m_CurrentJobs.end(), jobName) + == m_CurrentJobs.end()) + { + // add job to current jobs + m_CurrentJobs.emplace_back(jobName); + + // unlock before running the job + m_ThreadLock.unlock(); + + // run the job within our threadpool + // ? Does this actually run in a separate thread? + syncProject(jobName); + + // lock after running the job + m_ThreadLock.lock(); + + // erase the job from the currientJobs vector + m_CurrentJobs.erase( + std::find(m_CurrentJobs.begin(), m_CurrentJobs.end(), jobName) + ); + } - //sleep for 5 seconds so that we arnt running constantly and to prevent constant locking + m_ThreadLock.unlock(); + // sleep for 5 seconds so that we arent running constantly and to + // prevent constant locking std::this_thread::sleep_for(std::chrono::seconds(5)); } } -//sync a project using the commands from the the syncCommands map -void Queue::syncProject(std::string name){ - logger->info(name + " started"); - //used to get the status of rsync when it runs +// sync a project using the commands from the the syncCommands map +auto Queue::syncProject(const std::string& name) -> void +{ + m_Logger->info(name + " started"); + // used to get the status of rsync when it runs int status = -1; - //for each command in the vector of commands for the given project in the syncCommands map - //most projects have only one command but some have 2 or even 3 - for(std::string command : syncCommands[name]){ - //have the commands output to /dev/null so that we dont fill log files - command = command + " > /dev/null"; - if(dryrun == true){ - command = "echo \"" + command + "\""; - //run command - status = system(command.c_str()); + // for each command in the vector of commands for the given project in the + // syncCommands map most projects have only one command but some have 2 or + // even 3 + for (std::string command : m_SyncCommands.at(name)) + { + // have the commands output to /dev/null so that we dont fill log files + command += " > /dev/null"; + if (m_DoingDryrun) + { + command = std::format("echo \"%s\"", command); + // run command + // TODO: Replace use of `system()` with something with less overhead + status = ::system(command.c_str()); } - //check if password - else if(passwordFiles.find(name) != passwordFiles.end()){ - //read password in from passwordFile - std::ifstream f("configs/" + passwordFiles[name]); + else if (m_PasswordFiles.find(name) != m_PasswordFiles.end()) + { + // read password in from passwordFile + std::ifstream passwordFile("configs/" + m_PasswordFiles.at(name)); + + if (!passwordFile.good()) + { + m_Logger->error(std::format( + "Failed to read password file for `%s`. Continuing", + name + )); + continue; + } + std::string password; - std::getline(f, password); + std::getline(passwordFile, password); - //create string with environment variable - std::string passwordStr = "RSYNC_PASSWORD="+ password; - //convert std::string to char* - char* password_cstr = const_cast(passwordStr.c_str()); + // create string with environment variable + std::string passwordStr + = std::format("RSYNC_PASSWORD=%s", password); - //put password into command environment - putenv(password_cstr); - //run command - status = system(command.c_str()); + // put password into command environment + // ! This loads the password into the current process' environment, + // it should not do that. I think we use `exec()` for handling + // syncs. + ::putenv(passwordStr.data()); + + // run command + // TODO: Replace use of `system()` with something with less overhead + status = ::system(command.c_str()); } - else{ - //run command - status = system(command.c_str()); + else + { + // run command + // TODO: Replace use of `system()` with something with less overhead + status = ::system(command.c_str()); } } - - //temporary sleep for testing when doing a dry run - if(dryrun == true){ + + // temporary sleep for testing when doing a dry run + if (m_DoingDryrun) + { std::this_thread::sleep_for(std::chrono::seconds(10)); } - if(status == 0){ - logger->info(name + " completed succesfully"); + if (status == EXIT_SUCCESS) + { + m_Logger->info(std::format("Successfully synced `%s` project", name)); } - else{ - logger->error(name + " failed"); + else + { + m_Logger->error(std::format("Failed to sync `%s` project", name)); } - } -//create a map that maps a task to the commands needed to sync it -void Queue::createSyncCommandMap(json &config){ - //make sure the syncCommand and passwordFiles maps are empty - syncCommands.clear(); - passwordFiles.clear(); - - //loop through all mirror entries - for (auto& x : config.items()){ - //generate sync commands for each entry and add it to our map - syncCommands[x.key()] = generateSyncCommands(config[x.key()], x.key()); +// create a map that maps a task to the commands needed to sync it +auto Queue::createSyncCommandMap(const nlohmann::json& config) -> void +{ + // make sure the syncCommand and passwordFiles maps are empty + m_SyncCommands.clear(); + m_PasswordFiles.clear(); + + // loop through all mirror entries + for (auto& item : config.items()) + { + // generate sync commands for each entry and add it to our map + // TODO: Something seems off here, figure out what + m_SyncCommands.at(item.key()) + = generateSyncCommands(config.at(item.key()), item.key()); } } -//generate commands to sync a given project config -std::vector Queue::generateSyncCommands(json &config, std::string name){ - std::vector output; - //check if project has an rsync json object - auto rsyncData = config.find("rsync"); - //check if project has a script json object - auto scriptData = config.find("script"); - //(there is a chance that a project has neither rsync or script sync ie. templeOS) - - if(rsyncData != config.end()){ - //run rsync and sync the project - std::string options = config["rsync"].value("options", ""); - output.push_back(rsync(config["rsync"], options)); - - //2 stage syncs happen sometimes - options = config["rsync"].value("second", ""); - if(options != ""){ - output.push_back(rsync(config["rsync"], options)); +// generate commands to sync a given project config +auto Queue::generateSyncCommands( + const nlohmann::json& config, + const std::string& name +) -> std::vector +{ + std::vector output = {}; + // check if project has an rsync json object + auto rsyncData = config.find("rsync"); + // check if project has a script json object + auto scriptData = config.find("script"); + // (there is a chance that a project has neither rsync or script sync ie. + // templeOS) + + if (rsyncData != config.end()) + { + // run rsync and sync the project + std::string options = config.at("rsync").value("options", ""); + output.emplace_back(rsync(config.at("rsync"), options)); + + // 2 stage syncs happen sometimes + options = config.at("rsync").value("second", ""); + if (!options.empty()) + { + output.emplace_back(rsync(config.at("rsync"), options)); } - //a few mirrors are 3 stage syncs - options = config["rsync"].value("third", ""); - if(options != ""){ - output.push_back(rsync(config["rsync"], options)); + // a few mirrors are 3 stage syncs + options = config.at("rsync").value("third", ""); + if (!options.empty()) + { + output.emplace_back(rsync(config.at("rsync"), options)); } - //handle passwords - options = config["rsync"].value("password_file", ""); - if(options != ""){ - passwordFiles[name] = options; + // handle passwords + options = config.at("rsync").value("password_file", ""); + if (!options.empty()) + { + m_PasswordFiles.at(name) = options; } - } - //project uses script sync - else if(scriptData != config.end()){ - std::string command = config["script"].value("command", ""); - std::vector arguments = config["script"].value("arguments", std::vector {}); - for(std::string arg : arguments){ - command = command + " " + arg; + // project uses script sync + else if (scriptData != config.end()) + { + std::string command = config.at("script").value("command", ""); + std::vector arguments = config.at("script").value( + "arguments", + std::vector {} + ); + + for (const std::string& arg : arguments) + { + command = std::format("%s %s", command, arg); } - output.push_back(command); + + output.emplace_back(command); } + return output; } -//compose an rsync command -std::string Queue::rsync(json &config, std::string &options){ - std::string command = "rsync " + options + " "; +// compose an rsync command +auto Queue::rsync(const nlohmann::json& config, const std::string& options) + const -> std::string +{ + std::string command = std::format("rsync %s ", options); - std::string user = config.value("user", ""); - std::string host = config.value("host", ""); - std::string src = config.value("src", ""); - std::string dest = config.value("dest", ""); + const std::string user = config.value("user", ""); + const std::string host = config.value("host", ""); + const std::string src = config.value("src", ""); + const std::string dest = config.value("dest", ""); - if(user != ""){ + if (!user.empty()) + { + command = std::format("%s%s@%s::%s %s", command, user, host, src, dest); command = command + user + "@" + host + "::" + src + " " + dest; } - else{ + else + { + std::format("%s%s::%s %s", command, host, src, dest); command = command + host + "::" + src + " " + dest; } + return command; -} \ No newline at end of file +} diff --git a/mirror-sync-scheduler/src/queue.h b/mirror-sync-scheduler/src/queue.h deleted file mode 100644 index 63b575b..0000000 --- a/mirror-sync-scheduler/src/queue.h +++ /dev/null @@ -1,79 +0,0 @@ -#pragma once - -#include - -class Queue{ - public: - //delete copy and move constructors - //to prevent creation of multiple queue objects - Queue(Queue&) = delete; - Queue(Queue&&) = delete; - Queue &operator=(const Queue &) = delete; - Queue &operator=(const Queue &&) = delete; - - //create the queue singleton object the first execution - //and return the same object with every sequential execution - //@return pointer to queue singleton object - static Queue* getInstance(); - - //add a vector of jobs to the back of the queue - //@param name pointer to a vector of job names - void push_back_list(std::vector* name); - - //sync a project in a new seperate thread from the queue (similar to jobQueueThread) - //@param name project to sync - void manual_sync(std::string name); - - //start syncing projects from the queue - //@param maxThreads number of jobQueueThreads to create to sync jobs in parallel - void startQueue(std::size_t maxThreads); - - //generate sync commands for every mirror entry in mirrors.json using generateSyncCommands - //and store the commands in the syncCommands map - //@param config reference to mirrors.json object - void createSyncCommandMap(json &config); - - //set the dryrun flag which controls if syncs are echoed or not. - //@param dr true causes syncs to be "echoed" - void setDryrun(bool dr); - - private: - Queue(); - - //syncs jobs from the queue in parallel with other jobQueueThreads - void jobQueueThread(); - - //used to sync a given project with the commands from the syncCommandMap - //@param name project to sync - void syncProject(std::string name); - - //generate rsync or script commands to sync a specific project - //@param config reference to mirrors.json object - //@param name project to generate commands for - //@return vector of commands needed to sync a project - std::vector generateSyncCommands(json &config, std::string name); - - //create a rsync command inside generateSyncCommands - //@param config reference to a rsync json object - //@param options reference to the options string for this command - //@return rsync command string - std::string rsync(json &config, std::string &options); - - private: - //thread lock to prevent modifying data from multiple sync threads at the same time - std::mutex tLock; - //deque of queued jobes to be synced - std::deque queue_; - //keep track of what jobs we are currently syncing so that we never sync the same task in a different thread at the same time. - std::vector currentJobs; - //map of projects to the commands needed to sync the project - std::unordered_map> syncCommands; - //map of password files - std::unordered_map passwordFiles; - //used to prevent the queue from being started more than once - bool queueRunning; - //used to run program as a "dry run" - bool dryrun; - //connection to log server - mirror::Logger* logger = mirror::Logger::getInstance(); -}; \ No newline at end of file diff --git a/mirror-sync-scheduler/src/queue.hpp b/mirror-sync-scheduler/src/queue.hpp new file mode 100644 index 0000000..ee4a5d4 --- /dev/null +++ b/mirror-sync-scheduler/src/queue.hpp @@ -0,0 +1,153 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include + +#include + +class Queue +{ + public: // Constructors + Queue(Queue&) = delete; + Queue(Queue&&) = delete; + Queue& operator=(const Queue&) = delete; + Queue& operator=(const Queue&&) = delete; + + public: // Static Methods + /** + * @brief create the queue singleton object the first execution and return + * the same object with every sequential execution + * + * @return pointer to queue singleton object + */ + static auto getInstance() -> Queue*; + + public: // Methods + /** + * @brief add a vector of jobs to the back of the queue + * + * @param name pointer to a vector of job names + */ + auto push_back_list(const std::vector& name) -> void; + + /** + * @brief sync a project in a new separate thread from the queue (similar to + * jobQueueThread) + * + * @param name Name of the project to be synced + */ + auto manual_sync(const std::string& name) -> void; + + /** + * @brief start syncing projects from the queue + * + * @param maxThreads number of jobQueueThreads to create to sync jobs in + * parallel + */ + auto startQueue(const std::size_t maxThreads) -> void; + + /** + * @brief Generate sync commands for every mirror entry in mirrors.json + * using generateSyncCommands and store the commands in the syncCommands map + * + * @param config reference to mirrors.json object + */ + auto createSyncCommandMap(const nlohmann::json& config) -> void; + + /** + * @brief set the dryrun flag which controls if syncs are echoed or not. + * + * @param dryrun true causes syncs to be "echoed" + */ + auto setDryrun(const bool dryrun) -> void; + + private: // Constructors + /** + * @brief Default constructor + */ + Queue(); + + private: // Methods + /** + * @brief syncs jobs from the queue in parallel with other jobQueueThreads + */ + auto jobQueueThread() -> void; + + /** + * @brief used to sync a given project with the commands from the + * syncCommandMap + * + * @param name project to sync + */ + auto syncProject(const std::string& name) -> void; + + /** + * @brief generate rsync or script commands to sync a specific project + * + * @param config reference to mirrors.json object + * @param name project to generate commands for + * @return vector of commands needed to sync a project + */ + auto + generateSyncCommands(const nlohmann::json& config, const std::string& name) + -> std::vector; + + /** + * @brief create a rsync command inside generateSyncCommands + * + * @param config reference to a rsync json object + * @param options reference to the options string for this command + * @return rsync command string + */ + auto rsync(const nlohmann::json& config, const std::string& options) const + -> std::string; + + private: // Members + /** + * @brief thread lock to prevent modifying data from multiple sync threads + * at the same time + */ + std::mutex m_ThreadLock; + + /** + * @brief deque of queued jobes to be synced + */ + std::deque m_Queue; + + /** + * @brief keep track of what jobs we are currently syncing so that we never + * sync the same task in a different thread at the same time. + */ + std::vector m_CurrentJobs; + + /** + * @brief map of projects to the commands needed to sync the project + */ + std::unordered_map> m_SyncCommands; + + /** + * @brief map of password files + */ + std::unordered_map m_PasswordFiles; + + /** + * @brief used to prevent the queue from being started more than once + */ + bool m_QueueRunning; + + /** + * @brief used to run program as a "dry run" + */ + bool m_DoingDryrun; + + /** + * @brief connection to log server + */ + mirror::Logger* m_Logger = mirror::Logger::getInstance(); +}; diff --git a/mirror-sync-scheduler/src/schedule.cpp b/mirror-sync-scheduler/src/schedule.cpp index 0e22605..0d71b55 100644 --- a/mirror-sync-scheduler/src/schedule.cpp +++ b/mirror-sync-scheduler/src/schedule.cpp @@ -1,178 +1,231 @@ +#include "schedule.hpp" + +#include +#include #include -#include -#include #include -#include -#include +#include +#include -#include -using json = nlohmann::json; #include -#include "schedule.h" +#include -//private constructor for schedule class -Schedule::Schedule(): iterator(0){} +// private constructor for schedule class +Schedule::Schedule() + : iterator(0) +{ +} -//create an instance of Schedule the first time its ran on the heap -//every other time its ran it returns that same instance -Schedule* Schedule::getInstance(){ - //a static variable is not updated when getInstance is called a second time +// create an instance of Schedule the first time its ran on the heap +// every other time its ran it returns that same instance +auto Schedule::getInstance() -> Schedule* +{ + // a static variable is not updated when getInstance is called a second time static Schedule* schedule = new Schedule; return schedule; } -void Schedule::build(json &config){ - //clear the jobs vector of any old jobs +auto Schedule::build(nlohmann::json& config) -> void +{ + // clear the jobs vector of any old jobs jobs.clear(); iterator = 0; - //create Task vector from mirrors.json + // create Task vector from mirrors.json std::vector tasks = parseTasks(config); - - //compute the least common multiple of all sync frequencies - int lcm = 1; - for(int i = 0; i < tasks.size(); i++){ - int syncs = tasks[i].syncs; + + // compute the least common multiple of all sync frequencies + std::uint32_t leastCommonMultiple = 1; + for (std::size_t idx = 0; idx < tasks.size(); idx++) + { + int syncs = tasks.at(idx).syncs; int a; int b; - int rem; - if(lcm > syncs){ - a = lcm; + int remainder; + + if (leastCommonMultiple > syncs) + { + a = leastCommonMultiple; b = syncs; } - else { + else + { a = syncs; - b = lcm; + b = leastCommonMultiple; } - while(b != 0){ - rem = a % b; - a = b; - b = rem; + while (b != 0) + { + remainder = a % b; + a = b; + b = remainder; } - //a is now the greatest common denominator; - lcm = lcm * syncs / a; + // a is now the greatest common denominator; + leastCommonMultiple = leastCommonMultiple * syncs / a; } - double interval = 1.0/lcm; - for(int i = 0; i < lcm; i++){ + double interval = 1.0 / leastCommonMultiple; + for (std::uint32_t idx = 0; idx < leastCommonMultiple; idx++) + { // std::cout << "---------" << i << "-------------" << std::endl; - //create a job with all tasks whos syncs per day lines up with the currient fraction of the lcm + // create a job with all tasks who's syncs per day lines up with the + // currient fraction of the lcm Job job; - for(int j = 0; j < tasks.size(); j++){ - Task task = tasks[j]; - if(i%(lcm/task.syncs) == 0){ - // std::cout << tasks[j].syncs << " " << tasks[j].name << std::endl; - job.name.push_back(task.name); + for (std::size_t jdx = 0; jdx < tasks.size(); jdx++) + { + Task task = tasks.at(jdx); + if (idx % (leastCommonMultiple / task.syncs) == 0) + { + job.name.emplace_back(task.name); } } - //set job time based on interval and i - job.target_time = interval * i; - jobs.push_back(job); + // set job time based on interval and i + job.target_time = interval * idx; + jobs.emplace_back(job); } - //verify that the schedule works - bool success = verify(tasks); - if(success == true){ - logger->info("created and verified schedule."); + // verify that the schedule works + if (verifySchedule(tasks)) + { + logger->info("Created and verified sync schedule."); } - else{ - logger->error("failed to create or verify schedule."); + else + { + logger->error("Failed to create or verify sync schedule."); } } -//verify that the job schedule schedules each job the correct number of times. -//verify that the job.start_time increases for each job and 0 <= start_time <= 1 -bool Schedule::verify(std::vector tasks){ - //create Task vector from mirrors.json - //create a map of tasks +// verify that the job schedule schedules each job the correct number of times. +// verify that the job.start_time increases for each job and 0 <= start_time <= +// 1 +auto Schedule::verifySchedule(const std::vector& tasks) -> bool +{ + // create Task vector from mirrors.json + // create a map of tasks std::map taskMap; - for(int i = 0; i < tasks.size(); i++){ - taskMap[tasks[i].name] = 0; + for (std::size_t idx = 0; idx < tasks.size(); idx++) + { + taskMap.at(tasks.at(idx).name) = 0; } - - double prev_start_time = 0.0; - for(int i = 0; i < jobs.size(); i++){ - //check that start_time increases and that 0.0 <= start_time <= 1.0 - if(!(prev_start_time <= jobs[i].target_time <= 1.0)){ + + double previousStartTime = 0.0; + for (std::size_t idx = 0; idx < jobs.size(); idx++) + { + // check that start_time increases and that 0.0 <= start_time <= 1.0 + if (!(previousStartTime <= jobs.at(idx).target_time <= 1.0)) + { return false; } - prev_start_time = jobs[i].target_time; + previousStartTime = jobs.at(idx).target_time; - //increment the appropriate task in our map for each name in our job name vector - for(int j = 0; j < jobs[i].name.size(); j++){ - taskMap.find(jobs[i].name[j])->second++; + // increment the appropriate task in our map for each name in our job + // name vector + for (std::size_t jdx = 0; jdx < jobs.at(idx).name.size(); jdx++) + { + taskMap.find(jobs.at(idx).name.at(jdx))->second++; } } - //check that each job is scheduled the correct number of times - for(int i = 0; i < tasks.size(); i++){ - if(tasks[i].syncs != taskMap.find(tasks[i].name)->second){ + // check that each job is scheduled the correct number of times + for (std::size_t idx = 0; idx < tasks.size(); idx++) + { + if (tasks.at(idx).syncs != taskMap.find(tasks.at(idx).name)->second) + { return false; } - // std::cout << taskMap.find(tasks[i].name)->first << " " << taskMap.find(tasks[i].name)->second << " " << tasks[i].syncs << std::endl; } - + return true; } -std::vector* Schedule::nextJob(int &seconds_to_sleep){ +// TODO: See if there is a better return value for this function +auto Schedule::nextJob(int& seconds_to_sleep) -> std::vector* +{ double total_seconds_day = 86400.0; - //calculate seconds_since_midnight - std::time_t now = std::time(0); - std::tm* tm_gmt = std::gmtime(&now); - int seconds_since_midnight_gmt = tm_gmt->tm_sec + (tm_gmt->tm_min*60) + (tm_gmt->tm_hour*3600); - - //convert seconds_since_midnight to position in the schedule (0.0 <= scheduleTime <= 1.0) - double scheduleTime = (double)seconds_since_midnight_gmt / total_seconds_day; - - //find the first job that is greater than the currient time. - //we start at iterator so that we dont have to search the entire job array each time. - //we do it in this way so that if we start the program in the middle of the day it will still select the correct job. - //in normal operation it will just go through the loop once increasing the iteratior to the next job. - while(iterator < jobs.size() && jobs[iterator].target_time <= scheduleTime){ + // calculate seconds_since_midnight + // TODO: Use `std::chrono` + std::time_t now = std::time(0); + std::tm* tm_gmt = std::gmtime(&now); + int seconds_since_midnight_gmt + = tm_gmt->tm_sec + (tm_gmt->tm_min * 60) + (tm_gmt->tm_hour * 3600); + + // convert seconds_since_midnight to position in the schedule (0.0 <= + // scheduleTime <= 1.0) + double scheduleTime + = static_cast(seconds_since_midnight_gmt / total_seconds_day); + + /* + * Find the first job that is greater than the current time. + * We start at `iterator` so that we dont have to search the entire job + * array each time. We do it in this way so that if we start the program in + * the middle of the day it will still select the correct job. In normal + * operation, it will just go through the loop once increasing the iterator + * to the next job. + */ + while (iterator < jobs.size() + && jobs.at(iterator).target_time <= scheduleTime) + { iterator++; } - //if we are at the end of the schedule - if(iterator == jobs.size()){ - //reset the iterator + // if we are at the end of the schedule + if (iterator == jobs.size()) + { + // reset the iterator iterator = 0; - //sleep till midnight - //total seconds in a day - currient time in seconds cast to an interger and added 1 - seconds_to_sleep = (int)(total_seconds_day - (scheduleTime * total_seconds_day)) + 1; - return &(jobs[jobs.size()-1].name); + // sleep till midnight + // total seconds in a day - current time in seconds cast to an integer + // and added 1 + + // ** SIDE EFFECT ** + // TODO: Replace with something different + seconds_to_sleep = static_cast( + (total_seconds_day - (scheduleTime * total_seconds_day)) + 1 + ); + return &(jobs.at(jobs.size() - 1).name); } - //else sleep till next job - //target time in seconds - currient time in seconds cast to an interger and added 1 - seconds_to_sleep = (int)((jobs[iterator].target_time * total_seconds_day) - (scheduleTime * total_seconds_day)) + 1; - return &(jobs[iterator-1].name); + // else sleep till next job + // target time in seconds - currient time in seconds cast to an integer and + // added 1 + + // ** SIDE EFFECT ** + // TODO: Replace with something different + seconds_to_sleep = static_cast( + ((jobs.at(iterator).target_time * total_seconds_day) + - (scheduleTime * total_seconds_day)) + + 1 + ); + return &(jobs.at(iterator - 1).name); } -//read mirrors.json into a list of tasks -std::vector Schedule::parseTasks(json &config){ +// read mirrors.json into a list of tasks +auto Schedule::parseTasks(nlohmann::json& config) -> std::vector +{ std::vector tasks; - //create a vector of task structs from mirrors.json - for (auto& x : config.items()){ - json Xvalue = x.value(); - json rsync = Xvalue["rsync"]; - json script = Xvalue["script"]; - - if(!rsync.is_null()){ + // create a vector of task structs from mirrors.json + for (auto& x : config.items()) + { + nlohmann::json Xvalue = x.value(); + nlohmann::json rsync = Xvalue.at("rsync"); + nlohmann::json script = Xvalue.at("script"); + + if (!rsync.is_null()) + { Task task; - task.name = x.key(); + task.name = x.key(); task.syncs = rsync.value("syncs_per_day", 0); - tasks.push_back(task); + tasks.emplace_back(task); } - else if(!script.is_null()){ + else if (!script.is_null()) + { Task task; - task.name = x.key(); + task.name = x.key(); task.syncs = script.value("syncs_per_day", 0); - tasks.push_back(task); + tasks.emplace_back(task); } } return tasks; -} \ No newline at end of file +} diff --git a/mirror-sync-scheduler/src/schedule.h b/mirror-sync-scheduler/src/schedule.h deleted file mode 100644 index 2e78e2a..0000000 --- a/mirror-sync-scheduler/src/schedule.h +++ /dev/null @@ -1,67 +0,0 @@ -#pragma once - -//Task structs are used in the build algorithm to represent a project -//name - name of project -//syncs - number of times the project wants to sync per day -struct Task{ - std::string name; - int syncs; -}; - -//Job structs are created by the build algorithm to represent a job to be run at a specific time -//name - vector of names of all the projects that will be queued -//target_time - double between 0 and 1 representing the fraction of a day till its ran -struct Job{ - std::vector name; - double target_time; -}; - -class Schedule{ - private: - Schedule(); - - public: - //delete copy and move constructors - //to prevent creation of multiple schedule objects - Schedule(Schedule&) = delete; - Schedule(Schedule&&) = delete; - Schedule &operator=(const Schedule &) = delete; - Schedule &operator=(const Schedule &&) = delete; - - //create the schedule singleton object the first execution - //and return the same object with every sequential execution - //@return pointer to schedule singleton object - static Schedule* getInstance(); - - //use the mirrors.json file to create the schedule by populating the jobs vector - //@param config reference to mirrors.json object - void build(json &config); - - //calculate the next job to be updated using the schedule and the time to sleep till then - //@param seconds_to_sleep updated to the time to sleep till the next job (pass by refrence) - //@return pointer to a vector of strings with the names of the projects to be updated next - std::vector* nextJob(int &seconds_to_sleep); - - private: - //used inside build to run several sanity checks for the schedule - //@param tasks vector of Task structs - //@return bool representing pass/fail of sanity checks - bool verify(std::vector tasks); - - //used inside build to parse the mirrors.json file into a vector of Task structs - //@param config reference to mirrors.json object - //@return vector of Task structs for each project - std::vector parseTasks(json &config); - - public: - //used inside the main function to break out of the sleep loop to recalculate the next job - std::atomic_bool reloaded; - - private: - //used to hold the most recently ran job to speed up nextJob() - int iterator; - //vector of Jobs containing names of Tasks to sync and the time to sync them - std::vector jobs; - //connection to log server - mirror::Logger* logger = mirror::Logger::getInstance(); -}; \ No newline at end of file diff --git a/mirror-sync-scheduler/src/schedule.hpp b/mirror-sync-scheduler/src/schedule.hpp new file mode 100644 index 0000000..13b5c2b --- /dev/null +++ b/mirror-sync-scheduler/src/schedule.hpp @@ -0,0 +1,134 @@ +#pragma once + +#include +#include +#include +#include + +#include + +/** + * @brief Task structs are used in the build algorithm to represent a project + */ +struct Task +{ + /** + * @brief name of project + */ + std::string name; + + /** + * @brief number of times the project wants to sync per day + */ + std::int32_t syncs; +}; + +/** + * @brief Job structs are created by the build algorithm to represent a job to + * be run at a specific time + */ +struct Job +{ + /** + * @brief vector of names of all the projects that will be queued + */ + std::vector name; + + /** + * @brief double between 0 and 1 representing the fraction of a day till its + * ran + */ + double target_time; +}; + +class Schedule +{ + private: + Schedule(); + + public: + // delete copy and move constructors + Schedule(Schedule&) = delete; + Schedule(Schedule&&) = delete; + Schedule& operator=(const Schedule&) = delete; + Schedule& operator=(Schedule&&) = delete; + + /** + * @brief create the schedule singleton object the first execution and + * return the same object with every sequential execution + * + * @return pointer to schedule singleton object + */ + static auto getInstance() -> Schedule*; + + /** + * @brief use the mirrors.json file to create the schedule by populating the + * jobs vector + * + * @param config reference to mirrors.json object + */ + auto build(nlohmann::json& config) -> void; + + /** + * @brief calculate the next job to be updated using the schedule and the + * time to till then + * + * @param seconds_to_sleep updated to the time to sleep till the next job + * (pass by reference) + * + * @return pointer to a vector of strings with the names of the projects to + * be updated next + */ + /* + ? Why is this returning a pointer to a vector? + TODO: Update the return type of this function so that the + seconds_to_sleep are not being passed out through a reference. + `std::pair` could be used but there is likely a deeper issue with the + structure of this function that should be addressed + */ + auto nextJob(int& seconds_to_sleep) -> std::vector*; + + private: + /** + * @brief used inside build to run several sanity checks for the schedule + * + * @param tasks vector of `Task` structs + * @return whether the sanity checks passed or failed + */ + auto verifySchedule(const std::vector& tasks) -> bool; + + /** + * @brief used inside build to parse `mirrors.json` into a vector of Task + * structs + * + * @param config reference to mirrors.json object + * @return vector of Task structs for each project + */ + auto parseTasks(nlohmann::json& config) -> std::vector; + + public: + /** + * @brief used inside the main function to break out of the sleep loop to + * recalculate the next job + */ + std::atomic_bool reloaded; + + private: + /** + * @brief used to hold the most recently ran job to speed up nextJob() + * ?: Should this be a `std::size_t`? + * ?: Is `iterator` an accurate name for this variable? + */ + int iterator; + + /** + * @brief vector of Jobs containing names of Tasks to sync and the time to + * sync them + */ + std::vector jobs; + + /** + * @brief connection to log server + */ + mirror::Logger* logger = mirror::Logger::getInstance(); +}; diff --git a/mirror-sync-scheduler/src/utils.hpp b/mirror-sync-scheduler/src/utils.hpp new file mode 100644 index 0000000..bfc1d62 --- /dev/null +++ b/mirror-sync-scheduler/src/utils.hpp @@ -0,0 +1,106 @@ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "queue.hpp" +#include "schedule.hpp" + +// read json in from a file +//@param filename std::string json file to read +//@return json object +// TODO: convert file parameter to `const std::filesystem::path&` +auto readJSONFromFile(std::string filename) -> nlohmann::json +{ + std::ifstream f(filename); + nlohmann::json config = nlohmann::json::parse(f); + f.close(); + return config; +} + +// thread for handling manual sync through std input +// !: I feel like there has to be a better way to do this +// TODO: Think of alternative methods for manual syncs. `stdin` could be useful +// in addition to API endpoint, but probably not really needed. +auto cin_thread() -> void +{ + // create a pointer to the queue + Queue* queue = Queue::getInstance(); + + while (true) + { + std::string x; + std::cin >> x; + queue->manual_sync(x); + } +} + +// thread that sends a message to the log server every 29 minutes to keep the +// socket from closing +// ?: Do we actually want to keep alive or let the connection drop and +// reconnect when we need to send a message? +auto keep_alive_thread() -> void +{ + mirror::Logger* logger = mirror::Logger::getInstance(); + + while (true) + { + // sleep for 29 minutes + std::this_thread::sleep_for(std::chrono::minutes(29)); + // send keepalive message + logger->info("keep alive."); + } +} + +// thread that updates the schedule and syncCommandMap whenever there is any +// change made to mirrors.json +auto update_schedule_thread() -> void +{ + // create a pointer to the schedule + Schedule* schedule = Schedule::getInstance(); + // create a pointer to the queue + Queue* queue = Queue::getInstance(); + + const auto mirrorsJSONFile + = std::filesystem::relative("configs/mirrors.json"); + auto previousModificationTime + = std::filesystem::last_write_time(mirrorsJSONFile); + + while (true) + { + // sleep for 7 seconds because its the smallest number that doesnt + // divide 60 + // ?: why does the number have to be not divisible by 60? + std::this_thread::sleep_for(std::chrono::seconds(7)); + + const auto currentModificationTime + = std::filesystem::last_write_time(mirrorsJSONFile); + + if (currentModificationTime != previousModificationTime) + { + // TODO: Use logger instead of `std::cout` + std::cout << "`mirrors.json` last write time has changed. " + "Generating new sync schedule\n"; + + // retrieve the mirror data from mirrors.json + nlohmann::json config = readJSONFromFile("configs/mirrors.json"); + + // build the schedule based on the mirrors.json config + schedule->build(config.at("mirrors")); + // create a new sync command map + queue->createSyncCommandMap(config.at("mirrors")); + // set reloaded flag for main thread + schedule->reloaded = true; + + previousModificationTime = currentModificationTime; + } + } +} From 28f08ad31e9d7fb52a19442563782ebb34e28921 Mon Sep 17 00:00:00 2001 From: carykees98 Date: Wed, 20 Nov 2024 16:24:40 -0500 Subject: [PATCH 02/49] updated doxygen comments in utils.hpp and updated `read_JSON_from_file` --- mirror-sync-scheduler/src/utils.hpp | 37 +++++++++++++++++------------ 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/mirror-sync-scheduler/src/utils.hpp b/mirror-sync-scheduler/src/utils.hpp index bfc1d62..8d1a6c6 100644 --- a/mirror-sync-scheduler/src/utils.hpp +++ b/mirror-sync-scheduler/src/utils.hpp @@ -6,30 +6,33 @@ #include #include -#include - #include #include #include "queue.hpp" #include "schedule.hpp" -// read json in from a file -//@param filename std::string json file to read -//@return json object +/** + * @brief read json in from a file + * + * @param filename std::string json file to read + * + * @return json object + */ // TODO: convert file parameter to `const std::filesystem::path&` -auto readJSONFromFile(std::string filename) -> nlohmann::json +auto read_JSON_from_file(const std::string& filename) -> nlohmann::json { - std::ifstream f(filename); - nlohmann::json config = nlohmann::json::parse(f); - f.close(); + auto config = nlohmann::json::parse(std::ifstream(filename)); + return config; } -// thread for handling manual sync through std input // !: I feel like there has to be a better way to do this // TODO: Think of alternative methods for manual syncs. `stdin` could be useful // in addition to API endpoint, but probably not really needed. +/** + * @brief thread for handling manual sync through stdin + */ auto cin_thread() -> void { // create a pointer to the queue @@ -43,10 +46,12 @@ auto cin_thread() -> void } } -// thread that sends a message to the log server every 29 minutes to keep the -// socket from closing // ?: Do we actually want to keep alive or let the connection drop and // reconnect when we need to send a message? +/** + * @brief thread that sends a message to the log server every 29 minutes to keep + * the socket from closing + */ auto keep_alive_thread() -> void { mirror::Logger* logger = mirror::Logger::getInstance(); @@ -60,8 +65,10 @@ auto keep_alive_thread() -> void } } -// thread that updates the schedule and syncCommandMap whenever there is any -// change made to mirrors.json +/** + * @brief thread that updates the schedule and syncCommandMap whenever there is + * any change made to mirrors.json + */ auto update_schedule_thread() -> void { // create a pointer to the schedule @@ -91,7 +98,7 @@ auto update_schedule_thread() -> void "Generating new sync schedule\n"; // retrieve the mirror data from mirrors.json - nlohmann::json config = readJSONFromFile("configs/mirrors.json"); + nlohmann::json config = read_JSON_from_file("configs/mirrors.json"); // build the schedule based on the mirrors.json config schedule->build(config.at("mirrors")); From df365f8014672a75efe519b8589f6f5ec2881199 Mon Sep 17 00:00:00 2001 From: carykees98 Date: Fri, 7 Feb 2025 02:54:50 -0500 Subject: [PATCH 03/49] updated cmakelists --- mirror-sync-scheduler/CMakeLists.txt | 27 +++++---------- mirror-sync-scheduler/cmake/fetch_deps.cmake | 13 +++++++ mirror-sync-scheduler/src/CMakeLists.txt | 36 ++++++++++++++++++++ 3 files changed, 57 insertions(+), 19 deletions(-) create mode 100644 mirror-sync-scheduler/cmake/fetch_deps.cmake create mode 100644 mirror-sync-scheduler/src/CMakeLists.txt diff --git a/mirror-sync-scheduler/CMakeLists.txt b/mirror-sync-scheduler/CMakeLists.txt index 217ee05..ce38a82 100644 --- a/mirror-sync-scheduler/CMakeLists.txt +++ b/mirror-sync-scheduler/CMakeLists.txt @@ -1,25 +1,14 @@ -cmake_minimum_required(VERSION 3.22) -project(syncScheduler) +cmake_minimum_required(VERSION 3.20...3.30) -add_executable(${PROJECT_NAME} - src/main.cpp - src/schedule.cpp - src/queue.cpp -) - -set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 23) - -include(FetchContent) +find_program(CLANG_TIDY_EXE NAMES "clang-tidy" REQUIRED) +set(CLANG_TIDY_COMMAND ${CLANG_TIDY_EXE} --config-file=${CMAKE_SOURCE_DIR}/../.clang-tidy --header-filter=${CMAKE_SOURCE_DIR}/include) -FetchContent_Declare(json - GIT_REPOSITORY https://github.com/nlohmann/json - GIT_TAG v3.11.3 +project(syncScheduler + LANGUAGES CXX ) -FetchContent_MakeAvailable(json) + +include(cmake/fetch_deps.cmake) add_subdirectory(lib/mirror-logging) +add_subdirectory(src) -target_link_libraries(${PROJECT_NAME} - PUBLIC mirror-logging - PRIVATE nlohmann_json::nlohmann_json -) diff --git a/mirror-sync-scheduler/cmake/fetch_deps.cmake b/mirror-sync-scheduler/cmake/fetch_deps.cmake new file mode 100644 index 0000000..44ee014 --- /dev/null +++ b/mirror-sync-scheduler/cmake/fetch_deps.cmake @@ -0,0 +1,13 @@ +include(FetchContent) + +FetchContent_Declare(json + GIT_REPOSITORY https://github.com/nlohmann/json + GIT_TAG v3.11.3 +) +FetchContent_MakeAvailable(json) + +FetchContent_Declare(spdlog + GIT_REPOSITORY https://github.com/gabime/spdlog + GIT_TAG v1.15.1 +) +FetchContent_MakeAvailable(spdlog) diff --git a/mirror-sync-scheduler/src/CMakeLists.txt b/mirror-sync-scheduler/src/CMakeLists.txt new file mode 100644 index 0000000..9eb4f53 --- /dev/null +++ b/mirror-sync-scheduler/src/CMakeLists.txt @@ -0,0 +1,36 @@ +file(GLOB HEADER_LIST CONFIGURE_DEPENDS "${CMAKE_SOURCE_DIR}/include/*.hpp") + +add_executable(sync_scheduler) + +target_sources(sync_scheduler +PRIVATE + main.cpp + SyncScheduler.cpp + Schedule.cpp + Project.cpp + ${HEADER_LIST} +) + +target_include_directories(sync_scheduler +PRIVATE + ../include +) + +target_link_libraries(sync_scheduler +PRIVATE + mirror-logging + nlohmann_json::nlohmann_json + spdlog::spdlog +) + +target_compile_features(sync_scheduler +PRIVATE + cxx_std_23 +) + +set_target_properties(sync_scheduler +PROPERTIES + CXX_STANDARD 23 + CXX_CLANG_TIDY "${CLANG_TIDY_COMMAND}" +) + From c50837fb52e301b5b368c104f58b33694c239e08 Mon Sep 17 00:00:00 2001 From: carykees98 Date: Fri, 7 Feb 2025 02:55:56 -0500 Subject: [PATCH 04/49] source changes (I need committing to be more alluring else I'll continue to forget) --- .../include/mirror/sync_scheduler/Project.hpp | 60 ++++++++ .../mirror/sync_scheduler/Schedule.hpp | 36 +++++ .../mirror/sync_scheduler/SyncScheduler.hpp | 31 ++++ .../mirror/sync_scheduler/ThreadPool.hpp | 14 ++ mirror-sync-scheduler/src/Project.cpp | 129 +++++++++++++++++ mirror-sync-scheduler/src/Schedule.cpp | 60 ++++++++ mirror-sync-scheduler/src/SyncScheduler.cpp | 36 +++++ mirror-sync-scheduler/src/ThreadPool.cpp | 13 ++ mirror-sync-scheduler/src/main.cpp | 132 ++---------------- mirror-sync-scheduler/src_old/main-old.cpp | 130 +++++++++++++++++ .../{src/queue.cpp => src_old/queue-old.cpp} | 17 +-- .../{src/queue.hpp => src_old/queue-old.hpp} | 3 + .../schedule.cpp => src_old/schedule-old.cpp} | 33 +++-- .../schedule.hpp => src_old/schedule-old.hpp} | 24 +++- .../{src/utils.hpp => src_old/utils-old.hpp} | 44 ++++-- 15 files changed, 594 insertions(+), 168 deletions(-) create mode 100644 mirror-sync-scheduler/include/mirror/sync_scheduler/Project.hpp create mode 100644 mirror-sync-scheduler/include/mirror/sync_scheduler/Schedule.hpp create mode 100644 mirror-sync-scheduler/include/mirror/sync_scheduler/SyncScheduler.hpp create mode 100644 mirror-sync-scheduler/include/mirror/sync_scheduler/ThreadPool.hpp create mode 100644 mirror-sync-scheduler/src/Project.cpp create mode 100644 mirror-sync-scheduler/src/Schedule.cpp create mode 100644 mirror-sync-scheduler/src/SyncScheduler.cpp create mode 100644 mirror-sync-scheduler/src/ThreadPool.cpp create mode 100644 mirror-sync-scheduler/src_old/main-old.cpp rename mirror-sync-scheduler/{src/queue.cpp => src_old/queue-old.cpp} (96%) rename mirror-sync-scheduler/{src/queue.hpp => src_old/queue-old.hpp} (98%) rename mirror-sync-scheduler/{src/schedule.cpp => src_old/schedule-old.cpp} (90%) rename mirror-sync-scheduler/{src/schedule.hpp => src_old/schedule-old.hpp} (87%) rename mirror-sync-scheduler/{src/utils.hpp => src_old/utils-old.hpp} (72%) diff --git a/mirror-sync-scheduler/include/mirror/sync_scheduler/Project.hpp b/mirror-sync-scheduler/include/mirror/sync_scheduler/Project.hpp new file mode 100644 index 0000000..959f38a --- /dev/null +++ b/mirror-sync-scheduler/include/mirror/sync_scheduler/Project.hpp @@ -0,0 +1,60 @@ +/** + * @file Project.hpp + * @author Cary Keesler + * @brief + */ + +#pragma once + +// Standard Library Includes +#include +#include +#include +#include +#include + +// Third Party Library Includes +#include + +namespace mirror::sync_scheduler +{ + +class Project +{ + public: // Constructors + explicit Project(const nlohmann::json& project); + + public: // Methods + private: // Enums + enum class SyncMethod : std::uint8_t + { + SCRIPT, + RSYNC, + }; + + private: // Methods + static auto compose_rsync_command( + const nlohmann::json& rsyncConfig, + const std::string& optionsKey + ) -> std::string; + + static auto generate_sync_config(const nlohmann::json& project) + -> std::pair; + + static auto generate_rsync_config(const nlohmann::json& project) + -> std::pair; + + static auto generate_script_config(const nlohmann::json& project) + -> std::pair; + + private: // Members + std::string m_Name; + SyncMethod m_SyncMethod; + nlohmann::json m_SyncConfig; +}; + +struct static_project_exception : std::runtime_error +{ + using std::runtime_error::runtime_error; +}; +} // namespace mirror::sync_scheduler diff --git a/mirror-sync-scheduler/include/mirror/sync_scheduler/Schedule.hpp b/mirror-sync-scheduler/include/mirror/sync_scheduler/Schedule.hpp new file mode 100644 index 0000000..1fda63a --- /dev/null +++ b/mirror-sync-scheduler/include/mirror/sync_scheduler/Schedule.hpp @@ -0,0 +1,36 @@ +/** + * @file Schedule.hpp + * @author Cary Keesler + * @brief + */ + +#pragma once + +// Standard Library Includes +#include +#include + +// Third Party Library Includes +#include + +// Project Includes +#include + +namespace mirror::sync_scheduler +{ +class Schedule +{ + public: // Constructors + explicit Schedule(const nlohmann::json& mirrors); + + public: // Methods + auto build() -> void; + + private: // Methods + auto verify() -> bool; + + private: // Members + std::vector m_Projects; + std::array, 24> m_HoursToSync; +}; +} // namespace mirror::sync_scheduler diff --git a/mirror-sync-scheduler/include/mirror/sync_scheduler/SyncScheduler.hpp b/mirror-sync-scheduler/include/mirror/sync_scheduler/SyncScheduler.hpp new file mode 100644 index 0000000..8f01d56 --- /dev/null +++ b/mirror-sync-scheduler/include/mirror/sync_scheduler/SyncScheduler.hpp @@ -0,0 +1,31 @@ +/** + * @file SyncScheduler.hpp + * @author Cary Keesler + * @brief + */ + +#pragma once + +// Third Party Library Includes +#include + +// Project Includes +#include + +namespace mirror::sync_scheduler +{ +class SyncScheduler +{ + public: // Constructors + SyncScheduler(); + + public: // Methods + auto run() -> void; + + private: + static auto load_config() -> nlohmann::json; + + private: // Members + Schedule m_Schedule; +}; +} // namespace mirror::sync_scheduler diff --git a/mirror-sync-scheduler/include/mirror/sync_scheduler/ThreadPool.hpp b/mirror-sync-scheduler/include/mirror/sync_scheduler/ThreadPool.hpp new file mode 100644 index 0000000..58caea0 --- /dev/null +++ b/mirror-sync-scheduler/include/mirror/sync_scheduler/ThreadPool.hpp @@ -0,0 +1,14 @@ +/** + * @file ThreadPool.hpp + * @author Cary Keesler + * @brief + */ + +#pragma once + +namespace mirror::sync_scheduler +{ +class ThreadPool +{ +}; +} // namespace mirror::sync_scheduler diff --git a/mirror-sync-scheduler/src/Project.cpp b/mirror-sync-scheduler/src/Project.cpp new file mode 100644 index 0000000..aba3d24 --- /dev/null +++ b/mirror-sync-scheduler/src/Project.cpp @@ -0,0 +1,129 @@ +/** + * @file Project.cpp + * @author Cary Keesler + * @brief + */ + +// Header Being Defined +#include + +// Standard Library Includes +#include +#include + +// Third Party Includes +#include + +namespace mirror::sync_scheduler +{ +Project::Project(const nlohmann::json& project) +{ + if (project.contains("static")) + { + throw static_project_exception(std::format( + "Project '{}' uses a static sync. Project not created", + project.value("name", "") + )); + } + + const bool isRsyncOrScript + = project.contains("rsync") || project.contains("script"); + + if (!isRsyncOrScript) + { + throw std::runtime_error(std::format( + "Project '{}' is missing a sync type", + project.value("name", "") + )); + } + + m_Name = project.value("name", ""); + + std::tie(m_SyncMethod, m_SyncConfig) = generate_sync_config(project); +} + +auto Project::compose_rsync_command( + const nlohmann::json& rsyncConfig, + const std::string& optionsKey = "options" +) -> std::string +{ + std::string command + = std::format("/usr/bin/rsync %s", rsyncConfig.value(optionsKey, "")); + + const std::string user = rsyncConfig.value("user", ""); + const std::string host = rsyncConfig.value("host", ""); + const std::string src = rsyncConfig.value("src", ""); + const std::string dest = rsyncConfig.value("dest", ""); + + if (!user.empty()) + { + command + = std::format("{} {}@{}::{} {}", command, user, host, src, dest); + } + else + { + command = std::format("{} {}::{} {}", command, host, src, dest); + } + + return command; +} + +auto Project::generate_sync_config(const nlohmann::json& project) + -> std::pair +{ + return project.contains("rsync") ? generate_rsync_config(project) + : generate_script_config(project); +} + +auto Project::generate_rsync_config(const nlohmann::json& project) + -> std::pair +{ + nlohmann::json config; + + config.emplace("syncs_per_day", project.at("rsync").at("syncs_per_day")); + config.emplace("primary", compose_rsync_command(project.at("rsync"))); + + if (project.at("rsync").contains("second")) + { + config.emplace( + "secondary", + compose_rsync_command(project.at("rsync"), "second") + ); + } + if (project.at("rsync").contains("third")) + { + config.emplace( + "tertiary", + compose_rsync_command(project.at("rsync"), "third") + ); + } + + if (project.contains("password_file")) + { + config.emplace("password_file", project.at("password_file")); + } + + return std::make_pair(Project::SyncMethod::RSYNC, config); +} + +auto Project::generate_script_config(const nlohmann::json& project) + -> std::pair +{ + nlohmann::json config; + + config.emplace("syncs_per_day", project.at("script").at("syncs_per_day")); + + std::string command = project.at("script").at("command"); + std::vector arguments + = project.at("script").value("arguments", std::vector {}); + + for (const std::string& arg : arguments) + { + command = std::format("%s %s", command, arg); + } + + config.emplace("command", command); + + return std::make_pair(Project::SyncMethod::SCRIPT, config); +} +} // namespace mirror::sync_scheduler diff --git a/mirror-sync-scheduler/src/Schedule.cpp b/mirror-sync-scheduler/src/Schedule.cpp new file mode 100644 index 0000000..de03c15 --- /dev/null +++ b/mirror-sync-scheduler/src/Schedule.cpp @@ -0,0 +1,60 @@ +/** + * @file Schedule.cpp + * @author Cary Keesler + * @brief + */ + +// Header Being Defined +#include + +// Standard Library Includes +#include + +// Third Party Library Includes +#include +#include + +// Project Includes +#include + +namespace mirror::sync_scheduler +{ +Schedule::Schedule(const nlohmann::json& mirrors) + : m_Projects() +{ + if (mirrors.empty()) + { + throw std::runtime_error("No mirrors found in config!"); + } + + for (const auto& mirror : mirrors) + { + try + { + m_Projects.emplace_back(Project(mirror)); + } + catch (static_project_exception& e) + { + spdlog::warn(e.what()); + } + catch (std::runtime_error& e) + { + spdlog::error(e.what()); + } + } + + build(); + + if (!verify()) + { + throw std::runtime_error("Failed to verify sync schedule!"); + } +} + +auto Schedule::build() -> void { } + +auto Schedule::verify() -> bool +{ + return false; +} +} // namespace mirror::sync_scheduler diff --git a/mirror-sync-scheduler/src/SyncScheduler.cpp b/mirror-sync-scheduler/src/SyncScheduler.cpp new file mode 100644 index 0000000..e9e9c94 --- /dev/null +++ b/mirror-sync-scheduler/src/SyncScheduler.cpp @@ -0,0 +1,36 @@ +/** + * @file SyncScheduler.cpp + * @author Cary Keesler + * @brief + */ + +// Header Being Defined +#include + +// Standard Library Includes +#include + +// Third Party Library Includes +#include + +namespace mirror::sync_scheduler +{ +SyncScheduler::SyncScheduler() + : m_Schedule(load_config().value("mirrors", nlohmann::json {})) +{ +} + +auto SyncScheduler::load_config() -> nlohmann::json +{ + std::ifstream configFile("configs/mirrors.json"); + + return nlohmann::json::parse(configFile); +} + +auto SyncScheduler::run() -> void +{ + auto config = load_config(); + m_Schedule = Schedule(config.at("mirrors")); + m_Schedule.build(); +} +} // namespace mirror::sync_scheduler diff --git a/mirror-sync-scheduler/src/ThreadPool.cpp b/mirror-sync-scheduler/src/ThreadPool.cpp new file mode 100644 index 0000000..c5de496 --- /dev/null +++ b/mirror-sync-scheduler/src/ThreadPool.cpp @@ -0,0 +1,13 @@ +/** + * @file ThreadPool.cpp + * @author Cary Keesler + * @brief + */ + +// Header Being Defined +#include + +namespace mirror::sync_scheduler +{ + +} diff --git a/mirror-sync-scheduler/src/main.cpp b/mirror-sync-scheduler/src/main.cpp index 02a896f..956a1a5 100644 --- a/mirror-sync-scheduler/src/main.cpp +++ b/mirror-sync-scheduler/src/main.cpp @@ -1,131 +1,17 @@ -#include -#include -#include -#include -#include -#include +/** + * @file main.cpp + * @author Cary Keesler + * @brief + */ -#include -#include - -#include -#include - -#include "queue.hpp" -#include "schedule.hpp" -#include "utils.hpp" - -// used to stop the program cleanly with ctrl c -static bool keepRunning = true; - -// used by the SIGINT signal handler to end the program -auto intHandler(int i) -> void -{ - keepRunning = false; -} +// Project Includes +#include auto main() -> int { - // ctrl c signal handler - signal(SIGINT, intHandler); - - // read env data in from env.json - nlohmann::json env = readJSONFromFile("configs/sync-scheduler-env.json"); - - // read mirror data in from mirrors.json - nlohmann::json config = readJSONFromFile("configs/mirrors.json"); - - // initialize and configure connection to log server - mirror::Logger* logger = mirror::Logger::getInstance(); - logger->configure( - env.at("logServerPort"), - "Sync Scheduler", - env.at("logServerHost") - ); - - // create and build new schedule - Schedule* schedule = Schedule::getInstance(); - // build the schedule based on the mirrors.json config - schedule->build(config.at("mirrors")); - - // create a pointer to the job queue class - Queue* queue = Queue::getInstance(); - // set queue dryrun - queue->setDryrun(env.at("dryrun")); - // generate the sync command maps - queue->createSyncCommandMap(config.at("mirrors")); - // start the queue (parameter is number of queue threads) - queue->startQueue(env.at("queueThreads")); - - // keep alive thread - std::thread kt(keep_alive_thread); - - // cin thread for manual sync - std::thread ct(cin_thread); - - // update schedule thread - std::thread ust(update_schedule_thread); - - std::vector* name; - int seconds_to_sleep; - while (true) - { - // get the name of the next job and how long we have to sleep till the - // next job from the schedule - name = schedule->nextJob(seconds_to_sleep); - - /* - // print the next jobs and the time to sleep - for (int idx = 0; idx < name->size(); idx++) - { - std::cout << name->at(idx) << " " << std::endl; - } - std::cout << seconds_to_sleep << std::endl; - */ - - // reset reloaded flag - schedule->reloaded = false; - - // sleep for "seconds_to_sleep" seconds checking periodically for - // mirrors.json reloads or exit code - std::chrono::seconds secondsPassed(0); - - // interval between checks for reload and exit code - std::chrono::seconds interval(1); - while (secondsPassed <= seconds_to_sleep) - { - std::this_thread::sleep_for(std::chrono::seconds(interval)); - secondsPassed += interval; - if (schedule->reloaded) - { - break; - } - if (!keepRunning) - { - break; - } - } - if (!keepRunning) - { - break; - } - if (schedule->reloaded) - { - continue; - } - - // add job names to job queue - queue->push_back_list(name); - } + mirror::sync_scheduler::SyncScheduler syncScheduler {}; - // program cleanup - logger->info("Shutting down gracefully..."); - // make sure there is enough time for the logger to send a message before - // freeing. - std::this_thread::sleep_for(std::chrono::seconds(1)); - logger->close(); - delete schedule; - delete queue; + syncScheduler.run(); return 0; } diff --git a/mirror-sync-scheduler/src_old/main-old.cpp b/mirror-sync-scheduler/src_old/main-old.cpp new file mode 100644 index 0000000..593932c --- /dev/null +++ b/mirror-sync-scheduler/src_old/main-old.cpp @@ -0,0 +1,130 @@ +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include + +// used to stop the program cleanly when process is given a `SIGINT` signal +static bool keepRunning = true; + +// used by the SIGINT signal handler to end the program +auto sigint_handler(int signal) -> void +{ + keepRunning = false; +} + +auto main() -> int +{ + // ctrl c signal handler + ::signal(SIGINT, sigint_handler); + + // read env data in from env.json + auto env = mirror::sync_scheduler::read_JSON_from_file( + "configs/sync-scheduler-env.json" + ); + + // read mirror data in from mirrors.json + // ?: Does this need to be read in main? + auto config + = mirror::sync_scheduler::read_JSON_from_file("configs/mirrors.json"); + + // initialize and configure connection to log server + auto* logger = mirror::Logger::getInstance(); + logger->configure( + env.at("logServerPort"), + "Sync Scheduler", + env.at("logServerHost") + ); + + // create and build new schedule + auto* schedule = mirror::sync_scheduler::Schedule::getInstance(); + // build the schedule based on the mirrors.json config + schedule->build(config.at("mirrors")); + + // create a pointer to the job queue class + auto* queue = mirror::sync_scheduler::Queue::getInstance(); + // set queue dryrun + queue->setDryrun(env.at("dryrun")); + // generate the sync command maps + queue->createSyncCommandMap(config.at("mirrors")); + // start the queue (parameter is number of queue threads) + queue->startQueue(env.at("queueThreads")); + + // keep alive thread + // !: Not needed if the log server is being removed + std::thread keepAliveThread(mirror::sync_scheduler::keep_alive_thread); + + // cin thread for manual sync + // !: Not needed if manual syncs can be requested over a socket from the + // website + std::thread stdinThread(mirror::sync_scheduler::cin_thread); + + // update schedule thread + // !: Should be made into a call back function that is called when a common + // source of truth for mirrors.json detects a change to the file + std::thread updateScheduleThread( + mirror::sync_scheduler::update_schedule_thread + ); + + std::vector* name; // ?: name of what? + std::chrono::seconds secondsToSleep; + while (true) + { + // get the name of the next job and how long we have to sleep till the + // next job from the schedule + name = schedule->nextJob(secondsToSleep); + + // reset reloaded flag + schedule->reloaded = false; + + // sleep for "secondsToSleep" seconds checking periodically for + // mirrors.json reloads or exit code + std::chrono::seconds secondsPassed(0); + + // interval between checks for reload and exit code + std::chrono::seconds interval(1); + while (secondsPassed <= secondsToSleep) + { + std::this_thread::sleep_for(std::chrono::seconds(interval)); + secondsPassed += interval; + if (schedule->reloaded || !keepRunning) + { + break; + } + } + if (!keepRunning) + { + break; + } + if (schedule->reloaded) + { + continue; + } + + // add job names to job queue + queue->push_back_list(name); + } + + // program cleanup + logger->info("Shutting down gracefully..."); + // make sure there is enough time for the logger to send a message before + // freeing. + std::this_thread::sleep_for(std::chrono::seconds(1)); + logger->close(); + + // * Technically not required since the program is going to be ending in the + // following line and the OS will automatically reclaim the memory + delete schedule; + delete queue; + + return 0; +} diff --git a/mirror-sync-scheduler/src/queue.cpp b/mirror-sync-scheduler/src_old/queue-old.cpp similarity index 96% rename from mirror-sync-scheduler/src/queue.cpp rename to mirror-sync-scheduler/src_old/queue-old.cpp index 214abab..e82b524 100644 --- a/mirror-sync-scheduler/src/queue.cpp +++ b/mirror-sync-scheduler/src_old/queue-old.cpp @@ -1,21 +1,21 @@ -#include "queue.hpp" +#include #include #include #include -#include #include #include #include -#include #include #include -#include #include #include + #include +namespace mirror::sync_scheduler +{ // private constructor for Queue class Queue::Queue() : m_QueueRunning(false) @@ -42,7 +42,7 @@ auto Queue::push_back_list(const std::vector& name) -> void { m_ThreadLock.lock(); // store old queue size for duplicate check later. - std::size_t oldQueueSize = m_Queue.size(); + auto oldQueueSize = m_Queue.size(); // add name list to the queue for (std::size_t idx = 0; idx < name.size(); idx++) @@ -385,13 +385,14 @@ auto Queue::rsync(const nlohmann::json& config, const std::string& options) if (!user.empty()) { command = std::format("%s%s@%s::%s %s", command, user, host, src, dest); - command = command + user + "@" + host + "::" + src + " " + dest; + // command = command + user + "@" + host + "::" + src + " " + dest; } else { - std::format("%s%s::%s %s", command, host, src, dest); - command = command + host + "::" + src + " " + dest; + command = std::format("%s%s::%s %s", command, host, src, dest); + // command = command + host + "::" + src + " " + dest; } return command; } +} // namespace mirror::sync_scheduler diff --git a/mirror-sync-scheduler/src/queue.hpp b/mirror-sync-scheduler/src_old/queue-old.hpp similarity index 98% rename from mirror-sync-scheduler/src/queue.hpp rename to mirror-sync-scheduler/src_old/queue-old.hpp index ee4a5d4..81449c9 100644 --- a/mirror-sync-scheduler/src/queue.hpp +++ b/mirror-sync-scheduler/src_old/queue-old.hpp @@ -11,6 +11,8 @@ #include +namespace mirror::sync_scheduler +{ class Queue { public: // Constructors @@ -151,3 +153,4 @@ class Queue */ mirror::Logger* m_Logger = mirror::Logger::getInstance(); }; +} // namespace mirror::sync_scheduler diff --git a/mirror-sync-scheduler/src/schedule.cpp b/mirror-sync-scheduler/src_old/schedule-old.cpp similarity index 90% rename from mirror-sync-scheduler/src/schedule.cpp rename to mirror-sync-scheduler/src_old/schedule-old.cpp index 0d71b55..741e687 100644 --- a/mirror-sync-scheduler/src/schedule.cpp +++ b/mirror-sync-scheduler/src_old/schedule-old.cpp @@ -1,7 +1,5 @@ -#include "schedule.hpp" +#include -#include -#include #include #include #include @@ -11,7 +9,9 @@ #include -// private constructor for schedule class +namespace mirror::sync_scheduler +{ // private constructor for schedule class + Schedule::Schedule() : iterator(0) { @@ -22,7 +22,7 @@ Schedule::Schedule() auto Schedule::getInstance() -> Schedule* { // a static variable is not updated when getInstance is called a second time - static Schedule* schedule = new Schedule; + static auto* schedule = new Schedule; return schedule; } @@ -141,22 +141,23 @@ auto Schedule::verifySchedule(const std::vector& tasks) -> bool // TODO: See if there is a better return value for this function auto Schedule::nextJob(int& seconds_to_sleep) -> std::vector* { - double total_seconds_day = 86400.0; + constexpr auto total_seconds_day + = std::chrono::duration_cast(std::chrono::days(1) + ); - // calculate seconds_since_midnight - // TODO: Use `std::chrono` - std::time_t now = std::time(0); - std::tm* tm_gmt = std::gmtime(&now); - int seconds_since_midnight_gmt - = tm_gmt->tm_sec + (tm_gmt->tm_min * 60) + (tm_gmt->tm_hour * 3600); + const auto now = std::chrono::utc_clock::now(); + const auto time_since_midnight + = std::chrono::duration_cast( + now - std::chrono::floor(now) + ); // convert seconds_since_midnight to position in the schedule (0.0 <= // scheduleTime <= 1.0) - double scheduleTime - = static_cast(seconds_since_midnight_gmt / total_seconds_day); + const auto scheduleTime = time_since_midnight / total_seconds_day; /* * Find the first job that is greater than the current time. + * * We start at `iterator` so that we dont have to search the entire job * array each time. We do it in this way so that if we start the program in * the middle of the day it will still select the correct job. In normal @@ -206,7 +207,7 @@ auto Schedule::parseTasks(nlohmann::json& config) -> std::vector { std::vector tasks; // create a vector of task structs from mirrors.json - for (auto& x : config.items()) + for (const auto& x : config.items()) { nlohmann::json Xvalue = x.value(); nlohmann::json rsync = Xvalue.at("rsync"); @@ -227,5 +228,7 @@ auto Schedule::parseTasks(nlohmann::json& config) -> std::vector tasks.emplace_back(task); } } + return tasks; } +} // namespace mirror::sync_scheduler diff --git a/mirror-sync-scheduler/src/schedule.hpp b/mirror-sync-scheduler/src_old/schedule-old.hpp similarity index 87% rename from mirror-sync-scheduler/src/schedule.hpp rename to mirror-sync-scheduler/src_old/schedule-old.hpp index 13b5c2b..5eddf2d 100644 --- a/mirror-sync-scheduler/src/schedule.hpp +++ b/mirror-sync-scheduler/src_old/schedule-old.hpp @@ -7,6 +7,8 @@ #include +namespace mirror::sync_scheduler +{ /** * @brief Task structs are used in the build algorithm to represent a project */ @@ -41,6 +43,10 @@ struct Job double target_time; }; +/** + * @brief + * + */ class Schedule { private: @@ -78,14 +84,15 @@ class Schedule * * @return pointer to a vector of strings with the names of the projects to * be updated next + * + * @note + * ?: Why is this returning a pointer to a vector? + * + * TODO: Update the return type of this function so that the + * `seconds_to_sleep` are not being passed out through a reference. + * `std::pair` could be used but there is likely a deeper issue with the + * structure of this function that should be addressed */ - /* - ? Why is this returning a pointer to a vector? - TODO: Update the return type of this function so that the - seconds_to_sleep are not being passed out through a reference. - `std::pair` could be used but there is likely a deeper issue with the - structure of this function that should be addressed - */ auto nextJob(int& seconds_to_sleep) -> std::vector*; private: @@ -116,6 +123,8 @@ class Schedule private: /** * @brief used to hold the most recently ran job to speed up nextJob() + * + * @note * ?: Should this be a `std::size_t`? * ?: Is `iterator` an accurate name for this variable? */ @@ -132,3 +141,4 @@ class Schedule */ mirror::Logger* logger = mirror::Logger::getInstance(); }; +} // namespace mirror::sync_scheduler diff --git a/mirror-sync-scheduler/src/utils.hpp b/mirror-sync-scheduler/src_old/utils-old.hpp similarity index 72% rename from mirror-sync-scheduler/src/utils.hpp rename to mirror-sync-scheduler/src_old/utils-old.hpp index 8d1a6c6..93135b7 100644 --- a/mirror-sync-scheduler/src/utils.hpp +++ b/mirror-sync-scheduler/src_old/utils-old.hpp @@ -1,3 +1,5 @@ +#pragma once + #include #include #include @@ -9,34 +11,41 @@ #include #include -#include "queue.hpp" -#include "schedule.hpp" +#include +#include +namespace mirror::sync_scheduler +{ /** * @brief read json in from a file * * @param filename std::string json file to read * * @return json object + * + * @note + * TODO: convert file parameter to `const std::filesystem::path&` */ -// TODO: convert file parameter to `const std::filesystem::path&` auto read_JSON_from_file(const std::string& filename) -> nlohmann::json { - auto config = nlohmann::json::parse(std::ifstream(filename)); + auto jsonObj = nlohmann::json::parse(std::ifstream(filename)); - return config; + return jsonObj; } -// !: I feel like there has to be a better way to do this -// TODO: Think of alternative methods for manual syncs. `stdin` could be useful -// in addition to API endpoint, but probably not really needed. /** * @brief thread for handling manual sync through stdin + * + * @note + * !: I feel like there has to be a better way to do this + * + * TODO: Think of alternative methods for manual syncs. `stdin` could be useful + * in addition to API endpoint, but probably not really needed. */ auto cin_thread() -> void { // create a pointer to the queue - Queue* queue = Queue::getInstance(); + auto* queue = Queue::getInstance(); while (true) { @@ -46,15 +55,19 @@ auto cin_thread() -> void } } -// ?: Do we actually want to keep alive or let the connection drop and -// reconnect when we need to send a message? /** * @brief thread that sends a message to the log server every 29 minutes to keep * the socket from closing + * + * @deprecated Log server may be going away soon + * + * @note + * ?: Do we actually want to keep alive or let the connection drop and reconnect + * when we need to send a message? */ auto keep_alive_thread() -> void { - mirror::Logger* logger = mirror::Logger::getInstance(); + auto* logger = mirror::Logger::getInstance(); while (true) { @@ -72,9 +85,9 @@ auto keep_alive_thread() -> void auto update_schedule_thread() -> void { // create a pointer to the schedule - Schedule* schedule = Schedule::getInstance(); + auto* schedule = Schedule::getInstance(); // create a pointer to the queue - Queue* queue = Queue::getInstance(); + auto* queue = Queue::getInstance(); const auto mirrorsJSONFile = std::filesystem::relative("configs/mirrors.json"); @@ -98,7 +111,7 @@ auto update_schedule_thread() -> void "Generating new sync schedule\n"; // retrieve the mirror data from mirrors.json - nlohmann::json config = read_JSON_from_file("configs/mirrors.json"); + auto config = read_JSON_from_file("configs/mirrors.json"); // build the schedule based on the mirrors.json config schedule->build(config.at("mirrors")); @@ -111,3 +124,4 @@ auto update_schedule_thread() -> void } } } +} // namespace mirror::sync_scheduler From 6ece08c5b133af64fbebb053be8bc297299c6e6d Mon Sep 17 00:00:00 2001 From: carykees98 Date: Fri, 7 Feb 2025 18:53:05 -0500 Subject: [PATCH 05/49] remove mirror logging --- .../lib/mirror-logging/.gitignore | 4 - .../lib/mirror-logging/.idea/.gitignore | 8 - .../lib/mirror-logging/.idea/.name | 1 - .../mirror-logging/.idea/mirror-logging.iml | 2 - .../lib/mirror-logging/.idea/misc.xml | 4 - .../lib/mirror-logging/.idea/modules.xml | 8 - .../lib/mirror-logging/.idea/vcs.xml | 6 - .../lib/mirror-logging/CMakeLists.txt | 9 - .../lib/mirror-logging/Doxyfile | 2817 ----------------- .../mirror-logging/include/mirror/logger.hpp | 195 -- .../lib/mirror-logging/license | 0 .../lib/mirror-logging/readme.md | 1 - .../lib/mirror-logging/src/logger.cpp | 102 - 13 files changed, 3157 deletions(-) delete mode 100644 mirror-sync-scheduler/lib/mirror-logging/.gitignore delete mode 100644 mirror-sync-scheduler/lib/mirror-logging/.idea/.gitignore delete mode 100644 mirror-sync-scheduler/lib/mirror-logging/.idea/.name delete mode 100644 mirror-sync-scheduler/lib/mirror-logging/.idea/mirror-logging.iml delete mode 100644 mirror-sync-scheduler/lib/mirror-logging/.idea/misc.xml delete mode 100644 mirror-sync-scheduler/lib/mirror-logging/.idea/modules.xml delete mode 100644 mirror-sync-scheduler/lib/mirror-logging/.idea/vcs.xml delete mode 100644 mirror-sync-scheduler/lib/mirror-logging/CMakeLists.txt delete mode 100644 mirror-sync-scheduler/lib/mirror-logging/Doxyfile delete mode 100644 mirror-sync-scheduler/lib/mirror-logging/include/mirror/logger.hpp delete mode 100644 mirror-sync-scheduler/lib/mirror-logging/license delete mode 100644 mirror-sync-scheduler/lib/mirror-logging/readme.md delete mode 100644 mirror-sync-scheduler/lib/mirror-logging/src/logger.cpp diff --git a/mirror-sync-scheduler/lib/mirror-logging/.gitignore b/mirror-sync-scheduler/lib/mirror-logging/.gitignore deleted file mode 100644 index 69c3b0c..0000000 --- a/mirror-sync-scheduler/lib/mirror-logging/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -build/ -.vscode/ -cmake-build*/ -docs/ \ No newline at end of file diff --git a/mirror-sync-scheduler/lib/mirror-logging/.idea/.gitignore b/mirror-sync-scheduler/lib/mirror-logging/.idea/.gitignore deleted file mode 100644 index 13566b8..0000000 --- a/mirror-sync-scheduler/lib/mirror-logging/.idea/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml diff --git a/mirror-sync-scheduler/lib/mirror-logging/.idea/.name b/mirror-sync-scheduler/lib/mirror-logging/.idea/.name deleted file mode 100644 index 0f4b351..0000000 --- a/mirror-sync-scheduler/lib/mirror-logging/.idea/.name +++ /dev/null @@ -1 +0,0 @@ -mirror_logging \ No newline at end of file diff --git a/mirror-sync-scheduler/lib/mirror-logging/.idea/mirror-logging.iml b/mirror-sync-scheduler/lib/mirror-logging/.idea/mirror-logging.iml deleted file mode 100644 index f08604b..0000000 --- a/mirror-sync-scheduler/lib/mirror-logging/.idea/mirror-logging.iml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/mirror-sync-scheduler/lib/mirror-logging/.idea/misc.xml b/mirror-sync-scheduler/lib/mirror-logging/.idea/misc.xml deleted file mode 100644 index 79b3c94..0000000 --- a/mirror-sync-scheduler/lib/mirror-logging/.idea/misc.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/mirror-sync-scheduler/lib/mirror-logging/.idea/modules.xml b/mirror-sync-scheduler/lib/mirror-logging/.idea/modules.xml deleted file mode 100644 index 9821e42..0000000 --- a/mirror-sync-scheduler/lib/mirror-logging/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/mirror-sync-scheduler/lib/mirror-logging/.idea/vcs.xml b/mirror-sync-scheduler/lib/mirror-logging/.idea/vcs.xml deleted file mode 100644 index 35eb1dd..0000000 --- a/mirror-sync-scheduler/lib/mirror-logging/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/mirror-sync-scheduler/lib/mirror-logging/CMakeLists.txt b/mirror-sync-scheduler/lib/mirror-logging/CMakeLists.txt deleted file mode 100644 index 6454767..0000000 --- a/mirror-sync-scheduler/lib/mirror-logging/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -cmake_minimum_required(VERSION 3.22) -project(mirror-logging VERSION 0.0.0 LANGUAGES C CXX) - -set(CMAKE_CXX_STANDARD 17) - -add_library(mirror-logging src/logger.cpp) -target_link_libraries(mirror-logging zmq) -target_include_directories(mirror-logging PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/include) diff --git a/mirror-sync-scheduler/lib/mirror-logging/Doxyfile b/mirror-sync-scheduler/lib/mirror-logging/Doxyfile deleted file mode 100644 index 4fe54f5..0000000 --- a/mirror-sync-scheduler/lib/mirror-logging/Doxyfile +++ /dev/null @@ -1,2817 +0,0 @@ -# Doxyfile 1.9.8 - -# This file describes the settings to be used by the documentation system -# doxygen (www.doxygen.org) for a project. -# -# All text after a double hash (##) is considered a comment and is placed in -# front of the TAG it is preceding. -# -# All text after a single hash (#) is considered a comment and will be ignored. -# The format is: -# TAG = value [value, ...] -# For lists, items can also be appended using: -# TAG += value [value, ...] -# Values that contain spaces should be placed between quotes (\" \"). -# -# Note: -# -# Use doxygen to compare the used configuration file with the template -# configuration file: -# doxygen -x [configFile] -# Use doxygen to compare the used configuration file with the template -# configuration file without replacing the environment variables or CMake type -# replacement variables: -# doxygen -x_noenv [configFile] - -#--------------------------------------------------------------------------- -# Project related configuration options -#--------------------------------------------------------------------------- - -# This tag specifies the encoding used for all characters in the configuration -# file that follow. The default is UTF-8 which is also the encoding used for all -# text before the first occurrence of this tag. Doxygen uses libiconv (or the -# iconv built into libc) for the transcoding. See -# https://www.gnu.org/software/libiconv/ for the list of possible encodings. -# The default value is: UTF-8. - -DOXYFILE_ENCODING = UTF-8 - -# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by -# double-quotes, unless you are using Doxywizard) that should identify the -# project for which the documentation is generated. This name is used in the -# title of most generated pages and in a few other places. -# The default value is: My Project. - -PROJECT_NAME = mirror-logging - -# The PROJECT_NUMBER tag can be used to enter a project or revision number. This -# could be handy for archiving the generated documentation or if some version -# control system is used. - -PROJECT_NUMBER = - -# Using the PROJECT_BRIEF tag one can provide an optional one line description -# for a project that appears at the top of each page and should give viewer a -# quick idea about the purpose of the project. Keep the description short. - -PROJECT_BRIEF = - -# With the PROJECT_LOGO tag one can specify a logo or an icon that is included -# in the documentation. The maximum height of the logo should not exceed 55 -# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy -# the logo to the output directory. - -PROJECT_LOGO = - -# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path -# into which the generated documentation will be written. If a relative path is -# entered, it will be relative to the location where doxygen was started. If -# left blank the current directory will be used. - -OUTPUT_DIRECTORY = ./docs - -# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096 -# sub-directories (in 2 levels) under the output directory of each output format -# and will distribute the generated files over these directories. Enabling this -# option can be useful when feeding doxygen a huge amount of source files, where -# putting all generated files in the same directory would otherwise causes -# performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to -# control the number of sub-directories. -# The default value is: NO. - -CREATE_SUBDIRS = NO - -# Controls the number of sub-directories that will be created when -# CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every -# level increment doubles the number of directories, resulting in 4096 -# directories at level 8 which is the default and also the maximum value. The -# sub-directories are organized in 2 levels, the first level always has a fixed -# number of 16 directories. -# Minimum value: 0, maximum value: 8, default value: 8. -# This tag requires that the tag CREATE_SUBDIRS is set to YES. - -CREATE_SUBDIRS_LEVEL = 8 - -# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII -# characters to appear in the names of generated files. If set to NO, non-ASCII -# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode -# U+3044. -# The default value is: NO. - -ALLOW_UNICODE_NAMES = NO - -# The OUTPUT_LANGUAGE tag is used to specify the language in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all constant output in the proper language. -# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian, -# Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English -# (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek, -# Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with -# English messages), Korean, Korean-en (Korean with English messages), Latvian, -# Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, -# Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, -# Swedish, Turkish, Ukrainian and Vietnamese. -# The default value is: English. - -OUTPUT_LANGUAGE = English - -# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member -# descriptions after the members that are listed in the file and class -# documentation (similar to Javadoc). Set to NO to disable this. -# The default value is: YES. - -BRIEF_MEMBER_DESC = YES - -# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief -# description of a member or function before the detailed description -# -# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the -# brief descriptions will be completely suppressed. -# The default value is: YES. - -REPEAT_BRIEF = YES - -# This tag implements a quasi-intelligent brief description abbreviator that is -# used to form the text in various listings. Each string in this list, if found -# as the leading text of the brief description, will be stripped from the text -# and the result, after processing the whole list, is used as the annotated -# text. Otherwise, the brief description is used as-is. If left blank, the -# following values are used ($name is automatically replaced with the name of -# the entity):The $name class, The $name widget, The $name file, is, provides, -# specifies, contains, represents, a, an and the. - -ABBREVIATE_BRIEF = "The $name class" \ - "The $name widget" \ - "The $name file" \ - is \ - provides \ - specifies \ - contains \ - represents \ - a \ - an \ - the - -# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# doxygen will generate a detailed section even if there is only a brief -# description. -# The default value is: NO. - -ALWAYS_DETAILED_SEC = NO - -# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all -# inherited members of a class in the documentation of that class as if those -# members were ordinary class members. Constructors, destructors and assignment -# operators of the base classes will not be shown. -# The default value is: NO. - -INLINE_INHERITED_MEMB = NO - -# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path -# before files name in the file list and in the header files. If set to NO the -# shortest path that makes the file name unique will be used -# The default value is: YES. - -FULL_PATH_NAMES = YES - -# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. -# Stripping is only done if one of the specified strings matches the left-hand -# part of the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the path to -# strip. -# -# Note that you can specify absolute paths here, but also relative paths, which -# will be relative from the directory where doxygen is started. -# This tag requires that the tag FULL_PATH_NAMES is set to YES. - -STRIP_FROM_PATH = - -# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the -# path mentioned in the documentation of a class, which tells the reader which -# header file to include in order to use a class. If left blank only the name of -# the header file containing the class definition is used. Otherwise one should -# specify the list of include paths that are normally passed to the compiler -# using the -I flag. - -STRIP_FROM_INC_PATH = - -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but -# less readable) file names. This can be useful is your file systems doesn't -# support long names like on DOS, Mac, or CD-ROM. -# The default value is: NO. - -SHORT_NAMES = NO - -# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the -# first line (until the first dot) of a Javadoc-style comment as the brief -# description. If set to NO, the Javadoc-style will behave just like regular Qt- -# style comments (thus requiring an explicit @brief command for a brief -# description.) -# The default value is: NO. - -JAVADOC_AUTOBRIEF = NO - -# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line -# such as -# /*************** -# as being the beginning of a Javadoc-style comment "banner". If set to NO, the -# Javadoc-style will behave just like regular comments and it will not be -# interpreted by doxygen. -# The default value is: NO. - -JAVADOC_BANNER = NO - -# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first -# line (until the first dot) of a Qt-style comment as the brief description. If -# set to NO, the Qt-style will behave just like regular Qt-style comments (thus -# requiring an explicit \brief command for a brief description.) -# The default value is: NO. - -QT_AUTOBRIEF = NO - -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a -# multi-line C++ special comment block (i.e. a block of //! or /// comments) as -# a brief description. This used to be the default behavior. The new default is -# to treat a multi-line C++ comment block as a detailed description. Set this -# tag to YES if you prefer the old behavior instead. -# -# Note that setting this tag to YES also means that rational rose comments are -# not recognized any more. -# The default value is: NO. - -MULTILINE_CPP_IS_BRIEF = NO - -# By default Python docstrings are displayed as preformatted text and doxygen's -# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the -# doxygen's special commands can be used and the contents of the docstring -# documentation blocks is shown as doxygen documentation. -# The default value is: YES. - -PYTHON_DOCSTRING = YES - -# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the -# documentation from any documented member that it re-implements. -# The default value is: YES. - -INHERIT_DOCS = YES - -# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new -# page for each member. If set to NO, the documentation of a member will be part -# of the file/class/namespace that contains it. -# The default value is: NO. - -SEPARATE_MEMBER_PAGES = NO - -# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen -# uses this value to replace tabs by spaces in code fragments. -# Minimum value: 1, maximum value: 16, default value: 4. - -TAB_SIZE = 4 - -# This tag can be used to specify a number of aliases that act as commands in -# the documentation. An alias has the form: -# name=value -# For example adding -# "sideeffect=@par Side Effects:^^" -# will allow you to put the command \sideeffect (or @sideeffect) in the -# documentation, which will result in a user-defined paragraph with heading -# "Side Effects:". Note that you cannot put \n's in the value part of an alias -# to insert newlines (in the resulting output). You can put ^^ in the value part -# of an alias to insert a newline as if a physical newline was in the original -# file. When you need a literal { or } or , in the value part of an alias you -# have to escape them by means of a backslash (\), this can lead to conflicts -# with the commands \{ and \} for these it is advised to use the version @{ and -# @} or use a double escape (\\{ and \\}) - -ALIASES = - -# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources -# only. Doxygen will then generate output that is more tailored for C. For -# instance, some of the names that are used will be different. The list of all -# members will be omitted, etc. -# The default value is: NO. - -OPTIMIZE_OUTPUT_FOR_C = NO - -# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or -# Python sources only. Doxygen will then generate output that is more tailored -# for that language. For instance, namespaces will be presented as packages, -# qualified scopes will look different, etc. -# The default value is: NO. - -OPTIMIZE_OUTPUT_JAVA = NO - -# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran -# sources. Doxygen will then generate output that is tailored for Fortran. -# The default value is: NO. - -OPTIMIZE_FOR_FORTRAN = NO - -# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL -# sources. Doxygen will then generate output that is tailored for VHDL. -# The default value is: NO. - -OPTIMIZE_OUTPUT_VHDL = NO - -# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice -# sources only. Doxygen will then generate output that is more tailored for that -# language. For instance, namespaces will be presented as modules, types will be -# separated into more groups, etc. -# The default value is: NO. - -OPTIMIZE_OUTPUT_SLICE = NO - -# Doxygen selects the parser to use depending on the extension of the files it -# parses. With this tag you can assign which parser to use for a given -# extension. Doxygen has a built-in mapping, but you can override or extend it -# using this tag. The format is ext=language, where ext is a file extension, and -# language is one of the parsers supported by doxygen: IDL, Java, JavaScript, -# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice, -# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: -# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser -# tries to guess whether the code is fixed or free formatted code, this is the -# default for Fortran type files). For instance to make doxygen treat .inc files -# as Fortran files (default is PHP), and .f files as C (default is Fortran), -# use: inc=Fortran f=C. -# -# Note: For files without extension you can use no_extension as a placeholder. -# -# Note that for custom extensions you also need to set FILE_PATTERNS otherwise -# the files are not read by doxygen. When specifying no_extension you should add -# * to the FILE_PATTERNS. -# -# Note see also the list of default file extension mappings. - -EXTENSION_MAPPING = - -# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments -# according to the Markdown format, which allows for more readable -# documentation. See https://daringfireball.net/projects/markdown/ for details. -# The output of markdown processing is further processed by doxygen, so you can -# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in -# case of backward compatibilities issues. -# The default value is: YES. - -MARKDOWN_SUPPORT = YES - -# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up -# to that level are automatically included in the table of contents, even if -# they do not have an id attribute. -# Note: This feature currently applies only to Markdown headings. -# Minimum value: 0, maximum value: 99, default value: 5. -# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. - -TOC_INCLUDE_HEADINGS = 5 - -# The MARKDOWN_ID_STYLE tag can be used to specify the algorithm used to -# generate identifiers for the Markdown headings. Note: Every identifier is -# unique. -# Possible values are: DOXYGEN use a fixed 'autotoc_md' string followed by a -# sequence number starting at 0 and GITHUB use the lower case version of title -# with any whitespace replaced by '-' and punctuation characters removed. -# The default value is: DOXYGEN. -# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. - -MARKDOWN_ID_STYLE = DOXYGEN - -# When enabled doxygen tries to link words that correspond to documented -# classes, or namespaces to their corresponding documentation. Such a link can -# be prevented in individual cases by putting a % sign in front of the word or -# globally by setting AUTOLINK_SUPPORT to NO. -# The default value is: YES. - -AUTOLINK_SUPPORT = YES - -# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want -# to include (a tag file for) the STL sources as input, then you should set this -# tag to YES in order to let doxygen match functions declarations and -# definitions whose arguments contain STL classes (e.g. func(std::string); -# versus func(std::string) {}). This also make the inheritance and collaboration -# diagrams that involve STL classes more complete and accurate. -# The default value is: NO. - -BUILTIN_STL_SUPPORT = NO - -# If you use Microsoft's C++/CLI language, you should set this option to YES to -# enable parsing support. -# The default value is: NO. - -CPP_CLI_SUPPORT = NO - -# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: -# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen -# will parse them like normal C++ but will assume all classes use public instead -# of private inheritance when no explicit protection keyword is present. -# The default value is: NO. - -SIP_SUPPORT = NO - -# For Microsoft's IDL there are propget and propput attributes to indicate -# getter and setter methods for a property. Setting this option to YES will make -# doxygen to replace the get and set methods by a property in the documentation. -# This will only work if the methods are indeed getting or setting a simple -# type. If this is not the case, or you want to show the methods anyway, you -# should set this option to NO. -# The default value is: YES. - -IDL_PROPERTY_SUPPORT = YES - -# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES then doxygen will reuse the documentation of the first -# member in the group (if any) for the other members of the group. By default -# all members of a group must be documented explicitly. -# The default value is: NO. - -DISTRIBUTE_GROUP_DOC = NO - -# If one adds a struct or class to a group and this option is enabled, then also -# any nested class or struct is added to the same group. By default this option -# is disabled and one has to add nested compounds explicitly via \ingroup. -# The default value is: NO. - -GROUP_NESTED_COMPOUNDS = NO - -# Set the SUBGROUPING tag to YES to allow class member groups of the same type -# (for instance a group of public functions) to be put as a subgroup of that -# type (e.g. under the Public Functions section). Set it to NO to prevent -# subgrouping. Alternatively, this can be done per class using the -# \nosubgrouping command. -# The default value is: YES. - -SUBGROUPING = YES - -# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions -# are shown inside the group in which they are included (e.g. using \ingroup) -# instead of on a separate page (for HTML and Man pages) or section (for LaTeX -# and RTF). -# -# Note that this feature does not work in combination with -# SEPARATE_MEMBER_PAGES. -# The default value is: NO. - -INLINE_GROUPED_CLASSES = NO - -# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions -# with only public data fields or simple typedef fields will be shown inline in -# the documentation of the scope in which they are defined (i.e. file, -# namespace, or group documentation), provided this scope is documented. If set -# to NO, structs, classes, and unions are shown on a separate page (for HTML and -# Man pages) or section (for LaTeX and RTF). -# The default value is: NO. - -INLINE_SIMPLE_STRUCTS = NO - -# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or -# enum is documented as struct, union, or enum with the name of the typedef. So -# typedef struct TypeS {} TypeT, will appear in the documentation as a struct -# with name TypeT. When disabled the typedef will appear as a member of a file, -# namespace, or class. And the struct will be named TypeS. This can typically be -# useful for C code in case the coding convention dictates that all compound -# types are typedef'ed and only the typedef is referenced, never the tag name. -# The default value is: NO. - -TYPEDEF_HIDES_STRUCT = NO - -# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This -# cache is used to resolve symbols given their name and scope. Since this can be -# an expensive process and often the same symbol appears multiple times in the -# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small -# doxygen will become slower. If the cache is too large, memory is wasted. The -# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range -# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 -# symbols. At the end of a run doxygen will report the cache usage and suggest -# the optimal cache size from a speed point of view. -# Minimum value: 0, maximum value: 9, default value: 0. - -LOOKUP_CACHE_SIZE = 0 - -# The NUM_PROC_THREADS specifies the number of threads doxygen is allowed to use -# during processing. When set to 0 doxygen will based this on the number of -# cores available in the system. You can set it explicitly to a value larger -# than 0 to get more control over the balance between CPU load and processing -# speed. At this moment only the input processing can be done using multiple -# threads. Since this is still an experimental feature the default is set to 1, -# which effectively disables parallel processing. Please report any issues you -# encounter. Generating dot graphs in parallel is controlled by the -# DOT_NUM_THREADS setting. -# Minimum value: 0, maximum value: 32, default value: 1. - -NUM_PROC_THREADS = 1 - -# If the TIMESTAMP tag is set different from NO then each generated page will -# contain the date or date and time when the page was generated. Setting this to -# NO can help when comparing the output of multiple runs. -# Possible values are: YES, NO, DATETIME and DATE. -# The default value is: NO. - -TIMESTAMP = NO - -#--------------------------------------------------------------------------- -# Build related configuration options -#--------------------------------------------------------------------------- - -# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in -# documentation are documented, even if no documentation was available. Private -# class members and static file members will be hidden unless the -# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. -# Note: This will also disable the warnings about undocumented members that are -# normally produced when WARNINGS is set to YES. -# The default value is: NO. - -EXTRACT_ALL = NO - -# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will -# be included in the documentation. -# The default value is: NO. - -EXTRACT_PRIVATE = YES - -# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual -# methods of a class will be included in the documentation. -# The default value is: NO. - -EXTRACT_PRIV_VIRTUAL = NO - -# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal -# scope will be included in the documentation. -# The default value is: NO. - -EXTRACT_PACKAGE = NO - -# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be -# included in the documentation. -# The default value is: NO. - -EXTRACT_STATIC = NO - -# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined -# locally in source files will be included in the documentation. If set to NO, -# only classes defined in header files are included. Does not have any effect -# for Java sources. -# The default value is: YES. - -EXTRACT_LOCAL_CLASSES = YES - -# This flag is only useful for Objective-C code. If set to YES, local methods, -# which are defined in the implementation section but not in the interface are -# included in the documentation. If set to NO, only methods in the interface are -# included. -# The default value is: NO. - -EXTRACT_LOCAL_METHODS = NO - -# If this flag is set to YES, the members of anonymous namespaces will be -# extracted and appear in the documentation as a namespace called -# 'anonymous_namespace{file}', where file will be replaced with the base name of -# the file that contains the anonymous namespace. By default anonymous namespace -# are hidden. -# The default value is: NO. - -EXTRACT_ANON_NSPACES = NO - -# If this flag is set to YES, the name of an unnamed parameter in a declaration -# will be determined by the corresponding definition. By default unnamed -# parameters remain unnamed in the output. -# The default value is: YES. - -RESOLVE_UNNAMED_PARAMS = YES - -# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all -# undocumented members inside documented classes or files. If set to NO these -# members will be included in the various overviews, but no documentation -# section is generated. This option has no effect if EXTRACT_ALL is enabled. -# The default value is: NO. - -HIDE_UNDOC_MEMBERS = NO - -# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all -# undocumented classes that are normally visible in the class hierarchy. If set -# to NO, these classes will be included in the various overviews. This option -# will also hide undocumented C++ concepts if enabled. This option has no effect -# if EXTRACT_ALL is enabled. -# The default value is: NO. - -HIDE_UNDOC_CLASSES = NO - -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend -# declarations. If set to NO, these declarations will be included in the -# documentation. -# The default value is: NO. - -HIDE_FRIEND_COMPOUNDS = NO - -# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any -# documentation blocks found inside the body of a function. If set to NO, these -# blocks will be appended to the function's detailed documentation block. -# The default value is: NO. - -HIDE_IN_BODY_DOCS = NO - -# The INTERNAL_DOCS tag determines if documentation that is typed after a -# \internal command is included. If the tag is set to NO then the documentation -# will be excluded. Set it to YES to include the internal documentation. -# The default value is: NO. - -INTERNAL_DOCS = NO - -# With the correct setting of option CASE_SENSE_NAMES doxygen will better be -# able to match the capabilities of the underlying filesystem. In case the -# filesystem is case sensitive (i.e. it supports files in the same directory -# whose names only differ in casing), the option must be set to YES to properly -# deal with such files in case they appear in the input. For filesystems that -# are not case sensitive the option should be set to NO to properly deal with -# output files written for symbols that only differ in casing, such as for two -# classes, one named CLASS and the other named Class, and to also support -# references to files without having to specify the exact matching casing. On -# Windows (including Cygwin) and MacOS, users should typically set this option -# to NO, whereas on Linux or other Unix flavors it should typically be set to -# YES. -# Possible values are: SYSTEM, NO and YES. -# The default value is: SYSTEM. - -CASE_SENSE_NAMES = YES - -# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with -# their full class and namespace scopes in the documentation. If set to YES, the -# scope will be hidden. -# The default value is: NO. - -HIDE_SCOPE_NAMES = NO - -# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will -# append additional text to a page's title, such as Class Reference. If set to -# YES the compound reference will be hidden. -# The default value is: NO. - -HIDE_COMPOUND_REFERENCE= NO - -# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class -# will show which file needs to be included to use the class. -# The default value is: YES. - -SHOW_HEADERFILE = YES - -# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of -# the files that are included by a file in the documentation of that file. -# The default value is: YES. - -SHOW_INCLUDE_FILES = YES - -# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each -# grouped member an include statement to the documentation, telling the reader -# which file to include in order to use the member. -# The default value is: NO. - -SHOW_GROUPED_MEMB_INC = NO - -# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include -# files with double quotes in the documentation rather than with sharp brackets. -# The default value is: NO. - -FORCE_LOCAL_INCLUDES = NO - -# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the -# documentation for inline members. -# The default value is: YES. - -INLINE_INFO = YES - -# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the -# (detailed) documentation of file and class members alphabetically by member -# name. If set to NO, the members will appear in declaration order. -# The default value is: YES. - -SORT_MEMBER_DOCS = YES - -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief -# descriptions of file, namespace and class members alphabetically by member -# name. If set to NO, the members will appear in declaration order. Note that -# this will also influence the order of the classes in the class list. -# The default value is: NO. - -SORT_BRIEF_DOCS = NO - -# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the -# (brief and detailed) documentation of class members so that constructors and -# destructors are listed first. If set to NO the constructors will appear in the -# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. -# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief -# member documentation. -# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting -# detailed member documentation. -# The default value is: NO. - -SORT_MEMBERS_CTORS_1ST = NO - -# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy -# of group names into alphabetical order. If set to NO the group names will -# appear in their defined order. -# The default value is: NO. - -SORT_GROUP_NAMES = NO - -# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by -# fully-qualified names, including namespaces. If set to NO, the class list will -# be sorted only by class name, not including the namespace part. -# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. -# Note: This option applies only to the class list, not to the alphabetical -# list. -# The default value is: NO. - -SORT_BY_SCOPE_NAME = NO - -# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper -# type resolution of all parameters of a function it will reject a match between -# the prototype and the implementation of a member function even if there is -# only one candidate or it is obvious which candidate to choose by doing a -# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still -# accept a match between prototype and implementation in such cases. -# The default value is: NO. - -STRICT_PROTO_MATCHING = NO - -# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo -# list. This list is created by putting \todo commands in the documentation. -# The default value is: YES. - -GENERATE_TODOLIST = YES - -# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test -# list. This list is created by putting \test commands in the documentation. -# The default value is: YES. - -GENERATE_TESTLIST = YES - -# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug -# list. This list is created by putting \bug commands in the documentation. -# The default value is: YES. - -GENERATE_BUGLIST = YES - -# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) -# the deprecated list. This list is created by putting \deprecated commands in -# the documentation. -# The default value is: YES. - -GENERATE_DEPRECATEDLIST= YES - -# The ENABLED_SECTIONS tag can be used to enable conditional documentation -# sections, marked by \if ... \endif and \cond -# ... \endcond blocks. - -ENABLED_SECTIONS = - -# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the -# initial value of a variable or macro / define can have for it to appear in the -# documentation. If the initializer consists of more lines than specified here -# it will be hidden. Use a value of 0 to hide initializers completely. The -# appearance of the value of individual variables and macros / defines can be -# controlled using \showinitializer or \hideinitializer command in the -# documentation regardless of this setting. -# Minimum value: 0, maximum value: 10000, default value: 30. - -MAX_INITIALIZER_LINES = 30 - -# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at -# the bottom of the documentation of classes and structs. If set to YES, the -# list will mention the files that were used to generate the documentation. -# The default value is: YES. - -SHOW_USED_FILES = YES - -# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This -# will remove the Files entry from the Quick Index and from the Folder Tree View -# (if specified). -# The default value is: YES. - -SHOW_FILES = YES - -# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces -# page. This will remove the Namespaces entry from the Quick Index and from the -# Folder Tree View (if specified). -# The default value is: YES. - -SHOW_NAMESPACES = YES - -# The FILE_VERSION_FILTER tag can be used to specify a program or script that -# doxygen should invoke to get the current version for each file (typically from -# the version control system). Doxygen will invoke the program by executing (via -# popen()) the command command input-file, where command is the value of the -# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided -# by doxygen. Whatever the program writes to standard output is used as the file -# version. For an example see the documentation. - -FILE_VERSION_FILTER = - -# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed -# by doxygen. The layout file controls the global structure of the generated -# output files in an output format independent way. To create the layout file -# that represents doxygen's defaults, run doxygen with the -l option. You can -# optionally specify a file name after the option, if omitted DoxygenLayout.xml -# will be used as the name of the layout file. See also section "Changing the -# layout of pages" for information. -# -# Note that if you run doxygen from a directory containing a file called -# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE -# tag is left empty. - -LAYOUT_FILE = - -# The CITE_BIB_FILES tag can be used to specify one or more bib files containing -# the reference definitions. This must be a list of .bib files. The .bib -# extension is automatically appended if omitted. This requires the bibtex tool -# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. -# For LaTeX the style of the bibliography can be controlled using -# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the -# search path. See also \cite for info how to create references. - -CITE_BIB_FILES = - -#--------------------------------------------------------------------------- -# Configuration options related to warning and progress messages -#--------------------------------------------------------------------------- - -# The QUIET tag can be used to turn on/off the messages that are generated to -# standard output by doxygen. If QUIET is set to YES this implies that the -# messages are off. -# The default value is: NO. - -QUIET = NO - -# The WARNINGS tag can be used to turn on/off the warning messages that are -# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES -# this implies that the warnings are on. -# -# Tip: Turn warnings on while writing the documentation. -# The default value is: YES. - -WARNINGS = YES - -# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate -# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag -# will automatically be disabled. -# The default value is: YES. - -WARN_IF_UNDOCUMENTED = YES - -# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as documenting some parameters in -# a documented function twice, or documenting parameters that don't exist or -# using markup commands wrongly. -# The default value is: YES. - -WARN_IF_DOC_ERROR = YES - -# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete -# function parameter documentation. If set to NO, doxygen will accept that some -# parameters have no documentation without warning. -# The default value is: YES. - -WARN_IF_INCOMPLETE_DOC = YES - -# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that -# are documented, but have no documentation for their parameters or return -# value. If set to NO, doxygen will only warn about wrong parameter -# documentation, but not about the absence of documentation. If EXTRACT_ALL is -# set to YES then this flag will automatically be disabled. See also -# WARN_IF_INCOMPLETE_DOC -# The default value is: NO. - -WARN_NO_PARAMDOC = NO - -# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about -# undocumented enumeration values. If set to NO, doxygen will accept -# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag -# will automatically be disabled. -# The default value is: NO. - -WARN_IF_UNDOC_ENUM_VAL = NO - -# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when -# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS -# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but -# at the end of the doxygen process doxygen will return with a non-zero status. -# If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then doxygen behaves -# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined doxygen will not -# write the warning messages in between other messages but write them at the end -# of a run, in case a WARN_LOGFILE is defined the warning messages will be -# besides being in the defined file also be shown at the end of a run, unless -# the WARN_LOGFILE is defined as - i.e. standard output (stdout) in that case -# the behavior will remain as with the setting FAIL_ON_WARNINGS. -# Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT. -# The default value is: NO. - -WARN_AS_ERROR = NO - -# The WARN_FORMAT tag determines the format of the warning messages that doxygen -# can produce. The string should contain the $file, $line, and $text tags, which -# will be replaced by the file and line number from which the warning originated -# and the warning text. Optionally the format may contain $version, which will -# be replaced by the version of the file (if it could be obtained via -# FILE_VERSION_FILTER) -# See also: WARN_LINE_FORMAT -# The default value is: $file:$line: $text. - -WARN_FORMAT = "$file:$line: $text" - -# In the $text part of the WARN_FORMAT command it is possible that a reference -# to a more specific place is given. To make it easier to jump to this place -# (outside of doxygen) the user can define a custom "cut" / "paste" string. -# Example: -# WARN_LINE_FORMAT = "'vi $file +$line'" -# See also: WARN_FORMAT -# The default value is: at line $line of file $file. - -WARN_LINE_FORMAT = "at line $line of file $file" - -# The WARN_LOGFILE tag can be used to specify a file to which warning and error -# messages should be written. If left blank the output is written to standard -# error (stderr). In case the file specified cannot be opened for writing the -# warning and error messages are written to standard error. When as file - is -# specified the warning and error messages are written to standard output -# (stdout). - -WARN_LOGFILE = - -#--------------------------------------------------------------------------- -# Configuration options related to the input files -#--------------------------------------------------------------------------- - -# The INPUT tag is used to specify the files and/or directories that contain -# documented source files. You may enter file names like myfile.cpp or -# directories like /usr/src/myproject. Separate the files or directories with -# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING -# Note: If this tag is empty the current directory is searched. - -INPUT = ./include - -# This tag can be used to specify the character encoding of the source files -# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses -# libiconv (or the iconv built into libc) for the transcoding. See the libiconv -# documentation (see: -# https://www.gnu.org/software/libiconv/) for the list of possible encodings. -# See also: INPUT_FILE_ENCODING -# The default value is: UTF-8. - -INPUT_ENCODING = UTF-8 - -# This tag can be used to specify the character encoding of the source files -# that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify -# character encoding on a per file pattern basis. Doxygen will compare the file -# name with each pattern and apply the encoding instead of the default -# INPUT_ENCODING) if there is a match. The character encodings are a list of the -# form: pattern=encoding (like *.php=ISO-8859-1). See cfg_input_encoding -# "INPUT_ENCODING" for further information on supported encodings. - -INPUT_FILE_ENCODING = - -# If the value of the INPUT tag contains directories, you can use the -# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and -# *.h) to filter out the source-files in the directories. -# -# Note that for custom extensions or not directly supported extensions you also -# need to set EXTENSION_MAPPING for the extension otherwise the files are not -# read by doxygen. -# -# Note the list of default checked file patterns might differ from the list of -# default file extension mappings. -# -# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cxxm, -# *.cpp, *.cppm, *.c++, *.c++m, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, -# *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.ixx, *.l, *.cs, *.d, *.php, -# *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be -# provided as doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, -# *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice. - -FILE_PATTERNS = *.c \ - *.cc \ - *.cxx \ - *.cpp \ - *.c++ \ - *.java \ - *.ii \ - *.ixx \ - *.ipp \ - *.i++ \ - *.inl \ - *.idl \ - *.ddl \ - *.odl \ - *.h \ - *.hh \ - *.hxx \ - *.hpp \ - *.h++ \ - *.cs \ - *.d \ - *.php \ - *.php4 \ - *.php5 \ - *.phtml \ - *.inc \ - *.m \ - *.markdown \ - *.md \ - *.mm \ - *.dox \ - *.py \ - *.pyw \ - *.f90 \ - *.f95 \ - *.f03 \ - *.f08 \ - *.f18 \ - *.f \ - *.for \ - *.vhd \ - *.vhdl \ - *.ucf \ - *.qsf \ - *.ice - -# The RECURSIVE tag can be used to specify whether or not subdirectories should -# be searched for input files as well. -# The default value is: NO. - -RECURSIVE = YES - -# The EXCLUDE tag can be used to specify files and/or directories that should be -# excluded from the INPUT source files. This way you can easily exclude a -# subdirectory from a directory tree whose root is specified with the INPUT tag. -# -# Note that relative paths are relative to the directory from which doxygen is -# run. - -EXCLUDE = - -# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or -# directories that are symbolic links (a Unix file system feature) are excluded -# from the input. -# The default value is: NO. - -EXCLUDE_SYMLINKS = NO - -# If the value of the INPUT tag contains directories, you can use the -# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. -# -# Note that the wildcards are matched against the file with absolute path, so to -# exclude all test directories for example use the pattern */test/* - -EXCLUDE_PATTERNS = - -# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names -# (namespaces, classes, functions, etc.) that should be excluded from the -# output. The symbol name can be a fully qualified name, a word, or if the -# wildcard * is used, a substring. Examples: ANamespace, AClass, -# ANamespace::AClass, ANamespace::*Test - -EXCLUDE_SYMBOLS = - -# The EXAMPLE_PATH tag can be used to specify one or more files or directories -# that contain example code fragments that are included (see the \include -# command). - -EXAMPLE_PATH = - -# If the value of the EXAMPLE_PATH tag contains directories, you can use the -# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and -# *.h) to filter out the source-files in the directories. If left blank all -# files are included. - -EXAMPLE_PATTERNS = * - -# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be -# searched for input files to be used with the \include or \dontinclude commands -# irrespective of the value of the RECURSIVE tag. -# The default value is: NO. - -EXAMPLE_RECURSIVE = NO - -# The IMAGE_PATH tag can be used to specify one or more files or directories -# that contain images that are to be included in the documentation (see the -# \image command). - -IMAGE_PATH = - -# The INPUT_FILTER tag can be used to specify a program that doxygen should -# invoke to filter for each input file. Doxygen will invoke the filter program -# by executing (via popen()) the command: -# -# -# -# where is the value of the INPUT_FILTER tag, and is the -# name of an input file. Doxygen will then use the output that the filter -# program writes to standard output. If FILTER_PATTERNS is specified, this tag -# will be ignored. -# -# Note that the filter must not add or remove lines; it is applied before the -# code is scanned, but not when the output code is generated. If lines are added -# or removed, the anchors will not be placed correctly. -# -# Note that doxygen will use the data processed and written to standard output -# for further processing, therefore nothing else, like debug statements or used -# commands (so in case of a Windows batch file always use @echo OFF), should be -# written to standard output. -# -# Note that for custom extensions or not directly supported extensions you also -# need to set EXTENSION_MAPPING for the extension otherwise the files are not -# properly processed by doxygen. - -INPUT_FILTER = - -# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern -# basis. Doxygen will compare the file name with each pattern and apply the -# filter if there is a match. The filters are a list of the form: pattern=filter -# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how -# filters are used. If the FILTER_PATTERNS tag is empty or if none of the -# patterns match the file name, INPUT_FILTER is applied. -# -# Note that for custom extensions or not directly supported extensions you also -# need to set EXTENSION_MAPPING for the extension otherwise the files are not -# properly processed by doxygen. - -FILTER_PATTERNS = - -# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER) will also be used to filter the input files that are used for -# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). -# The default value is: NO. - -FILTER_SOURCE_FILES = NO - -# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file -# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and -# it is also possible to disable source filtering for a specific pattern using -# *.ext= (so without naming a filter). -# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. - -FILTER_SOURCE_PATTERNS = - -# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that -# is part of the input, its contents will be placed on the main page -# (index.html). This can be useful if you have a project on for instance GitHub -# and want to reuse the introduction page also for the doxygen output. - -USE_MDFILE_AS_MAINPAGE = - -# The Fortran standard specifies that for fixed formatted Fortran code all -# characters from position 72 are to be considered as comment. A common -# extension is to allow longer lines before the automatic comment starts. The -# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can -# be processed before the automatic comment starts. -# Minimum value: 7, maximum value: 10000, default value: 72. - -FORTRAN_COMMENT_AFTER = 72 - -#--------------------------------------------------------------------------- -# Configuration options related to source browsing -#--------------------------------------------------------------------------- - -# If the SOURCE_BROWSER tag is set to YES then a list of source files will be -# generated. Documented entities will be cross-referenced with these sources. -# -# Note: To get rid of all source code in the generated output, make sure that -# also VERBATIM_HEADERS is set to NO. -# The default value is: NO. - -SOURCE_BROWSER = NO - -# Setting the INLINE_SOURCES tag to YES will include the body of functions, -# classes and enums directly into the documentation. -# The default value is: NO. - -INLINE_SOURCES = NO - -# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any -# special comment blocks from generated source code fragments. Normal C, C++ and -# Fortran comments will always remain visible. -# The default value is: YES. - -STRIP_CODE_COMMENTS = YES - -# If the REFERENCED_BY_RELATION tag is set to YES then for each documented -# entity all documented functions referencing it will be listed. -# The default value is: NO. - -REFERENCED_BY_RELATION = NO - -# If the REFERENCES_RELATION tag is set to YES then for each documented function -# all documented entities called/used by that function will be listed. -# The default value is: NO. - -REFERENCES_RELATION = NO - -# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set -# to YES then the hyperlinks from functions in REFERENCES_RELATION and -# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will -# link to the documentation. -# The default value is: YES. - -REFERENCES_LINK_SOURCE = YES - -# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the -# source code will show a tooltip with additional information such as prototype, -# brief description and links to the definition and documentation. Since this -# will make the HTML file larger and loading of large files a bit slower, you -# can opt to disable this feature. -# The default value is: YES. -# This tag requires that the tag SOURCE_BROWSER is set to YES. - -SOURCE_TOOLTIPS = YES - -# If the USE_HTAGS tag is set to YES then the references to source code will -# point to the HTML generated by the htags(1) tool instead of doxygen built-in -# source browser. The htags tool is part of GNU's global source tagging system -# (see https://www.gnu.org/software/global/global.html). You will need version -# 4.8.6 or higher. -# -# To use it do the following: -# - Install the latest version of global -# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file -# - Make sure the INPUT points to the root of the source tree -# - Run doxygen as normal -# -# Doxygen will invoke htags (and that will in turn invoke gtags), so these -# tools must be available from the command line (i.e. in the search path). -# -# The result: instead of the source browser generated by doxygen, the links to -# source code will now point to the output of htags. -# The default value is: NO. -# This tag requires that the tag SOURCE_BROWSER is set to YES. - -USE_HTAGS = NO - -# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a -# verbatim copy of the header file for each class for which an include is -# specified. Set to NO to disable this. -# See also: Section \class. -# The default value is: YES. - -VERBATIM_HEADERS = YES - -#--------------------------------------------------------------------------- -# Configuration options related to the alphabetical class index -#--------------------------------------------------------------------------- - -# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all -# compounds will be generated. Enable this if the project contains a lot of -# classes, structs, unions or interfaces. -# The default value is: YES. - -ALPHABETICAL_INDEX = YES - -# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes) -# that should be ignored while generating the index headers. The IGNORE_PREFIX -# tag works for classes, function and member names. The entity will be placed in -# the alphabetical list under the first letter of the entity name that remains -# after removing the prefix. -# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. - -IGNORE_PREFIX = - -#--------------------------------------------------------------------------- -# Configuration options related to the HTML output -#--------------------------------------------------------------------------- - -# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output -# The default value is: YES. - -GENERATE_HTML = YES - -# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a -# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of -# it. -# The default directory is: html. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_OUTPUT = html - -# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each -# generated HTML page (for example: .htm, .php, .asp). -# The default value is: .html. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_FILE_EXTENSION = .html - -# The HTML_HEADER tag can be used to specify a user-defined HTML header file for -# each generated HTML page. If the tag is left blank doxygen will generate a -# standard header. -# -# To get valid HTML the header file that includes any scripts and style sheets -# that doxygen needs, which is dependent on the configuration options used (e.g. -# the setting GENERATE_TREEVIEW). It is highly recommended to start with a -# default header using -# doxygen -w html new_header.html new_footer.html new_stylesheet.css -# YourConfigFile -# and then modify the file new_header.html. See also section "Doxygen usage" -# for information on how to generate the default header that doxygen normally -# uses. -# Note: The header is subject to change so you typically have to regenerate the -# default header when upgrading to a newer version of doxygen. For a description -# of the possible markers and block names see the documentation. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_HEADER = - -# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each -# generated HTML page. If the tag is left blank doxygen will generate a standard -# footer. See HTML_HEADER for more information on how to generate a default -# footer and what special commands can be used inside the footer. See also -# section "Doxygen usage" for information on how to generate the default footer -# that doxygen normally uses. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_FOOTER = - -# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style -# sheet that is used by each HTML page. It can be used to fine-tune the look of -# the HTML output. If left blank doxygen will generate a default style sheet. -# See also section "Doxygen usage" for information on how to generate the style -# sheet that doxygen normally uses. -# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as -# it is more robust and this tag (HTML_STYLESHEET) will in the future become -# obsolete. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_STYLESHEET = - -# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined -# cascading style sheets that are included after the standard style sheets -# created by doxygen. Using this option one can overrule certain style aspects. -# This is preferred over using HTML_STYLESHEET since it does not replace the -# standard style sheet and is therefore more robust against future updates. -# Doxygen will copy the style sheet files to the output directory. -# Note: The order of the extra style sheet files is of importance (e.g. the last -# style sheet in the list overrules the setting of the previous ones in the -# list). -# Note: Since the styling of scrollbars can currently not be overruled in -# Webkit/Chromium, the styling will be left out of the default doxygen.css if -# one or more extra stylesheets have been specified. So if scrollbar -# customization is desired it has to be added explicitly. For an example see the -# documentation. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_EXTRA_STYLESHEET = - -# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or -# other source files which should be copied to the HTML output directory. Note -# that these files will be copied to the base HTML output directory. Use the -# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these -# files. In the HTML_STYLESHEET file, use the file name only. Also note that the -# files will be copied as-is; there are no commands or markers available. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_EXTRA_FILES = - -# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output -# should be rendered with a dark or light theme. -# Possible values are: LIGHT always generate light mode output, DARK always -# generate dark mode output, AUTO_LIGHT automatically set the mode according to -# the user preference, use light mode if no preference is set (the default), -# AUTO_DARK automatically set the mode according to the user preference, use -# dark mode if no preference is set and TOGGLE allow to user to switch between -# light and dark mode via a button. -# The default value is: AUTO_LIGHT. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_COLORSTYLE = AUTO_LIGHT - -# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen -# will adjust the colors in the style sheet and background images according to -# this color. Hue is specified as an angle on a color-wheel, see -# https://en.wikipedia.org/wiki/Hue for more information. For instance the value -# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 -# purple, and 360 is red again. -# Minimum value: 0, maximum value: 359, default value: 220. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_COLORSTYLE_HUE = 220 - -# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors -# in the HTML output. For a value of 0 the output will use gray-scales only. A -# value of 255 will produce the most vivid colors. -# Minimum value: 0, maximum value: 255, default value: 100. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_COLORSTYLE_SAT = 100 - -# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the -# luminance component of the colors in the HTML output. Values below 100 -# gradually make the output lighter, whereas values above 100 make the output -# darker. The value divided by 100 is the actual gamma applied, so 80 represents -# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not -# change the gamma. -# Minimum value: 40, maximum value: 240, default value: 80. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_COLORSTYLE_GAMMA = 80 - -# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML -# documentation will contain a main index with vertical navigation menus that -# are dynamically created via JavaScript. If disabled, the navigation index will -# consists of multiple levels of tabs that are statically embedded in every HTML -# page. Disable this option to support browsers that do not have JavaScript, -# like the Qt help browser. -# The default value is: YES. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_DYNAMIC_MENUS = YES - -# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML -# documentation will contain sections that can be hidden and shown after the -# page has loaded. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_DYNAMIC_SECTIONS = NO - -# If the HTML_CODE_FOLDING tag is set to YES then classes and functions can be -# dynamically folded and expanded in the generated HTML source code. -# The default value is: YES. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_CODE_FOLDING = YES - -# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries -# shown in the various tree structured indices initially; the user can expand -# and collapse entries dynamically later on. Doxygen will expand the tree to -# such a level that at most the specified number of entries are visible (unless -# a fully collapsed tree already exceeds this amount). So setting the number of -# entries 1 will produce a full collapsed tree by default. 0 is a special value -# representing an infinite number of entries and will result in a full expanded -# tree by default. -# Minimum value: 0, maximum value: 9999, default value: 100. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_INDEX_NUM_ENTRIES = 100 - -# If the GENERATE_DOCSET tag is set to YES, additional index files will be -# generated that can be used as input for Apple's Xcode 3 integrated development -# environment (see: -# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To -# create a documentation set, doxygen will generate a Makefile in the HTML -# output directory. Running make will produce the docset in that directory and -# running make install will install the docset in -# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at -# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy -# genXcode/_index.html for more information. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_DOCSET = NO - -# This tag determines the name of the docset feed. A documentation feed provides -# an umbrella under which multiple documentation sets from a single provider -# (such as a company or product suite) can be grouped. -# The default value is: Doxygen generated docs. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_FEEDNAME = "Doxygen generated docs" - -# This tag determines the URL of the docset feed. A documentation feed provides -# an umbrella under which multiple documentation sets from a single provider -# (such as a company or product suite) can be grouped. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_FEEDURL = - -# This tag specifies a string that should uniquely identify the documentation -# set bundle. This should be a reverse domain-name style string, e.g. -# com.mycompany.MyDocSet. Doxygen will append .docset to the name. -# The default value is: org.doxygen.Project. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_BUNDLE_ID = org.doxygen.Project - -# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify -# the documentation publisher. This should be a reverse domain-name style -# string, e.g. com.mycompany.MyDocSet.documentation. -# The default value is: org.doxygen.Publisher. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_PUBLISHER_ID = org.doxygen.Publisher - -# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. -# The default value is: Publisher. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_PUBLISHER_NAME = Publisher - -# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three -# additional HTML index files: index.hhp, index.hhc, and index.hhk. The -# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop -# on Windows. In the beginning of 2021 Microsoft took the original page, with -# a.o. the download links, offline the HTML help workshop was already many years -# in maintenance mode). You can download the HTML help workshop from the web -# archives at Installation executable (see: -# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo -# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe). -# -# The HTML Help Workshop contains a compiler that can convert all HTML output -# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML -# files are now used as the Windows 98 help format, and will replace the old -# Windows help format (.hlp) on all Windows platforms in the future. Compressed -# HTML files also contain an index, a table of contents, and you can search for -# words in the documentation. The HTML workshop also contains a viewer for -# compressed HTML files. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_HTMLHELP = NO - -# The CHM_FILE tag can be used to specify the file name of the resulting .chm -# file. You can add a path in front of the file if the result should not be -# written to the html output directory. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -CHM_FILE = - -# The HHC_LOCATION tag can be used to specify the location (absolute path -# including file name) of the HTML help compiler (hhc.exe). If non-empty, -# doxygen will try to run the HTML help compiler on the generated index.hhp. -# The file has to be specified with full path. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -HHC_LOCATION = - -# The GENERATE_CHI flag controls if a separate .chi index file is generated -# (YES) or that it should be included in the main .chm file (NO). -# The default value is: NO. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -GENERATE_CHI = NO - -# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) -# and project file content. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -CHM_INDEX_ENCODING = - -# The BINARY_TOC flag controls whether a binary table of contents is generated -# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it -# enables the Previous and Next buttons. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -BINARY_TOC = NO - -# The TOC_EXPAND flag can be set to YES to add extra items for group members to -# the table of contents of the HTML help documentation and to the tree view. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -TOC_EXPAND = NO - -# The SITEMAP_URL tag is used to specify the full URL of the place where the -# generated documentation will be placed on the server by the user during the -# deployment of the documentation. The generated sitemap is called sitemap.xml -# and placed on the directory specified by HTML_OUTPUT. In case no SITEMAP_URL -# is specified no sitemap is generated. For information about the sitemap -# protocol see https://www.sitemaps.org -# This tag requires that the tag GENERATE_HTML is set to YES. - -SITEMAP_URL = - -# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and -# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that -# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help -# (.qch) of the generated HTML documentation. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_QHP = NO - -# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify -# the file name of the resulting .qch file. The path specified is relative to -# the HTML output folder. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QCH_FILE = - -# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help -# Project output. For more information please see Qt Help Project / Namespace -# (see: -# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). -# The default value is: org.doxygen.Project. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_NAMESPACE = org.doxygen.Project - -# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt -# Help Project output. For more information please see Qt Help Project / Virtual -# Folders (see: -# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). -# The default value is: doc. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_VIRTUAL_FOLDER = doc - -# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom -# filter to add. For more information please see Qt Help Project / Custom -# Filters (see: -# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_CUST_FILTER_NAME = - -# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the -# custom filter to add. For more information please see Qt Help Project / Custom -# Filters (see: -# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_CUST_FILTER_ATTRS = - -# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this -# project's filter section matches. Qt Help Project / Filter Attributes (see: -# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_SECT_FILTER_ATTRS = - -# The QHG_LOCATION tag can be used to specify the location (absolute path -# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to -# run qhelpgenerator on the generated .qhp file. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHG_LOCATION = - -# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be -# generated, together with the HTML files, they form an Eclipse help plugin. To -# install this plugin and make it available under the help contents menu in -# Eclipse, the contents of the directory containing the HTML and XML files needs -# to be copied into the plugins directory of eclipse. The name of the directory -# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. -# After copying Eclipse needs to be restarted before the help appears. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_ECLIPSEHELP = NO - -# A unique identifier for the Eclipse help plugin. When installing the plugin -# the directory name containing the HTML and XML files should also have this -# name. Each documentation set should have its own identifier. -# The default value is: org.doxygen.Project. -# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. - -ECLIPSE_DOC_ID = org.doxygen.Project - -# If you want full control over the layout of the generated HTML pages it might -# be necessary to disable the index and replace it with your own. The -# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top -# of each HTML page. A value of NO enables the index and the value YES disables -# it. Since the tabs in the index contain the same information as the navigation -# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -DISABLE_INDEX = NO - -# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index -# structure should be generated to display hierarchical information. If the tag -# value is set to YES, a side panel will be generated containing a tree-like -# index structure (just like the one that is generated for HTML Help). For this -# to work a browser that supports JavaScript, DHTML, CSS and frames is required -# (i.e. any modern browser). Windows users are probably better off using the -# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can -# further fine tune the look of the index (see "Fine-tuning the output"). As an -# example, the default style sheet generated by doxygen has an example that -# shows how to put an image at the root of the tree instead of the PROJECT_NAME. -# Since the tree basically has the same information as the tab index, you could -# consider setting DISABLE_INDEX to YES when enabling this option. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_TREEVIEW = NO - -# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the -# FULL_SIDEBAR option determines if the side bar is limited to only the treeview -# area (value NO) or if it should extend to the full height of the window (value -# YES). Setting this to YES gives a layout similar to -# https://docs.readthedocs.io with more room for contents, but less room for the -# project logo, title, and description. If either GENERATE_TREEVIEW or -# DISABLE_INDEX is set to NO, this option has no effect. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -FULL_SIDEBAR = NO - -# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that -# doxygen will group on one line in the generated HTML documentation. -# -# Note that a value of 0 will completely suppress the enum values from appearing -# in the overview section. -# Minimum value: 0, maximum value: 20, default value: 4. -# This tag requires that the tag GENERATE_HTML is set to YES. - -ENUM_VALUES_PER_LINE = 4 - -# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used -# to set the initial width (in pixels) of the frame in which the tree is shown. -# Minimum value: 0, maximum value: 1500, default value: 250. -# This tag requires that the tag GENERATE_HTML is set to YES. - -TREEVIEW_WIDTH = 250 - -# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to -# external symbols imported via tag files in a separate window. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -EXT_LINKS_IN_WINDOW = NO - -# If the OBFUSCATE_EMAILS tag is set to YES, doxygen will obfuscate email -# addresses. -# The default value is: YES. -# This tag requires that the tag GENERATE_HTML is set to YES. - -OBFUSCATE_EMAILS = YES - -# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg -# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see -# https://inkscape.org) to generate formulas as SVG images instead of PNGs for -# the HTML output. These images will generally look nicer at scaled resolutions. -# Possible values are: png (the default) and svg (looks nicer but requires the -# pdf2svg or inkscape tool). -# The default value is: png. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_FORMULA_FORMAT = png - -# Use this tag to change the font size of LaTeX formulas included as images in -# the HTML documentation. When you change the font size after a successful -# doxygen run you need to manually remove any form_*.png images from the HTML -# output directory to force them to be regenerated. -# Minimum value: 8, maximum value: 50, default value: 10. -# This tag requires that the tag GENERATE_HTML is set to YES. - -FORMULA_FONTSIZE = 10 - -# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands -# to create new LaTeX commands to be used in formulas as building blocks. See -# the section "Including formulas" for details. - -FORMULA_MACROFILE = - -# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see -# https://www.mathjax.org) which uses client side JavaScript for the rendering -# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX -# installed or if you want to formulas look prettier in the HTML output. When -# enabled you may also need to install MathJax separately and configure the path -# to it using the MATHJAX_RELPATH option. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -USE_MATHJAX = NO - -# With MATHJAX_VERSION it is possible to specify the MathJax version to be used. -# Note that the different versions of MathJax have different requirements with -# regards to the different settings, so it is possible that also other MathJax -# settings have to be changed when switching between the different MathJax -# versions. -# Possible values are: MathJax_2 and MathJax_3. -# The default value is: MathJax_2. -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_VERSION = MathJax_2 - -# When MathJax is enabled you can set the default output format to be used for -# the MathJax output. For more details about the output format see MathJax -# version 2 (see: -# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3 -# (see: -# http://docs.mathjax.org/en/latest/web/components/output.html). -# Possible values are: HTML-CSS (which is slower, but has the best -# compatibility. This is the name for Mathjax version 2, for MathJax version 3 -# this will be translated into chtml), NativeMML (i.e. MathML. Only supported -# for NathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This -# is the name for Mathjax version 3, for MathJax version 2 this will be -# translated into HTML-CSS) and SVG. -# The default value is: HTML-CSS. -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_FORMAT = HTML-CSS - -# When MathJax is enabled you need to specify the location relative to the HTML -# output directory using the MATHJAX_RELPATH option. The destination directory -# should contain the MathJax.js script. For instance, if the mathjax directory -# is located at the same level as the HTML output directory, then -# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax -# Content Delivery Network so you can quickly see the result without installing -# MathJax. However, it is strongly recommended to install a local copy of -# MathJax from https://www.mathjax.org before deployment. The default value is: -# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2 -# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3 -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_RELPATH = https://cdn.jsdelivr.net/npm/mathjax@2 - -# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax -# extension names that should be enabled during MathJax rendering. For example -# for MathJax version 2 (see -# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions): -# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols -# For example for MathJax version 3 (see -# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html): -# MATHJAX_EXTENSIONS = ams -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_EXTENSIONS = - -# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces -# of code that will be used on startup of the MathJax code. See the MathJax site -# (see: -# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an -# example see the documentation. -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_CODEFILE = - -# When the SEARCHENGINE tag is enabled doxygen will generate a search box for -# the HTML output. The underlying search engine uses javascript and DHTML and -# should work on any modern browser. Note that when using HTML help -# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) -# there is already a search function so this one should typically be disabled. -# For large projects the javascript based search engine can be slow, then -# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to -# search using the keyboard; to jump to the search box use + S -# (what the is depends on the OS and browser, but it is typically -# , /