18#include <unordered_map>
30 bool isValidLogFileExtension(
const std::string& filePath)
noexcept
32 static const std::vector<std::string> VALID_EXTENSIONS = {
".log",
".txt"};
33 std::string extension = std::filesystem::path(filePath).extension().string();
34 std::ranges::transform(extension, extension.begin(), tolower);
35 return std::ranges::find(VALID_EXTENSIONS, extension) != VALID_EXTENSIONS.end();
44 std::optional<fers_log_level_t> parseLogLevel(
const std::string& level)
noexcept
46 static const std::unordered_map<std::string, fers_log_level_t> LEVEL_MAP = {
50 if (
const auto it = LEVEL_MAP.find(level); it != LEVEL_MAP.end())
64 std::expected<void, std::string> handleLogLevel(
const std::string& arg,
core::Config& config)
noexcept
66 const std::string level_str = arg.substr(12);
67 if (
const auto level = parseLogLevel(level_str))
69 config.log_level = *level;
73 std::cerr <<
"[ERROR] Invalid log level '" << level_str <<
"'\n";
74 return std::unexpected(
"Invalid log level: " + level_str);
85 std::expected<void, std::string> handleLogFile(
const std::string& arg,
core::Config& config)
noexcept
87 if (std::string log_file_path = arg.substr(11); isValidLogFileExtension(log_file_path))
89 config.log_file = log_file_path;
94 std::cerr <<
"[ERROR] Invalid log file extension. Must be .log or .txt\n";
95 return std::unexpected(
"Invalid log file extension: " + log_file_path);
107 std::expected<void, std::string> handleNumThreads(
const std::string& arg,
core::Config& config)
noexcept
111 config.num_threads = std::stoi(arg.substr(3));
112 if (config.num_threads == 0)
114 return std::unexpected(
"Number of threads must be greater than 0");
117 if (
const unsigned max_threads = std::thread::hardware_concurrency();
118 max_threads > 0 && config.num_threads > max_threads)
120 std::cerr <<
"[WARNING] Thread count exceeds available processors. Clamping.\n";
121 config.num_threads = max_threads;
125 catch (
const std::exception&)
127 return std::unexpected(
"Invalid number of threads specified.");
140 std::expected<void, std::string> handleArgument(
const std::string& arg,
core::Config& config,
bool& scriptFileSet,
141 const char* programName)
noexcept
143 if (arg ==
"--help" || arg ==
"-h")
146 return std::unexpected(
"Help requested.");
148 if (arg ==
"--version" || arg ==
"-v")
151 return std::unexpected(
"Version requested.");
153 if (arg.rfind(
"--log-level=", 0) == 0)
155 return handleLogLevel(arg, config);
157 if (arg.rfind(
"--log-file=", 0) == 0)
159 return handleLogFile(arg, config);
161 if (arg.rfind(
"-n=", 0) == 0)
163 return handleNumThreads(arg, config);
165 if (arg ==
"--no-validate")
167 config.validate =
false;
172 config.generate_kml =
true;
175 if (arg[0] !=
'-' && !scriptFileSet)
177 config.script_file = arg;
178 scriptFileSet =
true;
182 std::cerr <<
"[ERROR] Unrecognized option: '" << arg <<
"'\n";
183 return std::unexpected(
"Unrecognized argument: " + arg);
191 std::cout << R
"(/------------------------------------------------\
192| FERS - The Flexible Extensible Radar Simulator |
194\------------------------------------------------/
195Usage: )" << programName
196 << R"( <scriptfile> [options]
199 --help, -h Show this help message and exit
200 --version, -v Show version information and exit
201 --no-validate Disable XML schema validation before running.
202 --kml Generate a KML visualization of the scenario and exit. The output file
203 will have the same name as the input file with a .kml extension.
204 --log-level=<level> Set the logging level (TRACE, DEBUG, INFO, WARNING, ERROR, FATAL)
205 --log-file=<file> Log output to the specified .log or .txt file as well as the console.
206 -n=<threads> Number of threads to use
209 <scriptfile> Path to the simulation script file (XML)
213 << R"( simulation.fersxml --log-level=DEBUG --log-file=output.log -n=4
215This program runs radar simulations based on an XML script file.
216Make sure the script file follows the correct format to avoid errors.
223/------------------------------------------------\
224| FERS - The Flexible Extensible Radar Simulator |
226| Author: Marc Brooker |
227\------------------------------------------------/
231 std::expected<Config, std::string> parseArguments(const int argc,
char* argv[])
noexcept
234 bool script_file_set =
false;
239 return std::unexpected(
"No arguments provided.");
242 for (
int i = 1; i < argc; ++i)
244 if (
const auto result = handleArgument(argv[i], config, script_file_set, argv[0]); !result)
246 return std::unexpected(result.error());
250 if (!script_file_set)
252 return std::unexpected(
"No script file provided.");
Command-line argument parsing utilities for the application.
void showVersion() noexcept
Displays the version information.
std::expected< Config, std::string > parseArguments(const int argc, char *argv[]) noexcept
Parses command-line arguments.
void showHelp(const char *programName) noexcept
Displays the help message.
Configuration structure for the application.