您當前的位置是:  首頁 > 新聞 > 國內 >
 首頁 > 新聞 > 國內 >

Aterisk接口-ARI對通道的狀態(tài)管理

2017-06-26 09:27:58   作者:james.zhu   來源:Aterisk   評論:0  點擊:


  以前我們討論了ARI接口對DTMF事件的處理,其實ARI也支持對通道方面的處理方式,否則ARI就不能成為一個未來Asterisk接口通信的主要方式。Asterisk通過ARI接口可以獲得各種通道的狀態(tài)信息,然后通過ARI對其進行管理控制,按照用戶的業(yè)務需求做進一步的處理。首先我們簡單說明一下什么是通道和其狀態(tài)的含義。通道狀態(tài)是反映當前介于Asterisk和終端設備之間的通信介質的狀態(tài)。通道的狀態(tài)會影響所支持的操作,什么樣的通道狀態(tài)支持什么樣的通道操作,同時也會最終影響終端設備的操作執(zhí)行。
  目前通道可以支持多種狀態(tài),基本常見的幾種狀態(tài)是:
  • Down - 通信路徑存在,但是Asterisk和終端之間沒有任何媒體。
  • Ringing - 設備正在振鈴,在Asterisk和設備之間可能存在媒體。
  • Up - 設備之間已經(jīng)互相應答。當在UP狀態(tài)時,在Asterisk和設備之間的媒體可以雙向流動。
  注意
  某些通道介紹,例如Dahdi通道,可能支持了更多的狀態(tài) (例如,"Pre-ring" 或者 "Dialing Offhook")。當處理通道狀態(tài)市,需要訪問Channel data model 的各種變量信息。現(xiàn)在我們主要介紹幾種不同的狀態(tài)處理方式和腳本語言的處理流程。
  指示振鈴
  Asterisk 可以通知設備對呼叫方進行振鈴,使用的方式是POST /channels/{channel_id}/ring 的操作模式,或者可以使用
  DELETE /channels/{channel_id}/ring 的操作停止振鈴。這里用戶要注意,指示振鈴并不是真正從Asterisk到設備之間傳輸媒體。Asterisk僅是通知設備振鈴,但是終端是否振鈴取決于設備自己本身。
\
  應答通道
  當通道沒有應答之前,Asterisk仍然沒有通知設備如何進行通信。應答通道以后,Asterisk才完成了服務器端和設備端之間的通信路徑的創(chuàng)建,可以實現(xiàn)媒體的雙向流通。使用的方法是
  POST /channels/{channel_id}/answer 操作。
  對通道掛機
  用戶可以對通道掛機,使用的是DELETE /channels/{channel_id} 操作方式。當掛機以后,介于Asterisk和設備終端之間的通信會結束,通道離開Stasis的應用程序。用戶的應用程序通過一個 StasisEnd 事件被提醒。
  設備掛機也是同樣的方式。在這種環(huán)境下,Asterisk和終端之間的通信結束以后,通道掛機,用戶程序通過一個StasisEnd 事件被通知通道離開了應用程序。
  一般情況下,一旦通道離開了用戶應用程序,用戶就不能收到任何關于通道的事件信息。但是當用戶程序訂閱了通道的所有事件時,無論通道是否在應用程序中,如果通道確實掛機,用戶會收到一個ChannelDestroyed事件。
  舉例:控制通道的狀態(tài)
  這個實例ARI 程序會執(zhí)行以下步驟:
  • 等待通道進入到一個Stasis 應用程序。
  • 當通道進入到Stasis程序后,它會對通道指示振鈴,如果通道沒有準備好振鈴,那么就讓通道現(xiàn)在開始振鈴。
  • 幾秒鐘以后,通道應答呼叫。
  • 一旦通道應答了呼叫,我們對通道啟動一個靜音的時段。幾秒鐘以后,通道掛機。
  • 如果在這個過程中終端掛機,系統(tǒng)會平滑處理掛機流程。
  • 撥號規(guī)則
  以下?lián)芴枌嵗齼H表示通道進入到Stasis程序中:
  extensions.conf
\
  Python
  這個例子中我們使用了ari-py 支持包。這個基本的架構類似于channel-dump Python- 用戶可以參考這個實例來學習如何使用這個支持包連接ARI。
  這里,我們首先需要創(chuàng)建我們的ARI客戶端,并且注冊三個不同的事件-StasisStart,ChannelStateChange,和StasisEnd。
  很多流程的處理是在StasisStart中進行,當通道進入到我們的應用程序時,StasisStart就會被調用。大部分情況下,這里還要設置一個Python定時器來初始化通道中的一些指令。
  當通道應答呼叫后,ChannelStateChange 句柄會打印出通道改變狀態(tài)的輸出信息。
  最后,系統(tǒng)會把初始化的定時器權限,然后發(fā)出StasisEnd 的事件。當通道離開應用程序后,這個事件就會馬上被調用,通常情況下,可能是呼叫方掛機,或者我們自己掛機。
  我們可以啟動一個通道的定時器:
  \
  然后注冊三個事件:
\
  這里的StasisStart 事件需要用戶更多注意一下。
  • 首先,我們通知通道指令,2秒鐘以后,應答這個通道:
 \
  這里,我們在通道channel_timers 中存儲了一個定時器,如果用戶掛機以后,我們可以發(fā)出StasisEnd 的事件。
  • 一旦程序執(zhí)行進入到answer_channel中,系統(tǒng)應答這個通道,在通道中啟動一個靜音播放。注意,我們會繼續(xù)聲明一個answer_channel,這是一個在我們StasisStart 句柄內嵌的函數(shù):
\
  • 我們應答了通道以后,系統(tǒng)會在4秒鐘內啟動另外一個定時器對通道進行掛機處理。當定時器被觸發(fā)后,它會調用hangup_channel。這是通道中最后的掛機程序。另外,我們會在StasisStart句柄中再次聲明hangup_channel:
 \
  當創(chuàng)建了定時器以后,我們在通道中振鈴時,系統(tǒng)會把定時器信息存儲到程序中的channel_timers 中。在StasisEnd事件句柄中,我們可以取消掉定時器。如果通道離開了我們的應用程序后,我們可以觸發(fā)這個定時器來執(zhí)行某些流程或告警信息,例如HTTP返回消息等。
  \
  最后,我們打印出在ChannelStateChanged 句柄中的通道狀態(tài)信息。這里打印出通道應答的消息:
  \
  channel-state.py
  完整的channel-state.py 源代碼:
  channel-state.py
 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62

#!/usr/bin/env python
 
import ari
import logging
import threading
 
logging.basicConfig(level=logging.ERROR)
 
client = ari.connect('http://localhost:8088''asterisk''asterisk')
 
channel_timers = {}
 
def stasis_end_cb(channel, ev):
    """Handler for StasisEnd event"""
 
    print "Channel %s just left our application" % channel.json.get('name')
 
    # Cancel any pending timers
    timer = channel_timers.get(channel.id)
    if timer:
        timer.cancel()
        del channel_timers[channel.id]
 
def stasis_start_cb(channel_obj, ev):
    """Handler for StasisStart event"""
 
    def answer_channel(channel):
        """Callback that will actually answer the channel"""
        print "Answering channel %s" % channel.json.get('name')
        channel.answer()
        channel.startSilence()
 
        # Hang up the channel in 4 seconds
        timer = threading.Timer(4, hangup_channel, [channel])
        channel_timers[channel.id= timer
        timer.start()
 
     def hangup_channel(channel):
        """Callback that will actually hangup the channel"""
 
        print "Hanging up channel %s" % channel.json.get('name')
        channel.hangup()
 
    channel = channel_obj.get('channel')
    print "Channel %s has entered the application" % channel.json.get('name')
 
    channel.ring()
    # Answer the channel after 2 seconds
    timer = threading.Timer(2, answer_channel, [channel])
    channel_timers[channel.id= timer
    timer.start()
 
def channel_state_change_cb(channel, ev):
    """Handler for changes in a channel's state"""
    print "Channel %s is now: %s" % (channel.json.get('name'),
                                     channel.json.get('state'))
 
client.on_channel_event('StasisStart', stasis_start_cb)
client.on_channel_event('ChannelStateChange', channel_state_change_cb)
client.on_channel_event('StasisEnd', stasis_end_cb)
 
client.run(apps='channel-state')

  channel-state.py 實際輸出結果
  這是我們終端“alice” 使用PJSIP通道進入到我們應用程序時的輸出結果:
\
  JavaScript (Node.js)
  這個例子中我們使用了ari-client 支持包。這個基本的架構類似于channel-dump JavaScript- 用戶可以參考這個實例來學習如何使用這個支持包連接ARI。
  這里,我們首先需要創(chuàng)建我們的ARI客戶端,并且注冊三個不同的事件-StasisStart,ChannelStateChange,和StasisEnd。
  很多流程的處理是在StasisStart中進行,當通道進入到我們的應用程序時,StasisStart就會被調用。大部分情況下,這里還要設置一個Python定時器來初始化通道中的一些指令。
  當通道應答呼叫后,ChannelStateChange 句柄會打印出通道改變狀態(tài)的輸出信息。
  最后,系統(tǒng)會把初始化的定時器權限,然后發(fā)出StasisEnd 的事件。當通道離開應用程序后,這個事件就會馬上被調用,通常情況下,可能是呼叫方掛機,或者我們自己掛機。
  我們可以啟動一個通道的定時器:
  \
  然后注冊三個事件:
 \
  這里的StasisStart 事件需要用戶更多關注。
  首先我們通知通道指令,2秒鐘以后應答通道:
  \
  這里,我們在通道channel_timers 中存儲了一個定時器,如果用戶掛機以后,我們可以發(fā)出StasisEnd 的事件。
  一旦程序執(zhí)行進入到answer_channel中,系統(tǒng)應答這個通道,在通道中啟動一個靜音播放。注意,我們會繼續(xù)聲明一個answer_channel,這是一個在我們StasisStart 句柄內嵌的函數(shù):
\
  我們應答了通道以后,系統(tǒng)會在4秒鐘內啟動另外一個定時器對通道進行掛機處理。當定時器被觸發(fā)后,它會調用hangup_channel。這是通道中最后的掛機程序。另外,我們會在StasisStart句柄中再次聲明hangup_channel:
  \
  當創(chuàng)建了定時器以后,我們在通道中振鈴時,系統(tǒng)會把定時器信息存儲到程序中的channel_timers 中。在StasisEnd事件句柄中,我們可以取消掉定時器。如果通道離開了我們的應用程序后,我們可以觸發(fā)這個定時器來執(zhí)行某些流程或告警信息,例如HTTP返回消息等。
  \
  最后,我們打印出在ChannelStateChanged 句柄中的通道狀態(tài)信息。這里打印出通道應答的消息:
 \
  channel-state.js
  完整的channel-state.js 源代碼:
  channel-state.js
  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81

/*jshint node: true*/
'use strict';
 
var ari = require('ari-client');
var util = require('util');
 
var timers = {};
ari.connect('http://localhost:8088''asterisk''asterisk', clientLoaded);
 
// handler for client being loaded
function clientLoaded (err, client) {
  if (err) {
    throw err;
  }
 
  // handler for StasisStart event
  function stasisStart(event, channel) {
    console.log(util.format(
          'Channel %s has entered the application', channel.name));
 
    channel.ring(function(err) {
      if (err) {
        throw err;
      }
    });
    // answer the channel after 2 seconds
    var timer = setTimeout(answer, 2000);
    timers[channel.id] = timer;
 
    // callback that will answer the channel
    function answer() {
      console.log(util.format('Answering channel %s', channel.name));
      channel.answer(function(err) {
        if (err) {
          throw err;
        }
      });
      channel.startSilence(function(err) {
        if (err) {
          throw err;
        }
      });
      // hang up the channel in 4 seconds
      var timer = setTimeout(hangup, 4000);
      timers[channel.id] = timer;
    }
 
    // callback that will hangup the channel
    function hangup() {
      console.log(util.format('Hanging up channel %s', channel.name));
      channel.hangup(function(err) {
        if (err) {
          throw err;
        }
      });
    }
  }
 
  // handler for StasisEnd event
  function stasisEnd(event, channel) {
    console.log(util.format(
          'Channel %s just left our application', channel.name));
    var timer = timers[channel.id];
    if (timer) {
      clearTimeout(timer);
      delete timers[channel.id];
    }
  }
 
  // handler for ChannelStateChange event
  function channelStateChange(event, channel) {
    console.log(util.format(
          'Channel %s is now: %s', channel.name, channel.state));
  }
 
  client.on('StasisStart', stasisStart);
  client.on('StasisEnd', stasisEnd);
  client.on('ChannelStateChange', channelStateChange);
 
  client.start('channel-state');
}

channel-state.js in action
  這是我們終端“alice” 使用PJSIP通道進入到我們應用程序時的輸出結果:
\

相關閱讀:

專題