Provide your users with everything they've ever wanted in a subfile program.
Editor's Note: This article is an excerpt from the book Subfiles in Free-Format RPG published by MC Press.
If you've ever looked at Program Development Manager (PDM), you might have marveled at what a cool subfile application it is. PDM is extremely flexible. It allows you to move anyplace 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 their own, each of these features is simple to code in an RPG subfile program. The real fun begins, though, when you combine the features.
I worked for a software development house that wanted the IBM look-and-feel on all of its screens. The thinking was that users familiar with the System i would be comfortable using the interactive screens in our software and would require less training. It seemed simple enough at first, but as you will soon see, incorporating all the features included with PDM into a subfile application is no small task. In fact, PDM isn't even a subfile application; its displays are written using the User Interface Manager (UIM), the language used for many native i5/OS commands and all of the help panels on the System i.
The Dilemma
What do you do if you're an RPG programmer who likes the look, feel, and flexibility of PDM but doesn't know how to obtain these features using UIM? Do you learn UIM? You could, but if you already know RPG, is learning a new language the most effective use of your time? This was the dilemma I faced.
I'm not against learning UIM, but I thought that there must be a more efficient way to get the same results using 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 you're looking for—without having to learn UIM.
Data Queues 101
Data queues are a type of system object (type *DTAQ) you can create and maintain using i5/OS commands and APIs. They're System i objects that can be used to send and receive multiple record-like strings of data. Data may be sent to and received from a data queue from multiple programs, users, or jobs. This makes data queues an excellent mechanism for sharing data. They provide a fast means 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 entry placed on the queue. The sender ID, an attribute of the data queue established when the queue is created, contains the qualified job name and current user profile.
Another advantage to using data queues is that you can 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 zero to 99,999 tells the job to wait that number of seconds before processing.
High-level language programs (HLLs) can send data to a data queue using the Send to a Data Queue (QSNDDTAQ) Application Program Interface (API). Similarly, HLLs can receive data using the Receive from a Data Queue (QRCVDTAQ) API. Data queues can be read in FIFO, LIFO, or keyed sequence. The technique I use for building PDM-like subfile applications requires a keyed data queue. Keyed data queues allow the programmer to either specify a specific order in which entries on the data queue are received or retrieve only data queue entries that meet a criterion. That is, the programmer can receive a data queue entry that's 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.
Why Should I Use a Data Queue in a Subfile Program?
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. I wanted to provide the PDM look-and-feel by allowing users to go anywhere in the subfile using the position-to field and then page up or down from that new position. This is easily accomplished using a page-at-time subfile.
However, I also wanted the data that was changed on subfile records to be saved, regardless of where the user navigated in the subfile, until the user was ready to process them by pressing Enter. This is accomplished easily in a load-all or self-extending subfile but not in a page-at-a-time subfile.
With page-at-a-time subfiles, you have to clear and build one new page of subfile records every time the user pages and uses a position-to field. Any previously changed records are not saved. I needed a way to combine the flexibility of the page-at-a-time subfile with the capability to process the changed records only when desired. I needed a place to store and reuse changed subfile records until the user was ready to process them. Using data queues to accomplish this task instead of data structures, files, or arrays freed me from some additional work.
During an interactive job, the data queue APIs can provide better response times. They also decrease the size of the program, as well as 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 API, it's physically removed from the data queue. You don't have to add code to deal with unnecessary entries.
I use the data queue to store a replica of the changed subfile record. Each time a subfile record is changed and the Enter or Page key is pressed, the changed subfile record is stored in the data queue. I do this because I build the subfile one page at a time, so I need to know which records were previously changed. Once records are no longer displayed on the screen, they're not part of the subfile.
When the user positions through the subfile by scrolling or by keying something in the position-to field, the program checks to see if each record read from the data file exists in the data queue before loading it to the subfile. If the record exists in the data queue, it's written to the subfile, along with the earlier changes, and marked as changed. When the user is ready to process all the changed records by pressing Enter with nothing in the position-to field, the record is processed appropriately from the data queue.
The DDS
Before I 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 program, not i5/OS, must completely control the subfile. Of course, you know what that means: the subfile page (SFLPAG) and subfile size (SFLSIZ) values must be equal.
The subfile will never contain more than one page, but, as you'll see, the program will make it appear that the subfile contains much more than one page of data.
Control Language
It's important to note that even though an entry is removed from a data queue after it's received, the space containing the entry isn't. Over the long run, performance will suffer because the data queue's size will increase. For this reason, I delete and re-create the data queue each time the program is called.
Even if you build your data queues in QTEMP, as I do, it's best to delete and re-create them, in case the user calls the program more than once before signing off. Program SFL001CL accomplishes this task.
Abracadabra! The Subfile's Never More Than One Page
Now that the setup issues have been covered, it's time to perform some magic. Let's start with the RPG program. The program's first task is to load the subfile with one page of records (in this case, nine). This is code from SFL001RG, which can be downloaded here.
Notice that each time a record is read from the data file, the receive_queue subroutine is executed. For the initial subfile load, this subroutine won't accomplish anything. (I will explain this later). However, after the initial load, the receive_queue subroutine plays a vital part in the subfile load routine.
Once the subfile is initially loaded and displayed, the user can do several things:
- Scroll through the subfile
- Add, change, display, or delete records
- Position the cursor in another place in the subfile
- Exit the program
No matter what the user decides to do, the add_queue subroutine is executed each time the Enter key or a valid function key (other than F3 or F12) is pressed. This subroutine uses the READC op code to find changed records in the subfile and add them to the data queue using the QSNDDTAQ API.
When the user paged down, an entry was added to the data queue that consisted of the option (4) and the value in DBIDNM, which is the key to the data file, the key to the data queue, and a hidden field in the subfile.
The add_queue subroutine keeps track of all records changed through the subfile. For example, if the user decides to delete two records on the next page after pressing 4 to delete a record on the current page, the add_queue subroutine sends the two changed records to the data queue before rebuilding the subfile in the Page Forward (BUILD_SUBFILE) routine. Now there are three entries in the data queue, and nothing has been deleted. The same logic holds true if the user decides to move to another part of the subfile using the position-to field.
Now we can get to the details of the receive_queue routine. If a matching entry exists in the data queue, 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. If it does, it's displayed along with the previously selected option. A user who wanted to page up to see the first page, after having selected two records for deletion on the second page, could do so. The user would see a 4 in the original record selected for deletion, and the data queue would now contain the two records from the second page.
If the user presses the Enter key and the position-to field is empty, the add_queue routine executes one last time to load any changes to the current page, and the process_subfile routine is executed. The process_subfile routine in this example is a little different than the one in the original Name Master File Maintenance program. This subroutine uses the RCVDTAQ API instead of READC to process all the changed records.
Remember that changed records will reside in the data queue, not the subfile, which never contains more than one page of data. By setting the key value, DBIDNM, to one and the order to GE (greater than or equal to), you're sure to retrieve all entries in the data queue. The RCVDTAQ API will be run until the length (LEN) parameter is zero. That will happen when no more entries exist in the data queue.
Each time an entry is received, the data is run through a SELECT routine to determine which function needs to be performed. In this program, depending on the option taken, a display screen, an update screen, or a delete confirmation subfile will appear.
Summary
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. It will furnish your users with everything they've ever wanted in a subfile program. The use of data queues allows you to create the following features in one subfile program:
- Move anyplace in the subfile using the position-to feature.
- Page forward or backward from that position.
- Change a record on any page of the subfile.
- Process all changed records only when the Enter key is pressed.
The QSNDDTAQ API is used to write subfile records you want to save to the data queue. The RCVDTAQ API is used to retrieve the subfile records from the data queue and write back to the subfile
Editor's note: See related code files sfl001cl.txt and sfl001df.txt.
LATEST COMMENTS
MC Press Online