MythTV  master
statusbox.cpp
Go to the documentation of this file.
1 
2 #include "statusbox.h"
3 
4 using namespace std;
5 
6 #include <QRegExp>
7 #include <QHostAddress>
8 #include <QNetworkInterface>
9 
10 #include "mythcorecontext.h"
11 #include "filesysteminfo.h"
12 #include "mythmiscutil.h"
13 #include "mythdb.h"
14 #include "mythlogging.h"
15 #include "mythversion.h"
16 #include "mythdate.h"
17 
18 #include "config.h"
19 #include "remoteutil.h"
20 #include "tv.h"
21 #include "jobqueue.h"
22 #include "cardutil.h"
23 #include "recordinginfo.h"
24 
25 #include "mythuihelper.h"
26 #include "mythuibuttonlist.h"
27 #include "mythuitext.h"
28 #include "mythuistatetype.h"
29 #include "mythdialogbox.h"
30 #include "mythrender_base.h"
32 #include "mythdisplay.h"
34 
35 struct LogLine {
36  QString m_line;
37  QString m_detail;
38  QString m_help;
39  QString m_helpdetail;
40  QString m_data;
41  QString m_state;
42 };
43 
44 void StatusBoxItem::Start(int Interval)
45 {
46  connect(this, &QTimer::timeout, [=]() { emit UpdateRequired(this); });
47  start(Interval * 1000);
48 }
49 
60  : MythScreenType(parent, "StatusBox")
61 {
62  m_minLevel = gCoreContext->GetNumSetting("LogDefaultView",5);
63 
64  QStringList strlist;
65  strlist << "QUERY_IS_ACTIVE_BACKEND";
66  strlist << gCoreContext->GetHostName();
67 
69 
70  m_isBackendActive = (strlist[0] == "TRUE");
71  m_popupStack = GetMythMainWindow()->GetStack("popup stack");
72 }
73 
75 {
76  if (m_logList)
77  gCoreContext->SaveSetting("StatusBoxItemCurrent",
79 }
80 
82 {
83  if (!LoadWindowFromXML("status-ui.xml", "status", this))
84  return false;
85 
86  m_categoryList = dynamic_cast<MythUIButtonList *>(GetChild("category"));
87  m_logList = dynamic_cast<MythUIButtonList *>(GetChild("log"));
88 
89  m_iconState = dynamic_cast<MythUIStateType *>(GetChild("icon"));
90  m_helpText = dynamic_cast<MythUIText *>(GetChild("helptext"));
91  m_justHelpText = dynamic_cast<MythUIText *>(GetChild("justhelptext"));
92 
94  {
95  LOG(VB_GENERAL, LOG_ERR, "StatusBox, theme is missing "
96  "required elements");
97  return false;
98  }
99 
100  connect(m_categoryList, SIGNAL(itemSelected(MythUIButtonListItem *)),
102  connect(m_logList, SIGNAL(itemSelected(MythUIButtonListItem *)),
104  connect(m_logList, SIGNAL(itemClicked(MythUIButtonListItem *)),
105  SLOT(clicked(MythUIButtonListItem *)));
106 
107  BuildFocusList();
108  return true;
109 }
110 
112 {
113  auto *item = new MythUIButtonListItem(m_categoryList, tr("Listings Status"),
114  QVariant::fromValue((void*)SLOT(doListingsStatus())));
115  item->DisplayState("listings", "icon");
116 
117  item = new MythUIButtonListItem(m_categoryList, tr("Schedule Status"),
118  QVariant::fromValue((void*)SLOT(doScheduleStatus())));
119  item->DisplayState("schedule", "icon");
120 
121  item = new MythUIButtonListItem(m_categoryList, tr("Input Status"),
122  QVariant::fromValue((void*)SLOT(doTunerStatus())));
123  item->DisplayState("tuner", "icon");
124 
125  item = new MythUIButtonListItem(m_categoryList, tr("Job Queue"),
126  QVariant::fromValue((void*)SLOT(doJobQueueStatus())));
127  item->DisplayState("jobqueue", "icon");
128 
129  item = new MythUIButtonListItem(m_categoryList, tr("Video decoders"),
130  QVariant::fromValue((void*)SLOT(doDecoderStatus())));
131  item->DisplayState("decoders", "icon");
132 
133  item = new MythUIButtonListItem(m_categoryList, tr("Display"),
134  QVariant::fromValue((void*)SLOT(doDisplayStatus())));
135  item->DisplayState("display", "icon");
136 
137  item = new MythUIButtonListItem(m_categoryList, tr("Machine Status"),
138  QVariant::fromValue((void*)SLOT(doMachineStatus())));
139  item->DisplayState("machine", "icon");
140 
141  item = new MythUIButtonListItem(m_categoryList, tr("AutoExpire List"),
142  QVariant::fromValue((void*)SLOT(doAutoExpireList())));
143  item->DisplayState("autoexpire", "icon");
144 
145  int itemCurrent = gCoreContext->GetNumSetting("StatusBoxItemCurrent", 0);
146  m_categoryList->SetItemCurrent(itemCurrent);
147 }
148 
149 StatusBoxItem *StatusBox::AddLogLine(const QString & line,
150  const QString & help,
151  const QString & detail,
152  const QString & helpdetail,
153  const QString & state,
154  const QString & data)
155 {
156  LogLine logline;
157  logline.m_line = line;
158 
159  if (detail.isEmpty())
160  logline.m_detail = line;
161  else
162  logline.m_detail = detail;
163 
164  if (help.isEmpty())
165  logline.m_help = logline.m_detail;
166  else
167  logline.m_help = help;
168 
169  if (helpdetail.isEmpty())
170  logline.m_helpdetail = logline.m_detail;
171  else
172  logline.m_helpdetail = helpdetail;
173 
174  logline.m_state = state;
175  logline.m_data = data;
176 
177  auto *item = new StatusBoxItem(m_logList, line, QVariant::fromValue(logline));
178  if (logline.m_state.isEmpty())
179  logline.m_state = "normal";
180 
181  item->SetFontState(logline.m_state);
182  item->DisplayState(logline.m_state, "status");
183  item->SetText(logline.m_detail, "detail");
184  return item;
185 }
186 
187 bool StatusBox::keyPressEvent(QKeyEvent *event)
188 {
189  if (GetFocusWidget()->keyPressEvent(event))
190  return true;
191 
192  QStringList actions;
193  bool handled = GetMythMainWindow()->TranslateKeyPress("Status", event, actions);
194 
195  for (int i = 0; i < actions.size() && !handled; ++i)
196  {
197  QString action = actions[i];
198  handled = true;
199 
200  QRegExp logNumberKeys( "^[12345678]$" );
201 
203  QString currentItem;
204  if (currentButton)
205  currentItem = currentButton->GetText();
206 
207  if (action == "MENU")
208  {
209  if (currentItem == tr("Log Entries"))
210  {
211  QString message = tr("Acknowledge all log entries at "
212  "this priority level or lower?");
213 
214  auto *confirmPopup =
215  new MythConfirmationDialog(m_popupStack, message);
216 
217  confirmPopup->SetReturnEvent(this, "LogAckAll");
218 
219  if (confirmPopup->Create())
220  m_popupStack->AddScreen(confirmPopup, false);
221  }
222  }
223  else if ((currentItem == tr("Log Entries")) &&
224  (logNumberKeys.indexIn(action) == 0))
225  {
226  m_minLevel = action.toInt();
227  if (m_helpText)
228  m_helpText->SetText(tr("Setting priority level to %1")
229  .arg(m_minLevel));
230  if (m_justHelpText)
231  m_justHelpText->SetText(tr("Setting priority level to %1")
232  .arg(m_minLevel));
233  doLogEntries();
234  }
235  else
236  handled = false;
237  }
238 
239  if (!handled && MythScreenType::keyPressEvent(event))
240  handled = true;
241 
242  return handled;
243 }
244 
246 {
247  if (!item || GetFocusWidget() != m_logList)
248  return;
249 
250  LogLine logline = item->GetData().value<LogLine>();
251  if (m_helpText)
252  m_helpText->SetText(logline.m_helpdetail);
253  if (m_justHelpText)
254  m_justHelpText->SetText(logline.m_help);
255 }
256 
258 {
259  if (!item)
260  return;
261 
262  disconnect(this, SIGNAL(updateLog()),nullptr,nullptr);
263 
264  const char *slot = (const char *)item->GetData().value<void*>();
265 
266  connect(this, SIGNAL(updateLog()), slot);
267  emit updateLog();
268 }
269 
271 {
272  if (!item)
273  return;
274 
275  LogLine logline = item->GetData().value<LogLine>();
276 
278  QString currentItem;
279  if (currentButton)
280  currentItem = currentButton->GetText();
281 
282  // FIXME: Comparisons against strings here is not great, changing names
283  // breaks everything and it's inefficient
284  if (currentItem == tr("Log Entries"))
285  {
286  QString message = tr("Acknowledge this log entry?");
287 
288  auto *confirmPopup = new MythConfirmationDialog(m_popupStack, message);
289 
290  confirmPopup->SetReturnEvent(this, "LogAck");
291  confirmPopup->SetData(logline.m_data);
292 
293  if (confirmPopup->Create())
294  m_popupStack->AddScreen(confirmPopup, false);
295  }
296  else if (currentItem == tr("Job Queue"))
297  {
298  QStringList msgs;
299  int jobStatus = JobQueue::GetJobStatus(logline.m_data.toInt());
300 
301  if (jobStatus == JOB_QUEUED)
302  {
303  QString message = tr("Delete Job?");
304 
305  auto *confirmPopup =
306  new MythConfirmationDialog(m_popupStack, message);
307 
308  confirmPopup->SetReturnEvent(this, "JobDelete");
309  confirmPopup->SetData(logline.m_data);
310 
311  if (confirmPopup->Create())
312  m_popupStack->AddScreen(confirmPopup, false);
313  }
314  else if ((jobStatus == JOB_PENDING) ||
315  (jobStatus == JOB_STARTING) ||
316  (jobStatus == JOB_RUNNING) ||
317  (jobStatus == JOB_PAUSED))
318  {
319  QString label = tr("Job Queue Actions:");
320 
321  auto *menuPopup = new MythDialogBox(label, m_popupStack,
322  "statusboxpopup");
323 
324  if (menuPopup->Create())
325  m_popupStack->AddScreen(menuPopup, false);
326 
327  menuPopup->SetReturnEvent(this, "JobModify");
328 
329  QVariant data = QVariant::fromValue(logline.m_data);
330 
331  if (jobStatus == JOB_PAUSED)
332  menuPopup->AddButton(tr("Resume"), data);
333  else
334  menuPopup->AddButton(tr("Pause"), data);
335  menuPopup->AddButton(tr("Stop"), data);
336  menuPopup->AddButton(tr("No Change"), data);
337  }
338  else if (jobStatus & JOB_DONE)
339  {
340  QString message = tr("Requeue Job?");
341 
342  auto *confirmPopup =
343  new MythConfirmationDialog(m_popupStack, message);
344 
345  confirmPopup->SetReturnEvent(this, "JobRequeue");
346  confirmPopup->SetData(logline.m_data);
347 
348  if (confirmPopup->Create())
349  m_popupStack->AddScreen(confirmPopup, false);
350  }
351  }
352  else if (currentItem == tr("AutoExpire List"))
353  {
355 
356  if (rec)
357  {
358  QString label = tr("AutoExpire Actions:");
359 
360  auto *menuPopup = new MythDialogBox(label, m_popupStack,
361  "statusboxpopup");
362 
363  if (menuPopup->Create())
364  m_popupStack->AddScreen(menuPopup, false);
365 
366  menuPopup->SetReturnEvent(this, "AutoExpireManage");
367 
368  menuPopup->AddButton(tr("Delete Now"), QVariant::fromValue(rec));
369  if ((rec)->GetRecordingGroup() == "LiveTV")
370  {
371  menuPopup->AddButton(tr("Move to Default group"),
372  QVariant::fromValue(rec));
373  }
374  else if ((rec)->GetRecordingGroup() == "Deleted")
375  menuPopup->AddButton(tr("Undelete"), QVariant::fromValue(rec));
376  else
377  menuPopup->AddButton(tr("Disable AutoExpire"),
378  QVariant::fromValue(rec));
379  menuPopup->AddButton(tr("No Change"), QVariant::fromValue(rec));
380 
381  }
382  }
383 }
384 
385 void StatusBox::customEvent(QEvent *event)
386 {
387  if (event->type() == DialogCompletionEvent::kEventType)
388  {
389  auto *dce = (DialogCompletionEvent*)(event);
390 
391  QString resultid = dce->GetId();
392  int buttonnum = dce->GetResult();
393 
394  if (resultid == "LogAck")
395  {
396  if (buttonnum == 1)
397  {
398  QString sql = dce->GetData().toString();
399  MSqlQuery query(MSqlQuery::InitCon());
400  query.prepare("UPDATE mythlog SET acknowledged = 1 "
401  "WHERE logid = :LOGID ;");
402  query.bindValue(":LOGID", sql);
403  if (!query.exec())
404  MythDB::DBError("StatusBox::customEvent -- LogAck", query);
406  }
407  }
408  else if (resultid == "LogAckAll")
409  {
410  if (buttonnum == 1)
411  {
412  MSqlQuery query(MSqlQuery::InitCon());
413  query.prepare("UPDATE mythlog SET acknowledged = 1 "
414  "WHERE priority <= :PRIORITY ;");
415  query.bindValue(":PRIORITY", m_minLevel);
416  if (!query.exec())
417  MythDB::DBError("StatusBox::customEvent -- LogAckAll",
418  query);
419  doLogEntries();
420  }
421  }
422  else if (resultid == "JobDelete")
423  {
424  if (buttonnum == 1)
425  {
426  int jobID = dce->GetData().toInt();
428 
430  }
431  }
432  else if (resultid == "JobRequeue")
433  {
434  if (buttonnum == 1)
435  {
436  int jobID = dce->GetData().toInt();
437  JobQueue::ChangeJobStatus(jobID, JOB_QUEUED);
439  }
440  }
441  else if (resultid == "JobModify")
442  {
443  int jobID = dce->GetData().toInt();
444  if (buttonnum == 0)
445  {
446  if (JobQueue::GetJobStatus(jobID) == JOB_PAUSED)
448  else
450  }
451  else if (buttonnum == 1)
452  {
454  }
455 
457  }
458  else if (resultid == "AutoExpireManage")
459  {
460  auto* rec = dce->GetData().value<ProgramInfo*>();
461 
462  // button 2 is "No Change"
463  if (!rec || buttonnum == 2)
464  return;
465 
466  // button 1 is "Delete Now"
467  if ((buttonnum == 0) && rec->QueryIsDeleteCandidate())
468  {
469  if (!RemoteDeleteRecording(rec->GetRecordingID(),
470  false, false))
471  {
472  LOG(VB_GENERAL, LOG_ERR, QString("Failed to delete recording: %1").arg(rec->GetTitle()));
473  return;
474  }
475  }
476  // button 1 is "Move To Default Group" or "UnDelete" or "Disable AutoExpire"
477  else if (buttonnum == 1)
478  {
479  if ((rec)->GetRecordingGroup() == "Deleted")
480  {
481  RemoteUndeleteRecording(rec->GetRecordingID());
482  }
483  else
484  {
485  rec->SaveAutoExpire(kDisableAutoExpire);
486 
487  if ((rec)->GetRecordingGroup() == "LiveTV")
488  {
489  RecordingInfo ri(*rec);
491  *rec = ri;
492  }
493  }
494  }
495 
496  // remove the changed recording from the expire list
497  delete m_expList[m_logList->GetCurrentPos()];
498  m_expList.erase(m_expList.begin() + m_logList->GetCurrentPos());
499 
500  int pos = m_logList->GetCurrentPos();
501  int topPos = m_logList->GetTopItemPos();
502  doAutoExpireList(false);
503  m_logList->SetItemCurrent(pos, topPos);
504  }
505 
506  }
507 }
508 
510 {
511  if (m_iconState)
512  m_iconState->DisplayState("listings");
513  m_logList->Reset();
514 
515  QString helpmsg(tr("Listings Status shows the latest "
516  "status information from "
517  "mythfilldatabase"));
518  if (m_helpText)
519  m_helpText->SetText(helpmsg);
520  if (m_justHelpText)
521  m_justHelpText->SetText(helpmsg);
522 
523  QDateTime mfdLastRunStart;
524  QDateTime mfdLastRunEnd;
525  QDateTime mfdNextRunStart;
526  QString mfdLastRunStatus;
527  QString querytext;
528  QDateTime qdtNow;
529  QDateTime GuideDataThrough;
530 
531  qdtNow = MythDate::current();
532 
533  MSqlQuery query(MSqlQuery::InitCon());
534  query.prepare("SELECT max(endtime) FROM program WHERE manualid=0;");
535 
536  if (query.exec() && query.next())
537  GuideDataThrough = MythDate::fromString(query.value(0).toString());
538 
539  QString tmp = gCoreContext->GetSetting("mythfilldatabaseLastRunStart");
540  mfdLastRunStart = MythDate::fromString(tmp);
541  tmp = gCoreContext->GetSetting("mythfilldatabaseLastRunEnd");
542  mfdLastRunEnd = MythDate::fromString(tmp);
543  tmp = gCoreContext->GetSetting("MythFillSuggestedRunTime");
544  mfdNextRunStart = MythDate::fromString(tmp);
545 
546  mfdLastRunStatus = gCoreContext->GetSetting("mythfilldatabaseLastRunStatus");
547 
548  AddLogLine(tr("Mythfrontend version: %1 (%2)").arg(MYTH_SOURCE_PATH)
549  .arg(MYTH_SOURCE_VERSION), helpmsg);
550  AddLogLine(tr("Last mythfilldatabase guide update:"), helpmsg);
551  tmp = tr("Started: %1").arg(
553  mfdLastRunStart, MythDate::kDateTimeFull | MythDate::kSimplify));
554  AddLogLine(tmp, helpmsg);
555 
556  if (mfdLastRunEnd >= mfdLastRunStart)
557  {
558  tmp = tr("Finished: %1")
559  .arg(MythDate::toString(
560  mfdLastRunEnd,
562  AddLogLine(tmp, helpmsg);
563  }
564 
565  AddLogLine(tr("Result: %1").arg(mfdLastRunStatus), helpmsg);
566 
567 
568  if (mfdNextRunStart >= mfdLastRunStart)
569  {
570  tmp = tr("Suggested Next: %1")
571  .arg(MythDate::toString(
572  mfdNextRunStart,
574  AddLogLine(tmp, helpmsg);
575  }
576 
577  int DaysOfData = qdtNow.daysTo(GuideDataThrough);
578 
579  if (GuideDataThrough.isNull())
580  {
581  AddLogLine(tr("There's no guide data available!"), helpmsg,
582  "", "warning");
583  AddLogLine(tr("Have you run mythfilldatabase?"), helpmsg,
584  "", "warning");
585  }
586  else
587  {
588  AddLogLine(
589  tr("There is guide data until %1")
590  .arg(MythDate::toString(
591  GuideDataThrough,
593 
594  AddLogLine(QString("(%1).").arg(tr("%n day(s)", "", DaysOfData)),
595  helpmsg);
596  }
597 
598  if (DaysOfData <= 3)
599  AddLogLine(tr("WARNING: is mythfilldatabase running?"), helpmsg,
600  "", "", "warning");
601 }
602 
604 {
605  if (m_iconState)
606  m_iconState->DisplayState("schedule");
607  m_logList->Reset();
608 
609  QString helpmsg(tr("Schedule Status shows current statistics "
610  "from the scheduler."));
611  if (m_helpText)
612  m_helpText->SetText(helpmsg);
613  if (m_justHelpText)
614  m_justHelpText->SetText(helpmsg);
615 
616  MSqlQuery query(MSqlQuery::InitCon());
617 
618  query.prepare("SELECT COUNT(*) FROM record WHERE type = :TEMPLATE");
619  query.bindValue(":TEMPLATE", kTemplateRecord);
620  if (query.exec() && query.next())
621  {
622  QString rules = tr("%n template rule(s) (is) defined", "",
623  query.value(0).toInt());
624  AddLogLine(rules, helpmsg);
625  }
626  else
627  {
628  MythDB::DBError("StatusBox::doScheduleStatus()", query);
629  return;
630  }
631 
632  query.prepare("SELECT COUNT(*) FROM record "
633  "WHERE type <> :TEMPLATE AND search = :NOSEARCH");
634  query.bindValue(":TEMPLATE", kTemplateRecord);
635  query.bindValue(":NOSEARCH", kNoSearch);
636  if (query.exec() && query.next())
637  {
638  QString rules = tr("%n standard rule(s) (is) defined", "",
639  query.value(0).toInt());
640  AddLogLine(rules, helpmsg);
641  }
642  else
643  {
644  MythDB::DBError("StatusBox::doScheduleStatus()", query);
645  return;
646  }
647 
648  query.prepare("SELECT COUNT(*) FROM record WHERE search > :NOSEARCH");
649  query.bindValue(":NOSEARCH", kNoSearch);
650  if (query.exec() && query.next())
651  {
652  QString rules = tr("%n search rule(s) are defined", "",
653  query.value(0).toInt());
654  AddLogLine(rules, helpmsg);
655  }
656  else
657  {
658  MythDB::DBError("StatusBox::doScheduleStatus()", query);
659  return;
660  }
661 
662  QMap<RecStatus::Type, int> statusMatch;
663  QMap<RecStatus::Type, QString> statusText;
664  QMap<int, int> sourceMatch;
665  QMap<int, QString> sourceText;
666  QMap<int, int> cardMatch;
667  QMap<int, QString> cardText;
668  QMap<int, int> cardParent;
669  QMap<int, bool> cardSchedGroup;
670  QString tmpstr;
671  int maxSource = 0;
672  int maxCard = 0;
673  int lowerpriority = 0;
674  int hdflag = 0;
675 
676  query.prepare("SELECT MAX(sourceid) FROM videosource");
677  if (query.exec())
678  {
679  if (query.next())
680  maxSource = query.value(0).toInt();
681  }
682 
683  query.prepare("SELECT sourceid,name FROM videosource");
684  if (query.exec())
685  {
686  while (query.next())
687  sourceText[query.value(0).toInt()] = query.value(1).toString();
688  }
689 
690  query.prepare("SELECT MAX(cardid) FROM capturecard");
691  if (query.exec())
692  {
693  if (query.next())
694  maxCard = query.value(0).toInt();
695  }
696 
697  query.prepare("SELECT cardid, inputname, displayname, parentid, "
698  " schedgroup "
699  "FROM capturecard");
700  if (query.exec())
701  {
702  while (query.next())
703  {
704  int inputid = query.value(0).toInt();
705  cardText[inputid] = query.value(2).toString();
706  if (cardText[inputid].isEmpty())
707  cardText[inputid] = QString::number(query.value(1).toInt());
708  cardParent[inputid] = query.value(3).toInt();
709  cardSchedGroup[inputid] = query.value(4).toBool();
710  }
711  }
712 
713  ProgramList schedList;
714  LoadFromScheduler(schedList);
715 
716  tmpstr = tr("%n matching showing(s)", "", schedList.size());
717  AddLogLine(tmpstr, helpmsg);
718 
719  for (auto *s : schedList)
720  {
721  const RecStatus::Type recstatus = s->GetRecordingStatus();
722 
723  if (statusMatch[recstatus] < 1)
724  {
725  statusText[recstatus] = RecStatus::toString(
726  recstatus, s->GetRecordingRuleType());
727  }
728 
729  ++statusMatch[recstatus];
730 
731  if (recstatus == RecStatus::WillRecord ||
732  recstatus == RecStatus::Pending ||
733  recstatus == RecStatus::Recording ||
734  recstatus == RecStatus::Tuning ||
735  recstatus == RecStatus::Failing)
736  {
737  ++sourceMatch[s->GetSourceID()];
738  int inputid = s->GetInputID();
739  // When schedgroup is used, always attribute recordings to
740  // the parent inputs.
741  if (cardParent[inputid] && cardSchedGroup[cardParent[inputid]])
742  inputid = cardParent[inputid];
743  ++cardMatch[inputid];
744  if (s->GetRecordingPriority2() < 0)
745  ++lowerpriority;
746  if (s->GetVideoProperties() & VID_HDTV)
747  ++hdflag;
748  }
749  }
750 
751 #define ADD_STATUS_LOG_LINE(rtype, fstate) \
752  do { \
753  if (statusMatch[rtype] > 0) \
754  { \
755  tmpstr = QString("%1 %2").arg(statusMatch[rtype]) \
756  .arg(statusText[rtype]); \
757  AddLogLine(tmpstr, helpmsg, tmpstr, tmpstr, fstate);\
758  } \
759  } while (false)
770 
771  QString willrec = statusText[RecStatus::WillRecord];
772 
773  if (lowerpriority > 0)
774  {
775  tmpstr = QString("%1 %2 %3").arg(lowerpriority).arg(willrec)
776  .arg(tr("with lower priority"));
777  AddLogLine(tmpstr, helpmsg, tmpstr, tmpstr, "warning");
778  }
779  if (hdflag > 0)
780  {
781  tmpstr = QString("%1 %2 %3").arg(hdflag).arg(willrec)
782  .arg(tr("marked as HDTV"));
783  AddLogLine(tmpstr, helpmsg);
784  }
785  for (int i = 1; i <= maxSource; ++i)
786  {
787  if (sourceMatch[i] > 0)
788  {
789  tmpstr = QString("%1 %2 %3 %4 \"%5\"")
790  .arg(sourceMatch[i]).arg(willrec)
791  .arg(tr("from source")).arg(i).arg(sourceText[i]);
792  AddLogLine(tmpstr, helpmsg);
793  }
794  }
795  for (int i = 1; i <= maxCard; ++i)
796  {
797  if (cardMatch[i] > 0)
798  {
799  tmpstr = QString("%1 %2 %3 %4 \"%5\"")
800  .arg(cardMatch[i]).arg(willrec)
801  .arg(tr("on input")).arg(i).arg(cardText[i]);
802  AddLogLine(tmpstr, helpmsg);
803  }
804  }
805 }
806 
808 {
809  struct info
810  {
811  int m_inputid {0};
812  bool m_schedgroup {false};
813  QString m_displayname;
814  int m_errored {0};
815  int m_unavailable {0};
816  int m_sleeping {0};
817  int m_recording {0};
818  int m_livetv {0};
819  int m_available {0};
820  QStringList m_recordings;
821  };
822  QMap<int, struct info> info;
823  QList<int> inputids;
824 
825  if (m_iconState)
826  m_iconState->DisplayState("tuner");
827  m_logList->Reset();
828 
829  QString helpmsg(tr("Input Status shows the current information "
830  "about the state of backend inputs"));
831  if (m_helpText)
832  m_helpText->SetText(helpmsg);
833  if (m_justHelpText)
834  m_justHelpText->SetText(helpmsg);
835 
836  MSqlQuery query(MSqlQuery::InitCon());
837  query.prepare(
838  "SELECT cardid, parentid, schedgroup, displayname "
839  "FROM capturecard ORDER BY cardid");
840 
841  if (!query.exec() || !query.isActive())
842  {
843  MythDB::DBError("StatusBox::doTunerStatus()", query);
844  return;
845  }
846 
847  while (query.next())
848  {
849  int inputid = query.value(0).toInt();
850  int parentid = query.value(1).toInt();
851 
852  // If this is a schedgroup child, attribute all status to the
853  // parent.
854  int infoid = inputid;
855  if (parentid && info[parentid].m_schedgroup)
856  infoid = parentid;
857  else
858  {
859  info[infoid].m_inputid = inputid;
860  info[infoid].m_schedgroup = query.value(2).toBool();
861  info[infoid].m_displayname = query.value(3).toString();
862  inputids.append(inputid);
863  }
864 
865  QString cmd = QString("QUERY_REMOTEENCODER %1").arg(inputid);
866  QStringList strlist( cmd );
867  strlist << "GET_STATE";
868 
870  int state = strlist[0].toInt();
871 
872  QString status;
873  QString fontstate;
874  if (state == kState_Error)
875  {
876  strlist.clear();
877  strlist << QString("QUERY_REMOTEENCODER %1").arg(inputid);
878  strlist << "GET_SLEEPSTATUS";
879 
881  int sleepState = strlist[0].toInt();
882 
883  if (sleepState == -1)
884  info[infoid].m_errored += 1;
885  else if (sleepState == sStatus_Undefined)
886  info[infoid].m_unavailable += 1;
887  else
888  info[infoid].m_sleeping += 1;
889  }
890  else if (state == kState_RecordingOnly ||
891  state == kState_WatchingRecording)
892  info[infoid].m_recording += 1;
893  else if (state == kState_WatchingLiveTV)
894  info[infoid].m_livetv += 1;
895  else
896  info[infoid].m_available += 1;
897 
898  if (state == kState_RecordingOnly ||
899  state == kState_WatchingRecording ||
900  state == kState_WatchingLiveTV)
901  {
902  strlist = QStringList( QString("QUERY_RECORDER %1").arg(inputid));
903  strlist << "GET_RECORDING";
905  ProgramInfo pginfo(strlist);
906  if (pginfo.GetChanID())
907  {
908  QString titlesub = pginfo.GetTitle();
909  if (!pginfo.GetSubtitle().isEmpty())
910  titlesub += QString(" - ") + pginfo.GetSubtitle();
911  info[infoid].m_recordings += titlesub;
912  }
913  }
914  }
915 
916  foreach (int inputid, inputids)
917  {
918  QStringList statuslist;
919  if (info[inputid].m_errored)
920  statuslist << tr("%1 errored").arg(info[inputid].m_errored);
921  if (info[inputid].m_unavailable)
922  statuslist << tr("%1 unavailable").arg(info[inputid].m_unavailable);
923  if (info[inputid].m_sleeping)
924  statuslist << tr("%1 sleeping").arg(info[inputid].m_sleeping);
925  if (info[inputid].m_recording)
926  statuslist << tr("%1 recording").arg(info[inputid].m_recording);
927  if (info[inputid].m_livetv)
928  statuslist << tr("%1 live television").arg(info[inputid].m_livetv);
929  if (info[inputid].m_available)
930  statuslist << tr("%1 available").arg(info[inputid].m_available);
931 
932  QString fontstate;
933  if (info[inputid].m_errored)
934  fontstate = "error";
935  else if (info[inputid].m_unavailable || info[inputid].m_sleeping)
936  fontstate = "warning";
937 
938  QString shortstatus = tr("Input %1 %2: %3")
939  .arg(inputid).arg(info[inputid].m_displayname)
940  .arg(statuslist.join(tr(", ")));
941  QString longstatus = shortstatus + "\n" +
942  info[inputid].m_recordings.join("\n");
943 
944  AddLogLine(shortstatus, helpmsg, longstatus, longstatus, fontstate);
945  }
946 }
947 
949 {
950  if (m_iconState)
951  m_iconState->DisplayState("log");
952  m_logList->Reset();
953 
954  QString helpmsg(tr("Log Entries shows any unread log entries "
955  "from the system if you have logging enabled"));
956  if (m_helpText)
957  m_helpText->SetText(helpmsg);
958  if (m_justHelpText)
959  m_justHelpText->SetText(helpmsg);
960 
961  MSqlQuery query(MSqlQuery::InitCon());
962  query.prepare("SELECT logid, module, priority, logdate, host, "
963  "message, details "
964  "FROM mythlog WHERE acknowledged = 0 "
965  "AND priority <= :PRIORITY ORDER BY logdate DESC;");
966  query.bindValue(":PRIORITY", m_minLevel);
967 
968  if (query.exec())
969  {
970  QString line;
971  QString detail;
972  while (query.next())
973  {
974  line = QString("%1").arg(query.value(5).toString());
975 
976  detail = tr("On %1 from %2.%3\n%4\n")
977  .arg(MythDate::toString(
978  MythDate::as_utc(query.value(3).toDateTime()),
980  .arg(query.value(4).toString())
981  .arg(query.value(1).toString())
982  .arg(query.value(5).toString());
983 
984  QString tmp = query.value(6).toString();
985  if (!tmp.isEmpty())
986  detail.append(tmp);
987  else
988  detail.append(tr("No further details"));
989 
990  AddLogLine(line, helpmsg, detail, detail,
991  "", query.value(0).toString());
992  }
993 
994  if (query.size() == 0)
995  {
996  AddLogLine(tr("No items found at priority level %1 or lower.")
997  .arg(m_minLevel), helpmsg);
998  AddLogLine(tr("Use 1-8 to change priority level."), helpmsg);
999  }
1000  }
1001 }
1002 
1004 {
1005  if (m_iconState)
1006  m_iconState->DisplayState("jobqueue");
1007  m_logList->Reset();
1008 
1009  QString helpmsg(tr("Job Queue shows any jobs currently in "
1010  "MythTV's Job Queue such as a commercial "
1011  "detection job."));
1012  if (m_helpText)
1013  m_helpText->SetText(helpmsg);
1014  if (m_justHelpText)
1015  m_justHelpText->SetText(helpmsg);
1016 
1017  QMap<int, JobQueueEntry> jobs;
1018  QMap<int, JobQueueEntry>::Iterator it;
1019 
1022  JOB_LIST_RECENT);
1023 
1024  if (!jobs.empty())
1025  {
1026  QString detail;
1027  QString line;
1028 
1029  for (it = jobs.begin(); it != jobs.end(); ++it)
1030  {
1031  ProgramInfo pginfo((*it).chanid, (*it).recstartts);
1032 
1033  if (!pginfo.GetChanID())
1034  continue;
1035 
1036  detail = QString("%1\n%2 %3 @ %4\n%5 %6 %7 %8")
1037  .arg(pginfo.GetTitle())
1038  .arg(pginfo.GetChannelName())
1039  .arg(pginfo.GetChanNum())
1040  .arg(MythDate::toString(
1041  pginfo.GetRecordingStartTime(),
1043  .arg(tr("Job:"))
1044  .arg(JobQueue::JobText((*it).type))
1045  .arg(tr("Status: "))
1046  .arg(JobQueue::StatusText((*it).status));
1047 
1048  if ((*it).status != JOB_QUEUED)
1049  detail += " (" + (*it).hostname + ')';
1050 
1051  if ((*it).schedruntime > MythDate::current())
1052  {
1053  detail += '\n' + tr("Scheduled Run Time:") + ' ' +
1055  (*it).schedruntime,
1057  }
1058  else
1059  {
1060  detail += '\n' + (*it).comment;
1061  }
1062 
1063  line = QString("%1 @ %2").arg(pginfo.GetTitle())
1064  .arg(MythDate::toString(
1065  pginfo.GetRecordingStartTime(),
1067 
1068  QString font;
1069  if ((*it).status == JOB_ERRORED)
1070  font = "error";
1071  else if ((*it).status == JOB_ABORTED)
1072  font = "warning";
1073 
1074  AddLogLine(line, helpmsg, detail, detail, font,
1075  QString("%1").arg((*it).id));
1076  }
1077  }
1078  else
1079  AddLogLine(tr("Job Queue is currently empty."), helpmsg);
1080 
1081 }
1082 
1083 // Some helper routines for doMachineStatus() that format the output strings
1084 
1092 static QString sm_str(long long sizeKB, int prec=1)
1093 {
1094  if (sizeKB>1024*1024*1024) // Terabytes
1095  {
1096  double sizeGB = sizeKB/(1024*1024*1024.0);
1097  return QObject::tr("%1 TB").arg(sizeGB, 0, 'f', (sizeGB>10)?0:prec);
1098  }
1099  if (sizeKB>1024*1024) // Gigabytes
1100  {
1101  double sizeGB = sizeKB/(1024*1024.0);
1102  return QObject::tr("%1 GB").arg(sizeGB, 0, 'f', (sizeGB>10)?0:prec);
1103  }
1104  if (sizeKB>1024) // Megabytes
1105  {
1106  double sizeMB = sizeKB/1024.0;
1107  return QObject::tr("%1 MB").arg(sizeMB, 0, 'f', (sizeMB>10)?0:prec);
1108  }
1109  // Kilobytes
1110  return QObject::tr("%1 KB").arg(sizeKB);
1111 }
1112 
1113 static QString usage_str_kb(long long total,
1114  long long used,
1115  long long free)
1116 {
1117  QString ret = QObject::tr("Unknown");
1118  if (total > 0.0 && free > 0.0)
1119  {
1120  double percent = (100.0*free)/total;
1121  ret = StatusBox::tr("%1 total, %2 used, %3 (or %4%) free.")
1122  .arg(sm_str(total)).arg(sm_str(used))
1123  .arg(sm_str(free)).arg(percent, 0, 'f', (percent >= 10.0) ? 0 : 2);
1124  }
1125  return ret;
1126 }
1127 
1128 static QString usage_str_mb(float total, float used, float free)
1129 {
1130  return usage_str_kb((long long)(total*1024), (long long)(used*1024),
1131  (long long)(free*1024));
1132 }
1133 
1134 static void disk_usage_with_rec_time_kb(QStringList& out, long long total,
1135  long long used, long long free,
1136  const recprof2bps_t& prof2bps)
1137 {
1138  const QString tail = StatusBox::tr(", using your %1 rate of %2 kb/s");
1139 
1140  out<<usage_str_kb(total, used, free);
1141  if (free<0)
1142  return;
1143 
1144  // NOLINTNEXTLINE(modernize-loop-convert)
1145  for (auto it = prof2bps.begin(); it != prof2bps.end(); ++it)
1146  {
1147  const QString pro =
1148  tail.arg(it.key()).arg((int)((float)(*it) / 1024.0F));
1149 
1150  long long bytesPerMin = ((*it) >> 1) * 15;
1151  uint minLeft = ((free<<5)/bytesPerMin)<<5;
1152  minLeft = (minLeft/15)*15;
1153  uint hoursLeft = minLeft/60;
1154  QString hourstring = StatusBox::tr("%n hour(s)", "", hoursLeft);
1155  QString minstring = StatusBox::tr("%n minute(s)", "", minLeft%60);
1156  QString remainstring = StatusBox::tr("%1 remaining", "time");
1157  if (minLeft%60 == 0)
1158  out<<remainstring.arg(hourstring) + pro;
1159  else if (minLeft > 60)
1160  {
1161  out<<StatusBox::tr("%1 and %2 remaining", "time").arg(hourstring)
1162  .arg(minstring) + pro;
1163  }
1164  else
1165  {
1166  out<<remainstring.arg(minstring) + pro;
1167  }
1168  }
1169 }
1170 
1171 static QString uptimeStr(time_t uptime)
1172 {
1173  QString str = " " + StatusBox::tr("Uptime") + ": ";
1174 
1175  if (uptime == 0)
1176  return str + StatusBox::tr("unknown", "unknown uptime");
1177 
1178  int days = uptime/(60*60*24);
1179  uptime -= days*60*60*24;
1180  int hours = uptime/(60*60);
1181  uptime -= hours*60*60;
1182  int min = uptime/60;
1183  int secs = uptime%60;
1184 
1185  if (days > 0)
1186  {
1187  char buff[6];
1188  QString dayLabel = StatusBox::tr("%n day(s)", "", days);
1189 
1190  sprintf(buff, "%d:%02d", hours, min);
1191 
1192  return str + QString("%1, %2").arg(dayLabel).arg(buff);
1193  }
1194 
1195  char buff[9];
1196  sprintf(buff, "%d:%02d:%02d", hours, min, secs);
1197  return str + QString( buff );
1198 }
1199 
1203 void StatusBox::getActualRecordedBPS(const QString& hostnames)
1204 {
1205  m_recordingProfilesBps.clear();
1206 
1207  QString querystr;
1208  MSqlQuery query(MSqlQuery::InitCon());
1209 
1210  querystr =
1211  "SELECT sum(filesize) * 8 / "
1212  "sum(((unix_timestamp(endtime) - unix_timestamp(starttime)))) "
1213  "AS avg_bitrate "
1214  "FROM recorded WHERE hostname in (%1) "
1215  "AND (unix_timestamp(endtime) - unix_timestamp(starttime)) > 300;";
1216 
1217  query.prepare(querystr.arg(hostnames));
1218 
1219  if (query.exec() && query.next() &&
1220  query.value(0).toDouble() > 0)
1221  {
1222  QString rateStr = tr("average", "average rate");
1223 
1224  // Don't user a tr() directly here as the Qt tools will
1225  // not be able to extract the string for translation.
1226  m_recordingProfilesBps[rateStr] =
1227  (int)(query.value(0).toDouble());
1228  }
1229 
1230  querystr =
1231  "SELECT max(filesize * 8 / "
1232  "(unix_timestamp(endtime) - unix_timestamp(starttime))) "
1233  "AS max_bitrate "
1234  "FROM recorded WHERE hostname in (%1) "
1235  "AND (unix_timestamp(endtime) - unix_timestamp(starttime)) > 300;";
1236 
1237  query.prepare(querystr.arg(hostnames));
1238 
1239  if (query.exec() && query.next() &&
1240  query.value(0).toDouble() > 0)
1241  {
1242  QString rateStr = tr("maximum", "maximum rate");
1243 
1244  // Don't user a tr() directly here as the Qt tools will
1245  // not be able to extract the string for translation.
1246  m_recordingProfilesBps[rateStr] =
1247  (int)(query.value(0).toDouble());
1248  }
1249 }
1250 
1260 {
1261  if (m_iconState)
1262  m_iconState->DisplayState("machine");
1263  m_logList->Reset();
1264  QString machineStr = tr("Machine Status shows some operating system "
1265  "statistics of this machine and the MythTV "
1266  "server.");
1267 
1268  if (m_helpText)
1269  m_helpText->SetText(machineStr);
1270  if (m_justHelpText)
1271  m_justHelpText->SetText(machineStr);
1272 
1273  QString line;
1274  if (m_isBackendActive)
1275  line = tr("System:");
1276  else
1277  line = tr("This machine:");
1278  AddLogLine(line, machineStr);
1279 
1280  // Time
1281  StatusBoxItem *timebox = AddLogLine("");
1282  auto UpdateTime = [](StatusBoxItem* Item)
1283  {
1284  Item->SetText(" " + tr("System time") + ": " + QDateTime::currentDateTime().toString());
1285  };
1286  UpdateTime(timebox);
1287  connect(timebox, &StatusBoxItem::UpdateRequired, UpdateTime);
1288  timebox->Start();
1289 
1290  // Hostname & IP
1291  AddLogLine(" " + tr("Hostname") + ": " + gCoreContext->GetHostName());
1292  AddLogLine(" " + tr("OS") + QString(": %1 (%2)").arg(QSysInfo::prettyProductName())
1293  .arg(QSysInfo::currentCpuArchitecture()));
1294  AddLogLine(" " + tr("Qt version") + QString(": %1").arg(qVersion()));
1295 
1296  foreach(QNetworkInterface iface, QNetworkInterface::allInterfaces())
1297  {
1298  QNetworkInterface::InterfaceFlags f = iface.flags();
1299  if (!(f & QNetworkInterface::IsUp))
1300  continue;
1301  if (!(f & QNetworkInterface::IsRunning))
1302  continue;
1303  if (f & QNetworkInterface::IsLoopBack)
1304  continue;
1305 
1306 #if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
1307  QNetworkInterface::InterfaceType type = iface.type();
1308  QString name = type == QNetworkInterface::Wifi ? tr("WiFi") : tr("Ethernet");
1309 #else
1310  QString name = tr("Network");
1311 #endif
1312  AddLogLine(" " + name + QString(" (%1): ").arg(iface.humanReadableName()));
1313  AddLogLine(" " + tr("MAC Address") + ": " + iface.hardwareAddress());
1314  foreach(QNetworkAddressEntry addr, iface.addressEntries())
1315  {
1316  if (addr.ip().protocol() == QAbstractSocket::IPv4Protocol ||
1317  addr.ip().protocol() == QAbstractSocket::IPv6Protocol)
1318  {
1319  AddLogLine(" " + tr("IP Address") + ": " + addr.ip().toString());
1320  }
1321  }
1322  }
1323  AddLogLine(line, machineStr);
1324 
1325  // uptime
1326  time_t uptime = 0;
1327  if (getUptime(uptime))
1328  {
1329  auto UpdateUptime = [](StatusBoxItem* Item)
1330  {
1331  time_t time;
1332  getUptime(time);
1333  Item->SetText(uptimeStr(time));
1334  };
1335  StatusBoxItem *uptimeitem = AddLogLine(uptimeStr(uptime));
1336  connect(uptimeitem, &StatusBoxItem::UpdateRequired, UpdateUptime);
1337  uptimeitem->Start(60);
1338  }
1339 
1340  // weighted average loads
1341 #if !defined(_WIN32) && !defined(Q_OS_ANDROID)
1342  auto UpdateLoad = [](StatusBoxItem* Item)
1343  {
1344  double loads[3] = { 0.0 };
1345  getloadavg(loads, 3);
1346  Item->SetText(QString(" %1: %2 %3 %4").arg(tr("Load")).arg(loads[0], 1, 'f', 2)
1347  .arg(loads[1], 1, 'f', 2).arg(loads[2], 1, 'f', 2));
1348  };
1349  StatusBoxItem* loaditem = AddLogLine("");
1350  UpdateLoad(loaditem);
1351  connect(loaditem, &StatusBoxItem::UpdateRequired, UpdateLoad);
1352  loaditem->Start();
1353 #endif
1354 
1355  // memory usage
1356  int totalM = 0; // Physical memory
1357  int freeM = 0;
1358  int totalS = 0; // Virtual memory (swap)
1359  int freeS = 0;
1360  if (getMemStats(totalM, freeM, totalS, freeS))
1361  {
1362  auto UpdateMem = [](StatusBoxItem* Item)
1363  {
1364  int totm = 0;
1365  int freem = 0;
1366  int tots = 0;
1367  int frees = 0;
1368  if (getMemStats(totm, freem, tots, frees))
1369  Item->SetText(QString(" " + tr("RAM") + ": " + usage_str_mb(totm, totm - freem, freem)));
1370  };
1371 
1372  auto UpdateSwap = [](StatusBoxItem* Item)
1373  {
1374  int totm = 0;
1375  int freem = 0;
1376  int tots = 0;
1377  int frees = 0;
1378  if (getMemStats(totm, freem, tots, frees))
1379  Item->SetText(QString(" " + tr("Swap") + ": " + usage_str_mb(tots, tots - frees, frees)));
1380  };
1381  StatusBoxItem* mem = AddLogLine("", machineStr);
1382  StatusBoxItem* swap = AddLogLine("", machineStr);
1383  UpdateMem(mem);
1384  UpdateSwap(swap);
1385  connect(mem, &StatusBoxItem::UpdateRequired, UpdateMem);
1386  connect(swap, &StatusBoxItem::UpdateRequired, UpdateSwap);
1387  mem->Start(3);
1388  swap->Start(3);
1389  }
1390 
1391  if (!m_isBackendActive)
1392  {
1393  line = tr("MythTV server") + ':';
1394  AddLogLine(line, machineStr);
1395 
1396  // Hostname & IP
1397  line = " " + tr("Hostname") + ": " + gCoreContext->GetSetting("MasterServerName");
1398  line.append(", " + tr("IP") + ": " + gCoreContext->GetSetting("MasterServerIP"));
1399  AddLogLine(line, machineStr);
1400 
1401  // uptime
1402  if (RemoteGetUptime(uptime))
1403  {
1404  auto UpdateRemoteUptime = [](StatusBoxItem* Item)
1405  {
1406  time_t time;
1407  RemoteGetUptime(time);
1408  Item->SetText(uptimeStr(time));
1409  };
1410  StatusBoxItem *remoteuptime = AddLogLine(uptimeStr(uptime));
1411  connect(remoteuptime, &StatusBoxItem::UpdateRequired, UpdateRemoteUptime);
1412  remoteuptime->Start(60);
1413  }
1414 
1415  // weighted average loads
1416  double floads[3];
1417  if (RemoteGetLoad(floads))
1418  {
1419  auto UpdateRemoteLoad = [](StatusBoxItem* Item)
1420  {
1421  double loads[3] = { 0.0 };
1422  RemoteGetLoad(loads);
1423  Item->SetText(QString(" %1: %2 %3 %4").arg(tr("Load")).arg(loads[0], 1, 'f', 2)
1424  .arg(loads[1], 1, 'f', 2).arg(loads[2], 1, 'f', 2));
1425  };
1426  StatusBoxItem* remoteloaditem = AddLogLine("");
1427  UpdateRemoteLoad(remoteloaditem);
1428  connect(remoteloaditem, &StatusBoxItem::UpdateRequired, UpdateRemoteLoad);
1429  remoteloaditem->Start();
1430  }
1431 
1432  // memory usage
1433  if (RemoteGetMemStats(totalM, freeM, totalS, freeS))
1434  {
1435  auto UpdateRemoteMem = [](StatusBoxItem* Item)
1436  {
1437  int totm = 0;
1438  int freem = 0;
1439  int tots = 0;
1440  int frees = 0;
1441  if (RemoteGetMemStats(totm, freem, tots, frees))
1442  Item->SetText(QString(" " + tr("RAM") + ": " + usage_str_mb(totm, totm - freem, freem)));
1443  };
1444 
1445  auto UpdateRemoteSwap = [](StatusBoxItem* Item)
1446  {
1447  int totm = 0;
1448  int freem = 0;
1449  int tots = 0;
1450  int frees = 0;
1451  if (RemoteGetMemStats(totm, freem, tots, frees))
1452  Item->SetText(QString(" " + tr("Swap") + ": " + usage_str_mb(tots, tots - frees, frees)));
1453  };
1454  StatusBoxItem* rmem = AddLogLine("", machineStr);
1455  StatusBoxItem* rswap = AddLogLine("", machineStr);
1456  UpdateRemoteMem(rmem);
1457  UpdateRemoteSwap(rswap);
1458  connect(rmem, &StatusBoxItem::UpdateRequired, UpdateRemoteMem);
1459  connect(rswap, &StatusBoxItem::UpdateRequired, UpdateRemoteSwap);
1460  rmem->Start(10);
1461  rswap->Start(11);
1462  }
1463  }
1464 
1465  // get free disk space
1466  QString hostnames;
1467 
1468  QList<FileSystemInfo> fsInfos = FileSystemInfo::RemoteGetInfo();
1469  for (int i = 0; i < fsInfos.size(); ++i)
1470  {
1471  // For a single-directory installation just display the totals
1472  if ((fsInfos.size() == 2) && (i == 0) &&
1473  (fsInfos[i].getPath() != "TotalDiskSpace") &&
1474  (fsInfos[i+1].getPath() == "TotalDiskSpace"))
1475  i++;
1476 
1477  hostnames = QString("\"%1\"").arg(fsInfos[i].getHostname());
1478  hostnames.replace(' ', "");
1479  hostnames.replace(',', "\",\"");
1480 
1481  getActualRecordedBPS(hostnames);
1482 
1483  QStringList list;
1485  fsInfos[i].getTotalSpace(), fsInfos[i].getUsedSpace(),
1486  fsInfos[i].getTotalSpace() - fsInfos[i].getUsedSpace(),
1488 
1489  if (fsInfos[i].getPath() == "TotalDiskSpace")
1490  {
1491  line = tr("Total Disk Space:");
1492  AddLogLine(line, machineStr);
1493  }
1494  else
1495  {
1496  line = tr("MythTV Drive #%1:").arg(fsInfos[i].getFSysID());
1497  AddLogLine(line, machineStr);
1498 
1499  QStringList tokens = fsInfos[i].getPath().split(',');
1500 
1501  if (tokens.size() > 1)
1502  {
1503  AddLogLine(QString(" ") + tr("Directories:"), machineStr);
1504 
1505  int curToken = 0;
1506  while (curToken < tokens.size())
1507  AddLogLine(QString(" ") +
1508  tokens[curToken++], machineStr);
1509  }
1510  else
1511  {
1512  AddLogLine(QString(" " ) + tr("Directory:") + ' ' +
1513  fsInfos[i].getPath(), machineStr);
1514  }
1515  }
1516 
1517  for (auto & diskinfo : list)
1518  {
1519  line = QString(" ") + diskinfo;
1520  AddLogLine(line, machineStr);
1521  }
1522  }
1523 
1524 }
1525 
1527 {
1528  if (m_iconState)
1529  m_iconState->DisplayState("decoders");
1530  m_logList->Reset();
1531  QString displayhelp = tr("Available hardware decoders for video playback.");
1532  if (m_helpText)
1533  m_helpText->SetText(displayhelp);
1534  if (m_justHelpText)
1535  m_justHelpText->SetText(displayhelp);
1536 
1537  QStringList decoders = MythCodecContext::GetDecoderDescription();
1538  if (decoders.isEmpty())
1539  {
1540  AddLogLine(tr("None"));
1541  }
1542  else
1543  {
1544  foreach (QString decoder, decoders)
1545  AddLogLine(decoder);
1546  }
1547 }
1548 
1550 {
1551  if (m_iconState)
1552  m_iconState->DisplayState("display");
1553  m_logList->Reset();
1554  QString displayhelp = tr("Display and rendering information.");
1555  if (m_helpText)
1556  m_helpText->SetText(displayhelp);
1557  if (m_justHelpText)
1558  m_justHelpText->SetText(displayhelp);
1559 
1560  QStringList desc = MythDisplay::GetDescription();
1561  for (auto it = desc.cbegin(); it != desc.cend(); ++it)
1562  AddLogLine(*it);
1563  AddLogLine("");
1564 
1566  if (render)
1567  {
1569  if (gl && (gl->GetExtraFeatures() & kGLNVMemory))
1570  {
1571  auto UpdateGPUMem = [](StatusBoxItem *Item)
1572  {
1573  int total = 0;
1574  int available = 0;
1576  if (opengl)
1577  opengl->GetGPUMemory(available, total);
1578  if (total > 0)
1579  {
1580  int percent = static_cast<int>((available / static_cast<float>(total) * 100.0F));
1581  Item->SetText(tr("GPU Memory: %1 MB total, %2 MB used, %3 MB (or %4%) free")
1582  .arg(total).arg(total - available).arg(available).arg(percent));
1583  }
1584  };
1585  StatusBoxItem* gpumem = AddLogLine("");
1586  UpdateGPUMem(gpumem);
1587  connect(gpumem, &StatusBoxItem::UpdateRequired, UpdateGPUMem);
1588  gpumem->Start();
1589  }
1590 
1591  desc = render->GetDescription();
1592  for (auto it = desc.cbegin(); it != desc.cend(); ++it)
1593  AddLogLine(*it);
1594  }
1595 }
1596 
1600 void StatusBox::doAutoExpireList(bool updateExpList)
1601 {
1602  if (m_iconState)
1603  m_iconState->DisplayState("autoexpire");
1604  m_logList->Reset();
1605 
1606  QString helpmsg(tr("The AutoExpire List shows all recordings "
1607  "which may be expired and the order of "
1608  "their expiration. Recordings at the top "
1609  "of the list will be expired first."));
1610  if (m_helpText)
1611  m_helpText->SetText(helpmsg);
1612  if (m_justHelpText)
1613  m_justHelpText->SetText(helpmsg);
1614 
1615  QString contentLine;
1616  QString detailInfo;
1617  QString staticInfo;
1618  long long totalSize(0);
1619  long long liveTVSize(0);
1620  int liveTVCount(0);
1621  long long deletedGroupSize(0);
1622  int deletedGroupCount(0);
1623 
1624  vector<ProgramInfo *>::iterator it;
1625 
1626  if (updateExpList)
1627  {
1628  for (it = m_expList.begin(); it != m_expList.end(); ++it)
1629  delete *it;
1630  m_expList.clear();
1631 
1633  }
1634 
1635  for (it = m_expList.begin(); it != m_expList.end(); ++it)
1636  {
1637  ProgramInfo *pginfo = *it;
1638 
1639  totalSize += pginfo->GetFilesize();
1640  if (pginfo->GetRecordingGroup() == "LiveTV")
1641  {
1642  liveTVSize += pginfo->GetFilesize();
1643  liveTVCount++;
1644  }
1645  else if (pginfo->GetRecordingGroup() == "Deleted")
1646  {
1647  deletedGroupSize += pginfo->GetFilesize();
1648  deletedGroupCount++;
1649  }
1650  }
1651 
1652  staticInfo = tr("%n recording(s) consuming %1 (is) allowed to expire\n", "",
1653  m_expList.size()).arg(sm_str(totalSize / 1024));
1654 
1655  if (liveTVCount)
1656  staticInfo += tr("%n (is) LiveTV and consume(s) %1\n", "", liveTVCount)
1657  .arg(sm_str(liveTVSize / 1024));
1658 
1659  if (deletedGroupCount)
1660  {
1661  staticInfo += tr("%n (is) Deleted and consume(s) %1\n", "",
1662  deletedGroupCount)
1663  .arg(sm_str(deletedGroupSize / 1024));
1664  }
1665 
1666  for (it = m_expList.begin(); it != m_expList.end(); ++it)
1667  {
1668  ProgramInfo *pginfo = *it;
1669  QDateTime starttime = pginfo->GetRecordingStartTime();
1670  QDateTime endtime = pginfo->GetRecordingEndTime();
1671  contentLine =
1673  starttime, MythDate::kDateFull | MythDate::kSimplify) + " - ";
1674 
1675  contentLine +=
1676  "(" + ProgramInfo::i18n(pginfo->GetRecordingGroup()) + ") ";
1677 
1678  contentLine += pginfo->GetTitle() +
1679  " (" + sm_str(pginfo->GetFilesize() / 1024) + ")";
1680 
1681  detailInfo =
1684  " - " +
1687 
1688  detailInfo += " (" + sm_str(pginfo->GetFilesize() / 1024) + ")";
1689 
1690  detailInfo += " (" + ProgramInfo::i18n(pginfo->GetRecordingGroup()) + ")";
1691 
1692  detailInfo += "\n" + pginfo->toString(ProgramInfo::kTitleSubtitle, " - ");
1693 
1694  AddLogLine(contentLine, staticInfo, detailInfo,
1695  staticInfo + detailInfo);
1696  }
1697 }
1698 
1700 
1701 /* vim: set expandtab tabstop=4 shiftwidth=4: */
void doScheduleStatus()
Definition: statusbox.cpp:603
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:783
static void disk_usage_with_rec_time_kb(QStringList &out, long long total, long long used, long long free, const recprof2bps_t &prof2bps)
Definition: statusbox.cpp:1134
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
Definition: statusbox.cpp:187
void customEvent(QEvent *event) override
Definition: statusbox.cpp:385
int GetExtraFeatures(void) const
MythUIButtonList * m_categoryList
Definition: statusbox.h:82
Error State, if we ever try to enter this state errored is set.
Definition: tv.h:54
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:864
static QStringList GetDescription(void)
Watching LiveTV is the state for when we are watching a recording and the user has control over the c...
Definition: tv.h:63
vector< ProgramInfo * > m_expList
Definition: statusbox.h:88
static bool DeleteJob(int jobID)
Definition: jobqueue.cpp:875
Dialog asking for user confirmation.
void doMachineStatus()
Show machine status.
Definition: statusbox.cpp:1259
QString GetChannelName(void) const
This is the channel name in the local market, i.e.
Definition: programinfo.h:380
bool TranslateKeyPress(const QString &context, QKeyEvent *e, QStringList &actions, bool allowJumps=true)
Get a list of actions for a keypress in the given context.
virtual uint64_t GetFilesize(void) const
QString toString(MarkTypes type)
void doDisplayStatus()
Definition: statusbox.cpp:1549
bool Create(void) override
Definition: statusbox.cpp:81
void SaveSetting(const QString &key, int newValue)
QDateTime fromString(const QString &dtstr)
Converts kFilename && kISODate formats to QDateTime.
Definition: mythdate.cpp:30
static QString i18n(const QString &msg)
Translations for play,recording, & storage groups +.
static QString JobText(int jobType)
Definition: jobqueue.cpp:1115
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:125
int GetTopItemPos(void) const
StatusBox(MythScreenStack *parent)
Definition: statusbox.cpp:59
bool RemoteGetMemStats(int &totalMB, int &freeMB, int &totalVM, int &freeVM)
Definition: remoteutil.cpp:74
void doTunerStatus()
Definition: statusbox.cpp:807
Basic menu dialog, message and a list of options.
QString GetTitle(void) const
Definition: programinfo.h:355
int size(void) const
Definition: mythdbcon.h:203
void setHelpText(MythUIButtonListItem *item)
Definition: statusbox.cpp:245
virtual void SetText(const QString &text)
Definition: mythuitext.cpp:135
Holds information on a TV Program one might wish to record.
Definition: recordinginfo.h:34
QString toString(Verbosity v=kLongDescription, const QString &sep=":", const QString &grp="\"") const
void RemoteGetAllExpiringRecordings(vector< ProgramInfo * > &expiringlist)
Definition: remoteutil.cpp:168
MythScreenStack * GetStack(const QString &stackname)
Watching Recording is the state for when we are watching an in progress recording,...
Definition: tv.h:80
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
int m_minLevel
Definition: statusbox.h:92
size_t size(void) const
bool getMemStats(int &totalMB, int &freeMB, int &totalVM, int &freeVM)
Returns memory statistics in megabytes.
void getActualRecordedBPS(const QString &hostnames)
Fills in m_recordingProfilesBps w/ average bitrate from recorded table.
Definition: statusbox.cpp:1203
Do Today/Yesterday/Tomorrow transform.
Definition: mythdate.h:23
void clicked(MythUIButtonListItem *item)
Definition: statusbox.cpp:270
QString m_data
Definition: statusbox.cpp:40
QString GetChanNum(void) const
This is the channel "number", in the form 1, 1_2, 1-2, 1#1, etc.
Definition: programinfo.h:370
Q_DECLARE_METATYPE(GrabberScript *)
static guint32 * tmp
Definition: goom_core.c:35
QDateTime as_utc(const QDateTime &old_dt)
Returns copy of QDateTime with TimeSpec set to UTC.
Definition: mythdate.cpp:23
void RemoveItem(MythUIButtonListItem *item)
void BuildFocusList(void)
static int GetJobsInQueue(QMap< int, JobQueueEntry > &jobs, int findJobs=JOB_LIST_NOT_DONE)
Definition: jobqueue.cpp:1276
static Type kEventType
Definition: mythdialogbox.h:57
void doAutoExpireList(bool updateExpList=true)
Show list of recordings which may AutoExpire.
Definition: statusbox.cpp:1600
virtual QStringList GetDescription(void)
static bool StopJob(int jobID)
Definition: jobqueue.cpp:745
bool RemoteDeleteRecording(uint recordingID, bool forceMetadataDelete, bool forgetHistory)
Definition: remoteutil.cpp:110
QVariant value(int i) const
Definition: mythdbcon.h:198
static bool ResumeJob(int jobID)
Definition: jobqueue.cpp:727
MythScreenStack * m_popupStack
Definition: statusbox.h:90
Default local time.
Definition: mythdate.h:20
Holds information on recordings and videos.
Definition: programinfo.h:67
bool SendReceiveStringList(QStringList &strlist, bool quickTimeout=false, bool block=true)
Send a message to the backend and wait for a response.
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
Recording Only is a TVRec only state for when we are recording a program, but there is no one current...
Definition: tv.h:84
static QString sm_str(long long sizeKB, int prec=1)
Returns a short string describing an amount of space, choosing one of a number of useful units,...
Definition: statusbox.cpp:1092
static QList< FileSystemInfo > RemoteGetInfo(MythSocket *sock=nullptr)
StatusBoxItem * AddLogLine(const QString &line, const QString &help="", const QString &detail="", const QString &helpdetail="", const QString &state="", const QString &data="")
Definition: statusbox.cpp:149
void updateLog()
MythUIText * m_justHelpText
Definition: statusbox.h:81
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
QString GetSetting(const QString &key, const QString &defaultval="")
Default local time.
Definition: mythdate.h:16
QString m_helpdetail
Definition: statusbox.cpp:39
QString GetSubtitle(void) const
Definition: programinfo.h:357
static QString StatusText(int status)
Definition: jobqueue.cpp:1134
bool isActive(void) const
Definition: mythdbcon.h:204
recprof2bps_t m_recordingProfilesBps
Definition: statusbox.h:86
QString GetRecordingGroup(void) const
Definition: programinfo.h:413
unsigned int uint
Definition: compat.h:140
void doJobQueueStatus()
Definition: statusbox.cpp:1003
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:535
QString m_state
Definition: statusbox.cpp:41
#define getloadavg(x, y)
Definition: compat.h:322
void ApplyRecordRecGroupChange(const QString &newrecgroup)
Sets the recording group, both in this RecordingInfo and in the database.
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
Definition: mythdate.cpp:101
static bool LoadWindowFromXML(const QString &xmlfile, const QString &windowname, MythUIType *parent)
MythUIText * m_helpText
Definition: statusbox.h:80
MythRender * GetRenderDevice()
MythUIType * GetFocusWidget(void) const
void Reset() override
Reset the widget to it's original state, should not reset changes made by the theme.
MythMainWindow * GetMythMainWindow(void)
QString GetText(const QString &name="") const
QString m_help
Definition: statusbox.cpp:38
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
int GetNumSetting(const QString &key, int defaultval=0)
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:808
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
#define ADD_STATUS_LOG_LINE(rtype, fstate)
MythUIButtonList * m_logList
Definition: statusbox.h:83
void doListingsStatus()
Definition: statusbox.cpp:509
void updateLogList(MythUIButtonListItem *item)
Definition: statusbox.cpp:257
static bool PauseJob(int jobID)
Definition: jobqueue.cpp:718
uint GetChanID(void) const
This is the unique key used in the database to locate tuning information.
Definition: programinfo.h:366
static MythRenderOpenGL * GetOpenGLRender(void)
bool RemoteUndeleteRecording(uint recordingID)
Definition: remoteutil.cpp:139
void UpdateRequired(StatusBoxItem *Item)
A slave's sleep status is undefined when it has never connected to the master backend or is not able ...
Definition: tv.h:117
bool DisplayState(const QString &name)
QDateTime GetRecordingStartTime(void) const
Approximate time the recording started.
Definition: programinfo.h:398
QDateTime GetRecordingEndTime(void) const
Approximate time the recording should have ended, did end, or is intended to end.
Definition: programinfo.h:406
void SetItemCurrent(MythUIButtonListItem *item)
void Start(int Interval=1)
Definition: statusbox.cpp:44
static bool ChangeJobStatus(int jobID, int newStatus, const QString &comment="")
Definition: jobqueue.cpp:986
bool m_isBackendActive
Definition: statusbox.h:94
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:603
bool RemoteGetUptime(time_t &uptime)
Definition: remoteutil.cpp:54
QString m_line
Definition: statusbox.cpp:36
static QString usage_str_mb(float total, float used, float free)
Definition: statusbox.cpp:1128
static QString uptimeStr(time_t uptime)
Definition: statusbox.cpp:1171
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:179
Screen in which all other widgets are contained and rendered.
int GetCurrentPos() const
bool LoadFromScheduler(AutoDeleteDeque< TYPE * > &destination, bool &hasConflicts, const QString &altTable="", int recordid=-1)
Definition: programinfo.h:877
void doDecoderStatus()
Definition: statusbox.cpp:1526
static enum JobStatus GetJobStatus(int jobID)
Definition: jobqueue.cpp:1522
QString m_detail
Definition: statusbox.cpp:37
void doLogEntries()
Definition: statusbox.cpp:948
Default local time.
Definition: mythdate.h:21
QString GetHostName(void)
Event dispatched from MythUI modal dialogs to a listening class containing a result of some form.
Definition: mythdialogbox.h:41
MythUIType * GetChild(const QString &name) const
Get a named child of this UIType.
Definition: mythuitype.cpp:130
static QString toString(Type recstatus, uint id)
Converts "recstatus" into a short (unreadable) string.
Definition: recStatus.cpp:39
bool getUptime(time_t &uptime)
Returns uptime statistics.
~StatusBox(void) override
Definition: statusbox.cpp:74
MythUIStateType * m_iconState
Definition: statusbox.h:84
MythUIButtonListItem * GetItemCurrent() const
QMap< QString, unsigned int > recprof2bps_t
Definition: statusbox.h:21
bool GetGPUMemory(int &Available, int &Total)
void Init(void) override
Used after calling Load() to assign data to widgets and other UI initilisation which is prohibited in...
Definition: statusbox.cpp:111
static QStringList GetDecoderDescription(void)
static QString usage_str_kb(long long total, long long used, long long free)
Definition: statusbox.cpp:1113
bool RemoteGetLoad(double load[3])
Definition: remoteutil.cpp:39