Archive

Archive for the ‘ISPF’ Category

Dynamic ISPF Panels – Getting the Input Data

April 23, 2013 Leave a comment

In the previous article I looked at the general process of building the ISPF screen dynamically, including the creation of input fields. Now it’s time to get the users input from those fields into the application. Again I am using REXX as the driving code.

In a typical static ISPF panel, fields are referred are named and the data in them can be set and retrieved by variable name within the application.

With a dynamic ISPF panel there are no variables that you can refer to, only the variable being used to refer to the data string that is the panel definition. when the user enters some data into the input fields on your screen and presses enter, ISPF rebuilds the entire data string and updates the dynamic screen variable, complete with text fields you output, attributes and input data from the user. You have to manually extract the input data from that string and place the data into your own variables.

The process of extracting the input data from the screen variable is a lot simpler if you only ever use one attribute character to mark the start of an input field and one attribute character to mark the end of an input field.

In the previous article I had the following hexadecimal attribute definitions in my skeleton panel:

01 TYPE(DATAOUT) INTENS(LOW)                      
02 TYPE(DATAOUT) INTENS(HIGH)                     
03 TYPE(DATAIN)  INTENS(LOW) COLOR(GREEN) PAD(_)  
04 TYPE(DATAOUT) INTENS(LOW) COLOR(RED)           
05 TYPE(DATAOUT) INTENS(LOW) COLOR(YELLOW)

And I built each input line using the following code:

dol='01'x                                                       
din='03'x                                                       
doy='05'x                                                       
dynvar=doy||SetSize('Enter something..............',24)||din||, 
            SetSize('here',8)||dol

The effect of the above code is that the start of each input field is marked by a ’03’x attribute byte and the end of the input field is marked by an ’01’x attribute byte.

The following piece of code extracts the input fields from the screen variable and places them into stem variables, then sets the total number of input fields in the dot zero stem variable:

input.=''
i=0                                               
do while pos('03'x,dynvar) <> 0                   
   i=i+1                                          
   parse var dynvar . '03'x data '01'x dynvar     
   input.i=data                                   
   end                                            

input.0=i

Hopefully it is now obvious why I chose to use hexadecimal values for attribute characters. If I had used something the user could enter, as the end of field attribute byte then if the user entered that character, the above code would only extract data up to that character, in effect cutting the input data short.

If you use multiple differing attribute bytes to delimit the start or end of the input fields then the above code becomes more complex since you have to allow for the multiple differing start and end of field markers.

This technique works fine if the input fields are always in the same order on the screen. Remember, there is nothing in the input data from the screen that really defines what each field contains unless you look at the text that occurred before it, so you have to know that the first input field always contains data for a specific item, the second input field always contains data for the next specific item and so on.

If your application can build the screen with input fields that appear in differing order for different situations then YOU have to keep track of the order of the input fields on the screen and extract the data into the appropriate variables for your application code to process.

One way is certainly to look at the prompt that occurred before the input field and use that to determine what the data is and which variable it should go into in the application and I am sure there are many other ways to achieve the same thing. As all the best books say, this is left as an exercise for the reader!

Categories: ISPF, Mainframe, REXX

Dynamic ISPF Panels

April 19, 2013 4 comments

Typically, ISPF panels are very static in nature, at least as far as the content is concerned. Input and output fields are ‘named’ and can be set and contents retrieved by the underlying application through the use of variables and variable services. In addition the panel can contain a wide variety of processing statements to validate the input, thus relieving the application of much of that work.

In all my years working with mainframes I have never worked or had the need to use any other type of ISPF panel – until now!

I was looking at adding an ISPF based ‘wizard’ to some code and wanted to make is a ‘consumable’ as possible.

Well, the reality is that I could have used the typical ISPF static panel but I decided I wanted to try to mimic the more modern dynamic web page applications where the content of the page changes depending upon the options you select.

What I found was that you can define a ‘dynamic’ area in an ISPF panel and actually build the contents of the screen in the application. While I found some general guidelines on how to do this I did not find any specific examples of what you might call ‘real world’ code and so, as usual, I had to invent it all myself!

This article is the result of my experimentation. There are some things still missing like cursor positions for one and handling input and output fields that change order (you’ll see why this is important later on) but should you feel the need to have a go with creating dynamic ISPF panels, hopefully this will help get you started. They are pretty cool!

By the way, since this was simply an exercise my example code is all done in REXX to keep things simple and easy to change.

Panels and execs are all in the the standard ISPF locations. nothing fancy there.

The Panel Definition

Even though you can build pretty much the entire panel in the application, you still need a basic panel definition in the ISPPLIB concatenation. This is mine:

DynamicISPFPanel

The dynamic area is defined by the ‘$’ symbol in the panel. It extends the full width of the panel (80 chars) and is referred to by the name DYNVAR. The ‘$’ are NOT treated as attribute characters and do NOT take up space on the panel, they are in fact part of the dynamic area.

I also defined several hexadecimal attribute character strings x’01’ through x’05’. I used hexadecimal characters so that there is no way the user can enter them into the input areas. This is important because YOU have to find them in the screen var (DYNVAR in my case) in your code and if you sued a character the user could enter you might mistake user input for attribute characters.

Cursor positions is currently rudimentary and fixed. I position the cursor at column 27 in the dynamic screen variable DYNVAR, which is the location of my first input field.  Ifthe location of the first input field could move I would have to make that positioning more dynamic by using a variable and setting the offset in the dynamic var in the application code. For now, this works though!

The Dynamic area

In my case, the dynamic area is 80 characters long. The way it is built is that the first 80 characters of whatever is in the DYNVAR variable are used to build the first line of the dynamic area. Then the next 80 characters are used for the second line, the next 80 characters for the third line and so on.

So, if I do the following in the application exec (My test panel is called Z991):

dynvar='This is a test.'
address ISPEXEC 'DISPLAY PANEL(Z991)'

I would get this:

DynamicISPFPanel-001

Notice the cursor is where I positioned it in the panel definition in spite of the fact there is nothing there yet!

In order to get something to display on the next line I have to pad that text out to 80 characters and then add the next line of text. So I’d need driving code something like this:

dynvar=fit2line('This is a test.')||'This s line 2.'
address ISPEXEC 'DISPLAY PANEL(Z991)'

Which gives me this:

DynamicISPFPanel-002

In case you are wondering what ‘fit2line’ is, it’s a little function I wrote that just pads out whatever is passed to it to a multiple of the line length (80 characters in my case). It looks like this:

Fit2Line: procedure             

parse arg in                    
ll=80      /* must = line length */
l=length(in) // ll              
out=in||copies(' ',ll-l)        
return out

If I wanted to add a third line, the dynvar assignment would be something like this:

dynvar=fit2line('This is a test.')||fit2line('This s line 2.')||'This is line 3.'

Input Fields

So now lets add an input field at the cursor location.

Since DYNVAR starts in column 1 (remember the ‘$’ is NOT an attribute, just a marker for the dynamic area) and the cursor is at column 27 on line 1 of the area, we need to make the data up to the cursor position 26 characters long.

However, Since the cursor is at position 27 and this is going to be an input field, we need an attribute character just before it. It would also be nice if we had an attribute character before the text as well.

If you go back to the panel definition, you will see that it has the following attributes defined:

01 TYPE(DATAOUT) INTENS(LOW)                      
02 TYPE(DATAOUT) INTENS(HIGH)                     
03 TYPE(DATAIN)  INTENS(LOW) COLOR(GREEN) PAD(_)  
04 TYPE(DATAOUT) INTENS(LOW) COLOR(RED)           
05 TYPE(DATAOUT) INTENS(LOW) COLOR(YELLOW)

Each 2 digit value represents a single hexadecimal attribute byte.

The input attribute is hex ’03’ and there are a variety of output attributes. I’m going to make the text yellow so I am going to use the hex ’05’ value.

My code now looks like this:

din='03'x                                                     
doy='05'x                                                     
dynvar=doy||SetSize('Enter something..............',24)||din

I defined the attributes as variables to make them easier to recognize.

SetSize is a simple routine to set that length of a string to a specified size. It looks like this:

setsize: Procedure                        
parse arg in , l                          
if length(in) < l then return left(in,l)  
return left(in||copies(' ',l),l)

This is what the resultant panel looks like:

DynamicISPFPanel-003

Slight problem! We did not set the location of an attribute to indicate the end of the input field/start of the next field. Hence it was continued all the way to the end of the dynamic area!

Remember that everything follows on sequentially so we need to set a string to the required length of the input field and then add in a closing attribute byte. The input field string can be spaces or it can contain some initial data. So now my code looks like this:

dol='01'x                                                       
din='03'x                                                       
doy='05'x                                                       
dynvar=doy||SetSize('Enter something..............',24)||din||, 
            SetSize('here',8)||dol

I’ve added a ‘dataout low’ attribute (see panel definition) and set the input field size to 8 characters. I’ve also initialized the input field data to the word ‘here’. Now my screen looks like this:

DynamicISPFPanel-004

If I wanted to add another input field on the third line so that there is a blank line between the input lines then my code becomes something like this:

dol='01'x                                                         
din='03'x                                                         
doy='05'x                                                         
dynvar=doy||SetSize('Enter something..............',24)||din||,   
            SetSize('here',8)||dol                                
dynvar=fit2line(dynvar)||fit2line(' ')||,                         
       doy||SetSize('Enter something else.........',24)||din||,   
            SetSize('here',8)||dol

Notice that after setting the first line up I had to use the fit2line function to expand it to fill the whole line. Then I used the fit2line function with a single space to cause it to add a blank line and finally I added the new line to the dynamic variable. My screen now looks like this:

DynamicISPFPanel-005

Of course the data I have displayed in the input fields could have come from variables instead of hard coded literals, as could the prompts.

In the next part of this article I shall look at the steps needed to extract the input data from the screen into the application.

Categories: ISPF, Mainframe, REXX

The TSO/ISPF Client Gateway

January 25, 2012 Leave a comment

The inspiration for version 2 of my ‘ISPF on the web’ experiment is a feature of TSO/ISPF called the “TSO/ISPF client gateway”.

What caught my eye about it initially was a comment to the effect of “access ISPF from the web” and so of course I thought “cool, that’s just what I need”. It is, or course, not quite that simple and there are a few things to consider when using the gateway that I shall write about more in upcoming posts.

You can read about the gateway here:

ftp://public.dhe.ibm.com/software/websphere/awdtools/ispf/ispfgw10.pdf

Categories: Coding, ISPF, Mainframe

The case of the wrong case…

September 18, 2011 Leave a comment

This has been a bit of a detective story so I thought I’d mention it here in case it helps anyone.

As part of my efforts to move my web based ISPF interface to the stand alone IBM HTTP server I needed to run the REXX execs under USS. Now to be honest I rarely ever touch USS on z/OS but that’s where I needed to be so that’s where I am.

I typically write my REXX execs in lower case so I might end up with one exec invoking another with something like this:

data=myfunc(parm1,parm2,etc)

Where myfunc is another REXX exec in the standard search order (SYSPROC and SYSEXEC typically). This works fine because even though the function name is written in lower case, REXX converts it to upper case and that matches the member name in the library just fine. Everything works great.

BUT! Move over to USS land and things are not so simple. REXX in USS land is case sensitive. Using the example above, I had created the ‘myfunc’ exec file  in USS with a lower case file name and was surprised when the calling exec could not find it.

Eventually (after two days) I found that by default, REXX makes function names coded like this into UPPER CASE before searching for them (I knew this, I had just forgotten about it) so REXX was searching for a file called ‘MYFUNC’ whilst the file I had created was called ‘myfunc’. Not the same animal in a case sensitive environment.

I could make all my exec file names upper case to address this, but in the event you need to call a lower case function name you can code it like this:

data='myfunc'(parm1,parm2,etc)

And amazingly it will now find the lower case exec file.

ISPF on the web – Version 2

September 15, 2011 2 comments

Just a quick update, more details and info to follow soon.

My initial iteration of ISPF on the web required the user to still log on to TSO/ISPF on a 3270 and then start the web server inside their TSO/ISPF session before accessing it via a browser.

Whilst this was an interesting exercise and learning experience, obviously it’s about as useful as a chocolate fireguard in practice.

Hence the move to version 2!

The UI experience in the browser is the same but the back end is significantly different, running inside the IBM stand alone HTTP web server.

I am still developing this in my spare time, but as soon as I get a demo up and running I shall be adding more information so stay tuned.

Finding out where a Rexx exec was loaded from

April 24, 2011 Leave a comment

I had a need to find out which data set a Rexx exec was being loaded from within the exec. Whilst there are examples out there that trawl through the TIOT, since I was in an ISPF environment I came up with the following little bit of code:

/* rexx */

/* Find out where I came from.             */

address ispexec

PARSE SOURCE . . pgm execdd .

"qbaselib "execdd" id(dsns)"

dsns = Translate(DSNS,' ',',')
dsnum = words(dsns)

do n = 1 to dsnum

   dsn = strip(word(dsns,n),"B","'")
   "lminit dataid(id) dataset('"||dsn||"')"
   "lmopen dataid("id")"
   "lmmfind dataid("id") member("pgm") stats(yes)"
   findrc=rc
   "lmclose dataid("id")"
   "lmfree dataid("id")"
   if findrc = 0 then leave

   end /* do dsnum */

if findrc = 0 then do
   say pgm "was loaded from <"dsn">"
   return 0
   end
else do
   say "cannot determine where "pgm" was loaded from"
   return 4
   end

It would be easy to make this into a function to return the data set name (in the dsn variable).

Categories: Coding, ISPF, Mainframe, REXX

HTTPS

August 2, 2010 Leave a comment

We’re pretty busy at work and I’ve got lots going on at home so there’s not much spare time to work on side projects but I’ve decided to try to implements https support in some form or other into my RYO web server.

I’ve found some basic descriptions of the flows needed, but of course having a description and actually making it work are two totally different things.

 

I’ve also thought of a little application to build to use the REXX SDSF interface although I am just going to do it using native ISPF panels initially to see if I can get it to work.

Categories: Coding, ISPF, REXX

Well that was almost too easy…

April 29, 2010 Leave a comment

My little RYO REXX based web server only handled HTTP GET request which causes problems when browsers cache data to requests that are really dynamic, so I’ve been after changing it to handle POST requests as well for a while but I was concerned about the effort it would take to do it.

Turned out to be about ten lines of additional code.

Of course, the real knack is knowing WHICH ten lines to code!

Fixed my IE issues…

April 28, 2010 Leave a comment

Found an extra comma in the parameter lists I was passing to the dialog function for the confirm delete buttons. Removed that and that got rid of the javascript error.

By adding a DOCTYPE entry to my index page I got the toolbox to position itself correctly.

And by changing the DIVs I was using for the link items in the data set and member lists to SPANs, I was able to remove the forced inline display option I had had to add to make things display on a single line. Seems that DIVs inside anchor (A) tags is not W3C valid anyway. Live and learn!

Gotta love jQuery though for the ease it brings to developing for cross browser platforms. I’ve only ever done a cross browser (IE and FF) application using native javascript once and it was a nightmare to get everything to work correctly.

And so does the trashcan now.

April 25, 2010 Leave a comment

I’ve got the trashcan function working. Just have to be careful I don’t delete something I really want to keep, like all this code!

Next step is to play with the SDSF REXX interface and see if I can build a job monitoring function to track submitted jobs and pop up an message or something when one ends.