📄 vxml.cxx
字号:
{ PWaitAndSignal m(sessionMutex); if (!chan->Open(this)) return FALSE; vxmlChannel = chan; } return Execute();}BOOL PVXMLSession::Execute(){ PWaitAndSignal m(sessionMutex); // cannot open if no data is loaded if (loaded && vxmlThread == NULL) { threadRunning = TRUE; vxmlThread = PThread::Create(PCREATE_NOTIFIER(VXMLExecute), 0, PThread::NoAutoDeleteThread); } return TRUE;}BOOL PVXMLSession::Close(){ { PWaitAndSignal m(sessionMutex); if (vxmlThread != NULL) { // Stop condition for thread threadRunning = FALSE; forceEnd = TRUE; waitForEvent.Signal(); // Signal all syncpoints that could be waiting for things transferSync.Signal(); answerSync.Signal(); vxmlChannel->Close(); vxmlThread->WaitForTermination(); delete vxmlThread; vxmlThread = NULL; } vxmlChannel = NULL; } return PIndirectChannel::Close();}void PVXMLSession::VXMLExecute(PThread &, INT){ while (!forceEnd && threadRunning) { // process current node in the VXML script ExecuteDialog(); // wait for something to happen if (currentNode == NULL || IsPlaying()) waitForEvent.Wait(); } // Make sure the script has been run to the end so // submit actions etc. can be performed // record and audio and other user interaction commands should be skipped if (forceEnd) { PTRACE(1, "Fast forwarding through script because of forceEnd" ); while (currentNode != NULL) ExecuteDialog(); } OnEndSession(); //PWaitAndSignal m(sessionMutex); if (vxmlChannel != NULL) vxmlChannel->Close(); return;}void PVXMLSession::ProcessUserInput(){ // without this initialisation, gcc 4.1 gives a warning char ch = 0; { PWaitAndSignal m(userInputMutex); if (userInputQueue.size() == 0) return; ch = userInputQueue.front(); userInputQueue.pop(); } PTRACE(3, "VXML\tHandling user input " << ch); // recording if (recording) { if (recordDTMFTerm) RecordEnd(); } // playback else { if (activeGrammar != NULL) activeGrammar->OnUserInput(ch); }}void PVXMLSession::ExecuteDialog(){ // check for user input ProcessUserInput(); // process any active grammars ProcessGrammar(); // process current node in the VXML script ProcessNode(); // Wait for the buffer to complete before continuing to the next node if (currentNode == NULL) { if (IsPlaying()) return; } // if the current node has children, then process the first child else if (currentNode->IsElement() && (((PXMLElement *)currentNode)->GetElement(0) != NULL)) currentNode = ((PXMLElement *)currentNode)->GetElement(0); // else process the next sibling else { // Keep moving up the parents until we find a next sibling while ((currentNode != NULL) && currentNode->GetNextObject() == NULL) { currentNode = currentNode->GetParent(); // if we are on the backwards traversal through a <field> then wait // for a grammar recognition and throw events if necessary if (currentNode != NULL && (currentNode->IsElement() == TRUE) && (((PXMLElement*)currentNode)->GetName() *= "field")) { listening = TRUE; PlaySilence(timeout); } } if (currentNode != NULL) currentNode = currentNode->GetNextObject(); } // Determine if we should quit if ((currentNode == NULL) && (activeGrammar == NULL) && !IsPlaying() && !IsRecording() && allowFinish && finishWhenEmpty) { threadRunning = FALSE; waitForEvent.Signal(); }}void PVXMLSession::ProcessGrammar(){ if (activeGrammar == NULL) return; BOOL processGrammar(FALSE); // Stop if we've matched a grammar or have a failed recognition if (activeGrammar->GetState() == PVXMLGrammar::FILLED || activeGrammar->GetState() == PVXMLGrammar::NOMATCH) processGrammar = TRUE; // Stop the grammar if we've timed out else if (listening && !IsPlaying()) { activeGrammar->Stop(); processGrammar = TRUE; } // Let the loop run again if we're still waiting to time out and haven't resolved the grammar one way or the other if (!processGrammar && listening) return; if (processGrammar) { PVXMLGrammar::GrammarState state = activeGrammar->GetState(); grammarResult = activeGrammar->GetValue(); LoadGrammar(NULL); listening = FALSE; // Stop any playback if (vxmlChannel != NULL) { vxmlChannel->FlushQueue(); vxmlChannel->EndRecording(); } // Check we're not in a menu if (eventName.IsEmpty()) { // Figure out what happened switch (state) { case PVXMLGrammar::FILLED: eventName = "filled"; break; case PVXMLGrammar::NOINPUT: eventName = "noinput"; break; case PVXMLGrammar::NOMATCH: eventName = "nomatch"; break; default: ; //ERROR - unexpected grammar state } // Find the handler and move there PXMLElement * handler = FindHandler(eventName); if (handler != NULL) currentNode = handler; } }}void PVXMLSession::ProcessNode(){ if (currentNode == NULL) return; if (!currentNode->IsElement()) { if (!forceEnd) TraverseAudio(); else currentNode = NULL; } else { PXMLElement * element = (PXMLElement*)currentNode; PCaselessString nodeType = element->GetName(); PTRACE(3, "PVXML\t**** Processing VoiceXML element: <" << nodeType << "> ***"); if (nodeType *= "audio") { if (!forceEnd) TraverseAudio(); } else if (nodeType *= "block") { // check 'cond' attribute to see if this element's children are to be skipped // go on and process the children } else if (nodeType *= "break") TraverseAudio(); else if (nodeType *= "disconnect") currentNode = NULL; else if (nodeType *= "field") { currentField = (PXMLElement*)currentNode; timeout = DEFAULT_TIMEOUT; TraverseGrammar(); // this will set activeGrammar } else if (nodeType *= "form") { // this is now the current element - go on currentForm = element; currentField = NULL; // no active field in a new form } else if (nodeType *= "goto") TraverseGoto(); else if (nodeType *= "grammar") TraverseGrammar(); // this will set activeGrammar else if (nodeType *= "record") { if (!forceEnd) TraverseRecord(); } else if (nodeType *= "prompt") { if (!forceEnd) { // LATER: // check 'cond' attribute to see if the children of this node should be processed // check 'count' attribute to see if this node should be processed // flush all prompts if 'bargein' attribute is set to false // Update timeout of current recognition (if 'timeout' attribute is set) if (element->HasAttribute("timeout")) { PTimeInterval timeout = StringToTime(element->GetAttribute("timeout")); } } } else if (nodeType *= "say-as") { if (!forceEnd) { } } else if (nodeType *= "value") { if (!forceEnd) TraverseAudio(); } else if (nodeType *= "var") TraverseVar(); else if (nodeType *= "if") TraverseIf(); else if (nodeType *= "exit") TraverseExit(); else if (nodeType *= "menu") { if (!forceEnd) { TraverseMenu(); eventName = "menu"; } } else if (nodeType *= "choice") { if (!TraverseChoice(grammarResult)) defaultDTMF++; else { // If the correct choice has been found, /// make sure everything is reset correctly eventName.MakeEmpty(); grammarResult.MakeEmpty(); defaultDTMF = 1; } } else if (nodeType *= "transfer") { if (!forceEnd) TraverseTransfer(); } else if (nodeType *= "submit") TraverseSubmit(); else if (nodeType *= "property") TraverseProperty(); }}BOOL PVXMLSession::OnUserInput(const PString & str){ { PWaitAndSignal m(userInputMutex); for (PINDEX i = 0; i < str.GetLength(); i++) userInputQueue.push(str[i]); } waitForEvent.Signal(); return TRUE;}BOOL PVXMLSession::TraverseRecord(){ if (currentNode->IsElement()) { PString strName; PXMLElement * element = (PXMLElement *)currentNode; // Get the name (name) if (element->HasAttribute("name")) strName = element->GetAttribute("name"); else if (element->HasAttribute("id")) strName = element->GetAttribute("id"); // Get the destination filename (dest) PString strDest; if (element->HasAttribute("dest")) strDest = element->GetAttribute("dest"); // see if we need a beep if (element->GetAttribute("beep").ToLower() *= "true") { PBYTEArray beepData; GetBeepData(beepData, 1000); if (beepData.GetSize() != 0) PlayData(beepData); } if (strDest.IsEmpty()) { PTime now; strDest = GetVar("session.telephone.dnis" ) + "_" + GetVar( "session.telephone.ani" ) + "_" + now.AsString( "yyyyMMdd_hhmmss") + ".wav"; } // For some reason, if the file is there the create // seems to fail. PFile::Remove(strDest); PFilePath file(strDest); // Get max record time (maxtime) PTimeInterval maxTime = PMaxTimeInterval; if (element->HasAttribute("maxtime")) maxTime = StringToTime(element->GetAttribute("maxtime")); // Get terminating silence duration (finalsilence) PTimeInterval termTime(3000); if (element->HasAttribute("finalsilence")) termTime = StringToTime(element->GetAttribute("finalsilence")); // Get dtmf term (dtmfterm) BOOL dtmfTerm = TRUE; if (element->HasAttribute("dtmfterm")) dtmfTerm = !(element->GetAttribute("dtmfterm").ToLower() *= "false"); // create a semaphore, and then wait for the recording to terminate StartRecording(file, dtmfTerm, maxTime, termTime); recordSync.Wait(maxTime); if (!recordSync.Wait(maxTime)) { // The Wait() has timed out, to signal that the record timed out. // This is VXML version 2 property, but nice. // So it's possible to detect if the record timed out from within the // VXML script SetVar(strName + "$.maxtime", "true"); } else { // Normal hangup before timeout SetVar( strName + "$.maxtime", "false"); } // when this returns, we are done EndRecording(); } return TRUE;}PString PVXMLSession::GetXMLError() const{ return psprintf("(%i:%i) ", xmlFile.GetErrorLine(), xmlFile.GetErrorColumn()) + xmlFile.GetErrorString();}PString PVXMLSession::EvaluateExpr(const PString & oexpr){ PString expr = oexpr.Trim(); // see if all digits PINDEX i; BOOL allDigits = TRUE; for (i = 0; i < expr.GetLength(); i++) { allDigits = allDigits && isdigit(expr[i]); } if (allDigits) return expr; return GetVar(expr);}PString PVXMLSession::GetVar(const PString & ostr) const{ PString str = ostr; PString scope; // get scope PINDEX pos = str.Find('.'); if (pos != P_MAX_INDEX) { scope = str.Left(pos); str = str.Mid(pos+1); } // process session scope if (scope.IsEmpty() || (scope *= "session")) { if (sessionVars.Contains(str)) return sessionVars(str); } // assume any other scope is actually document or application return documentVars(str);}void PVXMLSession::SetVar(const PString & ostr, const PString & val){ PString str = ostr; PString scope; // get scope PINDEX pos = str.Find('.'); if (pos != P_MAX_INDEX) { scope = str.Left(pos); str = str.Mid(pos+1); } // do session scope if (scope.IsEmpty() || (scope *= "session")) { sessionVars.SetAt(str, val); return; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -