A historia dun proxecto ou como pasei 7 anos creando un PBX baseado en Asterisk e Php

Seguro que moitos de vós, coma min, tiñades unha idea de facer algo único. Neste artigo describirei os problemas técnicos e as solucións que tiven que enfrontar ao desenvolver a central. Quizais isto axude a alguén a decidir pola súa propia idea, e a alguén a seguir o camiño ben trillado, porque tamén me beneficiei da experiencia dos pioneiros.

A historia dun proxecto ou como pasei 7 anos creando un PBX baseado en Asterisk e Php

Idea e requisitos clave

E todo comezou simplemente co amor Asterisco (marco de aplicacións de comunicación de edificios), automatización de telefonía e instalacións PBX gratuíto (interfaz web para Asterisco). Se as necesidades da empresa fosen sen especificacións e estivesen dentro das capacidades PBX gratuíto - todo é xenial. Toda a instalación levouse a cabo nun prazo de XNUMX horas, a empresa recibiu unha centralita PBX configurada, unha interface fácil de usar e unha curta formación máis soporte se o desexa.

Pero as tarefas máis interesantes non eran estándar e entón non era tan fabulosa. Asterisco pode facer moito, pero para manter a interface web en funcionamento, foi necesario gastar moitas veces máis tempo. Polo tanto, un pequeno detalle pode levar moito máis tempo que instalar o resto da central. E a cuestión non é que leve moito tempo escribir unha interface web, senón que o punto está nas características arquitectónicas. PBX gratuíto. Enfoques e métodos da arquitectura PBX gratuíto foi presentado no momento de php4, e nese momento xa había php5.6 no que todo se podía facer máis sinxelo e cómodo.

A última gota foron os planos de marcación gráficos en forma de diagrama. Cando intentei construír algo así para PBX gratuíto, deime conta de que tería que reescribilo significativamente e sería máis fácil construír algo novo.

Os requisitos fundamentais foron:

  • configuración sinxela, accesible de forma intuitiva incluso para un administrador novato. Así, as empresas non requiren mantemento de PBX da nosa parte,
  • modificación sinxela para que as tarefas se resolvan no tempo adecuado,
  • facilidade de integración con PBX. U PBX gratuíto non había API para cambiar a configuración, é dicir. Non pode, por exemplo, crear grupos ou menús de voz desde unha aplicación de terceiros, só a propia API Asterisco,
  • código aberto: para os programadores, isto é moi importante para as modificacións do cliente.

A idea dun desenvolvemento máis rápido era que toda a funcionalidade consistía en módulos en forma de obxectos. Todos os obxectos tiñan que ter unha clase pai común, o que significa que xa se coñecen os nomes de todas as funcións principais e, polo tanto, xa hai implementacións predeterminadas. Os obxectos permítenche reducir drasticamente o número de argumentos en forma de matrices asociativas con claves de cadea, que podes descubrir en PBX gratuíto Foi posible examinando a función completa e as funcións aniñadas. No caso dos obxectos, o autocompletado banal mostrará todas as propiedades e, en xeral, simplificará a vida moitas veces. Ademais, a herdanza e a redefinición xa resolve moitos problemas coas modificacións.

O seguinte que retardou o tempo de reelaboración e que pagaba a pena evitar foi a duplicación. Se hai un módulo responsable de marcar un empregado, todos os demais módulos que necesiten enviar unha chamada a un empregado deberían usalo e non crear as súas propias copias. Entón, se necesitas cambiar algo, terás que cambiar só nun lugar e a busca de "como funciona" debe realizarse nun só lugar e non buscar en todo o proxecto.

Primeira versión e primeiros erros

O primeiro prototipo estaba listo nun ano. Todo o PBX, como estaba previsto, era modular e os módulos non só podían engadir novas funcionalidades para procesar chamadas, senón tamén cambiar a propia interface web.

A historia dun proxecto ou como pasei 7 anos creando un PBX baseado en Asterisk e Php
Si, a idea de construír un plan de marcación en forma de tal esquema non é miña, pero é moi conveniente e fixen o mesmo para Asterisco.

A historia dun proxecto ou como pasei 7 anos creando un PBX baseado en Asterisk e Php

Ao escribir un módulo, os programadores xa poderían:

  • crea a túa propia funcionalidade para o procesamento de chamadas, que se pode colocar no diagrama, así como no menú de elementos da esquerda,
  • crea as túas propias páxinas para a interface web e engade os teus modelos ás páxinas existentes (se o desenvolvedor da páxina proporcionou isto),
  • engade a túa configuración á pestana de configuración principal ou crea a túa propia pestana de configuración,
  • o programador pode herdar dun módulo existente, cambiar parte da funcionalidade e rexistrala cun novo nome ou substituír o módulo orixinal.

Por exemplo, así é como podes crear o teu propio menú de voz:

......
class CPBX_MYIVR extends CPBX_IVR
{
 function __construct()
 {
 parent::__construct();
 $this->_module = "myivr";
 }
}
.....
$myIvrModule = new CPBX_MYIVR();
CPBXEngine::getInstance()->registerModule($myIvrModule,__DIR__); //Зарегистрировать новый модуль
CPBXEngine::getInstance()->registerModuleExtension($myIvrModule,'ivr',__DIR__); //Подменить существующий модуль

As primeiras implementacións complexas trouxeron o primeiro orgullo e as primeiras decepcións. Alegroume de que funcionase, de que xa puiden reproducir as principais características PBX gratuíto. Alegroume de que á xente lle gustase a idea do esquema. Aínda había moitas opcións para simplificar o desenvolvemento, pero aínda nese momento algunhas das tarefas xa se estaban facilitando.

A API para cambiar a configuración da PBX foi unha decepción: o resultado non foi en absoluto o que queriamos. Tomei o mesmo principio que en PBX gratuíto, ao facer clic no botón Aplicar, recréase toda a configuración e reinician os módulos.

Parece así:

A historia dun proxecto ou como pasei 7 anos creando un PBX baseado en Asterisk e Php
*Dialplan é unha regra (algoritmo) pola cal se procesa unha chamada.

Pero con esta opción, é imposible escribir unha API normal para cambiar a configuración da PBX. En primeiro lugar, a operación de aplicar cambios a Asterisco demasiado longo e con moitos recursos.
En segundo lugar, non pode chamar dúas funcións ao mesmo tempo, porque ambos crearán a configuración.
En terceiro lugar, aplica todas as opcións de configuración, incluídas as realizadas polo administrador.

Nesta versión, como en Askozia, foi posible xerar a configuración de só módulos modificados e reiniciar só os módulos necesarios, pero todos son medias. Era necesario cambiar o enfoque.

Segunda versión. Nariz sacou cola atascada

A idea para resolver o problema non era recrear a configuración e o plano de marcación para Asterisco, pero garda información na base de datos e lea directamente desde a base de datos mentres procesa a chamada. Asterisco Xa sabía como ler configuracións da base de datos, só cambia o valor na base de datos e a seguinte chamada procesarase tendo en conta os cambios, e a función era perfecta para ler os parámetros do plan de marcación. REALTIME_HASH.

Ao final, nin sequera houbo que reiniciar Asterisco ao cambiar a configuración e todas as opcións comezaron a aplicarse inmediatamente Asterisco.

A historia dun proxecto ou como pasei 7 anos creando un PBX baseado en Asterisk e Php

Os únicos cambios no plano de marcación son a adición de números de extensión e consellos. Pero estes foron pequenos cambios puntuais

exten=>101,1,GoSub(‘sub-callusers’,s,1(1)); - точечное изменение, добавляется/изменяется через ami

; sub-callusers – универсальная функция генерится при установке модуля.
[sub-callusers]
exten =>s,1,Noop()
exten =>s,n,Set(LOCAL(TOUSERID)=${ARG1})
exten =>s,n,ClearHash(TOUSERPARAM)
exten =>s,n,Set(HASH(TOUSERPARAM)=${REALTIME_HASH(rl_users,id,${LOCAL(TOUSERID)})})
exten =>s,n,GotoIf($["${HASH(TOUSERPARAM,id)}"=""]?return)
...

Podes engadir ou cambiar facilmente unha liña no plano de marcación usando Ami (Interface de control Asterisco) e non é necesario reiniciar todo o plano de marcación.

Isto resolveu o problema coa API de configuración. Incluso podes ir directamente á base de datos e engadir un novo grupo ou cambiar, por exemplo, a hora de acceso telefónico no campo "hora de marcación" do grupo e a seguinte chamada xa duraría o tempo especificado (esta non é unha recomendación para acción, xa que algunhas operacións da API requiren Ami chamadas).

As primeiras implementacións difíciles volveron traer o primeiro orgullo e decepción. Alegroume de que funcionase. A base de datos converteuse nunha ligazón crítica, aumentou a dependencia do disco, había máis riscos, pero todo funcionaba de forma estable e sen problemas. E o máis importante, agora todo o que se podía facer a través da interface web podería facerse a través da API, e utilizáronse os mesmos métodos. Ademais, a interface web desfíxose do botón "aplicar a configuración a PBX", que os administradores a miúdo esquecían.

A decepción foi que o desenvolvemento se fixo máis complicado. Desde a primeira versión, a linguaxe PHP xerou un plan de marcación na linguaxe Asterisco e parece completamente ilexible, ademais da propia lingua Asterisco para escribir un dialplan é extremadamente primitivo.

Como era:

$usersInitSection = $dialplan->createExtSection('usersinit-sub','s');
$usersInitSection
 ->add('',new Dialplanext_gotoif('$["${G_USERINIT}"="1"]','exit'))
 ->add('',new Dialplanext_set('G_USERINIT','1'))
 ->add('',new Dialplanext_gosub('1','s','sub-AddOnAnswerSub','usersconnected-sub'))
 ->add('',new Dialplanext_gosub('1','s','sub-AddOnPredoDialSub','usersinitondial-sub'))
 ->add('',new Dialplanext_set('LOCAL(TECH)','${CUT(CHANNEL(name),/,1)}'))
 ->add('',new Dialplanext_gotoif('$["${LOCAL(TECH)}"="SIP"]','sipdev'))
 ->add('',new Dialplanext_gotoif('$["${LOCAL(TECH)}"="PJSIP"]','pjsipdev'))

Na segunda versión, o dialplan converteuse en universal, incluíu todas as opcións de procesamento posibles dependendo dos parámetros e o seu tamaño aumentou significativamente. Todo isto retardou moito o tempo de desenvolvemento, e a mesma idea de que unha vez máis era necesario interferir no dialplan púxome triste.

Terceira versión

A idea de solucionar o problema non era xerar Asterisco dialplan desde php e use FastAGI e escribir todas as regras de procesamento no propio PHP. FastAGI permite Asterisco, para procesar a chamada, conéctese ao socket. Recibe comandos desde alí e envía resultados. Así, a lóxica do dialplan xa está fóra dos límites Asterisco e pódese escribir en calquera idioma, no meu caso en PHP.

Houbo moito ensaio e erro. O principal problema era que xa tiña moitas clases/ficheiros. Tardou uns 1,5 segundos en crear obxectos, inicializalos e rexistrarse entre si, e este atraso por chamada non é algo que se poida ignorar.

A inicialización debería ocorrer só unha vez e, polo tanto, a busca dunha solución comezou coa escritura dun servizo en php usando Pthreads. Despois dunha semana de experimentación, esta opción foi abandonada debido ás complexidades de como funciona esta extensión. Despois dun mes de probas, tamén tiven que abandonar a programación asíncrona en PHP; necesitaba algo sinxelo, familiar para calquera principiante de PHP, e moitas extensións para PHP son sincrónicas.

A solución foi o noso propio servizo multiproceso en C, que foi compilado PHPLIB. Carga todos os ficheiros ATS php, agarda a que se inicialicen todos os módulos, engádese unha devolución de chamada entre si e, cando todo estea listo, almacena en caché. Ao consultar por FastAGI créase un fluxo, reprodúcese nela unha copia da caché de todas as clases e os datos e pásase a solicitude á función php.

Con esta solución, o tempo transcorrido desde o envío dunha chamada ao noso servizo ata o primeiro comando Asterisco diminuíu de 1,5 segundos a 0,05 segundos e este tempo depende lixeiramente do tamaño do proxecto.

A historia dun proxecto ou como pasei 7 anos creando un PBX baseado en Asterisk e Php

Como resultado, o tempo para o desenvolvemento do dialplan reduciuse significativamente, e podo apreciar isto xa que tiven que reescribir todo o dialplan de todos os módulos en PHP. En primeiro lugar, os métodos xa deberían estar escritos en php para obter un obxecto da base de datos; eran necesarios para mostrar na interface web e, en segundo lugar, e isto é o principal, finalmente é posible traballar convenientemente con cadeas con números e matrices. con base de datos e moitas extensións PHP.

Para procesar o dialplan na clase do módulo, cómpre implementar a función dialplanDynamicCall e argumentación pbxCallRequest conterá un obxecto co que interactuar Asterisco.

A historia dun proxecto ou como pasei 7 anos creando un PBX baseado en Asterisk e Php

Ademais, fíxose posible depurar o dialplan (php ten xdebug e funciona para o noso servizo), pode moverse paso a paso visualizando os valores das variables.

Datos da chamada

Calquera análise e informe require datos recollidos correctamente, e este bloque de PBX tamén pasou por moitas probas e erros desde a primeira ata a terceira versión. Moitas veces, os datos das chamadas son un sinal. Unha chamada = unha gravación: quen chamou, quen respondeu, canto tempo falaron. En opcións máis interesantes, hai un sinal adicional que indica que empregado da PBX foi chamado durante a chamada. Pero todo isto cobre só unha parte das necesidades.

Os requisitos iniciais foron:

  • gardar non só a quen chamou a PBX, senón tamén quen respondeu, porque hai interceptacións e isto haberá que telo en conta ao analizar as chamadas,
  • tempo antes de conectarse cun empregado. En PBX gratuíto e algúns outros PBX, a chamada considérase contestada en canto a PBX colle o teléfono. Pero para o menú de voz xa necesitas coller o teléfono, polo que se responden todas as chamadas e o tempo de espera para unha resposta pasa a ser de 0 a 1 segundo. Polo tanto, decidiuse aforrar non só o tempo antes dunha resposta, senón o tempo antes de conectarse cos módulos clave (o propio módulo establece esta bandeira. Actualmente é "Empleado", "Liña externa"),
  • para un dialplan máis complexo, cando unha chamada viaxa entre distintos grupos, era necesario poder examinar cada elemento por separado.

A mellor opción resultou ser cando os módulos PBX envían información sobre si mesmos nas chamadas e, finalmente, gardan a información en forma de árbore.

Parece así:

En primeiro lugar, información xeral sobre a chamada (como todos os demais, nada especial).

A historia dun proxecto ou como pasei 7 anos creando un PBX baseado en Asterisk e Php

  1. Recibiu unha chamada nunha liña externa "Para a proba"ás 05:55:52 dende o número 89295671458 ata o número 89999999999, ao final foi contestado por un empregado"Secretario 2» co número 104. O cliente agardou 60 segundos e falou durante 36 segundos.
  2. Empregado "Secretario 2"chama ao 112 e responde un empregado"Xerente 1» despois de 8 segundos. Falan durante 14 segundos.
  3. O cliente transfírese ao empregado "xestor 1" onde continúan falando durante outros 13 segundos

Pero esta é a punta do iceberg; para cada rexistro podes obter un historial de chamadas detallado a través da PBX.

A historia dun proxecto ou como pasei 7 anos creando un PBX baseado en Asterisk e Php

Toda a información preséntase como un aniñamento de chamadas:

  1. Recibiu unha chamada nunha liña externa "Para a proba» ás 05:55:52 dende o número 89295671458 ata o número 89999999999.
  2. Ás 05:55:53 a liña exterior envía unha chamada ao circuíto de entrada "proba»
  3. Ao procesar unha chamada segundo o esquema, o módulo "chamada do xestor", no que a chamada é de 16 segundos. Este é un módulo desenvolvido para o cliente.
  4. Módulo "chamada do xestor" envía unha chamada ao empregado responsable do número (cliente) "Xerente 1” e agarda 5 segundos por unha resposta. O director non respondeu.
  5. Módulo "chamada do xestor"envía unha chamada ao grupo"xestores CORP" Estes son outros xestores da mesma dirección (sentados na mesma sala) e esperando 11 segundos por unha resposta.
  6. Grupo "xestores CORP"chama aos empregados"Xerente 1, Xerente 2, Xerente 3"simultáneamente durante 11 segundos. Sen resposta.
  7. A chamada do xestor remata. E o circuíto envía unha chamada ao módulo "Seleccionando unha ruta de 1c" Tamén un módulo escrito para o cliente. Aquí procesouse a chamada durante 0 segundos.
  8. O circuíto envía unha chamada ao menú de voz "Básico con marcación adicional" O cliente esperou alí durante 31 segundos, non houbo marcación adicional.
  9. O esquema envía unha chamada ao Grupo "Secretarios", onde o cliente esperou 12 segundos.
  10. Nun grupo, 2 empregados son chamados ao mesmo tempo "Secretario 1"E"Secretario 2"e despois de 12 segundos o empregado responde"Secretario 2" A resposta á chamada duplícase nas chamadas dos pais. Resulta que no grupo respondeu “Secretario 2", ao chamar ao circuíto respondeu "Secretario 2" e respondeu á chamada na liña exterior con "Secretario 2».

É o gardado de información sobre cada operación e a súa anidación o que permitirá simplemente facer informes. Un informe sobre o menú de voz axudarache a descubrir canto axuda ou dificulta. Elabora un informe sobre as chamadas perdidas polos empregados, tendo en conta que a chamada foi interceptada e, polo tanto, non se considera perdida, e tendo en conta que se trataba dunha chamada de grupo e que outra persoa respondeu antes, o que significa que tampouco se perdeu a chamada.

Este almacenamento de información permítelle tomar cada grupo por separado e determinar a eficacia con que funciona, e construír un gráfico dos grupos contestados e perdidos por hora. Tamén pode comprobar a precisión da conexión co xestor responsable analizando as transferencias despois de conectarse co xestor.

Tamén pode realizar estudos bastante atípicos, por exemplo, cantas veces os números que non están na base de datos marcan a extensión correcta ou que porcentaxe de chamadas saíntes se reenvían a un teléfono móbil.

O resultado?

Non se require un especialista para manter a PBX; o administrador máis común pode facelo, probado na práctica.

Para as modificacións, non son necesarios especialistas con cualificacións serias; o coñecemento de PHP é suficiente, porque Xa se escribiron módulos para o protocolo SIP, e para a cola, e para chamar a un empregado e outros. Hai unha clase de envoltura para Asterisco. Para desenvolver un módulo, un programador pode (e de boa maneira debería) chamar a módulos preparados. E coñecemento Asterisco son completamente innecesarios se o cliente pide engadir unha páxina con algún informe novo. Pero a práctica demostra que aínda que os programadores de terceiros poden facer fronte, séntense inseguros sen a documentación e a cobertura normal dos comentarios, polo que aínda hai marxe de mellora.

Os módulos poden:

  • crear novas capacidades de procesamento de chamadas,
  • engadir novos bloques á interface web,
  • herdar de calquera dos módulos existentes, redefinir funcións e substituílo ou simplemente ser unha copia lixeiramente modificada,
  • engade a túa configuración ao modelo de configuración doutros módulos e moito máis.

Configuración de PBX mediante API. Como se describiu anteriormente, todos os axustes almacénanse na base de datos e lense no momento da chamada, polo que pode cambiar todos os axustes da PBX a través da API. Ao chamar á API, a configuración non se recrea e os módulos non se reinician, polo tanto, non importa cantas configuracións e empregados teñas. As solicitudes da API execútanse rapidamente e non se bloquean entre si.

O PBX almacena todas as operacións clave con chamadas con duración (espera/conversa), aniñadas e en termos PBX (empleado, grupo, liña externa, non canle, número). Isto permítelle crear varios informes para clientes específicos e a maior parte do traballo é crear unha interface amigable.

O tempo dirá o que pasará despois. Aínda quedan moitos matices por refacer, aínda quedan moitos plans, pero xa pasou un ano da creación da 3a versión e xa podemos dicir que a idea está a funcionar. A principal desvantaxe da versión 3 son os recursos de hardware, pero normalmente isto é o que hai que pagar para facilitar o desenvolvemento.

Fonte: www.habr.com

Engadir un comentario