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.
|