File indexing completed on 2024-06-01 07:06:55
0001
0002
0003
0004
0005 #include "eicrecon_cli.h"
0006
0007 #include <JANA/CLI/JBenchmarker.h>
0008 #include <JANA/CLI/JSignalHandler.h>
0009 #include <JANA/CLI/JVersion.h>
0010 #include <JANA/Services/JComponentManager.h>
0011 #include <algorithm>
0012 #include <cstdlib>
0013 #include <filesystem>
0014 #include <iostream>
0015 #include <memory>
0016 #include <set>
0017 #include <stdexcept>
0018 #include <string.h>
0019 #include <string>
0020 #include <utility>
0021
0022 #include "JANA/JApplication.h"
0023 #include "JANA/JEventSource.h"
0024 #include "JANA/JException.h"
0025 #include "JANA/Services/JParameterManager.h"
0026 #include "print_info.h"
0027
0028 #define QUOTE(name) #name
0029 #define STR(macro) QUOTE(macro)
0030
0031 #ifndef EICRECON_APP_VERSION
0032 #define EICRECON_APP_VERSION Error
0033 #endif
0034
0035 #define EICRECON_APP_VERSION_STR STR(EICRECON_APP_VERSION)
0036
0037 namespace jana {
0038
0039 void PrintUsageOptions() {
0040 std::cout << "Options:" << std::endl;
0041 std::cout << " -h --help Display this message" << std::endl;
0042 std::cout << " -v --version Display version information" << std::endl;
0043 std::cout << " -j --janaversion Display JANA version information" << std::endl;
0044 std::cout << " -c --configs Display configuration parameters" << std::endl;
0045 std::cout << " -l --loadconfigs <file> Load configuration parameters from file"
0046 << std::endl;
0047 std::cout << " -d --dumpconfigs <file> Dump configuration parameters to file" << std::endl;
0048 std::cout << " -b --benchmark Run in benchmark mode" << std::endl;
0049 std::cout << " -L --list-factories List all the factories without running"
0050 << std::endl;
0051 std::cout << " -Pkey=value Specify a configuration parameter" << std::endl;
0052 std::cout << " -Pplugin:param=value Specify a parameter value for a plugin"
0053 << std::endl;
0054 std::cout << std::endl;
0055
0056 std::cout << " --list-default-plugins List all the default plugins" << std::endl;
0057 std::cout << " --list-available-plugins List plugins at $JANA_PLUGIN_PATH and $EICrecon_MY"
0058 << std::endl;
0059 std::cout << std::endl;
0060
0061 std::cout << " -Pplugins=values Comma-separated list of extra plugins to load"
0062 << std::endl;
0063 std::cout << " -Pplugins_to_ignore=values Comma-separated list of plugins to ignore"
0064 << std::endl;
0065
0066 std::cout << std::endl << std::endl;
0067 }
0068
0069 void PrintUsageExample() {
0070
0071 std::cout << "Example:" << std::endl;
0072 std::cout << " eicrecon -Pplugins=plugin1,plugin2,plugin3 -Pnthreads=8 infile.root"
0073 << std::endl;
0074 std::cout << " eicrecon -Ppodio:print_type_table=1 infile.root" << std::endl << std::endl;
0075 std::cout << std::endl << std::endl;
0076 }
0077
0078 void PrintUsage() {
0079
0080
0081
0082
0083 std::cout << std::endl;
0084 std::cout << "Usage:" << std::endl;
0085 std::cout << " eicrecon [options] source1 source2 ..." << std::endl;
0086 std::cout << std::endl;
0087
0088 std::cout << "Description:" << std::endl;
0089 std::cout << " Command-line interface for running JANA plugins. This can be used to"
0090 << std::endl;
0091 std::cout << " read in events and process them. Command-line flags control configuration"
0092 << std::endl;
0093 std::cout << " while additional arguments denote input files, which are to be loaded and"
0094 << std::endl;
0095 std::cout << " processed by the appropriate EventSource plugin." << std::endl;
0096 std::cout << std::endl;
0097
0098 PrintUsageOptions();
0099 PrintUsageExample();
0100 }
0101
0102 void PrintVersion() { std::cout << "EICrecon version: " << EICRECON_APP_VERSION_STR << std::endl; }
0103
0104 void PrintJANAVersion() { std::cout << "JANA version: " << JVersion::GetVersion() << std::endl; }
0105
0106 void PrintDefaultPlugins(std::vector<std::string> const& default_plugins) {
0107 std::cout << "\n List default plugins:\n\n";
0108 printPluginNames(default_plugins);
0109 std::cout << std::endl << std::endl;
0110 }
0111
0112 void GetPluginNamesInDir(std::set<std::string>& plugin_names, std::string dir_str) {
0113
0114 if (std::filesystem::is_directory(dir_str) == false)
0115 return;
0116 if (std::filesystem::is_empty(dir_str))
0117 return;
0118
0119 std::string full_path, filename;
0120 for (const auto& entry : std::filesystem::directory_iterator(dir_str)) {
0121 full_path = std::string(entry.path());
0122 filename = full_path.substr(full_path.find_last_of("/") + 1);
0123 if (filename.substr(filename.size() - 3) == ".so") {
0124 std::string s = filename.substr(0, filename.size() - 3);
0125
0126 plugin_names.insert(s);
0127 }
0128 }
0129 }
0130
0131
0132
0133
0134 void GetPluginNamesFromEnvPath(std::set<std::string>& plugin_names, const char* env_var) {
0135 std::string dir_path, paths;
0136
0137 const char* env_p = getenv(env_var);
0138 if (env_p) {
0139 if (strcmp(env_var, "EICrecon_MY") == 0) {
0140 paths = std::string(env_p) + "/plugins";
0141 } else {
0142 paths = std::string(env_p);
0143 }
0144
0145 std::stringstream envvar_ss(paths);
0146 while (getline(envvar_ss, dir_path, ':')) {
0147 GetPluginNamesInDir(plugin_names, dir_path);
0148 }
0149 }
0150 }
0151
0152 std::vector<std::string> GetAvailablePluginNames(std::vector<std::string> const& default_plugins) {
0153
0154
0155 std::set<std::string> set_plugin_name;
0156 for (std::string s : default_plugins)
0157 set_plugin_name.insert(s);
0158
0159 jana::GetPluginNamesFromEnvPath(set_plugin_name, "JANA_PLUGIN_PATH");
0160 jana::GetPluginNamesFromEnvPath(set_plugin_name, "EICrecon_MY");
0161
0162 std::vector<std::string> plugin_names(set_plugin_name.begin(), set_plugin_name.end());
0163 return plugin_names;
0164 }
0165
0166 void PrintAvailablePlugins(std::vector<std::string> const& default_plugins) {
0167 std::cout << "\n List available plugins:\n\n";
0168 printPluginNames(GetAvailablePluginNames(default_plugins));
0169 std::cout << std::endl << std::endl;
0170 }
0171
0172 bool HasPrintOnlyCliOptions(UserOptions& options, std::vector<std::string> const& default_plugins) {
0173 if (options.flags[jana::ShowUsage]) {
0174 jana::PrintUsage();
0175 return true;
0176 }
0177 if (options.flags[jana::ShowVersion]) {
0178 jana::PrintVersion();
0179 return true;
0180 }
0181 if (options.flags[jana::ShowJANAVersion]) {
0182 jana::PrintJANAVersion();
0183 return true;
0184 }
0185 if (options.flags[jana::ShowDefaultPlugins]) {
0186 jana::PrintDefaultPlugins(default_plugins);
0187 return true;
0188 }
0189 if (options.flags[jana::ShowAvailablePlugins]) {
0190 jana::PrintAvailablePlugins(default_plugins);
0191 return true;
0192 }
0193 return false;
0194 }
0195
0196
0197
0198
0199 bool HasExcludeDefaultPluginsInCliParams(UserOptions& options, const std::string erase_str) {
0200 auto has_ignore_plugins = options.params.find("plugins_to_ignore");
0201 if (has_ignore_plugins == options.params.end())
0202 return false;
0203
0204
0205 size_t pos = has_ignore_plugins->second.find(erase_str);
0206 if (pos == std::string::npos)
0207 return false;
0208
0209
0210 std::string ignore_str;
0211 if (erase_str.length() + pos ==
0212 has_ignore_plugins->second.length()) {
0213 ignore_str = has_ignore_plugins->second.erase(pos, erase_str.length());
0214 } else {
0215 ignore_str = has_ignore_plugins->second.erase(pos, erase_str.length() + 1);
0216 }
0217 options.params["plugins_to_ignore"] = ignore_str;
0218 return true;
0219 }
0220
0221 void AddAvailablePluginsToOptionParams(UserOptions& options,
0222 std::vector<std::string> const& default_plugins) {
0223
0224 std::set<std::string> set_plugins;
0225
0226
0227
0228
0229 std::string plugins_str;
0230 for (std::string s : set_plugins)
0231 plugins_str += s + ",";
0232
0233
0234
0235 if (HasExcludeDefaultPluginsInCliParams(options, "default") == false)
0236
0237
0238 for (std::string s : default_plugins) {
0239 plugins_str += s + ",";
0240 }
0241
0242
0243 auto has_cli_plugin_params = options.params.find("plugins");
0244 if (has_cli_plugin_params != options.params.end()) {
0245 plugins_str += has_cli_plugin_params->second;
0246 options.params["plugins"] = plugins_str;
0247 } else {
0248 options.params["plugins"] = plugins_str.substr(0, plugins_str.size() - 1);
0249 }
0250 }
0251
0252 JApplication* CreateJApplication(UserOptions& options) {
0253
0254 auto* para_mgr =
0255 new JParameterManager();
0256
0257
0258 for (auto pair : options.params) {
0259 para_mgr->SetParameter(pair.first, pair.second);
0260 }
0261
0262
0263 if (options.flags[ListFactories]) {
0264 para_mgr->SetParameter("log:off", "JPluginLoader,JArrowProcessingController,JArrow");
0265 }
0266
0267 if (options.flags[LoadConfigs]) {
0268
0269 try {
0270 para_mgr->ReadConfigFile(options.load_config_file);
0271 } catch (JException& e) {
0272 std::cout << "Problem loading config file '" << options.load_config_file << "'. Exiting."
0273 << std::endl
0274 << std::endl;
0275 exit(-1);
0276 }
0277 std::cout << "Loaded config file '" << options.load_config_file << "'." << std::endl
0278 << std::endl;
0279 }
0280
0281
0282
0283 if (para_mgr->FindParameter("jana:timeout") == nullptr) {
0284 para_mgr->SetParameter("jana:timeout", 180);
0285 para_mgr->SetParameter("jana:warmup_timeout", 180);
0286 }
0287
0288 auto* app = new JApplication(para_mgr);
0289
0290 const char* env_p = getenv("EICrecon_MY");
0291 if (env_p) {
0292 app->AddPluginPath(std::string(env_p) + "/plugins");
0293 }
0294
0295 for (auto event_src : options.eventSources) {
0296 app->Add(event_src);
0297 }
0298 return app;
0299 }
0300
0301 void AddDefaultPluginsToJApplication(JApplication* app,
0302 std::vector<std::string> const& default_plugins) {
0303 for (std::string s : default_plugins)
0304 app->AddPlugin(s);
0305 }
0306
0307 void PrintFactories(JApplication* app) {
0308 std::cout << std::endl << "List all the factories:" << std::endl << std::endl;
0309 printFactoryTable(app->GetComponentSummary());
0310 std::cout << std::endl;
0311 }
0312
0313 void PrintPodioCollections(JApplication* app) {
0314 if (app->GetJParameterManager()->Exists("PODIO:PRINT_TYPE_TABLE")) {
0315 bool print_type_table = app->GetParameterValue<bool>("podio:print_type_table");
0316
0317
0318 if (print_type_table) {
0319 auto event_sources = app->GetService<JComponentManager>()->get_evt_srces();
0320 for (auto* event_source : event_sources) {
0321
0322
0323 if (event_source->GetPluginName().find("podio") != std::string::npos)
0324 event_source->DoInitialize();
0325 }
0326 }
0327 }
0328 }
0329
0330 void PrintConfigParameters(JApplication* app) {
0331
0332
0333
0334
0335
0336
0337
0338 auto params = app->GetJParameterManager()->GetAllParameters();
0339 size_t max_key_length = 0;
0340 size_t max_val_length = 0;
0341 size_t max_max_val_length = 32;
0342 for (auto& [key, p] : params) {
0343 if (key.length() > max_key_length)
0344 max_key_length = key.length();
0345 if (p->GetValue().length() > max_val_length) {
0346 if (p->GetValue().length() <= max_max_val_length)
0347 max_val_length = p->GetValue().length();
0348 }
0349 }
0350
0351 std::cout << "\nConfiguration Parameters:" << std::endl;
0352 std::cout << "Name" + std::string(std::max(max_key_length, size_t(4)) - 4, ' ') << " : ";
0353 std::cout << "Value" + std::string(std::max(max_val_length, size_t(5)) - 5, ' ') << " : ";
0354 std::cout << "Description" << std::endl;
0355 std::cout << std::string(max_key_length + max_val_length + 20, '-') << std::endl;
0356 for (auto& [key, p] : params) {
0357 std::stringstream ss;
0358 int key_length_diff = max_key_length - key.length();
0359 if (key_length_diff > 0)
0360 ss << std::string(key_length_diff, ' ');
0361 ss << key;
0362 ss << " | ";
0363
0364 int val_length_diff = max_val_length - p->GetValue().length();
0365 if (val_length_diff > 0)
0366 ss << std::string(val_length_diff, ' ');
0367 ss << p->GetValue();
0368 ss << " | ";
0369 ss << p->GetDescription();
0370
0371 std::cout << ss.str() << std::endl;
0372 }
0373 std::cout << std::string(max_key_length + max_val_length + 20, '-') << std::endl;
0374 }
0375
0376 int Execute(JApplication* app, UserOptions& options) {
0377
0378 std::cout << std::endl;
0379
0380
0381
0382
0383 if (options.flags[ShowConfigs]) {
0384
0385 app->Initialize();
0386 if (options.flags[Benchmark]) {
0387 JBenchmarker benchmarker(
0388 app);
0389 }
0390
0391 PrintConfigParameters(app);
0392 } else if (options.flags[DumpConfigs]) {
0393
0394 app->Initialize();
0395 std::cout << std::endl
0396 << "Writing configuration options to file: " << options.dump_config_file << std::endl;
0397 app->GetJParameterManager()->WriteConfigFile(options.dump_config_file);
0398 } else if (options.flags[Benchmark]) {
0399 JSignalHandler::register_handlers(app);
0400
0401 JBenchmarker benchmarker(app);
0402 benchmarker.RunUntilFinished();
0403 } else if (options.flags[ListFactories]) {
0404 app->Initialize();
0405 PrintFactories(app);
0406
0407
0408 PrintPodioCollections(app);
0409 } else {
0410
0411 try {
0412 printJANAHeaderIMG();
0413 JSignalHandler::register_handlers(app);
0414 app->Run();
0415 } catch (JException& e) {
0416 std::cout << "----------------------------------------------------------" << std::endl;
0417 std::cout << e << std::endl;
0418 app->SetExitCode(EXIT_FAILURE);
0419 } catch (std::runtime_error& e) {
0420 std::cout << "----------------------------------------------------------" << std::endl;
0421 std::cout << "Exception: " << e.what() << std::endl;
0422 app->SetExitCode(EXIT_FAILURE);
0423 }
0424 }
0425 return (int)app->GetExitCode();
0426 }
0427
0428 UserOptions GetCliOptions(int nargs, char* argv[], bool expect_extra) {
0429
0430 UserOptions options;
0431
0432 std::map<std::string, Flag> tokenizer;
0433 tokenizer["-h"] = ShowUsage;
0434 tokenizer["--help"] = ShowUsage;
0435 tokenizer["-v"] = ShowVersion;
0436 tokenizer["--version"] = ShowVersion;
0437 tokenizer["-j"] = ShowJANAVersion;
0438 tokenizer["--janaversion"] = ShowJANAVersion;
0439 tokenizer["-c"] = ShowConfigs;
0440 tokenizer["--configs"] = ShowConfigs;
0441 tokenizer["-l"] = LoadConfigs;
0442 tokenizer["--loadconfigs"] = LoadConfigs;
0443 tokenizer["-d"] = DumpConfigs;
0444 tokenizer["--dumpconfigs"] = DumpConfigs;
0445 tokenizer["-b"] = Benchmark;
0446 tokenizer["--benchmark"] = Benchmark;
0447 tokenizer["-L"] = ListFactories;
0448 tokenizer["--list-factories"] = ListFactories;
0449 tokenizer["--list-default-plugins"] = ShowDefaultPlugins;
0450 tokenizer["--list-available-plugins"] = ShowAvailablePlugins;
0451
0452
0453 if (nargs == 1) {
0454 options.flags[ShowUsage] = true;
0455 }
0456
0457 for (int i = 1; i < nargs; i++) {
0458
0459 std::string arg = argv[i];
0460
0461
0462 if (argv[i][0] != '-') {
0463 options.eventSources.push_back(arg);
0464 continue;
0465 }
0466
0467 switch (tokenizer[arg]) {
0468
0469 case Benchmark:
0470 options.flags[Benchmark] = true;
0471 break;
0472
0473 case ShowUsage:
0474 options.flags[ShowUsage] = true;
0475 break;
0476
0477 case ShowVersion:
0478 options.flags[ShowVersion] = true;
0479 break;
0480
0481 case ShowJANAVersion:
0482 options.flags[ShowJANAVersion] = true;
0483 break;
0484
0485 case ShowConfigs:
0486 options.flags[ShowConfigs] = true;
0487 break;
0488
0489 case LoadConfigs:
0490 options.flags[LoadConfigs] = true;
0491 if (i + 1 < nargs && argv[i + 1][0] != '-') {
0492 options.load_config_file = argv[i + 1];
0493 i += 1;
0494 } else {
0495 options.load_config_file = "jana.config";
0496 }
0497 break;
0498
0499 case DumpConfigs:
0500 options.flags[DumpConfigs] = true;
0501 if (i + 1 < nargs && argv[i + 1][0] != '-') {
0502 options.dump_config_file = argv[i + 1];
0503 i += 1;
0504 } else {
0505 options.dump_config_file = "jana.config";
0506 }
0507 break;
0508
0509 case ListFactories:
0510 options.flags[ListFactories] = true;
0511 break;
0512
0513 case ShowDefaultPlugins:
0514 options.flags[ShowDefaultPlugins] = true;
0515 break;
0516
0517 case ShowAvailablePlugins:
0518 options.flags[ShowAvailablePlugins] = true;
0519 break;
0520
0521
0522 case Unknown:
0523 if (argv[i][0] == '-' && argv[i][1] == 'P') {
0524
0525 size_t pos = arg.find("=");
0526 if ((pos != std::string::npos) && (pos > 2)) {
0527 std::string key = arg.substr(2, pos - 2);
0528 std::string val = arg.substr(pos + 1);
0529 if (options.params.find(key) != options.params.end()) {
0530 std::cout << "Duplicate parameter '" << arg << "' ignored" << std::endl;
0531 } else {
0532 options.params.insert({key, val});
0533 }
0534 } else {
0535 std::cout << "Invalid JANA parameter '" << arg << "': Expected format -Pkey=value"
0536 << std::endl;
0537 options.flags[ShowConfigs] = true;
0538 }
0539 } else {
0540 if (!expect_extra) {
0541 std::cout << "Invalid command line flag '" << arg << "'" << std::endl;
0542 options.flags[ShowUsage] = true;
0543 }
0544 }
0545 }
0546 }
0547 return options;
0548 }
0549 }