Джим Меггелен - Asterisk™: будущее телефонии Второе издание
Сценарий AGI, который мы напишем на Python, называется «Игра в вычитание». Источником идей для его написания стала программа на Perl, созданная Эдом Гаем (Ed Guy) и представленная им на конференции AstriCon в 2004 году. Эд рассказывал, в какой восторг он пришел от мощи и простоты Asterisk, когда обнаружил, что может написать короткий сценарий на Perl, чтобы помочь своей дочери с математикой. Поскольку мы уже написали Perl-программу, использующую AGI и Эд создал свою математическую программу на Perl, мы решили заняться реализацией этой задачи на Python! Итак, разберем наш сценарий на Python: #!/usr/bin/python
Данная строка указывает системе выполнять этот сценарий в интерпретаторе Python. Для небольших сценариев в эту строку можно добавить опцию -u, что обеспечит выполнение Python в режиме без буферизации. Однако это не рекомендуется для больших или часто используемых сценариев AGI, поскольку может сказаться на производительности системы. import sys import re import time import random
Здесь импортируются несколько библиотек, которые будут использоваться в сценарии AGI.
# Читаем и игнорируем среду AGI (читать до пустой строки)
env = {} tests = 0;
while 1:
line = sys.stdin.readline().strip()
if line == '': break
key,data = line.split(':') if key[:4] <> 'agi_':
# игнорируем ввод, который начинается не с agi_ sys.stderr.write("Did not work!n"); sys.stderr.flush() continue key = key.strip() data = data.strip() if key <> '':
env[key] = data
sys.stderr.write("AGI Environment Dump:n");
sys.stderr.flush()
for key in env.keys():
sys.stderr.write(" -- %s = %sn" % (key, env[key])) sys.stderr.flush()
Данный фрагмент кода читает переменные, передаваемые в сценарий из Asterisk, и сохраняет их в словарь env. Затем эти значения записываются в STDERR для целей отладки.
def checkresult (params): params = params.rstrip() if re.search('"200',params): result = re.search('result=(d+)',params) if (not result):
sys.stderr.write("FAIL ('%s')n" % params) sys.stderr.flush() return -1 else:
result = result.group(1)
#debug("Result:%s Params:%s" % (result, params)) sys.stderr.write("PASS (%s)n" % result) sys.stderr.flush() return result
else:
sys.stderr.write("FAIL (unexpected result '%s')n" % params)
sys.stderr.flush()
return -2
Функция checkresult по своему назначению практически идентична подпрограмме checkresult в примере AGI-сценария на Perl, который рассматривался ранее в этой главе. Она читает результат выполнения команды Asterisk, проводит синтаксический разбор результата и сообщает, была команда выполнена успешно или нет. def sayit (params):
sys.stderr.write("STREAM FILE %s ""n" % str(params)) sys.stderr.flush()
sys.stdout.write("STREAM FILE %s ""n" % str(params)) sys.stdout.flush()
result = sys.stdin.readline().strip() checkresult(result)
Функция sayit - это просто оболочка для команды STREAM FILE.
def saynumber (params):
sys.stderr.write("SAY NUMBER %s ""n" % params) sys.stderr.flush()
sys.stdout.write("SAY NUMBER %s ""n" % params) sys.stdout.flush()
result = sys.stdin.readline().strip() checkresult(result)
Функция saynumber - это просто оболочка для команды SAY NUMBER.
def getnumber (prompt, timelimit, digcount):
sys.stderr.write("GET DATA %s %d %dn" % (prompt, timelimit, digcount)) sys.stderr.flush()
sys.stdout.write("GET DATA %s %d %dn" % (prompt, timelimit, digcount)) sys.stdout.flush()
result = sys.stdin.readline().strip() result = checkresult(result) sys.stderr.write("digits are %sn" % result) sys.stderr.flush() if result:
return result else:
result = -1
Функция getnumber вызывает команду GET DATA для получения DTMF-вво- да от вызывающего абонента. Она используется в нашей программе для получения ответов абонента на поставленные задачи по вычитанию.
limit=20
digitcount=2
score=0
count=0
ttanswer=5000
Здесь выполняется задание исходных значений нескольким переменным, которые будут использоваться в программе.
starttime = time.time() t = time.time() - starttime
В этих строках переменной starttime задается текущее время, а переменной t - начальное значение 0. Переменная t будет использоваться для отсчета времени с момента запуска сценария AGI в секундах.
sayit("subtraction-game-welcome")
Далее, мы рады приветствовать абонента в нашей игре на вычитание.
while ( t < 180 ):
big = random.randint(0,limit+1) big += 10
subt= random.randint(0,big) ans = big - subt count += 1
#постановка задачи:
sayit("subtraction-game-next");
saynumber(big);
sayit("minus");
saynumber(subt);
res = getnumber("equals",ttanswer,digitcount);
if (int(res) == ans) : score+=1
sayit("subtraction-game-good"); else :
sayit("subtraction-game-wrong"); saynumber(ans);
t = time.time() - starttime Это сердце сценария AGI. При циклическом выполнении данного фрагмента кода абоненту в течение 180 с предлагаются задачи на вычитание. В начале цикла берутся два случайных числа и вычисляется их разность. Затем абоненту предлагается решить эту задачу. Читается ответ абонента. Если ответ неверен, дается правильный ответ. pct = float(score)/float(count)*100; sys.stderr.write("Percentage correct is %dn" % pct) sys.stderr.flush() sayit("subtraction-game-timesup") saynumber(score) sayit("subtraction-game-right") saynumber(count) sayit("subtraction-game-pct") saynumber(pct)
После того как абонент закончил решение примеров, ему сообщается, сколько баллов он набрал.
Как видите, при написании сценариев AGI на Python следует помнить такие основные моменты:
• Выходной буфер должен очищаться после каждой записи. Это гарантирует, что AGI-программа не зависнет из-за того, что Asterisk будет ожидать освобождения буфера для записи, а Python - ответа от Asterisk.
• Чтение данных из Asterisk осуществляется с помощью команды sys.stdin.readline.
• Запись команд в Asterisk выполняется с помощью команды sys. stdout.write. После записи не забывайте вызывать sys.stdout.flush.
Библиотека AGI для Python
Если вы планируете много работать с Python для AGI, вероятно, вам пригодится модуль Python Pyst, созданный Карлом Патлэндом (Karl Putland). Его можно найти по адресу http://sourceforge.net/projects/pyst.
Отладка в AGI
Отладка программ AGI, как и любых других программ, может приводить в уныние. К счастью, при отладке сценариев AGI есть два преимущества. Во-первых, поскольку весь обмен информацией между Asterisk и программой AGI происходит через STDIN и STDOUT (и конечно, STDERR), у вас должно получиться выполнять сценарий AGI непосредственно из операционной системы. Во-вторых, в Asterisk есть удобная команда для отображения всех взаимодействий между ним и сценарием AGI - agi debug.
Отладка из операционной системы
Как упоминалось выше, у вас должно получиться запустить свою программу прямо из операционной системы, чтобы проверить ее поведение. Хитрость здесь в том, чтобы действовать подобно Asterisk, предоставляя сценарию следующее:
• Список переменных и их значений, таких как agi_test:1.
• Символы перевода строки (n), указывающие на то, что передача переменных завершена.
• Ответы на каждую из команд AGI, поступающую из вашего сценария AGI. Обычно достаточно ввести 200 response=1.
При тестировании программы непосредственно из операционной системы, возможно, проще замечать ошибки в ней.
Использование команды Asterisk agi debug
В интерфейсе командной строки Asterisk есть очень полезная команда для отладки сценариев AGI, которая называется (вполне уместно) agi debug. Если ввести в консоли Asterisk agi debug и затем запустить AGI- сценарий, вы увидите нечто подобное:
-- Executing AGI("Zap/1-1", "temperature.php") in new stack
-- Launched AGI Script /var/lib/asterisk/agi-bin/temperature.php AGI Tx >> agi_request: temperature.php AGI Tx >> agi_channel: Zap/1-1 AGI Tx >> agi_language: en AGI Tx >> agi_type: Zap AGI Tx >> agi_uniqueid: 1116732890.8 AGI Tx >> agi_callerid: 101 AGI Tx >> agi_calleridname: Tom Jones
AGI Tx >> agi_callingpres: 00 AGI Tx >> agi_callingani2: 0 AGI Tx >> agi_callington: 0 AGI Tx >> agi_callingtns: 0 AGI Tx >> agi_dnid: unknown AGI Tx >> agi_rdnis: unknown AGI Tx >> agi_context: incoming AGI Tx >> agi_extension: 141 AGI Tx >> agi_priority: 2 AGI Tx >> agi_enhanced: 0.0 AGI Tx >> agi_accountcode: AGI Tx >> AGI Rx << STREAM FILE temperature AGI Tx >> 200 result=0 endpos=6400 AGI Rx << STREAM FILE is "" AGI Tx >> 200 result=0 endpos=5440 AGI Rx << SAY NUMBER 67 ""-- Playing 'digits/60' (language 'en')
-- Playing 'digits/7' (language 'en')
AGI Tx >> 200 result=0
AGI Rx << STREAM FILE degrees ""
AGI Tx >> 200 result=0 endpos=6720
AGI Rx << STREAM FILE fahrenheit ""
AGI Tx >> 200 result=0 endpos=8000
-- AGI Script temperature.php completed, returning 0
Во время выполнения сценария AGI будут выведены строки трех типов. Первый тип - строки, начинающиеся с AGI TX >>. Это строки, которые Asterisk передает в STDIN вашей программы. Второй тип - строки, начинающиеся с AGI RX <<. Это команды, которые ваша AGI-программа записывает в Asterisk через STDOUT. Третий тип - строки, начинающиеся с --. Это стандартные сообщения Asterisk, выводимые при выполнении определенных команд.
Чтобы отключить отладку AGI после запуска, просто введите в консоли Asterisk agi no debug.
Используя команду agi debug, можно увидеть взаимодействие между Asterisk и своей программой, что может быть очень полезным при отладке. Надеемся, эти два совета помогут вам создавать и отлаживать мощные AGI-программы.
Заключение
AGI для разработчика - это один из наиболее революционных и веских аргументов в пользу Asterisk, а не закрытой узкоспециализированной офисной АТС. Но AGI - это только часть картины. В главе 10 будет рассмотрен другой мощный интерфейс программирования, известный как Asterisk Manager Interface.10
Интерфейс Asterisk Manager (AMI) и Adhearsion
Лучше позаботьтесь о том, чтобы все слова ваши были понятны, пристойны и правильно расположены, чтобы каждое предложение и каждый ваш период, затейливый и полнозвучный, с наивозможною и доступною вам простотою и живостью передавали то, что вы хотите сказать; выражайтесь яснее, не запутывая и не затемняя смысла. - Мигель де Сервантес, предисловие к книге «Дон Кихот»