知行合一
全部博文(31)
分类: 系统运维
2010-05-04 21:38:29
“//” 注释掉整行
注释符会被词法扫描器移出,但是在应用和表达式中的“//”不会被辨认出来。最安全的放置注释符的地方是在”;”后或在空行中
最新的语法,可以像C格式的注释一样
/* this is a
multiline comment that
makes little or no sense!
*/
AEL中的上下文和extensions.conf中一样,都是分机(extensions)的集合。
context default {
}
上下文也可以被声明为“abstract”,在这种情况下,这种声明表现了作者的意图。这种上下文只用来被包含在另一个上下文中,不会单独使用(译者注:单独使用也是可以的)。目前这个关键字被用来防止“goto”语句被检查。
abstract context longdist {
_1NXXNXXXXXX => NoOp(generic long distance dialing actions in the
}
要在上下文中指定分机,那么要使用下面的语法。如果在分机中不止一个应用被调用,那么可以按照顺序将它们列在一个块(block)中。
context default {
1234 => Playback(tt-monkeys);
8000 => {
NoOp(one);
NoOp(two);
NoOp(three);
};
_5XXX => NoOp(it's a pattern!);
}
有两条可供选择的功能被添加进了AEL语法中,一条是描述,一条就是关键字匹配(本条功能会将该优先级强制变为2.)
你可以使用‘/’和CID数字结合的方式来匹配CID,见下面的例子:
context default {
regexten _5XXX => NoOp(it's a pattern!);
}
context default {
hint(Sip/1) _5XXX => NoOp(it's a pattern!);
}
context default {
regexten hint(Sip/1) _5XXX => NoOp(it's a pattern!);
}
如果两者要同时存在,那么必须将后者放在前者的前面。
CID匹配和extensions.conf一样。’/’后面的数字会匹配呼叫着的ID。
context zoombo
{
819/7079953345 => { NoOp(hello, 3345); }
}
上例中,如果呼叫着的ID是7079953345,并且拨叫的号码是819,那么将进入这个分机中进行处理。
上下文可以被包含在另外的上下文中。所有被包含的上下文要在一个单独的block中列出。
context default {
includes {
local;
longdistance;
international;
}
}
也可以指定限制的时间,就像extensions.conf文件格式一样。
context default {
includes {
local;
longdistance|16:00-23:59|mon-fri|*|*;
international;
}
}
你可以使用#include+“文件路径”的方式来包含其他的文件。
#include "/etc/asterisk/testfor.ael"
#include一个有意思的特点就是你可以在ael文件中的任何位置使用。甚至于在宏,上下文,分机中都可以包含其他文件。#include不必写在一行的开始处。被包含文件可以包含其他文件,可以达到50级。如果你提供的路径是相对路径,那么解析器会在配置文件的目录中去寻找包含的文件。(通常是/etc/asterisk)
Switch在上下文中被列在它们自己的block当中。使用它们的原因可以在asterisk-dual servers, 以及asterisk 配置文件extensions.conf中找到。
context default {
switches {
DUNDi/e164;
IAX2/box5;
};
eswitches {
IAX2/context@${CURSERVER};
}
}
Ignorepat可以用来告诉通道驱动在收到匹配的样式之前不停止拨号音。最常见的例子是‘
context outgoing {
ignorepat => 9;
}
Asterisk中的变量没有类型,所以如果要定义一个变量,必须要指定它的值先。
全局变量有它们自己的块(block)
globals {
CONSOLE=Console/dsp;
TRUNK=DAHDI/g2;
}
变量也可以在分机中被定义。
context foo {
555 => {
x=5;
y=blah;
divexample=10/2
NoOp(x is ${x} and y is ${y} !);
}
}
注意:AEL可以使用$[]来将赋值语句的右边的部分括起来以使用表达式。如果你不想这么干,那么可以通过使用Set()应用括在右边部分。可以查看README的variables部分来了解表达式$[]的要求和行为。
注意:下面的这些情况请使用$[]表达式:while();if();for(x;y;z)中的y表达式;赋值语句的右半部份,a=b -> Set(a=$[b])
向拨号方案函数写入数值可以同赋值变量相当对待。
context blah {
s => {
CALLERID(name)=ChickenMan;
NoOp(My name is ${CALLERID(name)} !);
}
}
你也可以像下面这样在一个宏中声明变量:
Macro myroutine(firstarg, secondarg)
{
Myvar=1;
NoOp(Myvar is set to ${myvar});
}
1.2和1.4的asterisk中,所有的变量都是通道变量(CHANNEL variables),包括函数的参数和相关的ARG1,ARG2还有其他的一些变量等。
而在1.6以及更高的版本中,对于一个宏调用,我们将变量都变成了本地变量。他们不会影响和他们同名的其他通道变量。这个包括ARG1,ARG2等等变量。
用户可以使用关键字local来声明他们自己的本地变量。
Macro myroutine(firstarg, secondarg)
{
local Myvar=1;
NoOp(Myvar is set to ${Myvar}, and firstarg is ${firstarg}, and secondarg is ${secondarg});
}
在上面的例子中,Myvar,firstarg,和secondarg都是本地变量,对于调用的代码(可能是一个分机,也可能是其他的宏)他们是不可见的
如果你需要在Set()应用中定义本地变量,你可以这样做:
Macro myroutine(firstarg, secondarg)
{
Set(LOCAL(Myvar)=1);
NoOp(Myvar is set to ${Myvar}, and firstarg is ${firstarg}, and secondarg is ${secondarg});
}
AEL实现了‘for’和‘while’的循环。
context loops {
1 => {
for (x=0; ${x} < 3; x=${x} + 1) {
Verbose(x is ${x} !);
}
}
2 => {
y=10;
while (${y} >= 0) {
Verbose(y is ${y} !);
y=${y}-1;
}
}
}
注意:条件表达式(上面的”${y} >=
注意:for循环表达式(上面的”$x <
AEL支持if和switch声明,并增加了ifTime和random。不像之前的AEL,不需要在if(),random()和ifTime()条件语句添加大括号。也允许有else语句。
context conditional {
_8XXX => {
Dial(SIP/${EXTEN});
if ("${DIALSTATUS}" = "BUSY")
{
NoOp(yessir);
Voicemail(${EXTEN},b);
}
else
Voicemail(${EXTEN},u);
ifTime (14:00-25:00,sat-sun,*,*)
Voicemail(${EXTEN},b);
else
{
Voicemail(${EXTEN},u);
NoOp(hi, there!);
}
random(51) NoOp(This should appear 51% of the time);
random( 60 )
{
NoOp( This should appear 60% of the time );
}
else
{
random(75)
{
NoOp( This should appear 30% of the time! );
}
else
{
NoOp( This should appear 10% of the time! );
}
}
}
_777X => {
switch (${EXTEN}) {
case 7771:
NoOp(You called 7771!);
break;
case 7772:
NoOp(You called 7772!);
break;
case 7773:
NoOp(You called 7773!);
// fall thru-
pattern 777[4-9]:
NoOp(You called 777 something!);
default:
NoOp(In the default clause!);
}
}
}
三个关键字,break,continue和return,为循环和switch提供流程的控制。
Break可以被用在switch和循环中,用来结束循环或是switch。
Continue可以用在循环中(while和for),立刻结束本次操作。
Return将结束context或macro,可以用在任何地方。
这个AEL中如何进行goto的例子。
context gotoexample {
s => {
begin:
NoOp(Infinite
Wait(1);
goto begin; // go to label in same extension
}
3 => {
goto s,begin; // go to label in different extension
}
4 => {
goto gotoexample,s,begin; // overkill go to label in same context
}
}
context gotoexample2 {
s => {
end:
goto gotoexample,s,begin; // go to label in different context
}
}
context gotoexample {
s => {
begin:
NoOp(Infinite
Wait(1);
jump s; // go to first extension in same extension
}
3 => {
jump s,begin; // go to label in different extension
}
4 => {
jump s,begin@gotoexample; // overkill go to label in same context
}
}
context gotoexample2 {
s => {
end:
jump s@gotoexample; // go to label in different context
}
}
“catch”block可以在宏中被标识一些特殊的分级(extensions)
macro std-exten( ext , dev ) {
Dial(${dev}/${ext},20);
switch(${DIALSTATUS) {
case BUSY:
Voicemail(${ext},b);
break;
default:
Voicemail(${ext},u);
}
catch a {
VoiceMailMain(${ext});
return;
}
}
宏可以使用下面的方法被引用,&std-exten。
context example {
_5XXX => &std-exten(${EXTEN}, "IAX2");
_6XXX => &std-exten(, "IAX2");
_7XXX => &std-exten(${EXTEN},);
_8XXX => &std-exten(,);
}
context demo {
s => {
Wait(1);
Answer();
TIMEOUT(digit)=5;
TIMEOUT(response)=10;
restart:
Background(demo-congrats);
instructions:
for (x=0; ${x} < 3; x=${x} + 1) {
Background(demo-instruct);
WaitExten();
}
}
2 => {
Background(demo-moreinfo);
goto s,instructions;
}
3 => {
LANGUAGE()=fr;
goto s,restart;
}
500 => {
Playback(demo-abouttotry);
Dial(IAX2/guest@misery.digium.com);
Playback(demo-nogo);
goto s,instructions;
}
600 => {
Playback(demo-echotest);
Echo();
Playback(demo-echodone);
goto s,instructions;
}
# => {
hangup:
Playback(demo-thanks);
Hangup();
}
t => goto #,hangup;
i => Playback(invalid);
}
AEL在解析后,编译前,将对拨号方案树做一些检查:
l 调用不存在的宏
l 宏调用上下文
l 参数不相符的宏调用
l 缺少&的宏调用
l 在“GotoIf”,“GotoIfTime”,“while”,“endwhile”,“Random”,和“execIf”的调用,将使得通话转到AEL goto,while等结构当中去。
l Goto到一个空的分机中的标识
l Goto到一个内嵌的分机,内嵌的上下文,或是一个不同上下文,或是任何包含(included)的上下文中不存在的标识。还会检查姊妹上下文引用(?)
l 在拨号方案中,总会对时间的值做检查的,包括ifTime()和包含的时间中的时间值:1、标明时间的范围需要使用破折号来进行分隔;2、小时必须在0到24之间3、如果提供了工作日列表,那么工作日必须在这个内部列表中4、一个月中的第几天必须是在1到31之间的范围当中;月份的名字则也必须在内部列表当中。
l 如果表达式被括在$[…]中,那么编译器将把它在括起来一边,将产生警告
l 会检查是否有重复的上下文名称。
l 会检查没有被其他上下文包含的抽象上下文。
l 如果一个标识是一个数值,那么产生警告
如果计划中的AAL(asterisk argument language)被开发出来并被整合进asterisk,那么一部分检查将会被移除。
检查一个字符串是否为null的最安全的方法是$[“${x}” = “”]。以前的做法和shell脚本中的做法一致,就是在两边都加上一些字符,如:$[${x}foo = foo]。但是这种做法的麻烦就是,如果x包含一些空格,则会产生语法错误。更加安全的做法就是如一开始提到的加上双引号。现在也有一些针对变量引用的函数可以被使用,如ISNULL()和LEN(),他们可以被用来测试一个空字符串:${ISNULL(${X})}或$[${LEN(${x})} = 0]。
你要记住的是赋值有两种不同的方法。如果你选择’x=y;’那么AEL将使用$[]括起右边的变量。所以,当编译的时候,结果就是’Set(x=$[y])’。如果你不想产生这样的效果,那么就像如下这样来替代‘Set(x=y)‘。
Asterisk的一个初学者看了上面的介绍和结构可能会问:“字符串处理函数在什么地方?”,“类似于其他语言提供的很酷的操作在什么地方?”等等
答案就是asterisk的这些丰富的功能都可以通过AEL来提供:
l applications:可以参考asterisk的application命令文档
l functions:函数通过${…}实现,并可以提供许多有用的功能
l expressions:在$[…]包含的表达式被表达式计算引擎处理。包括一些字符串的处理,算法表达式等
l 应用网关接口(Application Gateway interface):asterisk可以通过管道同它产生的进程进行通信。AGI应用可以使用任何语言实现。一些强大的应用可以通过这种方式来实现。
l 变量(variables):通信的通道拥有一些变量,并且asterisk也提供一些公共的变量。这些变量可以被上面提到的机制操作。