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 std::string log_file_path = arg.substr(11);
88 if (isValidLogFileExtension(log_file_path))
90 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);
106 std::expected<void, std::string> handleNumThreads(
const std::string& arg,
core::Config& config)
noexcept
110 const int requested_threads = std::stoi(arg.substr(3));
111 if (requested_threads <= 0)
113 return std::unexpected(
"Number of threads must be greater than 0");
116 config.num_threads =
static_cast<unsigned>(requested_threads);
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.rfind(
"--out-dir=", 0) == 0)
167 config.output_dir = arg.substr(10);
170 if (arg ==
"--no-validate")
172 config.validate =
false;
177 config.generate_kml =
true;
180 if (arg.rfind(
"--kml=", 0) == 0)
182 config.generate_kml =
true;
183 config.kml_file = arg.substr(6);
186 if (arg[0] !=
'-' && !scriptFileSet)
188 config.script_file = arg;
189 scriptFileSet =
true;
193 std::cerr <<
"[ERROR] Unrecognized option: '" << arg <<
"'\n";
194 return std::unexpected(
"Unrecognized argument: " + arg);
202 std::cout << R
"(/------------------------------------------------\
203| FERS - The Flexible Extensible Radar Simulator |
205\------------------------------------------------/
206Usage: )" << programName
207 << R"( <scriptfile> [options]
210 --help, -h Show this help message and exit
211 --version, -v Show version information and exit
212 --no-validate Disable XML schema validation before running.
213 --kml[=<file>] Generate a KML visualization of the scenario and exit. If a filename
214 is provided, it will be used. Otherwise, it defaults to the scenario
215 name with a .kml extension in the output directory.
216 --out-dir=<dir> Set the output directory for simulation results and default KML output.
217 Defaults to the directory containing the script file.
218 --log-level=<level> Set the logging level (TRACE, DEBUG, INFO, WARNING, ERROR, FATAL)
219 --log-file=<file> Log output to the specified .log or .txt file as well as the console.
220 -n=<threads> Number of threads to use
223 <scriptfile> Path to the simulation script file (XML)
227 << R"( simulation.fersxml --out-dir=./results --log-level=DEBUG -n=4
229This program runs radar simulations based on an XML script file.
230Make sure the script file follows the correct format to avoid errors.
237/------------------------------------------------\
238| FERS - The Flexible Extensible Radar Simulator |
240| Author: Marc Brooker |
241\------------------------------------------------/
245 std::expected<Config, std::string>
parseArguments(
const int argc,
char* argv[])
noexcept
248 bool script_file_set =
false;
253 return std::unexpected(
"No arguments provided.");
256 for (
int i = 1; i < argc; ++i)
258 if (
const auto result = handleArgument(argv[i], config, script_file_set, argv[0]); !result)
260 return std::unexpected(result.error());
264 if (!script_file_set)
266 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.