Historien om ett prosjekt eller hvordan jeg brukte 7 år på å lage en PBX basert på Asterisk og Php

Sikkert mange av dere, som meg, hadde en idé om å gjøre noe unikt. I denne artikkelen vil jeg beskrive de tekniske problemene og løsningene jeg måtte møte da jeg utviklet hussentralen. Kanskje dette vil hjelpe noen til å bestemme sin egen idé, og noen til å følge den opptråkkede veien, for jeg har også hatt nytte av erfaringene til pionerer.

Historien om ett prosjekt eller hvordan jeg brukte 7 år på å lage en PBX basert på Asterisk og Php

Idé og nøkkelkrav

Og det hele startet rett og slett med kjærlighet til Asterisk (rammeverk for bygging av kommunikasjonsapplikasjoner), automatisering av telefoni og installasjoner GratisPBX (nettgrensesnitt for Asterisk). Hvis selskapets behov var uten spesifikasjoner og falt innenfor evnene GratisPBX – alt er flott. Hele installasjonen skjedde innen XNUMX timer, selskapet fikk en konfigurert PBX, et brukervennlig grensesnitt og kort opplæring pluss support om ønskelig.

Men de mest interessante oppgavene var ikke-standardiserte, og da var det ikke så fabelaktig. Asterisk kan gjøre mye, men for å holde webgrensesnittet i orden, var det nødvendig å bruke mange ganger mer tid. Så en liten detalj kan ta mye lengre tid enn å installere resten av hussentralen. Og poenget er ikke at det tar lang tid å skrive et nettgrensesnitt, men at poenget ligger i de arkitektoniske funksjonene GratisPBX. Arkitekturtilnærminger og metoder GratisPBX ble lagt ut på tidspunktet for php4, og i det øyeblikket var det allerede php5.6 der alt kunne gjøres enklere og mer praktisk.

Den siste dråpen var grafiske ruteplaner i form av et diagram. Da jeg prøvde å bygge noe slikt for GratisPBX, innså jeg at jeg måtte skrive den om betydelig, og det ville være lettere å bygge noe nytt.

De viktigste kravene var:

  • enkelt oppsett, intuitivt tilgjengelig selv for en nybegynner administrator. Dermed krever ikke bedrifter PBX-vedlikehold på vår side,
  • enkel modifikasjon slik at oppgaver løses i tilstrekkelig tid,
  • enkel integrasjon med PBX. U GratisPBX det var ingen API for å endre innstillinger, dvs. Du kan for eksempel ikke lage grupper eller talemenyer fra en tredjepartsapplikasjon, bare selve APIen Asterisk,
  • opensource - for programmerere er dette ekstremt viktig for modifikasjoner for klienten.

Tanken med raskere utvikling var å få all funksjonalitet til å bestå av moduler i form av objekter. Alle objekter måtte ha en felles overordnet klasse, noe som betyr at navnene på alle hovedfunksjonene allerede er kjent og derfor er det allerede standardimplementeringer. Objekter vil tillate deg å dramatisk redusere antall argumenter i form av assosiative arrays med strengnøkler, som du kan finne ut i GratisPBX Det var mulig ved å undersøke hele funksjonen og nestede funksjoner. Når det gjelder objekter, vil banal autofullføring vise alle egenskapene, og vil generelt forenkle livet mange ganger. I tillegg løser arv og redefinering allerede mange problemer med modifikasjoner.

Den neste tingen som bremset omarbeidstiden og var verdt å unngå var duplisering. Hvis det er en modul som er ansvarlig for å ringe en ansatt, bør alle andre moduler som trenger å ringe til en ansatt bruke den, og ikke lage sine egne kopier. Så hvis du trenger å endre noe, må du bare endre på ett sted, og søket etter "hvordan det fungerer" bør utføres på ett sted, og ikke søkes gjennom hele prosjektet.

Første versjon og første feil

Den første prototypen var klar i løpet av et år. Hele hussentralen, som planlagt, var modulær, og modulene kunne ikke bare legge til ny funksjonalitet for behandling av samtaler, men også endre selve webgrensesnittet.

Historien om ett prosjekt eller hvordan jeg brukte 7 år på å lage en PBX basert på Asterisk og Php
Ja, ideen om å bygge en telefonplan i form av en slik ordning er ikke min, men den er veldig praktisk og jeg gjorde det samme for Asterisk.

Historien om ett prosjekt eller hvordan jeg brukte 7 år på å lage en PBX basert på Asterisk og Php

Ved å skrive en modul kunne programmerere allerede:

  • lag din egen funksjonalitet for samtalebehandling, som kan plasseres på diagrammet, så vel som i menyen med elementer til venstre,
  • lag dine egne sider for nettgrensesnittet og legg til malene dine på eksisterende sider (hvis sideutvikleren har sørget for dette),
  • legg til innstillingene dine i hovedinnstillingsfanen eller lag din egen innstillingsfane,
  • programmereren kan arve fra en eksisterende modul, endre deler av funksjonaliteten og registrere den under et nytt navn eller erstatte den originale modulen.

Slik kan du for eksempel lage din egen stemmemeny:

......
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ørste komplekse implementeringene brakte den første stoltheten og de første skuffelsene. Jeg var glad for at det fungerte, at jeg allerede var i stand til å gjengi hovedtrekkene GratisPBX. Jeg var glad for at folk likte ideen med ordningen. Det var fortsatt mange alternativer for å forenkle utviklingen, men selv på den tiden ble noen av oppgavene allerede gjort enklere.

API-en for å endre PBX-konfigurasjonen var en skuffelse - resultatet ble ikke i det hele tatt det vi ønsket. Jeg tok samme prinsipp som i GratisPBX, ved å klikke på Bruk-knappen, gjenskapes hele konfigurasjonen og modulene startes på nytt.

Det ser slik ut:

Historien om ett prosjekt eller hvordan jeg brukte 7 år på å lage en PBX basert på Asterisk og Php
*Dialplan er en regel (algoritme) som en samtale behandles etter.

Men med dette alternativet er det umulig å skrive en vanlig API for å endre PBX-innstillingene. Først operasjonen med å bruke endringer på Asterisk for lang og ressurskrevende.
For det andre kan du ikke kalle opp to funksjoner samtidig, fordi begge vil opprette konfigurasjonen.
For det tredje bruker den alle innstillinger, inkludert de som er gjort av administratoren.

I denne versjonen, som i Askozia, var det mulig å generere konfigurasjonen av bare endrede moduler og starte bare de nødvendige modulene på nytt, men disse er alle halve tiltak. Det var nødvendig å endre tilnærmingen.

Andre versjon. Nesen trukket ut halen sitter fast

Ideen for å løse problemet var ikke å gjenskape konfigurasjonen og oppringingsplanen for Asterisk, men lagre informasjon til databasen og les fra databasen direkte mens du behandler samtalen. Asterisk Jeg visste allerede hvordan jeg skulle lese konfigurasjoner fra databasen, bare endre verdien i databasen og neste samtale vil bli behandlet med hensyn til endringene, og funksjonen var perfekt for å lese dialplan-parametere REALTIME_HASH.

Til slutt var det ikke nødvendig å starte på nytt Asterisk når du endret innstillingene og alle innstillinger begynte å bli brukt umiddelbart på Asterisk.

Historien om ett prosjekt eller hvordan jeg brukte 7 år på å lage en PBX basert på Asterisk og Php

De eneste endringene i telefonplanen er tillegg av internnummer og hint. Men dette var små punktforandringer

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 legge til eller endre en linje i telefonplanen ved å bruke Ami (kontrollgrensesnitt Asterisk) og ingen omstart av hele telefonplanen er nødvendig.

Dette løste problemet med konfigurasjons-API. Du kan til og med gå direkte inn i databasen og legge til en ny gruppe eller endre for eksempel oppringningstiden i "oppringingstid"-feltet for gruppen og neste samtale vil allerede vare den angitte tiden (Dette er ikke en anbefaling for handling, siden noen API-operasjoner krever Ami samtaler).

De første vanskelige implementeringene brakte igjen den første stoltheten og skuffelsen. Jeg var glad for at det fungerte. Databasen ble en kritisk kobling, avhengigheten av disken økte, det var flere risikoer, men alt fungerte stabilt og uten problemer. Og viktigst av alt, nå kunne alt som kunne gjøres gjennom webgrensesnittet gjøres gjennom API, og de samme metodene ble brukt. I tillegg ble nettgrensesnittet kvitt "bruk innstillinger på PBX"-knappen, som administratorer ofte glemte.

Skuffelsen var at utviklingen ble mer komplisert. Siden den første versjonen har PHP-språket generert en oppringningsplan i språket Asterisk og det ser helt uleselig ut, pluss selve språket Asterisk for å skrive en telefonplan er det ekstremt primitivt.

Slik så det 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 andre versjonen ble dialplanen universell, den inkluderte alle mulige behandlingsalternativer avhengig av parametrene og dens størrelse økte betydelig. Alt dette bremset utviklingstiden kraftig, og selve tanken på at det nok en gang var nødvendig å blande seg inn i telefonplanen gjorde meg trist.

Tredje versjon

Ideen for å løse problemet var ikke å generere Asterisk dialplan fra php og bruk FastAGI og skriv alle behandlingsregler i PHP selv. FastAGI den lar Asterisk, for å behandle anropet, koble til kontakten. Motta kommandoer derfra og sende resultater. Dermed er dialplanens logikk allerede utenfor grensene Asterisk og kan skrives på alle språk, i mitt tilfelle i PHP.

Det var mye prøving og feiling. Hovedproblemet var at jeg allerede hadde mange klasser/filer. Det tok omtrent 1,5 sekunder å lage objekter, initialisere dem og registrere hverandre med hverandre, og denne forsinkelsen per samtale er ikke noe som kan ignoreres.

Initialisering skulle bare ha skjedd én gang, og derfor begynte søket etter en løsning med å skrive en tjeneste i php ved hjelp av Pthreads. Etter en uke med eksperimentering ble dette alternativet skrinlagt på grunn av vanskelighetene ved hvordan denne utvidelsen fungerer. Etter en måned med testing måtte jeg også forlate asynkron programmering i PHP; jeg trengte noe enkelt, kjent for enhver PHP-nybegynner, og mange utvidelser for PHP er synkrone.

Løsningen var vår egen flertrådede tjeneste i C, som ble kompilert med PHPLIB. Den laster alle ATS php-filene, venter på at alle modulene skal initialiseres, legger til en tilbakeringing til hverandre, og når alt er klart, cacher det. Ved henvendelse av FastAGI en strøm opprettes, en kopi fra cachen av alle klasser og data blir reprodusert i den, og forespørselen sendes til php-funksjonen.

Med denne løsningen er tiden fra å sende et anrop til vår tjeneste til den første kommandoen Asterisk redusert fra 1,5 s til 0,05 s, og denne tiden avhenger litt av størrelsen på prosjektet.

Historien om ett prosjekt eller hvordan jeg brukte 7 år på å lage en PBX basert på Asterisk og Php

Som et resultat ble tiden for dialplan-utvikling redusert betydelig, og jeg kan sette pris på dette siden jeg måtte skrive om hele dialplanen til alle moduler i PHP. For det første bør metoder allerede være skrevet i php for å hente et objekt fra databasen; de var nødvendig for visning i webgrensesnittet, og for det andre, og dette er hovedsaken, er det endelig mulig å enkelt jobbe med strenger med tall og matriser med database pluss mange PHP-utvidelser.

For å behandle telefonplanen i modulklassen må du implementere funksjonen dialplanDynamicCall og argumentasjon pbxCallRequest vil inneholde et objekt å samhandle med Asterisk.

Historien om ett prosjekt eller hvordan jeg brukte 7 år på å lage en PBX basert på Asterisk og Php

I tillegg ble det mulig å feilsøke dialplanen (php har xdebug og det fungerer for vår tjeneste), du kan flytte trinn for trinn ved å se verdiene til variabler.

Anropsdata

Eventuelle analyser og rapporter krever korrekt innsamlede data, og denne PBX-blokken gikk også gjennom mye prøving og feiling fra den første til den tredje versjonen. Ofte er samtaledata et tegn. Ett anrop = ett opptak: hvem ringte, hvem svarte, hvor lenge de snakket. I mer interessante alternativer er det et ekstra skilt som indikerer hvilken PBX-ansatt som ble oppringt under samtalen. Men alt dette dekker bare deler av behovene.

De første kravene var:

  • spar ikke bare hvem PBX ringte, men også hvem som svarte, fordi det er avlyttinger, og dette må tas i betraktning når du analyserer anrop,
  • tid før du kobler til en ansatt. I GratisPBX og noen andre PBX-er, anses anropet som besvart så snart PBX-en tar telefonen. Men for talemenyen må du allerede ta telefonen, så alle anrop blir besvart og ventetiden på svar blir 0-1 sekund. Derfor ble det besluttet å spare ikke bare tiden før et svar, men tiden før tilkobling med nøkkelmoduler (modulen selv setter dette flagget. For øyeblikket er det "Ansatt", "Ekstern linje"),
  • for en mer kompleks telefonplan, når en samtale går mellom ulike grupper, var det nødvendig å kunne undersøke hvert element separat.

Det beste alternativet viste seg å være når PBX-modulene sender informasjon om seg selv på samtaler og til slutt lagrer informasjonen i form av et tre.

Det ser slik ut:

Først, generell informasjon om samtalen (som alle andre - ikke noe spesielt).

Historien om ett prosjekt eller hvordan jeg brukte 7 år på å lage en PBX basert på Asterisk og Php

  1. Mottok en samtale på en ekstern linje "For deigen"kl. 05:55:52 fra nummeret 89295671458 til nummeret 89999999999, til slutt ble det besvart av en ansatt"Sekretær 2» med nummer 104. Klienten ventet 60 sekunder og snakket i 36 sekunder.
  2. ansatt"Sekretær 2"ringer 112 og en ansatt svarer"Manager1» etter 8 sekunder. De snakker i 14 sekunder.
  3. Kunden overføres til den ansatte"leder1" hvor de fortsetter å snakke i ytterligere 13 sekunder

Men dette er toppen av isfjellet; for hver post kan du få en detaljert samtalehistorikk gjennom hussentralen.

Historien om ett prosjekt eller hvordan jeg brukte 7 år på å lage en PBX basert på Asterisk og Php

All informasjon presenteres som en nesting av samtaler:

  1. Mottok en samtale på en ekstern linje "For deigen» kl 05:55:52 fra nummeret 89295671458 til nummeret 89999999999.
  2. Kl. 05:55:53 sender eksternlinjen et anrop til innkommende krets "test»
  3. Når du behandler en samtale i henhold til ordningen, vil modulen "lederanrop", hvor samtalen er på 16 sekunder. Dette er en modul utviklet for kunden.
  4. modul "lederanrop"sender en samtale til den ansatte som er ansvarlig for nummeret (klienten)"Manager1” og venter 5 sekunder på svar. Lederen svarte ikke.
  5. modul "lederanrop"sender en samtale til gruppen"CORP-ledere" Dette er andre ledere i samme retning (sitter i samme rom) og venter i 11 sekunder på svar.
  6. Gruppe "CORP-ledere"ringer ansatte"Manager1, Manager2, Manager3"samtidig i 11 sekunder. Ingen svar.
  7. Lederens samtale avsluttes. Og kretsen sender et anrop til modulen "Velge en rute fra 1c" Også en modul skrevet for oppdragsgiver. Her ble samtalen behandlet i 0 sekunder.
  8. Kretsen sender et anrop til talemenyen "Grunnleggende med ekstra oppringing" Klienten ventet der i 31 sekunder, det var ingen ekstra oppringing.
  9. Ordningen sender en samtale til gruppen "Sekretærer", hvor klienten ventet 12 sekunder.
  10. I en gruppe blir 2 ansatte tilkalt samtidig "Sekretær 1"Og"Sekretær 2"og etter 12 sekunder svarer den ansatte"Sekretær 2" Svaret på anropet dupliseres til foreldresamtaler. Det viser seg at han i gruppen svarte "Sekretær 2", da ringte kretsen svarte"Sekretær 2" og svarte på anropet på eksternlinjen med "Sekretær 2'.

Det er lagring av informasjon om hver operasjon og deres hekking som vil gjøre det mulig å enkelt lage rapporter. En rapport på stemmemenyen vil hjelpe deg å finne ut hvor mye den hjelper eller hindrer. Konstruer en rapport om tapte samtaler av ansatte, ta hensyn til at samtalen ble avlyttet og derfor ikke anses som tapt, og ta hensyn til at det var et gruppeanrop, og noen andre svarte tidligere, noe som betyr at samtalen heller ikke ble tapt.

Slik informasjonslagring vil tillate deg å ta hver gruppe separat og bestemme hvor effektivt den fungerer, og bygge en graf over besvarte og tapte grupper etter time. Du kan også sjekke hvor nøyaktig koblingen til ansvarlig leder er ved å analysere overføringene etter kobling til leder.

Du kan også gjennomføre ganske atypiske studier, for eksempel hvor ofte numre som ikke er i databasen ringer riktig linjenummer eller hvor stor prosentandel av utgående samtaler som viderekobles til en mobiltelefon.

Resultatet?

En spesialist er ikke pålagt å vedlikeholde PBX, den mest vanlige administrator kan gjøre det - testet i praksis.

For modifikasjoner er det ikke nødvendig med spesialister med seriøse kvalifikasjoner; kunnskap om PHP er tilstrekkelig, fordi Det er allerede skrevet moduler for SIP-protokollen, og for køen, og for å ringe en ansatt og andre. Det er en innpakningsklasse for Asterisk. For å utvikle en modul kan en programmerer (og bør på en god måte) kalle ferdige moduler. Og kunnskap Asterisk er helt unødvendig hvis klienten ber om å legge til en side med en ny rapport. Men praksis viser at selv om tredjepartsprogrammerere kan klare seg, føler de seg usikre uten dokumentasjon og normal dekning av kommentarer, så det er fortsatt rom for forbedring.

Moduler kan:

  • opprette nye samtalebehandlingsmuligheter,
  • legge til nye blokker til nettgrensesnittet,
  • arve fra en hvilken som helst av de eksisterende modulene, omdefinere funksjoner og erstatte dem, eller bare være en litt modifisert kopi,
  • legg til innstillingene dine i innstillingsmalen for andre moduler og mye mer.

PBX-innstillinger via API. Som beskrevet ovenfor, lagres alle innstillinger i databasen og leses på tidspunktet for samtalen, slik at du kan endre alle PBX-innstillinger gjennom API. Når du kaller opp API, gjenskapes ikke konfigurasjonen og modulene startes ikke på nytt, derfor spiller det ingen rolle hvor mange innstillinger og ansatte du har. API-forespørsler utføres raskt og blokkerer ikke hverandre.

PBXen lagrer alle nøkkeloperasjoner med samtaler med varighet (venter/samtale), nesting og i PBX-termer (ansatt, gruppe, ekstern linje, ikke kanal, nummer). Dette lar deg bygge ulike rapporter for spesifikke kunder og mesteparten av arbeidet er å lage et brukervennlig grensesnitt.

Tiden vil vise hva som skjer videre. Det er fortsatt mange nyanser som må gjøres om, det er fortsatt mange planer, men et år har gått siden opprettelsen av den tredje versjonen, og vi kan allerede si at ideen fungerer. Den største ulempen med versjon 3 er maskinvareressursene, men det er vanligvis dette du må betale for for enkel utvikling.

Kilde: www.habr.com

Legg til en kommentar