Calculate Progress, for file copy, download, upload, etc.

Share your advanced PureBasic knowledge/code with the community.
User avatar
Rescator
Addict
Addict
Posts: 1769
Joined: Sat Feb 19, 2005 5:05 pm
Location: Norway

Calculate Progress, for file copy, download, upload, etc.

Post by Rescator »

CalculateProgress.pb

This neat routine is a universal progress calculation that you can use to get estimated time as well as transfer rate for file copying, downloads and uploads, it is unit neutral in that you can use Bytes, KB, KiB, MB, MiB, GB or GiB etc. Supports multiple instances as it's structure based so you can use it for multiple simultaneous transfers.

Now you too can give your users the thrilling excitement of seeing that estimated time and time left jumping around while copying or downloading. :lol:

You can use it for estimated time/passed/left if you know a size or the total value, due to being data neutral any form of data point that "fit" can be used, like filesize (b/B/KiB/MiB/GiB etc) or percentage which is nice when you do not have a file/datasize but know "where" you are in a process.

Filetransfer, Filecopy, Download, Upload, Disc burning, Disc reading, Rendering, Conversion, Installer, Unininstaller, Updater, Work Timer, Work Stamp, Progress, and more.

Code: Select all

; Calculate Progress v2.0
;
; v1.0>v2.0 changes:
; Improved accuracy, estimates available sooner. (1ms theoretical accuracy)
; No longer shows 0 if a second has not passed yet, estimate/rate interpolation.
; Simplified init, redid examples to match, improved examples/code order in examples.
;
; Copyright (C) Roger Hågensen, EmSai 2007
;
; This software is provided 'as-is', without any express or implied
; warranty.  In no event will the authors be held liable for any damages
; arising from the use of this software.
;
; Permission is granted to anyone to use this software for any purpose,
; including commercial applications, and to alter it and redistribute it
; freely, subject to the following restrictions:
;
; 1. The origin of this software must not be misrepresented; you must not
;    claim that you wrote the original software. If you use this software
;    in a product, an acknowledgment in the product documentation would be
;    appreciated but is not required.
; 2. Altered source versions must be plainly marked as such, and must not be
;    misrepresented as being the original software.
; 3. This notice may not be removed or altered from any source distribution.

EnableExplicit

;The use of a structure allow us to use the same routine multiple times
;to track multiple multitasking/simultaneous file copies, or data downloads.
Structure CalculateProgressStruct
 ;user updated data (user read/write)
 pos.q
 ;result data (user should read only)
 passedtime.l ;in milliseconds
 remainingtime.l ;in milliseconds
 estimatedtime.l ;in milliseconds (passed+remaining)
 ratepersecond.f ;float, based on pos and size, scale independent rate.
 rateminimum.f
 ratemaximum.f
 rateaverage.f
 ;internal data only (user should ignore)
 oldpos.q
 size.q
 oldtimestamp.l
 newtimestamp.l
 starttimestamp.l
EndStructure

;The beauty lies in the simplicity.
Procedure CalculateProgress(*cps.CalculateProgressStruct,init.l=#False,size.q=#Null)
 Protected timestamp.l
 *cps\newtimestamp=ElapsedMilliseconds()
 If init
  *cps\size=size
  *cps\oldtimestamp=*cps\newtimestamp
  *cps\remainingtime=#Null
  *cps\ratepersecond=0.0
  *cps\oldpos=#Null
  *cps\starttimestamp=*cps\newtimestamp
  *cps\passedtime=#Null
  *cps\estimatedtime=#Null
  *cps\remainingtime=#Null
  *cps\pos=#Null
  *cps\rateminimum=0.0
  *cps\ratemaximum=0.0
  *cps\rateaverage=0.0
 Else
  timestamp=(*cps\newtimestamp-*cps\oldtimestamp)
  If timestamp>0
   If (timestamp>999) Or (*cps\passedtime=0)
    *cps\oldtimestamp=*cps\newtimestamp
    *cps\ratepersecond=(*cps\pos-*cps\oldpos)*(1000.0/timestamp)
    *cps\oldpos=*cps\pos
    *cps\passedtime=(*cps\newtimestamp-*cps\starttimestamp)
    *cps\rateaverage=(*cps\pos/*cps\passedtime)*1000.0
    If *cps\ratepersecond<*cps\rateminimum
     If *cps\ratepersecond>0.0
      *cps\rateminimum=*cps\ratepersecond
     EndIf
    EndIf
    If *cps\ratepersecond>*cps\ratemaximum
     *cps\ratemaximum=*cps\ratepersecond
    ElseIf *cps\rateminimum=0.0
     *cps\rateminimum=*cps\ratepersecond
    EndIf
    If *cps\size
     *cps\remainingtime=((*cps\size-*cps\pos)/((*cps\rateaverage+*cps\ratepersecond)*0.5))*1000.0
     *cps\estimatedtime=*cps\passedtime+*cps\remainingtime
    EndIf
   EndIf
  EndIf
 EndIf
EndProcedure

;A nice little helper routine for displaying time passed/remaining.
Procedure.s GetProgressTimeAsText(time.l) ;in seconds
 Protected txt$,days.l,hrs.l,mins.l,secs.l
 If time>999
  secs=time/1000
  time-(secs*1000)
  If secs>59
   mins=secs/60
   secs-(mins*60)
   If mins>59
    hrs=mins/60
    mins-(hrs*60)
    If hrs>23
     days=hrs/24
     hrs-(days*60)
     If days
      txt$+Str(days)+"d "
     EndIf
    EndIf
    If hrs
     txt$+RSet(Str(hrs),2,"0")+":"
    EndIf
   EndIf
   If mins
    txt$+RSet(Str(mins),2,"0")+":"
   EndIf
  EndIf
 EndIf
 txt$+RSet(Str(secs),2,"0")
 ProcedureReturn txt$
EndProcedure


;Example
Define transfer1.CalculateProgressStruct

Debug ""
Debug "Variable (unstable) copy/download rate"

;Init, always do this, right before the transfer starts
;We'll use KiB here, 10MiB is the same as 10240KiB
CalculateProgress(transfer1,#True,10240)
Repeat

 ;We'll simulate the transfer using a delay.
 Delay(250) ;In this test we'll use a 250ms loop this affect the rate in the test too.

 ;0-100 (in this case KiB is added per loop in our test), 0 would equal a 99% chance of stall.
 transfer1\pos+Random(100)

 If transfer1\pos>transfer1\size ;Since we use a random value in this example
  transfer1\pos=transfer1\size ;we have to do this lil fix to handle overflow
 EndIf ;normally you should always have full control of the pos value

 CalculateProgress(transfer1) ;Wow, simple huh? *laughs*
 Debug "Estimated time: "+GetProgressTimeAsText(transfer1\estimatedtime)+", passed: "+GetProgressTimeAsText(transfer1\passedtime)+", left: "+GetProgressTimeAsText(transfer1\remainingtime)+", Downloaded/Copied: "+Str(transfer1\pos)+" KiB, Transfer Speed: "+StrF(transfer1\ratepersecond,2)+" KiB/s."

Until (transfer1\pos>=transfer1\size) ;EOF, Do not do it this way if size is unknown.
Debug "Minimum: "+StrF(transfer1\rateminimum,2)+", KiB/s, Average: "+StrF(transfer1\rateaverage,2)+", KiB/s, Maximum: "+StrF(transfer1\ratemaximum,2)+" KiB/s"

Debug ""
Debug "Fixed (stable) copy/download rate"
;Lets test it again, but with a fixed data rate this time
;so you can better see timepassed/timeleft calculation relationship.

;Init, always do this, right before the transfer starts
;We'll use Bytes here, 10MiB is the same as 1048576 Bytes
CalculateProgress(transfer1,#True,1048576)
Repeat

 ;We'll simulate the transfer using a delay.
 Delay(100) ;In this test we'll use a 250ms loop this affect the rate in the test too.

 transfer1\pos+25000 ;in this case 25 KB is added per loop in our test

 If transfer1\pos>transfer1\size ;Since we use a arbitrary value
  transfer1\pos=transfer1\size ;we have to do this lil fix to handle overflow of final buffer
 EndIf ;normally you should always have full control of the pos value

 CalculateProgress(transfer1) ;Wow, simple huh? *laughs*
 Debug "Estimated time: "+GetProgressTimeAsText(transfer1\estimatedtime)+", passed: "+GetProgressTimeAsText(transfer1\passedtime)+", left: "+GetProgressTimeAsText(transfer1\remainingtime)+", Downloaded/Copied: "+Str(transfer1\pos/1024.0)+" KiB, Transfer Speed: "+StrF(transfer1\ratepersecond/1024.0,2)+" KiB/s."

Until (transfer1\pos>=transfer1\size) ;EOF, Do not do it this way if size is unknown.
Debug "Minimum: "+StrF(transfer1\rateminimum/1024.0,2)+", KiB/s, Average: "+StrF(transfer1\rateaverage/1024.0,2)+", KiB/s, Maximum: "+StrF(transfer1\ratemaximum/1024.0,2)+" KiB/s"

Debug ""

;Percentage example (or some other data point reference)
CalculateProgress(transfer1,#True,100)
Repeat
 Delay(Random(1000))
 transfer1\pos+1
 If transfer1\pos>transfer1\size
  transfer1\pos=transfer1\size
 EndIf
 CalculateProgress(transfer1)
 Debug "Estimated time: "+GetProgressTimeAsText(transfer1\estimatedtime)+", passed: "+GetProgressTimeAsText(transfer1\passedtime)+", left: "+GetProgressTimeAsText(transfer1\remainingtime)+", Percent: "+Str(transfer1\pos)+" %"
Until (transfer1\pos>=transfer1\size)
Last edited by Rescator on Thu Oct 18, 2007 11:14 am, edited 5 times in total.
Inf0Byt3
PureBasic Fanatic
PureBasic Fanatic
Posts: 2236
Joined: Fri Dec 09, 2005 12:15 pm
Location: Elbonia

Post by Inf0Byt3 »

Very usefull! Thanks.
None are more hopelessly enslaved than those who falsely believe they are free. (Goethe)
User avatar
Dreamland Fantasy
Enthusiast
Enthusiast
Posts: 335
Joined: Fri Jun 11, 2004 9:35 pm
Location: Glasgow, UK
Contact:

Post by Dreamland Fantasy »

Nice. :)

Thanks for sharing!

Kind regards,

Francis.
User avatar
Rescator
Addict
Addict
Posts: 1769
Joined: Sat Feb 19, 2005 5:05 pm
Location: Norway

Post by Rescator »

Updated first post, improved the code and example, higher accuracy, and more.
I do not forsee any further changes besides possible speed improvements/tweaks, so this "should" be the last version.
User avatar
NoahPhense
Addict
Addict
Posts: 1999
Joined: Thu Oct 16, 2003 8:30 pm
Location: North Florida

Re: Calculate Progress, for file copy, download, upload, etc

Post by NoahPhense »

Very nice code!

- np
User avatar
Rescator
Addict
Addict
Posts: 1769
Joined: Sat Feb 19, 2005 5:05 pm
Location: Norway

Post by Rescator »

Fixed some typos and added a percentage/misc data example,
this shows how flexible this routine is.

Percentage which is nice when you do not have a file/datasize but know "where" you are in a process.

You can use it for estimated time/passed/left if you know a size or the total value, due to being data neutral any form of data point that "fit" can be used, like filesize (b/B/KiB/MiB/GiB etc) or percentage which is nice when you do not have a file/datasize but know "where" you are in a process.

Filetransfer, Filecopy, Download, Upload, Disc burning, Disc reading, Rendering, Conversion, Installer, Unininstaller, Updater, Work Timer, Work Stamp, Progress, and more.
User avatar
Psychophanta
Always Here
Always Here
Posts: 5153
Joined: Wed Jun 11, 2003 9:33 pm
Location: Anare
Contact:

Post by Psychophanta »

Thank you Rescator.
It is useful endeed :!:
http://www.zeitgeistmovie.com

while (world==business) world+=mafia;
rsts
Addict
Addict
Posts: 2736
Joined: Wed Aug 24, 2005 8:39 am
Location: Southwest OH - USA

Post by rsts »

Thanks.

I always had trouble getting these right.

cheers
User avatar
NoahPhense
Addict
Addict
Posts: 1999
Joined: Thu Oct 16, 2003 8:30 pm
Location: North Florida

Re: Calculate Progress, for file copy, download, upload, etc

Post by NoahPhense »

As I mentioned in a previous post, awesome code.

I haven't had enough coffee today. My question is, how can I integrate this into a large file transfer from a file server over a UNC path? A small copy example would be great.

- np
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5494
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: Calculate Progress, for file copy, download, upload, etc

Post by Kwai chang caine »

Thanks a lot to sharing :roll:
ImageThe happiness is a road...
Not a destination
Post Reply