当初、アスタリスク機能の唯一の「プロバイダー」はモジュールでした。その多くは、アプリケーションとダイヤルプラン機能の兵器庫を拡張しました。
その後、当初、これらのコマンドと機能はすべて時代を先取りしており、それらのおかげで、アスタリスクは多くの商用製品を機能的に「作成」しました。
既存のアプリケーションと機能の制限を超える必要がある場合は、Cで独自のモジュールを作成できます。これは、機能が拡張され、どれだけ広くても既存の「セル」を終了する唯一の方法でした。
しかし、Cでのアスタリスクモジュールの開発は、些細な作業とはほとんど言えません。 これは非常に厄介なパスであり、非常に危険なパスでもあります。モジュールの重大なエラーが原因で、アスタリスクがコアに完全に落ち込んだためです。
必要なのは、機能を拡張し、他のシステムと統合するための、より「ソフト」でシンプルな方法でした。
そのため、AGIとAMIのインターフェースがありました。
アスタリスクゲートウェイインターフェイス(AGI)は、CGIとアーキテクチャ上「ラップ」された同期ダイヤルプラン実行インターフェイスです。 AGIダイヤルプランチームはプロセスを開始し、標準入出力を使用してコマンドを受信し、結果を送信しました。 AGIを使用すると、外部システムとの統合の問題を解決できます。たとえば、企業データベースにアクセスして、呼び出し元のクライアントの名前を番号で検索できます。
実際、AGIは、独自のビジネスロジックを構築するモジュールが提供するコマンドと機能を使用して、extensions.conf形式ではなく、独自のプログラミング言語でアスタリスクダイヤル計画を作成する方法を提供しました。
Asterisk Manager Interface(AMI)は、Asteriskのオブジェクトの内部状態を監視し、発生したイベントに関する情報を受け取ることができる非同期(イベント)インターフェイスです。 AGIがアーキテクチャ上CGIインターフェイスを連想させる場合、AMIセッションはtelnetセッションに似ており、サードパーティのアプリケーションがTCP / IPを介してAsterisk AMIポートに接続し、独自のコマンドを送信できます。 AMI接続でのコマンドへの応答に加えて、アスタリスクで発生するすべての種類のイベントは「フォール」し、クライアントに属するか、単に無視できるかを判断するのはクライアント次第です。
AGIについては、これは呼び出し実行メカニズムであり、AMIについては呼び出し制御メカニズムであると言えます。 ほとんどの場合、通信アプリケーションを構築するには、AGIとAMIを一緒に使用する必要があります。 さまざまなアプリケーションのビジネスロジックに「スミアリング」があり、その理解とさらなるメンテナンスと開発が複雑になっています。
さらに、さらにいくつかの制限があります。
- AGI:チャネルにサービスを提供するフローをブロックします。
- AGI:イベント(DTMF、状態変更)への反応は、AGIでのみ不可能または困難です。
- 基本的な操作は、チャネルで実行されるものに限定されます。 ただし、他のプリミティブがあります。ブリッジ、デバイス、状態、メッセージの表示、およびAGI / AMIでは利用できないチャネル上のメディアです。
- AMIとAGIは時代遅れです。 REST、XML / JSON RPCは、今日の世界ではより身近で便利です。
その結果、コマンドと機能の既存の制限を超えるために、低レベルの電話プリミティブを実装し、AGI&AMIを使用して外部システムと統合する独自のCモジュールを作成する必要があります。
それは、 アスタリスクRESTインターフェイスの前でした。
ARIの主要な概念:
- ARIを使用すると、コールの状態の制御(コール制御)とロジックの実行(コール実行)の両方を実行できます。
- ARIは非同期です。
- ARIは、チャネル、ブリッジ、デバイスなどのプリミティブを「露出」「生」します。 RESTインターフェースを介して。
- オブジェクトの状態は、WebSocket上のJSONイベントを通じて利用できます。
- ARI-ボイスメールアプリケーションへの呼び出しを「誘導」するのではなく、独自のボイスメールアプリケーションを作成します。
3頭のクジラARI:
- RESTfulインターフェイス。
- 制御されたリソースに関するイベントがJSON形式で送信されるWebSocket接続。
- Dialplanアプリケーション-Stasis、チャネル制御をARIアプリケーションに転送します。
制御をStaisに転送するダイヤルプランの例:
exten => _X.,1,Stasis(myapp,arg1,arg2) exten => _X.,n,NoOp(Left Stasis)
ARIにはいくつかの制限があります
- ARIはオブジェクトにはアクセスできませんが、それが制御するオブジェクトにのみアクセスできます。 これは、Stasisアプリケーションで盗まれていないチャンネルでは答えられないことを意味します。 ただし、チャネルリストは、Stasisで盗まれたチャネルだけでなく、すべてのアクティブなチャネルを返します。
- アスタリスク側で定義されている操作のみが使用可能です(このアスタリスクはすべてのREST操作を定義しているため、理解できます)。
- Stasisアプリケーションは、クライアント接続でのみ使用可能です。 このアプリケーションの名前でWebSocketへの接続がない場合、Stasisはエラーをスローし、ダイヤルプランをさらに進めます。
ARIで利用可能な操作のカテゴリを検討してください。
- アスタリスク
- 橋
- チャンネル
- デバイス(エンドポイント)
- デバイスの状態
- イベント
- メールボックス
- プレイバック
- 録音
- 音
さらに、各カテゴリについて詳しく説明します。
アスタリスク
- 動的構成(ソーサリー、pjsip)
- 組立情報
- モジュール管理(リスト、ロード、アンロード)
- ロギングおよびローテーション管理
- グローバル変数(読み取りおよび設定)
橋
- ブリッジの取得、作成、削除
- チャンネルの追加/削除
- 待機中に音楽を再生する
- 記録を有効にする
チャンネル
- アクティブなチャンネルとチャンネルの詳細のリスト。
- チャネルを作成(発信)および削除(ハングアップ)します。
- ダイヤルプラン出口
- チャンネルのリダイレクト
- 応答、リング、DTMF、ミュート、ホールド、MoH、無音、再生、録音、可変、スヌープ
チャンネル
- アクティブなチャンネルとチャンネルの詳細のリスト。
- チャネルを作成(発信)および削除(ハングアップ)します。
- ダイヤルプラン出口
- チャンネルのリダイレクト
- 応答、リング、DTMF、ミュート、ホールド、MoH、無音、再生、録音、可変、スヌープ
装置
- すべてのデバイスのリスト
- デバイスへのメッセージの送信(SIP、PJSIP、XMPP)
デバイスの状態
- 監視対象デバイスのステータスのリスト
- ステータス設定(NOT_INUSE、INUSE、BUSY、INVALID、UNAVAILABLE、RINGING、RINGINUSE、ONHOLD)
wikiアスタリスクで可能な操作の完全なリストを参照してください-https://wiki.asterisk.org/wiki/display/AST/Asterisk+13+ARI
イベント
接続されたアプリケーションのWebソケットで利用可能なイベントの部分的なリストは次のとおりです。
- StasisStart / StasisEnd-呼び出しがStasisに到達するとすぐにソケットに送信され、 呼び出しがStasisを離れると最後に送信されます。
- ChannelCreated / ChannelDestroyed-チャンネルを作成および破棄するとき。
- BridgeCreated / BridgeDestroyed-ブリッジを作成および破棄するとき。
- ChannelDtmfReceived -DTMFの受信時。
- ChannelStateChange-チャネル状態が変更されました。
- ChannelUsereventはカスタムイベントです。 イベントモデルARIに基づいて構築できる非常に便利なもの。
- DeviceStateChanged-デバイスの状態が変更されました(NOT_INUSE、INUSE、BUSY、INVALID、UNAVAILABLE、RINGING、RINGINUSE、ONHOLD)。
- EndpointStateChange-エンドポイントの状態が変更されました。
- PlaybackStarted / PlaybackFinished-ファイルの再生が開始および終了しました。
- TextMessageReceived-受信したメッセージ。
- その他( https://wiki.asterisk.org/wiki/display/AST/Asterisk+13+REST+Data+Models )
アスタリスク14 ARIの新機能
- レコードを取得する
- HTTPソースからのメディアの再生。
- メディアプレイリスト(1つのサウンドの終わりが次のサウンドを開始するのを非同期で待機する必要がありました)。
例
結論として、Python ARIライブラリを使用した元の呼び出しの例を示します。
この例では、指定された宴会でオリジナル化が行われ、原因コードが返されます。
#!/usr/bin/env python2.7 # Requirements: pip install ari gevent import argparse import ari import gevent from gevent.monkey import patch_all; patch_all() from gevent.event import Event import logging from requests.exceptions import HTTPError, ConnectionError import socket import time logging.basicConfig() # Important! # Otherwise you get No handlers could be found for # logger "ari.client" ARI_URL = 'http://192.168.56.101:8088/ari' ARI_USER = 'test' ARI_PASSWORD = 'test' client = ari.connect(ARI_URL, ARI_USER, ARI_PASSWORD) def run(): try: client.run('originator') except socket.error as e: if e.errno == 32: # Broken pipe as we close the client. pass except ValueError as e: if e.message == 'No JSON object could be decoded': # client.close() pass def originate(endpoint=None, callerid=None, context=None, extension=None, priority=None, timeout=None): # Go! evt = Event() # Wait flag for origination result = {} gevent.sleep(0.1) # Hack to let run() arrange all. start_time = time.time() try: channel = client.channels.originate( endpoint=endpoint, callerId=callerid, app='originator', timeout=timeout ) def state_change(channel, event): state = event['channel']['state'] if state == 'Up': channel = channel.continueInDialplan( context=context, extension=extension, priority=priority) def destroyed(channel, event): end_time = time.time() result['status'] = 'success' result['message'] = '%s (%s)' % ( event.get('cause_txt'), event.get('cause')) result['duration'] = '%0.2f' % (end_time - start_time) evt.set() channel.on_event('ChannelDestroyed', destroyed) channel.on_event('ChannelStateChange', state_change) # Wait until we get origination result evt.wait() client.close() return except HTTPError as e: result['status'] = 'error' try: error = e.response.json().get('error') result['message'] = e.response.json().get('error') except Exception: result['message'] = e.response.content finally: print result client.close() def parse_args(): parser = argparse.ArgumentParser() parser.add_argument('endpoint', type=str, help='Endpoint, eg SIP/operator/123456789') parser.add_argument('callerid', type=str, help='CallerID, eg 111111') parser.add_argument('context', type=str, help='Asterisk context to connect call, eg default') parser.add_argument('extension', type=str, help='Context\'s extension, eg s') parser.add_argument('priority', type=str, help='Context\'s priority, eg 1') parser.add_argument('timeout', type=int, help='Originate timeout, eg 60') return parser.parse_args() if __name__ == '__main__': args = parse_args() runner = gevent.spawn(run) originator = gevent.spawn(originate, endpoint=args.endpoint, callerid=args.callerid, context=args.context, extension=args.extension, priority=args.priority, timeout=args.timeout ) gevent.joinall([originator, runner])
スクリプトのコメント
- 非同期geventフレームワークは、websocketで接続を確立し、単一のスレッド内で着信メッセージを受信し、呼び出しを調整するために使用されます。
- 呼び出しのステータスとその継続時間を取得するには、接続された発信元アプリケーションをStasisにタクシーする必要があります。Stasisでは、ChannelDestroyedイベントがトリガーされ、その中で完了コードが処理されます。
- 接続後、チャネルはアップ状態になり、この場合、指定されたコンテキスト、拡張、優先度に転送されます。
- 呼び出しが完了すると、クライアント接続が閉じられます。
このスクリプトはコンソールから実行できますが、次のようなスクリプトが返されます。
(env)MacBook-Pro-Max:barrier max$ ./ari_originate.py SIP/operator 11111 default s 1 4 {'status': 'success', 'duration': '2.54', 'message': u'Normal Clearing (16)'}
パラメーターの指定:
(env)MacBook-Pro-Max:barrier max$ ./ari_originate.py -h usage: ari_originate.py [-h] endpoint callerid context extension priority timeout positional arguments: endpoint Endpoint, eg SIP/operator/123456789 callerid CallerID, eg 111111 context Asterisk context to connect call, eg default extension Context's extension, eg s priority Context's priority, eg 1 timeout Originate timeout, eg 60 optional arguments: -h, --help show this help message and exit
このスクリプトを実行するには、ariおよびgeventライブラリをインストールする必要があります。
pip install ari gevent
PS Asterconf 2016での著者のスピーチに基づいて書かれています。
PPSスクリプトはここにあります-https://gist.github.com/litnimax/2b0f9d99e49e49a07e59c45496112133