I can remember in my first programming job being introduced to RPG III programs with a list of all the indicators used in the comments at the top of the program. While I admired the throughness of the documentation, I never did like that an indicator would only be used for one purpose. I prefered to use a few indicators for file Input/Output and check immediately afterwards if the indicator was *ON, for example:
01 * 99=End of file, 98=Error (record lock) 02 C READ FILE1 9899 03 C *IN99 IFEQ *ON 04 * 05 * 99=Not found, 98=Error (record lock) 06 C KEY1 CHAINFILE1 9998 07 C *IN99 IFEQ *ON |
RPGLE/RPG IV introduced the "Operation Code Extender" which could replace the need for indicators for all of the operation codes. If I code the equivalent in RPG/free it could look like:
01 read FILE1 ; 02 if %eof ; 03 chain key1 FILE1 ; 04 if not(%found) ; |
But when RPGLE/RPG IV was first introduced you still had to use numbered indicators to communicate with display and printer files. Fortunately this is no longer the case as you can use the Indicator Data Structure, INDDS, to give the numbered indicators in these types of files meaningful names.
In this example I am going to use a display file as our example. This works in exactly the same way for printer files.
The first thing I need to do is to add the INDARA to the top of the display file's source, with the other keywords that apply to the entire file, see below:
A DSPSIZ(24 80 *DS3) A PRINT A ERRSFL A INDARA A ALTHELP(CA01) A HELP *------------------------------------------------------- A R SFL01 SFL |
In the RPGLE/RPG IV source member I add the INDDS keyword into the File specification for the display file, see below, and the data structure with the same name as was given in the INDDS. In the data structure's subfields notice that the Data Type is 'N' for a indicator type field on lines 5-7 and 10-11.
01 F@SFL_DSPF CF E WORKSTN indds(IndDs) 02 F sfile(SFL01:Z1RRN) 03 ******************************************************* 04 D IndDs DS 05 D F3_Exit 3 3N 06 D SflCtlDsp 30 30N 07 D SflDsp 31 31N 08 * Errors 09 D Errors 50 60A 10 D ErrOpt 50 50N 11 D ErrDept 51 51N |
Now I can just move *ON and *OFF to the named indicators like this:
// Intialize SFL SflCtlDsp = *off ; SflDsp = *off ; write CTL01 ; SflCtlDsp = *on ; |
The error section of the IndDs data structure shows, on line 9, a trick I found. Rather than move *OFF all the indicators individually I can define a data structure subfield to be the same length as all of the error indicators. It must be defined as 'A', alphanumeric, as it is longer than one byte. I can then move all zeroes to it, which is the equivalent of moving *OFF to all the indicators in one step, see line 51 below:
01 Errors = *all'0' ; 02 exsr Validation ; 03 if (Errors <> *all'0') ; 04 iter ; 05 endif ; 06 begsr Validation ; 07 if ((Z1OPT <> '1') 08 and (Z1OPT <> '2') 09 and (Z1OPT <> '3')) ; 10 ErrOpt = *on ; 11 endif ; 12 setll Z1DEPT DPTMAST ; 13 if not(%equal) ; 14 ErrDept = *on ; 15 endif ; 16 endsr ; |
In the subroutine Validation if any errors are found the appropriate indicator is turned *ON. When processing returns from the subroutine rather than test each indicator to see if it is *ON I can check if the Errors subfield is all zeroes, line 3, if it is not then an error has been found.
Now all the indicators are named there is not a need to list their function in the comments at the top of the program, or to puzzle about what an indicator is used for.
This article was written for IBM i 7.1, and it should work with earlier releases too.
Great resource and examples! Next is to show the advanced version of multiple indicator ds arrays and using pointers to turn the 99 indicators into 99 times however many you need.
ReplyDeleteWholeheartedly agree except for two things; your validation subroutine should be a sub procedure that returns an indicator (n) and your "if errors" trick becomes redundant as it becomes simply "if not validation" or better still call your sub procedure valid and it reads like proper English. Sub procedures offer so much more flexibility with return values and safety with locally scoped variables and prototyped calls that I have banned anyone from writing new subroutines in my team! The line clearing the errors array should be inside the sub procedure btw. I also prefer to qualify data structures so there is no chance of treading on variable names. This is more relevant where you are using multiple copy members to bring in common structure definitions of which your indicator structure may be one.
ReplyDeleteNo doubt the author would truly implement in subprocedures - but this is easier to document and teach the concept - how to use named indicators when interacting with DSPF...
ReplyDeleteI'd accept this reply if the "errors" character array hadn't been highlighted in the article as a neat trick to test for the presence of errors. It's unnecessary if you use a sub procedure with a return value and it makes the code much clearer.
DeleteAwesome, thanks for sharing.
ReplyDeleteOk Simon, but I reserve the right....no, the need to use indicator 42 in special programs...to pay homage to Hitch Hikers Guide to the Galaxy and the meaning of life...ok, so yes, numbered boolean should be avoided at all costs!!
ReplyDeleteWhen I write interactive programs I have a standard set of function keys that I use. F03 - Exit, F12 - Previous, F04 - Field Prompt etc. I like the *INKx indicators because I always know when a function key controls the execution of a block of code. So this would be nice to provide more descriptive function key indicators.
ReplyDeleteNumeric indicators never bothered me, until I ran out of them in an online order entry/invoice/credit memo, etc program....when I needed one, I had to hunt down where a numeric indicator was used, so I didn't use it on the same screen. Real pain.
ReplyDeleteI always hated them because IF *IN35;.... means absolutely positively nothing except testing to see if an indicator turned on...
ReplyDeleteI agree with you 100%.
DeleteI always wondered why they skipped *INKO (F15 key is *INKP)...Someone once told me in the early days of the S/38 that they couldn't get around the MicroCode so they just decided to skip 'O' and straight to 'P'
ReplyDeleteI always thought it was because people got the letter "O" confused with the digit "0", and some models of dummy tubes and printers rendered them almost exactly the same.
DeleteThat's probably it; it's easy to confuse O and 0. But I wonder why they didn't skip *INKI for the same reason?
DeleteOne of the many unanswered trivial questions of this platform, whose answer probably lies in whether or not some IBM engineer had regular or decaf coffee on some morning in 1971.
DeleteWow !! Will definitely try this.. 👍👍 Thanks for this wonderful tip !!
ReplyDeleteOn pressing F3 in the screen, the F3_Exit does not turn on.. :(
ReplyDeleteI have defined INDARA and CA03 in the dspf. Am I missing anything ?
Try CF03
ReplyDeletecould u plz elaborate the use of INDARA and INDDS
ReplyDeleteWhat more information would you like?
DeleteHi Simon
ReplyDeleteIf i have more than once screen(rexord format) within single display file, so they may have many common function keys
Like F3 F5 or many More ,how do we use Indara concept in such case apart from giving separate indicators to enable/disable the function keys for second record format ,is this feasible?
How do you do it now?
DeleteYou could define a character subfield that "overlays" all those screen indicators and then set that to *ALL'0'.