work:telephony:cisco-ivr-tcl

Различия

Показаны различия между двумя версиями страницы.

Ссылка на это сравнение

Следующая версия
Предыдущая версия
work:telephony:cisco-ivr-tcl [2024/07/10 11:04] – создано rollandwork:telephony:cisco-ivr-tcl [2025/05/22 16:52] (текущий) rolland
Строка 1: Строка 1:
 ====== Пишем голосовое IVR меню на языке TCL, с использованием Cisco IVR API ====== ====== Пишем голосовое IVR меню на языке TCL, с использованием Cisco IVR API ======
  
-Сегодня речь о голосовом меню (**IVR**) для маршрутизаторов Cisco, которое мы будем писать на языке TCL, и подключать на Cisco 3845.\\+Сегодня речь о голосовом меню (**IVR**) для маршрутизаторов Cisco, которое мы будем писать на языке TCL, и подключать на Cisco 3845.
 ====== Итак, для начала давайте разберемся в азах ====== ====== Итак, для начала давайте разберемся в азах ======
  
Строка 7: Строка 7:
 Cisco начиная с версии **IOS** 12 поддерживает как **VXML** так и **TCL** скрипты для работы с голосовым меню. Однако, в отличии от **VXML**, скрипты на **TCL** имеют гораздо больше возможностей взаимодействия с **Cisco IVR API**. Так же существует возможность подключать гибридные **IVR** скрипты, со встроенными кусками **VXML** кода внутри **TCL** скрипта.\\ Cisco начиная с версии **IOS** 12 поддерживает как **VXML** так и **TCL** скрипты для работы с голосовым меню. Однако, в отличии от **VXML**, скрипты на **TCL** имеют гораздо больше возможностей взаимодействия с **Cisco IVR API**. Так же существует возможность подключать гибридные **IVR** скрипты, со встроенными кусками **VXML** кода внутри **TCL** скрипта.\\
 \\ \\
-Все документы, связанные с IVR от Cisco, которые мне довелось получить можно скачать [[https://cloud.mail.ru/public/FFBy/ZEujVorKp|здесь]].\\+Все документы, связанные с IVR от Cisco, которые мне довелось получить можно скачать [[https://cloud.mail.ru/public/FFBy/ZEujVorKp|здесь]]. 
 ==== FSM ==== ==== FSM ====
  
Строка 15: Строка 16:
 Выглядит это примерно так: Выглядит это примерно так:
 <code> <code>
-''set ivr_fsm(CALLCOMES,ev_setup_indication) "act_Setup same_state" + 
-''+set ivr_fsm(CALLCOMES,ev_setup_indication) "act_Setup same_state"
  
 </code> </code>
Строка 25: Строка 26:
 Общий синтаксис этой команды таков: Общий синтаксис этой команды таков:
  
-<code>''set array(CURRSTATE, curr_event) “act_proc NEXTSTATE” +<code> 
-''+set array(CURRSTATE, curr_event) “act_proc NEXTSTATE”
  
 </code> </code>
  
-где:\ +где:\ **array** – это имя //**FSM**//  массива.\\ 
-**array** – это имя //**FSM**//  массива.\\ +**CURRSTATE** – имя текущего состояния, при котором получено событие //**curr_event**//  .\\ 
-**CURRSTATE** – имя текущего состояния, при котором получено событие //**curr_event**// .\\ +**act_proc** – имя функции, которую необходимо выполнить при поступлении события //**curr_event**//  .\\ 
-**act_proc** – имя функции, которую необходимо выполнить при поступлении события //**curr_event**// .\\ +**NEXTSTATE** – имя состояния, которое установится после выполнения //**act_proc**//  .\\
-**NEXTSTATE** – имя состояния, которое установится после выполнения //**act_proc**// .\\+
 \\ \\
 Другими словами, **FSM** это маркер, по которому Cisco сравнивает полученное от API событие с **curr_event** и текущий статус с **CURRSTATE**, если в каком либо FSM переходе они описаны, вызывается процедура **act_proc** и состояние изменяется на **NEXTSTATE**.\\ Другими словами, **FSM** это маркер, по которому Cisco сравнивает полученное от API событие с **curr_event** и текущий статус с **CURRSTATE**, если в каком либо FSM переходе они описаны, вызывается процедура **act_proc** и состояние изменяется на **NEXTSTATE**.\\
 \\ \\
-Самое главное в этом — это то, что текущее событие и состояние сравниваются со всеми описанными **FSM** переходами **одновременно**. Т.е. для Cisco не имеет значения порядок, в котором расположены FSM переходы, все они обрабатываются асинхронно.\\+Самое главное в этом — это то, что текущее событие и состояние сравниваются со всеми описанными **FSM** переходами **одновременно**. Т.е. для Cisco не имеет значения порядок, в котором расположены FSM переходы, все они обрабатываются асинхронно.
 ==== Функции ==== ==== Функции ====
  
Строка 44: Строка 44:
 Второй момент, это сами функции, которые должны быть описаны до инициализации скрипта.\\ Второй момент, это сами функции, которые должны быть описаны до инициализации скрипта.\\
 \\ \\
-Назначение всех команд и состояний подробно описано в файле tcl_ivr_2.0_programming_guide, который вы можете скачать [[https://cloud.mail.ru/public/FFBy/ZEujVorKp|здесь]], я поподробнее остановлюсь только на тех, которые буду использовать непосредственно в скрипте\\+Назначение всех команд и состояний подробно описано в файле tcl_ivr_2.0_programming_guide, который вы можете скачать [[https://cloud.mail.ru/public/FFBy/ZEujVorKp|здесь]], я поподробнее остановлюсь только на тех, которые буду использовать непосредственно в скрипте 
 ===== 1) Инициализация скрипта ===== ===== 1) Инициализация скрипта =====
  
 \\ \\
-Начало любого TCL IVR скрипта содержит процедуру //**init**// , в моем примере эта функция выглядит так:+Начало любого TCL IVR скрипта содержит процедуру //**init**//  , в моем примере эта функция выглядит так:
 <code> <code>
-''proc init { } {+ 
 +proc init { } {
     puts "\n proc Init start"     puts "\n proc Init start"
     global param     global param
 } }
-'' 
  
 </code> </code>
  
-Здесь по сути выполняется вывод на экран командой //**puts "..."**//  и определение глобальной переменной //**param** // \\+Здесь по сути выполняется вывод на экран командой //**puts ""**//  и определение глобальной переменной //**param** // \\
 \\ \\
-Инициализация скрипта происходит после описания всех функций, и начинается с запуска функции //**init**// . На этом простые вещи закончились, дальше все гораздо интереснее.\\+Инициализация скрипта происходит после описания всех функций, и начинается с запуска функции //**init**//  . На этом простые вещи закончились, дальше все гораздо интереснее.\\
 \\ \\
 Последней исполняемой строкой скрипта должна быть строка определения стартового **FSM** перехода и стартового состояния. В нашем случае это: Последней исполняемой строкой скрипта должна быть строка определения стартового **FSM** перехода и стартового состояния. В нашем случае это:
-<code>''fsm define ivr_fsm CALLCOMES +<code> 
-''+fsm define ivr_fsm CALLCOMES
  
 </code> </code>
  
-Это значит, что имя **FSM** массива задано как **ivr_fsm**, и стартовое состояние **CALLCOMES**. С инициализацией закончим, дальше будет понятнее, что происходит (я надеюсь).\\+Это значит, что имя **FSM** массива задано как **ivr_fsm**, и стартовое состояние **CALLCOMES**. С инициализацией закончим, дальше будет понятнее, что происходит (я надеюсь).
 ===== 2) Приветствие ===== ===== 2) Приветствие =====
  
-<code>''proc Play_Welcome { } {+<code> 
 +proc Play_Welcome { } {
     puts "\n\n IVR - proc Play_Welcome start \n\n"     puts "\n\n IVR - proc Play_Welcome start \n\n"
     global playng_files     global playng_files
Строка 113: Строка 115:
     timer start named_timer $numbers(waiting_time) t1     timer start named_timer $numbers(waiting_time) t1
 } }
-'' 
  
-</file>+</code>
  
-Здесь довольно подробно все описано.\+Здесь довольно подробно все описано.
  
-Результатом выполнения данной процедуры будет подключение входящей линии к Cisco за счет команд //**leg setupack, leg proceeding, leg connect,**//  и проигрыш музыкальных файлов по очереди во входящую линию за счет команды //**media play leg_incoming**// .\\ +Результатом выполнения данной процедуры будет подключение входящей линии к Cisco за счет команд //**leg setupack, leg proceeding, leg connect,**//  и проигрыш музыкальных файлов по очереди во входящую линию за счет команды //**media play leg_incoming**//  . Тут же запускается процесс сбора нажатых клавиш **//leg collectdigit//**  и таймер командой //**timer start**//  
-Тут же запускается процесс сбора нажатых клавиш **//leg collectdigit//**  и таймер командой //**timer start**// .\\ + 
-\\ +И проверяется рабочее сейчас время или нет, вызывая функцию //**GetDate**//  
-И проверяется рабочее сейчас время или нет, вызывая функцию //**GetDate**//+<code> 
-<code>''proc GetDate { } {+proc GetDate { } {
     global workingtime     global workingtime
  
Строка 131: Строка 132:
     set dayis [clock format [clock seconds] -format %A]     set dayis [clock format [clock seconds] -format %A]
     #Проверяем рабочее время     #Проверяем рабочее время
-    if {$houris > 17 || $houris < 8 || $dayis=="Sunday" || $dayis=="Saturday"} {+    if {$houris> 17 || $houris <8 || $dayis=="Sunday" || $dayis=="Saturday"} {
     set workingtime 0     set workingtime 0
     } else {     } else {
Строка 137: Строка 138:
     }     }
 } }
-'' 
  
 </code> </code>
  
-В зависимости от того рабочее время или нет мы меняем музыкальный файл, который будет проигран звонящему абоненту.\+В зависимости от того рабочее время или нет мы меняем музыкальный файл, который будет проигран звонящему абоненту.
  
-Так как стартовое состояние задано в нашем случае как //**fsm define ivr_fsm CALLCOMES**// , в него попадают сразу 3 **FSM**:+Так как стартовое состояние задано в нашем случае как //**fsm define ivr_fsm CALLCOMES**//  , в него попадают сразу 3 **FSM**:
 <code> <code>
-''set ivr_fsm(CALLCOMES,ev_setup_indication)    "Play_Welcome,   same_state"+ 
 +set ivr_fsm(CALLCOMES,ev_setup_indication)    "Play_Welcome,   same_state"
 set ivr_fsm(CALLCOMES,ev_collectdigits_done)  "CheckDestanation,  same_state" set ivr_fsm(CALLCOMES,ev_collectdigits_done)  "CheckDestanation,  same_state"
 set ivr_fsm(CALLCOMES,ev_named_timer)          "GoToReception,      same_state" set ivr_fsm(CALLCOMES,ev_named_timer)          "GoToReception,      same_state"
-'' 
  
 </code> </code>
  
-Событие //**ev_setup_indication**//  произойдет при поступлении звонка, и будет запущена процедура //**Play_Welcome**// , в которой описан старт процесса сбора нажатых цифр и старт таймера.\\+Событие //**ev_setup_indication**//  произойдет при поступлении звонка, и будет запущена процедура //**Play_Welcome**//  , в которой описан старт процесса сбора нажатых цифр и старт таймера.\\
 \\ \\
-После окончания проигрывания музыки абоненту, начнется обратный отчет таймера, который задается параметром //**param(initialDigitTimeout)**//  (который можно было задать чуть выше строкой //**set param(initialDigitTimeout) 15**//  и установить значение 15 секунд), т.к. он у нас не указан, его стандартное значение 10 секунд, после чего скрипт получит событие **//ev_collectdigits_done//** , при наступлении которого, как мы описали в **FSM** переходе, будет выполнена функция **//CheckDestanation//** .\\+После окончания проигрывания музыки абоненту, начнется обратный отчет таймера, который задается параметром //**param(initialDigitTimeout)**//  (который можно было задать чуть выше строкой //**set param(initialDigitTimeout) 15**//  и установить значение 15 секунд), т.к. он у нас не указан, его стандартное значение 10 секунд, после чего скрипт получит событие **//ev_collectdigits_done//**  , при наступлении которого, как мы описали в **FSM** переходе, будет выполнена функция **//CheckDestanation//**  .\\
 \\ \\
 Таймер, запущенный в **//Play_Welcome//**  командой: Таймер, запущенный в **//Play_Welcome//**  командой:
-<code>''#Тип таймера named_timer, длительность, взята из переменной numbers(waiting_time), имя таймера t1+<code> 
 +#Тип таймера named_timer, длительность, взята из переменной numbers(waiting_time), имя таймера t1
 timer start named_timer $numbers(waiting_time) t1 timer start named_timer $numbers(waiting_time) t1
-'' 
  
 </code> </code>
  
-После своего окончания сгенерирует событие //**ev_named_timer**// , которое будет обработано следующим **FSM** переходом: +После своего окончания сгенерирует событие //**ev_named_timer**//  , которое будет обработано следующим **FSM** переходом: 
-<code>''set ivr_fsm(CALLCOMES,ev_named_timer)   "GoToReception,      same_state" +<code> 
-''+set ivr_fsm(CALLCOMES,ev_named_timer)   "GoToReception,      same_state"
  
 </code> </code>
  
-и вызовется процедура **//GoToReception//** .\\+и вызовется процедура **//GoToReception//**  .
 ===== 3) Проверка введенного номера ===== ===== 3) Проверка введенного номера =====
  
-<code>''proc CheckDestanation { } {+<code> 
 +proc CheckDestanation { } {
     puts "\n\n IVR - proc CheckDestanation start \n\n"     puts "\n\n IVR - proc CheckDestanation start \n\n"
     global playng_files     global playng_files
Строка 243: Строка 244:
     }     }
 } }
-'' 
  
-</file>+</code>
  
-В процедуре CheckDestanation, которая будет вызвана после набора номера звонящим абонентом, мы сравниваем полученные при наборе цифры с настройками и переводим скрипт в соответствующее состояние командой //**fsm setstate**// .\\ +В процедуре CheckDestanation, которая будет вызвана после набора номера звонящим абонентом, мы сравниваем полученные при наборе цифры с настройками и переводим скрипт в соответствующее состояние командой //**fsm setstate**//  .\ \ Все состояния, попавшие в функцию, попадают под следующие FSM переходы: 
-\\ +<code> 
-Все состояния, попавшие в функцию, попадают под следующие FSM переходы: +set ivr_fsm(CALLCONNECTED,ev_setup_done)   "CallIsConnect,  same_state"
- +
-<code>''set ivr_fsm(CALLCONNECTED,ev_setup_done)   "CallIsConnect,  same_state"+
 set ivr_fsm(TORECEPTION,ev_media_done)   "GoToReception,  same_state" set ivr_fsm(TORECEPTION,ev_media_done)   "GoToReception,  same_state"
 set ivr_fsm(TRYAGAIN,ev_media_done)           "Play_TakeNumber,  TRYING" set ivr_fsm(TRYAGAIN,ev_media_done)           "Play_TakeNumber,  TRYING"
 set ivr_fsm(TRYING,ev_collectdigits_done)  "CheckDestanation,  same_state" set ivr_fsm(TRYING,ev_collectdigits_done)  "CheckDestanation,  same_state"
 set ivr_fsm(TRYING,ev_named_timer)   "GoToReception,  same_state" set ivr_fsm(TRYING,ev_named_timer)   "GoToReception,  same_state"
-'' 
  
 </code> </code>
Строка 267: Строка 264:
 3) Затем сравниваем полученные результаты с заданными переменными и изменяем состояние скрипта при совпадении: 3) Затем сравниваем полученные результаты с заданными переменными и изменяем состояние скрипта при совпадении:
  
-<code>''if {$digit == $numbers(fast_reception)} {+<code> 
 +if {$digit == $numbers(fast_reception)} {
  puts "\n\n IVR - proc CheckDestanation digit = $digit\nGoing to next reception \n\n"  puts "\n\n IVR - proc CheckDestanation digit = $digit\nGoing to next reception \n\n"
  fsm setstate CALLCONNECTED  fsm setstate CALLCONNECTED
  leg setup $numbers(reception) callinfo leg_incoming  leg setup $numbers(reception) callinfo leg_incoming
 } }
-'' 
  
 </code> </code>
Строка 278: Строка 275:
 ===== 4) Проверка номера звонящего абонента ===== ===== 4) Проверка номера звонящего абонента =====
  
-<code>''proc CheckCallersAndConnect {digit} {+<code> 
 +proc CheckCallersAndConnect {digit} {
  puts "\n\n IVR - proc CheckCallersAndConnect start \n\n"  puts "\n\n IVR - proc CheckCallersAndConnect start \n\n"
  
Строка 292: Строка 290:
  leg setup $digit callInfo leg_incoming  leg setup $digit callInfo leg_incoming
 } }
-'' 
  
 </code> </code>
  
-Данная функция позволяет изменить поле, отвечающее за написание имени звонящего. Просто ради эстетики, будет приятнее, когда на телефоне будет написан не только номер но и ID абонента. После изменения ID абонента происходит подключение линии к требуемому номеру.\\+Данная функция позволяет изменить поле, отвечающее за написание имени звонящего. Просто ради эстетики, будет приятнее, когда на телефоне будет написан не только номер но и ID абонента. После изменения ID абонента происходит подключение линии к требуемому номеру. 
 ===== 5) Подключение номера ===== ===== 5) Подключение номера =====
  
-<code>''proc CallIsConnect { } {+<code> 
 +proc CallIsConnect { } {
     puts "\n\n IVR - proc CallIsConnect start \n\n"     puts "\n\n IVR - proc CallIsConnect start \n\n"
     global playng_files     global playng_files
Строка 324: Строка 323:
     }     }
 } }
-'' 
  
-</file>+</code>
  
 Данная функция вызывается следующим FSM переходом: Данная функция вызывается следующим FSM переходом:
 <code> <code>
-''set ivr_fsm(CALLCONNECTED,ev_setup_done)   "CallIsConnect,  same_state" + 
-''+set ivr_fsm(CALLCONNECTED,ev_setup_done)   "CallIsConnect,  same_state"
  
 </code> </code>
  
-Событие //**ev_setup_done**//  наступает после подключения звонящего к требуемой линии.\\+Событие //**ev_setup_done**//  наступает после подключения звонящего к требуемой линии.
 ===== 6) Повторный запрос номера ===== ===== 6) Повторный запрос номера =====
  
-<code>''proc Play_TakeNumber { } {+<code> 
 +proc Play_TakeNumber { } {
     puts "\n\n IVR - proc Play_TakeNumber start \n\n"     puts "\n\n IVR - proc Play_TakeNumber start \n\n"
     global playng_files     global playng_files
Строка 365: Строка 364:
     }     }
 } }
-'' 
  
-</file>+</code>
  
-Данная функция проверяет какой раз ошибается звонящий, и если значение меньше чем **//$numbers(max_try)//**  просит ввести номер еще раз.\\ +Данная функция проверяет какой раз ошибается звонящий, и если значение меньше чем **//$numbers(max_try)//**  просит ввести номер еще раз.\ \ Данная функция вызывается следующими **FSM**: 
-\\ +<code> 
-Данная функция вызывается следующими **FSM**: +set ivr_fsm(TRYAGAIN,ev_media_done)   "Play_TakeNumber,     TRYING"
-<code>''set ivr_fsm(TRYAGAIN,ev_media_done)   "Play_TakeNumber,     TRYING"+
 set ivr_fsm(TRYING,ev_collectdigits_done)            "CheckDestanation,     same_state" set ivr_fsm(TRYING,ev_collectdigits_done)            "CheckDestanation,     same_state"
 set ivr_fsm(TRYING,ev_named_timer)                "GoToReception,             same_state" set ivr_fsm(TRYING,ev_named_timer)                "GoToReception,             same_state"
-'' 
  
 </code> </code>
Строка 381: Строка 377:
 ===== 7) Разрыв соединения ===== ===== 7) Разрыв соединения =====
  
-<code>''proc AbortCall { } {+<code> 
 +proc AbortCall { } {
  puts "\n\n IVR - proc AbortCall start \n\n"  puts "\n\n IVR - proc AbortCall start \n\n"
  call close  call close
 } }
-'' 
  
 </code> </code>
  
 Вызывается следующими **FSM**: Вызывается следующими **FSM**:
-<code>''set ivr_fsm(any_state,ev_disconnected)   "AbortCall,   same_state"+<code> 
 +set ivr_fsm(any_state,ev_disconnected)   "AbortCall,   same_state"
 set ivr_fsm(CALLACTIVE,ev_disconnected)     "AbortCall,  CALLDISCONNECTED" set ivr_fsm(CALLACTIVE,ev_disconnected)     "AbortCall,  CALLDISCONNECTED"
 set ivr_fsm(CALLDISCONNECTED,ev_disconnected)   "AbortCall,  same_state" set ivr_fsm(CALLDISCONNECTED,ev_disconnected)   "AbortCall,  same_state"
 set ivr_fsm(CALLDISCONNECTED,ev_media_done)    "AbortCall,  same_state" set ivr_fsm(CALLDISCONNECTED,ev_media_done)    "AbortCall,  same_state"
 set ivr_fsm(CALLDISCONNECTED,ev_disconnect_done)  "AbortCall,  same_state" set ivr_fsm(CALLDISCONNECTED,ev_disconnect_done)  "AbortCall,  same_state"
-'' 
  
 </code> </code>
Строка 404: Строка 400:
 Подключение на маршрутизаторе Cisco проходит в 2 этапа.\\ Подключение на маршрутизаторе Cisco проходит в 2 этапа.\\
 **Первое**, что нужно сделать это определить **application**: **Первое**, что нужно сделать это определить **application**:
-<code>''application+<code> 
 +application
  service voicemunu flash:voicemenu.tcl  service voicemunu flash:voicemenu.tcl
   param allowed_pattern 5[5-7]..   param allowed_pattern 5[5-7]..
Строка 423: Строка 420:
   param file_noworking flash:en_takenumber2.au   param file_noworking flash:en_takenumber2.au
   param file_takenumber flash:en_takenumber2.au   param file_takenumber flash:en_takenumber2.au
-'' 
  
-</file>+</code>
  
-**Второе**, подключить //**service**//  к **//dial-peer//** : +**Второе**, подключить //**service**//  к **//dial-peer//**  
-<code>''dial-peer voice 200 pots+<code> 
 +dial-peer voice 200 pots
  description -= ISP Beeline - INcoming call to number 3300100 =-  description -= ISP Beeline - INcoming call to number 3300100 =-
  service voicemunu  service voicemunu
  incoming called-number 3300100  incoming called-number 3300100
-'' 
  
 </code> </code>
  
-Таким образом, при поступлении звонка на номер 3300100, произойдет вызов нашего голосового меню **//voicemunu//** .\\+Таким образом, при поступлении звонка на номер 3300100, произойдет вызов нашего голосового меню **//voicemunu//**  .
 ===== 9) Полная версия скрипта ===== ===== 9) Полная версия скрипта =====
  
Строка 442: Строка 438:
 Выше были рассмотрены только основные функции скрипта, далее полный текст, имейте в виду, это практически самый простой вариант: Выше были рассмотрены только основные функции скрипта, далее полный текст, имейте в виду, это практически самый простой вариант:
  
-<code>''#######################################################+<code> 
 +#######################################################
 # Cisco IVR TCL script by Konovalov D.A. v.2 # Cisco IVR TCL script by Konovalov D.A. v.2
 ####################################################### #######################################################
 # #
 #    Для дебага скрипта #    Для дебага скрипта
-  debug voip application script+       debug voip application script
 #    Более полный дебага (не рекомендуется, может привести к перегрузке) #    Более полный дебага (не рекомендуется, может привести к перегрузке)
 #         debug voip ivr #         debug voip ivr
Строка 650: Строка 647:
     set dayis [clock format [clock seconds] -format %A]     set dayis [clock format [clock seconds] -format %A]
     #Проверяем рабочее время     #Проверяем рабочее время
-    if {$houris > 17 || $houris < 8 || $dayis=="Sunday" || $dayis=="Saturday"} {+    if {$houris> 17 || $houris <8 || $dayis=="Sunday" || $dayis=="Saturday"} {
     set workingtime 0     set workingtime 0
     } else {     } else {
Строка 901: Строка 898:
  
 fsm define ivr_fsm CALLCOMES fsm define ivr_fsm CALLCOMES
-'' 
  
-</file>+</code> 
 Источник [[https://habr.com/ru/articles/265453/|https://habr.com/ru/articles/265453/]] Источник [[https://habr.com/ru/articles/265453/|https://habr.com/ru/articles/265453/]]
  
  
  • work/telephony/cisco-ivr-tcl.1720598685.txt.gz
  • Последнее изменение: 2024/07/10 11:04
  • rolland