Отложенный вызов

Давно меня посещала мысль, сделать отложенный вызов CCSS(по терминологии Asterisk). И тут бац, статья на хабре . Спасибо Сергею, что указал куда копать)
Варианты применения:
1. CCBS. Вы звоните Михалычу, а он занял все линии и трещит по ним, Вы слышите сигнал занято, Вас это огорчает, В сименсовской АТС есть кнопка «перезвонить мне». Мы ее нажимаем и.. как Только Михалыч освободится, мы об этом узнаем)
2. CCNR. Звоним Лехе, а он не берет трубку, занят или ушел…, тут у него появляется стойкое желание позвонить жене, набирает жену и…. тут то мы и узнаем, что он на работе))
!!Набор не кладя трубки реализован даже на generic.
Кого Заинтересовало прошу
Немного терминологии:
agents — действует от имени вызывающего абонента(следящий). Сторона позвонившего.
monitors — контролирует состояние вызываемого абонента(за кем смотрим). Сторона не принявшая вызов.
Generic Agent и Monitors должны применяться ТОЛЬКО для телефонов. Использует протоколо-независимые (protocol agnostic) методы.
Native Agent и Monitors, могут применяться и к транкам, но использует специфические методы протокола, оборудование должно поддерживать эти методы.
!!Native Sip CC работает только с установленной: xml2. Я не нашел девайсов у которых есть нативная поддержка СС:(
Offer — Предложение CC относящееся к уведомлению, полученному вызывающим, что он может запросить CC.
Request — Когда звонящий решил подписаться на СС, он делает запрос)
Recall — Когда мы получили сообщение о том, что абонент доступен мы совершаем повторный звонок(Recall)

Для начала нужно настроить агентов, для звонящих(в sip.conf в описании пользователя cc_agent_policy), и хотя бы один монитор для получателя вызова(cc_monitor_policy).
cc_offer_timer — таймер за который нужно решить, будем мы заказывать отложенный вызов или нет

Когда мы решили что делаем запрос на СС. В зависимости от агента мы делаем это по разному.
в случае Generic мы кладем трубку и набираем специальный номер(как это обойти я написал ниже), или забиваем его под кнопку.
в случае native агента, возможен выбор еще во время первоначального вызова, как в панасонике 6ку)

Мониторинг generic следит за состоянием устройства, пока оно не станет NOT_INUSE. Native использует специальные сообщения.
Когда абонент стал доступен, при generic agent: * вызывает звонящего, выполняет макрос(если задан), а потом звонит адресату.
при Native agent:
Посылается сообщение, что абонент снова доступен и ему можно перезвонить..
Если мы ждали ждали и выключили телефон, а адресат появился, астериск будет отслеживать ваше состояние, как только вы станете доступны, астер вернется к мониторингу того кому вы хотели позвонить.

Для того чтобы перезвонить можно использовать переменную канала CC_INTERFACES, которая используется как аргумент в Dial().

Если мы совершили звонок и у нас опять фейл(не взяли или занято), то нужно будет повторить CC!!!

Рассмотрим параметры ccss.conf:
Раздел [generic]
cc_max_requests = 20 сколько одновременных запросов будет держать Астериск(Ресурсы сервака то у нас не безграничны)
!!Вот тут я делаю себе грабли!!
Привязка состояния СС к состоянию устройства в * по умолчанию:
cc_available_devstate = NOT_INUSE
cc_offered_devstate = NOT_INUSE
cc_caller_requested_devstate = NOT_INUSE
cc_active_devstate = INUSE
cc_callee_ready_devstate = INUSE
cc_caller_busy_devstate = ONHOLD
cc_recalling_devstate = RINGING
cc_complete_devstate = NOT_INUSE
cc_failed_devstate = NOT_INUSE

Можно задать свои значения.
Используется для создания DEVICE_STATE информации, которая может быть включена в Asterisk, чтобы проверить состояние помощью EXTENSION_STATE () или DEVICE_STATE ().

Состояние записывается в формате: «ccss:TECH/ID» на примере девайса SIP/3000 совершающего CallCompletionRequest() может быть проверено DEVICE_STATE(ccss:SIP/3000) или с помощью хинта.
[hint-context]
exten => *843000,hint,ccss:SIP/3000

А потом доступен с помощью EXTENSION_STATE(*[email protected]) или BLF на телефоне

Остальные параметры указываются в файле описания канала для каждого устройства
Например в описании пира в sip.conf.
Я использую MySQL для хранения пиров, поэтому поля добавлял в БД.

[Mark]
context=phone_calls
cc_agent_policy=generic
cc_monitor_policy=generic 
 
[Richard]
context=phone_calls
cc_agent_policy=generic
cc_monitor_policy=generic

Также можно задать:
cc_callback_macro= макрос который выполняется, после того как мы позвонили вызывающему абоненту, но до соединения с тем, кому хотели позвонить.
cc_max_monitors = 5 Сколько человек может следить за вашим состоянием одновременно.
cc_max_agents = 5 Сколько запросов, мы можем сделать !!ДЛЯ generic, эта опция игнорируется. ВСЕГДА =1(Храним только последний запрос).
cc_agent_policy=never
Возможные значения:
never — отключено
generic — протоколо независимый
native — зависит от реализации СС в протоколе
cc_monitor_policy=never
Возможные значения:
never — отключено
generic — протоколо независимый
native — зависит от реализации СС в протоколе
always — если не знаем нативный СС или нет в телефоне. * сам выберет на основе формата offer.
cc_recall_timer = 20 Сколько мы будем пытаться дозвониться вызывающему, если адресат стал доступен. Только для generic!
cc_offer_timer = 20 таймер за который нужно решить, будем мы заказывать отложенный вызов или нет.
ccbs_available_timer = 4800 Сколько мы будем ждать когда же освободиться адресат. Если по прошествию этого времени, линия занята, мониторинг адресата прекращается.
ccnr_available_timer = 7200 Сколько мы будем ждать активности адресата. Если по прошествию этого времени, не было активности, мониторинг адресата прекращается.

extensions.conf

[phone_calls]
exten => 1000,1,Dial(SIP/Mark,20)
exten => 1000,n,Hangup
exten => 2000,1,Dial(SIP/Richard,20)
exten => 2000,n,Hangup
exten => 30,1,CallCompletionRequest
exten => 30,n,Hangup
exten => 31,1,CallCompletionCancel
exten => 31,n,Hangup

При использовании generic agent, значение сс_max_agents игнорируется.
Так же есть функция CALLCOMPLETION для просмотра/установки параметров СС.

Если вдруг у нас появилось стойкое желание сделать астериск более ламповым, и дать возможность народу набирать номер не положив трубочку. Поскольку сип таки многоканальный, и приоритеты абонентов таки решают, шеф будет не очень доволен если его заставят жать кнопки) Поэтому мы всегда будем отправлять вызов абоненту, а там или по занятости или по неответу…

exten=> _710X,1,NoOp(start)
same => n,Dial(SIP/${EXTEN},20,n)
same => n,GotoIf($["${DIALSTATUS}" = "ANSWER"]?noo:unavail)
same => n(unavail),Read(uni,vm-extension&vm-isunavail,1)
same => n,GotoIf($[${uni} = 6]?yess:noo)
same => n(yess),CallCompletionRequest
same => n,PlayBack(vm-saved)
same => n(noo),Hangup()

Другие реализации через DEVICE_STATE окончились провалом)
Играясь с переменной и функцией набил пару шишек, из-за граблей которые сам себе тихонько разложил)
потом нашел статью[3], я не один такой, и решил что мой метод не так уж и плох, если кому не нравиться женский голос можем поставить сигнал занятости, ну и желательно выставить таймаут в Read.

Литература:
1. https://wiki.asterisk.org/wiki/display/AST/CCSS+Glossary
2. http://habrahabr.ru/post/154887/#habracut
3. http://asteriskfaqs.org/2012/03/21/asterisk-users/asterisk-1-8-busylevel-and-ccbs.html


Комментарии: