artdaq_mfextensions  v1_03_05
mvdlg.cc
1 #include <QMenu>
2 #include <QMessageBox>
3 #include <QProgressDialog>
4 #include <QScrollBar>
5 #include <QtGui>
6 
7 #include "cetlib/filepath_maker.h"
8 #include "fhiclcpp/ParameterSet.h"
9 #include "fhiclcpp/make_ParameterSet.h"
10 
11 #include "mfextensions/Binaries/mvdlg.hh"
12 
13 #if GCC_VERSION >= 701000 || defined(__clang__)
14 #pragma GCC diagnostic push
15 #pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
16 #endif
17 
18 #define TRACE_NAME "MessageViewer"
19 #include "trace.h"
20 
21 #if GCC_VERSION >= 701000 || defined(__clang__)
22 #pragma GCC diagnostic pop
23 #endif
24 
25 #include "mvdlg.hh"
26 
27 // replace the ${..} part in the filename with env variable
28 // throw if the env does not exist
29 static void process_fname(std::string& fname) {
30  size_t sub_start = fname.find("${");
31  size_t sub_end = fname.find("}");
32 
33  const size_t npos = std::string::npos;
34 
35  if ((sub_start == npos && sub_end != npos) || (sub_start != npos && sub_end == npos) || (sub_start > sub_end)) {
36  throw std::runtime_error("Unrecognized configuration file. Use default configuration instead.");
37  }
38 
39  if (sub_start == npos) return;
40 
41  std::string env = std::string(getenv(fname.substr(sub_start + 2, sub_end - sub_start - 2).c_str()));
42  fname.replace(sub_start, sub_end - sub_start + 1, env);
43 
44  // printf("%s\n", fname.c_str());
45 }
46 
47 static fhicl::ParameterSet readConf(std::string const& fname) {
48  if (fname.empty()) return fhicl::ParameterSet();
49 
50  std::string filename = fname;
51  process_fname(filename);
52 
53  std::string env("FHICL_FILE_PATH=");
54 
55  if (filename[0] == '/') {
56  env.append("/");
57  } else {
58  env.append(".");
59  }
60 
61  char* mfe_path = getenv("MFEXTENSIONS_DIR");
62  if (mfe_path) env.append(":").append(mfe_path).append("/config");
63 
64  putenv((char*)env.c_str());
65 
66  // printf("%s\n", env.c_str());
67 
68  cet::filepath_lookup policy("FHICL_FILE_PATH");
69 
70  // it throws when the file is not parsable
71  fhicl::ParameterSet pset;
72  fhicl::make_ParameterSet(filename, policy, pset);
73 
74  return pset;
75 }
76 
77 msgViewerDlg::msgViewerDlg(std::string const& conf, QDialog* parent)
78  : QDialog(parent),
79  paused(false),
80  shortMode_(false),
81  nMsgs(0),
82  nSupMsgs(0),
83  nThrMsgs(0),
84  nFilters(0),
85  nDeleted(0),
86  simpleRender(true),
87  searchStr(""),
88  msg_pool_(),
89  host_msgs_(),
90  cat_msgs_(),
91  app_msgs_(),
92  sup_menu(new QMenu(this)),
93  thr_menu(new QMenu(this)),
94  receivers_(readConf(conf).get<fhicl::ParameterSet>("receivers", fhicl::ParameterSet())) {
95  setupUi(this);
96 
97  // window geo settings
98  readSettings();
99 
100  // read configuration file
101  fhicl::ParameterSet pset = readConf(conf);
102 
103  // parse configuration file
104  parseConf(pset);
105 
106  // associate menu with push buttons
107  btnSuppression->setMenu(sup_menu);
108  btnThrottling->setMenu(thr_menu);
109 
110  // slots
111  connect(btnPause, SIGNAL(clicked()), this, SLOT(pause()));
112  connect(btnExit, SIGNAL(clicked()), this, SLOT(exit()));
113  connect(btnClear, SIGNAL(clicked()), this, SLOT(clear()));
114 
115  connect(btnRMode, SIGNAL(clicked()), this, SLOT(renderMode()));
116  connect(btnDisplayMode, SIGNAL(clicked()), this, SLOT(shortMode()));
117 
118  connect(btnSearch, SIGNAL(clicked()), this, SLOT(searchMsg()));
119  connect(btnSearchClear, SIGNAL(clicked()), this, SLOT(searchClear()));
120 
121  connect(btnFilter, SIGNAL(clicked()), this, SLOT(setFilter()));
122 
123  connect(btnError, SIGNAL(clicked()), this, SLOT(setSevError()));
124  connect(btnWarning, SIGNAL(clicked()), this, SLOT(setSevWarning()));
125  connect(btnInfo, SIGNAL(clicked()), this, SLOT(setSevInfo()));
126  connect(btnDebug, SIGNAL(clicked()), this, SLOT(setSevDebug()));
127 
128  connect(sup_menu, SIGNAL(triggered(QAction*)), this, SLOT(setSuppression(QAction*)));
129 
130  connect(thr_menu, SIGNAL(triggered(QAction*)), this, SLOT(setThrottling(QAction*)));
131 
132  connect(vsSeverity, SIGNAL(valueChanged(int)), this, SLOT(changeSeverity(int)));
133 
134  connect(&receivers_, SIGNAL(newMessage(qt_mf_msg const&)), this, SLOT(onNewMsg(qt_mf_msg const&)));
135 
136  connect(tabWidget, SIGNAL(currentChanged(int)), this, SLOT(tabWidgetCurrentChanged(int)));
137  connect(tabWidget, SIGNAL(tabCloseRequested(int)), this, SLOT(tabCloseRequested(int)));
138  MsgFilterDisplay allMessages;
139  allMessages.txtDisplay = txtMessages;
140  allMessages.nDisplayMsgs = 0;
141  allMessages.nDisplayedDeletedMsgs = 0;
142  allMessages.sevThresh = SINFO;
143  msgFilters_.push_back(allMessages);
144 
145  // https://stackoverflow.com/questions/2616483/close-button-only-for-some-tabs-in-qt
146  QTabBar* tabBar = tabWidget->findChild<QTabBar*>();
147  tabBar->setTabButton(0, QTabBar::RightSide, 0);
148  tabBar->setTabButton(0, QTabBar::LeftSide, 0);
149 
150  if (simpleRender)
151  btnRMode->setChecked(true);
152  else
153  btnRMode->setChecked(false);
154 
155  btnRMode->setEnabled(false);
156 
157  changeSeverity(SINFO);
158 
159  QTextDocument* doc = new QTextDocument(txtMessages);
160  txtMessages->setDocument(doc);
161 
162  receivers_.start();
163 }
164 
165 msgViewerDlg::~msgViewerDlg() {
166  receivers_.stop();
167  writeSettings();
168 }
169 
170 static void str_to_suppress(std::vector<std::string> const& vs, std::vector<suppress>& s, QMenu* menu) {
171  QAction* act;
172 
173  if (vs.empty()) {
174  act = menu->addAction("None");
175  act->setEnabled(false);
176  return;
177  }
178 
179  s.reserve(vs.size());
180 
181  for (size_t i = 0; i < vs.size(); ++i) {
182  s.push_back(suppress(vs[i]));
183  act = menu->addAction(QString(vs[i].c_str()));
184  act->setCheckable(true);
185  act->setChecked(true);
186  QVariant v = qVariantFromValue((void*)&s[i]);
187  act->setData(v);
188  }
189 }
190 
191 static void pset_to_throttle(std::vector<fhicl::ParameterSet> const& ps, std::vector<throttle>& t, QMenu* menu) {
192  QAction* act;
193 
194  if (ps.empty()) {
195  act = menu->addAction("None");
196  act->setEnabled(false);
197  return;
198  }
199 
200  t.reserve(ps.size());
201 
202  for (size_t i = 0; i < ps.size(); ++i) {
203  std::string name = ps[i].get<std::string>("name");
204  t.push_back(throttle(name, ps[i].get<int>("limit", -1), ps[i].get<long>("timespan", -1)));
205  act = menu->addAction(QString(name.c_str()));
206  act->setCheckable(true);
207  act->setChecked(true);
208  QVariant v = qVariantFromValue((void*)&t[i]);
209  act->setData(v);
210  }
211 }
212 
213 void msgViewerDlg::parseConf(fhicl::ParameterSet const& conf) {
214  fhicl::ParameterSet nulp;
215  // QAction * act;
216 
217  // suppression list
218  fhicl::ParameterSet sup = conf.get<fhicl::ParameterSet>("suppress", nulp);
219 
220  auto sup_host = sup.get<std::vector<std::string>>("hosts", std::vector<std::string>());
221  auto sup_app = sup.get<std::vector<std::string>>("applications", std::vector<std::string>());
222  auto sup_cat = sup.get<std::vector<std::string>>("categories", std::vector<std::string>());
223 
224  str_to_suppress(sup_host, e_sup_host, sup_menu);
225  sup_menu->addSeparator();
226 
227  str_to_suppress(sup_app, e_sup_app, sup_menu);
228  sup_menu->addSeparator();
229 
230  str_to_suppress(sup_cat, e_sup_cat, sup_menu);
231 
232  // throttling list
233  auto thr = conf.get<fhicl::ParameterSet>("throttle", nulp);
234 
235  auto thr_host = thr.get<std::vector<fhicl::ParameterSet>>("hosts", std::vector<fhicl::ParameterSet>());
236  auto thr_app = thr.get<std::vector<fhicl::ParameterSet>>("applications", std::vector<fhicl::ParameterSet>());
237  auto thr_cat = thr.get<std::vector<fhicl::ParameterSet>>("categories", std::vector<fhicl::ParameterSet>());
238 
239  pset_to_throttle(thr_host, e_thr_host, thr_menu);
240  thr_menu->addSeparator();
241 
242  pset_to_throttle(thr_app, e_thr_app, thr_menu);
243  thr_menu->addSeparator();
244 
245  pset_to_throttle(thr_cat, e_thr_cat, thr_menu);
246 
247  maxMsgs = conf.get<size_t>("max_message_buffer_size", 100000);
248  maxDeletedMsgs = conf.get<size_t>("max_displayed_deleted_messages", 100000);
249 }
250 
251 bool msgViewerDlg::msg_throttled(qt_mf_msg const& mfmsg) {
252  // suppression list
253 
254  ++nSupMsgs;
255 
256  for (size_t i = 0; i < e_sup_host.size(); ++i)
257  if (e_sup_host[i].match(mfmsg.host().toStdString())) return true;
258 
259  for (size_t i = 0; i < e_sup_app.size(); ++i)
260  if (e_sup_app[i].match(mfmsg.app().toStdString())) return true;
261 
262  for (size_t i = 0; i < e_sup_cat.size(); ++i)
263  if (e_sup_cat[i].match(mfmsg.cat().toStdString())) return true;
264 
265  --nSupMsgs;
266 
267  // throttling
268 
269  ++nThrMsgs;
270 
271  for (size_t i = 0; i < e_thr_host.size(); ++i)
272  if (e_thr_host[i].reach_limit(mfmsg.host().toStdString(), mfmsg.time())) return true;
273 
274  for (size_t i = 0; i < e_thr_app.size(); ++i)
275  if (e_thr_app[i].reach_limit(mfmsg.app().toStdString(), mfmsg.time())) return true;
276 
277  for (size_t i = 0; i < e_thr_cat.size(); ++i)
278  if (e_thr_cat[i].reach_limit(mfmsg.cat().toStdString(), mfmsg.time())) return true;
279 
280  --nThrMsgs;
281 
282  return false;
283 }
284 
285 void msgViewerDlg::writeSettings() {
286  QSettings settings("ARTDAQ", "MsgViewer");
287 
288  settings.beginGroup("MainWindow");
289  settings.setValue("size", size());
290  settings.setValue("pos", pos());
291  settings.endGroup();
292 }
293 
294 void msgViewerDlg::readSettings() {
295  QSettings settings("ARTDAQ", "MsgViewer");
296 
297  settings.beginGroup("MainWindow");
298  QPoint pos = settings.value("pos", QPoint(100, 100)).toPoint();
299  QSize size = settings.value("size", QSize(660, 760)).toSize();
300  resize(size);
301  move(pos);
302  settings.endGroup();
303 }
304 
305 void msgViewerDlg::onNewMsg(qt_mf_msg const& mfmsg) {
306  // 21-Aug-2015, KAB: copying the incrementing (and displaying) of the number
307  // of messages to here. I'm also not sure if we want to
308  // count all messages or just non-suppressed ones or what. But, at least this
309  // change gets the counter incrementing on the display.
310  ++nMsgs;
311  lcdMsgs->display(nMsgs);
312 
313  // test if the message is suppressed or throttled
314  if (msg_throttled(mfmsg)) {
315  lcdSuppressionCount->display(nSupMsgs);
316  lcdThrottlingCount->display(nThrMsgs);
317  return;
318  }
319 
320  // push the message to the message pool
321  msg_pool_.emplace_back(mfmsg);
322 
323  while (maxMsgs > 0 && msg_pool_.size() > maxMsgs) {
324  removeMsg(msg_pool_.begin());
325  }
326 
327  msgs_t::iterator it = --msg_pool_.end();
328 
329  // update corresponding lists of index
330  unsigned int flag = update_index(it);
331 
332  // update gui list
333  if (flag & LIST_APP) updateList<msg_iters_map_t>(lwApplication, app_msgs_);
334  if (flag & LIST_CAT) updateList<msg_iters_map_t>(lwCategory, cat_msgs_);
335  if (flag & LIST_HOST) updateList<msg_iters_map_t>(lwHost, host_msgs_);
336 
337  for (size_t d = 0; d < msgFilters_.size(); ++d) {
338  bool hostMatch =
339  msgFilters_[d].hostFilter.contains(it->host(), Qt::CaseInsensitive) || msgFilters_[d].hostFilter.size() == 0;
340  bool appMatch =
341  msgFilters_[d].appFilter.contains(it->app(), Qt::CaseInsensitive) || msgFilters_[d].appFilter.size() == 0;
342  bool catMatch =
343  msgFilters_[d].catFilter.contains(it->cat(), Qt::CaseInsensitive) || msgFilters_[d].catFilter.size() == 0;
344 
345  // Check to display the message
346  if (hostMatch && appMatch && catMatch) {
347  msgFilters_[d].msgs.push_back(it);
348  displayMsg(it, d);
349  }
350  }
351 }
352 
353 void msgViewerDlg::removeMsg(msgs_t::iterator it) {
354  std::unique_lock<std::mutex> lk(updating_mutex_);
355  QString const& app = it->app();
356  QString const& cat = it->cat();
357  QString const& host = it->host();
358 
359  auto catIter = std::find(cat_msgs_[cat].begin(), cat_msgs_[cat].end(), it);
360  auto hostIter = std::find(host_msgs_[host].begin(), host_msgs_[host].end(), it);
361  auto appIter = std::find(app_msgs_[app].begin(), app_msgs_[app].end(), it);
362  if (catIter != cat_msgs_[cat].end()) cat_msgs_[cat].erase(catIter);
363  if (hostIter != host_msgs_[host].end()) host_msgs_[host].erase(hostIter);
364  if (appIter != app_msgs_[app].end()) app_msgs_[app].erase(appIter);
365 
366  if (app_msgs_[app].size() == 0) {
367  app_msgs_.erase(app);
368  updateList<msg_iters_map_t>(lwApplication, app_msgs_);
369  }
370  if (cat_msgs_[cat].size() == 0) {
371  cat_msgs_.erase(cat);
372  updateList<msg_iters_map_t>(lwCategory, cat_msgs_);
373  }
374  if (host_msgs_[host].size() == 0) {
375  host_msgs_.erase(host);
376  updateList<msg_iters_map_t>(lwHost, host_msgs_);
377  }
378 
379  for (size_t d = 0; d < msgFilters_.size(); ++d) {
380  bool hostMatch =
381  msgFilters_[d].hostFilter.contains(it->host(), Qt::CaseInsensitive) || msgFilters_[d].hostFilter.size() == 0;
382  bool appMatch =
383  msgFilters_[d].appFilter.contains(it->app(), Qt::CaseInsensitive) || msgFilters_[d].appFilter.size() == 0;
384  bool catMatch =
385  msgFilters_[d].catFilter.contains(it->cat(), Qt::CaseInsensitive) || msgFilters_[d].catFilter.size() == 0;
386  bool sevMatch = it->sev() >= msgFilters_[d].sevThresh;
387 
388  // Check to display the message
389  if (hostMatch && appMatch && catMatch) {
390  auto filterIt = std::find(msgFilters_[d].msgs.begin(), msgFilters_[d].msgs.end(), it);
391  if (filterIt != msgFilters_[d].msgs.end()) msgFilters_[d].msgs.erase(filterIt);
392 
393  if (sevMatch) {
394  if (++msgFilters_[d].nDisplayedDeletedMsgs > static_cast<int>(maxDeletedMsgs) && maxDeletedMsgs > 0) {
395  lk.unlock();
396  displayMsg(d);
397  }
398  }
399  }
400  if ((int)d == tabWidget->currentIndex()) {
401  lcdDisplayedDeleted->display(msgFilters_[d].nDisplayedDeletedMsgs);
402  }
403  }
404 
405  msg_pool_.erase(it);
406  ++nDeleted;
407  lcdDeletedCount->display(nDeleted);
408 }
409 
410 unsigned int msgViewerDlg::update_index(msgs_t::iterator it) {
411  std::unique_lock<std::mutex> lk(updating_mutex_);
412  QString const& app = it->app();
413  QString const& cat = it->cat();
414  QString const& host = it->host();
415 
416  unsigned int update = 0x0;
417 
418  if (cat_msgs_.find(cat) == cat_msgs_.end()) update |= LIST_CAT;
419  if (host_msgs_.find(host) == host_msgs_.end()) update |= LIST_HOST;
420  if (app_msgs_.find(app) == app_msgs_.end()) update |= LIST_APP;
421 
422  cat_msgs_[cat].push_back(it);
423  host_msgs_[host].push_back(it);
424  app_msgs_[app].push_back(it);
425 
426  return update;
427 }
428 
429 void msgViewerDlg::displayMsg(msgs_t::const_iterator it, int display) {
430  if (it->sev() < msgFilters_[display].sevThresh) return;
431  std::unique_lock<std::mutex> lk(updating_mutex_);
432 
433  msgFilters_[display].nDisplayMsgs++;
434  if (display == tabWidget->currentIndex()) {
435  lcdDisplayedMsgs->display(msgFilters_[display].nDisplayMsgs);
436  }
437 
438  auto txt = it->text(shortMode_);
439  UpdateTextAreaDisplay(txt, msgFilters_[display].txtDisplay);
440 }
441 
442 void msgViewerDlg::displayMsg(int display) {
443  std::unique_lock<std::mutex> lk(updating_mutex_);
444  int n = 0;
445  msgFilters_[display].txtDisplay->clear();
446  msgFilters_[display].nDisplayMsgs = 0;
447  msgFilters_[display].nDisplayedDeletedMsgs = 0;
448 
449  msg_iters_t::const_iterator it;
450 
451  n = msgFilters_[display].msgs.size();
452  it = msgFilters_[display].msgs.begin();
453  QProgressDialog progress("Fetching data...", "Cancel", 0, n / 1000, this);
454 
455  progress.setWindowModality(Qt::WindowModal);
456  progress.setMinimumDuration(2000); // 2 seconds
457 
458  QString txt = "";
459  int i = 0, prog = 0;
460 
461  for (; it != msgFilters_[display].msgs.end(); ++it, ++i) {
462  if (it->get()->sev() >= msgFilters_[display].sevThresh) {
463  txt += it->get()->text(shortMode_);
464  ++msgFilters_[display].nDisplayMsgs;
465  }
466 
467  if (i == 1000) {
468  i = 0;
469  ++prog;
470  progress.setValue(prog);
471 
472  if (txt.length()) {
473  UpdateTextAreaDisplay(txt, msgFilters_[display].txtDisplay);
474  txt.clear();
475  }
476  }
477 
478  if (progress.wasCanceled()) break;
479  }
480 
481  if (display == tabWidget->currentIndex()) {
482  lcdDisplayedMsgs->display(msgFilters_[display].nDisplayMsgs);
483  }
484 
485  UpdateTextAreaDisplay(txt, msgFilters_[display].txtDisplay);
486 }
487 
488 // https://stackoverflow.com/questions/21955923/prevent-a-qtextedit-widget-from-scrolling-when-there-is-a-selection
489 void msgViewerDlg::UpdateTextAreaDisplay(QString text, QTextEdit* widget) {
490  const QTextCursor old_cursor = widget->textCursor();
491  const int old_scrollbar_value = widget->verticalScrollBar()->value();
492  const bool is_scrolled_down =
493  old_scrollbar_value >= widget->verticalScrollBar()->maximum() * 0.95; // At least 95% scrolled down
494 
495  // Insert the text at the position of the cursor (which is the end of the document).
496  widget->append(text);
497 
498  if (old_cursor.hasSelection() || !is_scrolled_down) {
499  // The user has selected text or scrolled away from the bottom: maintain position.
500  widget->setTextCursor(old_cursor);
501  widget->verticalScrollBar()->setValue(old_scrollbar_value);
502  } else {
503  // The user hasn't selected any text and the scrollbar is at the bottom: scroll to the bottom.
504  widget->moveCursor(QTextCursor::End);
505  widget->verticalScrollBar()->setValue(widget->verticalScrollBar()->maximum());
506  widget->horizontalScrollBar()->setValue(0);
507  }
508 }
509 
510 void msgViewerDlg::updateDisplays() {
511  for (size_t ii = 0; ii < msgFilters_.size(); ++ii) {
512  displayMsg(ii);
513  }
514 }
515 
516 template <typename M>
517 bool msgViewerDlg::updateList(QListWidget* lw, M const& map) {
518  bool nonSelectedBefore = (lw->currentRow() == -1);
519  bool nonSelectedAfter = true;
520 
521  QString item = nonSelectedBefore ? "" : lw->currentItem()->text();
522 
523  lw->clear();
524  int row = 0;
525  typename M::const_iterator it = map.begin();
526 
527  while (it != map.end()) {
528  lw->addItem(it->first);
529  if (!nonSelectedBefore && nonSelectedAfter) {
530  if (item == it->first) {
531  lw->setCurrentRow(row);
532  nonSelectedAfter = false;
533  }
534  }
535  ++row;
536  ++it;
537  }
538 
539  if (!nonSelectedBefore && nonSelectedAfter) return true;
540 
541  return false;
542 }
543 
544 msg_iters_t msgViewerDlg::list_intersect(msg_iters_t const& l1, msg_iters_t const& l2) {
545  msg_iters_t output;
546  msg_iters_t::const_iterator it1 = l1.begin();
547  msg_iters_t::const_iterator it2 = l2.begin();
548 
549  while (it1 != l1.end() && it2 != l2.end()) {
550  if (*it1 < *it2) {
551  ++it1;
552  } else if (*it2 < *it1) {
553  ++it2;
554  } else {
555  output.push_back(*it1);
556  ++it1;
557  ++it2;
558  }
559  }
560 
561  TLOG(10) << "list_intersect: output list has " << output.size() << " entries";
562  return output;
563 }
564 
565 std::string sev_to_string(sev_code_t s) {
566  switch (s) {
567  case SDEBUG:
568  return "DEBUG";
569  case SINFO:
570  return "INFO";
571  case SWARNING:
572  return "WARNING";
573  case SERROR:
574  return "ERROR";
575  }
576  return "UNKNOWN";
577 }
578 
579 void msgViewerDlg::setFilter() {
580  auto hostFilter = toQStringList(lwHost->selectedItems());
581  auto appFilter = toQStringList(lwApplication->selectedItems());
582  auto catFilter = toQStringList(lwCategory->selectedItems());
583 
584  lwHost->setCurrentRow(-1, QItemSelectionModel::Clear);
585  lwApplication->setCurrentRow(-1, QItemSelectionModel::Clear);
586  lwCategory->setCurrentRow(-1, QItemSelectionModel::Clear);
587 
588  if (hostFilter.isEmpty() && appFilter.isEmpty() && catFilter.isEmpty()) {
589  return;
590  }
591 
592  msg_iters_t result;
593  QString catFilterExpression = "";
594  QString hostFilterExpression = "";
595  QString appFilterExpression = "";
596  bool first = true;
597 
598  for (auto app = 0; app < appFilter.size(); ++app) { // app-sev index
599  msg_iters_map_t::const_iterator it = app_msgs_.find(appFilter[app]);
600  appFilterExpression += QString(first ? "" : " || ") + appFilter[app];
601  first = false;
602  if (it != app_msgs_.end()) {
603  msg_iters_t temp(it->second);
604  TLOG(10) << "setFilter: app " << appFilter[app].toStdString() << " has " << temp.size() << " messages";
605  result.merge(temp);
606  }
607  }
608  TLOG(10) << "setFilter: result contains %zu messages", result.size();
609 
610  first = true;
611  if (!hostFilter.isEmpty()) {
612  msg_iters_t hostResult;
613  for (auto host = 0; host < hostFilter.size(); ++host) { // host index
614  hostFilterExpression += QString(first ? "" : " || ") + hostFilter[host];
615  first = false;
616  msg_iters_map_t::const_iterator it = host_msgs_.find(hostFilter[host]);
617  if (it != host_msgs_.end()) {
618  msg_iters_t temp(it->second);
619  TLOG(10) << "setFilter: host " << hostFilter[host].toStdString() << " has " << temp.size() << " messages";
620  hostResult.merge(temp);
621  }
622  }
623  if (result.empty()) {
624  result = hostResult;
625  } else {
626  result = list_intersect(result, hostResult);
627  }
628  TLOG(10) << "setFilter: result contains " << result.size() << " messages";
629  }
630 
631  first = true;
632  if (!catFilter.isEmpty()) {
633  msg_iters_t catResult;
634  for (auto cat = 0; cat < catFilter.size(); ++cat) { // cat index
635  catFilterExpression += QString(first ? "" : " || ") + catFilter[cat];
636  first = false;
637  msg_iters_map_t::const_iterator it = cat_msgs_.find(catFilter[cat]);
638  if (it != cat_msgs_.end()) {
639  msg_iters_t temp(it->second);
640  TLOG(10) << "setFilter: cat " << catFilter[cat].toStdString() << " has " << temp.size() << " messages";
641  catResult.merge(temp);
642  }
643  }
644  if (result.empty()) {
645  result = catResult;
646  } else {
647  result = list_intersect(result, catResult);
648  }
649  TLOG(10) << "setFilter: result contains " << result.size() << " messages";
650  }
651 
652  // Create the filter expression
653  auto nFilterExpressions =
654  (appFilterExpression != "" ? 1 : 0) + (hostFilterExpression != "" ? 1 : 0) + (catFilterExpression != "" ? 1 : 0);
655  QString filterExpression = "";
656  if (nFilterExpressions == 1) {
657  filterExpression = catFilterExpression + hostFilterExpression + appFilterExpression;
658  } else {
659  filterExpression = "(" + (catFilterExpression != "" ? catFilterExpression + ") && (" : "") + hostFilterExpression +
660  (hostFilterExpression != "" && appFilterExpression != "" ? ") && (" : "") + appFilterExpression +
661  ")";
662  }
663 
664  // Add the tab and populate it
665 
666  auto newTabTitle = QString("Filter ") + QString::number(++nFilters);
667  QWidget* newTab = new QWidget();
668 
669  QTextEdit* txtDisplay = new QTextEdit(newTab);
670  QTextDocument* doc = new QTextDocument(txtDisplay);
671  txtDisplay->setDocument(doc);
672 
673  QVBoxLayout* layout = new QVBoxLayout();
674  layout->addWidget(txtDisplay);
675  layout->setContentsMargins(0, 0, 0, 0);
676  newTab->setLayout(layout);
677 
678  MsgFilterDisplay filteredMessages;
679  filteredMessages.msgs = result;
680  filteredMessages.hostFilter = hostFilter;
681  filteredMessages.appFilter = appFilter;
682  filteredMessages.catFilter = catFilter;
683  filteredMessages.txtDisplay = txtDisplay;
684  filteredMessages.nDisplayMsgs = result.size();
685  filteredMessages.nDisplayedDeletedMsgs = 0;
686  filteredMessages.sevThresh = SINFO;
687  msgFilters_.push_back(filteredMessages);
688 
689  tabWidget->addTab(newTab, newTabTitle);
690  tabWidget->setTabToolTip(tabWidget->count() - 1, filterExpression);
691  tabWidget->setCurrentIndex(tabWidget->count() - 1);
692 
693  displayMsg(msgFilters_.size() - 1);
694 }
695 
697  if (!paused) {
698  paused = true;
699  btnPause->setText("Resume");
700  // QMessageBox::about(this, "About MsgViewer", "Message receiving paused ...");
701  } else {
702  paused = false;
703  btnPause->setText("Pause");
704  }
705 }
706 
707 void msgViewerDlg::exit() { close(); }
708 
710  int ret =
711  QMessageBox::question(this, tr("Message Viewer"), tr("Are you sure you want to clear all received messages?"),
712  QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
713  std::unique_lock<std::mutex> lk(updating_mutex_);
714  switch (ret) {
715  case QMessageBox::Yes:
716  nMsgs = 0;
717  nSupMsgs = 0;
718  nThrMsgs = 0;
719  nDeleted = 0;
720  msg_pool_.clear();
721  host_msgs_.clear();
722  cat_msgs_.clear();
723  app_msgs_.clear();
724  updateList<msg_iters_map_t>(lwApplication, app_msgs_);
725  updateList<msg_iters_map_t>(lwCategory, cat_msgs_);
726  updateList<msg_iters_map_t>(lwHost, host_msgs_);
727  for (auto& display : msgFilters_) {
728  display.txtDisplay->clear();
729  display.msgs.clear();
730  display.nDisplayMsgs = 0;
731  display.nDisplayedDeletedMsgs = 0;
732  }
733 
734  lcdMsgs->display(nMsgs);
735  lcdDisplayedMsgs->display(0);
736  break;
737  case QMessageBox::No:
738  default:
739  break;
740  }
741 }
742 
744  if (!shortMode_) {
745  shortMode_ = true;
746  btnDisplayMode->setText("Long View");
747  } else {
748  shortMode_ = false;
749  btnDisplayMode->setText("Compact View");
750  }
751  updateDisplays();
752 }
753 
755  auto display = tabWidget->currentIndex();
756  switch (sev) {
757  case SERROR:
758  setSevError();
759  break;
760 
761  case SWARNING:
762  setSevWarning();
763  break;
764 
765  case SINFO:
766  setSevInfo();
767  break;
768 
769  default:
770  setSevDebug();
771  }
772 
773  displayMsg(display);
774 }
775 
776 void msgViewerDlg::setSevError() {
777  auto display = tabWidget->currentIndex();
778  msgFilters_[display].sevThresh = SERROR;
779  btnError->setChecked(true);
780  btnWarning->setChecked(false);
781  btnInfo->setChecked(false);
782  btnDebug->setChecked(false);
783  vsSeverity->setValue(SERROR);
784 }
785 
786 void msgViewerDlg::setSevWarning() {
787  auto display = tabWidget->currentIndex();
788  msgFilters_[display].sevThresh = SWARNING;
789  btnError->setChecked(false);
790  btnWarning->setChecked(true);
791  btnInfo->setChecked(false);
792  btnDebug->setChecked(false);
793  vsSeverity->setValue(SWARNING);
794 }
795 
796 void msgViewerDlg::setSevInfo() {
797  auto display = tabWidget->currentIndex();
798  msgFilters_[display].sevThresh = SINFO;
799  btnError->setChecked(false);
800  btnWarning->setChecked(false);
801  btnInfo->setChecked(true);
802  btnDebug->setChecked(false);
803  vsSeverity->setValue(SINFO);
804 }
805 
806 void msgViewerDlg::setSevDebug() {
807  auto display = tabWidget->currentIndex();
808  msgFilters_[display].sevThresh = SDEBUG;
809  btnError->setChecked(false);
810  btnWarning->setChecked(false);
811  btnInfo->setChecked(false);
812  btnDebug->setChecked(true);
813  vsSeverity->setValue(SDEBUG);
814 }
815 
816 void msgViewerDlg::renderMode() {
817  simpleRender = !simpleRender;
818 
819  if (simpleRender) {
820  btnRMode->setChecked(true);
821  for (auto display : msgFilters_) {
822  display.txtDisplay->setPlainText(display.txtDisplay->toPlainText());
823  }
824  } else {
825  btnRMode->setChecked(false);
826  updateDisplays();
827  }
828 }
829 
830 void msgViewerDlg::searchMsg() {
831  QString search = editSearch->text();
832 
833  if (search.isEmpty()) return;
834 
835  auto display = tabWidget->currentIndex();
836  if (search != searchStr) {
837  msgFilters_[display].txtDisplay->moveCursor(QTextCursor::Start);
838  if (!msgFilters_[display].txtDisplay->find(search)) {
839  msgFilters_[display].txtDisplay->moveCursor(QTextCursor::End);
840  searchStr = "";
841  } else
842  searchStr = search;
843  } else {
844  if (!msgFilters_[display].txtDisplay->find(search)) {
845  msgFilters_[display].txtDisplay->moveCursor(QTextCursor::Start);
846  if (!msgFilters_[display].txtDisplay->find(search)) {
847  msgFilters_[display].txtDisplay->moveCursor(QTextCursor::End);
848  searchStr = "";
849  }
850  }
851  }
852 }
853 
854 void msgViewerDlg::searchClear() {
855  auto display = tabWidget->currentIndex();
856  editSearch->setText("");
857  searchStr = "";
858  msgFilters_[display].txtDisplay->find("");
859  msgFilters_[display].txtDisplay->moveCursor(QTextCursor::End);
860 }
861 
862 void msgViewerDlg::setSuppression(QAction* act) {
863  bool status = act->isChecked();
864  suppress* sup = (suppress*)act->data().value<void*>();
865  sup->use(status);
866 }
867 
868 void msgViewerDlg::setThrottling(QAction* act) {
869  bool status = act->isChecked();
870  throttle* thr = (throttle*)act->data().value<void*>();
871  thr->use(status);
872 }
873 
874 void msgViewerDlg::tabWidgetCurrentChanged(int newTab) {
875  lcdDisplayedMsgs->display(msgFilters_[newTab].nDisplayMsgs);
876 
877  lwHost->setCurrentRow(-1, QItemSelectionModel::Clear);
878  lwApplication->setCurrentRow(-1, QItemSelectionModel::Clear);
879  lwCategory->setCurrentRow(-1, QItemSelectionModel::Clear);
880 
881  for (auto host : msgFilters_[newTab].hostFilter) {
882  auto items = lwHost->findItems(host, Qt::MatchExactly);
883  if (items.size() > 0) {
884  items[0]->setSelected(true);
885  }
886  }
887  for (auto app : msgFilters_[newTab].appFilter) {
888  auto items = lwApplication->findItems(app, Qt::MatchExactly);
889  if (items.size() > 0) {
890  items[0]->setSelected(true);
891  }
892  }
893  for (auto cat : msgFilters_[newTab].catFilter) {
894  auto items = lwCategory->findItems(cat, Qt::MatchExactly);
895  if (items.size() > 0) {
896  items[0]->setSelected(true);
897  }
898  }
899 
900  switch (msgFilters_[newTab].sevThresh) {
901  case SDEBUG:
902  setSevDebug();
903  break;
904  case SINFO:
905  setSevInfo();
906  break;
907  case SWARNING:
908  setSevWarning();
909  break;
910  default:
911  setSevError();
912  break;
913  }
914 }
915 
916 void msgViewerDlg::tabCloseRequested(int tabIndex) {
917  if (tabIndex == 0 || static_cast<size_t>(tabIndex) >= msgFilters_.size()) return;
918 
919  auto widget = tabWidget->widget(tabIndex);
920  tabWidget->removeTab(tabIndex);
921  delete widget;
922 
923  msgFilters_.erase(msgFilters_.begin() + tabIndex);
924 }
925 
926 void msgViewerDlg::closeEvent(QCloseEvent* event) { event->accept(); }
927 
928 QStringList msgViewerDlg::toQStringList(QList<QListWidgetItem*> in) {
929  QStringList out;
930 
931  for (auto i = 0; i < in.size(); ++i) {
932  out << in[i]->text();
933  }
934 
935  return out;
936 }
QString const & app() const
Get the application of the message
Definition: qt_mf_msg.hh:80
void use(bool flag)
Enable or disable this throttle
Definition: throttle.hh:39
timeval time() const
Get the message timestamp
Definition: qt_mf_msg.hh:85
void exit()
Exit the program.
Definition: mvdlg.cc:707
void start()
Start all receivers
Qt wrapper around MessageFacility message
Definition: qt_mf_msg.hh:30
Suppress messages based on a regular expression
Definition: suppress.hh:13
void clear()
Clear the message buffer.
Definition: mvdlg.cc:709
QString const & host() const
Get the host from which the message came
Definition: qt_mf_msg.hh:70
void use(bool flag)
Set whether the suppression is active
Definition: suppress.hh:32
Throttle messages based on name and time limits. Separate from MessageFacility limiting.
Definition: throttle.hh:17
void stop()
Stop all receivers
void pause()
Pause message receiving.
Definition: mvdlg.cc:696
void changeSeverity(int sev)
Change the severity threshold.
Definition: mvdlg.cc:754
QString const & cat() const
Get the category of the message
Definition: qt_mf_msg.hh:75
void shortMode()
Switch to/from Short message mode.
Definition: mvdlg.cc:743
void closeEvent(QCloseEvent *event)
Perform actions on window close.
Definition: mvdlg.cc:926
msgViewerDlg(std::string const &conf, QDialog *parent=0)
Message Viewer Dialog Constructor.
Definition: mvdlg.cc:77