Using VoiceXML Reusable Dialog Components

In a previous post I discussed what Reusable Dialog Components (RDC) are and the basis for adding support for them in the VoiceModel Project.  In this post I will discuss how to use RDC's with  the VoiceModel framework, which allows developers to create advanced voice applications using ASP.NET MVC, C# and Razor.  This will walk you through the example for RDC's in the VoiceModel Project that you can download from here.  Download the complete project for version 0.5 on this page and you will find this example in the project directory ReusableComponentEx.

To get started create a new empty MVC 3 project in Visual Studio.  Next, include a reference to the VoiceModel assembly and the RDC assembly.  The RDC assembly is called GetDateDtmf.  This RDC provides a dialog for collecting a date from the user using the telephone keypad.  VoiceModel uses a technology called Portable Areas to be able to reuse Controllers, Views, and other resources that are available in an assembly.  In order to take advantage of Portable Areas you will need to install  MVCContrib into your project using NuGet .

Now that we have the base project and references setup you will need to register the VoiceModel View Engine in the Global.asax.  This requires a single line of code in the Application_Start method, which should look like this:

        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            RegisterGlobalFilters(GlobalFilters.Filters);
            RegisterRoutes(RouteTable.Routes);
            VoiceViewEngine.Register(ViewEngines.Engines);
        }

Next you will need to add the Controller for the main application.  The actual steps for doing this are discussed in the Hello World Tutorial.  All VoiceModel Controllers have two methods you must overrride; BuildVoiceModels to define the Views that the VoiceXML Browser (i.e. IVR) will consume, and BuildCallFlow to define the call flow or state machine for this application.  The controller for our sample application will look like this:

public class MainController : VoiceController
{

    public override VoiceModels BuildVoiceModels()
    {
        VoiceModels views = new VoiceModels();
        views.Add(new Say("greeting","Welcome to the Reusable Dialog Component Example."));

        //This tells ViewModel to get the views from the reusable component.
        views.Add(new GetDateDtmfView("getStartDate"));

        //Clones the views again and assign it a unique ID
        //for getting the finish date.
        views.Add(new GetDateDtmfView("getFinishDate"));

        Prompt sayDiff = new Prompt();
        sayDiff.audios.Add(new 
           TtsMessage("The difference between the start and finish dates is "));
        sayDiff.audios.Add(new TtsVariable("d.daysDiff"));
        sayDiff.audios.Add(new TtsMessage(" days."));
        views.Add(new Say("differenceInDays",sayDiff));
        views.Add(new Exit("goodbye", "Goodbye."));
        return views;
    }

    public override CallFlow BuildCallFlow()
    {
        CallFlow flow = new CallFlow();
        flow.AddStartState(new State("greeting", "getStartDate"));

        //This tells the state machine to use the state machine in the reusable component.
        flow.AddState(new GetDateDtmfState("getStartDate", "saveStartDate", 
            new GetDateDtmfInput()
            {ReturnAction = this.ActionName, 
             AskDatePrompt = new Prompt("Enter the start date as a six digit number.")}));

        //When we return from the reusable component we need to do something 
        //with the information returned (i.e. the date entered).
        flow.AddState(new SaveStartDate("saveStartDate", "getFinishDate"));

        //Call the reusable component again to get the finish date.
        flow.AddState(new GetDateDtmfState("getFinishDate", "saveFinishDate",  
            new GetDateDtmfInput() 
            {ReturnAction = this.ActionName, 
             AskDatePrompt = new Prompt("Enter the finish date as a six digit number.") }));

        //Get the finish date and calculate the difference between 
        //the start and finish in days.
        flow.AddState(new SaveFinishDate("saveFinishDate", "differenceInDays"));

        flow.AddState(new State("differenceInDays", "goodbye"));
        flow.AddState(new State("goodbye"));
        return flow;
    }
}


To add the RDC in the collection of Views just add an instance of the object GetDateDtmfView which accepts one argument, a unique name that identifies this instance of the RDC.  This allows VoiceModel to transition to the first view in the RDC instead of referencing it through a subroutine call, which is much more efficient for the IVR to process.

To add the RDC to the Call Flow just add the object called GetDateDtmfState which accepts three arguments.  The first two arguments are the same for any State object which are a unique ID that matches the unique ID of the associated View, and the ID of the state that it will transition to if successful.  The final argument is the input arguments for the RDC.  Each RDC defines its own input object that holds the properties required to configure the RDC for this particular instance.  For the GetDateDtmf RDC it needs to know what controller/action to use to get back to the calling application and the prompt to use when prompting the user to enter the date.  As we evolve GetDateDtmf in the future it will accept additional configuration parameters for things like retry prompts and whether to confirm the users entry or not.

You will notice that there are additional states added to this applications call flow to handle the information we get back from the RDC.  SaveStartDate  takes the date returned by the RDC and puts it in a session variable for later use.  SaveFinishDate take the date returned by the RDC and does a difference between it and the start date and packages it up as JSON for the next View to use for voicing back the result.  You can explore the code for these custom states by downloading the source code from here.

That is all there is to use a RDC in  the VoiceModel framework.  As you can imagine having a library of advanced RDC's would greatly improve the efficiency for creating sophisticated voice applications.  A goal for the VoiceModel Project is to create an extensive library of RDC's and we are looking for contributors to help with that effort.  If you have any ideas on making it even easier to use RDC's please comment.

In the next post we will look at how to create Reusable Dialog Components.


Comments

Popular posts from this blog

Using Claims in ASP.NET Identity

Seeding & Customizing ASP.NET MVC SimpleMembership

Customizing Claims for Authorization in ASP.NET Core 2.0