Creating Reusable Dialog Components with VoiceXML

UPDATE: How Reusable Dialog Components in VoiceModel are created and used has been greatly simplified. Go to this post for more details.

This post is a continuation of a previous post on using Reusable Dialog Components (RDC).  In this post we will look at creating Reusable Dialog Components for a VoiceXML application using VoiceModel.  VoiceModel is an open source project that makes development of VoiceXML applications easier using ASP.NET MVC, C#, and Razor.  Reusable Dialog Components are reusable libraries that make development of VoiceXML application easier as discussed here.  In this example we will create an RDC for getting a date from a user using a telephone keypad.

To get started create a class library project in Visual Studio.  VoiceModel is developed in Visual Studio 2010 using .NET Framework version 4.0.  Next include in the references System.Web, System.Web.Mvc, VoiceModel, and MvcContrib.  MvcContrib is available via NuGet and is used for the Portable Areas that it supports.  Portable Areas allow you to reuse resources such as views and controllers from an MVC project.

Now that our references are all setup we need to create a controller for our RDC.  The controller should inherit from VoiceController.  As with any VoiceModel controller you will need to override two methods; BuildVoiceModels to define the views for the RDC, and BuildCallFlow to define the call flow or state machine that will define the possible paths in our RDC.  Here is what the code looks like for the example RDC which gets a date from a user.

public class GetDateDtmfController : VoiceController
{
    public override VoiceModels BuildVoiceModels()
    {
        VoiceModels views = new VoiceModels();
        views.Add(new Ask("getDate", 
            "Enter the date as a six digit number in the format month month day day year year",
            new Grammar("digits?minlength=6")));
        Prompt confirmPrompt = new Prompt();
        confirmPrompt.audios.Add(new TtsMessage("You Entered"));
        confirmPrompt.audios.Add(new TtsVariable("d.Month"));
        confirmPrompt.audios.Add(new TtsVariable("d.Day"));
        confirmPrompt.audios.Add(new TtsVariable("d.Year"));
        views.Add(new Say("confirmDate", confirmPrompt));
        views.Add(new Say("invalidDate","You entered and invalid date."));

        return views;
    }

    public override CallFlow BuildCallFlow()
    {
        CallFlow flow = new CallFlow();
        flow.AddStartState(new StartGetDateState("getDate","validateDate",this));
        flow.AddState(new ValidateDate("validateDate","confirmDate","invalidDate"));
        flow.AddState(new ReturnState("confirmDate", this));
        flow.AddState(new ReturnState("invalidDate", this));
        return flow;
    }

}

In this simple example we just prompt the user to enter the date using the telephone keypad, validate the date, and if it is valid we voice the date back.

All RDC's have some type of input object which defines how to configure the RDC and an output object that provides the calling application information on the success or failure of the RDC and any other information required by the application.  In this example the input object will contain information on what prompt to use to ask the caller to input a date.  All input objects must inherit from ComponentInput.  The code for the input object looks like this.

    public class GetDateDtmfInput : ComponentInput
    {
        public Prompt AskDatePrompt { get; set; }
    }



The output object for this example returns the date that was entered by the user and whether they were successful in entering the date.  All output objects must inherit from ComponentOutput. The code for the output is shown below.



    public class GetDateDtmfOutput : ComponentOutput
    {
        public DateTime Date { get; set; }
        public bool IsValidDate { get; set; }
    }


In order for the calling application to invoke the RDC it needs to know which view to initially use and what the starting state is for the RDC.  To do this we need to create two more objects for the calling application to use.  For the application to access the view model for the RDC we need to create an object that inherits from Component. Here is what our example code  looks like.



    public class GetDateDtmfView : Component
    {
        public GetDateDtmfView(string id) : base(id, new GetDateDtmfController())
        {
       
        }
    }


And for the application to access the RDC call flow we need to create an object that inherits from StartComponentState.  This object also accepts the input object we defined above to pass configuration information to the RDC. Here is what that code looks like.

    public class GetDateDtmfState : StartComponentState
    {
        public GetDateDtmfState(string id, string target, GetDateDtmfInput input) : 
            base(id, target, new GetDateDtmfController(), input) { }
    }



The last thing we need to do is create a registration object to register the RDC's controller with the main application using Portable Areas.  The main application will call this registration object in its Global.asax.  The registration object must inherit from PortableAreaRegistration and the code is shown below.

    class GetDateDtmfRegistration : PortableAreaRegistration
    {
        public override string AreaName
        {
            get
            {
                return "GetDateDtmf";
            }
        }

        public override void RegisterArea(AreaRegistrationContext context, 
            IApplicationBus bus)
        {
            context.MapRoute("resources",
                             AreaName + "/Resource/{resourceName}",
                             new {Controller = "EmbeddedResource", action = "Index"},
                             new string[] {"MvcContrib.PortableAreas"});

            base.RegisterArea(context, bus);

            context.MapRoute(
                "GetDateDtmf",
                AreaName + "/{controller}/{action}/{id}",
                new { controller = "GetDateDtmf", action = "Index", 
                  id = UrlParameter.Optional }
                );
        }

    }

That is all there is to creating an RDC in VoiceModel.  You can download the source code for this example from CodePlex and try it out.  This example was the first step in creating a framework for using RDC's in VoiceModel.  The long term goal for the VoiceModel Project is to create an extensive library of RDC's to make development of robust VoiceXML applications even easier.  If you would like to contribute to this effort please join the VoiceModel Project and drop me a note.

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