artdaq  v3_12_02
routingReceiver.cc
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"
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 {
27 {
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};
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};
33  fhicl::Atom<size_t> graph_width{fhicl::Name{"graph_width"}, fhicl::Comment{"Width of the summary graph"}, 40};
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;
37 };
38 
39 } // namespace artdaq
40 
41 static bool sighandler_init = false;
42 static bool should_stop = false;
43 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  TRACE_STREAMER(TLVL_ERROR, TLOG2("routingReceiver", 0), 0)
50 #endif
51  << "A signal of type " << signum << " was caught by routingReceiver. Stopping receive loop!";
52 
53  should_stop = true;
54 
55  sigset_t set;
56  pthread_sigmask(SIG_UNBLOCK, nullptr, &set);
57  pthread_sigmask(SIG_UNBLOCK, &set, nullptr);
58 }
59 
60 int main(int argc, char* argv[])
61 try
62 {
63  artdaq::configureMessageFacility("RoutingReceiver", false, false);
64  static std::mutex sighandler_mutex;
65  std::unique_lock<std::mutex> lk(sighandler_mutex);
66 
67  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  sighandler_init = true;
70  std::vector<int> signals = {SIGINT, SIGTERM, SIGUSR1, SIGUSR2}; // SIGQUIT is used by art in normal operation
71  for (auto signal : signals)
72  {
73  struct sigaction old_action;
74  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  if (old_action.sa_handler != SIG_IGN) // NOLINT(cppcoreguidelines-pro-type-cstyle-cast)
79  {
80  struct sigaction action;
81  action.sa_handler = signal_handler;
82  sigemptyset(&action.sa_mask);
83  for (auto sigblk : signals)
84  {
85  sigaddset(&action.sa_mask, sigblk);
86  }
87  action.sa_flags = 0;
88 
89  // Replace the signal handler of SIGINT with the one described by new_action
90  sigaction(signal, &action, nullptr);
91  }
92  }
93  }
94 
95  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  auto config_ps = init_ps.get<fhicl::ParameterSet>("daq", init_ps);
97  auto metric_ps = config_ps.get<fhicl::ParameterSet>("metrics", config_ps);
98  auto fr_ps = config_ps.get<fhicl::ParameterSet>("fragment_receiver", config_ps);
99  auto rmConfig = fr_ps.get<fhicl::ParameterSet>("routing_table_config", fhicl::ParameterSet());
100  artdaq::TableReceiver rr(rmConfig);
101 
102  auto host_map = artdaq::MakeHostMap(fr_ps);
103 
104  auto collection_time_ms = init_ps.get<size_t>("collection_time_ms", 1000);
105  auto max_graph_width = init_ps.get<size_t>("max_graph_width", 100);
106  bool print_verbose = init_ps.get<bool>("print_verbose_info", true);
107  bool verbose_clear_screen = init_ps.get<bool>("clear_screen", true);
108 
109  auto blue = "\033[34m";
110  auto cyan = "\033[36m";
111  auto green = "\033[32m";
112  auto yellow = "\033[93m";
113  auto red = "\033[31m";
114 
115  metricMan->initialize(metric_ps, "RoutingReceiver");
116  metricMan->do_start();
117  if (print_verbose && verbose_clear_screen)
118  {
119  std::cout << "\033[2J";
120  }
121 
122  std::map<int, int> receiver_table = std::map<int, int>();
123 
124  while (!should_stop)
125  {
126  auto start_time = std::chrono::steady_clock::now();
127 
128  auto this_table = rr.GetAndClearRoutingTable();
129 
130  if (!this_table.empty())
131  {
132  auto graph_width = this_table.size();
133  auto n = 1; // n becomes entries per graph character
134  auto graph_width_orig = graph_width;
135  while (graph_width > max_graph_width)
136  {
137  n++;
138  graph_width = graph_width_orig / n;
139  }
140 
141  for (auto& entry : this_table)
142  {
143  receiver_table[entry.second]++;
144  }
145 
146  auto average_entries_per_receiver = this_table.size() / receiver_table.size();
147  auto offset = 2 * n; // Offset is 2 characters, in entries
148 
149  auto cyan_threshold = ((average_entries_per_receiver - offset) / 2) / n;
150  auto green_threshold = (average_entries_per_receiver - offset) / n;
151  auto yellow_threshold = (average_entries_per_receiver + offset) / n;
152  auto red_threshold = (2 * average_entries_per_receiver) / n;
153 
154  TLOG(TLVL_TRACE) << "CT: " << cyan_threshold << ", GT: " << green_threshold << ", YT: " << yellow_threshold << ", RT: " << red_threshold;
155 
156  std::ostringstream report;
157  std::ostringstream verbose_report;
158 
159  if (print_verbose && verbose_clear_screen)
160  {
161  std::cout << "\033[;H\033[J";
162  }
163 
164  report << artdaq::TimeUtils::gettimeofday_us() << ": " << this_table.size() << " Entries, ";
165  for (auto& receiver : receiver_table)
166  {
167  auto percent = static_cast<int>(receiver.second * 100 / this_table.size());
168  report << receiver.first << ": " << receiver.second << " (" << percent << "%), ";
169  if (print_verbose)
170  {
171  verbose_report << receiver.first << ": " << receiver.second << " (" << percent << "%)\t[";
172 
173  size_t graph_characters = receiver.second / n;
174 
175  for (size_t ii = 0; ii < graph_characters; ++ii)
176  {
177  if (ii < cyan_threshold)
178  {
179  verbose_report << blue;
180  }
181  else if (ii < green_threshold)
182  {
183  verbose_report << cyan;
184  }
185  else if (ii < yellow_threshold)
186  {
187  verbose_report << green;
188  }
189  else if (ii < red_threshold)
190  {
191  verbose_report << yellow;
192  }
193  else
194  {
195  verbose_report << red;
196  }
197  verbose_report << "|";
198  }
199  std::string spaces = std::string(graph_width - graph_characters, ' ');
200  verbose_report << "\033[0m" << spaces << "]" << std::endl;
201  }
202  receiver.second = 0;
203  }
204  TLOG(TLVL_INFO) << report.str();
205  std::cout << report.str() << std::endl;
206  if (print_verbose)
207  {
208  std::cout << verbose_report.str() << std::endl;
209  }
210  }
211  std::this_thread::sleep_until(start_time + std::chrono::milliseconds(collection_time_ms));
212  }
213 
214  metricMan->do_stop();
216 
217  return 0;
218 }
219 catch (...)
220 {
221  return -1;
222 }
fhicl::Atom< size_t > collection_time_ms
&quot;collection_time_ms&quot;: Time to collect routing table updates between printing summaries ...
static void CleanUpGlobals()
Clean up statically-allocated Manager class instances.
Definition: Globals.hh:156
fhicl::TableFragment< artdaq::artdaqapp::Config > artdaqAppConfig
Configuration for artdaq Application (BoardReader, etc)
Class which receives routing tables and prints updates.
fhicl::Atom< size_t > graph_width
&quot;graph_width&quot;: Width of the summary graph
fhicl::OptionalTable< artdaq::TableReceiver::Config > routingTableConfig
Configuration for the TableReceiver. See artdaq::TableReceiver::Config.
Sends Fragment objects using TransferInterface plugins. Uses Routing Tables if confgiured, otherwise will Round-Robin Fragments to the destinations.
hostMap_t MakeHostMap(fhicl::ParameterSet const &pset, hostMap_t map=hostMap_t())
Make a hostMap_t from a HostMap::Config ParameterSet
Definition: HostMap.hh:68
fhicl::Atom< bool > print_verbose_info
&quot;print_verbose_info&quot; (Default: true): Print verbose information about each receiver detected in routi...