Сергей Маликов
31.12.2019
16916

Ограничение максимального количества вызовов через транки

В статье рассматривается установка ограничений по максимальному количеству занятых каналов в общем (для всех имеющихся транков) и в частности (для каждого отдельного транка). Установка ограничений по количеству звонков для отдельного транка. В веб-админке Freepbx ограничение для определенного транка можно установить, перейдя Connectivity —> Trunks (Маршрутизация —> Городские линии) и, открыв необходимый транк для редактирования, изменить […]

В статье рассматривается установка ограничений по максимальному количеству занятых каналов в общем (для всех имеющихся транков) и в частности (для каждого отдельного транка).

Установка ограничений по количеству звонков для отдельного транка.

В веб-админке Freepbx ограничение для определенного транка можно установить, перейдя Connectivity —> Trunks (Маршрутизация —> Городские линии) и, открыв необходимый транк для редактирования, изменить параметр Maximum Channels (Количество каналов).

Установка ограничений для отдельного транка
Установка ограничений для отдельного транка

Также, можно установить ограничение в конфигурационных файлах с помощью параметра call-limit. Например, установить ограничение для какого-либо определенного sip транка в Asterisk Vanilla можно добавив параметр call-limit в файле sip.conf, во Freepbx — в веб-интерфейсе, для кастомных транков, не присутствующих в веб-интерфейсе — в файле sip_custom.conf. В примере для транка sip_trunk1 установлено ограничение в два канала, для транка sip_trunk2 ограничений нет.

Установка ограничений для отдельного транка в конфигурационном файле
Установка ограничений для отдельного транка в конфигурационном файле

При превышении ограничений для отдельного транка в консоли появляется сообщение

Call to peer ‘sip_trunk1’ rejected due to usage limit of 2

Сообщение в консоли при превышении ограничения для транка
Сообщение в консоли при превышении ограничения для транка

Установка ограничений по общему количеству звонков через все транки.

Предположим, что имеются несколько офисов, имеющими транки от провайдеров и межофисные транки. Необходимо, чтобы одновременно было не более (в примере — 2-х) внешних вызовов (входящих или исходящих), при этом внутренние вызовы – без ограничений. На всех офисных серверах добавочные нумеруются по правилам. На первом имеют вид 1XX (для custom extension, напр. мобильных — нумерация вида 01XXX), на втором – 2XX (02XXX)… Таким образом, определить локальный вызов, например, во втором офисе, можно, если переменная CALLERID(num) будеть иметь длину в 3 цифры и начинаться с ‘2’ и

  • либо набран номер длиной в 3 цифры и первая ‘2’ – на попадающий под шаблон нумерации локальный номер;
  • либо набран номер в 4 цифры и первые ’02’ — на локальный кастомный номер (напр.  мобильный).

Для внесения ограничений по вызовам через транки необходимо немного изменить диалплан.

Во freepbx основные настройки находятся в веб-интерфейсе и изменения в маршрутизации, произведенные через веб-админку, заносятся в базу данных и записываются в текстовый файл extensions_additional.conf. Для того, чтобы переписать имеющийся диалплан (сделать изменения каких-либо параметров, которых нет в веб) из extensions_additional.conf необходимо скопировать часть диалплана (в данном случае — все для экстеншена s из контекстов macro-dialout-trunk и macro-dial-one) и записать в файл extensions_override_freepbx.conf. Нельзя для перезаписи маршрутизации вносить изменения в файл extensions_additional.conf  т.к. все внесенные изменения перепишутся имеющимся содержимым в базе данных при следующей правке настроек через веб-интерфейс.

Копируем из extensions_additional.conf  extensions_override_freepbx.conf и вносим изменения. В macro-dialout-trunk – ограничения для исходящих вызовов через транки. В macro-dial-one – ограничения для входящих вызовов. Пример — диалплан для офиса с нумерацией 2XX(02XX).

[macro-dialout-trunk]
include => macro-dialout-trunk-custom
exten => s,1,Set(DIAL_TRUNK=${ARG1})
;вносим изменения – задание группы и подсчет звонков
exten => s,n(checkcalls),Set(GROUP()=callers)
exten => s,n,NoOp(--- занято каналов -  ${GROUP_COUNT(callers)} ---)
;устанавливаем ограничение в 2 занятых канала, если ограничение превышено – переход на ;именованный приоритет over и отбой
exten => s,n,GotoIf($[${GROUP_COUNT(callers)} > 2]?over)
;для тестирования внесенных изменений можно использовать DumpChan – выведет список ;всех имеющихся на данный момент переменных и их значение
;exten => s,n,DumpChan()
;конец изменений, продолжение диалплана контекста macro-dialout-trunk из
;extensions_additional.conf
exten => s,n,GosubIf($[$["${ARG3}" != ""] & $["${DB(AMPUSER/${AMPUSER}/pinless)}" != "NOPASSWD"]]?sub-pincheck,s,1())
exten => s,n,GotoIf($["x${OUTDISABLE_${DIAL_TRUNK}}" = "xon"]?disabletrunk,1)
exten => s,n,Set(DIAL_NUMBER=${ARG2})
exten => s,n,Set(DIAL_TRUNK_OPTIONS=${DIAL_OPTIONS})
exten => s,n,Set(OUTBOUND_GROUP=OUT_${DIAL_TRUNK})
exten => s,n,GotoIf($["${OUTMAXCHANS_${DIAL_TRUNK}}foo" = "foo"]?nomax)
exten => s,n,GotoIf($[ ${GROUP_COUNT(OUT_${DIAL_TRUNK})} >= ${OUTMAXCHANS_${DIAL_TRUNK}} ]?chanfull)
exten => s,n(nomax),GotoIf($["${INTRACOMPANYROUTE}" = "YES"]?skipoutcid)
exten => s,n,Set(DIAL_TRUNK_OPTIONS=${IF($["${DB_EXISTS(TRUNK/${DIAL_TRUNK}/dialopts)}" = "1"]?${DB_RESULT}:${TRUNK_OPTIONS})})
exten => s,n,Macro(outbound-callerid,${DIAL_TRUNK})
exten => s,n(skipoutcid),GosubIf($["${PREFIX_TRUNK_${DIAL_TRUNK}}" != ""]?sub-flp-${DIAL_TRUNK},s,1())
exten => s,n,Set(OUTNUM=${OUTPREFIX_${DIAL_TRUNK}}${DIAL_NUMBER})
exten => s,n,Set(custom=${CUT(OUT_${DIAL_TRUNK},:,1)})
exten => s,n,ExecIf($["${MOHCLASS}"!="default" & "${MOHCLASS}"!="" & "${FORCE_CONFIRM}"="" ]?Set(DIAL_TRUNK_OPTIONS=M(setmusic^${MOHCLASS})${DIAL_TRUNK_OPTIONS}))
exten => s,n,ExecIf($["${FORCE_CONFIRM}"!="" ]?Set(DIAL_TRUNK_OPTIONS=${DIAL_TRUNK_OPTIONS}M(confirm)))
exten => s,n(gocall),Macro(dialout-trunk-predial-hook,)
exten => s,n,GotoIf($["${PREDIAL_HOOK_RET}" = "BYPASS"]?bypass,1)
exten => s,n,GotoIf($["${custom}" = "AMP"]?customtrunk)
exten => s,n,Dial(${OUT_${DIAL_TRUNK}}/${OUTNUM}${OUT_${DIAL_TRUNK}_SUFFIX},${TRUNK_RING_TIMER},${DIAL_TRUNK_OPTIONS})
exten => s,n,Noop(Dial failed for some reason with DIALSTATUS = ${DIALSTATUS} and HANGUPCAUSE = ${HANGUPCAUSE})
exten => s,n,GotoIf($["${ARG4}" = "on"]?continue,1:s-${DIALSTATUS},1)
exten => s,n(customtrunk),Set(pre_num=${CUT(OUT_${DIAL_TRUNK},$,1)})
exten => s,n,Set(the_num=${CUT(OUT_${DIAL_TRUNK},$,2)})
exten => s,n,Set(post_num=${CUT(OUT_${DIAL_TRUNK},$,3)})
exten => s,n,GotoIf($["${the_num}" = "OUTNUM"]?outnum:skipoutnum)
exten => s,n(outnum),Set(the_num=${OUTNUM})
exten => s,n(skipoutnum),Dial(${pre_num:4}${the_num}${post_num},${TRUNK_RING_TIMER},${DIAL_TRUNK_OPTIONS})
exten => s,n,Noop(Dial failed for some reason with DIALSTATUS = ${DIALSTATUS} and HANGUPCAUSE = ${HANGUPCAUSE})
exten => s,n,GotoIf($["${ARG4}" = "on"]?continue,1:s-${DIALSTATUS},1)
exten => s,n(chanfull),Noop(max channels used up)

; вносим изменения
;если лимит по вызовам превышен – переход в over, выводим сообщение и устанавливаем статус вызова CONGESTION – канал перегружен
exten => s,n(over),NoOp(--- каналы заняты, установленное ограничение превышено на  ${MATH(${GROUP_COUNT(callers)}-2)} вызова ---)
exten => s,n,Set(DIALSTATUS=CONGESTION)
;конец изменений

[macro-dial-one]
include => macro-dial-one-custom
exten => s,1,Set(DEXTEN=${ARG3})
;вносим изменения – задание группы и подсчет звонков
;Если вызов - не на внешние или в др. офис – вызов проходит, иначе - подсчет ограничений
;Вызов локальный, если
; CALLERID(num) 3 цифры и первая '2' - с локального и
;если набран номер (3 цифры) и первая '2' - локальный номер
exten => s,n,GotoIf($[${LEN(${EXTTOCALL})}=3 & ${EXTTOCALL:0:1}=2 & ${LEN(${CALLERID(num)})}=3 & ${CALLERID(num):0:1}=2]?setexttocall)
;Также вызов локальный, если
;CALLERID(num) 3 цифры и первая '2' - с локального и
;если набран номер (4 цифры) и первые '02' - на локальный номер (custom - напр.  мобильный)
exten => s,n,GotoIf($[${LEN(${EXTTOCALL})}=4 & "${EXTTOCALL:0:2}"="02" & ${LEN(${CALLERID(num)})}=3 & ${CALLERID(num):0:1}=2]?setexttocall)
;установка группы и подсчет ограничений
exten => s,n(checkcalls),Set(GROUP()=callers)
exten => s,n,NoOp(--- занято каналов -  ${GROUP_COUNT(callers)} ---)
;если ограничение по каналам превышено – переход на именованный приоритет over и отбой
exten => s,n,GotoIf($[${GROUP_COUNT(callers)} > 2]?over)
;конец внесенных изменений
exten => s,n(setexttocall),ExecIf($[${LEN(${EXTTOCALL})}=0 & ${LEN(${DEXTEN})}>0]?Set(EXTTOCALL=${DEXTEN}))
exten => s,n,Set(DIALSTATUS_CW=)
exten => s,n,GosubIf($["${FROM_DID}"!="" & "${SCREEN}"="" & "${DB(AMPUSER/${DEXTEN}/screen)}"!=""]?screen,1())
exten => s,n,GosubIf($["${DB(CF/${DEXTEN})}"!=""]?cf,1())
exten => s,n,GotoIf($["${DEXTEN:-1}"="#" | "${DB(DND/${DEXTEN})}"=""]?skip1)
exten => s,n,Set(DEXTEN=)
exten => s,n,Set(DIALSTATUS=BUSY)
exten => s,n,Goto(nodial)
exten => s,n(cwinusebusy),GotoIf($["${EXTHASCW}"!="" & "${CWINUSEBUSY}"="true"]?next3:continue)

exten => s,n(next3),ExecIf($["${EXTENSION_STATE(${DEXTEN})}"!="UNAVAILABLE" & "${EXTENSION_STATE(${DEXTEN})}"!="NOT_INUSE" & "${EXTENSION_STATE(${DEXTEN})}"!="UNKNOWN"]?Set(DIALSTATUS_CW=BUSY))
exten => s,n(continue),GotoIf($["${DEXTEN}"=""]?nodial)
exten => s,n,GosubIf($["${DEXTEN:-1}"!="#"]?dstring,1():dlocal,1())
exten => s,n,GotoIf($[${LEN(${DSTRING})}=0]?nodial)
exten => s,n,GotoIf($["${DEXTEN:-1}"="#"]?skiptrace)
exten => s,n,GosubIf($[${REGEX("^[\+]?[0-9]+$" ${CALLERID(number)})} = 1]?ctset,1():ctclear,1())
exten => s,n(skiptrace),Set(D_OPTIONS=${IF($["${NODEST}"!="" & ${REGEX("(M[(]auto-blkvm[)])" ${ARG2})} != 1]?${ARG2}M(auto-blkvm):${ARG2})})
exten => s,n,Noop(Blind Transfer: ${BLINDTRANSFER}, Attended Transfer: ${ATTENDEDTRANSFER}, User: ${AMPUSER}, Alert Info: ${ALERT_INFO})
exten => s,n,ExecIf($["${ALERT_INFO}"="" & ${LEN(${AMPUSER})}!=0 & ${LEN(${BLINDTRANSFER})}=0 & ${LEN(${ATTENDEDTRANSFER})}=0]?Set(ALERT_INFO=))
exten => s,n,ExecIf($[${LEN(${BLINDTRANSFER})}!=0]?Set(ALERT_INFO=))
exten => s,n,ExecIf($[${LEN(${ATTENDEDTRANSFER})}!=0]?Set(ALERT_INFO=))
exten => s,n,ExecIf($["${RVOL}"!=""]?Set(ALERT_INFO=${IF($["${ALERT_INFO}"!=""]?${ALERT_INFO}:Normal)}\;volume=${RVOL}))
exten => s,n,ExecIf($["${RVOL}"="" & "${DB(AMPUSER/${EXTTOCALL}/rvolume)}" != ""]?Set(ALERT_INFO=${IF($["${ALERT_INFO}"!=""]?${ALERT_INFO}:Normal)}\;volume=${DB(AMPUSER/${EXTTOCALL}/rvolume)}))
exten => s,n,GosubIf($["${ALERT_INFO}"!="" & "${ALERT_INFO}"!=" "]?func-set-sipheader,s,1(Alert-Info,${ALERT_INFO}))
exten => s,n,ExecIf($[("${MOHCLASS}"!="default") & ("${MOHCLASS}"!="")]?Set(CHANNEL(musicclass)=${MOHCLASS}))
exten => s,n,GosubIf($["${QUEUEWAIT}"!=""]?qwait,1())
exten => s,n,Set(__CWIGNORE=${CWIGNORE})
exten => s,n,Set(__KEEPCID=TRUE)
exten => s,n,GotoIf($["${USEGOTO}"="1"]?usegoto,1)
exten => s,n,Set(CALLEE_USERAGENT=${SIPPEER(${DEXTEN},useragent)})
exten => s,n,ExecIf(${REGEX("^Fanvil" ${CALLEE_USERAGENT})}?AGI(cut_callerid.pl,CALLERID(name),29,bytes))
exten => s,n,GotoIf($["${DB(AMPUSER/${EXTTOCALL}/cidname)}" = "" || "${DB(AMPUSER/${AMPUSER}/cidname)}" = ""]?godial)
exten => s,n,Set(CONNECTEDLINE(name,i)=${DB(AMPUSER/${EXTTOCALL}/cidname)})
exten => s,n,Set(CALLER_USERAGENT=${SIPPEER(${AMPUSER},useragent)})
exten => s,n,GotoIf($[${REGEX("^Fanvil" ${CALLER_USERAGENT})} = 0]?skip_cut_clname)
exten => s,n,Set(CUR_CONNECTEDLINE_NAME=${CONNECTEDLINE(name)})
exten => s,n,AGI(cut_callerid.pl,CUR_CONNECTEDLINE_NAME,39,bytes)
exten => s,n,Set(CONNECTEDLINE(name,i)=${CUR_CONNECTEDLINE_NAME})
exten => s,n(skip_cut_clname),Set(CONNECTEDLINE(num)=${EXTTOCALL})
exten => s,n,Set(D_OPTIONS=${D_OPTIONS}I)
exten => s,n(godial),Macro(dialout-one-predial-hook,)
exten => s,n,ExecIf($["${DIRECTION}" = "INBOUND"]?Set(D_OPTIONS=${STRREPLACE(D_OPTIONS,T)}I))
exten => s,n(dialapp),Noop()
exten => s,n,Dial(${DSTRING},${ARG1},${D_OPTIONS}b(func-apply-sipheaders^s^1))
exten => s,n,ExecIf($["${DIALSTATUS}"="ANSWER" & "${CALLER_DEST}"!=""]?MacroExit())
exten => s,n,ExecIf($["${DIALSTATUS_CW}"!=""]?Set(DIALSTATUS=${DIALSTATUS_CW}))
exten => s,n,GosubIf($[("${SCREEN}"!=""&("${DIALSTATUS}"="TORTURE"|"${DIALSTATUS}"="DONTCALL"))|"${DIALSTATUS}"="ANSWER"]?s-${DIALSTATUS},1())
exten => s,n,MacroExit()
exten => s,n(nodial),Noop()
exten => s,n,ExecIf($["${DIALSTATUS}" = ""]?Set(DIALSTATUS=NOANSWER))
exten => s,n,Noop(Returned from dial-one with nothing to call and DIALSTATUS: ${DIALSTATUS})
exten => s,n,MacroExit()
;внесение изменений
;если лимит по вызовам превышен – переход в over, выводим сообщение и устанавливаем статус вызова CONGESTION – канал перегружен
exten => s,n(over),NoOp(--- каналы заняты, установленное ограничение превышено на  ${MATH(${GROUP_COUNT(callers)}-2)} вызова ---)
exten => s,n,Set(DIALSTATUS=CONGESTION)
;конец внесения изменений

После внесения изменений в маршрутизации, необходимо выполнить dialplan reload для их применения.

В Asterisk CLI dialplan reload

или

В консоли Linux asterisk –rx ‘dialplan reload’

В случае превышения установленного лимита на внешние вызовы вызов будет сброшен и в консоли появится сообщение.

Сообщение в консоли при превышении общего количества вызовов через транки
Сообщение в консоли при превышении общего количества вызовов через транки

При этом локальные вызовы между сотрудниками одного офиса не ограничиваются

Локальные вызовы остаются без ограничений
Локальные вызовы остаются без ограничений
Книга 101 функция Asterisk
Познакомьтесь с возможностями Asterisk. Найдите инструменты, которые помогут вашей компании развиваться.
Скачать книгу
Подписаться
Уведомить о
guest
0 комментариев
Межтекстовые Отзывы
Посмотреть все комментарии

Остались вопросы?

Я - Кондрашин Игорь, менеджер компании Voxlink. Хотите уточнить детали или готовы оставить заявку? Укажите номер телефона, я перезвоню в течение 3-х секунд.

VoIP оборудование


ближайшие курсы

10 доводов в пользу Asterisk

Распространяется бесплатно.

Asterisk – программное обеспечение с открытым исходным кодом, распространяется по лицензии GPL. Следовательно, установив один раз Asterisk вам не придется дополнительно платить за новых абонентов, подключение новых транков, расширение функционала и прочие лицензии. Это приближает стоимость владения станцией к нулю.

Безопасен в использовании.

Любое программное обеспечение может стать объектом интереса злоумышленников, в том числе телефонная станция. Однако, сам Asterisk, а также операционная система, на которой он работает, дают множество инструментов защиты от любых атак. При грамотной настройке безопасности у злоумышленников нет никаких шансов попасть на станцию.

Надежен в эксплуатации.

Время работы серверов некоторых наших клиентов исчисляется годами. Это значит, что Asterisk работает несколько лет, ему не требуются никакие перезагрузки или принудительные отключения. А еще это говорит о том, что в районе отличная ситуация с электроэнергией, но это уже не заслуга Asterisk.

Гибкий в настройке.

Зачастую возможности Asterisk ограничивает только фантазия пользователя. Ни один конструктор шаблонов не сравнится с Asterisk по гибкости настройки. Это позволяет решать с помощью Asterisk любые бизнес задачи, даже те, в которых выбор в его пользу не кажется изначально очевидным.

Имеет огромный функционал.

Во многом именно Asterisk показал какой должна быть современная телефонная станция. За многие годы развития функциональность Asterisk расширилась, а все основные возможности по-прежнему доступны бесплатно сразу после установки.

Интегрируется с любыми системами.

То, что Asterisk не умеет сам, он позволяет реализовать за счет интеграции. Это могут быть интеграции с коммерческими телефонными станциями, CRM, ERP системами, биллингом, сервисами колл-трекинга, колл-бэка и модулями статистики и аналитики.

Позволяет телефонизировать офис за считанные часы.

В нашей практике были проекты, реализованные за один рабочий день. Это значит, что утром к нам обращался клиент, а уже через несколько часов он пользовался новой IP-АТС. Безусловно, такая скорость редкость, ведь АТС – инструмент зарабатывания денег для многих компаний и спешка во внедрении не уместна. Но в случае острой необходимости Asterisk готов к быстрому старту.

Отличная масштабируемость.

Очень утомительно постоянно возвращаться к одному и тому же вопросу. Такое часто бывает в случае некачественного исполнения работ или выбора заведомо неподходящего бизнес-решения. С Asterisk точно не будет такой проблемы! Телефонная станция, построенная на Asterisk может быть масштабируема до немыслимых размеров. Главное – правильно подобрать оборудование.

Повышает управляемость бизнеса.

Asterisk дает не просто набор полезных функций, он повышает управляемость организации, качества и комфортности управления, а также увеличивает прозрачность бизнеса для руководства. Достичь этого можно, например, за счет автоматизации отчетов, подключения бота в Telegram, санкционированного доступа к станции из любой точки мира.

Снижает расходы на связь.

Связь между внутренними абонентами IP-АТС бесплатна всегда, независимо от их географического расположения. Также к Asterisk можно подключить любых операторов телефонии, в том числе GSM сим-карты и настроить маршрутизацию вызовов по наиболее выгодному тарифу. Всё это позволяет экономить с первых минут пользования станцией.