50 : m_oksfilename{ oksfilename }
53 { ObjectKind::kSegment, {
"Segment",
"Application" } },
54 { ObjectKind::kApplication, {
"Application",
"Module" } },
55 { ObjectKind::kModule, {
"Module" } } }
56 , m_root_object_kind{ ObjectKind::kUndefined }
57 , m_session{
nullptr }
58 , m_session_name{ sessionname }
66 TLOG() <<
"Failed to load OKS database: " << exc <<
"\n";
71 std::vector<ConfigObject> session_objects{};
73 m_confdb->get(
"Session", session_objects);
75 if (m_session_name ==
"") {
76 if (session_objects.size() == 1) {
77 m_session_name = session_objects[0].UID();
79 std::stringstream errmsg;
80 errmsg <<
"No Session instance name was provided, and since " << session_objects.size()
81 <<
" session instances were found in \"" << m_oksfilename <<
"\" this is an error";
83 throw daqconf::GeneralGraphToolError(
ERS_HERE, errmsg.str());
87 std::ranges::find_if(session_objects, [&](
const ConfigObject& obj) {
return obj.UID() == m_session_name; });
89 if (it == session_objects.end()) {
90 std::stringstream errmsg;
91 errmsg <<
"Did not find Session instance \"" << m_session_name <<
"\" in \"" << m_oksfilename
92 <<
"\" and its includes";
93 throw daqconf::GeneralGraphToolError(
ERS_HERE, errmsg.str());
115 std::stringstream errmsg;
116 errmsg <<
"Unable to get session with UID \"" << m_session_name <<
"\"";
117 throw daqconf::GeneralGraphToolError(
ERS_HERE, errmsg.str());
120 std::vector<ConfigObject> every_object_deriving_from_class{};
121 std::vector<ConfigObject> objects_of_class{};
125 auto classnames = m_confdb->superclasses() | std::views::keys |
126 std::views::transform([](
const auto& ptr_to_class_name) {
return *ptr_to_class_name; });
128 for (
const auto&
classname : classnames) {
130 every_object_deriving_from_class.clear();
131 objects_of_class.clear();
133 m_confdb->get(
classname, every_object_deriving_from_class);
135 std::ranges::copy_if(every_object_deriving_from_class,
136 std::back_inserter(objects_of_class),
139 std::ranges::copy(objects_of_class, std::back_inserter(m_all_objects));
141 if (
classname.find(
"Application") != std::string::npos) {
142 for (
const auto& appobj : objects_of_class) {
150 if (res && res->is_disabled(*m_session)) {
151 m_ignored_application_uids.push_back(appobj.UID());
152 TLOG() <<
"Skipping disabled application " << appobj.UID() <<
"@" << daqapp->class_name();
156 TLOG(TLVL_DEBUG) <<
"Skipping non-SmartDaqApplication " << appobj.UID() <<
"@" << appobj.class_name();
157 m_ignored_application_uids.push_back(appobj.UID());
182GraphBuilder::calculate_graph(
const std::string& root_obj_uid)
190 auto true_root_object_kind = m_root_object_kind;
191 m_root_object_kind = ObjectKind::kSession;
192 find_candidate_objects();
195 std::ranges::find_if(m_all_objects, [&](
const ConfigObject& obj) {
return obj.UID() == m_session_name; });
197 find_objects_and_connections(*it_session);
199 if (!m_objects_for_graph.contains(root_obj_uid)) {
200 std::stringstream errmsg;
201 errmsg <<
"Unable to find requested object \"" << root_obj_uid <<
"\" in session \"" << m_session_name <<
"\"";
202 throw daqconf::GeneralGraphToolError(
ERS_HERE, errmsg.str());
208 m_objects_for_graph.clear();
209 m_incoming_connections.clear();
210 m_outgoing_connections.clear();
212 m_candidate_objects.clear();
214 m_root_object_kind = true_root_object_kind;
215 find_candidate_objects();
218 for (
auto& obj : m_candidate_objects) {
219 if (obj.UID() == root_obj_uid) {
221 find_objects_and_connections(obj);
228 calculate_network_connections();
232GraphBuilder::calculate_network_connections()
239 std::vector<std::string> incoming_matched;
240 std::vector<std::string> outgoing_matched;
242 for (
auto& incoming : m_incoming_connections) {
244 std::regex incoming_pattern(incoming.first);
246 for (
auto& outgoing : m_outgoing_connections) {
248 std::regex outgoing_pattern(outgoing.first);
252 if (incoming.first == outgoing.first) {
254 }
else if (incoming.first.find(
".*") != std::string::npos) {
255 if (std::regex_match(outgoing.first, incoming_pattern)) {
258 }
else if (outgoing.first.find(
".*") != std::string::npos) {
259 if (std::regex_match(incoming.first, outgoing_pattern)) {
266 bool low_level_plot =
267 m_root_object_kind == ObjectKind::kApplication || m_root_object_kind == ObjectKind::kModule;
269 for (
auto& receiver : incoming.second) {
270 for (
auto& sender : outgoing.second) {
278 if (!m_objects_for_graph.contains(sender) || !m_objects_for_graph.contains(receiver)) {
280 }
else if (m_objects_for_graph.at(sender).kind != m_objects_for_graph.at(receiver).kind) {
282 }
else if (low_level_plot && incoming.first.find(
"NetworkConnection") != std::string::npos) {
289 if (!low_level_plot || incoming.first.find(
".*") == std::string::npos) {
290 if (std::ranges::find(incoming_matched, incoming.first) == incoming_matched.end()) {
291 incoming_matched.push_back(incoming.first);
295 if (!low_level_plot || outgoing.first.find(
".*") == std::string::npos) {
296 if (std::ranges::find(outgoing_matched, outgoing.first) == outgoing_matched.end()) {
297 outgoing_matched.push_back(outgoing.first);
303 auto res = std::ranges::find(m_objects_for_graph.at(sender).receiving_object_infos, receiving_info);
304 if (res == m_objects_for_graph.at(sender).receiving_object_infos.end()) {
305 m_objects_for_graph.at(sender).receiving_object_infos.push_back(receiving_info);
313 auto incoming_unmatched =
314 m_incoming_connections | std::views::keys | std::views::filter([&incoming_matched](
auto& connection) {
315 return std::ranges::find(incoming_matched, connection) == incoming_matched.end();
318 auto included_classes = m_included_classes.at(m_root_object_kind);
320 for (
auto& incoming_conn : incoming_unmatched) {
323 const std::string incoming_vertex_name = incoming_conn;
326 for (
auto& receiving_object_name : m_incoming_connections[incoming_conn]) {
328 if (!m_objects_for_graph.contains(receiving_object_name)) {
332 if (std::ranges::find(included_classes,
"Module") != included_classes.end()) {
333 if (m_objects_for_graph.at(receiving_object_name).kind == ObjectKind::kModule) {
334 external_obj.receiving_object_infos.push_back({ incoming_conn, receiving_object_name });
336 }
else if (std::ranges::find(included_classes,
"Application") != included_classes.end()) {
337 if (m_objects_for_graph.at(receiving_object_name).kind == ObjectKind::kApplication) {
338 external_obj.receiving_object_infos.push_back({ incoming_conn, receiving_object_name });
343 m_objects_for_graph.insert({ incoming_vertex_name, external_obj });
346 auto outgoing_unmatched =
347 m_outgoing_connections | std::views::keys | std::views::filter([&outgoing_matched](
auto& connection) {
348 return std::ranges::find(outgoing_matched, connection) == outgoing_matched.end();
351 for (
auto& outgoing_conn : outgoing_unmatched) {
354 const std::string outgoing_vertex_name = outgoing_conn;
357 for (
auto& sending_object_name : m_outgoing_connections[outgoing_conn]) {
359 if (!m_objects_for_graph.contains(sending_object_name)) {
363 if (std::ranges::find(included_classes,
"Module") != included_classes.end()) {
364 if (m_objects_for_graph.at(sending_object_name).kind == ObjectKind::kModule) {
365 m_objects_for_graph.at(sending_object_name)
366 .receiving_object_infos.push_back({ outgoing_conn, outgoing_vertex_name });
368 }
else if (std::ranges::find(included_classes,
"Application") != included_classes.end()) {
369 if (m_objects_for_graph.at(sending_object_name).kind == ObjectKind::kApplication) {
370 m_objects_for_graph.at(sending_object_name)
371 .receiving_object_infos.push_back({ outgoing_conn, outgoing_vertex_name });
376 m_objects_for_graph.insert({ outgoing_vertex_name, external_obj });
390 if (starting_object.kind == ObjectKind::kSession || starting_object.kind == ObjectKind::kSegment) {
392 for (
auto& child_object : find_child_objects(starting_object.config_object)) {
394 if (std::ranges::find(m_candidate_objects, child_object) != m_candidate_objects.end()) {
395 find_objects_and_connections(child_object);
396 starting_object.child_object_names.push_back(child_object.UID());
399 }
else if (starting_object.kind == ObjectKind::kApplication) {
412 TLOG() <<
"Failed to load OKS database: " << exc <<
"\n";
420 daqapp->generate_modules(local_session);
421 auto modules = daqapp->get_modules();
423 std::vector<std::string> allowed_conns{};
425 if (m_root_object_kind == ObjectKind::kSession || m_root_object_kind == ObjectKind::kSegment) {
426 allowed_conns = {
"NetworkConnection" };
427 }
else if (m_root_object_kind == ObjectKind::kApplication || m_root_object_kind == ObjectKind::kModule) {
428 allowed_conns = {
"NetworkConnection",
"Queue",
"QueueWithSourceId" };
431 for (
const auto& module : modules) {
433 for (
auto in : module->get_inputs()) {
439 const std::string key = in->config_object().UID() +
"@" + in->config_object().class_name();
441 if (std::ranges::find(allowed_conns, in->config_object().class_name()) != allowed_conns.end()) {
442 m_incoming_connections[key].push_back(
object.UID());
443 m_incoming_connections[key].push_back(module->UID());
447 for (
auto out : module->get_outputs()) {
449 const std::string key = out->config_object().UID() +
"@" + out->config_object().class_name();
451 if (std::ranges::find(allowed_conns, out->config_object().class_name()) != allowed_conns.end()) {
452 m_outgoing_connections[key].push_back(
object.UID());
453 m_outgoing_connections[key].push_back(module->UID());
457 if (std::ranges::find(m_included_classes.at(m_root_object_kind),
"Module") !=
458 m_included_classes.at(m_root_object_kind).end()) {
459 find_objects_and_connections(module->config_object());
460 starting_object.child_object_names.push_back(module->UID());
466 assert(!m_objects_for_graph.contains(
object.UID()));
468 m_objects_for_graph.insert({
object.UID(), starting_object });
472GraphBuilder::construct_graph(std::string root_obj_uid)
475 if (root_obj_uid ==
"") {
476 root_obj_uid = m_session_name;
481 auto class_names_view =
482 m_all_objects | std::views::filter([root_obj_uid](
const ConfigObject& obj) {
return obj.UID() == root_obj_uid; }) |
483 std::views::transform([](
const ConfigObject& obj) {
return obj.class_name(); });
485 if (std::ranges::distance(class_names_view) != 1) {
486 std::stringstream errmsg;
487 errmsg <<
"Failed to find instance of desired root object \"" << root_obj_uid <<
"\"";
488 throw daqconf::GeneralGraphToolError(
ERS_HERE, errmsg.str());
491 const std::string& root_obj_class_name = *class_names_view.begin();
495 calculate_graph(root_obj_uid);
497 for (
auto& enhanced_object : m_objects_for_graph | std::views::values) {
499 if (enhanced_object.kind == ObjectKind::kIncomingExternal) {
500 enhanced_object.vertex_in_graph = boost::add_vertex(
VertexLabel(
"O",
""), m_graph);
501 }
else if (enhanced_object.kind == ObjectKind::kOutgoingExternal) {
502 enhanced_object.vertex_in_graph = boost::add_vertex(
VertexLabel(
"X",
""), m_graph);
504 auto& obj = enhanced_object.config_object;
505 enhanced_object.vertex_in_graph = boost::add_vertex(
VertexLabel(obj.UID(), obj.class_name()), m_graph);
509 for (
auto& parent_obj : m_objects_for_graph | std::views::values) {
510 for (
auto& child_obj_name : parent_obj.child_object_names) {
511 boost::add_edge(parent_obj.vertex_in_graph,
512 m_objects_for_graph.at(child_obj_name).vertex_in_graph,
518 for (
auto& possible_sender_object : m_objects_for_graph | std::views::values) {
519 for (
auto& receiver_info : possible_sender_object.receiving_object_infos) {
521 auto at_pos = receiver_info.connection_name.find(
"@");
522 const std::string connection_label = receiver_info.connection_name.substr(0, at_pos);
529 if (m_root_object_kind == ObjectKind::kSession || m_root_object_kind == ObjectKind::kSegment) {
530 if (m_objects_for_graph.at(receiver_info.receiver_label).kind == ObjectKind::kModule) {
535 if (m_root_object_kind == ObjectKind::kApplication || m_root_object_kind == ObjectKind::kModule) {
536 if (m_objects_for_graph.at(receiver_info.receiver_label).kind == ObjectKind::kApplication) {
541 boost::add_edge(possible_sender_object.vertex_in_graph,
542 m_objects_for_graph.at(receiver_info.receiver_label).vertex_in_graph,
543 { connection_label },
583GraphBuilder::write_graph(
const std::string& outputfilename)
const
586 std::stringstream outputstream;
588 boost::write_graphviz(outputstream,
590 boost::make_label_writer(boost::get(&GraphBuilder::VertexLabel::displaylabel, m_graph)),
591 boost::make_label_writer(boost::get(&GraphBuilder::EdgeLabel::displaylabel, m_graph)));
601 const std::string shape;
602 const std::string color;
605 const std::unordered_map<ObjectKind, VertexStyle> vertex_styles{ { ObjectKind::kSession, {
"octagon",
"black" } },
606 { ObjectKind::kSegment, {
"hexagon",
"brown" } },
607 { ObjectKind::kApplication, {
"pentagon",
"blue" } },
608 { ObjectKind::kModule, {
"rectangle",
"red" } } };
610 std::string dotfile_slurped = outputstream.str();
611 std::vector<std::string> legend_entries{};
612 std::vector<std::string> legend_ordering_code{};
614 for (
auto& eo : m_objects_for_graph | std::views::values) {
616 std::stringstream vertexstr{};
617 std::stringstream legendstr{};
618 std::stringstream labelstringstr{};
619 size_t insertion_location{ 0 };
621 auto calculate_insertion_location = [&]() {
622 labelstringstr <<
"label=\"" << eo.config_object.UID() <<
"\n";
623 insertion_location = dotfile_slurped.find(labelstringstr.str());
624 assert(insertion_location != std::string::npos);
625 return insertion_location;
634 auto add_vertex_info = [&]() {
635 vertexstr <<
"shape=" << vertex_styles.at(eo.kind).shape <<
", color=" << vertex_styles.at(eo.kind).color
636 <<
", fontcolor=" << vertex_styles.at(eo.kind).color <<
", ";
637 dotfile_slurped.insert(calculate_insertion_location(), vertexstr.str());
640 auto add_legend_entry = [&](
char letter,
const std::string objkind) {
641 legendstr <<
"legend" << letter <<
" [label=<<font color=\"" << vertex_styles.at(eo.kind).color <<
"\"><b><i>"
642 << vertex_styles.at(eo.kind).color <<
": " << objkind
643 <<
"</i></b></font>>, shape=plaintext, color=" << vertex_styles.at(eo.kind).color
644 <<
", fontcolor=" << vertex_styles.at(eo.kind).color <<
"];";
653 case ObjectKind::kSession:
655 add_legend_entry(
'A',
"session");
657 case ObjectKind::kSegment:
659 add_legend_entry(
'B',
"segment");
661 case ObjectKind::kApplication:
663 add_legend_entry(
'C',
"application");
665 case ObjectKind::kModule:
667 add_legend_entry(
'D',
"DAQModule");
669 case ObjectKind::kIncomingExternal:
671 <<
"legendE [label=<<font color=\"black\">O:<b><i> External Data Source</i></b></font>>, shape=plaintext];";
673 case ObjectKind::kOutgoingExternal:
675 <<
"legendF [label=<<font color=\"black\">X:<b><i> External Data Sink</i></b></font>>, shape=plaintext];";
681 if (std::ranges::find(legend_entries, legendstr.str()) == legend_entries.end()) {
682 legend_entries.emplace_back(legendstr.str());
686 std::ranges::sort(legend_entries);
699 auto legend_tokens = legend_entries | std::views::transform([](
const std::string& line) {
700 return line.substr(0, line.find(
' '));
703 auto it = legend_tokens.begin();
704 for (
auto next_it = std::next(it); next_it != legend_tokens.end(); ++it, ++next_it) {
705 std::stringstream astr{};
706 astr <<
" " << *it <<
" -> " << *next_it <<
" [style=invis];";
707 legend_ordering_code.push_back(astr.str());
710 constexpr int chars_to_last_brace = 2;
711 auto last_brace_iter = dotfile_slurped.end() - chars_to_last_brace;
712 assert(*last_brace_iter ==
'}');
713 size_t last_brace_loc = last_brace_iter - dotfile_slurped.begin();
715 std::string legend_code{};
716 legend_code +=
"\n\n\n";
718 for (
const auto& l : legend_entries) {
719 legend_code += l +
"\n";
722 legend_code +=
"\n\n\n";
724 for (
const auto& l : legend_ordering_code) {
725 legend_code += l +
"\n";
728 dotfile_slurped.insert(last_brace_loc, legend_code);
735 const std::string unlabeled_edge =
"label=\"\"";
736 const std::string edge_modifier =
", style=\"dotted\", arrowhead=\"none\"";
739 while ((pos = dotfile_slurped.find(unlabeled_edge, pos)) != std::string::npos) {
740 dotfile_slurped.replace(pos, unlabeled_edge.length(), unlabeled_edge + edge_modifier);
741 pos += (unlabeled_edge + edge_modifier).length();
746 std::ofstream outputfile;
747 outputfile.open(outputfilename);
749 if (outputfile.is_open()) {
750 outputfile << dotfile_slurped.c_str();
752 std::stringstream errmsg;
753 errmsg <<
"Unable to open requested file \"" << outputfilename <<
"\" for writing";
754 throw daqconf::GeneralGraphToolError(
ERS_HERE, errmsg.str());