Source · fairmq/
ProgOptions.cxxsource
Described in Configuration and properties, Threads in a device, 1-1: sampler and sink, qc: quality control on demand, Control a device from code, Command-line options, Logging.
1/********************************************************************************2 * Copyright (C) 2014-2022 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *3 * *4 * This software is distributed under the terms of the *5 * GNU Lesser General Public Licence (LGPL) version 3, *6 * copied verbatim in the file "LICENSE" *7 ********************************************************************************/8/*9 * File: ProgOptions.cxx10 * Author: winckler11 *12 * Created on March 11, 2015, 10:20 PM13 */14 15#include <fairmq/ProgOptions.h>16#include <fairmq/tools/Strings.h>17 18#include <boost/any.hpp>19#include <boost/regex.hpp>20 21#include <algorithm>22#include <exception>23#include <fairlogger/Logger.h>24#include <iomanip>25#include <iostream>26#include <sstream>27#include <utility> // pair28 29using namespace std;30using namespace fair::mq;31 32namespace po = boost::program_options;33 34struct ValInfo35{36 string value;37 string type;38 string origin;39};40 41namespace fair::mq42{43 44ValInfo ConvertVarValToValInfo(const po::variable_value& v)45{46 string origin;47 48 if (v.defaulted()) {49 origin = "[default]";50 } else if (v.empty()) {51 origin = "[empty]";52 } else {53 origin = "[provided]";54 }55 56 try {57 pair<string, string> info = PropertyHelper::GetPropertyInfo(v.value());58 return {info.first, info.second, origin};59 } catch (out_of_range& oor) {60 return {string("[unidentified_type]"), string("[unidentified_type]"), origin};61 }62}63 64string ConvertVarValToString(const po::variable_value& v)65{66 return ConvertVarValToValInfo(v).value;67}68 69ProgOptions::ProgOptions()70 : fAllOptions("FairMQ Command Line Options")71{72 fAllOptions.add_options()73 ("help,h", "Print help")74 ("version,v", "Print version")75 ("severity", po::value<string>()->default_value(""), "Log severity level (console): trace, debug, info, state, warn, error, fatal, nolog.")76 ("file-severity", po::value<string>()->default_value("debug"), "Log severity level (file): trace, debug, info, state, warn, error, fatal, nolog")77 ("verbosity", po::value<string>()->default_value("medium"), "Log verbosity level: veryhigh, high, medium, low")78 ("color", po::value<bool >()->default_value(true), "Log color (true/false)")79 ("log-to-file", po::value<string>()->default_value(""), "Log output to a file.")80 ("print-options", po::value<bool >()->implicit_value(true), "Print options in machine-readable format (<option>:<computed-value>:<type>:<description>)");81 82 ParseDefaults();83}84 85void ProgOptions::ParseDefaults()86{87 vector<string> emptyArgs = {"dummy"};88 89 vector<const char*> argv(emptyArgs.size());90 91 transform(emptyArgs.begin(), emptyArgs.end(), argv.begin(), [](const string& str) {92 return str.c_str();93 });94 95 std::lock_guard<std::mutex> lock(fMtx);96 po::store(po::parse_command_line(argv.size(), const_cast<char**>(argv.data()), fAllOptions), fVarMap);97}98 99void ProgOptions::ParseAll(const vector<string>& cmdArgs, bool allowUnregistered)100{101 vector<const char*> argv(cmdArgs.size());102 transform(cmdArgs.begin(), cmdArgs.end(), argv.begin(), [](const string& str) {103 return str.c_str();104 });105 ParseAll(argv.size(), const_cast<char**>(argv.data()), allowUnregistered);106}107 108void ProgOptions::ParseAll(const int argc, char const* const* argv, bool allowUnregistered)109{110 lock_guard<mutex> lock(fMtx);111 // clear the container because it was filled with default values and subsequent calls to store() do not overwrite the existing values112 fVarMap.clear();113 114 115 po::command_line_parser parser(argc, argv);116 117 parser.options(fAllOptions);118 119 if (allowUnregistered) {120 parser.allow_unregistered();121 }122 123 using namespace po::command_line_style;124 style_t style = style_t(allow_short | short_allow_adjacent | short_allow_next | allow_long | long_allow_adjacent | long_allow_next | allow_sticky | allow_dash_for_short);125 parser.style(style);126 127 po::parsed_options parsed = parser.run();128 129 fUnregisteredOptions = po::collect_unrecognized(parsed.options, po::include_positional);130 131 po::store(parsed, fVarMap);132}133 134void ProgOptions::Notify()135{136 lock_guard<mutex> lock(fMtx);137 po::notify(fVarMap);138}139 140void ProgOptions::AddToCmdLineOptions(const po::options_description optDesc, bool /* visible */)141{142 lock_guard<mutex> lock(fMtx);143 fAllOptions.add(optDesc);144}145 146po::options_description& ProgOptions::GetCmdLineOptions()147{148 return fAllOptions;149}150 151int ProgOptions::Count(const string& key) const152{153 lock_guard<mutex> lock(fMtx);154 return fVarMap.count(key);155}156 157unordered_map<string, int> ProgOptions::GetChannelInfo() const158{159 lock_guard<mutex> lock(fMtx);160 return GetChannelInfoImpl();161}162 163unordered_map<string, int> ProgOptions::GetChannelInfoImpl() const164{165 unordered_map<string, int> info;166 167 boost::regex re("chans\\..*\\.type");168 for (const auto& m : fVarMap) {169 if (boost::regex_match(m.first, re)) {170 string chan = m.first.substr(6);171 string::size_type n = chan.find('.');172 string chanName = chan.substr(0, n);173 174 if (info.find(chanName) == info.end()) {175 info.emplace(chanName, 1);176 } else {177 info[chanName] = info[chanName] + 1;178 }179 }180 }181 182 return info;183}184 185vector<string> ProgOptions::GetPropertyKeys() const186{187 lock_guard<mutex> lock(fMtx);188 189 vector<string> keys;190 191 for (const auto& it : fVarMap) {192 keys.emplace_back(it.first.c_str());193 }194 195 return keys;196}197 198string ProgOptions::GetPropertyAsString(const string& key) const199{200 lock_guard<mutex> lock(fMtx);201 202 if (fVarMap.count(key)) {203 return ConvertVarValToString(fVarMap.at(key));204 } else {205 throw PropertyNotFoundError(fair::mq::tools::ToString("Config has no key: ", key));206 }207}208 209string ProgOptions::GetStringValue(const string& key) const210{211 lock_guard<mutex> lock(fMtx);212 213 if (fVarMap.count(key)) {214 return ConvertVarValToString(fVarMap.at(key));215 } else {216 LOG(warn) << "Config has no key: " << key << ". Returning default constructed object.";217 return string();218 }219}220 221string ProgOptions::GetPropertyAsString(const string& key, const string& ifNotFound) const222{223 lock_guard<mutex> lock(fMtx);224 225 if (fVarMap.count(key)) {226 return ConvertVarValToString(fVarMap.at(key));227 }228 229 return ifNotFound;230}231 232Properties ProgOptions::GetProperties(const string& q) const233{234 boost::regex re(q);235 Properties properties;236 237 lock_guard<mutex> lock(fMtx);238 239 for (const auto& m : fVarMap) {240 if (boost::regex_match(m.first, re)) {241 properties.emplace(m.first, m.second.value());242 }243 }244 245 if (properties.empty()) {246 LOG(warn) << "could not find anything with \"" << q << "\"";247 }248 249 return properties;250}251 252Properties ProgOptions::GetPropertiesStartingWith(const string& q) const253{254 Properties properties;255 256 lock_guard<mutex> lock(fMtx);257 258 for (const auto& m : fVarMap) {259 if (m.first.compare(0, q.length(), q) == 0) {260 properties.emplace(m.first, m.second.value());261 }262 }263 264 return properties;265}266 267map<string, string> ProgOptions::GetPropertiesAsString(const string& q) const268{269 boost::regex re(q);270 map<string, string> properties;271 272 lock_guard<mutex> lock(fMtx);273 274 for (const auto& m : fVarMap) {275 if (boost::regex_match(m.first, re)) {276 properties.emplace(m.first, PropertyHelper::ConvertPropertyToString(m.second.value()));277 }278 }279 280 if (properties.empty()) {281 LOG(warn) << "could not find anything with \"" << q << "\"";282 }283 284 return properties;285}286 287map<string, string> ProgOptions::GetPropertiesAsStringStartingWith(const string& q) const288{289 map<string, string> properties;290 291 lock_guard<mutex> lock(fMtx);292 293 for (const auto& m : fVarMap) {294 if (m.first.compare(0, q.length(), q) == 0) {295 properties.emplace(m.first, PropertyHelper::ConvertPropertyToString(m.second.value()));296 }297 }298 299 return properties;300}301 302void ProgOptions::SetProperties(const Properties& input)303{304 unique_lock<mutex> lock(fMtx);305 306 map<string, boost::program_options::variable_value>& vm = fVarMap;307 for (const auto& m : input) {308 vm[m.first].value() = m.second;309 }310 311 lock.unlock();312 313 for (const auto& m : input) {314 PropertyHelper::fEventEmitters.at(m.second.type())(fEvents, m.first, m.second);315 fEvents.Emit<PropertyChangeAsString, string>(m.first, PropertyHelper::ConvertPropertyToString(m.second));316 }317}318 319bool ProgOptions::UpdateProperties(const Properties& input)320{321 unique_lock<mutex> lock(fMtx);322 323 for (const auto& m : input) {324 if (fVarMap.count(m.first) == 0) {325 LOG(debug) << "UpdateProperties failed, no property found with key '" << m.first << "'";326 return false;327 }328 }329 330 map<string, boost::program_options::variable_value>& vm = fVarMap;331 for (const auto& m : input) {332 vm[m.first].value() = m.second;333 }334 335 lock.unlock();336 337 for (const auto& m : input) {338 PropertyHelper::fEventEmitters.at(m.second.type())(fEvents, m.first, m.second);339 fEvents.Emit<PropertyChangeAsString, string>(m.first, PropertyHelper::ConvertPropertyToString(m.second));340 }341 342 return true;343}344 345void ProgOptions::DeleteProperty(const string& key)346{347 lock_guard<mutex> lock(fMtx);348 349 map<string, boost::program_options::variable_value>& vm = fVarMap;350 vm.erase(key);351}352 353void ProgOptions::AddChannel(const string& name, const Channel& channel)354{355 lock_guard<mutex> lock(fMtx);356 unordered_map<string, int> existingChannels = GetChannelInfoImpl();357 int index = 0;358 if (existingChannels.count(name) > 0) {359 index = existingChannels.at(name);360 }361 362 string prefix = fair::mq::tools::ToString("chans.", name, ".", index, ".");363 364 SetVarMapValue<string>(string(prefix + "type"), channel.GetType());365 SetVarMapValue<string>(string(prefix + "method"), channel.GetMethod());366 SetVarMapValue<string>(string(prefix + "address"), channel.GetAddress());367 SetVarMapValue<string>(string(prefix + "transport"), channel.GetTransportName());368 SetVarMapValue<int>(string(prefix + "sndBufSize"), channel.GetSndBufSize());369 SetVarMapValue<int>(string(prefix + "rcvBufSize"), channel.GetRcvBufSize());370 SetVarMapValue<int>(string(prefix + "sndKernelSize"), channel.GetSndKernelSize());371 SetVarMapValue<int>(string(prefix + "rcvKernelSize"), channel.GetRcvKernelSize());372 SetVarMapValue<int>(string(prefix + "linger"), channel.GetLinger());373 SetVarMapValue<int>(string(prefix + "rateLogging"), channel.GetRateLogging());374 SetVarMapValue<int>(string(prefix + "portRangeMin"), channel.GetPortRangeMin());375 SetVarMapValue<int>(string(prefix + "portRangeMax"), channel.GetPortRangeMax());376 SetVarMapValue<bool>(string(prefix + "autoBind"), channel.GetAutoBind());377}378 379void ProgOptions::PrintHelp() const380{381 cout << fAllOptions << endl;382}383 384void ProgOptions::PrintOptions() const385{386 map<string, ValInfo> mapinfo;387 388 int maxLenKey = 0;389 int maxLenValue = 0;390 int maxLenType = 0;391 int maxLenDefault = 0;392 393 std::lock_guard<std::mutex> lock(fMtx);394 395 for (const auto& m : fVarMap) {396 maxLenKey = max(maxLenKey, static_cast<int>(m.first.length()));397 398 ValInfo valinfo = ConvertVarValToValInfo(m.second);399 mapinfo[m.first] = valinfo;400 401 maxLenValue = max(maxLenValue, static_cast<int>(valinfo.value.length()));402 maxLenType = max(maxLenType, static_cast<int>(valinfo.type.length()));403 maxLenDefault = max(maxLenDefault, static_cast<int>(valinfo.origin.length()));404 }405 406 if (maxLenValue > 100) {407 maxLenValue = 100;408 }409 410 for (const auto& o : fUnregisteredOptions) {411 LOG(debug) << "detected unregistered option: " << o;412 }413 414 stringstream ss;415 ss << "Configuration: \n";416 417 for (const auto& p : mapinfo) {418 string type("<" + p.second.type + ">");419 ss << setfill(' ') << left420 << setw(maxLenKey) << p.first << " = "421 << setw(maxLenValue) << p.second.value << " "422 << setw(maxLenType + 2) << type << " "423 << setw(maxLenDefault) << p.second.origin424 << "\n";425 }426 427 LOG(debug) << ss.str();428}429 430void ProgOptions::PrintOptionsRaw() const431{432 std::lock_guard<std::mutex> lock(fMtx);433 434 const vector<boost::shared_ptr<po::option_description>>& options = fAllOptions.options();435 436 for (const auto& o : options) {437 ValInfo value;438 if (fVarMap.count(o->canonical_display_name())) {439 value = ConvertVarValToValInfo(fVarMap[o->canonical_display_name()]);440 }441 442 string description = o->description();443 444 replace(description.begin(), description.end(), '\n', ' ');445 446 cout << o->long_name() << ":" << value.value << ":" << (value.type.empty() ? "<unknown>" : value.type) << ":" << description << endl;447 }448}449 450} // namespace fair::mq451 452template void fair::mq::ProgOptions::SetProperty<std::string>(const std::string& key, std::string val);453template void fair::mq::ProgOptions::SetProperty<int>(const std::string& key, int val);