$treeview $search $mathjax $extrastylesheet
artdaq_mfextensions
v1_03_03
$projectbrief
|
$projectbrief
|
$searchbox |
00001 #include <QMenu> 00002 #include <QMessageBox> 00003 #include <QProgressDialog> 00004 #include <QScrollBar> 00005 #include <QtGui> 00006 00007 #include "cetlib/filepath_maker.h" 00008 #include "fhiclcpp/ParameterSet.h" 00009 #include "fhiclcpp/make_ParameterSet.h" 00010 00011 #include "mfextensions/Binaries/mvdlg.hh" 00012 00013 #if GCC_VERSION >= 701000 || defined(__clang__) 00014 #pragma GCC diagnostic push 00015 #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" 00016 #endif 00017 00018 #include "trace.h" 00019 00020 #if GCC_VERSION >= 701000 || defined(__clang__) 00021 #pragma GCC diagnostic pop 00022 #endif 00023 00024 #include "mvdlg.hh" 00025 00026 // replace the ${..} part in the filename with env variable 00027 // throw if the env does not exist 00028 static void process_fname(std::string& fname) { 00029 size_t sub_start = fname.find("${"); 00030 size_t sub_end = fname.find("}"); 00031 00032 const size_t npos = std::string::npos; 00033 00034 if ((sub_start == npos && sub_end != npos) || (sub_start != npos && sub_end == npos) || (sub_start > sub_end)) { 00035 throw std::runtime_error("Unrecognized configuration file. Use default configuration instead."); 00036 } 00037 00038 if (sub_start == npos) return; 00039 00040 std::string env = std::string(getenv(fname.substr(sub_start + 2, sub_end - sub_start - 2).c_str())); 00041 fname.replace(sub_start, sub_end - sub_start + 1, env); 00042 00043 // printf("%s\n", fname.c_str()); 00044 } 00045 00046 static fhicl::ParameterSet readConf(std::string const& fname) { 00047 if (fname.empty()) return fhicl::ParameterSet(); 00048 00049 std::string filename = fname; 00050 process_fname(filename); 00051 00052 std::string env("FHICL_FILE_PATH="); 00053 00054 if (filename[0] == '/') { 00055 env.append("/"); 00056 } else { 00057 env.append("."); 00058 } 00059 00060 char* mfe_path = getenv("MFEXTENSIONS_DIR"); 00061 if (mfe_path) env.append(":").append(mfe_path).append("/config"); 00062 00063 putenv((char*)env.c_str()); 00064 00065 // printf("%s\n", env.c_str()); 00066 00067 cet::filepath_lookup policy("FHICL_FILE_PATH"); 00068 00069 // it throws when the file is not parsable 00070 fhicl::ParameterSet pset; 00071 fhicl::make_ParameterSet(filename, policy, pset); 00072 00073 return pset; 00074 } 00075 00076 msgViewerDlg::msgViewerDlg(std::string const& conf, QDialog* parent) 00077 : QDialog(parent), 00078 paused(false), 00079 shortMode_(false), 00080 nMsgs(0), 00081 nSupMsgs(0), 00082 nThrMsgs(0), 00083 nFilters(0), 00084 nDeleted(0), 00085 simpleRender(true), 00086 searchStr(""), 00087 msg_pool_(), 00088 host_msgs_(), 00089 cat_msgs_(), 00090 app_msgs_(), 00091 sup_menu(new QMenu(this)), 00092 thr_menu(new QMenu(this)), 00093 receivers_(readConf(conf).get<fhicl::ParameterSet>("receivers", fhicl::ParameterSet())) { 00094 setupUi(this); 00095 00096 // window geo settings 00097 readSettings(); 00098 00099 // read configuration file 00100 fhicl::ParameterSet pset = readConf(conf); 00101 00102 // parse configuration file 00103 parseConf(pset); 00104 00105 // associate menu with push buttons 00106 btnSuppression->setMenu(sup_menu); 00107 btnThrottling->setMenu(thr_menu); 00108 00109 // slots 00110 connect(btnPause, SIGNAL(clicked()), this, SLOT(pause())); 00111 connect(btnExit, SIGNAL(clicked()), this, SLOT(exit())); 00112 connect(btnClear, SIGNAL(clicked()), this, SLOT(clear())); 00113 00114 connect(btnRMode, SIGNAL(clicked()), this, SLOT(renderMode())); 00115 connect(btnDisplayMode, SIGNAL(clicked()), this, SLOT(shortMode())); 00116 00117 connect(btnSearch, SIGNAL(clicked()), this, SLOT(searchMsg())); 00118 connect(btnSearchClear, SIGNAL(clicked()), this, SLOT(searchClear())); 00119 00120 connect(btnFilter, SIGNAL(clicked()), this, SLOT(setFilter())); 00121 00122 connect(btnError, SIGNAL(clicked()), this, SLOT(setSevError())); 00123 connect(btnWarning, SIGNAL(clicked()), this, SLOT(setSevWarning())); 00124 connect(btnInfo, SIGNAL(clicked()), this, SLOT(setSevInfo())); 00125 connect(btnDebug, SIGNAL(clicked()), this, SLOT(setSevDebug())); 00126 00127 connect(sup_menu, SIGNAL(triggered(QAction*)), this, SLOT(setSuppression(QAction*))); 00128 00129 connect(thr_menu, SIGNAL(triggered(QAction*)), this, SLOT(setThrottling(QAction*))); 00130 00131 connect(vsSeverity, SIGNAL(valueChanged(int)), this, SLOT(changeSeverity(int))); 00132 00133 connect(&receivers_, SIGNAL(newMessage(qt_mf_msg const&)), this, SLOT(onNewMsg(qt_mf_msg const&))); 00134 00135 connect(tabWidget, SIGNAL(currentChanged(int)), this, SLOT(tabWidgetCurrentChanged(int))); 00136 connect(tabWidget, SIGNAL(tabCloseRequested(int)), this, SLOT(tabCloseRequested(int))); 00137 MsgFilterDisplay allMessages; 00138 allMessages.txtDisplay = txtMessages; 00139 allMessages.nDisplayMsgs = 0; 00140 allMessages.nDisplayedDeletedMsgs = 0; 00141 allMessages.sevThresh = SINFO; 00142 msgFilters_.push_back(allMessages); 00143 00144 // https://stackoverflow.com/questions/2616483/close-button-only-for-some-tabs-in-qt 00145 QTabBar* tabBar = tabWidget->findChild<QTabBar*>(); 00146 tabBar->setTabButton(0, QTabBar::RightSide, 0); 00147 tabBar->setTabButton(0, QTabBar::LeftSide, 0); 00148 00149 if (simpleRender) 00150 btnRMode->setChecked(true); 00151 else 00152 btnRMode->setChecked(false); 00153 00154 btnRMode->setEnabled(false); 00155 00156 changeSeverity(SINFO); 00157 00158 QTextDocument* doc = new QTextDocument(txtMessages); 00159 txtMessages->setDocument(doc); 00160 00161 receivers_.start(); 00162 } 00163 00164 msgViewerDlg::~msgViewerDlg() { 00165 receivers_.stop(); 00166 writeSettings(); 00167 } 00168 00169 static void str_to_suppress(std::vector<std::string> const& vs, std::vector<suppress>& s, QMenu* menu) { 00170 QAction* act; 00171 00172 if (vs.empty()) { 00173 act = menu->addAction("None"); 00174 act->setEnabled(false); 00175 return; 00176 } 00177 00178 s.reserve(vs.size()); 00179 00180 for (size_t i = 0; i < vs.size(); ++i) { 00181 s.push_back(suppress(vs[i])); 00182 act = menu->addAction(QString(vs[i].c_str())); 00183 act->setCheckable(true); 00184 act->setChecked(true); 00185 QVariant v = qVariantFromValue((void*)&s[i]); 00186 act->setData(v); 00187 } 00188 } 00189 00190 static void pset_to_throttle(std::vector<fhicl::ParameterSet> const& ps, std::vector<throttle>& t, QMenu* menu) { 00191 QAction* act; 00192 00193 if (ps.empty()) { 00194 act = menu->addAction("None"); 00195 act->setEnabled(false); 00196 return; 00197 } 00198 00199 t.reserve(ps.size()); 00200 00201 for (size_t i = 0; i < ps.size(); ++i) { 00202 std::string name = ps[i].get<std::string>("name"); 00203 t.push_back(throttle(name, ps[i].get<int>("limit", -1), ps[i].get<long>("timespan", -1))); 00204 act = menu->addAction(QString(name.c_str())); 00205 act->setCheckable(true); 00206 act->setChecked(true); 00207 QVariant v = qVariantFromValue((void*)&t[i]); 00208 act->setData(v); 00209 } 00210 } 00211 00212 void msgViewerDlg::parseConf(fhicl::ParameterSet const& conf) { 00213 fhicl::ParameterSet nulp; 00214 // QAction * act; 00215 00216 // suppression list 00217 fhicl::ParameterSet sup = conf.get<fhicl::ParameterSet>("suppress", nulp); 00218 00219 auto sup_host = sup.get<std::vector<std::string>>("hosts", std::vector<std::string>()); 00220 auto sup_app = sup.get<std::vector<std::string>>("applications", std::vector<std::string>()); 00221 auto sup_cat = sup.get<std::vector<std::string>>("categories", std::vector<std::string>()); 00222 00223 str_to_suppress(sup_host, e_sup_host, sup_menu); 00224 sup_menu->addSeparator(); 00225 00226 str_to_suppress(sup_app, e_sup_app, sup_menu); 00227 sup_menu->addSeparator(); 00228 00229 str_to_suppress(sup_cat, e_sup_cat, sup_menu); 00230 00231 // throttling list 00232 auto thr = conf.get<fhicl::ParameterSet>("throttle", nulp); 00233 00234 auto thr_host = thr.get<std::vector<fhicl::ParameterSet>>("hosts", std::vector<fhicl::ParameterSet>()); 00235 auto thr_app = thr.get<std::vector<fhicl::ParameterSet>>("applications", std::vector<fhicl::ParameterSet>()); 00236 auto thr_cat = thr.get<std::vector<fhicl::ParameterSet>>("categories", std::vector<fhicl::ParameterSet>()); 00237 00238 pset_to_throttle(thr_host, e_thr_host, thr_menu); 00239 thr_menu->addSeparator(); 00240 00241 pset_to_throttle(thr_app, e_thr_app, thr_menu); 00242 thr_menu->addSeparator(); 00243 00244 pset_to_throttle(thr_cat, e_thr_cat, thr_menu); 00245 00246 maxMsgs = conf.get<size_t>("max_message_buffer_size", 100000); 00247 maxDeletedMsgs = conf.get<size_t>("max_displayed_deleted_messages", 100000); 00248 } 00249 00250 bool msgViewerDlg::msg_throttled(qt_mf_msg const& mfmsg) { 00251 // suppression list 00252 00253 ++nSupMsgs; 00254 00255 for (size_t i = 0; i < e_sup_host.size(); ++i) 00256 if (e_sup_host[i].match(mfmsg.host().toStdString())) return true; 00257 00258 for (size_t i = 0; i < e_sup_app.size(); ++i) 00259 if (e_sup_app[i].match(mfmsg.app().toStdString())) return true; 00260 00261 for (size_t i = 0; i < e_sup_cat.size(); ++i) 00262 if (e_sup_cat[i].match(mfmsg.cat().toStdString())) return true; 00263 00264 --nSupMsgs; 00265 00266 // throttling 00267 00268 ++nThrMsgs; 00269 00270 for (size_t i = 0; i < e_thr_host.size(); ++i) 00271 if (e_thr_host[i].reach_limit(mfmsg.host().toStdString(), mfmsg.time())) return true; 00272 00273 for (size_t i = 0; i < e_thr_app.size(); ++i) 00274 if (e_thr_app[i].reach_limit(mfmsg.app().toStdString(), mfmsg.time())) return true; 00275 00276 for (size_t i = 0; i < e_thr_cat.size(); ++i) 00277 if (e_thr_cat[i].reach_limit(mfmsg.cat().toStdString(), mfmsg.time())) return true; 00278 00279 --nThrMsgs; 00280 00281 return false; 00282 } 00283 00284 void msgViewerDlg::writeSettings() { 00285 QSettings settings("ARTDAQ", "MsgViewer"); 00286 00287 settings.beginGroup("MainWindow"); 00288 settings.setValue("size", size()); 00289 settings.setValue("pos", pos()); 00290 settings.endGroup(); 00291 } 00292 00293 void msgViewerDlg::readSettings() { 00294 QSettings settings("ARTDAQ", "MsgViewer"); 00295 00296 settings.beginGroup("MainWindow"); 00297 QPoint pos = settings.value("pos", QPoint(100, 100)).toPoint(); 00298 QSize size = settings.value("size", QSize(660, 760)).toSize(); 00299 resize(size); 00300 move(pos); 00301 settings.endGroup(); 00302 } 00303 00304 void msgViewerDlg::onNewMsg(qt_mf_msg const& mfmsg) { 00305 // 21-Aug-2015, KAB: copying the incrementing (and displaying) of the number 00306 // of messages to here. I'm also not sure if we want to 00307 // count all messages or just non-suppressed ones or what. But, at least this 00308 // change gets the counter incrementing on the display. 00309 ++nMsgs; 00310 lcdMsgs->display(nMsgs); 00311 00312 // test if the message is suppressed or throttled 00313 if (msg_throttled(mfmsg)) { 00314 lcdSuppressionCount->display(nSupMsgs); 00315 lcdThrottlingCount->display(nThrMsgs); 00316 return; 00317 } 00318 00319 // push the message to the message pool 00320 msg_pool_.emplace_back(mfmsg); 00321 00322 while (maxMsgs > 0 && msg_pool_.size() > maxMsgs) { 00323 removeMsg(msg_pool_.begin()); 00324 } 00325 00326 msgs_t::iterator it = --msg_pool_.end(); 00327 00328 // update corresponding lists of index 00329 unsigned int flag = update_index(it); 00330 00331 // update gui list 00332 if (flag & LIST_APP) updateList<msg_iters_map_t>(lwApplication, app_msgs_); 00333 if (flag & LIST_CAT) updateList<msg_iters_map_t>(lwCategory, cat_msgs_); 00334 if (flag & LIST_HOST) updateList<msg_iters_map_t>(lwHost, host_msgs_); 00335 00336 for (size_t d = 0; d < msgFilters_.size(); ++d) { 00337 bool hostMatch = 00338 msgFilters_[d].hostFilter.contains(it->host(), Qt::CaseInsensitive) || msgFilters_[d].hostFilter.size() == 0; 00339 bool appMatch = 00340 msgFilters_[d].appFilter.contains(it->app(), Qt::CaseInsensitive) || msgFilters_[d].appFilter.size() == 0; 00341 bool catMatch = 00342 msgFilters_[d].catFilter.contains(it->cat(), Qt::CaseInsensitive) || msgFilters_[d].catFilter.size() == 0; 00343 00344 // Check to display the message 00345 if (hostMatch && appMatch && catMatch) { 00346 msgFilters_[d].msgs.push_back(it); 00347 displayMsg(it, d); 00348 } 00349 } 00350 } 00351 00352 void msgViewerDlg::removeMsg(msgs_t::iterator it) { 00353 std::unique_lock<std::mutex> lk(updating_mutex_); 00354 QString const& app = it->app(); 00355 QString const& cat = it->cat(); 00356 QString const& host = it->host(); 00357 00358 auto catIter = std::find(cat_msgs_[cat].begin(), cat_msgs_[cat].end(), it); 00359 auto hostIter = std::find(host_msgs_[host].begin(), host_msgs_[host].end(), it); 00360 auto appIter = std::find(app_msgs_[app].begin(), app_msgs_[app].end(), it); 00361 if (catIter != cat_msgs_[cat].end()) cat_msgs_[cat].erase(catIter); 00362 if (hostIter != host_msgs_[host].end()) host_msgs_[host].erase(hostIter); 00363 if (appIter != app_msgs_[app].end()) app_msgs_[app].erase(appIter); 00364 00365 if (app_msgs_[app].size() == 0) { 00366 app_msgs_.erase(app); 00367 updateList<msg_iters_map_t>(lwApplication, app_msgs_); 00368 } 00369 if (cat_msgs_[cat].size() == 0) { 00370 cat_msgs_.erase(cat); 00371 updateList<msg_iters_map_t>(lwCategory, cat_msgs_); 00372 } 00373 if (host_msgs_[host].size() == 0) { 00374 host_msgs_.erase(host); 00375 updateList<msg_iters_map_t>(lwHost, host_msgs_); 00376 } 00377 00378 for (size_t d = 0; d < msgFilters_.size(); ++d) { 00379 bool hostMatch = 00380 msgFilters_[d].hostFilter.contains(it->host(), Qt::CaseInsensitive) || msgFilters_[d].hostFilter.size() == 0; 00381 bool appMatch = 00382 msgFilters_[d].appFilter.contains(it->app(), Qt::CaseInsensitive) || msgFilters_[d].appFilter.size() == 0; 00383 bool catMatch = 00384 msgFilters_[d].catFilter.contains(it->cat(), Qt::CaseInsensitive) || msgFilters_[d].catFilter.size() == 0; 00385 bool sevMatch = it->sev() >= msgFilters_[d].sevThresh; 00386 00387 // Check to display the message 00388 if (hostMatch && appMatch && catMatch) { 00389 auto filterIt = std::find(msgFilters_[d].msgs.begin(), msgFilters_[d].msgs.end(), it); 00390 if (filterIt != msgFilters_[d].msgs.end()) msgFilters_[d].msgs.erase(filterIt); 00391 00392 if (sevMatch) { 00393 if (++msgFilters_[d].nDisplayedDeletedMsgs > static_cast<int>(maxDeletedMsgs) && maxDeletedMsgs > 0) { 00394 lk.unlock(); 00395 displayMsg(d); 00396 } 00397 } 00398 } 00399 if ((int)d == tabWidget->currentIndex()) { 00400 lcdDisplayedDeleted->display(msgFilters_[d].nDisplayedDeletedMsgs); 00401 } 00402 } 00403 00404 msg_pool_.erase(it); 00405 ++nDeleted; 00406 lcdDeletedCount->display(nDeleted); 00407 } 00408 00409 unsigned int msgViewerDlg::update_index(msgs_t::iterator it) { 00410 std::unique_lock<std::mutex> lk(updating_mutex_); 00411 QString const& app = it->app(); 00412 QString const& cat = it->cat(); 00413 QString const& host = it->host(); 00414 00415 unsigned int update = 0x0; 00416 00417 if (cat_msgs_.find(cat) == cat_msgs_.end()) update |= LIST_CAT; 00418 if (host_msgs_.find(host) == host_msgs_.end()) update |= LIST_HOST; 00419 if (app_msgs_.find(app) == app_msgs_.end()) update |= LIST_APP; 00420 00421 cat_msgs_[cat].push_back(it); 00422 host_msgs_[host].push_back(it); 00423 app_msgs_[app].push_back(it); 00424 00425 return update; 00426 } 00427 00428 void msgViewerDlg::displayMsg(msgs_t::const_iterator it, int display) { 00429 if (it->sev() < msgFilters_[display].sevThresh) return; 00430 std::unique_lock<std::mutex> lk(updating_mutex_); 00431 00432 msgFilters_[display].nDisplayMsgs++; 00433 if (display == tabWidget->currentIndex()) { 00434 lcdDisplayedMsgs->display(msgFilters_[display].nDisplayMsgs); 00435 } 00436 00437 auto txt = it->text(shortMode_); 00438 UpdateTextAreaDisplay(txt, msgFilters_[display].txtDisplay); 00439 } 00440 00441 void msgViewerDlg::displayMsg(int display) { 00442 std::unique_lock<std::mutex> lk(updating_mutex_); 00443 int n = 0; 00444 msgFilters_[display].txtDisplay->clear(); 00445 msgFilters_[display].nDisplayMsgs = 0; 00446 msgFilters_[display].nDisplayedDeletedMsgs = 0; 00447 00448 msg_iters_t::const_iterator it; 00449 00450 n = msgFilters_[display].msgs.size(); 00451 it = msgFilters_[display].msgs.begin(); 00452 QProgressDialog progress("Fetching data...", "Cancel", 0, n / 1000, this); 00453 00454 progress.setWindowModality(Qt::WindowModal); 00455 progress.setMinimumDuration(2000); // 2 seconds 00456 00457 QString txt = ""; 00458 int i = 0, prog = 0; 00459 00460 for (; it != msgFilters_[display].msgs.end(); ++it, ++i) { 00461 if (it->get()->sev() >= msgFilters_[display].sevThresh) { 00462 txt += it->get()->text(shortMode_); 00463 ++msgFilters_[display].nDisplayMsgs; 00464 } 00465 00466 if (i == 1000) { 00467 i = 0; 00468 ++prog; 00469 progress.setValue(prog); 00470 00471 if (txt.length()) { 00472 UpdateTextAreaDisplay(txt, msgFilters_[display].txtDisplay); 00473 txt.clear(); 00474 } 00475 } 00476 00477 if (progress.wasCanceled()) break; 00478 } 00479 00480 if (display == tabWidget->currentIndex()) { 00481 lcdDisplayedMsgs->display(msgFilters_[display].nDisplayMsgs); 00482 } 00483 00484 UpdateTextAreaDisplay(txt, msgFilters_[display].txtDisplay); 00485 } 00486 00487 // https://stackoverflow.com/questions/21955923/prevent-a-qtextedit-widget-from-scrolling-when-there-is-a-selection 00488 void msgViewerDlg::UpdateTextAreaDisplay(QString text, QTextEdit* widget) { 00489 const QTextCursor old_cursor = widget->textCursor(); 00490 const int old_scrollbar_value = widget->verticalScrollBar()->value(); 00491 const bool is_scrolled_down = 00492 old_scrollbar_value >= widget->verticalScrollBar()->maximum() * 0.95; // At least 95% scrolled down 00493 00494 // Insert the text at the position of the cursor (which is the end of the document). 00495 widget->append(text); 00496 00497 if (old_cursor.hasSelection() || !is_scrolled_down) { 00498 // The user has selected text or scrolled away from the bottom: maintain position. 00499 widget->setTextCursor(old_cursor); 00500 widget->verticalScrollBar()->setValue(old_scrollbar_value); 00501 } else { 00502 // The user hasn't selected any text and the scrollbar is at the bottom: scroll to the bottom. 00503 widget->moveCursor(QTextCursor::End); 00504 widget->verticalScrollBar()->setValue(widget->verticalScrollBar()->maximum()); 00505 widget->horizontalScrollBar()->setValue(0); 00506 } 00507 } 00508 00509 void msgViewerDlg::updateDisplays() { 00510 for (size_t ii = 0; ii < msgFilters_.size(); ++ii) { 00511 displayMsg(ii); 00512 } 00513 } 00514 00515 template <typename M> 00516 bool msgViewerDlg::updateList(QListWidget* lw, M const& map) { 00517 bool nonSelectedBefore = (lw->currentRow() == -1); 00518 bool nonSelectedAfter = true; 00519 00520 QString item = nonSelectedBefore ? "" : lw->currentItem()->text(); 00521 00522 lw->clear(); 00523 int row = 0; 00524 typename M::const_iterator it = map.begin(); 00525 00526 while (it != map.end()) { 00527 lw->addItem(it->first); 00528 if (!nonSelectedBefore && nonSelectedAfter) { 00529 if (item == it->first) { 00530 lw->setCurrentRow(row); 00531 nonSelectedAfter = false; 00532 } 00533 } 00534 ++row; 00535 ++it; 00536 } 00537 00538 if (!nonSelectedBefore && nonSelectedAfter) return true; 00539 00540 return false; 00541 } 00542 00543 msg_iters_t msgViewerDlg::list_intersect(msg_iters_t const& l1, msg_iters_t const& l2) { 00544 msg_iters_t output; 00545 msg_iters_t::const_iterator it1 = l1.begin(); 00546 msg_iters_t::const_iterator it2 = l2.begin(); 00547 00548 while (it1 != l1.end() && it2 != l2.end()) { 00549 if (*it1 < *it2) { 00550 ++it1; 00551 } else if (*it2 < *it1) { 00552 ++it2; 00553 } else { 00554 output.push_back(*it1); 00555 ++it1; 00556 ++it2; 00557 } 00558 } 00559 00560 TLOG(10) << "list_intersect: output list has " << output.size() << " entries"; 00561 return output; 00562 } 00563 00564 std::string sev_to_string(sev_code_t s) { 00565 switch (s) { 00566 case SDEBUG: 00567 return "DEBUG"; 00568 case SINFO: 00569 return "INFO"; 00570 case SWARNING: 00571 return "WARNING"; 00572 case SERROR: 00573 return "ERROR"; 00574 } 00575 return "UNKNOWN"; 00576 } 00577 00578 void msgViewerDlg::setFilter() { 00579 auto hostFilter = toQStringList(lwHost->selectedItems()); 00580 auto appFilter = toQStringList(lwApplication->selectedItems()); 00581 auto catFilter = toQStringList(lwCategory->selectedItems()); 00582 00583 lwHost->setCurrentRow(-1, QItemSelectionModel::Clear); 00584 lwApplication->setCurrentRow(-1, QItemSelectionModel::Clear); 00585 lwCategory->setCurrentRow(-1, QItemSelectionModel::Clear); 00586 00587 if (hostFilter.isEmpty() && appFilter.isEmpty() && catFilter.isEmpty()) { 00588 return; 00589 } 00590 00591 msg_iters_t result; 00592 QString catFilterExpression = ""; 00593 QString hostFilterExpression = ""; 00594 QString appFilterExpression = ""; 00595 bool first = true; 00596 00597 for (auto app = 0; app < appFilter.size(); ++app) { // app-sev index 00598 msg_iters_map_t::const_iterator it = app_msgs_.find(appFilter[app]); 00599 appFilterExpression += QString(first ? "" : " || ") + appFilter[app]; 00600 first = false; 00601 if (it != app_msgs_.end()) { 00602 msg_iters_t temp(it->second); 00603 TLOG(10) << "setFilter: app " << appFilter[app].toStdString() << " has " << temp.size() << " messages"; 00604 result.merge(temp); 00605 } 00606 } 00607 TLOG(10) << "setFilter: result contains %zu messages", result.size(); 00608 00609 first = true; 00610 if (!hostFilter.isEmpty()) { 00611 msg_iters_t hostResult; 00612 for (auto host = 0; host < hostFilter.size(); ++host) { // host index 00613 hostFilterExpression += QString(first ? "" : " || ") + hostFilter[host]; 00614 first = false; 00615 msg_iters_map_t::const_iterator it = host_msgs_.find(hostFilter[host]); 00616 if (it != host_msgs_.end()) { 00617 msg_iters_t temp(it->second); 00618 TLOG(10) << "setFilter: host " << hostFilter[host].toStdString() << " has " << temp.size() << " messages"; 00619 hostResult.merge(temp); 00620 } 00621 } 00622 if (result.empty()) { 00623 result = hostResult; 00624 } else { 00625 result = list_intersect(result, hostResult); 00626 } 00627 TLOG(10) << "setFilter: result contains " << result.size() << " messages"; 00628 } 00629 00630 first = true; 00631 if (!catFilter.isEmpty()) { 00632 msg_iters_t catResult; 00633 for (auto cat = 0; cat < catFilter.size(); ++cat) { // cat index 00634 catFilterExpression += QString(first ? "" : " || ") + catFilter[cat]; 00635 first = false; 00636 msg_iters_map_t::const_iterator it = cat_msgs_.find(catFilter[cat]); 00637 if (it != cat_msgs_.end()) { 00638 msg_iters_t temp(it->second); 00639 TLOG(10) << "setFilter: cat " << catFilter[cat].toStdString() << " has " << temp.size() << " messages"; 00640 catResult.merge(temp); 00641 } 00642 } 00643 if (result.empty()) { 00644 result = catResult; 00645 } else { 00646 result = list_intersect(result, catResult); 00647 } 00648 TLOG(10) << "setFilter: result contains " << result.size() << " messages"; 00649 } 00650 00651 // Create the filter expression 00652 auto nFilterExpressions = 00653 (appFilterExpression != "" ? 1 : 0) + (hostFilterExpression != "" ? 1 : 0) + (catFilterExpression != "" ? 1 : 0); 00654 QString filterExpression = ""; 00655 if (nFilterExpressions == 1) { 00656 filterExpression = catFilterExpression + hostFilterExpression + appFilterExpression; 00657 } else { 00658 filterExpression = "(" + (catFilterExpression != "" ? catFilterExpression + ") && (" : "") + hostFilterExpression + 00659 (hostFilterExpression != "" && appFilterExpression != "" ? ") && (" : "") + appFilterExpression + 00660 ")"; 00661 } 00662 00663 // Add the tab and populate it 00664 00665 auto newTabTitle = QString("Filter ") + QString::number(++nFilters); 00666 QWidget* newTab = new QWidget(); 00667 00668 QTextEdit* txtDisplay = new QTextEdit(newTab); 00669 QTextDocument* doc = new QTextDocument(txtDisplay); 00670 txtDisplay->setDocument(doc); 00671 00672 QVBoxLayout* layout = new QVBoxLayout(); 00673 layout->addWidget(txtDisplay); 00674 layout->setContentsMargins(0, 0, 0, 0); 00675 newTab->setLayout(layout); 00676 00677 MsgFilterDisplay filteredMessages; 00678 filteredMessages.msgs = result; 00679 filteredMessages.hostFilter = hostFilter; 00680 filteredMessages.appFilter = appFilter; 00681 filteredMessages.catFilter = catFilter; 00682 filteredMessages.txtDisplay = txtDisplay; 00683 filteredMessages.nDisplayMsgs = result.size(); 00684 filteredMessages.nDisplayedDeletedMsgs = 0; 00685 filteredMessages.sevThresh = SINFO; 00686 msgFilters_.push_back(filteredMessages); 00687 00688 tabWidget->addTab(newTab, newTabTitle); 00689 tabWidget->setTabToolTip(tabWidget->count() - 1, filterExpression); 00690 tabWidget->setCurrentIndex(tabWidget->count() - 1); 00691 00692 displayMsg(msgFilters_.size() - 1); 00693 } 00694 00695 void msgViewerDlg::pause() { 00696 if (!paused) { 00697 paused = true; 00698 btnPause->setText("Resume"); 00699 // QMessageBox::about(this, "About MsgViewer", "Message receiving paused ..."); 00700 } else { 00701 paused = false; 00702 btnPause->setText("Pause"); 00703 } 00704 } 00705 00706 void msgViewerDlg::exit() { close(); } 00707 00708 void msgViewerDlg::clear() { 00709 int ret = 00710 QMessageBox::question(this, tr("Message Viewer"), tr("Are you sure you want to clear all received messages?"), 00711 QMessageBox::Yes | QMessageBox::No, QMessageBox::No); 00712 std::unique_lock<std::mutex> lk(updating_mutex_); 00713 switch (ret) { 00714 case QMessageBox::Yes: 00715 nMsgs = 0; 00716 nSupMsgs = 0; 00717 nThrMsgs = 0; 00718 nDeleted = 0; 00719 msg_pool_.clear(); 00720 host_msgs_.clear(); 00721 cat_msgs_.clear(); 00722 app_msgs_.clear(); 00723 updateList<msg_iters_map_t>(lwApplication, app_msgs_); 00724 updateList<msg_iters_map_t>(lwCategory, cat_msgs_); 00725 updateList<msg_iters_map_t>(lwHost, host_msgs_); 00726 for (auto& display : msgFilters_) { 00727 display.txtDisplay->clear(); 00728 display.msgs.clear(); 00729 display.nDisplayMsgs = 0; 00730 display.nDisplayedDeletedMsgs = 0; 00731 } 00732 00733 lcdMsgs->display(nMsgs); 00734 lcdDisplayedMsgs->display(0); 00735 break; 00736 case QMessageBox::No: 00737 default: 00738 break; 00739 } 00740 } 00741 00742 void msgViewerDlg::shortMode() { 00743 if (!shortMode_) { 00744 shortMode_ = true; 00745 btnDisplayMode->setText("Long View"); 00746 } else { 00747 shortMode_ = false; 00748 btnDisplayMode->setText("Compact View"); 00749 } 00750 updateDisplays(); 00751 } 00752 00753 void msgViewerDlg::changeSeverity(int sev) { 00754 auto display = tabWidget->currentIndex(); 00755 switch (sev) { 00756 case SERROR: 00757 setSevError(); 00758 break; 00759 00760 case SWARNING: 00761 setSevWarning(); 00762 break; 00763 00764 case SINFO: 00765 setSevInfo(); 00766 break; 00767 00768 default: 00769 setSevDebug(); 00770 } 00771 00772 displayMsg(display); 00773 } 00774 00775 void msgViewerDlg::setSevError() { 00776 auto display = tabWidget->currentIndex(); 00777 msgFilters_[display].sevThresh = SERROR; 00778 btnError->setChecked(true); 00779 btnWarning->setChecked(false); 00780 btnInfo->setChecked(false); 00781 btnDebug->setChecked(false); 00782 vsSeverity->setValue(SERROR); 00783 } 00784 00785 void msgViewerDlg::setSevWarning() { 00786 auto display = tabWidget->currentIndex(); 00787 msgFilters_[display].sevThresh = SWARNING; 00788 btnError->setChecked(false); 00789 btnWarning->setChecked(true); 00790 btnInfo->setChecked(false); 00791 btnDebug->setChecked(false); 00792 vsSeverity->setValue(SWARNING); 00793 } 00794 00795 void msgViewerDlg::setSevInfo() { 00796 auto display = tabWidget->currentIndex(); 00797 msgFilters_[display].sevThresh = SINFO; 00798 btnError->setChecked(false); 00799 btnWarning->setChecked(false); 00800 btnInfo->setChecked(true); 00801 btnDebug->setChecked(false); 00802 vsSeverity->setValue(SINFO); 00803 } 00804 00805 void msgViewerDlg::setSevDebug() { 00806 auto display = tabWidget->currentIndex(); 00807 msgFilters_[display].sevThresh = SDEBUG; 00808 btnError->setChecked(false); 00809 btnWarning->setChecked(false); 00810 btnInfo->setChecked(false); 00811 btnDebug->setChecked(true); 00812 vsSeverity->setValue(SDEBUG); 00813 } 00814 00815 void msgViewerDlg::renderMode() { 00816 simpleRender = !simpleRender; 00817 00818 if (simpleRender) { 00819 btnRMode->setChecked(true); 00820 for (auto display : msgFilters_) { 00821 display.txtDisplay->setPlainText(display.txtDisplay->toPlainText()); 00822 } 00823 } else { 00824 btnRMode->setChecked(false); 00825 updateDisplays(); 00826 } 00827 } 00828 00829 void msgViewerDlg::searchMsg() { 00830 QString search = editSearch->text(); 00831 00832 if (search.isEmpty()) return; 00833 00834 auto display = tabWidget->currentIndex(); 00835 if (search != searchStr) { 00836 msgFilters_[display].txtDisplay->moveCursor(QTextCursor::Start); 00837 if (!msgFilters_[display].txtDisplay->find(search)) { 00838 msgFilters_[display].txtDisplay->moveCursor(QTextCursor::End); 00839 searchStr = ""; 00840 } else 00841 searchStr = search; 00842 } else { 00843 if (!msgFilters_[display].txtDisplay->find(search)) { 00844 msgFilters_[display].txtDisplay->moveCursor(QTextCursor::Start); 00845 if (!msgFilters_[display].txtDisplay->find(search)) { 00846 msgFilters_[display].txtDisplay->moveCursor(QTextCursor::End); 00847 searchStr = ""; 00848 } 00849 } 00850 } 00851 } 00852 00853 void msgViewerDlg::searchClear() { 00854 auto display = tabWidget->currentIndex(); 00855 editSearch->setText(""); 00856 searchStr = ""; 00857 msgFilters_[display].txtDisplay->find(""); 00858 msgFilters_[display].txtDisplay->moveCursor(QTextCursor::End); 00859 } 00860 00861 void msgViewerDlg::setSuppression(QAction* act) { 00862 bool status = act->isChecked(); 00863 suppress* sup = (suppress*)act->data().value<void*>(); 00864 sup->use(status); 00865 } 00866 00867 void msgViewerDlg::setThrottling(QAction* act) { 00868 bool status = act->isChecked(); 00869 throttle* thr = (throttle*)act->data().value<void*>(); 00870 thr->use(status); 00871 } 00872 00873 void msgViewerDlg::tabWidgetCurrentChanged(int newTab) { 00874 lcdDisplayedMsgs->display(msgFilters_[newTab].nDisplayMsgs); 00875 00876 lwHost->setCurrentRow(-1, QItemSelectionModel::Clear); 00877 lwApplication->setCurrentRow(-1, QItemSelectionModel::Clear); 00878 lwCategory->setCurrentRow(-1, QItemSelectionModel::Clear); 00879 00880 for (auto host : msgFilters_[newTab].hostFilter) { 00881 auto items = lwHost->findItems(host, Qt::MatchExactly); 00882 if (items.size() > 0) { 00883 items[0]->setSelected(true); 00884 } 00885 } 00886 for (auto app : msgFilters_[newTab].appFilter) { 00887 auto items = lwApplication->findItems(app, Qt::MatchExactly); 00888 if (items.size() > 0) { 00889 items[0]->setSelected(true); 00890 } 00891 } 00892 for (auto cat : msgFilters_[newTab].catFilter) { 00893 auto items = lwCategory->findItems(cat, Qt::MatchExactly); 00894 if (items.size() > 0) { 00895 items[0]->setSelected(true); 00896 } 00897 } 00898 00899 switch (msgFilters_[newTab].sevThresh) { 00900 case SDEBUG: 00901 setSevDebug(); 00902 break; 00903 case SINFO: 00904 setSevInfo(); 00905 break; 00906 case SWARNING: 00907 setSevWarning(); 00908 break; 00909 default: 00910 setSevError(); 00911 break; 00912 } 00913 } 00914 00915 void msgViewerDlg::tabCloseRequested(int tabIndex) { 00916 if (tabIndex == 0 || static_cast<size_t>(tabIndex) >= msgFilters_.size()) return; 00917 00918 auto widget = tabWidget->widget(tabIndex); 00919 tabWidget->removeTab(tabIndex); 00920 delete widget; 00921 00922 msgFilters_.erase(msgFilters_.begin() + tabIndex); 00923 } 00924 00925 void msgViewerDlg::closeEvent(QCloseEvent* event) { event->accept(); } 00926 00927 QStringList msgViewerDlg::toQStringList(QList<QListWidgetItem*> in) { 00928 QStringList out; 00929 00930 for (auto i = 0; i < in.size(); ++i) { 00931 out << in[i]->text(); 00932 } 00933 00934 return out; 00935 }