Interactive Help Systems with MS Agents
You may have read and worked through our first article on using Microsoft Agents in your Web page and want to learn more, or skipped it to dive right in and learn how to move those guys around by reading data out of a database. If either case describes you, you're in the right place.
This article goes beyond a simple, script-based use of an agent and uses the technique that almost every n-tier Web-based application is using: reading data from a database.
I'm going to give you two examples to illustrate the basic principles. The first will simply read information from a Microsoft Access database on the server and move the Agent around. The second example tracks the last few visitors to the Web site and the Agent will tell you about them.
If components are grouped into one monolithic, tightly coupled pile, they lose all their intrinsic benefits and add maintenance complexity. What is required is to view components as heterogeneous sets of tiers or layers, where each component has something in common with the other, but solves a different problem.
The idea of partitioning the work is not new. This highly successful model is known as the three-tiered model in the client/server community. This is a primary design guideline for all COM programming.
One of the most successful models to emerge from the client/server world has been the three-tiered model. A tier, or layer, is a collection or set of independent homogeneous objects (each solving a small problem) that together solve a larger but common problem.
How do are Agent examples fit into this model? Well for starters, the browser is the presentation layer. It's main role is to display the Agents. Internet Information Server is the middle layer. It services the ASPs. The third tier is the ODBC connection to the database. This diagram shows the layout.
Both examples in this article use Microsoft Access databases on the server. ODBC connections allow easy access to the database.
Chances are, even if you use a database type other than Access, you'll need to create an ODBC DSN. A complete explanation of how I created the ODBC DSN for the first example might help if you're unfamiliar with creating ODBC DSN. To read this, simply click on the link below.
The First Example
To get started, we're going to open a database, read commands for the Agent, and have the Agent respond. To open the database, I use an ADO object which uses our ODBC DSN for the database access.
The database has only four fields. The first field contains the command that the Agent will perform. Examples of commands are Show and Speak. The second and third fields contain numeric parameters. These determine the relative position when MoveTo commands are carried out. All of the values range from 0-100 and represent a percentage of the screen. This makes the Agent move around similarly for all screen resolutions. If you expand this example on your own, these parameter fields can be used for values that other commands might expect. The last field in the database contains speech text. Click here see the database table.
The Agent code is similar to what I used in the second example of the first article, except that I add the code necessary for automatic download of the controls and Agent characters.
This code instantiates the Agent control, but doesn't attempt to download it if it's not already install.
This code instantiates the Agent control, and attempts to download and install the control if it hasn't already been installed.
Before going any further, it would be best if you took a look at the example Web page. That way you'll understand the discussion more completely.
Note that the first example used an ASP file instead of an HTML file. In order to do the database access using ADO, we must use ASP files instead of HTML files.
The first thing I did was to create and open a connection to the database. Then, I created a query string and executed the query. The following short code fragment shows you this code, and if you download and look at the entire ASP file, you'll see that it's at the top and is executed when the ASP is first loaded.
This code creates a database object with the Server.CreateObject method.
It then opens the connection by calling the Open method.
A simple query is assigned to a string object:
SELECT * FROM [AgentEvents]
The Recordset object is created with the Server.CreateObject method.
Finally, the query is executed when the Recordset object is opened.
Set cn = Server.CreateObject( "ADODB.Connection" ) cn.Open "DSN=Agents_DSN1" strSql = "SELECT * FROM [AgentEvents]" Set rs = Server.CreateObject( "ADODB.RECORDSET" ) rs.Open strSql, cn
As you would expect, we need to make sure that the Agent and Speech controls are embedded into the HTML code as objects. The following code does this, and also downloads the controls if they're not already installed:
This code instantiates the Agent and Speech objects, and downloads them if they're not already installed.
For this example, I created the Agent control code by copying from a previous example and then pasting it into my ASP file. I then removed all commands that made the Agent do anything such as calls to the MoveTo and Speak methods. I took this code out because I needed to replace it with code that took data from the database and generated the correct Genie commands in the HTML stream. The following code shows you the shell code, with the obvious omission of anything that controls the Genie:
This code does the Agent initialization, but doesn't issue any commands.
By the way, an interesting problem arises when you try to mix and match the VBScript of the Agent code and the VBScript of the ASP code. They aren't compatible. You see, the Agent VBScript code must be part of the HTML file that's delivered to the client browser. The ASP VBScipt code operates behind the scenes and is never seen by the client browser.
To solve this problem, all VBScript code for the Agents must be output to the HTML stream using calls to Response.Write methods. All of the previous code must be output in this manner. For instance, the this VBScript code to control Agents.
Doing this takes some time, but is a necessary step. One other thing to note: quotes that are within the VBScript code that must be output to the HTML stream must be converted to Chr( 34 ). Take the following examples:
VBScript code needed in the HTML stream
strMyString = "This is a test"
ASP VBScript code to insert into the HTML stream
Response.Write( "strMyString = " + Chr( 34 ) + "This is a test" + Chr( 34 ) )
Essentially, any quotes should be replaced with:
" + Chr( 34 ) + "
With the boilerplate code in place, we need to create the code that'll convert data to the Agent commands. For starters, we need to be aware of what data is coming in. Recordsets are comprised of a command, two numeric parameters, and a string that will contain a speech string for use when the command is 'Speak'.
This example only supports four commands: Show, Speak, MoveTo, and GestureAt. This makes it easy to output the commands since there aren't many to consider.
When the next record contains the Show command, we ignore the other parameters and output the following code:
Command in recordset: Show Genie.Show
The previous code is output to the HTML stream with a call to the Response.Write method as follows:
Response.Write( " Genie.Show" )
It's slightly more complicated when we encounter a command with parameters. When we do, we must take the parameters and insert them correctly into the Agent command that's output to the HTML stream. It's also necessary to convert numeric values to string values.
Let's take for instance, the MoveTo command. It has two numeric values. (For right now, assume for the sake of clarity that they represent x and y pixel values. Later, you'll find out that they don't.) The following code shows the Agent code we want to be in the HTML stream:
Command in recordset: MoveTo Parameters: Two numeric values representing x and y. For this example, use 75 and 42.
The following code shows how to output the Agent command to the HTML stream.
The example that you viewed, though is a little more complicated since it uses param1 as a percentage of the screen width and param2 as a percentage of the screen height.
If for instance the screen is 800 pixels wide, and the value in the database is 50, the actual display value will be 400 after the calculation. The calculation formulas are as follows:
DisplayX = ( GenieX * rs( "param1" ) ) / 100 DisplayY = ( GenieY * rs( "param2" ) ) / 100
So the real code used in the example to output the Agent command to the HTML stream is as follows:
Code to output the Agent command to the HTML stream: nXPercentage = CInt( rs( "Param1" ) ) nYPercentage = CInt( rs( "Param2" ) ) Response.Write("Genie.MoveTo(GenieRightX*"+CStr(nXPercentage)+")/100,_ (GenieBottomY*"+CStr(nYPercentage)+")/100"+chr(13)+chr(10))
And the resulting HTML code is:
A similar process is followed when dealing with the GestureAt command since it uses the two numeric parameters.
The Speak command is a little easier because it uses the Recordset's fourth field which is already a string and requires no conversion. We also won't do any calculations.
The Second Example
The second example in the article shows you how you can use an Agent for interactive help on a Web page. It does this in two ways. First, when users enter information in a form and click on the submit button, some simple data validation is performed on the form data (mainly that the required fields are filled out.) If something is missing in the form, Merlin goes down to the form field in question, gestures to the field, then informs the user that the field must contain some information.
Another thing this the example does is to give users the opportunity to asks questions of Merlin. By clicking on Merlin, a dialog box appears in which you can ask Merlin a question. He does an extremely simple analysis of the question and then gives you an answer. (The natural language parsing to carry out a real analysis of users questions is an article in itself.) Programmatically, though, you'll learn how to respond to Agent events. This example responds to a click event, but there are many other events available.
When you view this example, try clicking on the submit button. Merlin should alert you to any problems in the form. One thing I want to point out is that the Stop method is called before the form validation process begins. If Merlin is speaking, he'll stop. That way there isn't a long wait while he finishes what he's saying before he tells the user anything new. I have a variable named StopIntro that I use so that while he's reciting the introduction, he doesn't start a new line of the introduction if for some reason we need to interrupt him. The following code shows how I interrupt Merlin to make sure he can say something new without having to wait until speech that's in the queue finishes up.
Code to stop Merlin from speaking
StopIntro = true Merlin.Stop
Before you view the example, I'll give you some hints as to what kinds of questions Merlin answers best. For starters, you can always type 'Give me some examples'. He'll then explain what kinds of questions to ask. He will respond best to questions that start with 'How do I' and What is'. For instance, if you ask 'How do I eat pizza' he'll begin his response 'So you want to know how to eat pizza'. If you ask him 'What is a frog' he'll begin his response with 'So you want to know what a frog is'.
Before delving into explanations of the code, take a look at the second example now by clicking on the following link.
How the Form Data is Validated
The name of the form is ProductOrder. To service the submit button, I added a function to the VBScript code named ProductOrder_onsubmit. When the user clicks on the submit button, this function is called.
The function begins by dutifully interrupting Merlin in case he's in the process of speaking. It then looks at the fields in the form one at a time. If it finds the Name field is blank, Merlin goes down, gestures at the field, then tells the user that the Name field must contain information. This code is the ProductOrder_onsubmit function.
The next thing I want to talk about is adding event handling for the Agents. In your VBScript code, you simply add a subroutine named AgentName_EventName where AgentName is what you named the Agent and EventName is the name of the supported event.
To add a click event, I added a subroutine named AgentControl_Click. The code for my click event handler follows:
The AgentControl_Click subroutine Sub AgentControl_Click (CharacterID,Button,Shift,X,Y) StopIntro = true Merlin.Stop strQuestion = InputBox( "What would you like to know?" ) If len( strQuestion ) > 0 then GetHelp( strQuestion ) End If End Sub
Here are prototypes of the events that you might want to use in your Web applications:
|Sub agent_Click (ByVal CharacterID, ByVal Button, ByVal Shift, ByVal X, ByVal Y)||Occurs when the user clicks a character or the characters icon.|
|Sub agent_DblClick (ByVal CharacterID, ByVal Button, ByVal Shift, ByVal X, ByVal Y)||Occurs when the user double-clicks a character.|
|Sub agent_DragComplete (ByVal CharacterID, ByVal Button, ByVal Shift, ByVal X, ByVal Y)||Occurs when the user completes dragging a character.|
|Sub agent_DragStart (ByVal CharacterID, ByVal Button, ByVal Shift, ByVal X, ByVal Y)||Occurs when the user begins dragging a character.|
|Sub agent_Hide (ByVal CharacterID, ByVal Cause)||Occurs when a character is hidden.|
|Sub agent_IdleComplete (ByVal CharacterID)||Occurs when the server ends the Idling state of a character.|
|Sub agent_IdleStart (ByVal CharacterID)||Occurs when the server sets a character to the Idling state.|
|Sub agent_Move (ByVal CharacterID, ByVal X, ByVal Y, ByVal Cause)||Occurs when a character is moved.|
|Sub agent_Show (ByVal CharacterID, ByVal Cause)||Occurs when a character is displayed.|
The last thing I'll show you is the subroutine that responds to the user's question. It's straightforward VBScript, so many of you will quickly enhance it.
You may want to download both ASP files for the two examples along with the Access database. Click on the following link to download the files.
As you can see from these examples, Agents can enhance your Web presentations tenfold. They're easy to add and use, and event the advanced features such as event handling are a piece of cake.
Send me your questions, I'll do my best to get you an answer. Also, please send me the URL to your Web pages if you use an Agent.
The next article in this series will show you how to use the Agent and its speech recognition capability to allow users to say their commands.