Line data Source code
1 : #define TRACE_NAME "RoutingReceiver"
2 : #include "artdaq/DAQdata/Globals.hh"
3 :
4 : #include <arpa/inet.h>
5 : #include <netinet/in.h>
6 : #include <poll.h>
7 : #include <sys/socket.h>
8 : #include <sys/types.h>
9 : #include <chrono>
10 : #include <csignal>
11 : #include <thread>
12 : #include "artdaq/Application/LoadParameterSet.hh"
13 : #include "artdaq/DAQdata/TCPConnect.hh"
14 : #include "artdaq/DAQrate/detail/RoutingPacket.hh"
15 : #include "artdaq/DAQrate/detail/TableReceiver.hh"
16 : #include "canvas/Utilities/Exception.h"
17 : #include "fhiclcpp/types/Atom.h"
18 : #include "fhiclcpp/types/OptionalTable.h"
19 : #include "fhiclcpp/types/TableFragment.h"
20 : #include "proto/artdaqapp.hh"
21 :
22 : namespace artdaq {
23 : /**
24 : * \brief Class which receives routing tables and prints updates
25 : */
26 : struct RoutingReceiverConfig
27 : {
28 : /// "collection_time_ms": Time to collect routing table updates between printing summaries
29 : fhicl::Atom<size_t> collection_time_ms{fhicl::Name{"collection_time_ms"}, fhicl::Comment{"Time to collect routing table updates between printing summaries"}, 1000};
30 : /// "print_verbose_info" (Default: true): Print verbose information about each receiver detected in routing tables
31 : fhicl::Atom<bool> print_verbose_info{fhicl::Name{"print_verbose_info"}, fhicl::Comment{"Print verbose information about each receiver detected in routing tables"}, true};
32 : /// "graph_width": Width of the summary graph
33 : fhicl::Atom<size_t> graph_width{fhicl::Name{"graph_width"}, fhicl::Comment{"Width of the summary graph"}, 40};
34 : /// Configuration for the TableReceiver. See artdaq::TableReceiver::Config
35 : fhicl::OptionalTable<artdaq::TableReceiver::Config> routingTableConfig{fhicl::Name{"routing_table_config"}, fhicl::Comment{"Configuration for the TableReceiver"}};
36 : fhicl::TableFragment<artdaq::artdaqapp::Config> artdaqAppConfig; ///< Configuration for artdaq Application (BoardReader, etc)
37 : };
38 :
39 : } // namespace artdaq
40 :
41 : static bool sighandler_init = false;
42 : static bool should_stop = false;
43 0 : static void signal_handler(int signum)
44 : {
45 : // Messagefacility may already be gone at this point, TRACE ONLY!
46 : #if TRACE_REVNUM < 1459
47 : TRACE_STREAMER(TLVL_ERROR, &("routingReceiver")[0], 0, 0, 0)
48 : #else
49 0 : TRACE_STREAMER(TLVL_ERROR, TLOG2("routingReceiver", 0), 0)
50 : #endif
51 0 : << "A signal of type " << signum << " was caught by routingReceiver. Stopping receive loop!";
52 :
53 0 : should_stop = true;
54 :
55 : sigset_t set;
56 0 : pthread_sigmask(SIG_UNBLOCK, nullptr, &set);
57 0 : pthread_sigmask(SIG_UNBLOCK, &set, nullptr);
58 0 : }
59 :
60 0 : int main(int argc, char* argv[])
61 : try
62 : {
63 0 : artdaq::configureMessageFacility("RoutingReceiver", false, false);
64 : static std::mutex sighandler_mutex;
65 0 : std::unique_lock<std::mutex> lk(sighandler_mutex);
66 :
67 0 : if (!sighandler_init) //&& manager_id_ == 0) // ELF 3/22/18: Taking out manager_id_==0 requirement as I think kill(getpid()) is enough protection
68 : {
69 0 : sighandler_init = true;
70 0 : std::vector<int> signals = {SIGINT, SIGTERM, SIGUSR1, SIGUSR2}; // SIGQUIT is used by art in normal operation
71 0 : for (auto signal : signals)
72 : {
73 : struct sigaction old_action;
74 0 : sigaction(signal, nullptr, &old_action);
75 :
76 : // If the old handler wasn't SIG_IGN (it's a handler that just
77 : // "ignore" the signal)
78 0 : if (old_action.sa_handler != SIG_IGN) // NOLINT(cppcoreguidelines-pro-type-cstyle-cast)
79 : {
80 : struct sigaction action;
81 0 : action.sa_handler = signal_handler;
82 0 : sigemptyset(&action.sa_mask);
83 0 : for (auto sigblk : signals)
84 : {
85 0 : sigaddset(&action.sa_mask, sigblk);
86 : }
87 0 : action.sa_flags = 0;
88 :
89 : // Replace the signal handler of SIGINT with the one described by new_action
90 0 : sigaction(signal, &action, nullptr);
91 : }
92 : }
93 0 : }
94 :
95 0 : fhicl::ParameterSet init_ps = LoadParameterSet<artdaq::RoutingReceiverConfig>(argc, argv, "routingReceiver", "This application receives Routing Tables, and calculates statistics about the usage of the receivers");
96 0 : auto config_ps = init_ps.get<fhicl::ParameterSet>("daq", init_ps);
97 0 : auto metric_ps = config_ps.get<fhicl::ParameterSet>("metrics", config_ps);
98 0 : auto fr_ps = config_ps.get<fhicl::ParameterSet>("fragment_receiver", config_ps);
99 0 : auto rmConfig = fr_ps.get<fhicl::ParameterSet>("routing_table_config", fhicl::ParameterSet());
100 0 : artdaq::TableReceiver rr(rmConfig);
101 :
102 0 : auto host_map = artdaq::MakeHostMap(fr_ps);
103 :
104 0 : auto collection_time_ms = init_ps.get<size_t>("collection_time_ms", 1000);
105 0 : auto max_graph_width = init_ps.get<size_t>("max_graph_width", 100);
106 0 : bool print_verbose = init_ps.get<bool>("print_verbose_info", true);
107 0 : bool verbose_clear_screen = init_ps.get<bool>("clear_screen", true);
108 :
109 0 : auto blue = "\033[34m";
110 0 : auto cyan = "\033[36m";
111 0 : auto green = "\033[32m";
112 0 : auto yellow = "\033[93m";
113 0 : auto red = "\033[31m";
114 :
115 0 : metricMan->initialize(metric_ps, "RoutingReceiver");
116 0 : metricMan->do_start();
117 0 : if (print_verbose && verbose_clear_screen)
118 : {
119 0 : std::cout << "\033[2J";
120 : }
121 :
122 0 : std::map<int, int> receiver_table = std::map<int, int>();
123 :
124 0 : while (!should_stop)
125 : {
126 0 : auto start_time = std::chrono::steady_clock::now();
127 :
128 0 : auto this_table = rr.GetAndClearRoutingTable();
129 :
130 0 : if (!this_table.empty())
131 : {
132 0 : auto graph_width = this_table.size();
133 0 : auto n = 1; // n becomes entries per graph character
134 0 : auto graph_width_orig = graph_width;
135 0 : while (graph_width > max_graph_width)
136 : {
137 0 : n++;
138 0 : graph_width = graph_width_orig / n;
139 : }
140 :
141 0 : for (auto& entry : this_table)
142 : {
143 0 : receiver_table[entry.second]++;
144 : }
145 :
146 0 : auto average_entries_per_receiver = this_table.size() / receiver_table.size();
147 0 : auto offset = 2 * n; // Offset is 2 characters, in entries
148 :
149 0 : auto cyan_threshold = ((average_entries_per_receiver - offset) / 2) / n;
150 0 : auto green_threshold = (average_entries_per_receiver - offset) / n;
151 0 : auto yellow_threshold = (average_entries_per_receiver + offset) / n;
152 0 : auto red_threshold = (2 * average_entries_per_receiver) / n;
153 :
154 0 : TLOG(TLVL_TRACE) << "CT: " << cyan_threshold << ", GT: " << green_threshold << ", YT: " << yellow_threshold << ", RT: " << red_threshold;
155 :
156 0 : std::ostringstream report;
157 0 : std::ostringstream verbose_report;
158 :
159 0 : if (print_verbose && verbose_clear_screen)
160 : {
161 0 : std::cout << "\033[;H\033[J";
162 : }
163 :
164 0 : report << artdaq::TimeUtils::gettimeofday_us() << ": " << this_table.size() << " Entries, ";
165 0 : for (auto& receiver : receiver_table)
166 : {
167 0 : auto percent = static_cast<int>(receiver.second * 100 / this_table.size());
168 0 : report << receiver.first << ": " << receiver.second << " (" << percent << "%), ";
169 0 : if (print_verbose)
170 : {
171 0 : verbose_report << receiver.first << ": " << receiver.second << " (" << percent << "%)\t[";
172 :
173 0 : size_t graph_characters = receiver.second / n;
174 :
175 0 : for (size_t ii = 0; ii < graph_characters; ++ii)
176 : {
177 0 : if (ii < cyan_threshold)
178 : {
179 0 : verbose_report << blue;
180 : }
181 0 : else if (ii < green_threshold)
182 : {
183 0 : verbose_report << cyan;
184 : }
185 0 : else if (ii < yellow_threshold)
186 : {
187 0 : verbose_report << green;
188 : }
189 0 : else if (ii < red_threshold)
190 : {
191 0 : verbose_report << yellow;
192 : }
193 : else
194 : {
195 0 : verbose_report << red;
196 : }
197 0 : verbose_report << "|";
198 : }
199 0 : std::string spaces = std::string(graph_width - graph_characters, ' ');
200 0 : verbose_report << "\033[0m" << spaces << "]" << std::endl;
201 0 : }
202 0 : receiver.second = 0;
203 : }
204 0 : TLOG(TLVL_INFO) << report.str();
205 0 : std::cout << report.str() << std::endl;
206 0 : if (print_verbose)
207 : {
208 0 : std::cout << verbose_report.str() << std::endl;
209 : }
210 0 : }
211 0 : std::this_thread::sleep_until(start_time + std::chrono::milliseconds(collection_time_ms));
212 0 : }
213 :
214 0 : metricMan->do_stop();
215 0 : artdaq::Globals::CleanUpGlobals();
216 :
217 0 : return 0;
218 0 : }
219 0 : catch (...)
220 : {
221 0 : return -1;
222 0 : }
|