Wir wollen hier übersichtlich darstellen, wie AEL im Vergleich zur
herkömmlichen extensions.conf
aussieht. Das kann man
am besten an Beispielen veranschaulichen. Es wird vorausgesetzt, dass Sie
bereits mit der Dialplan-Programmierung vertraut sind.
Befehle müssen in AEL immer mit „;
“
(Semikolon) abgeschlossen werden, da theoretisch auch mehrere Befehle in
einer Zeile stehen könnten (was jedoch unüblich ist).
Der Unterschied in der Schreibweise von Contexten, Extensions und
Prioritäten bestimmt das ganze Erscheinungsbild. In AEL werden
geschweifte Klammern („{
“) verwendet. Die Angabe von Prioritäten
( ...
}1
, n
) ist nicht mehr
erforderlich. In der extensions.ael
hat man also
endlich nicht mehr den zweifelhaften Charme früher BASIC-Programme, in
denen man noch die Zeilennummern angeben musste, und man erspart sich
auch das überflüssige mehrfache Tippen der gleichen Extension für
mehrere Zeilen bzw. Befehle. Diese Stärke kommt vor allem bei längeren
Dialplänen zum Tragen, weshalb die kurzen Beispiele hier manchmal etwas
unfair gegen AEL sind.
extensions.conf | extensions.ael |
---|---|
[interne-benutzer] exten => 21,1,Dial(SIP/anna) exten => 21,n,VoiceMail(anna) exten => 22,1,Dial(SIP/lisa) exten => 22,n,VoiceMail(lisa) exten => _3X,1,Dial(SIP/${EXTEN}) | context interne-benutzer { 21 => { Dial(SIP/anna); VoiceMail(anna); } 22 => { Dial(SIP/lisa); VoiceMail(lisa); } _3X => { Dial(SIP/${EXTEN}); } } |
Bei einer Extension, in der nur ein Befehl ausgeführt wird, könnte man in AEL die geschweiften Klammern übrigens auch weglassen und nur
context default { 23 => Playback(hello-world); } |
schreiben. Gewöhnen Sie sich das bitte aber erst gar nicht an, und verwenden Sie immer die volle Schreibweise
context default { 23 => { Playback(hello-world); } } |
denn man hat ja sowieso bei den meisten Extensions
mehrere Befehle und erreicht so ein einheitliches Format. Nur in wenigen
Fällen (jump
, siehe „Labels, goto und jump“) kann die kurze Schreibweise sinnvoll
sein.
Wichtig | |
---|---|
Die öffnende geschweifte Klammer „ |
Kommentare werden in AEL durch „//
“
(zwei Schrägstriche) eingeleitet.
Wichtig | |
---|---|
Bitte verwenden Sie für Kommentare nicht den C-Stil ( |
extensions.conf | extensions.ael |
---|---|
; ein Kommentar exten => 10,1,Dial(SIP/anna) ; Dial | // ein Kommentar 10 => { Dial(SIP/anna); // Dial } |
Wie aus Abschnitt 4, „Includes im Dialplan“ und „Includes zeitgesteuert“ bekannt ist, können Sie in Contexte andere Contexte einbinden.
extensions.conf | extensions.ael |
---|---|
[verkauf] exten => 2001,1,Dial(SIP/anna) exten => 2002,1,Dial(SIP/hans) [lager] exten => 3001,1,Dial(SIP/lisa) [tag] include => verkauf include => lager [nacht] exten => _.,1,VoiceMail(${EXTEN},u) [von-extern] include => tag|09:00-17:00|mon-fri|*|* include => tag|09:00-14:00|sat|*|* include => nacht | context verkauf { 2001 => { Dial(SIP/anna); } 2002 => { Dial(SIP/hans); } } context lager { 3001 => { Dial(SIP/lisa); } } context tag { includes { verkauf; lager; } } context nacht { _. => { VoiceMail(${EXTEN},u); } } context von-extern { includes { tag|09:00-17:00|mon-fri|*|*; tag|09:00-14:00|sat|*|*; nacht; } } |
Wichtig | |
---|---|
Bitte beachten Sie in AEL das „ |
Globale Variablen (siehe „Variablen“) können in AEL im speziellen
Block globals
gesetzt werden.
extensions.conf | extensions.ael |
---|---|
[globals]
KUCHEN=Marmorkuchen
KLINGELZEIT=60
| globals { KUCHEN=Marmorkuchen; KLINGELZEIT=60; } |
In AEL werden intern automatisch die Ausdrücke
(expressions) in Kontrollstrukturen wie
if()
, while()
, der (mittleren)
Abbruchbedingung in for()
sowie der rechten Seite von
Zuweisungen (assignments) so behandelt, als stünden
sie in einem $[
-Ausdruck
(siehe Expression)....
]
Das klingt zuerst kompliziert, entspicht aber dem ganz normalen Verhalten, wie man es von anderen Programmiersprachen kennt. Bei Zuweisungen ist dieses Verhalten allerdings untypisch für Asterisk und kann leicht zu merkwürdigen Fehlern führen. Denken Sie daran, dass auch AEL eben keine „richtige“ Programmiersprache ist und z. B. Strings nie als solche mit Anführungszeichen gekennzeichnet werden. Wir empfehlen daher, Zuweisungen nicht so zu schreiben:
context test {
123 => {
ergebnis=10/2;
NoOp(ergebnis ist ${ergebnis});
}
} |
sondern nach wie vor die Applikation
Set()
(siehe Abschnitt 149, „Set()
“) zu
verwenden:
context test {
123 => {
Set(ergebnis=$[ 10 / 2 ]);
NoOp(ergebnis ist ${ergebnis});
}
} |
Für Sprachkonstrukte wie if()
, while()
usw. ist dieses Verhalten allerdings gut, da es die unübersichtlichen
Klammern $[
einspart: ...
]
extensions.conf | extensions.ael |
---|---|
exten => 50,1,Set(a=test)
exten => 50,n,ExecIf($["${a}" = "101"],SayDigits,123)
| 50 => {
Set(a=test);
if ("${a}" = "test") {
SayDigits(123);
}
} |
Als hartgesottener
extensions.conf
-Programmierer ist man (zwangsweise)
daran gewöhnt, mit Goto()
, GotoIf()
,
Gosub()
und GosubIf()
zu
„Prioritäten“ oder Labels (Markern) zu springen.
(Eigentlich ist das aber nur eine Behelfslösung, weil es in der
extensions.conf
keine sauberen Kontrollstrukturen
für den Programmablauf gibt.)
Labels befinden sich immer innerhalb einer Extension und werden in
AEL auf einer eigenen Zeile geschrieben. Bitte beachten Sie den
Doppelpunkt („:
“) am Zeilenende:
extensions.conf | extensions.ael |
---|---|
[beispiel] ; zu einem Label in der ; gleichen Extension gehen: ; exten => 10,1(anfang),NoOp() exten => 10,n,Wait(1) exten => 10,n,SayNumber(1) exten => 10,n,NoOp(Endlosschleife) exten => 10,n,Goto(anfang) ; zu einem Label in einer ; anderen Extension im ; gleichen Kontext gehen: ; exten => 20,1,SayNumber(20) exten => 20,n,Goto(10,anfang) ; zu einem Label in einem ; anderen Kontext gehen: ; exten => 30,1,SayNumber(30) exten => 30,n,Goto(cntxt2,40,vierzig) [cntxt2] exten => 40,1(vierzig),NoOp() exten => 40,n,SayNumber(40) exten => 50,1,Goto(40,1) exten => 60,1,Goto(beispiel,10,1) | context beispiel { // zu einem Label in der // gleichen Extension gehen: // 10 => { anfang: Wait(1); SayNumber(10); NoOp(Endlosschleife); goto anfang; } // zu einem Label in einer // anderen Extension im // gleichen Kontext gehen: // 20 => { SayNumber(20); goto 10|anfang; } // zu einem Label in einem // anderen Kontext gehen: // 30 => { SayNumber(30); goto cntxt2|40|vierzig; } } context cntxt2 { 40 => { vierzig: SayNumber(40); } 50 => jump 40; 60 => jump 10@beispiel; } |
In dem obigen Beispiel sehen wir auch eine andere Syntax für die
Sprünge. Während man in der extensions.conf
auf die
Applikation Goto()
(siehe Abschnitt 65, „Goto()
“) angewiesen ist, sollte man diese in AEL
nicht mehr verwenden (man kann es aber problemlos tun). Dafür gibt es
jetzt das neue Sprachkonstrukt goto
.
Ein Vergleich der Syntax von Goto()
und
goto
zeigt, dass es hier keinen großen Erklärungsbedarf
gibt:
.conf |
|
.ael |
|
Dass man in AEL natürlich nicht auf Idee kommen sollte, zu einer
Priorität anhand ihrer Nummer zu springen, versteht sich auch von
selbst, denn wie der AEL-Compiler die Befehlszeilen in Prioritäten
umsetzt, sollte beim Schreiben von AEL nicht interessieren. Weil es aber
vorkommt, dass man zu einer anderen Extension (im gleichen oder in einem
anderen Context) springen will, gibt es in AEL zusätzlich zu
goto
auch noch die Anweisung
jump
.
.conf |
|
.ael (schlecht!) |
|
.ael (gut) |
|
Im Folgenden („Bedingte Anweisungen (conditionals)“ und „Schleifen (loops)“) werden wir aber lernen, dass man in AEL nicht
mehr darauf angewiesen ist, den Kontrollfluss (control
flow) des Programms durch goto
-Sprünge zu
definieren, da es echte Kontrollstrukturen gibt.
Bedingte Anweisungen[31] (conditionals[32]) gehören zu den Kontrollstrukturen (control structures) eines Programmablaufs.
Anmerkung | |
---|---|
Für Informationen über Ausdrücke (expressions) siehe auch Expression. |
In AEL gibt es sowohl if
- als auch switch
-Blöcke. Das ist ein riesiger
Vorteil, denn es erleichtert die Lesbarkeit ganz entscheidend, und je
umfangreicher die Programmlogik wird, desto mehr kommt dieser Vorteil
zum Tragen. Vergleichen Sie selbst:
if
extensions.conf | extensions.ael |
---|---|
exten => 90,1,Dial(SIP/anna) exten => 90,n,GotoIf($["${DIALSTATUS}" = "BUSY"]?b:n) exten => 90,10(b),Answer() exten => 90,11,Playback(hello-world) exten => 90,12,Voicemail(anna,b) exten => 90,13,Goto(ende) exten => 90,20(n),Dial(SIP/lisa) exten => 90,21,Playback(beeperr) exten => 90,22,Goto(ende) exten => 90,30(ende),NoOp(Fertig) | 90 => { Dial(SIP/anna); if ("${DIALSTATUS}" = "BUSY") { Answer(); Playback(hello-world) Voicemail(anna,b); } else { Dial(SIP/lisa); Playback(beeperr); } NoOp(Fertig); } |
Wer an AEL gewöhnt ist, empfindet das unübersichtliche
Herumspringen mit GotoIf()
zu Recht als umständlich, mal
ganz zu schweigen davon, dass man bei einem Befehl wie
GotoIf($["${DIALSTATUS}" = "BUSY"]?b:n)
nicht auf den
ersten Blick sieht, ob all die Klammern richtig sind.
Wichtig | |
---|---|
Auch hier gilt, dass die öffnende geschweifte Klammer
„ |
switch
extensions.conf | extensions.ael |
---|---|
exten => 70,1,Dial(SIP/anna) exten => 70,n,Goto(70-${DIALSTATUS},10) exten => 70,n(ende),NoOp(Fertig) exten => 70-BUSY,10,NoOp(besetzt) exten => 70-BUSY,11,Goto(ende) exten => 70-NOANSWER,10,NoOp(hebt nicht ab) exten => 70-NOANSWER,11,Goto(ende) exten => _70-.,10,NoOp(was anderes) exten => _70-.,11,Goto(ende) | 70 => { Dial(SIP/anna); switch ("${DIALSTATUS}") { case "BUSY": NoOp(besetzt); break; case "NOANSWER": NoOp(hebt nicht ab); break; default: NoOp(was anderes); } NoOp(Fertig); } |
Wichtig | |
---|---|
Bitte denken Sie bei den |
Dass das Äquivalent einer einfachen switch
-Anweisung
in der traditionellen extensions.conf
eine mittlere
Katastrophe ist, braucht wohl nicht groß erwähnt zu werden.
Verschachtelte if
- oder switch
-Blöcke wären
vollkommen unübersichtlich und unwartbar.
Übrigens gibt es – falls man das wirklich mal brauchen
sollte – in switch
-Blöcken nicht nur
case
-Vergleiche, sondern auch pattern
:
extensions.conf | extensions.ael |
---|---|
exten => _70,1,NoOp(Gewaehlt: ${EXTEN}) exten => _70,n,Goto(70-${EXTEN},10) exten => 70-703,10,NoOp(703) exten => 70-703,11,Goto(ende) exten => 70-704,10,NoOp(704) exten => 70-704,11,Goto(ende) exten => _70-70[5-8],10,NoOp(70[5-8]); exten => _70-70[5-8],11,Goto(ende) exten => _70-.,10,NoOp(was anderes) exten => _70-.,11,Goto(ende) exten => 70,n(ende),NoOp(Fertig) | _70. => { NoOp(Gewaehlt: ${EXTEN}); switch (${EXTEN}) { case 703: NoOp(703); break; case 704: NoOp(704); break; pattern 70[5-8]: NoOp(70[5-8]); break; default: NoOp(was anderes); } NoOp(Fertig); } |
ifTime
Es sei noch erwähnt, dass es in AEL auch eine Entsprechung für
GotoIfTime()
(siehe Abschnitt 67, „GotoIfTime()
“) gibt, nämlich das Sprachkonstrukt
ifTime
.
20 => { ifTime (08:00-18:00|mon-fri|*|*) { Dial(SIP/20); } else { Playback(ansage-geschlossen); Voicemail(20,s); } } |
Die Syntax der Zeitangabe entspricht der von
GotoIfTime()
. ifTime
ist eigentlich
überflüssig, denn mit einem normalen if
und der Funktion
IFTIME()
(Abschnitt 51, „IFTIME()
“) kann man das
Gleiche erreichen. Der Programmcode ist nur etwas länger:
20 => { if (${IFTIME(08:00-18:00|mon-fri|*|*?1:0)}) { Dial(SIP/20); } else { Playback(ansage-geschlossen); Voicemail(20,s); } } |
random
random(){...}
ist ein Sprachkonstrukt, bei dem man in
Klammern einen ganzzahligen Prozentwert von 1 bis 99 angibt, der
bestimmt, mit welcher Wahrscheinlichkeit der Code-Block ausgeführt
wird.
20 => { random (42) { NoOp(42 % Chance); } else { NoOp(58 % Chance); } } |
Das Sprachkonstrukt random
ist eigentlich
überflüssig, denn mit einem normalen if
und der Funktion
RAND()
(Abschnitt 80, „RAND()
“) kann man das
Gleiche erreichen. Der Code ist nur etwas länger:
20 => { if (${RAND(0,100)} < 42) { NoOp(42 % Chance); } else { NoOp(58 % Chance); } } |
Schleifen (loops) gehören – wie bedingte Anweisungen – zu den Kontrollstrukturen (control structures) eines Programmablaufs.
In AEL gibt es for
-
und while
-Schleifen, wie
sie aus anderen Programmiersprachen bekannt sind.
while
Ein while
-Block in AEL entspricht in etwa der
Verwendung von While()
(Abschnitt 195, „While()
“) und EndWhile()
(Abschnitt 47, „EndWhile()
“).
Auch die aus anderen Sprachen bekannten Befehle break
und continue
lassen sich in Schleifen nutzen.
break
springt zum Ende des Schleifenblocks,
continue
zum Anfang. Das ist also die Entsprechung zu
ExitWhile()
(Abschnitt 53, „ExitWhile()
“)
bzw. ContinueWhile()
(Abschnitt 24, „ContinueWhile()
“).
extensions.conf | extensions.ael |
---|---|
exten => 30,1,Set(x=0) exten => 30,n,While($[${x} <= 9]) exten => 30,n,NoOp(x ist ${x}) exten => 30,n,ExecIf($[${x} > 5],ExitWhile) exten => 30,n,Playback(beep) exten => 30,n,Set(x=$[${x} + 1]) exten => 30,n,EndWhile() exten => 30,n,NoOp(Fertig) | 30 => { x=0; while (${x} <= 9) { NoOp(x ist ${x}); if (${x} > 5) { break; } Playback(beep); x=${x} + 1; } NoOp(Fertig); } |
Anmerkung | |
---|---|
In AEL brauchen wir hier nur deshalb mehr Zeilen, weil wir
darauf verzichten, in dem |
for
Zusätzlich zu while
gibt es in AEL auch
for
-Schleifen. Diese habe keine Entsprechung im
.conf
-Format. (Allerdings lässt sich jede
for
-Schleife als while
-Schleife
schreiben.)
extensions.conf | extensions.ael |
---|---|
exten => 40,1,Set(x=0) exten => 40,n,While($[${x} <= 5]) exten => 40,n,NoOp(x ist ${x}) exten => 40,n,Playback(beep) exten => 40,n,Set(x=$[${x} + 1]) exten => 40,n,EndWhile() exten => 40,n,NoOp(Fertig) | 40 => { for (x=0; ${x}<=5; x=${x}+1) { NoOp(x ist ${x}); Playback(beep); } NoOp(Fertig); } |
In AEL muss man sich keine Gedanken darüber machen, ob man die
nicht mehr empfohlene Applikation Macro()
(Abschnitt 81, „Macro()
“) oder Gosub()
(Abschnitt 63, „Gosub()
“) verwenden soll, denn Makros gibt es in
AEL als Sprachkonstrukt macro
.
extensions.conf | extensions.ael |
---|---|
[macro-countdown] exten => s,1,Set(c=${ARG1}) exten => s,n,While($[ ${c} > 0]) exten => s,n,SayNumber(${c}) exten => s,n,Set(c=$[ ${c} - 1 ]) exten => s,n,EndWhile() [default] exten => 123,1,Macro(countdown,3) exten => 124,1,Macro(countdown,5) | macro countdown( count ) { for (c=${count}; ${c}>0; c=${c}-1) { SayNumber(${c}); } } context default { 123 => { &countdown(3); } 124 => &countdown(5); } |
Intern macht der AEL-Compiler aus macro
automatisch
eine Gosub()
-Subroutine, was uns aber glücklicherweise
nicht interessieren muss.
Wie sogenannte „Hints“ in AEL geschrieben werden, erklären wir ausführlich in Kapitel 23, BLF, Hints, Pickup. Hier nur ein einfaches Beispiel:
extensions.conf | extensions.ael |
---|---|
[interne-benutzer] exten => 21,hint,SIP/anna exten => 21,1,Dial(SIP/anna) exten => 22,hint,SIP/lisa exten => 22,1,Dial(SIP/lisa) | context interne-benutzer { hint(SIP/anna) 21 => { Dial(SIP/anna); } hint(SIP/lisa) 22 => { Dial(SIP/lisa); } } |
... und eines mit Pattern:
extensions.conf | extensions.ael |
---|---|
[interne-benutzer] exten => 21,hint,SIP/21 exten => 22,hint,SIP/22 exten => _2X,1,Dial(SIP/${EXTEN}) | context interne-benutzer { hint(SIP/21) 21 => {} hint(SIP/22) 22 => {} _2X => { Dial(SIP/${EXTEN}); } } |
Man braucht es zwar in der Praxis eher selten, aber die Syntax für eine (in der Asterisk-Community viel zitierte) „Ex-Girlfriend Extension“ sähe so aus:
extensions.conf | extensions.ael |
---|---|
exten => 10/55555,1,NoOp(Ex-Freundin) exten => 10/55555,n,Busy() exten => 10,1,Dial(SIP/karl) exten => 10,n,Voicemail(karl) | 10/55555 => {
NoOp(Ex-Freundin);
Busy();
}
10 => {
Dial(SIP/karl);
Voicemail(karl);
} |
Wenn also die Ex-Freundin von der Nummer (Caller-ID)
55555
anruft, würde sie auf Busy()
geleitet, alle anderen Anrufer jedoch nicht.
Übrigens sind hier auch Pattern erlaubt. Mit /_0123.
könnte man also direkt einen ganzen Vorwahl-Bereich matchen.
AMOOCON 2010
Noch kein Ticket? Dann wird es Zeit. Nur noch wenige Tage.
- Infos unter www.amoocon.de.
- twitter.com/AMOOCON