Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-07-02 08:55:20

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         AddWiringFile(*m_wirings_input_file);
0023     }
0024 }
0025 
0026 void JWiringService::AddWirings(std::vector<std::unique_ptr<Wiring>>& wirings_bundle, const std::string& bundle_source) {
0027 
0028     std::set<std::string> prefixes_in_bundle;
0029     for (auto& wiring: wirings_bundle) {
0030 
0031         // Assert that this wiring's prefix is unique _within_ this bundle.
0032         auto bundle_it = prefixes_in_bundle.find(wiring->prefix);
0033         if (bundle_it != prefixes_in_bundle.end()) {
0034             throw JException("Duplicated prefix '%s' in wiring bundle '%s'", wiring->prefix.c_str(), bundle_source.c_str());
0035         }
0036         prefixes_in_bundle.insert(wiring->prefix);
0037 
0038         // Check whether we have seen this prefix before
0039         auto it = m_wirings_from_prefix.find(wiring->prefix);
0040         if (it == m_wirings_from_prefix.end()) {
0041             // This is a new wiring
0042             m_wirings_from_prefix[wiring->prefix] = wiring.get();
0043             m_wirings_from_type_and_plugin_names[{wiring->type_name, wiring->plugin_name}].push_back(wiring.get());
0044             m_wirings.push_back(std::move(wiring));
0045         }
0046         else {
0047             // Wiring is already defined; overlay this wiring _below_ the existing wiring
0048             // First we do some sanity checks
0049             if (wiring->type_name != it->second->type_name) {
0050                 throw JException("Wiring mismatch: type name '%s' vs '%s'", wiring->type_name.c_str(), it->second->type_name.c_str());
0051             }
0052             if (wiring->plugin_name != it->second->plugin_name) {
0053                 throw JException("Wiring mismatch: plugin name '%s' vs '%s'", wiring->plugin_name.c_str(), it->second->plugin_name.c_str());
0054             }
0055             Overlay(*(it->second), *wiring);
0056             // Useful information from `wiring` has been copied into `it->second`.
0057             // `wiring` will now be automatically destroyed
0058         }
0059     }
0060     // At this point all wirings have been moved out of wirings_bundle, so we clear it to avoid confusing callers
0061     wirings_bundle.clear();
0062 }
0063 
0064 void JWiringService::AddWirings(const toml::table& table, const std::string& source) {
0065 
0066     std::vector<std::unique_ptr<Wiring>> wirings;
0067     auto facs = table["factory"].as_array();
0068     if (facs == nullptr) {
0069         throw JException("No factories found!");
0070     }
0071     for (const auto& fac : *facs) {
0072         auto wiring = std::make_unique<Wiring>();
0073 
0074         if (fac.as_table() == nullptr) {
0075             throw JException("Invalid format: 'factory' is not a table");
0076         }
0077         auto& f = *fac.as_table();
0078 
0079         wiring->plugin_name = f["plugin_name"].value<std::string>().value_or("");
0080         wiring->type_name = f["type_name"].value<std::string>().value();
0081         wiring->prefix = f["prefix"].value<std::string>().value();
0082 
0083         wiring->level = parseEventLevel(f["level"].value_or<std::string>("None"));
0084 
0085         auto input_names = f["input_names"].as_array();
0086         if (input_names != nullptr) {
0087             for (const auto& input_name : *input_names) {
0088                 wiring->input_names.push_back(input_name.value<std::string>().value());
0089             }
0090         }
0091 
0092         auto variadic_input_names = f["variadic_input_names"].as_array();
0093         if (variadic_input_names != nullptr) {
0094             for (const auto& input_name_vec : *variadic_input_names) {
0095                 std::vector<std::string> temp;
0096                 for (const auto& input_name : *(input_name_vec.as_array())) {
0097                     temp.push_back(input_name.as_string()->get());
0098                 }
0099                 wiring->variadic_input_names.push_back(temp);
0100             }
0101         }
0102 
0103         auto input_levels = f["input_levels"].as_array();
0104         if (input_levels != nullptr) {
0105             for (const auto& input_level : *input_levels) {
0106                 wiring->input_levels.push_back(parseEventLevel(input_level.value<std::string>().value()));
0107             }
0108         }
0109 
0110         auto variadic_input_levels = f["variadic_input_levels"].as_array();
0111         if (variadic_input_levels != nullptr) {
0112             for (const auto& input_level : *variadic_input_levels) {
0113                 wiring->variadic_input_levels.push_back(parseEventLevel(input_level.value<std::string>().value()));
0114             }
0115         }
0116 
0117         auto output_names = f["output_names"].as_array();
0118         if (output_names != nullptr) {
0119             for (const auto& output_name : *f["output_names"].as_array()) {
0120                 wiring->output_names.push_back(output_name.value<std::string>().value());
0121             }
0122         }
0123 
0124         auto variadic_output_names = f["variadic_output_names"].as_array();
0125         if (variadic_output_names != nullptr) {
0126             for (const auto& output_name_vec : *variadic_output_names) {
0127                 std::vector<std::string> temp;
0128                 for (const auto& output_name : *(output_name_vec.as_array())) {
0129                     temp.push_back(output_name.as_string()->get());
0130                 }
0131                 wiring->variadic_output_names.push_back(temp);
0132             }
0133         }
0134 
0135 
0136         auto configs = f["configs"].as_table();
0137         if (configs != nullptr) {
0138             for (const auto& config : *configs) {
0139                 std::string config_name(config.first);
0140                 // For now, parse all config values as strings. 
0141                 // Later we may go for a deeper integration with toml types and/or with JParameterManager.
0142                 wiring->configs[config_name] = config.second.value<std::string>().value();
0143             }
0144         }
0145 
0146         wirings.push_back(std::move(wiring));
0147     }
0148     AddWirings(wirings, source);
0149 }
0150 
0151 void JWiringService::AddWiringFile(const std::string& filename) {
0152     try {
0153         auto tbl = toml::parse_file(filename);
0154         AddSharedParameters(tbl, filename);
0155         AddWirings(tbl, filename);
0156     }
0157     catch (const toml::parse_error& err) {
0158         auto e = JException("Error parsing TOML file: '%s'", filename.c_str());
0159         e.nested_exception = std::current_exception();
0160         throw e;
0161     }
0162 }
0163 
0164 const JWiringService::Wiring* JWiringService::GetWiring(const std::string& prefix) const {
0165     auto it = m_wirings_from_prefix.find(prefix);
0166     if (it == m_wirings_from_prefix.end()) {
0167         return nullptr;
0168     }
0169     return it->second;
0170 }
0171 
0172 const std::vector<JWiringService::Wiring*>&
0173 JWiringService::GetWirings(const std::string& plugin_name, const std::string& type_name) const {
0174 
0175     auto it = m_wirings_from_type_and_plugin_names.find({type_name, plugin_name});
0176     if (it == m_wirings_from_type_and_plugin_names.end()) {
0177         return m_no_wirings;
0178     }
0179     return it->second;
0180 }
0181 
0182 
0183 void JWiringService::Overlay(Wiring& above, const Wiring& below) {
0184 
0185     // In theory this should be handled by the caller, but let's check just in case
0186     if (above.plugin_name != below.plugin_name) throw JException("Plugin name mismatch!");
0187     if (above.type_name != below.type_name) throw JException("Type name mismatch!");
0188 
0189     if (above.input_names.empty() && !below.input_names.empty()) {
0190         above.input_names = std::move(below.input_names);
0191     }
0192     if (above.input_levels.empty() && !below.input_levels.empty()) {
0193         above.input_levels = std::move(below.input_levels);
0194     }
0195     if (above.output_names.empty() && !below.output_names.empty()) {
0196         above.output_names = std::move(below.output_names);
0197     }
0198     for (const auto& [key, val] : below.configs) {
0199         if (above.configs.find(key) == above.configs.end()) {
0200             above.configs[key] = val;
0201         }
0202     }
0203 }
0204 
0205 
0206 void JWiringService::AddSharedParameters(const toml::table& table, const std::string& /*source*/) {
0207     auto shared_params = table["configs"].as_table();
0208     if (shared_params == nullptr) {
0209         LOG_INFO(GetLogger()) << "No configs found!" << LOG_END;
0210         return;
0211     }
0212     for (const auto& param : *shared_params) {
0213         std::string key(param.first);
0214         std::string val = *param.second.value<std::string>();
0215         m_shared_parameters[key] = val;
0216     }
0217 }
0218 
0219 
0220 
0221 const std::map<std::string, std::string>& JWiringService::GetSharedParameters() const {
0222     return m_shared_parameters;
0223 }
0224 
0225 
0226 } // namespace jana::services