Back to home page

EIC code displayed by LXR

 
 

    


Warning, file /jana2/src/libraries/JANA/Services/JWiringService.cc was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 
0002 #include "JWiringService.h"
0003 #include "toml.hpp"
0004 #include <exception>
0005 #include <memory>
0006 #include <set>
0007 
0008 namespace jana::services {
0009 
0010 JWiringService::JWiringService() {
0011     SetPrefix("jana");
0012 }
0013 
0014 void JWiringService::Init() {
0015     LOG_INFO(GetLogger()) << "Initializing JWiringService" << LOG_END;
0016     // User is _only_ allowed to specify wiring file via parameter
0017     // This way, we can restrict calling JWiringService::Init until inside JApplication::Init
0018     // Then we can load the wiring file exactly once. All WiredFactoryGenerators 
0019     // (recursively) load files
0020 
0021     if (!m_wirings_input_file().empty()) {
0022         ApplyWiringSet(ParseWiringSetFromFilename(*m_wirings_input_file));
0023     }
0024 }
0025 
0026 void JWiringService::ApplyWiringSet(JWiringService::WiringSet&& wiring_set) {
0027 
0028     // Recursively overlay any included files
0029     OverlayAllIncludes(wiring_set);
0030 
0031     // Clear everything from before
0032     m_wiring_set = std::move(wiring_set);
0033     m_added_prefixes.clear();
0034 
0035     // Populate lookup table
0036     for (auto& it: m_wiring_set.wirings) {
0037         if (it.second->action == Action::Add) {
0038             auto& wiring = it.second;
0039             m_added_prefixes[{wiring->type_name, wiring->plugin_name}].push_back(wiring->prefix);
0040         }
0041     }
0042 }
0043 
0044 void JWiringService::OverlayAllIncludes(WiringSet &wiring_set) {
0045     for (auto& include_file_name : wiring_set.include_file_names) {
0046         auto include_wiring_set = ParseWiringSetFromFilename(include_file_name);
0047         OverlayAllIncludes(include_wiring_set);
0048         OverlayWiringSet(wiring_set, include_wiring_set);
0049     }
0050 }
0051 
0052 std::unique_ptr<JWiringService::Wiring> ParseWiring(const toml::table& f) {
0053 
0054     auto wiring = std::make_unique<JWiringService::Wiring>();
0055 
0056     auto unparsed_action = f["action"].value<std::string>().value_or("(missing)");
0057     if (unparsed_action == "update") {
0058         wiring->action = JWiringService::Action::Update;
0059     }
0060     else if (unparsed_action == "add") {
0061         wiring->action = JWiringService::Action::Add;
0062     }
0063     else if (unparsed_action == "remove") {
0064         wiring->action = JWiringService::Action::Remove;
0065     }
0066     else {
0067         throw JException("JWiringService: Invalid action '%s'! Valid values are {'update', 'add', 'remove'}", unparsed_action.c_str());
0068     }
0069     wiring->plugin_name = f["plugin_name"].value<std::string>().value_or("");
0070     wiring->type_name = f["type_name"].value<std::string>().value();
0071     wiring->prefix = f["prefix"].value<std::string>().value();
0072     wiring->level = parseEventLevel(f["level"].value_or<std::string>("None"));
0073 
0074     auto input_names = f["input_names"].as_array();
0075     if (input_names != nullptr) {
0076         if (wiring->action == JWiringService::Action::Remove) {
0077             throw JException("Removed wiring has superfluous input names");
0078         }
0079         for (const auto& input_name : *input_names) {
0080             wiring->input_names.push_back(input_name.value<std::string>().value());
0081         }
0082     }
0083 
0084     auto variadic_input_names = f["variadic_input_names"].as_array();
0085     if (variadic_input_names != nullptr) {
0086         if (wiring->action == JWiringService::Action::Remove) {
0087             throw JException("Removed wiring has superfluous variadic input names");
0088         }
0089         for (const auto& input_name_vec : *variadic_input_names) {
0090             std::vector<std::string> temp;
0091             for (const auto& input_name : *(input_name_vec.as_array())) {
0092                 temp.push_back(input_name.as_string()->get());
0093             }
0094             wiring->variadic_input_names.push_back(temp);
0095         }
0096     }
0097 
0098     auto input_levels = f["input_levels"].as_array();
0099     if (input_levels != nullptr) {
0100         if (wiring->action == JWiringService::Action::Remove) {
0101             throw JException("Removed wiring has superfluous input levels");
0102         }
0103         for (const auto& input_level : *input_levels) {
0104             wiring->input_levels.push_back(parseEventLevel(input_level.value<std::string>().value()));
0105         }
0106     }
0107 
0108     auto variadic_input_levels = f["variadic_input_levels"].as_array();
0109     if (variadic_input_levels != nullptr) {
0110         if (wiring->action == JWiringService::Action::Remove) {
0111             throw JException("Removed wiring has superfluous variadic input levels");
0112         }
0113         for (const auto& input_level : *variadic_input_levels) {
0114             wiring->variadic_input_levels.push_back(parseEventLevel(input_level.value<std::string>().value()));
0115         }
0116     }
0117 
0118     auto output_names = f["output_names"].as_array();
0119     if (output_names != nullptr) {
0120         if (wiring->action == JWiringService::Action::Remove) {
0121             throw JException("Removed wiring has superfluous output names");
0122         }
0123         for (const auto& output_name : *f["output_names"].as_array()) {
0124             wiring->output_names.push_back(output_name.value<std::string>().value());
0125         }
0126     }
0127 
0128     auto variadic_output_names = f["variadic_output_names"].as_array();
0129     if (variadic_output_names != nullptr) {
0130         if (wiring->action == JWiringService::Action::Remove) {
0131             throw JException("Removed wiring has superfluous variadic output names");
0132         }
0133         for (const auto& output_name_vec : *variadic_output_names) {
0134             std::vector<std::string> temp;
0135             for (const auto& output_name : *(output_name_vec.as_array())) {
0136                 temp.push_back(output_name.as_string()->get());
0137             }
0138             wiring->variadic_output_names.push_back(temp);
0139         }
0140     }
0141 
0142     auto configs = f["configs"].as_table();
0143     if (configs != nullptr) {
0144         for (const auto& config : *configs) {
0145             std::string config_name(config.first);
0146             // For now, parse all config values as strings. 
0147             // Later we may go for a deeper integration with toml types and/or with JParameterManager.
0148             wiring->configs[config_name] = config.second.value<std::string>().value();
0149         }
0150     }
0151 
0152     return wiring;
0153 }
0154 
0155 
0156 JWiringService::WiringSet JWiringService::ParseWiringSet(const toml::table& table) {
0157 
0158     WiringSet wiring_set;
0159 
0160     // Parse include file names
0161     auto includes = table["includes"].as_array();
0162     if (includes != nullptr) {
0163         for (const auto& include : *includes) {
0164             wiring_set.include_file_names.push_back(include.as<std::string>()->get());
0165         }
0166     }
0167 
0168     // Parse plugin names
0169     auto plugins = table["plugins"].as_array();
0170     if (plugins != nullptr) {
0171         for (const auto& plugin_name : *plugins) {
0172             wiring_set.include_file_names.push_back(plugin_name.as<std::string>()->get());
0173         }
0174     }
0175 
0176     // Parse shared parameters
0177     auto shared_params = table["configs"].as_table();
0178     if (shared_params != nullptr) {
0179         for (const auto& param : *shared_params) {
0180             std::string key(param.first);
0181             std::string val = *param.second.value<std::string>();
0182             wiring_set.shared_parameters[key] = val;
0183         }
0184     }
0185 
0186     // Parse use_short_names
0187     auto use_short_names = table["use_short_names"].as<bool>();
0188     if (use_short_names != nullptr) {
0189         wiring_set.use_short_names = use_short_names->get();
0190     }
0191 
0192     // Parse wirings
0193     auto wirings_array = table["wiring"].as_array();
0194     if (wirings_array != nullptr) {
0195         for (const auto& wiring_node : *wirings_array) {
0196             auto wiring_table = wiring_node.as_table();
0197             if (wiring_table != nullptr) {
0198                 auto wiring = ParseWiring(*wiring_table);
0199 
0200                 // Check for uniqueness before adding
0201                 auto it = wiring_set.wirings.find(wiring->prefix);
0202                 if (it != wiring_set.wirings.end()) {
0203                     throw JException("Duplicated prefix '%s' in wiring set", wiring->prefix.c_str());
0204                 }
0205 
0206                 // Add to wiring set
0207                 wiring_set.wirings[wiring->prefix] = std::move(wiring);
0208             }
0209         }
0210     }
0211 
0212     return wiring_set;
0213 }
0214 
0215 JWiringService::WiringSet JWiringService::ParseWiringSetFromFilename(const std::string& filename) {
0216     try {
0217         auto table = toml::parse_file(filename);
0218         return ParseWiringSet(table);
0219     }
0220     catch (const toml::parse_error& err) {
0221         auto e = JException("Error parsing TOML file: '%s'", filename.c_str());
0222         e.nested_exception = std::current_exception();
0223         throw e;
0224     }
0225 }
0226 
0227 JWiringService::Wiring* JWiringService::GetWiring(const std::string& prefix) const {
0228     auto it = m_wiring_set.wirings.find(prefix);
0229     if (it == m_wiring_set.wirings.end()) {
0230         return nullptr;
0231     }
0232     return it->second.get();
0233 }
0234 
0235 const std::vector<std::string>&
0236 JWiringService::GetPrefixesForAddedInstances(const std::string& plugin_name, const std::string& type_name) const {
0237 
0238     auto it = m_added_prefixes.find({type_name, plugin_name});
0239     if (it == m_added_prefixes.end()) {
0240         return m_no_added_prefixes;
0241     }
0242     return it->second;
0243 }
0244 
0245 void JWiringService::OverlayWiringSet(WiringSet &above, const WiringSet &below) {
0246 
0247     // TODO: Figure out merging of includes, plugins, use_short_names
0248 
0249     // Iterate over 'below' to ensure that every wiring from below propagates above
0250     for (auto& below_it: below.wirings) {
0251         auto& below_wiring = below_it.second;
0252         auto& prefix = below_wiring->prefix;
0253         const auto& above_it = above.wirings.find(prefix);
0254         if (above_it == above.wirings.end()) {
0255             // This is a new wiring, so we add it and we are done
0256             above.wirings[prefix] = std::make_unique<Wiring>(*below_wiring);
0257         }
0258         else {
0259             // Wiring shows up in both places, so we need to overlay the two
0260             auto& above_wiring = above_it->second;
0261 
0262             // First we validate that the two wirings are compatible
0263             if (above_wiring->type_name != below_wiring->type_name) {
0264                 throw JException("Wiring mismatch: type name '%s' vs '%s'",
0265                                  above_wiring->type_name.c_str(), below_wiring->type_name.c_str());
0266             }
0267             if (above_wiring->plugin_name != below_wiring->plugin_name) {
0268                 throw JException("Wiring mismatch: plugin name '%s' vs '%s'",
0269                                  above_wiring->plugin_name.c_str(), below_wiring->plugin_name.c_str());
0270             }
0271 
0272             // Next we do the overlay, which modifies above_wiring
0273             Overlay(*above_wiring, *below_wiring);
0274         }
0275     }
0276 }
0277 
0278 void JWiringService::Overlay(Wiring& above, const Wiring& below) {
0279 
0280     // In theory this should be handled by the caller, but let's check just in case
0281     if (above.plugin_name != below.plugin_name) throw JException("Plugin name mismatch!");
0282     if (above.type_name != below.type_name) throw JException("Type name mismatch!");
0283 
0284     // Overlay actions
0285     if (above.action == Action::Add) {
0286         if (below.action != Action::Remove) {
0287             throw JException("Attempted to add a wiring that is already present. plugin=%s, prefix=%s",
0288                              above.plugin_name.c_str(), above.prefix.c_str());
0289         }
0290         else {
0291             // Add(Remove(wiring)) = Update(wiring)
0292             above.action = Action::Update;
0293         }
0294     }
0295     else if (above.action == Action::Update) {
0296         if (below.action == Action::Add) {
0297             above.action = Action::Add;
0298         }
0299         else if (below.action == Action::Remove) {
0300             throw JException("Attempted to update a wiring that has been removed. plugin=%s, prefix=%s",
0301                              above.plugin_name.c_str(), above.prefix.c_str());
0302         }
0303         // Update(Update(wiring)) => Update(wiring)
0304     }
0305     else if (above.action == Action::Remove) {
0306         if (below.action == Action::Remove) {
0307             throw JException("Attempted to remove a wiring that has already been removed. plugin=%s, prefix=%s",
0308                              above.plugin_name.c_str(), above.prefix.c_str());
0309         }
0310         // Remove(Add(wiring)) => Remove(wiring)  // This one is odd, might reconsider
0311         // Remove(Update(wiring)) => Remove(wiring)
0312     }
0313 
0314     if (above.input_names.empty() && !below.input_names.empty()) {
0315         above.input_names = std::move(below.input_names);
0316     }
0317     if (above.input_levels.empty() && !below.input_levels.empty()) {
0318         above.input_levels = std::move(below.input_levels);
0319     }
0320     if (above.output_names.empty() && !below.output_names.empty()) {
0321         above.output_names = std::move(below.output_names);
0322     }
0323     for (const auto& [key, val] : below.configs) {
0324         if (above.configs.find(key) == above.configs.end()) {
0325             above.configs[key] = val;
0326         }
0327     }
0328 }
0329 
0330 const std::map<std::string, std::string>& JWiringService::GetSharedParameters() const {
0331     return m_wiring_set.shared_parameters;
0332 }
0333 
0334 void JWiringService::CheckAllWiringsAreUsed() {
0335     std::vector<JWiringService::Wiring*> m_unused_wirings;
0336     for (const auto& wiring : m_wiring_set.wirings) {
0337         if (wiring.second->is_used == false) {
0338             m_unused_wirings.push_back(wiring.second.get());
0339         }
0340     }
0341     if (m_unused_wirings.size() != 0) {
0342         LOG_ERROR(GetLogger()) << "Wirings were found but never used:";
0343         for (auto wiring : m_unused_wirings) {
0344             LOG_ERROR(GetLogger()) << "  " << wiring->type_name << " " << wiring->prefix;
0345         }
0346         throw JException("Unused wirings found");
0347     }
0348 }
0349 
0350 } // namespace jana::services