2 Aspecte generale ale rețelelor 4G LTE. Supervizarea și managementul acestora.
CUPRINS
1 Introducere
2 Aspecte generale ale rețelelor 4G LTE. Supervizarea și managementul acestora.
2.1 Necesitatea introducerii sistemelor LTE de tip SmallCell
2.2 Avantajele și dezavantajele sistemelor 4G LTE SmallCell (Metro) fața de sistemele clasice 4G LTE (Macro)
2.3 Supervizarea și managementul sistemelor 4G LTE.
2.4 Scalabilitatea aplicațiilor de supervizare.
2.5 Tehnologii software pentru dezvoltarea aplicațiilor distribuite.
2.5.1 Arhitectura pe mai multe nivele folosind ACE
2.5.2 Alegerea protocoalelor de comunicare
2.5.3 Strategii pentru serializarea și deserializarea datelor.
2.5.4 Strategii pentru multiplexarea conexiunilor de rețea.
2.5.5 Strategii multithreading.
2.5.6 Modele (design patterns) pentru tratarea evenimentelor in aplicații server.
3 Realizarea practică
3.1 Serverul de supervizare și management
3.2 Managementul alarmelor.
3.3 Aplicația client pentru supervizare.
3.4 Aplicația client pentru management.
3.5 Simulatorul pentru supervizare și management.
3.6 Serializarea și deserializarea mesajelor.
3.7 Conectarea aplicațiilor la rețea.
3.8 Mediul de dezvoltare
4 Concluzii
5 Bibliografie
6 Anexa
Figura 1 Vedere de ansamblu asupra rețelei 4G LTE.[26] 8
Figura 2 Echipament 4G SmallCell (Metro) eNodeB 9
Figura 3 Supervizarea și managementul sistemlor 4G LTE. 10
Figura 4 Arhitectura framework-ului ACE pe mai multe nivele. 11
Figura 5 Protocoale orientate pe conexiune vs. protocole fară conexiune (tcp vs. udp) 13
Figura 6 Alternative pentru mutiplexarea conexiunilor.[4] 13
Figura 7 Strategia sincrona vs. asincrona pentru schimbul de mesaje.[4] 14
Figura 8 Strategia Half-sync/Half-async vs. Leader/followers[4] 15
Figura 9 Structura participantilor in patternul “Acceptor Connector”[21] 16
Figura 10 Colaborarea între componentele patternului “Acceptor”.[8] 17
Figura 11 Colaborarea participantilor la patternul reactor.[8] 18
Figura 12 Interconectarea aplicațiilor. 19
Figura 13 Ierarhia clasele aplicației server. 20
Figura 14 Instanțierea claselor acceptor. 21
Figura 15 Diagrama de secvență a aplicației server. 22
Figura 16 Serviciile serverului 23
Figura 17 Înregistrarea unui serviciu la reactor. 24
Figura 18Notificarea în timp real a clientului de supervizare. 24
Figura 19 Citirea listei de alarme la pornirea clientului de supervizare. 25
Figura 20 Agregarea claselor Alarm și Alarm_Originator 26
Figura 21 Clasa Supervision_Client 26
Figura 22 Diagrama de secvență a clientului de supervizare. 27
Figura 23Afișarea alarmelor. 28
Figura 24 Descrierea mesajelor 30
Figura 25 Deserializarea și verificare câmpurilor opționale 30
Figura 26 Serializarea mesajelor 31
Figura 27 Conectarea la rețea 31
Figura 28 Intrări esențiale într-un fișier CMakeLists.txt 32
Introducere
Funcționarea continuua și corectă a rețelelor de telecomunicații este vitală pentru viața de zi cu zi a populației. Apelurile telefonice, mesajele scrise și continuuand cu accesul mobil la internet utilizat pentru diferite activitați cum sunt plați electronice, monitorizarea parcurilor auto, accesul la informații și email sau la rețelele de socializare fac parte integrantă din viata noastra.
Reducerea masivă a costurilor de expoatarea a rețelelor mobile de către operatorii de telecomunicații, apariția tehnologiilor 3G și 4G a dus la utilizarea pe scara larga a comunicațiilor mobile. Creșterea foarte mare a numărului de echipamente telecom instalatate în locații indepartate și greu accesibile a dus la dezvoltarea sistemelor de supervizare și management de la disanța a acestora. Domeniul telecom este critic din punct de vedere al timpului total de funcționare fără incidente (high abailability). Nefuncționarea unui echipament duce supraâncarcarea echipamentelor adiacente sau la lipsa serviciului, toate acestea ducând la pierderi financiare și la pierderea încrederii clienților în serviciile oferite de operatorul de telecomunicații. Toate acestea duc la creșterea importanței sistemelor de supervizare.
Numărul foarte mare de echipamente supervizate în timp real introduce probleme de scalabilitate și complexitate foarte mare în proiectarea aplicațiilor de supervizare. Pentru rezolvarea acestor probleme este indicată folosirea unor tehnici de programare care si-au dovedit eficienta, și anume utilizarea unor “design patterns” și folosirea unor framework-uri dedicate.
Aspecte generale ale rețelelor 4G LTE. Supervizarea și managementul acestora.
Necesitatea introducerii sistemelor LTE de tip SmallCell
Odată cu cresterea spectaculoasa a numărului de termainale mobile capabile de trafic LTE a aparut necesitatea creșterii vitezei și lațimii de bandă pentru traficul de tip LTE.
Sistemele clasice au ajuns la limita maxima de viteza și latime de banda, astfel aparând necesitatea crearii unor celule mai mici și mai dense pentru aglomerarile urbane cat și în locurile publice cu un număr foarte mare de utilizatori (stadioane, centre comerciale și clădiri de birouri).
Figura 1 Vedere de ansamblu asupra rețelei 4G LTE.[26]
Conform statisticilor publicate de Verizon, la ultima ediție a Super Bowl traficul inregistrat cu 2 ore înainte de inceperea meciului a fost cu 800% mai mare decat traficul înregistrat la ediția precedentă, transferânduse 1.9 terabytes de date.
Statisticile furnizate de alt operator major din SUA, AT&T au relevat faptul că traficul total din interiorul stadionului a depașit cu mult maximul înregistrat cu alte ocazii similare. [25]
Aceste performațe au fost atinse cu echipamente 4G LTE de tip Small Cell (Metro) furnizate de Alcatel-Lucent.
Avantajele și dezavantajele sistemelor 4G LTE SmallCell (Metro) fața de sistemele clasice 4G LTE (Macro)
Sistemele clasice de tip Macro au nevoie de spații amenajate pentru instalare, turnuri sau construcții dedicate costisitoare pe când sistemele 4G LTE Metro se instalează în locuri publice, pe stâlpi de iluminat, in interiorul clădirilor publice, neavând nevoie de condiții speciale.
Un echipament Macro deservește un număr mare de utilizatori și necesita o planificare prealabilă a planului de frecvențe. Un echipament Metro deservește un număr mai mic de utilizatori și nu necesita o planificare a planului de frecvente. Instalarea sistemelor Metro este mult mai simpla, necesitând doar instalarea fizica, alimentarea și conectarea la rețea (fibra optică sau cablu etherent).
Figura 2 Echipament 4G SmallCell (Metro) eNodeB
Supervizarea și managementul sistemelor 4G LTE.
Supervizarea și managementul de realizează centralizat prin intermediul unei aplicatii software ce permite urmărirea funcționarii echipamentelor. Aceasta aplicație prezintă sub formă de alarme sau evenimente cazurile de eroare sau de depășire a unor praguri ai parametrilor de funcționare normală (temperatură, conectari nereusite, legaturi intrerupte, etc). De asemenea aplicația de management și supervizare permite operații de management: software update, lock/unlock,reset.
Figura 3 Supervizarea și managementul sistemlor 4G LTE.
Datorită numărului foarte mare al echipamentelor supervizate este nevoie ca arhitecura aplicației de supervizare și management să permită scalabilitatea.
Scopul aceste lucrări este prezentarea unor modele de arhitectura pentru acest tip de aplicații.
Scalabilitatea aplicațiilor de supervizare.
Sunt cateva moduri prin care se poate realiza o aplicație scalabilă care să suporte un număr mare de conexiuni și sa proceseze o cantitate mare de date.
– realizarea unei arhitecturi pe mai multe nivele complet separate. Acest lucru permite înlocuirea codului din unele nivele fara necesitatea de a modifica și alte nivele.
– folosirea unei arhitecturi bazata pe design patterns.
– folosirea unui framework dedicat pentru acest tip de software. Un exemplu de framework orientat catre design patterns pentru sisteme concurente și de rețea este ACE (The ADAPTIVE Communication Environment)
– folosirea unor tehnici de balansare a încărcarii pentru distribuirea cererilor de conexiune venite de la echipamentele (eNodeB) 4G LTE SmallCell la mai multe servere.
Tehnologii software pentru dezvoltarea aplicațiilor distribuite.
Arhitectura pe mai multe nivele folosind ACE
ACE – ADAPTIVE Communication Environment
Figura 4 Arhitectura framework-ului ACE pe mai multe nivele.
Sistemul de operare și protocoalele furnizează infrastructura pentru accesarea resurselor hardware.
Host infrastructure middleware încapsuleaza mecanisme native ale sistemului de operare pentru a crea componente reutilizabile. Furnizând acceași interfață catre aplicație, indiferent de sistemul de operare gazda portarea aplicațieie pe diverse sisteme de operare suportate de middleware este mult simplificata.
Distribution middleware simplifică dezvolatarea componentelor care accesează rețeaua și extinde mecanismele sistemului de operare. Se evită dependența de localizarea obiectelor (local sau la distanta), de sistemul de operare, protocoale și hardware.
Common middleware services definește servicii de nivel înalt dependente de domeniul de utilizare. Se pot enumera câteva exemple: managementul dinamic al resurselor, autentificarea și autorizarea, comportamentul tranzacțiilor, conectarea la bazele de date.
Domain-specific middleware services sunt servicii dedicate diverselor domenii, de exemplu telecom, comertului electronic sau automatizarea proceselor.
Avantajul major al programării pe mai multe nivele este că, definind o interfață intre aceste nivele, unele se pot reutiliza in mai multe aplicații sau in contexte diferite. Un dezavantaj este eficiența mai scazută decat în cazul unei implementari monolitice.
Alegerea protocoalelor de comunicare
Un protocol de comunicare este un set de reguli care specifică modul cum diverse sisteme schimbă informații între ele.
Protocoale orientate pe conexiune vs. protocoale fără conexiune (TCP vs. UDP)
Protocoalele orientate pe conexiune oferă servicii sigure, cu secventa garantata și fara duplicarea datelor.Acest tip de protocol este recomandat pentru aplicațiile care nu tolerează pierderea datelor.
Protocoalele fara conexiune oferă servicii nesigure unde fiecare datagramă este rutatată și livrată independent. Din acest motiv, datagramele pot să fie pierdute sau să ajungă în altă ordine decât cea în care au fost trimise.
Figura 5 Protocoale orientate pe conexiune vs. protocole fară conexiune (tcp vs. udp)
Strategii pentru serializarea și deserializarea datelor.
Există doua categorii mari strategii. Una dintre ele se bazează pe transmiterea în format text, sub diverse forme (xml de exemplu). O alta categorie mult mai folosita în aplicațiile cu constrângeri de timp este transmiterea în format binar folosind diverse formate. O modalitate foarte apreciata este încapsularea folosind "Protocol buffers". Aceasta modalitate a fost dezvoltata de catre Google și este folosită intensiv pentru schimbul de informații intre diversele componente software dezvoltate de către Google. Compania Google a permis folosirea liberă a bibliotecii "Protocol Buffers".
Strategii pentru multiplexarea conexiunilor de rețea.
Figura 6 Alternative pentru mutiplexarea conexiunilor.[4]
Conexiuni multiplexate.
În acest caz toate cererile trimise de clientii din acelasi proces sunt procesate În aceeasi conexiune de catre server. Avantajul major este economisirea resurselor. Dezavantajele majore sunt dificultatea implementării și o eficienta scazută.
Conexiuni nemultiplexate.
În acest caz fiecare client utilizează o conexiune separată pentru a comunica cu serviciul pereche.
Avantajele majore sunt controlul mai fin al priorităților și un consum redus de resurse pentru sincronizare. Un dezavantaj este consumul ceva mai ridicat de resurse ale sistemului de operare.
Schimbul de mesaje sincron vs. schimbul de mesaje asincron
Figura 7 Strategia sincrona vs. asincrona pentru schimbul de mesaje.[4]
Schimbul de mesaje sincron funcționeaza intr-o succesiune "trimite" -> "asteapta" -> "receptioneaza". Se imple-specific middleware services sunt servicii dedicate diverselor domenii, de exemplu telecom, comertului electronic sau automatizarea proceselor.
Avantajul major al programării pe mai multe nivele este că, definind o interfață intre aceste nivele, unele se pot reutiliza in mai multe aplicații sau in contexte diferite. Un dezavantaj este eficiența mai scazută decat în cazul unei implementari monolitice.
Alegerea protocoalelor de comunicare
Un protocol de comunicare este un set de reguli care specifică modul cum diverse sisteme schimbă informații între ele.
Protocoale orientate pe conexiune vs. protocoale fără conexiune (TCP vs. UDP)
Protocoalele orientate pe conexiune oferă servicii sigure, cu secventa garantata și fara duplicarea datelor.Acest tip de protocol este recomandat pentru aplicațiile care nu tolerează pierderea datelor.
Protocoalele fara conexiune oferă servicii nesigure unde fiecare datagramă este rutatată și livrată independent. Din acest motiv, datagramele pot să fie pierdute sau să ajungă în altă ordine decât cea în care au fost trimise.
Figura 5 Protocoale orientate pe conexiune vs. protocole fară conexiune (tcp vs. udp)
Strategii pentru serializarea și deserializarea datelor.
Există doua categorii mari strategii. Una dintre ele se bazează pe transmiterea în format text, sub diverse forme (xml de exemplu). O alta categorie mult mai folosita în aplicațiile cu constrângeri de timp este transmiterea în format binar folosind diverse formate. O modalitate foarte apreciata este încapsularea folosind "Protocol buffers". Aceasta modalitate a fost dezvoltata de catre Google și este folosită intensiv pentru schimbul de informații intre diversele componente software dezvoltate de către Google. Compania Google a permis folosirea liberă a bibliotecii "Protocol Buffers".
Strategii pentru multiplexarea conexiunilor de rețea.
Figura 6 Alternative pentru mutiplexarea conexiunilor.[4]
Conexiuni multiplexate.
În acest caz toate cererile trimise de clientii din acelasi proces sunt procesate În aceeasi conexiune de catre server. Avantajul major este economisirea resurselor. Dezavantajele majore sunt dificultatea implementării și o eficienta scazută.
Conexiuni nemultiplexate.
În acest caz fiecare client utilizează o conexiune separată pentru a comunica cu serviciul pereche.
Avantajele majore sunt controlul mai fin al priorităților și un consum redus de resurse pentru sincronizare. Un dezavantaj este consumul ceva mai ridicat de resurse ale sistemului de operare.
Schimbul de mesaje sincron vs. schimbul de mesaje asincron
Figura 7 Strategia sincrona vs. asincrona pentru schimbul de mesaje.[4]
Schimbul de mesaje sincron funcționeaza intr-o succesiune "trimite" -> "asteapta" -> "receptioneaza". Se implementează simplu dar marele dezavantaj este blocarea întregii aplicații pană la recepeționarea răspunsului.
Schimbul de mesaje asincron se realizează fără asteptarea răspunsului. Este mult mai eficient din punct de vedere al paralelismului dar implică o implementare mai complicata.
Strategii multithreading.
Figura 8 Strategia Half-sync/Half-async vs. Leader/followers[4]
Un aspect foarte important al proiectării aplicațiilor este strategia multithreading (“thread pool eager spawning”). Acest lucru se referă la modul în care threadurile aplicației server tratează cererile noi de conexiune și mesajele primite de la clienții conectati.
Una din strategii este cunoscută sub numele "Half-sync/Half-async”. Aceasta constă intr-un grup predefinit de theaduri create la lanasarea serviciului și care citesc dintr-o coada in care sunt puse cererile de conexiune și mesajele recepționate. Acest mod de lucru aduce o scalabiliate ridicată dar și unele aspecte negative cum sunt: multe schimbări de context in cazul threadurilor dar și un consum mai mare de resurse cauzat de mutarea datelor receptionate de mai multe ori.
O altă strategie este cea numita "Leader/Followers". Aceasta constă dintr-un grup de threaduri din care la un moment dat unul este "leader". Acest lucru inseamnă ca el citește și proceseaza evenimentele aparute (cerere de conexiune și date receptionate). Dupa ce citeste, alt thread este ales ca și "leader" și la rândul lui citește și procesează evenimentele aparute. Aceasta strategie aduce un consum redus de resurse dar este mai puțin scalabilă.
Modele (design patterns) pentru tratarea evenimentelor in aplicații server.
Patternul "Acceptor-Connector", asa cum este descris in literatura de specialitate decuplează conectarea și inițializarea serviciilor de rețea de procesarea mesajelor dupa ce serviciile s-au conectat și inițializat. Decuplarea este realizată cu ajutorul a 3 componente principale:
Componenta "acceptor" asteaptă in mod pasiv cereri de conexiune de la clienți stabilind o conexiune în momentul în care este primită o cerere de conexiune.
Componenta "conector" stabilește in mod activ o conexiune cu un acceptor.
Componenta "Service handler" implementează procesearea specifică aplicației.
Figura 9 Structura participantilor in patternul “Acceptor Connector”[21]
Colaborarea între componentele "acceptor" și "service handler".
Figura 10 Colaborarea între componentele patternului “Acceptor”.[8]
Patternul Reactor.
Patternul "Reactor" permite aplicațiilor bazate pe evenimente (event-driven) să demultiplexeze și să redirectioneze cereri de servicii care sunt recepționate de la unul sau mai multi clienți.
Patternul "Reactor" se bazeaza pe interacatiunea a trei componente majore:
Componenta "Handler" este o funcție oferită de sistemul de operare pentru identificarea sursei evenimentelor, cum sunt conexiunile de rețea.
Componenta "Synchronous event demultiplexer" este o funcție apelată când unul sau mai multe evenimente au loc pe un set de handlere.
Componenta "Event handler" definește o interfață pentru procesarea evenimentelor care sunt gestionate de un handler.
Figura 11 Colaborarea participantilor la patternul reactor.[8]
Utilizarea patternului Reactor.
La reactor se înregistrează o clasa de tip Event_Handler a carei metodă handle_input este apelată automat la producerea unui eveniment. Frameworkul ACE implementează reactorul ca un singleton asigurând în acest fel crearea unei singure instanțe a reactorului.
La înregistrarea clasei handler se specifică de asemenea daca se urmăresc evenimente de scriere sau citire (sau ambele).
Realizarea practică
Realizarea practică a lucrarii constă în implementarea unei aplicații de tip server care acceptă și tratează cereri de conexiune de la mai multe instanțe ale unui simulator de servicii de supervizare și management. De asemenea acceptă și tratează cereri de conexiune de la o aplicație de superivizare și o alta de management. Simultorul implementeaza funcționalitatea de supervizare și management dupa același principiu in care această implementare se regăseste într-un echipament eNodeB LTE 4G real.
Simulatorul poate să genereze alarme prin interacțiunea cu utilizatorul. Alarmele pot să fie ridicate sau șterse în funcție de starea evenimentului. De exemplu, la depăsirea unui prag minor de tensiune se va genera o alarmă iar când tensiunea revine la normal alarma este stearsă. În ambele cazuri, cele două evenimente vor ajunge la aplicația de supervizare.
Aplicația de supervizare este de tip client și la conectarea la serverul de supervizare și management citește lista de alarme curente. Alarmele ridicate de simulator după conectarea clientului de superivizare se vor afișa în timp real.
Asemănător, aplicația de management este de tip client și la conectarea la serverul de supervizare și management citește configurația curentă a echipamentelor eNodeB conectate (simulatoare).
Aplicațiile sunt scrise in limbajul de programare C++ și folosesc biblioteca ACE (ADAPTIVE Communication Environment) pentru conctivitatea la rețea și pentru tratarea în mod asincron a mesajelor. De asemenea se folosește biblioteca Protocol Buffers pentru serializarea-deserializarea mesajelor. Din biblioteca STL se folosesc containerele map și list.
Figura 12 Interconectarea aplicațiilor.
Serverul de supervizare și management
Implementează un server cu mai multe servicii. Din biblioteca ACE se folosește patternul reactor (clasa ACE_Reactor) pentru tratamentul asincron și acceptor (ACE_Acceptor) pentru acceptarea cererilor de conexiune.
Figura 13 Ierarhia clasele aplicației server.
Acceptarea cererilor de conexiune se face în clasele eNodeB_Client_Acceptor, Supervision_Client_Acceptor și Management_Client_Acceptor. Ele moștenesc clasa ACE_Acceptor. În clasele derivate se suprascriu metodele open, handle_close și accept_svc_handler.
Clasa de bază ACE_Acceptor oferă servicii de tip server, mai precis acceptarea cererilor de conexiune. Clasele derivate din aceasta implementează metodele open, handle_close și accept_svc_handler cu funcționalitatea necesara pentru fiecare caz.
Clasele eNodeB_Client_Acceptor, Supervision_Client_Acceptor și Management_Client_Acceptopr sunt instațiate în funcția main a aplicației (liniile 7-9 din figura de mai jos).
Figura 14 Instanțierea claselor acceptor.
Tot in funcția main (11-13) se crează obiectele de tip ACE_Inet_Addr.
Clasa ACE_Inet_Addr oferă o interfață unică pentru managementul adreselor de rețea.
Figura 15 Diagrama de secvență a aplicației server.
Serviciile oferite de server sunt implementate in handlere de servicii care sunt înregistrate la reactor.
Figura 16 Serviciile serverului
Obiectul de tip eNodeB_Handler este înregistrat la reactor pentru tratarea evenimentelor recepționate de la simulatoarele eNodeB.
Obiectul de tip Supervision_Handler este înregistrat la reactor pentru tratarea evenimentelor recepționate de la clientul de supervizare.
Obiectul de tip Management_Handler este înregistrat la reactor pentru tratarea evenimentelor recepționate de la clientul de management.
Metodele handle_input ale claselor eNodeB_Handler, Supervision_Handler și Management_Handler sunt apelate în mod automat de către reactor în momentul în care la socketul fiecarui serviciu există date care pot să fie citite.
Figura 17 Înregistrarea unui serviciu la reactor.
Linia 6 exemplifică înregistrarea unui serviciu (în cazul acesta eNodeB_Handler prin pointerul this) la reactor.
Managementul alarmelor.
Fiecare simulator conectat la serverul de supervizare poate sa trimită în orice moment un început sau sfârșit de alarmă. La recepționarea unui început de alarmă, instanța clasei eNodeB_Handler trateaza mesajul, adaugă alarma în lista de alarme curente pe baza unei chei unice. La receptionarea unui sfârsit de alarmă, șterge alarma din lista curenta de alarme și notifica clientul de supervizare asupra acestui lucru.
Figura 18Notificarea în timp real a clientului de supervizare.
În cazul în care clientul de supervizare se conecteaza mai tarziu sau se reconecteaza, el cere serverului de supervizare lista curentă de alarme.
Figura 19 Citirea listei de alarme la pornirea clientului de supervizare.
Informațiile despre alarme sunt salvate intr-un obiect de tip Alarm.
Fiecare alarmă are un număr unic de identificare, o descriere și timpul la care a apărut evenimentul.
Figura 20 Agregarea claselor Alarm și Alarm_Originator
Aplicația client pentru supervizare.
Figura 21 Clasa Supervision_Client
Aplicația pentru supervizare este implementată ca o clasa derivate din ACE_Svc_Handler. Ea suprascrie anumite metode din clasa de bază pentru a implementa funcționalitatea dorită.
De asemenea, aplicația de supervizare instanțiaza o clasa ACE_Reactor pentru a asigura un tratament asincron in lucrul cu rețeaua.
Figura 22 Diagrama de secvență a clientului de supervizare.
Metoda handle_input din clasa Supervision_Clinet este apelată de fiecare dată când reactorul detecteaza prezența datelor la socketul deschis de către connector la realizarea conexiunii. În metoda handle_input este implementată deserializarea datelor și tratarea mesajelor recepționate.
Metoda handle_close este apelată automat la închiderea conexiunii. Suprascrierea ei se folosește pentru a apela cod in mod automat la închiderea conexiunii de către aplicația server.
Funcționalitatea de aplicație client este data de instanțierea clasei ACE_Connector. Clasa ACE_Connector implementează patternul Connector descris in partea introductiva și in [8].
Metoda connect din clasa ACE_Connector se apelează pentru realizarea conexiunii la retea. Parametrii de conectare (adresa IP și port) se tratează într-un obiect de tipul ACE_INET_Addr.
La pornire, după realizarea conexiunii cu serverul de supervizare se trimite un mesaj GET_CURRENT_ALARMS către serverul de supervizare. Acest lucru se realizează pentru citirea alarmelor active. Orice modificare a unei alarme (on/off) este notificata in timp real de catre serverul de supervizare printr-un mesaj ALARM_ON/ALARM_OFF.
Mesajele de supervizare sunt serializate și deserializate conform cu structurile SupervisionHeader și AlarmHeader din fișierul oam.proto conform sintaxei “protocol buffers”.
Figura 23Afișarea alarmelor.
Aplicația client pentru management.
Structura acestei aplicații este similară cu cea a aplicației pentru supervizare.
Simulatorul pentru supervizare și management.
Aplicația care simulează comportamentul unui modul de supervizare și management dintr-un eNodeB este implementată ca o clasa derivata din ACE_Svc_Handler, suprascriind metodele handle_input unde se tratează mesjele primite și handle_close pentru executarea codului atunci cand conexiunea este inchisa. Metoda rise_alarm generează o alarmă, setand câmpurile structurii mesajului AlarmHeader și SupervisionHeader.
În constructorul clasei eNodeB_OAM_Simulator_Handler se înregistreaza obiectul de tip eNodeB_OAM_Simulator_Handler la reactor prin intermediul pointerului this.
Simulatorul citeste apasarea unei taste pentru a trimite un început sau sfârsit de alarmă.
La pornirea aplicatiei este instanțiată clasa ACE_Connector care realizează o conexiune tcp cu serverul de supervizare. La fel ca și în celelate exemple, adresa IP și portul sunt tratate intr-o instanță a clasei ACE_INET_Addr.
Clasa eNodeB_OAM_Simulator_Handler este intr-o relatie de agregare cu clasa eNodeB_Config. Clasa eNodeB_Config trateaza configuratia curenta a simulatorului.
Serializarea și deserializarea mesajelor.
Pentru serializarea și deserializarea mesajelor s-a folosit biblioteca “Protocol buffers”.
Structura mesajelor se descrie într-un fisier structure.proto. Pornind de la această descriere, programele utilitare existente în pachetul “Protocol buffers” genereaza clase și metode accesor pentru serializarea și deserializarea datelor. De asemenea se oferă support pentru verificarea prezenței câmpurilor opționale.
Figura 24 Descrierea mesajelor
Mesajul AlarmHeader are un câmp obligatoriu (linia 2) și doua campuri opționale (liniile 3-4). La randul lui, este un mesaj optional în cadrul mesajului ApplicationMessage (linia 9).
Marele avantaj al utilizării câmpurilor opționale este dat de faptul ca interfața pentru mesaje poate evolua fără a impacta aplicațiile care trimit mesaje. În acest mod se poate schimba doar aplicația server, aplicatiile client ramânând la versiunile curente.
Figura 25 Deserializarea și verificare câmpurilor opționale
După cum se poate vedea in figură, deserializarea este foarte simplă. La linia 4 se verifică prezența câmpului alarm. Codul generat de utilitarele din pachetul protocol buffers oferă metode get pentru citirea câmpurilor din structura.
Figura 26 Serializarea mesajelor
Serializarea implica instantierea claselor generate (linia 1) și apelarea metodelor set (liniile 2-3 ) pentru setarea valorilor. La linia 7 mesajul se serializeaza intr-un buffer fiind pregatit pentru transmitere.
Conectarea aplicațiilor la rețea.
Testarea aplicațiilor s-a făcut folosind interfața de rețea locala (loopback – 127.0.0.1). Porturile folosite sunt:
10001 pentru aplicația de supervizare
10002 pentru aplicația de management
10003 pentru simulatorul eNodeB
Figura 27 Conectarea la rețea
Mediul de dezvoltare
Pentru compilarea proiectului s-a folosit pachetul de programe utilitare CMake. CMake generează fișiere Makefile pe baza descrierii cerintelor de compilare în fisierele CMakeLists.txt prezente în fiecare director. Directorul părinte al proiectului conține urmatorul fisier CMakeLists.txt
Figura 28 Intrări esențiale într-un fișier CMakeLists.txt
La linia 2 se seteaza numele proiectului, liniile 4-8 setează subdirectoarele proiectului.
La linia 11 se setează flagurile de compilare. –Wall este folosit pentru a informa compilatorul să activeze toate avertismentele de compilare. –g activeaza flagul pentru a informa compilatorul să includă informații pentru depanare.
Linia 13 setează verificarea existenței pachetului “Protobuf”. Linia 15 setează directorul unde se gasesc fisierele *.h
Linia 16 informează compilatorul unde se găsește biblioteca generată în urma compilarii fișierelor din directorul src.
Pentru dezvoltarea proiectului s-a folosit o distribuție linux Debian 7.0.3, compilatorul gcc 4.7.2, gdb, valgrind și ca mediu de dezvoltare emacs. Diagramele claselor și desenele au fost realizate cu programul yEd Graph Editor disponibil gratuit.
Concluzii
Sistemele de comunicații sunt elemente critice din viata de zi cu zi. Supervizarea corespunzătoare este obligatorie deoarece funcționarea necorespunzatoare a acestora are impact major asupra utilizatorilor. Aceste aspecte impun o disciplina strictă in procesul de dezvoltare software. Folosirea unui framework dedicat asigură o dezvoltare rapidă, cu calitate ridicată și costuri mai mici decât în cazul dezvoltării pornind de la 0 a produsului software. De asemenea folosirea unor tehnici de programare corespunzatoare asigură o întreținere ușoară a proiectului software. In general, aplicațiile de supervizare și management au o complexitate foarte ridicată și un cod sursă foarte vast. Folosirea unor tehnici de programare descrise in literature de specialitate [6][7][8] asigură o întelegere mai rapida a codului sursă. Folosirea unui framework dedicate reduce major timpul de dezvoltare deoarece anumite funcționalităti au fost dezvoltate și testate anterior. Reutilizarea codului sursă duce la o mai usoară intelegere și intretinere a lui. Folosirea framework-urilor duce în timp la o abordare de dezvoltare pe mai multe nivele, programatorii de aplicații ignorând detaliile de nivel jos (rețea, semnale). Marele dezavantaj este că pe termen lung ei pierd deprinderile de a programa la nivel scazut (rețea, semnale).
Bibliografie
[1] An Introduction to GCC for the GNU Compilers gcc and g++
Brian Gough
ISBN 0-9541617-9-3
[2] The Linux Programming Interface – A Linux and UNIX System Programming Handbook
Michael Kerrisk
[3] ACE Programmer's Guide, The: Practical Design Patterns for Network and Systems Programming
Stephen D. Huston, James CE Johnson, Umar Syyid
Addison Wesley, November 14, 2003
123-139pp 168-184pp
[4] C++ Network Programming, Volume 1: Mastering Complexity with ACE and Patterns
By Douglas C. Schmidt, Stephen D. Huston
Publisher : Addison Wesley
Pub Date : December 10, 2001
21-32 pp
[5] C++ Network Programming, Volume 2: Systematic Reuse with ACE and Frameworks
By Douglas C. Schmidt, Stephen D. Huston
Publisher : Addison Wesley
Pub Date : October 29, 2002
46-60pp 203-256pp
[6] Design Patterns: Elements of Reusable Object-Oriented Software
by Erich Gamma , Richard Helm , Ralph Johnson , John Vlissides
Publication Date: November 10, 1994
97-106 pp
[7] Pattern-Oriented Software Architecture Volume 1: A System of Patterns
by Frank Buschmann , Regine Meunier, Hans Rohnert, Peter Sommerlad, Michael Stal, Michael Stal
Publication Date: August 16, 1996
ISBN-10: 0471958697 | ISBN-13: 978-0471958697
97-98 pp
[8] Pattern-Oriented Software Architecture Volume 2: Patterns for Concurrent and Networked Objects
Douglas Schmidt, Michael Stal, Hans Rohnert, Frank Buschmannrich Gamma, Richard Helm, Ralph Johnson, and John Vlissides
September 14, 2000
179-214 pp 285-322 pp
[9] Thinking in C++: Introduction to Standard C++, Volume One
Bruce Eckel
Prentice Hall; 2 edition (March 25, 2000)
[10] The C++ Programming Language, 4th Edition
Bjarne Stroustrup
May 19, 2013
[11] C++ Primer Plus (6th Edition) (Developer's Library)
Stephen Prata
October 28, 2011
[12] The C++ Standard Library: A Tutorial and Reference (2nd Edition)
Nicolai M. Josuttis
April 9, 2012
[13] Modern C++ Design: Generic Programming and Design Patterns Applied
Andrei Alexandrescu
Addison-Wesley Professional; 1 edition (February 23, 2001)
[14] Code Complete: A Practical Handbook of Software Construction, Second Edition
Steve McConnell
June 19, 2004
[15] Clean Code: A Handbook of Agile Software Craftsmanship
Robert C. Martin
August 11, 2008
ISBN-10: 0132350882 | ISBN-13: 978-0132350884
[16] UML Distilled: A Brief Guide to the Standard Object Modeling Language (3rd Edition)
Martin Fowler
Addison-Wesley Professional; 3 edition (September 25, 2003)
Language: English
35-61 pp
[17] Designing Concurrent, Distributed, and Real-Time Applications with UML
Hassan Gomaa
Addison-Wesley Professional 10/16/2013
57-90 pp
[18] Fourth-generation Wireless Networks: Applications and Innovations
Sasan Adibi , Amin Mobasher, Tom Tofigh
Information Science Publishing; 1 edition (December 31, 2009)
[19] Small Cell Networks: Deployment, PHY Techniques, and Resource Management
Tony Q. S. Quek, Guillaume de la Roche, Ismail Güvenç, Marios Kountouris
Cambridge University Press (June 28, 2013)
[20] The Design and Use of the ACE Reactor
An Object-Oriented Framework for Event Demultiplexing
Douglas C. Schmidt and Irfan Pyarali
Department of Computer Science
Washington University, St. Louis 631301
http://www.cs.wustl.edu/~schmidt/PDF/reactor-rules.pdf
[21] Acceptor-Connector
An Object Creational Pattern for Connecting and Initializing Communication Services
Douglas C. Schmidt
Department of Computer Science
Washington University
St. Louis, MO 63130, USA
http://www.cs.wustl.edu/~schmidt/PDF/Acc-Con.pdf
[22] Pattern-Oriented Software Architectures
Patterns & Frameworks for Concurrent & Distributed Systems
Douglas C. Schmidt
Vanderbilt University Nashville, Tennessee
www.dre.vanderbilt.edu/~schmidt/posa2.ppt
[23] Documentația bibliotecii “Protocol buffers”
https://developers.google.com/protocol-buffers/docs/overview
[24] Documențatia framework-ului ACE
http://www.dre.vanderbilt.edu/Doxygen/6.2.6/html/libace-doc/annotated.html
[25] Impactul evenimentelor sportive asupra retelelor 4G
http://www.fiercewireless.com/special-reports/super-bowl-xlviii-how-did-tier-1-wireless-carriers-networks-hold
[26] Qualcomm small cell deployment model
http://www.qualcomm.com/media/documents/neighborhood-small-cell-deployment-model
Anexa
Anexa conține o parte din fișierele cu implementarea aplicației.
Fisierul oam_appli.cpp
#include <iostream>
#include "oam.pb.h"
#include "Supervision_Client_Acceptor.h"
#include "Supervision_Handler.h"
#include "Management_Client_Acceptor.h"
#include "Management_Handler.h"
#include "eNodeB_Client_Acceptor.h"
#include "eNodeB_Handler.h"
#include "Handler_Manager.h"
#include "defs.h"
#define SUPERVISION_PORT 10001
#define MANAGEMENT_PORT 10002
#define ENODEB_PORT 10003
#define HOST "127.0.0.1"
int main(int argc, char**argv)
{
Supervision_Client_Acceptor *supervision_connection_acceptor;
Management_Client_Acceptor *management_connection_acceptor;
eNodeB_Client_Acceptor *enodeb_connection_acceptor;
Handler_Manager *handler_manager;
ACE_NEW_RETURN (handler_manager,Handler_Manager(),-1);
ACE_NEW_RETURN (supervision_connection_acceptor,Supervision_Client_Acceptor(handler_manager),-1);
ACE_NEW_RETURN (management_connection_acceptor,Management_Client_Acceptor(handler_manager),-1);
ACE_NEW_RETURN (enodeb_connection_acceptor,eNodeB_Client_Acceptor(handler_manager),-1);
Fisierul eNodeB_Handler.h:
#ifndef ENODEB_HANDLER_H
#define ENODEB_HANDLER_H
#include <iostream>
#include <string>
#include <map>
#include <utility>
#include <ace/SOCK_Connector.h>
#include <ace/Svc_Handler.h>
#include "defs.h"
#include "Handler_Manager.h"
#include "Alarm.h"
#include "Alarm_Originator.h"
#include "Active_Alarm.h"
#include "eNodeB_Config.h"
class Handler_Manager;
class eNodeB_Handler : public ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_SYNCH>
{
public:
eNodeB_Handler (void);
~eNodeB_Handler (void);
virtual int open (void * = 0);
virtual int handle_close (ACE_HANDLE = ACE_INVALID_HANDLE,
ACE_Reactor_Mask = ACE_Event_Handler::ALL_EVENTS_MASK);
virtual int handle_input (ACE_HANDLE = ACE_INVALID_HANDLE);
void handler_manager(Handler_Manager *);
Handler_Manager *handler_manager(void) const;
void peer_handle(HANDLE handle){peer_handle_ = handle;};
HANDLE peer_handle(){ return peer_handle_; }
void add_active_alarm(std::string, Active_Alarm *);
void remove_alarm(std::string);
void add_current_config(std::string, eNodeB_Config *);
void remove_config(std::string);
void get_active_alarms(void);
void get_current_config(void);
private:
int forwardSupervisionClient(char *message, int size);
int forward_management_client(char *message, int size);
int send_message(void *buffer, int size, int destination);
Handler_Manager *handler_manager_;
std::map<std::string, Active_Alarm *> activeAlarms;
std::map<std::string, eNodeB_Config *> currentConfigs;
HANDLE peer_handle_;
};
#endif
Fisierul eNodeB_Handler.cpp:
#include "eNodeB_Handler.h"
#include "Alarm.h"
#include <iostream>
#include "oam.pb.h"
eNodeB_Handler::eNodeB_Handler(void){}
eNodeB_Handler::~eNodeB_Handler (void){}
int eNodeB_Handler::open(void * )
{
handler_manager_->setENodeBHandler(this);
peer_handle(peer().get_handle());
if(ACE_Reactor::instance ()->register_handler(this, ACE_Event_Handler::READ_MASK) == -1){
return -1;
}
return 0;
}
int eNodeB_Handler::handle_close(ACE_HANDLE, ACE_Reactor_Mask)
{
this->peer ().close ();
this->peer ().set_handle (-1);
ACE_Reactor::instance ()->remove_handler(this, ACE_Event_Handler::READ_MASK | ACE_Event_Handler::DONT_CALL);
return -1;
}
int eNodeB_Handler::handle_input(ACE_HANDLE)
{
std::cout << "eNodeB_Handler::handle_input" << std::endl;
char message[1024];
char header_rec[256];
int received = 0;
ssize_t msg_size;
msg_size = peer().recv(header_rec, 10);
if ( msg_size != APPLI_HEADER_SIZE)
{
std::cout << "recv_n error" << std::endl;
}
if(msg_size <= 0)
return -1;
memcpy(message, header_rec, APPLI_HEADER_SIZE);
std::cout << "Received: " << received << std::endl;
ApplicationHeader appli_header;
appli_header.ParseFromArray(header_rec,APPLI_HEADER_SIZE);
std::cout << "Version: " << appli_header.version() << std::endl;
std::cout << "Length: " << appli_header.length() << std::endl;
char payload_rec[256];
msg_size = peer().recv(payload_rec, appli_header.length());
if ( msg_size != appli_header.length()) {
std::cout << "recv_n error 2" << std::endl;
}
if(msg_size <= 0)
return -1;
memcpy(message+APPLI_HEADER_SIZE, payload_rec, msg_size);
ApplicationMessage application_msg;
application_msg.ParseFromArray(payload_rec, msg_size);
if(application_msg.has_config_parameters()){
std::cout << "Are config " << std::endl;
const ConfigParameters &config = application_msg.config_parameters();
std::cout << "ID: " << config.unique_id().c_str() << std::endl;
std::cout << "IP: " << config.ip_address().c_str() << std::endl;
std::cout << "Nume: " << config.name().c_str() << std::endl;
std::cout << "Adresa: " << config.address().c_str() << std::endl;
std::cout << "Tip: " << config.type() << std::endl;
std::cout << "Putere: " << config.tx_power() << std::endl;
eNodeB_Config *current_config = new eNodeB_Config(config.ip_address(), config.name(), config.address(), config.type(), config.tx_power());
add_current_config(config.unique_id(), current_config);
forward_management_client(message, msg_size + APPLI_HEADER_SIZE);
}
if(application_msg.has_supervision()){
const SupervisionHeader &supervision = application_msg.supervision();
char name[256];
strcpy(name, supervision.equipment_name().c_str());
std::cout << "Name: " << name << std::endl;
std::cout << "Equipment id: " << supervision.unique_id().c_str() << std::endl;
std::cout << "Type: " << supervision.type() << std::endl;
const AlarmHeader &alarm = application_msg.alarm();
char description[256];
strcpy(description, alarm.alarm_description().c_str());
std::cout << "alarm id: " << alarm.alarm_id() << std::endl;
std::cout << "alarm description: " << description << std::endl;
//std::string time="10:1:1";
Alarm *alarm_info = new Alarm(alarm.alarm_id(), alarm.alarm_description(), alarm.time_stamp());
Alarm_Originator *alarm_originator = new Alarm_Originator(supervision.unique_id(),
supervision.ip(),
supervision.equipment_name(),
supervision.equipment_address());
Active_Alarm *active_alarm = new Active_Alarm(alarm_info, alarm_originator);
char alarm_id[16];
sprintf(alarm_id, "%d-", alarm.alarm_id());
std::string alarm_key(alarm_id);
alarm_key = alarm_key + supervision.unique_id();
switch(supervision.type()){
case ALARM_ON:
add_active_alarm(alarm_key, active_alarm);
break;
case ALARM_OFF:
remove_alarm(alarm_key);
break;
}
forwardSupervisionClient(message, msg_size + APPLI_HEADER_SIZE);
}
return 0;
}
int eNodeB_Handler::forwardSupervisionClient(char *message, int size)
{
Supervision_Handler *supervision = NULL;
supervision = handler_manager_->getSupervisionHandler();
if(supervision){
HANDLE handle = supervision->peer_handle();
send(handle, message, size,0);
}
return 0;
}
int eNodeB_Handler::forward_management_client(char *message, int size)
{
Management_Handler *management = NULL;
management = handler_manager_->getManagementHandler();
if(management){
HANDLE handle = management->peer_handle();
send(handle, message, size,0);
}
return 0;
}
void eNodeB_Handler::handler_manager(Handler_Manager *handler_manager)
{
handler_manager_ = handler_manager;
}
Handler_Manager *eNodeB_Handler::handler_manager(void) const
{
return handler_manager_;
}
void eNodeB_Handler::add_active_alarm(std::string key, Active_Alarm *active_alarm)
{
std::cout << "Add alarm with key: " << key.c_str() << "size: " << activeAlarms.size() << std::endl;
activeAlarms.insert(std::make_pair(key, active_alarm));
}
void eNodeB_Handler::remove_alarm(std::string key)
{
std::cout << "Remove alarm with key: " << key.c_str() << std::endl;
Active_Alarm *active_alarm=NULL;
for (std::map<std::string, Active_Alarm *>::iterator it=activeAlarms.begin();
it!=activeAlarms.end(); ++it){
if((*it).first == key){
active_alarm = (*it).second;
activeAlarms.erase(it);
break;
}
}
if(active_alarm){
delete active_alarm;
}
}
void eNodeB_Handler::add_current_config(std::string key, eNodeB_Config *config)
{
std::cout << "Add config with key: " << key.c_str() << "size: " << currentConfigs.size() << std::endl;
currentConfigs.insert(std::make_pair(key, config));
}
void eNodeB_Handler::remove_config(std::string key)
{
eNodeB_Config *config=NULL;
for (std::map<std::string, eNodeB_Config *>::iterator it=currentConfigs.begin(); it!=currentConfigs.end(); ++it){
if((*it).first == key){
config = (*it).second;
currentConfigs.erase(it);
break;
}
}
if(config){
delete config;
}
}
int eNodeB_Handler::send_message(void *buffer, int size, int destination)
{
char data[256];
bzero(data,256);
ApplicationHeader appli_header;
appli_header.set_version(0x01);
appli_header.set_length(size);
int header_size = appli_header.ByteSize();
void *header_data = malloc(header_size);
appli_header.SerializeToArray(header_data,APPLI_HEADER_SIZE);
memcpy(data, header_data, APPLI_HEADER_SIZE);
memcpy(data + APPLI_HEADER_SIZE, buffer, size);
if(destination==1){
Supervision_Handler *supervision = NULL;
supervision = handler_manager_->getSupervisionHandler();
if(supervision){
HANDLE handle = supervision->peer_handle();
send(handle, data, size+APPLI_HEADER_SIZE ,0);
}
}
if(destination==2){
Management_Handler *management = NULL;
management = handler_manager_->getManagementHandler();
if(management){
HANDLE handle = management->peer_handle();
send(handle, data, size+APPLI_HEADER_SIZE ,0);
}
}
return 0;
}
void eNodeB_Handler::get_active_alarms(void)
{
for(std::map<std::string, Active_Alarm*>::iterator ii=activeAlarms.begin(); ii!=activeAlarms.end(); ++ii)
{
ApplicationMessage application_msg;
SupervisionHeader *supervision= application_msg.mutable_supervision();
AlarmHeader *alarm_header = application_msg.mutable_alarm();
std::string key;
key = (*ii).first;
std::cout << "KEY in active alarms: " << key.c_str() << std::endl;
Alarm_Originator *alarm_originator = (*ii).second->get_Alarm_Originator();
Alarm *alarm = (*ii).second->get_Alarm();
supervision->set_unique_id(alarm_originator->get_uniqueId());
supervision->set_ip(alarm_originator->get_ip());
supervision->set_equipment_name(alarm_originator->get_equipmentName());
supervision->set_equipment_address(alarm_originator->get_equipmentAddress());
supervision->set_type(1);
alarm_header->set_alarm_id(alarm->get_alarmId());
alarm_header->set_alarm_description(alarm->get_alarmDescription());
alarm_header->set_time_stamp(alarm->get_timeStamp());
int size = application_msg.ByteSize();
std::cout << "SIZE: " << size << std::endl;
void *buffer = malloc(size);
application_msg.SerializeToArray(buffer, size);
send_message(buffer,size, 1);
}
}
void eNodeB_Handler::get_current_config(void)
{
for(std::map<std::string, eNodeB_Config*>::iterator ii=currentConfigs.begin(); ii!=currentConfigs.end(); ++ii)
{
ApplicationMessage application_msg;
ConfigParameters *config_parameters= application_msg.mutable_config_parameters();
std::string key;
key = (*ii).first;
std::cout << "KEY in current config: " << key.c_str() << std::endl;
eNodeB_Config *enodeb_config = (*ii).second;
config_parameters->set_unique_id((*ii).first);
config_parameters->set_ip_address(enodeb_config->get_eNodeB_IP_Address());
config_parameters->set_name(enodeb_config->get_eNodeB_Name());
config_parameters->set_address(enodeb_config->get_eNodeB_Address());
config_parameters->set_type(enodeb_config->get_eNodeB_Type());
config_parameters->set_tx_power(enodeb_config->get_eNodeB_txPower());
int size = application_msg.ByteSize();
std::cout << "SIZE: " << size << std::endl;
void *buffer = malloc(size);
application_msg.SerializeToArray(buffer, size);
send_message(buffer,size, 2);
}
}
Fisierul eNodeB_Client_Acceptor.h
#ifndef ENODEB_CLIENT_ACCEPTOR_H
#define ENODEB_CLIENT_ACCEPTOR_H
#include <ace/SOCK_Acceptor.h>
#include <ace/Acceptor.h>
#include "eNodeB_Handler.h"
#include "Handler_Manager.h"
class eNodeB_Client_Acceptor : public ACE_Acceptor<eNodeB_Handler, ACE_SOCK_Acceptor>
{
public:
typedef ACE_Acceptor<eNodeB_Handler, ACE_SOCK_Acceptor> super;
eNodeB_Client_Acceptor(Handler_Manager *);
virtual int open(const ACE_SOCK_Acceptor::PEER_ADDR &local_addr,
ACE_Reactor *reactor = ACE_Reactor::instance(),
int flags = 0, int use_select = 1, int reuse_addr = 1);
virtual int handle_close (ACE_HANDLE = ACE_INVALID_HANDLE,ACE_Reactor_Mask = ACE_Event_Handler::ALL_EVENTS_MASK);
virtual int accept_svc_handler (eNodeB_Handler *sh);
private:
Handler_Manager *handler_manager_;
};
#endif
Fisierul eNodeB_Client_Acceptor.cpp
#include "eNodeB_Client_Acceptor.h"
eNodeB_Client_Acceptor::eNodeB_Client_Acceptor(Handler_Manager *handler_manager) : handler_manager_(handler_manager)
{
}
int eNodeB_Client_Acceptor::open (const ACE_SOCK_Acceptor::PEER_ADDR &local_addr,
ACE_Reactor *reactor,
int flags, int use_select, int reuse_addr)
{
if(super::open (local_addr, reactor, flags, use_select, reuse_addr) != 0)
return -1;
return 0;
}
int eNodeB_Client_Acceptor::handle_close (ACE_HANDLE h, ACE_Reactor_Mask mask)
{
super::handle_close (h, mask);
delete this;
return 0;
}
int eNodeB_Client_Acceptor::accept_svc_handler (eNodeB_Handler *sh)
{
if(super::accept_svc_handler(sh) == -1){
return -1;
}else{
ACE_INET_Addr remote_addr;
if(sh->peer().get_remote_addr(remote_addr) == -1){
return -1;
}
sh->handler_manager(handler_manager_);
}
return 0;
}
Descrierea structurii mesajelot. Fisierul oam.proto
message ApplicationHeader{
required sfixed32 version = 1;
required sfixed32 length = 2;
};
message SupervisionHeader{
required string unique_id = 1;
required string ip = 2;
required string equipment_name = 3;
required string equipment_address = 4;
required int32 type = 5;
};
message AlarmHeader{
required int32 alarm_id = 1;
optional string alarm_description = 2;
required string time_stamp = 3;
};
message ConfigParameters{
required string unique_id = 1;
optional string ip_address = 2;
optional string name = 3;
optional string address = 4;
optional int32 type = 5;
optional int32 tx_power = 6;
};
message ManagementMessages{
required int32 message = 1;
};
message ApplicationMessage{
optional SupervisionHeader supervision = 1;
optional AlarmHeader alarm = 2;
optional ManagementMessages management = 3;
optional ConfigParameters config_parameters = 4;
};
Bibliografie
[1] An Introduction to GCC for the GNU Compilers gcc and g++
Brian Gough
ISBN 0-9541617-9-3
[2] The Linux Programming Interface – A Linux and UNIX System Programming Handbook
Michael Kerrisk
[3] ACE Programmer's Guide, The: Practical Design Patterns for Network and Systems Programming
Stephen D. Huston, James CE Johnson, Umar Syyid
Addison Wesley, November 14, 2003
123-139pp 168-184pp
[4] C++ Network Programming, Volume 1: Mastering Complexity with ACE and Patterns
By Douglas C. Schmidt, Stephen D. Huston
Publisher : Addison Wesley
Pub Date : December 10, 2001
21-32 pp
[5] C++ Network Programming, Volume 2: Systematic Reuse with ACE and Frameworks
By Douglas C. Schmidt, Stephen D. Huston
Publisher : Addison Wesley
Pub Date : October 29, 2002
46-60pp 203-256pp
[6] Design Patterns: Elements of Reusable Object-Oriented Software
by Erich Gamma , Richard Helm , Ralph Johnson , John Vlissides
Publication Date: November 10, 1994
97-106 pp
[7] Pattern-Oriented Software Architecture Volume 1: A System of Patterns
by Frank Buschmann , Regine Meunier, Hans Rohnert, Peter Sommerlad, Michael Stal, Michael Stal
Publication Date: August 16, 1996
ISBN-10: 0471958697 | ISBN-13: 978-0471958697
97-98 pp
[8] Pattern-Oriented Software Architecture Volume 2: Patterns for Concurrent and Networked Objects
Douglas Schmidt, Michael Stal, Hans Rohnert, Frank Buschmannrich Gamma, Richard Helm, Ralph Johnson, and John Vlissides
September 14, 2000
179-214 pp 285-322 pp
[9] Thinking in C++: Introduction to Standard C++, Volume One
Bruce Eckel
Prentice Hall; 2 edition (March 25, 2000)
[10] The C++ Programming Language, 4th Edition
Bjarne Stroustrup
May 19, 2013
[11] C++ Primer Plus (6th Edition) (Developer's Library)
Stephen Prata
October 28, 2011
[12] The C++ Standard Library: A Tutorial and Reference (2nd Edition)
Nicolai M. Josuttis
April 9, 2012
[13] Modern C++ Design: Generic Programming and Design Patterns Applied
Andrei Alexandrescu
Addison-Wesley Professional; 1 edition (February 23, 2001)
[14] Code Complete: A Practical Handbook of Software Construction, Second Edition
Steve McConnell
June 19, 2004
[15] Clean Code: A Handbook of Agile Software Craftsmanship
Robert C. Martin
August 11, 2008
ISBN-10: 0132350882 | ISBN-13: 978-0132350884
[16] UML Distilled: A Brief Guide to the Standard Object Modeling Language (3rd Edition)
Martin Fowler
Addison-Wesley Professional; 3 edition (September 25, 2003)
Language: English
35-61 pp
[17] Designing Concurrent, Distributed, and Real-Time Applications with UML
Hassan Gomaa
Addison-Wesley Professional 10/16/2013
57-90 pp
[18] Fourth-generation Wireless Networks: Applications and Innovations
Sasan Adibi , Amin Mobasher, Tom Tofigh
Information Science Publishing; 1 edition (December 31, 2009)
[19] Small Cell Networks: Deployment, PHY Techniques, and Resource Management
Tony Q. S. Quek, Guillaume de la Roche, Ismail Güvenç, Marios Kountouris
Cambridge University Press (June 28, 2013)
[20] The Design and Use of the ACE Reactor
An Object-Oriented Framework for Event Demultiplexing
Douglas C. Schmidt and Irfan Pyarali
Department of Computer Science
Washington University, St. Louis 631301
http://www.cs.wustl.edu/~schmidt/PDF/reactor-rules.pdf
[21] Acceptor-Connector
An Object Creational Pattern for Connecting and Initializing Communication Services
Douglas C. Schmidt
Department of Computer Science
Washington University
St. Louis, MO 63130, USA
http://www.cs.wustl.edu/~schmidt/PDF/Acc-Con.pdf
[22] Pattern-Oriented Software Architectures
Patterns & Frameworks for Concurrent & Distributed Systems
Douglas C. Schmidt
Vanderbilt University Nashville, Tennessee
www.dre.vanderbilt.edu/~schmidt/posa2.ppt
[23] Documentația bibliotecii “Protocol buffers”
https://developers.google.com/protocol-buffers/docs/overview
[24] Documențatia framework-ului ACE
http://www.dre.vanderbilt.edu/Doxygen/6.2.6/html/libace-doc/annotated.html
[25] Impactul evenimentelor sportive asupra retelelor 4G
http://www.fiercewireless.com/special-reports/super-bowl-xlviii-how-did-tier-1-wireless-carriers-networks-hold
[26] Qualcomm small cell deployment model
http://www.qualcomm.com/media/documents/neighborhood-small-cell-deployment-model
Anexa
Anexa conține o parte din fișierele cu implementarea aplicației.
Fisierul oam_appli.cpp
#include <iostream>
#include "oam.pb.h"
#include "Supervision_Client_Acceptor.h"
#include "Supervision_Handler.h"
#include "Management_Client_Acceptor.h"
#include "Management_Handler.h"
#include "eNodeB_Client_Acceptor.h"
#include "eNodeB_Handler.h"
#include "Handler_Manager.h"
#include "defs.h"
#define SUPERVISION_PORT 10001
#define MANAGEMENT_PORT 10002
#define ENODEB_PORT 10003
#define HOST "127.0.0.1"
int main(int argc, char**argv)
{
Supervision_Client_Acceptor *supervision_connection_acceptor;
Management_Client_Acceptor *management_connection_acceptor;
eNodeB_Client_Acceptor *enodeb_connection_acceptor;
Handler_Manager *handler_manager;
ACE_NEW_RETURN (handler_manager,Handler_Manager(),-1);
ACE_NEW_RETURN (supervision_connection_acceptor,Supervision_Client_Acceptor(handler_manager),-1);
ACE_NEW_RETURN (management_connection_acceptor,Management_Client_Acceptor(handler_manager),-1);
ACE_NEW_RETURN (enodeb_connection_acceptor,eNodeB_Client_Acceptor(handler_manager),-1);
Fisierul eNodeB_Handler.h:
#ifndef ENODEB_HANDLER_H
#define ENODEB_HANDLER_H
#include <iostream>
#include <string>
#include <map>
#include <utility>
#include <ace/SOCK_Connector.h>
#include <ace/Svc_Handler.h>
#include "defs.h"
#include "Handler_Manager.h"
#include "Alarm.h"
#include "Alarm_Originator.h"
#include "Active_Alarm.h"
#include "eNodeB_Config.h"
class Handler_Manager;
class eNodeB_Handler : public ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_SYNCH>
{
public:
eNodeB_Handler (void);
~eNodeB_Handler (void);
virtual int open (void * = 0);
virtual int handle_close (ACE_HANDLE = ACE_INVALID_HANDLE,
ACE_Reactor_Mask = ACE_Event_Handler::ALL_EVENTS_MASK);
virtual int handle_input (ACE_HANDLE = ACE_INVALID_HANDLE);
void handler_manager(Handler_Manager *);
Handler_Manager *handler_manager(void) const;
void peer_handle(HANDLE handle){peer_handle_ = handle;};
HANDLE peer_handle(){ return peer_handle_; }
void add_active_alarm(std::string, Active_Alarm *);
void remove_alarm(std::string);
void add_current_config(std::string, eNodeB_Config *);
void remove_config(std::string);
void get_active_alarms(void);
void get_current_config(void);
private:
int forwardSupervisionClient(char *message, int size);
int forward_management_client(char *message, int size);
int send_message(void *buffer, int size, int destination);
Handler_Manager *handler_manager_;
std::map<std::string, Active_Alarm *> activeAlarms;
std::map<std::string, eNodeB_Config *> currentConfigs;
HANDLE peer_handle_;
};
#endif
Fisierul eNodeB_Handler.cpp:
#include "eNodeB_Handler.h"
#include "Alarm.h"
#include <iostream>
#include "oam.pb.h"
eNodeB_Handler::eNodeB_Handler(void){}
eNodeB_Handler::~eNodeB_Handler (void){}
int eNodeB_Handler::open(void * )
{
handler_manager_->setENodeBHandler(this);
peer_handle(peer().get_handle());
if(ACE_Reactor::instance ()->register_handler(this, ACE_Event_Handler::READ_MASK) == -1){
return -1;
}
return 0;
}
int eNodeB_Handler::handle_close(ACE_HANDLE, ACE_Reactor_Mask)
{
this->peer ().close ();
this->peer ().set_handle (-1);
ACE_Reactor::instance ()->remove_handler(this, ACE_Event_Handler::READ_MASK | ACE_Event_Handler::DONT_CALL);
return -1;
}
int eNodeB_Handler::handle_input(ACE_HANDLE)
{
std::cout << "eNodeB_Handler::handle_input" << std::endl;
char message[1024];
char header_rec[256];
int received = 0;
ssize_t msg_size;
msg_size = peer().recv(header_rec, 10);
if ( msg_size != APPLI_HEADER_SIZE)
{
std::cout << "recv_n error" << std::endl;
}
if(msg_size <= 0)
return -1;
memcpy(message, header_rec, APPLI_HEADER_SIZE);
std::cout << "Received: " << received << std::endl;
ApplicationHeader appli_header;
appli_header.ParseFromArray(header_rec,APPLI_HEADER_SIZE);
std::cout << "Version: " << appli_header.version() << std::endl;
std::cout << "Length: " << appli_header.length() << std::endl;
char payload_rec[256];
msg_size = peer().recv(payload_rec, appli_header.length());
if ( msg_size != appli_header.length()) {
std::cout << "recv_n error 2" << std::endl;
}
if(msg_size <= 0)
return -1;
memcpy(message+APPLI_HEADER_SIZE, payload_rec, msg_size);
ApplicationMessage application_msg;
application_msg.ParseFromArray(payload_rec, msg_size);
if(application_msg.has_config_parameters()){
std::cout << "Are config " << std::endl;
const ConfigParameters &config = application_msg.config_parameters();
std::cout << "ID: " << config.unique_id().c_str() << std::endl;
std::cout << "IP: " << config.ip_address().c_str() << std::endl;
std::cout << "Nume: " << config.name().c_str() << std::endl;
std::cout << "Adresa: " << config.address().c_str() << std::endl;
std::cout << "Tip: " << config.type() << std::endl;
std::cout << "Putere: " << config.tx_power() << std::endl;
eNodeB_Config *current_config = new eNodeB_Config(config.ip_address(), config.name(), config.address(), config.type(), config.tx_power());
add_current_config(config.unique_id(), current_config);
forward_management_client(message, msg_size + APPLI_HEADER_SIZE);
}
if(application_msg.has_supervision()){
const SupervisionHeader &supervision = application_msg.supervision();
char name[256];
strcpy(name, supervision.equipment_name().c_str());
std::cout << "Name: " << name << std::endl;
std::cout << "Equipment id: " << supervision.unique_id().c_str() << std::endl;
std::cout << "Type: " << supervision.type() << std::endl;
const AlarmHeader &alarm = application_msg.alarm();
char description[256];
strcpy(description, alarm.alarm_description().c_str());
std::cout << "alarm id: " << alarm.alarm_id() << std::endl;
std::cout << "alarm description: " << description << std::endl;
//std::string time="10:1:1";
Alarm *alarm_info = new Alarm(alarm.alarm_id(), alarm.alarm_description(), alarm.time_stamp());
Alarm_Originator *alarm_originator = new Alarm_Originator(supervision.unique_id(),
supervision.ip(),
supervision.equipment_name(),
supervision.equipment_address());
Active_Alarm *active_alarm = new Active_Alarm(alarm_info, alarm_originator);
char alarm_id[16];
sprintf(alarm_id, "%d-", alarm.alarm_id());
std::string alarm_key(alarm_id);
alarm_key = alarm_key + supervision.unique_id();
switch(supervision.type()){
case ALARM_ON:
add_active_alarm(alarm_key, active_alarm);
break;
case ALARM_OFF:
remove_alarm(alarm_key);
break;
}
forwardSupervisionClient(message, msg_size + APPLI_HEADER_SIZE);
}
return 0;
}
int eNodeB_Handler::forwardSupervisionClient(char *message, int size)
{
Supervision_Handler *supervision = NULL;
supervision = handler_manager_->getSupervisionHandler();
if(supervision){
HANDLE handle = supervision->peer_handle();
send(handle, message, size,0);
}
return 0;
}
int eNodeB_Handler::forward_management_client(char *message, int size)
{
Management_Handler *management = NULL;
management = handler_manager_->getManagementHandler();
if(management){
HANDLE handle = management->peer_handle();
send(handle, message, size,0);
}
return 0;
}
void eNodeB_Handler::handler_manager(Handler_Manager *handler_manager)
{
handler_manager_ = handler_manager;
}
Handler_Manager *eNodeB_Handler::handler_manager(void) const
{
return handler_manager_;
}
void eNodeB_Handler::add_active_alarm(std::string key, Active_Alarm *active_alarm)
{
std::cout << "Add alarm with key: " << key.c_str() << "size: " << activeAlarms.size() << std::endl;
activeAlarms.insert(std::make_pair(key, active_alarm));
}
void eNodeB_Handler::remove_alarm(std::string key)
{
std::cout << "Remove alarm with key: " << key.c_str() << std::endl;
Active_Alarm *active_alarm=NULL;
for (std::map<std::string, Active_Alarm *>::iterator it=activeAlarms.begin();
it!=activeAlarms.end(); ++it){
if((*it).first == key){
active_alarm = (*it).second;
activeAlarms.erase(it);
break;
}
}
if(active_alarm){
delete active_alarm;
}
}
void eNodeB_Handler::add_current_config(std::string key, eNodeB_Config *config)
{
std::cout << "Add config with key: " << key.c_str() << "size: " << currentConfigs.size() << std::endl;
currentConfigs.insert(std::make_pair(key, config));
}
void eNodeB_Handler::remove_config(std::string key)
{
eNodeB_Config *config=NULL;
for (std::map<std::string, eNodeB_Config *>::iterator it=currentConfigs.begin(); it!=currentConfigs.end(); ++it){
if((*it).first == key){
config = (*it).second;
currentConfigs.erase(it);
break;
}
}
if(config){
delete config;
}
}
int eNodeB_Handler::send_message(void *buffer, int size, int destination)
{
char data[256];
bzero(data,256);
ApplicationHeader appli_header;
appli_header.set_version(0x01);
appli_header.set_length(size);
int header_size = appli_header.ByteSize();
void *header_data = malloc(header_size);
appli_header.SerializeToArray(header_data,APPLI_HEADER_SIZE);
memcpy(data, header_data, APPLI_HEADER_SIZE);
memcpy(data + APPLI_HEADER_SIZE, buffer, size);
if(destination==1){
Supervision_Handler *supervision = NULL;
supervision = handler_manager_->getSupervisionHandler();
if(supervision){
HANDLE handle = supervision->peer_handle();
send(handle, data, size+APPLI_HEADER_SIZE ,0);
}
}
if(destination==2){
Management_Handler *management = NULL;
management = handler_manager_->getManagementHandler();
if(management){
HANDLE handle = management->peer_handle();
send(handle, data, size+APPLI_HEADER_SIZE ,0);
}
}
return 0;
}
void eNodeB_Handler::get_active_alarms(void)
{
for(std::map<std::string, Active_Alarm*>::iterator ii=activeAlarms.begin(); ii!=activeAlarms.end(); ++ii)
{
ApplicationMessage application_msg;
SupervisionHeader *supervision= application_msg.mutable_supervision();
AlarmHeader *alarm_header = application_msg.mutable_alarm();
std::string key;
key = (*ii).first;
std::cout << "KEY in active alarms: " << key.c_str() << std::endl;
Alarm_Originator *alarm_originator = (*ii).second->get_Alarm_Originator();
Alarm *alarm = (*ii).second->get_Alarm();
supervision->set_unique_id(alarm_originator->get_uniqueId());
supervision->set_ip(alarm_originator->get_ip());
supervision->set_equipment_name(alarm_originator->get_equipmentName());
supervision->set_equipment_address(alarm_originator->get_equipmentAddress());
supervision->set_type(1);
alarm_header->set_alarm_id(alarm->get_alarmId());
alarm_header->set_alarm_description(alarm->get_alarmDescription());
alarm_header->set_time_stamp(alarm->get_timeStamp());
int size = application_msg.ByteSize();
std::cout << "SIZE: " << size << std::endl;
void *buffer = malloc(size);
application_msg.SerializeToArray(buffer, size);
send_message(buffer,size, 1);
}
}
void eNodeB_Handler::get_current_config(void)
{
for(std::map<std::string, eNodeB_Config*>::iterator ii=currentConfigs.begin(); ii!=currentConfigs.end(); ++ii)
{
ApplicationMessage application_msg;
ConfigParameters *config_parameters= application_msg.mutable_config_parameters();
std::string key;
key = (*ii).first;
std::cout << "KEY in current config: " << key.c_str() << std::endl;
eNodeB_Config *enodeb_config = (*ii).second;
config_parameters->set_unique_id((*ii).first);
config_parameters->set_ip_address(enodeb_config->get_eNodeB_IP_Address());
config_parameters->set_name(enodeb_config->get_eNodeB_Name());
config_parameters->set_address(enodeb_config->get_eNodeB_Address());
config_parameters->set_type(enodeb_config->get_eNodeB_Type());
config_parameters->set_tx_power(enodeb_config->get_eNodeB_txPower());
int size = application_msg.ByteSize();
std::cout << "SIZE: " << size << std::endl;
void *buffer = malloc(size);
application_msg.SerializeToArray(buffer, size);
send_message(buffer,size, 2);
}
}
Fisierul eNodeB_Client_Acceptor.h
#ifndef ENODEB_CLIENT_ACCEPTOR_H
#define ENODEB_CLIENT_ACCEPTOR_H
#include <ace/SOCK_Acceptor.h>
#include <ace/Acceptor.h>
#include "eNodeB_Handler.h"
#include "Handler_Manager.h"
class eNodeB_Client_Acceptor : public ACE_Acceptor<eNodeB_Handler, ACE_SOCK_Acceptor>
{
public:
typedef ACE_Acceptor<eNodeB_Handler, ACE_SOCK_Acceptor> super;
eNodeB_Client_Acceptor(Handler_Manager *);
virtual int open(const ACE_SOCK_Acceptor::PEER_ADDR &local_addr,
ACE_Reactor *reactor = ACE_Reactor::instance(),
int flags = 0, int use_select = 1, int reuse_addr = 1);
virtual int handle_close (ACE_HANDLE = ACE_INVALID_HANDLE,ACE_Reactor_Mask = ACE_Event_Handler::ALL_EVENTS_MASK);
virtual int accept_svc_handler (eNodeB_Handler *sh);
private:
Handler_Manager *handler_manager_;
};
#endif
Fisierul eNodeB_Client_Acceptor.cpp
#include "eNodeB_Client_Acceptor.h"
eNodeB_Client_Acceptor::eNodeB_Client_Acceptor(Handler_Manager *handler_manager) : handler_manager_(handler_manager)
{
}
int eNodeB_Client_Acceptor::open (const ACE_SOCK_Acceptor::PEER_ADDR &local_addr,
ACE_Reactor *reactor,
int flags, int use_select, int reuse_addr)
{
if(super::open (local_addr, reactor, flags, use_select, reuse_addr) != 0)
return -1;
return 0;
}
int eNodeB_Client_Acceptor::handle_close (ACE_HANDLE h, ACE_Reactor_Mask mask)
{
super::handle_close (h, mask);
delete this;
return 0;
}
int eNodeB_Client_Acceptor::accept_svc_handler (eNodeB_Handler *sh)
{
if(super::accept_svc_handler(sh) == -1){
return -1;
}else{
ACE_INET_Addr remote_addr;
if(sh->peer().get_remote_addr(remote_addr) == -1){
return -1;
}
sh->handler_manager(handler_manager_);
}
return 0;
}
Descrierea structurii mesajelot. Fisierul oam.proto
message ApplicationHeader{
required sfixed32 version = 1;
required sfixed32 length = 2;
};
message SupervisionHeader{
required string unique_id = 1;
required string ip = 2;
required string equipment_name = 3;
required string equipment_address = 4;
required int32 type = 5;
};
message AlarmHeader{
required int32 alarm_id = 1;
optional string alarm_description = 2;
required string time_stamp = 3;
};
message ConfigParameters{
required string unique_id = 1;
optional string ip_address = 2;
optional string name = 3;
optional string address = 4;
optional int32 type = 5;
optional int32 tx_power = 6;
};
message ManagementMessages{
required int32 message = 1;
};
message ApplicationMessage{
optional SupervisionHeader supervision = 1;
optional AlarmHeader alarm = 2;
optional ManagementMessages management = 3;
optional ConfigParameters config_parameters = 4;
};
Copyright Notice
© Licențiada.org respectă drepturile de proprietate intelectuală și așteaptă ca toți utilizatorii să facă același lucru. Dacă consideri că un conținut de pe site încalcă drepturile tale de autor, te rugăm să trimiți o notificare DMCA.
Acest articol: 2 Aspecte generale ale rețelelor 4G LTE. Supervizarea și managementul acestora. (ID: 162700)
Dacă considerați că acest conținut vă încalcă drepturile de autor, vă rugăm să depuneți o cerere pe pagina noastră Copyright Takedown.
