RPG 101

(click to open)

Quick Page Table of Contents

Scanning…

RPG 101

Introduction

This page is designed to help the PHP programmer unfamiliar with traditional RPG 5250 programming. A few design liberties have been taken over RPG code you see in active service, so the we can also demonstrate the MVC pattern using the same RPG SRVPGM in an Apache “stateless” environment.

Perhaps “RPG” syntax of various MVC elements initially look unrecognizable to a HTML / PHP programmer, but i think if you view them one at time you can began to see the similarity of design. Matter of fact, i believe some RPGers have been doing a version of MVC for some time now (who knew).

Here is the basic bill of materials for this 5250 application (wrklibpdm qiwiki → Opt 12):

  • View (HTML): display file (DVDSDSPF *FILE DSPF)
  • Control (PHP): main program (DVDSORDR *PGM RPGLE)
  • Model (database): service program (DVDSSERV *SRVPGM RPGLE)

View: display file (DVDSDSPF)

What does a 5250 display file look like?

This is a very common sort of 5250 pattern sample. The first screen presented asks for search criteria, followed by a list screen for user selection. The list screen uses a subfile, which allows for automatic paging if the list exceeds the 5250 window size.

The first section of the display file establishes the screen size (rows 24 and columns 80) and the external indicator area (INDARA) for the RPG program. As a PHP developer, display file may look a foreign entity at first glance, but if you remember your first HTML attempt I believe you would have to say you did a lot of cut/paste.

     A* DDS SOURCE FOR SCREENS USED BY DVDSORDR.
     A*
     A                                      DSPSIZ(24 80 *DS3)
     A                                      INDARA

5250 search screen


This screen allows the user to enter various types of string search patterns by Title, Author, etc. Searches by title or author are wild card based, so as long as the respective database field starts with the string it will be included in the search results (do not use ‘*’, just starting characters).

  • To run:
    • addlible zphpdemo
    • call dvdsordr

The display file format DVDSDSPF3 is responsible for this window. If you look closely you can see the numbers like “1 33, 1 70, etc.”, that are simply x,y screen coordinates across a 80 column by 24 row screen. The variable names will be waiting for your RPG program when you open the workstation file like “SCSTITLE 50A B”, a 50 character ‘B’oth input/output field at location row 3 column 25, with a builtin CHECK(LC) to make sure the character string looks good (don’t you wish HTML would do that for you?).

     A* ***************************************************
     A          R DVDSDSPF3
     A                                      CA12(12)
     A                                  1 33'DVD Search'
     A                                      DSPATR(HI)
     A                                  1 70'Search'
     A                                      DSPATR(HI)
     A                                  2 10'Select Type of Search:'
     A                                      DSPATR(HI)
     A                                  3 10'Title:'
     A            SCSTITLE      50A  B  3 25CHECK(LC)
     A                                  4 10'Actor:'

How do you use a subfile in a display?


After the user presses enter with search criteria, this screen will display a subfile list of all records that match the search request. The user may select DVDs to be included in his shopping cart. For this example the data is ignored on enter and we return back to search screen.

The second display file format is a bit more complicated because it overlays a subfile (DVDSDSP4S) on top of the screen format (DVDSDSP4C) that acts much like a mini database with relative records. The screen x,y positions mark the first row and pattern all rows to follow (SC4TITLE 19 chars; O=ouput; row 4 column 10, etc.). There are indicators to control the subfile list (52 *ON clear the list; N52 *OFF more… may be active; etc.) and the are directives for sizes (SFLPAG(0018) more than 18 items user needs to scroll or PgDn/PgUp the list).

     A* ***************************************************
     A          R DVDSDSP4S                 SFL
     A            SC4ADD         1   B  4  5
     A            SC4TITLE      19   O  4 10
     A            SC4ACTOR      19   O  4 30
     A            SC4PRICE      11   O  4 50
     A            SC4ITEM        9  0H
     A            SC4LINE        3  0H
     A          R DVDSDSP4C                 SFLCTL(DVDSDSP4S)
     A                                      CA12(12)
     A                                      OVERLAY
     A N53                                  SFLDSP
     A N52                                  SFLDSPCTL
     A  52                                  SFLCLR
     A N52                                  SFLEND(*MORE)
     A                                      SFLSIZ(20)
     A                                      SFLPAG(0018)
     A                                  1 33'DVD Search'
     A                                      DSPATR(HI)
     A                                  1 70'Results'
     A                                      DSPATR(HI)

Control: main program (DVDSORDR)

What does modern RPG syntax look like (/free /end-free)?

As a PHP programmer you are going to find that many of the modern RPG statements look very familiar. The magic of /free /end-free has nearly eliminated the old school C specification (calculation specification), in favor of a modern feel syntax.

No surprise, a /free statement ends with a semi-colon. Strings are single quote (not double — warned you). and string continuation is +. The begin and end of blocks has matching style dou/enddo and if/endif, which many RPGers find preferable to read over {}.

       dou scErrMsg = *blanks;
           if (SCSMAX<=0);
             SCSMAX=1;
           endif;
           exfmt DVDSDSPF3;
           scErrMsg = *Blanks;
           err=0;
           if (dspfunc.CANCEL);
              return *OFF;
           endif;
           if SCSTITLE<>*blanks;
             err += 1;
           endif;
           if SCSACTOR<>*blanks;
             err += 1;
           endif;
           if SCSCAT<>*blanks;
             err += 1;
           endif;
           if err<=0 or err>=2;
              scErrMsg = 'You must enter one of +
                          title, actor or category +
                          for search';
           elseif SCSMAX<=0 or SCSMAX>=11;
              scErrMsg = 'You must enter +
                         a number between 1-11';
           endif;
       enddo;
       Search_k.TITLE    = SCSTITLE;
       Search_k.ACTOR    = SCSACTOR;
       Search_k.CATEGORY = SCSCAT;
       Search_k.MAX      = SCSMAX;

What do RPG data specifications look like?

The biggest RPG difference over PHP is the data structures and variables. The D spec (data specification), is the heart and soul of any RPG program, and most of the work in any RPG application. Unfortunately, the syntax takes a little practice. In this example, we have a interface include (include “thing.php”;), but the syntax is

 
      /copy DVDS_H

What do RPG prototype includes look like?

This is a fairly good representation of modern RPG interface technique for the MVC pattern. In this case our main program wishes to call shared services from the service program (DVDSSERV *SRVPGM). In c language, the contents of dvds.h are prototypes and data structures, the same is true for our RPG /copy DVDS_H. Instead of PHP array() or c struct {} the RPG module uses “D name ds” (DS=data structure). Instead of function(); the RPG module uses “D name PR” (PR=prototype). Data structure elements use a single character that identifies the type of data (TITLE 50a alphabetic character; MAX 3p 0 packed decimal number; 10i 0 integer; etc.). Unfortunately you will have to look these single character types up in the RPG manual. As you can see in the following example you can easily pass data structures in/out of RPG routines in our *SRVPGM (DVDSSERV).

      *****************************************************
      * Template for products search
      *****************************************************
     D SEARCH_t        ds                  qualified
     D                                     based(Template)
     D   TITLE                       50a
     D   ACTOR                       50a
     D   CATEGORY                    50a
     D   MAX                          3p 0

      *****************************************************
      *  ORDER_loadSearch(): Routine to load the search items
      *     on disk.
      *
      *     Search_k  = (input)  Search to load from disk
      *     Count     = (output) number of items
      *     Item      = (output) array of items
      *
      * Returns *ON when successful, *OFF otherwise.
      *****************************************************
     D ORDER_loadSearch...
     D                 PR             1N   extproc(*CL:'ORDER_loadSearch')
     D  Search_k                           likeds(SEARCH_t)
     D  Count                         3p 0
     D  Item                               likeds(SEARCH_ITEM_t) dim(999)

Note the implementation of ORDER_loadSearch() is specified in the *SRVPGM with “PI” (PI = implementation);

     P ORDER_loadSearch...
     P                 B                   export
     D ORDER_loadSearch...
     D                 PI             1N
     D  Search_k                           likeds(SEARCH_t)
     D  Count                         3p 0
     D  Item                               likeds(SEARCH_ITEM_t) dim(999)
      /free

What is the RPG cycle (main loop)?

An RPG program “main” is subject to the RPG cycle, which just means it is always in a never ending loop, so we have to force the loop to stop with a return out of main (or turn indicator *inlr = *on; — sorry mate there is always a little bit of new). In fact, if the RPG compiler can not see a way to end the “main” loop it will refuse to compile your program.

      *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
      * main(): Control flow
      *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
      /free
         select;
         when   step = 3;
             if (getSearch(Search_k)=*OFF);
                *inlr = *on;
                return;
             else;
                 step = step + 1;
             endif;
         when   step = 4;
             if ( loadSearch(Search_k) = *OFF );
                 step = step - 1;
             else;
                 step = step + 1;
             endif;
         when   step = 5;
             if ( displaySearch() = *OFF );
                 step = 3;
             else;
                 step = step + 1;
             endif;
         other;
             step = 3;
         endsl;
      /end-free

How do you write/read 5250 display with exfmt?

Oh yes! We have to declare the 5250 workstation with an F spec (file specification) and identify our display file (DVDSDSPF). We also identify any subfiles with additional F spec(s), so that our view “main” program can use the variables. Notice that we have declared a relative record index variable (RRN4) to handle iteration in our subfile.

     FDVDSDSPF  CF   E             WORKSTN INDDS(DspFunc)
     F                                     SFILE(DVDSDSP4S: RRN4)

     D RRN4            s              4s 0
     D HighRRN4        s                   like(RRN4)

       DspFunc.ClearSfl4 = *ON;
       write DVDSDSP4C;
       DspFunc.ClearSfl4 = *OFF;
       RRN4 = 0;

       for x = 1 to Count;
          sc4LINE   = x;
          SC4ADD    = *Blanks;
          sc4ITEM   = Item(x).PROD_ID;
          sc4TITLE  = Item(x).TITLE;
          sc4ACTOR  = Item(x).ACTOR;
          sc4PRICE  = %char(Item(x).PRICE);
          RRN4 += 1;
          write DVDSDSP4S;
       endfor;

       HighRRN4 = RRN4;

Then write/read the format with exfmt DVDSDSP4C. And write the subfile contents with write DVDSDSP4F. If you look back at the display file you will see all the “SC” variables used match the names in each format.

       dou scErrMsg = *blanks;

         write DVDSDSP4F;
         exfmt DVDSDSP4C;
         scErrMsg = *blanks;

         if (dspfunc.CANCEL);
            return *OFF;
         endif;

         scErrMsg = 'No items selected for cart';
         for RRN4 = 1 to HighRRN4;
           chain RRN4 DVDSDSP4S;
           if (not %found);
              iter;
           endif;
           if SC4ADD = *Blanks;
              iter;
           endif;
           scErrMsg = *Blanks;
         endfor;

       enddo;

Well, that is it for the main program. You could cut/paste this pattern to do most anything with a 5250 display now. You are even in the RPG elite with control over subfiles.

Model: service program (DVDSSERV)

Last, we have RPG model. For this example we have been using the MVC pattern, so the model code has little more that DB2 file access routines. This allows us great flexibility for both our 5250 control/view and our PHP control/view.

How do you declare RPG native I/O files?

We are using native file support over DB2 SQL to demonstrate the common way of accessing data with the “native” RPG interfaces. Therefore we need to declare the files/views/indexes that our RPG program is going to open with the F spec (file specification). In this example we declared a series of indexes over the file QIWIKI/PRODUCTS, for fast and easy search by title, author, etc.:

      *****************************************************
      * Native I/O files
      *****************************************************
     FPRODUCTS  IF   E           K DISK    USROPN Rename(PRODUCTS :XPROD_t)
     FXPRODCAT  IF   E           K DISK    USROPN Rename(PRODUCTS :XPRODC_t)
     FXPRODSPEC IF   E           K DISK    USROPN Rename(PRODUCTS :XPRODS_t)
     FXPRODTITLEIF   E           K DISK    USROPN Rename(PRODUCTS :XPRODT_t)
     FXPRODACT  IF   E           K DISK    USROPN Rename(PRODUCTS :XPRODA_t)

How do you use long name PHP/Java files with RPG?

In this example we created the schema qiwiki and all tables and indexes with a PHP program. Too long names is a common problem for RPG / web mix programs, so we are demonstrating a popular RENAME technique that allows both sides to use a long name (PHP/Java) or short name alias (RPG). (You’re welcome).

$sql = array (
// create the product PF
"CREATE TABLE $lib.PRODUCTS(
  PROD_ID INTEGER NOT NULL,
  CATEGORY INTEGER NOT NULL,
  TITLE VARCHAR(50) NOT NULL,
  ACTOR VARCHAR(50) NOT NULL,
  PRICE DECIMAL(12,2) NOT NULL,
  SPECIAL INTEGER NOT NULL)",
// establish primary key
"ALTER TABLE $lib.PRODUCTS ADD PRIMARY KEY (PROD_ID)",
// Long name indexes are often used in Java or PHP
"CREATE INDEX $lib.IX_PROD_CATEGORY ON $lib.PRODUCTS (CATEGORY)",
"CREATE INDEX $lib.IX_PROD_SPECIAL ON $lib.PRODUCTS (SPECIAL)",
"CREATE INDEX $lib.IX_PROD_TITLE ON $lib.PRODUCTS (TITLE)",
"CREATE INDEX $lib.IX_PROD_ACTOR ON $lib.PRODUCTS (ACTOR)",
// Short name aliases used in RPG program (common RPG problem)
"RENAME INDEX $lib.IX_PROD_CATEGORY TO SYSTEM NAME xprodcat",
"RENAME INDEX $lib.IX_PROD_SPECIAL TO SYSTEM NAME xprodspec",
"RENAME INDEX $lib.IX_PROD_TITLE TO SYSTEM NAME xprodtitle",
"RENAME INDEX $lib.IX_PROD_ACTOR TO SYSTEM NAME xprodact",
);

How do you open native db2 files in RPG?

We built a routine to make sure the files are opened once for every worker process handling requests from the PHP front-end. Remember that there may be 100′s of users all getting information from the *SRVPGM at “the same time” as they click their browsers, so we designed the RPG model to run in many processes to handle the load.

We keep track of the files already opened with global variable “D FilesAreOpen s 1N inz(*OFF)”.

      *****************************************************
      * Global data
      *****************************************************
     D FilesAreOpen    s              1N   inz(*OFF)

      *****************************************************
      * openFiles(): Open files used by this srvpgm
      *****************************************************
     P openFiles       B
     D openFiles       PI
      /free
        if (FilesAreOpen);
           return;
        endif;

        open PRODUCTS;
        open XPRODCAT;
        open XPRODSPEC;
        open XPRODTITLE;
        open XPRODACT;

        FilesAreOpen=*ON;
        return;

        begsr *pssr;
           close *all;
           FilesAreOpen=*OFF;
        endsr;

      /end-free
     P                 E

Now all that is needed is to call the openFiles() routine in any exported *SRVPGM routine.

     P ORDER_loadSearch...
     P                 B                   export
     D ORDER_loadSearch...
     D                 PI             1N
     D  Search_k                           likeds(SEARCH_t)
     D  Count                         3p 0
     D  Item                               likeds(SEARCH_ITEM_t) dim(999)

      /free
          openFiles();
          select;
          when   Search_k.CATEGORY<>*blanks;
            return ORDER_loadSearch_Cat(Search_k:Count:Item);
          when   Search_k.TITLE<>*blanks;
            return ORDER_loadSearch_Title(Search_k:Count:Item);
          when   Search_k.ACTOR<>*blanks;
            return ORDER_loadSearch_Actor(Search_k:Count:Item);
          other;
            SetError( ORDER_ERROR_BAD_SEARCH
                  : 'Bad search request');
          endsl;
          return *OFF;
      /end-free
     P                 E

How do you write/read with RPG native I/O?

The rest of the search module is simply native file I/O with RPG native file functions like setll, read, etc. Unfortunately you will need to look these up in the RPG manuals to understand all these functions can do.

      *****************************************************
     P ORDER_loadSearch_Cat...
     P                 B
     D ORDER_loadSearch_Cat...
     D                 PI             1N
     D  Search_k                           likeds(SEARCH_t)
     D  Count                         3p 0
     D  Item                               likeds(SEARCH_ITEM_t) dim(999)

     D cat             s              9b 0
     D PRODFILE1       ds                  likerec(XPRODC_t:*INPUT)
      /free
          openFiles();
          cat=0;
          for cat = 1 to CAT_NBR+1;
            if cat >= CAT_NBR+1;
              SetError( ORDER_ERROR_CAT_NOT_FOUND
                    : 'Category ' + %TRIM(Search_k.CATEGORY) + 
                      ' not valid!');
              return *OFF;
            endif;
            if Search_k.CATEGORY = CAT_ARRAY(cat);
              leave;
            endif;
          endfor;
          Count = 0;
          setll cat XPRODCAT;
          reade(n) cat XPRODCAT PRODFILE1;
          dow not %eof(XPRODCAT);
            if Count=Search_k.MAX;
              leave;
            endif;
            Count += 1;
            Item(Count).DVDADD   = *Blanks;
            Item(Count).PROD_ID  = PRODFILE1.PROD_ID;
            Item(Count).TITLE    = PRODFILE1.TITLE;
            Item(Count).ACTOR    = PRODFILE1.ACTOR;
            Item(Count).PRICE    = PRODFILE1.PRICE;
            reade(n) cat XPRODCAT PRODFILE1;
          enddo;
          if Count<=0;
              SetError( ORDER_ERROR_NODATA_SEARCH
                    : 'Category ' + %TRIM(Search_k.CATEGORY) + 
                      ' items not found!');
              return *OFF;
          endif;
          return *ON;
      /end-free
     P                 E