L'histoire d'un projet ou comment j'ai passé 7 ans à créer un PBX basé sur Asterisk et Php

Beaucoup d’entre vous, comme moi, ont sûrement eu l’idée de faire quelque chose d’unique. Dans cet article, je décrirai les problèmes techniques et les solutions auxquels j'ai dû faire face lors du développement du PBX. Peut-être que cela aidera quelqu'un à décider de sa propre idée et à suivre le chemin tracé, car j'ai également bénéficié de l'expérience des pionniers.

L'histoire d'un projet ou comment j'ai passé 7 ans à créer un PBX basé sur Asterisk et Php

Idée et exigences clés

Et tout a commencé simplement par l'amour pour astérisque (cadre pour applications de communication du bâtiment), automatisation de la téléphonie et des installations FreePBX (interface web pour astérisque). Si les besoins de l’entreprise étaient dépourvus de précisions et rentraient dans les limites des capacités FreePBX - tout est bon. L'installation complète s'est déroulée en XNUMX heures, l'entreprise a reçu un PBX configuré, une interface conviviale et une courte formation ainsi qu'une assistance si vous le souhaitez.

Mais les tâches les plus intéressantes étaient non standard et ce n'était pas si fabuleux. astérisque peut faire beaucoup, mais pour maintenir l'interface Web en état de fonctionnement, il a fallu y consacrer plusieurs fois plus de temps. Un petit détail pourrait donc prendre beaucoup plus de temps que l'installation du reste du PBX. Et le problème n’est pas qu’il faut beaucoup de temps pour écrire une interface Web, mais plutôt les caractéristiques architecturales. FreePBX. Approches et méthodes architecturales FreePBX a été conçu à l'époque de php4, et à ce moment-là il y avait déjà php5.6 sur lequel tout pouvait être rendu plus simple et plus pratique.

La goutte d'eau qui a fait déborder le vase était les plans de cadran graphiques sous la forme d'un diagramme. Quand j'ai essayé de construire quelque chose comme ça pour FreePBX, j'ai réalisé que je devrais le réécrire considérablement et qu'il serait plus facile de construire quelque chose de nouveau.

Les principales exigences étaient les suivantes :

  • configuration simple, accessible intuitivement même à un administrateur novice. Ainsi, les entreprises n'exigent pas de maintenance PBX de notre part,
  • modification facile pour que les tâches soient résolues dans un délai adéquat,
  • facilité d'intégration avec PBX. U FreePBX il n'y avait pas d'API pour modifier les paramètres, c'est-à-dire Vous ne pouvez pas, par exemple, créer des groupes ou des menus vocaux depuis une application tierce, uniquement l'API elle-même astérisque,
  • open source - pour les programmeurs, cela est extrêmement important pour les modifications du client.

L'idée d'un développement plus rapide était que toutes les fonctionnalités soient constituées de modules sous forme d'objets. Tous les objets devaient avoir une classe parent commune, ce qui signifie que les noms de toutes les fonctions principales sont déjà connus et qu'il existe donc déjà des implémentations par défaut. Les objets permettront de réduire considérablement le nombre d'arguments sous forme de tableaux associatifs avec des clés de chaîne, que vous pourrez découvrir dans FreePBX Cela a été possible en examinant la fonction entière et les fonctions imbriquées. Dans le cas des objets, l'auto-complétion banale montrera toutes les propriétés et simplifiera en général la vie plusieurs fois. De plus, l'héritage et la redéfinition résolvent déjà de nombreux problèmes liés aux modifications.

La prochaine chose qui a ralenti le temps de retouche et qui méritait d'être évitée était la duplication. S'il existe un module chargé de composer un numéro d'employé, alors tous les autres modules qui doivent envoyer un appel à un employé doivent l'utiliser et ne pas créer leurs propres copies. Ainsi, si vous devez modifier quelque chose, vous ne devrez le modifier qu'à un seul endroit et la recherche de « comment cela fonctionne » doit être effectuée à un seul endroit, et non dans tout le projet.

Première version et premières erreurs

Le premier prototype était prêt en un an. L'ensemble du PBX, comme prévu, était modulaire et les modules pouvaient non seulement ajouter de nouvelles fonctionnalités pour traiter les appels, mais également modifier l'interface Web elle-même.

L'histoire d'un projet ou comment j'ai passé 7 ans à créer un PBX basé sur Asterisk et Php
Oui, l'idée de​​construire un plan de numérotation sous la forme d'un tel schéma n'est pas la mienne, mais c'est très pratique et j'ai fait de même pour astérisque.

L'histoire d'un projet ou comment j'ai passé 7 ans à créer un PBX basé sur Asterisk et Php

En écrivant un module, les programmeurs pouvaient déjà :

  • créer votre propre fonctionnalité de traitement des appels, qui pourra être placée sur le schéma, ainsi que dans le menu des éléments de gauche,
  • créez vos propres pages pour l'interface web et ajoutez vos modèles aux pages existantes (si le développeur de la page l'a prévu),
  • ajoutez vos paramètres à l'onglet paramètres principal ou créez votre propre onglet paramètres,
  • le programmeur peut hériter d'un module existant, modifier une partie de la fonctionnalité et l'enregistrer sous un nouveau nom ou remplacer le module d'origine.

Par exemple, voici comment vous pouvez créer votre propre menu vocal :

......
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__); //Подменить существующий модуль

Les premières mises en œuvre complexes ont apporté les premières fiertés et les premières déceptions. J'étais content que cela fonctionne, que j'étais déjà capable de reproduire les principales fonctionnalités FreePBX. J'étais heureux que les gens aiment l'idée du projet. Il existait encore de nombreuses options pour simplifier le développement, mais déjà à cette époque, certaines tâches étaient déjà facilitées.

L'API pour modifier la configuration du PBX a été décevante - le résultat n'était pas du tout celui que nous souhaitions. J'ai repris le même principe que dans FreePBX, en cliquant sur le bouton Appliquer, toute la configuration est recréée et les modules sont redémarrés.

Cela ressemble à ceci:

L'histoire d'un projet ou comment j'ai passé 7 ans à créer un PBX basé sur Asterisk et Php
*Dialplan est une règle (algorithme) par laquelle un appel est traité.

Mais avec cette option, il est impossible d'écrire une API normale pour modifier les paramètres du PBX. Premièrement, l'opération d'application des modifications à astérisque trop long et gourmand en ressources.
Deuxièmement, vous ne pouvez pas appeler deux fonctions en même temps, car les deux créeront la configuration.
Troisièmement, il applique tous les paramètres, y compris ceux définis par l'administrateur.

Dans cette version, comme dans Askozia, il a été possible de générer la configuration des modules modifiés uniquement et de redémarrer uniquement les modules nécessaires, mais ce ne sont que des demi-mesures. Il fallait changer d’approche.

Deuxième version. Nez arraché, queue coincée

L'idée pour résoudre le problème n'était pas de recréer la configuration et le plan de numérotation pour astérisque, mais enregistrez les informations dans la base de données et lisez-les directement pendant le traitement de l'appel. astérisque Je savais déjà lire les configurations de la base de données, il suffit de changer la valeur dans la base de données et le prochain appel sera traité en tenant compte des modifications, et la fonction était parfaite pour lire les paramètres du plan de numérotation REALTIME_HASH.

Au final, il n'était même pas nécessaire de redémarrer astérisque lors de la modification des paramètres et tous les paramètres ont commencé à être appliqués immédiatement à astérisque.

L'histoire d'un projet ou comment j'ai passé 7 ans à créer un PBX basé sur Asterisk et Php

Les seuls changements apportés au plan de numérotation sont l'ajout de numéros de poste et indices. Mais il s'agissait de petits changements ponctuels

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)
...

Vous pouvez facilement ajouter ou modifier une ligne dans le plan de numérotation en utilisant À moi (interface de contrôle astérisque) et aucun redémarrage de l'ensemble du plan de numérotation n'est requis.

Cela a résolu le problème avec l'API de configuration. Vous pouvez même accéder directement à la base de données et ajouter un nouveau groupe ou modifier, par exemple, l'heure de connexion dans le champ « heure de connexion » du groupe et le prochain appel durerait déjà la durée spécifiée (ce n'est pas une recommandation pour action, car certaines opérations de l'API nécessitent À moi appels).

Les premières mises en œuvre difficiles ont apporté à nouveau les premières fiertés et déceptions. J'étais content que cela ait fonctionné. La base de données est devenue un maillon critique, la dépendance au disque a augmenté, il y avait plus de risques, mais tout a fonctionné de manière stable et sans problème. Et plus important encore, tout ce qui pouvait être fait via l'interface Web pouvait désormais être fait via l'API, et les mêmes méthodes étaient utilisées. De plus, l'interface Web a supprimé le bouton « Appliquer les paramètres au PBX », que les administrateurs oubliaient souvent.

La déception était que le développement devenait plus compliqué. Depuis la première version, le langage PHP génère un plan de numérotation dans le langage astérisque et ça a l'air complètement illisible, plus la langue elle-même astérisque pour écrire un plan de numérotation, c'est extrêmement primitif.

A quoi ça ressemblait :

$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'))

Dans la deuxième version, le plan de numérotation est devenu universel, il comprenait toutes les options de traitement possibles en fonction des paramètres et sa taille a considérablement augmenté. Tout cela a considérablement ralenti le temps de développement, et la simple pensée qu'il fallait encore une fois interférer avec le plan de numérotation m'a rendu triste.

La troisième version

L'idée pour résoudre le problème n'était pas de générer astérisque plan de numérotation à partir de php et utilisation FastAGI et écrivez toutes les règles de traitement en PHP lui-même. FastAGI il permet astérisque, pour traiter l'appel, connectez-vous à la socket. Recevez des commandes à partir de là et envoyez les résultats. Ainsi, la logique du plan de numérotation dépasse déjà les limites astérisque et peut être écrit dans n'importe quel langage, dans mon cas en PHP.

Il y a eu beaucoup d’essais et d’erreurs. Le principal problème était que j'avais déjà beaucoup de classes/fichiers. Il a fallu environ 1,5 seconde pour créer des objets, les initialiser et s'enregistrer les uns avec les autres, et ce délai par appel ne peut pas être ignoré.

L'initialisation n'aurait dû avoir lieu qu'une seule fois et la recherche d'une solution a donc commencé par l'écriture d'un service en php en utilisant Fils de discussion. Après une semaine d’expérimentation, cette option a été abandonnée en raison de la complexité du fonctionnement de cette extension. Après un mois de tests, j'ai également dû abandonner la programmation asynchrone en PHP ; j'avais besoin de quelque chose de simple, familier à tout débutant PHP, et de nombreuses extensions pour PHP sont synchrones.

La solution était notre propre service multithread en C, compilé avec PHPLIBName. Il charge tous les fichiers php ATS, attend que tous les modules s'initialisent, s'ajoute un rappel les uns aux autres et, lorsque tout est prêt, le met en cache. Lors d'une demande auprès de FastAGI un flux est créé, une copie du cache de toutes les classes et données y est reproduite et la requête est transmise à la fonction php.

Avec cette solution, le délai entre l'envoi d'un appel à notre service et la première commande astérisque diminué de 1,5 s à 0,05 s et ce temps dépend légèrement de la taille du projet.

L'histoire d'un projet ou comment j'ai passé 7 ans à créer un PBX basé sur Asterisk et Php

En conséquence, le temps de développement du plan de numérotation a été considérablement réduit, et je peux l'apprécier puisque j'ai dû réécrire l'intégralité du plan de numérotation de tous les modules en PHP. Premièrement, les méthodes devaient déjà être écrites en php pour obtenir un objet de la base de données ; elles étaient nécessaires pour l'affichage dans l'interface Web, et deuxièmement, et c'est l'essentiel, il est enfin possible de travailler facilement avec des chaînes avec des nombres et des tableaux avec base de données et de nombreuses extensions PHP.

Pour traiter le plan de numérotation dans la classe de module, vous devez implémenter la fonction plan de numérotationDynamicCall et argumentation pbxCallRequest contiendra un objet avec lequel interagir astérisque.

L'histoire d'un projet ou comment j'ai passé 7 ans à créer un PBX basé sur Asterisk et Php

De plus, il est devenu possible de déboguer le plan de numérotation (php a xdebug et cela fonctionne pour notre service), vous pouvez avancer pas à pas en visualisant les valeurs des variables.

Données d'appel

Toutes les analyses et tous les rapports nécessitent des données correctement collectées, et ce bloc PBX a également subi de nombreux essais et erreurs de la première à la troisième version. Souvent, les données d’appel sont un signe. Un appel = un enregistrement : qui a appelé, qui a répondu, combien de temps ils ont parlé. Dans des options plus intéressantes, il existe un panneau supplémentaire indiquant quel employé du PBX a été appelé lors de l'appel. Mais tout cela ne couvre qu’une partie des besoins.

Les exigences initiales étaient les suivantes :

  • enregistrez non seulement qui a appelé le PBX, mais aussi qui a répondu, car il y a des interceptions et il faudra en tenir compte lors de l'analyse des appels,
  • temps avant de communiquer avec un employé. Dans FreePBX et certains autres PBX, l'appel est considéré comme répondu dès que le PBX décroche le téléphone. Mais pour le menu vocal, vous devez déjà décrocher le téléphone, donc tous les appels reçoivent une réponse et le temps d'attente pour une réponse devient de 0 à 1 seconde. Par conséquent, il a été décidé d'économiser non seulement le temps avant une réponse, mais aussi le temps avant la connexion aux modules clés (le module lui-même définit ce drapeau. Actuellement, il s'agit de « Employé », « Ligne externe »),
  • pour un plan de numérotation plus complexe, lorsqu'un appel transite entre différents groupes, il fallait pouvoir examiner chaque élément séparément.

La meilleure option s'est avérée lorsque les modules PBX envoient des informations sur eux-mêmes lors des appels et enregistrent finalement les informations sous la forme d'une arborescence.

Cela ressemble à ceci:

Tout d'abord, des informations générales sur l'appel (comme tout le monde - rien de spécial).

L'histoire d'un projet ou comment j'ai passé 7 ans à créer un PBX basé sur Asterisk et Php

  1. Reçu un appel sur une ligne extérieure "Pour la pâte"à 05:55:52 du numéro 89295671458 au numéro 89999999999, un employé a finalement répondu"Secrétaire2» avec le numéro 104. Le client a attendu 60 secondes et a parlé pendant 36 secondes.
  2. Employé "Secrétaire2"appelle le 112 et un employé répond"Gestionnaire1» après 8 secondes. Ils parlent pendant 14 secondes.
  3. Le Client est transféré au Salarié "manager1" où ils continuent à parler pendant encore 13 secondes

Mais ce n'est que la pointe de l'iceberg : pour chaque enregistrement, vous pouvez obtenir un historique détaillé des appels via le PBX.

L'histoire d'un projet ou comment j'ai passé 7 ans à créer un PBX basé sur Asterisk et Php

Toutes les informations sont présentées sous forme d'imbrication d'appels :

  1. Reçu un appel sur une ligne extérieure "Pour la pâte» à 05:55:52 du numéro 89295671458 au numéro 89999999999.
  2. A 05:55:53 la ligne extérieure envoie un appel au circuit entrant "tester»
  3. Lors du traitement d'un appel selon le schéma, le module «appel du responsable", dans lequel l'appel dure 16 secondes. Il s'agit d'un module développé pour le client.
  4. Module "appel du responsable" envoie un appel à l'employé responsable du numéro (client) "Gestionnaire1» et attend 5 secondes pour une réponse. Le directeur n'a pas répondu.
  5. Module "appel du responsable"envoie un appel au groupe"Dirigeants de CORP" Ce sont d'autres managers de la même direction (assis dans la même pièce) et attendant 11 secondes une réponse.
  6. Grouper "Dirigeants de CORP"appelle les employés"Gestionnaire1, Gestionnaire2, Gestionnaire3"simultanément pendant 11 secondes. Pas de réponse.
  7. L'appel du manager prend fin. Et le circuit envoie un appel au module "Sélection d'un itinéraire à partir de 1c" Également un module écrit pour le client. Ici, l'appel a été traité pendant 0 seconde.
  8. Le circuit envoie un appel au menu vocal "Basique avec numérotation supplémentaire" Le client y a attendu 31 secondes, il n'y a eu aucune numérotation supplémentaire.
  9. Le schéma envoie un appel au Groupe "Secrétaires", où le client a attendu 12 secondes.
  10. Dans un groupe, 2 salariés sont appelés en même temps"Secrétaire1"Et"Secrétaire2" et au bout de 12 secondes l'employé répond "Secrétaire2" La réponse à l'appel est dupliquée dans les appels parents. Il s'avère que dans le groupe il a répondu «Secrétaire2", lors de l'appel du circuit, j'ai répondu "Secrétaire2" et a répondu à l'appel sur la ligne extérieure avec "Secrétaire2».

C'est la sauvegarde des informations sur chaque opération et leur imbrication qui permettra de réaliser simplement des reportings. Un rapport sur le menu vocal vous aidera à savoir dans quelle mesure cela aide ou gêne. Construisez un rapport sur les appels manqués par les employés, en tenant compte du fait que l'appel a été intercepté et n'est donc pas considéré comme manqué, et en tenant compte du fait qu'il s'agissait d'un appel de groupe et que quelqu'un d'autre a répondu plus tôt, ce qui signifie que l'appel n'a pas non plus été manqué.

Un tel stockage d'informations vous permettra de prendre chaque groupe séparément et de déterminer son efficacité, ainsi que de créer un graphique des groupes répondus et manqués par heure. Vous pouvez également vérifier la précision de la connexion avec le gestionnaire responsable en analysant les transferts après vous être connecté au gestionnaire.

Vous pouvez également réaliser des études assez atypiques, par exemple sur la fréquence à laquelle des numéros qui ne figurent pas dans la base de données composent le bon poste ou sur le pourcentage d'appels sortants qui sont renvoyés vers un téléphone mobile.

Le résultat?

Un spécialiste n'est pas nécessaire pour entretenir le PBX, l'administrateur le plus ordinaire peut le faire - testé dans la pratique.

Pour les modifications, des spécialistes ayant de sérieuses qualifications ne sont pas nécessaires, la connaissance de PHP est suffisante, car Des modules ont déjà été écrits pour le protocole SIP, pour la file d'attente, pour appeler un employé, etc. Il existe une classe wrapper pour astérisque. Pour développer un module, un programmeur peut (et devrait, dans le bon sens), appeler des modules prêts à l'emploi. Et la connaissance astérisque sont totalement inutiles si le client demande d'ajouter une page avec un nouveau rapport. Mais la pratique montre que même si les programmeurs tiers peuvent s'en sortir, ils ne se sentent pas en sécurité sans documentation et sans couverture normale des commentaires, il y a donc encore place à l'amélioration.

Les modules peuvent :

  • créer de nouvelles capacités de traitement des appels,
  • ajouter de nouveaux blocs à l'interface web,
  • hériter de l'un des modules existants, redéfinir les fonctions et le remplacer, ou simplement en être une copie légèrement modifiée,
  • ajoutez vos paramètres au modèle de paramètres d'autres modules et bien plus encore.

Paramètres du PBX via API. Comme décrit ci-dessus, tous les paramètres sont stockés dans la base de données et lus au moment de l'appel, vous pouvez donc modifier tous les paramètres du PBX via l'API. Lors de l'appel de l'API, la configuration n'est pas recréée et les modules ne sont pas redémarrés. Par conséquent, le nombre de paramètres et d'employés dont vous disposez n'a pas d'importance. Les requêtes API sont exécutées rapidement et ne se bloquent pas.

Le PBX stocke toutes les opérations clés avec les appels avec des durées (attente/conversation), imbrication et en termes de PBX (employé, groupe, ligne externe, pas de canal, numéro). Cela vous permet de créer divers rapports pour des clients spécifiques et la majeure partie du travail consiste à créer une interface conviviale.

Le temps nous dira ce qui se passera ensuite. Il y a encore beaucoup de nuances à refaire, il y a encore beaucoup de projets, mais un an s'est écoulé depuis la création de la 3ème version et on peut déjà dire que l'idée fonctionne. Le principal inconvénient de la version 3 réside dans les ressources matérielles, mais c'est généralement ce que vous devez payer pour faciliter le développement.

Source: habr.com

Ajouter un commentaire