7.3. extensions.conf <-> extensions.ael
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.7.3.1. Zeilenende/Befehlsende
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).7.3.2. Kontexte, Extensions, Prioritäten
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
schreiben. Gewöhnen Sie sich das bitte aber erst gar
nicht an, und verwenden Sie immer die volle Schreibweise
denn man hat ja sowieso bei den meisten Extensions
mehrere Befehle und erreicht so ein einheitliches Format. Nur in wenigen
Fällen (
context default { 23 => Playback(hello-world); } |
context default { 23 => { Playback(hello-world); } } |
jump
, siehe Abschnitt 7.3.7, „Labels, goto und jump“) kann die kurze Schreibweise sinnvoll
sein.Wichtig
Die öffnende geschweifte Klammer „
{
“
eines Blocks muss immer auf der gleichen Zeile stehen, nicht auf einer
eigenen!7.3.3. Kommentare
Kommentare werden in AEL durch „
//
“
(zwei Schrägstriche) eingeleitet.Wichtig
Bitte verwenden Sie für Kommentare nicht den C-Stil (
/* ...
*/
). Für mehrzeilige Kommentare leiten Sie bitte
jede Zeile separat mit „//
“ ein.extensions.conf | extensions.ael |
---|---|
; ein Kommentar exten => 10,1,Dial(SIP/anna) ; Dial | // ein Kommentar 10 => { Dial(SIP/anna); // Dial } |
7.3.4. Includes – Andere Contexte einbinden
Wie aus Abschnitt 3.4, „Includes im Dialplan“ und Abschnitt 3.4.4, „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 „
s
“ am
Ende von „includes
“.7.3.5. Globale Variablen
Globale Variablen (siehe Abschnitt 6.1.2, „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; } |
7.3.6. Ausdrücke und Zuweisungen
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:
sondern nach wie vor die Applikation
context test {
123 => {
ergebnis=10/2;
NoOp(ergebnis ist ${ergebnis});
}
} |
Set()
(siehe Abschnitt C.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);
}
} |
7.3.7. Labels, goto und jump
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 C.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 (Abschnitt 7.3.8, „Bedingte Anweisungen (conditionals)“ und Abschnitt 7.3.9, „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.7.3.8. Bedingte Anweisungen (conditionals)
Bedingte Anweisungen[31] (conditionals[32]) gehören zu den Kontrollstrukturen (control
structures) eines Programmablaufs.
Anmerkung
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
„
{
“ eines Blocks auf der gleichen Zeile
stehen muss, nicht auf einer eigenen!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
case
-Sprungpunkten im
switch
-Block in AEL immer an die
break
-Anweisungen! Ohne diese Begrenzung läuft die
Programmausführung einfach nach unten weiter, also zum nächsten
case
oder default
– eben zur nächsten
Zeile.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 C.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 D.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 D.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); } } |
7.3.9. Schleifen (loops)
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 C.195, „While()
“) und EndWhile()
(Abschnitt C.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 C.53, „ExitWhile()
“)
bzw. ContinueWhile()
(Abschnitt C.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
if
-Block vor dem
break
noch einen zweiten Befehl wie NoOp()
auszuführen. Dann nämlich reicht im .conf
-Format
nicht mehr ein ExecIf()
, sondern man braucht ein
kompliziertes mehrzeiliges Konstrukt mit GotoIf()
.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); } |
7.3.10. Makros (macros)
In AEL muss man sich keine Gedanken darüber machen, ob man die
nicht mehr empfohlene Applikation
Macro()
(Abschnitt C.81, „Macro()
“) oder Gosub()
(Abschnitt C.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.7.3.11. Hints
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}); } } |
7.3.12. Filtern nach Anrufernummer
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.