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

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

В статье рассматривается установка ограничений по максимальному количеству занятых каналов в общем (для всех имеющихся транков) и в частности (для каждого отдельного транка). Установка ограничений по количеству звонков для отдельного транка. В веб-админке 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’

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

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

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

Локальные вызовы остаются без ограничений
Локальные вызовы остаются без ограничений
Подписаться
Уведомить о
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 сим-карты и настроить маршрутизацию вызовов по наиболее выгодному тарифу. Всё это позволяет экономить с первых минут пользования станцией.