diff --git a/client/pers_file_xfer.cpp b/client/pers_file_xfer.cpp
index 211828d076..b9710d5ff0 100644
--- a/client/pers_file_xfer.cpp
+++ b/client/pers_file_xfer.cpp
@@ -1,6 +1,6 @@
// This file is part of BOINC.
// http://boinc.berkeley.edu
-// Copyright (C) 2008 University of California
+// Copyright (C) 2022 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
@@ -446,11 +446,13 @@ int PERS_FILE_XFER::write(MIOFILE& fout) {
if (fxp) {
fout.printf(
" \n"
+ " %f\n"
" %f\n"
" %f\n"
" %f\n"
" %s\n"
" \n",
+ estimated_xfer_time_remaining(),
fxp->bytes_xferred,
fxp->file_offset,
fxp->xfer_speed,
@@ -476,6 +478,24 @@ void PERS_FILE_XFER::suspend() {
fip->upload_offset = -1;
}
+// Determines the amount of time for a pfx to complete. Returns time in seconds.
+//
+double PERS_FILE_XFER::estimated_xfer_time_remaining() {
+ // The estimated transfer duration will be set to 0 (or, '---' as displayed in the Manager) in three conditions:
+ // 1. The pfx is complete.
+ // 2. The file has not started transferring.
+ // 3. If the transfer speed is 0. This is for conditions when xfer_speed has not been calculated yet
+ // (either from the transfer returning from suspension or the BOINC starting up).
+ if (pers_xfer_done || fxp==0 || fxp->xfer_speed==0) {
+ return 0;
+ }
+ double bytes_remaining = (fip->nbytes - last_bytes_xferred);
+ double est_duration = bytes_remaining / fxp->xfer_speed;
+ if (est_duration <= 0) est_duration = 1;
+ return est_duration;
+}
+
+
PERS_FILE_XFER_SET::PERS_FILE_XFER_SET(FILE_XFER_SET* p) {
file_xfers = p;
}
diff --git a/client/pers_file_xfer.h b/client/pers_file_xfer.h
index 64902d1740..78035f354c 100644
--- a/client/pers_file_xfer.h
+++ b/client/pers_file_xfer.h
@@ -1,6 +1,6 @@
// This file is part of BOINC.
// http://boinc.berkeley.edu
-// Copyright (C) 2008 University of California
+// Copyright (C) 2022 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
@@ -115,6 +115,7 @@ public:
int create_xfer();
int start_xfer();
void suspend();
+ double estimated_xfer_time_remaining();
};
class PERS_FILE_XFER_SET {
diff --git a/clientgui/ViewTransfers.cpp b/clientgui/ViewTransfers.cpp
index 49c2acf1dc..9a52ea27ea 100644
--- a/clientgui/ViewTransfers.cpp
+++ b/clientgui/ViewTransfers.cpp
@@ -1,6 +1,6 @@
// This file is part of BOINC.
// http://boinc.berkeley.edu
-// Copyright (C) 2008 University of California
+// Copyright (C) 2022 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
@@ -49,6 +49,7 @@
#define COLUMN_TIME 4
#define COLUMN_SPEED 5
#define COLUMN_STATUS 6
+#define COLUMN_TOCOMPLETION 7
// DefaultShownColumns is an array containing the
// columnIDs of the columns to be shown by default,
@@ -57,7 +58,7 @@
//
// For now, show all columns by default
static int DefaultShownColumns[] = { COLUMN_PROJECT, COLUMN_FILE, COLUMN_PROGRESS,
- COLUMN_SIZE, COLUMN_TIME, COLUMN_SPEED,
+ COLUMN_SIZE, COLUMN_TIME, COLUMN_TOCOMPLETION, COLUMN_SPEED,
COLUMN_STATUS};
// buttons in the "tasks" area
@@ -70,6 +71,7 @@ CTransfer::CTransfer() {
m_fBytesXferred = -1.0;
m_fTotalBytes = -1.0;
m_dTime = -1.0;
+ m_fTimeToCompletion = -1.0;
m_dSpeed = -1.0;
}
@@ -78,6 +80,12 @@ CTransfer::~CTransfer() {
m_strProjectName.Clear();
m_strFileName.Clear();
m_strStatus.Clear();
+ m_strProjectURL.Clear();
+ m_strProgress.Clear();
+ m_strSize.Clear();
+ m_strTime.Clear();
+ m_strTimeToCompletion.Clear();
+ m_strSpeed.Clear();
}
@@ -146,6 +154,14 @@ static bool CompareViewTransferItems(int iRowIndex1, int iRowIndex2) {
result = 1;
}
break;
+ case COLUMN_TOCOMPLETION:
+ if (transfer1->m_fTimeToCompletion < transfer2->m_fTimeToCompletion) {
+ result = -1;
+ }
+ else if (transfer1->m_fTimeToCompletion > transfer2->m_fTimeToCompletion) {
+ result = 1;
+ }
+ break;
case COLUMN_SPEED:
if (transfer1->m_dSpeed < transfer2->m_dSpeed) {
result = -1;
@@ -211,6 +227,7 @@ CViewTransfers::CViewTransfers(wxNotebook* pNotebook) :
m_aStdColNameOrder->Insert(_("Progress"), COLUMN_PROGRESS);
m_aStdColNameOrder->Insert(_("Size"), COLUMN_SIZE);
m_aStdColNameOrder->Insert(_("Elapsed"), COLUMN_TIME);
+ m_aStdColNameOrder->Insert(_("Remaining (estimated)"), COLUMN_TOCOMPLETION);
m_aStdColNameOrder->Insert(_("Speed"), COLUMN_SPEED);
m_aStdColNameOrder->Insert(_("Status"), COLUMN_STATUS);
@@ -226,6 +243,7 @@ CViewTransfers::CViewTransfers(wxNotebook* pNotebook) :
m_iStdColWidthOrder.Insert(60, COLUMN_PROGRESS);
m_iStdColWidthOrder.Insert(80, COLUMN_SIZE);
m_iStdColWidthOrder.Insert(80, COLUMN_TIME);
+ m_iStdColWidthOrder.Insert(100, COLUMN_TOCOMPLETION);
m_iStdColWidthOrder.Insert(80, COLUMN_SPEED);
m_iStdColWidthOrder.Insert(150, COLUMN_STATUS);
@@ -273,6 +291,10 @@ void CViewTransfers::AppendColumn(int columnID){
m_pListPane->AppendColumn((*m_aStdColNameOrder)[COLUMN_TIME],
wxLIST_FORMAT_LEFT, m_iStdColWidthOrder[COLUMN_TIME]);
break;
+ case COLUMN_TOCOMPLETION:
+ m_pListPane->AppendColumn((*m_aStdColNameOrder)[COLUMN_TOCOMPLETION],
+ wxLIST_FORMAT_RIGHT, m_iStdColWidthOrder[COLUMN_TOCOMPLETION]);
+ break;
case COLUMN_SPEED:
m_pListPane->AppendColumn((*m_aStdColNameOrder)[COLUMN_SPEED],
wxLIST_FORMAT_LEFT, m_iStdColWidthOrder[COLUMN_SPEED]);
@@ -498,6 +520,9 @@ wxString CViewTransfers::OnListGetItemText(long item, long column) const {
case COLUMN_TIME:
strBuffer = transfer->m_strTime;
break;
+ case COLUMN_TOCOMPLETION:
+ strBuffer = transfer->m_strTimeToCompletion;
+ break;
case COLUMN_SPEED:
strBuffer = transfer->m_strSpeed;
break;
@@ -632,6 +657,14 @@ bool CViewTransfers::SynchronizeCacheItem(wxInt32 iRowIndex, wxInt32 iColumnInde
bNeedRefresh = true;
}
break;
+ case COLUMN_TOCOMPLETION:
+ GetDocTimeToCompletion(m_iSortedIndexes[iRowIndex], fDocumentDouble);
+ if (fDocumentDouble != transfer->m_fTimeToCompletion) {
+ transfer->m_fTimeToCompletion = fDocumentDouble;
+ transfer->m_strTimeToCompletion = FormatTime(fDocumentDouble);
+ bNeedRefresh = true;
+ }
+ break;
case COLUMN_SPEED:
GetDocSpeed(m_iSortedIndexes[iRowIndex], fDocumentDouble);
if (fDocumentDouble != transfer->m_dSpeed) {
@@ -803,6 +836,22 @@ void CViewTransfers::GetDocTime(wxInt32 item, double& fBuffer) const {
}
}
+void CViewTransfers::GetDocTimeToCompletion(wxInt32 item, double& fBuffer) const {
+ FILE_TRANSFER* transfer = NULL;
+ CMainDocument* pDoc = wxGetApp().GetDocument();
+
+ if (pDoc) {
+ transfer = pDoc->file_transfer(item);
+ }
+
+ if (transfer) {
+ fBuffer = transfer->estimated_xfer_time_remaining;
+ }
+ else {
+ fBuffer = 0.0;
+ }
+}
+
void CViewTransfers::GetDocSpeed(wxInt32 item, double& fBuffer) const {
FILE_TRANSFER* transfer = NULL;
CMainDocument* pDoc = wxGetApp().GetDocument();
diff --git a/clientgui/ViewTransfers.h b/clientgui/ViewTransfers.h
index f0fa0989fb..914048288a 100644
--- a/clientgui/ViewTransfers.h
+++ b/clientgui/ViewTransfers.h
@@ -1,6 +1,6 @@
// This file is part of BOINC.
// http://boinc.berkeley.edu
-// Copyright (C) 2008 University of California
+// Copyright (C) 2022 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
@@ -37,12 +37,14 @@ public:
double m_fBytesXferred;
double m_fTotalBytes;
double m_dTime;
+ double m_fTimeToCompletion;
double m_dSpeed;
wxString m_strStatus;
wxString m_strProjectURL; // Used internally, not displayed
wxString m_strProgress;
wxString m_strSize;
wxString m_strTime;
+ wxString m_strTimeToCompletion;
wxString m_strSpeed;
};
@@ -95,6 +97,7 @@ protected:
void GetDocTotalBytes(wxInt32 item, double& fBuffer) const;
wxInt32 FormatSize( double fBytesSent, double fFileSize, wxString& strBuffer ) const;
void GetDocTime(wxInt32 item, double& fBuffer) const;
+ void GetDocTimeToCompletion(wxInt32 item, double& fBuffer) const;
void GetDocSpeed(wxInt32 item, double& fBuffer) const;
wxInt32 FormatSpeed( double fBuffer, wxString& strBuffer ) const;
void GetDocStatus(wxInt32 item, wxString& strBuffer) const;
diff --git a/lib/gui_rpc_client.h b/lib/gui_rpc_client.h
index 4bace5fb20..990b3c1bb6 100644
--- a/lib/gui_rpc_client.h
+++ b/lib/gui_rpc_client.h
@@ -1,6 +1,6 @@
// This file is part of BOINC.
// https://boinc.berkeley.edu
-// Copyright (C) 2020 University of California
+// Copyright (C) 2022 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
@@ -329,6 +329,7 @@ struct FILE_TRANSFER {
double next_request_time;
int status;
double time_so_far;
+ double estimated_xfer_time_remaining;
double bytes_xferred;
double file_offset;
double xfer_speed;
diff --git a/lib/gui_rpc_client_ops.cpp b/lib/gui_rpc_client_ops.cpp
index b804ee40ad..ef84f8f48e 100644
--- a/lib/gui_rpc_client_ops.cpp
+++ b/lib/gui_rpc_client_ops.cpp
@@ -1,6 +1,6 @@
// This file is part of BOINC.
// https://boinc.berkeley.edu
-// Copyright (C) 2020 University of California
+// Copyright (C) 2022 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
@@ -784,6 +784,7 @@ int FILE_TRANSFER::parse(XML_PARSER& xp) {
if (xp.parse_double("next_request_time", next_request_time)) continue;
if (xp.parse_int("status", status)) continue;
if (xp.parse_double("time_so_far", time_so_far)) continue;
+ if (xp.parse_double("estimated_xfer_time_remaining", estimated_xfer_time_remaining)) continue;
if (xp.parse_double("last_bytes_xferred", bytes_xferred)) continue;
if (xp.parse_double("file_offset", file_offset)) continue;
if (xp.parse_double("xfer_speed", xfer_speed)) continue;
@@ -809,6 +810,7 @@ void FILE_TRANSFER::clear() {
next_request_time = 0;
status = 0;
time_so_far = 0;
+ estimated_xfer_time_remaining = 0;
bytes_xferred = 0;
file_offset = 0;
xfer_speed = 0;
@@ -1965,7 +1967,7 @@ int RPC_CLIENT::run_benchmarks() {
// start or stop a graphics app on behalf of the screensaver.
// (needed for Mac OS X 10.15+)
//
-//