I have a proposal for an advancement:
Instead of actual the progress bar every second it would be better to actual it about 100 times regardless of how long the process durates, because with a fixed intervall of 1 sec, you have the effect for a process that lasts 3 sec that only 33, 67 an 100 % ist displayed as progress. In my opinion the progress bar should work for all processes identically. To accomplish this, one has to calculate just the 100th part of the number of iterations and every time this value is passed, actual the progress bar and set the counter back to zero. Of course you would need two counters, one for the whole progress and the one which is reset to zero whenever actualisation is necessary. In this way the TIME function together with the - 1sec - calculation is avoided which may be more time consuming than just incrementing another int variable...
Window.OPEN('Processing data... @1@@@@@@@@@@');
NoOfRecs := MyRecord.COUNT;
REPEAT
CurrRec += 1;
IF NoOfRecs <= 100 THEN
Window.UPDATE(1,(CurrRec / NoOfRecs * 10000) DIV 1)
ELSE IF CurrRec MOD (NoOfRecs DIV 100) = 0 THEN
Window.UPDATE(1,(CurrRec / NoOfRecs * 10000) DIV 1);
// Your processing here...
UNTIL MyRecord.NEXT = 0;
Window.CLOSE;
This way doen't need another variable but an IF-Statement for the case that less than 100 records exists.
In the beginning, I used that system, but it has some drawbacks:
E.g. you are processing 1.000.000 records and each records takes some amount of time to process, it means that you wont update your dialogbox for a lot of seconds (or minutes) giving the impression that Navision does not react and giving the user the bad idea of killing the Navision session.
With the 1 second rule, even when the progressbar is not moving, the user will see that the session responds to his mouseclicks and will know the session is still working.
Regards,Alain Krikilion No PM,please use the forum. || May the <SOLVED>-attribute be in your title!
... it means that you wont update your dialogbox for a lot of seconds (or minutes) giving the impression that Navision does not react...
... and even more, there is no possibility to cancel during that interval.
This is a really good argument for the 1s method.
So in the long run, I think the 1s method is the safest one (though not the fastest one) and should be used throughout, because in the case of a fast process the lack of too few actualisations of the bar is negligible (perhaps the 1s could be changed to 500ms) - in the case of a long lasting process, however, the possibility to cancel at any time (or any second) is by far more import.
Nevertheless I will present here the ultimate progress bar, as far and safe as possible
----------------------------- OnPreDataItem ---
DiagProgress.OPEN('@1@@@@@@@@@@@@@@@@@@');
// --- evtl. put your code here
NoOfRecs := COUNT;
NoOfRecsProgress := NoOfRecs DIV 100;
Counter := 0;
NoOfProgressed := 0;
TimeProgress := TIME;
----------------------------- OnAfterGetRecord ---
Counter := Counter + 1;
IF (Counter >= NoOfRecsProgress) OR (TIME - TimeProgress > 1000) THEN BEGIN
NoOfProgressed := NoOfProgressed + Counter;
DiagProgress.UPDATE(1,ROUND(NoOfProgressed / NoOfRecs * 10000,1));
Counter := 0;
TimeProgress := TIME;
END;
// --- evtl. put your code here
----------------------------- OnPostDataItem ---
// --- evtl. put your code here
DiagProgress.CLOSE;
Some Remarks:
The TIME function is called twice, if the progress bar is to be updated - this could be avoided by a further time variable.
In the IF statement always both conditions of the OR expression are evaluated (even if the first one already gives TRUE)...
I think your ultimate progressbar is a little overkill.
To be honest, when I defined my progressbar a few years ago, I also thought about that system, but like you said, Navision ALWAYS tests both expressions and I wanted a dialogbox that is always useful and not too complex to use. Also when I am looping an internal variable with some calculations on it without any I/O operations. In this case it gets important that the processor can work as fast as possible. Meaning give it as little as possible overhead.
Regards,Alain Krikilion No PM,please use the forum. || May the <SOLVED>-attribute be in your title!
In some developments I've seen a runtime DivByZero error using the progressbar, due to the lack of control on intProgressTotal variable: I prefer to set it to Count+1 in my code.
...but I'm quite sure that in standard code this situation is always checked! O:)
In some developments I've seen a runtime DivByZero error using the progressbar, due to the lack of control on intProgressTotal variable: I prefer to set it to Count+1 in my code.
...but I'm quite sure that in standard code this situation is always checked! O:)
If COUNT is 0, you shouldn't even get into that code that divides by the total.
Regards,Alain Krikilion No PM,please use the forum. || May the <SOLVED>-attribute be in your title!
Then you catch the total number of records and you exit before even starting the loop. Logically, that wouldn't even make sense... if there are no records to process, then you sholdn't even go IN the loop, so you SHOULD never have that error.
In code it would be something like:
MyRec.SETFILTER(whatever fields, whatever values
IF MyRec.FINDSET THEN BEGIN
// prepare the dialog box with the progress bar
// initialize variables, one of them would be
NoOfProgressed := 0;
NoOfRecords := COUNTAPPROX;
REPEAT
NoOfProgressed += 1;
// update progress bar
UNTIL NEXT = 0;
END;);
So if there are no records in the record var, it simply never executes the progress bar, and you never get divide by 0 errors.
Then you catch the total number of records and you exit before even starting the loop. Logically, that wouldn't even make sense... if there are no records to process, then you sholdn't even go IN the loop, so you SHOULD never have that error.
In code it would be something like:
MyRec.SETFILTER(whatever fields, whatever values
IF MyRec.FINDSET THEN BEGIN
// prepare the dialog box with the progress bar
// initialize variables, one of them would be
NoOfProgressed := 0;
NoOfRecords := COUNTAPPROX;
REPEAT
NoOfProgressed += 1;
// update progress bar
UNTIL NEXT = 0;
END;);
So if there are no records in the record var, it simply never executes the progress bar, and you never get divide by 0 errors.
Exactly my point!
Regards,Alain Krikilion No PM,please use the forum. || May the <SOLVED>-attribute be in your title!
IF (CurrentRecordNo MOD 500) = 0 THEN
dWindow.UPDATE(1,ROUND(CurrentRecordNo / NoOfRecords * 10000,1));
The important part is MOD 500. Set another number if the expected number of records in the table justifies it.
I normally use COUNTAPPROX even though the number can be out of sync. I find that it's not that important in reports.
My first try was like that, but sometimes, you have 50 records and each take 10 seconds to process, so you don't see moving the dialogbox.
And you can have 1.000.000 loops going at 1.000 per second and if you have a to low "MOD n", it slows down a lot. You can check it with this:
[code]FOR int := 1 to 1000000000 DO BEGIN
IF (int MOD 500) = 0 THEN
dWindow.UPDATE(1,ROUND(int / 1000000000 * 10000,1));
END;[/code]
Regards,Alain Krikilion No PM,please use the forum. || May the <SOLVED>-attribute be in your title!
I love this solution kriki, but i noticed one thing while developing a process for nav 2009 3tiers client.
the opening of the progress bar (obviously, not only with your solution ) takes some time, and it is useless when the process takes less than 2 seconds for example...based on the design of the process, and based on how much time does a single loop takes (mine is to read some lines in a table and process them, one line takes about 0.1 secs) we can deny the opening of the dialog box at all.
repeat
IF INTProgressCounter = 5 THEN BEGIN
DLGProgressDialog.OPEN('#1##############\@2@@@@@@@@@@@@@@@@@@@@@@@@@\',TXTDialogText,INTProgress);
END;
IF INTProgressCounter > 5 THEN BEGIN
IF TMTimeProgress < TIME - 1000 THEN BEGIN //update the dialog box every second
TMTimeProgress := TIME;
INTProgress := ROUND(INTProgressCounter/INTProgressTotal * 10000,1);
DLGProgressDialog.UPDATE;
END;
END;
INTProgressCounter += 1;
until condition = true;
IF INTProgressCounter > 6 THEN //6 because the counter is increased after the process...viceversa, it would be 5
DLGProgressDialog.CLOSE;
No further variables to add to the original solution
-Mirko-
"Never memorize what you can easily find in a book".....Or Mibuso My Blog
If you know it is fast (less then a few seconds), you can avoid using a progressbar. After all: a progressbar serves only to show the user something is happening.
But if you don't know how much time each loop takes, you can't use your code. If each loop takes 20 seconds and you have only 3 loops to do, the total is 60 seconds, but in your case nothing will be shown.....
Regards,Alain Krikilion No PM,please use the forum. || May the <SOLVED>-attribute be in your title!
sure, that's why i specified that the developer must know how much a single loop takes...
in my process, the user selects "n" lines from a list and then process these lines: he can choose 3 or 100 lines...
-Mirko-
"Never memorize what you can easily find in a book".....Or Mibuso My Blog
Just one tip: Instead TIME datatype use the DATATIME, else it will freeze when running over midnight... :-) it is known problem in standard Cost Adjustment batch...
Just one tip: Instead TIME datatype use the DATATIME, else it will freeze when running over midnight... :-) it is known problem in standard Cost Adjustment batch...
Well, good idea, but what about adjustment of summer time? If a dataport accidentally runs on 31/10/2010 at 2.59 a.m. (GMT + 1.00) it may also freeze for one hour... :-k
Actually, when you got 200 records you do not need to deal with window at all can't imagine a process which will process 200 records in some time, which can be spotted on UI Or, if you need to update window - just update unconditionally. All this code make sense if you got big enough number of records.
And you are right, if recordcount is less than 100 my code will not update progressbar at all, if it is between 100 and 199 we will got update for each record, but in all other cases this will be updated only when whole next percent is reached. no, only if record count is more than 999 the progress bar will be updated each percent. Everything below 1000 will be wrong
Apparently, there is no way to make progressbar accurate :) Look your code is accurate enough. Thanks!
Comments
Instead of actual the progress bar every second it would be better to actual it about 100 times regardless of how long the process durates, because with a fixed intervall of 1 sec, you have the effect for a process that lasts 3 sec that only 33, 67 an 100 % ist displayed as progress. In my opinion the progress bar should work for all processes identically. To accomplish this, one has to calculate just the 100th part of the number of iterations and every time this value is passed, actual the progress bar and set the counter back to zero. Of course you would need two counters, one for the whole progress and the one which is reset to zero whenever actualisation is necessary. In this way the TIME function together with the - 1sec - calculation is avoided which may be more time consuming than just incrementing another int variable...
your idea is the way, I do it a long time.
Here is an example: This way doen't need another variable but an IF-Statement for the case that less than 100 records exists.
Microsoft Dynamics NAV Developer since 1997
MSDynamics.de - German Microsoft Dynamics Community - member of [clip]
E.g. you are processing 1.000.000 records and each records takes some amount of time to process, it means that you wont update your dialogbox for a lot of seconds (or minutes) giving the impression that Navision does not react and giving the user the bad idea of killing the Navision session.
With the 1 second rule, even when the progressbar is not moving, the user will see that the session responds to his mouseclicks and will know the session is still working.
No PM,please use the forum. || May the <SOLVED>-attribute be in your title!
This is a really good argument for the 1s method.
So in the long run, I think the 1s method is the safest one (though not the fastest one) and should be used throughout, because in the case of a fast process the lack of too few actualisations of the bar is negligible (perhaps the 1s could be changed to 500ms) - in the case of a long lasting process, however, the possibility to cancel at any time (or any second) is by far more import.
Nevertheless I will present here the ultimate progress bar, as far and safe as possible
Some Remarks:
The TIME function is called twice, if the progress bar is to be updated - this could be avoided by a further time variable.
In the IF statement always both conditions of the OR expression are evaluated (even if the first one already gives TRUE)...
RIS Plus, LLC
To be honest, when I defined my progressbar a few years ago, I also thought about that system, but like you said, Navision ALWAYS tests both expressions and I wanted a dialogbox that is always useful and not too complex to use. Also when I am looping an internal variable with some calculations on it without any I/O operations. In this case it gets important that the processor can work as fast as possible. Meaning give it as little as possible overhead.
No PM,please use the forum. || May the <SOLVED>-attribute be in your title!
...but I'm quite sure that in standard code this situation is always checked! O:)
No PM,please use the forum. || May the <SOLVED>-attribute be in your title!
In code it would be something like: So if there are no records in the record var, it simply never executes the progress bar, and you never get divide by 0 errors.
RIS Plus, LLC
No PM,please use the forum. || May the <SOLVED>-attribute be in your title!
The important part is MOD 500. Set another number if the expected number of records in the table justifies it.
I normally use COUNTAPPROX even though the number can be out of sync. I find that it's not that important in reports.
And you can have 1.000.000 loops going at 1.000 per second and if you have a to low "MOD n", it slows down a lot. You can check it with this:
[code]FOR int := 1 to 1000000000 DO BEGIN
IF (int MOD 500) = 0 THEN
dWindow.UPDATE(1,ROUND(int / 1000000000 * 10000,1));
END;[/code]
No PM,please use the forum. || May the <SOLVED>-attribute be in your title!
the opening of the progress bar (obviously, not only with your solution ) takes some time, and it is useless when the process takes less than 2 seconds for example...based on the design of the process, and based on how much time does a single loop takes (mine is to read some lines in a table and process them, one line takes about 0.1 secs) we can deny the opening of the dialog box at all. No further variables to add to the original solution
"Never memorize what you can easily find in a book".....Or Mibuso
My Blog
But if you don't know how much time each loop takes, you can't use your code. If each loop takes 20 seconds and you have only 3 loops to do, the total is 60 seconds, but in your case nothing will be shown.....
No PM,please use the forum. || May the <SOLVED>-attribute be in your title!
in my process, the user selects "n" lines from a list and then process these lines: he can choose 3 or 100 lines...
"Never memorize what you can easily find in a book".....Or Mibuso
My Blog
MVP - Dynamics NAV
My BLOG
NAVERTICA a.s.
MVP - Dynamics NAV
My BLOG
NAVERTICA a.s.
no new variables, DIV and MOD are fast enough.
Thanks.
Object Manager
If TotalRecNo < 100 this cause division by 0 error
so,
If you have a total of 199, the progress bar will go to 50%.
This one is better:
Object Manager
It will update the bar 200 times.
Object Manager
And you are right, if recordcount is less than 100 my code will not update progressbar at all, if it is between 100 and 199 we will got update for each record, but in all other cases this will be updated only when whole next percent is reached. no, only if record count is more than 999 the progress bar will be updated each percent. Everything below 1000 will be wrong
Apparently, there is no way to make progressbar accurate :) Look your code is accurate enough. Thanks!
Text001 = Generating data for the month of April-2013
When I run the report the dialog box show only Generating data for the.
Manish
=>
'Campaigns processed: @\'
and if it is not enoug, add more @.
No PM,please use the forum. || May the <SOLVED>-attribute be in your title!
But adding more # does not works.
Manish