Historia jednego projektu, czyli jak spędziłem 7 lat tworząc centralę PBX w oparciu o Asterisk i Php

Z pewnością wielu z Was, podobnie jak ja, miało pomysł, aby zrobić coś wyjątkowego. W tym artykule opiszę problemy techniczne i rozwiązania, z którymi musiałem się zmierzyć podczas opracowywania centrali PBX. Być może komuś to pomoże zdecydować się na własny pomysł, a komuś pójść wydeptaną ścieżką, bo i ja korzystałem z doświadczeń pionierów.

Historia jednego projektu, czyli jak spędziłem 7 lat tworząc centralę PBX w oparciu o Asterisk i Php

Pomysł i kluczowe wymagania

A wszystko zaczęło się po prostu od miłości do Gwiazdka (framework dla aplikacji komunikacyjnych budynków), automatyka telefonii i instalacji Bezpłatna centrala PBX (interfejs sieciowy dla Gwiazdka). Gdyby potrzeby firmy były bez konkretów i mieściły się w możliwościach Bezpłatna centrala PBX - wszystko w porządku. Cała instalacja odbyła się w ciągu XNUMX godzin, firma otrzymała skonfigurowaną centralę PBX, przyjazny interfejs użytkownika oraz krótkie szkolenie i wsparcie w razie potrzeby.

Ale najciekawsze zadania były niestandardowe i wtedy nie było już tak bajecznie. Gwiazdka może wiele, ale aby utrzymać interfejs WWW w dobrym stanie, trzeba było poświęcić wiele razy więcej czasu. Zatem drobny szczegół może zająć znacznie więcej czasu niż instalacja reszty centrali PBX. I nie chodzi o to, że napisanie interfejsu internetowego zajmuje dużo czasu, ale raczej o cechy architektoniczne Bezpłatna centrala PBX. Podejścia i metody architektury Bezpłatna centrala PBX został opracowany w czasach php4, a w tym momencie istniał już php5.6, w którym wszystko mogło być prostsze i wygodniejsze.

Ostatnią kroplą były graficzne tarcze w formie diagramu. Kiedy próbowałem zbudować coś takiego dla Bezpłatna centrala PBX, zdałem sobie sprawę, że będę musiał go znacznie przepisać i łatwiej będzie zbudować coś nowego.

Kluczowymi wymaganiami były:

  • prosta konfiguracja, intuicyjnie dostępna nawet dla początkującego administratora. Dzięki temu firmy nie wymagają konserwacji central PBX z naszej strony,
  • łatwą modyfikację tak, aby zadania zostały rozwiązane w odpowiednim czasie,
  • łatwość integracji z centralą PBX. U Bezpłatna centrala PBX nie było API umożliwiającego zmianę ustawień tj. Nie można na przykład tworzyć grup ani menu głosowych z poziomu aplikacji innej firmy, a jedynie poprzez sam interfejs API Gwiazdka,
  • opensource - dla programistów jest to niezwykle ważne przy modyfikacjach dla klienta.

Ideą szybszego rozwoju było to, aby cała funkcjonalność składała się z modułów w formie obiektów. Wszystkie obiekty musiały mieć wspólną klasę nadrzędną, co oznacza, że ​​nazwy wszystkich głównych funkcji są już znane i dlatego istnieją już domyślne implementacje. Obiekty pozwolą radykalnie zmniejszyć liczbę argumentów w postaci tablic asocjacyjnych z kluczami łańcuchowymi, o czym możesz się dowiedzieć w Bezpłatna centrala PBX Było to możliwe dzięki zbadaniu całej funkcji i funkcji zagnieżdżonych. W przypadku obiektów banalne autouzupełnianie pokaże wszystkie właściwości i w ogóle wielokrotnie uprości życie. Ponadto dziedziczenie i redefinicja rozwiązują już wiele problemów związanych z modyfikacjami.

Następną rzeczą, która spowalniała czas przeróbek i której warto było unikać, było powielanie. Jeśli istnieje moduł odpowiedzialny za wybieranie numeru do pracownika, to wszystkie pozostałe moduły, które mają wykonać połączenie do pracownika, powinny z niego korzystać, a nie tworzyć własne kopie. Jeśli więc trzeba coś zmienić, to trzeba będzie to zmienić tylko w jednym miejscu i szukanie „jak to działa” powinno odbywać się w jednym miejscu, a nie przeszukiwać cały projekt.

Pierwsza wersja i pierwsze błędy

Pierwszy prototyp był gotowy w ciągu roku. Cała centrala zgodnie z planem była modułowa, a moduły mogły nie tylko dodawać nowe funkcjonalności do obsługi połączeń, ale także zmieniać sam interfejs WWW.

Historia jednego projektu, czyli jak spędziłem 7 lat tworząc centralę PBX w oparciu o Asterisk i Php
Tak, pomysł zbudowania dialplanu w formie takiego schematu nie jest mój, ale jest bardzo wygodny i zrobiłem to samo dla Gwiazdka.

Historia jednego projektu, czyli jak spędziłem 7 lat tworząc centralę PBX w oparciu o Asterisk i Php

Pisząc moduł, programiści mogli już:

  • stworzyć własną funkcjonalność obsługi połączeń, którą można umieścić na schemacie, a także w menu elementów po lewej stronie,
  • twórz własne strony dla interfejsu WWW i dodawaj swoje szablony do istniejących stron (jeśli twórca strony to przewidział),
  • dodaj swoje ustawienia do głównej zakładki ustawień lub utwórz własną zakładkę ustawień,
  • programista może odziedziczyć z istniejącego modułu, zmienić część funkcjonalności i zarejestrować go pod nową nazwą lub zastąpić oryginalny moduł.

Na przykład w ten sposób możesz stworzyć własne menu głosowe:

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

Pierwsze skomplikowane wdrożenia przyniosły pierwszą dumę i pierwsze rozczarowania. Cieszyłem się, że zadziałało, że udało mi się już odtworzyć główne cechy Bezpłatna centrala PBX. Cieszyłem się, że ludziom spodobał się pomysł programu. Nadal istniało wiele możliwości uproszczenia rozwoju, ale już w tamtym czasie niektóre zadania były już ułatwiane.

API umożliwiające zmianę konfiguracji centrali PBX było rozczarowaniem – efekt zupełnie nie był taki, jakiego oczekiwaliśmy. Przyjąłem tę samą zasadę co w Bezpłatna centrala PBX, po kliknięciu przycisku Zastosuj cała konfiguracja zostanie odtworzona i moduły uruchomione ponownie.

To wygląda tak:

Historia jednego projektu, czyli jak spędziłem 7 lat tworząc centralę PBX w oparciu o Asterisk i Php
*Dialplan to reguła (algorytm), według której realizowane jest połączenie.

Ale przy tej opcji nie da się napisać normalnego API do zmiany ustawień centrali. Po pierwsze, operacja stosowania zmian do Gwiazdka zbyt długie i wymagające dużych zasobów.
Po drugie, nie można wywołać dwóch funkcji jednocześnie, ponieważ oba utworzą konfigurację.
Po trzecie, stosuje wszystkie ustawienia, także te wprowadzone przez administratora.

W tej wersji, podobnie jak w Askozia, możliwe było wygenerowanie konfiguracji tylko zmienionych modułów i ponowne uruchomienie tylko niezbędnych modułów, ale to wszystko półśrodki. Konieczna była zmiana podejścia.

Druga wersja. Nos wyciągnięty, ogon utknięty

Pomysł rozwiązania problemu nie polegał na odtworzeniu konfiguracji i planu wybierania numerów Gwiazdka, ale zapisz informacje w bazie danych i odczytaj je bezpośrednio z bazy danych podczas przetwarzania połączenia. Gwiazdka Wiedziałem już jak odczytać konfiguracje z bazy danych, wystarczy zmienić wartość w bazie danych i kolejne wywołanie zostanie zrealizowane z uwzględnieniem zmian, a funkcja świetnie sprawdziła się do odczytu parametrów dialplanu REALTIME_HASH.

Ostatecznie nie było potrzeby nawet restartować Gwiazdka przy zmianie ustawień i wszystkie ustawienia zaczęły być natychmiast stosowane Gwiazdka.

Historia jednego projektu, czyli jak spędziłem 7 lat tworząc centralę PBX w oparciu o Asterisk i Php

Jedyne zmiany w planie wybierania to dodanie numerów wewnętrznych i wskazówki. Były to jednak drobne, punktowe zmiany

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

Możesz łatwo dodać lub zmienić linię w planie wybierania za pomocą Ami (interfejs sterujący Gwiazdka) i nie jest wymagane ponowne uruchomienie całego planu wybierania.

To rozwiązało problem z interfejsem API konfiguracji. Można nawet bezpośrednio wejść do bazy danych i dodać nową grupę lub zmienić np. godzinę połączenia w polu „godzina wybierania” dla danej grupy, a kolejne połączenie będzie trwało już określony czas (nie jest to zalecenie dla akcji, ponieważ wymagają tego niektóre operacje API Ami rozmowy).

Pierwsze trudne wdrożenia znów przyniosły pierwszą dumę i rozczarowanie. Cieszyłem się, że to zadziałało. Baza danych stała się ogniwem krytycznym, wzrosła zależność od dysku, było więcej zagrożeń, ale wszystko działało stabilnie i bez problemów. A co najważniejsze, teraz wszystko, co można było zrobić za pośrednictwem interfejsu internetowego, można było zrobić za pośrednictwem API i zastosowano te same metody. Dodatkowo w interfejsie WWW pozbyliśmy się przycisku „zastosuj ustawienia do centrali”, o którym często zapominali administratorzy.

Rozczarowaniem było to, że rozwój stał się bardziej skomplikowany. Od pierwszej wersji język PHP generował plan wybierania numerów w tym języku Gwiazdka i wygląda zupełnie nieczytelnie, podobnie jak sam język Gwiazdka do pisania planu wybierania jest niezwykle prymitywny.

Jak to wyglądało:

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

W drugiej wersji dialplan stał się uniwersalny, obejmował wszystkie możliwe opcje przetwarzania w zależności od parametrów, a jego rozmiar znacznie się zwiększył. Wszystko to znacznie spowolniło czas rozwoju, a sama myśl, że po raz kolejny trzeba było ingerować w dialplan, napawała mnie smutkiem.

Trzecia wersja

Pomysł rozwiązania problemu nie polegał na generowaniu Gwiazdka dialplan z php i użyj SzybkiAGI i napisz wszystkie reguły przetwarzania w samym PHP. SzybkiAGI pozwala on Gwiazdka, aby przetworzyć połączenie, podłącz do gniazdka. Otrzymuj stamtąd polecenia i wysyłaj wyniki. Zatem logika planu wybierania jest już poza granicami Gwiazdka i może być napisany w dowolnym języku, w moim przypadku w PHP.

Było wiele prób i błędów. Głównym problemem było to, że miałem już wiele klas/plików. Utworzenie obiektów, ich inicjalizacja i wzajemna rejestracja zajmowały około 1,5 sekundy, a tego opóźnienia na połączenie nie można zignorować.

Inicjalizacja powinna nastąpić tylko raz, dlatego poszukiwania rozwiązania rozpoczęły się od napisania usługi w php przy użyciu Pwątki. Po tygodniu eksperymentów opcja ta została odłożona na półkę ze względu na zawiłości działania tego rozszerzenia. Po miesiącu testów musiałem też porzucić programowanie asynchroniczne w PHP; potrzebowałem czegoś prostego, znanego każdemu początkującemu PHP, a wiele rozszerzeń PHP jest synchronicznych.

Rozwiązaniem była nasza własna wielowątkowa usługa w języku C, z którą została skompilowana PHPLIB. Ładuje wszystkie pliki php ATS, czeka na inicjalizację wszystkich modułów, dodaje do siebie wywołania zwrotne, a gdy wszystko jest gotowe, buforuje je. Kiedy pytasz przez SzybkiAGI tworzony jest strumień, odtwarzana jest w nim kopia z pamięci podręcznej wszystkich klas i danych, a żądanie przekazywane jest do funkcji php.

Dzięki temu rozwiązaniu czas od wysłania połączenia do naszego serwisu do pierwszego polecenia Gwiazdka spadł z 1,5 s do 0,05 s i czas ten zależy w niewielkim stopniu od wielkości projektu.

Historia jednego projektu, czyli jak spędziłem 7 lat tworząc centralę PBX w oparciu o Asterisk i Php

W rezultacie czas tworzenia dialplanu został znacznie skrócony, co mogę docenić, ponieważ musiałem przepisać cały plan dialplanu wszystkich modułów w PHP. Po pierwsze, metody powinny być już napisane w php, aby uzyskać obiekt z bazy danych; były potrzebne do wyświetlenia w interfejsie WWW, a po drugie, i to najważniejsze, w końcu można wygodnie pracować z ciągami znaków z liczbami i tablicami z bazą danych i wieloma rozszerzeniami PHP.

Aby przetworzyć plan wybierania w klasie modułu, musisz zaimplementować funkcję dialplanDynamicCall i argumentacja pbxCallRequest będzie zawierać obiekt, z którym można wchodzić w interakcję Gwiazdka.

Historia jednego projektu, czyli jak spędziłem 7 lat tworząc centralę PBX w oparciu o Asterisk i Php

Dodatkowo możliwe stało się debugowanie dialplanu (php ma xdebug i działa dla naszego serwisu), można poruszać się krok po kroku przeglądając wartości zmiennych.

Dane połączeń

Wszelkie analizy i raporty wymagają poprawnie zebranych danych, a ten blok PBX również przeszedł wiele prób i błędów od pierwszej do trzeciej wersji. Często dane połączeń są znakiem. Jedno połączenie = jedno nagranie: kto dzwonił, kto odebrał, jak długo rozmawiali. W ciekawszych opcjach pojawia się dodatkowy znak wskazujący, do którego pracownika centrali dzwoniono podczas rozmowy. Ale to wszystko pokrywa tylko część potrzeb.

Początkowe wymagania były następujące:

  • zapisz nie tylko to, do kogo dzwoniła centrala, ale także kto odebrał, ponieważ zdarzają się przechwyty i trzeba to wziąć pod uwagę analizując rozmowy,
  • czas przed połączeniem się z pracownikiem. W Bezpłatna centrala PBX i niektórych innych central PBX, połączenie uważa się za odebrane w momencie, gdy centrala PBX odbierze telefon. Ale w przypadku menu głosowego musisz już podnieść telefon, aby wszystkie połączenia zostały odebrane, a czas oczekiwania na odpowiedź wynosi 0-1 sekundy. Dlatego zdecydowano się zaoszczędzić nie tylko czas przed odpowiedzią, ale także czas przed połączeniem z kluczowymi modułami (moduł sam ustawia tę flagę. Obecnie jest to „Pracownik”, „Linia zewnętrzna”),
  • w przypadku bardziej złożonego planu wybierania, gdy połączenie odbywa się między różnymi grupami, konieczna była możliwość sprawdzenia każdego elementu osobno.

Najlepszą opcją okazało się rozwiązanie, w którym moduły centrali podczas połączeń przesyłają informacje o sobie i docelowo zapisują je w formie drzewa.

To wygląda tak:

Na początek ogólne informacje o rozmowie (jak wszyscy - nic specjalnego).

Historia jednego projektu, czyli jak spędziłem 7 lat tworząc centralę PBX w oparciu o Asterisk i Php

  1. Odebrano połączenie na linii zewnętrznej”Do ciasta„o godzinie 05:55:52 z numeru 89295671458 na numer 89999999999 ostatecznie odebrał pracownik”Sekretarz 2» z numerem 104. Klient czekał 60 sekund i mówił przez 36 sekund.
  2. Pracownik "Sekretarz 2„dzwoni pod numer 112, a pracownik odbiera”Menedżer 1» po 8 sekundach. Rozmawiają przez 14 sekund.
  3. Klient zostaje przeniesiony na Pracownika”menadżer 1", gdzie kontynuują rozmowę przez kolejne 13 sekund

Ale to wierzchołek góry lodowej, dla każdego rekordu można uzyskać szczegółową historię połączeń za pośrednictwem centrali.

Historia jednego projektu, czyli jak spędziłem 7 lat tworząc centralę PBX w oparciu o Asterisk i Php

Wszystkie informacje prezentowane są w formie zagnieżdżenia wywołań:

  1. Odebrano połączenie na linii zewnętrznej”Do ciasta» o godzinie 05:55:52 z numeru 89295671458 na numer 89999999999.
  2. O godzinie 05:55:53 linia zewnętrzna wysyła połączenie do obwodu przychodzącego „test»
  3. Podczas przetwarzania połączenia zgodnie ze schematem moduł „telefon menedżera", w którym rozmowa trwa 16 sekund. Jest to moduł opracowany dla klienta.
  4. Moduł "telefon menedżera" wysyła połączenie do pracownika odpowiedzialnego za numer (klienta) "Menedżer 1” i czeka 5 sekund na odpowiedź. Menedżer nie odpowiedział.
  5. Moduł "telefon menedżera„wysyła połączenie do grupy”Menedżerowie CORP" Są to inni menedżerowie o tym samym kierunku (siedzący w tym samym pokoju) i czekający 11 sekund na odpowiedź.
  6. Grupa "Menedżerowie CORP„dzwoni do pracowników”Menedżer 1, Menedżer 2, Menedżer 3„jednocześnie przez 11 sekund. Brak odpowiedzi.
  7. Rozmowa menedżera kończy się. A obwód wysyła wywołanie do modułu „Wybór trasy z 1c" Również moduł napisany dla klienta. Tutaj połączenie zostało przetworzone przez 0 sekund.
  8. Obwód wysyła połączenie do menu głosowego ”Podstawowy z dodatkowym wybieraniem" Klient czekał tam 31 sekund, nie było żadnego dodatkowego wybierania numeru.
  9. Schemat wysyła wezwanie do Grupy „Sekretarze", gdzie klient czekał 12 sekund.
  10. W grupie wzywa się jednocześnie 2 pracowników”Sekretarz 1"A"Sekretarz 2„i po 12 sekundach pracownik odbiera”Sekretarz 2" Odpowiedź na połączenie jest duplikowana w połączeniach rodziców. Okazuje się, że w grupie odpowiedział „Sekretarz 2„, po wywołaniu obwód odpowiedział”Sekretarz 2" i odebrałem połączenie na linii zewnętrznej za pomocą "Sekretarz 2".

To właśnie zapisanie informacji o każdej operacji i ich zagnieżdżeniu umożliwi proste tworzenie raportów. Raport w menu głosowym pomoże Ci dowiedzieć się, jak bardzo pomaga lub przeszkadza. Sporządź raport o połączeniach nieodebranych przez pracowników, biorąc pod uwagę, że połączenie zostało przechwycone i w związku z tym nie jest uważane za nieodebrane oraz biorąc pod uwagę, że było to połączenie grupowe i ktoś inny odebrał wcześniej, co oznacza, że ​​połączenie również nie zostało nieodebrane.

Takie przechowywanie informacji pozwoli Ci wziąć każdą grupę z osobna i określić, jak efektywnie ona działa, a także zbudować wykres grup odebranych i nieodebranych według godzin. Możesz także sprawdzić jak dokładne jest połączenie z odpowiedzialnym menadżerem analizując przelewy po połączeniu się z menadżerem.

Można też przeprowadzić dość nietypowe badania, np. jak często numery, których nie ma w bazie wybierają właściwy numer wewnętrzny lub jaki procent połączeń wychodzących jest przekazywany na telefon komórkowy.

Wynik?

Do obsługi centrali nie jest potrzebny specjalista, może to zrobić najzwyklejszy administrator – sprawdzony w praktyce.

Do modyfikacji nie są potrzebni specjaliści z poważnymi kwalifikacjami, wystarczy znajomość PHP, bo Napisano już moduły dla protokołu SIP, dla kolejki, dla wywołania pracownika i innych. Istnieje klasa opakowania dla Gwiazdka. Aby opracować moduł, programista może (i w dobrym tego słowa znaczeniu powinien) wywołać gotowe moduły. I wiedza Gwiazdka są zupełnie niepotrzebne, jeśli klient poprosi o dodanie strony z jakimś nowym raportem. Jednak praktyka pokazuje, że choć zewnętrzni programiści dają sobie radę, czują się niepewnie bez dokumentacji i normalnego omawiania komentarzy, więc nadal jest miejsce na ulepszenia.

Moduły mogą:

  • stworzyć nowe możliwości przetwarzania połączeń,
  • dodawać nowe bloki do interfejsu WWW,
  • dziedziczyć z dowolnego z istniejących modułów, przedefiniowywać funkcje i zastępować je lub po prostu być lekko zmodyfikowaną kopią,
  • dodaj swoje ustawienia do szablonu ustawień innych modułów i wiele więcej.

Ustawienia centrali poprzez API. Jak opisano powyżej, wszystkie ustawienia są zapisywane w bazie danych i odczytywane w momencie połączenia, dzięki czemu możesz zmienić wszystkie ustawienia centrali poprzez API. Przy wywołaniu API konfiguracja nie jest odtwarzana i moduły nie są restartowane, dlatego nie ma znaczenia ilu masz ustawień i pracowników. Żądania API są realizowane szybko i nie blokują się nawzajem.

Centrala przechowuje wszystkie kluczowe operacje związane z połączeniami z czasem trwania (oczekiwanie/rozmowa), zagnieżdżeniem oraz w kategoriach PBX (pracownik, grupa, linia zewnętrzna, nie kanał, numer). Pozwala to na budowanie różnorodnych raportów dla konkretnych klientów, a większość pracy polega na stworzeniu przyjaznego dla użytkownika interfejsu.

Czas pokaże, co będzie dalej. Jest jeszcze wiele niuansów do dopracowania, planów jeszcze wiele, ale od powstania 3. wersji minął rok i już możemy powiedzieć, że pomysł się sprawdza. Główną wadą wersji 3 są zasoby sprzętowe, ale zwykle za to trzeba zapłacić, aby ułatwić programowanie.

Źródło: www.habr.com

Dodaj komentarz