Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 10:17:35

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 void JWiringService::Init() {
0011     LOG_INFO(GetLogger()) << "Initializing JWiringService" << LOG_END;
0012     // User is _only_ allowed to specify wiring file via parameter
0013     // This way, we can restrict calling JWiringService::Init until inside JApplication::Init
0014     // Then we can load the wiring file exactly once. All WiredFactoryGenerators 
0015     // (recursively) load files
0016 
0017     if (!m_wirings_input_file().empty()) {
0018         AddWirings(*m_wirings_input_file);
0019     }
0020 }
0021 
0022 void JWiringService::AddWirings(std::vector<std::unique_ptr<Wiring>>& wirings_bundle, const std::string& bundle_source) {
0023 
0024     std::set<std::string> prefixes_in_bundle;
0025     for (auto& wiring: wirings_bundle) {
0026 
0027         // Assert that this wiring's prefix is unique _within_ this bundle.
0028         auto bundle_it = prefixes_in_bundle.find(wiring->prefix);
0029         if (bundle_it != prefixes_in_bundle.end()) {
0030             throw JException("Duplicated prefix '%s' in wiring bundle '%s'", wiring->prefix.c_str(), bundle_source.c_str());
0031         }
0032         prefixes_in_bundle.insert(wiring->prefix);
0033 
0034         // Check whether we have seen this prefix before
0035         auto it = m_wirings_from_prefix.find(wiring->prefix);
0036         if (it == m_wirings_from_prefix.end()) {
0037             // This is a new wiring
0038             m_wirings_from_prefix[wiring->prefix] = wiring.get();
0039             m_wirings_from_type_and_plugin_names[{wiring->type_name, wiring->plugin_name}].push_back(wiring.get());
0040             m_wirings.push_back(std::move(wiring));
0041         }
0042         else {
0043             // Wiring is already defined; overlay this wiring _below_ the existing wiring
0044             // First we do some sanity checks
0045             if (wiring->type_name != it->second->type_name) {
0046                 throw JException("Wiring mismatch: type name '%s' vs '%s'", wiring->type_name.c_str(), it->second->type_name.c_str());
0047             }
0048             if (wiring->plugin_name != it->second->plugin_name) {
0049                 throw JException("Wiring mismatch: plugin name '%s' vs '%s'", wiring->plugin_name.c_str(), it->second->plugin_name.c_str());
0050             }
0051             Overlay(*(it->second), *wiring);
0052             // Useful information from `wiring` has been copied into `it->second`.
0053             // `wiring` will now be automatically destroyed
0054         }
0055     }
0056     // At this point all wirings have been moved out of wirings_bundle, so we clear it to avoid confusing callers
0057     wirings_bundle.clear();
0058 }
0059 
0060 void JWiringService::AddWirings(const toml::table& table, const std::string& source) {
0061 
0062     std::vector<std::unique_ptr<Wiring>> wirings;
0063     auto facs = table["factory"].as_array();
0064     if (facs == nullptr) {
0065         throw JException("No factories found!");
0066     }
0067     for (const auto& fac : *facs) {
0068         auto wiring = std::make_unique<Wiring>();
0069 
0070         if (fac.as_table() == nullptr) {
0071             throw JException("Invalid format: 'factory' is not a table");
0072         }
0073         auto& f = *fac.as_table();
0074 
0075         wiring->plugin_name = f["plugin_name"].value<std::string>().value_or("");
0076         wiring->type_name = f["type_name"].value<std::string>().value();
0077         wiring->prefix = f["prefix"].value<std::string>().value();
0078 
0079         wiring->level = parseEventLevel(f["level"].value_or<std::string>("None"));
0080 
0081         auto input_names = f["input_names"].as_array();
0082         if (input_names != nullptr) {
0083             for (const auto& input_name : *f["input_names"].as_array()) {
0084                 wiring->input_names.push_back(input_name.value<std::string>().value());
0085             }
0086         }
0087 
0088         auto output_names = f["output_names"].as_array();
0089         if (output_names != nullptr) {
0090             for (const auto& output_name : *f["output_names"].as_array()) {
0091                 wiring->output_names.push_back(output_name.value<std::string>().value());
0092             }
0093         }
0094 
0095         auto input_levels = f["input_levels"].as_array();
0096         if (input_levels != nullptr) {
0097             for (const auto& input_level : *input_levels) {
0098                 wiring->input_levels.push_back(parseEventLevel(input_level.value<std::string>().value()));
0099             }
0100         }
0101 
0102         auto configs = f["configs"].as_table();
0103         if (configs != nullptr) {
0104             for (const auto& config : *configs) {
0105                 std::string config_name(config.first);
0106                 // For now, parse all config values as strings. 
0107                 // Later we may go for a deeper integration with toml types and/or with JParameterManager.
0108                 wiring->configs[config_name] = config.second.value<std::string>().value();
0109             }
0110         }
0111 
0112         wirings.push_back(std::move(wiring));
0113     }
0114     AddWirings(wirings, source);
0115 }
0116 
0117 void JWiringService::AddWirings(const std::string& filename) {
0118     try {
0119         auto tbl = toml::parse_file(filename);
0120         AddWirings(tbl, filename);
0121     }
0122     catch (const toml::parse_error& err) {
0123         auto e = JException("Error parsing TOML file: '%s'", filename.c_str());
0124         e.nested_exception = std::current_exception();
0125         throw e;
0126     }
0127 }
0128 
0129 const JWiringService::Wiring* JWiringService::GetWiring(const std::string& prefix) const {
0130     auto it = m_wirings_from_prefix.find(prefix);
0131     if (it == m_wirings_from_prefix.end()) {
0132         return nullptr;
0133     }
0134     return it->second;
0135 }
0136 
0137 const std::vector<JWiringService::Wiring*>&
0138 JWiringService::GetWirings(const std::string& plugin_name, const std::string& type_name) const {
0139 
0140     auto it = m_wirings_from_type_and_plugin_names.find({type_name, plugin_name});
0141     if (it == m_wirings_from_type_and_plugin_names.end()) {
0142         return m_no_wirings;
0143     }
0144     return it->second;
0145 }
0146 
0147 
0148 void JWiringService::Overlay(Wiring& above, const Wiring& below) {
0149 
0150     // In theory this should be handled by the caller, but let's check just in case
0151     if (above.plugin_name != below.plugin_name) throw JException("Plugin name mismatch!");
0152     if (above.type_name != below.type_name) throw JException("Type name mismatch!");
0153 
0154     if (above.input_names.empty() && !below.input_names.empty()) {
0155         above.input_names = std::move(below.input_names);
0156     }
0157     if (above.input_levels.empty() && !below.input_levels.empty()) {
0158         above.input_levels = std::move(below.input_levels);
0159     }
0160     if (above.output_names.empty() && !below.output_names.empty()) {
0161         above.output_names = std::move(below.output_names);
0162     }
0163     for (const auto& [key, val] : below.configs) {
0164         if (above.configs.find(key) == above.configs.end()) {
0165             above.configs[key] = val;
0166         }
0167     }
0168 }
0169 
0170 
0171 
0172 } // namespace jana::services