Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2024-06-01 07:06:55

0001 //
0002 // Created by xmei on 9/7/22.
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   /// Prints jana.cc command-line options to stdout, for use by the CLI.
0080   /// This does not include JANA parameters, which come from
0081   /// JParameterManager::PrintParameters() instead.
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   // Edge case handler: taking care of invalid and empty dirs
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()); // Example: "/usr/local/plugins/Tutorial.so"
0122     filename  = full_path.substr(full_path.find_last_of("/") + 1); // Example: "Tutorial.so"
0123     if (filename.substr(filename.size() - 3) == ".so") {
0124       std::string s = filename.substr(0, filename.size() - 3);
0125       //                std::cout << filename << "==> "  << s << std::endl;
0126       plugin_names.insert(s);
0127     }
0128   }
0129 }
0130 
0131 /// Get the plugin names by searching for files named as *.so under $JANA_PLUGIN_PATH and
0132 /// $EICrecon_MY/plugins.
0133 /// @note It does not guarantee any effectiveness of the plugins.
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   // Use set to remove duplicates.
0154   /// @note The plugins will be override if you use the same plugin name at different paths.
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 /// Detect whether the cli params @param options.params contain
0197 /// "-Pplugins_to_ignore=...<erase_str>...". If true, delete @param erase_str from the original cli
0198 /// string "-Pplugins_to_ignore".
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   // Has cli option "-Pplugins_to_ignore". Look for @param erase_str
0205   size_t pos = has_ignore_plugins->second.find(erase_str);
0206   if (pos == std::string::npos) // does not contain @param erase_str
0207     return false;
0208 
0209   // Detect @param flag_str. Delete flag_str from the original cli option.
0210   std::string ignore_str;
0211   if (erase_str.length() + pos ==
0212       has_ignore_plugins->second.length()) { // @param flag_str is at the end
0213     ignore_str = has_ignore_plugins->second.erase(pos, erase_str.length());
0214   } else { // erase "<flag_str>," from "-Pplugins_to_ignore".
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   // Add the plugins at $EICrecon_MY/plugins.
0226   //        jana::GetPluginNamesFromEnvPath(set_plugins, "EICrecon_MY");  // disabled as we do not
0227   //        want to automatically add these
0228 
0229   std::string plugins_str; // the complete plugins list
0230   for (std::string s : set_plugins)
0231     plugins_str += s + ",";
0232 
0233   // Add the default plugins into the plugin set if there is no
0234   // "-Pplugins_to_ignore=default (exclude all default plugins)" option
0235   if (HasExcludeDefaultPluginsInCliParams(options, "default") == false)
0236     /// @note: The sequence of adding the default plugins matters.
0237     /// Have to keep the original sequence to not causing troubles.
0238     for (std::string s : default_plugins) {
0239       plugins_str += s + ",";
0240     }
0241 
0242   // Insert other plugins in cli option "-Pplugins=pl1,pl2,..."
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); // exclude last ","
0249   }
0250 }
0251 
0252 JApplication* CreateJApplication(UserOptions& options) {
0253 
0254   auto* para_mgr =
0255       new JParameterManager(); // JApplication owns params_copy, does not own eventSources
0256 
0257   // Add the cli options based on the user inputs
0258   for (auto pair : options.params) {
0259     para_mgr->SetParameter(pair.first, pair.second);
0260   }
0261 
0262   // Shut down the [INFO] msg of adding plugins, printing cpu info
0263   if (options.flags[ListFactories]) {
0264     para_mgr->SetParameter("log:off", "JPluginLoader,JArrowProcessingController,JArrow");
0265   }
0266 
0267   if (options.flags[LoadConfigs]) {
0268     // If the user specified an external config file, we should definitely use that
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   // If the user hasn't specified a timeout (on cmd line or in config file), set the timeout to
0282   // something reasonably high
0283   if (para_mgr->FindParameter("jana:timeout") == nullptr) {
0284     para_mgr->SetParameter("jana:timeout", 180);        // seconds
0285     para_mgr->SetParameter("jana:warmup_timeout", 180); // seconds
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     // cli criteria: Ppodio:print_type_table=1
0318     if (print_type_table) {
0319       auto event_sources = app->GetService<JComponentManager>()->get_evt_srces();
0320       for (auto* event_source : event_sources) {
0321         //                    std::cout << event_source->GetPluginName() << std::endl;  // podio.so
0322         //                    std::cout << event_source->GetResourceName() << std::endl;
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   /// Print a table of the currently defined configuration parameters.
0332   /// n.b. this mostly duplicates a call to app->GetJParameterManager()->PrintParameters()
0333   /// but avoids the issue it has of setting the values column to same
0334   /// width for all parameters. (That leads to lots of whitespace being
0335   /// printed due to the very long podio:output_include_collections param.
0336 
0337   // Determine column widths
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; // maximum width allowed for column.
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   // std::cout << "JANA " << JVersion::GetVersion() << " [" << JVersion::GetRevision() << "]" <<
0381   // std::endl;
0382 
0383   if (options.flags[ShowConfigs]) {
0384     // Load all plugins, collect all parameters, exit without running anything
0385     app->Initialize();
0386     if (options.flags[Benchmark]) {
0387       JBenchmarker benchmarker(
0388           app); // Show benchmarking configs only if benchmarking mode specified
0389     }
0390     //            app->GetJParameterManager()->PrintParameters(true);
0391     PrintConfigParameters(app);
0392   } else if (options.flags[DumpConfigs]) {
0393     // Load all plugins, dump parameters to file, exit without running anything
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     // Run JANA in benchmark mode
0401     JBenchmarker benchmarker(app);  // Benchmarking params override default params
0402     benchmarker.RunUntilFinished(); // Benchmarker will control JApp Run/Stop
0403   } else if (options.flags[ListFactories]) {
0404     app->Initialize();
0405     PrintFactories(app);
0406 
0407     // TODO: more elegant processing here
0408     PrintPodioCollections(app);
0409   } else {
0410     // Run JANA in normal mode
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   // `eicrecon` has the same effect with `eicrecon -h`
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     // std::cout << "Found arg " << arg << std::endl;
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     // TODO: add exclude plugin options
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 } // namespace jana