ibmi-brunch-learn

Announcement

Collapse
No announcement yet.

Message subfile related problem

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Message subfile related problem

    I've read some message subfile examples - from main page and forum, but still have a question.

    All of these examples use program queue and (the important thing) clear program queue from *ALL or *OLD messages after displaying them via subfile. I've wrote test pgm to find out if joblog still contains these messages. And joblog is empty. This is bad for my purposes.

    I want to display the error message from QCMDEXC (which I call in app) in message subfile AND leave it in joblog.

    Is there way to do it (using SFLINZ, i.e. Single Output Operation) or I'm asking too much?

    Sorry for bad english.

  • #2
    Re: Message subfile related problem

    The messages that u r writing to message subfile will not goto Job log.
    The messages will just goto message subfile to display on the screen.
    Once u end the program, the messages will be stored no where.

    If ur requirement is such that u have to show messages using message subfile as well as those messages should goto Job log, after sending the message to message subfile send the same message to job log using Qp0zLprintf API.
    Then even though ur program is ended, the messages will be still there in Job log.

    Thanks,
    Giri
    Thanks,
    Giri

    Comment


    • #3
      Re: Message subfile related problem

      "u", "r" & "ur" are very funny


      The messages that u r writing to message subfile will not goto Job log.
      I'm not writing any messages to subfile. Read about SFLINZ keyword.

      The messages will just goto message subfile to display on the screen.
      The messages go to program queue. Read the examples from this site.
      And from program queue by the system (w/o my help) the messages are displayed in subfile. Read about SFLINZ keyword.

      Once u end the program, the messages will be stored no where.
      Once program ended the messages (if they wasn't cleared by "call 'QMHRMVPM' ... parm '*ALL' MSGRMV") will stay in joblog. IMHO.

      Qp0zLprintf API.
      tnx for hint anyway



      Any other comments on the problem?

      Sorry for bad English

      Comment


      • #4
        Re: Message subfile related problem

        Welcome Dimitry,


        Giri - Do you have a better example ?

        Thanks for the help


        Here is more detail


        Bob Cozzi Jr - (I think)

        How many times while in your RPG program have you wanted to record a piece
        of information to the job log? You basically had two choices: Call a CL
        program to post the data or call the QMHSNDPM API and set up and pass
        all those cryptic parameters. Consequently, many RPG developers will
        go into debug on a program simply to view the contents of a variable
        or two. Wouldn't it be great to be able to write the contents of
        virtually any field to the job log, easily, from within RPG IV?

        This article illustrates a rather uncommon
        (and apparently unknown) API that I've been using for
        years and have incorporated into the RPG xTools.
        The API is Qp0zLprintf (Print Formatted Job Log Data).
        Its purpose is to write a message in the job log.
        Qp0zLprintf is different from calling the message API
        or a CL program because it writes the text directly to
        the job log without the clutter of other methods. In fact,
        if you press F1 on the job log entry, no message ID is written.
        It does this by sending an impromptu message to the job log.

        There are only three rules for calling Qp0zLprintf.

        The name Qp0zLprintf is case-sensitive and, yes, there is a zero
        in the middle of the name. You may insert substitution variables
        at runtime to build the job log entry dynamically. In order for
        the entry to be written to the job log, you must terminate the
        text with a "linefeed" character. As I've mentioned in other
        articles on CGI programming, a linefeed character in RPG IV is
        represented by X'25', and, no, Qp0zLprintf has nothing to do
        with CGI programming.

        The API is really provided for non-RPG IV programmers who want
        to log information where iSeries and AS/400 users can find it.
        But it does make things a bit easier for RPG IV programmers as
        well. For example, the following writes the text string
        'Hello OS/400' out to the job log from within RPG IV,
        using the Qp0zLprintf API:
        Code:
             C                   Callp     Qp0zLprintf('Hello OS/400')
        You could use job log messages of this type to verify that milestones
        in the code were being performed in the correct sequence
        (or even at all). But what if you want to insert some varying
        data, such as a customer name or number, into a part of a static
        message? For example, suppose you want the customer number and
        name to be recorded to the job log, like this:

        CustNo: 3470 The Big Software Company

        You can use Qp0zLprintf to build this message dynamically at
        runtime by coding the following:
        Code:
        C                   callp     Qp0zLprintf('CustNo: %s %s': 
        C                                %char(CustNo):%Trimr(CustName))
        Note the %s values in the message text. These are substitution variable
        placeholders. These placeholders are part of the Qp0zLprintf API and are
        based on the C language's age-old printf function. Effectively, it means
        that each subsequent parameter (following the first parameter's text literal)
        is automatically inserted into the first parameter in order. So the first %s
        value is replaced with the content of Qp0zLprintf's second parameter, the
        second %s is replaced with the content of the third parameter, and so on.

        The %s value means the API is expecting a "string"--or more accurately,
        a C language-style null-terminated string for the associated parameter.
        There are other substitution symbols for numeric and single characters,
        but I find that just using %s is easier and more practical in RPG IV.
        Besides, with the %CHAR() built-in function, converting numeric to character
        is more than easy to do.

        So in our example, the CUSTNO field is converted from numeric to character
        using the %CHAR built-in function, and then it's inserted into the location
        of the first %s substitution symbol in your message text. The content of
        the CUSTNAME field is inserted in place of the second %s substitution
        symbol. Note that I remove trailing blanks from CUSTNAME just for good
        measure.

        Writing to the Job Log
        Of course, the example would not actually send anything to the job
        log because it was not terminated with a linefeed character.
        To make this work, you need to change the example to the following:
        Code:
        C                   callp     Qp0zLprintf('CustNo: %s %s' + X'25' : 
        C                                %char(CustNo):%Trimr(CustName))
        Note the addition of the X'25' character after the original message
        text. This causes the API to write the message to the job log.
        With the X'25', the data will just sit out there until either
        the X'25' is sent or the total length of all the text sent via
        Qp0zLprintf reaches 512 characters. At that point, the X'25' is
        forced.

        Qp0zLprintf Prototype
        Let's look at a prototype, as it appears in the IBM manuals,
        for the Qp0zLprintf API:
        Code:
        int Qp0zLprintf(char *format-string, ...);
        The API has a parameter named format-string, which is straightforward;
        it is a C language null-terminated string. But the second parameter
        is not so straightforward; it is an ellipse (...). An ellipse in C
        means that the parameter may be repeated as necessary. RPG IV does
        not have an equivalent of this capability, so we have to make due
        with what can be simulated in RPG IV.

        Qp0zLprintf Parameters
        The first parameter, as I mentioned, is a C null-terminated string.
        Passing a parameter of this data type is extremely easy in RPG IV.
        We simply specify the OPTIONS(*STRING) keyword on the prototype
        parameter, and the compiler does the conversion from character to
        null-terminated for us. So the first parameter would be defined
        as follows:
        Code:
        D  szOutputStg                    *   Value OPTIONS(*STRING)
        This is very similar to coding something like the following:
        Code:
        D  szOutputStg                  32A   Const
        The difference is, since the API expects a null-terminated string,
        you would have to call the procedure as follows to make it work:
        Code:
        C                   Callp     Qp0zLprintf('Hello OS/400'+X'00')
        Effectively, what you're doing is adding X'00' to the end of the text.
        This is not pretty. So instead, you code the parameter so that the
        compiler automatically converts your input value into a
        null-terminated string and then passes that temporary value to
        the API. The "length" of a null-terminated string is determined
        by the location of the X'00' in the string. There is no predefined
        length for null-terminated strings, but they are limited to a length
        of 64K minus 1. Using these variables is vaguely similar to using
        VARYING fields in RPG IV.

        The next requirement is to define the second through Nth parameters.
        It's up to you to decide how many of these parameters you would
        like to permit. Since you don't have to assign a name to parameter,
        you can define the parameter once and then copy the line of code
        as many times as you want.

        As I mentioned, the API allows you to insert character, numeric, or
        strings as substitution parameters. If you convert everything to
        strings, you only need one type of parameter, one that is similar
        to the first parameter itself, as follows:

        [/code]
        D * Value OPTIONS(*STRING:*NOPASS)
        [code]
        In this parameter, the OPTIONS(*STRING) keyword is used, but it
        also includes a second value, *NOPASS. This means the parameter
        is optional. This is the key to prototyping an API that has a
        variable number of parameters via the ellipse; you specify
        OPTIONS(*NOPASS) in addition to everything else you specify.

        So the prototype for Qp0zLprintf ends up looking like this:
        Code:
        D Qp0zLprintf     PR            10I 0 ExtProc('Qp0zLprintf')
        D  szOutputStg                    *   Value OPTIONS(*STRING)
        D                                 *   Value OPTIONS(*STRING:*NOPASS)
        Of course, just one substitution variable isn't all that compelling.
        You want more. So how do you specify more than one substitution
        parameter? By simply copying the second parameter over and over
        again until you have enough to handle as many substitution values
        as you believe you'll need. Remember, these parameters are optional,
        so when you call the API, you only specify the ones you need. Here's
        an example of the Qp0zLprintf prototype with 10 substitution variable
        parameters:
        Code:
        D Qp0zLprintf     PR            10I 0 ExtProc('Qp0zLprintf')
        D  szOutputStg                    *   Value OPTIONS(*STRING)
        D                                 *   Value OPTIONS(*STRING:*NOPASS)
        D                                 *   Value OPTIONS(*STRING:*NOPASS)
        D                                 *   Value OPTIONS(*STRING:*NOPASS)
        D                                 *   Value OPTIONS(*STRING:*NOPASS)
        D                                 *   Value OPTIONS(*STRING:*NOPASS)
        D                                 *   Value OPTIONS(*STRING:*NOPASS)
        D                                 *   Value OPTIONS(*STRING:*NOPASS)
        D                                 *   Value OPTIONS(*STRING:*NOPASS)
        D                                 *   Value OPTIONS(*STRING:*NOPASS)
        D                                 *   Value OPTIONS(*STRING:*NOPASS)
        Now, when you call Qp0zLprintf, you can specify one parameter or more
        than one parameter. If you specify one parameter, you imply that
        you're not doing any substitutions; if you specify more than one
        parameter, you imply that you are doing substitutions.

        Now, you can call Qp0zLprintf as follows:
        Code:
        C                   callp     Qp0zLprintf('CustNo: %s %s' + X'25' : 
        C                                %char(CustNo):%Trimr(CustName))
        And sitting out in the job log will be something similar to the following:

        CustNo: 3470 The Big Software Company

        Qp0zLprintf is great for logging important debug information to the job
        log, but if performance is an issue, Qp0zLprintf is not a great choice
        because the overhead is a bit more than you might expect. I recommend
        using Qp0zLprintf only to log things you need to know of during debug
        mode. Or you could create a "switch" in your code that you can turn
        on externally. Then, if that switch is on, call the Qp0zLprintf;
        otherwise, bypass it.

        Finally, if you're a fan of TRACE, there is an equivalent API to
        write out to the user trace area. That API is Qp0zUprintf.
        The only difference is the "U" in the name instead of the "L."
        The prototype for Qp0zUprintf is as follows:
        Code:
        D Qp0zUprintf     PR            10I 0 ExtProc('Qp0zUprintf')
        D  szOutputStg                    *   Value OPTIONS(*STRING)
        D                                 *   Value OPTIONS(*STRING:*NOPASS)
        D                                 *   Value OPTIONS(*STRING:*NOPASS)
        D                                 *   Value OPTIONS(*STRING:*NOPASS)
        D                                 *   Value OPTIONS(*STRING:*NOPASS)
        D                                 *   Value OPTIONS(*STRING:*NOPASS)
        D                                 *   Value OPTIONS(*STRING:*NOPASS)
        D                                 *   Value OPTIONS(*STRING:*NOPASS)
        D                                 *   Value OPTIONS(*STRING:*NOPASS)
        D                                 *   Value OPTIONS(*STRING:*NOPASS)
        D                                 *   Value OPTIONS(*STRING:*NOPASS)
        To view the data in the user trace space, use the DSPUSRTRC CL command.
        All my answers were extracted from the "Big Dummy's Guide to the As400"
        and I take no responsibility for any of them.

        www.code400.com

        Comment


        • #5
          Re: Message subfile related problem

          @jamief
          workflow of app
          1)
          Code:
          call QCMDEXC
          which leave error message in program queue
          2)
          Code:
          write MSGSFLCR
          (with SFLINZ in DDS and linked with program queue) automatically displays error message from QCMDEXC and any other messages on program queue in subfile.


          3) a) if I'll remove this message from program queue it won't appear in joblog - bad

          3) b) if I won't remove this message from program queue all subsequent
          Code:
          write MSGSFLCR
          will display this message over and over. - also bad


          To duplicate messages in joblog manually is an option. but it won't make me very happy.

          thnx anyway

          Comment


          • #6
            Re: Message subfile related problem

            no problem ---welcome again to code400 - let us know what u figure out

            Comment


            • #7
              Re: Message subfile related problem

              I'm back.

              what if I'll mark the message as '*OLD'?
              Would it appear in the message subfile filled by Single Output Operation (SFLINZ)?

              Does anyone know exact answer?

              PS avatara is something strange, but I like it

              Comment


              • #8
                Re: Message subfile related problem

                Glad to see you back....

                Okay you can specify the message type when you use API QMHSNDPM to send them.

                Types are
                • *COMP Completion
                • *DIAG Diagnostic
                • *ESCAPE Escape
                • *INFO Informational
                • *INQ Inquiry.You can send inquiry messages only to the external message queue.
                • *NOTIFY Notify
                • *RQS Request
                • *STATUS Status



                With the API QMHRCVPM you can recieve messages by type and also control
                what to do with them.

                Code:
                Message action 
                INPUT; CHAR(10) 
                The action to take after the message is received. Valid values follow:
                
                *OLD Keep the message in the message queue and mark it as an old message. 
                You can receive the message again only by using the message key or by 
                specifying the message type *NEXT, *PRV (previous), *FIRST, or *LAST. 
                
                
                *REMOVE Remove the message from the message queue. This instance of the 
                message is no longer available for you to work with. The message key is no
                longer valid; therefore, you cannot receive the message again. 
                
                *SAME Keep the message in the message queue without changing its new or old 
                designation. *SAME lets you receive the message again later without using the message key. 
                
                
                For Integrated Language Environment (ILE) programs, the message action also
                 indicates if an exception message should be handled. If the message action is 
                *OLD or *REMOVE, an exception is handled. If *SAME is specified, the exception is
                 not handled.


                so from the example on the site change MSGRMV to *OLD instead of *ALL

                Code:
                      *
                      * Initialize the message subfile fields
                      *
                     c                   Movel     'LBIMSGF'     MSGF             20
                     c                   Movel     '*LIBL'       MSGLIB           10
                     c                   Move      MSGLIB        MSGF
                     c                   Move      *blanks       MSGKY            04
                     c                   Move      *blanks       MSGDTA           80
                     c                   Movel     '*DIAG'       MSGTYP           10
                     c                   Movel     '*ALL'        MSGRMV           10
                     c                   Movel     *blanks       MSGID            07
                This should work....
                Can you give it a try and let us know?

                Thank you
                Jamie
                All my answers were extracted from the "Big Dummy's Guide to the As400"
                and I take no responsibility for any of them.

                www.code400.com

                Comment

                Working...
                X