Het verhaal van één project of hoe ik 7 jaar lang een PBX heb gemaakt op basis van Asterisk en Php

Velen van jullie hadden vast, net als ik, een idee om iets unieks te doen. In dit artikel beschrijf ik de technische problemen en oplossingen waarmee ik te maken kreeg bij het ontwikkelen van de PBX. Misschien helpt dit iemand om tot een eigen idee te komen, en iemand om het platgetreden pad te volgen, omdat ik ook heb geprofiteerd van de ervaring van pioniers.

Het verhaal van één project of hoe ik 7 jaar lang een PBX heb gemaakt op basis van Asterisk en Php

Idee en belangrijkste vereisten

En het begon allemaal simpelweg met liefde voor Asterisk (framework voor gebouwcommunicatietoepassingen), automatisering van telefonie en installaties FreePBX (webinterface voor Asterisk). Als de behoeften van het bedrijf geen specifieke kenmerken hadden en binnen de mogelijkheden vielen FreePBX - alles is geweldig. De gehele installatie vond binnen XNUMX uur plaats, het bedrijf kreeg een geconfigureerde telefooncentrale, een gebruiksvriendelijke interface en een korte training plus ondersteuning indien gewenst.

Maar de meest interessante taken waren niet-standaard en toen was het niet zo fantastisch. Asterisk kan veel, maar om de webinterface in goede staat te houden, was het nodig om vele malen meer tijd te besteden. Een klein detail kan dus veel langer duren dan het installeren van de rest van de PBX. En het punt is niet dat het veel tijd kost om een ​​webinterface te schrijven, maar het punt zit hem in de architectonische kenmerken FreePBX. Architectuurbenaderingen en -methoden FreePBX was aangelegd ten tijde van php4, en op dat moment was er al php5.6 waarop alles eenvoudiger en handiger kon worden gemaakt.

De laatste druppel waren grafische belplannen in de vorm van een diagram. Toen ik probeerde zoiets als dit te bouwen FreePBXbesefte ik dat ik het aanzienlijk zou moeten herschrijven en dat het gemakkelijker zou zijn om iets nieuws te bouwen.

De belangrijkste vereisten waren:

  • eenvoudige installatie, intuïtief toegankelijk, zelfs voor een beginnende beheerder. Bedrijven hebben dus aan onze kant geen PBX-onderhoud nodig,
  • eenvoudige aanpassing zodat taken binnen voldoende tijd worden opgelost,
  • gemakkelijke integratie met PBX. U FreePBX er was geen API voor het wijzigen van instellingen, d.w.z. U kunt bijvoorbeeld geen groepen of spraakmenu's maken vanuit een applicatie van derden, alleen via de API zelf Asterisk,
  • opensource - voor programmeurs is dit uiterst belangrijk voor aanpassingen voor de klant.

Het idee van een snellere ontwikkeling was om alle functionaliteit te laten bestaan ​​uit modules in de vorm van objecten. Alle objecten moesten een gemeenschappelijke bovenliggende klasse hebben, wat betekent dat de namen van alle hoofdfuncties al bekend zijn en daarom zijn er al standaardimplementaties. Met objecten kunt u het aantal argumenten dramatisch verminderen in de vorm van associatieve arrays met tekenreekssleutels, die u kunt vinden in FreePBX Het was mogelijk door de gehele functie en geneste functies te onderzoeken. In het geval van objecten zal banale automatische aanvulling alle eigenschappen tonen, en in het algemeen het leven vele malen vereenvoudigen. Bovendien lossen overerving en herdefinitie al veel problemen met aanpassingen op.

Het volgende dat de herbewerkingstijd vertraagde en de moeite waard was om te vermijden, was duplicatie. Als er een module is die verantwoordelijk is voor het bellen van een medewerker, dan moeten alle andere modules die een oproep naar een medewerker moeten sturen deze gebruiken en niet hun eigen kopieën maken. Dus als je iets moet veranderen, dan hoef je maar op één plek te veranderen en moet de zoektocht naar “hoe het werkt” op één plek worden uitgevoerd, en niet gedurende het hele project.

Eerste versie en eerste fouten

Binnen een jaar was het eerste prototype gereed. De gehele PBX was, zoals gepland, modulair en de modules konden niet alleen nieuwe functionaliteit toevoegen voor het verwerken van oproepen, maar ook de webinterface zelf veranderen.

Het verhaal van één project of hoe ik 7 jaar lang een PBX heb gemaakt op basis van Asterisk en Php
Ja, het idee om een ​​belplan te bouwen in de vorm van een dergelijk schema is niet van mij, maar het is erg handig en ik deed hetzelfde voor Asterisk.

Het verhaal van één project of hoe ik 7 jaar lang een PBX heb gemaakt op basis van Asterisk en Php

Door een module te schrijven konden programmeurs al:

  • creëer uw eigen functionaliteit voor oproepverwerking, die in het diagram kan worden geplaatst, maar ook in het menu met elementen aan de linkerkant,
  • maak uw eigen pagina's voor de webinterface en voeg uw sjablonen toe aan bestaande pagina's (indien de pagina-ontwikkelaar dit heeft voorzien),
  • voeg uw instellingen toe aan het hoofdinstellingentabblad of maak uw eigen instellingentabblad,
  • de programmeur kan overnemen van een bestaande module, een deel van de functionaliteit wijzigen en onder een nieuwe naam registreren of de originele module vervangen.

Zo kunt u bijvoorbeeld uw eigen spraakmenu maken:

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

De eerste complexe implementaties brachten de eerste trots en de eerste teleurstellingen. Ik was blij dat het werkte, dat ik de belangrijkste kenmerken al kon reproduceren FreePBX. Ik was blij dat mensen het idee van het plan leuk vonden. Er waren nog steeds veel opties om de ontwikkeling te vereenvoudigen, maar zelfs in die tijd werden sommige taken al eenvoudiger gemaakt.

De API voor het wijzigen van de PBX-configuratie was een teleurstelling - het resultaat was helemaal niet wat we wilden. Ik heb hetzelfde principe gevolgd als in FreePBXDoor op de knop Toepassen te klikken, wordt de volledige configuratie opnieuw aangemaakt en worden de modules opnieuw gestart.

Het ziet er zo uit:

Het verhaal van één project of hoe ik 7 jaar lang een PBX heb gemaakt op basis van Asterisk en Php
*Kiesplan is een regel (algoritme) waarmee een oproep wordt verwerkt.

Maar met deze optie is het onmogelijk om een ​​normale API te schrijven voor het wijzigen van de PBX-instellingen. Ten eerste de werking van het toepassen van wijzigingen op Asterisk te lang en arbeidsintensief.
Ten tweede kun je niet tegelijkertijd twee functies oproepen, omdat beide zullen de configuratie creëren.
Ten derde worden alle instellingen toegepast, inclusief die van de beheerder.

In deze versie, zoals in Askozia, was het mogelijk om de configuratie van alleen gewijzigde modules te genereren en alleen de noodzakelijke modules opnieuw op te starten, maar dit zijn allemaal halve maatregelen. Het was noodzakelijk om de aanpak te veranderen.

Tweede versie. Neus uitgetrokken staart zit vast

Het idee om het probleem op te lossen was niet om de configuratie en het belplan opnieuw te maken Asterisk, maar sla informatie op in de database en lees deze rechtstreeks uit de database tijdens het verwerken van de oproep. Asterisk Ik wist al hoe ik configuraties uit de database moest lezen, verander gewoon de waarde in de database en de volgende oproep wordt verwerkt, rekening houdend met de wijzigingen, en de functie was perfect voor het lezen van dialplan-parameters REALTIME_HASH.

Uiteindelijk was het niet eens nodig om opnieuw op te starten Asterisk bij het wijzigen van de instellingen en alle instellingen werden onmiddellijk toegepast Asterisk.

Het verhaal van één project of hoe ik 7 jaar lang een PBX heb gemaakt op basis van Asterisk en Php

De enige wijzigingen in het kiesplan zijn de toevoeging van toestelnummers en hints. Maar dit waren kleine vlekjesveranderingen

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

Via het belplan kunt u eenvoudig een lijn toevoegen of wijzigen ami (besturingsinterface Asterisk) en er is geen herstart van het gehele dialplan vereist.

Dit loste het probleem met de configuratie-API op. U kunt zelfs rechtstreeks naar de database gaan en een nieuwe groep toevoegen of bijvoorbeeld de inbeltijd in het veld "inbeltijd" voor de groep wijzigen, zodat het volgende gesprek al de opgegeven tijd duurt (dit is geen aanbeveling voor actie, omdat sommige API-bewerkingen dit vereisen ami oproepen).

De eerste moeilijke implementaties brachten opnieuw de eerste trots en teleurstelling. Ik was blij dat het werkte. De database werd een kritische schakel, de afhankelijkheid van de schijf nam toe, er waren meer risico's, maar alles werkte stabiel en zonder problemen. En het allerbelangrijkste: nu kon alles wat via de webinterface kon worden gedaan, via de API worden gedaan, en werden dezelfde methoden gebruikt. Bovendien heeft de webinterface de knop 'Instellingen toepassen op PBX' verwijderd, die beheerders vaak vergaten.

De teleurstelling was dat de ontwikkeling ingewikkelder werd. Sinds de eerste versie heeft de PHP-taal een dialplan in de taal gegenereerd Asterisk en het ziet er volkomen onleesbaar uit, plus de taal zelf Asterisk voor het schrijven van een belplan is het uiterst primitief.

Hoe het eruit zag:

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

In de tweede versie werd het dialplan universeel, het omvatte alle mogelijke verwerkingsopties, afhankelijk van de parameters, en de omvang ervan nam aanzienlijk toe. Dit alles vertraagde de ontwikkelingstijd enorm, en alleen al de gedachte dat het opnieuw nodig was om zich met het belplan te bemoeien, maakte me verdrietig.

Derde versie

Het idee om het probleem op te lossen was niet om te genereren Asterisk dialplan van php en gebruik SnelAGI en schrijf alle verwerkingsregels in PHP zelf. SnelAGI laat AsteriskOm de oproep te verwerken, maakt u verbinding met het stopcontact. Van daaruit opdrachten ontvangen en resultaten verzenden. De logica van het belplan ligt dus al buiten de grenzen Asterisk en kan in elke taal worden geschreven, in mijn geval in PHP.

Er was veel vallen en opstaan. Het grootste probleem was dat ik al veel klassen/bestanden had. Het duurde ongeveer 1,5 seconde om objecten te maken, te initialiseren en elkaar bij elkaar te registreren, en deze vertraging per oproep kan niet worden genegeerd.

Initialisatie had maar één keer mogen gebeuren en daarom begon de zoektocht naar een oplossing met het schrijven van een service in php met behulp van P-threads. Na een week experimenteren werd deze optie op de plank gelegd vanwege de complexiteit van de manier waarop deze extensie werkt. Na een maand testen moest ik ook het asynchrone programmeren in PHP opgeven; ik had iets eenvoudigs nodig, bekend voor elke PHP-beginner, en veel extensies voor PHP zijn synchroon.

De oplossing was onze eigen multi-threaded dienst in C, waarmee werd gecompileerd PHPLIB. Het laadt alle ATS php-bestanden, wacht tot alle modules zijn geïnitialiseerd, voegt een callback naar elkaar toe en wanneer alles gereed is, wordt het in de cache opgeslagen. Bij navraag bij SnelAGI er wordt een stream gemaakt, een kopie uit de cache van alle klassen en gegevens wordt daarin gereproduceerd en het verzoek wordt doorgegeven aan de php-functie.

Met deze oplossing verstrijkt de tijd tussen het sturen van een oproep naar onze service en het eerste commando Asterisk afgenomen van 1,5s naar 0,05s en deze tijd is enigszins afhankelijk van de grootte van het project.

Het verhaal van één project of hoe ik 7 jaar lang een PBX heb gemaakt op basis van Asterisk en Php

Als gevolg hiervan werd de tijd voor de ontwikkeling van het dialplan aanzienlijk verkort, en ik kan dit op prijs stellen omdat ik het volledige dialplan van alle modules in PHP moest herschrijven. Ten eerste zouden er al methoden in php geschreven moeten zijn om een ​​object uit de database te halen; ze waren nodig voor weergave in de webinterface, en ten tweede, en dit is het belangrijkste, het is eindelijk mogelijk om gemakkelijk met strings met getallen en arrays te werken met database plus veel PHP-extensies.

Om het dialplan in de moduleklasse te verwerken, moet u de functie implementeren belplanDynamicCall en betoog pbxCallRequest zal een object bevatten om mee te communiceren Asterisk.

Het verhaal van één project of hoe ik 7 jaar lang een PBX heb gemaakt op basis van Asterisk en Php

Bovendien werd het mogelijk om het dialplan te debuggen (php heeft xdebug en het werkt voor onze service), je kunt stap voor stap bewegen door de waarden van variabelen te bekijken.

Gegevens bellen

Voor elke analyse en rapportage zijn correct verzamelde gegevens nodig, en ook dit PBX-blok heeft van de eerste tot de derde versie veel vallen en opstaan ​​ondergaan. Vaak zijn oproepgegevens een teken. Eén oproep = één opname: wie belde, wie opnam, hoe lang ze spraken. Bij interessantere opties is er een extra bordje dat aangeeft welke PBX-medewerker tijdens het gesprek is gebeld. Maar dit alles dekt slechts een deel van de behoeften.

De initiële vereisten waren:

  • bespaar niet alleen wie de PBX heeft gebeld, maar ook wie opnam, omdat er zijn onderscheppingen en hiermee moet rekening worden gehouden bij het analyseren van oproepen,
  • tijd voordat u verbinding maakt met een medewerker. In FreePBX en sommige andere PBX's wordt de oproep als beantwoord beschouwd zodra de PBX de telefoon opneemt. Maar voor het spraakmenu moet u al de telefoon opnemen, zodat alle oproepen worden beantwoord en de wachttijd voor een antwoord 0-1 seconde wordt. Daarom werd besloten om niet alleen de tijd vóór een antwoord te besparen, maar ook de tijd voordat verbinding werd gemaakt met sleutelmodules (de module zelf stelt deze vlag in. Momenteel is dit "Werknemer", "Externe lijn"),
  • voor een complexer belplan, waarbij een gesprek tussen verschillende groepen reist, was het noodzakelijk om elk element afzonderlijk te kunnen onderzoeken.

De beste optie bleek te zijn wanneer de PBX-modules tijdens oproepen informatie over zichzelf verzenden en deze informatie uiteindelijk in de vorm van een boom opslaan.

Het ziet er zo uit:

Eerst algemene informatie over het gesprek (zoals iedereen - niets bijzonders).

Het verhaal van één project of hoe ik 7 jaar lang een PBX heb gemaakt op basis van Asterisk en Php

  1. Ik heb een oproep ontvangen op een buitenlijn "Voor het deeg"om 05:55:52 van het nummer 89295671458 naar het nummer 89999999999, uiteindelijk werd er door een medewerker opgenomen"Secretaris2» met nummer 104. De cliënt wachtte 60 seconden en sprak 36 seconden.
  2. Medewerker "Secretaris2"belt naar 112 en een medewerker neemt op"Beheerder1» na 8 seconden. Ze praten 14 seconden.
  3. De Klant gaat over op de Medewerker "beheerder1" waar ze nog 13 seconden blijven praten

Maar dit is het topje van de ijsberg; voor elk record kunt u via de PBX een gedetailleerde belgeschiedenis opvragen.

Het verhaal van één project of hoe ik 7 jaar lang een PBX heb gemaakt op basis van Asterisk en Php

Alle informatie wordt gepresenteerd als een nesting van oproepen:

  1. Ik heb een oproep ontvangen op een buitenlijn "Voor het deeg» om 05:55:52 van het nummer 89295671458 naar het nummer 89999999999.
  2. Om 05:55:53 stuurt de buitenlijn een oproep naar het inkomende circuit "proef»
  3. Bij het verwerken van een oproep volgens het schema, de module “manager bellen", waarbij het gesprek 16 seconden duurt. Dit is een module ontwikkeld voor de klant.
  4. Module "manager bellen" stuurt een oproep naar de medewerker die verantwoordelijk is voor het nummer (klant) "Beheerder1' en wacht 5 seconden op een reactie. De manager antwoordde niet.
  5. Module "manager bellen"stuurt een oproep naar de groep"CORP-managers" Dit zijn andere managers van dezelfde richting (die in dezelfde kamer zitten) en 11 seconden wachten op antwoord.
  6. Groep "CORP-managers"belt medewerkers"Beheerder1, Beheerder2, Beheerder3"Tegelijkertijd gedurende 11 seconden. Geen antwoord.
  7. Het gesprek van de manager eindigt. En het circuit stuurt een oproep naar de module "Een route selecteren uit 1c" Tevens een module geschreven voor de opdrachtgever. Hier werd de oproep gedurende 0 seconden verwerkt.
  8. Het circuit stuurt een oproep naar het spraakmenu "Basis met extra kiesnummers" De klant wachtte daar 31 seconden, er werd niet meer gebeld.
  9. Het schema stuurt een oproep naar de Groep "Secretarissen", waarbij de klant 12 seconden wachtte.
  10. In een groep worden 2 medewerkers tegelijk gebeld"Secretaris1"En"Secretaris2" en na 12 seconden antwoordt de medewerker "Secretaris2" Het antwoord op de oproep wordt gedupliceerd in bovenliggende oproepen. Het blijkt dat hij in de groep antwoordde: “Secretaris2", bij het bellen antwoordde het circuit"Secretaris2" en beantwoordde de oproep op de buitenlijn met "Secretaris2.

Het is het opslaan van informatie over elke bewerking en de nesting ervan die het mogelijk maakt om eenvoudig rapporten te maken. Een rapport over het spraakmenu helpt u erachter te komen hoeveel het helpt of hindert. Maak een rapport op van gemiste oproepen door medewerkers, rekening houdend met het feit dat het gesprek is onderschept en daarom niet als gemist wordt beschouwd, en rekening houdend met het feit dat het een groepsgesprek was en iemand anders eerder heeft opgenomen, waardoor het gesprek ook niet is gemist.

Met een dergelijke informatieopslag kunt u elke groep afzonderlijk bekijken en bepalen hoe effectief deze werkt, en een grafiek maken van beantwoorde en gemiste groepen per uur. U kunt ook controleren hoe nauwkeurig de verbinding met de verantwoordelijke manager is door de overdrachten te analyseren nadat u verbinding heeft gemaakt met de manager.

Je kunt ook vrij atypische onderzoeken doen, bijvoorbeeld hoe vaak nummers die niet in de database staan ​​het juiste toestel bellen of welk percentage van de uitgaande oproepen wordt doorgeschakeld naar een mobiele telefoon.

Het resultaat?

Voor het onderhoud van de telefooncentrale is geen specialist nodig, dat kan de meest gewone beheerder wel - in de praktijk getest.

Voor aanpassingen zijn geen specialisten met serieuze kwalificaties nodig; kennis van PHP is namelijk voldoende Er zijn al modules geschreven voor het SIP-protocol, en voor de wachtrij, en voor het bellen van een medewerker, en anderen. Er is een wrapper-klasse voor Asterisk. Om een ​​module te ontwikkelen, kan een programmeur kant-en-klare modules aanroepen (en dat zou op een goede manier ook moeten gebeuren). En kennis Asterisk zijn volkomen overbodig als de klant vraagt ​​om een ​​pagina met een nieuw rapport toe te voegen. Maar de praktijk leert dat hoewel externe programmeurs het wel aankunnen, ze zich onzeker voelen zonder documentatie en normale berichtgeving, dus er is nog steeds ruimte voor verbetering.

Modules kunnen:

  • nieuwe gespreksverwerkingsmogelijkheden creëren,
  • nieuwe blokken toevoegen aan de webinterface,
  • erven van een van de bestaande modules, functies opnieuw definiëren en vervangen, of gewoon een licht gewijzigde kopie zijn,
  • voeg uw instellingen toe aan de instellingensjabloon van andere modules en nog veel meer.

PBX-instellingen via API. Zoals hierboven beschreven worden alle instellingen in de database opgeslagen en uitgelezen op het moment van het gesprek, zodat u via de API alle PBX-instellingen kunt wijzigen. Bij het aanroepen van de API wordt de configuratie niet opnieuw aangemaakt en worden de modules niet opnieuw opgestart, het maakt dus niet uit hoeveel instellingen en medewerkers je hebt. API-verzoeken worden snel uitgevoerd en blokkeren elkaar niet.

De PBX slaat alle toetsbewerkingen op bij oproepen met duur (wacht/gesprek), nesting en in PBX-termen (medewerker, groep, buitenlijn, niet kanaal, nummer). Hierdoor kun je voor specifieke klanten verschillende rapporten bouwen en het meeste werk bestaat uit het creëren van een gebruiksvriendelijke interface.

De tijd zal leren wat er verder zal gebeuren. Er zijn nog steeds veel nuances die opnieuw moeten worden gedaan, er zijn nog steeds veel plannen, maar er is een jaar verstreken sinds de creatie van de derde versie en we kunnen al zeggen dat het idee werkt. Het grootste nadeel van versie 3 zijn de hardwarebronnen, maar meestal moet je hiervoor betalen voor het gemak van de ontwikkeling.

Bron: www.habr.com

Voeg een reactie