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