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.

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)