Asterisk™: будущее телефонии Второе издание - Меггелен Джим Ван
Затем с помощью функции H0TDESK_L0GGED_IN_USER() получаем добавочный номер зарегистрированного пользователя. Переменная L0CATI0N содержит значение desk_1, обозначающее устройство, которое мы хотим проверить, а ${R0W_C0UNTER} содержит номер итерации цикла. Оба эти значения передаются как аргументы функции диалплана. Результат затем присваивается переменной WH0 (кто):
exten => logout_login,n,Set(WH0=${H0TDESK_L0GGED_IN_USER(${L0CATI0N}, ${R0W_C0UNTER})})
Далее функция H0TDESK_L0GGED_IN_USER() извлекает из базы данных строку, соответствующую итерации цикла, которую мы пытаемся обработать:
[L0GGED_IN_USER]
prefix=H0TDESK
dsn=asterisk
read=SELECT extension FR0M ast_hotdesk WHERE status = '1' AND location = '${ARG1}' 0RDER BY id LIMIT '1' 0FFSET '${ARG2}'
Теперь, когда известен добавочный номер, данные для которого требуется изменить, выполняем запись в функцию HOTDESK_STATUS() и присваиваем 0 столбцу status для строки, в которой добавочный номер соответствует значению переменной ${WHO} (то есть 1101). Завершаем цикл с помощью EndWhile() и возвращаемся к добавочному номеру valid_login в приоритет с меткой set_login_status (как обсуждалось ранее):
exten => logout_login,n,Set(HOTDESK_STATUS(${WHO})=0) ; отмена регистрации телефона
exten => logout_login,n,Set(ROW_COUNTER=$[${ROW_COUNTER} + 1]) exten => logout_login,n,EndWhile()
exten => logout_login,n,Goto(valid_login,set_login_status) ; возвращаемся к процессу регистрации
Все остальное должно быть достаточно понятным (если что-то неясно, вернитесь к главам 5 и 6). Затруднения может вызвать только прием с использованием переменной канала ${ODB CROWS}, которая задается функцией HOTDESK_STATUS(). Она сообщает, сколько строк было изменено в результате SQL-запроса UPDATE (обновить). Мы предполагаем, что это значение равно 1. Если значение ${ODBCROWS} меньше 1, мы рассматриваем это как ошибку и обрабатываем соответствующим образом: exten => logout,1,NoOp()
exten => logout,n,Set(HOTDESK_STATUS(${E})=0) exten => logout,n,GotoIf($[${ODBCROWS} < 1]?error,1) exten => logout,n,Playback(silence/1&agent-loggedoff) exten => logout,n,Hangup()
exten => login_fail,1,NoOp()
exten => login_fail,n,Playback(silence/1&login-fail) exten => login_fail,n,Hangup()
exten => error,1,NoOp()
exten => error,n,Playback(silence/1&connection-failed) exten => error,n,Hangup()
exten => invalid_user,1,NoOp()
exten => invalid_user,n,Verbose(1|Hot Desk extension ${E} does not exist)
exten => invalid_user,n,Playback(silence/2&invalid)
exten => invalid_user,n,Hangup()
Также включаем контекст hotdesk_outbound, который будет обрабатывать наши исходящие звонки после регистрации агента в системе:
include => hotdesk_outbound Контекст hotdesk_outbound преимущественно следует тем же принципам и правилам, которые обсуждались ранее, поэтому не будем рассматривать его слишком подробно. Фактически контекст [hotdesk_ outbound] будет обрабатывать все номера, набираемые с настольных телефонов. Сначала задаем переменную LOCATION, используя переменную CHANNEL, затем определяем, какой добавочный номер (агент) зарегистрировался в системе, и сохраняем его в переменную WHO. Если значение этой переменной NULL, отклоняем исходящий звонок. Если значение переменной не NULL, с помощью функции H0TDESK_INF0() получаем информацию об агенте и сохраняем ее в нескольких переменных канала CHANNEL. Сюда входит и контекст для обработки звонка, где выполняется переход (с помощью функции Goto()) в заданный для нас контекст (который управляет нашим исходящим доступом). Если попытаться набрать номер, не обрабатываемый нашим контекстом (или одним из промежуточных контекстов - то есть контекст international содержит переход в контекст long distance, который, в свою очередь, содержит переход в local), выполняется встроенный добавочный номер i, который воспроизводит для вызывающего абонента сообщение о невозможности такого действия и отсоединяет его:
[hotdesk_outbound]
exten => _X.,1,No0p()
exten => _X.,n,Set(L0CATI0N=${CUT(CHANNEL,/,2)})
exten => _X.,n,Set(L0CATI0N=${CUT(L0CATI0N,-,1)})
exten => _X.,n,Set(WH0=${H0TDESK_PH0NE_STATUS(${L0CATI0N})})
exten => _X.,n,GotoIf($[${ISNULL(${WH0})}]?no_outgoing,1)
exten => _X.,n,Set(${WH0}_CID_NAME=${H0TDESK_INF0(cid_name,${WH0})})
exten => _X.,n,Set(${WH0}_CID_NUMBER=${H0TDESK_INF0(cid_number,${WH0})})
exten => _X.,n,Set(${WH0}_C0NTEXT=${H0TDESK_INF0(context,${WH0})})
exten => _X.,n,Goto(${${WH0}_C0NTEXT},${EXTEN},1)
[international]
exten => _011.,1,No0p()
exten => _011.,n,Set(E=${EXTEN})
exten => _011.,n,Goto(outgoing,call,1)
exten => i,1,No0p()
exten => i,n,Playback(silence/2&sorry-cant-let-you-do-that2) exten => i,n,Hangup()
include => longdistance
[longdistance]
exten => _1NXXNXXXXXX,1,No0p()
exten => _1NXXNXXXXXX,n,Set(E=${EXTEN})
exten => _1NXXNXXXXXX,n,Goto(outgoing,call,1)
exten => _NXXNXXXXXX,1,Goto(1${EXTEN},1)
exten => i,1,No0p()
exten => i,n,Playback(silence/2&sorry-cant-let-you-do-that2) exten => i,n,Hangup()
include => local
[local]
exten => _416NXXXXXX,1,No0p()
exten => _416NXXXXXX,n,Set(E=${EXTEN})
exten => _416NXXXXXX,n,Goto(outgoing,call,1) exten => i,1,NoOp()
exten => i,n,Playback(silence/2&sorry-cant-let-you-do-that2) exten => i,n,Hangup()
Если звонок может быть выполнен, он направляется на обработку в контекст [outgoing], где с помощью функции CALLERID() задаются имя и номер для ID вызывающего абонента. После этого вызов передается по SIP-каналу с помощью service_provider, который был создан в файле sip.conf. [outgoing]
exten => call,1,NoOp()
exten => call,n,Set(CALLERID(name)=${${WHO}_CID_NAME}) exten => call,n,Set(CALLERID(number)=${${WHO}_CID_NUMBER}) exten => call,n,Dial(SIP/service_provider/${E}) exten => call,n,Playback(silence/2&pls-try-call-later) exten => call,n,Hangup()
Наш service_provider в файле sip.conf мог бы выглядеть примерно так:
[service_provider] type=friend
host=switch1.service_provider.net
username=my_username
fromuser=my_username
secret=welcome
context=incoming
canreinvite=no
disallow=all
allow=ulaw
И это все! Полностью диалплан, используемый для реализации возможности «горячих столов», можно увидеть в приложении G. Мы только что рассмотрели столько всего, что можно реализовать с помощью func_odbc! Теперь вы понимаете, почему нас так восторгает эта функция?!
Обратная совместимость func_odbc
С Asterisk 1.4 можно использовать версию func_odbc, созданную для обеспечения обратной совместимости, которая применяет немного другой формат конфигурации. Это позволяет использовать множество DSN-соединений с разными базами данных, а также применять переменную канала ${ODBCROWS} для SQL-запросов read (SELECT). Загрузить версию func_odbc для обратной совместимости и установить ее (что приведет к перезаписи существующего файла func_odbc.c) можно так:
# cd /usr/src/
# svn co http://svncommunity.digium.com/svn/func_odbc/1.4 ./func_odbc-1.4
# cp func_odbc-1.4/func_odbc.c ./asterisk-1.4/funcs
# cp: overwrite /asterisk-1.4/funcs/func_odbc.c'? y
• cd asterisk-1.4
• make install
Описанная в данной главе версия работает с текущей версией Asterisk 1.4, но в версии для обратной совместимости (и Asterisk 1.6) будет использоваться следующий измененный синтаксис:
• read станет readsql.
• write станет writesql.
• dsn станет readhandle и writehandle (для отдельных строк в базе данных для чтения и записи).