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
0017
0018
0019
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
0029 OverlayAllIncludes(wiring_set);
0030
0031
0032 m_wiring_set = std::move(wiring_set);
0033 m_added_prefixes.clear();
0034
0035
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
0147
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
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
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
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
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
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
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
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
0248
0249
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
0256 above.wirings[prefix] = std::make_unique<Wiring>(*below_wiring);
0257 }
0258 else {
0259
0260 auto& above_wiring = above_it->second;
0261
0262
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
0273 Overlay(*above_wiring, *below_wiring);
0274 }
0275 }
0276 }
0277
0278 void JWiringService::Overlay(Wiring& above, const Wiring& below) {
0279
0280
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
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
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
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
0311
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 }