I received a message asking me if there was an easy way in RPG to have a screen constantly update without the user needing to press a key. The idea is that as the program progresses it would write a message to the screen to inform the user of what is happening. Fortunately there is, otherwise I would not have be written this post.
First we need to understand what RPG's operation code Execute Format, EXFMT, does. Simply put it is a combination of a Write operation of the record format to the workstation, followed by a Read operation to retrieve the input from the record format. I can easily replace the EXFMT in any program with a WRITE followed by a READ. If I want to just write a display file's record format to the screen I could just perform a write, but how can I stop the user for using the keyboard while the record format is displayed?
I can use Lock keyboard keyword, LOCK, within the record format to stop the use of the keyboard while this record format is displayed.
Below is a very simple display file I will be using. While the program is running I want to display what step has finished and what step is currently being performed. When all the steps have been performed I will display a screen that informs the user that it has finished and they have to press the Enter key to continue.
01 A DSPSIZ(24 80 *DS3) 02 A R SCREEN1 03 A LOCK 04 A 3 3'What has finished :' 05 A FLD001 50 O 3 23 06 A 4 3'What is going on :' 07 A FLD002 R O 4 23REFFLD(FLD001 *SRC) 08 A R SCREEN2 09 A 3 3'Job has finished' 10 A 4 3'Press Enter'
Update: Thank you to Chris Ringer and Mario Alexis Salgado Montenegro for pointing out something I overlooked. The display file must be compiled with "Defer Write" set to no, DFRWRT(*NO). My poor excuse is that this is that I have changed my default CRTDSPF to have RSTDSP(*YES) and DFTWRT(*NO), therefore, I never caught this error.
The LOCK keyword, line 3, is in the SCREEN1 record format and while that record format is displayed the user will not be able to use the keyboard. Depending on how your users are configured you will find that any key they press while SCREEN1 is displayed is buffered, and will be executed when SCREEN2, which does not have the lock, is displayed.
The RPG code is simple too:
01 ctl-opt dftactgrp(*no) ; 02 dcl-pr QCMDEXC extpgm ; 03 *n char(13) const ; 04 *n packed(15:5) const ; 05 end-pr ; 06 dcl-f TESTDSPF workstn ; 07 dcl-s i packed(2) ; 08 for i = 1 to 10 ; 09 FLD001 = FLD002 ; 10 FLD002 = 'Step = ' + %char(i) ; 11 write SCREEN1 ; 12 QCMDEXC('DLYJOB DLY(2)':13) ; 13 endfor ; 14 exfmt SCREEN2 ; 15 *inlr = *on ;
For those of using servers that do not have the all free RPG you can see a version with fixed format definitions here.
The first thing you are going to wonder is why I am using the QCMDEXC API in this program. It has only been added to help demonstrate how this program works. If I did not stop the program for a couple of seconds you would not be able to see what is displayed on the screen as it would complete too quickly. By using the QCMDEXC to execute a Delay Job command, DLYJOB, the program is stopped long enough for us to see what is displayed on the screen. In a live program this would not be present. I have defined QCMDEXC as an external procedure, if you are not familiar with doing this see Defining Procedures in RPG all free.
There is nothing special with the definition of the display file, line 6.
I start a For group, line 8, to execute ten times. If you have not used a For group see FOR replaces DO. Lines 9 and 10 are self explanatory. On line 11 I write the record format, as I only want to display what is in the record format. On line 12 I delay the program for two seconds so we can observe the changes on the screen.
After the tenth iteration of the For group we reach line 14 and display the second record format. This time I use the EXFMT as I want the user to press Enter.
When the program runs it looks like this:
What has finished : What is going on : Step = 1 -- // -- What has finished : Step = 1 What is going on : Step = 2 -- // -- What has finished : Step = 2 What is going on : Step = 3 -- // --Repeats 6 times until…
-- // -- What has finished : Step = 9 What is going on : Step = 10 -- // -- Job has finished Press Enter
If I had multiple record formats with the LOCK keyword I could display one after another like a PowerPoint presentation auto playing:
11 write SLIDE1 ; 12 QCMDEXC('DLYJOB DLY(5)':13) ; 13 write SLIDE2 ; 14 QCMDEXC('DLYJOB DLY(5)':13) ; 15 write SLIDE3 ; 16 QCMDEXC('DLYJOB DLY(5)':13) ; 24 exfmt LASTSLIDE ; 25 *inlr = *on ;
While the original question was about RPG, I can do the same as the original RPG program in CL.
For IBM i 7.2 the program could look like as in that release the %CHAR built in function was added to CL. If you are unfamiliar with it see Char built in function added to CL.
01 PGM 02 DCL VAR(&COUNT) TYPE(*DEC) LEN(2 0) VALUE(1) 03 DCLF FILE(TESTDSPF) 04 DOUNTIL COND(&COUNT > 10) 05 CHGVAR VAR(&FLD001) VALUE(&FLD002) 06 CHGVAR VAR(&FLD002) VALUE('Step = ' || %CHAR(&COUNT)) 07 SNDF RCDFMT(SCREEN1) 08 DLYJOB DLY(2) 09 CHGVAR VAR(&COUNT) VALUE(&COUNT + 1) 10 ENDDO 11 SNDRCVF RCDFMT(SCREEN2) 12 ENDPGM
For earlier releases I would need to use a character version of the &COUNT variable, see lines 3, 7, and 8 below:
01 PGM 02 DCL VAR(&COUNT) TYPE(*DEC) LEN(2 0) VALUE(1) 03 DCL VAR(&COUNTA) TYPE(*CHAR) LEN(2) 04 DCLF FILE(TESTDSPF) 05 DOUNTIL COND(&COUNT > 10) 06 CHGVAR VAR(&FLD001) VALUE(&FLD002) 07 CHGVAR VAR(&COUNTA) VALUE(&COUNT) 08 CHGVAR VAR(&FLD002) VALUE('Step = ' || &COUNTA) 09 SNDF RCDFMT(SCREEN1) 10 DLYJOB DLY(2) 11 CHGVAR VAR(&COUNT) VALUE(&COUNT + 1) 12 ENDDO 14 SNDRCVF RCDFMT(SCREEN2) 15 ENDPGM
The SNDF command, on line 7 or 9, is CL's equivalent as RPG's Write operation.
If you have not used a DOUNTIL in CL see CL does DO.
You can learn more about the LOCK keyword in DDS on the IBM web site here.
This article was written for IBM i 7.2, and should work for earlier releases too.
Fixed format version of the RPG program
01 H dftactgrp(*no) 02 FTESTDSPF CF E WORKSTN 03 D QCMDEXC PR extpgm('QCMDEXC') 04 D 13 const 05 D 15 5 const 06 D i S 2 0 /free 07 for i = 1 to 10 ; 08 FLD001 = FLD002 ; 09 FLD002 = 'Step = ' + %char(i) ; 10 write SCREEN1 ; 11 QCMDEXC('DLYJOB DLY(2)':13) ; 12 endfor ; 13 exfmt SCREEN2 ; 14 *inlr = *on ;