Simplify your interactive applications by using data queues
Brief: Data queues can be used to pass asynchronous data between programs. They are ideal for passing data to a background task. You'll learn how to define and program data queues, recognize their strengths and pitfalls and use them for slick interactive sorts.
You may have heard that data queues are the fastest way to get data between programs. But, why and how would you use them as you design a system? In this article, you will see several examples of data queues in action. These examples will allow you to see how they work and will give you ideas about how you can use data queues to design more efficient applications.
Data queues are similar to other queues (i.e., output, job, message queues) with which most of you have probably had experience. A queue simply accepts items to be processed whether or not the processing will occur immediately or later. Data queues accept data that can be processed any way that you care to process it and at any time.
What Are Data Queues?
Data queues act as repositories of data. Think of a data queue as a tank of gasoline that serves several pumps at a gas station. The tanker truck from the oil company periodically fills the tank, and customers draw from the tank by pumping the gasoline into their cars. All pumps are serviced with equal priority-the gasoline is used whenever a particular user needs it.
Similarly, programs can send data to a data queue and let it accumulate there. Other programs can receive data from the data queue at any time-perhaps more than one program at the same time, until the data in the data queue is exhausted. Once a data record is received from a data queue, the data queue "forgets" that record.
Data queues are objects of type *DTAQ that can contain data. A data queue can be manipulated by many programs at the same time. Like data areas (*DTAARA), data queues do not allow DDS to define fields; therefore, the data contained in data queues is flat. However, data queues are more like files in that they can contain many records.
Data Queue Attributes
Data queues come in three flavors: *FIFO, *LIFO and *KEYED. *FIFO (first-in, first-out) means that records are taken out of the data queue in the same order in which they were placed, while *LIFO (last-in, first-out) is the reverse order. V2R1.0 introduced *KEYED, which lets you receive records sequentially or randomly by key.
Besides type, data queues have a record length that you must specify when you create the data queue. If the data queue is *KEYED, you must also specify the length of the key.
Creating and Deleting Data Queues
As you might expect, you create and delete data queues with the Create Data Queue (CRTDTAQ) and the Delete Data Queue (DLTDTAQ) commands. Surprisingly, there's no Change Data Queue (CHGDTAQ) or Display Data Queue (DSPDTAQ), although there's a Work with Data Queues (WRKDTAQ) command.
The CRTDTAQ command has the following parameters:
Data queue (DTAQ), to supply the qualified name of the data queue you want to create.
Maximum entry length (MAXLEN), to define the record length. It can be any number between 1 and 64512, and has no default value.
Sequence (SEQ), which lets you choose between *FIFO, *LIFO and *KEYED. The default value is *FIFO.
Key length (KEYLEN), valid only for *KEYED data queues, specifies the length of the key field (maximum length is 256). This key field is stored as a separate entity and is not included as part of the record space allocation, unless you specifically make it part of the record.
Text (TEXT), which allows you to enter a description of the data queue.
Force to auxiliary storage (FORCE), which lets you force each record into DASD as soon as it is sent to the data queue (*YES) or remain in memory (*NO). The default is *NO. You may want to set this value to *YES if you can't afford to lose data during a power failure.
Include sender ID (SENDERID), which lets you splice an identification to each record in the data queue. This identification contains the complete qualified job name and the user profile name of the sender. This sender ID is not part of the record, so you don't have to allocate space for it in MAXLEN.
Public authority (AUT), to indicate what authority to the data queue the public should be given. The default is *LIBCRTAUT.
Sending, Receiving and Clearing
You must use the QSNDDTAQ Application Program Interface (API) to send data to a data queue. QSNDDTAQ expects either four or six parameters: four if the data queue is *FIFO or *LIFO, six if *KEYED. 1 shows an example in RPG, using six parameters. If your data queue is not *KEYED, drop the last two parameters.
You must use the QSNDDTAQ Application Program Interface (API) to send data to a data queue. QSNDDTAQ expects either four or six parameters: four if the data queue is *FIFO or *LIFO, six if *KEYED. Figure 1 shows an example in RPG, using six parameters. If your data queue is not *KEYED, drop the last two parameters.
Similarly, you can use an API named QRCVDTAQ to receive data from a data queue. QRCVDTAQ expects either five or 10 parameters. If your data queue is *KEYED or if you are using sender IDs (see following section), you must supply 10 parameters. In all other cases, supply the first five parameters only. 2 provides an RPG example using 10 parameters.
Similarly, you can use an API named QRCVDTAQ to receive data from a data queue. QRCVDTAQ expects either five or 10 parameters. If your data queue is *KEYED or if you are using sender IDs (see following section), you must supply 10 parameters. In all other cases, supply the first five parameters only. Figure 2 provides an RPG example using 10 parameters.
To clear a data queue, you'll need to use another API, QCLRDTAQ. QCLRDTAQ only expects two parameters, both 10 characters long: the name of the data queue and the name of the library where it resides. See 3.
To clear a data queue, you'll need to use another API, QCLRDTAQ. QCLRDTAQ only expects two parameters, both 10 characters long: the name of the data queue and the name of the library where it resides. See Figure 3.
Receiving Sender IDs
When you use QRCVDTAQ to receive data from a data queue that includes sender IDs, you must receive the sender ID into a character field that is at least 44 characters long. The sender ID identifies the user and the job that sent a particular record to the data queue. You may want to use the sender ID if you want to track transactions by job or user.
The character field that receives the sender ID contains six subfields that I have been able to identify after doing some experimentation. Here they are:
1-8: Not used. 9-18: Job name. 19-28: Job user. 29-34: Job number. 35-44: User profile name. Beyond 44: Blank.
Theory in Practice
Enough about theory. Now that I have presented the foundation, we can see how to put data queues to work. My background is in the manufacturing industry, so my first example is set in a manufacturing environment.
Imagine a busy manufacturing plant where trucks come and go all day long-some delivering raw materials into the Receiving department, others picking up final assemblies from the Shipping department. These receipts and shipments have to be reported to the computer as inventory transactions.
At the same time, Inventory Control may have six data entry clerks constantly engaged in entering inventory transactions of other types, such as production receipts, material movements, transfers, scraps, and so on. Yet more people in the Sales department can also report inventory transactions when they sell products to their customers.
The inventory transaction entry would have to update several files: inventory balances, inventory master, sales order, purchase order, shop order, work order, material allocations, requirements and even a transaction history file, which could have half a million records, with five or six logicals (with immediate access path maintenance) built on top.
It's a bleak scenario. The system would be so overburdened with file I/O that performance would suffer substantially. Here's a place where data queues can greatly improve performance. Simply speaking, we would have a batch job running all day long, receiving inventory transactions from a data queue and performing all file maintenance, including the addition of new transactions to the history file (4a illustrates this system's use of data queues). Since the file updates are handled by a separate batch job, the data entry people can run simplified, faster-responding versions of the interactive entry programs which validate entries and send the transactions to the data queue. This approach relieves the QINTER subsystem from performing heavy file I/O, a task that can be better achieved by QBATCH. Bottom line-the users are going to be happier with their response time and the programming is simplified.
It's a bleak scenario. The system would be so overburdened with file I/O that performance would suffer substantially. Here's a place where data queues can greatly improve performance. Simply speaking, we would have a batch job running all day long, receiving inventory transactions from a data queue and performing all file maintenance, including the addition of new transactions to the history file (Figure 4a illustrates this system's use of data queues). Since the file updates are handled by a separate batch job, the data entry people can run simplified, faster-responding versions of the interactive entry programs which validate entries and send the transactions to the data queue. This approach relieves the QINTER subsystem from performing heavy file I/O, a task that can be better achieved by QBATCH. Bottom line-the users are going to be happier with their response time and the programming is simplified.
Learning by Example
There's nothing like a good example to help you visualize a technique, so I'll present a simplified version of the above scenario. Let me begin this presentation by defining ITH, the Inventory Transaction History file (4b). I'm keeping the file layout simple for this example. In a real-life situation, you would want to add many more fields. The interactive portion consists of a display file (4c), a record layout for our data queue (4d) and an RPG program (4e). The batch portion consists of a CL program (5a) and an RPG program (5b). Enter the code and compile each piece following the instructions given at the bottom of each figure.
There's nothing like a good example to help you visualize a technique, so I'll present a simplified version of the above scenario. Let me begin this presentation by defining ITH, the Inventory Transaction History file (Figure 4b). I'm keeping the file layout simple for this example. In a real-life situation, you would want to add many more fields. The interactive portion consists of a display file (Figure 4c), a record layout for our data queue (Figure 4d) and an RPG program (Figure 4e). The batch portion consists of a CL program (Figure 5a) and an RPG program (Figure 5b). Enter the code and compile each piece following the instructions given at the bottom of each figure.
Starting the Batch Job
First, submit program INV002CL to batch with the following command:
SBMJOB CMD(CALL INV002CL)
INV002CL creates data queue INVTRNDQ as *FIFO, having a record length of 42. We're also requesting to include the identification of the sender. Then it overrides file ITH to FRCRATIO(1) and calls RPG program INV002RG, the file update program. The FRCRATIO(1) override forces the program to write each record one at a time instead of blocking them and delaying the actual physical update. This enables you to peek with DSPPFM and see the progress of INV002RG.
INV002RG adds records to ITH by entering an infinite loop. At the top of the loop it calls QRCVDTAQ to receive a transaction record from the data queue. It uses 10 parameters because we're using sender IDs in the data queue. Notice, in particular, that the inventory transaction is received into field DQDTA, which is a data structure externally defined by INV001DD (4d); this data structure provides a record layout for the data queue.
INV002RG adds records to ITH by entering an infinite loop. At the top of the loop it calls QRCVDTAQ to receive a transaction record from the data queue. It uses 10 parameters because we're using sender IDs in the data queue. Notice, in particular, that the inventory transaction is received into field DQDTA, which is a data structure externally defined by INV001DD (Figure 4d); this data structure provides a record layout for the data queue.
If the data queue is empty at the moment, DQ#LEN is set to zero. In this case, the program checks the system time; if it's past 9:00 p.m., it leaves the infinite loop and ends the program on its own accord. On the other hand, if it received a record from the data queue, the data contained in the queue fields are validated for accuracy and then moved into the ITH fields and we WRITE to the file. Then the process continues.
Starting the Interactive Job
To enter inventory transactions, you must CALL program INV001RG. In a real- life situation, you would use a command or issue the CALL command from a menu.
As you can see in 4e, program INV001RG is very simple. In a real-life scenario, you would want to include some validity checks for the user input. INV001RG uses an externally described data structure to provide a record layout to the data queue. INV001RG uses QSNDDTAQ to send the transaction data to the data queue, listing only four parameters since the data queue is not *KEYED.
As you can see in Figure 4e, program INV001RG is very simple. In a real-life scenario, you would want to include some validity checks for the user input. INV001RG uses an externally described data structure to provide a record layout to the data queue. INV001RG uses QSNDDTAQ to send the transaction data to the data queue, listing only four parameters since the data queue is not *KEYED.
Try it out. Sign on to two sessions. In session A, submit the batch job by submitting (CALL INV002CL) if you haven't done it already, then CALL INV001RG. Key in one transaction and press Enter (INV001RG performs no data validation so you can enter anything). Now go to session B and run the Display Physical File Member (DSPPFM) command on file ITH. Your transaction will already be there! Repeat the process; each time, DSPPFM will show the latest transaction in ITH.
To end this example, press F3 to end interactive program INV001RG. Batch program INV002CL will end at 9:00 p.m. automatically, but you can cancel it manually if you'd like: run WRK-SBMJOB, then select Option 4, press F4, and specify OPTION(*IMMED).
Reviewing the Process
How does it work? The interactive portion (program INV001RG) sends a transaction to the data queue but doesn't update any files. The batch program (which is running all day long) is continually attempting to receive transactions from the data queue. It keeps trying until a transaction arrives. When this happens, it updates the files (in our simplified example, that's ITH only).
The data queue has provided a vehicle to pass data from the interactive job to the batch job. Because the data queue can be accessed from any job (it's not in QTEMP), there can be any number of interactive jobs sending inventory transactions to the data queue; the batch job will process them all in the sequence they were entered (the data queue is *FIFO).
Example of Keyed Data Queues
Keyed data queues are slick. As you have seen, *FIFO data queues can be read forward; *LIFO data queues can be read backwards. *KEYED data queues can be read randomly by key or sequentially by key in any order.
You're already familiar with the concept thanks to your experience in languages like RPG: you can read a file sequentially by key if you declare the file as keyed (K in column 31 of the F-spec) and use SETLL and READ in your C- specs. Or you can read it randomly by key by using CHAIN instead of READ. Keyed data queues let you do the same thing. As an example of using a keyed data queue, I'll use an interactive program to sort records before presenting them on the screen.
Imagine having a database file that contains one record per employee in your office, containing the employee's first and last name, title and phone extension number. Now you want to be able to display the whole list in a subfile, but resequence the records on demand by any of the four fields mentioned. You want to be able to press a function key and have the records redisplayed in a different sequence.
You could have a physical file and three logicals, but then you would have four files in your interactive program, creating complicated code to read one file (and not the others) depending on the sequence requested by the user. Maintaining multiple logical files which are only used in a single, perhaps infrequently used, application would also add substantial system overhead. Another approach would be to end the program, run OPNQRYF, and restart the program when a new sequence is requested. This also complicates the program's logic to some extent.
Use a keyed data queue instead! Since keyed data queues let you receive records sequentially by key, they provide automatic sorting of all the records you send to them. It's like being able to sort a file inside your RPG program.
The Solution
File OFCEMP (6a) contains only four fields. In a real application, you may want to draw the data from your employee master file, which would have many more fields. This file is indexed by last name and first name, although this is not vital.
File OFCEMP (Figure 6a) contains only four fields. In a real application, you may want to draw the data from your employee master file, which would have many more fields. This file is indexed by last name and first name, although this is not vital.
CL program OFC001CL (6b) starts the process by creating data queue OFCEMP in QTEMP. The data queue is created as *KEYED. The total record length is 54 (total of all four fields we want to display), and the key length is 20 since the widest field that we may select as the key has a length of 20 (OETITL).
CL program OFC001CL (Figure 6b) starts the process by creating data queue OFCEMP in QTEMP. The data queue is created as *KEYED. The total record length is 54 (total of all four fields we want to display), and the key length is 20 since the widest field that we may select as the key has a length of 20 (OETITL).
Display file OFC001DF (6c) defines the F6 key to sort the employee database; you move the cursor to the column containing the field by which you want to sort, press F6, and the file is sorted. It uses the CSRLOC keyword to position the cursor on output-that way the cursor stays in the same position it was before you pressed F6, even though there are no input fields on the screen.
Display file OFC001DF (Figure 6c) defines the F6 key to sort the employee database; you move the cursor to the column containing the field by which you want to sort, press F6, and the file is sorted. It uses the CSRLOC keyword to position the cursor on output-that way the cursor stays in the same position it was before you pressed F6, even though there are no input fields on the screen.
RPG program OFC001RG (6d) begins execution at the *INZSR subroutine. It positions the cursor at row 3, column 16 (the first name column of the subfile) and runs subroutine SORT.
RPG program OFC001RG (Figure 6d) begins execution at the *INZSR subroutine. It positions the cursor at row 3, column 16 (the first name column of the subfile) and runs subroutine SORT.
SORT first clears the data queue by calling QCLRDTAQ, then clears the subfile. Next, it reads all records in the OFCEMP file, sending each record to the data queue. Notice that I have coded a SELEC group which assigns a different value to KEYFLD depending on the position of the cursor. Actually, only the column number has any bearing: if less than 16, KEYFLD receives the extension number; if less than 35, it receives the first name; if less than 53, it receives the last name; otherwise, it receives the title. Thus we send the employee record to the data queue, but specifying a different field as the key field. Lastly, the data queue is read sequentially by key, writing each record into the subfile.
Control is then transferred to the mainline, which displays the subfile. If the user moves the cursor and presses F6, OFC001RG determines the position of the cursor via the INFDS of the display file and runs SORT all over again.
With the small file (30 records), performance was excellent. If you decide to implement this technique with your large files, performance may be a consideration.
How It Works
Subroutine SORT is the place where everything happens. All I've done is read the file sequentially (in arrival sequence-not even by key) and write each record into the data queue, repeating one of the four fields as the key field (KEYFLD). Which field is used depends on the position of the cursor. Because the data queue is keyed, I must use six parameters when I call QSNDDTAQ.
Then I turn around and read the data queue sequentially by key. This is done by specifying 'GE' as the order in which I want to receive the records and specifying *BLANK as the key value. Consequently, the data queue will issue the record that has the lowest key. Since records are automatically deleted from the data queue when QRCVDTAQ receives them, the next loop through QRCVDTAQ (again using 'GE' and *BLANK) will receive the next record in ascending value of the key field. The records come out of the queue in alphabetic sequence. If I had specified 'LE' for the order and *HIVAL (or all 9's) for the key value, the records would have come out in descending order.
Try it out! Enter the source code in Figures 6a to 6d and compile them as indicated at the bottom of each figure. Then use DFU or SQL to add a few records to OFCEMP. To use DFU, for example, run the Update Data (UPDDTA) command. Then CALL OFC001CL to start the demo.
Drawbacks of Data Queues
Data queues provide the fastest method to pass information between jobs, but they do have a few problems that I'd like to submit to you:
You can't display their contents because there's no Display Data Queue (DSPDTAQ) command. QUSRTOOL provides one, but it doesn't work. When I tested it, it only displayed the first character of each record instead of the whole record. I'm convinced it's a bug somewhere.
Since entries are automatically deleted the moment they're received with QRCVDTAQ, there's a potential for losing data if the program that receives the record from the data queue aborts before making use of it. The record would be lost and there's no way to retrieve it. Commitment control won't help because data queues are not files.
As you send records to data queues, the size of the *DTAQ object in-creases. When records are received from the data queue, the object does not shrink. If the Display Object Description (DSPOBJD) command shows an abnormally large size for a data queue, you should delete it and re-create it or use the Reorganize Data Queues command I've created (see page 66).
The Queue Review
You have seen how much you can accomplish with data queues. They provide an excellent vehicle for program-to-program transferral of data without overburdening your system. While they are not a drop-dead-if-you-don't-use-'em technique, data queues do have a place in modern, structured programming and can improve the performance of your system considerably.
Line Up for Data Queues
Figure 1 Using QSNDDTAQ in RPG
Figure 1: Using QSNDDTAQ in RPG ... 1 ...+... 2 ...+... 3 ...+... 4 ...+... 5 ...+... 6 C CALL 'QSNDDTAQ' C PARM DTAQ 10 C PARM DQLIB 10 C PARM LEN 50 C PARM DATA C PARM KEYLEN 30 C PARM KEY DTAQ: Data queue name DQLIB: Data queue library name LEN: Record length DATA: Record data. Must be of the length indicated by LEN. Optional Parameters KEYLEN: Key length, if *KEYED. KEY: Key value, if *KEYED. Must be of the length indicated by KEYLEN.
Line Up for Data Queues
Figure 2 Using QRCVDTAQ in RPG
Figure 2: Using QRCVDTAQ in RPG ... 1 ...+... 2 ...+... 3 ...+... 4 ...+... 5 ...+... 6 C CALL 'QRCVDTAQ' C PARM DTAQ 10 C PARM DQLIB 10 C PARM LEN 50 C PARM DATA C PARM WAIT 50 C PARM ORDER 2 C PARM KEYLEN 30 C PARM KEY C PARM SNDLEN 30 C PARM SNDID DTAQ: Data queue name DQLIB: Data queue library name LEN: Length of record received. Set to zero if no record was received. DATA: Data received. Must be of the length indicated by LEN. WAIT: Seconds to wait for a record. Zero means no wait. A negative number means to wait indefinitely. Optional Parameters ORDER: Order to receive by key, if *KEYED. Can be EQ, NE, LT, LE, GT or GE. KEYLEN: Key length, if *KEYED. KEY: Key value, if *KEYED. Must be of the length indicated by KEYLEN. SNDLEN: Length of SNDID field. 44 is recommended. SNDID: Field to receive sender ID. Character, 44 bytes is recommended.
Line Up for Data Queues
Figure 3 Example of QCLRDTAQ in RPG
Figure 3: Example of QCLRDTAQ in RPG ... 1 ...+... 2 ...+... 3 ...+... 4 ...+... 5 ...+... 6 C CALL 'QCLRDTAQ' C PARM DTAQ 10 C PARM DQLIB 10
Line Up for Data Queues
Figure 4A Inventory System (unable to display graphic)
Line Up for Data Queues
Figure 4B Physical file ITH
A R ITREC A ITITEM 15 TEXT('ITEM NUMBER') A ITQTY 7P 0 TEXT('TRANSACTION QUANTITY') A EDTCDE(J)
Line Up for Data Queues
Figure 4C Display file INV001DF
A DSPSIZ(24 80 *DS3) A REF(ITH) A PRINT A CA03(03 'Exit') A CA12(12 'Cancel') * A R ENTRY A BLINK A 1 27'Enter Inventory Transactions' A DSPATR(HI) A 4 2'Enter a transaction:' A COLOR(BLU) A 6 10'Item number' A 6 35'Quantity' A EITEM R B 7 10REFFLD(ITITEM) A EQTY R B 7 34REFFLD(ITQTY) A 23 2'F3=Exit F12=Cancel' A COLOR(BLU)
Line Up for Data Queues
Figure 4D DDS INV001DD for Data Queue
A REF(ITH) * A R DQREC A DQITEM R REFFLD(ITITEM) A DQQTY R REFFLD(ITQTY)
Line Up for Data Queues
Figure 4E RPG program INV001RG
FINV001DFCF E WORKSTN * IDQDTA E DSINV001DD * C 'FOREVER' DOWEQ'FOREVER' C EXFMTENTRY C *IN03 IFEQ *ON C *IN12 OREQ *ON C LEAVE C ENDIF * Move data entry fields to subfields of DQDTA structure C MOVE EITEM DQITEM C Z-ADDEQTY DQQTY * Send DQDTA structure to data queue C CALL 'QSNDDTAQ' C PARM 'INVTRNDQ'DQ#NAM 10 C PARM '*LIBL' DQ#LIB 10 C PARM 19 DQ#LEN 50 C PARM DQDTA * Erase entry fields C MOVE *BLANK EITEM C Z-ADD*ZERO EQTY C ENDDO * C MOVE *ON *INLR
Line Up for Data Queues
Figure 5A CL program INV002CL
INV002CL: PGM DCL VAR(&RTNLIB) TYPE(*CHAR) LEN(10) RTVOBJD OBJ(INV002CL) OBJTYPE(*PGM) RTNLIB(&RTNLIB) CRTDTAQ DTAQ(&RTNLIB/INVTRNDQ) MAXLEN(19) SEQ(*FIFO) MONMSG MSGID(CPF0000) OVRDBF FILE(ITH) FRCRATIO(1) CALL PGM(INV002RG) ENDPGM
Line Up for Data Queues
Figure 5B RPG program INV002RG
FITH O E DISK A * IDQDTA E DSINV001DD * C 'FOREVER' DOWEQ'FOREVER' * Get transaction request from data queue C CALL 'QRCVDTAQ' C PARM 'INVTRNDQ'DQ#NAM 10 C PARM '*LIBL' DQ#LIB 10 C PARM 19 DQ#LEN 50 C PARM DQDTA C PARM 300 DQ#WT 50 * If none retrieved, check system time to end program C DQ#LEN IFEQ *ZERO C TIME SYSTIM 60 C SYSTIM IFGT 210000 C LEAVE C ENDIF C ELSE * Else, write transaction to history file and continue C MOVE DQITEM ITITEM C Z-ADDDQQTY ITQTY C WRITEITREC C ENDIF C ENDDO * C MOVE *ON *INLR
Line Up for Data Queues
Figure 6A Physical file OFCEMP
Figure 6a: Physical File OFCEMP ... 1 ...+... 2 ...+... 3 ...+... 4 ...+... 5 ...+... 6 ...+... 7 ...+ ...8 A UNIQUE A R OEREC A OELNAM 15 TEXT('Last name') A OEFNAM 15 TEXT('First name') A OETITL 20 TEXT('Title') A OEEXTN 4S 0 TEXT('Phone extension number') A EDTCDE(3) A K OELNAM A K OEFNAM ... 1 ...+... 2 ...+... 3 ...+... 4 ...+... 5 ...+... 6 ...+... 7 ...+ ...8 To compile: CRTPF FILE(xxx/OFCEMP) SRCFILE(xxx/QDDSSRC)
Line Up for Data Queues
Figure 6B CL program OFC001CL
Figure 6b: CL Program OFC001CL OFC001CL: PGM DLTDTAQ DTAQ(QTEMP/OFCEMP) MONMSG MSGID(CPF0000) CRTDTAQ DTAQ(QTEMP/OFCEMP) MAXLEN(54) SEQ(*KEYED) + KEYLEN(20) CALL PGM(OFC001RG) ENDPGM To compile: CRTCLPGM PGM(xxx/OFC001CL) SRCFILE(xxx/QCLSRC)
Line Up for Data Queues
Figure 6C Display file OFC001DF
Figure 6c: Display File OFC001DF ... 1 ...+... 2 ...+... 3 ...+... 4 ...+... 5 ...+... 6 ...+... 7 ...+ ...8 A DSPSIZ(24 80 *DS3) A PRINT A CA03(03 'Exit') A CA06(06 'Sort') A CA12(12 'Cancel') A REF(OFCEMP) * A R EMPRCD SFL A $EXTN R O 4 8REFFLD(OEEXTN) A $FNAM R O 4 16REFFLD(OEFNAM) A $LNAM R O 4 35REFFLD(OELNAM) A $TITL R O 4 53REFFLD(OETITL) * A R EMPCTL SFLCTL(EMPRCD) A BLINK A CSRLOC(CSRROW CSRCOL) A OVERLAY A 81 SFLDSP A 81 SFLDSPCTL A N81 SFLCLR A N80 SFLEND A SFLSIZ(0300) A SFLPAG(0018) A CSRROW 3 0H A CSRCOL 3 0H A 1 29'Display Office Employees' A DSPATR(HI) A 3 5'Extension' A DSPATR(HI) A 3 16'First Name' A DSPATR(HI) A 3 35'Last Name' A DSPATR(HI) A 3 53'Title' A DSPATR(HI) * A R FKEYS A LOCK A 23 2'F3=Exit F6=Sort (cursor sensitiv- A e) F12=Cancel' A COLOR(BLU) ... 1 ...+... 2 ...+... 3 ...+... 4 ...+... 5 ...+... 6 ...+... 7 ...+ ...8 To compile: CRTDSPF FILE(xxx/OFC001DF) SRCFILE(xxx/QDDSSRC)
Line Up for Data Queues
Figure 6D RPG program OFC001RG
Figure 6d: RPG Program OFC001RG ... 1 ...+... 2 ...+... 3 ...+... 4 ...+... 5 ...+... 6 ...+... 7 ...+ ...8 FOFC001DFCF E WORKSTN F SFLRRNKSFILE EMPRCD F KINFDS INFDS FOFCEMP IF E DISK * ... 1 ...+... 2 ...+... 3 ...+... 4 ...+... 5 ...+... 6 ...+... 7 ...+ ...8 IINFDS DS I B 370 3710CURSOR * IDQDTA E DSOFCEMP I OELNAM LNAM I OEFNAM FNAM I OETITL TITL I OEEXTN EXTN *======================================================= C 'FOREVER' DOWEQ'FOREVER' C EXFMTEMPCTL C SELEC C *IN03 WHEQ *ON Exit C *IN12 OREQ *ON Cancel C LEAVE C *IN06 WHEQ *ON Sort C CURSOR DIV 256 CSRROW C MVR CSRCOL C EXSR SORT C ENDSL C ENDDO C MOVE *ON *INLR *======================================================= C *INZSR BEGSR C WRITEFKEYS C Z-ADD3 CSRROW C Z-ADD16 CSRCOL C EXSR SORT C ENDSR *======================================================= C SORT BEGSR * Clear data queue C CALL 'QCLRDTAQ' C PARM 'OFCEMP' DQ#NAM 10 C PARM '*LIBL' DQ#LIB 10 C MOVE *OFF *IN81 C WRITEEMPCTL C MOVE *ON *IN81 * Read database file, writing to data queue C Z-ADD*ZERO RRN 40 C 'FOREVER' DOWEQ'FOREVER' C ADD 1 RRN C RRN CHAINOFCEMP 99 C *IN99 IFEQ *ON C LEAVE C ENDIF * Key depends on position of cursor C SELEC C CSRCOL WHLT 16 C MOVELOEEXTN KEYFLD 20 C CSRCOL WHLT 35 C MOVELOEFNAM KEYFLD C CSRCOL WHLT 53 C MOVELOELNAM KEYFLD C OTHER C MOVELOETITL KEYFLD C ENDSL C MOVE OELNAM LNAM C MOVE OEFNAM FNAM C MOVE OETITL TITL C Z-ADDOEEXTN EXTN C CALL 'QSNDDTAQ' Send C PARM DQ#NAM C PARM DQ#LIB C PARM 54 DQ#LEN 50 C PARM DQDTA C PARM 20 DQ#KL 30 C PARM KEYFLD DQ#KEY 20 C ENDDO * Read data queue sequentially by key, writing to subfile C SUB 1 RRN C 1 DO RRN SFLRRN 40 C CALL 'QRCVDTAQ' Receive C PARM DQ#NAM C PARM DQ#LIB C PARM 54 DQ#LEN C PARM DQDTA C PARM *ZERO DQ#WT 50 C PARM 'GE' DQ#ORD 2 C PARM 20 DQ#KL 30 C PARM *BLANK DQ#KEY C PARM 44 DQ#SL 30 C PARM *BLANK DQ#SND 44 C Z-ADDEXTN $EXTN C MOVE FNAM $FNAM C MOVE LNAM $LNAM C MOVE TITL $TITL C WRITEEMPRCD C ENDDO C ENDSR ... 1 ...+... 2 ...+... 3 ...+... 4 ...+... 5 ...+... 6 ...+... 7 ...+ ...8 To compile: CRTRPGPGM PGM(xxx/OFC001RG) SRCFILE(xxx/QRPGSRC)
LATEST COMMENTS
MC Press Online