#include #include #include #include #include #include #include #include #include #include #include using namespace std; #include "config.h" #include "statusbox.h" #include "mythcontext.h" #include "remoteutil.h" #include "programinfo.h" #include "tv.h" #include "jobqueue.h" #include "util.h" #include "mythdbcon.h" #include "cardutil.h" #define REC_CAN_BE_DELETED(rec) \ ((((rec)->programflags & FL_INUSEPLAYING) == 0) && \ ((((rec)->programflags & FL_INUSERECORDING) == 0) || \ ((rec)->recgroup != "LiveTV"))) /** \class StatusBox * \brief Reports on various status items. * * StatusBox reports on the listing status, that is how far * into the future program listings exits. It also reports * on the status of each tuner, the log entries, the status * of the job queue, and the machine status. */ StatusBox::StatusBox(MythMainWindow *parent, const char *name) : MythDialog(parent, name), errored(false) { // Set this value to the number of items in icon_list // to prevent scrolling off the bottom int item_count = 0; dateFormat = gContext->GetSetting("ShortDateFormat", "M/d"); timeFormat = gContext->GetSetting("TimeFormat", "h:mm AP"); timeDateFormat = timeFormat + " " + dateFormat; setNoErase(); LoadTheme(); if (IsErrored()) return; icon_list->SetItemText(item_count++, QObject::tr("Listings Status")); icon_list->SetItemText(item_count++, QObject::tr("Tuner Status")); icon_list->SetItemText(item_count++, QObject::tr("Log Entries")); icon_list->SetItemText(item_count++, QObject::tr("Job Queue")); icon_list->SetItemText(item_count++, QObject::tr("Machine Status")); icon_list->SetItemText(item_count++, QObject::tr("AutoExpire List")); icon_list->SetItemCurrent(0); icon_list->SetActive(true); QStringList strlist; strlist << "QUERY_IS_ACTIVE_BACKEND"; strlist << gContext->GetHostName(); gContext->SendReceiveStringList(strlist); if (QString(strlist[0]) == "FALSE") isBackend = false; else if (QString(strlist[0]) == "TRUE") isBackend = true; else isBackend = false; VERBOSE(VB_NETWORK, QString("QUERY_IS_ACTIVE_BACKEND=%1").arg(strlist[0])); max_icons = item_count; inContent = false; contentPos = 0; contentTotalLines = 0; contentSize = 0; contentMid = 0; min_level = gContext->GetNumSetting("LogDefaultView",5); my_parent = parent; clicked(); gContext->addCurrentLocation("StatusBox"); } StatusBox::~StatusBox(void) { gContext->removeCurrentLocation(); } void StatusBox::paintEvent(QPaintEvent *e) { QRect r = e->rect(); if (r.intersects(TopRect)) updateTopBar(); if (r.intersects(SelectRect)) updateSelector(); if (r.intersects(ContentRect)) updateContent(); } void StatusBox::updateContent() { QRect pr = ContentRect; QPixmap pix(pr.size()); pix.fill(this, pr.topLeft()); QPainter tmp(&pix); QPainter p(this); // Normalize the variables here and set the contentMid contentSize = list_area->GetItems(); if (contentSize > contentTotalLines) contentSize = contentTotalLines; contentMid = contentSize / 2; int startPos = 0; int highlightPos = 0; if (contentPos < contentMid) { startPos = 0; highlightPos = contentPos; } else if (contentPos >= (contentTotalLines - contentMid)) { startPos = contentTotalLines - contentSize; highlightPos = contentSize - (contentTotalLines - contentPos); } else if (contentPos >= contentMid) { startPos = contentPos - contentMid; highlightPos = contentMid; } if (content == NULL) return; LayerSet *container = content; list_area->ResetList(); for (int x = startPos; (x - startPos) <= contentSize; x++) { if (contentLines.contains(x)) { list_area->SetItemText(x - startPos, contentLines[x]); if (contentFont.contains(x)) list_area->EnableForcedFont(x - startPos, contentFont[x]); } } list_area->SetItemCurrent(highlightPos); if (inContent) { helptext->SetText(contentDetail[contentPos]); update(TopRect); } list_area->SetUpArrow((startPos > 0) && (contentSize < contentTotalLines)); list_area->SetDownArrow((startPos + contentSize) < contentTotalLines); container->Draw(&tmp, 0, 0); container->Draw(&tmp, 1, 0); container->Draw(&tmp, 2, 0); container->Draw(&tmp, 3, 0); container->Draw(&tmp, 4, 0); container->Draw(&tmp, 5, 0); container->Draw(&tmp, 6, 0); container->Draw(&tmp, 7, 0); container->Draw(&tmp, 8, 0); tmp.end(); p.drawPixmap(pr.topLeft(), pix); } void StatusBox::updateSelector() { QRect pr = SelectRect; QPixmap pix(pr.size()); pix.fill(this, pr.topLeft()); QPainter tmp(&pix); QPainter p(this); if (selector == NULL) return; LayerSet *container = selector; container->Draw(&tmp, 0, 0); container->Draw(&tmp, 1, 0); container->Draw(&tmp, 2, 0); container->Draw(&tmp, 3, 0); container->Draw(&tmp, 4, 0); container->Draw(&tmp, 5, 0); container->Draw(&tmp, 6, 0); container->Draw(&tmp, 7, 0); container->Draw(&tmp, 8, 0); tmp.end(); p.drawPixmap(pr.topLeft(), pix); } void StatusBox::updateTopBar() { QRect pr = TopRect; QPixmap pix(pr.size()); pix.fill(this, pr.topLeft()); QPainter tmp(&pix); QPainter p(this); if (topbar == NULL) return; LayerSet *container = topbar; container->Draw(&tmp, 0, 0); tmp.end(); p.drawPixmap(pr.topLeft(), pix); } void StatusBox::LoadTheme() { int screenheight = 0, screenwidth = 0; float wmult = 0, hmult = 0; gContext->GetScreenSettings(screenwidth, wmult, screenheight, hmult); theme = new XMLParse(); theme->SetWMult(wmult); theme->SetHMult(hmult); if (!theme->LoadTheme(xmldata, "status", "status-")) { VERBOSE(VB_IMPORTANT, "StatusBox: Unable to load theme."); errored = true; return; } for (QDomNode child = xmldata.firstChild(); !child.isNull(); child = child.nextSibling()) { QDomElement e = child.toElement(); if (!e.isNull()) { if (e.tagName() == "font") { theme->parseFont(e); } else if (e.tagName() == "container") { QRect area; QString name; int context; theme->parseContainer(e, name, context, area); if (name.lower() == "topbar") TopRect = area; if (name.lower() == "selector") SelectRect = area; if (name.lower() == "content") ContentRect = area; } else { QString msg = QString(tr("The theme you are using contains an " "unknown element ('%1'). It will be ignored")) .arg(e.tagName()); VERBOSE(VB_IMPORTANT, msg); errored = true; } } } selector = theme->GetSet("selector"); if (!selector) { VERBOSE(VB_IMPORTANT, "StatusBox: Failed to get selector container."); errored = true; } icon_list = (UIListType*)selector->GetType("icon_list"); if (!icon_list) { VERBOSE(VB_IMPORTANT, "StatusBox: Failed to get icon list area."); errored = true; } content = theme->GetSet("content"); if (!content) { VERBOSE(VB_IMPORTANT, "StatusBox: Failed to get content container."); errored = true; } list_area = (UIListType*)content->GetType("list_area"); if (!list_area) { VERBOSE(VB_IMPORTANT, "StatusBox: Failed to get list area."); errored = true; } topbar = theme->GetSet("topbar"); if (!topbar) { VERBOSE(VB_IMPORTANT, "StatusBox: Failed to get topbar container."); errored = true; } heading = (UITextType*)topbar->GetType("heading"); if (!heading) { VERBOSE(VB_IMPORTANT, "StatusBox: Failed to get heading area."); errored = true; } helptext = (UITextType*)topbar->GetType("helptext"); if (!helptext) { VERBOSE(VB_IMPORTANT, "StatusBox: Failed to get helptext area."); errored = true; } } void StatusBox::keyPressEvent(QKeyEvent *e) { bool handled = false; QStringList actions; gContext->GetMainWindow()->TranslateKeyPress("Status", e, actions); for (unsigned int i = 0; i < actions.size() && !handled; i++) { QString action = actions[i]; QString currentItem; QRegExp logNumberKeys( "^[12345678]$" ); currentItem = icon_list->GetItemText(icon_list->GetCurrentItem()); handled = true; if (action == "SELECT") { clicked(); } else if (action == "MENU") { if ((inContent) && (currentItem == QObject::tr("Log Entries"))) { int retval = MythPopupBox::show2ButtonPopup(my_parent, QString("AckLogEntry"), QObject::tr("Acknowledge all log entries at " "this priority level or lower?"), QObject::tr("Yes"), QObject::tr("No"), 0); if (retval == 0) { MSqlQuery query(MSqlQuery::InitCon()); query.prepare("UPDATE mythlog SET acknowledged = 1 " "WHERE priority <= :PRIORITY ;"); query.bindValue(":PRIORITY", min_level); query.exec(); doLogEntries(); } } else if ((inContent) && (currentItem == QObject::tr("Job Queue"))) { clicked(); } } else if (action == "UP") { if (inContent) { if (contentPos > 0) contentPos--; update(ContentRect); } else { if (icon_list->GetCurrentItem() > 0) icon_list->SetItemCurrent(icon_list->GetCurrentItem()-1); clicked(); setHelpText(); update(SelectRect); } } else if (action == "DOWN") { if (inContent) { if (contentPos < (contentTotalLines - 1)) contentPos++; update(ContentRect); } else { if (icon_list->GetCurrentItem() < (max_icons - 1)) icon_list->SetItemCurrent(icon_list->GetCurrentItem()+1); clicked(); setHelpText(); update(SelectRect); } } else if (action == "PAGEUP" && inContent) { contentPos -= contentSize; if (contentPos < 0) contentPos = 0; update(ContentRect); } else if (action == "PAGEDOWN" && inContent) { contentPos += contentSize; if (contentPos > (contentTotalLines - 1)) contentPos = contentTotalLines - 1; update(ContentRect); } else if ((action == "RIGHT") && (!inContent) && ((contentTotalLines > contentSize) || (doScroll))) { clicked(); inContent = true; contentPos = 0; icon_list->SetActive(false); list_area->SetActive(true); update(SelectRect); update(ContentRect); } else if (action == "LEFT") { if (inContent) { inContent = false; contentPos = 0; list_area->SetActive(false); icon_list->SetActive(true); setHelpText(); update(SelectRect); update(ContentRect); } else { if (gContext->GetNumSetting("UseArrowAccels", 1)) accept(); } } else if ((currentItem == QObject::tr("Log Entries")) && (logNumberKeys.search(action) == 0)) { min_level = action.toInt(); helptext->SetText(QObject::tr("Setting priority level to %1") .arg(min_level)); update(TopRect); doLogEntries(); } else handled = false; } if (!handled) MythDialog::keyPressEvent(e); } void StatusBox::setHelpText() { if (inContent) { helptext->SetText(contentDetail[contentPos]); } else { topbar->ClearAllText(); QString currentItem; currentItem = icon_list->GetItemText(icon_list->GetCurrentItem()); if (currentItem == QObject::tr("Listings Status")) helptext->SetText(QObject::tr("Listings Status shows the latest " "status information from " "mythfilldatabase")); if (currentItem == QObject::tr("Tuner Status")) helptext->SetText(QObject::tr("Tuner Status shows the current " "information about the state of " "backend tuner cards")); if (currentItem == QObject::tr("DVB Status")) helptext->SetText(QObject::tr("DVB Status shows the quality " "statistics of all DVB cards, if " "present")); if (currentItem == QObject::tr("Log Entries")) helptext->SetText(QObject::tr("Log Entries shows any unread log " "entries from the system if you " "have logging enabled")); if (currentItem == QObject::tr("Job Queue")) helptext->SetText(QObject::tr("Job Queue shows any jobs currently " "in Myth's Job Queue such as a " "commercial flagging job.")); if (currentItem == QObject::tr("Machine Status")) { QString machineStr = QObject::tr("Machine Status shows " "some operating system " "statistics of this machine"); if (!isBackend) machineStr.append(" " + QObject::tr("and the MythTV server")); helptext->SetText(machineStr); } if (currentItem == QObject::tr("AutoExpire List")) helptext->SetText(QObject::tr("The AutoExpire List shows all " "recordings which may be expired and the order of their " "expiration. Recordings at the top of the list will be " "expired first.")); } update(TopRect); } void StatusBox::clicked() { QString currentItem = icon_list->GetItemText(icon_list->GetCurrentItem()); if (inContent) { if (currentItem == QObject::tr("Log Entries")) { int retval; retval = MythPopupBox::show2ButtonPopup(my_parent, QString("AckLogEntry"), QObject::tr("Acknowledge this log entry?"), QObject::tr("Yes"), QObject::tr("No"), 0); if (retval == 0) { MSqlQuery query(MSqlQuery::InitCon()); query.prepare("UPDATE mythlog SET acknowledged = 1 " "WHERE logid = :LOGID ;"); query.bindValue(":LOGID", contentData[contentPos]); query.exec(); doLogEntries(); } } else if (currentItem == QObject::tr("Job Queue")) { QStringList msgs; int jobStatus; int retval; jobStatus = JobQueue::GetJobStatus( contentData[contentPos].toInt()); if (jobStatus == JOB_QUEUED) { retval = MythPopupBox::show2ButtonPopup(my_parent, QString("JobQueuePopup"), QObject::tr("Delete Job?"), QObject::tr("Yes"), QObject::tr("No"), 1); if (retval == 0) { JobQueue::DeleteJob(contentData[contentPos].toInt()); doJobQueueStatus(); } } else if ((jobStatus == JOB_PENDING) || (jobStatus == JOB_STARTING) || (jobStatus == JOB_RUNNING)) { msgs << QObject::tr("Pause"); msgs << QObject::tr("Stop"); msgs << QObject::tr("No Change"); retval = MythPopupBox::showButtonPopup(my_parent, QString("JobQueuePopup"), QObject::tr("Job Queue Actions:"), msgs, 2); if (retval == 0) { JobQueue::PauseJob(contentData[contentPos].toInt()); doJobQueueStatus(); } else if (retval == 1) { JobQueue::StopJob(contentData[contentPos].toInt()); doJobQueueStatus(); } } else if (jobStatus == JOB_PAUSED) { msgs << QObject::tr("Resume"); msgs << QObject::tr("Stop"); msgs << QObject::tr("No Change"); retval = MythPopupBox::showButtonPopup(my_parent, QString("JobQueuePopup"), QObject::tr("Job Queue Actions:"), msgs, 2); if (retval == 0) { JobQueue::ResumeJob(contentData[contentPos].toInt()); doJobQueueStatus(); } else if (retval == 1) { JobQueue::StopJob(contentData[contentPos].toInt()); doJobQueueStatus(); } } else if (jobStatus & JOB_DONE) { retval = MythPopupBox::show2ButtonPopup(my_parent, QString("JobQueuePopup"), QObject::tr("Requeue Job?"), QObject::tr("Yes"), QObject::tr("No"), 1); if (retval == 0) { JobQueue::ChangeJobStatus(contentData[contentPos].toInt(), JOB_QUEUED); doJobQueueStatus(); } } } else if (currentItem == QObject::tr("AutoExpire List")) { ProgramInfo* rec; rec = expList[contentPos]; if (rec) { QStringList msgs; int retval; msgs << QObject::tr("Delete Now"); msgs << QObject::tr("Disable AutoExpire"); msgs << QObject::tr("No Change"); retval = MythPopupBox::showButtonPopup(my_parent, QString("AutoExpirePopup"), QObject::tr("AutoExpire Actions:"), msgs, 2); if (retval == 0 && REC_CAN_BE_DELETED(rec)) { RemoteDeleteRecording(rec, false, false); } else if (retval == 1) { rec->SetAutoExpire(0); if ((rec)->recgroup == "LiveTV") rec->ApplyRecordRecGroupChange("Default"); } // Update list, prevent selected item going off bottom doAutoExpireList(); if (contentPos >= (int)expList.size()) contentPos = max((int)expList.size()-1,0); } } return; } // Clear all visible content elements here // I'm sure there's a better way to do this but I can't find it content->ClearAllText(); list_area->ResetList(); contentLines.clear(); contentDetail.clear(); contentFont.clear(); contentData.clear(); if (currentItem == QObject::tr("Listings Status")) doListingsStatus(); else if (currentItem == QObject::tr("Tuner Status")) doTunerStatus(); else if (currentItem == QObject::tr("Log Entries")) doLogEntries(); else if (currentItem == QObject::tr("Job Queue")) doJobQueueStatus(); else if (currentItem == QObject::tr("Machine Status")) doMachineStatus(); else if (currentItem == QObject::tr("AutoExpire List")) doAutoExpireList(); } void StatusBox::doListingsStatus() { QString mfdLastRunStart, mfdLastRunEnd, mfdLastRunStatus, mfdNextRunStart; QString querytext, Status, DataDirectMessage; int DaysOfData; QDateTime qdtNow, GuideDataThrough; int count = 0; contentLines.clear(); contentDetail.clear(); contentFont.clear(); doScroll = false; qdtNow = QDateTime::currentDateTime(); MSqlQuery query(MSqlQuery::InitCon()); query.prepare("SELECT max(endtime) FROM program WHERE manualid=0;"); query.exec(); if (query.isActive() && query.size()) { query.next(); GuideDataThrough = QDateTime::fromString(query.value(0).toString(), Qt::ISODate); } mfdLastRunStart = gContext->GetSetting("mythfilldatabaseLastRunStart"); mfdLastRunEnd = gContext->GetSetting("mythfilldatabaseLastRunEnd"); mfdLastRunStatus = gContext->GetSetting("mythfilldatabaseLastRunStatus"); mfdNextRunStart = gContext->GetSetting("MythFillSuggestedRunTime"); DataDirectMessage = gContext->GetSetting("DataDirectMessage"); mfdNextRunStart.replace("T", " "); extern const char *myth_source_version; contentLines[count++] = QObject::tr("Myth version:") + " " + MYTH_BINARY_VERSION + " " + myth_source_version; contentLines[count++] = QObject::tr("Last mythfilldatabase guide update:"); contentLines[count++] = QObject::tr("Started: ") + mfdLastRunStart; if (mfdLastRunEnd >= mfdLastRunStart) //if end < start, it's still running. contentLines[count++] = QObject::tr("Finished: ") + mfdLastRunEnd; contentLines[count++] = QObject::tr("Result: ") + mfdLastRunStatus; if (mfdNextRunStart >= mfdLastRunStart) contentLines[count++] = QObject::tr("Suggested Next: ") + mfdNextRunStart; DaysOfData = qdtNow.daysTo(GuideDataThrough); if (GuideDataThrough.isNull()) { contentLines[count++] = ""; contentLines[count++] = QObject::tr("There's no guide data available!"); contentLines[count++] = QObject::tr("Have you run mythfilldatabase?"); } else { contentLines[count++] = QObject::tr("There is guide data until ") + QDateTime(GuideDataThrough) .toString("yyyy-MM-dd hh:mm"); if (DaysOfData > 0) { Status = QString("(%1 ").arg(DaysOfData); if (DaysOfData >1) Status += QObject::tr("days"); else Status += QObject::tr("day"); Status += ")."; contentLines[count++] = Status; } } if (DaysOfData <= 3) { contentLines[count++] = QObject::tr("WARNING: is mythfilldatabase " "running?"); } if (!DataDirectMessage.isNull()) { contentLines[count++] = QObject::tr("DataDirect Status: "); contentLines[count++] = DataDirectMessage; } contentTotalLines = count; update(ContentRect); } void StatusBox::doTunerStatus() { doScroll = true; contentLines.clear(); contentDetail.clear(); contentFont.clear(); MSqlQuery query(MSqlQuery::InitCon()); query.prepare( "SELECT cardid, cardtype, videodevice " "FROM capturecard WHERE parentid='0' ORDER BY cardid"); if (!query.exec() || !query.isActive()) { MythContext::DBError("StatusBox::doTunerStatus()", query); contentTotalLines = 0; update(ContentRect); return; } uint count = 0; while (query.next()) { int cardid = query.value(0).toInt(); QString cmd = QString("QUERY_REMOTEENCODER %1").arg(cardid); QStringList strlist = cmd; strlist << "GET_STATE"; gContext->SendReceiveStringList(strlist); int state = strlist[0].toInt(); QString status = ""; if (state == kState_Error) status = tr("is unavailable"); else if (state == kState_WatchingLiveTV) status = tr("is watching live TV"); else if (state == kState_RecordingOnly || state == kState_WatchingRecording) status = tr("is recording"); else status = tr("is not recording"); QString tun = tr("Tuner %1 ").arg(cardid); QString devlabel = CardUtil::GetDeviceLabel( cardid, query.value(1).toString(), query.value(2).toString()); contentLines[count] = tun + status; contentDetail[count] = tun + devlabel + " " + status; if (state == kState_RecordingOnly || state == kState_WatchingRecording) { strlist = QString("QUERY_RECORDER %1").arg(cardid); strlist << "GET_RECORDING"; gContext->SendReceiveStringList(strlist); ProgramInfo *proginfo = new ProgramInfo; proginfo->FromStringList(strlist, 0); status += " " + proginfo->title; status += "\n"; status += proginfo->subtitle; contentDetail[count] = tun + devlabel + " " + status; } count++; } contentTotalLines = count; update(ContentRect); } void StatusBox::doLogEntries(void) { QString line; int count = 0; doScroll = true; contentLines.clear(); contentDetail.clear(); contentFont.clear(); contentData.clear(); MSqlQuery query(MSqlQuery::InitCon()); query.prepare("SELECT logid, module, priority, logdate, host, " "message, details " "FROM mythlog WHERE acknowledged = 0 " "AND priority <= :PRIORITY ORDER BY logdate DESC;"); query.bindValue(":PRIORITY", min_level); query.exec(); if (query.isActive()) { while (query.next()) { line = QString("%1").arg(query.value(5).toString()); contentLines[count] = line; if (query.value(6).toString() != "") line = tr("On %1 %2 from %3.%4\n%5\n%6") .arg(query.value(3).toDateTime() .toString(dateFormat)) .arg(query.value(3).toDateTime() .toString(timeFormat)) .arg(query.value(4).toString()) .arg(query.value(1).toString()) .arg(query.value(5).toString()) .arg(QString::fromUtf8(query.value(6).toString())); else line = tr("On %1 %2 from %3.%4\n%5\nNo other details") .arg(query.value(3).toDateTime() .toString(dateFormat)) .arg(query.value(3).toDateTime() .toString(timeFormat)) .arg(query.value(4).toString()) .arg(query.value(1).toString()) .arg(query.value(5).toString()); contentDetail[count] = line; contentData[count++] = query.value(0).toString(); } } if (!count) { doScroll = false; contentLines[count++] = QObject::tr("No items found at priority " "level %1 or lower.") .arg(min_level); contentLines[count++] = QObject::tr("Use 1-8 to change priority " "level."); } contentTotalLines = count; if (contentPos > (contentTotalLines - 1)) contentPos = contentTotalLines - 1; update(ContentRect); } void StatusBox::doJobQueueStatus() { QMap jobs; QMap::Iterator it; int count = 0; QString detail; JobQueue::GetJobsInQueue(jobs, JOB_LIST_NOT_DONE | JOB_LIST_ERROR | JOB_LIST_RECENT); doScroll = true; contentLines.clear(); contentDetail.clear(); contentFont.clear(); contentData.clear(); if (jobs.size()) { for (it = jobs.begin(); it != jobs.end(); ++it) { QString chanid = it.data().chanid; QDateTime starttime = it.data().starttime; ProgramInfo *pginfo; pginfo = ProgramInfo::GetProgramFromRecorded(chanid, starttime); if (!pginfo) continue; detail = pginfo->title + "\n" + pginfo->channame + " " + pginfo->chanstr + " @ " + starttime.toString(timeDateFormat) + "\n" + tr("Job:") + " " + JobQueue::JobText(it.data().type) + " " + tr("Status: ") + JobQueue::StatusText(it.data().status); if (it.data().status != JOB_QUEUED) detail += " (" + it.data().hostname + ")"; detail += "\n" + it.data().comment; contentLines[count] = pginfo->title + " @ " + starttime.toString(timeDateFormat); contentDetail[count] = detail; contentData[count] = QString("%1").arg(it.data().id); if (it.data().status == JOB_ERRORED) contentFont[count] = "error"; else if (it.data().status == JOB_ABORTED) contentFont[count] = "warning"; count++; delete pginfo; } } else { contentLines[count++] = QObject::tr("Job Queue is currently empty."); doScroll = false; } contentTotalLines = count; update(ContentRect); } // Some helper routines for doMachineStatus() that format the output strings /** \fn sm_str(long long, int) * \brief Returns a short string describing an amount of space, choosing * one of a number of useful units, "TB", "GB", "MB", or "KB". * \param sizeKB Number of kilobytes. * \param prec Precision to use if we have less than ten of whatever * unit is chosen. */ static const QString sm_str(long long sizeKB, int prec=1) { if (sizeKB>1024*1024*1024) // Terabytes { double sizeGB = sizeKB/(1024*1024*1024.0); return QString("%1 TB").arg(sizeGB, 0, 'f', (sizeGB>10)?0:prec); } else if (sizeKB>1024*1024) // Gigabytes { double sizeGB = sizeKB/(1024*1024.0); return QString("%1 GB").arg(sizeGB, 0, 'f', (sizeGB>10)?0:prec); } else if (sizeKB>1024) // Megabytes { double sizeMB = sizeKB/1024.0; return QString("%1 MB").arg(sizeMB, 0, 'f', (sizeMB>10)?0:prec); } // Kilobytes return QString("%1 KB").arg(sizeKB); } static const QString usage_str_kb(long long total, long long used, long long free) { QString ret = QObject::tr("Unknown"); if (total > 0.0 && free > 0.0) { double percent = (100.0*free)/total; ret = QObject::tr("%1 total, %2 used, %3 (or %4%) free.") .arg(sm_str(total)).arg(sm_str(used)) .arg(sm_str(free)).arg(percent, 0, 'f', (percent >= 10.0) ? 0 : 2); } return ret; } static const QString usage_str_mb(float total, float used, float free) { return usage_str_kb((long long)(total*1024), (long long)(used*1024), (long long)(free*1024)); } static void disk_usage_with_rec_time_kb(QStringList& out, long long total, long long used, long long free, const recprof2bps_t& prof2bps) { const QString tail = QObject::tr(", using your %1 rate of %2 Kb/sec"); out<> 1) * 15; uint minLeft = ((free<<5)/bytesPerMin)<<5; minLeft = (minLeft/15)*15; uint hoursLeft = minLeft/60; if (hoursLeft > 3) out< 90) out< 0) { char buff[6]; QString dayLabel; if (days == 1) dayLabel = QObject::tr("day"); else dayLabel = QObject::tr("days"); sprintf(buff, "%d:%02d", hours, min); return str + QString("%1 %2, %3").arg(days).arg(dayLabel).arg(buff); } else { char buff[9]; sprintf(buff, "%d:%02d:%02d", hours, min, secs); return str + buff; } } /** \fn StatusBox::getActualRecordedBPS(QString hostnames) * \brief Fills in recordingProfilesBPS w/ average bitrate from recorded table */ void StatusBox::getActualRecordedBPS(QString hostnames) { recordingProfilesBPS.clear(); QString querystr; MSqlQuery query(MSqlQuery::InitCon()); querystr = "SELECT sum(filesize) * 8 / " "sum(((unix_timestamp(endtime) - unix_timestamp(starttime)))) " "AS avg_bitrate " "FROM recorded WHERE hostname in (%1) " "AND (unix_timestamp(endtime) - unix_timestamp(starttime)) > 300;"; query.prepare(querystr.arg(hostnames)); if (query.exec() && query.isActive() && query.size() > 0 && query.next() && query.value(0).toDouble() > 0) { recordingProfilesBPS[QObject::tr("average")] = (int)(query.value(0).toDouble()); } querystr = "SELECT max(filesize * 8 / " "(unix_timestamp(endtime) - unix_timestamp(starttime))) " "AS max_bitrate " "FROM recorded WHERE hostname in (%1) " "AND (unix_timestamp(endtime) - unix_timestamp(starttime)) > 300;"; query.prepare(querystr.arg(hostnames)); if (query.exec() && query.isActive() && query.size() > 0 && query.next() && query.value(0).toDouble() > 0) { recordingProfilesBPS[QObject::tr("maximum")] = (int)(query.value(0).toDouble()); } } /** \fn StatusBox::doMachineStatus() * \brief Show machine status. * * This returns statisics for master backend when using * a frontend only machine. And returns info on the current * system if frontend is running on a backend machine. * \bug We should report on all backends and the current frontend. */ void StatusBox::doMachineStatus() { int count(0); int totalM, usedM, freeM; // Physical memory int totalS, usedS, freeS; // Virtual memory (swap) time_t uptime; int detailBegin; QString detailString; int detailLoop; contentLines.clear(); contentDetail.clear(); contentFont.clear(); doScroll = true; detailBegin = count; detailString = ""; if (isBackend) contentLines[count] = QObject::tr("System") + ":"; else contentLines[count] = QObject::tr("This machine") + ":"; detailString += contentLines[count] + "\n"; count++; // uptime if (!getUptime(uptime)) uptime = 0; contentLines[count] = uptimeStr(uptime); // weighted average loads contentLines[count].append(". " + QObject::tr("Load") + ": "); double loads[3]; if (getloadavg(loads,3) == -1) contentLines[count].append(QObject::tr("unknown") + " - getloadavg() " + QObject::tr("failed")); else { char buff[30]; sprintf(buff, "%0.2lf, %0.2lf, %0.2lf", loads[0], loads[1], loads[2]); contentLines[count].append(QString(buff)); } detailString += contentLines[count] + "\n"; count++; // memory usage if (getMemStats(totalM, freeM, totalS, freeS)) { usedM = totalM - freeM; if (totalM > 0) { contentLines[count] = " " + QObject::tr("RAM") + ": " + usage_str_mb(totalM, usedM, freeM); detailString += contentLines[count] + "\n"; count++; } usedS = totalS - freeS; if (totalS > 0) { contentLines[count] = " " + QObject::tr("Swap") + ": " + usage_str_mb(totalS, usedS, freeS); detailString += contentLines[count] + "\n"; count++; } } for (detailLoop = detailBegin; detailLoop < count; detailLoop++) contentDetail[detailLoop] = detailString; detailBegin = count; detailString = ""; if (!isBackend) { contentLines[count] = QObject::tr("MythTV server") + ":"; detailString += contentLines[count] + "\n"; count++; // uptime if (!RemoteGetUptime(uptime)) uptime = 0; contentLines[count] = uptimeStr(uptime); // weighted average loads contentLines[count].append(". " + QObject::tr("Load") + ": "); float loads[3]; if (RemoteGetLoad(loads)) { char buff[30]; sprintf(buff, "%0.2f, %0.2f, %0.2f", loads[0], loads[1], loads[2]); contentLines[count].append(QString(buff)); } else contentLines[count].append(QObject::tr("unknown")); detailString += contentLines[count] + "\n"; count++; // memory usage if (RemoteGetMemStats(totalM, freeM, totalS, freeS)) { usedM = totalM - freeM; if (totalM > 0) { contentLines[count] = " " + QObject::tr("RAM") + ": " + usage_str_mb(totalM, usedM, freeM); detailString += contentLines[count] + "\n"; count++; } usedS = totalS - freeS; if (totalS > 0) { contentLines[count] = " " + QObject::tr("Swap") + ": " + usage_str_mb(totalS, usedS, freeS); detailString += contentLines[count] + "\n"; count++; } } } for (detailLoop = detailBegin; detailLoop < count; detailLoop++) contentDetail[detailLoop] = detailString; detailBegin = count; detailString = ""; // get free disk space QString hostnames; vector fsInfos = RemoteGetFreeSpace(); for (uint i=0; i::iterator it; for (it = expList.begin(); it != expList.end(); it++) delete *it; expList.clear(); RemoteGetAllExpiringRecordings(expList); for (it = expList.begin(); it != expList.end(); it++) { pginfo = *it; totalSize += pginfo->filesize; if (pginfo->recgroup == "LiveTV") { liveTVSize += pginfo->filesize; liveTVCount++; } } staticInfo = tr("%1 recordings consuming %2 are allowed to expire") .arg(expList.size()).arg(sm_str(totalSize / 1024)) + "\n"; if (liveTVCount) staticInfo += tr("%1 of these are LiveTV and consume %2") .arg(liveTVCount).arg(sm_str(liveTVSize / 1024)) + "\n"; else staticInfo += "\n"; for (it = expList.begin(); it != expList.end(); it++) { pginfo = *it; contentLine = pginfo->recstartts.toString(dateFormat) + " - "; if (pginfo->recgroup == "LiveTV") contentLine += "(" + tr("LiveTV") + ") "; contentLine += pginfo->title + " (" + sm_str(pginfo->filesize / 1024) + ")"; detailInfo = staticInfo; detailInfo += pginfo->recstartts.toString(timeDateFormat) + " - " + pginfo->recendts.toString(timeDateFormat); detailInfo += " (" + sm_str(pginfo->filesize / 1024) + ")"; if (pginfo->recgroup == "LiveTV") detailInfo += " (" + tr("LiveTV") + ")"; detailInfo += "\n" + pginfo->title; if (pginfo->subtitle != "") detailInfo += " - " + pginfo->subtitle + ""; contentLines[count] = contentLine; contentDetail[count] = detailInfo; count++; } contentTotalLines = count; update(ContentRect); } /* vim: set expandtab tabstop=4 shiftwidth=4: */