Do you ever look at the Programming Development Manager (PDM) on the AS/400 and marvel at what a cool subfile application it is? (If you dont, please play along for nowyou wont regret it.) PDM is extremely flexible. It allows you to position to any place in a subfile-like panel, page forward or backward from that position, change a record on any page of the subfile, and process all changed records only when the Enter key is pressed. On its own, each feature is simple to code in an RPG program. But, when you combine the features, the real fun begins.
I worked for a software development house that wanted the IBM look and feel on all of its screens. That way, users familiar with the AS/400 would be comfortable when using the interactive screens in our software and would require less training. It seemed simple enough at first, but, if you have ever tried to incorporate all the features included with PDM into a subfile application, you know that it is no small task. In fact, PDM is not even a subfile application; its displays are written using the User Interface Manager (UIM), the language used for many native OS/400 commands and all of the help panels on the AS/400.
Well, what do you do if you are an RPG programmer who likes the look, feel, and flexibility of PDM, but does not know how to obtain them using UIM? Do you learn UIM? You could, but if you already know RPG and have coded subfile programs before, is learning a new language the most effective use of your time? This was the dilemma I faced.
I am not against learning the UIM language, but I thought that there must be an efficient way to get the same results using good old RPG and subfile processing. After some research, I recommended to my programming group that we use data queues to add the necessary flexibility to our subfile applications. Data queues are the way to get the features and flexibility youre looking for without learning UIM. (For more information on this subject, see UIM List Panels, MC, January 1998.)
Why Data Queues?
What are data queues? Data queues are a type of system object (type *DTAQ) you can create and maintain using OS/400 commands and APIs. They provide a fast method of asynchronous communication between two jobs, because they use less system resources than database files, message queues, or data areas. Data queues have the ability to attach a sender ID to each message being placed on the queue. The sender ID, an attribute of the data queue that is established when the queue is created, contains the qualified job name and current user profile. Another advantage to using data queues is the ability to set the length of time a job will wait for an entry before continuing its processing. A negative wait parameter tells the job to wait indefinitely for an entry before processing. A wait parameter of 0 to 99999 tells the job to wait that number of seconds before processing.
High-level language (HLL) programs can send data to a data queue using the Send to a Data Queue (QSNDDTAQ) API and receive data using the Receive from a Data Queue (QRCVDTAQ) API. Data queues can be read in FIFO sequence, in LIFO sequence, or in keyed sequence. The technique I use for building PDM-like subfile applications requires a keyed data queue. Keyed data queues allow the programmer to specify a specific order in which to receive messages on the data queue or to retrieve only data queue messages that meet a criterion. That is, the programmer can receive a data queue message that is equal to (EQ), greater than (GT), greater than or equal to (GE), less than (LT), or less than or equal to (LE) a search key.
The reason behind using data queues in a subfile program stems from a combination of user requirements and an interest in selecting the most efficient solution for that combination. Using data queues instead of data structures, files, or arrays frees your job from performing some work. During an interactive job, the data queue APIs can provide better response time and can decrease the size of the interactive program and its process activation group (PAG). This, in turn, can help overall system performance. In addition, data queues allow the programmer to do less work When you receive an entry from a data queue using the QRCVDTAQ command, it is physically removed from the data queue. The programmer does not have to add code to deal with unnecessary entries. Heres how to do it.
I use the data queue to store a replica of the changed subfile record. Each time a subfile record is changed and the Enter key or Page key is pressed, the changed subfile record is stored in the data queue. I need to do this because I am building the subfile one page at a time and need to know what records were previously changed. Once records are no longer displayed on the screen, they are not part of the subfile. When the user positions through the subfile by using the Page keys or by keying something in the position-to field, the program checks to see if each record read from the file exists in the data queue before loading it to the subfile. If the record exists in the data queue, it is written to the subfile along with the earlier changes and is marked as changed. When the user is ready to process all the records by pressing the Enter key with nothing in the position-to field, the remaining entries in the data queue are processed appropriately.
A Work with Template
I have designed an application template for a file maintenance application using these principles. In Figure 1, I have provided a sample master file you can use to verify and explore these techniques.
The template itself consists of three objectsa CL program to drive the application (Figure 2), a display file to interact with the user (Figure 3), and an RPG III program (Figure 4) to do the grunt work.
Before we get to the RPG, I need to say a few things about the DDS and CL. For this application to have the necessary flexibility, the RPG programnot OS/400must
completely control the subfile. For this to happen, the subfile page (SFLPAG) and subfile size (SFLSIZ) values must be equal. The subfile will never contain more than one page, but, as you will see later, the program will make it appear that the subfile contains much more than one page of data.
Remember that even though entries are removed from a data queue after they are received, the space containing the entry is not. So, over the long run, performance will suffer because the data queues size will increase. For this reason, I delete and recreate the data queue each time the program is called. Even if you build the data queues in QTEMP, it is best to delete and recreate them in case the user calls the program more than once before signing off.
Now that the setup issues have been covered, lets get to the fun stuff. Look again at the RPG program in Figure 4. Its first task is to load the subfile with one page of records (in this case, 13). Notice in the SFLBLD routine that each time a record is read from the data file, the RCVQUE subroutine is executed. For the initial subfile load, this subroutine will not accomplish anythingso I will defer explanation until later. Once the subfile is initially loaded and displayed, the user can do several things. No matter what the user decides, each time the Enter key or a valid function key (other than F3 or F12) is pressed, the ADDQUE subroutine is executed. This subroutine uses the READC op code to find changed records in the subfile and to add them to the data queue. The data queue entry is an exact copy of the subfile record layout and is keyed exactly as the database file used to build the subfile.
The ADDQUE subroutine keeps track of all records changed through the subfile. For example, if the user deletes two records on the current page and then pages forward to delete records on the next page, the ADDQUE sends the two changed records to the data queue before rebuilding the subfile in the page forward (SFLBLD) routine. The same logic holds true if the user decides to position to another part of the subfile using the position-to field.
Now, back to the RCVQUE routine. This routine attempts to receive an entry from the data queue using the same key as the record read from the database file. If a matching entry exists in the data queue, then the entry in the data queue, not the data from the database file, is written to the subfile. With this, the user can page and position through the subfile and store any changed records in the data queue. Whenever a record is read from the file, the data queue is checked to see if that record exists and, if so, it is displayed along with the option that was previously selected.
If the user presses the Enter key and the position-to field is empty, the ADDQUE routine executes one last time to load any changes to the current page, and the PRCSFL routine is executed. This routine processes all entries still left in the data queue by setting the key value to *BLANKS and processing all data queue entries with a key greater than or equal to the key value.
Each time an entry is received, the data is run through a select routine to determine what function to perform. In the case of this program, depending on the option taken, a display screen, an update screen, or a delete confirmation subfile will appear.
No Regrets
Controlling the subfile within the RPG program and using data queues to store and retrieve changed subfile records allows you to create an extremely flexible subfile application that will furnish your users virtually unlimited capabilities. I told you that if you played along you wouldnt regret it.
Reference
AS/400 System API Reference V3R2 (SC41-3801-01, CD-ROM QBKAVD01)
*================================================================
*
* To compile:
*
* CRTPF FILE(XXX/SFL006PF) SRCFILE(XXX/QDDSSRC)
*
*================================================================
A UNIQUE
A R PFR
A DBILIB 10
A DBIFIL 10
A DBIATR 2
A DBIFLD 10
A DBIOWN 10
A DBITYP 10
A K DBILIB
A K DBIFIL /*============================================================*/
/* To compile: */
/* */
/* CRTCLPGM PGM(XXX/SFL006CL) SRCFILE(XXX/QCLSRC) */
/* */
/*============================================================*/
PGM
DLTDTAQ DTAQ(QTEMP/SFL006DQ)
MONMSG MSGID(CPF2105)
CRTDTAQ DTAQ(QTEMP/SFL006DQ) MAXLEN(256) SEQ(*KEYED) +
KEYLEN(20)
CALL PGM(*LIBL/SFL006RG)
ENDPGM *================================================================
* To compile:
*
* CRTDSPF FILE(XXX/SFL006DF) SRCFILE(XXX/QDDSSRC)
*
*================================================================
A DSPSIZ(24 80 *DS3)
A PRINT
A ERRSFL
A CA03
A CA12
*================================================================
A R SFL1 SFL
A 74 SFLNXTCHG
A OPTION 1A B 9 3VALUES( 2 4 5)
)
A DBILIB 10A O 9 7
A DBIFIL 10A O 9 20
A DBIATR 2A O 9 33
A DBIFLD 10A O 9 38
*================================================================
A R SF1CTL SFLCTL(SFL1)
A SFLSIZ(0013)
A SFLPAG(0013)
A ROLLUP(27)
A ROLLDOWN(28)
A OVERLAY
A N32 SFLDSP
A N31 SFLDSPCTL
A 31 SFLCLR
A 90 SFLEND(*MORE)
A RRN1 4S 0H SFLRCDNBR
A 1 26Data Queue/ Subfile Application
Figure 1: The example application uses this physical file as you would use one of your master files
Figure 2: This CL program drives the application
A DSPATR(HI)
A 3 2Position to file . .
A POSTO 10A B 3 23
A 5 2Type options, press Enter.
A COLOR(BLU)
A 6 42=Change
A COLOR(BLU)
A 6 174=Delete
A COLOR(BLU)
A 6 305=Display
A COLOR(BLU)
A 8 2Opt
A DSPATR(HI)
A 8 7Library
A DSPATR(HI)
A 8 20File
A DSPATR(HI)
A 8 33Atr
A DSPATR(HI)
A 8 38Field
A DSPATR(HI)
*================================================================
A R FKEY1
A 23 2F3=Exit
A COLOR(BLU)
A 23 12F12=Cancel
A COLOR(BLU)
*================================================================
A R SFL2 SFL
A DBILIB 10A O 7 7
A DBIFIL 10A O 7 20
A DBIATR 2A O 7 33
A DBIFLD 10A O 7 38
*================================================================
A R SF2CTL SFLCTL(SFL2)
A SFLSIZ(0999)
A SFLPAG(0015)
A OVERLAY
A N32 SFLDSP
A N31 SFLDSPCTL
A 31 SFLCLR
A N99 SFLEND(*MORE)
A RRN2 4S 0H SFLRCDNBR
A 1 26Data Queue/Subfile Application
A DSPATR(HI)
A 3 2Press Enter to confirm your choice
A s for Delete.
A COLOR(BLU)
A 4 2Press F12 to return to chan
A ge your choices.
A COLOR(BLU)
A 6 20File
A DSPATR(HI)
A 6 33Atr
A DSPATR(HI)
A 6 38Field
A DSPATR(HI)
A 6 7Library
A DSPATR(HI)
*================================================================
A R PANEL1
A STATUS 7A O 1 6DSPATR(RI)
A 1 26Data Queue/Subfile Application
A DSPATR(HI)
A 4 3Library . . .
A DBILIB 10A B 4 17
A 41 DSPATR(PR)
A 6 3File. . . . .
A DBIFIL 10A B 6 17
A 41 DSPATR(PR)
A 8 3Field . . . .
A DBIFLD 10A B 8 17
A 41 DSPATR(PR)
A 10 3Attribute . .
A DBIATR 2A B 10 17
A 41 DSPATR(PR)
A 12 3Type. . . . .
A DBITYP 10A B 12 17
A 41 DSPATR(PR)
A 14 3Owner . . . .
A DBIOWN 10A B 14 17
A 41 DSPATR(PR)
A 23 3F3=Exit
A COLOR(BLU)
A 23 13F12=Cancel
A COLOR(BLU) *================================================================
*
* To compile:
*
* CRTRPGPGM PGM(XXX/SFL006RG) SRCFILE(XXX/QRPGSRC)
*
*================================================================
FSFL006DFCF E WORKSTN
F RRN1 KSFILE SFL1
F RRN2 KSFILE SFL2
FSFL006PFUF E K DISK
*
IDATA DS
I 1 1 OPTION
I 2 3 DBIATR
I 4 13 DBIFLD
I 14 23 DBILIB
I 24 33 DBIFIL
I 14 33 KEY
I 13 C SFLPAG
I 14 C PLUS1
*
*****************************************************************
* Main Routine
*****************************************************************
*
C *LOVAL SETLLSFL006PF
*
C EXSR SFLBLD
*
C *INKC DOUEQ*ON
*
C WRITEFKEY1
C EXFMTSF1CTL
*
C SELEC
*
* Roll up - load the data Qs before loading subfile
*
C *IN27 WHEQ *ON
C *IN90 ANDEQ*OFF
C EXSR ADDQUE
C EXSR SFLBLD
*
* Roll down - load the data Qs before loading the subfile.
*
C *IN28 WHEQ *ON
C EXSR ADDQUE
C EXSR RLDOWN
*
C *INKL WHEQ *ON
C LEAVE
*
* If ENTER key is pressed and position-to is blank, process option
* taken. If position-to field is non blank, re-position the subfile
*
C OTHER
C *IN32 IFEQ *OFF
C EXSR ADDQUE
*
C POSTO IFEQ *BLANKS
C EXSR PRCSFL
C SAVKEY SETLLSFL006PF
C ELSE
*
* If position-to is greater than last record in the file,
* display the last record instead of empty screen.
*
C POSTO SETLLSFL006PF 98
C 98 DO 2
Figure 3: The display file interacts with the user
C READPSFL006PF 98
C ENDDO
C CLEARPOSTO
C ENDIF
*
C EXSR SFLBLD
C ENDIF
*
C ENDSL
*
C ENDDO
*
C MOVE *ON *INLR
*
*****************************************************************
* ADDQUE - Add subfile data to Data Queues
*****************************************************************
*
C ADDQUE BEGSR
*
* Read the changed subfile records and write them to the data Q.
* The data queue is keyed by whatever the unique key of the file
* is. If no unique key in the file, use the relative record number. The
* data queue is used to save options selected on a specific subfile line.
*
C READCSFL1 91
*
C *IN91 DOWEQ*OFF
C OPTION ANDGT*BLANKS
*
C Z-ADD256 LEN
*
C CALL QSNDDTAQ
C PARM QUE
C PARM LIB
C PARM LEN
C PARM DATA
C PARM KEYLN
C PARM KEY
*
C READCSFL1 91
C ENDDO
*
C ENDSR
*
*****************************************************************
* RCVQUE - Check DATAQUEUE before writing to subfile
*****************************************************************
*
C RCVQUE BEGSR
*
* Read the data Q by the whatever the unique key from the
* database file to see if there is a saved option. If so, display
* the saved option when the subfile is displayed.
*
C MOVE EQ ORDER
C CALL QRCVDTAQ
C PARM QUE
C PARM LIB
C PARM LEN
C PARM DATA
C PARM WAIT
C PARM ORDER
C PARM KEYLN
C PARM KEY
C PARM SNDLEN
C PARM SNDR
*
* Set on next change indicator if record is found is Queue.
*
C LEN IFGT *ZERO
C MOVE *ON *IN74
C ENDIF
*
C ENDSR
*
*****************************************************************
* PRCSFL - process the options taken in the subfile.
*****************************************************************
*
C PRCSFL BEGSR
*
C MOVE *ON *IN31
C WRITESF2CTL
C MOVE *OFF *IN31
C Z-ADD*ZERO RRN2
*
* Receive data queue records until the queue is empty LEN = 0
*
C MOVE *BLANKS KEY
*
C LEN DOUEQ*ZERO
*
C MOVE *BLANKS OPTION
*
C MOVE GE ORDER
C CALL QRCVDTAQ
C PARM QUE
C PARM LIB
C PARM LEN
C PARM DATA
C PARM WAIT
C PARM ORDER
C PARM KEYLN
C PARM KEY
C PARM SNDLEN
C PARM SNDR
*
C SELEC
*
* process the edit program or subroutine
*
C OPTION WHEQ 2
C MOVE *OFF *IN41
C MOVELUPDATE STATUS P
C EXFMTPANEL1
*
* write option 4 records to another subfile for delete confirmation
*
C OPTION WHEQ 4
C ADD 1 RRN2
C WRITESFL2
*
* process the display program or subroutine
*
C OPTION WHEQ 5
C MOVE *ON *IN41
C MOVELDISPLAY STATUS P
C EXFMTPANEL1
*
C ENDSL
C ENDDO
*
C RRN2 IFGT *ZERO
C Z-ADDRRN2 LSRRN2
C Z-ADD1 RRN2
C EXFMTSF2CTL
C *INKC IFEQ *OFF
C *INKL ANDEQ*OFF
C EXSR DELET
C ENDIF
C ENDIF
*
C ENDSR
*
*****************************************************************
* SFLBLD - Build the List
*****************************************************************
*
C SFLBLD BEGSR
*
* Clear subfile
*
C Z-ADD*ZERO RRN1
C MOVE *ON *IN31
C WRITESF1CTL
C MOVE *OFF *IN31
*
* Load data to subfile
*
C DO SFLPAG
C READ SFL006PF 90
C *IN90 IFEQ *ON
C LEAVE
C ENDIF
*
C MOVE *BLANKS OPTION
C EXSR RCVQUE
C ADD 1 RRN1
C RRN1 IFEQ 1
C MOVE DBILIB SAVLIB
C MOVE DBIFIL SAVFIL
C ENDIF
C WRITESFL1
C MOVE *OFF *IN74
C ENDDO
*
* If there are exactly 13 records left in the file, SFLEND will
* not be indicated even though there are no more records.
* The following code will rectify that problem.
*
C *IN90 IFEQ *OFF
C READ SFL006PF 90
C N90 READPSFL006PF 90
C ENDIF
*
C RRN1 IFEQ *ZERO
C MOVE *ON *IN32
C ENDIF
*
C ENDSR
*
*****************************************************************
* RLDOWN - page backward one page
*****************************************************************
*
C RLDOWN BEGSR
*
C SAVKEY SETLLSFL006PF
*
* Re-position files for rolling backward.
*
C DO PAGN1
C READPSFL006PF 91
C *IN91 IFEQ *ON
C *LOVAL SETLLSFL006PF
C LEAVE
C ENDIF
C ENDDO
*
C EXSR SFLBLD
*
C ENDSR
*
*****************************************************************
* DELET - Delete subfile records
*****************************************************************
*
C DELET BEGSR
*
C 1 DO LSRRN2 X 40
C X CHAINSFL2 99
C KEY1 DELETSFL006PF 9998
C ENDDO
*
C ENDSR
*
*****************************************************************
* *INZSR - Initialization
*****************************************************************
*
C *INZSR BEGSR
*
* Data Queue variables
*
C MOVELQTEMP LIB 10 P
C MOVELSFL006DQ QUE 10 P
C Z-ADD256 LEN 50
C Z-ADD20 KEYLN 30
C Z-ADD0 WAIT 50
C Z-ADD0 SNDLEN 30
C MOVE EQ ORDER 2
C MOVE *BLANKS SNDR 10
C MOVE *BLANKS POSTO
*
C ENDSR
*
*****************************************************************
* KEYLISTS AND DEFINES
*****************************************************************
*
C *LIKE DEFN RRN2 LSRRN2
C *LIKE DEFN DBILIB SAVLIB
C *LIKE DEFN DBIFIL SAVFIL
*
C KEY1 KLIST
C KFLD DBILIB
C KFLD DBIFIL
*
C SAVKEY KLIST
C KFLD SAVLIB
C KFLD SAVFIL
LATEST COMMENTS
MC Press Online