Escolar Documentos
Profissional Documentos
Cultura Documentos
This chapter will take you through the steps to create and run an RPG IV program. It assumes you are using one of two ways to do your coding: 1. 2. RD Power (or its earlier cousins RDi or WDSC) PDM and SEU through some type of 5250 emulator
Initial setup
If you are using RD Power:
create a connection to your IBM i create a library create a source file create a source member in the file called HELLO.RPGLE open the member in the editor If you are using PDM and SEU:
logon to a session on your IBM i create a library using the CRTLIB command create a source file using the CRTSRCPF command WRKMBRPDM specifying your library and source file create a source member HELLO in the file using F6 in WRKMBRPDM or using the ADDPFM command, giving it type RPGLE open the member in SEU by using the "2" option
Compile the program. If you are using RD Power, you can compile from the menu within the editor, or you can save your changes and compile by rightclicking on the member in the navigation. Either way, select the CRTBNDRPG command, and take all the defaults. o If you are using SEU, exit the editor using F3, and compile using option 14. You could also compile from the command line using the CRTBNDRPG command, and using F4 to prompt the command.
Call the program If you are using RD Power, at this point you need to logon to a session on the IBM i. (If you are using PDM, you are already logged on.) Use the CALL command to call the HELLO program, substituting the name of your library for "yourlibrary". ===> CALL yourlibrary/HELLO
It should put up a message saying DSPLY Hello World Just press ENTER to end the program.
Edit your source again, and change it so instead of displaying 'Hello World', it tries to display a variable called "name". This will be an undefined variable and will generate a compiler error. /free dsply name; return;
Delete the old version of the program using the DLTPGM command. Compile the program again. It will fail with a severity 30 error. If you are using RD Power, click on the RNF7030 error in the Error List Window. It should highlight the line that tried to display the "name" variable. If you are using SEU, use the WRKSPLF command and then use F18 to get to the end of the list of spooled files. Use option 5 on the HELLO spooled file and use the B command to go to the end of the listing. Page up a bit until you find the list of the error messages, then take note of the error message with the highest severity (the RNF7030 message). Return to the top of the file and enter RNF7030 on the search line, then hit F16 to locate the error message in the listing. The error message will have a statement number (statement 2). Page back in the listing to find statement 2 (on the left hand side of the listing). It should be the line that tried to display the "name" variable. The error message indicates that NAME is not defined. To correct it, you'll have to add a definition for a variable called NAME. (Note that RPG IV is not case-sensitive for variable names, so variable "name" is the same as variable "NAME". The upper-case form of the name will appear in the listing in error messages and the cross reference.) Add the following definition statement to the beginning of your source member. Be sure the initial D goes in column 6, and be sure the other text on the line stays in the same columns it appears here. RPG is column-sensitive for most of its statements, so the code must start in the correct column. D name /free dsply name; return; s 10a inz('Jim')
Recompile the program and call it again. It should put up a message saying DSPLY Jim
Debugging
Compile your program again, specifying a debug view other than *NONE or *STMT. For example, specify DBGVIEW(*LIST). (But *ALL, *SOURCE, or *COPY would all work fine here.) Now, start the debugger. For now, I'll just mention the system debugger, but there is a more user-friendly debugger associated with RD Power, RDi or WDSC that you can investigate separately. Assuming your program is called QTEMP/MYPGM, do the following commands: ===> STRDBG QTEMP/MYPGM - at this point, just use F10 to set a breakpoint on the first executable statement - you could also use F6 to set a breakpoint on a particular statement ===> CALL QTEMP/MYPGM - when you get a breakpoint, step (F10) through the program - display variables (F11) along the way ===> ENDDBG
That's it!
From now on, I'll assume you know
how to edit and compile your code how to locate the error messages associated with a failed compile how to call a program how to debug a program
The specifications must appear in the order given above, but it is not necessary to code all the specifications. A module could contain just a single C spec. Most RPG specifications contain the specification type in column 6. The exception is free-form calculation specifications which are coded between /FREE and /END-FREE directives. Free-form calculations have blanks in column 6 and 7. The examples given in chapter 1 all used free-form calculations.
RPG cycle
RPG was originally created to handle files. The intention was that the program would read a record from the "primary file" and for each record, it would perform the calculations. When it reached the last record, it would close the file and end the program. To artificially create the "last record" situation, the program could set on the "Last Record" indicator, called *INLR. For programs that do not have a primary file, it is still necessary to stop the program from looping through the calculations. This can be done two ways: 1. 2. By using the RETURN operation. By setting on the Last Record indicator. *inlr = '1'; There are subtle differences in these two mechanisms that you will learn in later chapters.
Define a constant
The constant is the simplest RPG definition. It has a name, a definition type of 'C' and a value. Paste the following code into a source member, then compile and run it. D max_elems c 100 D default_city_name... D c 'London' /free dsply max_elems; dsply default_city_name; return;
The "for loop". The %char built-in function which converts the numeric value to a readable character form such as "-12.345". The '+' operator acts on strings as well as numeric values. Exercise 3-1
1. 2. 3.
Define a standalone field of type character with length 5. The data type for character is 'A'. Code an assignment statement to set the character field to 'hello'. Display the character field.
Paste the following code into a source member, then compile and run it. Note: This example uses INZ keyword which provides an initialization for the subfield. You can also code the INZ keyword for a standalone field. D info ds qualified D name 10a inz('Sam') D salary 9p 2 inz(50000.25) D otherInfo ds likeds(info) D inz(*likeds) /free dsply (info.name + ' has a salary of' + %char(info.salary)); otherInfo.name = 'Joe'; otherInfo.salary += 10000; dsply (otherInfo.name + ' has a salary of' + %char(otherInfo.salary)); return; Bonus features in this example:
The QUALIFIED keyword which means that the subfields of the data structure must be qualified by the data structure name, DS.SUBFIELD. Without the QUALIFIED keyword, subfields are referred to just by their name. The LIKEDS keyword is used to define another data structure with the same subfields as the parent data structure. The LIKEDS data structure is automatically qualified. The INZ(*LIKEDS) keyword is used to initialize the LIKEDS data structure the same as the parent. The += operator works the same as it does in C and Java. It adds the value on the right-hand-side to the variable on the left-hand-side.
Define an array
You can define an array of scalars or an array of data structures. RPG only supports one dimension for arrays. Multiple-dimension arrays can be simulated by using data structure arrays with array subfields; instead of coding cell(i j k) you would code table(i).row(j).col(k). The dimension of the array is specified using the DIM keyword. Array indexes are specified with parentheses. Define a scalar array Paste the following code into a source member, then compile and run it. D dates s d datfmt(*iso) dim(3) /free dates(1) = %date(); // the current date dates(2) = dates(1) + %days(1); // tomorrow dates(3) = dates(1) + %years(1); // next year dsply (%char(dates(1)) + ' ' + %char(dates(2)) + ' ' + %char(dates(3))); return; Bonus features in this example:
The date data type with the ISO format (yyyy-mm-dd). The %date built-in function which returns the current date when no parameter is specified. %date can also convert a character or numeric parameter to a "true date".
Define a data structure array Paste the following code into a source member, then compile and run it. D person ds qualified D name 25a varying D age 5u 0 D families ds qualified dim(5) D address 50a varying
D D
numPeople 3u 0 people likeds(person) dim(8) /free families(1).address = '10 Mockingbird Lane'; families(1).people(1).name = 'Alice'; families(1).people(1).age = 3; families(1).people(2).name = 'Bill'; families(1).people(2).age = 15; families(1).numPeople = 2; dsply (families(1).people(1).name + ' is ' + %char(families(1).people(1).age) + ' years old.'); return;
The "name" and "address" subfields are defined with the VARYING keyword. When a string variable is defined with this keyword, the variable is prefixed by a 2 or 4 byte value that holds the current length of the data part of the variable. In the example, the assignment of "Alice" to the name subfield would set the current length to 5. The "people" subfield of the "families" data structure is defined with the LIKEDS keyword, so it is both a subfield and a data structure. The 5u and 3u data types. 5u defines a 2-byte unsigned integer that can hold up to 5 digits. 3u defines a 1-byte unsigned integer that can hold up to 3 digits.
Exercise 3-2 1. 2. 3. Remove the VARYING keyword from the "name" subfield. Recompile and run the program. Explain why the output doesn't look the same as before.
Define a prototype
RPG prototypes describe how to call a program, procedure or Java method. The definition type is "PR". The EXTPROC or EXTPGM keyword indicates whether it is calling a procedure or program and it also indicates exactly which procedure or program to call. (Calls to Java methods also use the EXTPROC keyword.) If neither the EXTPROC nor EXTPGM keyword is coded, the EXTPROC keyword is assumed. Example 1: Call a program A common program to call is QCMDEXC. This program runs a system command. D qcmdexc pr extpgm('QCMDEXC') D theCmd 3000a const D cmdLen 15p 5 const D dbcs 3a const options(*nopass) D cmd s 100a varying /free cmd = 'DSPJOB OUTPUT(*PRINT)'; qcmdexc (cmd : %len(cmd)); qcmdexc ('WRKSPLF' : 7); return; Bonus features in this example:
The program name in the EXTPGM keyword is case sensitive. The system would not be able to find the program if the RPG program specified say 'QcmdExc' in the EXTPGM keyword. The CONST keyword indicates that the called program will not modify the parameter. When CONST is specified, the passed parameter does not have to exactly match the type and length on the prototype. If the type and length don't match, the RPG compiler will create a temporary variable of the required type and pass that temporary to the called program. Coding CONST also allows literals and expressions to be passed as parameters. The call using the prototype is coded with the parameters in parentheses. The parameter separator is a colon, not a comma as is more usual in other languages.
The %len built-in function returns the current length of the varying-length variable "cmd".
Example 2: Call a procedure So far in these examples, the DSPLY opcode has been used. DSPLY has many limitations, including a 52-byte maximum. DSPLY also prints to the external message queue which is not always desirable. For this example, we'll call the C runtime printf() function to print a message to the standard output instead of to the external message queue. /if defined(*CRTBNDRPG) H dftactgrp(*no) H actgrp(*NEW) /endif H bnddir('QC2LE') D print D msg D num pr s 5000a varying const 10i 0 inz(25)
/free print ('This message is much longer than the 52 ' + 'characters that DSPLY allows. ' + 'The value of variable "num" is ' + %char(num)); return; /end-free P D D D D D D print b print pi msg printf pr template dummy NEWLINE c /free printf(msg + NEWLINE); /end-free P print e Bonus features in this example:
5000a
Instead of calling printf() directly, this example has a subprocedure called print() that handles the call to printf(). Calling printf() directly is a bit awkward because it has to add the new-line character, so it's convenient to wrap it in our own procedure. The print() procedure defines a constant NEWLINE with the hexadecimal value x'15'. The prototype for printf() has an extra "dummy" parameter. This is required because the C prototype for printf() indicates that it takes a variable number of parameters. The RPG way to indicate that a procedure takes a variable number of parameters is to use the OPTIONS(*NOPASS) keyword. OPTIONS(*NOPASS) indicates that it is not necessary to pass that parameter. The "template" parameter for printf() is defined as a pointer (the * data type). The parameter is passed by value (the VALUE keyword). The parameter is defined with the OPTIONS(*STRING) keyword which allows you to pass a character string as the parameter. When a character string is coded as the parameter, the RPG compiler will create a temporary variable with a "null-terminated" version of the parameter. printf() assumes that the first parameter will be null-terminated; the null-terminator is used by printf() to determine the length of the first parameter. Conditional compile directives for the H spec, /IF and /ENDIF. The DFTACTGRP and ACTGRP keywords are only allowed with CRTBNDRPG, these directives control whether those keywords are seen by the compiler. If the CRTBNDRPG command is used, "*CRTBNDRPG" will be defined, and the two H specs between the /IF and the /ENDIF will be used in the compile. If the CRTRPGMOD command is used, those two lines will not be included in the compile. The H spec has three keywords 1. DFTACTGRP(*NO): This keyword is required if the program makes bound calls. 2. ACTGRP(*NEW): This keyword sets the activation group for the program. 3. BNDDIR('QC2LE'): This keyword tells the compiler to add the QC2LE binding directory to the binding step.
Solutions to exercises
Exercise 3-2 Without the VARYING keyword, the variable contains trailing blanks which are used when the variable is concatenated with other text. Bonus question: What RPG built-in function could be used in the concatenation expression to produce the same output in the version that does not have the VARYING keyword?
A simple example
Let's start with a little example where we will just read all the records of a file. First, let's get a file to read. Enter the following command on the command line. The command will produce a file QTEMP/RPGTESTF that lists the *FILE objects in QGPL whose names start with QRPG. ===> DSPOBJD OBJ(QGPL/QRPG*) OBJTYPE(*FILE) OUTPUT(*OUTFILE) OUTFILE(QTEMP/RPGTESTF) Now, compile and run the following RPG program. Specify DBGVIEW(*LIST) on the compile command. Press ENTER on each DSPLY that shows up. Frpgtestf ip e disk /free dsply ODOBNM; If you haven't seen the power of RPG before, that might seem quite amazing that such a small and apparently simple program can do so much. Try running it under debug. When you first see the debug view, it will look very different from your original code. You will see several RPG statements that were generated by the RPG compiler. These are "Input specifications", and they describe the input buffer of the RPGTEST file. There is one I spec for each field in the file. When you step through the program, you will notice that you only get a breakpoint on the ODOBNM I spec. That is because the RPG program didn't use any of the other fields, so the RPG compiler did an optimization to avoid loading the data for those other fields. You will also notice that you step to the I spec and DSPLY opcode twice (at least, it was twice on my system, once for QRPGLESRC and once for QRPGSRC). This RPG program uses the RPG cycle. (You may have noticed that there is no RETURN operation and no setting of *INLR in the program.) The RPGESTF file is defined as a primary file (the "P" in the "IP" part of the definition). The RPG compiler generates code to read each record of the primary file and perform the calculations once for each record. If you are wondering how the file got opened, by default, RPG opens all files during module initialization. This is called "implicit open". If you don't want a particular file to be opened implicitly, you can code the USROPN keyword for the file, and code an explicit OPEN operation.
3. 4. 5. 6. 7.
8. 9.
CLOSE the file. Try compiling the program at this point. It will fail to compile with a message saying that the compiler cannot tell how the program will end. Recall that earlier examples all had a RETURN operation. 10. Add a RETURN operation. Here is the final program: Frpgtestf if e /free open rpgtestf; read rpgtestf; dow not %eof; dsply ODOBNM; read rpgtestf; enddo; close rpgtestf; return;
disk
usropn
The RPG compiler will implicitly open the file when you call your program. But what about closing the file? The RPG compiler does not always close files when a program ends by using the RETURN operation. It only closes files when it finds the "Last Record" indicator, *INLR, to be on. You can simply set *INLR on at some point before reaching the end of calculations, or you can set on *INLR and immediately return. Many RPG programmers set *INLR on as the very first calculation, as a visible clue that the calculations are only meant to be run once. Other RPG programmers set *INLR on at the end of calculations. Either way works fine to cause the calculations to end and to cause the file to be closed. Here is the final program. Frpgtestf if e /free *inlr = '1'; read rpgtestf; dow not %eof; disk
Exercise 4-2
Using the version of the program with the USROPN keyword as an example, remove the CLOSE operation (you can just comment it out using //). Frpgtestf if e disk usropn /free open rpgtestf; read rpgtestf; dow not %eof; dsply ODOBNM; read rpgtestf; enddo; // close rpgtestf; return; Call the program twice. Why does the program get an error the second time it is called?
Solutions to exercises
Exercise 4-1 When the RETURN opcode is used, and *INLR is not on when the program returns, the RPG compiler does not implicitly close the file. The next time the program is called, the file is still open and still at end of file, so subsequent READ operations will not find any records and the loop will exit immediately. Bonus activity to correct the problem: Add a SETLL *START operation for the file, before the READ loop. That will set up the file so it is positioned at beginning of file, and the program will work the same both time. setll *start rpgtestf; Exercise 4-2 When the RETURN opcode is used, the RPG compiler does not implicitly close the file. The next time the program is called, the OPEN operation fails because the the file already open. Bonus activity to correct the problem: Add a check to see if the file is already open before using the OPEN opcode. if not %open(rpgtestf); open rpgtestf; endif;
This chapter will introduce you to using display files, also called "workstation files".
Show a screen
Compile and run the following program. Frpgdspf cf e workstn /free exfmt getname; dsply ('Your name is ' + name); *inlr = '1'; This program just shows the screen and reads the input value. Then it uses the DSPLY opcode to show the value that it got.