An externally-callable Rexx exec that puts up a scrolling progress display of messages from your Rexx exec.
Each line of your Rexx exec that issues a 'say' e.g.
Have you any worked examples?
type TSO MYSAYMNU (or TSO MSAYMENU) to display a menu of examples.
Please study these well-commented code examples. You will learn how (in 1 - 3 lines of code) to do any of:
- Set the 'aboutme' blurb which describes what the Rexx is doing
- Set the occasional status line 'dynamic2'
- Set up a progress histogram, and keep it going when other displays intervene.
- Suppress the review of the progress displays at the end of the run.
- Change the colours of the dynamic areas on the fly.
- Cause a tree of low-level execs which also display via 'mysay' to synchronize with the top-level exec so the displays all flow together.
How does Mysay work in batch?
'Mysay' works in batch like the old 'say' - it simply detects it is in the background and issues a 'say' to SYSTSPRT.
You CAN force mysay to work like the old 'say' in foreground, the main reason I can think of is to demonstrate the
superiority of the new method by switching from 'oldsay' mode to mysay mode while users look on.
Must I hand-edit all my execs to use this?
I provide an edit macro USEMYSAY to convert your Execs to use 'mysay'.
NOTE! Before converting your valuable Execs with macro USEMYSAY, **PLEASE BACK UP YOUR SOURCE**.
You should consider USEMYSAY to be a dangerous piece of crap that could destroy your code, or worse, introduce insidious logic bugs.
See Discussion of pros and cons of USEMYSAY
My application is structured into a tree of execs calling execs. Does 'Mysay' works when low-level execs issue displays?
Yes. See Achieving Smooth Integration of Mysay between Top-Level Execs and Called Execs
How do I initialise mysay?
In low-level execs, you don't. Only top-level execs.
In your top-level exec, YOU MUST call Mysay0 once at the start of the run before any 'mysay's are attempted.
rv=Mysay0("string of variable assignments to be interpreted",top_level_exec_name)
Since it is a top-level exec, you must also TIDY UP after 'mysay' by coding a 'mysay9' call at the end of the run - see below.
What goes into the mysay0 call?
At a bare minimum, the name of the top-level exec. 'Mysay' uses the execname in the name of the dsn it allocates to store displays for later review. If you don't supply execname, then 'mysay' might even fail to display anything at all!
parse source typeofcall execname . /* get the execname from Rexx */
myblurb = execname "runs for a long time and does wonderful stuff. The progress display will keep you informed"
The double quotes and single quotes are required to get the contents of your variable 'myblurb' into the shared pool variable 'aboutme' which is one of many that drives 'mysay'. The blurb string is interpreted to assign a variable called 'aboutme'. This is why the variable you pass cannot exceed 250 chars (the limit for interpret) and also why you cannot include single or double quotes in your blurb. Sorry.
From a usability viewpoint, your progress panel SHOULD have a description or heading explaining what it is that is progressing. So we also need a blurb, description or heading. 'mysay' uses a variable called 'aboutme'.
So the EFFECTIVE minimal call to be both practical AND usable is
If you really need to include quotes or double quotes in your blurb, email me and I'll code a solution. I just have no budget to work on this, so I haven't bothered to do it.
How do I use a different panel from the default 'scroller'?
Sorry for the finicky exactness required for the format of passing these parms. You/I/we COULD create a new exec 'mysay0P' which just sets 'panelid*' in the shared pool for session *. And another 'mysay0A', for setting 'Aboutme'.
I get along with 'mysay0' parameter passing the way I do it, email me if you hate it.
How do I set the 1-deep status line?
Mysay2 sets 'dynamic2', the status line.
rv=mysay2("the text of the message")
This can be done ay any time in the run.
When I set the 1-deep status line, my histogram of asterisks disappears, then reappears.
See example exec TMYSAY7.
You need to inform mysay2 of the number of stars to refresh onscreen. If you don't tell it, it assumes you don't want to refresh the stars (asterisks).
rv=mysay2("the text of the message",numstars) will maintain the histogram with that number of asterisks.
How do I set the colours of the dynamic areas?
The supplied routine MYSAYCOL allows you to set the colours of 'aboutme', 'dynamic1' and 'dynamic2' to any of red, blue, turq, yellow, pink, green, white or whatever the eighth colour may be.
rv=mysaycol('yellow/green/red')will set 'aboutme' to an arresting yellow, the scrolling portion to green, and any status line messages to red. Try TMYSAY5 worked example. (TSO MSAYMENU or MYSAYMNU to invoke the example menu)
You may switch these colours at any time, but don't overdo it!
How do I cause my alternative panel to display in a popup?
You have cloned 'scroller' supplied panel and made a panel 'better'. Your exec when it's 'mysay0' call specified 'scroller' used to run in a popup. When you changed your 'mysay0' call to
and it doesn't run in a popup. What gives?
Answer: Mysay is hardcoded to do 'Addpop' and 'Rempop' if the panelid = 'SCROLLER'. Find this code and add your 'better' to the test.
How do I pause the application (e.g. when an error is detected) to force the user to read a message?
Code rv=presskey('sometext')'Mysay' displays the default message "Press <enter> key to continue" in the scrolling area then waits for the user to press <enter>. Also, rv=presskey("blah")
displays "Blah ... (Press <enter> key to continue...)".
Of course, in Batch, presskey() does not pause.
It would be a GOOD IDEA to also issue a red status-line message to draw his attention to the 'presskey' message (which as of May 2003 goes in with all the other messages, in the same colour as the rest)
rv=mysaycol('green/green/RED') /* set colours of dynamic areas */
rv=mysay2('PROBLEM! Situation X has happened!') /* set status message */
rv=presskey() /* Press <enter> to continue ...*/
How do I pause the application for a few seconds, then continue automatically?
rv=mysay('This message will sit still for 3 seconds before scrolling resumes, for emphasis')
if (sysvar('sysenv') <> 'BACK' then rv=tsowait(3)
Tsowait is one of the rexxes made available to the TSO/ISPF community by some saint.
How do I pause the application but allow the user to do something such as go off and resize a pds?
Code rv=waitmenu('menupanelid')This will force a pause, and the display of that menu.
It is up to you to code the menu! But I suppose you could give the user access to an ISPF Command prompt from where s/he could issue START to start a new ispf session and run whatever process is required to get around the roadblock.
Your rexx code is so MESSY, partly because you set all these pool variables separately suffixed with session number 'zscreen'. Why make your code so complex?
See previous question and answer. If I didn't, suppose your exec invoked 'waitmenu(somemenu)', then from that 'somemenu' your user did a START (new ISPF session) and ran a fixup process which happened to invoke 'mysay'. Then all the 'mysay' pool variables in your original session would be stuffed. By distinguishing them all with the value of 'zscreen', at least they have a chance to survive :-)
How do I slow down these displays so I can read them? (I am debugging/my CPU is too fast/whatever)
Most people would complain it is too slow ...
Type TSO SETPVAR WAITFLAG Y will define a profile variable WAITFLAG and set it to whatever follows (Y in this case)
Then TSO SETPVAR WAITVAL 2
Mysay will now wait 2 seconds (via a call to TSOWAIT) every time it issues a display. To turn this 'feature' off,
TSO SETPVAR WAITFLAG
TSO SETPVAR WAITVAL
will reset these variables to empty. Mysay will then run as fast as it ever does.
Why does my exec scroll the progress messages past, but then the screen clears and I never get to read the last message?
A top-level exec must have a call like this to mysay9.
rv=mysay9()Mysay9 collects the onscreen displays and saved displays and invites the online user to review the displays. It also erases some pool variables, to clean up after itself.
The 'mysay9' call must be placed BY YOU just before the top-level exec exits to TSO, and AFTER the last call to 'mysay'.
USEMYSAY edit macro makes a stab at properly placing this call in monolithic top-level execs (those without internal subroutines) but doesn't even try when the subroutine has internal subroutine structure.
Thus even if you used USEMYSAY to convert your exec (or, expecially if you did) the 'mysay9' call will probably be in the wrong spot or not exist at all. You must then code this one-line call, and your problem will be fixed.
Did you say that my online user can review all the messages issued by 'mysay'?
Yes. All the messages sent to the scrolling panel area may be reviewed at run's end.
Your user will be prompted (to allow them to see the last displays from the run) then <enter> places him/her into the ISPF editor looking at a saved dsn of messages.
Note that the correct placement of a call like this
is essential to achieve this, and edit macro USEMYSAY may not insert the call in the correct place, if it even tries. (It won't try if it can't figure out the spot in your exec just before it finishes executing eg highly structured execs)
- The saved dsns of messages are stored under your user id or prefix.
If your module name is BIGEXEC then look under youruserid.BIGEXEC for dsns containing the saved displays.
I don't delete these datasets, I leave that to you. See the code for MYSTASH to see how the dsn name is constructed.
How do I code my own dynamic panel to take advantage of mysay?
You can write YOUR OWN dynamic panel, let's suppose you call it YOURPANL.
The only mandatory field is 'dynamic1', with any width up to 74 and depth up to 20 or so.
mysay() will detect the width and depth of YOURPANL and use it (via a call to 'getdyn' which does a PQuery).
If YOURPANL additionally contains 'aboutme', you should set it via a call to 'mysay0'.
If it contains 'histo1' (and a little bit of code in the panel )PROC section which you can copy from Scroller)
then you can display a progress histogram on YOURPANL.
If you code a dynamic area 'dynamic2', then mysay() can set that from time to time during the run, 'dynamic2' does
not scroll like 'dynamic1' does, so it serves as a status area.
How do I stop the d*** application? It ignores the ATTention key!
I admit it is not as easy to stop as a 'say' app.
Actually, because I use 'CONTROL DISPLAY LOCK', it is sometimes impossible to stop a 'mysay' exec until it decides it is finished. Often the ATTention key WILL stop a 'mysay' exec, it depends what it is doing.
If you are a developer this may be a godsend, because now when your user runs your exec, it is guaranteed to run to completion (or to an internal abend). The 'say' way, the user has to hit <enter> the exact right number of times. If the user walks away for long enough, a 'say' app can be logged off when the session times out, while displaying '***'. Or the user can abort the run with the ATTention key. Do you want that? NO! Make them watch your work and appreciate the great technical care that has gone into the product.
As a developer, if you have discovered an infinite loop in testing, you can always kill your VTAM session , LOGOFF, and LOGON again.
You know that the same effect could easily be coded as an routine, not external like 'mysay'?
Yep. One effect at least. But in the absence of Global variables in Rexx, the inline solution fails.
See Good reasons why Mysay is an external routine
Defects and TO-DOs
- Quite often, you can't cleanly halt a MYSAY program that is putting out thousands and thousands of lines of mysay output.
You have to kill your ISPF session to stop it. Why? I suppose it is due to
'CONTROL DISPLAY LOCK'. If you know a way to code around this locking of the screen, please share that knowledge with me. (Other times it kills fine)