La storia di un progetto o di come ho trascorso 7 anni a creare un PBX basato su Asterisk e Php

Sicuramente molti di voi, come me, hanno avuto l'idea di fare qualcosa di unico. In questo articolo descriverò i problemi tecnici e le soluzioni che ho dovuto affrontare durante lo sviluppo del centralino. Forse questo aiuterà qualcuno a decidere la propria idea e qualcuno a seguire il percorso già tracciato, perché ho beneficiato anche dell'esperienza dei pionieri.

La storia di un progetto o di come ho trascorso 7 anni a creare un PBX basato su Asterisk e Php

Idea e requisiti chiave

E tutto è iniziato semplicemente con l'amore per asterisco (quadro per applicazioni di comunicazione degli edifici), automazione della telefonia e degli impianti FreePBX (interfaccia web per asterisco). Se le esigenze dell’azienda fossero prive di specificità e rientrassero nelle capacità FreePBX - è tutto bellissimo. L'intera installazione è avvenuta nell'arco di XNUMX ore, l'azienda ha ricevuto un PBX configurato, un'interfaccia user-friendly e una breve formazione oltre al supporto se lo si desidera.

Ma i compiti più interessanti erano non standard e quindi non era così favoloso. asterisco può fare molto, ma per mantenere funzionante l'interfaccia web è stato necessario dedicare molto più tempo. Quindi un piccolo dettaglio potrebbe richiedere molto più tempo rispetto all'installazione del resto del PBX. E il punto non è che ci vuole molto tempo per scrivere un'interfaccia web, ma piuttosto nelle caratteristiche architettoniche FreePBX. Approcci e metodi dell'architettura FreePBX è stato realizzato ai tempi di php4, e in quel momento esisteva già php5.6 su cui tutto poteva essere reso più semplice e conveniente.

L'ultima goccia sono stati i dialplan grafici sotto forma di diagramma. Quando ho provato a costruire qualcosa di simile per FreePBX, mi sono reso conto che avrei dovuto riscriverlo in modo significativo e sarebbe stato più facile costruire qualcosa di nuovo.

I requisiti fondamentali erano:

  • configurazione semplice, accessibile in modo intuitivo anche a un amministratore inesperto. Pertanto, le aziende non richiedono la manutenzione del PBX da parte nostra,
  • facile modifica in modo che i compiti vengano risolti in tempo adeguato,
  • facilità di integrazione con il PBX. U FreePBX non esisteva un'API per modificare le impostazioni, ad es. Non è possibile, ad esempio, creare gruppi o menu vocali da un'applicazione di terze parti, ma solo dall'API stessa asterisco,
  • opensource: per i programmatori questo è estremamente importante per le modifiche per il cliente.

L'idea di uno sviluppo più rapido era che tutte le funzionalità fossero costituite da moduli sotto forma di oggetti. Tutti gli oggetti dovevano avere una classe genitore comune, il che significa che i nomi di tutte le funzioni principali sono già noti e quindi esistono già implementazioni predefinite. Gli oggetti ti permetteranno di ridurre drasticamente il numero di argomenti sotto forma di array associativi con chiavi di stringa, che puoi scoprire in FreePBX È stato possibile esaminando l'intera funzione e le funzioni nidificate. Nel caso degli oggetti, il banale completamento automatico mostrerà tutte le proprietà e in generale semplificherà la vita molte volte. Inoltre, l'ereditarietà e la ridefinizione risolvono già molti problemi con le modifiche.

La cosa successiva che ha rallentato i tempi di rilavorazione e che valeva la pena evitare è stata la duplicazione. Se esiste un modulo responsabile della chiamata a un dipendente, tutti gli altri moduli che devono inviare una chiamata a un dipendente dovrebbero utilizzarlo e non creare le proprie copie. Quindi, se hai bisogno di cambiare qualcosa, dovrai cambiare solo in un posto e la ricerca di "come funziona" dovrebbe essere effettuata in un posto e non cercata nell'intero progetto.

Prima versione e primi errori

Il primo prototipo era pronto entro un anno. L'intero PBX, come previsto, era modulare e i moduli potevano non solo aggiungere nuove funzionalità per l'elaborazione delle chiamate, ma anche modificare l'interfaccia web stessa.

La storia di un progetto o di come ho trascorso 7 anni a creare un PBX basato su Asterisk e Php
Sì, l'idea di costruire un dialplan sotto forma di uno schema del genere non è mia, ma è molto comoda e ho fatto lo stesso per asterisco.

La storia di un progetto o di come ho trascorso 7 anni a creare un PBX basato su Asterisk e Php

Scrivendo un modulo, i programmatori potrebbero già:

  • creare la propria funzionalità per l'elaborazione delle chiamate, che potrebbe essere posizionata nel diagramma, nonché nel menu degli elementi a sinistra,
  • crea le tue pagine per l'interfaccia web e aggiungi i tuoi modelli alle pagine esistenti (se lo sviluppatore della pagina lo ha previsto),
  • aggiungi le tue impostazioni alla scheda delle impostazioni principali o crea la tua scheda delle impostazioni,
  • il programmatore può ereditare da un modulo esistente, modificare parte della funzionalità e registrarla con un nuovo nome o sostituire il modulo originale.

Ad esempio, ecco come puoi creare il tuo menu vocale:

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

Le prime complesse implementazioni portarono i primi orgogliosi e le prime delusioni. Ero contento che funzionasse, che fossi già in grado di riprodurne le caratteristiche principali FreePBX. Sono stato contento che alla gente sia piaciuta l'idea dello schema. C'erano ancora molte opzioni per semplificare lo sviluppo, ma già allora alcuni compiti venivano semplificati.

L'API per modificare la configurazione del PBX è stata una delusione: il risultato non era affatto quello che volevamo. Ho preso lo stesso principio di FreePBX, facendo clic sul pulsante Applica, viene ricreata l'intera configurazione e i moduli vengono riavviati.

Sembra questo:

La storia di un progetto o di come ho trascorso 7 anni a creare un PBX basato su Asterisk e Php
*Dialplan è una regola (algoritmo) in base alla quale viene elaborata una chiamata.

Ma con questa opzione è impossibile scrivere una normale API per modificare le impostazioni del PBX. Innanzitutto, l'operazione di applicazione delle modifiche a asterisco troppo lungo e dispendioso in termini di risorse.
In secondo luogo, non puoi chiamare due funzioni contemporaneamente, perché entrambi creeranno la configurazione.
In terzo luogo, applica tutte le impostazioni, comprese quelle effettuate dall'amministratore.

In questa versione, come in Askozia, era possibile generare la configurazione solo dei moduli modificati e riavviare solo i moduli necessari, ma queste sono tutte mezze misure. Era necessario cambiare approccio.

Seconda versione. Il naso tirato fuori e la coda bloccata

L'idea per risolvere il problema non era quella di ricreare la configurazione e il dialplan di asterisco, ma salva le informazioni nel database e leggi direttamente dal database durante l'elaborazione della chiamata. asterisco Sapevo già come leggere le configurazioni dal database, basta cambiare il valore nel database e la chiamata successiva verrà elaborata tenendo conto delle modifiche, e la funzione era perfetta per leggere i parametri del dialplan REALTIME_HASH.

Alla fine non c’era nemmeno bisogno di riavviare asterisco quando si modificano le impostazioni e tutte le impostazioni iniziano ad essere applicate immediatamente asterisco.

La storia di un progetto o di come ho trascorso 7 anni a creare un PBX basato su Asterisk e Php

Le uniche modifiche al dialplan sono l'aggiunta di numeri di interno e suggerimenti. Ma si trattava di piccoli cambiamenti puntuali

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

Puoi facilmente aggiungere o modificare una linea nel dialplan utilizzando Ami (interfaccia di controllo asterisco) e non è richiesto il riavvio dell'intero dialplan.

Ciò ha risolto il problema con l'API di configurazione. Potresti anche andare direttamente nel database e aggiungere un nuovo gruppo o modificare, ad esempio, il tempo di connessione nel campo "dialtime" per il gruppo e la chiamata successiva durerebbe già il tempo specificato (questa non è una raccomandazione per azione, poiché alcune operazioni API richiedono Ami chiamate).

Le prime difficili implementazioni hanno portato ancora una volta orgoglio e delusione. Sono stato contento che abbia funzionato. Il database è diventato un collegamento critico, la dipendenza dal disco è aumentata, c'erano più rischi, ma tutto ha funzionato stabilmente e senza problemi. E, cosa più importante, ora tutto ciò che poteva essere fatto tramite l'interfaccia web poteva essere fatto tramite l'API e venivano utilizzati gli stessi metodi. Inoltre, l'interfaccia web ha eliminato il pulsante “applica impostazioni al PBX”, di cui gli amministratori spesso si dimenticavano.

La delusione è stata che lo sviluppo è diventato più complicato. Fin dalla prima versione, il linguaggio PHP ha generato un dialplan nel linguaggio asterisco e sembra completamente illeggibile, così come la lingua stessa asterisco per scrivere un dialplan è estremamente primitivo.

Come appariva:

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

Nella seconda versione, il dialplan è diventato universale, includeva tutte le possibili opzioni di elaborazione a seconda dei parametri e le sue dimensioni sono aumentate in modo significativo. Tutto ciò ha rallentato notevolmente i tempi di sviluppo, e il solo pensiero che ancora una volta fosse necessario interferire con il dialplan mi ha rattristato.

Terza versione

L'idea per risolvere il problema non era quella di generare asterisco dialplan da php e utilizzare VeloceAGI e scrivere tutte le regole di elaborazione nello stesso PHP. VeloceAGI permette asterisco, per elaborare la chiamata, collegarsi alla presa. Ricevi comandi da lì e invia risultati. Pertanto, la logica del dialplan è già fuori dai confini asterisco e può essere scritto in qualsiasi linguaggio, nel mio caso in PHP.

Ci sono stati molti tentativi ed errori. Il problema principale era che avevo già molte classi/file. Ci sono voluti circa 1,5 secondi per creare oggetti, inizializzarli e registrarli tra loro, e questo ritardo per chiamata non è qualcosa che può essere ignorato.

L'inizializzazione sarebbe dovuta avvenire una sola volta e quindi la ricerca di una soluzione è iniziata con la scrittura di un servizio in php using Discussioni. Dopo una settimana di sperimentazione, questa opzione è stata accantonata a causa della complessità del funzionamento di questa estensione. Dopo un mese di test, ho dovuto abbandonare anche la programmazione asincrona in PHP; avevo bisogno di qualcosa di semplice, familiare a qualsiasi principiante di PHP, e molte estensioni per PHP sono sincrone.

La soluzione è stata il nostro servizio multi-thread in C, che è stato compilato con PHPLIB. Carica tutti i file php ATS, attende l'inizializzazione di tutti i moduli, aggiunge una richiamata l'uno all'altro e, quando tutto è pronto, lo memorizza nella cache. Quando si chiede a VeloceAGI viene creato uno stream, al suo interno viene riprodotta una copia dalla cache di tutte le classi e i dati e la richiesta viene passata alla funzione php.

Con questa soluzione il tempo passa dall'invio di una chiamata al nostro servizio al primo comando asterisco è diminuito da 1,5 a 0,05 e questo tempo dipende leggermente dalle dimensioni del progetto.

La storia di un progetto o di come ho trascorso 7 anni a creare un PBX basato su Asterisk e Php

Di conseguenza, il tempo per lo sviluppo del dialplan è stato ridotto in modo significativo e posso apprezzarlo poiché ho dovuto riscrivere l'intero dialplan di tutti i moduli in PHP. In primo luogo, i metodi dovrebbero già essere scritti in php per ottenere un oggetto dal database, erano necessari per la visualizzazione nell'interfaccia web e, in secondo luogo, e questa è la cosa principale, è finalmente possibile lavorare comodamente con stringhe con numeri e matrici con database più molte estensioni PHP.

Per elaborare il dialplan nella classe del modulo è necessario implementare la funzione dialplanDynamicCall e argomento pbxCallRequest conterrà un oggetto con cui interagire asterisco.

La storia di un progetto o di come ho trascorso 7 anni a creare un PBX basato su Asterisk e Php

Inoltre, è diventato possibile eseguire il debug del dialplan (php ha xdebug e funziona per il nostro servizio), puoi muoverti passo dopo passo visualizzando i valori delle variabili.

Dati della chiamata

Qualsiasi analisi e report richiede dati raccolti correttamente e anche questo blocco PBX ha attraversato molti tentativi ed errori dalla prima alla terza versione. Spesso i dati delle chiamate sono un segno. Una chiamata = una registrazione: chi ha chiamato, chi ha risposto, per quanto tempo hanno parlato. Nelle opzioni più interessanti è presente un segno aggiuntivo che indica quale dipendente del PBX è stato chiamato durante la chiamata. Ma tutto ciò copre solo una parte dei bisogni.

I requisiti iniziali erano:

  • salva non solo chi ha chiamato il PBX, ma anche chi ha risposto, perché ci sono intercettazioni e bisognerà tenerne conto nell'analisi delle chiamate,
  • tempo prima di connettersi con un dipendente. In FreePBX e alcuni altri PBX, la chiamata viene considerata con risposta non appena il PBX risponde al telefono. Ma per il menu vocale è già necessario sollevare il telefono, quindi a tutte le chiamate viene data risposta e il tempo di attesa per una risposta diventa 0-1 secondo. Pertanto, si è deciso di risparmiare non solo il tempo prima di una risposta, ma anche il tempo prima di connettersi con i moduli chiave (il modulo stesso imposta questo flag. Attualmente è “Dipendente”, “Linea esterna”),
  • per un dialplan più complesso, quando una chiamata viaggia tra gruppi diversi, era necessario poter esaminare ogni elemento separatamente.

L'opzione migliore si è rivelata quando i moduli PBX inviano informazioni su se stessi durante le chiamate e alla fine salvano le informazioni sotto forma di albero.

Sembra così:

Innanzitutto, informazioni generali sulla chiamata (come tutti gli altri, niente di speciale).

La storia di un progetto o di come ho trascorso 7 anni a creare un PBX basato su Asterisk e Php

  1. Ricevuta una chiamata su una linea esterna "Per la pasta"alle 05:55:52 dal numero 89295671458 al numero 89999999999, alla fine ha risposto un dipendente"Segretario2» con il numero 104. Il cliente ha aspettato 60 secondi e ha parlato 36 secondi.
  2. Dipendente "Segretario2"fa una chiamata al 112 e risponde un dipendente"Gestore1» dopo 8 secondi. Parlano per 14 secondi.
  3. Il Cliente viene trasferito al Dipendente "direttore1" dove continuano a parlare per altri 13 secondi

Ma questa è solo la punta dell'iceberg: per ogni registrazione è possibile ottenere uno storico dettagliato delle chiamate tramite il PBX.

La storia di un progetto o di come ho trascorso 7 anni a creare un PBX basato su Asterisk e Php

Tutte le informazioni sono presentate come un annidamento di chiamate:

  1. Ricevuta una chiamata su una linea esterna "Per la pasta» alle 05:55:52 dal numero 89295671458 al numero 89999999999.
  2. Alle 05:55:53 la linea urbana invia una chiamata al circuito Entrante"test»
  3. Quando si elabora una chiamata secondo lo schema, il modulo “chiamata del direttore", in cui la chiamata dura 16 secondi. Questo è un modulo sviluppato per il cliente.
  4. Modulo "chiamata del direttore" invia una chiamata al dipendente responsabile del numero (cliente) "Gestore1" e attende 5 secondi per una risposta. Il direttore non ha risposto.
  5. Modulo "chiamata del direttore"invia una chiamata al gruppo"dirigenti CORP" Questi sono altri manager della stessa direzione (seduti nella stessa stanza) e aspettano 11 secondi per una risposta.
  6. Gruppo "dirigenti CORP"chiama i dipendenti"Gestore1, Gestore2, Gestore3"contemporaneamente per 11 secondi. Nessuna risposta.
  7. La chiamata del manager termina. E il circuito invia una chiamata al modulo "Selezione di un percorso da 1c" Anche un modulo scritto per il cliente. Qui la chiamata è stata elaborata per 0 secondi.
  8. Il circuito invia una chiamata al menu vocale "Base con composizione aggiuntiva" Il cliente ha aspettato lì per 31 secondi, non è stata effettuata alcuna composizione aggiuntiva.
  9. Lo schema lancia una chiamata al Gruppo"Segretari", dove il client ha aspettato 12 secondi.
  10. In un gruppo vengono chiamati contemporaneamente 2 dipendenti "Segretario1"E"Segretario2" e dopo 12 secondi il dipendente risponde "Segretario2" La risposta alla chiamata viene duplicata nelle chiamate dei genitori. Si scopre che nel gruppo ha risposto “Segretario2", alla chiamata il circuito ha risposto "Segretario2" e ho risposto alla chiamata sulla linea esterna con "Segretario2'.

È il salvataggio delle informazioni su ciascuna operazione e il loro annidamento che consentirà di creare semplicemente report. Un rapporto sul menu vocale ti aiuterà a scoprire quanto aiuta o ostacola. Costruire un report sulle chiamate perse dei dipendenti, tenendo conto che la chiamata è stata intercettata e quindi non è considerata persa, e tenendo conto che si trattava di una chiamata di gruppo e qualcun altro ha risposto prima, il che significa che anche la chiamata non è stata persa.

Tale memorizzazione delle informazioni ti consentirà di prendere ciascun gruppo separatamente e di determinarne l'efficacia, nonché di creare un grafico dei gruppi con risposta e di quelli persi per ora. Puoi anche verificare la precisione della connessione con il gestore responsabile analizzando i trasferimenti dopo esserti connesso al gestore.

È inoltre possibile condurre studi piuttosto atipici, ad esempio sulla frequenza con cui numeri che non sono presenti nel database compongono l'interno corretto o quale percentuale di chiamate in uscita viene inoltrata a un telefono cellulare.

Il risultato?

Per la manutenzione del PBX non è necessario che uno specialista si occupi della manutenzione del PBX, può farlo anche l'amministratore più ordinario: testato nella pratica.

Per le modifiche non sono necessari specialisti con qualifiche serie; è sufficiente la conoscenza di PHP, perché Sono già stati scritti moduli per il protocollo SIP, per la coda, per chiamare un dipendente e altri. Esiste una classe wrapper per asterisco. Per sviluppare un modulo, un programmatore può (e in senso buono dovrebbe) richiamare moduli già pronti. E conoscenza asterisco sono completamente inutili se il cliente chiede di aggiungere una pagina con qualche nuovo report. Ma la pratica dimostra che, sebbene i programmatori di terze parti possano farcela, si sentono insicuri senza la documentazione e la normale copertura dei commenti, quindi c'è ancora spazio per miglioramenti.

I moduli possono:

  • creare nuove funzionalità di elaborazione delle chiamate,
  • aggiungere nuovi blocchi all'interfaccia web,
  • ereditare da uno qualsiasi dei moduli esistenti, ridefinire le funzioni e sostituirlo, o semplicemente essere una copia leggermente modificata,
  • aggiungi le tue impostazioni al modello di impostazioni di altri moduli e molto altro ancora.

Impostazioni PBX tramite API. Come descritto sopra, tutte le impostazioni vengono archiviate nel database e lette al momento della chiamata, quindi è possibile modificare tutte le impostazioni del PBX tramite l'API. Quando si chiama l'API, la configurazione non viene ricreata e i moduli non vengono riavviati, quindi non importa quante impostazioni e dipendenti hai. Le richieste API vengono eseguite rapidamente e non si bloccano a vicenda.

Il PBX memorizza tutte le operazioni chiave con chiamate con durata (attesa/conversazione), annidamento e in termini PBX (dipendente, gruppo, linea urbana, non canale, numero). Ciò ti consente di creare vari report per clienti specifici e la maggior parte del lavoro consiste nel creare un'interfaccia intuitiva.

Il tempo dirà cosa accadrà dopo. Ci sono ancora molte sfumature da rifare, ci sono ancora molti progetti, ma è passato un anno dalla creazione della 3a versione e possiamo già dire che l'idea sta funzionando. Lo svantaggio principale della versione 3 sono le risorse hardware, ma di solito questo è ciò che devi pagare per facilitare lo sviluppo.

Fonte: habr.com

Aggiungi un commento