'****************************************************************************** ' 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