Friday, November 17, 2006

Calculating the Expected Time Left in a Process In C#

At times we need to write a lengthy process that needs to keep the user informed of how much longer it will be running. You may be parsing a large file, uploading one or more files to an ftp site what ever the case the user needs to know the status. Not knowing what is happening in the process could lead the user to click cancel, terminate the process, or whatever one does when they think the application is locked up when in fact it is not.

Calculating the remaining time left is actually pretty simple, lets take file upload as an example:

Note: I'm not going to focus too much on the process of getting the byte counts and setting progress etc, this post is designed more on the calculation itself.

First create a DateTime variable to hold our start time. (I will use blue to signify code)

DateTime startedTime = DateTime.Now;

Next, we need to keep track of the current progress, this is normally handled using a delegate and event allowing a thread to inform the app it is progressing.

It could look something like so:

public delegate void ProgressChanged(int CurrByte, int MaxByte);

...

public event ProgressChanged OnProgressChanged;


(Some Class)
int MaxByte = GetByteCount(ourProcessedFile);

(Some Loop uploading the file)
CurrByte = stream.Read(byte[], 0, length);
... do something

// Thread informs app of change
if (OnProgressChanged != null)
OnProgressChanged(CurrByte, MaxByte);


Ok so the thread has reported a change in progress, we must now calculate how much more time this is going to take. Remember at the start of the process we stored the DateTime which we are going to use in our calculation.

First, Get how many seconds it has been

// Get Elapsed Time
TimeSpan timeSpan = DateTime.Now - startedTime;

int intHours = 0;
int intMinutes = 0;
int intSeconds = 0;
int EstimatedTime = 0; // Holds total time in seconds

int ByteProcessTime = 0;


// Calculate how many bytes we are processing per second
ByteProcessTime = Convert.ToInt32(CurrByte / timeSpan.TotalSeconds);

// Calculate how many seconds are remaining
EstimatedTime = Convert.ToInt32((MaxByte - CurrByte) / ByteProcessTime);

EstimatedTime is excluding the bytes already processed (MaxByte - CurrByte) and dividing the total of remaining bytes by the number of bytes we can process a second. This results in the total time remaining in seconds. Now we need to make this more user friendly by covnerting this to Hours, Minutes, Seconds.

// Get breakdown
if (EstimatedTime > 60) // greater than 60 seconds
{
intMinutes = Convert.ToInt32(EstimatedTime / 60); // total minutes
intSeconds = Convert.ToInt32(EstimatedTime % 60); // additional seconds

if (intMinutes > 60) // get hours
{
intHours = Convert.ToInt32(intMinutes / 60); // total hours
intMinutes = Convert.ToInt32(intMinutes % 60); // remaining minutes
}
} else
intSeconds = EstimatedTime; // less than a minute left

// Now format this for user display
string TimeFrameRemaining = string.Format("Estimated Time Remaining: {0:#0}:{1:#0}:{2:#0}", intHours, intMinutes, intSeconds);

This sums it up, I started with minutes because it is easier to calculate minutes from seconds and hours from minutes. Using (%) to get the modulus of the remaining digits for complete accuracy of time left.






No comments:

 
Creative Commons License
Blogged Information and Code is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License.