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