Berättelsen om ett projekt eller hur jag tillbringade 7 år med att skapa en PBX baserad på Asterisk och Php

Säkert många av er, som jag, hade en idé om att göra något unikt. I den här artikeln kommer jag att beskriva de tekniska problem och lösningar som jag var tvungen att möta när jag utvecklade telefonväxeln. Kanske kommer detta att hjälpa någon att bestämma sin egen idé, och någon att följa den upptrampade vägen, för jag har också haft nytta av pionjärernas erfarenhet.

Berättelsen om ett projekt eller hur jag tillbringade 7 år med att skapa en PBX baserad på Asterisk och Php

Idé och nyckelkrav

Och allt började helt enkelt med kärlek till Asterisk (ramverk för att bygga kommunikationsapplikationer), automatisering av telefoni och installationer FreePBX (webbgränssnitt för Asterisk). Om företagets behov var utan specifikation och föll inom kapaciteten FreePBX - allt är bra. Hela installationen skedde inom XNUMX timmar, företaget fick en konfigurerad växel, ett användarvänligt gränssnitt och kort utbildning samt support om så önskades.

Men de mest intressanta uppgifterna var icke-standardiserade och då var det inte så fantastiskt. Asterisk kan göra mycket, men för att hålla webbgränssnittet i fungerande skick var det nödvändigt att spendera många gånger mer tid. Så en liten detalj kan ta mycket längre tid än att installera resten av telefonväxeln. Och poängen är inte att det tar lång tid att skriva ett webbgränssnitt, utan snarare är poängen i de arkitektoniska funktionerna FreePBX. Arkitekturs tillvägagångssätt och metoder FreePBX lades ut vid tidpunkten för php4, och i det ögonblicket fanns det redan php5.6 där allt kunde göras enklare och bekvämare.

Den sista droppen var grafiska visningsritningar i form av ett diagram. När jag försökte bygga något sånt här för FreePBX, insåg jag att jag skulle behöva skriva om det betydligt och det skulle bli lättare att bygga något nytt.

De viktigaste kraven var:

  • enkel installation, intuitivt tillgänglig även för en nybörjare. Därför kräver inte företagen PBX-underhåll från vår sida,
  • enkel modifiering så att uppgifter löses i tillräcklig tid,
  • enkel integration med PBX. U FreePBX det fanns inget API för att ändra inställningar, dvs. Du kan till exempel inte skapa grupper eller röstmenyer från en tredjepartsapplikation, bara själva API:et Asterisk,
  • opensource - för programmerare är detta extremt viktigt för modifieringar för klienten.

Tanken med snabbare utveckling var att all funktionalitet skulle bestå av moduler i form av objekt. Alla objekt måste ha en gemensam överordnad klass, vilket innebär att namnen på alla huvudfunktioner redan är kända och därför finns det redan standardimplementationer. Objekt kommer att tillåta dig att dramatiskt minska antalet argument i form av associativa arrayer med strängnycklar, vilket du kan ta reda på i FreePBX Det var möjligt genom att undersöka hela funktionen och kapslade funktioner. När det gäller objekt kommer banal autokomplettering att visa alla egenskaper och i allmänhet förenkla livet många gånger om. Dessutom löser arv och omdefiniering redan många problem med modifieringar.

Nästa sak som saktade ner omarbetningstiden och var värd att undvika var dubbelarbete. Om det finns en modul som ansvarar för att ringa en anställd, så ska alla andra moduler som behöver skicka ett samtal till en anställd använda den, och inte skapa sina egna kopior. Så om du behöver ändra något måste du bara ändra på ett ställe och sökningen efter "hur det fungerar" bör utföras på ett ställe och inte genomsökas genom hela projektet.

Första versionen och första felen

Den första prototypen var klar inom ett år. Hela växeln, som planerat, var modulär och modulerna kunde inte bara lägga till ny funktionalitet för att behandla samtal, utan också ändra själva webbgränssnittet.

Berättelsen om ett projekt eller hur jag tillbringade 7 år med att skapa en PBX baserad på Asterisk och Php
Ja, idén att bygga en uppringningsplan i form av ett sådant schema är inte min, men det är väldigt bekvämt och jag gjorde detsamma för Asterisk.

Berättelsen om ett projekt eller hur jag tillbringade 7 år med att skapa en PBX baserad på Asterisk och Php

Genom att skriva en modul kunde programmerare redan:

  • skapa din egen funktionalitet för samtalsbehandling, som kan placeras på diagrammet, såväl som i menyn med element till vänster,
  • skapa dina egna sidor för webbgränssnittet och lägg till dina mallar på befintliga sidor (om sidutvecklaren har tillhandahållit detta),
  • lägg till dina inställningar på huvudinställningsfliken eller skapa din egen inställningsflik,
  • programmeraren kan ärva från en befintlig modul, ändra en del av funktionaliteten och registrera den under ett nytt namn eller ersätta den ursprungliga modulen.

Så här kan du till exempel skapa din egen röstmeny:

......
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 första komplexa implementeringarna gav den första stoltheten och de första besvikelserna. Jag var glad att det fungerade, att jag redan kunde återskapa huvuddragen FreePBX. Jag var glad att folk gillade idén med schemat. Det fanns fortfarande många alternativ för att förenkla utvecklingen, men redan vid den tidpunkten gjordes vissa av uppgifterna enklare.

API:et för att ändra PBX-konfigurationen var en besvikelse - resultatet blev inte alls vad vi ville ha. Jag tog samma princip som i FreePBX, genom att klicka på knappen Verkställ återskapas hela konfigurationen och modulerna startas om.

Det ser ut så här:

Berättelsen om ett projekt eller hur jag tillbringade 7 år med att skapa en PBX baserad på Asterisk och Php
*Dialplan är en regel (algoritm) genom vilken ett samtal behandlas.

Men med det här alternativet är det omöjligt att skriva ett normalt API för att ändra PBX-inställningarna. Först, operationen för att tillämpa ändringar på Asterisk för lång och resurskrävande.
För det andra kan du inte anropa två funktioner samtidigt, eftersom båda kommer att skapa konfigurationen.
För det tredje tillämpar den alla inställningar, inklusive de som gjorts av administratören.

I den här versionen, som i Askozia, det var möjligt att generera konfigurationen av endast ändrade moduler och starta om endast de nödvändiga modulerna, men dessa är alla halva åtgärder. Det var nödvändigt att ändra inställningen.

Andra versionen. Nosen utdragen svans fastnat

Tanken att lösa problemet var inte att återskapa konfigurationen och uppringningsplanen för Asterisk, men spara information till databasen och läs direkt från databasen medan samtalet behandlas. Asterisk Jag visste redan hur man läser konfigurationer från databasen, ändra bara värdet i databasen och nästa anrop kommer att behandlas med hänsyn till ändringarna, och funktionen var perfekt för att läsa dialplan-parametrar REALTIME_HASH.

Till slut behövdes det inte ens starta om Asterisk när du ändrade inställningarna och alla inställningar började tillämpas omedelbart på Asterisk.

Berättelsen om ett projekt eller hur jag tillbringade 7 år med att skapa en PBX baserad på Asterisk och Php

De enda ändringarna i uppringningsplanen är tillägg av anknytningsnummer och tips. Men det var små punktförändringar

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

Du kan enkelt lägga till eller ändra en rad i telefonplanen med hjälp av Ami (kontrollgränssnitt Asterisk) och ingen omstart av hela telefonplanen krävs.

Detta löste problemet med konfigurations-API:et. Du kan till och med direkt gå in i databasen och lägga till en ny grupp eller ändra till exempel uppringningstiden i fältet "uppringningstid" för gruppen och nästa samtal skulle redan pågå den angivna tiden (Detta är inte en rekommendation för åtgärd, eftersom vissa API-operationer kräver Ami samtal).

De första svåra implementeringarna gav återigen den första stoltheten och besvikelsen. Jag var glad att det fungerade. Databasen blev en kritisk länk, beroendet av disken ökade, det fanns fler risker, men allt fungerade stabilt och utan problem. Och viktigast av allt, nu kunde allt som kunde göras via webbgränssnittet göras genom API:t, och samma metoder användes. Dessutom blev webbgränssnittet av med knappen "tillämpa inställningar på PBX", som administratörer ofta glömde bort.

Besvikelsen var att utvecklingen blev mer komplicerad. Sedan den första versionen har PHP-språket genererat en uppringningsplan i språket Asterisk och det ser helt oläsligt ut, plus själva språket Asterisk för att skriva en telefonplan är det extremt primitivt.

Hur det såg ut:

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

I den andra versionen blev telefonplanen universell, den inkluderade alla möjliga bearbetningsalternativ beroende på parametrarna och dess storlek ökade avsevärt. Allt detta saktade ner utvecklingstiden avsevärt, och själva tanken på att det återigen var nödvändigt att störa telefonplanen gjorde mig ledsen.

Tredje versionen

Tanken att lösa problemet var inte att generera Asterisk dialplan från php och använd FastAGI och skriv alla bearbetningsregler i PHP själv. FastAGI det gör Asterisk, för att behandla samtalet, anslut till uttaget. Ta emot kommandon därifrån och skicka resultat. Därmed ligger dialplanens logik redan utanför gränserna Asterisk och kan skrivas på vilket språk som helst, i mitt fall i PHP.

Det var mycket försök och misstag. Det största problemet var att jag redan hade många klasser/filer. Det tog cirka 1,5 sekunder att skapa objekt, initiera dem och registrera varandra med varandra, och denna fördröjning per samtal är inget som kan ignoreras.

Initiering borde bara ha skett en gång och därför började sökandet efter en lösning med att skriva en tjänst i php med hjälp av Pthreads. Efter en veckas experiment lades det här alternativet på hyllan på grund av krångligheterna i hur denna förlängning fungerar. Efter en månads testning var jag också tvungen att överge asynkron programmering i PHP; jag behövde något enkelt, bekant för alla PHP-nybörjare, och många tillägg för PHP är synkrona.

Lösningen blev vår egen flertrådiga tjänst i C, som sammanställdes med PHPLIB. Den laddar alla ATS php-filer, väntar på att alla moduler ska initieras, lägger till en återuppringning till varandra och när allt är klart cachar det. När man frågar av FastAGI en ström skapas, en kopia från cachen av alla klasser och data reproduceras i den, och begäran skickas till php-funktionen.

Med denna lösning är tiden från att du skickar ett samtal till vår tjänst till det första kommandot Asterisk minskat från 1,5 s till 0,05 s och denna tid beror något på projektets storlek.

Berättelsen om ett projekt eller hur jag tillbringade 7 år med att skapa en PBX baserad på Asterisk och Php

Som ett resultat minskade tiden för utveckling av dialplan avsevärt, och jag kan uppskatta detta eftersom jag var tvungen att skriva om hela dialplanen för alla moduler i PHP. För det första borde metoder redan vara skrivna i php för att få ett objekt från databasen; de behövdes för visning i webbgränssnittet, och för det andra, och detta är huvudsaken, är det äntligen möjligt att bekvämt arbeta med strängar med siffror och arrayer med databas plus många PHP-tillägg.

För att bearbeta uppringningsplanen i modulklassen måste du implementera funktionen dialplanDynamicCall och argument pbxCallRequest kommer att innehålla ett objekt att interagera med Asterisk.

Berättelsen om ett projekt eller hur jag tillbringade 7 år med att skapa en PBX baserad på Asterisk och Php

Dessutom blev det möjligt att felsöka dialplanen (php har xdebug och det fungerar för vår tjänst), du kan flytta steg för steg genom att se värdena för variabler.

Samtalsdata

Alla analyser och rapporter kräver korrekt insamlad data, och detta PBX-block gick också igenom en hel del trial and error från den första till den tredje versionen. Ofta är samtalsdata ett tecken. Ett samtal = en inspelning: vem ringde, vem svarade, hur länge de pratade. I mer intressanta alternativ finns det ytterligare en skylt som indikerar vilken PBX-anställd som ringdes upp under samtalet. Men allt detta täcker bara en del av behoven.

De ursprungliga kraven var:

  • spara inte bara vem telefonväxeln ringde, utan också vem som svarade, eftersom det finns avlyssningar och detta måste beaktas vid analys av samtal,
  • tid innan kontakten med en anställd. I FreePBX och vissa andra växeln, anses samtalet besvarat så snart växeln tar upp telefonen. Men för röstmenyn behöver du redan lyfta luren, så alla samtal besvaras och väntetiden på svar blir 0-1 sekund. Därför beslutades det att spara inte bara tiden före ett svar, utan tiden innan du ansluter till nyckelmoduler (modulen själv sätter denna flagga. För närvarande är det "Anställd", "Extern linje"),
  • för en mer komplex uppringningsplan, när ett samtal går mellan olika grupper, var det nödvändigt att kunna undersöka varje element separat.

Det bästa alternativet visade sig vara när PBX-modulerna skickar information om sig själva vid samtal och i slutändan sparar informationen i form av ett träd.

Det ser ut så här:

Först, allmän information om samtalet (som alla andra - inget speciellt).

Berättelsen om ett projekt eller hur jag tillbringade 7 år med att skapa en PBX baserad på Asterisk och Php

  1. Fick ett samtal på en extern linje "För degen"kl. 05:55:52 från numret 89295671458 till numret 89999999999, till slut besvarades det av en anställd"Sekreterare 2» med nummer 104. Klienten väntade 60 sekunder och talade i 36 sekunder.
  2. anställd"Sekreterare 2"ringer till 112 och en anställd svarar"Manager1» efter 8 sekunder. De pratar i 14 sekunder.
  3. Kunden överförs till den anställde "chef1" där de fortsätter prata i ytterligare 13 sekunder

Men detta är toppen av isberget, för varje post kan du få en detaljerad samtalshistorik via telefonväxeln.

Berättelsen om ett projekt eller hur jag tillbringade 7 år med att skapa en PBX baserad på Asterisk och Php

All information presenteras som en kapsling av samtal:

  1. Fick ett samtal på en extern linje "För degen» vid 05:55:52 från numret 89295671458 till numret 89999999999.
  2. Klockan 05:55:53 skickar den yttre linjen ett samtal till den inkommande kretsen "testa»
  3. När du behandlar ett samtal enligt schemat, modulen "chefssamtal", där samtalet är 16 sekunder. Detta är en modul utvecklad för kunden.
  4. Modul "chefssamtal" skickar ett samtal till den anställde som är ansvarig för numret (klienten)"Manager1” och väntar 5 sekunder på svar. Chefen svarade inte.
  5. Modul "chefssamtal"sänder ett samtal till gruppen"CORP-chefer" Dessa är andra chefer i samma riktning (som sitter i samma rum) och väntar 11 sekunder på ett svar.
  6. Grupp "CORP-chefer"ringer anställda"Manager1, Manager2, Manager3"samtidigt i 11 sekunder. Inget svar.
  7. Chefens samtal avslutas. Och kretsen skickar ett samtal till modulen "Välja en rutt från 1c" Även en modul skriven för kunden. Här behandlades samtalet i 0 sekunder.
  8. Kretsen skickar ett samtal till röstmenyn "Grundläggande med extra uppringning" Klienten väntade där i 31 sekunder, det fanns ingen ytterligare uppringning.
  9. Systemet skickar ett samtal till gruppen "Sekreterare", där klienten väntade 12 sekunder.
  10. I en grupp kallas 2 anställda samtidigt "Sekreterare 1"Och"Sekreterare 2"och efter 12 sekunder svarar den anställde"Sekreterare 2" Svaret på samtalet dupliceras till föräldrasamtal. Det visar sig att han i gruppen svarade "Sekreterare 2", när du ringde svarade kretsen"Sekreterare 2" och svarade på samtalet på extern linje med "Sekreterare 2".

Det är sparandet av information om varje operation och deras kapsling som gör det möjligt att enkelt göra rapporter. En rapport om röstmenyn hjälper dig att ta reda på hur mycket den hjälper eller hindrar. Skapa en rapport om missade samtal av anställda, med hänsyn till att samtalet avlyssnades och därför inte anses vara missat, och med hänsyn till att det var ett gruppsamtal och någon annan svarade tidigare, vilket betyder att samtalet inte heller missades.

Sådan informationslagring gör att du kan ta varje grupp separat och bestämma hur effektivt den fungerar, och bygga en graf över besvarade och missade grupper per timme. Du kan också kontrollera hur korrekt kopplingen till ansvarig chef är genom att analysera överföringarna efter att du har kopplat till chefen.

Man kan också göra ganska atypiska studier, till exempel hur ofta nummer som inte finns i databasen slår rätt anknytning eller hur stor andel av utgående samtal som vidarekopplas till en mobiltelefon.

Resultatet?

En specialist krävs inte för att underhålla växeln, den vanligaste administratören kan göra det - testat i praktiken.

För ändringar behövs inte specialister med seriösa kvalifikationer, kunskaper i PHP är tillräckliga, eftersom Moduler har redan skrivits för SIP-protokollet, och för kön, och för att ringa en anställd och andra. Det finns en omslagsklass för Asterisk. För att utveckla en modul kan en programmerare (och på ett bra sätt bör) anropa färdiga moduler. Och kunskap Asterisk är helt onödiga om kunden ber att få lägga till en sida med någon ny rapport. Men praxis visar att även om tredjepartsprogrammerare klarar sig så känner de sig osäkra utan dokumentation och normal bevakning av kommentarer, så det finns fortfarande utrymme för förbättringar.

Moduler kan:

  • skapa nya samtalsbehandlingsmöjligheter,
  • lägg till nya block till webbgränssnittet,
  • ärva från någon av de befintliga modulerna, omdefiniera funktioner och ersätta dem, eller helt enkelt vara en något modifierad kopia,
  • lägg till dina inställningar i inställningsmallen för andra moduler och mycket mer.

PBX-inställningar via API. Som beskrivits ovan lagras alla inställningar i databasen och läses vid tidpunkten för samtalet, så du kan ändra alla PBX-inställningar via API:et. När man anropar API:et återskapas inte konfigurationen och modulerna startas inte om, därför spelar det ingen roll hur många inställningar och anställda man har. API-förfrågningar exekveras snabbt och blockerar inte varandra.

Växeln lagrar alla nyckeloperationer med samtal med varaktighet (väntar/samtal), kapsling och i växeltermer (anställd, grupp, extern linje, inte kanal, nummer). Detta gör att du kan bygga olika rapporter för specifika kunder och det mesta av arbetet är att skapa ett användarvänligt gränssnitt.

Tiden får utvisa vad som händer härnäst. Det finns fortfarande många nyanser som måste göras om, det finns fortfarande många planer, men ett år har gått sedan skapandet av den 3:e versionen och vi kan redan säga att idén fungerar. Den största nackdelen med version 3 är hårdvaruresurserna, men det är oftast det du måste betala för för att det ska vara lätt att utveckla.

Källa: will.com

Lägg en kommentar