File indexing completed on 2026-05-21 08:39:02
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
0016
0017
0018
0019
0020 if (!m_wirings_input_file().empty()) {
0021 LOG_INFO(GetLogger()) << "JWiringService: Using wiring file: " << *m_wirings_input_file << LOG_END;
0022 ApplyWiringSet(ParseWiringSetFromFilename(*m_wirings_input_file));
0023 }
0024 else {
0025 LOG_INFO(GetLogger()) << "JWiringService: No wiring file used" << LOG_END;
0026 }
0027 }
0028
0029 void JWiringService::ApplyWiringSet(JWiringService::WiringSet&& wiring_set) {
0030
0031
0032 OverlayAllIncludes(wiring_set);
0033
0034
0035 m_wiring_set = std::move(wiring_set);
0036 m_added_prefixes.clear();
0037
0038
0039 for (auto& it: m_wiring_set.wirings) {
0040 if (it.second->action == Action::Add) {
0041 auto& wiring = it.second;
0042 m_added_prefixes[{wiring->type_name, wiring->plugin_name}].push_back(wiring->prefix);
0043 }
0044 }
0045 }
0046
0047 void JWiringService::OverlayAllIncludes(WiringSet &wiring_set) {
0048 for (auto& include_file_name : wiring_set.include_file_names) {
0049 auto include_wiring_set = ParseWiringSetFromFilename(include_file_name);
0050 OverlayAllIncludes(include_wiring_set);
0051 OverlayWiringSet(wiring_set, include_wiring_set);
0052 }
0053 }
0054
0055 std::unique_ptr<JWiringService::Wiring> ParseWiring(const toml::table& f) {
0056
0057 auto wiring = std::make_unique<JWiringService::Wiring>();
0058
0059 auto unparsed_action = f["action"].value<std::string>().value_or("(missing)");
0060 if (unparsed_action == "update") {
0061 wiring->action = JWiringService::Action::Update;
0062 }
0063 else if (unparsed_action == "add") {
0064 wiring->action = JWiringService::Action::Add;
0065 }
0066 else if (unparsed_action == "remove") {
0067 wiring->action = JWiringService::Action::Remove;
0068 }
0069 else {
0070 throw JException("JWiringService: Invalid action '%s'! Valid values are {'update', 'add', 'remove'}", unparsed_action.c_str());
0071 }
0072 wiring->plugin_name = f["plugin_name"].value<std::string>().value_or("");
0073 wiring->type_name = f["type_name"].value<std::string>().value();
0074 wiring->prefix = f["prefix"].value<std::string>().value();
0075 wiring->level = parseEventLevel(f["level"].value_or<std::string>("None"));
0076
0077 auto input_names = f["input_names"].as_array();
0078 if (input_names != nullptr) {
0079 if (wiring->action == JWiringService::Action::Remove) {
0080 throw JException("Removed wiring has superfluous input names");
0081 }
0082 for (const auto& input_name : *input_names) {
0083 wiring->input_names.push_back(input_name.value<std::string>().value());
0084 }
0085 }
0086
0087 auto variadic_input_names = f["variadic_input_names"].as_array();
0088 if (variadic_input_names != nullptr) {
0089 if (wiring->action == JWiringService::Action::Remove) {
0090 throw JException("Removed wiring has superfluous variadic input names");
0091 }
0092 for (const auto& input_name_vec : *variadic_input_names) {
0093 std::vector<std::string> temp;
0094 for (const auto& input_name : *(input_name_vec.as_array())) {
0095 temp.push_back(input_name.as_string()->get());
0096 }
0097 wiring->variadic_input_names.push_back(temp);
0098 }
0099 }
0100
0101 auto input_levels = f["input_levels"].as_array();
0102 if (input_levels != nullptr) {
0103 if (wiring->action == JWiringService::Action::Remove) {
0104 throw JException("Removed wiring has superfluous input levels");
0105 }
0106 for (const auto& input_level : *input_levels) {
0107 wiring->input_levels.push_back(parseEventLevel(input_level.value<std::string>().value()));
0108 }
0109 }
0110
0111 auto variadic_input_levels = f["variadic_input_levels"].as_array();
0112 if (variadic_input_levels != nullptr) {
0113 if (wiring->action == JWiringService::Action::Remove) {
0114 throw JException("Removed wiring has superfluous variadic input levels");
0115 }
0116 for (const auto& input_level : *variadic_input_levels) {
0117 wiring->variadic_input_levels.push_back(parseEventLevel(input_level.value<std::string>().value()));
0118 }
0119 }
0120
0121 auto output_names = f["output_names"].as_array();
0122 if (output_names != nullptr) {
0123 if (wiring->action == JWiringService::Action::Remove) {
0124 throw JException("Removed wiring has superfluous output names");
0125 }
0126 for (const auto& output_name : *f["output_names"].as_array()) {
0127 wiring->output_names.push_back(output_name.value<std::string>().value());
0128 }
0129 }
0130
0131 auto variadic_output_names = f["variadic_output_names"].as_array();
0132 if (variadic_output_names != nullptr) {
0133 if (wiring->action == JWiringService::Action::Remove) {
0134 throw JException("Removed wiring has superfluous variadic output names");
0135 }
0136 for (const auto& output_name_vec : *variadic_output_names) {
0137 std::vector<std::string> temp;
0138 for (const auto& output_name : *(output_name_vec.as_array())) {
0139 temp.push_back(output_name.as_string()->get());
0140 }
0141 wiring->variadic_output_names.push_back(temp);
0142 }
0143 }
0144
0145 auto configs = f["configs"].as_table();
0146 if (configs != nullptr) {
0147 for (const auto& config : *configs) {
0148 std::string config_name(config.first);
0149
0150
0151 wiring->configs[config_name] = config.second.value<std::string>().value();
0152 }
0153 }
0154
0155 return wiring;
0156 }
0157
0158
0159 JWiringService::WiringSet JWiringService::ParseWiringSet(const toml::table& table) {
0160
0161 WiringSet wiring_set;
0162
0163
0164 auto includes = table["includes"].as_array();
0165 if (includes != nullptr) {
0166 for (const auto& include : *includes) {
0167 wiring_set.include_file_names.push_back(include.as<std::string>()->get());
0168 }
0169 }
0170
0171
0172 auto plugins = table["plugins"].as_array();
0173 if (plugins != nullptr) {
0174 for (const auto& plugin_name : *plugins) {
0175 wiring_set.plugin_names.push_back(plugin_name.as<std::string>()->get());
0176 }
0177 }
0178
0179
0180 auto shared_params = table["configs"].as_table();
0181 if (shared_params != nullptr) {
0182 for (const auto& param : *shared_params) {
0183 std::string key(param.first);
0184 std::string val = *param.second.value<std::string>();
0185 wiring_set.shared_parameters[key] = val;
0186 }
0187 }
0188
0189
0190 auto use_short_names = table["use_short_names"].as<bool>();
0191 if (use_short_names != nullptr) {
0192 wiring_set.use_short_names = use_short_names->get();
0193 }
0194
0195
0196 auto wirings_array = table["wiring"].as_array();
0197 if (wirings_array != nullptr) {
0198 for (const auto& wiring_node : *wirings_array) {
0199 auto wiring_table = wiring_node.as_table();
0200 if (wiring_table != nullptr) {
0201 auto wiring = ParseWiring(*wiring_table);
0202
0203
0204 auto it = wiring_set.wirings.find(wiring->prefix);
0205 if (it != wiring_set.wirings.end()) {
0206 throw JException("Duplicated prefix '%s' in wiring set", wiring->prefix.c_str());
0207 }
0208
0209
0210 wiring_set.wirings[wiring->prefix] = std::move(wiring);
0211 }
0212 }
0213 }
0214
0215 return wiring_set;
0216 }
0217
0218 JWiringService::WiringSet JWiringService::ParseWiringSetFromFilename(const std::string& filename) {
0219 try {
0220 auto table = toml::parse_file(filename);
0221 return ParseWiringSet(table);
0222 }
0223 catch (const toml::parse_error& err) {
0224 auto e = JException("Error parsing TOML file: '%s'", filename.c_str());
0225 e.nested_exception = std::current_exception();
0226 throw e;
0227 }
0228 }
0229
0230 JWiringService::Wiring* JWiringService::GetWiring(const std::string& prefix) const {
0231 auto it = m_wiring_set.wirings.find(prefix);
0232 if (it == m_wiring_set.wirings.end()) {
0233 return nullptr;
0234 }
0235 return it->second.get();
0236 }
0237
0238 const std::vector<std::string>&
0239 JWiringService::GetPrefixesForAddedInstances(const std::string& plugin_name, const std::string& type_name) const {
0240
0241 auto it = m_added_prefixes.find({type_name, plugin_name});
0242 if (it == m_added_prefixes.end()) {
0243 return m_no_added_prefixes;
0244 }
0245 return it->second;
0246 }
0247
0248 void JWiringService::OverlayWiringSet(WiringSet &above, const WiringSet &below) {
0249
0250
0251
0252
0253 for (auto& below_it: below.wirings) {
0254 auto& below_wiring = below_it.second;
0255 auto& prefix = below_wiring->prefix;
0256 const auto& above_it = above.wirings.find(prefix);
0257 if (above_it == above.wirings.end()) {
0258
0259 above.wirings[prefix] = std::make_unique<Wiring>(*below_wiring);
0260 }
0261 else {
0262
0263 auto& above_wiring = above_it->second;
0264
0265
0266 if (above_wiring->type_name != below_wiring->type_name) {
0267 throw JException("Wiring mismatch: type name '%s' vs '%s'",
0268 above_wiring->type_name.c_str(), below_wiring->type_name.c_str());
0269 }
0270 if (above_wiring->plugin_name != below_wiring->plugin_name) {
0271 throw JException("Wiring mismatch: plugin name '%s' vs '%s'",
0272 above_wiring->plugin_name.c_str(), below_wiring->plugin_name.c_str());
0273 }
0274
0275
0276 Overlay(*above_wiring, *below_wiring);
0277 }
0278 }
0279 }
0280
0281 void JWiringService::Overlay(Wiring& above, const Wiring& below) {
0282
0283
0284 if (above.plugin_name != below.plugin_name) throw JException("Plugin name mismatch!");
0285 if (above.type_name != below.type_name) throw JException("Type name mismatch!");
0286
0287
0288 if (above.action == Action::Add) {
0289 if (below.action != Action::Remove) {
0290 throw JException("Attempted to add a wiring that is already present. plugin=%s, prefix=%s",
0291 above.plugin_name.c_str(), above.prefix.c_str());
0292 }
0293 else {
0294
0295 above.action = Action::Update;
0296 }
0297 }
0298 else if (above.action == Action::Update) {
0299 if (below.action == Action::Add) {
0300 above.action = Action::Add;
0301 }
0302 else if (below.action == Action::Remove) {
0303 throw JException("Attempted to update a wiring that has been removed. plugin=%s, prefix=%s",
0304 above.plugin_name.c_str(), above.prefix.c_str());
0305 }
0306
0307 }
0308 else if (above.action == Action::Remove) {
0309 if (below.action == Action::Remove) {
0310 throw JException("Attempted to remove a wiring that has already been removed. plugin=%s, prefix=%s",
0311 above.plugin_name.c_str(), above.prefix.c_str());
0312 }
0313
0314
0315 }
0316
0317 if (above.input_names.empty() && !below.input_names.empty()) {
0318 above.input_names = std::move(below.input_names);
0319 }
0320 if (above.input_levels.empty() && !below.input_levels.empty()) {
0321 above.input_levels = std::move(below.input_levels);
0322 }
0323 if (above.output_names.empty() && !below.output_names.empty()) {
0324 above.output_names = std::move(below.output_names);
0325 }
0326 for (const auto& [key, val] : below.configs) {
0327 if (above.configs.find(key) == above.configs.end()) {
0328 above.configs[key] = val;
0329 }
0330 }
0331 }
0332
0333 const std::map<std::string, std::string>& JWiringService::GetSharedParameters() const {
0334 return m_wiring_set.shared_parameters;
0335 }
0336
0337 void JWiringService::CheckAllWiringsAreUsed() {
0338 std::vector<JWiringService::Wiring*> m_unused_wirings;
0339 for (const auto& wiring : m_wiring_set.wirings) {
0340 if (wiring.second->is_used == false) {
0341 m_unused_wirings.push_back(wiring.second.get());
0342 }
0343 }
0344 if (m_unused_wirings.size() != 0) {
0345 LOG_ERROR(GetLogger()) << "Wirings were found but never used:";
0346 for (auto wiring : m_unused_wirings) {
0347 LOG_ERROR(GetLogger()) << " " << wiring->type_name << " " << wiring->prefix;
0348 }
0349 throw JException("Unused wirings found");
0350 }
0351 }
0352
0353 }