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->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 auto modules = daqapp->generate_modules(local_session);
422 std::vector<std::string> allowed_conns{};
424 if (m_root_object_kind == ObjectKind::kSession || m_root_object_kind == ObjectKind::kSegment) {
425 allowed_conns = {
"NetworkConnection" };
426 }
else if (m_root_object_kind == ObjectKind::kApplication || m_root_object_kind == ObjectKind::kModule) {
427 allowed_conns = {
"NetworkConnection",
"Queue",
"QueueWithSourceId" };
430 for (
const auto& module : modules) {
432 for (
auto in : module->get_inputs()) {
438 const std::string key = in->config_object().UID() +
"@" + in->config_object().class_name();
440 if (std::ranges::find(allowed_conns, in->config_object().class_name()) != allowed_conns.end()) {
441 m_incoming_connections[key].push_back(
object.UID());
442 m_incoming_connections[key].push_back(module->UID());
446 for (
auto out : module->get_outputs()) {
448 const std::string key = out->config_object().UID() +
"@" + out->config_object().class_name();
450 if (std::ranges::find(allowed_conns, out->config_object().class_name()) != allowed_conns.end()) {
451 m_outgoing_connections[key].push_back(
object.UID());
452 m_outgoing_connections[key].push_back(module->UID());
456 if (std::ranges::find(m_included_classes.at(m_root_object_kind),
"Module") !=
457 m_included_classes.at(m_root_object_kind).end()) {
458 find_objects_and_connections(module->config_object());
459 starting_object.child_object_names.push_back(module->UID());
465 assert(!m_objects_for_graph.contains(
object.UID()));
467 m_objects_for_graph.insert({
object.UID(), starting_object });
471GraphBuilder::construct_graph(std::string root_obj_uid)
474 if (root_obj_uid ==
"") {
475 root_obj_uid = m_session_name;
480 auto class_names_view =
481 m_all_objects | std::views::filter([root_obj_uid](
const ConfigObject& obj) {
return obj.UID() == root_obj_uid; }) |
482 std::views::transform([](
const ConfigObject& obj) {
return obj.class_name(); });
484 if (std::ranges::distance(class_names_view) != 1) {
485 std::stringstream errmsg;
486 errmsg <<
"Failed to find instance of desired root object \"" << root_obj_uid <<
"\"";
487 throw daqconf::GeneralGraphToolError(
ERS_HERE, errmsg.str());
490 const std::string& root_obj_class_name = *class_names_view.begin();
494 calculate_graph(root_obj_uid);
496 for (
auto& enhanced_object : m_objects_for_graph | std::views::values) {
498 if (enhanced_object.kind == ObjectKind::kIncomingExternal) {
499 enhanced_object.vertex_in_graph = boost::add_vertex(
VertexLabel(
"O",
""), m_graph);
500 }
else if (enhanced_object.kind == ObjectKind::kOutgoingExternal) {
501 enhanced_object.vertex_in_graph = boost::add_vertex(
VertexLabel(
"X",
""), m_graph);
503 auto& obj = enhanced_object.config_object;
504 enhanced_object.vertex_in_graph = boost::add_vertex(
VertexLabel(obj.UID(), obj.class_name()), m_graph);
508 for (
auto& parent_obj : m_objects_for_graph | std::views::values) {
509 for (
auto& child_obj_name : parent_obj.child_object_names) {
510 boost::add_edge(parent_obj.vertex_in_graph,
511 m_objects_for_graph.at(child_obj_name).vertex_in_graph,
517 for (
auto& possible_sender_object : m_objects_for_graph | std::views::values) {
518 for (
auto& receiver_info : possible_sender_object.receiving_object_infos) {
520 auto at_pos = receiver_info.connection_name.find(
"@");
521 const std::string connection_label = receiver_info.connection_name.substr(0, at_pos);
528 if (m_root_object_kind == ObjectKind::kSession || m_root_object_kind == ObjectKind::kSegment) {
529 if (m_objects_for_graph.at(receiver_info.receiver_label).kind == ObjectKind::kModule) {
534 if (m_root_object_kind == ObjectKind::kApplication || m_root_object_kind == ObjectKind::kModule) {
535 if (m_objects_for_graph.at(receiver_info.receiver_label).kind == ObjectKind::kApplication) {
540 boost::add_edge(possible_sender_object.vertex_in_graph,
541 m_objects_for_graph.at(receiver_info.receiver_label).vertex_in_graph,
542 { connection_label },
582GraphBuilder::write_graph(
const std::string& outputfilename)
const
585 std::stringstream outputstream;
587 boost::write_graphviz(outputstream,
589 boost::make_label_writer(boost::get(&GraphBuilder::VertexLabel::displaylabel, m_graph)),
590 boost::make_label_writer(boost::get(&GraphBuilder::EdgeLabel::displaylabel, m_graph)));
600 const std::string shape;
601 const std::string color;
604 const std::unordered_map<ObjectKind, VertexStyle> vertex_styles{ { ObjectKind::kSession, {
"octagon",
"black" } },
605 { ObjectKind::kSegment, {
"hexagon",
"brown" } },
606 { ObjectKind::kApplication, {
"pentagon",
"blue" } },
607 { ObjectKind::kModule, {
"rectangle",
"red" } } };
609 std::string dotfile_slurped = outputstream.str();
610 std::vector<std::string> legend_entries{};
611 std::vector<std::string> legend_ordering_code{};
613 for (
auto& eo : m_objects_for_graph | std::views::values) {
615 std::stringstream vertexstr{};
616 std::stringstream legendstr{};
617 std::stringstream labelstringstr{};
618 size_t insertion_location{ 0 };
620 auto calculate_insertion_location = [&]() {
621 labelstringstr <<
"label=\"" << eo.config_object.UID() <<
"\n";
622 insertion_location = dotfile_slurped.find(labelstringstr.str());
623 assert(insertion_location != std::string::npos);
624 return insertion_location;
633 auto add_vertex_info = [&]() {
634 vertexstr <<
"shape=" << vertex_styles.at(eo.kind).shape <<
", color=" << vertex_styles.at(eo.kind).color
635 <<
", fontcolor=" << vertex_styles.at(eo.kind).color <<
", ";
636 dotfile_slurped.insert(calculate_insertion_location(), vertexstr.str());
639 auto add_legend_entry = [&](
char letter,
const std::string objkind) {
640 legendstr <<
"legend" << letter <<
" [label=<<font color=\"" << vertex_styles.at(eo.kind).color <<
"\"><b><i>"
641 << vertex_styles.at(eo.kind).color <<
": " << objkind
642 <<
"</i></b></font>>, shape=plaintext, color=" << vertex_styles.at(eo.kind).color
643 <<
", fontcolor=" << vertex_styles.at(eo.kind).color <<
"];";
652 case ObjectKind::kSession:
654 add_legend_entry(
'A',
"session");
656 case ObjectKind::kSegment:
658 add_legend_entry(
'B',
"segment");
660 case ObjectKind::kApplication:
662 add_legend_entry(
'C',
"application");
664 case ObjectKind::kModule:
666 add_legend_entry(
'D',
"DAQModule");
668 case ObjectKind::kIncomingExternal:
670 <<
"legendE [label=<<font color=\"black\">O:<b><i> External Data Source</i></b></font>>, shape=plaintext];";
672 case ObjectKind::kOutgoingExternal:
674 <<
"legendF [label=<<font color=\"black\">X:<b><i> External Data Sink</i></b></font>>, shape=plaintext];";
680 if (std::ranges::find(legend_entries, legendstr.str()) == legend_entries.end()) {
681 legend_entries.emplace_back(legendstr.str());
685 std::ranges::sort(legend_entries);
698 auto legend_tokens = legend_entries | std::views::transform([](
const std::string& line) {
699 return line.substr(0, line.find(
' '));
702 auto it = legend_tokens.begin();
703 for (
auto next_it = std::next(it); next_it != legend_tokens.end(); ++it, ++next_it) {
704 std::stringstream astr{};
705 astr <<
" " << *it <<
" -> " << *next_it <<
" [style=invis];";
706 legend_ordering_code.push_back(astr.str());
709 constexpr int chars_to_last_brace = 2;
710 auto last_brace_iter = dotfile_slurped.end() - chars_to_last_brace;
711 assert(*last_brace_iter ==
'}');
712 size_t last_brace_loc = last_brace_iter - dotfile_slurped.begin();
714 std::string legend_code{};
715 legend_code +=
"\n\n\n";
717 for (
const auto& l : legend_entries) {
718 legend_code += l +
"\n";
721 legend_code +=
"\n\n\n";
723 for (
const auto& l : legend_ordering_code) {
724 legend_code += l +
"\n";
727 dotfile_slurped.insert(last_brace_loc, legend_code);
734 const std::string unlabeled_edge =
"label=\"\"";
735 const std::string edge_modifier =
", style=\"dotted\", arrowhead=\"none\"";
738 while ((pos = dotfile_slurped.find(unlabeled_edge, pos)) != std::string::npos) {
739 dotfile_slurped.replace(pos, unlabeled_edge.length(), unlabeled_edge + edge_modifier);
740 pos += (unlabeled_edge + edge_modifier).length();
745 std::ofstream outputfile;
746 outputfile.open(outputfilename);
748 if (outputfile.is_open()) {
749 outputfile << dotfile_slurped.c_str();
751 std::stringstream errmsg;
752 errmsg <<
"Unable to open requested file \"" << outputfilename <<
"\" for writing";
753 throw daqconf::GeneralGraphToolError(
ERS_HERE, errmsg.str());