Download

'******************************************************************************
' This is the weather by ZIP code demo in which the user enters the ZIP code 
' via the keypad and the HTTP GET transaction is performed asynchronously.
'******************************************************************************/
Imports System
Imports System.IO
Imports System.Net
Imports System.Xml
Imports System.Text
Imports System.Threading
Imports System.Reflection
Imports System.Collections

Imports IVRForBeginners

'******************************************************************************
' All .Net IVR applications are implemented as classes derived from our base
'******************************************************************************/
Public Class VbWeatherSR
 Inherits NetClientCall

 Private Const ZipGrammarId As Integer = 22 ' We use off the wall numbers to make
 Private Const QuitGrammarId As Integer = 67 'the point that they are arbitrary

 Private Shared states As Hashtable
 Private Shared prompt As String
 Private Shared welcome As String
 Private Shared regrets As String
 Private Shared baseQuery As String
 Private Shared propertyNames(5) As String

 Private zip As String
 Private weather As String
 Private iar As IAsyncResult
 Private req As HttpWebRequest
 Private resp As HttpWebResponse
 Private rec As Recognizer
 Private syn As Synthesizer

 '******************************************************************************
 ' We set some constant variables 
 '******************************************************************************/
 Public Shared Shadows Function Initialize() As Boolean

  prompt = "Please tell me the ZIP code for the town whose weather forecast you want to hear."
  welcome = "Welcome to the weather demonstration application. " + _
            "Say good bye or hang up when you are done."
  regrets = "We are sorry but we had trouble determining the weather for that ZIP code. " + _
            "If the ZIP code is valid you can try again later."

  baseQuery = "http://weather.yahooapis.com/forecastrss?p="

  BuildStatesTable()
  BuildPropertyNames()

  Initialize = True

 End Function

 '******************************************************************************
 ' The required Answer() method always comprises the entire Voice User 
 ' Interface for an IVR application. 
 '******************************************************************************/
 Public Sub Answer()

  Dim i, conf, millis As Integer
  Dim buffer As StringBuilder
  Dim temp As String

  Try

   ' If the caller hangs up we want an exception

   hangupThrows(True)

   ' Get an instance of the recognizer
   ' Load a grammar that handles the request for the ZIP code
   ' Load another that handles the request to quit the application

   rec = getRecognizer()
   rec.loadGrammar("ZIP", ZipGrammarId)
   rec.loadGrammar("Quit", QuitGrammarId)

   ' Get an instance of the synthesizer and greet the caller
   ' We should check for a null object which indicates failure

   syn = getSynthesizer()
   syn.speak(welcome)
   syn.wait()

   Do While (True)

    ' Start listening first and then prompt the caller

    rec.listen()
    syn.speak(prompt)

    ' Wait for the recognizer to tell us how it did

    conf = rec.wait(10000, 30000)

    ' Stop listening

    rec.stopListening()

    ' Dump the state of the recognition for debugging

    rec.dump()

    ' On an error or disconnect we are done

    If conf < 0 Then Exit Do

    ' Arbitrarily we decide 65% confidence is our threashold

    If conf < 6500 Then
     NoRecognition()

    Else

     ' If we recognized a phrase in Quit grammar then we are done

     If rec.getGrammarId() = QuitGrammarId Then Exit Do

     ' Tell the user to be patient

     syn.speak("Please wait")

     ' Build the ZIP code from the parts
     ' See the grammar

     buffer = New StringBuilder
     For i = 0 To 4
      temp = rec.getPropertyText(propertyNames(i))
      buffer.Append(temp)
     Next i
     zip = buffer.ToString()

     ' Make an asynchronous request of the web service 

     RequestWeather()

     Do While Not iar.IsCompleted

      ' Amuse the user with "music on hold" while we wait

      millis = playMessage("strumming")

      ' Wait for the request to complete, the music to stop,
      ' or for the user to hang up. When the music stops we
      ' just play it again but we could count the iterations
      ' of the loop to enforce a time out.

      wait(iar.AsyncWaitHandle, millis)
     Loop

     ' When the request completes we fetch the data and build the forecast

     CompleteRequest()

     ' That done we kill the music, add a bit of silence 
     ' and then deliver the forecast to the caller 

     stopPlaying()
     wait(500)
     syn.speak(weather)

     ' While speaking we add an entry to the call detail records

     cdrStatusMessage(0, zip)
     cdrStatusMessage(1, weather)

     ' Then we wait for the forecast to be completely spoken

     syn.wait()
    End If

   Loop

   ' Be polite and say good bye before we hang up

   syn.speak("Good bye.")
   syn.wait()

  ' If the caller hangs up we will cancel the web service request

  Catch e1 As NetClientCallTermination
   CancelRequest()
   Console.WriteLine(e1.Message)

  ' Shouldn't happen, but you never know

  Catch e2 As System.Exception
   CancelRequest()
   Console.WriteLine(e2.Message)

  End Try

 End Sub

 '******************************************************************************
 ' We use this hash table to translate state abbreviations we get from the web
 ' service to more easily pronounceable stuff.
 '******************************************************************************/
 Private Shared Sub BuildStatesTable()

  states = New Hashtable

  states.Add("AL", "Alabama")
  states.Add("AK", "Alaska")
  states.Add("AZ", "Arizona")
  states.Add("AR", "Arkansas")
  states.Add("CA", "California")
  states.Add("CO", "Colorado")
  states.Add("CT", "Connecticut")
  states.Add("DE", "Delaware")
  states.Add("DC", "D C")
  states.Add("FL", "Florida")
  states.Add("GA", "Georgia")
  states.Add("HI", "Hawaii")
  states.Add("ID", "Idaho")
  states.Add("IL", "Illinois")
  states.Add("IN", "Indiana")
  states.Add("IA", "Iowa")
  states.Add("KS", "Kansas")
  states.Add("KY", "Kentucky")
  states.Add("LA", "Louisiana")
  states.Add("ME", "Maine")
  states.Add("MD", "Maryland")
  states.Add("MA", "Massachusetts")
  states.Add("MI", "Michigan")
  states.Add("MN", "Minnesota")
  states.Add("MS", "Mississippi")
  states.Add("MO", "Missouri")
  states.Add("MT", "Montana")
  states.Add("NE", "Nebraska")
  states.Add("NV", "Nevada")
  states.Add("NH", "New Hampshire")
  states.Add("NJ", "New Jersey")
  states.Add("NM", "New Mexico")
  states.Add("NY", "New York")
  states.Add("NC", "North Carolina")
  states.Add("ND", "North Dakota")
  states.Add("OH", "Ohio")
  states.Add("OK", "Oklahoma")
  states.Add("OR", "Oregon")
  states.Add("PA", "Pennsylvania")
  states.Add("PR", "Puerto Rico")
  states.Add("RI", "Rhode Island")
  states.Add("SC", "South Carolina")
  states.Add("SD", "South Dakota")
  states.Add("TN", "Tennessee")
  states.Add("TX", "Texas")
  states.Add("UT", "Utah")
  states.Add("VT", "Vermont")
  states.Add("VA", "Virginia")
  states.Add("WA", "Washington")
  states.Add("WV", "West Virginia")
  states.Add("WI", "Wisconsin")
  states.Add("WY", "Wyoming")

 End Sub

 '******************************************************************************
 '******************************************************************************/
 Private Shared Sub BuildPropertyNames()

  propertyNames(0) = "Digit1"
  propertyNames(1) = "Digit2"
  propertyNames(2) = "Digit3"
  propertyNames(3) = "Digit4"
  propertyNames(4) = "Digit5"

 End Sub

 '******************************************************************************
 ' Pulls the city and state from the XML document
 '******************************************************************************/
 Private Function GetCity(ByVal doc As XmlDocument, ByVal mgr As XmlNamespaceManager) As String

  Dim city, temp1, temp2 As String
  Dim node As XmlNode

  ' The city and the state reside in the same node ...

  node = doc.SelectSingleNode("//channel/yweather:location", mgr)

  ' ... as distinct attributes 

  temp1 = node.Attributes.GetNamedItem("city").InnerText
  temp2 = node.Attributes.GetNamedItem("region").InnerText

  ' Expand the state abbreviation if we know it ...

  If states.Contains(temp2) Then
   GetCity = temp1 + " " + states.Item(temp2)

  ' ... or take it as is if we don't

  Else
   GetCity = temp1 + " " + temp2

  End If

 End Function

 '******************************************************************************
 ' Pulls the temperature from the XML document
 '******************************************************************************/
 Private Function GetTemperature(ByVal doc As XmlDocument, ByVal mgr As XmlNamespaceManager) As String

  Dim temp As String
  Dim node As XmlNode

  node = doc.SelectSingleNode("//channel/item/yweather:condition", mgr)
  temp = node.Attributes.GetNamedItem("temp").InnerText
  GetTemperature = temp + " degrees Fahrenheit"

 End Function

 '******************************************************************************
 ' Pulls the current condition from the XML document
 '******************************************************************************/
 Private Function GetOutlook(ByVal doc As XmlDocument, ByVal mgr As XmlNamespaceManager) As String

  Dim node As XmlNode

  node = doc.SelectSingleNode("//channel/item/yweather:condition", mgr)
  GetOutlook = node.Attributes.GetNamedItem("text").InnerText

 End Function

 '******************************************************************************
 ' Pulls the wind speed and direction from the XML document
 '******************************************************************************/
 Private Function GetWind(ByVal doc As XmlDocument, ByVal mgr As XmlNamespaceManager) As String

  Dim degrees As Integer
  Dim wind, temp1, temp2 As String
  Dim node As XmlNode

  node = doc.SelectSingleNode("//channel/yweather:wind", mgr)
  temp1 = node.Attributes.GetNamedItem("direction").InnerText
  temp2 = node.Attributes.GetNamedItem("speed").InnerText

  degrees = Val(temp1)
  GetWind = "from the " + Direction(degrees) + " at " + temp2 + " miles per hour"

 End Function

 Function Direction(ByVal num As Integer) As String

  If num <= 11 Then
   Direction = "north"

  ElseIf num <= 33 Then
   Direction = "north northeast"

  ElseIf num <= 56 Then
   Direction = "northeast"

  ElseIf num <= 78 Then
   Direction = "east northeast"

  ElseIf num <= 101 Then
   Direction = "east"

  ElseIf num <= 123 Then
   Direction = "east southeast"

  ElseIf num <= 146 Then
   Direction = "southeast"

  ElseIf num <= 168 Then
   Direction = "south southeast"

  ElseIf num <= 191 Then
   Direction = "south"

  ElseIf num <= 213 Then
   Direction = "south southwest"

  ElseIf num <= 236 Then
   Direction = "southwest"

  ElseIf num <= 258 Then
   Direction = "west southwest"

  ElseIf num <= 281 Then
   Direction = "west"

  ElseIf num <= 303 Then
   Direction = "west northwest"

  ElseIf num <= 326 Then
   Direction = "northwest"

  ElseIf num <= 348 Then
   Direction = "north northwest"

  Else
   Direction = "north"

End If

End Function

 '******************************************************************************
 ' If the user hangs up we use this function to cancel the outstanding web 
 ' service(request)
 '******************************************************************************/
 Private Sub CancelRequest()

  Try

   If Not req Is Nothing Then
    req.Abort()
    CompleteRequest()
   End If

  Catch e As System.Exception
   Console.WriteLine(e.Message)
  End Try

 End Sub

 '******************************************************************************
 ' When the asynchronous HTTP transation completes we fetch the results
 '******************************************************************************/
 Private Sub CompleteRequest()

  Dim isOpen As Boolean
  Dim city, outlook, temp, wind As String
  Dim doc As XmlDocument
  Dim msg As StringBuilder
  Dim mgr As XmlNamespaceManager

  doc = Nothing
  resp = Nothing
  isOpen = False
  weather = Nothing

  Try

   resp = req.EndGetResponse(iar)
   isOpen = True

   ' The response from the web service is XML so load it into a document

   doc = New XmlDocument
   doc.Load(resp.GetResponseStream())

   ' Get a namespace manager and add the namespace

   mgr = New XmlNamespaceManager(doc.NameTable)
   mgr.AddNamespace("yweather", "http://xml.weather.yahoo.com/ns/rss/1.0")

   ' Then pull the city, temperature, current condition and wind speed from it

   city = GetCity(doc, mgr)
   temp = GetTemperature(doc, mgr)
   outlook = GetOutlook(doc, mgr)
   wind = GetWind(doc, mgr)

   ' We take those bits of information to build a two sentence forecast

   msg = New StringBuilder
   msg.Append("In ")
   msg.Append(city)
   msg.Append(", the weather is ")
   msg.Append(outlook)
   msg.Append(" with a temperature of ")
   msg.Append(temp)
   msg.Append(". The winds are ")
   msg.Append(wind)
   msg.Append(".")

   weather = msg.ToString()

  Catch e As InvalidOperationException
   isOpen = False

  Catch e As System.Exception
   Console.WriteLine(e.Message)

  ' Make sure we close the connection when we are done

  Finally
   If (isOpen) Then resp.Close()
  End Try

  ' If we failed to build a forecast we'll offer an apology instead

  If weather = Nothing Then weather = regrets

  End Sub

 '******************************************************************************
 ' Starts an asynchronous HTTP GET transaction
 '******************************************************************************/
 Private Sub RequestWeather()

  req = WebRequest.Create(baseQuery + zip)
  iar = req.BeginGetResponse(Nothing, Nothing)

 End Sub

 '******************************************************************************
 ' Apologizes to the caller when we can't recognize his speech
 '******************************************************************************/
 Private Sub NoRecognition()

  rec.reset()
  syn.speak("I'm sorry, but I didn't get that.")
  syn.wait()

 End Sub

End Class