Send inquiry messages and receive a reply! Use the power of messages for error-handling, user feedback, or user interaction.
In previous articles, we have seen how to send a variety of messages to the user of an application. The message types covered include *ESCAPEs in the case of hard errors ("Inform Users of Problems by Sending Error Messages from Application Programs"), *DIAGnostics in order to provide additional information on error conditions ("More on Sending Messages from an Application Program"), *COMPletion messages to indicate success in our task ("Sending Non-Error-Related Messages from an Application Program"), and *STATUS messages to keep the user informed about the current running of the application (also found in "Sending Non-Error-Related Messages from an Application Program"). Today, we'll look at sending *INQUIRY messages, which can be used for either success or failure, and receiving a response to the inquiry message.
We'll start by adding message description APP0005 to the QGPL/APPLMSGF message file used in the earlier articles.
ADDMSGD MSGID(APP0005) MSGF(QGPL/APPLMSGF) +
MSG('Pick a number: 0 - 999, P') +
SECLVL('Enter a value from 0 to 999, or P for the program to pick.') +
TYPE(*DEC) LEN(3 0) SPCVAL(('p' -1) (P -1)) RANGE(0 999) DFT(1)
The APP0005 message prompts the user to enter either a number from 0 to 999 or the special value P. The type of reply, if not a special value, must be a decimal number with a maximum of three digits. Any decimal positions specified by the user will not be returned in the reply. If the user enters either a lowercase or uppercase letter P, then a reply value of -1 is to be returned to the program. If the user uses Enter without providing any reply, then a default reply value of 1 is to be used.
The following program prompts the user with message APP0005, receives the reply entered, calculates a reply value if the user replied with a p or P value, and then displays the reply value.
dSndMsg pr extpgm('QSYS/QMHSNDPM')
d MsgID 7 const
d QualMsgF 20 const
d MsgDta 65535 const options(*varsize)
d LenMsgDta 10i 0 const
d MsgType 10 const
d CallStackEntry 65535 const options(*varsize)
d CallStackCntr 10i 0 const
d MsgKey 4
d QUSEC likeds(QUSEC)
d LenCSE 10i 0 const options(*nopass)
d CSEQual 20 const options(*nopass)
d DSPWaitTime 10i 0 const options(*nopass)
d CSEType 10 const options(*nopass)
d CCSID 10i 0 const options(*nopass)
dRcvMsg pr extpgm('QSYS/QMHRCVPM')
d MsgDta 1 options(*varsize)
d LenMsgDta 10i 0 const
d Format 8 const
d CallStackEntry 65535 const options(*varsize)
d CallStackCntr 10i 0 const
d MsgType 10 const
d MsgKey 4 const
d RpyWaitTime 10i 0 const
d MsgAcn 10 const
d QUSEC likeds(QUSEC)
d LenCSE 10i 0 const options(*nopass)
d CSEQual 20 const options(*nopass)
d CSEType 10 const options(*nopass)
d CCSID 10i 0 const options(*nopass)
d AlwDftRpyRej 10 const options(*nopass)
/copy qsysinc/qrpglesrc,qmhrcvpm
/copy qsysinc/qrpglesrc,qusec
dMsgDta ds qualified
d Common likeds(QMHM010001)
d Nbr 3
dMsgFName ds
d Name 10 inz('APPLMSGF')
d Lib 10 inz('QGPL')
dResponse s 10i 0
dMsgKey s 4
/free
QUSBPRV = 0;
SndMsg( 'APP0005' :MsgFName :' ' :0 :'*INQ'
:'*EXT' :0 :MsgKey :QUSEC);
RcvMsg( MsgDta :%size(MsgDta) :'RCVM0100' :'*' :0
:'*RPY' :MsgKey :-1 :'*OLD' :QUSEC);
if MsgDta.Common.QMHDRTN00 < MsgDta.Common.QMHDAVL00;
dsply 'Reply value out of range';
else;
Response = %int(%subst(MsgDta.Nbr :1 :MsgDta.Common.QMHDRTN00));
if Response = -1;
Response = (%subdt(%timestamp() :*MS) / 1000);
endif;
dsply Response;
endif;
*inlr = *on;
return;
/end-free
Sending the Inquiry Message
When initially sending message APP0005 to the user, the program uses the Send Program Message (QMHSNDPM) API that we have previously used for sending escapes, diagnostics, completion, and status messages. When sending the message, the message type is set to *INQ for inquiry and the destination for the message to *EXT for the external message queue.
Receiving the Response to the Inquiry Message
After sending the message, the program calls the Receive Program Message (QMHRCVPM) API documented here. The first parameter, MsgDta, is a receiver variable where the API will return information related to the message being received. As with most receive type APIs, the next two parameters further define the receiver variable. The second parameter specifies the allocated size of the receiver variable MsgDta; the third parameter specifies the format of the information to be returned in MsgDta. The QMHSNDPM API supports several formats, with the program requesting format RCVM0100.
Format RCVM0100 contains both fixed-length and variable-length fields. The fixed-length fields are defined in member QMHRCVPM of QSYSINC/QRPGLESRC and are copied into the program using the /copy compiler directive. The receiver variable MsgDta is defined as a qualified data structure using the base definition of format RCVM0100, named QMHM010001 within the QSYSINC copy member, followed by a character field, Nbr, of length 3.
It is worth pointing out that while the APP0005 message description calls for TYPE(*DEC), this only defines the type of value the user can enter when replying to the message. It does not define the data type to be used in returning the reply value to the program. Inquiry message replies are always returned in character form. That is, a user reply value of 1 is returned to the program as the character value 1, not a packed decimal (3 0) value of 1 (x'001F').
The fourth parameter when calling QMHSNDPM specifies where the message reply will be found. While it may seem reasonable for this parameter to be set to the *EXT special value used when sending the inquiry message, this is not the correct value for receiving the reply. When an inquiry message is sent, the system creates a sender copy message that is logically tied to the original inquiry message. This sender copy message is where you will find the reply and is accessed using the special value of an asterisk (*) in addition to the value of the seventh parameter passed when calling the QMHRCVPM API, which will be discussed shortly.
The fifth and sixth parameters do not require much explanation. The value of 0 for Call stack counter indicates that the message to be received is associated with the call stack identified by the fourth parameter (which is the special value *), and the Message type value of '*RPY' indicates that we want to receive a reply message.
The seventh parameter is used to identify the specific message to be received. In previous articles demonstrating how to send messages using QMHSNDPM, we provided the MsgKey parameter, which the API dutifully returned, and which we then never used. It's now time to find out what this parameter is for! When sending a message, the system assigns a message key for all message types except *STATUS. This message key uniquely identifies the message within the context of the sender of the message. By specifying the MsgKey value returned by QMHSNDPM when we call QMHRCVPM, we are explicitly asking for the reply to that specific instance of the inquiry message.
For information on the remaining parameters, refer to the Information Center documentation for the QMHRCVPM API.
Processing the User Response to the Inquiry Message
Upon successful completion of the QMHRCVPM API call, the MsgDta data structure now contains information related to the reply message. Though not strictly necessary, the program, prior to processing the reply value, now performs a sanity check on the returned value. Just in case "someone" changed the APP0005 message description to allow a larger range of values (for instance, LEN(5 0) and RANGE(0 99999)) and forgot to update the program, the program checks to see if the length of the returned reply data (QMHDRTN00) is less than the length of all available reply data (QMHDAVL00). This would be the case if the user typed in a reply value of 9999—which will not fit into the three bytes we allocated for MsgDta.Nbr— as there would be four bytes available but only the first three bytes returned. If this situation is detected, then the message "Reply value out of range" is displayed using the DSPLY operation code.
As an exercise, you might change the program from using DSPLY to sending an escape message to the program's caller. If you need a refresher on sending escape messages, see the article "Inform Users of Problems by Sending Error Messages from Application Programs." Of course "someone" may have also changed the message definition in other ways, such as the TYPE from *DEC to *CHAR, and again forgotten to update the program (which would most likely result in a decimal data error when later processing the reply value). For this situation (along with for any API call failures as the program is using an API error code Bytes Provided value of 0), a global MONITOR group would be appropriate. In this article, I have elected to omit this additional error handling as the topic is sending and receiving inquiry messages, not how to handle errors (a topic that has been covered in previous articles, such as "What to Do with Messages in the Application Program").
Having done some rudimentary validation, the program converts the character value of MsgDta.Nbr (the reply the user provided) to the numeric variable Response. To do this, the program substrings the returned number of reply characters (MsgDta.Common.QMHDRTN00) within MsgDta.Nbr, converting this substring value to integer form. The substring operation is used as a reply value, such as 1 would only update the first byte of variable MsgDta.Nbr (which is defined as being three bytes in length), leaving the remaining two bytes unchanged from their initial value.
The program then checks to see if the value of Response is -1. This would be the case if the user replied to message APP0005 with the special value p or P. If so, the program then picks a number in the range of 0 to 999 by extracting the milliseconds component of the current system time. The actual process of picking a number is not of much interest; the intent here is to demonstrate how reply message special values might be supported within an application program.
Whether the program calculated a value, or the value was provided by the user, the program now displays the numeric value. Again, as an exercise, you might want to display this value using a predefined message of your own, utilizing replacement data for the Response value.
In this article, we have reviewed how to send inquiry messages, receive a reply to an inquiry message, and then process the reply. When combined with previous articles related to message-handling on the system, you should be in a good position to use the power of messages within your IBM i applications, whether it be for error-handling, user feedback, or user interaction.
If you have any API questions, send them to me at
LATEST COMMENTS
MC Press Online