Asterisk Gateway Interace (AGI)
Asterisk 提供了名為 Asterisk Gateway Interface (以下簡稱為 AGI) 的功能,開發者可能以透過 Dialplan 以外的工具或程式 (E.g. Perl, Shell Script,C++, Java, PHP, Mono, etc….)來開發Asterisk的應用程序,控制Asterisk 的 telephony channel,包括Play music,讀寫DTMF等等。
AGI 是一個獨立程式,Asterisk 會經由Dialplan 調用 AGI() 這一程式來執行AGI。它執行後便會用 stdin和 stdout 來控制Asterisk。直至那 channel 收線為止。
AGI 在Asterisk 下,會根據本身的用途和行為特徵分了四種類型:
- AGI – AGI 應用程序是與Asterisk 一同在本機內執行。
- EAGI – 有別於 AGI,它除了用 stdin 和 stdout 外,它還會把 音訊輸出到 File descriptor 3。其他應用程式可以用它的File descriptor 3來搜取這channel 的音訊。
- FastAGI – 用 TCP 和 port 4537 來連接 AGI。因此這樣AGI 程式是可 以安裝在網路上不同時的機器上。
- DeadAGI – 這特別的AGI 只有在channel 掛線後才會起作用。
可參考以下的網址進一步認識 AGI
http://www.voip-info.org/wiki-Asterisk+AGI
以下網址除了有AGI的語法外,還有各種範例提供
http://home.cogeco.ca/~camstuff/agi.html
Fast AGI 的實例
上月,我幫建設了一個基於Asterisk的電話系統。因為這電話系統的需求是:
- 平衡負載 – 這系統有多部 Asterisk 同一時間接聽電話,和收發傳真。倘若有其中一部出現問題時,其他 Asterisk 也可以在那時間分擔任務。
- 我朋友的公司是從事電話服務系統相關的業務。因為那電話系統的流量極大,如果程式要在Asterisk Box 裏運行,那會加重Asterisk 的負擔。而且朋友想簡化系操作和維護程序,所以我自行開發了一個 Fast AGI Server (Application Server) 在某一伺服器上獨立執行。所有Asterisk Box 將會用Fast AGI 連接這台伺服器。因此,所有Call Flow,Business Logic,和其他與電話底層不相關的模組會全部由 Fast AGI Server 負責。而Asterisk 只負責電話底層的任務。
我們自行設計的 Application Server 是基於 MS-Windows 設計。它是以 Windows Service 形式運作。它會開一個 TCP Socket 4573 接收來自 Asterisk 的 FastAGI 要求。接受要求後,Asterisk 的Dialplan 便把控制權移交到 Application Server 中,而Asterisk 那方便一直等待,直至 Application Server 這方的工作完成為止。
當然,Application Server 這方的控制程序並不是硬寫的。它借用了 Mozilla 的SpiderMonkey 模組作一個 Script Engine。SpiderMonkey 原是 Mozilla 和 FireFox的 JavaScript Engine。那麼,開發人員便可以像開發DialPlan一樣地簡單,用JavaScript 在Application Server上開發 Call Flow。
http://www.mozilla.org/js/spidermonkey/
看過以下的網址後,你會發現 AGI 每一指令正是反映了Asterisk 在Dialpan 中的一道指令。而AGI 沒有提供的便可能以用 “Exec” 指令來呼叫 Asterisk 其他的指令。
http://www.bitflipper.ca/Documentation/agi.html
那麼,我們可以用 SpiderMonkey 來定義我們的 Call Flow 語法和指令了。例如以下的 JavaScript:
function main() {
var ret = Answer();
if(ret == 0) {
ret = PlayMsg(”hello-world”, “#”);
}
HangUp();
return 0;
}
在這個例子中,Answer(), PlayMsg(), 和 HangUp() 是我自行定義的 JavaScript 功能。 它內裏也只是調用到 Astreisk 或 AGI 的功能而已。
Application Server 一旦接收到 Asterisk 的 FastAGI 的請求後,Asterisk 會首先傳一組資料給 Application Server。這組資料可以給 Application Server 詳細的資訊來選擇Call Flow或其他功能。以下是 Asterisk 首先傳一組資料給 Application Server的例子。這樣段資料中,每一行它會用 line feed 作分隔,傳送完畢後會用兩個 line feed作完結。
agi_network: yes agi_request: agi://192.168.0.30/frame1?param1=1¶m2=2¶m3=3 agi_channel: Zap/1-1 agi_language: en agi_type: Zap agi_uniqueid: 1157046217.3 agi_callerid: 88888888 agi_calleridname: unknown agi_callingpres: 0 agi_callingani2: 0 agi_callington: 0 agi_callingtns: 0 agi_dnid: unknown agi_rdnis: unknown agi_context: incoming agi_extension: s agi_priority: 2 agi_enhanced: 0.0 agi_accountcode:
在這裡,我們可以看到什麼 channel(agi_channel) 用這AGI,對方的來電(agi_callerid, agi_calleridname),DNIS(afi_rdnis)等等。
另外,agi_request 便是在Dialplan 裏所調用 AGI()的 URL。例如上方的AGI request 便是由於以下的 Dialplan 調用:
[callflow] exten => s,1,AGI(agi://192.168.0.30/frame1?param1=1¶m2=2¶m3=3)
這樣,Asterisk 便可以用URL來傳遞額外的參數,同時我們的 Application Server便可以用種種方法拆解這URL來得到那額外的參數了。
Application Server 做完以上的動作後,接著便會調用 SpiderMonkey 來運行我們預備好的 JavaScript。以後,所有關於 AGI 的動作將會由 JavaScript包裝起來,以下是一小段例子:
JSBool CCallFlow::Script_PlayMessage(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
CString strMsg;
JSBool ret = JS_FALSE;
unsigned long scriptContextNo = 0;
CCallFlow* lpThis = NULL;
scriptContextNo = (unsigned long)cx;
lpThis = (*mapScriptContext)[scriptContextNo];
if(lpThis != NULL){
strMsg.Format(”CCallFlow::Script_PlayMessage : Called”);
lpThis->WriteSysLog(DDEBUG_LEVEL, strMsg);
if(argc == 2){
char strCmd[MAX_PATH];
char strRet[MAX_PATH];
char* strVoxFile;
char* strDigit;
ZeroMemory(strCmd, MAX_PATH);
ZeroMemory(strRet, MAX_PATH);
if(JSVAL_IS_STRING(argv[0])){
strVoxFile = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
if(JSVAL_IS_STRING(argv[1])){
strDigit = JS_GetStringBytes(JSVAL_TO_STRING(argv[1]));
if(strlen(strDigit) <= 0){
strDigit = “”"”;
}
wsprintf(strCmd, “STREAM FILE %s %sn”, strVoxFile, strDigit);
lpThis->ProcessAGI(strCmd, strRet);
Sleep(200);
if(strlen(strRet) <= 0){
sprintf(strRet, “%d”, 0);
}
JSString* str = JS_NewStringCopyZ(cx, lpThis->mapKeyPad[strRet].c_str());
if(!str){
ret = JS_FALSE;
}else{
rval[0] = STRING_TO_JSVAL(str);
ret = JS_TRUE;
}
}else{
strMsg.Format(”CCallFlow::Script_PlayMessage : Invalid digits”);
lpThis->WriteSysLog(ERROR_LEVEL, strMsg);
}
}else{
strMsg.Format(”CCallFlow::Script_PlayMessage : Invalid file”);
lpThis->WriteSysLog(ERROR_LEVEL, strMsg);
}
}else{
strMsg.Format(”CScriptHandler::Script_PlayMessage : parameters not match”);
lpThis->WriteSysLog(ERROR_LEVEL, strMsg);
}
}else{
strMsg.Format(”CScriptHandler::Script_PlayMessage[%d] : invalid script object.”, lpThis->label);
lpThis->WriteSysLog(ERROR_LEVEL, strMsg);
}
return ret;
}
大家會看到第三十和三十一行,在 JavaScript 眼裏看到是否一個 PlayMessage 的JavaScript Function,
但這PlayMessage 裏是調用了 STREAM FILE 這個 AGI Function,而同時整合了一串參數(這裡是一個
wave file name),然後調用 ProcessAGI()。ProcessAGI()只是很簡單地把這AGI Function 經
Socket 傳遞到Asterisk 中。
BOOL CCallFlow::ProcessAGI(char* strAGICmd, char* strRet){
BOOL ret = FALSE;
char _strRet[MAX_PATH];
char _strRet2[MAX_PATH];
if(sck != NULL){
cout << strAGICmd << endl;
ZeroMemory(_strRet, MAX_PATH);
sck->Send((const char*)strAGICmd, strlen(strAGICmd));
int len = sck->Receive(_strRet, MAX_PATH);
if(len > 0){
mapAGIResult.clear();
if(strncmp(_strRet, “200 “, replyOffset) == 0){
ZeroMemory(_strRet2, MAX_PATH);
strncpy(_strRet2, _strRet + replyOffset, len - replyOffset);
ParseCommand2(_strRet2, ‘ ‘, &mapAGIResult);
strcpy(strRet, mapAGIResult["result"].c_str());
ret = TRUE;
}else{
mapAGIResult["return_error"] = _strRet;
}
}
}
return ret;
}
其實,關於 SpiderMonkey 的 Document 不多,很多 SpiderMonkey 的功能是透過以下網址所得的資訊然後再推敲出來的。希望這樣篇文章可以幫到大家。




不好意思我目前在研究所研究VoIP領域,想請教一下專家要如何把我們所撰寫的程式放進Elastix系統裡作聯繫來取的通話資訊,因為我要做的實驗是要把一個分類演算法與Elastix Server作聯繫來取的通話資訊,之前研究一下是要透過AMI端口來取得,但目前小弟不知AMI是在哪哩,也不知道要怎麼把程式放進系統作聯繫(因為Elastix安裝完畢是文字介面),請專家救救我><
AMI 是 Asterisk Manager Interface (即是 Fast AGI), 我記得如果 fresh install 的 Asterisk, 這設定是關閉的。你可以用 vi 打開 /etc/asterisk/manager.conf 看看 AMI 是否有開啟。
[general]
enabled = yes << 把這裡設定為 yes
;webenabled = yes
port = 5038
謝謝你寶貴的意見,那程式要怎麼放入系統呢??要怎麼連結到AMI呢(工具??)
如果你用 Java 開發的話,可以用 Asterisk Java,除了 FastAGI 外,她也有提供了 ManagerAPI 經 AMI 控制 Asterisk。應該合乎你的要求。
http://asterisk-java.org/
http://asterisk-java.org/0.2/tutorial.html
用 .Net 或 Mono 的,也有 Asterisk.Net
http://sourceforge.net/projects/asterisk-dotnet/
謝謝你題共這些網頁對我幫助很大,
那如果java不熟的話可以利用c或者c#嗎??
Yes, you can find most resources in Internet.
看看有沒有合作空間
哈謝謝你的關照,那想問一下專家該怎麼去Server裡的資料庫抓取通話紀錄呢?
想請教一下專家們,小弟現在已成功經由Java連到AMI,那現在遇到一個問題:測試許久都無法擷取系統裡的通話紀錄,想請問是否有範例程式能讓Server裡通話紀錄(CDREvents)自動傳送到本電腦嗎?拜託各位專家們幫忙><
對不起,我現在在旅遊中,要回港才能詳細回覆
沒關係!!先好好放鬆一下吧^^
AMI 是不會傳回 CDR Event 的。Asterisk可以把CDR設定為記錄在 MySQL 中,然後你可以經由 JDBC搜取 CDR。
可參考:
http://www.voip-info.org/wiki/view/Asterisk+cdr+mysql
哈你回來了啊!!我最近有利用JAVA還有C#(Asterisk NET)連進AMI,
但C#連進去後,ELASTIX網頁裡的EXTENSION按進去
是空白的,那也不能打電話了超奇怪><,那用JAVA就蠻順利的,
但遇到一個問題是我研究需要即時性所以需要一個範例程式(API)能自動讓通話紀錄傳送到CLIENT,那次要選擇可以到MySQL取得CDR,不過可能就沒那麼即時性了,那想問一下專家一些有關範例程式能參考^^
那如果是直接去MYSQL抓取CDR的話,因為我是用Elastix系統,那還要載Asterisk-addons嗎??
不需要。你只用 MySQL 的 JDBC 或 ODBC 連接便可
那想請問你是否知道有提供通話紀錄的機購或公司,因為我研究上需要
通話紀錄(call-id、duration…..等),那也想問你是否知道可以產生正常流量或垃圾語音流量的工具!!謝謝~><
>那想請問你是否知道有提供通話紀錄的機購或公司
不太明白意思,可否詳細一點呢?
不好意思,沒說清楚~我的意思是因為我需要像一筆CDR通話紀錄(call-id、duration、ip…等)來作實驗,所以想問您知不知道有哪裡可以提供下載或公司可以提供呢??
沒有的。
說的也是><,謝謝你捏一直煩你~~
客氣客氣~
因為研究上有些問題沒辦法解決才跟您請教,希望未來能跟你一起共事XD
不好意思我想請問一下我用一個軟體產生出.call file ,但要如何利用elastix發送出去呢??
把你的 .call file 上載到 /var/spool/asterisk/outgoing 中。
那放到裡面去之後就會自動撥打嗎??還是還需要哪些動作呢??
Yes.會自動撥打
那請問一下!!專家知道asterisk權限密碼嗎??
asterisk的什麼權限密碼呢?
因為我要播.CALL檔關係,但因為我們是ROOT權限好像沒辦法撥!要以使用者asterisk權限才行,但需要密碼才能換權限,所以才想請問密碼是什麼呢><