Displaying field-dependent errors in the message line: Difference between revisions

From Try-AS/400
Jump to navigation Jump to search
(→‎Subfile Next Changed: Better formatting)
Line 100: Line 100:


===== Subfile Next Changed =====
===== Subfile Next Changed =====
An important extension to ''SFLMSG(ID)'' is [https://www.ibm.com/support/knowledgecenter/ssw_ibm_i_72/rzakc/rzakcmstdfsfnch.htm SFLNXTCHG]. Imagine the following:<br />
An important extension to ''SFLMSG(ID)'' is [https://www.ibm.com/support/knowledgecenter/ssw_ibm_i_72/rzakc/rzakcmstdfsfnch.htm SFLNXTCHG]. Imagine the following:
An user fills out an SFL form and sends the data by pressing <code>Enter</code>. Your SFL Routine wades through all '''changed''' SFL records, detects an error somewhere, sets <code>*IN91</code> as stated [[#Subfiles|above]] and writes back to the screen. Now, your user doesn't change anything and just again presses <code>Enter</code>. What happens?<br />
* An user fills out an SFL form and sends the data by pressing <code>Enter</code>.
Your SFL Routine again wades through all '''changed''' SFL records, skips the erroneous record '''because the user has not changed it'''. In the end, the record on disk will stay the same as the user didn't get a second error message and thinks all went well. This undiscovered loss of data usually is not acceptable.
* Your SFL Routine wades through all '''changed''' SFL records, detects an error somewhere, sets <code>*IN91</code> as stated [[#Subfiles|above]] and writes back to the screen.
* Your user doesn't change anything and just again presses <code>Enter</code>.
* Your SFL Routine again wades through all '''changed''' SFL records, skips the erroneous record '''because the user has not changed it'''.


Here ''SFLNXTCHG'' comes into play. If active, '''the complete record''' is marked as changed.<ref>For that reason, ''SFLNXTCHG'' is always switched on as needed by your program via an Indicator</ref> Example excerpt:
In the end, the record on disk will stay the same as the user didn't get a second error message and thinks all went well. This undiscovered loss of data usually is not acceptable. Here ''SFLNXTCHG'' comes into play. If active, '''the complete record''' is marked as changed.<ref>For that reason, ''SFLNXTCHG'' is always switched on as needed by your program via an Indicator</ref> Example excerpt:


== Footnotes ==
== Footnotes ==

Revision as of 16:36, 18 March 2019

It is always considered good habit to let a program's users know what's going on. Or what failed, and why. OS/400 provides components to relatively easily display field-dependent errors in the message line (at the bottom of the screens). Surely you already have seen such messages by other OS/400 components.

Probably you didn't notice that this message line is often a subfile. With one line, though, but you can place the cursor there and scroll through it's content with the appropriate Page-Up and Page-Down-Keys. If you ever wondered how to see other messages from one program invocation without bringing up the job log, now you know.

There are multiple ways to provide this functionality. I'm showing the way I considered the best for me. That's my choice and doesn't have to match yours. At least it should provide a starting point for your own experimentation.

We use the following ingredients:

  • Message Files,
  • Display Files (certain statements within, that is),
  • ILE RPG Code.

For the following examples, we assume an already existing application which should not show the standard error screen when an already locked record is encountered. Instead, it should show a message in the message line and skip the record. Your current library is assumed to be the library of that mentioned application.

Message Files

Message Files are special database files. Before elaborating, let's create a message file:

CRTMSGF MSGF(GENERICMSG) CCSID(*JOB)

Now, we populate this file with a message:

ADDMSGD MSGID(ERR1218) MSGF(*CURLIB/GENERICMSG) MSG('Record &1 is already in use.') SECLVL('Record &1 has been locked by another user for update. Wait until the lock will be released or use WRKOBJLCK OBJ(*CURLIB/MYPF) OBJTYPE(*FILE)  MBR(*FIRST) to find out who is locking. So you can contact the user in question to close that record.') SEV(10) FMT((*CHAR 10 0)) TYPE(*NONE) LEN(*NONE)

Noteworthy stuff:

  • The &1 in the message text is a variable which we'll use later to customize this message a bit before showing it to the user.
  • The MSG is to be shown in the message line. It should give a concise description of what's going on. The SECLVL text will be shown when the user positions the cursor in the message line and presses the HELP- or F1-Key. It should provide information what to do next to probably solve the issue.

There's a system wide message file QSYS/QCPFMSG, with proper localized texts. Problem with this file is that it contains hundreds of standard messages, so it is cumbersome to find the right one.

To change and view content of a message file, you can use:

WRKMSGD MSGF(GENERICMSG)
Conclusion
Message files enable the to have a proper place for probably longer text, because handling string literals in RPG is really cumbersome. Plus, with a message file, your application can be more easily localized for other languages without wading through all the RPG program code.

Display Files

Display Files define the screen appearance to the user and provide an interface in program code to get data in and out of this screen. The message line is usually part of that screen, and thus of a message file.

We could handle the entire message subfile ourselves. But we also could let OS/400 do that for us. This is accomplished by adding the ERRSFL Keyword in the global (top) section of any DSPF DDS file. I strongly recommend to include this statement per default.

The next steps are dependent on your Record Format types. That is, we need to differ between ordinary Record Formats with just one page, and Subfile Record Format, which always come in twos: The SFL record to describe one block[1] of data, and the SFLCTL which actually controls the behavior of the SFL itself. See List views: Subfiles for details.

For clarity, ordinary record formats will be called Details Record Formats[2] and Subfile Record Formats will be called Subfiles, unless we're referencing the SFL or SFLCTL itself.

To understand the next sections it is important that you understand that the shown DDS keywords always tie messages to a certain field.

Automatic Error Handling


A somewhat automatic error handling can be established with the CHKMSGID keyword to a field. It must be used together with some predefined check provided by DDS statements, such as a VALUES option for a field which lists valid data to be entered into the field.

Often, this is used in a SFL for the OPT-Field, where the user enters (usually) decimal numbers to tell the system what to do with the chosen record. With this facility you can offer the user a better understandable error message then the standard text from OS/400. Example:

     A            OPT            1A  I  9  3VALUES(' ' '2' '4' '5' '+' '-')
     A                                      CHKMSGID(SFL0001 GENERICMSG)

Again, noteworthy stuff:

  • SFL0001 is an example ID. There's no definition for it in the above example message file!
  • If the VALUES-Check of the corresponding field fails, the corresponding field will be automatically set to DSPATR(RI) (Reverse Image) to show the user where the error occurred.[3]

CHKMSGID is not limited to subfiles, and not limited to provide error messages for a range of valid entries!

Programmatic Error Handling


In contrast to the above, the next section applies when your program has to cope with other error conditions, for which OS/400 doesn't provide a proper handling procedure.

Details Record Format

This is a relatively easy one. See the following example minimal DDS excerpt:

     A                                      ERRSFL
     A            TYP       R        B  8 12
     A  91                                  ERRMSGID(ERR1218 GENERICMSG 91 &ERRSTR)
     A            ERRSTR        16A  P

Again, noteworthy stuff:

  • TYP is an ordinary field, referenced from a file not shown or otherwise referenced here for minimality.
  • The ERRMSG(ID) statement is conditioned with *IN91. When this indicator is set to true and the Record Format is written to, then
    • The referenced message is pulled from the message file and displayed in the message line.
    • The field will be automatically set to DSPATR(RI) (Reverse Image) to show the user where the error occurred.
    • The given *IN91 (third parameter) will be reset after the Record Format is read, to automatically clear the error condition when the user submits the screen for processing.
  • ERRSTR is a (P)rogram defined field. This equals to a global variable in ILE RPG though the usual file reference. You may write a program provided string into this variable which is then shown at the corresponding position (&1) in the message file text. (See above.)

Subfiles

Handling Subfiles is always a lot more work than just coping with a simple Details Record Format. Error output is no exception in the best tradition of Subfiles.

In contrast with CHECKMSGID above, programmatic message output is done in the SFLCTL Record Format. This yields to an exception to the rule stated above: Here, the output is not tied to a field. Example excerpt:

     A                                      ERRSFL
     A          R MYCTL                     SFLCTL(MYSFL)
     A                                      […]
     A  91                                  SFLMSGID(ERR1218 GENERICMSG 91 &ERRSTR)
     A            ERRSTR        16A  P

Again, noteworthy stuff:

  • The SFLMSG(ID) statement is specified in the global section of the SFLCTL, before any fields are defined, and conditioned with *IN91. When this indicator is set to true and the Record Format is written to, then
    • The referenced message is pulled from the message file and displayed in the message line.
    • The given *IN91 (third parameter) will be reset after the Record Format is read, to automatically clear the error condition when the user submits the screen for processing.
  • ERRSTR is a (P)rogram defined field. This equals to a global variable in ILE RPG though the usual file reference. You may write a program provided string into this variable which is then shown at the corresponding position (&1) in the message file text. (See above.)

Per default, there is no visible indication where the error happened. This tying to a specific field must be done by the programmer. Fortunately, this is really easy:

     A            OPT            1A  I  9  3VALUES(' ' '2' '4' '5' '+' '-')
     A                                      CHKMSGID(SFL0001 GENERICMSG)
     A  91                                  DSPATR(RI)                     

The first two lines are the same as stated above. The third line sets OPT automatically to DSPATR(RI) (Reverse Image) when *IN91 is set on to indicate an error, which is utilized in the SFLCTL anyway. So we can show the user where the error occurred in the record.
Since a Subfile is a way to show many likewise records on one screen, every entry of a subfile has it's own set or unset indicators. Since *IN91 is set on only for that particular row, the user can see where the error occurred. In which record, to be precise.

Subfile Next Changed

An important extension to SFLMSG(ID) is SFLNXTCHG. Imagine the following:

  • An user fills out an SFL form and sends the data by pressing Enter.
  • Your SFL Routine wades through all changed SFL records, detects an error somewhere, sets *IN91 as stated above and writes back to the screen.
  • Your user doesn't change anything and just again presses Enter.
  • Your SFL Routine again wades through all changed SFL records, skips the erroneous record because the user has not changed it.

In the end, the record on disk will stay the same as the user didn't get a second error message and thinks all went well. This undiscovered loss of data usually is not acceptable. Here SFLNXTCHG comes into play. If active, the complete record is marked as changed.[4] Example excerpt:

Footnotes

  1. I deliberately don't state one line, since one record in a subfile display may actually span multiple lines in a display file. This is not limited to an implicit wrap around at the right end of the screen.
  2. Since they usually contain more fields to display than a subfile.
  3. Now you probably understand why the ERRMSG(ID) is always tied to a field.
  4. For that reason, SFLNXTCHG is always switched on as needed by your program via an Indicator

Weblinks