artdaq  v3_11_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[]) try
61 {
62  artdaq::configureMessageFacility("RoutingReceiver", false, false);
63  static std::mutex sighandler_mutex;
64  std::unique_lock<std::mutex> lk(sighandler_mutex);
65 
66  if (!sighandler_init) //&& manager_id_ == 0) // ELF 3/22/18: Taking out manager_id_==0 requirement as I think kill(getpid()) is enough protection
67  {
68  sighandler_init = true;
69  std::vector<int> signals = {SIGINT, SIGTERM, SIGUSR1, SIGUSR2}; // SIGQUIT is used by art in normal operation
70  for (auto signal : signals)
71  {
72  struct sigaction old_action;
73  sigaction(signal, nullptr, &old_action);
74 
75  //If the old handler wasn't SIG_IGN (it's a handler that just
76  // "ignore" the signal)
77  if (old_action.sa_handler != SIG_IGN) // NOLINT(cppcoreguidelines-pro-type-cstyle-cast)
78  {
79  struct sigaction action;
80  action.sa_handler = signal_handler;
81  sigemptyset(&action.sa_mask);
82  for (auto sigblk : signals)
83  {
84  sigaddset(&action.sa_mask, sigblk);
85  }
86  action.sa_flags = 0;
87 
88  //Replace the signal handler of SIGINT with the one described by new_action
89  sigaction(signal, &action, nullptr);
90  }
91  }
92  }
93 
94  fhicl::ParameterSet init_ps = LoadParameterSet<artdaq::RoutingReceiverConfig>(argc, argv, "routingReceiver", "This application receives Routing Tables, and calculates statistics about the usage of the receivers");
95  auto config_ps = init_ps.get<fhicl::ParameterSet>("daq", init_ps);
96  auto metric_ps = config_ps.get<fhicl::ParameterSet>("metrics", config_ps);
97  auto fr_ps = config_ps.get<fhicl::ParameterSet>("fragment_receiver", config_ps);
98  auto rmConfig = fr_ps.get<fhicl::ParameterSet>("routing_table_config", fhicl::ParameterSet());
99  artdaq::TableReceiver rr(rmConfig);
100 
101  auto host_map = artdaq::MakeHostMap(fr_ps);
102 
103  auto collection_time_ms = init_ps.get<size_t>("collection_time_ms", 1000);
104  auto max_graph_width = init_ps.get<size_t>("max_graph_width", 100);
105  bool print_verbose = init_ps.get<bool>("print_verbose_info", true);
106  bool verbose_clear_screen = init_ps.get<bool>("clear_screen", true);
107 
108  auto blue = "\033[34m";
109  auto cyan = "\033[36m";
110  auto green = "\033[32m";
111  auto yellow = "\033[93m";
112  auto red = "\033[31m";
113 
114  metricMan->initialize(metric_ps, "RoutingReceiver");
115  metricMan->do_start();
116  if (print_verbose && verbose_clear_screen)
117  {
118  std::cout << "\033[2J";
119  }
120 
121  std::map<int, int> receiver_table = std::map<int, int>();
122 
123  while (!should_stop)
124  {
125  auto start_time = std::chrono::steady_clock::now();
126 
127  auto this_table = rr.GetAndClearRoutingTable();
128 
129  if (!this_table.empty())
130  {
131  auto graph_width = this_table.size();
132  auto n = 1; // n becomes entries per graph character
133  auto graph_width_orig = graph_width;
134  while (graph_width > max_graph_width)
135  {
136  n++;
137  graph_width = graph_width_orig / n;
138  }
139 
140  for (auto& entry : this_table)
141  {
142  receiver_table[entry.second]++;
143  }
144 
145  auto average_entries_per_receiver = this_table.size() / receiver_table.size();
146  auto offset = 2 * n; // Offset is 2 characters, in entries
147 
148  auto cyan_threshold = ((average_entries_per_receiver - offset) / 2) / n;
149  auto green_threshold = (average_entries_per_receiver - offset) / n;
150  auto yellow_threshold = (average_entries_per_receiver + offset) / n;
151  auto red_threshold = (2 * average_entries_per_receiver) / n;
152 
153  TLOG(TLVL_TRACE) << "CT: " << cyan_threshold << ", GT: " << green_threshold << ", YT: " << yellow_threshold << ", RT: " << red_threshold;
154 
155  std::ostringstream report;
156  std::ostringstream verbose_report;
157 
158  if (print_verbose && verbose_clear_screen)
159  {
160  std::cout << "\033[;H\033[J";
161  }
162 
163  report << artdaq::TimeUtils::gettimeofday_us() << ": " << this_table.size() << " Entries, ";
164  for (auto& receiver : receiver_table)
165  {
166  auto percent = static_cast<int>(receiver.second * 100 / this_table.size());
167  report << receiver.first << ": " << receiver.second << " (" << percent << "%), ";
168  if (print_verbose)
169  {
170  verbose_report << receiver.first << ": " << receiver.second << " (" << percent << "%)\t[";
171 
172  size_t graph_characters = receiver.second / n;
173 
174  for (size_t ii = 0; ii < graph_characters; ++ii)
175  {
176  if (ii < cyan_threshold)
177  {
178  verbose_report << blue;
179  }
180  else if (ii < green_threshold)
181  {
182  verbose_report << cyan;
183  }
184  else if (ii < yellow_threshold)
185  {
186  verbose_report << green;
187  }
188  else if (ii < red_threshold)
189  {
190  verbose_report << yellow;
191  }
192  else
193  {
194  verbose_report << red;
195  }
196  verbose_report << "|";
197  }
198  std::string spaces = std::string(graph_width - graph_characters, ' ');
199  verbose_report << "\033[0m" << spaces << "]" << std::endl;
200  }
201  receiver.second = 0;
202  }
203  TLOG(TLVL_INFO) << report.str();
204  std::cout << report.str() << std::endl;
205  if (print_verbose)
206  {
207  std::cout << verbose_report.str() << std::endl;
208  }
209  }
210  std::this_thread::sleep_until(start_time + std::chrono::milliseconds(collection_time_ms));
211  }
212 
213  metricMan->do_stop();
215 
216  return 0;
217 }
218 catch (...)
219 {
220  return -1;
221 }
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:150
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:66
fhicl::Atom< bool > print_verbose_info
&quot;print_verbose_info&quot; (Default: true): Print verbose information about each receiver detected in routi...