Q: We have a requirement to extract data from an existing green-screen display into an Attention-key program. The users run the green-screen application as usual, and when they get to the right screen, they hit the Esc key on their keyboard, which is mapped to the 5250 Attention key and causes my RPG program to run. My RPG program should read certain numbers off of the screen and then pass those numbers to a PC program (via the STRPCCMD command).
I can't change the existing green-screen program (it's third-party software, and I don't have the code), and the users need to continue to run the application interactively (I can't run the application in a separate screen-scraper job). So I need to extract the numbers straight from the screen image. I know the correct rows and columns to get the data from, but I can't figure out how to extract data from the screen! I've scoured the Internet looking for a solution to no avail. Do you have any advice?
A: I have an idea that just might do the trick.
Starting with V2R3, OS/400 has had an API called Read Screen (QsnReadScr). When you call the API, it asks the 5250 terminal (or emulator) to return the contents of the screen, and it stores that screen image in a buffer. If you overlay that buffer with an array, you can use that array to read the screen.
For the sake of demonstration, let's say your screen looks like this:
10/08/08 Displaying Order Number: 72938 OEICBDS2C Customer: 8811 ACME INDUSTRIES Order Taker: SPONGEBOB Bill to: 5800 123 IMPORTANT LN Ordered on: 2007/12/27 Disc Type: ANYTOWN, USA at: 8:37:08 Disc Rate: Deliver on: 2008/01/07 Disc Amnt: P.O.#: 837829 Route/Stop: 6R / 000 Date Paid: 01/28/08 Ordered Prod. Shipped Billed Quantity Unit Code Product Description Qty. Price Unit Extension ------------------------------------------------------------------------------- 126.00 BXS 5080 BLUE ROUND WIDGET 1512.00 1.99 LBS 2647.81 30.00 BXS 5212 RED SQUARE WIDGET 360.00 1.99 LBS 630.43 126.00 BXS 5660 GREEN PLUSH WIDGET 1512.00 1.99 LBS 2647.81 180.00 BXS 5690 PLAIN WHITE WIDGET 2160.00 1.99 LBS 3782.59 170.00 BXS 5720 ORANGE TRIANGLE WIDGET 2040.00 1.99 LBS 3572.45 80.00 BXS 5740 BLUE SQUARE WIDGET 960.00 1.99 LBS 1681.15 150.00 BXS 5745 ORANGE JUICE 150.00 23.88 BXS 3152.16 210.00 BXS 5750 RED ROUND WIDGET 2520.00 1.99 LBS 4413.02 F3=Exit F12=Cancel F11=Display Billing Info F6=Print Bottom |
For this example, we need to get the fields that I've highlighted in red: the order number from line 1, column 49; the screen ID from line 1, column 71; and the customer number from line 3, column 13.
The screen ID is used only to verify that the user presses the Esc key on the correct display. If the wrong screen ID is entered, the program simply ignores the request.
My program to extract the data starts by determining whether this is a 132x27 screen or an 80x24 screen by calling the Retrieve Screen Dimensions (QsnRtvScrDim) API.
D height s 10I 0
D width s 10I 0
.
.
QsnRtvScrDim( height
: width
: *omit
: *omit );
This API passes back the screen height and width into the parameters I've provided, above. The last two parameters are omissible, and since I don't need them, I've provided the *OMIT special value.
The screen dimensions are important because they dictate the buffer size. Remember, we're going to load the whole screen into this buffer, so it has to be large enough for either 80x24 or 132x27, depending on the current screen dimensions. IBM provides the Create Input Buffer (QsnCrtInpBuf) API for creating the buffer.
D inbuf s 10I 0
.
.
inbuf = QsnCrtInpBuf( height * width
: *omit
: *omit
: *omit
: *omit );
This API creates an input buffer in the computer's memory, and it returns a handle to that buffer. When I want to retrieve the screen contents, I pass that inbuf handle to the QsnReadScr API and that tells the system where to store the screen contents.
D p_screen s *
D screen80 s 80A dim(24) based(p_screen)
D screen132 s 132A dim(27) based(p_screen)
.
.
QsnReadScr( *omit
: inbuf
: *omit
: *omit
: *omit );
p_screen = QsnRtvDta( inbuf
: *omit
: *omit );
The QsnReadScr API is the key piece of this program. This API causes the operating system to request the screen contents from the 5250 terminal (or emulator). The terminal sends back the screen, and the operating system stores it in the buffer that we created earlier. Now that it's in that buffer, I call the Retrieve Pointer to Data in Input Buffer (QsnRtvDta) API, and it tells me where the screen data is located in the computer's memory.
Because my screen80 and screen132 arrays are based on the p_screen pointer, they overlay the same spot in memory as my screen image. Now I can read those arrays to get the data from the screen.
Why do I have two arrays? Well, the layout of the buffer will be a little different if the screen is 80 column instead of 132 column. So one array is designed to interpret 80-column data, and the other interprets 132-column data.
D ordno s 5A
D cust s 4a
D screen s 9a
if (width = 132);
screen = %subst(Screen132(1): 71: 9);
ordno = %subst(Screen132(1): 49: 5);
cust = %subst(Screen132(3): 13: 4);
else;
screen = %subst(Screen80(1): 71: 9);
ordno = %subst(Screen80(1): 49: 5);
cust = %subst(Screen80(3): 13: 4);
endif;
QsnDltBuf( inbuf : *omit );
Each entry in my array represents one row of the screen. So if I read Screen80(1), it gives me the contents of the first row. If I read Screen80(3), it gives me the contents of the third row. To extract only part of the row, I use the %subst() BIF.
At this point, if all is well, the screen ID is in the variable named screen, the order number is in the variable named ordno, and the customer number is in the variable named cust. I no longer need to interrogate the screen further, so I call the Delete Buffer (QsnDltBuf) API to tell the system I no longer need the buffer, and it can use the memory for something else.
Finally, I can use that data with Start PC Command (STRPCCMD). For the sake of example, I tell the PC to open a browser window, and I tell it to display the particular order and customer number. Obviously, in your scenario, the PC command will be different.
if (screen = 'OEICBDS2C');
callp(e) QCMDEXC('STRPCO': 6);
cmd = 'STRPCCMD PCCMD('rundll32 url,FileProtocolHandler '
+ 'http://as400.example.com/cgi-bin/oeiinvhr4.pgm'
+ '?order=' + %trim(Ordno)
+ '&cust=' + %trim(cust)
+ ') PAUSE(*NO)';
callp(e) QCMDEXC(cmd: %len(cmd));
endif;
After I finish coding my program, I need to install it as the Attention key program in my job. To do that, I type the following:
SETATNPGM PGM(GRABDATA) SET(*ON)
Now that I've done that, whenever I press the 5250 Attention key (usually the Esc key on the PC), the operating system runs my GRABDATA program, which extracts the appropriate data and runs my PC command.
The Set Attention Program (SETATNPGM) command sets the Attention key only temporarily. If I want to make it permanent, I can run the following command instead:
CHGUSRPRF USRPRF(my-user-id) ATNPGM(GRABDATA)
You can download the complete GRABDATA program from the following link.
http://www.pentontech.com/IBMContent/Documents/article/57287_684_GrabData.zip