Wednesday, October 14, 2015

A better way of sending break messages

qezsndmg api better than sndbrkmsg and sndmsg

I have never liked the Send Break Message command, SNDBRKMSG, as it is not possible to send the message to a user without knowing which work station they are at. There is an alternative: the Send Message API, QEZSNDMG. This API allows me to send a message to directly to the user, without caring where they are. It is the only API I can think of that has an user interface that can be used interactively, and the same API can be used in a program to run without the user interface.

Below I am going to describe how this can be used with the user interface, and then how to call the API in a program.


User interface

All I have to do to use the user interface is just a program call:

  CALL QEZSNDMG

Below is the screen that is displayed:

                                 Send a Message

 Type information below, then press F10 to send.

   Message needs reply  . . . . . .   N            Y=Yes, N=No

   Interrupt user . . . . . . . . .   N            Y=Yes, N=No

   Message text . . . . . . . . . .   __________________________________
________________________________________________________________________
________________________________________________________________________
________________________________________________________________________
________________________________________________________________________
________________________________________________________________________
____________________________________________________

   Send to  . . . . . . . . . . . .  __________  Name, F4 for list
                                     __________
                                     __________
                                     __________
                                     __________
                                                                  More...
 F1=Help   F3=Exit   F10=Send   F12=Cancel

The parameters displayed are pretty much self evident.

Message needs reply  If this is 'Y' when I send the message the receiving user(s) can enter a reply to the message is returned to me.

Interrupt user  If 'Y' when the message is sent is will "break"/interrupt the user.

Message text  This is the message you want to send.

Send to  This is my favorite part of the API. I can list one of more user profiles who I wish to send the message to.

F10=Send  You need to press F10 to send the message. If you press Enter a message is displayed informing you: "Press F10 to send message".

When I get one of those dreaded call at night from the operator I use this to communicate with him. For example:

                                 Send a Message

 Type information below, then press F10 to send.

   Message needs reply  . . . . . .   Y            Y=Yes, N=No

   Interrupt user . . . . . . . . .   Y            Y=Yes, N=No

   Message text . . . . . . . . . .   The message has been handled. You 
can now continue________________________________________________________
________________________________________________________________________
________________________________________________________________________
________________________________________________________________________
________________________________________________________________________
____________________________________________________

   Send to  . . . . . . . . . . . .  OPERATOR1_  Name, F4 for list
                                     __________
                                     __________
                                     __________
                                     __________

If this message is sent to someone who is signed on I get the following message:

  Message sent to OPERATOR1.

If OPERATOR1 is not signed on then I get this instead:

  Message sent.  User OPERATOR1 not signed on.

OPERATOR1 receives the following message that interrupts them on all their active sessions:

From  . . . :   SIMON          DD/DD/DD   TT:TT:TT
   ===> OPERATOR1: The message has been handled. You can now continue
     Reply . . .   ____________________________________________________

They enter their reply press Enter. I receive their reply as a message.

From  . . . :   SIMON          DD/DD/DD   TT:TT:TT
   ===> OPERATOR1: The message has been handled. You can now continue
     Reply . . .   ok


Including in a program

I wish that QEZSNDMG was as simple to include in a program as it is to use interactively.

I cannot remember when I first wrote a program to use this API, it was many years and releases ago. I chose to write a "black box" type program, one were the user does not have to deal with all of the parameters, formatting, etc. They just pass a couple of parameters and the code in the program does the rest.

I chose to write it as a CL program as I found it easier than writing the equivalent in RPGIII.

01  PGM PARM(&USER &MSG)

02  DCL VAR(&MSGTYPE) TYPE(*CHAR) LEN(10) VALUE('*INFO')
03  DCL VAR(&DELIVERY) TYPE(*CHAR) LEN(10) VALUE('*BREAK')
04  DCL VAR(&MSG) TYPE(*CHAR) LEN(32)
05  DCL VAR(&MSGLENGTH) TYPE(*CHAR) LEN(4)
06  DCL VAR(&USER) TYPE(*CHAR) LEN(10)
07  DCL VAR(&NBRUSERS) TYPE(*CHAR) LEN(4)
08  DCL VAR(&MSGSENT) TYPE(*CHAR) LEN(4)
09  DCL VAR(&FUNCREQSTD) TYPE(*CHAR) LEN(4)
10  DCL VAR(&ERROR) TYPE(*CHAR) LEN(8)
11  DCL VAR(&DISPLAY) TYPE(*CHAR) LEN(1) VALUE('N')
12  DCL VAR(&MSGQ) TYPE(*CHAR) LEN(20)
13  DCL VAR(&NAMETYPE) TYPE(*CHAR) LEN(4) VALUE('*USR')
14  DCL VAR(&CCSID) TYPE(*CHAR) LEN(4)

15  CHGVAR VAR(%BIN(&MSGLENGTH)) VALUE(32)
16  CHGVAR VAR(%BIN(&NBRUSERS)) VALUE(1)
17  CHGVAR VAR(%BIN(&MSGSENT)) VALUE(0)
18  CHGVAR VAR(%BIN(&FUNCREQSTD)) VALUE(0)
19  CHGVAR VAR(%BIN(&CCSID)) VALUE(0)

20  CALL PGM(QEZSNDMG) PARM(&MSGTYPE +
                            &DELIVERY +
                            &MSG +
                            &MSGLENGTH +
                            &USER +
                            &NBRUSERS +
                            &MSGSENT +
                            &FUNCREQSTD +
                            &ERROR +
                            &DISPLAY +
                            &MSGQ +
                            &NAMETYPE +
                            &CCSID)

21 ENDPGM

The program just accepts two parameters, line 1, user profile and the message to be sent.

I define call the variables on lines 2 – 14. If there is a default value that I am going to use I use the VALUE parameter to set it, rather than have a CHGVAR later in the program. Some of the variables need to contain a binary value, but I cannot define variables as binary data type in CL. I need to define them as character variables and then use the %BIN build in function to move the binary value to the character variable, lines 15 – 19.

Notice that the variable &DISPLAY, line 11, is set to 'N'. If it is 'Y' then the API's user interface is diplayed when I call it.

This program is simple as I am only accepting one user profile to send the message to and only allowing the message to be 32 characters. If I want to have the ability to send to more than one profile I would have to increase the size of the &USER variable to be ((number of the users passed) * 10). And pass the number of user profiles sent. Below would be the modifications I could make.

01   PGM PARM(&USER &NBR@ &MSG)

02   DCL VAR(&NBR@) TYPE(*CHAR) LEN(3)
03B  DCL VAR(&NBR) TYPE(*DEC) LEN(3 0)
04   DCL VAR(&USER) TYPE(*CHAR) LEN(30)


05A  CHGVAR VAR(%BIN(&NBRUSERS)) VALUE(%DEC(&NBR@ 3 0))


05B  CHGVAR VAR(&NBR) VALUE(&NBR@)
06B  CHGVAR VAR(%BIN(&NBRUSERS)) VALUE(&NBR)
                                                       

Above contains the code for both IBM i 7.1 and 7.2. I would add line 5A if I was running 7.2. Lines 3B, 5B, and 6B would be added if I needed to do this in 7.1.

I can do the same in modern RPG:

01  ctl-opt main(Main) ;

02  dcl-pr Main extpgm('TESTRPG') ;
03    *n char(10) ;
04    *n char(32) ;
05  end-pr ;

06  dcl-pr QEZSNDMG extpgm  ;
07    *n char(10) const ;
08    *n char(10) const ;
09    *n char(32) const ;
10    *n int(10) const  ;
11    *n char(10) const ;
12    *n int(10) const ;
13    *n int(10) const ;
14    *n int(10) const ;
15    *n char(8) ;
16    *n char(1) const ;
17    *n char(20) const ;
18    *n char(4) const ;
19    *n int(10) const ;
20  end-pr ;

21  dcl-proc Main ;
22    dcl-pi *n ;
23      inUser char(10) ;
24      inMessage char(32) ;
25    end-pi ;

26    dcl-s ErrorCode char(8) ;

27    QEZSNDMG('*INFO':
               '*BREAK':
               inMessage:
               32:
               inUser:
               1:
               0:
               0:
               ErrorCode:
               'N':
               ' ':
               '*USR':
               0) ;

28    ErrorCode = ErrorCode ;
29  end-proc ;                      

The fixed format definitions can be found here.

In the RPG above I have "gotten off the RPG cycle" by using a Main procedure, line 1. If you are unfamiliar with this read Getting off the RPG cycle. I have define the procedure interface for the Main procedure, lines 2 – 5, with the same parameters as my CL program: user and message.

Lines 6 – 20 are all the parameters used by QEZSNDMSG. On line 6 I have used the EXTPGM with no program name so that the compiler knows to call the name that is given as the procedure name, QEZSNDMSG. Lines 7 – 19 are the parameters that will be passed to the API. They are all set to pass the values as constants, CONST, i.e. they cannot be changed by the called program. The only exception is on line 15, which is the error code, which passed back to the calling program. In my CL program I passed some of the paramteres as binary, in RPG I define, INT, and pass them as integer values.

Line 21 is the start of the Main procedure, which ends on line 28. The procedure interface, lines 22 – 25, define the parameters passed to this procedure. I need to define a variable to contain the error code, which I do on line 26.

The API is called on line 27. To make it easier to understand I always pass the values to the program directly. I think this is clearer than defining a variable move a value to it, and then use the variable in the "call". The exceptions are the parameters which do vary: user, message, and error code.


With what you have learned I am sure you will agree that is a very useful API to use for "break" messaging, and is a lot better than the SNDBRKMSG command.

 

You can learn more about QEZSNDMSG on the IBM web site here.

 

This article was written for IBM i 7.2, and should work for earlier releases too.


Definitions in fixed format

  D QEZSNDMG        PR                  extpgm('QEZSNDMG')
  D                               10    const
  D                               10    const
  D                               30    const
  D                               10I 0 const
  D                               10    const
  D                               10I 0 const
  D                               10I 0 const
  D                               10I 0 const
  D                                8
  D                                1    const
  D                               20    const
  D                                4    const
  D                               10I 0 const

  D inUser          S             10
  D inMessage       S             32
  D ErrorCode       S              8

  C     *entry        plist
  C                   parm                    inUser
  C                   parm                    inMessage
   /free

Return

3 comments:

  1. You can specify a 2, 4, or 8 byte binary number in CL by using the data type of *INT (8 byte is only allowed in CLLE). If you omit the length, the default is 4 bytes:

    DCL VAR(&MSGLENGTH) TYPE(*INT)
    CHGVAR VAR(&MSGLENGTH) VALUE(32)
    or
    DCL VAR(&MSGLENGTH) TYPE(*INT) VALUE(32)

    This was available at 6.1 (perhaps even 5.4 but I'm not sure).

    ReplyDelete
  2. It would be fairly easy to revise your CL (or RPG) program to be a Command Processing Program (CPP), and create a CL Command interface for this function. The current program input parameters (&USER and &MSG) could be required parms for the CL command, and if you wish, other optional CL command parms could be defined for other API parameters, such as MSGTYPE. The required user/user profile input could be defined as a list CL parm, with a minimum of 1 value required, but a maximum of a larger number if desired. The command analyzer then automatically passes the number of values specified by the user to the CPP - that is, the user need not specify a separate parameter value for the number of input USERs.

    https://www-01.ibm.com/support/knowledgecenter/ssw_ibm_i_71/rbam6/dflst.htm

    ReplyDelete

To prevent "comment spam" all comments are moderated.
Learn about this website's comments policy here.

Some people have reported that they cannot post a comment using certain computers and browsers. If this is you feel free to use the Contact Form to send me the comment and I will post it for you, please include the title of the post so I know which one to post the comment to.