623{
624
625 std::stringstream outputstream;
626
627 boost::write_graphviz(outputstream,
631
632
633
634
635
636
637
638 struct VertexStyle
639 {
640 const std::string shape;
641 const std::string color;
642 };
643
644 const std::unordered_map<ObjectKind, VertexStyle> vertex_styles{ {
ObjectKind::kSession, {
"octagon",
"black" } },
648
649 std::string dotfile_slurped = outputstream.str();
650 std::vector<std::string> legend_entries{
651 "legendGA [label=<<font color=\"black\"><b><i>⟶ Network Connection</i></b></font>>, shape=plaintext];",
652 "legendGB [label=<<font color=\"blue\"><b><i>⟶ Pub/Sub Network</i></b></font>>, shape=plaintext];"
653 };
654 std::vector<std::string> internal_legend_entries{
655 "legendGC [label=<<font color=\"green\"><b><i>⟶ Data Move Callback</i></b></font>>, shape=plaintext];",
656 "legendGD [label=<<font color=\"red\"><b><i>⟶ Queue</i></b></font>>, shape=plaintext];",
657 "legendGE [label=<<font color=\"orange\"><b><i>⟶ Queue w/ Source ID</i></b></font>>, shape=plaintext];"
658 };
659 bool internal_legend_added = false;
660 std::vector<std::string> legend_ordering_code{};
661
663
664 std::stringstream vertexstr{};
665 std::stringstream legendstr{};
666 std::stringstream labelstringstr{};
667 size_t insertion_location{ 0 };
668
669 auto calculate_insertion_location = [&]() {
670 labelstringstr << "label=\"" << eo.config_object.UID() << "\n";
671 insertion_location = dotfile_slurped.find(labelstringstr.str());
672 assert(insertion_location != std::string::npos);
673 return insertion_location;
674 };
675
676
677
678
679
680
681
682 auto add_vertex_info = [&]() {
683 vertexstr << "shape=" << vertex_styles.at(eo.kind).shape << ", color=" << vertex_styles.at(eo.kind).color
684 << ", fontcolor=" << vertex_styles.at(eo.kind).color << ", ";
685 dotfile_slurped.insert(calculate_insertion_location(), vertexstr.str());
686 };
687
688 auto add_legend_entry = [&](char letter, const std::string objkind) {
689 legendstr << "legend" << letter << " [label=<<font color=\"" << vertex_styles.at(eo.kind).color << "\"><b><i>"
690 << vertex_styles.at(eo.kind).color << ": " << objkind
691 << "</i></b></font>>, shape=plaintext, color=" << vertex_styles.at(eo.kind).color
692 << ", fontcolor=" << vertex_styles.at(eo.kind).color << "];";
693 };
694
695
696
697
698
699
700 switch (eo.kind) {
702 add_vertex_info();
703 add_legend_entry('A', "session");
704 break;
706 add_vertex_info();
707 add_legend_entry('B', "segment");
708 break;
710 add_vertex_info();
711 add_legend_entry('C', "application");
712 break;
714 add_vertex_info();
715 add_legend_entry('D', "DAQModule");
716 if (!internal_legend_added) {
717 legend_entries.insert(legend_entries.end(),
718 internal_legend_entries.begin(),
719 internal_legend_entries.end());
720 internal_legend_added = true;
721 }
722 break;
724 legendstr
725 << "legendE [label=<<font color=\"black\">O:<b><i> External Data Source</i></b></font>>, shape=plaintext];";
726 break;
728 legendstr
729 << "legendF [label=<<font color=\"black\">X:<b><i> External Data Sink</i></b></font>>, shape=plaintext];";
730 break;
731 default:
732 assert(false);
733 }
734
735 if (std::ranges::find(legend_entries, legendstr.str()) == legend_entries.end()) {
736 legend_entries.emplace_back(legendstr.str());
737 }
738 }
739
740 std::ranges::sort(legend_entries);
741
742
743
744
745
746
747
748
749
750
751
752
753 auto legend_tokens = legend_entries | std::views::transform([](const std::string& line) {
754 return line.substr(0, line.find(' '));
755 });
756
757 auto it = legend_tokens.begin();
758 for (auto next_it = std::next(it); next_it != legend_tokens.end(); ++it, ++next_it) {
759 std::stringstream astr{};
760 astr << " " << *it << " -> " << *next_it << " [style=invis];";
761 legend_ordering_code.push_back(astr.str());
762 }
763
764 constexpr int chars_to_last_brace = 2;
765 auto last_brace_iter = dotfile_slurped.end() - chars_to_last_brace;
766 assert(*last_brace_iter == '}');
767 size_t last_brace_loc = last_brace_iter - dotfile_slurped.begin();
768
769 std::string legend_code{};
770 legend_code += "\n\n\n";
771
772 for (const auto& l : legend_entries) {
773 legend_code +=
l +
"\n";
774 }
775
776 legend_code += "\n\n\n";
777
778 for (const auto& l : legend_ordering_code) {
779 legend_code +=
l +
"\n";
780 }
781
782 dotfile_slurped.insert(last_brace_loc, legend_code);
783
784
785
786
787
788
789 const std::string unlabeled_edge = "label=\"\"";
790 const std::string edge_modifier = ", style=\"dotted\", arrowhead=\"none\"";
791
792 size_t pos = 0;
793 while ((pos = dotfile_slurped.find(unlabeled_edge, pos)) != std::string::npos) {
794 dotfile_slurped.replace(pos, unlabeled_edge.length(), unlabeled_edge + edge_modifier);
795 pos += (unlabeled_edge + edge_modifier).length();
796 }
797
798
799 std::vector<std::pair<std::string, std::string>> connection_colors = { { "@NetworkConnection@kSendRecv\"", "\", color=black" },
800 { "@NetworkConnection@kPubSub\"", "\", color=blue" },
801 { "@QueueWithSourceId\"", "\", color=orange" },
802 { "@Queue\"", "\", color=red" },
803 { "@DataMoveCallbackConf\"",
804 "\", color=green" } };
805 for (auto& color_pair : connection_colors) {
806 auto conn_type = color_pair.first;
807 auto color_info = color_pair.second;
808 pos = 0;
809 while ((pos = dotfile_slurped.find(conn_type, pos)) != std::string::npos) {
810 dotfile_slurped.replace(pos, conn_type.length(), color_info);
811 pos += color_info.length();
812 }
813 }
814
815
816
817 std::ofstream outputfile;
818 outputfile.open(outputfilename);
819
820 if (outputfile.is_open()) {
821 outputfile << dotfile_slurped.c_str();
822 } else {
823 std::stringstream errmsg;
824 errmsg << "Unable to open requested file \"" << outputfilename << "\" for writing";
825 throw daqconf::GeneralGraphToolError(
ERS_HERE, errmsg.str());
826 }
827}
const std::string displaylabel
const std::string displaylabel