Cover V04, I05
Article
Figure 1
Figure 2
Figure 3
Listing 1
Listing 2
Listing 3
Listing 4
Sidebar 1

sep95.tar


Real-Time between DOS, UNIX, and MVS

Colt Johnson

As business needs change and evolve, so do the computing needs required to support them. At our site, we needed to automatically verify cartons as they were received via a conveyor system. The problem was that the verification had to be performed against a completely different platform: the mainframe. By combining DOS, UNIX and MVS, we were able to build a solution.

Application Overview

The application I will discuss here involves weighing and scanning our receivables as they arrive in their fixed-size vendor cartons. Our vendors provide our merchandise in fixed-size boxes that fit directly into the racks in our warehouse. To eliminate the need for our receiving clerks to open up each box and count each piece, boxes are automatically weighed and scanned once placed on a conveyor belt (think of this as the checkout counter process at the supermarket, but without the checkout clerk). Since we know the individual weight of each piece from our inventory file and we know the tare weight of the box, we can calculate the number of pieces in each box from the total weight.

Once the weight and barcode of each box is collected, it is transmitted as an ASCII data string to a DOS-based PC running a proprietary program supplied by the scale manufacturer. As the program receives the ASCII data string from the scale and scanner it passes it to a host system. Then it waits for a response back from the host system. Once the response is received, the program then passes that response back to the scale system, then waits for the next box. The response that is passed back to the scale system is an alpha code indicating one of three directions the box will take as it travels down the conveyor belt: (1) the staging area where the box will be put away in the warehouse; (2) the quality control lane, should the merchandise need visual checking; or (3) the reject lane, should there be something physically wrong with the box.

The scale was designed with a DOS "controller" PC so as to be generic and thus provide connection with virtually any host. In our case, the host is an IBM mainframe ES9121 model 621 running the MVS/ESA operating system. Our enterprise databases are VSAM (Virtual Storage Access Method) datasets used by IBM'S CICS (Customer Information Control System) command-level programs. This created a connection problem, since the character set on the mainframe is EBCDIC (Extended Binary-Coded Decimal Interchange Code), as opposed to ASCII on the DOS scale PC, and the mainframe is synchronous while the PC is asynchronous. In addition, physically connecting the two systems involves a Terminal Cluster Controller on the mainframe versus a serial port on the PC (see the sidebar "IBM Mainframe Hardware"). Our solution was to place a SCO UNIX box between the IBM mainframe and the DOS scale PC. The SCO box performs the handshaking between the two systems as follows: When the ASCII data stream is sent by the DOS PC, it is first received by the SCO box. It is then automatically converted to EBCDIC and sent to the mainframe via emulation software. When the mainframe responds back, it's the SCO box that receives the response first and then forwards it on to the DOS PC, (see Figure 1). In one sense we have two hosts doing the work here, but that is all transparent to the DOS-based scale PC. It couldn't care less -- as long as it gets a response back from the message it originally sent, it's happy.

DOS PC Connection

The DOS connection involves an AT-Class PC running MS-DOS and a proprietary software program provided by the scale manufacturer. The PC contains a multi-port serial card providing four RS-422 serial connections. As with other system parameters, the individual port setups for characteristics such as line speed and protocol are configured and controlled through the scale's proprietary software program. As far as the physical port connections are concerned, the first port connects to a laser barcode reader, the second port connects to the actual weighing scale, the third port connects to a TTY line on the SCO UNIX box, and the fourth port is currently unused. Of interest here is the connection to the SCO UNIX box. As barcodes and weights are received from ports one and two, a data record is formatted and sent out the port to the SCO box. We ran that serial line through an RS-422 to RS-232 converter since our multi-port adapter on the SCO box was RS-232. Because this is a proprietary software program, all installation, configuration, and setup was performed by the scale manufacturer.

UNIX/Mainframe Connection

The base UNIX system involves an Austin 486/50 tower PC with 16Mb of RAM and twin 500Mb hard disks, an internal tape backup drive, a modem for dial-in support, and an 8-port Megaport multi-serial port adapter. The system runs SCO UNIX System V/386 release 3.2v4.2 operating system. To make the connection to the IBM mainframe, we installed the Cleo 3270LINKix kit, from Cleo Communications of Ann Arbor, MI. The kit contains the hardware and software necessary (except the cable) for connecting into the IBM.

The Cleo hardware is an ICOT ShortCUT board that installs in an open slot in the UNIX system. We used the default settings for interrupt, I/O address, shared-memory address, and DMA channels without a problem. However, each of these settings can be easily reconfigured with dip switches on the board should we need to change them. The cable was a simple twisted pair wire with RJ-11 (telephone style) connectors on each side. We used a balum on each end to convert the cable back to the standard coax BNC-Style connector. One end plugged into the BNC connector on the ICOT board and the other plugged into a BNC connector port on the IBM 3174 cluster controller. At this point it was necessary to get an IBM systems programmer involved to configure the IBM 3174 port as a "Distributed Function Terminal (DFT)" port (see the sidebar "IBM Mainframe Hardware"). The Cleo 3270 software provides five 3270 terminal sessions over one physical DFT line.

The Cleo software was easily installed using SCO's sysadmsh software install option. (sysadmsh actually calls custom to do the install). Installing was as easy as following the screen prompts from the Cleo install script and typing in the default hardware settings mentioned above. Once the UNIX kernel was relinked and the mainframe activated its line, the SCO UNIX box appeared as a remote 3174 Terminal Cluster Controller to IBM. We knew we had succeeded when we typed com3270 from a UNIX terminal and got the IBM terminal screen!

Making It Work

Once we got all the hardware and system software installed on all three platforms we began work on the application, which basically centered on a C program that ran on the SCO box. This program was written in house and performed the handshaking between the DOS scale PC and the IBM mainframe. The program reads data from the serial line that is connected to the DOS PC, sends that data to the IBM mainframe, waits for a response from the mainframe, and then sends that response back to the DOS PC. Some interesting programming issues were addressed in this application, and the program was compiled so that we could take advantage of curses the terminal screen handling routines provided by SCO. These routines are listed in detail in the SCO Programmers Guide and contain a wealth of tools that add excellent screen handling capabilities to any program. Refer to Figure 2 for syntax on using curses.

The first programming issue was keyboard polling. As the program continuously looped, monitoring the data from the DOS-based PC, we needed a way to cleanly stop the program. In DOS you can simply call the kbhit() function to check the status of the keyboard. If the function returns a true value, it means a key was pressed. You can then use the getch() function to fetch the keystroke (see Listing 1). Unfortunately, the kbhit() function is not available in UNIX; however, the same result can be achieved using the powerful UNIX file handling controls. Since the user's terminal is a special file (/dev/TTY), we can open it up with a "no delay" flag. We then perform a read on this file. Instead of waiting until data is available, the "no delay" flag says "read the file, and if there's data, get it; if there's not any data, don't wait, continue on." In this way we can monitor the user's keyboard and exit the program (or perform any other action) should a key be pressed. To achieve exactly what we want, we must set other characteristics of the user's terminal. Listing 2 shows the coding fragment. The following is an explanation of the code statements:

1. Include termio.h for the necessary definitions.

2. Define two structures of type termio.

3. Close the user's terminal keyboard. "0" represents the file descriptor for the user's terminal.

4. Open the user's terminal with the "no delay" and "read/write" parameters.

5. Use the I/O control function to read the current settings into the temporary structure. "0" represents the open file descriptor for the user's terminal. TCGETA selects the control function that gets the parameters; ttyOParms is the address of the receiving data structure.

6. Assign the new structure from the temporary structure.

7. Clear the ICANON and ECHO flags in c_lflag so the input characters are not assembled into lines before they are read, and there is no character echo.

8. Set the minimum characters to read to 1. The read will be satisfied after reading a single character.

9. Set the time to 0. Time here is really irrelevant since we are only reading a single character anyway; however, VMIN and VTIME must be set if the ICANON flag is cleared. Otherwise, you may get strange results.

10. Use the I/O control function to reset the user's terminal keyboard. "0" represents the open file descriptor for the user's keyboard: TCSETA selects the control function to reset the parameters according to the values in ttyNParms.

11. wgetch is the curses function to fetch a keystroke if one exists in the user's terminal keyboard buffer. The function returns immediately, regardless of the result.

The second programming issue was CPU overhead in reading data from the DOS PC. In keyboard polling we want to read a single character at a time; however, when reading the data from the DOS PC, it would be more efficient to read a block of data at a time -- in other words, only satisfy the read request once the entire message is present. We can do this with "canonical" processing. With canonical processing, the input characters are assembled into lines before they are read. That is, they are terminated with an ASCII LF character. Setting up the TTY line for the DOS PC in canonical mode also requires the use of the I/O control function (ioctl). Listing 3 contains this code fragment. The following is an explanation of the code statements:

1. Include termio.h for the necessary definitions.

2. Define structure of type termio.

3. RdChars will be the number of characters read from port. ImbsPort is the file descriptor for the TTY line.

4. This structure is the buffer where the read characters are placed.

5. Open the TTY line connected to the DOS PC with "no delay, read/write".

6. Use the I/O control function (ioctl) to get the current setting of the port. ImbsPort represents the open file descriptor for the TTY line connected to the DOS PC. TCGETA selects the control function that gets the parameters. NewParms is the address of the receiving data structure.

Four fields in the termio structure are used to setup line and protocol settings: c_ifflag describes the basic terminal input control, c_oflag specifies the system treatment of output; c_cflag describes the hardware control of the terminal; and c_lflag is used by the line discipline to control terminal functions.

7. The line is a local connection with no modem control.

8. Set the character size to 7.

9. Set the stop bits to 2.

10. Turn on canonical processing -- that is, wait until an ASCII LF character is sent before reading the port. The message from the DOS PC terminates each time with the ASCII LF character. This is its "end of transmission" character.

11. Turn off character echo on the TTY line.

12. Do not even echo the new-line character. The new-line character gets echoed even if echo is turned off; therefore, this statement was necessary.

13. Disable start/stop output control because the DOS PC controlls the data flow.

14. Set the baud rate to 9600.

15. Enable even parity checking.

16. Now use the I/O control function (ioctl) to reset the TTY line with the new settings. ImbsPort is the open file descriptor; TCSETAF is the command to reset the TTY line. TCSETAF is different from TCSETA in that it first waits for any output on the line to drain, then it flushes the TTY line input queue, then it sets the values. We use it to flush any garbage characters that might exist on the line.

17. Initialize RdChars to zero.

18. Read a block of data. The read request will be satisfied when an ASCII LF character is received. ImbsPort is the open file descriptor of the TTY line connected to the DOS PC: &Imbs is the address of the data structure where the data will be placed; and sizeof(Imbs) is the size of the buffer.

The final programming issue had to do with communication with the mainframe. Once we received the data from the DOS PC, we had to forward it on to the mainframe for validation against the enterprise databases. This is where the Cleo 3270 product came into play. In addition to providing 3270 terminal emulation to the mainframe, it also provides a way of communicating through what is know as HLLAPI. (High Level Language Application Programmer Interface). HLLAPI is an interface used in applications to communicate with the mainframe through the 3270 emulation program. In other words, programs can be written to automate tasks such as logging on and running jobs that usually require human intervention. Anything that can be done from the users 3270 keyboard can be automated through HLLAPI. Although an entire article could be dedicated just to the subject of HLLAPI, I will discuss only the basics here.

HLLAPI works by first running the 3270 emulation program (in our case, we start it running in the background, since we designed the program not to need human intervention) and then executing the HLLAPI application. HLLAPI communicates with the mainframe by processing special function requests made in the application. These functions are part of the development kit that comes with the product. In addition, the program is compiled and linked with a special include file and library. The following steps are performed when the application makes a request to a HLLAPI function:

1. The application calls a HLLAPI function, passing it the appropriate parameters.

2. The HLLAPI function is part of the library and it's the library that interprets and sends the request to the 3270 emulation program.

3. The 3270 emulation program performs the necessary mainframe communications, processes the request, and sends back a return code indicating the results of that request to the library.

4. The library then sends the return code back to the application.

Refer to Figure 2 for the syntax in compiling and linking a HLLAPI program.

One other point to mention concerns "presentation space." The application program and the mainframe communicate through what's referred to as the 3270 presentation space, or terminal display. This is actually a screen buffer of the session to which the program is attached. Using the Model 2 (24x80) screen mode, for example, we have a presentation space of 24 rows and 80 characters across, or 1920 characters. Simply put, we have a single dimension array of 1920 characters each time we receive a mainframe screen. HLLAPI functions act on this array -- for example, searching for a specific piece of text. This is how a communication dialog is formed between the two systems.

Our mainframe, like most, uses an online programming interface called CICS (Customer Information Control System). CICS, in conjunction with the COBOL programming language, is the standard way we write 3270 terminal screen programs for accessing our mainframe files. Because we already had this efficient method for performing mainframe file I/O, there was no need to reinvent the wheel. We decided to take the following approach: write a CICS program to handle the mainframe file I/O, and write a UNIX HLLAPI program to control the CICS program. The CICS programming part was handle by the mainframe programming staff. The CICS program contains a field on its screen for accepting the data record from UNIX and a field to display the return code from the mainframe I/O request. Each time we send data, we check the return code field and act accordingly. While the HLLAPI program was being developed, the CICS program was also being developed and tested on a standalone 3270 terminal. The programmer manually entered the data and checked the return code field, as would be done automatically by the HLLAPI application.

There are three items basic to calling any HLLAPI function. The first is to include the structures and function constants in the source module(s). The second is to set up the four standard arguments for any HLLAPI function (there are three integers and one character string). The last is to call the HLLAPI function (refer to Listing 2). There are dozens of HLLAPI functions available for use; however, only the relative functions used in the program are discussed here.

The following are line-by-line explanations of the numbered lines of code in Figure 2:

1. Includes the basic structure and function constants.

2. This is the function code of the HLLAPI function being called. Function codes are constants defined in hapi_c.h.

3. This is a string of text used for control information needed by the function; or it can be an empty string in which the HLLAPI function will return some data.

4. Usually used to specify the length of HDataString. It may also be used for return data.

5. Usually contains the return code from the function. This return codes indicates whether the function was successful or not. This field can also indicate the screen position of the cursor.

6. This is the syntax of the HLLAPI function call. Notice we use "call by reference" when passing HFunction, HLength, and HRetCode arguments to the function.

Having explained the mechanics, I would now like to put it all together with the code example in Listing 4. Lines 1 through 18 contain the main code, lines 19 through 56 contain the functions called by the main code.

The following are line-by-line explanations of the statements in Listing 4. For the sake of simplicity, no return codes are checked in this example. In the real world, we would check the return codes passed from the HLLAPI functions and act accordingly. For example, in lines 1 and 2, if host session A was not available, we would not be able to log into the mainframe and might display an error message and/or exit the program. We must first log into the mainframe.

Line 1 checks the availability of host session A.

Line 2 connects to host session A.

Lines 3 through 13 use the WaitForMsg() and ReplyWithMsg() functions to send and wait for text from the mainframe. The text exists in the presentation space (terminal screen). The @E imbedded in the character strings is a macro substitution by HLLAPI to send the enter key. If the Enter key was not sent, the screen would not change. Remember, it's just as if someone was typing at the keyboard. The @C in line 12 is the macro substitution for the Clear key.

Lines 14 through 17 are a basic loop that reads data from the DOS PC (explained earlier) and then uses the ReplyWithMsg() function to send the data to the mainframe. Line 15 does the keyboard polling explained earlier. Here is where we would want to check the keyboard, and if it was pressed, respond accordingly. Line 18 uses DiscHostSessA() to disconnect from the host session. The following lines are the functions that were called from above.

Lines 19 through 26 constitute the CheckHostSessA() function, which returns a TRUE value if host session A is available. (Host sessions are configured in the 3270 software setup program.) Here HDataString contains the desired session ID.

Lines 27 through 34 comprise the ConnHostSessA() function. We must call this function to actually connect to host session A. A TRUE value is returned if we actually connected.

Lines 35 through 45 present the WaitForMsg() function, which uses two HLLAPI functions. First we copy the presentation space so we can access the screen buffer array (Lines 37 through 40). Then we search the presentation space looking for the desired text starting at array element 0 (the beginning of the array). To make this more efficient, you would probably code it in some sort of loop. If you don't get the text, continue to loop, reading the presentation space and searching. After a fixed period of time, or number of loops, exit.

Lines 46 through 56 show the ReplyWithMsg() function, which also uses two HLLAPI functions to send data to the mainframe. Lines 48 through 51 actually send the "message" (including the Enter or Clear key, as in our example) and then lines 52 through 56 perform a HLLAPI wait function. The wait is important, because after we send the Enter, Clear, or some other action key to the host, we must wait until the host screen refreshes before continuing. 3270 terminals are half-duplex and when an action key (Enter, Clear, function key, etc.) is pressed, the terminal keyboard locks until the screen refreshes. Lines 57 through 64 show the DiscHostSess() function that disconnects the mainframe session.

Summary

A few years ago I remember reading articles that said the big mainframes were going away. In fact, one article even said the mainframe was dead. People are now finding that the mainframe has matured over the past few decades into a machine that already offers the robustness needed for today's transaction-based and client-server applications. As more and more 'open system' technologies interconnect with the mainframe, applications like the one discussed in this article will become more and more common.

Bibliography

3270LINKix 3270 User's Guide and HLLAPI Programmer's Guide. Ann Arbor, MI: Cleo Communications, 1991.

Martin, James. Data Communication Technology. Englewood Cliffs, NJ: Pretice Hall, 1988. ISBN: 0-13-196643-X.

About the Author

Colt Johnson earned a BS in Mathematics from Liberty University. He is currently doing Systems Programming for J. Crew Group, Inc., in Lynchburg, VA in the areas of MVS/ESA, UNIX, and MS-DOS. Over the past 13 years he has done extensive systems work in the areas of PCs, telecommunications, UNIX, IBM Mainframe, and C programming, as well as cross-platform system integration. He can be reached at 647-9549@mcimail.com.