hangup

Повестка дня:
1. Hangup() Softhangup()
2. Hangup cause
3. h extension
4. Hangupcause в Asterisk
5. hangupsource

1. Приложение Hangup.
Hangup() Завершить канал связи и вернуть -1, Есть возможность задать код причины завершения вызова(в скобках).
Для SIP:
Возможно поставить различные ответы («404 Not found», «484 Address incomplete» итд) установив одно из значений описанных в RFC 3398 — стр 24.
это ISDN код причины и не код ответа SIP. Например, Чтобы завершить звонок с кодом SIP 503, В диалплане нужно выполнить hangup(42).
Когда звонок завершается, Asterisk отправляет дополнительный SIP заголовок «X-Asterisk-HangupCauseCode» в BYE сообщении.
Механизм HangupCauseCode заменяет подобные методы специфичные для каждого типа каналов(SIP, PRI, IAX2 etc)

1 = Unallocated number
16 = Normal call clearing
17 = User busy
18 = No user responding (telephone device not connected)
21 = Call rejected
22 = Number changed
27 = Destignation out of order
38 = Network out of order
41 = Temporary failure

SoftHangup — так же пытается завершить канал, Если канала не существует то приложение сообщит об этом.
SoftHangup(Technology/Resource[,options])
options
a: Разорвать все каналы на устройстве, вместо отключения определенного канала(ресурса).

2.
ISUP cause code

Список кодов в Asterisk

Преобразование ISUP в SIP
1 unallocated number 404
2 no route to network 404
3 no route to destination 420
16 normal call clearing — (*)
17 user busy 486 Busy here
18 no user responding 408 Request Timeout
19 no answer from the user 480 Temporarily unavailable
20 subscriber absent 480 Temporarily unavailable
21 call rejected 403 Forbidden (+)
22 number changed (w/o diagnostic) 410 Gone
22 number changed (w/ diagnostic) 301 Moved Permanently
23 redirection to new destination 410 Gone
26 non-selected user clearing 404 Not Found (=)
27 destination out of order 502 Bad Gateway
28 address incomplete 484 Address incomplete
29 facility rejected 501 Not implemented
31 normal unspecified 480 Temporarily unavailable
(*) ISDN Cause 16 will usually result in a BYE or CANCEL
(+) Если назначение ‘user’ тогда 6xx код может быть выдан вместо 4xx (403 —> 603)
(=) ANSI procedure — в ANSI сетях, 26 переопределен для описания
‘misrouted ported number’. В противном случае cause 26 обычно не используется в ISUP процедурах.

REL с ISDN cause 22 (number changed) в поле диагностики может содержать информацию о новом номере, по которому может быть доступен абонент. Если MGC может обработать эту информацию она ДОЛЖНА быть добавлена в SIP ответ (301) в поле Contact заголовка.

Resource unavailable
Этот тип ошибок обозначает временный сбой. ‘Retry-After’ хидер МОЖЕТ быть добавлен к ответу если уместно.
34 no circuit available 503 Service unavailable
38 network out of order 503 Service unavailable
41 temporary failure 503 Service unavailable
42 switching equipment congestion 503 Service unavailable
47 resource unavailable 503 Service unavailable

Этот тип ошибок говорит о проблеме с запросом
55 incoming calls barred within CUG 403 Forbidden
57 bearer capability not authorized 403 Forbidden
58 bearer capability not presently 503 Service unavailable
available

Service or option not available
65 bearer capability not implemented 488 Not Acceptable Here
70 only restricted digital avail 488 Not Acceptable Here
79 service or option not implemented 501 Not implemented

Invalid message
87 user not member of CUG 403 Forbidden
88 incompatible destination 503 Service unavailable

Protocol error
102 recovery of timer expiry 504 Gateway timeout
111 protocol error 500 Server internal error

Interworking
127 interworking unspecified 500 Server internal error

В Asterisk начиная с 1.8 так же поддерживает ‘use_q850_reason’ опцию для генерации и чтения, если доступно(чтобы бородатые дядьки телефонисты и другие адепты ОКС7 понимали о чем это мы):

Reason: Q.850;cause=<cause code>

Почему ввели: Несколько SIP status codes преобразовывались в единственный ISUP cause code, если использовать use_q850_reason, тогда код посылается в ядро и должен появиться в новом SIP header («Reason: …»), который вы сможете прочесть в ${HANGUPCAUSE}.»
Канал назначения умирает сразу после Dial, но вы можете получить инфу пока еще этот канал жив

400 Bad Request 41 Temporary Failure
401 Unauthorized 21 Call rejected (*)
402 Payment required 21 Call rejected
403 Forbidden 21 Call rejected
404 Not found 1 Unallocated number
405 Method not allowed 63 Service or option unavailable
406 Not acceptable 79 Service/option not implemented (+)
407 Proxy authentication required 21 Call rejected (*)
408 Request timeout 102 Recovery on timer expiry
410 Gone 22 Number changed (w/o diagnostic)
413 Request Entity too long 127 Interworking (+)
414 Request-URI too long 127 Interworking (+)
415 Unsupported media type 79 Service/option not implemented (+)
416 Unsupported URI Scheme 127 Interworking (+)
420 Bad extension 127 Interworking (+)
421 Extension Required 127 Interworking (+)
423 Interval Too Brief 127 Interworking (+)
480 Temporarily unavailable 18 No user responding
481 Call/Transaction Does not Exist 41 Temporary Failure
482 Loop Detected 25 Exchange — routing error
483 Too many hops 25 Exchange — routing error
484 Address incomplete 28 Invalid Number Format (+)
485 Ambiguous 1 Unallocated number
486 Busy here 17 User busy
487 Request Terminated — (no mapping)
488 Not Acceptable here — by Warning header
500 Server internal error 41 Temporary failure
501 Not implemented 79 Not implemented, unspecified
502 Bad gateway 38 Network out of order
503 Service unavailable 41 Temporary failure
504 Server time-out 102 Recovery on timer expiry
504 Version Not Supported 127 Interworking (+)
513 Message Too Large 127 Interworking (+)
600 Busy everywhere 17 User busy
603 Decline 21 Call rejected
604 Does not exist anywhere 1 Unallocated number
606 Not acceptable — by Warning header

(*) В некоторых случаях,SIP шлюзы могут предоставлять полномочия SIP UAS отвергать INVITE когда авторизация неуспешна. Только если шлюз не может аутентифицировать себя он отправляет код 21.
(+) Если возможно SIP шлюз должен отвечать на ошибки и постараться их исправить, попытавшись переустановить сессию.

Когда появляется Warning header в SIP 606 или 488 сообщении, может быть особое представление ISDN cause code в Warning code. ’31 Normal, unspecified’ ДОЛЖЕН по умолчанию использоваться для наиболее распространенных Warning codes. Если Warning code говорит о недоступности канала передачи данных, то рекомендуется использовать cause code ’65 Bearer Capability Not Implemented’.

3. h extension
Позволяет выполнить часть диалплана после того как повесили трубку.
Используется DeadAGI вместо AGI.
Не вызывайте Hangup из h extension.

Когда запускается ‘h’ extension, стороны разговора уже отключены. Нет способа задержать это, и вы не можете читать аудио из канала. Playback() или Background(), не работают!. По существу, единственная вещь которую можно делать в ‘h’ extension это не делать ничего с каналами которые участвовали в разговоре. Нет аудио, нет DTMF итд..

При использовании макросов или Subов нужно создавать свой собственный h extension
Удобно использовать для создания колбека, биллинга, выполнения всяких полезностей(AGI скрипты) итд

4. Hangupcause в Asterisk
После завершения звонка вы можете посмотреть причину завершения звонка несколькими способами, самый простой это с помощью переменной
${HANGUPCAUSE} хранит причину завершения вызова(см п.2) Удобно записывать ее значение в CDR, чтобы было проще делать выборку о неуспешных вызовов.
same => n,Set(CDR(userfield)=HC:${HANGUPCAUSE})

Но это лишь верхушка айсберга. В 11й версии были добавлены обработчики(Hangup Handlers)
Для понимания как они работают разберем пару функций:
HANGUPCAUSE(channel,type) функция выдает код завершения или с точки зрения технологии или причину которую уже интерпретировал атсериск.
аргументы:
channel — Имя канала по которому мы хотим код завершения вызова.
type — какой тип информации хотим получить:
tech — с точки зрения технологии канала.
ast — код интерпретированный астериском.

Пример:
same => n,Noop(${HANGUPCAUSE(ИМЯ_КАНАЛА,tech)})
same => n,Noop(${HANGUPCAUSE(ИМЯ_КАНАЛА,ast)})
Technology-specific cause information: IAX2 HANGUP (16),
Asterisk Cause Code: Normal Clearing

HANGUPCAUSE_KEYS() Функция выдает список(разделенных запятыми) каналов для которых доступны код завершения вызова(если инициировано несколько одновременных вызовов) и которые потом можно использовать в функции HANGUPCAUSE.

HangupCauseClear() Приложение очищает все коды завершения уникальные только для этого типа канала. Это никогда не делается автоматически(например для новых Dial()ов).
exten => s,1,NoOp()
same => n,Set(CHANNEL(hangup_handler_push)=handler,s,1)
same => n,Dial(SIP/foo,10)
same => n,HangupCauseClear()
same => n,Dial(SIP/bar,10)
same => n,Hangup()

[handler]
exten => s,1,NoOp()
same => n,Set(HANGUPCAUSE_STRING=${HANGUPCAUSE_KEYS()})
same => n,Verbose(0, Channels with hangup cause information: ${HANGUPCAUSE_STRING})
same => n,Return()

В результате этого мы ничего не узнаем о результате звонка SIP/foo!

И так, что же такое Hangup Handlers — это специальные саброуты(subroutines), вспоминаем Gosub. Они прикрепляются к каналу и следуют за ним по всему диалплану, в отличии от подлого h, который нужно создавать в каждом контексте((( К каналу можно подцепить несколько обработчиков, и они будут выполняться в порядке их объявления.
!Обработчики выполняются вне зависимости от h. Они могут выполняться до и после h.
!Трансфер, перехват, парковка могут приводить к обработчикам, вспоминаем старую архитектуру ядра астериска, а именно бридж, до 13!
!Hangup handlers могут быть подключены к любому плечу вызова используя pre-dial handlers.
!!Поскольку hangup handlers это саброуты, то они должны использовать Return для возврата.
!!Добавив hangup handler в h или в другой hangup handler мы получим…(расскажите что).
!!hangup handlers, как и h, должны быстро обрабатываться(последовательность отброса плеча вызова). Некоторые протоколы как ISDN и SIP могут не поддерживать чрезмерную задержку при завершении вызова.
Все манипуляции проводим с помощью функции CHANNEL
;навешиваем обработчик
same => n,Set(CHANNEL(hangup_handler_push)=[[context,]exten,]priority[(arg1[,...][,argN])])
;Отключаем обработчик или его заменяем
same => n,Set(CHANNEL(hangup_handler_pop)=[[[context,]exten,]priority[(arg1[,...][,argN])]])
;удаляем все обработчики
same => n,Set(CHANNEL(hangup_handler_wipe)=[[[context,]exten,]priority[(arg1[,...][,argN])]])

команда CLI которая покажет обработчики для всех каналов
core show hanguphandlers all

Некоторые SIP UA могут не отправлять код завершения, в результате будет отображено последнее состояние о котором сообщил UA. Например: SIP 180 Ringing.

Живой пример: звонок на 2 телефона одновременно к SIP/foo и SIP/bar. hangup handler подключен к вызывающему каналу. Парсятся ответы от каналов и вводится 2 вида кода завершения(уникальный для данного типа канала и интерпретированный астериском).
[default]
exten => s,1,NoOp()
same => n,Set(CHANNEL(hangup_handler_push)=handler,s,1)
same => n,Dial(SIP/foo&SIP/bar,10)
same => n,Hangup()

[handler]
exten => s,1,NoOp()
same => n,Set(HANGUPCAUSE_STRING=${HANGUPCAUSE_KEYS()})
; Начало обработчика
same => n(hu_begin),NoOp()
; проверяем условие выхода из цикла (нет больше каналов для проверки)
same => n,GotoIf($[${LEN(${HANGUPCAUSE_STRING})}=0]?hu_exit)
; выбираем следующий канал
same => n,Set(ARRAY(item)=${HANGUPCAUSE_STRING})
same => n,Set(HANGUPCAUSE_STRING=${HANGUPCAUSE_STRING:${LEN(${item})}})
; отображаем имя канала и код завершения
same => n,Verbose(0, Got Channel ID ${item} with Technology Cause Code ${HANGUPCAUSE(${item},tech)}, Asterisk Cause Code ${HANGUPCAUSE(${item},ast)})
; проверяем условие выхода из цикла (нет больше каналов для проверки)
same => n,GotoIf($[${LEN(${HANGUPCAUSE_STRING})}=0]?hu_exit)
; убираем лидирующую запятую
same => n,Set(HANGUPCAUSE_STRING=${HANGUPCAUSE_STRING:1})
; идем в начало цикла
same => n,Goto(hu_begin)
same => n(hu_exit),NoOp()
same => n,Return()

5.hangupsource
Данная переменная канала(CHANNEL(hangupsource)) позволяет отслеживать кто бросил трубку.
Можно выводить ее значение, или сразу писать в CDR.

exten => h,1,Set(CDR(userfield)=${CHANNEL(hangupsource)})

P.S. Если вы хотите дропнуть канал, то в CLI следует выполнить:
channel request hangup ИМЯ_КАНАЛА


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