Getting to the Tootsie Pop Center
Everyone looks for an easy way to solve a problem. If you can do something in fewer steps, so much the better. The ability to center text in RPG IV is a task programmers frequently perform and one that we constantly rewrite every time we need it, usually trying to improve on our technique. Heres a method for centering text that uses a minimal amount of statements and that you can include in a copybook or create as a service program for all your RPG IV centering needs. This algorithm has only four executable statements and a couple of declarative lines:
Declare an array with each element containing a single character and the number of elements equal to the size of the variable containing the text to be centered.
Trim the leading and trailing blanks.
Compute the starting position of the centered text as [(declared size of the variable- length of text with leading and trailing blanks trimmed) / 2] + 1.
Move the trimmed text from the variable into the array with the starting position computed above.
Move the centered array elements back to the original variable. The example code in Figure 1 shows how to center text using a minimal number of statements. By changing the length of the Text parameter, this technique can be easily modified to handle character strings of any size.
Mahendra Reddy
One Touch Does It All
Ive created a utility, RUNCLCMD, which takes a CLP source member, compiles it into library QTEMP, and executes that program from QTEMP, all in one step. This utility is useful for creating and running any of those one-time CL jobs we all create and use, and it also has these benefits:
You save time by not having to compile and execute in separate steps.
You dont clutter up libraries with compiled versions of temporary CL programs.
You can rest assured that you are always running the latest version of source.
You can easily submit a job from PDM Work with Members to compile and run your CL program in one step.
To create the utility, first compile the command and the command processing program for the command RUNCLCMD. Source for RUNCLCMD is shown in Figure 2; Figure 3 shows source for RUNCL. Note that this CL program uses the FWDPGMMSG utility described in How to Forward Messages in CL, MC, January 1998, and is available for downloading from the Web at www.midrange computing.com/ftp/prog/98/b980107.txt.
The best use Ive found for this command is as a PDM option. When I need to run a one-time CL program, I just enter the PDM option in front of that source member, and away it goes!
Richard Leitch
Cleanliness Is Next to Godliness
A scope message is a special type of message that can call a program when a job or stack entry ends, whether or not the end is normal. Similar ILE functions allow you to register procedures called when a job or stack entry ends, but scope messages are more flexible in that they can be used in OPM and ILE environments. Scope messages come in handy, for example, when you want to run a cleanup program or other system utility after a job ends abnormally.
The code for this tip can be downloaded at www.midrange computing.com/mc/99/05. This tip also requires that you create the Message Tool Kit utility described in RPG Building Blocks: ILE Message Handling, MC, November 1998. You can download the toolkit at www.midrangecomputing.com/ftp/prog/98/b981108.txt. The toolkit might look complex at first, but after you create the utility, using it and the scope messages technique is a piece of cake. Heres how to create the Test Send Scope Message (TSTSNDSCP) utility. You can use this program to see how scope messages work and then modify the code to meet your particular needs.
Module Creation
The scope message test program consists of one module containing a single main procedure. This module must be created prior to creating the scope message test program. The name of this main procedure is TstSndScp. Use the following commands to create the TstSndScp module:
CRTRPGMOD MODULE (objlib/TSTSNDSCP)
SRCFILE(srclib/QRPGLESRC) DBGVIEW(*SOURCE)
Specifying DBGVIEW(*SOURCE) allows you to step through the source during debugging.
Program Creation
The TstSndScp module contains the main procedure TstSndScp, which is used as the programs entry point procedure. The program is also bound to the MsgTkt service program. MsgTkt supplies the subprocedure SndAPIErr, which sends messages returned by the API QMHSndSM. Use the following commands to create the TstSndScp program:
CRTPGM PGM (objlib/TSTSNDSCP)
BNDSRVPGM (MSGTKT)
The default activation group of *NEW is used and creates a temporary activation group when TstSndScp is called.
Using the Scope Message Test Program
After creating the TstSndScp program and the MsgTkt service program, you can
call the TstSndScp program. To see it work, call it from a command line and use as a parameter any program name to call. The program you pass is called when your job ends. For testing purposes, you should pass an innocuous program that receives either one or no parameters. The table in Figure 4 describes the parameters passed to the TstSndScp program.
The following example demonstrates a call to the TstSndScp program made from a command line. In this example, TESTPGM receives a single-character parameter with the value PARMVAL and is called when the job ends:
CALL TSTSNDSCP (0 TESTPGM *LIBL PARMVAL)
This test program should be run in batch. If you test it interactively, you must sign off before the program in the scope message can run.
David Morris
What a Drag!
If you have Client Access/400 V2R3, CA/400 Operations Navigator (OpsNav) loaded on your PC, and OS/400 Version 4.2 or above loaded on your AS/400, theres a handy way to transfer a spool file from your AS/400 to your PC. Open OpsNav and double-click on the Basic Operations icon. Then, double-click on the Printer Output icon. The PC client queries your AS/400 and returns a list of AS/400 spooled files owned by your user profile. To move one or all of these spooled files to the PC, click on it and drag it to the desktop. You now have a text file (.txt) of the same name as the spool file on your PC. To print it, double-click on the document to open the word processor registered to handle *.txt file types and select Print from within. Thats all there is to it!
Shannon ODonnell
Associate Technical Editor Midrange Computing
Logging Document History in a Notes Document
Imagine you have a heavily used Notes database and want to track who is changing things within it. One way to do that is to create a field that logs information into the document itself. Should a question ever arise about how something was changed, you can go to that field and see who has done things to the document.
Accomplishing this is exceptionally easy. Open the document form that you want to track in your Notes database. Click the View/Action bar. That displays the list of button functions available to this document form in a window at the right of the screen. There is undoubtedly one that says Save. If you click on this function, the programmers window at the bottom of the screen has an @ function that says @Command([FileSave]) or @PostedCommand([FileSave]). This function saves the document when the user clicks the button. Change this function to read the following:
FIELD Process_History := Process_History +
Text(@Today) +
modified this document; @Command([FileSave])
This statement tells Notes to initiate a field called Process_History and concatenate into the new field whatever was previously in the Process_History field. It then says to add a new line to that field, followed by the textual representation of the date and name of the
current user. It also concatenates the string modified this document as a reference to the action that has taken place.
A Notes document database works by adding a new field to the database schema at any time even if that field is never actually displayed on the form itself. Each click of the Save button then automatically re-creates and concatenates the Process_History field, logging the name of the user and the date. The field Process_History accumulates this information every time the Save button is clicked. You can create other entries in the Process_History field to log other activities, such as clicking a Mail Send button. Each new log entry is added appropriately at the bottom of the Process_History field.
So how do you display the Process_History field? Well, the easiest way is to right- click the document in question and select Document Properties. Click the Fields tab and scroll down to the Process_History field. You will see there every entry that has been logged into the Process_History field.
On the other hand, you can actually create the field in the form itself so it is displayed every time a document is opened. If thats your requirement, I suggest making it a multiline layout region with a scroll bar so you can scroll through all the entries. If you dont want this layout region displayed all the time, you can incorporate it into a controlled access section or use an @ formula to hide the display of the region based on some other condition.
Thomas Stockwell
Editor in Chief Midrange Computing
Model 170 Setup Woes
A key to setting up a new AS/400e series Model 170 sent to you factory-direct is knowing how the 170 has been configured at the IBM plant for system console support. It seems that the documentation included with a new Model 170 does not include one crucial piece of information: how you are allowed to set up your system console. There are three ways to set up a new 170 system console, the dedicated terminal, or PC that functions as the system console, and each method is predicated on the type of cables shipped with the 170:
You can use the traditional method of a twinax cable (part no. 72X5645) connected to port 1 (C08) with a dumb terminal functioning as the system console.
You can use a PC running Client Access/400 to serve as the system console via a special cable (part no. 44H7504) connected to port 1 (C08).
You can use the new Operations Console, which includes the Graphical Control Panel, as your system console. This option requires two cables, part no. 97H7557 attached to Port 0 (C08) and part no. 97H77591 attached to port M1.
I was sent a 170 with the cables for using Client Access/400 to run the system console on a PC. Unfortunately, I wanted to try out the new Graphical Control Panel using Operations Console, so I reordered the appropriate cables. When I attached the new cables to the proper ports, nothing worked. After calling IBM, I discovered there is a setting in the Dedicated Service Tools (DST) Work with System Devices menu shown in Figure 5 that controls the type of system console allowed for your machine. When you choose option 6 (Console mode) from this menu, youre presented with the Select Console Type menu shown in Figure 6. In my case, the factory had preset this value at 1 (Local Console), so no matter what I did, the system would not recognize the new cables I installed. After changing this value to 2 (Operations Console), my Operations Console and Graphical Control Panel started with no problems. If you want to alter the shipped configuration of your system console, you must use the DST to change this value as well as order new cables. In any case, I think its a good idea to find out from your IBM representative how the factory has preset this value before receiving your new Model 170.
D. Ellis Green
Senior Technical Editor Midrange Computing
Looking for Addresses in All the Wrong Places
Q: Ive found that if I want to retrieve the Internet Protocol (IP) address of a display device in an RPG program, I can use the Retrieve Device Address (QDCRDEVD) API with format DEVD0600 for display devices. However, using this API to retrieve the IP address for a printer with format DEVD1100, which is a format for printer devices, does not seem to work.
Is there another way? All our printers are set up in the TCP/IP host table. Jeremy Ruth
A: While QDCRDEVD is enhanced via a PTF to return the IP address of display devices for OS/400 V4R1 and below, I do not believe the PTF includes support for printer devices. However, the ability to retrieve the IP address of printer devices using the QDCRDEVD API is now supported in V4R2 and above at decimal offset 1404 of format DEVD1100.
Bruce Vining
IBM Rochester
*================================================================
* To * Compile:
*
* CRTBNDRPG PGM(XXX/CenterTxt) SRCFILE(xxx/QRPGLESRC) +
* MBR(CenterTxt)
*
*================================================================
DArrDArr S 1 DIM(45)
DI S 2 0
DReply S 1a
DText S 45a Inz('My Very Special Data')
C Eval Text = %Trim(Text)
C Eval I = ((%Len(Text) - %Len(%Trim(Text))) / 2 )+1
C MoveA Text Arr(I)
C MoveA Arr Text
C Text Dsply Reply
C Eval *INLR = *ON
Figure 1: Heres a great way to center text with RPG IV!
/*==================================================================*/
/* To compile: */
/* */
/* CRTCMD CMD(XXX/RUNCLCMD) PGM(XXX/RUNCL) + */
/* SRCFILE(XXX/QCMDSRC) */
/* */
/*==================================================================*/
CMD PROMPT('Run CL (after temp. compile)')
PARM KWD(MBR) TYPE(*CHAR) LEN(10) MIN(1) +
PROMPT('Name of CLP source member')
PARM KWD(SRCF) TYPE(SRCF) PROMPT('Source file')
SRCF: QUAL TYPE(*NAME) DFT(QCLSRC) SPCVAL((*ALL))
QUAL TYPE(*NAME) DFT(*LIBL) SPCVAL((*LIBL)) +
PROMPT('Library'
Figure 2: You can use RUNCLCMD to create and run CL programs in QTEMP.
/*==================================================================*/
/* To compile: */
/* */
/* CRTCLPGM PGM(XXX/RUNCL) SRCFILE(XXX/QCLSRC) */
/* */
/*==================================================================*/
RUNCL: PGM PARM(&MBR &SRCF)
DCL VAR(&MBR) TYPE(*CHAR) LEN(10)
DCL VAR(&SRCF) TYPE(*CHAR) LEN(20)
DCL VAR(&SRCLIB) TYPE(*CHAR) LEN(10)
DCL VAR(&SRCNAME) TYPE(*CHAR) LEN(10)
DCL VAR(&SRCTYPE) TYPE(*CHAR) LEN(10)
MONMSG MSGID(CPF0000) EXEC(GOTO CMDLBL(FATAL))
CHGVAR VAR(&SRCNAME) VALUE(%SST(&SRCF 1 10))
CHGVAR VAR(&SRCLIB) VALUE(%SST(&SRCF 11 10))
RTVMBRD FILE(&SRCLIB/&SRCNAME) MBR(&MBR) +
SRCTYPE(&SRCTYPE)
IF COND(&SRCTYPE *NE CLP) THEN(DO)
SNDPGMMSG MSGID(CPF9898) MSGF(QCPFMSG) MSGDTA(&MBR +
*TCAT ' in ' *CAT &SRCLIB *TCAT '/' *CAT +
&SRCNAME *TCAT ' is not of type: CLP.') +
MSGTYPE(*ESCAPE)
RETURN
ENDDO
DLTPGM PGM(QTEMP/&MBR)
MONMSG MSGID(CPF2105) /* Program not found. */
OVRPRTF FILE(*PRTF) HOLD(*YES) USRDTA(RUNCL)
CRTCLPGM PGM(QTEMP/&MBR) SRCFILE(&SRCLIB/&SRCNAME) +
ALWRTVSRC(*NO)
DLTOVR FILE(*PRTF)
CALL PGM(QTEMP/&MBR)
DLTPGM PGM(QTEMP/&MBR)
RETURN
FATAL: FWDPGMMSG
MONMSG MSGID(CPF0000)
ENDPGM
Figure 3: Attach this program to the RUNCLCMD as the command processor for command RUNCL.
Parameter Type Description
Error Flag Output Returns flag indicating whether errors occurred on call to
QMHSndSM API Scope Program Input The program that is called at job end Scope Library Input Optional library name for scope program; if not passed, *LIBL is used
Scope Data Input Optional parameter passed to the scope program, TstSndScp could be modified to accept a data structure or pointer for this paramter
Figure 4: These parameters are passed to the TstSndScp program.
Figure 5: You can get to the Console Type selection screen from the Dedicated Service Tools menu.
|
|
Figure 6: Select Console Type from this menu in Dedicated Service Tools.
LATEST COMMENTS
MC Press Online