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