CCXML Voxeo 1.0 Development GuideHome  |  Frameset Home

  Call Legs: An Overview  |  TOC  |  CCXML Token Applications  
This documentation is for CCXML 1.0-Voxeo, which has been superceded by CCXML 1.0-W3C. The CCXML-Voxeo platform is not being updated any longer. The CCXML 1.0-W3C version, however, has many new features and is actively being enhanced. If you're writing a new CCXML application, you should use CCXML 1.0-W3C. Click here for the CCXML 1.0-W3C documentation.

Tutorial: Connecting Call Legs

This Lesson will revisit how CCXML is able to interact with VoiceXML 2.0 dialogs and delve into the world of connecting two call legs together in a conference-style application. This tutorial is considerably more complicated than our previous ones, and is not recommended as a starting point.

In this tutorial, we will:

Note: this tutorial requires the enabling of outdial priveleges on the Voxeo network, and the provisioning of a alphanumeric token string to your application. In order to get hooked up with all these neat features, check our Support Guide for all the juicy details.

Step 1: creating our initial CCXML structure

From our previous tutorials, we now recognize the following structure as a normal starting point:


<?xml version="1.0" encoding="UTF-8"?>
<!-- NOTE THAT WE *MUST* DECLARE THE xmlns ATTRIBUTE -->
<ccxml version="1.0" xmlns:voxeo="http://community.voxeo.com/xmlns/ccxml">
</ccxml>


Step 2: setting up our basic handlers

We are already comfortable with state machines and handlers (both "event" and "error"), so let's take care of those first.


<?xml version="1.0" encoding="UTF-8"?>
<!-- NOTE THAT WE *MUST* DECLARE THE xmlns ATTRIBUTE -->
<ccxml version="1.0" xmlns:voxeo="http://community.voxeo.com/xmlns/ccxml">
  <var name="state0" expr="'init'"/>
  <eventhandler statevariable="state0">
    <transition state="'init'" event="connection.CONNECTION_ALERTING">
      <accept/>     
    </transition>

    <transition state="'init'" event="connection.CONNECTION_CONNECTED" name="evt">
      <var name="callid_in" expr="evt.callid"/>
    </transition>

    <transition event="call.CALL_INVALID" name="evt">
      <if cond="callid_in == evt.callid">
        <exit/>
      </if>

    </transition>

    <transition event="error.*" name="evt">
      <log expr="'an error has occured (' + evt.error + ')'"/>
      <exit/>
    </transition>
  </eventhandler>   
</ccxml>


A few items to note so far:

Step 3: introducing VoiceXML dialogs into the fray

CCXML is designed as a powerful, call control structure, but often phone applications expect pesky items, like audio files, to be played or even voice recognition inputs to gather information. These are accomplished through "dialogs" to VoiceXML applications. Let's incorporate a simple dialog into our call acceptance transition:


    <transition state="'init'" event="connection.CONNECTION_CONNECTED" name="evt">
      <var name="callid_in" expr="evt.callid"/>
      <assign name="state0" expr="'enterpin'"/>
      <dialogstart src="'enterpin.vxml'" type="'application/xml+vxml;platform=motorola'"/>
    </transition>


We change the state and launch the dialog contained in enterpin.vxml (note, the VoiceXML documents will be linked to, but not directly listed within this tutorial; after all, you already know how to code VoiceXML, right?). This dialog is very basic, gathering up a PIN code with voice recognition.

Now what? Our dialog will end naturally when the caller enters their information, and an event will be returned to CCXML from the VoiceXML dialog via the VoiceXML <exit> element. In this case, it is returning a variable called "pin":

      <exit namelist="pin"/>

Okay, so I lied about not displaying any VoiceXML code... Once this VoiceXML element fires, one of our favorite asynchronous events will be created, so we had best be prepared for it:


    <transition state="'enterpin'" event="dialog.exit" name="evt">
      <log expr="'PIN = [' + evt.pin + ']'"/>
    </transition>


This seems straightforward enough -- a "dialog.exit" event is captured, and our "pin" variable is contained within the "evt" object. Since we went to all the trouble of gathering user input, we should clearly do something with it. Let's extend our transition a bit, and move on.


    <transition state="'enterpin'" event="dialog.exit" name="evt">
      <log expr="'PIN = [' + evt.pin + ']'"/>
    <if cond="'1234' != evt.pin">
        <exit/>
      <else/>
        <var name="pin" expr="evt.pin"/>
      </if>

       
      <assign name="state0" expr="'calling'"/>           
      <dialogstart src="'holdmusic.vxml'" name="holdMusicDlg" type="'application/xml+vxml;platform=motorola'"/>                 
      <createcall dest="'8315551234'"/>
    </transition>


So we just introduced quite a bit of complexity, but we can break it down. Our first conditional block will simply exit the session if the user inputed a "pin" code other than "1234". Let's assume, for the moment, the caller entered our super secret code -- we then create a variable with the evt.pin information (so that it will not be lost), change the state, start a "hold music" VoiceXML dialog, and actually make an outbound call (to my mom's house).

Using hold music creates a nice delay for the state machine to process the outbound call while not leaving the inbound call in limbo. While the caller is listening to pretty, pretty music, let's explore what might happen with the outbound call.

Step 4: outbound call possibilities

My mom often doesn't answer her phone, so let's make sure we have a provision for this:


    <transition state="'calling'" event="connection.CONNECTION_FAILED">
      <assign name="state0" expr="'callfailed'"/>
      <dialogterminate sessionid="holdMusicDlg"/>
    </transition>

    <transition state="'callfailed'" event="dialog.exit">
      <assign name="state0" expr="'playingCallFailed'"/>
      <dialogstart src="'callfailed.vxml'" type="'application/xml+vxml;platform=motorola'"/>                 
    </transition>

    <transition state="'playingCallFailed'" event="dialog.exit">
      <disconnect/>
    </transition>


So here we see a three step process (bearing in mind, of course, that this will all occur asynchronously). First, we trap the "CONNECTION_FAILED" event and terminate our cheesy "hold music" dialog. Second, we pounce on the dialog.exit event and start a new VoiceXML dialog that tells our inbound caller the outbound call to my mom failed. Lastly, after the message has been delivered, we trap the (new) dialog.exit event and fire off a <disconnect> event (which will be caught by our already listed "CALL_INVALID" trap). This concludes the logical sequence for the outbound call not being completed.

Now let's handle the call being answered:


    <transition state="'calling'" event="connection.CONNECTION_CONNECTED" name="evt">
      <var name="callid_out" expr="evt.callid"/>
      <assign name="state0" expr="'callAccepted'"/>
      <dialogstart src="'newcall.php'" namelist="pin" type="'application/xml+vxml;platform=motorola'"/>
    </transition>

    <transition state="'callAccepted'" event="dialog.exit" name="evt">
      <if cond="evt.callid == callid_in">
        <exit/>
      <else/>
        <assign name="state0" expr="'beforeBridging'"/>
      <dialogterminate sessionid="holdMusicDlg"/>
      </if>
    </transition>

    <transition state="'beforeBridging'" event="dialog.exit">
      <assign name="state0" expr="'bridged'"/>
      <join sessionid1="callid_in" sessionid2="callid_out"/>
    </transition>


Like our state machine branch to handle a call failure situation, our call success scenario has three steps. The first saves off the Call ID of the outbound leg (remember, we already saved off the inbound Call ID) and runs a VoiceXML server-side script dialog, passing in our "pin" as a querystring variable (our example uses PHP, but any script that has the capability to pull information from the querystring is valid). This little dialog simply reads the "pin" information to person who answered the phone as a type of "whisper mode" operation. Slick.

Our second transition has a quick check to ensure that our "hold music" dialog did not end prior to our "new call" dialog. Assuming we are still on track, we now kill the "hold music" dialog.

This tosses another "dialog.exit" event, and our long road to conferencing call legs together finally comes to fruition. With <join>, we link our two saved Call IDs together, and the beauty of human-to-human communication is achieved.

Step 5: the complete script

Whew. Possibly pushing the cube on tutorial complexity, I hope you kept up. Here is the complete CCXML script, with links to download all the code following:

<?xml version="1.0" encoding="UTF-8"?>
<!-- NOTE THAT WE *MUST* DECLARE THE xmlns ATTRIBUTE -->
<ccxml version="1.0" xmlns:voxeo="http://community.voxeo.com/xmlns/ccxml">
  <var name="state0" expr="'init'"/>
  <eventhandler statevariable="state0">
    <transition state="'init'" event="connection.CONNECTION_ALERTING">
      <accept/>     
    </transition>

    <transition state="'init'" event="connection.CONNECTION_CONNECTED" name="evt">
      <var name="callid_in" expr="evt.callid"/>
      <assign name="state0" expr="'enterpin'"/>
      <dialogstart src="'enterpin.vxml'" type="'application/xml+vxml;platform=motorola'"/>
    </transition>

    <transition state="'enterpin'" event="dialog.exit" name="evt">
      <log expr="'PIN = [' + evt.pin + ']'"/>
      <if cond="'1234' != evt.pin">
        <exit/>
      <else/>
        <var name="pin" expr="evt.pin"/>
      </if>
       
      <assign name="state0" expr="'calling'"/>           
      <dialogstart src="'holdmusic.vxml'" name="holdMusicDlg" type="'application/xml+vxml;platform=motorola'"/>                 
      <createcall dest="'8315551234'"/>
    </transition>   
   
    <transition state="'calling'" event="connection.CONNECTION_FAILED">
      <assign name="state0" expr="'callfailed'"/>
      <dialogterminate sessionid="holdMusicDlg"/>
    </transition>

    <transition state="'callfailed'" event="dialog.exit">
      <assign name="state0" expr="'playingCallFailed'"/>
      <dialogstart src="'callfailed.vxml'" type="'application/xml+vxml;platform=motorola'"/>                 
    </transition>

    <transition state="'playingCallFailed'" event="dialog.exit">
      <disconnect/>
    </transition>
   
    <transition state="'calling'" event="connection.CONNECTION_CONNECTED" name="evt">
      <var name="callid_out" expr="evt.callid"/>
      <assign name="state0" expr="'callAccepted'"/>
      <dialogstart src="'newcall.php'" namelist="pin" type="'application/xml+vxml;platform=motorola'"/>
    </transition>

    <transition state="'callAccepted'" event="dialog.exit" name="evt">
      <if cond="evt.callid == callid_in">
        <exit/>
      <else/>
        <assign name="state0" expr="'beforeBridging'"/>
        <dialogterminate sessionid="holdMusicDlg"/>
      </if>
    </transition>

    <transition state="'beforeBridging'" event="dialog.exit">
      <assign name="state0" expr="'bridged'"/>
      <join sessionid1="callid_in" sessionid2="callid_out"/>
    </transition>   
   
    <transition event="call.CALL_INVALID" name="evt">
      <if cond="callid_in == evt.callid">
              <voxeo:sendemail to="'yourEmail@there.com'"
                        from="'myApp@here.com'"
                        type="'debug'"
                        body=" 'call.CALL_INVALID detected ! ' "/>
        <exit/>
      </if>
    </transition>

    <transition event="error.*" name="evt">
      <log expr="'an error has occured (' + evt.error + ')'"/>
              <voxeo:sendemail to="'yourEmail@there.com'"
                        from="'myApp@here.com'"
                        type="'debug'"
                        body=" 'generic error detected ! ' "/>
      <exit/>
    </transition>
  </eventhandler>   
</ccxml>



  Download the CCXML source code!


What we covered:



  ANNOTATIONS: EXISTING POSTS
jason.m.hanna
2/23/2007 9:24 AM (EST)
I noticed that the holdmusic.vxml dialog generates an error in the logs because it creates a loop by calling itself.

Obviously this is the behavior we want while waiting for a call to be connected, but does the occurence and handling of the error degrade performance or otherwise cause issues on the Voxeo platform in any way?

In short I guess I'm asking if there is a better way to program an audio loop.

14:14:37.543: =========================== An error occurred while executing the following dialog. Initial URL: file:///c:/nuance/callrouting/motnewcall.vxml Current URL: holdmusic.vxml Calling Number (ANI): 0000123456 Called Number (DNIS): dialog.vxml.http%3a%2f%2fmydomain.com%3a8080%2fvoice%2fpa Redirecting Number (RDNIS): "" State: Form VoiceXML Browser Version: 5.5.129.122 Date/Time: 2007/2/23 14:14:37.543 VoiceException: error.semantic.fatal Fetched and compiled 20 documents in a row that didn't listen to the user. Probable dialog loop. Dialog stack trace: State (Dialog) URL (Document) -------------- ------------------------------ Form http://mydomain.com:8080/voice/pages/ccxml/SampleBridge/holdmusic.vxml


Thanks!
-jmh
MattHenry
2/23/2007 2:19 PM (EST)

Hi there,

The short answer is that this can really be rectified by having a much longer audio or TTS prompt in the dialog itself, or by creating a "dummy" field as illustrated in our VXML docs. In terms of this affecting the network at any level, the message that you have received is a notification that our loop detection is stopping the VXML long before it could degrade performance on our server.

Hope this helps to explain!

~Matthew Henry


login
  Call Legs: An Overview  |  TOC  |  CCXML Token Applications  

© 2008 Voxeo Corporation  |  Voxeo IVR  |  VoiceXML & CCXML IVR Developer Site