Understanding the RPG Cycle: A Migration Guide
Estimated reading time: 11 minutes
Overview
The IBM i RPG Program Cycle is a series of ordered steps that the main procedure goes through for each record read. Depending on the coded specifications, your program may or may not use each step in the cycle.
Primary and secondary files indicate input is controlled by the program cycle. A full procedural file indicates that input is controlled by program-specified calculation operations (for example, READ
and CHAIN
).
To control the cycle, you can have:
- One primary file and, optionally, one or more secondary files.
- Only full procedural files (including external Printfiles).
- A combination of one primary file, optional secondary files, and one or more full procedural files in which some of the input is controlled by the cycle, and other input is controlled by the program.
- No files (for example, input can come from a parameter list or a DataArea Data structure).
QSys Cycle
The RPG implicit logic of the Cycle
is made explicit during the translation to C#.
Each program that uses the Cycle
is migrated with the StarEntry
method calling the generated _StartCycle
method as indicated by the following pseudo-code:
void StarEntry(int cparms)
{
_StartCycle();
}
The _StartCycle()
method (subroutine) contains the code that supports, in C#, RPG’s implicit Cycle
. It consists of a do loop
that repeats until the Last Record Indicator (_INLR
) is set. Within this loop the code tests for the program conditions that control the calls to the different cycle methods as detailed below. When _INLR
is set, the program is said to have processed the Last Record (on the primary file) and it should terminate.
The following Flow Chart shows the steps that are completed, the conditions that are tested and the points in which the following pre-defined methods are called:
_DetailCalc
_DetailOutput
_TotalCalc
_TotalOutput
_FetchOverflow
Detailed numbered flow-chart building blocks
-
_StartCycle is the name of the method that runs the
Cycle
. This method is generated by the C# translation process. It assumes the primary file and all secondary files are already open, which causes the first record from the primary and all secondary files to be read, and all program fields have their correct values including first page indicator (_IN1P
is initialized to ‘1’ in the class constructor). -
If _IN1P is ‘1’ (this is the first time _StartCycle is called), go to step 3. Otherwise, go to step 4.
-
Perform header and detail output by calling the
_DetailOutput
method (generated fromDetailPrintSpec
andHeaderPrintSpec
commands originally in the AVRBegCycleOutput
section). Set_IN1P
to ‘0’. -
Set all record identifying and
_L1
through_L9
indicators to ‘0’. -
If
_INLR
is ‘1’, go to step 6, otherwise go to step 7. -
Set the level indicators
_INL1
through_INL9
to ‘1’, and go to step 18. -
If this is not the first time thru the loop, read a record from the last file processed.
-
If
FORCE
was issued on the previous time thru the loop, go to step 9, otherwise go to step 10. -
The forced file is selected for processing. If the forced file is at
EOF
, go to step 10. Otherwise, the match record indicator (_INMR
) is set to ‘0’ and the match fields in the forced file are saved. Continue at step 13. -
If match fields are used in the program, go to step 11. Otherwise go to step 12.
-
The match field routine selects the next file to process. Continue at step 13.
-
The next file is selected: it will be either the primary if it is not at
EOF
, or the first secondary that is not atEOF
, selected in the order by which they are specified in the program. -
If all files are at
EOF
, then go to 14. Otherwise go to 15. -
Set
_INLR
and all level indicators_INL1
through_INL9
to ‘1’. Continue at 18. -
The record identifying indicator is set ‘1’ for the record selected for processing.
-
If there is a control break, go to 17. Otherwise go to 18.
-
The appropriate control level indicator (
_INL1
through_INL9
) together with all lower-numbered level indicators are set to ‘1’. -
Determine whether totals need to be executed and if so go to 19. Otherwise go to 20. If no control levels are specified for any record, totals are bypassed on the first cycle and are always processed after the first cycle. If control levels are specified, totals are bypassed until the first record containing control fields has been processed.
-
Call the Total Calculations (
_TotalCalc
) method, followed by the Total Output (_TotalOutput
) method (generated from theTotalPrintSpec
commands in theBegCycleOutput
section of the AVR program). -
If
_INLR
is ‘1’, go to step 22. -
The match record indicator (
_INMR
) is set ‘1’ or ‘0’ depending on whether the record read is a matching record. Data from the last record read is made available for processing. The Detail Calculations (_DetailCalc
) method is called. Continue at step 3. -
Return to the caller.
Cycle Input
Specifications
During Migration, if there are legacy RPG Input Specifications with Field Description entries corresponding to Control Level or Matching Fields, then for each record where these kind of fields are defined, the migration produces an AVR declaration that indicates the Control Level number and under which condition it is set, and/or the Match Record number and under which condition it is set.
The syntax is as follows:
DclFmtCycleAttr record-format <indicator> Ln(field-name1, field-name2) Mn(field-name)
Where,
Ln
can be L1
, L2
, L3
, L4
, L5
, L6
, L7
, L8
or L9
(depending on the Level Break indicator to use for this record)
and,
Mn
can be M1
, M2
, M3
, M4
, M5
, M6
, M7
, M8
or M9
(depending on the Matching Fields indicator to use for this record)
The indicator is optional and will be set to ‘1’ when a record of this kind is read. See Record Identification Input Spec.
For example:
DclFmtCycleAttr Master *In77 M1(MOrder#) L2(MOrder#)
DclFmtCycleAttr Detail *In42 M1(DOrder#)
DclFmtCycleAttr Orders *In43 M1(DOrder#) L1(DOrder#, DItem#)
There can be more than one field declared as an
Ln
keyword. Only one field can be declared in theMn
keyword.
Cycle Output
Specifications
During Migration, if there are legacy RPG Output Specifications of type 'H'
, 'D'
, 'T'
, a section of the intermediate AVR will be generated with a syntax as follows:
BegCycleOutput
DetailDiskSpec file Cond(ind-expr) Op(file-operation) Flds(list)
TotalDiskSpec file Cond(ind-expr) Op(file-operation) Flds(list)
HeadingPrintSpec format Cond(ind-expr) FetchOverflow(yes/no)
DetailPrintSpec format Cond(ind-expr) FetchOverflow(yes/no)
TotalPrintSpec format Cond(ind-expr) FetchOverflow(yes/no)
EndCycleOutput
Notes:
- Cycle Output specifications are
Disk
(Database) related orPrint
(Printfile) related. - Disk Output Specs operations are
*ADD
,*UPDATE
, or*DELETE
.
For example:
BegCycleOutput
HeadingPrintSpec QPRINT_DETH1P Cond(*InOF *Or *In1P )
HeadingPrintSpec QPRINT_DETH1L1F Cond(*InL1 ) FetchOverflow(*Yes)
DetailPrintSpec QPRINT_DET01 Cond(*In01 )
TotalPrintSpec QPRINT_TOTAL2LRF Cond(*InLR ) FetchOverflow(*Yes)
EndCycleOutput
Where QPRINT_DETH1P, QPRINT_DETH1L1F, QPRINT_DET01 and QPRINT_TOTAL2LRF are record formats. Note that RPG unnamed record formats are named at migration time by the RPG Agent using the name of the file, the record type, and the indicator conditions (to make a unique name used by the Printfile Agent).
The C# conversion will translate each of these into explicit disk or printfile operations with its proper Conditions and used Fields into the body of the methods discussed above (_DetailOutput
and _TotalOutput
). Detail and Heading specs end up in _DetailOutput
, while Total specs end up in _TotalOutput
.
void _DetailOutput()
{
if ((bool)_INOF || (bool)_IN1P)
QPRINT.Write("QPRINT_DETH1P", _IN.Array);
if ((bool)_INL1)
{
_FetchOverflow(QPRINT);
QPRINT.Write("QPRINT_DETH1L1F", _IN.Array);
}
if ((bool)_IN01)
QPRINT.Write("QPRINT_DET01", _IN.Array);
}
void _TotalOutput()
{
if ((bool)_INLR)
{
_FetchOverflow(QPRINT);
QPRINT.Write("QPRINT_TOTAL2LRF", _IN.Array);
}
}
Database File Designations
Certain Database files participate in the Cycle logic. Files with Designation Primary
and Secondary
, participate in the program Cycle
.
In the C# conversion, the file designation is indicated by the isPrimary
parameter in the DatabaseCycleFile constructor ( true means Primary, false means Secondary ).
First Page
Before the first record is read the first time through the loop, the program resolves any parameters passed to it, writes the records conditioned by the _IN1P
(first page) indicator, and processes any heading or detail output operations having no conditioning indicators.
For example, heading lines printed before the first record is read might consist of constant or page heading information or fields for reserved words, such as PAGE
and UDATE
.
The first page indicator is turned on in the class constructor and turned off after the first page headings (if any) are printed.
Matching Records
To process matching records in QSys
programs, the fields defined as the matching record fields are assigned using M1…M9 on the DclFmtCycleAttr
record for each file format on which the matching is to occur. For instance, if you have an order master file, an order detail file, and a back-order file matched by their respective order numbers you would specify the order number as the matching field in each file.
Level Breaks
To define level breaks, the control fields are assigned using L1…L9 on the DclFmtCycleAttr
record for each file format on which the level breaks are to occur. For instance, you have an order master file (one record per order), an order detail file (one record per item in the order), and a backorder file (multiple records per order and item). Specify the order number as the level break field in each file and include the item level on the backorder file.
Last Record
During the last time a program goes through the loop, when no more records are available, the _INLR
(last record) indicator and all level indicators (_L1
through _L9
) are set to ‘1’. Any total calculation and total output are completed and then control is returned to the caller.
Overflow
The fetch overflow routine allows you to alter the overflow logic to prevent printing over the perforation and to let you use as much of the page as possible. Fetch overflow in legacy is specified with an F
in position 16 of the output specifications on any detail, total, or exception lines for a PRINTER file.
Fetching for Overflow means that before printing the record format, the Overflow
condition needs to be checked and if overflow printing would occur, the overflow indicator is set to true. When the indicator is set, all lines conditioned on overflow will be printed first.
Look-ahead Fields
Lookahead fields are not supported but code can be added to the migrated _DetailCalc
section to add the look ahead functionality.