![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() | ![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
Tandem Connect User's Guide
CHAPTER 4
An action is similar to a programming statement in that it takes input in the form of parameters and performs specific tasks. Please see the chapters in the Composer User's Guide devoted to Actions.
Within the Tandem Component Editor, a set of instructions for processing XML documents or communicating with non-XML data sources is created as part of an Action Model. The Action Model performs all data mapping, data transformation, data transfer between hosts and XML documents, and data transfer within components and services.
An Action Model is made up of a list of actions that work together. As an example, one Action Model might read invoice data from a disk, retrieve data from a host inventory database, map the result to a temporary XML document, make a conversion, and map the converted data to an output XML document.
The Action Model mentioned above would be composed of several actions. These actions would:
The Tandem Connect includes two actions that are specific to the Tandem environment: Check Screen and Send Buffer.
The purpose of these actions is to allow the Tandem component (running in a deployed service) to replicate, at runtime, the terminal/host interactions that occur in a terminal session. The usage and meanings of these actions are described in further detail below.
The Send Buffer action encapsulates "keystroke data" (whether actually obtained from keystrokes, or through a drag-and-drop mapping, or via an ECMAScript expression built with the Expression Builder) that will be sent to the host in a single transmission at component execution time. When the Send Buffer action executes, the buffered data are sent to the host in the form of a properly Tandem 6530-escaped byte stream. Send Buffer actions should always be preceded by a Check Screen action (see next section).
The Send Buffer action can be created in several ways:
In Record mode, just begin typing after a Check Screen action has been created. Keystrokes are automatically captured to a new Send Buffer action.
Right-mouse-click anywhere in the Action Model; a contextual menu appears. Select New Action and Send Buffer.
In the main menu bar, under Action, select New Action and Send Buffer.
To
create a Send Buffer action using menu commands:
Right-mouse-click anywhere in the Action Model and select New Action, then Send Buffer, from the contextual menu (or use the Action menu as described above). The Send Buffer dialog will appear.
To map a DOM element's contents to the buffer, click the XPath radio button, then select a DOM from the pulldown list and type the appropriate XPath node name in the text area (or click the Expression icon at right and build the node name using the Expression Builder).
To specify the buffer's contents using ECMAScript, click the Expression radio button, then use the Expression Builder dialog to create an ECMAScript expression that evaluates to a string.
To specify the contents of the buffer manually (by typing a string into the text field), first check the Accept Key Strokes checkbox, then begin typing. The Expression radio button will become selected automatically and every key you press will be entered into a quoted string in the text area. Control keys (arrow keys, function keys, etc.) will automatically be translated to the appropriate escape sequences. (See discussion below.)
When you are in "Accept Key Strokes" mode, normal editing of text via backspacing, cut/paste, etc. is not possible, since every keystroke is captured to the dialog as an escaped string-literal value. For example, if you hit the F11 key, a value of "<F11>" will be appended to the string buffer, instead of the previous character being deleted. This may not be what you want.
To edit the buffer contents directly (using cut, paste, backspace, and so on), first uncheck the Accept Key Strokes checkbox. Then edit your text. To return to key-capture mode, check Accept Key Strokes. Any additional keystrokes will then be translated to escape sequences and appended to the existing text.
On some occasions, you may wish to enter a key sequence manually. You can do this by unchecking Accept Key Strokes and typing the value in question anywhere in the current text string. If you don't know the key sequence for a given control key or function key, you can find it by clicking the Expression icon to the right of the text area (which brings up the Expression Builder dialog) and then double-clicking the appropriate control-key entry in the picklist in the upper part of the Expression Builder dialog.
If you want to know what a given key sequence means in plain English, simply select (highlight) the key sequence(s) of interest and let the mouse hover over the selection. See below.
A hover-help box will appear, containing the escape sequence's plain-English translation. For example, in the graphic above, the key sequence "03031922< F11>" has been highlighted and the mouse is hovering over the selection. The hover-help box shows the key sequence translates to "03031922< F11>".
If a group of key sequences is selected, you will see (in the hover-help box) all character equivalents, wrapped in angle brackets.
All special (non-printing) keys and their terminal equivalents are listed in See "Tandem Keyboard Equivalents" on page 131. in Appendix B.
When a Send Buffer action is created, the keystrokes that are captured in real time are displayed in the Action Model either as plain alphanumeric values or as a string which represents the key name in angle brackets. For example, an up arrow will be translated into <up> and F7 will be translated into <f7>.
Backspace and delete keystrokes are also represented as strings. Therefore, if you wish to correct typos in your Send Buffer action, you may want to doubleclick the action in the Action Model (which brings up the Send Buffer dialog) and edit the buffer string by hand.
Because of the latency involved in terminal sessions and the possibility that screen data may arrive in an arbitrary, host-application-defined order, it is essential that your component can depend on the terminal screen being in a given state before it operates on the current screen data. The Check Screen action makes it possible for your component to stay "in sync" with the host. You will manually create Check Screen actions at various points in your Action Model so that precisely the correct screens are acted on at precisely the right time(s).
To create a new Check Screen action, you can do one of the following:
Click on the "Create Check Screen Action" button on the main toolbar, or
Perform a right mouse click inside the action list, then select New Action and Check Screen from the contextual menu, or
In the component editor's main menu bar, select Action, then New Action, then Check Screen
While you are in Record mode, with your cursor in the Native Environment Pane, right-click then select Check Screen.
NOTE: You will most often use the toolbar button when you are in Record mode.
To create a
Check Screen action using a menu command:
With your cursor positioned in the Action Model on the action item after which you want your new item to appear, perform a right mouse click. Then select New Action and Check Screen from the contextual menu (or use the Action menu in the main menu bar as described above). The Check Screen dialog appears.
Click one of the three radio buttons (Cursor position, Prompt, or Expression), depending on how you want to specify the go-ahead (screen readiness) criterion. (The default is "Cursor position.") See discussion below.
Specify a Timeout value in milliseconds. (See discussion further below.)
Specify a Min wait value in milliseconds. (See discussion further below.)
It is important that the execution of actions in your Action Model not proceed until the host application is ready, and all screen data have arrived (that is, the screen is in a known state).
Your component must have some way of "knowing" when the current screen is ready. The Check Screen Action is how you specify the readiness criteria.
The purpose of the Check Screen Action dialog is twofold:
It allows you to specify a wait time for program synchronization.
It allows you to specify an expression which will be used as a criterion to judge whether the screen is in a state of readiness at execution time.
These factors are discussed in some detail below. Be sure to read and understand the following sections before creating your first Tandem Component.
You can base readiness on the location of the terminal's cursor. Simply enter the row and column number of the cursor's "prompt position." (The values shown in the Row and Column fields of the dialog will always automatically default to the cursor's current position. You will normally not have to enter the numbers manually.)
The current prompt position can be specified on the basis of the character string that immediately precedes the cursor position in the terminal emulation window. For example, the prompt may say "Choose one: (A, B, C, D)". In this instance, you could specify "Choose one: (A, B, C, D)", or "(A, B, C, D)", or perhaps simply ")", as the go-ahead prompt. (The default value shown for the prompt string will be the current screen contents for the line in which the cursor is positioned. The default string will include all characters from the beginning of the prompt line up to and including the last space character, if any, preceding the cursor.)
It is possible that the prompt position or prompt text could vary dynamically at runtime. For the ultimate flexibility in determining the go-ahead criterion, you can click the Expression radio button in the Check Screen Action dialog and enter an ECMAScript expression in the associated text field. At runtime, if the expression evaluates as "true," the screen will be considered ready; but not otherwise.
Expressions are discussed in detail in the heading titled "Tandem-Specific Expression Builder Extensions" below.
The timeout value (in milliseconds) represents the maximum amount of time that your component will wait for screen data to both arrive and meet the readiness criterion specified in the top part of the dialog. If the available screen data do not meet the readiness criteria before the specified number of milliseconds have elapsed, an exception is thrown.
NOTE: Obviously, since the latency involved in a terminal session can vary greatly from application to application, from connection to connection, or even from screen to screen, a great deal of discretion should be exercised in deciding on a Timeout value. Careful testing of the component at design time as well as on the server will be required in order to determine "safe" timeout values.
The default Timeout value will vary depending on whether you are in Record mode or you are merely creating Actions manually. In Record mode, the default Timeout value is a calculated value based on the actual time that elapses between the last operation and the loading of the new screen. (The value displayed in the dialog is twice this "observed load time," rounded up to the nearest full second.) When you are creating a Check Screen action manually (not in Record mode), the default value is 1500 milliseconds.
The Min Wait time (in milliseconds) represents the amount of time your component should wait before the initial check of the screen buffer. For example, if you specify a Min Wait of 500, your component will check the screen for readiness (according to the criteria you specified) after waiting 500 milliseconds. If the go-ahead criteria are met, the screen will be rechecked after another 100 milliseconds. Only if the second check is also good will execution of the component proceed. If not, the screen will be rechecked at 100-millisecond intervals until the Timeout value (above) has been reached. At that point, if the screen still does not meet readiness requirements, an exception is thrown.
NOTE: Every Check Screen action checks the screen a minimum of two times. Go-ahead doesn't occur unless two consecutive checks are passed.
The default value for Min Wait is 50 milliseconds. But regardless of the Min Wait time, the screen will be checked one final time at the expiration of the Timeout period, so that even if the Min Wait time is greater than the Timeout value, the screen will still be checked once.
The easiest way to create an Action Model for your component is to use Record mode. When you build an Action Model in this way a new Send Buffer action is created for you automaticallyas soon as you begin typing or drag an element from the Input DOM into the appropriate field onscreen. This makes it easy to build an Action Model, since all you have to do is click the Check Screen button, begin typing (or drag an element from the Input DOM into the prompt area onscreen), wait for the next screen to arrive from the host, click Check Screen, begin typing (or dragging), etc., repeatedly. In this fashion, a sequence of Check Screen and Send Buffer actions can be built very quickly and naturally.
When a Send Buffer action has been created automatically for you, all of your subsequent keystrokes will be captured to the buffer until one of the following occurs:
Working in record mode will be discussed further below in the section entitled "Recording a Tandem Session".
The Connect for Tandem exposes a number of Tandem-specific ECMAScript global variables and object extensions, which are visible in Expression Builder picklists. The Tandem-specific items are listed under the node labelled "Tandem." There are three child nodes: Login, Screen Methods, and Keys. See illustration below.
Tandem Connection Resources have two global variables that are accessible from Expression Builder dialogs: the USERID and PASSWORD. These properties (available under the Login node of the picktree) specify the User ID and Password values that may be requested by the host system when you connect. You can map these variables into the terminal screen, which eliminates the need for typing user and password information explicitly in a map action.
NOTE: You can also create a Send Buffer action where the XPath source is defined as $PASSWORD.
When an Expression Builder window is accessed from a Map or Function action in the Tandem Component, the picklists at the top of the window expose special Tandem-specific ECMAScript extensions, consisting of various methods of the Screen object and predefined escape sequences corresponding to various "special keys" on the virtual terminal's keyboard.
Hover-help is available if you let the mouse loiter over a given picktree item. (See illustration.)
In addition, you can obtain more complete online help by clicking Help in the lower left corner of the dialog.
The Screen object offers methods with the following names, signatures, and usage conventions:
This method will return the display attribute value of the character at the screen position given by nRow, nColumn. The complete set of possible display attribute values is listed in Appendix C. An example of using this method is:
if (Screen.getAttribute( 5, 20 ) == 1) // if character at 5, 20 is bold
// do something
This method returns the current column position of the cursor in the Tandem terminal emulator screen (Native Environment Pane). Column positions are one-based rather than zero-based. In other words, in 24x80 mode, this method would return a value from 1 to 80, inclusive.
This method returns the current row position of the cursor in the Tandem terminal emulator screen (Native Environment Pane). Row positions are one-based rather than zero-based. In other words, in 24x80 mode, this method would return a value from 1 to 24, inclusive.
This method returns the native column-width dimension of the current screen. (Due to possible mode changes in the course of host-program execution, this value can change from screen to screen. Do not depend on this value staying constant over the life of the component.) When the program is in 24x80 mode, this method will return 80. To retrieve all of the contents of row 15 of the current screen, regardless of its native dimensions, you could do:
var myRow = Screen.getTextAt( 15, 1, Screen.getMaxColumn() );
The getPrompt()
method returns the string representing all characters in the cursor's row, starting at column 1 and continuing to, but not including, getCursorColumn()
—in other words, everything from the beginning of the line to the cursor position. (This is the same as the default prompt string shown in the Check Screen dialog.) Example:
var thePrompt = Screen.getPrompt();
if (thePrompt().toLowerCase().indexOf("password") != -1)
Screen.setText(PASSWORD);
This method returns the native vertical dimension of the current screen. (Due to possible mode changes in the course of host-program execution, this value can change from screen to screen. Do not depend on this value staying constant over the life of the component.) When a program is in 24x80 mode, this method will return 24. To loop over all rows of a screen, regardless of its native dimensions, you could do:
for (var i = 1; i <= Screen.getMaxRow(); i++)
{
var myRow = Screen.getTextAt( i, 1, Screen.getMaxColumn() );
// do something with myRow
}
This method returns the string of characters (of length nLength
) that occurs in the Screen object at the byte offset given by nOffset
. Note that the offset is one-based, not zero-based. Thus, to obtain all of a 24 x 80 screen as an ECMAScript String, you would do:
var wholeScreen = Screen.getText( 1, 24 * 80 );
Any attempt to obtain character data beyond the bounds of the screen buffer will result in an exception. For example, the following call will fail:
var wholeScreen = Screen.getText( 1, 1 + 24 * 80 ); // ERROR!
This method returns an ECMAScript String that represents the sequence of characters (of length nLength
) in the current screen starting at the row and column position specified. Note that nRow
and nColumn
are one-based, not zero-based. A zero value for either of these parameters will cause an exception.
To obtain all of row 20 of a 24x80 screen, you would do:
var myRow = Screen.getTextAt( 20, 1, 80 );
The getTextAt()
technique is used internally in drag-and-drop Map actions involving screen selections created as described in "Selecting Continuous Data" further below.
This method returns a single String consisting of substrings (one per row) comprising all the characters within the bounding box defined by the top left and bottom right row/column coordinates specified as parameters. So for example, in 24x80 mode, you could obtain the upper left quarter of the screen by doing:
var topLeftQuadrant = Screen.getTextFromRectangle(1,1,12,40);
The getTextFromRectangle()
method is used internally in drag-and-drop Map actions involving rectangular screen selection regions created using the Shift-selection method (see "Selecting Rectangular Regions" below).
Note that the string returned by this method contains newline (\u000a) delimiters between substrings. That is, there will be one newline at the end of each row's worth of data. The overall length of the returned string will thus be the number of rows times the number of columns, plus the number of rows. For example, Screen.getTextFromRectangle(1,1,4,4).length
will equal 20.
The setText()
method allows you to send data to the screen (and therefore the host application) programmatically, without explicitly creating a Send Buffer action. Example:
var myPhone = "(203) 225-1800";
if (Screen.getPrompt().indexOf("Phone") != -1)
Screen.setText( myPhone + "\r" ); // send string + CR
The Keys node of the Tandem-specific picktree in the Expression Builder dialog has child nodes labelled Common Keys, NumPad Keys, Control Keys, and Other Keys. These keys were discussed in detail in "About Tandem Keyboard Support" on page -24. By double-clicking the picklist items under these categories, you can automatically generate the key string for any non-printing characters, special keys and function keys you wish to transmit to the host. The detailed contents of these picktree items can be found in Appendix B.
There are two main ways of selecting data on the terminal screen (in the Native Environment Pane) at design time, for purposes of dragging out. One method selects text in a continuous stream, from one screen-buffer offset to another; the other method selects text in an arbitrary onscreen bounding box or region.
When you drag across multiple rows of data without holding the Shift key down, all characters from the initial screen offset (at the mouse-down event) to the final screen offset (at mouse-up) are selected, as shown in the graphic below. (The selected text is "reversed out." A partial row has been selected, followed by three complete rows, followed by a partial row.)
As indicated in the component editor window's status line (lower left), the selection in the above example actually begins at row 5, column 26, and ends at row 9, column 35. If you were to drag this selection out of the Native Environment Pane, into a DOM, a Map action would be generated as follows:
Notice that the getTextAt()
method is used. This means the captured screen characters form one string, which is mapped to Output/Inquiry/Response/Info. No newlines or other special characters are inserted into the string. (Areas of the screen shown in black are simply represented as space characters in the string.)
Sometimes you may not want the selection behavior described above. In certain cases, screen data may be grouped into zones with their own natural boundaries. For example, in the screen shown previously, there is a box two-thirds of the way down the screen containing information on the availability of a given book. You may want to capture (for drag-out purposes) just the data enclosed within this particular rectangular region on the screen. To do this, first hold the Shift key down, then drag your mouse across the portion of the screen that you want to select. The selected area is highlighted and the appropriate row/column start and end points are displayed in the status line of the component editor's window, as below:
In this instance, when you drag the rectangular highlight region out of the Native Environment Pane, into a DOM, the resulting Map action uses the getTextFromRectangle()
method described on page 45.The resulting action looks like:
This method operates in a different fashion from getTextAt()
, because the string returned by getTextFromRectangle()
is wrapped at the rectangle's right edge. Newlines are inserted at the wrap points as discussed in the API description of getTextFromRectangle()
, further above.
The Tandem Component differs from other components in that a major portion of the Action Model is built for you automatically. This happens as you interact with the host in the Native Environment pane as part of a live Tandem terminal session. Composer records your interactions as a set of auto-generated actions in the Action Model. Typically, in other exteNd Composer components (such as a JDBC Component), you must manually create actions in the Action Model, which then perform the mapping, logging, transformation, communication, and other tasks required by the component or service. By contrast, when you create a Tandem Component, you record requests and responses to and from the host, which end up as actions in the Action Model. In addition, you can add standard actions (Map, Log, Function, etc.) to the Action Model just the same as in other components.
NOTE: In order to successfully build a Tandem Component, you should be familiar with the Tandem 6530 commands and the specifics of the application you intend to use in your XML integration project.
The following example demonstrates several common tasks that you will encounter in building Tandem Components, such as:
Drag-and-drop mapping of Input DOM elements to Tandem-screen prompts
Drag-and-drop mapping from the Native Environment Screen to the Output DOM
The use of ECMAScript expressions to manipulate Screen object elements
In the following example, we start with an input XML document that contains the title and author of a book. The goal of our Web Service is to do an author search online, using the terminal app, to see if a book by the given title exists in the library system. If so, we retrieve its ISBN (International Standard Book Number) code in an Output DOM. Whether we succeed or not, we insert an appropriate status message in the Output DOM.
Create a Tandem Component per the procedure shown on page 19 of the previous chapter.
Once created, the Tandem Component Editor window appears, with the words "Tandem Terminal Emulation" in the center of the Native Environment Pane, indicating that no connection has yet been established with a host.
Click the Record button. You are automatically connected to the host that you selected in the Connection Resource for the component. An input screen appears in the Native Environment pane as shown below.
Click the Create Check Screen Action button in the toolbar. A new Check Screen action appears in the action list. It defaults to a go-ahead condition based on the current cursor position (which we assume will always be 21,56 on this screen, with every future execution of this component—an assumption worth questioning). We will tentatively accept the default Timeout of 1500 milliseconds for this Check Screen action, since the CONSULS program has a relatively quick response time. (Even so, careful testing of the component should be done in order to verify that this timeout value is safe.)
Type the letter A (for Author) in the input screen of the Tandem environment pane. A new Send Buffer action appears automatically in your component's action list. Notice that the `A' you typed is already in the action.
NOTE: Terminal commands are often case-sensitive and should generally be entered in ALL CAPS.
In this part of this particular host application, merely typing a single character (without hitting Enter or Return) causes a new screen to appear. The host, in other words, processes the typed character immediately. This is a common terminal idiom. You will not always need to hit Return or Enter to get to a new screen.
In response to `A', the host program sends the new screen shown above.
Because we wish to terminate the Send Buffer action and go on to interact with the new screen, you should click the Check Screen button in the toolbar, at this point, to allow the component to "sync" our next action with the current screen. Click the Create Check Screen Action button now. The new Check Screen action appears in the action list.
NOTE: Were you to simply start typing your next command at this point (without first creating a new Check Screen action), the command would be appended to the still-active Send Buffer. In essence, you would be creating a "type-ahead" buffer. At runtime, the buffer (containing two sets of screen commands concatenated together) would be sent all at once. While this would work okay in this particular program, the type-ahead technique could fail in other real-world terminal programs. Therefore, use caution when deliberately overloading a Send Buffer action. A "best practices" approach is to create a new Check Screen action for every new screen that appears during your session.
Drag the BOOKINQUIRY/AUTHOR/LASTNAME node from the Input DOM to the cursor position in the Native Environment Pane. "Clancy" (without quotation marks) appears in the prompt zone and a new Send Buffer action appears automatically in the Action Model.
NOTE: This terminal application is expecting the author's name to be provided as Last Name followed by First Name (with a space in between). Hence, we dragged the LASTNAME element first.
Hit the spacebar on your keyboard. Notice that a space character is added to "Clancy" in the Native Environment Pane. Also, a new Send Buffer action is created containing just the space character.
Drag the BOOKINQUIRY/AUTHOR/FIRSTNAME element from the Input DOM to the cursor position in the Native Environment Pane. "Tom" (without quotation marks) appears after "Clancy " in the prompt zone and a new Send Buffer action appears in the Action Model.
Note that the terminal screen has not changed (the host has not acted on our input), because it is waiting for Return or Enter. Press Enter to tell the host that our query string (the author's name) is complete. A new Send Buffer action appears, containing <enter>, and the Native Environment Pane updates to reflect the query results.
Click the Create Check Screen button in the toolbar. A new Check Screen action appears, with a default go-ahead condition based on the cursor location of row 24, column 38. (Row 24 is the bottom row and column 38 is about halfway across the 80-column screen; see screenshot above.) There is no need to change the Check Screen default in this case.
In the Native Environment Pane, select the terminal-screen text in row 2, from column 2 to column 18, by clicking and dragging the mouse.
NOTE: Notice that as you click and drag, the onscreen row/column coordinates of the selected area are displayed in the status line of the component editor window (lower left corner).
Lift your finger off the mouse button and place the mouse over the selected text. A finger cursor will appear. Click-drag the selection to the Output DOM InquiryResponse/Status node. The selected text is inserted into the DOM at the desired location, and a new Map Action is generated in the Action Model automatically.
In the example above, the goal is to find the ISBN (International Standard Book Number) information for the book we're interested in and map it into the Output DOM. Therefore, when the application shows the result of your author search, you need to scan that screen, looking for the book title in question. If the title exists, our next action should be to send the corresponding line number, which will cause the application to display a new screen showing detailed information (including ISBN) for the book.
By simple visual inspection of the terminal emulator screen (see previous illustration), it's easy to see that Tom Clancy's Debt of Honor is listed as line-item number 3 in the search-results screen. But this only holds true for this particular search. A search on a different author/title combination might yield a hit at a different line position. (Or if Tom Clancy writes more books, Debt of Honor could assume a different listing position.) To determine the line position of the book at runtime, we should iterate through lines 4 through 11 of the terminal screen, searching for the string stored in the BOOKINQUIRY/TITLE node of our Input DOM. The next example shows how to do this, building on the previous example.
To search for a data item one row at a time:
At the bottom of the Action Model, add a new Repeat While action. (Perform a right-mouse-click, then select New Action, Repeat, and Repeat While.) The Repeat While dialog appears.
In the While text-entry box, type an expression representing the loop-termination condition you wish to apply to this loop. In this case, our condition involves a check of the index variable, rowIndex
. We will be checking 8 rows of screen data in all.
In the Index Variable text-entry area, enter the name of your index variable (in this case, rowIndex
).
Since we are only retreiving a single value (one book) from the screen, we do not need to fill in the optional Target portion of the dialog. Therefore, just click OK. A new Repeat While action is added to the component's Action Model.
In this example, we're looking for a specific string within a given row. If the string is found, we will take several actions, then break out of the loop. We will perform our row parsing and string search within a Decision Action. Create a new Decision Action by clicking the right mouse button and selecting New Action > Decision from the contextual menu. The Decision Action dialog appears.
Enter a Decision Expression. In this example, the three-line expression is:
var myRow = Screen.getTextAt(rowIndex+4, 1, 80).toLowerCase();
var bookTitle = String(Input.XPath("BOOKINQUIRY/TITLE")).toLowerCase();
myRow.indexOf( bookTitle ) != -1
The first line uses the Screen object's getTextAt()
method (see page 41) to retrieve the 80 characters of data (i.e., one full line, in a 24x80 terminal screen) at rowIndex + 4
. We add an offset of 4 to the index variable because our search of screen data should begin at row 4 and continue through row 11. (The index variable itself will have values from 0 to 7. The loop terminates when rowIndex
reaches 8.)
The second line of code above simply retrieves the book title as a lowercase string from the Input DOM. (Notice that because we don't want our search to be case-sensitive, we force both strings—the query string and the target-object string—to be lowercase.)
The final line of code is the actual "condition check." It relies on the core-ECMAScript String method indexOf()
, which returns –1 when the argument string is not a substring of the string on which the method is being called.
In the TRUE branch of the Decision Action, create a new Send Buffer action. (Right-mouse-click, then choose New Action > Send Buffer from the contextual menu.) The Send Buffer dialog appears.
Click the Expression radio button and then enter an ECMAScript expression in the text-edit area. In this example, we've entered:
var item = Screen.getTextAt( rowIndex + 4, 1,10);
var regex = new RegExp("\\d+");
The first line retrieves the first ten characters of data in the "hit" row using the getTextAt()
method. Within this string, we want the first substring of numeric characters, representing the line number of the book (i.e., 3). One way to extract this substring is with the ECMAScript String method, match()
, which takes a regular expression object as an argument. On success, this method returns an array, of which the zeroth item is the matched text. Our regular expression consists of backslash-d followed by a plus sign, which means "one or more digit characters in a row."
NOTE: The RegExp constructor takes a String argument, in which backslashes that are to appear as literal backslashes "must be escaped with a backslash."
The net result of these lines of ECMAScript is that the number preceding the book title in the target row (namely, `3') is supplied to the host application via a Send Buffer action. No newline need accompany the number `3'. Upon receiving this number, the host application will immediately send back a new screen giving detailed information about the indicated book, as shown below.
Create a new Check Screen action by performing a right-mouse-click and selecting New Action > Check Screen from the contextual menu. The Check Screen dialog appears.
Select the Expression radio button and enter "true" in the text-edit area. Set a Min wait value of 100, which (in this case) we know from experience is generous.
NOTE: The combination of "true" and 100 means we will automatically accept any screen data that get sent within 100 milliseconds.
Create a new Function Action. (Right-mouse-click: select New Action > Function.) In this action, we will retrieve the first ISBN number on the page, if one exists, and store it into an ECMAScript global.
The expression we will use is:
this.isbn = "Not found"; // set up global
var screen = Screen.getText( 1, 24 * 80 ); // fetch whole screen
if (screen.indexOf('ISBN') != -1) // if `ISBN' occurs, get it
this.isbn = lTrim( screen.split('ISBN')[1] ).split(' ')[0];
The first line above simply declares and initializes an ECMAScript global variable (which, on success, will be overwritten with a valid ISBN value).
The second line of code retrieves the entire screen buffer as a string and places it in a local variable, text
. (We assume here that we're in 24x80 mode.)
The third line checks the screen buffer to see if "ISBN" occurs in it. If so, we split the buffer into an array of substrings using "ISBN" as the delimiter. The array member at index 1 will contain the ISBN number, trailed by a partial screen's worth of information (and possibly containing one or more leading space characters). The custom ECMAScript function lTrim()
is used to trimming leading spaces, while the split method is again employed to break our string into an array of substrings, assuming spaces to be the delimiters. The zeroth item of this final array is the ISBN string that we're looking for. See the series of graphics below.
On finding the information we're looking for, we no longer need to iterate through line items. Therefore, create a Break Action to break out of the loop. (Right-mouse-click; New Action; Break.)
Create a Map action that maps this.isbn
to the InquiryResponse/ISBN node of the Output DOM.
The completed Action Model looks like this:
You will encounter times when you need to edit a previously recorded action model. Unlike the situation with other components, editing a Tandem Component requires extra attention. When a Tandem Component executes, it plays back a sequence of actions that expect certain screens and data to appear at certain times in order to work properly. So when editing a component you must be careful not to make the action model sequence inconsistent with the host program execution sequence you recorded earlier.
In general, to ensure successful edits, the following recommendations apply:
Exercise extreme care when using Cut, Copy, and/or Paste to delete, move, or replicate actions in your Action Model. Actions that were created automatically during a "Record" session will often create data dependencies that are easily overlooked in the editing process.
When you need to use drag-and-drop to add new Map actions to your Action Model, click the Start Animation button in the Action Pane toolbar and step to the line of interest in your Action Model; then Pause animation and turn on Record mode. At this point, you can safely drag to and from the screen. Following this procedure will prevent your Action Model from getting out of sync with the host or conflicting with previously mapped DOM data.
The following procedure will explain how to change an existing action in a previously recorded session.
To Change an existing action in a previously recorded Action Model:
Open the component that includes the Action Model you'd like to edit. The component appears in the Tandem Component Editor window.
Navigate to the action in the Action Model where you'd like to make your edit and highlight the action.
Click the Toggle Breakpoint button (or press F2). The highlighted action becomes red.
Click the Start Animation button. The animation tools (in the Actions pane's toolbar) become enabled.
Click the Step to Breakpoint/End button. The Action Model executes all of the actions from the beginning of the Action Model to the breakpoint you set in step 3 above.
Perform any additional drag-and-drop (or other) actions that you'd like to make to the Action Model.
The following procedure explains how to add a new action in a previously recorded session.
To Add a Action to a previously recorded Action Model:
Open the component that includes the Action Model you'd like to add an action in. The component appears in the Tandem Component Editor window.
Navigate to the action in the Action Model where you'd like to make your addition and highlight the action.
Click the Toggle Breakpoint button (or press F2). The highlighted action becomes red.
Click the Start Animation button. The animation tools (in the Actions pane's toolbar) become enabled.
Click the Step to Breakpoint/End button. The Action Model executes all of the actions from the beginning of the Action Model to the breakpoint you set in step 3 above.
Use Composer's drag and drop features to add new Map actions that interact with the screen. The new action will be added directly under the highlighted line.
If you are adding Map Actions in a loop that are alias perform the following steps:
To Add an Alias Action to a previously recorded Action Model:
From the Action menu, select New Action, then Map. The Map Action dialog box displays.
Select the Expression for Source, and the dropdown box is grayed out.
Either type in the information, or click the Expression Builder button and create a new expression.
Create an XPath to be represented by the alias. Click from the dropdown list for the alias.
The new action is inserted below the line you select. (New line is highlighted in the screen below to show it was inserted.
The following procedure explains how to delete an action in a previously recorded session
To Delete an Action to a previously recorded Action Model:
Highlight the action line that you want to delete and click on the RMB and select Delete from the menu. You may also highlight the line and press the Delete button on your keyboard.
Composer includes animation tools that allow you to easily test your component. On the Tandem Component Editor tool bar you'll find the Execute button, which allows you to execute the entire Action Model and verify that your component works as you intend. It is important to test a newly created Tandem Component to be sure that Timeout values in all Check Screen actions are appropriate and that Send Buffer and other actions work as intended.
To execute a Tandem Component:
Open a Tandem Component. The Tandem Component Editor window appears.
Select the Execute button. The actions in the Action Model execute. If the component executes successfully, a message appears as follows.
After executing the component, you may want to doublecheck the contents of your DOMs to be sure all of the appropriate data mappings occurred as expected. To make all data elements visible, select Expand XML Documents from the View menu. This expands all of the parents, children, data elements, etc. of the DOM trees, so that you can easily see the results of execution of the component.
In the Action Model, you'll find animation tools that allow you to test a particular section of the Action Model by setting one or more breakpoints. Using these tools, you can run through the actions that work properly, stop at the actions that are giving you trouble, and then troubleshoot the problem actions one at a time.
The following procedure is a brief example of the functionality of the animation tools. For a complete description of all the animation tools and their functionality, please refer to the exteNd Composer User's Guide.
To run a Tandem Component using Animation Tools:
1. Open a Tandem Component. The component appears in the Tandem Component Editor window.
NOTE: Animation and Recording are mutually exclusive modes in the component. In order to record during animation, you must either pause, or stop animation and then turn on record mode.
Click the Start Animation button in the Action Model tool bar, or press F5 on the keyboard. All of the tools on the tool bar become active, and a connection is established with the host. The Native Environment Pane becomes active.
Click the Step Into button. The first Check Screen action becomes highlighted.
Click the Step Into button again. The Check Screen action (above) executes and the next action becomes highlighted.
Click the Step Into button repeatedly to execute actions one-by-one.
Click other buttons (Step Over, Run To Breakpoint, Pause, etc.) as desired to control the execution of the component. Note that you can set a breakpoint at any time during execution by clicking the mouse on an action line and hitting F2 or using the Set Breakpoint button.
The following tips may be helpful to you in building reliable Tandem Components.
Always precede a Send Buffer action with a Check Screen action.
Aways follow a Send Buffer action with a Check Screen action.
In Check Screen actions, accept the default go-ahead condition (based on cursor position) only when you are certain that the absolute cursor position will always be constant for the given screen. Many times, it is safer to write a custom expression.
A fast, accurate way to create a prompt-based Check Screen action during recording is to highlight (select) the characters of interest immediately preceding the cursor (up to but not including the cursor position), then click the right mouse button and select Check Screen. This automatically creates a Check Screen action based on the prompt you highlighted.
When typing a custom prompt string under Prompt (in the Check Screen dialog), remember to escape any quotation marks that might appear within the prompt string.
Avoid using Check Screen go-ahead criteria based on variable information, such as dates, times, etc.
Avoid Check Screens that do nothing but wait a specified period of time using the Min Wait setting. While this technique may work, it can create significant performance bottlenecks.
Remember that the default Timeout values used in Check Screen actions are calculated from actual response times during the design session. This has a couple of implications. First, the default Timeout value may need to be increased, for load-sensitive applications. Secondly, deleting a Check Screen action may cause synchronization timeouts on subsequent executions. Careful testing will reveal these sorts of problems.
When disjoint go-ahead criteria come into play, such as when the middle of a screen remains constant during a repaint but the first and last lines change, you may want to create two Check Screen actions then combine them into one action that's based on an expression.
In addition to the Check Screen and Send Buffer actions, you have all the standard Basic and Advanced Composer actions at your disposal as well. The complete listing of Basic Composer Actions can be found in Chapter 7 of the Composer User's Guide. Chapter 8 contains a listing of the more Advanced Actions available to you.
In testing a Tandem Component, you may encounter errors relating to Check Screen and/or Send Buffer actions. The result is a dialog similar to the following:
This section discusses possible error conditions and how to deal with them.
Most of the errors you are likely to encounter at execution time will be related to Check Screen actions. It is important to realize that every one of the Check Screen errors discussed below is a timeout error. If one of the errors described below occurs, it means that the go-ahead criteria you specified in the Check Screen setup dialog were not met within the Timeout period. Therefore, you should first try to determine whether slow host response might be the real problem (in which case, the solution is to increase the Timeout value for the Check Screen action in question). If the error still occurs after the Timeout value has been increased, then you can be sure the error is due to an incorrect or inappropriate go-ahead condition in your Check Screen action.
The following paragraphs describe typical error messages and their meanings.
This error means that the Check Screen failed because the cursor was not at the expected location at the expiration of the Timeout period. Perhaps the host application changed, or the prompt line may be varying dynamically in some way that you weren't anticipating, etc. It's also possible, as explained above, that the Check Screen simply "timed out" for reasons having to do with heavy host load or a bad connection. Try increasing the Timeout value for the given Check Screen action. If that doesn't help (or if you suspect that the problem involves an inappropriate choice of go-ahead criteria), try rewriting the Check Screen go-ahead condition based on something other than fixed cursor coordinates. For example, specify a prompt string, or use an Expression to validate the screen contents in some way.
This error means that the Check Screen failed because the prompt was not identical to the specified (expected) prompt string prior to the expiration of the Timeout period. The prompt line may be varying dynamically in some way that you weren't anticipating. Or (as explained above) the host response time may simply have increased unexpectedly due to heavy load or other factors. If you suspect that host latency is a problem, try increasing the Timeout value for the Check Screen action. Otherwise, rewrite your Check Screen go-ahead criteria to be based on something other than a hard-coded prompt value. For example, specify an Expression that validates the prompt in some way.
This error happens when the Check Screen go-ahead is based on an ECMAScript expression and the expression happens to evaluate as false at execution time. Once again, it's important to realize that this sort of error can be triggered simply on the basis of slow host response (timeout). When the host is slow to respond, it means that your ECMAScript expression will be evaluated on the basis of whatever is in the screen buffer as of the moment of timeout. If no data (or insufficient data) have arrived, the expression is bound to evaluate as false.
To fix this sort of problem, either increase the Timeout value for this Check Screen action (if you suspect that the problem is host latency) or try modifying the logic in your ECMAScript expression.
Send Buffer errors will, in general, be rare. Be on guard, however, for Send Buffers that contain more than one screen's worth of commands (so-called "type-ahead" buffering). Such actions are easy to create accidentally. An Action Model with overloaded Send Buffers may work correctly as you step through actions at animation time, but can fail when the component-as-a-whole is executed, due to screen synchronization problems. The way to avoid problems here is to make sure that for every Send Buffer action, there is always be a corresponding Check Screen action.
If connection pooling is used, and there has been an attempt to log on with a bad UserID or Password, that connection instance will not be usable and that member of the pool will be skipped over in subsequent connection requests. An error message will be sent to the server log saying "Logon connection in pool <Pool name> was discarded for User ID <User ID>." You should check for messages of this sort during preproduction testing and/or any time performance issues arise.
When you have a large Action Model (containing dozens or hundreds of Check Screen and Send Buffer actions), simply locating the action that's responsible for an error can be a challenge. One way to find the problematic action is to:
Select and Copy the text after "Expected" in the error dialog. (Click the Details button if need be, to expose the full error description. Highlight the relevant text, such as cursor coordinates. Then use Control-C to Copy.)
Of course, if you have multiple Check Screen actions that are based on identical go-ahead criteria, the foregoing technique won't necessarily be helpful. If that's the case, set a breakpoint at the midpoint of your Action Model, and run the component. If the error doesn't occur, move the breakpoint to a spot halfway between the original breakpoint and the end of the action list. (Otherwise, if the error does happen, set the breakpoint at a spot one quarter of the way down from the top of the action list.) Run the component again. Keep relocating the breakpoint, each time halving the distance between the last breakpoint or the top or bottom of the action list, as appropriate. In this way, you can quickly narrow down the location of the problematic action. (Using this "binary search" strategy, you should be able to debug an Action Model containing 128 actions in just 7 tries.)
Copyright © 2003 Novell, Inc. All rights reserved. Copyright © 1997, 1998, 1999, 2000, 2001, 2002, 2003 SilverStream Software, LLC. All rights reserved. more ...