Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2024-09-27 07:03:09

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   if (options.flags[ShowConfigs]) {
0381     // Load all plugins, collect all parameters, exit without running anything
0382     app->Initialize();
0383     if (options.flags[Benchmark]) {
0384       JBenchmarker benchmarker(
0385           app); // Show benchmarking configs only if benchmarking mode specified
0386     }
0387     //            app->GetJParameterManager()->PrintParameters(true);
0388     PrintConfigParameters(app);
0389   } else if (options.flags[DumpConfigs]) {
0390     // Load all plugins, dump parameters to file, exit without running anything
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     // Run JANA in benchmark mode
0398     JBenchmarker benchmarker(app);  // Benchmarking params override default params
0399     benchmarker.RunUntilFinished(); // Benchmarker will control JApp Run/Stop
0400   } else if (options.flags[ListFactories]) {
0401     app->Initialize();
0402     PrintFactories(app);
0403 
0404     // TODO: more elegant processing here
0405     PrintPodioCollections(app);
0406   } else {
0407     if ((JVersion::GetMajorNumber() == 2) && (JVersion::GetMinorNumber() == 3) && (JVersion::GetPatchNumber() <= 1)) {
0408       // JANA2 2.3.x has a bug with not filtering default-state parameters, which causes enormous printouts
0409       if (not app->GetJParameterManager()->Exists("jana:parameter_verbosity")) {
0410         app->GetJParameterManager()->SetParameter("jana:parameter_verbosity", 0);
0411       }
0412     }
0413     // Run JANA in normal mode
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   // `eicrecon` has the same effect with `eicrecon -h`
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     // std::cout << "Found arg " << arg << std::endl;
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     // TODO: add exclude plugin options
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 } // namespace jana