Asterisk呼叫中心之由xml生成结构菜单(三)
来源:互联网 发布:linux 获取系统时区 编辑:程序博客网 时间:2024/04/26 17:44
引语:
xml文件作为配置文件,会给程序带来很大的便利性,只需直接配置xml文件,然后我们的程序依次去读取和解析xml文件,即可生成相应的结构树。
由此,设想:用户只需要随意的在可视化界面增添修改相关目录和文件,即可完成自己所需要的个性化功能?而不需要去看底层程序。
任务解读:
在呼叫中心中有xml文件如下:
<?xml version="1.0"?>
<menu>
<mainmenu flags=0 title="IVR Demo Main Menu">
<option option="s" action=AST_ACTION_BACKGROUND str="demo-congrats"></option>
<option option="g" action=AST_ACTION_BACKGROUND str="demo-instruct"></option>
<option option="g" action=AST_ACTION_WAITOPTION></option>
<option option="1" action=AST_ACTION_PLAYBACK str="digits/1"></option>
<option option="1" action=AST_ACTION_RESTART></option>
<option option="2" action=AST_ACTION_MENU submenu="ivr_submenu"></option>
<option option="2" action=AST_ACTION_RESTART></option>
<option option="i" action=AST_ACTION_PLAYBACK str="invalid"></option>
<option option="i" action=AST_ACTION_REPEAT ulong=2></option>
<option option="#" action=AST_ACTION_EXIT></option>
</mainmenu>
<submenu1 flags=0 title="IVR Demo Sub Menu1">
<option option="s" action=AST_ACTION_BACKGROUND str="demo-abouttotry"></option>
<option option="s" action=AST_ACTION_WAITOPTION></option>
<option option="1" action=AST_ACTION_PLAYBACK str="digits/1"></option>
<option option="1" action=AST_ACTION_RESTART></option>
<option option="2" action=AST_ACTION_PLAYLIST str="digits/2;digits/3"></option>
<option option="3" action=AST_ACTION_CALLBACK fun="ivr_demo_func"></option>
<option option="*" action=AST_ACTION_REPEAT></option>
<option option="#" action=AST_ACTION_UPONE></option>
</submenu1>
</menu>
这是一个典型的树结构。文件节点树看上去如下所示:(这里"-"指向下一个节点,"|"指向第一个子节点。)
menu | mainmenu - submenu
| option - option - option | | |
XXX XXX XXX 这里对应几个不同属性的option
.。
而呼叫中心菜单项的结构体为:
struct ast_ivr_menu {
char *title; /*!< Title of menu */
unsigned int flags;/*!< Flags */
struct ast_ivr_option *options;/*!< All options */
};
struct ast_ivr_option {
char *option;
ast_ivr_action action;
void *adata;
};
在本呼叫中心系统即有两个任务:
一、从xml文件读取xml节点树,并返回一个节点树的头。
二、如何将此节点树与ivr的菜单项对接起来?
一、从xml文件读取xml节点树,并返回一个节点树的头。
str;
str=mxmlFindElement(str, mainmenu, "option", NULL, NULL,MXML_NO_DESCEND))
int value;
const char *string;
} value_string;
static const value_string option_vals[] = {
{ AST_ACTION_UPONE, "AST_ACTION_UPONE" },
{ AST_ACTION_EXIT, "AST_ACTION_EXIT" },
{ AST_ACTION_CALLBACK, "AST_ACTION_CALLBACK" },
{ AST_ACTION_PLAYBACK, "AST_ACTION_PLAYBACK" },
{ AST_ACTION_BACKGROUND, "AST_ACTION_BACKGROUND" },
{ AST_ACTION_PLAYLIST, "AST_ACTION_PLAYLIST" },
{ AST_ACTION_MENU, "AST_ACTION_MENU" },
{ AST_ACTION_REPEAT, "AST_ACTION_REPEAT" },
{ AST_ACTION_RESTART, "AST_ACTION_RESTART" },
{ AST_ACTION_TRANSFER, "AST_ACTION_TRANSFER" },
{ AST_ACTION_WAITOPTION, "AST_ACTION_WAITOPTION" },
{ AST_ACTION_NOOP, "AST_ACTION_NOOP" },
{ AST_ACTION_BACKLIST, "AST_ACTION_BACKLIST" }
};
{
int i ;
int action_num = sizeof(option_vals) / sizeof(option_vals[0]);
// fprintf(stderr,"The action_num is %d\n",action_num);
for(i=0;i<action_num;i++)
{
// fprintf(stderr,"The str is %s\n",str);
// fprintf(stderr,"The option_vals[i].string is %s\n",option_vals[i].string);
if( !strcmp(str,option_vals[i].string) )
{
return (ast_ivr_action)i;
}
}
}
同样的对option第三个参数进行解析时,也需要从string转换为ast_ivr_menu_t*(若str=submenu) 和ivr_demo_func(若str=func)。不同类型的进行转换时考虑这三部曲,结构体、结构体数组实例化、循环匹配并返回。
二、如何将此节点树与ivr的菜单项对接起来?
在将读取xml的菜单之后,如何将这个解析出来的结构体头与ivrdemohanshu对接起来的过程中碰到如下问题:
1、首先是枚举类型重复定义,一直想的是如何将枚举类型定义为静态的,这样就可以在两个文件中不会冲突,这是犯的第一个错误。
2、犯得第二个错误是,一直在注重enum的静态。没有去认真查错误的真正原因,在网友Jagen的帮助下,才得知错误的原因。
总结如下:
错误:/usr/include/asterisk/app.h:210: error: expected specifier-qualifier-list before ‘AST_LIST_ENTRY 。
查找原因如下:会遇到expected specifier-qualifier-list before sth之类的错误。specifiers是指void、char、struct Foo等词汇;qualifiers是指像const和volatile一类的关键字。一个词汇再未定义之前就使用就会出项这种错误,可以通过typedef进行定义以后再使用。
未定义???
网友解决过程:struct ast_group_info {
struct ast_channel *chan;
char *category;
char *group;
AST_LIST_ENTRY(ast_group_info) list;
}; 这是在app.h里面的定义。
#define AST_LIST_ENTRY(type) 这是在linkedlists.h里面的定义。网友的解决思路就是这个定义的头文件需要包含进来。
查看其它的几个demo程序,居然都包含了好几个库的头文件,于是心头一动,全部包含进来,居然。。。。。编译成功!!
解决方案:包含如下头文件:
#include "asterisk.h"
#include "asterisk/file.h"
#include "asterisk/logger.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/lock.h"
#include "asterisk/app.h"
因为枚举类型、静态 动态、文件包含很多几个知识点都不是很熟,所以会出现这样的出了问题,胡乱一搜,没明白具体出现在哪里。
总结:1、多请教。 2、查找错误,关键是理解错误,还是不懂原理。 3、多和demo程序比较。 4、看书啊看书。
完整程序如下:
#include<stdio.h>#include <string.h>#include<mxml.h>#include <assert.h>#define FILE_PATH "./filename.xml"//define for menu attr#define TITLE "title"#define FLAGS "flags"//define for option attr#define OPTION "option"#define ACTION "action"#define STR "str" typedef enum {False = 0, True = 1} BOOL;typedef struct ivr_menu_element{const char *title_str;const char *flags_str;}ivr_menu_element;typedef struct ivr_option_element{const char *option_str; const char *action_str;const char *str_str;}ivr_option_element;typedef enum {AST_ACTION_UPONE, /*!< adata is unused */AST_ACTION_EXIT, /*!< adata is the return value for ast_ivr_menu_run if channel was not hungup */AST_ACTION_CALLBACK, /*!< adata is an ast_ivr_callback */AST_ACTION_PLAYBACK, /*!< adata is file to play */AST_ACTION_BACKGROUND, /*!< adata is file to play */AST_ACTION_PLAYLIST, /*!< adata is list of files, separated by ; to play */AST_ACTION_MENU, /*!< adata is a pointer to an ast_ivr_menu */AST_ACTION_REPEAT, /*!< adata is max # of repeats, cast to a pointer */AST_ACTION_RESTART, /*!< adata is like repeat, but resets repeats to 0 */AST_ACTION_TRANSFER, /*!< adata is a string with exten\verbatim[@context]\endverbatim */AST_ACTION_WAITOPTION, /*!< adata is a timeout, or 0 for defaults */AST_ACTION_NOOP, /*!< adata is unused */AST_ACTION_BACKLIST, /*!< adata is list of files separated by ; allows interruption */} ast_ivr_action; typedef enum {STR_ATTR,SUBMENU_ATTR,ULONG_ATTR,FUN_ATTR} option_last_attr_flag;struct ast_ivr_option { char *option; ast_ivr_action action; void *adata;}; struct ast_ivr_menu { char *title; /*!< Title of menu */ unsigned int flags; /*!< Flags */ struct ast_ivr_option *options; /*!< All options */ }; typedef struct _value_string { int value; const char *string;} value_string;//this table use to change string to ast_ivr_actionstatic const value_string option_vals[] = {{ AST_ACTION_UPONE, "AST_ACTION_UPONE" },{ AST_ACTION_EXIT, "AST_ACTION_EXIT" },{ AST_ACTION_CALLBACK, "AST_ACTION_CALLBACK" },{ AST_ACTION_PLAYBACK, "AST_ACTION_PLAYBACK" },{ AST_ACTION_BACKGROUND, "AST_ACTION_BACKGROUND" },{ AST_ACTION_PLAYLIST, "AST_ACTION_PLAYLIST" },{ AST_ACTION_MENU, "AST_ACTION_MENU" },{ AST_ACTION_REPEAT, "AST_ACTION_REPEAT" },{ AST_ACTION_RESTART, "AST_ACTION_RESTART" },{ AST_ACTION_TRANSFER, "AST_ACTION_TRANSFER" },{ AST_ACTION_WAITOPTION, "AST_ACTION_WAITOPTION" },{ AST_ACTION_NOOP, "AST_ACTION_NOOP" },{ AST_ACTION_BACKLIST, "AST_ACTION_BACKLIST" }};static value_string option_last_attr_vals[] = {{ STR_ATTR, "str" },{ SUBMENU_ATTR, "submenu" },{ ULONG_ATTR, "ulong" },{ FUN_ATTR, "fun" }};typedef struct ast_ivr_menu ast_ivr_menu_t ;typedef struct ast_ivr_option ast_ivr_option_t ;static ast_ivr_menu_t ivr_menu;static ast_ivr_menu_t ivr_submenu;typedef struct _string_to_menu {const char *string;ast_ivr_menu_t * menu_ptr;} string_to_menu;//this table use to change string to menu pointerstatic string_to_menu string_menu_vals[] = {{ "ivr_submenu",&ivr_submenu}};typedef int (*ivr_demo_func)(const char *chan, void *data);typedef struct _string_to_fun { const char *string;ivr_demo_func fun;} string_to_fun;static int ivr_demo_function(const char *chan, void *data){fprintf(stderr, "Open the file %s error!\n",(char *)data);return 1;}static string_to_fun string_fun_vals[] = {{ "ivr_demo_func", ivr_demo_function }};static BOOL parse_menu_attrs(mxml_node_t *node, struct ast_ivr_menu *menu_des); static BOOL parse_option_attrs(mxml_node_t *node,int num_attrs,ast_ivr_option_t *option_des);static BOOL load_xml_file(const char *filename,const char *menuname,ast_ivr_menu_t *ivr_menu_ptr);static ast_ivr_action str_to_action(const char *str);static BOOL parse_last_option_attr(mxml_node_t *node,ast_ivr_option_t *option_des);static ast_ivr_menu_t* string_to_menu_pointer(const char *str);static ast_ivr_action str_to_action(const char *str);static ivr_demo_func string_to_fun_pointer(const char *str);int main(){int i=0;load_xml_file(FILE_PATH,"mainmenu",&ivr_menu);printf("解析出:title=%s flags=%d\n",ivr_menu.title,ivr_menu.flags);while(i<=2){// fprintf(stderr,"The ivr_menu.options[%d] is %s\n",i,ivr_menu.options[i]);fprintf(stderr,"The ivr_menu.options[%d] :\n",i);fprintf(stderr,"\tThe option %s \n",(char *)ivr_menu.options[i].option);fprintf(stderr,"\tThe action %d \n",(ast_ivr_action)ivr_menu.options[i].action);// fprintf(stderr,"\tThe adata %s \n",(char *)ivr_menu.options[i].adata);i++;}}static BOOL load_xml_file(const char *filename,const char *menuname,ast_ivr_menu_t *ivr_menu_ptr){FILE *fp;mxml_node_t *tree;mxml_node_t *data;mxml_node_t *str;mxml_node_t *menu,*mainmenu;// if((fp=fopen(filename,"r"))==NULL){printf("Open the file error!\n");return 0;}tree=mxmlLoadFile(NULL,fp,MXML_TEXT_CALLBACK);if((menu=mxmlFindElement(tree,tree,NULL,NULL,NULL,MXML_DESCEND))==NULL){printf( "Unable to read the XML tree!\n");return 0;}if((mainmenu=mxmlFindElement(menu, menu, menuname, NULL, NULL,MXML_DESCEND))==NULL){printf("Unable to find first element in XML tree!\n");return 0;}// ivr_menu_element menu_des;if( !parse_menu_attrs(mainmenu,ivr_menu_ptr) ) {printf("Parse menu attrs error !\n");return 0;}printf("%s\n",ivr_menu_ptr->title);// ivr_menu_ptr.title=menu_des.title_str;// printf("%s\n",ivr_menu.title);printf("%d\n",ivr_menu_ptr->flags);int i=0;for(str=mxmlFindElement(mainmenu, mainmenu, "option", NULL, NULL,MXML_DESCEND_FIRST);str;str=mxmlFindElement(str, mainmenu, "option", NULL, NULL,MXML_NO_DESCEND)){ast_ivr_option_t option_des;int num_attrs = str->value.element.num_attrs ; //<option.......>内包含的属性数量//printf("%s\n",str->child->value.text.string);parse_option_attrs(str,num_attrs,&option_des);printf("option:%s\t action:%d\t adata:%s\t\n",option_des.option,option_des.action,option_des.adata);i++;}fclose(fp);return 0;}/* to parse the "flags=0 " and "title="IVR Demo Main Menu"" in "<mainmenu flags=0 title="IVR Demo Main Menu"> " */static BOOL parse_menu_attrs(mxml_node_t *node, struct ast_ivr_menu *menu_des){assert( node != NULL );const char *tmp = NULL ;tmp = mxmlElementGetAttr(node,TITLE) ;if(tmp==NULL ){return 0;}menu_des->title = (char *)tmp ;tmp = mxmlElementGetAttr(node,FLAGS) ;if(tmp==NULL ){return 0;}menu_des->flags = atoi(tmp);return True;}static BOOL parse_option_attrs(mxml_node_t *node,int num_attrs,ast_ivr_option_t *option_des){const char *tmp = NULL; //mxmlElementGetAtt返回类型为const char *tmp = mxmlElementGetAttr(node,OPTION) ;if(tmp==NULL ){return 0;}option_des->option =(char *)tmp ;tmp = mxmlElementGetAttr(node,ACTION) ;if(tmp==NULL ){return 0;}option_des->action=str_to_action(tmp);if(num_attrs == 3){parse_last_option_attr(node,option_des);}}static BOOL parse_last_option_attr(mxml_node_t *node,ast_ivr_option_t *option_des){const char *tmp;int num = sizeof(option_last_attr_vals) / sizeof(option_last_attr_vals[0]);// fprintf(stderr,"The num is %d\n",num);int i;BOOL found;for(i=0;i<num;i++){tmp = mxmlElementGetAttr(node,option_last_attr_vals[i].string);if(tmp !=NULL){switch((option_last_attr_flag)i){case STR_ATTR:option_des->adata = (void *)tmp ;found = True;break;case SUBMENU_ATTR:{ast_ivr_menu_t* menu = string_to_menu_pointer(tmp);if( !menu ){found = False ;}option_des->adata = (void *)menu;found = True;}break;case ULONG_ATTR:option_des->adata = (void *)atoi(tmp);found = True;break;case FUN_ATTR:{ivr_demo_func fun = string_to_fun_pointer(tmp);if( !fun ){found = False ;}option_des->adata = (void *)fun;found = True;}break;default:found = False ;break;}}}}static ast_ivr_action str_to_action(const char *str) {int i ;int action_num = sizeof(option_vals) / sizeof(option_vals[0]);// fprintf(stderr,"The action_num is %d\n",action_num);for(i=0;i<action_num;i++){// fprintf(stderr,"The str is %s\n",str);// fprintf(stderr,"The option_vals[i].string is %s\n",option_vals[i].string);if( !strcmp(str,option_vals[i].string) ){return (ast_ivr_action)i;}}}static ast_ivr_menu_t* string_to_menu_pointer(const char *str) //因为前面定义的类型为ast_ivr_menu_t*{int i;int num=sizeof(string_menu_vals) / sizeof(string_menu_vals[0]);for(i=0;i<num;i++){if(!strcmp(str,string_menu_vals[i].string)){return string_menu_vals[i].menu_ptr;}elsereturn (ast_ivr_menu_t*)NULL;}}static ivr_demo_func string_to_fun_pointer(const char *str){int i ;int num = sizeof(string_fun_vals) / sizeof(string_fun_vals[0]);for(i=0;i<num;i++){if( !strcmp(str,string_fun_vals[i].string) ){return string_fun_vals[i].fun ;}elsereturn (ivr_demo_func)NULL;}}
- Asterisk呼叫中心之由xml生成结构菜单(三)
- Asterisk呼叫中心之语音导航(一)
- Asterisk呼叫中心之数据库查询与播报(二)
- Asterisk呼叫中心之web后台(四)
- Asterisk呼叫中心之freepbx安装(五)
- 开源 Asterisk 呼叫中心客户端
- Asterisk呼叫中心优缺点分析
- 呼叫中心开发框架Asterisk
- 第五代呼叫中心之SOA(三)
- Asterisk Queue呼叫中心的实现
- Asterisk Queue呼叫中心的实现
- asterisk 呼叫中心软件系统最新动态
- Asterisk Queue呼叫中心的实现
- Asterisk Queue呼叫中心的实现
- 浅谈呼叫中心结构化通均管控
- 由程控交换机组成的呼叫中心系统
- asterisk 呼叫中心改造 命令跳转 判断输入等
- freeswitch 解决方案之呼叫中心
- iOS弹键盘、收键盘
- leetcode - ZigZag Conversion
- 当类是ToolBar时候,设置代理不能用delegate关键字
- 线程(四)Monitor
- 关于{ 正在创建 .unsuccessfulbuild 因为已指定“AlwaysCreate” }的解决方案
- Asterisk呼叫中心之由xml生成结构菜单(三)
- Ubuntu14.04下JDK安装
- 让某种视图view成为键盘输入辅助工具
- vim中跳转到头文件
- glPushMatrix() glPopMatrix() GL_DEPTH_TEST
- 切割子视图越界的部分【将越界部分设置为不可见从而达到效果】
- 上传图片时预览效果
- 根据字符串长度计算像素大小
- 第九周项目3(4)