File indexing completed on 2024-09-27 07:03:09
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 if (options.flags[ShowConfigs]) {
0381
0382 app->Initialize();
0383 if (options.flags[Benchmark]) {
0384 JBenchmarker benchmarker(
0385 app);
0386 }
0387
0388 PrintConfigParameters(app);
0389 } else if (options.flags[DumpConfigs]) {
0390
0391 app->Initialize();
0392 std::cout << std::endl
0393 << "Writing configuration options to file: " << options.dump_config_file << std::endl;
0394 app->GetJParameterManager()->WriteConfigFile(options.dump_config_file);
0395 } else if (options.flags[Benchmark]) {
0396 JSignalHandler::register_handlers(app);
0397
0398 JBenchmarker benchmarker(app);
0399 benchmarker.RunUntilFinished();
0400 } else if (options.flags[ListFactories]) {
0401 app->Initialize();
0402 PrintFactories(app);
0403
0404
0405 PrintPodioCollections(app);
0406 } else {
0407 if ((JVersion::GetMajorNumber() == 2) && (JVersion::GetMinorNumber() == 3) && (JVersion::GetPatchNumber() <= 1)) {
0408
0409 if (not app->GetJParameterManager()->Exists("jana:parameter_verbosity")) {
0410 app->GetJParameterManager()->SetParameter("jana:parameter_verbosity", 0);
0411 }
0412 }
0413
0414 try {
0415 JSignalHandler::register_handlers(app);
0416 app->Run();
0417 } catch (JException& e) {
0418 std::cout << "----------------------------------------------------------" << std::endl;
0419 std::cout << e << std::endl;
0420 app->SetExitCode(EXIT_FAILURE);
0421 } catch (std::runtime_error& e) {
0422 std::cout << "----------------------------------------------------------" << std::endl;
0423 std::cout << "Exception: " << e.what() << std::endl;
0424 app->SetExitCode(EXIT_FAILURE);
0425 }
0426 }
0427 return (int)app->GetExitCode();
0428 }
0429
0430 UserOptions GetCliOptions(int nargs, char* argv[], bool expect_extra) {
0431
0432 UserOptions options;
0433
0434 std::map<std::string, Flag> tokenizer;
0435 tokenizer["-h"] = ShowUsage;
0436 tokenizer["--help"] = ShowUsage;
0437 tokenizer["-v"] = ShowVersion;
0438 tokenizer["--version"] = ShowVersion;
0439 tokenizer["-j"] = ShowJANAVersion;
0440 tokenizer["--janaversion"] = ShowJANAVersion;
0441 tokenizer["-c"] = ShowConfigs;
0442 tokenizer["--configs"] = ShowConfigs;
0443 tokenizer["-l"] = LoadConfigs;
0444 tokenizer["--loadconfigs"] = LoadConfigs;
0445 tokenizer["-d"] = DumpConfigs;
0446 tokenizer["--dumpconfigs"] = DumpConfigs;
0447 tokenizer["-b"] = Benchmark;
0448 tokenizer["--benchmark"] = Benchmark;
0449 tokenizer["-L"] = ListFactories;
0450 tokenizer["--list-factories"] = ListFactories;
0451 tokenizer["--list-default-plugins"] = ShowDefaultPlugins;
0452 tokenizer["--list-available-plugins"] = ShowAvailablePlugins;
0453
0454
0455 if (nargs == 1) {
0456 options.flags[ShowUsage] = true;
0457 }
0458
0459 for (int i = 1; i < nargs; i++) {
0460
0461 std::string arg = argv[i];
0462
0463
0464 if (argv[i][0] != '-') {
0465 options.eventSources.push_back(arg);
0466 continue;
0467 }
0468
0469 switch (tokenizer[arg]) {
0470
0471 case Benchmark:
0472 options.flags[Benchmark] = true;
0473 break;
0474
0475 case ShowUsage:
0476 options.flags[ShowUsage] = true;
0477 break;
0478
0479 case ShowVersion:
0480 options.flags[ShowVersion] = true;
0481 break;
0482
0483 case ShowJANAVersion:
0484 options.flags[ShowJANAVersion] = true;
0485 break;
0486
0487 case ShowConfigs:
0488 options.flags[ShowConfigs] = true;
0489 break;
0490
0491 case LoadConfigs:
0492 options.flags[LoadConfigs] = true;
0493 if (i + 1 < nargs && argv[i + 1][0] != '-') {
0494 options.load_config_file = argv[i + 1];
0495 i += 1;
0496 } else {
0497 options.load_config_file = "jana.config";
0498 }
0499 break;
0500
0501 case DumpConfigs:
0502 options.flags[DumpConfigs] = true;
0503 if (i + 1 < nargs && argv[i + 1][0] != '-') {
0504 options.dump_config_file = argv[i + 1];
0505 i += 1;
0506 } else {
0507 options.dump_config_file = "jana.config";
0508 }
0509 break;
0510
0511 case ListFactories:
0512 options.flags[ListFactories] = true;
0513 break;
0514
0515 case ShowDefaultPlugins:
0516 options.flags[ShowDefaultPlugins] = true;
0517 break;
0518
0519 case ShowAvailablePlugins:
0520 options.flags[ShowAvailablePlugins] = true;
0521 break;
0522
0523
0524 case Unknown:
0525 if (argv[i][0] == '-' && argv[i][1] == 'P') {
0526
0527 size_t pos = arg.find("=");
0528 if ((pos != std::string::npos) && (pos > 2)) {
0529 std::string key = arg.substr(2, pos - 2);
0530 std::string val = arg.substr(pos + 1);
0531 if (options.params.find(key) != options.params.end()) {
0532 std::cout << "Duplicate parameter '" << arg << "' ignored" << std::endl;
0533 } else {
0534 options.params.insert({key, val});
0535 }
0536 } else {
0537 std::cout << "Invalid JANA parameter '" << arg << "': Expected format -Pkey=value"
0538 << std::endl;
0539 options.flags[ShowConfigs] = true;
0540 }
0541 } else {
0542 if (!expect_extra) {
0543 std::cout << "Invalid command line flag '" << arg << "'" << std::endl;
0544 options.flags[ShowUsage] = true;
0545 }
0546 }
0547 }
0548 }
0549 return options;
0550 }
0551 }