diff --git a/src/onedrive.d b/src/onedrive.d index bd944522..b0f7f3f9 100644 --- a/src/onedrive.d +++ b/src/onedrive.d @@ -1383,7 +1383,10 @@ class OneDriveApi { if (fileSize >= thresholdFileSize){ // Download Progress variables size_t expected_total_segments = 20; - ulong start_unix_time = Clock.currTime.toUnixTime(); + + // Time sensitive and ETA string items + SysTime currentTime = Clock.currTime(); + long start_unix_time = currentTime.toUnixTime(); int h, m, s; string etaString; bool barInit = false; @@ -1425,10 +1428,10 @@ class OneDriveApi { if (appConfig.getValueLong("rate_limit") > 0) { // User configured rate limit // How much data should be in each segment to qualify for 5% - ulong dataPerSegment = to!ulong(floor(double(dltotal)/expected_total_segments)); + size_t dataPerSegment = cast(size_t)(floor(double(dltotal)/expected_total_segments)); // How much data received do we need to validate against - ulong thisSegmentData = dataPerSegment * segmentCount; - ulong nextSegmentData = dataPerSegment * (segmentCount + 1); + size_t thisSegmentData = dataPerSegment * segmentCount; + size_t nextSegmentData = dataPerSegment * (segmentCount + 1); // Has the data that has been received in a 5% window that we need to increment the progress bar at if ((dlnow > thisSegmentData) && (dlnow < nextSegmentData) && (previousProgressPercent != currentDLPercent) || (dlnow == dltotal)) { @@ -1440,15 +1443,16 @@ class OneDriveApi { // Not 100% yet // Calculate the output segmentCount++; - auto eta = calc_eta(segmentCount, expected_total_segments, start_unix_time); - dur!"seconds"(eta).split!("hours", "minutes", "seconds")(h, m, s); - etaString = format!"| ETA %02d:%02d:%02d"( h, m, s); + // Generate ETA time output + etaString = formatETA(calc_eta(segmentCount, expected_total_segments, start_unix_time)); + // Calculate percentage string percentage = leftJustify(to!string(currentDLPercent) ~ "%", 5, ' '); addLogEntry(downloadLogEntry ~ percentage ~ etaString, ["consoleOnly"]); } else { // 100% done - ulong end_unix_time = Clock.currTime.toUnixTime(); - auto upload_duration = cast(int)(end_unix_time - start_unix_time); + SysTime endTime = Clock.currTime(); + long end_unix_time = endTime.toUnixTime(); + int upload_duration = cast(int)(end_unix_time - start_unix_time); dur!"seconds"(upload_duration).split!("hours", "minutes", "seconds")(h, m, s); etaString = format!"| DONE in %02d:%02d:%02d"( h, m, s); string percentage = leftJustify(to!string(currentDLPercent) ~ "%", 5, ' '); @@ -1472,15 +1476,16 @@ class OneDriveApi { // Not 100% yet // Calculate the output segmentCount++; - auto eta = calc_eta(segmentCount, expected_total_segments, start_unix_time); - dur!"seconds"(eta).split!("hours", "minutes", "seconds")(h, m, s); - etaString = format!"| ETA %02d:%02d:%02d"( h, m, s); + // Generate ETA time output + etaString = formatETA(calc_eta(segmentCount, expected_total_segments, start_unix_time)); + // Calculate percentage string percentage = leftJustify(to!string(currentDLPercent) ~ "%", 5, ' '); addLogEntry(downloadLogEntry ~ percentage ~ etaString, ["consoleOnly"]); } else { // 100% done - ulong end_unix_time = Clock.currTime.toUnixTime(); - auto upload_duration = cast(int)(end_unix_time - start_unix_time); + SysTime endTime = Clock.currTime(); + long end_unix_time = endTime.toUnixTime(); + int upload_duration = cast(int)(end_unix_time - start_unix_time); dur!"seconds"(upload_duration).split!("hours", "minutes", "seconds")(h, m, s); etaString = format!"| DONE in %02d:%02d:%02d"( h, m, s); string percentage = leftJustify(to!string(currentDLPercent) ~ "%", 5, ' '); diff --git a/src/sync.d b/src/sync.d index e2e3d9f5..2d53eb95 100644 --- a/src/sync.d +++ b/src/sync.d @@ -9371,6 +9371,15 @@ class SyncEngine { enum CHUNK_SIZE = 327_680L; // 320 KiB enum MAX_FRAGMENT_BYTES = 60L * 1_048_576L; // 60 MiB = 62,914,560 bytes + // Time sensitive and ETA string items + SysTime currentTime = Clock.currTime(); + long start_unix_time = currentTime.toUnixTime(); + int h, m, s; + string etaString; + + // Upload string template + string uploadLogEntry = "Uploading: " ~ uploadSessionData["localPath"].str ~ " ... "; + // Calculate base size using configured fragment size baseSize = appConfig.getValueLong("file_fragment_size") * 2^^20; @@ -9391,10 +9400,6 @@ class SyncEngine { // Estimate total number of expected fragments size_t expected_total_fragments = cast(size_t) ceil(double(thisFileSize) / double(fragmentSize)); - long start_unix_time = Clock.currTime.toUnixTime(); - int h, m, s; - string etaString; - string uploadLogEntry = "Uploading: " ~ uploadSessionData["localPath"].str ~ " ... "; // If we get a 404, create a new upload session and store it here JSONValue newUploadSession; @@ -9405,17 +9410,9 @@ class SyncEngine { fragmentCount++; if (debugLogging) {addLogEntry("Fragment: " ~ to!string(fragmentCount) ~ " of " ~ to!string(expected_total_fragments), ["debug"]);} - // What ETA string do we use? - auto eta = calc_eta((fragmentCount -1), expected_total_fragments, start_unix_time); - if (eta == 0) { - // Initial calculation ... - etaString = format!"| ETA --:--:--"; - } else { - // we have at least an ETA provided - dur!"seconds"(eta).split!("hours", "minutes", "seconds")(h, m, s); - etaString = format!"| ETA %02d:%02d:%02d"(h, m, s); - } - + // Generate ETA time output + etaString = formatETA(calc_eta((fragmentCount -1), expected_total_fragments, start_unix_time)); + // Calculate this progress output auto ratio = cast(double)(fragmentCount - 1) / expected_total_fragments; // Convert the ratio to a percentage and format it to two decimal places diff --git a/src/util.d b/src/util.d index 4f256a21..5ee33b14 100644 --- a/src/util.d +++ b/src/util.d @@ -1509,49 +1509,74 @@ string getUserName() { } // Calculate the ETA for when a 'large file' will be completed (upload & download operations) -int calc_eta(size_t counter, size_t iterations, ulong start_time) { - if (counter == 0) { - return 0; // Avoid division by zero - } +int calc_eta(size_t counter, size_t iterations, long start_time) { + if (counter == 0) { + return 0; // Avoid division by zero + } + + // Get the current time as a Unix timestamp (seconds since the epoch, January 1, 1970, 00:00:00 UTC) + SysTime currentTime = Clock.currTime(); + long current_time = currentTime.toUnixTime(); - double ratio = cast(double) counter / iterations; - auto current_time = Clock.currTime.toUnixTime(); - ulong duration = (current_time - start_time); + // 'start_time' must be less than 'current_time' otherwise ETA will have negative values + if (start_time > current_time) { + if (debugLogging) { + addLogEntry("Warning: start_time is in the future. Cannot calculate ETA.", ["debug"]); + } + return 0; + } + + // Calculate duration + long duration = (current_time - start_time); - // Segments left to download - auto segments_remaining = (iterations > counter) ? (iterations - counter) : 0; - - // Calculate the average time per iteration so far - double avg_time_per_iteration = cast(double) duration / counter; + // Calculate the ratio we are at + double ratio = cast(double) counter / iterations; - // Debug output for the ETA calculation + // Calculate segments left to download + auto segments_remaining = (iterations > counter) ? (iterations - counter) : 0; + + // Calculate the average time per iteration so far + double avg_time_per_iteration = cast(double) duration / counter; + + // Debug output for the ETA calculation if (debugLogging) { - addLogEntry("counter: " ~ to!string(counter), ["debug"]); - addLogEntry("iterations: " ~ to!string(iterations), ["debug"]); - addLogEntry("segments_remaining: " ~ to!string(segments_remaining), ["debug"]); - addLogEntry("ratio: " ~ format("%.2f", ratio), ["debug"]); - addLogEntry("start_time: " ~ to!string(start_time), ["debug"]); - addLogEntry("current_time: " ~ to!string(current_time), ["debug"]); - addLogEntry("duration: " ~ to!string(duration), ["debug"]); + addLogEntry("counter: " ~ to!string(counter), ["debug"]); + addLogEntry("iterations: " ~ to!string(iterations), ["debug"]); + addLogEntry("segments_remaining: " ~ to!string(segments_remaining), ["debug"]); + addLogEntry("ratio: " ~ format("%.2f", ratio), ["debug"]); + addLogEntry("start_time: " ~ to!string(start_time), ["debug"]); + addLogEntry("current_time: " ~ to!string(current_time), ["debug"]); + addLogEntry("duration: " ~ to!string(duration), ["debug"]); addLogEntry("avg_time_per_iteration: " ~ format("%.2f", avg_time_per_iteration), ["debug"]); } // Return the ETA or duration if (counter != iterations) { - auto eta_sec = avg_time_per_iteration * segments_remaining; + auto eta_sec = avg_time_per_iteration * segments_remaining; // ETA Debug if (debugLogging) { - addLogEntry("eta_sec: " ~ to!string(eta_sec), ["debug"]); - addLogEntry("estimated_total_time: " ~ to!string(avg_time_per_iteration * iterations), ["debug"]); + addLogEntry("eta_sec: " ~ to!string(eta_sec), ["debug"]); + addLogEntry("estimated_total_time: " ~ to!string(avg_time_per_iteration * iterations), ["debug"]); } // Return ETA - return eta_sec > 0 ? cast(int) ceil(eta_sec) : 0; - } else { + return eta_sec > 0 ? cast(int) ceil(eta_sec) : 0; + } else { // Return the average time per iteration for the last iteration - return cast(int) ceil(avg_time_per_iteration); + return cast(int) ceil(avg_time_per_iteration); } } +// Use the ETA value and return a formatted string in a consistent manner +string formatETA(int eta) { + // How do we format the ETA string. Guard against zero and negative values + if (eta <= 0) { + return "| ETA --:--:--"; + } + int h, m, s; + dur!"seconds"(eta).split!("hours", "minutes", "seconds")(h, m, s); + return format!"| ETA %02d:%02d:%02d"(h, m, s); +} + // Force Exit due to failure void forceExit() { // Allow any logging complete before we force exit