📄 vxml.cxx
字号:
else {
// Get the body of the response in a PBYTEArray (might be binary data)
PBYTEArray incomingData;
client.ReadContentBody(replyMIME, incomingData);
contentType = replyMIME(PHTTPClient::ContentTypeTag);
// write the data in the file
PFile cacheFile(fn, PFile::WriteOnly);
cacheFile.Write(incomingData.GetPointer(), incomingData.GetSize() );
// if we have a cache and we are using it, then save the data
if (useCache)
PVXMLCache::GetResourceCache().Put("url", url.AsString(), fileType, contentType, fn, dataFn);
// data is loaded
stat = TRUE;
}
}
}
// files on the local file system get loaded locally
else if (url.GetScheme() *= "file") {
dataFn = url.AsFilePath();
stat = TRUE;
}
// unknown schemes give an error
else
stat = FALSE;
return stat;
}
PXMLElement * PVXMLSession::FindForm(const PString & id)
{
// NOTE: should have some flag to know if it is loaded
PXMLElement * root = xmlFile.GetRootElement();
if (root == NULL)
return NULL;
// Only handle search of top level nodes for <form> element
PINDEX i;
for (i = 0; i < root->GetSize(); i++) {
PXMLObject * xmlObject = root->GetElement(i);
if (xmlObject->IsElement()) {
PXMLElement * xmlElement = (PXMLElement*)xmlObject;
if (
(xmlElement->GetName() *= "form") &&
(id.IsEmpty() || (xmlElement->GetAttribute("id") *= id))
)
return xmlElement;
}
}
return NULL;
}
BOOL PVXMLSession::Open(BOOL isPCM)
{
if (isPCM)
return Open(VXML_PCM16);
else
return Open(VXML_G7231);
}
BOOL PVXMLSession::Open(const PString & _mediaFormat)
{
mediaFormat = _mediaFormat;
PVXMLChannel * chan = PFactory<PVXMLChannel>::CreateInstance(mediaFormat);
if (chan == NULL) {
PTRACE(1, "VXML\tCannot create VXML channel with format " << mediaFormat);
return FALSE;
}
chan->SetVXMLSession( this );
// set the underlying channel
if (!PIndirectChannel::Open(chan, chan))
return FALSE;
// start the VXML session in another thread
{
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();
}
}
void PVXMLSession::OnEndPlay( const PString & identifier )
{
}
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("beep", beepData);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -